Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HW]: Add mem_multibanked_pwrgate for correct power management #246

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ tests:
PARAM1: [-GN=1, -GN=2, -GN=3, -GN=4, -GN=8, -GN=16]
- TOPLEVEL: fifo_tb
PARAM1: [-GDEPTH=1, -GDEPTH=13, -GDEPTH=32 -GFALL_THROUGH=1]
- TOPLEVEL: mem_multibank_pwrgate_tb
PARAM1: [-gNumLogicBanks=1, -gNumLogicBanks=2, -gNumLogicBanks=4, -gNumLogicBanks=8]
PARAM2: [-gLatency=0, -gLatency=1, -gLatency=2]
# - TOPLEVEL: [cdc_2phase_tb, cdc_2phase_clearable_tb]
# PARAM1: -GUNTIL=1000000
# - TOPLEVEL: cdc_fifo_tb
Expand Down
2 changes: 2 additions & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ sources:
- src/lfsr_8bit.sv
- src/lossy_valid_to_stream.sv
- src/mv_filter.sv
- src/mem_multibank_pwrgate.sv
- src/onehot_to_bin.sv
- src/plru_tree.sv
- src/passthrough_stream_fifo.sv
Expand Down Expand Up @@ -129,6 +130,7 @@ sources:
- test/fifo_tb.sv
- test/graycode_tb.sv
- test/id_queue_tb.sv
- test/mem_multibank_pwrgate_tb.sv
- test/passthrough_stream_fifo_tb.sv
- test/popcount_tb.sv
- test/rr_arb_tree_tb.sv
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ Please note that cells with status *deprecated* are not to be used for new desig
| `popcount` | Combinatorial popcount (hamming weight) | active | |
| `mem_to_banks_detailed` | Split memory access over multiple parallel banks with detailed response signals | active | |
| `mem_to_banks` | Split memory access over multiple parallel banks | active | |
| `mem_multibank_pwrgate` | Power-aware wrapper for memory bank with bank retention and power-off capabilities | active | |

### Data Structures

Expand Down
1 change: 1 addition & 0 deletions common_cells.core
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ filesets:
- src/lfsr_8bit.sv
- src/multiaddr_decode.sv
- src/mv_filter.sv
- src/mem_multibank_pwrgate.sv
- src/onehot_to_bin.sv
- src/plru_tree.sv
- src/passthrough_stream_fifo.sv
Expand Down
211 changes: 211 additions & 0 deletions src/mem_multibank_pwrgate.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// Copyright 2024 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
//
// Lorenzo Leone <lleone@iis.ee.ethz.ch>

