lowRISC / ibex

Ibex is a small 32 bit RISC-V CPU core, previously known as zero-riscy.
https://www.lowrisc.org
Apache License 2.0
1.37k stars 543 forks source link

Integration of 6T SRAM with the Ibex core #2194

Open kolappanc opened 3 months ago

kolappanc commented 3 months ago

We are trying to use our 6T SRAM design instead of the BRAM for simulation. I understand that we would still need the BRAM to copy the image(vmem or elf) into the memory. My idea is to copy the image into the BRAM and then write it into our SRAM design. I am considering instantiating our SRAM design instead of ram_1p/ram_2p in the RTL design. We are trying to use 1MB SRAM. I need some advice and help to understand the files I need to modify apart from the core files used by fusesoc, on how to load the image into the BRAM and then write it into our SRAM before the start of simulation. Please correct me if I am wrong here.

My Environment

EDA tool and version: I am using Verilator for simulation and Spike as ISS

Operating system: The operating system is CentOS 7

Version of the Ibex source code:

rswarbrick commented 3 months ago

This is slightly confusing to me. Your tool/environment description sounds very much like you are at the frontend stage (using Verilator and Spike, and no mention of FPGA tools). In that case, I'd strongly recommend leaving things as they are in the repository. If you are just doing Verilator simulations, the memory implementation won't matter anyway.

Are you trying to use ibex_simple_system.sv and have a physical target (such as an FPGA) and have a 6T SRAM that you wish to use? If so, the easiest first step is probably to use your favoured RAM instead of prim_ram_2p. If you want it to work easily in simulation, you'll have to port the DPI function signatures that are in prim_util_memload.svh. But that's probably unnecessary work: there's hopefully no need to run a Verilator simulation of a processor core to try out a particular RAM implementation...

kolappanc commented 3 months ago

We are trying to run the simulation and not using any FPGA targets. We are trying to test the working of our 6T SRAM behavioural model with the Ibex core. We do not want to test it with an FPGA and just do a Verilator simulation on it. We want to load the elf programs and map the stack memory into this 6T SRAM. I see the use readmemh() functions for reading the MemInitFile, which is stored in an array. The problem here is I am stuck on how to write the values from the BRAM/array to our 6T SRAM after the simulation begins, to see the working of our SRAM.

tju-sun-lab commented 3 months ago

I encountered the same issue as you. I am currently aiming to tape out a chip using SMIC's 180nm process. I am keeping the ibex core unchanged, but I need to embed SRAM on the chip. I use SMIC's memory compiler to generate the RAM's Verilog and liberty files, but I need to verify whether this SRAM can connect well with the ibex core and function properly. This is because the read/write timing of the SRAM generated by the memory compiler is different from that of prim_ram_2p. The prim_ram_2p can read out data in the current cycle, while the SRAM generated by the memory compiler requires a delay of one cycle to read out data. Therefore, I need to verify whether this SRAM works well; otherwise, I will need to design additional timing control logic.

I want to use the Ibex Simple System for verification. My steps are as follows:

  1. Use the memory compiler to generate SRAM with width=32 and depth=1024*1024/4. By the way, are these parameter selections correct?
  2. Replace prim_ram_2p with SRAM.v and connect the input and output port signals properly.
  3. Compile the Ibex Simple System software to generate the vmem file.
  4. Use ./build/lowrisc_ibex_ibex_simple_system_0/sim-vcs/lowrisc_ibex_ibex_simple_system_0 for simulation verification.

However, the simulation reports errors. It seems that the vmem is not read into the SRAM, and the instructions in the trace_core_00000000.log file are empty, showing INVALID. Is it possible that the SRAM cannot use the $readmemh command to load data? How should I proceed to verify it?

rswarbrick commented 3 months ago

Hi there,

Thank you both for the messages. For this specific problem, I think we're trying to ensure we can actually load up a block of data into memory. I think you've already found the implementation of simutil_memload in prim_util_memload.svh. This is used by some C++ code when it wants to "load a binary blob from a file into an enormous array".

You might have worked this out already, but the way we do it in our RAM primitives is pretty simple. The prim_util_memload task is in prim_util_memload.svh. This gets included into the RAM primitive (e.g. prim_generic_ram_1p.sv) and the code there picks up the mem array that we want to be the target.

For your more specific memory, you're going to have to do something slightly cleverer, but the basic idea should work the same. You can define a DPI task called prim_util_memload. Implement that somewhere that can see your memory model. It will be called with a filename and will have to fill in your memory accordingly.

The simplest approach for the prim_util_memload implementation is probably to make a local array, fill that up with $readmemh (like the existing implementation) and then copy the loaded data into your memory model with a loop.

You'll also have to implement simutil_set_mem and simutil_get_mem analogously (but that will probably be simpler).

tju-sun-lab commented 3 months ago

Hi there,

Thank you both for the messages. For this specific problem, I think we're trying to ensure we can actually load up a block of data into memory. I think you've already found the implementation of simutil_memload in prim_util_memload.svh. This is used by some C++ code when it wants to "load a binary blob from a file into an enormous array".

You might have worked this out already, but the way we do it in our RAM primitives is pretty simple. The prim_util_memload task is in prim_util_memload.svh. This gets included into the RAM primitive (e.g. prim_generic_ram_1p.sv) and the code there picks up the mem array that we want to be the target.

For your more specific memory, you're going to have to do something slightly cleverer, but the basic idea should work the same. You can define a DPI task called prim_util_memload. Implement that somewhere that can see your memory model. It will be called with a filename and will have to fill in your memory accordingly.

The simplest approach for the prim_util_memload implementation is probably to make a local array, fill that up with $readmemh (like the existing implementation) and then copy the loaded data into your memory model with a loop.

You'll also have to implement simutil_set_mem and simutil_get_mem analogously (but that will probably be simpler).

Thank you very much for your response. I fully understand your point.

I have another question: if I need to tape out an SoC with an embedded Ibex core, how should I initialize the memory? Should I refer to the implementation method in the Ibex demo system and also embed an on-chip debug module (dm_top.v)? Or is there a better method?