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
190 lines
5.6 KiB
Verilog
190 lines
5.6 KiB
Verilog
`timescale 1ns/1ps
|
|
|
|
// ================================================================
|
|
// MEM_ARBITER
|
|
//
|
|
// Arbitrates a single shared byte-level memory master port (feeding
|
|
// a shared int8_memory_access -> memory_interface -> psram_controller
|
|
// chain) between three byte-level requesters:
|
|
//
|
|
// Port A: spi_engine.v (WRITE_RAM / READ_RAM opcodes)
|
|
// Port B: neuron_memory.v (its own X/W/bias reads during a run)
|
|
// Port C: layer_sequencer.v (Phase 5: descriptor reads + output
|
|
// buffer writes between layers)
|
|
//
|
|
// Fixed priority B > C > A when more than one requests on the same
|
|
// idle cycle (an in-progress inference is treated as more
|
|
// time-critical than the sequencer's own bookkeeping, which in turn
|
|
// is treated as more time-critical than a newly-arriving manual SPI
|
|
// RAM access). In normal operation B and C are temporally disjoint
|
|
// anyway -- neuron_memory only requests while running, and
|
|
// layer_sequencer only requests in the gaps between layers -- so
|
|
// this priority mostly matters for the edge case of a manual
|
|
// WRITE_RAM/READ_RAM arriving while a Phase 5 run is in progress.
|
|
// Once a port is granted, the arbiter holds ownership until that
|
|
// single transaction's m_ready pulse, then releases -- all three
|
|
// masters already issue `req` as a clean one-cycle pulse (matching
|
|
// int8_memory_access's own contract), so a simple grant-and-forward
|
|
// design is sufficient; no request queuing/pipelining is needed.
|
|
// ================================================================
|
|
|
|
module mem_arbiter #(
|
|
parameter ADDR_WIDTH = 23
|
|
)(
|
|
input wire clk,
|
|
input wire rst,
|
|
|
|
// ------------------------------------------------------------
|
|
// Port A - spi_engine
|
|
// ------------------------------------------------------------
|
|
|
|
input wire a_req,
|
|
input wire a_wr,
|
|
input wire [ADDR_WIDTH-1:0] a_addr,
|
|
input wire signed [7:0] a_wdata,
|
|
output reg signed [7:0] a_rdata,
|
|
output reg a_ready,
|
|
|
|
// ------------------------------------------------------------
|
|
// Port B - neuron_memory
|
|
// ------------------------------------------------------------
|
|
|
|
input wire b_req,
|
|
input wire b_wr,
|
|
input wire [ADDR_WIDTH-1:0] b_addr,
|
|
input wire signed [7:0] b_wdata,
|
|
output reg signed [7:0] b_rdata,
|
|
output reg b_ready,
|
|
|
|
// ------------------------------------------------------------
|
|
// Port C - layer_sequencer
|
|
// ------------------------------------------------------------
|
|
|
|
input wire c_req,
|
|
input wire c_wr,
|
|
input wire [ADDR_WIDTH-1:0] c_addr,
|
|
input wire signed [7:0] c_wdata,
|
|
output reg signed [7:0] c_rdata,
|
|
output reg c_ready,
|
|
|
|
// ------------------------------------------------------------
|
|
// Shared master port
|
|
// ------------------------------------------------------------
|
|
|
|
output reg m_req,
|
|
output reg m_wr,
|
|
output reg [ADDR_WIDTH-1:0] m_addr,
|
|
output reg signed [7:0] m_wdata,
|
|
|
|
input wire signed [7:0] m_rdata,
|
|
input wire m_ready
|
|
);
|
|
|
|
localparam SEL_NONE = 2'd0;
|
|
localparam SEL_A = 2'd1;
|
|
localparam SEL_B = 2'd2;
|
|
localparam SEL_C = 2'd3;
|
|
|
|
reg [1:0] owner;
|
|
|
|
always @(posedge clk) begin
|
|
|
|
if (rst) begin
|
|
|
|
owner <= SEL_NONE;
|
|
|
|
m_req <= 1'b0;
|
|
m_wr <= 1'b0;
|
|
m_addr <= {ADDR_WIDTH{1'b0}};
|
|
m_wdata <= 8'sd0;
|
|
|
|
a_rdata <= 8'sd0;
|
|
a_ready <= 1'b0;
|
|
|
|
b_rdata <= 8'sd0;
|
|
b_ready <= 1'b0;
|
|
|
|
c_rdata <= 8'sd0;
|
|
c_ready <= 1'b0;
|
|
|
|
end else begin
|
|
|
|
m_req <= 1'b0;
|
|
a_ready <= 1'b0;
|
|
b_ready <= 1'b0;
|
|
c_ready <= 1'b0;
|
|
|
|
case (owner)
|
|
|
|
SEL_NONE: begin
|
|
|
|
if (b_req) begin
|
|
|
|
owner <= SEL_B;
|
|
m_req <= 1'b1;
|
|
m_wr <= b_wr;
|
|
m_addr <= b_addr;
|
|
m_wdata <= b_wdata;
|
|
|
|
end else if (c_req) begin
|
|
|
|
owner <= SEL_C;
|
|
m_req <= 1'b1;
|
|
m_wr <= c_wr;
|
|
m_addr <= c_addr;
|
|
m_wdata <= c_wdata;
|
|
|
|
end else if (a_req) begin
|
|
|
|
owner <= SEL_A;
|
|
m_req <= 1'b1;
|
|
m_wr <= a_wr;
|
|
m_addr <= a_addr;
|
|
m_wdata <= a_wdata;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
SEL_A: begin
|
|
|
|
if (m_ready) begin
|
|
a_rdata <= m_rdata;
|
|
a_ready <= 1'b1;
|
|
owner <= SEL_NONE;
|
|
end
|
|
|
|
end
|
|
|
|
SEL_B: begin
|
|
|
|
if (m_ready) begin
|
|
b_rdata <= m_rdata;
|
|
b_ready <= 1'b1;
|
|
owner <= SEL_NONE;
|
|
end
|
|
|
|
end
|
|
|
|
SEL_C: begin
|
|
|
|
if (m_ready) begin
|
|
c_rdata <= m_rdata;
|
|
c_ready <= 1'b1;
|
|
owner <= SEL_NONE;
|
|
end
|
|
|
|
end
|
|
|
|
default: begin
|
|
owner <= SEL_NONE;
|
|
end
|
|
|
|
endcase
|
|
|
|
end
|
|
|
|
end
|
|
|
|
endmodule
|