Files
FPGA-Neural/sim/int8_memory_access_tb.v
T
micheleandClaude Sonnet 5 7e2711fa27 feat: widen ADDR_WIDTH to 23 bits for full 8MB PSRAM addressing
Bumps ADDR_WIDTH's default from 22 to 23 bits across every RTL
module (neuron_memory, layer_sequencer, spi_engine, spi_neuron_top,
mem_arbiter, int8_memory_access, memory_interface, psram_controller,
memory_model) and every testbench that mirrors it, so the system's
byte-address space reaches the full 8 MiB the recommended PSRAM part
(ISSI IS66WVE4M16EBLL-70BLI, docs/FPGA-Neural-Hardware-Design.md §3)
actually provides -- previously only 4 MiB (half the chip) was
reachable, since int8_memory_access.v's byte->word address shift
(addr >> 1) turned the old 22-bit byte address into only 21 real word
bits, one short of the chip's real 22-bit word address (A0-A21). At
23 bits, that same shift lands exactly on all 22 chip address lines,
so the whole part is usable now instead of deferred to a future
widening.

Also fixes a stray 22'd11-sized literal in layer_sequencer.v's
descriptor-table address increment (numerically already safe via
Verilog's zero-extension, but now correctly unsized so it always
matches ADDR_WIDTH instead of silently assuming 22).

Updated docs/FPGA-NeuralNetwork-Engine.md's SPI protocol address-field
note (23 bits, top 1 reserved bit instead of 2) and
docs/FPGA-Neural-Hardware-Design.md's PSRAM section (the "chip has
one spare address line" framing is gone now that all 22 are wired
and used).

Full regression (all 11 ADDR_WIDTH-touching testbenches, plus a
Yosys elaboration check of spi_neuron_top with the new default and
no override) passes clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
2026-09-02 21:00:46 +02:00

405 lines
9.2 KiB
Verilog

