mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-13 04:25:32 +08:00
refactor(core/cam_core_banked): extract per-bank modules for improved timing isolation
- Extract cam_bank as a parameterized submodule with independent read/write ports - Replace flat 2D memory array with generate loop of bank instances - Derive bank selection from address bit slicing instead of modulo arithmetic - Align rd_base_row_i check with new bank addressing scheme - Add test verifying bank address isolation across multiple banks
This commit is contained in:
@@ -1,6 +1,34 @@
|
|||||||
`timescale 1ns / 1ps
|
`timescale 1ns / 1ps
|
||||||
`include "cam_params.svh"
|
`include "cam_params.svh"
|
||||||
|
|
||||||
|
module cam_bank #(
|
||||||
|
parameter int HASH_BITS = 512,
|
||||||
|
parameter int BANK_DEPTH = 512,
|
||||||
|
parameter int BANK_ADDR_BITS = 9
|
||||||
|
) (
|
||||||
|
input logic clk,
|
||||||
|
|
||||||
|
input logic wr_en,
|
||||||
|
input logic [BANK_ADDR_BITS-1:0] wr_addr,
|
||||||
|
input logic [HASH_BITS-1:0] wr_data,
|
||||||
|
|
||||||
|
input logic rd_en,
|
||||||
|
input logic [BANK_ADDR_BITS-1:0] rd_addr,
|
||||||
|
output logic [HASH_BITS-1:0] rd_data
|
||||||
|
);
|
||||||
|
(* ram_style = "block" *) logic [HASH_BITS-1:0] mem [0:BANK_DEPTH-1];
|
||||||
|
|
||||||
|
always_ff @(posedge clk) begin
|
||||||
|
if (wr_en) begin
|
||||||
|
mem[wr_addr] <= wr_data;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (rd_en) begin
|
||||||
|
rd_data <= mem[rd_addr];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
module cam_core_banked (
|
module cam_core_banked (
|
||||||
input logic clk,
|
input logic clk,
|
||||||
input logic rst_n,
|
input logic rst_n,
|
||||||
@@ -18,21 +46,51 @@ module cam_core_banked (
|
|||||||
output logic [(`LANES)-1:0] rd_lane_valid_o
|
output logic [(`LANES)-1:0] rd_lane_valid_o
|
||||||
);
|
);
|
||||||
localparam int BANKS = `LANES;
|
localparam int BANKS = `LANES;
|
||||||
localparam int BANK_DEPTH = `NUM_ROWS / `LANES;
|
localparam int BANK_DEPTH = `NUM_ROWS / BANKS;
|
||||||
|
localparam int BANK_SEL_BITS = $clog2(BANKS);
|
||||||
|
localparam int BANK_ADDR_BITS = $clog2(BANK_DEPTH);
|
||||||
|
|
||||||
(* ram_style = "block" *) logic [(`HASH_BITS)-1:0] bank_mem [0:BANKS-1][0:BANK_DEPTH-1];
|
logic [BANK_SEL_BITS-1:0] wr_bank_id;
|
||||||
|
logic [BANK_ADDR_BITS-1:0] wr_bank_addr;
|
||||||
|
logic [BANK_ADDR_BITS-1:0] rd_bank_addr;
|
||||||
|
logic [(`HASH_BITS)-1:0] bank_rd_data [0:BANKS-1];
|
||||||
|
|
||||||
assign wr_ready = 1'b1;
|
assign wr_ready = 1'b1;
|
||||||
|
assign wr_bank_id = wr_row[BANK_SEL_BITS-1:0];
|
||||||
|
assign wr_bank_addr = wr_row[(`ROW_BITS)-1:BANK_SEL_BITS];
|
||||||
|
assign rd_bank_addr = rd_base_row_i[(`ROW_BITS)-1:BANK_SEL_BITS];
|
||||||
|
|
||||||
`ifndef SYNTHESIS
|
`ifndef SYNTHESIS
|
||||||
initial begin
|
initial begin
|
||||||
if (`NUM_ROWS % `LANES != 0) $fatal(1, "NUM_ROWS must be divisible by LANES");
|
if (`NUM_ROWS != (BANK_DEPTH * BANKS)) $fatal(1, "NUM_ROWS must be divisible by LANES");
|
||||||
|
if (`LANES != (1 << BANK_SEL_BITS)) $fatal(1, "LANES must be a power of two");
|
||||||
end
|
end
|
||||||
`endif
|
`endif
|
||||||
|
|
||||||
always_ff @(posedge clk) begin
|
generate
|
||||||
if (wr_valid) begin
|
for (genvar b = 0; b < BANKS; b++) begin : g_bank
|
||||||
bank_mem[wr_row % `LANES][wr_row / `LANES] <= wr_hash;
|
cam_bank #(
|
||||||
|
.HASH_BITS(`HASH_BITS),
|
||||||
|
.BANK_DEPTH(BANK_DEPTH),
|
||||||
|
.BANK_ADDR_BITS(BANK_ADDR_BITS)
|
||||||
|
) u_bank (
|
||||||
|
.clk(clk),
|
||||||
|
.wr_en(wr_valid && (wr_bank_id == b[BANK_SEL_BITS-1:0])),
|
||||||
|
.wr_addr(wr_bank_addr),
|
||||||
|
.wr_data(wr_hash),
|
||||||
|
.rd_en(rd_valid_i),
|
||||||
|
.rd_addr(rd_bank_addr),
|
||||||
|
.rd_data(bank_rd_data[b])
|
||||||
|
);
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
always_comb begin
|
||||||
|
rd_hashes_o = '0;
|
||||||
|
if (rd_valid_o) begin
|
||||||
|
for (int lane = 0; lane < `LANES; lane++) begin
|
||||||
|
rd_hashes_o[lane*`HASH_BITS +: `HASH_BITS] = bank_rd_data[lane];
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -40,21 +98,19 @@ module cam_core_banked (
|
|||||||
if (!rst_n) begin
|
if (!rst_n) begin
|
||||||
rd_valid_o <= 1'b0;
|
rd_valid_o <= 1'b0;
|
||||||
rd_row_ids_o <= '0;
|
rd_row_ids_o <= '0;
|
||||||
rd_hashes_o <= '0;
|
|
||||||
rd_lane_valid_o <= '0;
|
rd_lane_valid_o <= '0;
|
||||||
end else begin
|
end else begin
|
||||||
rd_valid_o <= rd_valid_i;
|
rd_valid_o <= rd_valid_i;
|
||||||
rd_lane_valid_o <= rd_valid_i ? {`LANES{1'b1}} : '0;
|
rd_lane_valid_o <= rd_valid_i ? {`LANES{1'b1}} : '0;
|
||||||
|
|
||||||
`ifndef SYNTHESIS
|
`ifndef SYNTHESIS
|
||||||
if (rd_valid_i && ((rd_base_row_i % `LANES) != 0)) begin
|
if (rd_valid_i && ((rd_base_row_i[BANK_SEL_BITS-1:0]) != '0)) begin
|
||||||
$fatal(1, "rd_base_row_i must be LANES-aligned");
|
$fatal(1, "rd_base_row_i must be LANES-aligned");
|
||||||
end
|
end
|
||||||
`endif
|
`endif
|
||||||
|
|
||||||
for (int lane = 0; lane < `LANES; lane++) begin
|
for (int lane = 0; lane < `LANES; lane++) begin
|
||||||
rd_row_ids_o[lane*`ROW_BITS +: `ROW_BITS] <= rd_base_row_i + lane[(`ROW_BITS)-1:0];
|
rd_row_ids_o[lane*`ROW_BITS +: `ROW_BITS] <= rd_base_row_i + lane[(`ROW_BITS)-1:0];
|
||||||
rd_hashes_o[lane*`HASH_BITS +: `HASH_BITS] <= bank_mem[lane][rd_base_row_i / `LANES];
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,6 +19,26 @@ async def reset_core(dut):
|
|||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
|
|
||||||
|
|
||||||
|
def lane_word(value: int, lane: int, width: int) -> int:
|
||||||
|
return (value >> (lane * width)) & ((1 << width) - 1)
|
||||||
|
|
||||||
|
|
||||||
|
async def write_row(dut, row: int, hash_value: int):
|
||||||
|
dut.wr_valid.value = 1
|
||||||
|
dut.wr_row.value = row
|
||||||
|
dut.wr_hash.value = hash_value
|
||||||
|
await RisingEdge(dut.clk)
|
||||||
|
dut.wr_valid.value = 0
|
||||||
|
|
||||||
|
|
||||||
|
async def request_batch(dut, base_row: int):
|
||||||
|
dut.rd_base_row_i.value = base_row
|
||||||
|
dut.rd_valid_i.value = 1
|
||||||
|
await RisingEdge(dut.clk)
|
||||||
|
dut.rd_valid_i.value = 0
|
||||||
|
await RisingEdge(dut.clk)
|
||||||
|
|
||||||
|
|
||||||
@cocotb.test()
|
@cocotb.test()
|
||||||
async def banked_core_reads_aligned_eight_row_batch_after_one_cycle(dut):
|
async def banked_core_reads_aligned_eight_row_batch_after_one_cycle(dut):
|
||||||
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
|
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
|
||||||
@@ -47,3 +67,35 @@ async def banked_core_reads_aligned_eight_row_batch_after_one_cycle(dut):
|
|||||||
got_hash = (int(dut.rd_hashes_o.value) >> (lane * HASH_BITS)) & ((1 << HASH_BITS) - 1)
|
got_hash = (int(dut.rd_hashes_o.value) >> (lane * HASH_BITS)) & ((1 << HASH_BITS) - 1)
|
||||||
assert got_row == lane
|
assert got_row == lane
|
||||||
assert got_hash == lane + 0x100
|
assert got_hash == lane + 0x100
|
||||||
|
|
||||||
|
|
||||||
|
@cocotb.test()
|
||||||
|
async def banked_core_keeps_internal_bank_addresses_isolated(dut):
|
||||||
|
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
|
||||||
|
await reset_core(dut)
|
||||||
|
|
||||||
|
LANES = len(dut.rd_lane_valid_o)
|
||||||
|
ROW_BITS = len(dut.rd_row_ids_o) // LANES
|
||||||
|
HASH_BITS = len(dut.rd_hashes_o) // LANES
|
||||||
|
|
||||||
|
expected_by_row = {}
|
||||||
|
for bank_addr in range(3):
|
||||||
|
base_row = bank_addr * LANES
|
||||||
|
for lane in range(LANES):
|
||||||
|
row = base_row + lane
|
||||||
|
hash_value = 0xABC000 + (bank_addr << 8) + lane
|
||||||
|
expected_by_row[row] = hash_value
|
||||||
|
await write_row(dut, row, hash_value)
|
||||||
|
|
||||||
|
for bank_addr in range(3):
|
||||||
|
base_row = bank_addr * LANES
|
||||||
|
await request_batch(dut, base_row)
|
||||||
|
assert int(dut.rd_valid_o.value) == 1
|
||||||
|
assert int(dut.rd_lane_valid_o.value) == (1 << LANES) - 1
|
||||||
|
|
||||||
|
for lane in range(LANES):
|
||||||
|
row = base_row + lane
|
||||||
|
got_row = lane_word(int(dut.rd_row_ids_o.value), lane, ROW_BITS)
|
||||||
|
got_hash = lane_word(int(dut.rd_hashes_o.value), lane, HASH_BITS)
|
||||||
|
assert got_row == row
|
||||||
|
assert got_hash == expected_by_row[row]
|
||||||
|
|||||||
Reference in New Issue
Block a user