Anlu SparkRoad Development Board Review (8) MCU Soft-core RISC-V Programming
[Copy link]
I have already guessed that the program of Agile RISC-V soft core is placed in the BRAM IP, in units of 32-bit words, and starts from 99 (decimal) of BRAM after reset. The entry address converted into byte address is 0x18C.
It is more convenient to use .mif files to initialize BRAM, so I slightly modified cpu_gate.v and changed the four EG_LOGIC_BRAM module parameters INIT_FILE to "init1.mif", "init2.mif", "init3.mif", and "init4.mif" respectively, and no longer embedded the data in the .v file in string form.
As a preparatory work, I also need to write a tool to convert the .bin file generated by the RISC-V compiler into a .mif file. Because this is a special purpose, the 32-bit word needs to be split into four bytes and scattered into four files. Write one in C:
The first test program tests whether the loop can be executed:
Compilation instruction: riscv gcc -c -O2 -march=rv32i -mabi=ilp32 test0.c
Generate ELF file: riscv ld -Ttext 0x18c test0.o -o test0.elf
Disassemble and view the compilation results: 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 compilation instructions: Because the instruction set implemented by Agile RISC-V is unknown, the most basic 32-bit instruction architecture is used, specified with -march=rv32i. No other code is required when linking. In order to set the _start entry address at 0x18c, the .text segment address is specified as 0x18c.
Generate .bin file: riscv objcopy -Obinary test0.elf test0.bin
2022-05-07 19:02 36 test0.bin
1 file 36 bytes
The program has 9 instructions in total, each 4 bytes. In order to put these 9 instructions at the address 0x18c in RAM, you need to put the program content from the 99th byte when generating the .mif file. This has been done in the conversion program I posted above.
For example, the generated init1.mif file content is as follows
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;
Copy the four .mif files to the FPGA project directory, re-synthesize, and download. Observe the running effect:
It can be seen that the instruction fetch address starts at 99, increases to 107, then changes to 102, and then loops between 102 and 107. This is consistent with the last instruction (0x1ac) in the code I wrote, which jumps to 0x198. It can be determined that the loop is executed correctly. In the loop, numbers are written to addresses 0x100, 0x104, 0x108, and 0x10c, and the result of this operation cannot be verified yet.
Next, find the external bus address of the soft core. Because the bus_addr width of the soft core is 28-bit, the data is 32-bit, and the address range is 1GB. First guess that the external bus address is aligned to the 256MB boundary, that is, it starts at an integer multiple of 0x10000000. Write a program to try to write to certain addresses, and then watch the signals on the external bus. If there are address and data outputs and write signals, the address mapping relationship can be inferred.
Second test program:
Disassembled code after compilation:
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>
To observe the bus action, connect to the LED on the board:
Operation effect:
Four bus write operations are captured in a large loop. Through the address and data indicated by the LED, it can be determined that the external bus mapping address is in the range of 0x80000000~0xBFFFFFFC. Because there is no bit mask, only 32-bit read and write are supported.
In this way, the external bus can be accessed in the program, and hardware devices can be written in the FPGA for program operation.
We also need to determine the address of the SRAM in the soft core. The above program does not use SRAM, so it is okay if we do not know its address. Now guess that the SRAM is located at an address below 0x80000000. First search the address aligned with 128MB (a total of 16 addresses) and determine whether it is a readable and writable address: if the written number is consistent with the read number, it is considered that the SRAM exists. Use the external bus output to indicate the valid address found.
The third test program:
When running, write external bus address 1, data 0x80. Therefore, SRAM is found when i=0 in the program. Therefore, SRAM is located at the beginning of address 0x0.
Finally, a test program is used to read the contents of the SRAM and write them to the external bus, so that the contents of the SRAM can be viewed through the LED. Reading the external bus is to read the states of 16 switches, and the SRAM address to be read is selected through the switches.
Disassembled code:
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>
Running effect (speeding up the clock to improve response speed)
It can be found that most of the contents of SRAM are 0, and the value 0x93 is read at the address 0x18c, and the data at several other locations are consistent with the corresponding addresses of the program code. Therefore, it shows that SRAM and the ROM storing the program are the same storage, which also indirectly verifies the purpose of using dual-port BRAM in the soft core: the program and data use the same RAM space.
Knowing the RAM address and program address, you can write more complex programs.
This MCU soft core also has GPIO and UART, as well as external interrupt input functions. However, due to the lack of information, it is difficult to find out how to use them.
|