feat(v2): M5 Neural Director, first-free job scheduling

Implements M5: neural_director.v dispatches job descriptors to
whichever of N_SLOTS (memory_manager, neural_processor) pairs is
currently free (first-free scheduling per §9's initial policy), with
a parametric-depth ready-queue FIFO for jobs arriving faster than
slots can absorb them.

Scope for this milestone (see decisions.log DEC-0007): a reduced
4-state FSM (DIR_IDLE/SCAN_READY/ALLOCATE/ERROR) rather than §9's full
8-state baseline -- dependency tracking, the waiting queue, and
wake-up are §10's explicit responsibility (Dependency Manager, M6, not
yet built), and slot-completion detection runs as an always-active
per-slot tracker rather than a dedicated FSM state, for the same
reason DEC-0002 already gave for the Neural Processor's own FSM
(gating concurrent per-unit progress behind one shared state kills
throughput).

Verified with Verilator (N_SLOTS=2, each slot backed by its own
independent behavioral memory rather than sharing V1's real PSRAM --
M4 already proved that path for one slot; this milestone's own concern
is scheduling across multiple slots): 4/4 tests pass -- 3 jobs
submitted to 2 slots (first two dispatch immediately, third correctly
queues until a slot frees), and a deliberate burst that forces the
ready queue to genuinely fill and recover.

