安路SparkRoad开发板测评(8) MCU 软核 RISC-V程序编写
[复制链接]
前面我已经推测出来,Agile RISC-V软核的程序是放在当中的BRAM IP内的,以32-bit字为单位,复位后从BRAM的99(十进制)开始运行。入口地址换算成字节地址是0x18C.
初始化BRAM的方式用.mif文件比较方便,所以我对cpu_gate.v再略作修改,将四个EG_LOGIC_BRAM模块参数INIT_FILE分别改为 ”init1.mif”,”init2.mif”,”init3.mif”,”init4.mif”,不再将数据以字符串方式嵌在.v文件内。
作为准备工作,我还需要编写一个将RISC-V编译器生成的.bin文件转换为.mif文件的工具。因为这个用途特殊,需要将32位字拆成四个字节分散到四个文件内。用C编写一个:
第一个测试程序,测试循环是否能够执行:
编译指令:riscv gcc -c -O2 -march=rv32i -mabi=ilp32 test0.c
生成ELF文件:riscv ld -Ttext 0x18c test0.o -o test0.elf
反汇编查看编译结果:riscv objdump -d test0.elf
test0.elf: file format elf32-littleriscv
Disassembly of section .text:
0000018c <_start>:
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 <_start-0x8c>
1a0: 10d02223 sw a3,260(zero) # 104 <_start-0x88>
1a4: 10e02423 sw a4,264(zero) # 108 <_start-0x84>
1a8: 10f02623 sw a5,268(zero) # 10c <_start-0x80>
1ac: fedff06f j 198 <_start+0xc>
GCC编译说明:因为不知道Agile RISC-V实现的指令集,使用最基础的32位指令架构,用 -march=rv32i指定。链接时候不需要任何其它代码,为了将 _start入口地址定在0x18c,将.text段地址指定为0x18c.
生成.bin文件:riscv objcopy -Obinary test0.elf test0.bin
2022-05-07 19:02 36 test0.bin
1 个文件 36 字节
程序一共9条指令,每条指令4字节。为了将这9条指令放到RAM的0x18c地址处,需要在生成.mif文件的时候从第99个字节开始放程序内容。上面我贴的转换程序中已经做了处理。
例如,生成的init1.mif文件内容如下
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;
将四个.mif文件复制到FPGA工程目录,重新综合,下载。观察运行效果:
可以看到取指令地址从99开始,增加到107之后变到102,然后在102到107之间循环。这和我编写的代码中最后一条指令(0x1ac处)是跳转到0x198吻合。可以判定循环得到了正确执行。循环当中往0x100, 0x104, 0x108, 0x10c地址写了数,这个操作结果还无法验证。
下一步,查找软核的外部总线地址在哪里。因为软核引出的bus_addr宽度是28-bit,数据为32-bit,地址范围是1GB. 先猜测外部总线地址是对齐到256MB边界的,也就是开始于0x10000000的整数倍这样的地址。写个程序尝试往某些地址写入,然后观看外部总线上的信号,如果有地址和数据输出以及有写信号,就能推断地址映射关系。
第二个测试程序:
编译后反汇编代码:
Disassembly of section .text:
0000018c <_start>:
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 <_start+0xc>
1ac: fe5ff06f j 190 <_start+0x4>
为了观察总线动作,连接到板子上的LED:
运行效果:
一个大循环中捕捉到四次总线总线写操作,通过LED指示的地址和数据,可以判断外部总线映射地址在0x80000000~0xBFFFFFFC范围,因为没有bit mask,只支持32位的读写。
这样就可以在程序中访问外部总线了,FPGA中可以编写硬件设备供程序操作。
还需要判断一下软核中SRAM的地址。上面的程序中没有用到SRAM所以不知道其地址也没有问题。现在猜SRAM位于0x80000000以下的地址,先按128MB对齐的地址搜索一遍(一共16个地址),判断一下是否是可读写的地址:写的数和读回来的一致则认为存在SRAM. 使用外部总线输出指示找到的有效地址。
第三个测试程序:
运行时候,写外部总线地址 1, 数据0x80. 因此程序中i=0 时发现了SRAM. 故SRAM位于地址0x0开始的地方。
最后再用一个测试程序,将SRAM的内容读出来,写到外部总线,这样可以通过LED查看SRAM中的内容。读外部总线是读16个开关状态,通过开关选择要读的SRAM地址。
反汇编代码:
Disassembly of section .text:
0000018c <_start>:
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 <_start+0x8>
运行效果(将时钟加快了,提高响应速度)
可以发现,SRAM的内容大部分都是0, 在0x18c地址,读到了0x93值,以及其它几个位置都和程序代码地址对应的数据一致。因此说明SRAM和存放程序的ROM是同一块存储,这也侧面验证了软核中使用双端口BRAM的目的:程序和数据使用同一RAM空间。
知道了RAM的地址和程序地址,就可以编写复杂一点的程序了。
这个MCU软核中还有GPIO和UART,以及外部中断输入功能。但是因为缺乏资料,很难去探测怎么使用。
|