小提示:
1,大家本身了解eclipse使用,也已经安装了gnu+eclipse环境,参考: http://blog.csdn.net/kuankuan02/article/details/79233442
2,同时下载了 picoRV32的 open source code:
https://github.com/cliffordwolf/picorv32
3,你要有硬件环境可以跑bin,我是有数字工程师提供了FPGA平台。
正文:
1,eclipse新建一个工程,选择RISC-V c project创建。
2,然后加入你所下载的picoRV32_master/fireware里的c code,我下面简单介绍下ASM部分
start.s是启动代码以及trap处理代码,reset_vec 是开始,摆在0地址;后面.balign 16 对齐,因为picoRV32 trap地址在0x10,所以后面是trap处理代码。记住picoRV32 ASM 不要使用CSRs读写指令,CPU不认的。它的trap上下文切换所要用的stack是irq_regs定义的数据块,因为我的bin都是下载到flash中,所以给它加个.bss section属性,把它移到ram去。正好再提一下,系统stack是在ld脚本里申请的section,这个code是把系统stack和trap stack分开了,很多情况下都是用的同一个。它的技巧是自定义指令getq和setq来交换自定义寄存器q0~q3,每次trap都重新获取trap stack 指针,这是个很笨的上下文保存方法,大家只拿来先跑起来bin就好。启动代码还要注意的是.bss 必须清0,以及.data 必须搬入ram中,sp 赋值以后就可以开始进入c code了,输出个hello world。我的系统是uart输出,所以uart初始化也加到start.s中。
irq_regs:
// registers are saved to this memory region during interrupt handling
// the program counter is saved as register 0
.fill 32,4
// stack for the interrupt handler
.fill 256,4
irq_stack:
3,
picoRV32额外实现了一些指令,getq、setq、retirq等等,具体内容可以到README.md查找。
这些指令gcc编译器是不认识的,我们不能用指令名字来使用它们,它们只能采用.word 0x1234abcd 样式的机器码方式把它们插入代码中,这在汇编代码里是可以实现的。(参考custom_ops.S)
4,对于bin的连接,最重要的就是ld脚本,这个跟不同系统的address mapping相关,由于一些原因这里无法展开说。
额外解释两个问题:
picoRV32为什么要自定义ISA呢?
因为它没有实现特权级文档里的CSRs状态寄存器(除了cycle counter和instruction-retired counter),而在trap/interrupt 响应的时候,有一些事情是需要额外寄存器协助处理。在这里picoRV32搭配了四个32bits寄存器用于Interrupt function,这就是我们之前介绍的custom interrupt controller。
q0~q3四个寄存器:
q0 存储了interrupt return地址
q1 用于表示interrupt status bit
q2、 q3 用于和通用寄存器进行数据交换
getq 把q[n]赋值到通用寄存器
setq 把通用寄存器值赋值到q[n]
retirq 返回到q0存储的地址执行,用于从interrupt返回
maskirq 可以操作interrupt mask register,用于屏蔽interrupt bit ,我把它用于中断开关控制了,就像ARM cpsr中的irq enable bit一样使用
None OS:正常流程下while(1) <>interrupt ,程序流程比较简单,代码只需实现save/restore/return
FreeRTOS:task<>task ,task<>interrupt,interrupt<>task,程序相对复杂,代码要把os port部分都实现掉
Trap:可以理解为一个硬件问题处理机制,当硬件自己处理不了的时候让CPU跳转到一个固定地址,让软件工程师写code去帮助硬件来处理它无法处理的状况,比如是interrupt/softinterrupt/exception等等。这些事情的判断就需要去读取各种CSRs,才能知道CPU为什么要跳转出来,同时记录CPU跳转时的地址处理完毕才能正确返回。
在picoRV32上把这些都简化了,而且它的Trap约等于了interrupt,工程师可直接按interrupt的方式处理绝大部分状况。把返回地址赋值给q0,然后调用retirq就可以从interrupt返回到q0所保存的地址,这个操作同样适用于task间切换。
关于FreeRTOS 和picoRV32间port我有实现好,但还没经过产品验证,暂时不能抛出来,我想肯定会有还未发现的bug,等产品出来后再介绍吧。