cruelfox 发表于 2022-5-11 13:22

安路SparkRoad开发板测评(8) MCU 软核 RISC-V程序编写

<p>  前面我已经推测出来,Agile RISC-V软核的程序是放在当中的BRAM IP内的,以32-bit字为单位,复位后从BRAM的99(十进制)开始运行。入口地址换算成字节地址是0x18C.</p>

<p>  初始化BRAM的方式用.mif文件比较方便,所以我对cpu_gate.v再略作修改,将四个EG_LOGIC_BRAM模块参数INIT_FILE分别改为 &rdquo;init1.mif&rdquo;,&rdquo;init2.mif&rdquo;,&rdquo;init3.mif&rdquo;,&rdquo;init4.mif&rdquo;,不再将数据以字符串方式嵌在.v文件内。</p>

<p></p>

<p>  作为准备工作,我还需要编写一个将RISC-V编译器生成的.bin文件转换为.mif文件的工具。因为这个用途特殊,需要将32位字拆成四个字节分散到四个文件内。用C编写一个:</p>

<p></p>

<p>&nbsp;</p>

<p>  第一个测试程序,测试循环是否能够执行:</p>

<p></p>

<p>&nbsp;</p>

<p>  编译指令:<span style="color:#2980b9;"><span style="font-family:Courier;"><strong>riscv gcc -c -O2 -march=rv32i -mabi=ilp32 test0.c</strong></span></span></p>

<p>  生成ELF文件:<strong><span style="color:#2980b9;"><span style="font-family:Courier;">riscv ld -Ttext 0x18c test0.o -o test0.elf</span></span></strong></p>

<p>  反汇编查看编译结果:<span style="color:#2980b9;"><span style="font-family:Courier;"><strong>riscv objdump -d test0.elf</strong></span></span></p>

<pre>
<code>test0.elf:   file format elf32-littleriscv

Disassembly of section .text:

0000018c &lt;_start&gt;:
18c:   00100693                li      a3,1
190:   00200713                li      a4,2
194:   00300793                li      a5,3
198:   00000013                nop
19c:   10002023                sw      zero,256(zero) # 100 &lt;_start-0x8c&gt;
1a0:   10d02223                sw      a3,260(zero) # 104 &lt;_start-0x88&gt;
1a4:   10e02423                sw      a4,264(zero) # 108 &lt;_start-0x84&gt;
1a8:   10f02623                sw      a5,268(zero) # 10c &lt;_start-0x80&gt;
1ac:   fedff06f                j       198 &lt;_start+0xc&gt;
</code></pre>

<p>&nbsp;</p>

<p>  GCC编译说明:因为不知道Agile RISC-V实现的指令集,使用最基础的32位指令架构,用 -march=rv32i指定。链接时候不需要任何其它代码,为了将 _start入口地址定在0x18c,将.text段地址指定为0x18c.</p>

<p>  生成.bin文件:<span style="color:#2980b9;"><strong><span style="font-family:Courier;">riscv objcopy -Obinary test0.elf test0.bin</span></strong></span></p>

<p><span style="font-family:Microsoft YaHei;"><span style="background-color:#dddddd;">2022-05-07 19:02 36    test0.bin</span></span></p>

<p><span style="font-family:Microsoft YaHei;"><span style="background-color:#dddddd;">         1 个文件 36 字节</span></span></p>

<p>  程序一共9条指令,每条指令4字节。为了将这9条指令放到RAM的0x18c地址处,需要在生成.mif文件的时候从第99个字节开始放程序内容。上面我贴的转换程序中已经做了处理。</p>

<p>  例如,生成的init1.mif文件内容如下</p>

<pre>
<code>DEPTH=1024;
WIDTH=8;
ADDRESS_RADIX=UNS;
DATA_RADIX=HEX;

CONTENT BEGIN
0 : 00;
1 : 00;
2 : 00;
3 : 00;
…省略若干行…
97 : 00;
98 : 00;
99 : 93;
100 : 13;
101 : 93;
102 : 13;
103 : 23;
104 : 23;
105 : 23;
106 : 23;
107 : 6F;
END;</code></pre>

<p>  将四个.mif文件复制到FPGA工程目录,重新综合,下载。观察运行效果:</p>

<p><iframe allowfullscreen="true" frameborder="0" height="510" src="https://training.eeworld.com.cn/shareOpenCourseAPI?isauto=true&amp;lessonid=33364" style="background:#eee;margin-bottom:10px;" width="700"></iframe></p>

<p>&nbsp;</p>

<p>  可以看到取指令地址从99开始,增加到107之后变到102,然后在102到107之间循环。这和我编写的代码中最后一条指令(0x1ac处)是跳转到0x198吻合。可以判定循环得到了正确执行。循环当中往0x100, 0x104, 0x108, 0x10c地址写了数,这个操作结果还无法验证。</p>

<p>&nbsp;</p>

<p>  下一步,查找软核的外部总线地址在哪里。因为软核引出的bus_addr宽度是28-bit,数据为32-bit,地址范围是1GB. 先猜测外部总线地址是对齐到256MB边界的,也就是开始于0x10000000的整数倍这样的地址。写个程序尝试往某些地址写入,然后观看外部总线上的信号,如果有地址和数据输出以及有写信号,就能推断地址映射关系。</p>