`timescale 1ns/1ps
module tb;
localparam ADDR_WIDTH = 23;
localparam CLK_PERIOD = 12.5;
reg clk;
reg rst;
// ============================================================
// INT8 interface
// ============================================================
reg req;
reg wr;
reg [ADDR_WIDTH-1:0] addr;
reg signed [7:0] wdata;
wire signed [7:0] rdata;
wire ready;
// ============================================================
// Memory interface
// ============================================================
wire mem_req;
wire mem_wr;
wire [ADDR_WIDTH-1:0] mem_addr;
wire [15:0] mem_wdata;
wire mem_lb_n;
wire mem_ub_n;
wire [15:0] mem_rdata;
wire mem_ready;
// ============================================================
// DUT
// ============================================================
int8_memory_access #(
.ADDR_WIDTH(ADDR_WIDTH)
) dut (
.clk (clk),
.rst (rst),
.req (req),
.wr (wr),
.addr (addr),
.wdata (wdata),
.rdata (rdata),
.ready (ready),
.mem_req (mem_req),
.mem_wr (mem_wr),
.mem_addr (mem_addr),
.mem_wdata (mem_wdata),
.mem_lb_n (mem_lb_n),
.mem_ub_n (mem_ub_n),
.mem_rdata (mem_rdata),
.mem_ready (mem_ready)
);
// ============================================================
// Simple memory model
// ============================================================
reg [15:0] memory [0:1023];
reg [15:0] model_rdata;
reg model_ready;
integer i;
assign mem_rdata = model_rdata;
assign mem_ready = model_ready;
// ============================================================
// Clock
// ============================================================
initial begin
clk = 1'b0;
forever #(CLK_PERIOD / 2.0)
clk = ~clk;
end
// ============================================================
// VCD
// ============================================================
initial begin
$dumpfile("sim/int8_memory_access.vcd");
$dumpvars(0, tb);
end
// ============================================================
// Memory model
// ============================================================
always @(posedge clk) begin
model_ready <= 1'b0;
if (mem_req) begin
if (mem_wr) begin
if (!mem_lb_n)
memory[mem_addr][7:0] <= mem_wdata[7:0];
if (!mem_ub_n)
memory[mem_addr][15:8] <= mem_wdata[15:8];
end else begin
model_rdata <= memory[mem_addr];
end
model_ready <= 1'b1;
end
end
// ============================================================
// Write helper
// ============================================================
task write_byte;
input [ADDR_WIDTH-1:0] byte_addr;
input signed [7:0] data;
begin
@(posedge clk);
addr <= byte_addr;
wdata <= data;
wr <= 1'b1;
req <= 1'b1;
@(posedge clk);
req <= 1'b0;
wait (ready);
$display(
"WRITE BYTE addr=0x%08x data=0x%02x PASS",
byte_addr,
data
);
@(posedge clk);
end
endtask
// ============================================================
// Read helper
// ============================================================
task read_byte;
input [ADDR_WIDTH-1:0] byte_addr;
input signed [7:0] expected;
begin
@(posedge clk);
addr <= byte_addr;
wr <= 1'b0;
req <= 1'b1;
@(posedge clk);
req <= 1'b0;
wait (ready);
if (rdata !== expected) begin
$display(
"READ BYTE addr=0x%08x FAIL got=0x%02x expected=0x%02x",
byte_addr,
rdata,
expected
);
$fatal;
end else begin
$display(
"READ BYTE addr=0x%08x data=0x%02x PASS",
byte_addr,
rdata
);
end
@(posedge clk);
end
endtask
// ============================================================
// Test
// ============================================================
initial begin
req = 1'b0;
wr = 1'b0;
addr = 0;
wdata = 0;
model_rdata = 16'h0000;
model_ready = 1'b0;
for (i = 0; i < 1024; i = i + 1)
memory[i] = 16'h0000;
rst = 1'b1;
repeat (5)
@(posedge clk);
rst = 1'b0;
$display("");
$display("========================================");
$display("INT8 MEMORY ACCESS TEST");
$display("========================================");
$display("");
// ========================================================
// Basic byte writes
// ========================================================
write_byte(22'h000000, 8'h12);
write_byte(22'h000001, 8'h34);
write_byte(22'h000002, 8'h56);
write_byte(22'h000003, 8'h78);
// ========================================================
// Reads
// ========================================================
read_byte(22'h000000, 8'h12);
read_byte(22'h000001, 8'h34);
read_byte(22'h000002, 8'h56);
read_byte(22'h000003, 8'h78);
// ========================================================
// Verify physical packing
// ========================================================
if (memory[0] !== 16'h3412) begin
$display(
"PACKING ERROR word[0]=0x%04x expected=0x3412",
memory[0]
);
$fatal;
end else begin
$display(
"PACKING word[0] = 0x%04x PASS",
memory[0]
);
end
if (memory[1] !== 16'h7856) begin
$display(
"PACKING ERROR word[1]=0x%04x expected=0x7856",
memory[1]
);
$fatal;
end else begin
$display(
"PACKING word[1] = 0x%04x PASS",
memory[1]
);
end
// ========================================================
// Byte preservation test
// ========================================================
// Start with 0x1234
write_byte(22'h000010, 8'h34);
write_byte(22'h000011, 8'h12);
if (memory[8] !== 16'h1234) begin
$display(
"INITIAL WORD ERROR = 0x%04x",
memory[8]
);
$fatal;
end
// Change low byte only:
// 0x1234 -> 0x12AA
write_byte(22'h000010, 8'hAA);
if (memory[8] !== 16'h12AA) begin
$display(
"LOW BYTE PRESERVE ERROR = 0x%04x expected=0x12AA",
memory[8]
);
$fatal;
end else begin
$display(
"LOW BYTE PRESERVE 0x1234 -> 0x12AA PASS"
);
end
// Change high byte only:
// 0x12AA -> 0xBBAA
write_byte(22'h000011, 8'hBB);
if (memory[8] !== 16'hBBAA) begin
$display(
"HIGH BYTE PRESERVE ERROR = 0x%04x expected=0xBBAA",
memory[8]
);
$fatal;
end else begin
$display(
"HIGH BYTE PRESERVE 0x12AA -> 0xBBAA PASS"
);
end
// ========================================================
// Signed INT8 values
// ========================================================
write_byte(22'h000020, -8'sd1);
write_byte(22'h000021, -8'sd128);
write_byte(22'h000022, 8'sd127);
read_byte(22'h000020, -8'sd1);
read_byte(22'h000021, -8'sd128);
read_byte(22'h000022, 8'sd127);
// ========================================================
// Final result
// ========================================================
$display("");
$display("========================================");
$display("INT8 MEMORY ACCESS TEST PASSED");
$display("BYTE ADDRESSING : PASS");
$display("LOW BYTE : PASS");
$display("HIGH BYTE : PASS");
$display("INT8 PACKING : PASS");
$display("BYTE PRESERVATION : PASS");
$display("SIGNED INT8 : PASS");
$display("========================================");
$display("");
$finish;
end
endmodule