working with memory

This commit is contained in:
2026-09-02 13:09:20 +02:00
parent d4ae2417a4
commit 16769eaa4b
40 changed files with 1263404 additions and 3560 deletions
+404
View File
@@ -0,0 +1,404 @@
`timescale 1ns/1ps
module neuron_memory #(
parameter ADDR_WIDTH = 22,
parameter DATA_WIDTH = 8,
parameter N_INPUTS = 32,
parameter PARALLEL = 8,
parameter ACC_WIDTH = 32
)(
input wire clk,
input wire rst,
input wire start,
// ------------------------------------------------------------
// Memory interface
//
// BYTE-ADDRESS / INT8 interface
// ------------------------------------------------------------
output wire mem_req,
output wire mem_wr,
output wire [ADDR_WIDTH-1:0] mem_addr,
output wire signed [7:0] mem_wdata,
input wire signed [7:0] mem_rdata,
input wire mem_ready,
// ------------------------------------------------------------
// Network memory layout
// ------------------------------------------------------------
input wire [ADDR_WIDTH-1:0] x_base,
input wire [ADDR_WIDTH-1:0] w_base,
input wire [ADDR_WIDTH-1:0] bias_addr,
// ------------------------------------------------------------
// Result
// ------------------------------------------------------------
output reg signed [7:0] y,
output reg busy,
output reg done
);
// ============================================================
// STATES
// ============================================================
localparam STATE_IDLE = 4'd0;
localparam STATE_READ_X = 4'd1;
localparam STATE_READ_W = 4'd2;
localparam STATE_READ_BIAS = 4'd3;
localparam STATE_START_N = 4'd4;
localparam STATE_WAIT_N = 4'd5;
reg [3:0] state;
reg [$clog2(N_INPUTS+1)-1:0] index;
// ============================================================
// LOCAL MEMORY ARRAYS
// ============================================================
reg signed [7:0] x_mem [0:N_INPUTS-1];
reg signed [7:0] w_mem [0:N_INPUTS-1];
reg signed [7:0] bias_reg;
// ============================================================
// NEURON BUS
// ============================================================
wire signed [DATA_WIDTH*N_INPUTS-1:0] x_bus;
wire signed [DATA_WIDTH*N_INPUTS-1:0] w_bus;
genvar i;
generate
for (i = 0; i < N_INPUTS; i = i + 1) begin : GEN_BUS
assign x_bus[i*DATA_WIDTH +: DATA_WIDTH] = x_mem[i];
assign w_bus[i*DATA_WIDTH +: DATA_WIDTH] = w_mem[i];
end
endgenerate
// ============================================================
// INT8 MEMORY ACCESS
//
// This converts BYTE addresses into 16-bit word accesses.
//
// IMPORTANT:
// The memory side of this block is connected to the EXTERNAL
// memory_interface through the neuron_memory ports.
//
// It must NOT be connected directly to the PSRAM controller.
// ============================================================
reg access_req;
reg access_wr;
reg [ADDR_WIDTH-1:0] access_addr;
reg signed [7:0] access_wdata;
wire signed [7:0] access_rdata;
wire access_ready;
wire access_mem_req;
wire access_mem_wr;
wire [ADDR_WIDTH-1:0] access_mem_addr;
wire [15:0] access_mem_wdata;
wire access_mem_lb_n;
wire access_mem_ub_n;
// ------------------------------------------------------------
// Return data from the external memory interface.
//
// memory_interface returns a 16-bit word, while neuron_memory
// exposes only the requested INT8 byte.
//
// int8_memory_access expects the complete 16-bit word.
// ------------------------------------------------------------
wire [15:0] access_mem_rdata;
assign access_mem_rdata =
access_addr[0]
? {mem_rdata, 8'h00}
: {8'h00, mem_rdata};
// ------------------------------------------------------------
// IMPORTANT:
//
// mem_ready comes from the EXTERNAL memory_interface.
// mem_rdata comes from the EXTERNAL memory_interface.
//
// This fixes the previous deadlock where int8_memory_access
// was waiting for the PSRAM controller's mem_ready directly.
// ------------------------------------------------------------
int8_memory_access #(
.ADDR_WIDTH(ADDR_WIDTH)
) u_mem (
.clk (clk),
.rst (rst),
.req (access_req),
.wr (access_wr),
.addr (access_addr),
.wdata (access_wdata),
.rdata (access_rdata),
.ready (access_ready),
.mem_req (access_mem_req),
.mem_wr (access_mem_wr),
.mem_addr (access_mem_addr),
.mem_wdata (access_mem_wdata),
.mem_lb_n (access_mem_lb_n),
.mem_ub_n (access_mem_ub_n),
.mem_rdata (access_mem_rdata),
.mem_ready (mem_ready)
);
// ============================================================
// EXTERNAL MEMORY INTERFACE
//
// The external interface expects the INT8-level signals.
// The testbench converts these into its 16-bit bus.
//
// IMPORTANT:
// access_mem_addr is already a WORD address.
// However, the external neuron_memory interface is defined
// as a BYTE address.
//
// Therefore expose the original byte address here.
// ============================================================
assign mem_req = access_mem_req;
assign mem_wr = access_mem_wr;
assign mem_addr =
access_addr;
assign mem_wdata =
access_wdata;
// ============================================================
// NEURON
// ============================================================
reg neuron_start;
wire signed [7:0] neuron_y;
wire neuron_busy;
wire neuron_done;
neuron_parallel #(
.DATA_WIDTH(DATA_WIDTH),
.N_INPUTS(N_INPUTS),
.PARALLEL(PARALLEL),
.ACC_WIDTH(ACC_WIDTH)
) u_neuron (
.clk(clk),
.rst(rst),
.start(neuron_start),
.x_bus(x_bus),
.w_bus(w_bus),
.bias(bias_reg),
.y(neuron_y),
.busy(neuron_busy),
.done(neuron_done)
);
// ============================================================
// CONTROLLER
// ============================================================
always @(posedge clk) begin
if (rst) begin
state <= STATE_IDLE;
index <= 0;
bias_reg <= 0;
access_req <= 1'b0;
access_wr <= 1'b0;
access_addr <= 0;
access_wdata <= 0;
neuron_start <= 1'b0;
y <= 0;
busy <= 1'b0;
done <= 1'b0;
end else begin
// ----------------------------------------------------
// Default pulse signals
// ----------------------------------------------------
access_req <= 1'b0;
neuron_start <= 1'b0;
done <= 1'b0;
case (state)
// =================================================
// IDLE
// =================================================
STATE_IDLE: begin
busy <= 1'b0;
if (start) begin
busy <= 1'b1;
index <= 0;
// First X byte
access_addr <= x_base;
access_wr <= 1'b0;
access_req <= 1'b1;
state <= STATE_READ_X;
end
end
// =================================================
// READ X
// =================================================
STATE_READ_X: begin
if (access_ready) begin
x_mem[index] <= access_rdata;
if (index == N_INPUTS-1) begin
index <= 0;
access_addr <= w_base;
access_wr <= 1'b0;
access_req <= 1'b1;
state <= STATE_READ_W;
end else begin
index <= index + 1'b1;
access_addr <= x_base + index + 1'b1;
access_req <= 1'b1;
end
end
end
// =================================================
// READ W
// =================================================
STATE_READ_W: begin
if (access_ready) begin
w_mem[index] <= access_rdata;
if (index == N_INPUTS-1) begin
access_addr <= bias_addr;
access_wr <= 1'b0;
access_req <= 1'b1;
state <= STATE_READ_BIAS;
end else begin
index <= index + 1'b1;
access_addr <= w_base + index + 1'b1;
access_req <= 1'b1;
end
end
end
// =================================================
// READ BIAS
// =================================================
STATE_READ_BIAS: begin
if (access_ready) begin
bias_reg <= access_rdata;
state <= STATE_START_N;
end
end
// =================================================
// START NEURON
// =================================================
STATE_START_N: begin
neuron_start <= 1'b1;
state <= STATE_WAIT_N;
end
// =================================================
// WAIT NEURON
// =================================================
STATE_WAIT_N: begin
if (neuron_done) begin
y <= neuron_y;
busy <= 1'b0;
done <= 1'b1;
state <= STATE_IDLE;
end
end
// =================================================
// DEFAULT
// =================================================
default: begin
state <= STATE_IDLE;
busy <= 1'b0;
end
endcase
end
end
endmodule