New sdram_slot_arbiter2.v + tb_np_director_n2_system.v: real
neural_director_packed.v dispatching to 2 real packed_slot.v
instances sharing one real SDRAM controller. Jobs submitted one at a
time through the Director's own producer interface -- the Director's
own scheduling decisions determine slot assignment here, unlike every
prior V3 test.
Bug 1 (real, structural): the arbiter's first design registered its
grant one cycle late; layer_prefetch_ctrl.v's ctrl_req is a one-shot
pulse with no retry (every prior use wired it directly to a
controller, never behind arbitration), so a slot's first request
could be silently lost, hanging it forever. Fixed with a new
S_MEMWAIT state in packed_slot.v (wait for a combinational mem_grant
before ever pulsing layer_prefetch_ctrl's start) and a combinational-
first grant in the arbiter.
Bug 2 (testbench): node_id used a stray bit-slice (li[15:8]) instead
of a real multiply, making every layer produce the same node_ids and
silently checking results against the wrong layer's golden value.
Fixed.
Result: 12/12 PASS, 0 errors, real concurrent execution across both
slots (slot 0: positions {0,1,4,5,8,9}, slot 1: {2,3,6,7,10,11}).
Also noted (user correction): the SDR SDRAM controller used
throughout this memory path is a declared placeholder -- the real
target is DDR3 on a custom XC7A100T board, not yet built.
Full writeup in hardware/v2/logs/experiments.log EXP-0066.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
116 lines
5.0 KiB
Verilog
116 lines
5.0 KiB
Verilog
`timescale 1ns/1ps
|
|
|
|
// ============================================================
|
|
// V3 -- 2-way arbiter between packed_slot.v's own weight-fetch ctrl
|
|
// port and ONE real, shared sdram_controller.v.
|
|
//
|
|
// Grants LOCK for the whole duration of a slot's mem_active (its
|
|
// entire multi-burst layer fetch), not per-transaction -- a slot's
|
|
// own layer_prefetch_ctrl.v issues MANY back-to-back ctrl_req bursts
|
|
// per fetch, and interleaving those with the OTHER slot's bursts
|
|
// would corrupt both (neither is designed to have its own multi-burst
|
|
// sequence interrupted mid-flight). First-active-wins priority; the
|
|
// other slot's ctrl_ready is held at 0 (never pulses) while not
|
|
// granted, so its own req/ready FSM simply waits, harmlessly, exactly
|
|
// like it already does for ordinary controller busy cycles.
|
|
// ============================================================
|
|
module sdram_slot_arbiter2 #(
|
|
parameter ADDR_WIDTH = 25,
|
|
parameter BURST_LEN = 8
|
|
)(
|
|
input wire clk,
|
|
input wire rst,
|
|
|
|
input wire slot0_active,
|
|
output wire slot0_grant,
|
|
input wire slot0_req,
|
|
input wire slot0_wr,
|
|
input wire [ADDR_WIDTH-1:0] slot0_addr,
|
|
input wire [16*BURST_LEN-1:0] slot0_wdata,
|
|
input wire [2*BURST_LEN-1:0] slot0_wmask,
|
|
output wire [16*BURST_LEN-1:0] slot0_rdata,
|
|
output wire slot0_ready,
|
|
output wire slot0_busy,
|
|
|
|
input wire slot1_active,
|
|
output wire slot1_grant,
|
|
input wire slot1_req,
|
|
input wire slot1_wr,
|
|
input wire [ADDR_WIDTH-1:0] slot1_addr,
|
|
input wire [16*BURST_LEN-1:0] slot1_wdata,
|
|
input wire [2*BURST_LEN-1:0] slot1_wmask,
|
|
output wire [16*BURST_LEN-1:0] slot1_rdata,
|
|
output wire slot1_ready,
|
|
output wire slot1_busy,
|
|
|
|
output wire ctrl_req,
|
|
output wire ctrl_wr,
|
|
output wire [ADDR_WIDTH-1:0] ctrl_addr,
|
|
output wire [16*BURST_LEN-1:0] ctrl_wdata,
|
|
output wire [2*BURST_LEN-1:0] ctrl_wmask,
|
|
input wire [16*BURST_LEN-1:0] ctrl_rdata,
|
|
input wire ctrl_ready,
|
|
input wire ctrl_busy
|
|
);
|
|
// grant_now is COMBINATIONAL, not registered: layer_prefetch_ctrl.v
|
|
// issues ctrl_req as a genuine one-shot pulse (it has only ever
|
|
// been used wired DIRECTLY to a controller before this arbiter --
|
|
// EXP-0057/58/62/65 -- so it assumes immediate visibility, not a
|
|
// registered/one-cycle-late grant). A purely-registered arbiter
|
|
// (grant decided AT the clock edge, valid only the FOLLOWING
|
|
// cycle) misses that first pulse entirely -- found empirically:
|
|
// slot1 hung forever in its own S_WAIT state, ctrl_req correctly
|
|
// pulsed for exactly one cycle then dropped, but the registered
|
|
// grant hadn't caught up yet, so the real controller never saw it
|
|
// and ctrl_ready never came. `locked`/`grant_reg` below only
|
|
// LATCH a decision already available combinationally this same
|
|
// cycle, purely to keep it sticky once BOTH slots are active
|
|
// (prevents switching mid-fetch), never to delay the FIRST grant.
|
|
reg locked;
|
|
reg grant_reg;
|
|
|
|
wire grant_now = locked ? grant_reg : (slot0_active ? 1'b0 : 1'b1);
|
|
wire either_active = slot0_active || slot1_active;
|
|
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
locked <= 1'b0;
|
|
grant_reg<= 1'b0;
|
|
end else begin
|
|
if (!locked) begin
|
|
if (either_active) begin
|
|
locked <= 1'b1;
|
|
grant_reg <= grant_now;
|
|
end
|
|
end else begin
|
|
if (grant_reg == 1'b0 && !slot0_active) locked <= 1'b0;
|
|
if (grant_reg == 1'b1 && !slot1_active) locked <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
wire sel0 = either_active && (grant_now == 1'b0);
|
|
wire sel1 = either_active && (grant_now == 1'b1);
|
|
|
|
// combinational grant feedback: a slot must see its OWN grant
|
|
// asserted (in response to its own mem_active going high, same
|
|
// cycle) before it may pulse layer_prefetch_ctrl.v's one-shot
|
|
// ctrl_req -- see packed_slot.v's own S_MEMWAIT state.
|
|
assign slot0_grant = sel0;
|
|
assign slot1_grant = sel1;
|
|
|
|
assign ctrl_req = sel0 ? slot0_req : (sel1 ? slot1_req : 1'b0);
|
|
assign ctrl_wr = sel0 ? slot0_wr : (sel1 ? slot1_wr : 1'b0);
|
|
assign ctrl_addr = sel0 ? slot0_addr : (sel1 ? slot1_addr : {ADDR_WIDTH{1'b0}});
|
|
assign ctrl_wdata = sel0 ? slot0_wdata : (sel1 ? slot1_wdata : {(16*BURST_LEN){1'b0}});
|
|
assign ctrl_wmask = sel0 ? slot0_wmask : (sel1 ? slot1_wmask : {(2*BURST_LEN){1'b0}});
|
|
|
|
assign slot0_rdata = ctrl_rdata;
|
|
assign slot0_ready = sel0 ? ctrl_ready : 1'b0;
|
|
assign slot0_busy = sel0 ? ctrl_busy : 1'b1;
|
|
|
|
assign slot1_rdata = ctrl_rdata;
|
|
assign slot1_ready = sel1 ? ctrl_ready : 1'b0;
|
|
assign slot1_busy = sel1 ? ctrl_busy : 1'b1;
|
|
endmodule
|