Menu Close

Loading of block memory files in vivado simulation projects

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.

ITCM directory

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.

Posted in FPGA, RISC-V, RISC-V Textbook, Textbook and Training Project

Related Articles

Leave a Reply

Your email address will not be published.

Leave the field below empty!