// ## Description:
// A wrapper for `tc_sram_impl` that instantiates logic banks with retention mode
// or power-off capability.
// This module can be used for power-aware simulations, with control signals driven
// directly by UPF signals.
//
// ## Goal:
// In a memory with multiple banks that support power gating and retention,
// each bank’s addressing must ensure that interleaving remains intact. During retention
// or power-off states, only contiguous addresses should be switched.
// The memory should always appear as a set of contiguous addresses, with no gaps in the
// address mapping.
// This module is responsible for managing the correct memory addressing
//
`include "common_cells/assertions.svh"
module mem_multibank_pwrgate #(
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 int unsigned NumLogicBanks = 32'd1, // Logic bank for Power Management
parameter string SimInit = "none", // Simulation initialization
parameter bit PrintSimCfg = 1'b0, // Print configuration
parameter string ImplKey = "none", // Reference to specific implementation
// 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
input logic [NumLogicBanks-1:0] deepsleep_i, // deep sleep enable
input logic [NumLogicBanks-1:0] powergate_i, // power gate enable
// output ports
output data_t [ NumPorts-1:0] rdata_o // read data
);

// Implementation type for Power Gating and Deppesleep ports
typedef struct packed {
logic deepsleep;
logic powergate;
} impl_in_t;


if (NumLogicBanks == 32'd0) begin : gen_no_logic_bank
$fatal("Error: %d logic banks are not supported", NumLogicBanks);
niwis marked this conversation as resolved.
Show resolved Hide resolved
end else if (NumLogicBanks == 32'd1) begin : gen_simple_sram
tc_sram_impl #(
.NumWords (NumWords),
.DataWidth (DataWidth),
.ByteWidth (ByteWidth),
.NumPorts (NumPorts),
.Latency (Latency),
.SimInit (SimInit),
.PrintSimCfg(PrintSimCfg),
.ImplKey (ImplKey),
.impl_in_t (impl_in_t),
.impl_out_t (impl_in_t)
) i_tc_sram_impl (
.clk_i,
.rst_ni,
.impl_i({deepsleep_i, powergate_i}),
.impl_o(),
.req_i,
.we_i,
.addr_i,
.wdata_i,
.be_i,
.rdata_o
);

end else begin : gen_logic_bank // block: gen_simple_sram
localparam int unsigned LogicBankSize = NumWords / NumLogicBanks;
localparam int unsigned BankSelWidth = (NumLogicBanks > 32'd1) ?
$clog2(NumLogicBanks) : 32'd1;


// Signals from/to logic banks
logic [NumLogicBanks-1:0][ NumPorts-1:0] req_cut;
logic [NumLogicBanks-1:0][ NumPorts-1:0] we_cut;
logic [NumLogicBanks-1:0][ NumPorts-1:0][AddrWidth-BankSelWidth-1:0] addr_cut;
data_t [NumLogicBanks-1:0][ NumPorts-1:0] wdata_cut;
be_t [NumLogicBanks-1:0][ NumPorts-1:0] be_cut;
data_t [NumLogicBanks-1:0][ NumPorts-1:0] rdata_cut;

// Signals to select the right bank
logic [NumPorts-1:0][BankSelWidth-1:0] bank_sel;

// Identify bank looking at the BankSelWidth-th MSBs of the Address
for (genvar PortIdx = 0; PortIdx < NumPorts; PortIdx++) begin : gen_bank_sel
assign bank_sel[PortIdx] = addr_i[PortIdx][AddrWidth-1-:BankSelWidth];
end

// Read Data Mux Logic:
//
// If the memory has Latency != 0, the read data will arive after a certain delay.
// During this time, the bank_select signal must be stored in order to
// correctly select the output bank after the expected latency.
if (Latency == 32'd0) begin : gen_no_latency
for (genvar PortIdx = 0; PortIdx < NumPorts; PortIdx++) begin : gen_read_mux_signals
assign rdata_o[PortIdx] = rdata_cut[bank_sel[PortIdx]][PortIdx];
end
end else begin : gen_read_latency
// Define input/output registers to hold the read value
logic [NumPorts-1:0][Latency-1:0][BankSelWidth-1:0] out_mux_sel_d, out_mux_sel_q;

always_comb begin
for (int PortIdx = 0; PortIdx < NumPorts; PortIdx++) begin : gen_read_mux_signals
rdata_o[PortIdx] = rdata_cut[out_mux_sel_q[PortIdx][0]][PortIdx];
for (int shift_idx = 0; shift_idx < (Latency - 1); shift_idx++) begin : gen_shift
out_mux_sel_d[PortIdx][shift_idx] = out_mux_sel_q[PortIdx][shift_idx+1];
end
out_mux_sel_d[PortIdx][Latency-1] = bank_sel[PortIdx];
end
end

always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
out_mux_sel_q <= '0;
end else begin
out_mux_sel_q <= out_mux_sel_d;
end

// for (int PortIdx = 0; PortIdx < NumPorts; PortIdx++) begin
// if (!rst_ni) begin
// out_mux_sel_q[PortIdx] <= '0;
// end else begin
// for (int shift_idx = 0; shift_idx < Latency; shift_idx++) begin
// out_mux_sel_q[PortIdx][shift_idx] <= out_mux_sel_d[PortIdx][shift_idx];
// end
// end
// end
Lore0599 marked this conversation as resolved.
Show resolved Hide resolved
end
end : gen_read_latency

// Write data Mux Logic
//
for (genvar BankIdx = 0; BankIdx < NumLogicBanks; BankIdx++) begin : gen_logic_bank
for (genvar PortIdx = 0; PortIdx < NumPorts; PortIdx++) begin: gen_port_write_logic
// DEMUX the input signals to the correct logic bank
// Assign req channel to the correct logic bank
assign req_cut[BankIdx][PortIdx] = req_i[PortIdx] && (bank_sel[PortIdx] == BankIdx);
// Assign lowest part of the address to the correct logic bank
assign addr_cut[BankIdx][PortIdx] = req_cut[BankIdx][PortIdx] ?
addr_i[PortIdx][AddrWidth-BankSelWidth-1:0] : '0;
// Assign data to the correct logic bank
assign wdata_cut[BankIdx][PortIdx] = req_cut[BankIdx][PortIdx] ? wdata_i[PortIdx] : '0;
assign we_cut[BankIdx][PortIdx] = req_cut[BankIdx][PortIdx] ? we_i[PortIdx] : '0;
assign be_cut[BankIdx][PortIdx] = req_cut[BankIdx][PortIdx] ? be_i[PortIdx] : '0;
end

tc_sram_impl #(
.NumWords (LogicBankSize),
.DataWidth (DataWidth),
.ByteWidth (ByteWidth),
.NumPorts (NumPorts),
.Latency (Latency),
.SimInit (SimInit),
.PrintSimCfg(PrintSimCfg),
.ImplKey (ImplKey),
.impl_in_t (impl_in_t),
.impl_out_t (impl_in_t)
) i_tc_sram_impl (
.clk_i,
.rst_ni,
.impl_i ({deepsleep_i[BankIdx], powergate_i[BankIdx]}),
.impl_o (),
.req_i (req_cut[BankIdx]),
.we_i (we_cut[BankIdx]),
.addr_i (addr_cut[BankIdx]),
.wdata_i(wdata_cut[BankIdx]),
.be_i (be_cut[BankIdx]),
.rdata_o(rdata_cut[BankIdx])
);
end : gen_logic_bank
`ifndef COMMON_CELLS_ASSERTS_OFF
`ASSERT_INIT(pwr2_bank, LogicBankSize == 2 ** (AddrWidth - BankSelWidth),
"Logic Bank size is not a power of two: UNSUPPORTED!")
`endif

end

// Trigger warnings when power signals (deepsleep_i and powergate_i) are not connected.
// Usually those signals must be linked through the UPF.
`ifndef VERILATOR
`ifndef SYNTHESIS
initial begin
assert (!$isunknown(deepsleep_i))
else $warning("deepsleep_i has some unconnected signals");
assert (!$isunknown(powergate_i))
else $warning("powergate_i has some unconnected signals");
end
`endif
`endif

endmodule //endmodule: mem_multibank_pwrgate
Loading
Loading