<p>&nbsp;</p>

<p>  第二个测试程序:</p>

<p></p>

<p>  编译后反汇编代码:</p>

<pre>
<code>Disassembly of section .text:

0000018c &lt;_start&gt;:
18c:   01000613                li      a2,16
190:   00000013                nop
194:   00000793                li      a5,0
198:   01c79713                slli    a4,a5,0x1c
19c:   00578693                addi    a3,a5,5
1a0:   00d72e23                sw      a3,28(a4)
1a4:   00178793                addi    a5,a5,1
1a8:   fec798e3                bne   a5,a2,198 &lt;_start+0xc&gt;
1ac:   fe5ff06f                j       190 &lt;_start+0x4&gt;</code></pre>

<p>  为了观察总线动作,连接到板子上的LED:</p>

<p></p>

<p>  运行效果:</p>

<p><iframe allowfullscreen="true" frameborder="0" height="510" src="https://training.eeworld.com.cn/shareOpenCourseAPI?isauto=true&amp;lessonid=33365" style="background:#eee;margin-bottom:10px;" width="700"></iframe><br />
&nbsp;</p>

<p>  一个大循环中捕捉到四次总线总线写操作,通过LED指示的地址和数据,可以判断外部总线映射地址在0x80000000~0xBFFFFFFC范围,因为没有bit mask,只支持32位的读写。</p>

<p>这样就可以在程序中访问外部总线了,FPGA中可以编写硬件设备供程序操作。</p>

<p>&nbsp;</p>

<p>  还需要判断一下软核中SRAM的地址。上面的程序中没有用到SRAM所以不知道其地址也没有问题。现在猜SRAM位于0x80000000以下的地址,先按128MB对齐的地址搜索一遍(一共16个地址),判断一下是否是可读写的地址:写的数和读回来的一致则认为存在SRAM. 使用外部总线输出指示找到的有效地址。</p>

<p>&nbsp;</p>

<p>  第三个测试程序:</p>

<p></p>

<p></p>

<p>  运行时候,写外部总线地址 1, 数据0x80. 因此程序中i=0 时发现了SRAM. 故SRAM位于地址0x0开始的地方。</p>

<p>&nbsp;</p>

<p>  最后再用一个测试程序,将SRAM的内容读出来,写到外部总线,这样可以通过LED查看SRAM中的内容。读外部总线是读16个开关状态,通过开关选择要读的SRAM地址。</p>

<p></p>

<p>  反汇编代码:</p>

<pre>
<code>Disassembly of section .text:

0000018c &lt;_start&gt;:
18c:   00000793                li      a5,0
190:   800006b7                lui   a3,0x80000
194:   00f687b3                add   a5,a3,a5
198:   0007a783                lw      a5,0(a5)
19c:   0007c703                lbu   a4,0(a5)
1a0:   00279793                slli    a5,a5,0x2
1a4:   00f68633                add   a2,a3,a5
1a8:   0ff77713                andi    a4,a4,255
1ac:   00e62023                sw      a4,0(a2)
1b0:   fe5ff06f                j       194 &lt;_start+0x8&gt;</code></pre>

<p>&nbsp;</p>

<p>  运行效果(将时钟加快了,提高响应速度)</p>

<p><iframe allowfullscreen="true" frameborder="0" height="510" src="https://training.eeworld.com.cn/shareOpenCourseAPI?isauto=true&amp;lessonid=33366" style="background:#eee;margin-bottom:10px;" width="700"></iframe><br />
&nbsp;</p>

<p>  可以发现,SRAM的内容大部分都是0, 在0x18c地址,读到了0x93值,以及其它几个位置都和程序代码地址对应的数据一致。因此说明SRAM和存放程序的ROM是同一块存储,这也侧面验证了软核中使用双端口BRAM的目的:程序和数据使用同一RAM空间。</p>

<p>  知道了RAM的地址和程序地址,就可以编写复杂一点的程序了。</p>

<p>  这个MCU软核中还有GPIO和UART,以及外部中断输入功能。但是因为缺乏资料,很难去探测怎么使用。</p>

littleshrimp 发表于 2022-5-11 16:16

<p>不如试试picorv32,有人好像有人移植过。</p>

bigbat 发表于 2022-5-13 16:35

<p>picorv32好像还有jtag接口,就不需要使用内置的方式写程序了</p>

cruelfox 发表于 2022-5-13 18:14

<p>弄pico risc-v就随便一块FPGA搞了,与本评测的目的不在一条线上。</p>

sans5555 发表于 2022-5-18 20:12

<p>大佬好强,RISCV芯片目前可以使用的开发板不是很多</p>

mrhsue 发表于 2023-9-4 16:44

bigbat 发表于 2022-5-13 16:35
picorv32好像还有jtag接口,就不需要使用内置的方式写程序了

<p>picorv32有jtag接口吗?印象中是没有的呢</p>
页: [1]
查看完整版本: 安路SparkRoad开发板测评(8) MCU 软核 RISC-V程序编写