// Copyright (c) 2020 ETH Zurich and University of Bologna. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law // or agreed to in writing, software, hardware and materials distributed under // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // Author: Wolfgang Roenninger <wroennin@ethz.ch> // Description: Functional module of a generic SRAM // // Parameters: // - NumWords: Number of words in the macro. Address width can be calculated with: // `AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1` // The module issues a warning if there is a request on an address which is // not in range. // - DataWidth: Width of the ports `wdata_i` and `rdata_o`. // - ByteWidth: Width of a byte, the byte enable signal `be_i` can be calculated with the // ceiling division `ceil(DataWidth, ByteWidth)`. // - NumPorts: Number of read and write ports. Each is a full port. Ports with a higher // index read and write after the ones with lower indices. // - Latency: Read latency, the read data is available this many cycles after a request. // - SimInit: Macro simulation initialization. Values are: // "zeros": Each bit gets initialized with 1'b0. // "ones": Each bit gets initialized with 1'b1. // "random": Each bit gets random initialized with 1'b0 or 1'b1. // "none": Each bit gets initialized with 1'bx. (default) // - PrintSimCfg: Prints at the beginning of the simulation a `Hello` message with // the instantiated parameters and signal widths. // // Ports: // - `clk_i`: Clock // - `rst_ni`: Asynchronous reset, active low // - `req_i`: Request, active high // - `we_i`: Write request, active high // - `addr_i`: Request address // - `wdata_i`: Write data, has to be valid on request // - `be_i`: Byte enable, active high // - `rdata_o`: Read data, valid `Latency` cycles after a request with `we_i` low. // // Behaviour: // - Address collision: When Ports are making a write access onto the same address, // the write operation will start at the port with the lowest address // index, each port will overwrite the changes made by the previous ports // according how the respective `be_i` signal is set. // - Read data on write: This implementation will not produce a read data output on the signal // `rdata_o` when `req_i` and `we_i` are asserted. The output data is stable // on write requests. module tc_sram #( parameter int unsigned NumWords = 32'd1024, // Number of Words in data array parameter int unsigned DataWidth = 32'd128, // Data signal width parameter int unsigned ByteWidth = 32'd8, // Width of a data byte parameter int unsigned NumPorts = 32'd2, // Number of read and write ports parameter int unsigned Latency = 32'd1, // Latency when the read data is available parameter SimInit = "none", // Simulation initialization parameter bit PrintSimCfg = 1'b0, // Print configuration // DEPENDENT PARAMETERS, DO NOT OVERWRITE! parameter int unsigned AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1, parameter int unsigned BeWidth = (DataWidth + ByteWidth - 32'd1) / ByteWidth, // ceil_div parameter type addr_t = logic [AddrWidth-1:0], parameter type data_t = logic [DataWidth-1:0], parameter type be_t = logic [BeWidth-1:0] ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low // input ports input logic [NumPorts-1:0] req_i, // request input logic [NumPorts-1:0] we_i, // write enable input addr_t [NumPorts-1:0] addr_i, // request address input data_t [NumPorts-1:0] wdata_i, // write data input be_t [NumPorts-1:0] be_i, // write byte enable // output ports output data_t [NumPorts-1:0] rdata_o // read data ); // memory array //data_t sram [NumWords-1:0]; // hold the read address when no read access is made //addr_t [NumPorts-1:0] r_addr_q; generate if (DataWidth == 32 && NumWords == 256) begin sram_sp_advplus sram_instance(.Q(rdata_o), .CLK(clk_i), .CEN(~req_i), .WEN(we_i), .A(addr_i), .D(wdata_i), .EMA(3'b111), .RETN(1'b0)); end else if (DataWidth == 256 && NumWords == 64)begin //wire [0:127] data_o_0_127_inst1 ; //assign data_o_0_127_inst1 = rdata_o[0][127:0]; //assign data_i_0_127_inst1 = wdata_i[127:0]; //assign data_o_128_255_inst1 = rdata_o[255:128]; //assign data_i_128_255_inst1 = wdata_i[255:128]; rf_sp_adv fr_sp_instance1(.Q(rdata_o[0][127:0]), .CLK(clk_i), .CEN(~req_i), .WEN(we_i), .A(addr_i), .D(wdata_i[0][127:0]), .EMA(3'b000), .RETN(1'b0)); rf_sp_adv fr_sp_instance2(.Q(rdata_o[0][255:128]), .CLK(clk_i), .CEN(~req_i), .WEN(we_i), .A(addr_i), .D(wdata_i[0][255:128]), .EMA(3'b000), .RETN(1'b0)); end else begin // memory array data_t sram [NumWords-1:0]; // hold the read address when no read access is made addr_t [NumPorts-1:0] r_addr_q; // SRAM simulation initialization data_t [NumWords-1:0] init_val; //initial begin : proc_sram_init // for (int unsigned i = 0; i < NumWords; i++) begin // for (int unsigned j = 0; j < DataWidth; j++) begin // case (SimInit) // "zeros": init_val[i][j] = 1'b0; // "ones": init_val[i][j] = 1'b1; // "random": init_val[i][j] = $urandom(); // default: init_val[i][j] = 1'bx; // endcase // end // end //end // set the read output if requested // The read data at the highest array index is set combinational. // It gets then delayed for a number of cycles until it gets available at the output at // array index 0. // read data output assignment data_t [NumPorts-1:0][Latency-1:0] rdata_q, rdata_d; if (Latency == 32'd0) begin : gen_no_read_lat for (genvar i = 0; i < NumPorts; i++) begin : gen_port assign rdata_o[i] = (req_i[i] && !we_i[i]) ? sram[addr_i[i]] : sram[r_addr_q[i]]; end end else begin : gen_read_lat always_comb begin for (int unsigned i = 0; i < NumPorts; i++) begin rdata_o[i] = rdata_q[i][0]; for (int unsigned j = 0; j < (Latency-1); j++) begin rdata_d[i][j] = rdata_q[i][j+1]; end rdata_d[i][Latency-1] = (req_i[i] && !we_i[i]) ? sram[addr_i[i]] : sram[r_addr_q[i]]; end end end // write memory array always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin for (int unsigned i = 0; i < NumWords; i++) begin sram[i] <= init_val[i]; end for (int i = 0; i < NumPorts; i++) begin r_addr_q[i] <= {AddrWidth{1'b0}}; // initialize the read output register for each port if (Latency != 32'd0) begin for (int unsigned j = 0; j < Latency; j++) begin rdata_q[i][j] <= init_val[{AddrWidth{1'b0}}]; end end end end else begin // read value latch happens before new data is written to the sram for (int unsigned i = 0; i < NumPorts; i++) begin if (Latency != 0) begin for (int unsigned j = 0; j < Latency; j++) begin rdata_q[i][j] <= rdata_d[i][j]; end end end // there is a request for the SRAM, latch the required register for (int unsigned i = 0; i < NumPorts; i++) begin if (req_i[i]) begin if (we_i[i]) begin // update value when write is set at clock for (int unsigned j = 0; j < DataWidth; j++) begin if (be_i[i][j/ByteWidth]) begin sram[addr_i[i]][j] <= wdata_i[i][j]; end end end else begin // otherwise update read address for subsequent non request cycles r_addr_q[i] <= addr_i[i]; end end // if req_i end // for ports end // if !rst_ni end // Validate parameters. // pragma translate_off `ifndef VERILATOR `ifndef TARGET_SYNTHESYS initial begin: p_assertions assert ($bits(addr_i) == NumPorts * AddrWidth) else $fatal(1, "AddrWidth problem on `addr_i`"); assert ($bits(wdata_i) == NumPorts * DataWidth) else $fatal(1, "DataWidth problem on `wdata_i`"); assert ($bits(be_i) == NumPorts * BeWidth) else $fatal(1, "BeWidth problem on `be_i`" ); assert ($bits(rdata_o) == NumPorts * DataWidth) else $fatal(1, "DataWidth problem on `rdata_o`"); assert (NumWords >= 32'd1) else $fatal(1, "NumWords has to be > 0"); assert (DataWidth >= 32'd1) else $fatal(1, "DataWidth has to be > 0"); assert (ByteWidth >= 32'd1) else $fatal(1, "ByteWidth has to be > 0"); assert (NumPorts >= 32'd1) else $fatal(1, "The number of ports must be at least 1!"); end initial begin: p_sim_hello if (PrintSimCfg) begin $display("#################################################################################"); $display("tc_sram functional instantiated with the configuration:" ); $display("Instance: %m" ); $display("Number of ports (dec): %0d", NumPorts ); $display("Number of words (dec): %0d", NumWords ); $display("Address width (dec): %0d", AddrWidth ); $display("Data width (dec): %0d", DataWidth ); $display("Byte width (dec): %0d", ByteWidth ); $display("Byte enable width (dec): %0d", BeWidth ); $display("Latency Cycles (dec): %0d", Latency ); $display("Simulation init (str): %0s", SimInit ); $display("#################################################################################"); end end for (genvar i = 0; i < NumPorts; i++) begin : gen_assertions assert property ( @(posedge clk_i) disable iff (!rst_ni) (req_i[i] |-> (addr_i[i] < NumWords))) else $warning("Request address %0h not mapped, port %0d, expect random write or read behavior!", addr_i[i], i); end `endif `endif // pragma translate_on end endgenerate endmodule