From 8bcad1f23f7869cc2215d6eff042659248f0952c Mon Sep 17 00:00:00 2001 From: SikongJueluo Date: Tue, 19 May 2026 13:59:25 +0800 Subject: [PATCH] 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 --- hw/rtl/core/cam_core_banked.sv | 78 ++++++++++++++++--- .../cam_core_banked/test_cam_core_banked.py | 52 +++++++++++++ 2 files changed, 119 insertions(+), 11 deletions(-) diff --git a/hw/rtl/core/cam_core_banked.sv b/hw/rtl/core/cam_core_banked.sv index 9d1c7f6..8f69fbc 100644 --- a/hw/rtl/core/cam_core_banked.sv +++ b/hw/rtl/core/cam_core_banked.sv @@ -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 diff --git a/hw/sim/tests/modules/cam_core_banked/test_cam_core_banked.py b/hw/sim/tests/modules/cam_core_banked/test_cam_core_banked.py index 43341f0..77c7890 100644 --- a/hw/sim/tests/modules/cam_core_banked/test_cam_core_banked.py +++ b/hw/sim/tests/modules/cam_core_banked/test_cam_core_banked.py @@ -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]