Skip to content

Commit 9889e5a

Browse files
committed
[HW]: Add mem_multibanked_pwrgate for correct power management
1 parent 554ebbc commit 9889e5a

File tree

4 files changed

+428
-0
lines changed

4 files changed

+428
-0
lines changed

Bender.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ sources:
4646
- src/lfsr_8bit.sv
4747
- src/lossy_valid_to_stream.sv
4848
- src/mv_filter.sv
49+
- src/mem_multibank_pwrgate.sv
4950
- src/onehot_to_bin.sv
5051
- src/plru_tree.sv
5152
- src/passthrough_stream_fifo.sv
@@ -129,6 +130,7 @@ sources:
129130
- test/fifo_tb.sv
130131
- test/graycode_tb.sv
131132
- test/id_queue_tb.sv
133+
- test/mem_multibank_pwrgate_tb.sv
132134
- test/passthrough_stream_fifo_tb.sv
133135
- test/popcount_tb.sv
134136
- test/rr_arb_tree_tb.sv

src/mem_multibank_pwrgate.sv

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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

Comments
 (0)