|
| 1 | +// Copyright 2024 ETH Zurich and University of Bologna. |
| 2 | +// Solderpad Hardware License, Version 0.51, see LICENSE for details. |
| 3 | +// SPDX-License-Identifier: SHL-0.51 |
| 4 | +// |
| 5 | +// Lorenzo Leone <[email protected]> |
| 6 | + |
| 7 | +// ## Description: |
| 8 | +// A wrapper for `tc_sram_impl` that instantiates logic banks with retention mode |
| 9 | +// or power-off capability. |
| 10 | +// This module can be used for power-aware simulations, with control signals driven |
| 11 | +// directly by UPF signals. |
| 12 | +// |
| 13 | +// ## Goal: |
| 14 | +// In a memory with multiple banks that support power gating and retention, |
| 15 | +// each bank’s addressing must ensure that interleaving remains intact. During retention |
| 16 | +// or power-off states, only contiguous addresses should be switched. |
| 17 | +// The memory should always appear as a set of contiguous addresses, with no gaps in the |
| 18 | +// address mapping. |
| 19 | +// This module is responsible for managing the correct memory addressing |
| 20 | +// |
| 21 | +module mem_multibank_pwrgate #( |
| 22 | + parameter int unsigned NumWords = 32'd1024, // Number of Words in data array |
| 23 | + parameter int unsigned DataWidth = 32'd128, // Data signal width |
| 24 | + parameter int unsigned ByteWidth = 32'd8, // Width of a data byte |
| 25 | + parameter int unsigned NumPorts = 32'd2, // Number of read and write ports |
| 26 | + parameter int unsigned Latency = 32'd1, // Latency when the read data is available |
| 27 | + parameter int unsigned NumLogicBanks = 32'd1, // Logic bank for Power Management |
| 28 | + parameter SimInit = "none", // Simulation initialization |
| 29 | + parameter bit PrintSimCfg = 1'b0, // Print configuration |
| 30 | + parameter ImplKey = "none", // Reference to specific implementation |
| 31 | + // DEPENDENT PARAMETERS, DO NOT OVERWRITE! |
| 32 | + parameter int unsigned AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1, |
| 33 | + parameter int unsigned BeWidth = (DataWidth + ByteWidth - 32'd1) / ByteWidth, // ceil_div |
| 34 | + parameter type addr_t = logic [AddrWidth-1:0], |
| 35 | + parameter type data_t = logic [DataWidth-1:0], |
| 36 | + parameter type be_t = logic [BeWidth-1:0] |
| 37 | +) ( |
| 38 | + input logic clk_i, // Clock |
| 39 | + input logic rst_ni, // Asynchronous reset active low |
| 40 | + // input ports |
| 41 | + input logic [ NumPorts-1:0] req_i, // request |
| 42 | + input logic [ NumPorts-1:0] we_i, // write enable |
| 43 | + input addr_t [ NumPorts-1:0] addr_i, // request address |
| 44 | + input data_t [ NumPorts-1:0] wdata_i, // write data |
| 45 | + input be_t [ NumPorts-1:0] be_i, // write byte enable |
| 46 | + input logic [NumLogicBanks-1:0] deepsleep_i, // deep sleep enable |
| 47 | + input logic [NumLogicBanks-1:0] powergate_i, // power gate enable |
| 48 | + // output ports |
| 49 | + output data_t [ NumPorts-1:0] rdata_o // read data |
| 50 | +); |
| 51 | + |
| 52 | + // Implementation type for Power Gating and Deppesleep ports |
| 53 | + typedef struct packed { |
| 54 | + logic deepsleep; |
| 55 | + logic powergate; |
| 56 | + } impl_in_t; |
| 57 | + |
| 58 | + |
| 59 | + if (NumLogicBanks == 32'd0) begin : gen_no_logic_bank |
| 60 | + $fatal("Error: %d logic banks are not supported", NumLogicBanks); |
| 61 | + end else if (NumLogicBanks == 32'd1) begin : gen_simple_sram |
| 62 | + tc_sram_impl #( |
| 63 | + .NumWords (NumWords), |
| 64 | + .DataWidth (DataWidth), |
| 65 | + .ByteWidth (ByteWidth), |
| 66 | + .NumPorts (NumPorts), |
| 67 | + .Latency (Latency), |
| 68 | + .SimInit (SimInit), |
| 69 | + .PrintSimCfg(PrintSimCfg), |
| 70 | + .ImplKey (ImplKey), |
| 71 | + .impl_in_t (impl_in_t), |
| 72 | + .impl_out_t (impl_in_t) |
| 73 | + ) i_tc_sram_impl ( |
| 74 | + .clk_i, |
| 75 | + .rst_ni, |
| 76 | + .impl_i({deepsleep_i, powergate_i}), |
| 77 | + .impl_o(), |
| 78 | + .req_i, |
| 79 | + .we_i, |
| 80 | + .addr_i, |
| 81 | + .wdata_i, |
| 82 | + .be_i, |
| 83 | + .rdata_o |
| 84 | + ); |
| 85 | + |
| 86 | + end else begin : gen_logic_bank // block: gen_simple_sram |
| 87 | + localparam int unsigned LogicBankSize = NumWords / NumLogicBanks; |
| 88 | + localparam int unsigned BankSelWidth = (NumLogicBanks > 32'd1) ? $clog2( |
| 89 | + NumLogicBanks |
| 90 | + ) : 32'd1; |
| 91 | + |
| 92 | + if (LogicBankSize != 2 ** (AddrWidth - BankSelWidth)) |
| 93 | + $fatal("Logic Bank size is not a power of two: UNSUPPORTED "); |
| 94 | + |
| 95 | + // Signals from/to logic banks |
| 96 | + logic [NumLogicBanks-1:0][ NumPorts-1:0] req_cut; |
| 97 | + logic [NumLogicBanks-1:0][ NumPorts-1:0] we_cut; |
| 98 | + logic [NumLogicBanks-1:0][ NumPorts-1:0][AddrWidth-BankSelWidth-1:0] addr_cut; |
| 99 | + data_t [NumLogicBanks-1:0][ NumPorts-1:0] wdata_cut; |
| 100 | + be_t [NumLogicBanks-1:0][ NumPorts-1:0] be_cut; |
| 101 | + data_t [NumLogicBanks-1:0][ NumPorts-1:0] rdata_cut; |
| 102 | + |
| 103 | + // Signals to select the right bank |
| 104 | + logic [ NumPorts-1:0][BankSelWidth-1:0] bank_sel; |
| 105 | + logic [NumPorts-1:0][Latency-1:0][BankSelWidth-1:0] out_mux_sel_d, out_mux_sel_q; |
| 106 | + |
| 107 | + // Identify bank looking at the BankSelWidth-th MSBs of the Address |
| 108 | + for (genvar PortIdx = 0; PortIdx < NumPorts; PortIdx++) begin : gen_bank_sel |
| 109 | + assign bank_sel[PortIdx] = addr_i[PortIdx][AddrWidth-1-:BankSelWidth]; |
| 110 | + end |
| 111 | + |
| 112 | + // Read Data Mux Logic: |
| 113 | + // |
| 114 | + // If the memory has Latency != 0, the read data will arive after a certain delay. |
| 115 | + // During this time, the bank_select signal must be stored in order to |
| 116 | + // correctly select the output bank after the expected latency. |
| 117 | + if (Latency == 32'd0) begin : gen_no_latency |
| 118 | + for (genvar PortIdx = 0; PortIdx < NumPorts; PortIdx++) begin : gen_read_mux_signals |
| 119 | + assign rdata_o[PortIdx] = rdata_cut[bank_sel[PortIdx]][PortIdx]; |
| 120 | + end |
| 121 | + end else begin : gen_read_latency |
| 122 | + always_comb begin |
| 123 | + for (int PortIdx = 0; PortIdx < NumPorts; PortIdx++) begin : gen_read_mux_signals |
| 124 | + rdata_o[PortIdx] = rdata_cut[out_mux_sel_q[PortIdx][0]][PortIdx]; |
| 125 | + for (int shift_idx = 0; shift_idx < (Latency - 1); shift_idx++) begin : gen_shift |
| 126 | + out_mux_sel_d[PortIdx][shift_idx] = out_mux_sel_q[PortIdx][shift_idx+1]; |
| 127 | + end |
| 128 | + out_mux_sel_d[PortIdx][Latency-1] = bank_sel[PortIdx]; |
| 129 | + end |
| 130 | + end |
| 131 | + |
| 132 | + always_ff @(posedge clk_i or negedge rst_ni) begin |
| 133 | + for (int PortIdx = 0; PortIdx < NumPorts; PortIdx++) begin |
| 134 | + if (!rst_ni) begin |
| 135 | + out_mux_sel_q[PortIdx] = '0; |
| 136 | + end else begin |
| 137 | + for (int shift_idx = 0; shift_idx < Latency; shift_idx++) begin |
| 138 | + out_mux_sel_q[PortIdx][shift_idx] = out_mux_sel_d[PortIdx][shift_idx]; |
| 139 | + end |
| 140 | + end |
| 141 | + end |
| 142 | + end |
| 143 | + end : gen_read_latency |
| 144 | + |
| 145 | + // Write data Mux Logic |
| 146 | + // |
| 147 | + for (genvar BankIdx = 0; BankIdx < NumLogicBanks; BankIdx++) begin : gen_logic_bank |
| 148 | + for (genvar PortIdx = 0; PortIdx < NumPorts; PortIdx++) begin |
| 149 | + // DEMUX the input signals to the correct logic bank |
| 150 | + // Assign req channel to the correct logic bank |
| 151 | + assign req_cut[BankIdx][PortIdx] = req_i[PortIdx] && (bank_sel[PortIdx] == BankIdx); |
| 152 | + // Assign lowest part of the address to the correct logic bank |
| 153 | + assign addr_cut[BankIdx][PortIdx] = req_cut[BankIdx][PortIdx] ? addr_i[PortIdx][AddrWidth-BankSelWidth-1:0] : '0; |
| 154 | + // Assign data to the correct logic bank |
| 155 | + assign wdata_cut[BankIdx][PortIdx] = req_cut[BankIdx][PortIdx] ? wdata_i[PortIdx] : '0; |
| 156 | + assign we_cut[BankIdx][PortIdx] = req_cut[BankIdx][PortIdx] ? we_i[PortIdx] : '0; |
| 157 | + assign be_cut[BankIdx][PortIdx] = req_cut[BankIdx][PortIdx] ? be_i[PortIdx] : '0; |
| 158 | + end |
| 159 | + |
| 160 | + tc_sram_impl #( |
| 161 | + .NumWords (LogicBankSize), |
| 162 | + .DataWidth (DataWidth), |
| 163 | + .ByteWidth (ByteWidth), |
| 164 | + .NumPorts (NumPorts), |
| 165 | + .Latency (Latency), |
| 166 | + .SimInit (SimInit), |
| 167 | + .PrintSimCfg(PrintSimCfg), |
| 168 | + .ImplKey (ImplKey), |
| 169 | + .impl_in_t (impl_in_t), |
| 170 | + .impl_out_t (impl_in_t) |
| 171 | + ) i_tc_sram_impl ( |
| 172 | + .clk_i, |
| 173 | + .rst_ni, |
| 174 | + .impl_i ({deepsleep_i[BankIdx], powergate_i[BankIdx]}), |
| 175 | + .impl_o (), |
| 176 | + .req_i (req_cut[BankIdx]), |
| 177 | + .we_i (we_cut[BankIdx]), |
| 178 | + .addr_i (addr_cut[BankIdx]), |
| 179 | + .wdata_i(wdata_cut[BankIdx]), |
| 180 | + .be_i (be_cut[BankIdx]), |
| 181 | + .rdata_o(rdata_cut[BankIdx]) |
| 182 | + ); |
| 183 | + end |
| 184 | + end |
| 185 | + |
| 186 | + // Trigger warnings when power signals (deepsleep_i and powergate_i) are not connected. |
| 187 | + // Usually those signals must be linked through the UPF. |
| 188 | +`ifndef VERILATOR |
| 189 | +`ifndef TARGET_SYNTHESIS |
| 190 | + initial begin |
| 191 | + assert (!$isunknown(deepsleep_i)) |
| 192 | + else $warning("deepsleep_i has some unconnected signals"); |
| 193 | + assert (!$isunknown(powergate_i)) |
| 194 | + else $warning("powergate_i has some unconnected signals"); |
| 195 | + end |
| 196 | +`endif |
| 197 | +`endif |
| 198 | + |
| 199 | +endmodule //endmodule: mem_multibank_pwrgate |
0 commit comments