404 lines
11 KiB
Verilog
404 lines
11 KiB
Verilog
`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 |