Real synthesis: 0 CHECK problems, 382 LUT4/366 FF/4 CCU2C/0 DSP. Real
place&route (via a synthesis-only timing harness, same TRELLIS_IO
pin-budget reason as M2/M4): Fmax 250.50 MHz, PASS at 80MHz.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
This commit is contained in:
2026-09-05 14:46:32 +02:00
co-authored by Claude Sonnet 5
parent 175f697ae1
commit 2e4cedc761
18 changed files with 82810 additions and 1 deletions
@@ -0,0 +1,66 @@
// ================================================================
// SYNTHESIS-ONLY TIMING HARNESS -- NOT a functional deliverable.
// Same rationale as harness_neural_processor_array.v / harness_
// memory_manager.v (see errors.log ERR-0005): neural_director's
// per-slot arrayed ports (N_SLOTS=4 * 23-bit addresses x3) exceed the
// LFE5U-45F's TRELLIS_IO budget as a bare top-level module.
// ================================================================
module harness_neural_director #(
parameter ADDR_WIDTH = 23,
parameter N_SLOTS = 4,
parameter QUEUE_DEPTH = 8
)(
input wire clk,
input wire rst,
input wire [7:0] seed,
output wire [7:0] checksum
);
reg [31:0] lfsr;
always @(posedge clk) begin
if (rst) lfsr <= {24'h0, seed} | 32'h1;
else lfsr <= {lfsr[30:0], lfsr[31] ^ lfsr[21] ^ lfsr[1] ^ lfsr[0]};
end
wire job_in_valid = lfsr[0];
wire [ADDR_WIDTH-1:0] job_in_x_base = lfsr[ADDR_WIDTH-1:0];
wire [ADDR_WIDTH-1:0] job_in_w_base = {lfsr[3:0], lfsr[ADDR_WIDTH-5:0]};
wire [15:0] job_in_n_tiles = lfsr[15:0];
wire [ADDR_WIDTH-1:0] job_in_result_addr = {lfsr[7:0], lfsr[ADDR_WIDTH-9:0]};
wire [15:0] job_in_node_id = lfsr[31:16];
wire [N_SLOTS-1:0] slot_job_done = lfsr[N_SLOTS-1:0];
wire job_in_ready;
wire [N_SLOTS-1:0] slot_job_start;
wire [ADDR_WIDTH*N_SLOTS-1:0] slot_x_base, slot_w_base, slot_result_addr;
wire [16*N_SLOTS-1:0] slot_n_tiles;
wire job_out_done;
wire [$clog2(N_SLOTS)-1:0] job_out_slot;
wire [3:0] dir_state;
wire dir_error;
neural_director #(
.ADDR_WIDTH(ADDR_WIDTH), .N_SLOTS(N_SLOTS), .QUEUE_DEPTH(QUEUE_DEPTH)
) dut (
.clk(clk), .rst(rst),
.job_in_valid(job_in_valid), .job_in_ready(job_in_ready),
.job_in_x_base(job_in_x_base), .job_in_w_base(job_in_w_base),
.job_in_n_tiles(job_in_n_tiles), .job_in_result_addr(job_in_result_addr),
.job_in_node_id(job_in_node_id),
.slot_job_start(slot_job_start), .slot_x_base(slot_x_base), .slot_w_base(slot_w_base),
.slot_n_tiles(slot_n_tiles), .slot_result_addr(slot_result_addr), .slot_job_done(slot_job_done),
.job_out_done(job_out_done), .job_out_slot(job_out_slot),
.dir_state(dir_state), .dir_error(dir_error)
);
reg [7:0] chk;
always @(posedge clk) begin
if (rst) chk <= 8'h0;
else chk <= chk ^ {7'h0, job_in_ready} ^ slot_job_start ^ slot_x_base[7:0]
^ slot_w_base[7:0] ^ slot_result_addr[7:0] ^ slot_n_tiles[7:0]
^ {7'h0, job_out_done} ^ {6'h0, job_out_slot} ^ dir_state ^ {7'h0, dir_error};
end
assign checksum = chk;
endmodule
@@ -0,0 +1,192 @@
Info: Logic utilisation before packing:
Info: Total LUT4s: 163/43848 0%
Info: logic LUTs: 107/43848 0%
Info: carry LUTs: 8/43848 0%
Info: RAM LUTs: 32/ 5481 0%
Info: RAMW LUTs: 16/10962 0%
Info: Total DFFs: 98/43848 0%
Info: Packing IOs..
Info: Packing constants..
Info: Packing carries...
Info: Packing LUTs...
Info: Packing LUT5-7s...
Info: Packing FFs...
Info: 66 FFs paired with LUTs.
Info: Generating derived timing constraints...
Info: Promoting globals...
Info: promoting clock net clk$TRELLIS_IO_IN to global network
Info: Checksum: 0x77eb46c4
Info: Device utilisation:
Info: TRELLIS_IO: 18/ 245 7%
Info: DCCA: 1/ 56 1%
Info: DP16KD: 0/ 108 0%
Info: MULT18X18D: 0/ 72 0%
Info: ALU54B: 0/ 36 0%
Info: EHXPLLL: 0/ 4 0%
Info: EXTREFB: 0/ 2 0%
Info: DCUA: 0/ 2 0%
Info: PCSCLKDIV: 0/ 2 0%
Info: IOLOGIC: 0/ 160 0%
Info: SIOLOGIC: 0/ 85 0%
Info: GSR: 0/ 1 0%
Info: JTAGG: 0/ 1 0%
Info: OSCG: 0/ 1 0%
Info: SEDGA: 0/ 1 0%
Info: DTR: 0/ 1 0%
Info: USRMCLK: 0/ 1 0%
Info: CLKDIVF: 0/ 4 0%
Info: ECLKSYNCB: 0/ 10 0%
Info: DLLDELD: 0/ 8 0%
Info: DDRDLL: 0/ 4 0%
Info: DQSBUFM: 0/ 10 0%
Info: TRELLIS_ECLKBUF: 0/ 8 0%
Info: ECLKBRIDGECS: 0/ 2 0%
Info: DCSC: 0/ 2 0%
Info: TRELLIS_FF: 98/ 43848 0%
Info: TRELLIS_COMB: 169/ 43848 0%
Info: TRELLIS_RAMW: 8/ 5481 0%
Info: Placed 0 cells based on constraints.
Info: Creating initial analytic placement for 134 cells, random placement wirelen = 12480.
Info: at initial placer iter 0, wirelen = 1480
Info: at initial placer iter 1, wirelen = 1128
Info: at initial placer iter 2, wirelen = 1103
Info: at initial placer iter 3, wirelen = 1100
Info: Running main analytical placer, max placement attempts per cell = 10804.
Info: at iteration #1, type ALL: wirelen solved = 1119, spread = 1315, legal = 1359; time = 0.00s
Info: HeAP Placer Time: 0.02s
Info: of which solving equations: 0.01s
Info: of which spreading cells: 0.00s
Info: of which strict legalisation: 0.00s
Info: Running simulated annealing placer for refinement.
Info: at iteration #1: temp = 0.000000, timing cost = 12, wirelen = 1359
Info: at iteration #5: temp = 0.000000, timing cost = 33, wirelen = 1072
Info: at iteration #10: temp = 0.000000, timing cost = 31, wirelen = 1074
Info: at iteration #12: temp = 0.000000, timing cost = 23, wirelen = 1051
Info: SA placement time 0.05s
Info: Max frequency for clock '$glbnet$clk$TRELLIS_IO_IN': 275.10 MHz (PASS at 80.00 MHz)
Info: Max delay <async> -> posedge $glbnet$clk$TRELLIS_IO_IN: 4.52 ns
Info: Max delay posedge $glbnet$clk$TRELLIS_IO_IN -> <async> : 4.86 ns
Info: Slack histogram:
Info: legend: * represents 1 endpoint(s)
Info: + represents [1,1) endpoint(s)
Info: [ 8865, 9019) |*********************
Info: [ 9019, 9173) |*******************
Info: [ 9173, 9327) |*****************
Info: [ 9327, 9481) |**************
Info: [ 9481, 9635) |***
Info: [ 9635, 9789) |***
Info: [ 9789, 9943) |**
Info: [ 9943, 10097) |****
Info: [ 10097, 10251) |*****
Info: [ 10251, 10405) |*****
Info: [ 10405, 10559) |**************
Info: [ 10559, 10713) |********************************
Info: [ 10713, 10867) |*****************************************
Info: [ 10867, 11021) |************************************
Info: [ 11021, 11175) |****************************
Info: [ 11175, 11329) |******************************
Info: [ 11329, 11483) |****
Info: [ 11483, 11637) |************
Info: [ 11637, 11791) |***************
Info: [ 11791, 11945) |***
Info: Checksum: 0x23c4f63e
Info: Routing globals...
Info: routing clock net $glbnet$clk$TRELLIS_IO_IN using global 0
Info: Routing..
Info: Setting up routing queue.
Info: Routing 887 arcs.
Info: | (re-)routed arcs | delta | remaining| time spent |
Info: IterCnt | w/ripup wo/ripup | w/r wo/r | arcs| batch(sec) total(sec)|
Info: 1000 | 154 829 | 154 829 | 48| 0.08 0.08|
Info: 1047 | 154 875 | 0 46 | 0| 0.04 0.11|
Info: Routing complete.
Info: Router1 time 0.11s
Info: Checksum: 0x86bbec10
Info: Critical path report for clock '$glbnet$clk$TRELLIS_IO_IN' (posedge -> posedge):
Info: type curr total name
Info: clk-to-q 0.40 0.40 Source dut.slot_busy_TRELLIS_FF_Q_2.Q
Info: routing 0.68 1.07 Net dut.dir_state_LUT4_D_Z[1] (27,37) -> (28,37)
Info: Sink dut.slot_busy_TRELLIS_FF_Q_DI_LUT4_Z_D_LUT4_C_D_CCU2C_S0_B0_LUT4_Z.D
Info: Defined in:
Info: hardware/v2/rtl/neural_director.v:93.23-93.32
Info: logic 0.18 1.25 Source dut.slot_busy_TRELLIS_FF_Q_DI_LUT4_Z_D_LUT4_C_D_CCU2C_S0_B0_LUT4_Z.F
Info: routing 0.85 2.10 Net dut.slot_busy_TRELLIS_FF_Q_DI_LUT4_Z_D_LUT4_C_D_CCU2C_S0_B0 (28,37) -> (30,38)
Info: Sink dut.slot_busy_TRELLIS_FF_Q_DI_LUT4_Z_D_LUT4_C_D_CCU2C_S0$CCU2_COMB0.B
Info: logic 0.35 2.46 Source dut.slot_busy_TRELLIS_FF_Q_DI_LUT4_Z_D_LUT4_C_D_CCU2C_S0$CCU2_COMB0.FCO
Info: routing 0.00 2.46 Net dut.slot_busy_TRELLIS_FF_Q_DI_LUT4_Z_D_LUT4_C_D_CCU2C_S0$CCU2_FCI_INT (30,38) -> (30,38)
Info: Sink dut.slot_busy_TRELLIS_FF_Q_DI_LUT4_Z_D_LUT4_C_D_CCU2C_S0$CCU2_COMB1.FCI
Info: logic 0.29 2.75 Source dut.slot_busy_TRELLIS_FF_Q_DI_LUT4_Z_D_LUT4_C_D_CCU2C_S0$CCU2_COMB1.F
Info: routing 0.94 3.69 Net dut.dir_state_LUT4_D_Z[3] (30,38) -> (28,35)
Info: Sink dut.q_w_base.0.1_DO_LUT4_B_Z_LUT4_Z.D
Info: Defined in:
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:108.23-108.24
Info: logic 0.18 3.87 Source dut.q_w_base.0.1_DO_LUT4_B_Z_LUT4_Z.F
Info: routing 0.12 3.99 Net dut.q_w_base.0.1_DO_LUT4_B_Z[2] (28,35) -> (28,35)
Info: Sink dut.slot_w_base_TRELLIS_FF_Q_2.DI
Info: setup 0.00 3.99 Source dut.slot_w_base_TRELLIS_FF_Q_2.DI
Info: 1.40 ns logic, 2.59 ns routing
Info: Critical path report for cross-domain path '<async>' -> 'posedge $glbnet$clk$TRELLIS_IO_IN':
Info: type curr total name
Info: source 0.00 0.00 Source rst$tr_io.O
Info: routing 2.75 2.75 Net rst$TRELLIS_IO_IN (29,0) -> (29,37)
Info: Sink dut.slot_job_start_TRELLIS_FF_Q_LSR_LUT4_Z.C
Info: Defined in:
Info: hardware/v2/synthesis/harness_neural_director.v:15.17-15.20
Info: logic 0.18 2.93 Source dut.slot_job_start_TRELLIS_FF_Q_LSR_LUT4_Z.F
Info: routing 0.47 3.40 Net dut.slot_job_start_TRELLIS_FF_Q_LSR (29,37) -> (28,36)
Info: Sink dut.slot_job_start_TRELLIS_FF_Q_3.LSR
Info: setup 0.29 3.68 Source dut.slot_job_start_TRELLIS_FF_Q_3.LSR
Info: 0.47 ns logic, 3.22 ns routing
Info: Critical path report for cross-domain path 'posedge $glbnet$clk$TRELLIS_IO_IN' -> '<async>':
Info: type curr total name
Info: clk-to-q 0.40 0.40 Source chk_TRELLIS_FF_Q_5.Q
Info: routing 2.94 3.34 Net checksum[6]$TRELLIS_IO_OUT (32,39) -> (90,38)
Info: Sink checksum[6]$tr_io.I
Info: Defined in:
Info: hardware/v2/synthesis/harness_neural_director.v:57.15-57.18
Info: 0.40 ns logic, 2.94 ns routing
Info: Max frequency for clock '$glbnet$clk$TRELLIS_IO_IN': 250.50 MHz (PASS at 80.00 MHz)
Info: Max delay <async> -> posedge $glbnet$clk$TRELLIS_IO_IN: 3.68 ns
Info: Max delay posedge $glbnet$clk$TRELLIS_IO_IN -> <async> : 3.34 ns
Info: Slack histogram:
Info: legend: * represents 1 endpoint(s)
Info: + represents [1,1) endpoint(s)
Info: [ 8508, 8673) |**
Info: [ 8673, 8838) |********
Info: [ 8838, 9003) |***********************
Info: [ 9003, 9168) |******
Info: [ 9168, 9333) |***********************
Info: [ 9333, 9498) |****************
Info: [ 9498, 9663) |*
Info: [ 9663, 9828) |
Info: [ 9828, 9993) |***
Info: [ 9993, 10158) |*
Info: [ 10158, 10323) |*******
Info: [ 10323, 10488) |****
Info: [ 10488, 10653) |*********
Info: [ 10653, 10818) |*****************************
Info: [ 10818, 10983) |***************************
Info: [ 10983, 11148) |******************************************
Info: [ 11148, 11313) |***************************************
Info: [ 11313, 11478) |***********************************
Info: [ 11478, 11643) |********************
Info: [ 11643, 11808) |*************
Info: Program finished normally.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,56 @@
Info: Logic utilisation before packing:
Info: Total LUT4s: 522/43848 1%
Info: logic LUTs: 382/43848 0%
Info: carry LUTs: 8/43848 0%
Info: RAM LUTs: 88/ 5481 1%
Info: RAMW LUTs: 44/10962 0%
Info: Total DFFs: 366/43848 0%
Info: Packing IOs..
Info: Packing constants..
Info: Packing carries...
Info: Packing LUTs...
Info: Packing LUT5-7s...
Info: Packing FFs...
Info: 362 FFs paired with LUTs.
Info: Generating derived timing constraints...
Info: Promoting globals...
Info: promoting clock net clk$TRELLIS_IO_IN to global network
Info: Checksum: 0xb47c9b6c
Info: Device utilisation:
Info: TRELLIS_IO: 461/ 245 188%
Info: DCCA: 1/ 56 1%
Info: DP16KD: 0/ 108 0%
Info: MULT18X18D: 0/ 72 0%
Info: ALU54B: 0/ 36 0%
Info: EHXPLLL: 0/ 4 0%
Info: EXTREFB: 0/ 2 0%
Info: DCUA: 0/ 2 0%
Info: PCSCLKDIV: 0/ 2 0%
Info: IOLOGIC: 0/ 160 0%
Info: SIOLOGIC: 0/ 85 0%
Info: GSR: 0/ 1 0%
Info: JTAGG: 0/ 1 0%
Info: OSCG: 0/ 1 0%
Info: SEDGA: 0/ 1 0%
Info: DTR: 0/ 1 0%
Info: USRMCLK: 0/ 1 0%
Info: CLKDIVF: 0/ 4 0%
Info: ECLKSYNCB: 0/ 10 0%
Info: DLLDELD: 0/ 8 0%
Info: DDRDLL: 0/ 4 0%
Info: DQSBUFM: 0/ 10 0%
Info: TRELLIS_ECLKBUF: 0/ 8 0%
Info: ECLKBRIDGECS: 0/ 2 0%
Info: DCSC: 0/ 2 0%
Info: TRELLIS_FF: 366/ 43848 0%
Info: TRELLIS_COMB: 528/ 43848 1%
Info: TRELLIS_RAMW: 22/ 5481 0%
Info: Placed 0 cells based on constraints.
ERROR: Unable to place cell 'job_in_n_tiles[9]$tr_io', no BELs remaining to implement cell type 'TRELLIS_IO'
0 warnings, 1 error
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff