When using Xilinx Vivado to do simulation projects, sometimes it is necessary to change the initial data of block memory several times quickly. This paper proposes a filling method. Avoid adding additional Verilog logic to dynamically fill the contents of block memory.
In the design and development of RISC-V CPU, simulation is an important means to verify whether the code is correct. In RISC-V FPGA simulation, we will use dual ports as ITCM to store the machine code compiled by the software. How to quickly update the content in the dual ports in order to speed up the simulation process is also what we have been concerned about. The code loading and program simulation will be introduced one by one in a decomposed manner.
1. Load the machine code into ITCM
Since the block memory IP is often used to implement ITCM in the development of RISC-V CPU, a dual port is used as an example to introduce the method of loading the machine code into the dual-port RAM, so that there is no need to modify the port a or port b of the dual-port RAM. Logic, just modify the content in the block memory to achieve the simulation we need. The following content takes the FII_RISCV_V2.01.002 CPU project as an example to introduce step by step.
-
RISC-V project directory:
As can be seen in Figure 1, the exact location of the ITCM in the RISC-V CPU.
figure 1
Our instantiated ITCM (dual port) is in fii_cpu_sys/fii_riscv_cpu_inst/fii_rv32i_core_inst/program_inst, and the directory is relatively deep.
-
Add the following code to the simulation file:
localparam FILE_NAME = "../../../asm_run_led.sim"; integer file_handle = 0; initial begin file_handle = $fopen(FILE_NAME,"r"); if(!file_handle) begin $display("Could not open File \r"); $stop; end $readmemh (FILE_NAME, fii_cpu_sys_inst.fii_riscv_cpu_inst.fii_rv32i_core_inst.program_inst.inst.native_mem_module.blk_mem_gen_v8_4_1_inst.memory); $fclose(file_handle); end
In the simulation file:
In the above code, the loading of $readmemh for block memory is very complicated, such as
$readmemh (FILE_NAME, fii_cpu_sys_inst.fii_riscv_cpu_inst.fii_rv32i_core_inst.program_inst .inst.native_mem_module.blk_mem_gen_v8_4_1_inst.memory);
Read the asm_run_led.sim file and write the contents of the file to the dual port. The red part is the instantiated name of the project module, and the blue part points to the block memory.
With such a statement, it is difficult for ordinary users to accurately describe the correct location of the storage unit of block memory. In response to this problem, there is an article on the IC knowledge base website that specifically introduces this aspect. See the use of $readmemh in simulation projects .
localparam FILE_NAME = “../../../asm_run_led.sim”;
asm_run_led.sim is only the valid data part in the *.coe file, and the data is placed in the .sim directory. Refer to Figure 2 for the specific location. ,image 3.
figure 2
image 3
2. The software program is simulated on the RISC-V CPU
Test project: Marquee experiment written in assembly language under FII-PRX100-D. (software control LED light)
Software assembly code:
.globl _start _start: LI x8, 0xf0000000; # x8 = 0xf000_0000 gpio base address ADDI x6, x0, 0; # x6 = 0 LI x7, 0x00400000; # x7 = 0x0040_0000 delay counter # LI x7, 0x4; # x7 = 0x0040_0000 delay counter START: ADDI x10, x0, 0x80; # x10 = 0x80 NOT x18, x10; # x18 = ~x10 SH x18, 0(x8); # addr[0xf000_0000] = val[0x18] SH x0, 4(x8); # addr[0xf000_0004] = 0 as output LOOP: ADDI x6, x6, 1; # x6 = x6 + 1 BNE x6, x7, LOOP; # if(x6 != x7) goto loop ADDI x6, x0, 0; # x6 = 0 SRLI x10, x10,1; # x10 >> 1 BEQ x10, x0, START; # if(x10 == 0x100) goto start NOT x18, x10; # x18 = ~x10 SH x18, 0(x8); # addr[0xf000_0000] = x18 JAL LOOP; # pc = 0x3c
The above RISC-V assembly language can be used to generate corresponding *.coe, *.sim files, etc. using the asm_compile tool.
asm_run_led.coe :
memory_initialization_radix = 16; memory_initialization_vector = f0000437 00000313 004003b7 08000513 fff54913 01241023 00041223 00130313 fe731ee3 00000313 00155513 fe0500e3 fff54913 01241023 fe5ff0ef 00000000 00000000 ;
asm_run_led.sim :
f0000437 00000313 004003b7 08000513 fff54913 01241023 00041223 00130313 fe731ee3 00000313 00155513 fe0500e3 fff54913 01241023 fe5ff0ef 00000000 00000000
Simulation waveform:
Figure 4
As can be seen from Figure 4, the data (addresses, instructions) of the real simulation are consistent with the content loaded from the *.sim file.
In this way, the simulation file can write the content of the data file into the dual-port, so there is no need to change the logic of the Verilog module, nor to re-synthesize the dual-port IP core (modify the *.coe file). The above design simulation method is non-intrusive simulation. When learning RISC-V CPU, this is a very useful method to quickly import the assembly code written by the user into the FPGA project so that the CPU can simulate.
Through this method of loading machine code, the subsequent steps of RISC-V CPU decoding, execution, and memory access can be easily and quickly debugged and verified by using simulation tools.