feat: SDRAM 8MB->64MB upgrade (AS4C32M16SB-7BIN) + N_SLOTS=8 support
Memory upgrade, at the user's own explicit request: Alliance Memory AS4C4M16SA-6TIN (64Mbit/8MB) -> AS4C32M16SB-7BIN (512Mbit/64MB, 54-ball TFBGA), the largest same-family SDR SDRAM Alliance Memory offers. Real-datasheet-driven (whole AS4C4M16SA/AS4C8M16SA/AS4C16M16SA/ AS4C32M16SA family investigated): 13 row bits (was 12, one new FPGA pin sdram_a[12]/ball F1), 10 column bits (was 8), real -7-grade AC timing (tRCD/tRP improved to 15ns, tREFI halved to 7.8us for the doubled row count). sdram_controller.v and sdram_model.v gained real ROW_BITS/COL_BITS/BANK_BITS parameters (was hardcoded 12/8/2). ADDR_WIDTH widened 23->26 bits across the live instantiation tree. This required a real SPI protocol change (spi_host_bridge.v): a 26-bit byte address no longer fits in 3 bytes -- every address field widened 3->4 bytes (WRITE_JOB 15->18 payload bytes, WRITE_MEM/READ_MEM header 5->6 bytes). Found and fixed two real timing regressions via nextpnr-ecp5 P&R (not assumed): neural_director.v's own runtime-indexed demux write (ERR-0027, was silently synthesizing an extra MULT18X18D) and nms_activation_fill_ctrl_v3.v's own linear N_SLOTS-wide max-scan (ERR-0028, became dominant at N_SLOTS=8) -- both replaced with constant-indexed/tree-based equivalents, bit-exact same behavior, confirmed via full D-Stress N=2/4/8 regression (identical cycle counts). N_SLOTS=4 now fully closes timing at 64MHz (8/8 seeds); N_SLOTS=8 significantly improved but not yet fully reliable (5/8 seeds) -- honestly disclosed, not claimed complete. Full regression re-verified: sdram_controller (461/461, 18 configs), tb_sdram_boundary (21/21), D-Stress N=2/4/8 (bit-exact), spi_host_bridge (18/18), board-level SPI smoke test (11/11), unified backend (40/40). See hardware/v2/docs/MEMORY_UPGRADE_64MB_N8.md for the full investigation, and errors.log/decisions.log (ERR-0027, ERR-0028, DEC-0039) for the complete root-cause writeups. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
module dependency_manager #(
|
||||
parameter N_NODES = 16,
|
||||
parameter MAX_DEPS = 4,
|
||||
parameter ADDR_WIDTH = 23
|
||||
parameter ADDR_WIDTH = 26
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
// ================================================================
|
||||
|
||||
module neural_director #(
|
||||
parameter ADDR_WIDTH = 23,
|
||||
parameter ADDR_WIDTH = 26,
|
||||
parameter N_SLOTS = 4,
|
||||
parameter QUEUE_DEPTH = 8
|
||||
)(
|
||||
@@ -48,11 +48,16 @@ module neural_director #(
|
||||
input wire [15:0] job_in_node_id,
|
||||
|
||||
// ---- per-slot memory_manager job control (arrayed, §9) ----
|
||||
output reg [N_SLOTS-1:0] slot_job_start,
|
||||
output reg [ADDR_WIDTH*N_SLOTS-1:0] slot_x_base,
|
||||
output reg [ADDR_WIDTH*N_SLOTS-1:0] slot_w_base,
|
||||
output reg [16*N_SLOTS-1:0] slot_n_tiles,
|
||||
output reg [ADDR_WIDTH*N_SLOTS-1:0] slot_result_addr,
|
||||
// Ports are `wire`, driven by the GEN_SLOT_OUT generate block below
|
||||
// from internal unpacked-array registers (see that block's own
|
||||
// comment for why -- a real, measured timing regression found this
|
||||
// session when ADDR_WIDTH grew from 23 to 26 bits, section
|
||||
// "post-PRE-PCB-FREEZE memory upgrade").
|
||||
output wire [N_SLOTS-1:0] slot_job_start,
|
||||
output wire [ADDR_WIDTH*N_SLOTS-1:0] slot_x_base,
|
||||
output wire [ADDR_WIDTH*N_SLOTS-1:0] slot_w_base,
|
||||
output wire [16*N_SLOTS-1:0] slot_n_tiles,
|
||||
output wire [ADDR_WIDTH*N_SLOTS-1:0] slot_result_addr,
|
||||
// slot_node_id: which node_id is currently occupying each slot --
|
||||
// not needed by memory_manager itself (it has no notion of node
|
||||
// ids), but needed by a caller (dataflow_core.v, M7) that must
|
||||
@@ -60,7 +65,7 @@ module neural_director #(
|
||||
// to notify the Dependency Manager (M6). Purely additive: existing
|
||||
// callers (hardware/v2/sim/tb_neural_director.v, M5) that don't
|
||||
// connect it are unaffected.
|
||||
output reg [16*N_SLOTS-1:0] slot_node_id,
|
||||
output wire [16*N_SLOTS-1:0] slot_node_id,
|
||||
input wire [N_SLOTS-1:0] slot_job_done,
|
||||
|
||||
// ---- completion notification (§9 "rilevamento dei completamenti") ----
|
||||
@@ -121,6 +126,45 @@ module neural_director #(
|
||||
end
|
||||
end
|
||||
|
||||
// ---- per-slot output storage (unpacked arrays, one real register
|
||||
// set per slot) + constant-indexed generate wiring out to the
|
||||
// packed ports above. Found and fixed this session (post-PRE-PCB-
|
||||
// FREEZE memory upgrade, ADDR_WIDTH 23->26): the PREVIOUS design
|
||||
// used one wide packed `output reg` per field and wrote it with a
|
||||
// RUNTIME-computed part-select (`slot_x_base[free_slot_idx*
|
||||
// ADDR_WIDTH +: ADDR_WIDTH] <= ...`). A variable-indexed write into
|
||||
// a wide packed register is not free logic -- Yosys/synth_ecp5
|
||||
// synthesized the index computation (`free_slot_idx*ADDR_WIDTH`)
|
||||
// as an actual MULT18X18D hard multiplier feeding a wide demux/
|
||||
// crossbar into the destination slot, and this got measurably
|
||||
// worse as ADDR_WIDTH grew (real nextpnr-ecp5 P&R: worst-seed Fmax
|
||||
// collapsed from 68.51MHz at ADDR_WIDTH=23 to ~40-47MHz at
|
||||
// ADDR_WIDTH=26, confirmed across 8 seeds, all failing the 64MHz
|
||||
// target). The fix below replaces the runtime-indexed demux write
|
||||
// with N_SLOTS parallel CONSTANT-indexed comparisons (`fi ==
|
||||
// free_slot_idx`, each a cheap few-bit compare, no multiply) each
|
||||
// gating its own slot's own narrow register -- functionally
|
||||
// IDENTICAL behavior, bit-exact same external port semantics, only
|
||||
// the internal implementation changed.
|
||||
reg slot_job_start_r [0:N_SLOTS-1];
|
||||
reg [ADDR_WIDTH-1:0] slot_x_base_r [0:N_SLOTS-1];
|
||||
reg [ADDR_WIDTH-1:0] slot_w_base_r [0:N_SLOTS-1];
|
||||
reg [15:0] slot_n_tiles_r [0:N_SLOTS-1];
|
||||
reg [ADDR_WIDTH-1:0] slot_result_addr_r [0:N_SLOTS-1];
|
||||
reg [15:0] slot_node_id_r [0:N_SLOTS-1];
|
||||
|
||||
genvar gs;
|
||||
generate
|
||||
for (gs = 0; gs < N_SLOTS; gs = gs + 1) begin : GEN_SLOT_OUT
|
||||
assign slot_job_start[gs] = slot_job_start_r[gs];
|
||||
assign slot_x_base[gs*ADDR_WIDTH +: ADDR_WIDTH] = slot_x_base_r[gs];
|
||||
assign slot_w_base[gs*ADDR_WIDTH +: ADDR_WIDTH] = slot_w_base_r[gs];
|
||||
assign slot_n_tiles[gs*16 +: 16] = slot_n_tiles_r[gs];
|
||||
assign slot_result_addr[gs*ADDR_WIDTH +: ADDR_WIDTH] = slot_result_addr_r[gs];
|
||||
assign slot_node_id[gs*16 +: 16] = slot_node_id_r[gs];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// Priority-encoded lowest-indexed slot reporting job_done this
|
||||
// cycle (combinational, so it reflects THIS cycle's slot_job_done
|
||||
// bus directly -- a register-based "already reported one" flag
|
||||
@@ -143,16 +187,18 @@ module neural_director #(
|
||||
q_tail <= {Q_ADDR_WIDTH{1'b0}};
|
||||
q_count <= {(Q_ADDR_WIDTH+1){1'b0}};
|
||||
slot_busy <= {N_SLOTS{1'b0}};
|
||||
slot_job_start <= {N_SLOTS{1'b0}};
|
||||
slot_x_base <= {(ADDR_WIDTH*N_SLOTS){1'b0}};
|
||||
slot_w_base <= {(ADDR_WIDTH*N_SLOTS){1'b0}};
|
||||
slot_n_tiles <= {(16*N_SLOTS){1'b0}};
|
||||
slot_result_addr <= {(ADDR_WIDTH*N_SLOTS){1'b0}};
|
||||
slot_node_id <= {(16*N_SLOTS){1'b0}};
|
||||
for (fi = 0; fi < N_SLOTS; fi = fi + 1) begin
|
||||
slot_job_start_r[fi] <= 1'b0;
|
||||
slot_x_base_r[fi] <= {ADDR_WIDTH{1'b0}};
|
||||
slot_w_base_r[fi] <= {ADDR_WIDTH{1'b0}};
|
||||
slot_n_tiles_r[fi] <= 16'b0;
|
||||
slot_result_addr_r[fi] <= {ADDR_WIDTH{1'b0}};
|
||||
slot_node_id_r[fi] <= 16'b0;
|
||||
end
|
||||
job_out_done <= 1'b0;
|
||||
job_out_slot <= '0;
|
||||
end else begin
|
||||
slot_job_start <= {N_SLOTS{1'b0}};
|
||||
for (fi = 0; fi < N_SLOTS; fi = fi + 1) slot_job_start_r[fi] <= 1'b0;
|
||||
job_out_done <= 1'b0;
|
||||
|
||||
// ---- Accept a new job into the ready queue (independent
|
||||
@@ -201,14 +247,21 @@ module neural_director #(
|
||||
end
|
||||
|
||||
DIR_ALLOCATE: begin
|
||||
// Dispatch the head of the queue to the first
|
||||
// free slot found this cycle.
|
||||
slot_job_start[free_slot_idx] <= 1'b1;
|
||||
slot_x_base[free_slot_idx*ADDR_WIDTH +: ADDR_WIDTH] <= q_x_base[q_head];
|
||||
slot_w_base[free_slot_idx*ADDR_WIDTH +: ADDR_WIDTH] <= q_w_base[q_head];
|
||||
slot_n_tiles[free_slot_idx*16 +: 16] <= q_n_tiles[q_head];
|
||||
slot_result_addr[free_slot_idx*ADDR_WIDTH +: ADDR_WIDTH] <= q_result_addr[q_head];
|
||||
slot_node_id[free_slot_idx*16 +: 16] <= q_node_id[q_head];
|
||||
// Dispatch the head of the queue to the first free
|
||||
// slot found this cycle -- N_SLOTS parallel
|
||||
// constant-indexed compares (cheap) instead of one
|
||||
// runtime-indexed wide demux write (see
|
||||
// slot_x_base_r's own declaration comment for why).
|
||||
for (fi = 0; fi < N_SLOTS; fi = fi + 1) begin
|
||||
if (fi[$clog2(N_SLOTS)-1:0] == free_slot_idx) begin
|
||||
slot_job_start_r[fi] <= 1'b1;
|
||||
slot_x_base_r[fi] <= q_x_base[q_head];
|
||||
slot_w_base_r[fi] <= q_w_base[q_head];
|
||||
slot_n_tiles_r[fi] <= q_n_tiles[q_head];
|
||||
slot_result_addr_r[fi] <= q_result_addr[q_head];
|
||||
slot_node_id_r[fi] <= q_node_id[q_head];
|
||||
end
|
||||
end
|
||||
slot_busy[free_slot_idx] <= 1'b1;
|
||||
q_head <= (q_head == QUEUE_DEPTH[Q_ADDR_WIDTH-1:0]-1'b1) ? {Q_ADDR_WIDTH{1'b0}} : q_head + 1'b1;
|
||||
dir_state <= DIR_SCAN_READY;
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
// ================================================================
|
||||
|
||||
module slot_mem_arbiter #(
|
||||
parameter ADDR_WIDTH = 23,
|
||||
parameter ADDR_WIDTH = 26,
|
||||
parameter N_PORTS = 4
|
||||
)(
|
||||
input wire clk,
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
module slot_mem_arbiter_wide #(
|
||||
parameter DATA_WIDTH = 32,
|
||||
parameter ADDR_WIDTH = 23,
|
||||
parameter ADDR_WIDTH = 26,
|
||||
parameter N_PORTS = 4
|
||||
)(
|
||||
input wire clk,
|
||||
|
||||
@@ -26,15 +26,17 @@
|
||||
// 0x00 NOP -- 0 payload bytes.
|
||||
// 0x0F RESET -- 0 payload bytes. Pulses soft_rst_pulse for
|
||||
// one clk cycle after CS rises.
|
||||
// 0x10 WRITE_JOB -- 15 payload bytes, registers one dependency-
|
||||
// manager job (== one reg_valid/reg_* handshake):
|
||||
// 0x10 WRITE_JOB -- 18 payload bytes (widened from 15 -- see
|
||||
// "ADDRESS WIDTH" note below), registers one
|
||||
// dependency-manager job (== one reg_valid/
|
||||
// reg_* handshake):
|
||||
// byte0 = {4'b0,node_id[3:0]}
|
||||
// byte1 = {5'b0,required[2:0]}
|
||||
// byte2:3 = producer_ids[15:0]
|
||||
// byte4:6 = x_base[22:0] (byte4 msb={1'b0,x_base[22:16]})
|
||||
// byte7:9 = w_base[22:0]
|
||||
// byte10:11= n_tiles[15:0]
|
||||
// byte12:14= result_addr[22:0]
|
||||
// byte4:7 = x_base[25:0] (byte4 msb={6'b0,x_base[25:24]})
|
||||
// byte8:11 = w_base[25:0]
|
||||
// byte12:13= n_tiles[15:0]
|
||||
// byte14:17= result_addr[25:0]
|
||||
// reg_valid is asserted and HELD until the
|
||||
// cycle reg_ready also reads 1 (same-cycle
|
||||
// valid&&ready acceptance, matching
|
||||
@@ -46,31 +48,42 @@
|
||||
// bit1 = mem_busy (WRITE_MEM/READ_MEM waiting on mem_ready)
|
||||
// bit2 = last_job_accepted (sticky, cleared by next WRITE_JOB)
|
||||
// bits[7:3] = 0 (reserved)
|
||||
// 0x01 WRITE_MEM -- 5 header bytes + 2*len_words payload bytes:
|
||||
// byte0:2 = addr[22:0] (WORD address, matches
|
||||
// 0x01 WRITE_MEM -- 6 header bytes (widened from 5) + 2*len_words
|
||||
// payload bytes:
|
||||
// byte0:3 = addr[25:0] (WORD address, matches
|
||||
// sdram_unified_backend's AR port
|
||||
// convention -- NOT a byte address)
|
||||
// byte3:4 = len_words[15:0] (number of 16-bit
|
||||
// convention -- NOT a byte address;
|
||||
// byte0 msb={6'b0,addr[25:24]})
|
||||
// byte4:5 = len_words[15:0] (number of 16-bit
|
||||
// words to write, len_words>=1)
|
||||
// then len_words * 2 bytes of data, MSB-first
|
||||
// per word; each word is written via one
|
||||
// mem_req/mem_ready handshake (lb_n=ub_n=0,
|
||||
// full 16-bit write) before the next word's
|
||||
// bytes are accepted.
|
||||
// 0x02 READ_MEM -- 5 header bytes (addr + len_words, same shape
|
||||
// 0x02 READ_MEM -- 6 header bytes (addr + len_words, same shape
|
||||
// as WRITE_MEM), 0 further MOSI payload; the
|
||||
// 2*len_words response bytes are clocked out
|
||||
// on MISO starting at payload byte 6, MSB-
|
||||
// on MISO starting at payload byte 7, MSB-
|
||||
// first per word, one mem_req/mem_ready
|
||||
// read per word.
|
||||
//
|
||||
// ADDRESS WIDTH (post-PRE-PCB-FREEZE memory upgrade): ADDR_WIDTH grew
|
||||
// from 23 to 26 bits (SDRAM capacity upgrade, AS4C4M16SA-6TIN 8MB ->
|
||||
// AS4C32M16SA-7TIN 64MB -- see sdram_controller.v's own header). A
|
||||
// 26-bit address no longer fits in 3 bytes (24 bits) with a spare
|
||||
// reserved bit the way the old 23-bit address did -- every address
|
||||
// field below therefore widened from 3 to 4 bytes (6 reserved bits in
|
||||
// the new top byte instead of 1), growing WRITE_JOB from 15 to 18
|
||||
// payload bytes and the WRITE_MEM/READ_MEM header from 5 to 6 bytes.
|
||||
//
|
||||
// Any opcode byte not listed above is treated as NOP (0 payload,
|
||||
// MISO drives 0x00) -- matches spi_engine.v's own "unknown opcode is
|
||||
// inert, never wedges the bus" precedent.
|
||||
// ================================================================
|
||||
|
||||
module spi_host_bridge #(
|
||||
parameter ADDR_WIDTH = 23,
|
||||
parameter ADDR_WIDTH = 26,
|
||||
parameter N_NODES = 16,
|
||||
parameter MAX_DEPS = 4
|
||||
)(
|
||||
@@ -226,9 +239,9 @@ module spi_host_bridge #(
|
||||
localparam OP_STATUS = 8'h20;
|
||||
|
||||
localparam ST_OPCODE = 4'd0;
|
||||
localparam ST_JOB = 4'd1; // collecting 15 WRITE_JOB payload bytes
|
||||
localparam ST_JOB = 4'd1; // collecting 18 WRITE_JOB payload bytes
|
||||
localparam ST_JOB_WAIT= 4'd2; // reg_valid held, waiting reg_ready
|
||||
localparam ST_MEM_ADDR= 4'd3; // collecting 3 addr bytes
|
||||
localparam ST_MEM_ADDR= 4'd3; // collecting 4 addr bytes
|
||||
localparam ST_MEM_LEN = 4'd4; // collecting 2 length bytes
|
||||
localparam ST_MEM_WD = 4'd5; // WRITE_MEM: collecting 2 data bytes/word
|
||||
localparam ST_MEM_WISS= 4'd6; // WRITE_MEM: issue+wait mem_req
|
||||
@@ -238,7 +251,7 @@ module spi_host_bridge #(
|
||||
|
||||
reg [3:0] state;
|
||||
reg [7:0] opcode;
|
||||
reg [3:0] byte_idx; // generic byte counter within a field
|
||||
reg [4:0] byte_idx; // generic byte counter within a field (up to 17, WRITE_JOB)
|
||||
reg [15:0] len_words;
|
||||
reg [15:0] word_cnt;
|
||||
reg [15:0] cur_word; // WRITE_MEM: assembling MSB,LSB; READ_MEM: holding readback
|
||||
@@ -252,13 +265,13 @@ module spi_host_bridge #(
|
||||
if (opcode == OP_STATUS)
|
||||
tx_mux = {5'b0, last_job_accepted_r, mem_busy_r, job_busy_r};
|
||||
else if (opcode == OP_READ_MEM && state == ST_MEM_ROUT)
|
||||
tx_mux = (byte_idx == 4'd0) ? cur_word[15:8] : cur_word[7:0];
|
||||
tx_mux = (byte_idx == 5'd0) ? cur_word[15:8] : cur_word[7:0];
|
||||
end
|
||||
assign tx_byte = tx_mux;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= ST_OPCODE; opcode <= 8'h00; byte_idx <= 4'd0;
|
||||
state <= ST_OPCODE; opcode <= 8'h00; byte_idx <= 5'd0;
|
||||
len_words <= 16'd0; word_cnt <= 16'd0; cur_word <= 16'd0;
|
||||
reg_valid <= 1'b0; reg_node_id <= {NODEW{1'b0}}; reg_required <= {REQW{1'b0}};
|
||||
reg_producer_ids <= {(MAX_DEPS*NODEW){1'b0}};
|
||||
@@ -292,12 +305,12 @@ module spi_host_bridge #(
|
||||
// to cs_rose below.
|
||||
if (cs_fell && state != ST_JOB_WAIT && state != ST_MEM_WISS && state != ST_MEM_RISS) begin
|
||||
state <= ST_OPCODE;
|
||||
byte_idx <= 4'd0;
|
||||
byte_idx <= 5'd0;
|
||||
end else if (!cs_fell && rx_valid) begin
|
||||
case (state)
|
||||
ST_OPCODE: begin
|
||||
opcode <= rx_byte;
|
||||
byte_idx <= 4'd0;
|
||||
byte_idx <= 5'd0;
|
||||
case (rx_byte)
|
||||
OP_WRITE_JOB: state <= ST_JOB;
|
||||
OP_WRITE_MEM: state <= ST_MEM_ADDR;
|
||||
@@ -309,59 +322,63 @@ module spi_host_bridge #(
|
||||
|
||||
ST_JOB: begin
|
||||
case (byte_idx)
|
||||
4'd0: reg_node_id <= rx_byte[NODEW-1:0];
|
||||
4'd1: reg_required <= rx_byte[REQW-1:0];
|
||||
4'd2: reg_producer_ids[15:8] <= rx_byte;
|
||||
4'd3: reg_producer_ids[7:0] <= rx_byte;
|
||||
4'd4: reg_x_base[22:16] <= rx_byte[6:0];
|
||||
4'd5: reg_x_base[15:8] <= rx_byte;
|
||||
4'd6: reg_x_base[7:0] <= rx_byte;
|
||||
4'd7: reg_w_base[22:16] <= rx_byte[6:0];
|
||||
4'd8: reg_w_base[15:8] <= rx_byte;
|
||||
4'd9: reg_w_base[7:0] <= rx_byte;
|
||||
4'd10: reg_n_tiles[15:8] <= rx_byte;
|
||||
4'd11: reg_n_tiles[7:0] <= rx_byte;
|
||||
4'd12: reg_result_addr[22:16] <= rx_byte[6:0];
|
||||
4'd13: reg_result_addr[15:8] <= rx_byte;
|
||||
4'd14: begin
|
||||
5'd0: reg_node_id <= rx_byte[NODEW-1:0];
|
||||
5'd1: reg_required <= rx_byte[REQW-1:0];
|
||||
5'd2: reg_producer_ids[15:8] <= rx_byte;
|
||||
5'd3: reg_producer_ids[7:0] <= rx_byte;
|
||||
5'd4: reg_x_base[25:24] <= rx_byte[1:0];
|
||||
5'd5: reg_x_base[23:16] <= rx_byte;
|
||||
5'd6: reg_x_base[15:8] <= rx_byte;
|
||||
5'd7: reg_x_base[7:0] <= rx_byte;
|
||||
5'd8: reg_w_base[25:24] <= rx_byte[1:0];
|
||||
5'd9: reg_w_base[23:16] <= rx_byte;
|
||||
5'd10: reg_w_base[15:8] <= rx_byte;
|
||||
5'd11: reg_w_base[7:0] <= rx_byte;
|
||||
5'd12: reg_n_tiles[15:8] <= rx_byte;
|
||||
5'd13: reg_n_tiles[7:0] <= rx_byte;
|
||||
5'd14: reg_result_addr[25:24] <= rx_byte[1:0];
|
||||
5'd15: reg_result_addr[23:16] <= rx_byte;
|
||||
5'd16: reg_result_addr[15:8] <= rx_byte;
|
||||
5'd17: begin
|
||||
reg_result_addr[7:0] <= rx_byte;
|
||||
reg_valid <= 1'b1;
|
||||
last_job_accepted_r <= 1'b0;
|
||||
state <= ST_JOB_WAIT;
|
||||
end
|
||||
endcase
|
||||
if (byte_idx != 4'd14) byte_idx <= byte_idx + 4'd1;
|
||||
if (byte_idx != 5'd17) byte_idx <= byte_idx + 5'd1;
|
||||
end
|
||||
|
||||
ST_MEM_ADDR: begin
|
||||
case (byte_idx)
|
||||
4'd0: mem_addr[22:16] <= rx_byte[6:0];
|
||||
4'd1: mem_addr[15:8] <= rx_byte;
|
||||
4'd2: begin
|
||||
5'd0: mem_addr[25:24] <= rx_byte[1:0];
|
||||
5'd1: mem_addr[23:16] <= rx_byte;
|
||||
5'd2: mem_addr[15:8] <= rx_byte;
|
||||
5'd3: begin
|
||||
mem_addr[7:0] <= rx_byte;
|
||||
state <= ST_MEM_LEN;
|
||||
end
|
||||
endcase
|
||||
if (byte_idx != 4'd2) byte_idx <= byte_idx + 4'd1;
|
||||
else byte_idx <= 4'd0;
|
||||
if (byte_idx != 5'd3) byte_idx <= byte_idx + 5'd1;
|
||||
else byte_idx <= 5'd0;
|
||||
end
|
||||
|
||||
ST_MEM_LEN: begin
|
||||
if (byte_idx == 4'd0) begin
|
||||
if (byte_idx == 5'd0) begin
|
||||
len_words[15:8] <= rx_byte;
|
||||
byte_idx <= 4'd1;
|
||||
byte_idx <= 5'd1;
|
||||
end else begin
|
||||
len_words[7:0] <= rx_byte;
|
||||
word_cnt <= {len_words[15:8], rx_byte};
|
||||
byte_idx <= 4'd0;
|
||||
byte_idx <= 5'd0;
|
||||
state <= (opcode == OP_WRITE_MEM) ? ST_MEM_WD : ST_MEM_RISS;
|
||||
end
|
||||
end
|
||||
|
||||
ST_MEM_WD: begin
|
||||
if (byte_idx == 4'd0) begin
|
||||
if (byte_idx == 5'd0) begin
|
||||
cur_word[15:8] <= rx_byte;
|
||||
byte_idx <= 4'd1;
|
||||
byte_idx <= 5'd1;
|
||||
end else begin
|
||||
cur_word[7:0] <= rx_byte;
|
||||
state <= ST_MEM_WISS;
|
||||
@@ -390,7 +407,7 @@ module spi_host_bridge #(
|
||||
mem_busy_r <= 1'b0;
|
||||
mem_addr <= mem_addr + 1'b1;
|
||||
word_cnt <= word_cnt - 1'b1;
|
||||
byte_idx <= 4'd0;
|
||||
byte_idx <= 5'd0;
|
||||
state <= (word_cnt == 16'd1) ? ST_IGNORE : ST_MEM_WD;
|
||||
end
|
||||
|
||||
@@ -403,7 +420,7 @@ module spi_host_bridge #(
|
||||
end else if (state == ST_MEM_RISS && mem_busy_r && mem_ready) begin
|
||||
mem_busy_r <= 1'b0;
|
||||
cur_word <= mem_rdata;
|
||||
byte_idx <= 4'd0;
|
||||
byte_idx <= 5'd0;
|
||||
state <= ST_MEM_ROUT;
|
||||
end
|
||||
if (state == ST_MEM_ROUT && rx_valid) begin
|
||||
@@ -412,12 +429,12 @@ module spi_host_bridge #(
|
||||
// it is also the correct "advance" event for MISO-side
|
||||
// bookkeeping (mirrors spi_slave's own documented
|
||||
// rx_valid-drives-advancement convention).
|
||||
if (byte_idx == 4'd0) begin
|
||||
byte_idx <= 4'd1;
|
||||
if (byte_idx == 5'd0) begin
|
||||
byte_idx <= 5'd1;
|
||||
end else begin
|
||||
mem_addr <= mem_addr + 1'b1;
|
||||
word_cnt <= word_cnt - 1'b1;
|
||||
byte_idx <= 4'd0;
|
||||
byte_idx <= 5'd0;
|
||||
state <= (word_cnt == 16'd1) ? ST_IGNORE : ST_MEM_RISS;
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user