Begins the V2 Neural Multiprocessor / Dataflow architecture per docs/v2-description.md, per explicit user request to freeze V1 and start V2 development, copying from V1 what's needed. Scaffold: - hardware/v1/: byte-exact, read-only copy of the current V1 codebase (rtl, testbenches, tools, constraints, a representative subset of synthesis results, and reference docs) -- verified identical via diff/cmp against the live top-level tree before being made filesystem-read-only. The live top-level tree is untouched and remains the project's "production" V1 (see hardware/v1/README.md and hardware/v2/logs/decisions.log DEC-0001 for why copy-not-move). - hardware/v2/: mandatory structure (rtl/sim/constraints/synthesis/ reports/scripts/logs/docs) plus the full logging system required by the spec (development/architecture/simulation/synthesis/timing/ benchmark/decisions/experiments/errors.log). M1 -- Neural Processor (hardware/v2/rtl/neural_processor.v): - 8-stage pipelined perceptron unit (P_IN=8): input align, 8 multipliers, 3-level adder tree, accumulator, bias+activation, INT8 saturation. Genuine 1-tile/cycle throughput, not just a wider combinational datapath. - 7-state FSM (NP_IDLE..NP_ERROR per docs/v2-description.md §6, with 4 baseline states merged into NP_WAIT_OPERANDS -- see decisions.log DEC-0002); valid/ready/data/last stream interfaces per §7. - Bit-exact vs the frozen hardware/v1/rtl/neuron_parallel.v + mac8.v + mac_unit.v: 7/7 tests pass (hardware/v2/sim/tb_neural_processor.v), covering regular/mixed-sign/extreme-INT8 vectors, both activations, a zero-idle-gap back-to-back-tiles throughput check, and an 8-tile job -- verified with Verilator (see below for why). - Real synthesis + place&route (Yosys + nextpnr-ecp5): 0 CHECK problems, Fmax 183.12 MHz at ACC_WIDTH=32 (PASS at 80MHz, ~3x V1's isolated PARALLEL=8 Fmax of 61.71 MHz) and 176.21 MHz at ACC_WIDTH=24 (a user-requested comparison experiment, also bit-exact-verified; see experiments.log EXP-0001/EXP-0002 and benchmark.log). Three real bugs found and resolved during M1 development (full diagnostic record in errors.log): - Two independent, reproducible Icarus Verilog v13.0 scheduling defects (ERR-0001, ERR-0002) that silently produced wrong simulation results for standard sequential Verilog -- confirmed via Verilator 5.050 giving correct results on the same minimal repros. Verilator is now the trusted simulator for hardware/v2/ (decisions.log DEC-0004); Icarus's affected protocol-violation check was removed from the RTL and deferred architecturally to the Neural Director (DEC-0003) rather than chased further. - One real RTL bug (ERR-0003): last0 wasn't gated like valid0, letting a "last tile" tag leak into the pipeline ahead of its actual valid tile on back-to-back jobs. Fixed and verified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
455 lines
12 KiB
Verilog
455 lines
12 KiB
Verilog
`timescale 1ns/1ps
|
|
|
|
module tb;
|
|
|
|
localparam ADDR_WIDTH = 23;
|
|
localparam DATA_WIDTH = 16;
|
|
localparam CLK_PERIOD = 12.5; // 80 MHz
|
|
|
|
// ============================================================
|
|
// Clock / Reset
|
|
// ============================================================
|
|
|
|
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 [DATA_WIDTH-1:0] mem_wdata;
|
|
wire mem_lb_n;
|
|
wire mem_ub_n;
|
|
|
|
wire [DATA_WIDTH-1:0] mem_rdata;
|
|
wire mem_ready;
|
|
|
|
// ============================================================
|
|
// Memory interface -> PSRAM controller
|
|
// ============================================================
|
|
|
|
wire psram_mem_req;
|
|
wire psram_mem_wr;
|
|
wire [ADDR_WIDTH-1:0] psram_mem_addr;
|
|
wire [DATA_WIDTH-1:0] psram_mem_wdata;
|
|
wire psram_mem_lb_n;
|
|
wire psram_mem_ub_n;
|
|
|
|
wire [DATA_WIDTH-1:0] psram_mem_rdata;
|
|
wire psram_mem_ready;
|
|
|
|
// ============================================================
|
|
// PSRAM physical interface
|
|
// ============================================================
|
|
|
|
wire [ADDR_WIDTH-1:0] psram_a;
|
|
wire [DATA_WIDTH-1:0] psram_dq;
|
|
|
|
wire psram_ce_n;
|
|
wire psram_oe_n;
|
|
wire psram_we_n;
|
|
wire psram_lb_n;
|
|
wire psram_ub_n;
|
|
wire psram_zz_n;
|
|
|
|
// ============================================================
|
|
// INT8 MEMORY ACCESS
|
|
// ============================================================
|
|
|
|
int8_memory_access #(
|
|
.ADDR_WIDTH(ADDR_WIDTH)
|
|
) int8_access (
|
|
.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)
|
|
);
|
|
|
|
// ============================================================
|
|
// MEMORY INTERFACE
|
|
// ============================================================
|
|
|
|
memory_interface #(
|
|
.ADDR_WIDTH(ADDR_WIDTH),
|
|
.DATA_WIDTH(DATA_WIDTH)
|
|
) memory_if (
|
|
.clk (clk),
|
|
.rst (rst),
|
|
|
|
.req (mem_req),
|
|
.wr (mem_wr),
|
|
.addr (mem_addr),
|
|
.wdata (mem_wdata),
|
|
.lb_n (mem_lb_n),
|
|
.ub_n (mem_ub_n),
|
|
|
|
.rdata (mem_rdata),
|
|
.ready (mem_ready),
|
|
|
|
.mem_req (psram_mem_req),
|
|
.mem_wr (psram_mem_wr),
|
|
.mem_addr (psram_mem_addr),
|
|
.mem_wdata (psram_mem_wdata),
|
|
.mem_lb_n (psram_mem_lb_n),
|
|
.mem_ub_n (psram_mem_ub_n),
|
|
|
|
.mem_rdata (psram_mem_rdata),
|
|
.mem_ready (psram_mem_ready)
|
|
);
|
|
|
|
// ============================================================
|
|
// PSRAM CONTROLLER
|
|
// ============================================================
|
|
|
|
psram_controller #(
|
|
.ADDR_WIDTH (ADDR_WIDTH),
|
|
.DATA_WIDTH (DATA_WIDTH),
|
|
.CLK_FREQ_MHZ (80)
|
|
) psram_ctrl (
|
|
.clk (clk),
|
|
.rst (rst),
|
|
|
|
.mem_req (psram_mem_req),
|
|
.mem_wr (psram_mem_wr),
|
|
.mem_addr (psram_mem_addr),
|
|
.mem_wdata (psram_mem_wdata),
|
|
.mem_lb_n (psram_mem_lb_n),
|
|
.mem_ub_n (psram_mem_ub_n),
|
|
|
|
.mem_rdata (psram_mem_rdata),
|
|
.mem_ready (psram_mem_ready),
|
|
|
|
.psram_a (psram_a),
|
|
.psram_dq (psram_dq),
|
|
|
|
.psram_ce_n(psram_ce_n),
|
|
.psram_oe_n(psram_oe_n),
|
|
.psram_we_n(psram_we_n),
|
|
.psram_lb_n(psram_lb_n),
|
|
.psram_ub_n(psram_ub_n),
|
|
.psram_zz_n(psram_zz_n)
|
|
);
|
|
|
|
// ============================================================
|
|
// PSRAM MODEL
|
|
// ============================================================
|
|
|
|
psram_model #(
|
|
.ADDR_WIDTH(ADDR_WIDTH),
|
|
.DATA_WIDTH(DATA_WIDTH),
|
|
.DEPTH(16384)
|
|
) psram (
|
|
.clk (clk),
|
|
.a (psram_a),
|
|
.dq (psram_dq),
|
|
.ce_n (psram_ce_n),
|
|
.oe_n (psram_oe_n),
|
|
.we_n (psram_we_n),
|
|
.lb_n (psram_lb_n),
|
|
.ub_n (psram_ub_n),
|
|
.zz_n (psram_zz_n)
|
|
);
|
|
|
|
// ============================================================
|
|
// Clock
|
|
// ============================================================
|
|
|
|
initial begin
|
|
|
|
clk = 1'b0;
|
|
|
|
forever #(CLK_PERIOD / 2.0)
|
|
clk = ~clk;
|
|
|
|
end
|
|
|
|
// ============================================================
|
|
// VCD
|
|
// ============================================================
|
|
|
|
initial begin
|
|
|
|
$dumpfile("sim/int8_psram_integration.vcd");
|
|
$dumpvars(0, tb);
|
|
|
|
end
|
|
|
|
// ============================================================
|
|
// Write INT8
|
|
// ============================================================
|
|
|
|
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 INT8
|
|
// ============================================================
|
|
|
|
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
|
|
// ============================================================
|
|
|
|
integer i;
|
|
integer test_addr;
|
|
reg signed [7:0] test_data;
|
|
|
|
initial begin
|
|
|
|
req = 1'b0;
|
|
wr = 1'b0;
|
|
addr = 0;
|
|
wdata = 0;
|
|
|
|
rst = 1'b1;
|
|
|
|
repeat (5)
|
|
@(posedge clk);
|
|
|
|
rst = 1'b0;
|
|
|
|
$display("");
|
|
$display("========================================");
|
|
$display("INT8 + PSRAM FULL INTEGRATION TEST");
|
|
$display("========================================");
|
|
$display("");
|
|
|
|
// --------------------------------------------------------
|
|
// Wait for PSRAM initialization
|
|
// --------------------------------------------------------
|
|
|
|
wait (psram_ctrl.state == psram_ctrl.STATE_IDLE);
|
|
|
|
$display("PSRAM initialization complete");
|
|
$display("");
|
|
|
|
// ========================================================
|
|
// BASIC INT8 PACKING
|
|
// ========================================================
|
|
|
|
write_byte(22'h000000, 8'h12);
|
|
write_byte(22'h000001, 8'h34);
|
|
|
|
write_byte(22'h000002, 8'h56);
|
|
write_byte(22'h000003, 8'h78);
|
|
|
|
read_byte(22'h000000, 8'h12);
|
|
read_byte(22'h000001, 8'h34);
|
|
read_byte(22'h000002, 8'h56);
|
|
read_byte(22'h000003, 8'h78);
|
|
|
|
// ========================================================
|
|
// BYTE PRESERVATION
|
|
// ========================================================
|
|
|
|
write_byte(22'h000010, 8'h34);
|
|
write_byte(22'h000011, 8'h12);
|
|
|
|
write_byte(22'h000010, 8'hAA);
|
|
|
|
read_byte(22'h000010, 8'hAA);
|
|
read_byte(22'h000011, 8'h12);
|
|
|
|
write_byte(22'h000011, 8'hBB);
|
|
|
|
read_byte(22'h000010, 8'hAA);
|
|
read_byte(22'h000011, 8'hBB);
|
|
|
|
// ========================================================
|
|
// SIGNED INT8
|
|
// ========================================================
|
|
|
|
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);
|
|
|
|
// ========================================================
|
|
// SPARSE ADDRESSES
|
|
// ========================================================
|
|
|
|
write_byte(22'h000100, 8'h11);
|
|
write_byte(22'h000101, 8'h22);
|
|
|
|
write_byte(22'h001000, 8'h33);
|
|
write_byte(22'h001001, 8'h44);
|
|
|
|
write_byte(22'h003FFE, 8'h55);
|
|
write_byte(22'h003FFF, 8'h66);
|
|
|
|
read_byte(22'h000100, 8'h11);
|
|
read_byte(22'h000101, 8'h22);
|
|
|
|
read_byte(22'h001000, 8'h33);
|
|
read_byte(22'h001001, 8'h44);
|
|
|
|
read_byte(22'h003FFE, 8'h55);
|
|
read_byte(22'h003FFF, 8'h66);
|
|
|
|
// ========================================================
|
|
// STRESS TEST
|
|
// ========================================================
|
|
|
|
$display("");
|
|
$display("========================================");
|
|
$display("INT8 PSRAM STRESS TEST");
|
|
$display("2048 WRITE + READ BYTE TRANSACTIONS");
|
|
$display("========================================");
|
|
$display("");
|
|
|
|
for (i = 0; i < 2048; i = i + 1) begin
|
|
|
|
test_addr =
|
|
((i * 7919) ^ (i << 5)) & 16'h7FFF;
|
|
|
|
test_data =
|
|
((i * 1237) ^ 8'hA5);
|
|
|
|
write_byte(
|
|
test_addr,
|
|
test_data
|
|
);
|
|
|
|
read_byte(
|
|
test_addr,
|
|
test_data
|
|
);
|
|
|
|
if ((i % 128) == 0)
|
|
$display(
|
|
"STRESS %0d / 2048 PASS",
|
|
i
|
|
);
|
|
|
|
end
|
|
|
|
// ========================================================
|
|
// Final result
|
|
// ========================================================
|
|
|
|
$display("");
|
|
$display("========================================");
|
|
$display("INT8 + PSRAM INTEGRATION TEST PASSED");
|
|
$display("BYTE ADDRESSING : PASS");
|
|
$display("LB# / UB# : PASS");
|
|
$display("INT8 PACKING : PASS");
|
|
$display("BYTE PRESERVATION : PASS");
|
|
$display("SIGNED INT8 : PASS");
|
|
$display("SPARSE ADDRESSES : PASS");
|
|
$display("2048 STRESS : PASS");
|
|
$display("========================================");
|
|
$display("");
|
|
|
|
$finish;
|
|
|
|
end
|
|
|
|
endmodule |