mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-12 20:15:31 +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
|
||||
`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 (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
@@ -17,22 +45,52 @@ module cam_core_banked (
|
||||
output logic [(`LANES)*(`HASH_BITS)-1:0] rd_hashes_o,
|
||||
output logic [(`LANES)-1:0] rd_lane_valid_o
|
||||
);
|
||||
localparam int BANKS = `LANES;
|
||||
localparam int BANK_DEPTH = `NUM_ROWS / `LANES;
|
||||
localparam int BANKS = `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
|
||||
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
|
||||
`endif
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (wr_valid) begin
|
||||
bank_mem[wr_row % `LANES][wr_row / `LANES] <= wr_hash;
|
||||
generate
|
||||
for (genvar b = 0; b < BANKS; b++) begin : g_bank
|
||||
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
|
||||
|
||||
@@ -40,21 +98,19 @@ module cam_core_banked (
|
||||
if (!rst_n) begin
|
||||
rd_valid_o <= 1'b0;
|
||||
rd_row_ids_o <= '0;
|
||||
rd_hashes_o <= '0;
|
||||
rd_lane_valid_o <= '0;
|
||||
end else begin
|
||||
rd_valid_o <= rd_valid_i;
|
||||
rd_lane_valid_o <= rd_valid_i ? {`LANES{1'b1}} : '0;
|
||||
|
||||
`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");
|
||||
end
|
||||
`endif
|
||||
|
||||
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_hashes_o[lane*`HASH_BITS +: `HASH_BITS] <= bank_mem[lane][rd_base_row_i / `LANES];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,6 +19,26 @@ async def reset_core(dut):
|
||||
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()
|
||||
async def banked_core_reads_aligned_eight_row_batch_after_one_cycle(dut):
|
||||
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)
|
||||
assert got_row == lane
|
||||
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