The LOAD and STORE instructions in the RISC-V CPU control the SRAM and GPIO modules, SRAM, GPIO, etc., can be regarded as the external storage of the cpu. In the Harvard architecture, use the data bus to access these external modules
Related reference articles:
RISC-V teaching plan
In the RISC-V v2.01 version, SRAM, GPIO and other modules are in the lsu module. The lsu module is used as the interface to connect with the cpu core. Corresponding modifications will be made in subsequent versions, including adding a bus. Includes bus expansion. Also remove other peripherals from the current module so that more peripherals can be added. At present, the location of sram in the address space is 0x9000_0000, which is actually 256M space. SRAM and DTCM are identical. In the current version, the DTCM is composed of the SRAM module.
SRAM code:
module D_sram #( parameter MEM_D_DEEP = 1024, //memory data depth parameter MEM_D_W = 32, //memory data width parameter MEM_MSK_W = 4, //memory data mask width parameter MEM_ADDR_W = 32 //memory address width ) ( input clk, // system clock input [ MEM_D_W - 1 : 0 ] din, // data write input [ MEM_ADDR_W - 1 : 0 ] addr, // data bus address output [ MEM_ADDR_W - 1 : 0 ] o_D_PC, // not using input cs, // module chip select input we, // write signal input [ MEM_MSK_W - 1: 0 ] wem, // write mask bit // output reg mem_init_rdy, output [ MEM_D_W - 1: 0 ] dout, // data read input rst_n ); //================================================================================= reg [ MEM_D_W - 1: 0 ] mem_r[ 0: MEM_D_DEEP - 1 ]; //================================================================================= wire ren = cs & ( ~we ); wire [ MEM_MSK_W - 1: 0 ] wen = ( { MEM_MSK_W{ cs & we } } & wem ); reg [ MEM_ADDR_W - 1: 0 ] addr_r = 0; always @( posedge clk ) addr_r <= addr; wire [ MEM_ADDR_W - 1: 0 ] addr_mem = {addr[ 31:2]}; // integer mem_init_addr; //================================================================================= genvar wi; generate for ( wi = 0; wi < MEM_MSK_W; wi = wi + 1 ) begin: mem_write always @( posedge clk ) begin if ( wen[ wi ] ) begin mem_r[ addr_mem ][ 8 * wi + 7: 8 * wi ] <= din[ 8 * wi + 7: 8 * wi ]; end end end endgenerate //================================================================================== wire [ MEM_D_W - 1: 0 ] dout_pre; wire [ MEM_D_W - 1: 0 ] dout_w; //assign dout_pre = mem_r[ addr_r ]; assign dout_pre = mem_r[ addr_mem ]; genvar ri; generate for ( ri = 0; ri < MEM_D_W; ri = ri + 1 ) begin: mem_read `ifdef SIM//{ assign dout_w[ ri ] = ( dout_pre[ ri ] === 1'bx ) ? 1'b0 : dout_pre[ ri ]; `else //}{ assign dout_w[ ri ] = dout_pre[ ri ]; `endif//} end endgenerate //==================================================================================== //wire [4:0] data_sft = addr[1:0] * 8; wire [4:0] data_sft = {addr[1:0], 3'b000}; assign dout = dout_w > > data_sft; assign o_D_PC = addr_r; //==================================================================================== endmodule
Port introduction:
input clk , // system clock
input [ MEM_D_W – 1 : 0 ] din , // data write
input [ MEM_ADDR_W – 1 : 0 ] addr , // data bus address
input cs , // module chip selection
input we , // Write signal
input [ MEM_MSK_W – 1: 0 ] wem , // write mask bit
output [ MEM_D_W – 1: 0 ] dout , // data read
Storage register:
reg[MEM_D_W-1:0] mem_r [0:MEM_D_DEEP-1];
Read signal, write signal:
wire ren = cs & ( ~we );
wire [ MEM_MSK_W – 1: 0 ] wen = ( { MEM_MSK_W{ cs & we } } & wem );
SRAM address: Since the current device is a 32-bit device, the input address only needs to be [31:2], as the memory access unit.
wire [ MEM_ADDR_W – 1: 0 ] addr_mem = {addr[31:2]};
Write data to the storage unit:
genvar wi; generate for ( wi = 0; wi < MEM_MSK_W; wi = wi + 1 ) begin: mem_write always @( posedge clk ) begin if ( wen[ wi ] ) begin mem_r[ addr_mem ][ 8 * wi + 7: 8 * wi ] <= din[ 8 * wi + 7: 8 * wi ]; end end end endgenerate
Read data unit:
wire [ MEM_D_W - 1: 0 ] dout_pre; wire [ MEM_D_W - 1: 0 ] dout_w; //assign dout_pre = mem_r[ addr_r ]; assign dout_pre = mem_r[ addr_mem ]; genvar ri; generate for ( ri = 0; ri < MEM_D_W; ri = ri + 1 ) begin: mem_read `ifdef SIM//{ assign dout_w[ ri ] = ( dout_pre[ ri ] === 1'bx ) ? 1'b0 : dout_pre[ ri ]; `else //} { assign dout_w[ri] = dout_pre[ri]; `endif//} end endgenerate
Load instruction related operations: LB, LH, LW
wire [4:0] data_sft = {addr[1:0], 3’b000};
assign dout = dout_w >> data_sft;
The entire SRAM module is currently composed of d flip-flops, which can also be replaced by block memory (IP) later, but it should be noted that block memory usually needs to be delayed by one clock cycle, so in the entire CPU design, it must be noted that it needs to be in The state machine, or adding a clock to the pipeline, acts as memory access. Of course, there are other methods that can be used.