| // SPDX-FileCopyrightText: |
| // 2022 Nguyen Dao |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the 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. |
| // |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| module BlockRAM_1KB (clk, rd_addr, rd_data, wr_addr, wr_data, C0, C1, C2, C3, C4, C5); |
| |
| parameter READ_ADDRESS_MSB_FROM_DATALSB = 24; //default 24 means bits wr_data[25:24] will become bits [9:8] of read address |
| parameter WRITE_ADDRESS_MSB_FROM_DATALSB = 16; //default 16 means bits wr_data[17:16] will become bits [9:8] of write address |
| parameter WRITE_ENABLE_FROM_DATA = 20; //default 20 means bit wr_data[20] will become the dynamic writeEnable input |
| input clk; |
| input [7:0] rd_addr; |
| output [31:0] rd_data; |
| |
| input [7:0] wr_addr; |
| input [31:0] wr_data; |
| |
| input C0;//naming of these doesnt really matter |
| input C1;// C0,C1 select write port width |
| input C2;// C2,C3 select read port width |
| input C3; |
| input C4;//C4 selects the alwaysWriteEnable |
| input C5; //C5 selects register bypass |
| // NOTE, the read enable is currently constantly ON |
| // NOTE, the R/W port on the standard cell is used only in write mode |
| // NOTE, enable ports on the primitive RAM are active lows |
| wire [1:0] rd_port_configuration; |
| wire [1:0] wr_port_configuration; |
| wire optional_register_enabled_configuration; |
| wire alwaysWriteEnable; |
| assign wr_port_configuration = {C0, C1}; |
| assign rd_port_configuration = {C2, C3}; |
| assign alwaysWriteEnable = C4; |
| assign optional_register_enabled_configuration = C5; |
| |
| reg memWriteEnable; |
| always @ (*) begin // write enable |
| if(alwaysWriteEnable) begin |
| memWriteEnable = 0; // This RAM primitive is active low. |
| end else begin |
| memWriteEnable = (!(wr_data[WRITE_ENABLE_FROM_DATA])); // inverting the bit to make it active-high |
| end |
| end |
| reg [3:0] mem_wr_mask; |
| reg [31:0] muxedDataIn; |
| |
| wire [1:0] wr_addr_topbits; |
| assign wr_addr_topbits = wr_data[WRITE_ADDRESS_MSB_FROM_DATALSB+1:WRITE_ADDRESS_MSB_FROM_DATALSB]; |
| always @ (*) begin //write port config -> mask + write data multiplex |
| muxedDataIn = 32'dx; |
| if(wr_port_configuration == 0) begin |
| mem_wr_mask = 4'b1111; |
| muxedDataIn = wr_data; |
| end else if(wr_port_configuration == 1) begin |
| if(wr_addr_topbits == 0) begin |
| mem_wr_mask = 4'b0011; |
| muxedDataIn[15:0] = wr_data[15:0]; |
| end else begin |
| mem_wr_mask = 4'b1100; |
| muxedDataIn[31:16] = wr_data[15:0]; |
| end |
| end else if(wr_port_configuration == 2) begin |
| if(wr_addr_topbits == 0) begin |
| mem_wr_mask = 4'b0001; |
| muxedDataIn[7:0] = wr_data[7:0]; |
| end else if(wr_addr_topbits == 1) begin |
| mem_wr_mask = 4'b0010; |
| muxedDataIn[15:8] = wr_data[7:0]; |
| end else if(wr_addr_topbits == 2) begin |
| mem_wr_mask = 4'b0100; |
| muxedDataIn[23:16] = wr_data[7:0]; |
| end else begin |
| mem_wr_mask = 4'b1000; |
| muxedDataIn[31:24] = wr_data[7:0]; |
| end |
| end |
| end |
| wire [31:0] mem_dout; |
| sram_1rw1r_32_256_8_sky130 memory_cell( //dout0 is unused |
| .clk0(clk),.csb0(memWriteEnable),.web0(memWriteEnable),.wmask0(mem_wr_mask),.addr0(wr_addr[7:0]),.din0(muxedDataIn),//.dout0(), |
| .clk1(clk),.csb1(1'b0),.addr1(rd_addr[7:0]),.dout1(mem_dout) |
| ); |
| reg [1:0] rd_dout_sel; |
| always @ (posedge clk) begin |
| rd_dout_sel <= wr_data[READ_ADDRESS_MSB_FROM_DATALSB+1:READ_ADDRESS_MSB_FROM_DATALSB]; |
| end |
| reg [31:0] rd_dout_muxed; |
| always @ (*) begin |
| rd_dout_muxed[31:0] = mem_dout[31:0]; // a default value. Could be 32'dx if tools support it for logic saving! |
| if(rd_port_configuration == 0) begin |
| rd_dout_muxed[31:0] = mem_dout[31:0]; |
| end else if(rd_port_configuration == 1) begin |
| if(rd_dout_sel[0] == 0) begin |
| rd_dout_muxed[15:0] = mem_dout[15:0]; |
| end else begin |
| rd_dout_muxed[15:0] = mem_dout[31:16]; |
| end |
| end else if(rd_port_configuration == 2) begin |
| if(rd_dout_sel == 0) begin |
| rd_dout_muxed[7:0] = mem_dout[7:0]; |
| end else if(rd_dout_sel == 1) begin |
| rd_dout_muxed[7:0] = mem_dout[15:8]; |
| end else if(rd_dout_sel == 2) begin |
| rd_dout_muxed[7:0] = mem_dout[23:16]; |
| end else begin |
| rd_dout_muxed[7:0] = mem_dout[31:24]; |
| end |
| end |
| end |
| reg [31:0] rd_dout_additional_register; |
| always @ (posedge clk) begin |
| rd_dout_additional_register <= rd_dout_muxed; |
| end |
| reg [31:0] final_dout; |
| assign rd_data = final_dout; |
| always @ (*) begin |
| if(optional_register_enabled_configuration) begin |
| final_dout = rd_dout_additional_register; |
| end else begin |
| final_dout = rd_dout_muxed; |
| end |
| end |
| endmodule |
| |
| |
| (* blackbox *) |
| module sram_1rw1r_32_256_8_sky130( |
| //`ifdef USE_POWER_PINS |
| // vdd, |
| // gnd, |
| //`endif |
| // Port 0: RW |
| clk0,csb0,web0,wmask0,addr0,din0,dout0, |
| // Port 1: R |
| clk1,csb1,addr1,dout1 |
| ); |
| |
| parameter NUM_WMASKS = 4 ; |
| parameter DATA_WIDTH = 32 ; |
| parameter ADDR_WIDTH = 8 ; |
| parameter RAM_DEPTH = 1 << ADDR_WIDTH; |
| // FIXME: This delay is arbitrary. |
| parameter DELAY = 3 ; |
| //`ifdef USE_POWER_PINS |
| // inout vdd; |
| // inout gnd; |
| //`endif |
| input clk0; // clock |
| input csb0; // active low chip select |
| input web0; // active low write control |
| input [NUM_WMASKS-1:0] wmask0; // write mask |
| input [ADDR_WIDTH-1:0] addr0; |
| input [DATA_WIDTH-1:0] din0; |
| output [DATA_WIDTH-1:0] dout0; |
| input clk1; // clock |
| input csb1; // active low chip select |
| input [ADDR_WIDTH-1:0] addr1; |
| output [DATA_WIDTH-1:0] dout1; |
| endmodule |