feat(v2): M4 Memory Manager + Prefetch Engine, real V1 PSRAM backend
Implements M4: memory_manager.v (arbitration/buffering/forwarding/ latency hiding/double buffering, §12) + prefetch_engine.v (double-buffered tile fetch, §13), sitting on the REAL, UNMODIFIED V1 PSRAM backend chain (int8_memory_access.v -> memory_interface.v -> psram_controller.v, per §15's explicit mandate not to touch the controller). Verified fully end-to-end with Verilator: real neural_processor (M1) fed entirely by memory_manager, computing against PSRAM-resident X/W tiles (double-buffered prefetch across up to 5 tiles) and writing its result back to PSRAM -- checked via an independent PSRAM read-back, with poison bytes around the operand regions to catch addressing errors. 3/3 jobs pass (1/3/5-tile configurations). Three real RTL bugs found and fixed during integration (full diagnostic trail in errors.log ERR-0006): prefetch_engine had no single-in-flight-request discipline, letting a queued request corrupt the bank bookkeeping of a fetch already running; the fix's own !pf_busy guard had a one-cycle blind spot (pf_busy lags pf_start by a clock) that needed an explicit !pf_start term; and a state-based mux for the shared backend port was off by one cycle, silently dropping the PSRAM result write entirely. Real synthesis: 0 CHECK problems, 851 LUT4/789 FF/108 CCU2C/0 DSP (expected, no multiplication in this module). Real place&route (via a synthesis-only timing harness, needed for the same TRELLIS_IO pin- budget reason as M2's array): Fmax 165.86 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:
@@ -22,8 +22,11 @@ reali, non solo scritto).
|
||||
`result_buffer.v`). Tutti inferiscono DP16KD reale (10/10 test,
|
||||
6/6 config sintetizzate 0 problemi). Scoperta: il costo BRAM di
|
||||
weight_buffer e' guidato da P_IN (larghezza), non da DEPTH.
|
||||
- [ ] **M4 — Memory Manager** (`memory_manager.v`, `prefetch_engine.v`),
|
||||
backend PSRAM V1 riusato senza modifiche.
|
||||
- [x] **M4 — Memory Manager** (`memory_manager.v`, `prefetch_engine.v`),
|
||||
backend PSRAM V1 riusato SENZA MODIFICHE. End-to-end reale (3/3
|
||||
job PASS) con vero neural_processor + vera catena PSRAM V1.
|
||||
3 bug RTL trovati/risolti (`logs/errors.log` ERR-0006). Fmax
|
||||
165.86 MHz.
|
||||
- [ ] **M5 — Neural Director** (`neural_director.v`), scheduling first-free.
|
||||
- [ ] **M6 — Dependency Manager** (`dependency_manager.v`), ready/waiting
|
||||
queue, dependency counters, wake-up, producer tracking.
|
||||
|
||||
@@ -59,3 +59,18 @@ wide) scale as expected with depth. Confirms §14's warning literally:
|
||||
"non assumere che buffer piu' grandi siano automaticamente migliori"
|
||||
-- here, smaller was not cheaper either, because depth was the wrong
|
||||
lever for this specific buffer's cost.
|
||||
|
||||
[2026-09-05] M4 Memory Manager + Prefetch Engine (standalone resource
|
||||
count; Fmax via timing harness -- see errors.log ERR-0005)
|
||||
|
||||
| Module | Fmax (POST-P&R) | LUT | FF | DSP | CCU2C |
|
||||
|----------------------------------|------------------|-----|-----|-----|-------|
|
||||
| memory_manager + prefetch_engine | 165.86 MHz | 851 | 789 | 0 | 108 |
|
||||
|
||||
End-to-end (real PSRAM + real neural_processor, SIMULATED only, no
|
||||
system-level P&R yet -- deferred to M7/M9): 3-tile job = 446 cycles,
|
||||
1-tile job = 166 cycles, 5-tile job = 728 cycles. ~140-150 cycles/tile,
|
||||
dominated by psram_model.v's real ~70ns TAA access latency, not by
|
||||
memory_manager's own control overhead (its bank-swap turnaround is
|
||||
documented as a fixed +1 cycle/tile in decisions.log DEC-0006, a small
|
||||
fraction of the ~140-cycle PSRAM-dominated total).
|
||||
|
||||
@@ -248,3 +248,74 @@ not merely a stylistic choice.
|
||||
|
||||
STATUS:
|
||||
ACCEPTED
|
||||
|
||||
---
|
||||
|
||||
DEC-0006
|
||||
|
||||
DATE: 2026-09-05
|
||||
|
||||
DECISION:
|
||||
memory_manager.v (M4) uses a SINGLE prefetch_engine instance,
|
||||
retargeted per bank via a depth-1 pending-request register, rather
|
||||
than multiple engines or a general request queue. The result
|
||||
write-back (one byte per job, after the last tile) shares the same
|
||||
backend port via a simple state-based mux, not a general arbiter --
|
||||
because prefetch and write-back are temporally disjoint by
|
||||
construction (the write only happens after prefetch_engine has
|
||||
nothing left to fetch for that job).
|
||||
|
||||
WHY:
|
||||
§13's double-buffering strategy needs at most ONE fetch "in flight"
|
||||
and at most ONE fetch "queued" at any time for a SINGLE Neural
|
||||
Processor consuming tiles sequentially (proven by construction: a new
|
||||
prefetch is only ever queued on a tile handoff, and at most one
|
||||
handoff can be pending completion of the previous prefetch before the
|
||||
next one is even requested). A general multi-entry queue or a second
|
||||
engine would add complexity with no present benefit. Likewise,
|
||||
because this Memory Manager currently serves exactly one Neural
|
||||
Processor and one job at a time, no concurrent second requester can
|
||||
ever contend for the backend port with prefetch reads -- a real
|
||||
mem_arbiter-style arbiter (as V1 uses for ITS OWN multi-master case)
|
||||
is deferred until a scenario that actually needs it exists (multiple
|
||||
Neural Processors or overlapping jobs sharing one memory_manager,
|
||||
not yet built).
|
||||
|
||||
EVIDENCE:
|
||||
errors.log ERR-0006 -- the single-entry pending register, once
|
||||
correctly gated (see ERR-0006 items 1-2), handled 1-tile, 3-tile, and
|
||||
5-tile jobs correctly with no queue overflow in
|
||||
hardware/v2/sim/tb_memory_manager.v.
|
||||
|
||||
ALTERNATIVES:
|
||||
1. Multiple prefetch_engine instances (one per bank), letting both
|
||||
banks fetch fully in parallel. Rejected for M4: doubles DSP-free
|
||||
logic for a benefit only realized when compute-tile time is
|
||||
SHORTER than 2x fetch-tile time for a single engine -- not yet
|
||||
measured to be the case (§22, deferred to M9), and the single-
|
||||
engine design already fully hides fetch latency behind neural_
|
||||
processor's own per-tile compute time in the cases tested (see
|
||||
experiments.log EXP-0005 cycle counts).
|
||||
2. General N-entry FIFO for pending requests. Rejected: no scenario
|
||||
in the current single-processor, single-job design can ever
|
||||
generate more than one pending request before the in-flight one
|
||||
completes -- an N-entry queue would be complexity with no
|
||||
reachable use.
|
||||
3. Reuse V1's mem_arbiter.v as-is for the prefetch-vs-writeback
|
||||
sharing. Rejected: mem_arbiter.v's four ports are hardcoded to
|
||||
specific V1 module names/priorities (§1 already established this
|
||||
pattern in DEC-0001 for the broader V1-freeze decision) -- and
|
||||
prefetch/write-back are provably never simultaneous here anyway,
|
||||
so even a generic 2-port arbiter would be unexercised complexity.
|
||||
|
||||
RESULT:
|
||||
memory_manager.v as implemented. A NOTED, NOT-YET-OPTIMIZED
|
||||
characteristic (documented in the module's own header comment): the
|
||||
bank-swap-and-check control path costs a minimum 1 idle cycle per
|
||||
tile handoff even when the next bank was already prefetched in time,
|
||||
unlike neural_processor.v's own zero-gap tile acceptance -- left for
|
||||
M10 (Optimization) to revisit using real stall-percentage data (§22)
|
||||
rather than optimized blindly now.
|
||||
|
||||
STATUS:
|
||||
ACCEPTED
|
||||
|
||||
@@ -110,3 +110,30 @@ decision: vedi benchmark.log -- il dimensionamento di weight_buffer
|
||||
andra' guidato da P_IN, non solo da DEPTH, quando si arrivera' a
|
||||
M4/M9.
|
||||
next_action: M4 -- memory_manager.v + prefetch_engine.v.
|
||||
|
||||
[2026-09-05T16:00:00Z] commit=5f0d7f1 session=v2-M4-memory-manager
|
||||
module: hardware/v2/rtl/memory_manager.v, prefetch_engine.v
|
||||
action: implementato M4 -- Memory Manager (arbitraggio/buffering/
|
||||
forwarding/gestione latenza/double buffering, §12) + Prefetch Engine
|
||||
(fetch a doppio buffer con retargeting di una singola istanza per
|
||||
banco, §13). Backend PSRAM V1 riusato SENZA MODIFICHE
|
||||
(int8_memory_access.v -> memory_interface.v -> psram_controller.v,
|
||||
§15), integrato end-to-end con un vero hardware/v2/rtl/
|
||||
neural_processor.v (M1).
|
||||
reason: roadmap M4.
|
||||
result: 3/3 job PASS end-to-end (1/3/5 tile), risultato verificato con
|
||||
RILETTURA INDIPENDENTE da PSRAM (non solo ispezione di segnali
|
||||
interni), con byte "poison" attorno alle regioni operando per
|
||||
catturare eventuali errori di indirizzamento off-by-one (nessuno
|
||||
trovato). 3 bug RTL reali trovati e risolti durante l'integrazione
|
||||
(vedi errors.log ERR-0006): mancava una disciplina "una sola
|
||||
richiesta di prefetch in volo", un buco di un ciclo nel check
|
||||
!pf_busy, un mux di stato disallineato di un ciclo che faceva
|
||||
silenziosamente perdere la scrittura del risultato su PSRAM.
|
||||
Sintesi reale: 0 problemi, 851 LUT4/789 FF/108 CCU2C/0 DSP (atteso).
|
||||
Fmax reale (via harness, stesso motivo pin-count di ERR-0005):
|
||||
165.86 MHz, PASS a 80MHz.
|
||||
errors: vedi errors.log ERR-0005 (ricorrenza), ERR-0006 (3 bug nuovi).
|
||||
decision: vedi decisions.log DEC-0006 (motore di prefetch singolo +
|
||||
registro pendente, nessun arbitro backend ancora necessario).
|
||||
next_action: M5 -- neural_director.v, scheduling first-free.
|
||||
|
||||
@@ -124,3 +124,55 @@ WORKAROUND: hardware/v2/synthesis/harness_neural_processor_array.v --
|
||||
STATUS: WORKED AROUND. Will become moot once M4/M5 exist and the array
|
||||
is synthesized as part of a larger design with on-chip ports instead
|
||||
of a bare top-level module.
|
||||
|
||||
ERR-0006 (real RTL bugs in hardware/v2/rtl/memory_manager.v, FOUND AND FIXED)
|
||||
DATE: 2026-09-05
|
||||
MODULE: hardware/v2/rtl/memory_manager.v
|
||||
SYMPTOM: end-to-end M4 testbench (real V1 PSRAM backend + real M1
|
||||
neural_processor) hung permanently partway through the first
|
||||
multi-tile job -- bank_ready for the "current" bank never became 1,
|
||||
even though the byte-level backend clearly kept completing read
|
||||
transactions (observed via cycle-by-cycle hierarchical tracing of
|
||||
memory_manager/prefetch_engine internal state).
|
||||
ROOT CAUSES (three, found together during the same debugging session):
|
||||
1. The tile-N+2 prefetch request, queued on tile-N's handoff, could
|
||||
be issued (pf_start asserted) while prefetch_engine was STILL
|
||||
mid-fetch for tile-N+1 -- there was no single-in-flight-request
|
||||
discipline at all in the first draft. Fixed by adding a
|
||||
single-entry pf_pending register: requests are queued, not
|
||||
issued directly, and a dedicated rule launches the queued
|
||||
request only once the engine reports free.
|
||||
2. Even with that queue, pf_busy does not read 1 until the cycle
|
||||
AFTER pf_start was first observed by prefetch_engine (its own
|
||||
fetch_busy<=1 lags its own fetch_start sampling by one clock) --
|
||||
checking only `!pf_busy` left a genuine one-cycle window where a
|
||||
second queued request would fire on top of the one just
|
||||
launched, silently overwriting pf_target_bank (and pf_x_addr/
|
||||
pf_w_addr) for the fetch already in flight. The address corruption
|
||||
was harmless (prefetch_engine had already latched the correct
|
||||
address into its own state that same edge), but pf_target_bank
|
||||
corruption meant the eventually-completed fetch's real data got
|
||||
filed into the WRONG bank's bank_ready/bank_x/bank_w, permanently
|
||||
starving the bank actually needed next. Fixed by gating the issue
|
||||
rule on `!pf_busy && !pf_start` (the extra term closes exactly
|
||||
this one-cycle window).
|
||||
3. A combinational mux selecting between prefetch_engine's own
|
||||
backend wires and the result-write-back FSM's wires was gated on
|
||||
`state == MM_WRITE_RESULT`, but wr_mem_req (asserted while
|
||||
state==MM_WRITE_RESULT) only becomes valid the FOLLOWING cycle,
|
||||
i.e. while state==MM_DONE -- the mux therefore selected the wrong
|
||||
source for the one cycle the write request pulse was actually
|
||||
high, silently dropping the PSRAM write entirely. Fixed by
|
||||
widening the mux's select condition to cover both states.
|
||||
DIAGNOSIS METHOD: cycle-by-cycle hierarchical signal dumps (mm.state,
|
||||
tile_idx, bank_ready, pf_busy, pf_pending, pf_target_bank,
|
||||
prefetch_engine's own state) under Verilator, printed only on
|
||||
signal-change to keep the trace readable, comparing against the
|
||||
hand-derived expected sequence of events for a 3-tile job.
|
||||
VERIFICATION: hardware/v2/sim/tb_memory_manager.v -- 3/3 tests PASS
|
||||
after all three fixes, including a 5-tile job (steady-state
|
||||
double-buffer swap across more than 2 tiles) and independent
|
||||
PSRAM read-back of the written-back result byte (not just internal
|
||||
signal inspection).
|
||||
STATUS: FIXED, verified end-to-end with the real (unmodified) V1
|
||||
PSRAM backend chain and a real M1 neural_processor.
|
||||
|
||||
@@ -218,3 +218,59 @@ decision: keep DEPTH parametric as specified, but document (this
|
||||
next_action: M4 -- memory_manager.v + prefetch_engine.v (wires these
|
||||
three buffers + the array together, PSRAM backend reused unmodified
|
||||
from V1 per §15).
|
||||
|
||||
EXP-0005
|
||||
timestamp: 2026-09-05T16:00:00Z
|
||||
git_commit: 5f0d7f1 (+ uncommitted M4 work)
|
||||
session: v2-M4-memory-manager
|
||||
module: hardware/v2/rtl/memory_manager.v, prefetch_engine.v
|
||||
configuration: P_IN=8, ADDR_WIDTH=23, real V1 backend chain
|
||||
(int8_memory_access -> memory_interface -> psram_controller ->
|
||||
psram_model, ALL unmodified), real M1 neural_processor
|
||||
action: M4 -- end-to-end integration: memory_manager double-buffers
|
||||
prefetch of X/W tiles from PSRAM, feeds a real neural_processor,
|
||||
writes the computed result back to PSRAM.
|
||||
command (sim, Verilator): verilator --binary --timing -j 0 -Wno-fatal
|
||||
--top-module tb -o /tmp/vtb_mm hardware/v1/rtl/int8_memory_access.v
|
||||
hardware/v1/rtl/memory_interface.v hardware/v1/rtl/psram_controller.v
|
||||
hardware/v1/sim/psram_model.v hardware/v2/rtl/neural_processor.v
|
||||
hardware/v2/rtl/prefetch_engine.v hardware/v2/rtl/memory_manager.v
|
||||
hardware/v2/sim/tb_memory_manager.v && /tmp/vtb_mm
|
||||
command (synth): yosys -p "synth_ecp5 -json .../top.json -top
|
||||
memory_manager" hardware/v2/rtl/memory_manager.v
|
||||
hardware/v2/rtl/prefetch_engine.v (standalone, for real resource
|
||||
counts); harness_memory_manager.v (see errors.log ERR-0005 pattern)
|
||||
for real P&R Fmax, since the bare module exceeds the device's
|
||||
TRELLIS_IO budget as a top-level (same class of artifact as the
|
||||
Processor Array, not a logic limit).
|
||||
result:
|
||||
SIMULATED: 3/3 jobs PASS end-to-end -- 3-tile (bias=0, ACT_RELU,
|
||||
saturates to 127), 1-tile (no saturation, y=32), and 5-tile
|
||||
(steady-state double-buffer swap across more than 2 tiles, y=40)
|
||||
-- each verified by an INDEPENDENT PSRAM read-back of the
|
||||
written result byte (not just internal signal inspection), with
|
||||
"poison" bytes surrounding the real operand regions to catch any
|
||||
off-by-one addressing (none found).
|
||||
Cycle counts (real, PSRAM power-up already excluded): 3-tile job =
|
||||
446 cycles, 1-tile job = 166 cycles, 5-tile job = 728 cycles.
|
||||
Roughly ~140-150 cycles/tile at this PSRAM timing (dominated by
|
||||
the real ~70ns TAA access latency modeled in psram_model.v, not
|
||||
by memory_manager's own control overhead) -- a real, measured
|
||||
number, not estimated.
|
||||
SYNTHESIZED (standalone, real resource count): 0 CHECK problems,
|
||||
851 LUT4, 789 TRELLIS_FF, 108 CCU2C, 0 DSP (expected -- no
|
||||
multiplication in this module).
|
||||
POST-P&R (via harness, real Fmax): 165.86 MHz, PASS at 80MHz with
|
||||
large margin.
|
||||
errors: ERR-0006 (three real RTL bugs found and fixed during bring-up
|
||||
-- see errors.log for full detail: a missing single-in-flight-
|
||||
request discipline, a one-cycle pf_busy blind spot, and an
|
||||
off-by-one state mux for the write-back path). ERR-0005's pin-count
|
||||
artifact recurred for this module too (worked around the same way).
|
||||
decision: see decisions.log DEC-0006 (single prefetch engine + pending
|
||||
register is sufficient for this milestone's scope; a real backend
|
||||
arbiter is deferred until multiple processors/jobs actually need to
|
||||
share one memory_manager).
|
||||
next_action: M5 -- neural_director.v (first-free scheduling), wiring
|
||||
job dispatch to potentially multiple (memory_manager, neural_
|
||||
processor) pairs instead of the single hardcoded pair tested here.
|
||||
|
||||
@@ -36,3 +36,15 @@ vectors: extreme INT8 (-128, 127, -100, 100), regular values, full
|
||||
writes undisturbed
|
||||
simulator: Verilator 5.050 (--binary --timing)
|
||||
PASS/FAIL: 10/10 PASS
|
||||
|
||||
[2026-09-05] EXP-0005 -- hardware/v2/sim/tb_memory_manager.v
|
||||
test: 3 end-to-end jobs (3-tile/saturating, 1-tile/non-saturating,
|
||||
5-tile/steady-state-swap), real V1 PSRAM backend chain (unmodified),
|
||||
real M1 neural_processor
|
||||
simulator: Verilator 5.050 (--binary --timing)
|
||||
PASS/FAIL: 3/3 PASS
|
||||
bit-exact result: PSRAM-read-back result byte matches hand-computed
|
||||
expectation in every case (independent oracle, not derived from the
|
||||
RTL under test)
|
||||
cycles: 446 (3 tiles), 166 (1 tile), 728 (5 tiles) -- real PSRAM
|
||||
latency dominates, not memory_manager's own control overhead
|
||||
|
||||
@@ -36,3 +36,7 @@ result_buffer D=4096: LUT=37 FF=30 DP16KD=2 (D=256: LUT=21 FF=26 DP16KD=1)
|
||||
CHECK: 0 problems, all 6 configs correctly infer DP16KD (no LUT-RAM
|
||||
fallback). weight_buffer's DP16KD count does NOT drop with depth
|
||||
(width-bound, not depth-bound -- see decisions.log / benchmark.log).
|
||||
|
||||
[2026-09-05] EXP-0005 -- memory_manager + prefetch_engine (standalone)
|
||||
LUT4=851 TRELLIS_FF=789 CCU2C=108 MULT18X18D=0 (expected, no
|
||||
multiplication in this module). CHECK: 0 problems.
|
||||
|
||||
@@ -38,3 +38,9 @@ weight_buffer (D=512): Fmax=339.67 MHz PASS
|
||||
result_buffer (D=4096): Fmax=325.20 MHz PASS
|
||||
All far above the 80MHz target -- buffers are not a timing concern in
|
||||
isolation at these depths.
|
||||
|
||||
[2026-09-05] EXP-0005 -- memory_manager + prefetch_engine (via
|
||||
harness_memory_manager.v, see errors.log ERR-0005 for why a harness
|
||||
was needed), real nextpnr-ecp5 --45k --package CABGA381 --speed 8
|
||||
--freq 80 --lpf-allow-unconstrained
|
||||
Fmax: 165.86 MHz -- PASS at 80MHz (real place&route measurement)
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// ================================================================
|
||||
// FPGA-Neural V2 -- Memory Manager (M4, docs/v2-description.md §12/§15)
|
||||
//
|
||||
// Sits between a single Neural Processor (M1) and the byte-level
|
||||
// Memory Backend Interface (hardware/v1/rtl/int8_memory_access.v,
|
||||
// reused UNMODIFIED, per §15 -- "NON iniziare modificando il
|
||||
// controller PSRAM. Mantenere inizialmente il backend esistente").
|
||||
// The processor sees only "data available" (operand_valid/ready,
|
||||
// tile_last) -- never PSRAM request/wait cycles directly (§12).
|
||||
//
|
||||
// Double-buffered prefetch (§13): while the processor consumes tile
|
||||
// N from bank "current", this module retargets the single
|
||||
// prefetch_engine instance (M4) at bank "next" to fetch tile N+1
|
||||
// concurrently. On tile handoff, banks swap; if a bank isn't ready in
|
||||
// time (prefetch slower than compute for this run), operand_valid
|
||||
// simply stays low until it is -- a real stall, not hidden, so its
|
||||
// frequency is genuinely measurable (§22, deferred to M9). NOTE
|
||||
// (measured characteristic, not yet optimized -- see
|
||||
// hardware/v2/logs/decisions.log DEC-0006): the bank-swap-and-check
|
||||
// control path itself costs a minimum 1 idle cycle per tile handoff
|
||||
// even when the next bank was already prefetched in time, unlike
|
||||
// neural_processor.v's own zero-gap tile acceptance -- a real,
|
||||
// deliberately-not-hidden overhead of this first Memory Manager
|
||||
// implementation, left for M10 (Optimization) to revisit with real
|
||||
// stall-percentage data (§22) rather than optimized blindly now.
|
||||
//
|
||||
// One job = one neuron's worth of tiles (n_tiles), read from x_base/
|
||||
// w_base (PSRAM byte addresses), followed by writing the single
|
||||
// INT8 result back to result_addr. The result write only happens
|
||||
// after the last tile has been handed off and prefetch_engine is
|
||||
// idle (temporally disjoint from prefetching by construction), so no
|
||||
// separate backend arbiter is needed at this milestone -- see
|
||||
// decisions.log DEC-0006 for why, and what changes once multiple
|
||||
// concurrent jobs/processors need to share one backend port
|
||||
// (deferred, not yet needed).
|
||||
// ================================================================
|
||||
|
||||
module memory_manager #(
|
||||
parameter DATA_WIDTH = 8,
|
||||
parameter P_IN = 8,
|
||||
parameter ADDR_WIDTH = 23
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
// ---- job control (from a future Neural Director, M5; driven
|
||||
// directly by a testbench at M4) ----
|
||||
input wire job_start,
|
||||
input wire [ADDR_WIDTH-1:0] x_base,
|
||||
input wire [ADDR_WIDTH-1:0] w_base,
|
||||
input wire [15:0] n_tiles,
|
||||
input wire [ADDR_WIDTH-1:0] result_addr,
|
||||
output reg job_done, // one-cycle pulse
|
||||
|
||||
// ---- Neural Processor-facing operand stream (mirrors
|
||||
// neural_processor.v's own operand port exactly) ----
|
||||
output reg operand_valid,
|
||||
input wire operand_ready,
|
||||
output reg signed [DATA_WIDTH*P_IN-1:0] input_data,
|
||||
output reg signed [DATA_WIDTH*P_IN-1:0] weight_data,
|
||||
output reg tile_last,
|
||||
|
||||
// ---- Neural Processor-facing result consumption ----
|
||||
input wire result_valid,
|
||||
output reg result_ready,
|
||||
input wire signed [DATA_WIDTH-1:0] result_data,
|
||||
|
||||
// ---- Memory Backend Interface (matches int8_memory_access.v) ----
|
||||
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
|
||||
);
|
||||
|
||||
localparam MM_IDLE = 3'd0;
|
||||
localparam MM_PREFETCH_FIRST = 3'd1;
|
||||
localparam MM_STREAM = 3'd2;
|
||||
localparam MM_WAIT_RESULT = 3'd3;
|
||||
localparam MM_WRITE_RESULT = 3'd4;
|
||||
localparam MM_DONE = 3'd5;
|
||||
|
||||
reg [2:0] state;
|
||||
|
||||
reg [ADDR_WIDTH-1:0] x_base_reg, w_base_reg, result_addr_reg;
|
||||
reg [15:0] n_tiles_reg;
|
||||
reg [15:0] tile_idx; // tile currently presented (bank `current`)
|
||||
reg current_bank; // 0 or 1
|
||||
|
||||
reg [1:0] bank_ready; // bank_ready[b] = bank b holds valid, unconsumed prefetched data
|
||||
|
||||
// ---- double-buffer storage (owned here, filled by prefetch_engine) ----
|
||||
reg signed [DATA_WIDTH*P_IN-1:0] bank_x [0:1];
|
||||
reg signed [DATA_WIDTH*P_IN-1:0] bank_w [0:1];
|
||||
|
||||
// ---- single prefetch_engine instance, retargeted per bank ----
|
||||
reg pf_start;
|
||||
reg [ADDR_WIDTH-1:0] pf_x_addr, pf_w_addr;
|
||||
wire pf_busy, pf_done;
|
||||
wire signed [DATA_WIDTH*P_IN-1:0] pf_tile_x, pf_tile_w;
|
||||
|
||||
reg pf_target_bank; // which bank the CURRENTLY-running (or just-launched) prefetch fills
|
||||
|
||||
// Single-entry pending-request register: prefetch_engine is one
|
||||
// instance, so a NEW fetch can only be launched once it has
|
||||
// genuinely returned to idle (pf_busy low) -- issuing pf_start
|
||||
// while it is still mid-fetch would silently corrupt
|
||||
// pf_target_bank for the fetch ALREADY in flight (a real bug
|
||||
// found and fixed here -- see hardware/v2/logs/errors.log
|
||||
// ERR-0006). Every "kick a prefetch" site below sets this
|
||||
// descriptor instead of touching pf_start directly; a single
|
||||
// always-active rule issues pf_start once the engine is free.
|
||||
reg pf_pending;
|
||||
reg [ADDR_WIDTH-1:0] pf_pending_x, pf_pending_w;
|
||||
reg pf_pending_bank;
|
||||
|
||||
// prefetch_engine drives its OWN internal backend wires; the
|
||||
// result-write FSM below drives its own. A combinational mux
|
||||
// (never both at once, by construction -- see file header)
|
||||
// selects which one actually reaches the real output port,
|
||||
// avoiding a two-driver conflict on mem_req/mem_wr/mem_addr/
|
||||
// mem_wdata.
|
||||
wire pf_mem_req, pf_mem_wr;
|
||||
wire [ADDR_WIDTH-1:0] pf_mem_addr;
|
||||
wire signed [7:0] pf_mem_wdata;
|
||||
|
||||
prefetch_engine #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) u_prefetch (
|
||||
.clk(clk), .rst(rst),
|
||||
.fetch_start(pf_start), .x_addr(pf_x_addr), .w_addr(pf_w_addr),
|
||||
.fetch_busy(pf_busy), .fetch_done(pf_done),
|
||||
.tile_x(pf_tile_x), .tile_w(pf_tile_w),
|
||||
.mem_req(pf_mem_req), .mem_wr(pf_mem_wr), .mem_addr(pf_mem_addr), .mem_wdata(pf_mem_wdata),
|
||||
.mem_rdata(mem_rdata), .mem_ready(mem_ready)
|
||||
);
|
||||
|
||||
reg wr_mem_req;
|
||||
reg [ADDR_WIDTH-1:0] wr_mem_addr;
|
||||
reg signed [7:0] wr_mem_wdata;
|
||||
|
||||
// wr_mem_req is SET while state==MM_WRITE_RESULT but only becomes
|
||||
// valid (via NBA) the FOLLOWING cycle, i.e. while state==MM_DONE --
|
||||
// the mux must select the write-back source across BOTH states,
|
||||
// not just the one that issues it (an off-by-one here silently
|
||||
// dropped the write request entirely -- found and fixed here, see
|
||||
// hardware/v2/logs/errors.log ERR-0006).
|
||||
wire wr_active = (state == MM_WRITE_RESULT) || (state == MM_DONE);
|
||||
assign mem_req = wr_active ? wr_mem_req : pf_mem_req;
|
||||
assign mem_wr = wr_active ? 1'b1 : pf_mem_wr;
|
||||
assign mem_addr = wr_active ? wr_mem_addr : pf_mem_addr;
|
||||
assign mem_wdata = wr_active ? wr_mem_wdata : pf_mem_wdata;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= MM_IDLE;
|
||||
job_done <= 1'b0;
|
||||
operand_valid <= 1'b0;
|
||||
tile_last <= 1'b0;
|
||||
input_data <= {DATA_WIDTH*P_IN{1'b0}};
|
||||
weight_data <= {DATA_WIDTH*P_IN{1'b0}};
|
||||
result_ready <= 1'b0;
|
||||
pf_start <= 1'b0;
|
||||
current_bank <= 1'b0;
|
||||
bank_ready <= 2'b00;
|
||||
tile_idx <= 16'h0;
|
||||
wr_mem_req <= 1'b0;
|
||||
wr_mem_addr <= {ADDR_WIDTH{1'b0}};
|
||||
wr_mem_wdata <= 8'sd0;
|
||||
pf_pending <= 1'b0;
|
||||
end else begin
|
||||
job_done <= 1'b0;
|
||||
pf_start <= 1'b0;
|
||||
result_ready <= 1'b0;
|
||||
|
||||
// Latch a completed prefetch into its target bank.
|
||||
if (pf_done) begin
|
||||
bank_x[pf_target_bank] <= pf_tile_x;
|
||||
bank_w[pf_target_bank] <= pf_tile_w;
|
||||
bank_ready[pf_target_bank] <= 1'b1;
|
||||
end
|
||||
|
||||
// Issue a pending fetch request as soon as the (single)
|
||||
// prefetch engine is genuinely free. The `!pf_start` guard
|
||||
// is required, not cosmetic: pf_busy does not read 1 until
|
||||
// the cycle AFTER pf_start was first observed (prefetch_
|
||||
// engine's own fetch_busy<=1 is one clock behind its own
|
||||
// fetch_start sampling), so checking !pf_busy alone leaves
|
||||
// a genuine one-cycle window where a second pending
|
||||
// request would fire on top of the one just launched,
|
||||
// silently corrupting pf_target_bank for the fetch already
|
||||
// in flight (found and fixed here -- see
|
||||
// hardware/v2/logs/errors.log ERR-0006).
|
||||
if (pf_pending && !pf_busy && !pf_start) begin
|
||||
pf_start <= 1'b1;
|
||||
pf_x_addr <= pf_pending_x;
|
||||
pf_w_addr <= pf_pending_w;
|
||||
pf_target_bank <= pf_pending_bank;
|
||||
pf_pending <= 1'b0;
|
||||
end
|
||||
|
||||
case (state)
|
||||
|
||||
MM_IDLE: begin
|
||||
if (job_start) begin
|
||||
x_base_reg <= x_base;
|
||||
w_base_reg <= w_base;
|
||||
n_tiles_reg <= n_tiles;
|
||||
result_addr_reg <= result_addr;
|
||||
tile_idx <= 16'h0;
|
||||
current_bank <= 1'b0;
|
||||
bank_ready <= 2'b00;
|
||||
operand_valid <= 1'b0;
|
||||
// kick off the very first fetch (tile 0 into bank 0)
|
||||
pf_pending <= 1'b1;
|
||||
pf_pending_x <= x_base;
|
||||
pf_pending_w <= w_base;
|
||||
pf_pending_bank <= 1'b0;
|
||||
state <= MM_PREFETCH_FIRST;
|
||||
end
|
||||
end
|
||||
|
||||
MM_PREFETCH_FIRST: begin
|
||||
if (bank_ready[0] || (pf_done && pf_target_bank == 1'b0)) begin
|
||||
// Present tile 0; concurrently start prefetching
|
||||
// tile 1 into bank 1, if there is one.
|
||||
operand_valid <= 1'b1;
|
||||
input_data <= pf_done ? pf_tile_x : bank_x[0];
|
||||
weight_data <= pf_done ? pf_tile_w : bank_w[0];
|
||||
tile_last <= (n_tiles_reg == 16'h1);
|
||||
if (n_tiles_reg > 16'h1) begin
|
||||
pf_pending <= 1'b1;
|
||||
pf_pending_x <= x_base_reg + P_IN[ADDR_WIDTH-1:0];
|
||||
pf_pending_w <= w_base_reg + P_IN[ADDR_WIDTH-1:0];
|
||||
pf_pending_bank <= 1'b1;
|
||||
end
|
||||
state <= MM_STREAM;
|
||||
end
|
||||
end
|
||||
|
||||
MM_STREAM: begin
|
||||
if (operand_valid && operand_ready) begin
|
||||
// This tile consumed; free its bank, swap.
|
||||
bank_ready[current_bank] <= 1'b0;
|
||||
current_bank <= ~current_bank;
|
||||
tile_idx <= tile_idx + 16'h1;
|
||||
operand_valid <= 1'b0; // re-asserted below once the new bank is ready
|
||||
|
||||
if (tile_idx + 16'h1 == n_tiles_reg) begin
|
||||
// That was the last tile -- nothing more to present.
|
||||
state <= MM_WAIT_RESULT;
|
||||
end else if (tile_idx + 16'h2 < n_tiles_reg) begin
|
||||
// Queue a prefetch for the tile AFTER next into
|
||||
// the bank we just freed (current_bank, pre-swap)
|
||||
// -- it will actually launch once the (single)
|
||||
// prefetch engine is free (see the pf_pending
|
||||
// issue rule above); it is very likely still
|
||||
// busy with the tile-N+1 fetch kicked off on the
|
||||
// PREVIOUS handoff, so this almost always queues
|
||||
// rather than launching immediately.
|
||||
pf_pending <= 1'b1;
|
||||
pf_pending_x <= x_base_reg + (tile_idx + 16'h2) * P_IN[ADDR_WIDTH-1:0];
|
||||
pf_pending_w <= w_base_reg + (tile_idx + 16'h2) * P_IN[ADDR_WIDTH-1:0];
|
||||
pf_pending_bank <= current_bank; // the one just freed
|
||||
end
|
||||
end else if (!operand_valid) begin
|
||||
// Waiting for the new current bank to become ready
|
||||
// (either just swapped, or a stall still in
|
||||
// progress).
|
||||
if (bank_ready[current_bank] && tile_idx < n_tiles_reg) begin
|
||||
operand_valid <= 1'b1;
|
||||
input_data <= bank_x[current_bank];
|
||||
weight_data <= bank_w[current_bank];
|
||||
tile_last <= (tile_idx == n_tiles_reg - 16'h1);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
MM_WAIT_RESULT: begin
|
||||
result_ready <= 1'b1;
|
||||
if (result_valid && result_ready) begin
|
||||
wr_mem_wdata <= result_data;
|
||||
state <= MM_WRITE_RESULT;
|
||||
end
|
||||
end
|
||||
|
||||
MM_WRITE_RESULT: begin
|
||||
// prefetch_engine is guaranteed idle here (no more
|
||||
// tiles to fetch for this job), so driving the shared
|
||||
// backend port directly is safe -- see file header.
|
||||
wr_mem_req <= 1'b1;
|
||||
wr_mem_addr <= result_addr_reg;
|
||||
state <= MM_DONE;
|
||||
end
|
||||
|
||||
MM_DONE: begin
|
||||
wr_mem_req <= 1'b0;
|
||||
if (mem_ready) begin
|
||||
job_done <= 1'b1;
|
||||
state <= MM_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
default: state <= MM_IDLE;
|
||||
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,129 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// ================================================================
|
||||
// FPGA-Neural V2 -- Prefetch Engine (M4, docs/v2-description.md §13)
|
||||
//
|
||||
// Fetches ONE tile (P_IN activation bytes + P_IN weight bytes) from
|
||||
// the byte-level Memory Backend Interface into a pair of output
|
||||
// registers, sequentially (2*P_IN single-byte transactions -- the
|
||||
// same byte-at-a-time convention hardware/v1/rtl/neuron_memory.v
|
||||
// already uses against the same backend, reused unmodified here).
|
||||
//
|
||||
// This module fetches exactly one tile per fetch_start pulse; the
|
||||
// double-buffering strategy itself (§13: compute tile N while
|
||||
// prefetching tile N+1, swap, repeat) is memory_manager.v's
|
||||
// responsibility -- it retargets this single engine at whichever
|
||||
// bank currently needs refilling, so no internal arbitration between
|
||||
// multiple fetch engines sharing the backend port is ever needed.
|
||||
//
|
||||
// The backend port (mem_req/mem_wr/mem_addr/mem_wdata/mem_rdata/
|
||||
// mem_ready) matches hardware/v1/rtl/int8_memory_access.v's contract
|
||||
// exactly -- this engine can sit directly on top of that unmodified
|
||||
// V1 module (which itself sits on memory_interface.v ->
|
||||
// psram_controller.v, also unmodified, per §15).
|
||||
// ================================================================
|
||||
|
||||
module prefetch_engine #(
|
||||
parameter DATA_WIDTH = 8,
|
||||
parameter P_IN = 8,
|
||||
parameter ADDR_WIDTH = 23
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire fetch_start,
|
||||
input wire [ADDR_WIDTH-1:0] x_addr, // base addr of this tile's P_IN X bytes
|
||||
input wire [ADDR_WIDTH-1:0] w_addr, // base addr of this tile's P_IN W bytes
|
||||
output reg fetch_busy,
|
||||
output reg fetch_done, // one-cycle pulse
|
||||
output reg signed [DATA_WIDTH*P_IN-1:0] tile_x,
|
||||
output reg signed [DATA_WIDTH*P_IN-1:0] tile_w,
|
||||
|
||||
output reg mem_req,
|
||||
output reg mem_wr,
|
||||
output reg [ADDR_WIDTH-1:0] mem_addr,
|
||||
output reg signed [7:0] mem_wdata,
|
||||
input wire signed [7:0] mem_rdata,
|
||||
input wire mem_ready
|
||||
);
|
||||
|
||||
localparam ST_IDLE = 2'd0;
|
||||
localparam ST_READ_X = 2'd1;
|
||||
localparam ST_READ_W = 2'd2;
|
||||
localparam ST_DONE = 2'd3;
|
||||
|
||||
reg [1:0] state;
|
||||
reg [$clog2(P_IN+1)-1:0] byte_idx;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= ST_IDLE;
|
||||
byte_idx <= 0;
|
||||
fetch_busy <= 1'b0;
|
||||
fetch_done <= 1'b0;
|
||||
mem_req <= 1'b0;
|
||||
mem_wr <= 1'b0;
|
||||
mem_addr <= {ADDR_WIDTH{1'b0}};
|
||||
mem_wdata <= 8'sd0;
|
||||
end else begin
|
||||
mem_req <= 1'b0;
|
||||
fetch_done <= 1'b0;
|
||||
|
||||
case (state)
|
||||
|
||||
ST_IDLE: begin
|
||||
if (fetch_start) begin
|
||||
fetch_busy <= 1'b1;
|
||||
byte_idx <= 0;
|
||||
mem_req <= 1'b1;
|
||||
mem_wr <= 1'b0;
|
||||
mem_addr <= x_addr;
|
||||
state <= ST_READ_X;
|
||||
end
|
||||
end
|
||||
|
||||
ST_READ_X: begin
|
||||
if (mem_ready) begin
|
||||
tile_x[byte_idx*DATA_WIDTH +: DATA_WIDTH] <= mem_rdata;
|
||||
if (byte_idx == P_IN[$clog2(P_IN+1)-1:0] - 1'b1) begin
|
||||
byte_idx <= 0;
|
||||
mem_req <= 1'b1;
|
||||
mem_wr <= 1'b0;
|
||||
mem_addr <= w_addr;
|
||||
state <= ST_READ_W;
|
||||
end else begin
|
||||
byte_idx <= byte_idx + 1'b1;
|
||||
mem_req <= 1'b1;
|
||||
mem_wr <= 1'b0;
|
||||
mem_addr <= x_addr + byte_idx + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ST_READ_W: begin
|
||||
if (mem_ready) begin
|
||||
tile_w[byte_idx*DATA_WIDTH +: DATA_WIDTH] <= mem_rdata;
|
||||
if (byte_idx == P_IN[$clog2(P_IN+1)-1:0] - 1'b1) begin
|
||||
state <= ST_DONE;
|
||||
end else begin
|
||||
byte_idx <= byte_idx + 1'b1;
|
||||
mem_req <= 1'b1;
|
||||
mem_wr <= 1'b0;
|
||||
mem_addr <= w_addr + byte_idx + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ST_DONE: begin
|
||||
fetch_busy <= 1'b0;
|
||||
fetch_done <= 1'b1;
|
||||
state <= ST_IDLE;
|
||||
end
|
||||
|
||||
default: state <= ST_IDLE;
|
||||
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,287 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// ============================================================
|
||||
// M4 testbench (docs/v2-description.md §12/§13/§15/§20): full
|
||||
// end-to-end stack -- memory_manager.v + prefetch_engine.v (V2, M4)
|
||||
// driving a REAL hardware/v2/rtl/neural_processor.v (M1) on one side,
|
||||
// and the REAL, UNMODIFIED hardware/v1 PSRAM backend chain
|
||||
// (int8_memory_access -> memory_interface -> psram_controller ->
|
||||
// psram_model) on the other -- exactly the layering §15 mandates
|
||||
// ("Memory Manager -> Memory Backend Interface -> PSRAM Controller"),
|
||||
// with the backend reused byte-for-byte from the frozen V1 tree.
|
||||
//
|
||||
// Verified with Verilator (see decisions.log DEC-0004).
|
||||
//
|
||||
// Coverage:
|
||||
// - end-to-end job: PSRAM pre-loaded with real X/W bytes at known
|
||||
// addresses, memory_manager fetches them (double-buffered
|
||||
// prefetch across multiple tiles), feeds neural_processor, and
|
||||
// writes the computed result back to PSRAM -- read back
|
||||
// independently afterward and checked against a hand-computed
|
||||
// expectation (an oracle independent of the RTL under test).
|
||||
// - multi-tile job (prefetch actually has to overlap tile N+1's
|
||||
// fetch with tile N's compute, not just single-tile).
|
||||
// - "poison" bytes surrounding the real operand region, to catch
|
||||
// any off-by-one addressing error in prefetch_engine.
|
||||
// ============================================================
|
||||
|
||||
module tb;
|
||||
|
||||
localparam ADDR_WIDTH = 23;
|
||||
localparam DATA_WIDTH = 8;
|
||||
localparam P_IN = 8;
|
||||
localparam ACC_WIDTH = 32;
|
||||
localparam PSRAM_DATA_WIDTH = 16;
|
||||
localparam CLK_PERIOD = 12.5; // 80 MHz, matches psram_controller's CLK_FREQ_MHZ
|
||||
|
||||
reg clk, rst;
|
||||
initial begin clk = 1'b0; forever #(CLK_PERIOD/2.0) clk = ~clk; end
|
||||
|
||||
// ---- memory_manager <-> neural_processor ----
|
||||
reg job_start;
|
||||
reg [ADDR_WIDTH-1:0] x_base, w_base, result_addr;
|
||||
reg [15:0] n_tiles;
|
||||
wire job_done;
|
||||
|
||||
wire mm_operand_valid, mm_operand_ready;
|
||||
wire signed [DATA_WIDTH*P_IN-1:0] mm_input_data, mm_weight_data;
|
||||
wire mm_tile_last;
|
||||
|
||||
wire mm_result_valid, mm_result_ready;
|
||||
wire signed [DATA_WIDTH-1:0] mm_result_data;
|
||||
|
||||
// ---- memory_manager <-> int8_memory_access (Memory Backend Interface) ----
|
||||
wire mem_req, mem_wr;
|
||||
wire [ADDR_WIDTH-1:0] mem_addr;
|
||||
wire signed [7:0] mem_wdata;
|
||||
wire signed [7:0] mem_rdata;
|
||||
wire mem_ready;
|
||||
|
||||
memory_manager #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) u_mm (
|
||||
.clk(clk), .rst(rst),
|
||||
.job_start(job_start), .x_base(x_base), .w_base(w_base),
|
||||
.n_tiles(n_tiles), .result_addr(result_addr), .job_done(job_done),
|
||||
.operand_valid(mm_operand_valid), .operand_ready(mm_operand_ready),
|
||||
.input_data(mm_input_data), .weight_data(mm_weight_data), .tile_last(mm_tile_last),
|
||||
.result_valid(mm_result_valid), .result_ready(mm_result_ready), .result_data(mm_result_data),
|
||||
.mem_req(mem_req), .mem_wr(mem_wr), .mem_addr(mem_addr), .mem_wdata(mem_wdata),
|
||||
.mem_rdata(mem_rdata), .mem_ready(mem_ready)
|
||||
);
|
||||
|
||||
// ---- real Neural Processor (M1), driven entirely by memory_manager ----
|
||||
reg job_valid_np;
|
||||
wire job_ready_np;
|
||||
wire result_valid_np;
|
||||
wire signed [DATA_WIDTH-1:0] result_data_np;
|
||||
wire [15:0] result_node_id_np;
|
||||
wire [3:0] np_state;
|
||||
wire np_error;
|
||||
|
||||
neural_processor #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .ACC_WIDTH(ACC_WIDTH)
|
||||
) u_np (
|
||||
.clk(clk), .rst(rst),
|
||||
.job_valid(job_valid_np), .job_ready(job_ready_np),
|
||||
.job_node_id(16'h0), .job_bias(8'sd0), .job_activation(2'd1), // ACT_RELU
|
||||
.operand_valid(mm_operand_valid), .operand_ready(mm_operand_ready),
|
||||
.input_data(mm_input_data), .weight_data(mm_weight_data), .tile_last(mm_tile_last),
|
||||
.result_valid(result_valid_np), .result_ready(mm_result_ready),
|
||||
.result_data(result_data_np), .result_node_id(result_node_id_np),
|
||||
.np_state(np_state), .np_error(np_error)
|
||||
);
|
||||
assign mm_result_valid = result_valid_np;
|
||||
assign mm_result_data = result_data_np;
|
||||
|
||||
// job_valid_np must pulse once per memory_manager job, synchronized
|
||||
// to job_start (both start a "job" at the same moment: memory_manager
|
||||
// begins prefetching tile 0 while neural_processor waits in NP_IDLE
|
||||
// until tile 0 actually arrives, exactly like any other operand
|
||||
// producer feeding it).
|
||||
always @(posedge clk) begin
|
||||
if (rst) job_valid_np <= 1'b0;
|
||||
else if (job_start) job_valid_np <= 1'b1;
|
||||
else if (job_valid_np && job_ready_np) job_valid_np <= 1'b0;
|
||||
end
|
||||
|
||||
// ---- REAL, unmodified V1 backend chain ----
|
||||
wire if_mem_req, if_mem_wr;
|
||||
wire [ADDR_WIDTH-1:0] if_mem_addr;
|
||||
wire [PSRAM_DATA_WIDTH-1:0] if_mem_wdata;
|
||||
wire if_mem_lb_n, if_mem_ub_n;
|
||||
wire [PSRAM_DATA_WIDTH-1:0] if_mem_rdata;
|
||||
wire if_mem_ready;
|
||||
|
||||
int8_memory_access #(.ADDR_WIDTH(ADDR_WIDTH)) u_int8 (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(mem_req), .wr(mem_wr), .addr(mem_addr), .wdata(mem_wdata),
|
||||
.rdata(mem_rdata), .ready(mem_ready),
|
||||
.mem_req(if_mem_req), .mem_wr(if_mem_wr), .mem_addr(if_mem_addr), .mem_wdata(if_mem_wdata),
|
||||
.mem_lb_n(if_mem_lb_n), .mem_ub_n(if_mem_ub_n),
|
||||
.mem_rdata(if_mem_rdata), .mem_ready(if_mem_ready)
|
||||
);
|
||||
|
||||
wire pc_mem_req, pc_mem_wr;
|
||||
wire [ADDR_WIDTH-1:0] pc_mem_addr;
|
||||
wire [PSRAM_DATA_WIDTH-1:0] pc_mem_wdata;
|
||||
wire pc_mem_lb_n, pc_mem_ub_n;
|
||||
wire [PSRAM_DATA_WIDTH-1:0] pc_mem_rdata;
|
||||
wire pc_mem_ready;
|
||||
|
||||
memory_interface #(.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(PSRAM_DATA_WIDTH)) u_memif (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(if_mem_req), .wr(if_mem_wr), .addr(if_mem_addr), .wdata(if_mem_wdata),
|
||||
.lb_n(if_mem_lb_n), .ub_n(if_mem_ub_n),
|
||||
.rdata(if_mem_rdata), .ready(if_mem_ready),
|
||||
.mem_req(pc_mem_req), .mem_wr(pc_mem_wr), .mem_addr(pc_mem_addr), .mem_wdata(pc_mem_wdata),
|
||||
.mem_lb_n(pc_mem_lb_n), .mem_ub_n(pc_mem_ub_n),
|
||||
.mem_rdata(pc_mem_rdata), .mem_ready(pc_mem_ready)
|
||||
);
|
||||
|
||||
wire [ADDR_WIDTH-1:0] psram_a;
|
||||
wire [PSRAM_DATA_WIDTH-1:0] psram_dq;
|
||||
wire psram_ce_n, psram_oe_n, psram_we_n, psram_lb_n, psram_ub_n, psram_zz_n;
|
||||
|
||||
psram_controller #(
|
||||
.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(PSRAM_DATA_WIDTH), .CLK_FREQ_MHZ(80)
|
||||
) u_psram_ctrl (
|
||||
.clk(clk), .rst(rst),
|
||||
.mem_req(pc_mem_req), .mem_wr(pc_mem_wr), .mem_addr(pc_mem_addr), .mem_wdata(pc_mem_wdata),
|
||||
.mem_lb_n(pc_mem_lb_n), .mem_ub_n(pc_mem_ub_n),
|
||||
.mem_rdata(pc_mem_rdata), .mem_ready(pc_mem_ready),
|
||||
.psram_a(psram_a), .psram_dq(psram_dq),
|
||||
.psram_ce_n(psram_ce_n), .psram_oe_n(psram_oe_n), .psram_we_n(psram_we_n),
|
||||
.psram_lb_n(psram_lb_n), .psram_ub_n(psram_ub_n), .psram_zz_n(psram_zz_n)
|
||||
);
|
||||
|
||||
psram_model #(.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(PSRAM_DATA_WIDTH), .DEPTH(16384)) u_psram (
|
||||
.clk(clk), .a(psram_a), .dq(psram_dq),
|
||||
.ce_n(psram_ce_n), .oe_n(psram_oe_n), .we_n(psram_we_n),
|
||||
.lb_n(psram_lb_n), .ub_n(psram_ub_n), .zz_n(psram_zz_n)
|
||||
);
|
||||
|
||||
// ---- helper: poke one byte directly into psram_model's backing
|
||||
// array (test setup only, bypasses the real write path -- same
|
||||
// convention as hardware/v1/sim's own testbenches that pre-load
|
||||
// psram_model for read-side tests). ----
|
||||
task automatic poke_byte(input [ADDR_WIDTH-1:0] byte_addr, input [7:0] val);
|
||||
reg [ADDR_WIDTH-2:0] word_addr;
|
||||
begin
|
||||
word_addr = byte_addr[ADDR_WIDTH-1:1];
|
||||
if (byte_addr[0] == 1'b0)
|
||||
u_psram.mem[word_addr][7:0] = val;
|
||||
else
|
||||
u_psram.mem[word_addr][15:8] = val;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic peek_byte(input [ADDR_WIDTH-1:0] byte_addr, output [7:0] val);
|
||||
reg [ADDR_WIDTH-2:0] word_addr;
|
||||
begin
|
||||
word_addr = byte_addr[ADDR_WIDTH-1:1];
|
||||
val = (byte_addr[0] == 1'b0) ? u_psram.mem[word_addr][7:0] : u_psram.mem[word_addr][15:8];
|
||||
end
|
||||
endtask
|
||||
|
||||
integer errors, tests;
|
||||
integer i;
|
||||
|
||||
function automatic signed [7:0] expect_relu(input integer acc);
|
||||
begin
|
||||
if (acc <= 0) expect_relu = 0;
|
||||
else if (acc > 127) expect_relu = 127;
|
||||
else expect_relu = acc[7:0];
|
||||
end
|
||||
endfunction
|
||||
|
||||
task automatic run_job(
|
||||
input [ADDR_WIDTH-1:0] xb, input [ADDR_WIDTH-1:0] wb,
|
||||
input [15:0] nt, input [ADDR_WIDTH-1:0] resaddr,
|
||||
input signed [7:0] exp_y
|
||||
);
|
||||
integer wd;
|
||||
reg [7:0] rb;
|
||||
begin
|
||||
@(posedge clk);
|
||||
tests = tests + 1;
|
||||
x_base = xb; w_base = wb; n_tiles = nt; result_addr = resaddr;
|
||||
job_start = 1'b1;
|
||||
@(posedge clk);
|
||||
job_start = 1'b0;
|
||||
wd = 0;
|
||||
while (!job_done && wd < 2000) begin
|
||||
@(posedge clk);
|
||||
wd = wd + 1;
|
||||
end
|
||||
if (!job_done) begin
|
||||
$display("FAIL job xb=%0d: no job_done within watchdog (%0d cycles)", xb, wd);
|
||||
errors = errors + 1;
|
||||
end else begin
|
||||
peek_byte(resaddr, rb);
|
||||
if (rb !== exp_y[7:0]) begin
|
||||
$display("FAIL job xb=%0d: PSRAM result byte=%0d expected=%0d (%0d cycles)", xb, $signed(rb), exp_y, wd);
|
||||
errors = errors + 1;
|
||||
end else begin
|
||||
$display("PASS job xb=%0d: PSRAM result byte=%0d correct, %0d cycles, n_tiles=%0d", xb, $signed(rb), wd, nt);
|
||||
end
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
errors = 0; tests = 0;
|
||||
rst = 1; job_start = 0; x_base = 0; w_base = 0; n_tiles = 0; result_addr = 0;
|
||||
repeat(5) @(posedge clk);
|
||||
rst = 0;
|
||||
|
||||
// Wait for the real PSRAM controller's power-up sequence
|
||||
// (~150us @ 80MHz) before issuing any request -- same
|
||||
// requirement/convention documented in
|
||||
// hardware/v1/docs/FPGA-NeuralNetwork-Engine.md's
|
||||
// WRITE_RAM/READ_RAM backpressure warning.
|
||||
wait (u_psram_ctrl.state == u_psram_ctrl.STATE_IDLE);
|
||||
@(posedge clk);
|
||||
|
||||
// ---- pre-load PSRAM: X at 0x1000, W at 0x2000, 3 tiles
|
||||
// (24 inputs), with "poison" bytes immediately before/after
|
||||
// the real region to catch any off-by-one in prefetch_engine's
|
||||
// addressing. ----
|
||||
for (i = -4; i < 24+4; i = i + 1) begin
|
||||
poke_byte(23'h1000 + i, 8'sd99); // poison
|
||||
poke_byte(23'h2000 + i, 8'sd99); // poison
|
||||
end
|
||||
for (i = 0; i < 24; i = i + 1) begin
|
||||
poke_byte(23'h1000 + i, 8'sd2); // X = 2
|
||||
poke_byte(23'h2000 + i, 8'sd3); // W = 3
|
||||
end
|
||||
// acc = 24 * 2 * 3 = 144 -> ACT_RELU saturates to 127
|
||||
run_job(23'h1000, 23'h2000, 16'd3, 23'h3000, expect_relu(144));
|
||||
|
||||
// ---- second job: 1 tile (8 inputs), smaller, no saturation ----
|
||||
for (i = 0; i < 8; i = i + 1) begin
|
||||
poke_byte(23'h4000 + i, 8'sd1); // X = 1
|
||||
poke_byte(23'h5000 + i, 8'sd4); // W = 4
|
||||
end
|
||||
// acc = 8*1*4 = 32
|
||||
run_job(23'h4000, 23'h5000, 16'd1, 23'h3001, expect_relu(32));
|
||||
|
||||
// ---- third job: 5 tiles (40 inputs), exercises the
|
||||
// steady-state double-buffer swap across more than 2 tiles. ----
|
||||
for (i = 0; i < 40; i = i + 1) begin
|
||||
poke_byte(23'h6000 + i, 8'sd1); // X = 1
|
||||
poke_byte(23'h7000 + i, 8'sd1); // W = 1
|
||||
end
|
||||
// acc = 40*1*1 = 40
|
||||
run_job(23'h6000, 23'h7000, 16'd5, 23'h3002, expect_relu(40));
|
||||
|
||||
$display("========================================");
|
||||
if (errors == 0)
|
||||
$display("ALL %0d TESTS PASSED (memory_manager + prefetch_engine, real V1 PSRAM backend, real neural_processor)", tests);
|
||||
else
|
||||
$display("FAILED: %0d/%0d test(s) had errors -- see messages above", errors, tests);
|
||||
$display("========================================");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,71 @@
|
||||
// ================================================================
|
||||
// SYNTHESIS-ONLY TIMING HARNESS -- NOT a functional deliverable.
|
||||
// Same rationale/pattern as harness_neural_processor_array.v (see its
|
||||
// header and hardware/v2/logs/errors.log ERR-0005): memory_manager's
|
||||
// wide ports (input_data/weight_data alone, 64 bits) exceed the
|
||||
// LFE5U-45F's TRELLIS_IO budget as a bare top-level module. Drives
|
||||
// them from an internal LFSR and reduces outputs to a small
|
||||
// checksum, keeping only clk/rst/seed/checksum as real pins, to get
|
||||
// a representative Fmax for memory_manager's own logic/routing.
|
||||
// ================================================================
|
||||
|
||||
module harness_memory_manager #(
|
||||
parameter DATA_WIDTH = 8,
|
||||
parameter P_IN = 8,
|
||||
parameter ADDR_WIDTH = 23
|
||||
)(
|
||||
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_start = lfsr[0];
|
||||
wire [ADDR_WIDTH-1:0] x_base = lfsr[ADDR_WIDTH-1:0];
|
||||
wire [ADDR_WIDTH-1:0] w_base = {lfsr[7:0], lfsr[ADDR_WIDTH-9:0]};
|
||||
wire [15:0] n_tiles = lfsr[15:0];
|
||||
wire [ADDR_WIDTH-1:0] result_addr = {lfsr[3:0], lfsr[ADDR_WIDTH-5:0]};
|
||||
wire operand_ready = lfsr[2];
|
||||
wire result_valid = lfsr[3];
|
||||
wire signed [DATA_WIDTH-1:0] result_data = lfsr[7:0];
|
||||
wire signed [7:0] mem_rdata = lfsr[15:8];
|
||||
wire mem_ready = lfsr[4];
|
||||
|
||||
wire job_done;
|
||||
wire operand_valid;
|
||||
wire signed [DATA_WIDTH*P_IN-1:0] input_data, weight_data;
|
||||
wire tile_last;
|
||||
wire result_ready;
|
||||
wire mem_req, mem_wr;
|
||||
wire [ADDR_WIDTH-1:0] mem_addr;
|
||||
wire signed [7:0] mem_wdata;
|
||||
|
||||
memory_manager #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) dut (
|
||||
.clk(clk), .rst(rst),
|
||||
.job_start(job_start), .x_base(x_base), .w_base(w_base),
|
||||
.n_tiles(n_tiles), .result_addr(result_addr), .job_done(job_done),
|
||||
.operand_valid(operand_valid), .operand_ready(operand_ready),
|
||||
.input_data(input_data), .weight_data(weight_data), .tile_last(tile_last),
|
||||
.result_valid(result_valid), .result_ready(result_ready), .result_data(result_data),
|
||||
.mem_req(mem_req), .mem_wr(mem_wr), .mem_addr(mem_addr), .mem_wdata(mem_wdata),
|
||||
.mem_rdata(mem_rdata), .mem_ready(mem_ready)
|
||||
);
|
||||
|
||||
reg [7:0] chk;
|
||||
always @(posedge clk) begin
|
||||
if (rst) chk <= 8'h0;
|
||||
else chk <= chk ^ input_data[7:0] ^ weight_data[7:0] ^ {7'h0, job_done}
|
||||
^ {6'h0, tile_last, result_ready} ^ mem_addr[7:0]
|
||||
^ {7'h0, mem_req} ^ {7'h0, mem_wr} ^ mem_wdata;
|
||||
end
|
||||
assign checksum = chk;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,302 @@
|
||||
|
||||
|
||||
Info: Logic utilisation before packing:
|
||||
Info: Total LUT4s: 349/43848 0%
|
||||
Info: logic LUTs: 257/43848 0%
|
||||
Info: carry LUTs: 92/43848 0%
|
||||
Info: RAM LUTs: 0/ 5481 0%
|
||||
Info: RAMW LUTs: 0/10962 0%
|
||||
|
||||
Info: Total DFFs: 203/43848 0%
|
||||
|
||||
Info: Packing IOs..
|
||||
Info: Packing constants..
|
||||
Info: Packing carries...
|
||||
Info: Packing LUTs...
|
||||
Info: Packing LUT5-7s...
|
||||
Info: Packing FFs...
|
||||
Info: 72 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: 0x6e129573
|
||||
|
||||
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: 203/ 43848 0%
|
||||
Info: TRELLIS_COMB: 383/ 43848 0%
|
||||
Info: TRELLIS_RAMW: 0/ 5481 0%
|
||||
|
||||
Info: Placed 0 cells based on constraints.
|
||||
Info: Creating initial analytic placement for 333 cells, random placement wirelen = 28028.
|
||||
Info: at initial placer iter 0, wirelen = 1283
|
||||
Info: at initial placer iter 1, wirelen = 1162
|
||||
Info: at initial placer iter 2, wirelen = 1104
|
||||
Info: at initial placer iter 3, wirelen = 1098
|
||||
Info: Running main analytical placer, max placement attempts per cell = 45753.
|
||||
Info: at iteration #1, type ALL: wirelen solved = 1068, spread = 2127, legal = 2276; time = 0.01s
|
||||
Info: at iteration #2, type ALL: wirelen solved = 1116, spread = 2137, legal = 2271; time = 0.01s
|
||||
Info: at iteration #3, type ALL: wirelen solved = 1148, spread = 1979, legal = 2159; time = 0.00s
|
||||
Info: at iteration #4, type ALL: wirelen solved = 1209, spread = 1880, legal = 2056; time = 0.00s
|
||||
Info: at iteration #5, type ALL: wirelen solved = 1233, spread = 1999, legal = 2152; time = 0.00s
|
||||
Info: at iteration #6, type ALL: wirelen solved = 1282, spread = 1933, legal = 2171; time = 0.00s
|
||||
Info: at iteration #7, type ALL: wirelen solved = 1355, spread = 1921, legal = 2143; time = 0.00s
|
||||
Info: at iteration #8, type ALL: wirelen solved = 1411, spread = 1875, legal = 2071; time = 0.00s
|
||||
Info: at iteration #9, type ALL: wirelen solved = 1399, spread = 1911, legal = 2120; time = 0.00s
|
||||
Info: HeAP Placer Time: 0.07s
|
||||
Info: of which solving equations: 0.04s
|
||||
Info: of which spreading cells: 0.01s
|
||||
Info: of which strict legalisation: 0.00s
|
||||
|
||||
Info: Running simulated annealing placer for refinement.
|
||||
Info: at iteration #1: temp = 0.000000, timing cost = 91, wirelen = 2056
|
||||
Info: at iteration #5: temp = 0.000000, timing cost = 72, wirelen = 1619
|
||||
Info: at iteration #10: temp = 0.000000, timing cost = 93, wirelen = 1531
|
||||
Info: at iteration #15: temp = 0.000000, timing cost = 90, wirelen = 1514
|
||||
Info: at iteration #15: temp = 0.000000, timing cost = 89, wirelen = 1514
|
||||
Info: SA placement time 0.09s
|
||||
|
||||
Info: Max frequency for clock '$glbnet$clk$TRELLIS_IO_IN': 164.28 MHz (PASS at 80.00 MHz)
|
||||
|
||||
Info: Max delay <async> -> posedge $glbnet$clk$TRELLIS_IO_IN: 4.99 ns
|
||||
Info: Max delay posedge $glbnet$clk$TRELLIS_IO_IN -> <async> : 4.67 ns
|
||||
|
||||
Info: Slack histogram:
|
||||
Info: legend: * represents 1 endpoint(s)
|
||||
Info: + represents [1,1) endpoint(s)
|
||||
Info: [ 6413, 6680) |*********
|
||||
Info: [ 6680, 6947) |*****
|
||||
Info: [ 6947, 7214) |****
|
||||
Info: [ 7214, 7481) |********
|
||||
Info: [ 7481, 7748) |***
|
||||
Info: [ 7748, 8015) |
|
||||
Info: [ 8015, 8282) |**
|
||||
Info: [ 8282, 8549) |********
|
||||
Info: [ 8549, 8816) |*******
|
||||
Info: [ 8816, 9083) |*************
|
||||
Info: [ 9083, 9350) |***************************************
|
||||
Info: [ 9350, 9617) |****************************
|
||||
Info: [ 9617, 9884) |*********************
|
||||
Info: [ 9884, 10151) |*******************************
|
||||
Info: [ 10151, 10418) |**************************
|
||||
Info: [ 10418, 10685) |********************************
|
||||
Info: [ 10685, 10952) |**********
|
||||
Info: [ 10952, 11219) |*******************
|
||||
Info: [ 11219, 11486) |*********************************************
|
||||
Info: [ 11486, 11753) |***************************************
|
||||
Info: Checksum: 0x691562ea
|
||||
Info: Routing globals...
|
||||
Info: routing clock net $glbnet$clk$TRELLIS_IO_IN using global 0
|
||||
|
||||
Info: Routing..
|
||||
Info: Setting up routing queue.
|
||||
Info: Routing 1629 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 | 209 742 | 209 742 | 884| 0.08 0.08|
|
||||
Info: 2000 | 361 1528 | 152 786 | 65| 0.13 0.21|
|
||||
Info: 2066 | 363 1587 | 2 59 | 0| 0.01 0.22|
|
||||
Info: Routing complete.
|
||||
Info: Router1 time 0.22s
|
||||
Info: Checksum: 0x5e42e5ef
|
||||
|
||||
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.tile_idx_TRELLIS_FF_Q_2.Q
|
||||
Info: routing 0.93 1.32 Net dut.tile_idx[3] (31,5) -> (32,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB0.B
|
||||
Info: Defined in:
|
||||
Info: hardware/v2/rtl/memory_manager.v:90.26-90.34
|
||||
Info: logic 0.35 1.68 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB0.FCO
|
||||
Info: routing 0.00 1.68 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_FCI_INT (32,2) -> (32,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB1.FCI
|
||||
Info: logic 0.00 1.68 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB1.FCO
|
||||
Info: routing 0.00 1.68 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN (32,2) -> (32,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB0.FCI
|
||||
Info: Defined in:
|
||||
Info: hardware/v2/rtl/memory_manager.v:255.38-255.54
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/arith_map_ccu2c.v:74.7-80.4
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/ccu2c_sim.vh:9.9-9.13
|
||||
Info: logic 0.06 1.73 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB0.FCO
|
||||
Info: routing 0.00 1.73 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_FCI_INT (32,2) -> (32,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB1.FCI
|
||||
Info: logic 0.00 1.73 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB1.FCO
|
||||
Info: routing 0.00 1.73 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN (32,2) -> (33,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB0.FCI
|
||||
Info: Defined in:
|
||||
Info: hardware/v2/rtl/memory_manager.v:255.38-255.54
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/arith_map_ccu2c.v:74.7-80.4
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/ccu2c_sim.vh:9.9-9.13
|
||||
Info: logic 0.06 1.79 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB0.FCO
|
||||
Info: routing 0.00 1.79 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_FCI_INT (33,2) -> (33,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB1.FCI
|
||||
Info: logic 0.00 1.79 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB1.FCO
|
||||
Info: routing 0.00 1.79 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT_CIN (33,2) -> (33,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB0.FCI
|
||||
Info: Defined in:
|
||||
Info: hardware/v2/rtl/memory_manager.v:255.38-255.54
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/arith_map_ccu2c.v:74.7-80.4
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/ccu2c_sim.vh:9.9-9.13
|
||||
Info: logic 0.06 1.84 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB0.FCO
|
||||
Info: routing 0.00 1.84 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_FCI_INT (33,2) -> (33,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB1.FCI
|
||||
Info: logic 0.00 1.84 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB1.FCO
|
||||
Info: routing 0.00 1.84 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT_CIN (33,2) -> (33,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT$CCU2_COMB0.FCI
|
||||
Info: Defined in:
|
||||
Info: hardware/v2/rtl/memory_manager.v:255.38-255.54
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/arith_map_ccu2c.v:74.7-80.4
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/ccu2c_sim.vh:9.9-9.13
|
||||
Info: logic 0.06 1.90 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT$CCU2_COMB0.FCO
|
||||
Info: routing 0.00 1.90 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT$CCU2_FCI_INT (33,2) -> (33,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT$CCU2_COMB1.FCI
|
||||
Info: logic 0.00 1.90 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN_CCU2C_COUT$CCU2_COMB1.FCO
|
||||
Info: routing 0.00 1.90 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_CIN (33,2) -> (33,2)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1$CCU2_COMB0.FCI
|
||||
Info: Defined in:
|
||||
Info: hardware/v2/rtl/memory_manager.v:255.38-255.54
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/arith_map_ccu2c.v:74.7-80.4
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/ccu2c_sim.vh:9.9-9.13
|
||||
Info: logic 0.33 2.23 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1$CCU2_COMB0.F
|
||||
Info: routing 1.01 3.24 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_A0_CCU2C_S1_S0 (33,2) -> (34,3)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB1.A
|
||||
Info: logic 0.35 3.60 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_CIN_CCU2C_COUT$CCU2_COMB1.FCO
|
||||
Info: routing 0.00 3.60 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT_CIN (34,3) -> (35,3)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT$CCU2_COMB0.FCI
|
||||
Info: Defined in:
|
||||
Info: hardware/v2/rtl/memory_manager.v:255.38-255.68
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/arith_map_ccu2c.v:74.7-80.4
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/ccu2c_sim.vh:9.9-9.13
|
||||
Info: logic 0.06 3.65 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT$CCU2_COMB0.FCO
|
||||
Info: routing 0.00 3.65 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT$CCU2_FCI_INT (35,3) -> (35,3)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT$CCU2_COMB1.FCI
|
||||
Info: logic 0.00 3.65 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z_CCU2C_COUT$CCU2_COMB1.FCO
|
||||
Info: routing 0.00 3.65 Net $nextpnr_CCU2C_5$CIN (35,3) -> (35,3)
|
||||
Info: Sink $nextpnr_CCU2C_5$CCU2_COMB0.FCI
|
||||
Info: logic 0.33 3.98 Source $nextpnr_CCU2C_5$CCU2_COMB0.F
|
||||
Info: routing 0.71 4.69 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z[2] (35,3) -> (35,4)
|
||||
Info: Sink dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D0_L6MUX21_Z_D1_PFUMX_Z_ALUT_LUT4_Z.C
|
||||
Info: Defined in:
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:108.23-108.24
|
||||
Info: logic 0.18 4.87 Source dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D0_L6MUX21_Z_D1_PFUMX_Z_ALUT_LUT4_Z.F
|
||||
Info: routing 0.00 4.87 Net dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D0_L6MUX21_Z_D1_PFUMX_Z_ALUT (35,4) -> (35,4)
|
||||
Info: Sink dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D0_L6MUX21_Z_D1_PFUMX_Z_BLUT_LUT4_Z.F1
|
||||
Info: Defined in:
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:157.30-157.32
|
||||
Info: logic 0.13 5.00 Source dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D0_L6MUX21_Z_D1_PFUMX_Z_BLUT_LUT4_Z.OFX
|
||||
Info: routing 0.00 5.00 Net dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D0_L6MUX21_Z_D1 (35,4) -> (35,4)
|
||||
Info: Sink dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D0_L6MUX21_Z_D1_PFUMX_Z_ALUT_LUT4_Z.FXB
|
||||
Info: Defined in:
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:157.54-157.56
|
||||
Info: logic 0.18 5.18 Source dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D0_L6MUX21_Z_D1_PFUMX_Z_ALUT_LUT4_Z.OFX
|
||||
Info: routing 0.00 5.18 Net dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D0 (35,4) -> (35,4)
|
||||
Info: Sink dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D0_PFUMX_Z_ALUT_LUT4_Z.FXA
|
||||
Info: Defined in:
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:157.66-157.68
|
||||
Info: logic 0.18 5.36 Source dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D0_PFUMX_Z_ALUT_LUT4_Z.OFX
|
||||
Info: routing 0.67 6.03 Net dut.pf_pending_x_TRELLIS_FF_Q_CE (35,4) -> (35,7)
|
||||
Info: Sink dut.pf_pending_x_TRELLIS_FF_Q_1.CE
|
||||
Info: setup 0.00 6.03 Source dut.pf_pending_x_TRELLIS_FF_Q_1.CE
|
||||
Info: 2.71 ns logic, 3.32 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 1.39 1.39 Net rst$TRELLIS_IO_IN (38,0) -> (35,7)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_C_LUT4_Z.B
|
||||
Info: Defined in:
|
||||
Info: hardware/v2/synthesis/harness_memory_manager.v:18.17-18.20
|
||||
Info: logic 0.18 1.57 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_C_LUT4_Z.F
|
||||
Info: routing 0.92 2.49 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C[2] (35,7) -> (36,4)
|
||||
Info: Sink dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A.C
|
||||
Info: Defined in:
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:108.23-108.24
|
||||
Info: logic 0.18 2.67 Source dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A.F
|
||||
Info: routing 0.49 3.16 Net dut.operand_valid_TRELLIS_FF_Q_CE_LUT4_Z_C_LUT4_A_Z[3] (36,4) -> (35,4)
|
||||
Info: Sink dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D1_PFUMX_Z_ALUT_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.34 Source dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D1_PFUMX_Z_ALUT_LUT4_Z.F
|
||||
Info: routing 0.00 3.34 Net dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D1_PFUMX_Z_ALUT (35,4) -> (35,4)
|
||||
Info: Sink dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D1_PFUMX_Z_BLUT_LUT4_Z.F1
|
||||
Info: Defined in:
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:157.46-157.48
|
||||
Info: logic 0.13 3.47 Source dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D1_PFUMX_Z_BLUT_LUT4_Z.OFX
|
||||
Info: routing 0.00 3.47 Net dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D1 (35,4) -> (35,4)
|
||||
Info: Sink dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D1_PFUMX_Z_ALUT_LUT4_Z.FXB
|
||||
Info: Defined in:
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:157.62-157.64
|
||||
Info: logic 0.18 3.65 Source dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D1_PFUMX_Z_ALUT_LUT4_Z.OFX
|
||||
Info: routing 0.00 3.65 Net dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1 (35,4) -> (35,4)
|
||||
Info: Sink dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D0_PFUMX_Z_ALUT_LUT4_Z.FXB
|
||||
Info: Defined in:
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:157.70-157.72
|
||||
Info: logic 0.18 3.83 Source dut.pf_pending_x_TRELLIS_FF_Q_CE_L6MUX21_Z_D1_L6MUX21_Z_D0_PFUMX_Z_ALUT_LUT4_Z.OFX
|
||||
Info: routing 0.67 4.50 Net dut.pf_pending_x_TRELLIS_FF_Q_CE (35,4) -> (35,7)
|
||||
Info: Sink dut.pf_pending_x_TRELLIS_FF_Q_1.CE
|
||||
Info: setup 0.00 4.50 Source dut.pf_pending_x_TRELLIS_FF_Q_1.CE
|
||||
Info: 1.03 ns logic, 3.47 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_2.Q
|
||||
Info: routing 2.91 3.30 Net checksum[2]$TRELLIS_IO_OUT (38,8) -> (90,11)
|
||||
Info: Sink checksum[2]$tr_io.I
|
||||
Info: Defined in:
|
||||
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:108.23-108.24
|
||||
Info: 0.40 ns logic, 2.91 ns routing
|
||||
|
||||
Info: Max frequency for clock '$glbnet$clk$TRELLIS_IO_IN': 165.86 MHz (PASS at 80.00 MHz)
|
||||
|
||||
Info: Max delay <async> -> posedge $glbnet$clk$TRELLIS_IO_IN: 4.50 ns
|
||||
Info: Max delay posedge $glbnet$clk$TRELLIS_IO_IN -> <async> : 3.30 ns
|
||||
|
||||
Info: Slack histogram:
|
||||
Info: legend: * represents 1 endpoint(s)
|
||||
Info: + represents [1,1) endpoint(s)
|
||||
Info: [ 6471, 6738) |*******
|
||||
Info: [ 6738, 7005) |**
|
||||
Info: [ 7005, 7272) |**
|
||||
Info: [ 7272, 7539) |*
|
||||
Info: [ 7539, 7806) |*
|
||||
Info: [ 7806, 8073) |***
|
||||
Info: [ 8073, 8340) |*************
|
||||
Info: [ 8340, 8607) |***
|
||||
Info: [ 8607, 8874) |****
|
||||
Info: [ 8874, 9141) |*************
|
||||
Info: [ 9141, 9408) |**********************************************
|
||||
Info: [ 9408, 9675) |***********************************
|
||||
Info: [ 9675, 9942) |**************************
|
||||
Info: [ 9942, 10209) |******************************
|
||||
Info: [ 10209, 10476) |****************************
|
||||
Info: [ 10476, 10743) |****************
|
||||
Info: [ 10743, 11010) |**************
|
||||
Info: [ 11010, 11277) |*********************
|
||||
Info: [ 11277, 11544) |**********************************************
|
||||
Info: [ 11544, 11811) |**************************************
|
||||
|
||||
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 one or more lines are too long
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
Info: Logic utilisation before packing:
|
||||
Info: Total LUT4s: 1067/43848 2%
|
||||
Info: logic LUTs: 851/43848 1%
|
||||
Info: carry LUTs: 216/43848 0%
|
||||
Info: RAM LUTs: 0/ 5481 0%
|
||||
Info: RAMW LUTs: 0/10962 0%
|
||||
|
||||
Info: Total DFFs: 789/43848 1%
|
||||
|
||||
Info: Packing IOs..
|
||||
Info: Packing constants..
|
||||
Info: Packing carries...
|
||||
Info: Packing LUTs...
|
||||
Info: Packing LUT5-7s...
|
||||
Info: Packing FFs...
|
||||
Info: 167 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: 0x6e816bc9
|
||||
|
||||
Info: Device utilisation:
|
||||
Info: TRELLIS_IO: 272/ 245 111%
|
||||
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: 789/ 43848 1%
|
||||
Info: TRELLIS_COMB: 1117/ 43848 2%
|
||||
Info: TRELLIS_RAMW: 0/ 5481 0%
|
||||
|
||||
Info: Placed 0 cells based on constraints.
|
||||
ERROR: Unable to place cell 'input_data[54]$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
Reference in New Issue
Block a user