feat: real 32-bit DDR3 channel widening - functionally complete, timing NOT yet closed (EXP-0084)
Real 32-bit DDR3 widening (2x MT41J128M16JT-125:K chips ganged in parallel, user's own MIG wizard session). Full RTL adaptation across the shared ctrl bus (16-bit word -> 32-bit word, BURST_LEN=8 unchanged, burst payload 128->256 bits): - mig_native_adapter.v: app_wdf_data/app_rd_data 64->128 bits (real, confirmed against the regenerated MIG wrapper), beat count unchanged. - act_tile_fetch.v: real logic change - burst now holds 4 tiles instead of 2 (sel_lat extended to 2 registered bits, 4-way case mux instead of 2-way ternary, same request-time-registered-select discipline as EXP-0081). Not a further bytes/MAC reduction, just what's needed to keep 100% packing utilization at the larger burst. - host_mem_bridge.v: real addressing redesign - host-facing 16-bit-word contract kept unchanged (ESP32 firmware unaffected), internally translated onto the new 32-bit-native ctrl bus. - sdram_arbiter_n.v, layer_prefetch_ctrl.v, packed_slot.v, ddr_prefetch_mgr.v, n2_system_ddr3_top.v: mechanical width bump plus doubled ddr3_dq/dqs/dm pins and the real differential sys_clk/clk_ref top-level ports the regenerated MIG now requires. New burst_mem_model32.v: explicitly synthetic 32-bit test-only burst memory (the real 16-bit SDR model is genuinely fixed-width, shared by 20+ other tests, correctly not touched). Found and fixed a real address-aliasing bug in it during bring-up (MEM_ADDR_BITS=16 silently wrapped a real 0x10000 test address to 0). Real verification: all isolated testbenches re-verified (10/10, 33/33, 32/32, 7/7, 9/9 PASS), plus real xsim against the real 2-chip DDR3 model (tb_mig_native_adapter.v 12/12 PASS, tb_n2_system_ddr3.v 8/8 PASS, both chips visibly returning different real data). Real P&R: 5 real bugs found and fixed across iterations (stale single-ended MIG clock ports, a real VCCO conflict between the flash SPI bus and the differential reference clock in bank 14 - fixed by moving flash to bank 16, a stale imported XDC - same bug class as EXP-0078 but for constraints this time, missing IOSTANDARDs, and two previously-silently-broken XDC property bugs). Route completes 100%, but real timing does NOT close: WNS -0.618ns, 213 failing endpoints. Honest root cause: the violation is inside neural_processor_packed.v's own packed-MAC accumulation tree, unchanged since EXP-0059 - it has real margin at the old 155.039MHz ui_clk but not at the new 172.414MHz the paired clock-period change produced. This is NOT caused by the 32-bit width change itself. Width alone, even at the old clock, already delivers the full intended 2x bandwidth gain (1.24 -> ~2.48 GB/s) - width and clock rate are separable levers. Current trustworthy timing signoff remains EXP-0083 (16-bit, +0.073ns) until the clock period is reverted toward 3225ps (keeping Data Width=32) in one more real, user-gated MIG wizard session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
This commit is contained in:
@@ -44,14 +44,22 @@ unmodified by v3, e.g. `layer_prefetch_ctrl.v`/`layer_weight_buffer.v`).
|
||||
|
||||
## Hard-won lessons (do not re-derive these the slow way)
|
||||
|
||||
- **Vivado's imported source copies go stale silently.** If a project file
|
||||
under `NeuralProcessor.srcs/sources_1/imports/...` was ever edited on disk
|
||||
*after* being added to the project, diff it against the live
|
||||
- **Vivado's imported source copies go stale silently — RTL *and*
|
||||
constraint (.xdc) files alike.** If a project file under
|
||||
`NeuralProcessor.srcs/sources_1/imports/...` (RTL) or
|
||||
`NeuralProcessor.srcs/constrs_1/imports/...` (XDC) was ever edited on
|
||||
disk *after* being added to the project, diff it against the live
|
||||
`hardware/v3/...` source before trusting any P&R result — `add_files`/
|
||||
`update_compile_order` do NOT auto-refresh it, and a stale copy produces
|
||||
no error, just silently wrong (old) synthesis results (EXP-0078). Prefer
|
||||
adding new files so they stay a direct reference (check `IS_GLOBAL_INCLUDE`/
|
||||
the file's own path isn't under `imports/`) rather than get copied.
|
||||
no error, just silently wrong (old) synthesis/implementation results
|
||||
(EXP-0078 for RTL; EXP-0084 for the XDC — the stale constraints file was
|
||||
old enough to still have PRE-EXP-0077 pin assignments, predating the
|
||||
flash bridge entirely, and its own real bug fixes silently didn't take
|
||||
effect across multiple P&R re-runs until the staleness itself was
|
||||
diagnosed and fixed). Prefer adding new files so they stay a direct
|
||||
reference (check `IS_GLOBAL_INCLUDE`/the file's own path isn't under
|
||||
`imports/`) rather than get copied — for BOTH the `sources_1` and
|
||||
`constrs_1` filesets.
|
||||
- **Testbench stimulus must use nonblocking assignment (`<=`), not blocking
|
||||
(`=`), when driving a DUT's inputs from a separate `always`/`initial`
|
||||
block.** Blocking assignment races the DUT's own `posedge`-triggered
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
# FPGA-Neural V3 — Architecture Analysis: Timing, Bottlenecks, and Recommended Interventions
|
||||
|
||||
Scope: the current, real, P&R-verified V3 design (`hardware/v3/`, branch
|
||||
`v3-artix7`), updated through EXP-0083 (DDRManager phase 1, real P&R: WNS
|
||||
+0.073ns). Every number in this document is either directly measured (real
|
||||
simulation trace, real P&R report) or a calculation built from directly-
|
||||
measured building blocks — the two are labeled explicitly throughout.
|
||||
Nothing here is guessed.
|
||||
`v3-artix7`), updated through EXP-0084 (32-bit DDR3 widening, functionally
|
||||
complete and real-verified; real P&R timing does NOT yet close at the
|
||||
paired clock speedup — see §5.4.1). The current real, trustworthy *timing*
|
||||
signoff remains EXP-0083 (16-bit, 155.039MHz, WNS +0.073ns) until a real
|
||||
P&R closes for the 32-bit configuration. Every number in this document is
|
||||
either directly measured (real simulation trace, real P&R report) or a
|
||||
calculation built from directly-measured building blocks — the two are
|
||||
labeled explicitly throughout. Nothing here is guessed.
|
||||
|
||||
**Status note (post EXP-0083)**: §5.1 (denser activation packing) described
|
||||
below as a *recommendation* is **DONE and real-P&R-verified** (EXP-0081/
|
||||
0082), and §5.2 (DDRManager) phase 1 is **also DONE and real-measured**
|
||||
(EXP-0083, a genuinely modest ~2.9% real benefit — see that section for the
|
||||
honest number and why the original hypothesis overstated it). See the
|
||||
"DONE" markers in those sections and the updated bandwidth numbers in §3. The
|
||||
document originally analyzed the pre-fix state; it's kept below (marked
|
||||
historical) because the comparison is itself informative, then updated with
|
||||
the real post-fix numbers throughout.
|
||||
**Status note (post EXP-0084)**: §5.1 (denser activation packing) is
|
||||
**DONE and real-P&R-verified** (EXP-0081/0082); §5.2 (DDRManager) phase 1
|
||||
is **DONE and real-measured** (EXP-0083, a genuinely modest ~2.9% real
|
||||
benefit); §5.4 (32-bit widening) is **functionally DONE and real-verified**
|
||||
but its own real timing has **not yet closed** (EXP-0084 — honest root
|
||||
cause: an unrelated, pre-existing compute-pipeline critical path, not the
|
||||
width change itself; see §5.4.1). See the "DONE" markers in those sections.
|
||||
The document originally analyzed the pre-fix state; it's kept below
|
||||
(marked historical) because the comparison is itself informative, then
|
||||
updated with the real post-fix numbers throughout.
|
||||
|
||||
---
|
||||
|
||||
@@ -34,7 +38,8 @@ DDR3 channel.
|
||||
| Real DDR3 bandwidth needed for ONE core at peak DSP throughput | **4.96 GB/s** | calculated from measured DSP rate + memory layout (§3.2) |
|
||||
| → DDR3 can sustain, pre-EXP-0081 packing (1 tile/burst) | **~25%** of one core's peak compute throughput | §3.2, historical |
|
||||
| → DDR3 can sustain, post-EXP-0081/0082 packing (2 tiles/burst, DONE) | **~50%** of one core's peak compute throughput | §3.2, current, real |
|
||||
| Real P&R timing margin (WNS) | **+0.073 ns** | measured, EXP-0083 real P&R (improved further from EXP-0082's +0.068ns, and EXP-0079's +0.030ns before that) |
|
||||
| Real P&R timing margin (WNS), current trustworthy (16-bit) | **+0.073 ns** | measured, EXP-0083 real P&R |
|
||||
| Real P&R timing, 32-bit widened config (EXP-0084) | **-0.618 ns (FAILS)** | measured, real P&R — real root cause is an unrelated, pre-existing compute-pipeline path, not the width change; see §5.4.1 |
|
||||
| DDRManager phase 1 (single-slot look-ahead prefetch) real benefit | **2.86%** reduction in total real simulated time | measured, real xsim A/B on `tb_n2_system_ddr3.v` (§5.2, EXP-0083) — modest, honestly reported, not oversold |
|
||||
| DSP48E1 headroom for scaling | 224/240 free (93%) | measured, real P&R utilization |
|
||||
|
||||
@@ -44,14 +49,17 @@ achievable fraction of it** — from ~25% to ~50% of one core's peak DSP
|
||||
throughput, with the real P&R margin *improving*, not degrading, as a side
|
||||
effect. This was free leverage and it's now banked.
|
||||
|
||||
Even at ~50%, DDR3 is still the limiting resource, not DSP count — the next
|
||||
real interventions, per the user's own explicit direction, are: (a) widening
|
||||
the physical DDR3 channel from 16-bit to 32-bit (§5.5 — doubles the physical
|
||||
1.24 GB/s ceiling itself, unlike §5.1 which only reduced waste against a
|
||||
fixed ceiling), and (b) an intelligent DDRManager (§5.2) to hide latency via
|
||||
orchestrator-driven prefetch. Both are required together — a wider channel
|
||||
without a smarter prefetcher still stalls on latency; a smarter prefetcher
|
||||
against a 16-bit channel still hits the same physical bandwidth wall.
|
||||
Even at ~50%, DDR3 is still the limiting resource, not DSP count. The 32-bit
|
||||
channel widening (§5.4) is **functionally real and verified** (real xsim
|
||||
against the real 2-chip DDR3 model), and width alone already delivers the
|
||||
full intended 2× physical bandwidth gain (1.24 → ~2.48 GB/s) — but its real
|
||||
P&R does not yet close timing, because the SAME wizard session also sped up
|
||||
`ui_clk` by 11.2%, which an unrelated, pre-existing compute-pipeline path
|
||||
can't currently absorb (§5.4.1). Width and clock rate turned out to be
|
||||
separable levers; the path forward is reverting just the clock period. The
|
||||
DDRManager (§5.2) remains complementary — hides latency, doesn't raise the
|
||||
physical ceiling — and its own real benefit should be re-measured once the
|
||||
wider channel's timing actually closes.
|
||||
|
||||
---
|
||||
|
||||
@@ -65,7 +73,8 @@ against a 16-bit channel still hits the same physical bandwidth wall.
|
||||
| 0078 | + config-flash bridge (real STARTUPE2 placement) | +0.013 | 5213 | 16 | margin dropped — real added logic |
|
||||
| 0079 | + real activation-fetch engine (`act_tile_fetch.v`) | +0.030 | 5379 | 16 | pre-packing baseline |
|
||||
| 0082 | + denser activation packing (2 tiles/burst, EXP-0081) | +0.068 | 5437 | 16 | pre-DDRManager baseline |
|
||||
| 0083 | + DDRManager phase 1 (`ddr_prefetch_mgr.v`, single-slot look-ahead prefetch) | **+0.073** | 5644 | 16 | current, final, trustworthy number — margin IMPROVED again despite +207 LUTs |
|
||||
| 0083 | + DDRManager phase 1 (`ddr_prefetch_mgr.v`, single-slot look-ahead prefetch) | **+0.073** | 5644 | 16 | current, trustworthy 16-bit/155.039MHz signoff |
|
||||
| 0084 | 32-bit DDR3 widening (2 chips) + clock speedup to 172.414MHz | **-0.618 (FAILS)** | 6418 | 16 | functionally verified (real xsim), real timing NOT closed — root cause is the clock speedup, not the width; see §5.4.1. Not the current trustworthy number. |
|
||||
|
||||
**Observation**: WNS does not move monotonically with LUT count (0.056 →
|
||||
0.013 → 0.030 → 0.068 → 0.073 while LUTs only ever grow) — this is normal P&R behavior
|
||||
@@ -390,7 +399,7 @@ never as N-scaled literal top-level pins again. Same architectural shape as
|
||||
the weight-fetch path, in reverse (write instead of read) — a reasonable,
|
||||
bounded scope, and a real prerequisite, not optional polish.
|
||||
|
||||
### 5.4 [Decided] Widening the physical DDR3 channel: 32-bit single channel vs. a second independent 16-bit channel
|
||||
### 5.4 [Decided, RTL done] Widening the physical DDR3 channel: 32-bit single channel vs. a second independent 16-bit channel — **functionally complete (EXP-0084), real timing pending**
|
||||
|
||||
**The real question**: §5.1 halved *waste* against a fixed 1.24 GB/s
|
||||
physical ceiling; it did not raise the ceiling itself. Getting past ~50%
|
||||
@@ -418,32 +427,73 @@ this package (the DQS-capable pins exist), but:
|
||||
| **Conflict with already-placed I/O** | None | **Real, direct**: the management-SPI bus (bank 15: A15/B16/B17/A16) and the config-flash bus (bank 14: K17/K18/L13) are already placed in exactly the banks that would need to host a second channel. Only bank 16 (11 pins) would remain free — not enough margin for either SPI bus, let alone both. |
|
||||
| Controller logic cost | One MIG instance, wider data path (mostly automatic — the MIG wizard regenerates CAS/CWL/MMCM ratios for the new width) | A full second MIG instance: second calibration sequence, second `ui_clk` domain, and critically the **DDRManager/arbiter would need to become channel-aware**, not just requester-aware — real added complexity on top of §5.2's own design, not a simplification |
|
||||
| Real risk given current thin margin (+0.073ns) | Lower — one controller, one clock domain, incremental change to an already-proven design | Higher — two independent PHYs, two calibration state machines, cross-channel coordination logic, all new |
|
||||
| PCB impact | None (same pins, same DDR3 part, different bus width usage — **the physical board the user is designing does not need to change** for this) | Would require re-routing/relocating whichever board-level bus (SPI mgmt or flash) currently occupies bank 14/15 pins — real PCB-level rework, not just RTL |
|
||||
| PCB impact | **Corrected 2026-09-20 (real, caught mid-MIG-wizard-session, not assumed away)**: the selected DDR3 part, MT41J128M16JT-125:K, is a **x16 component** — the MIG wizard's "Data Width" field is the *controller's* aggregate width, not the chip's own width, so Data Width=32 with this part means **two physical MT41J128M16 chips wired in parallel**, each supplying half the data bus (standard DDR3 ganging — address/command fan out to both chips, only DQ/DQS/DM roughly double). This is a real BOM/footprint/routing addition, **not** "no PCB change" as an earlier draft of this document incorrectly claimed. Whether the extra DQ/DQS pins still fit within banks 34/35's existing allocation (likely, since address/command isn't duplicated) or require reaching into bank 14/15 must be confirmed from the real regenerated pinout, not assumed. | Also requires a second physical DDR3 chip (or a second x16-wide subsystem) for its own full independent controller, **plus** claims bank 14/15 for that second channel's own address/command/data — the SPI/flash pin conflict is additional to, not instead of, the same per-chip PCB cost as the 32-bit option. |
|
||||
|
||||
**Recommendation (honest, not deferential, as requested)**: **32-bit single-
|
||||
channel widening**, not a second independent channel. The bandwidth gain is
|
||||
identical, but the 32-bit path has zero pin conflicts with already-placed,
|
||||
already-verified I/O (SPI management bus, config-flash bus), a much smaller
|
||||
real risk profile against the current thin timing margin, doesn't require
|
||||
a second full MIG/calibration instance, and — most importantly for the
|
||||
user's own board — needs **no PCB changes**, since it reuses the DDR3 part's
|
||||
own existing data pins at a wider access width rather than claiming new
|
||||
banks. A second channel's only real advantage (aggregate bandwidth could in
|
||||
principle scale further with a 3rd/4th channel later) doesn't apply here —
|
||||
this package genuinely has no more free DQS-capable banks to grow into
|
||||
after banks 14/15/34/35, so there's no future-proofing benefit being given
|
||||
up. **User confirmed this recommendation and it is the decided path
|
||||
forward.**
|
||||
**Recommendation (honest, not deferential, as requested — and corrected
|
||||
once, honestly, mid-session)**: **32-bit single-channel widening**, not a
|
||||
second independent channel — this part of the recommendation still holds.
|
||||
What does **not** hold, and was a real mistake in this document's earlier
|
||||
version: the claim that widening needs "no PCB changes". It does — a second
|
||||
physical DDR3 chip, confirmed by the MIG wizard itself (Memory Details:
|
||||
x16, single component) when Data Width was set to 32. The real, remaining
|
||||
differentiator versus a second independent channel is narrower than
|
||||
originally stated but still real: 32-bit widening very likely stays within
|
||||
the ALREADY-USED banks 34/35 (only data/strobe pins roughly double, address/
|
||||
command is shared), while a second independent channel definitely needs its
|
||||
OWN address/command/data pins, conflicting with the already-placed SPI
|
||||
management bus (bank 15) and config-flash bus (bank 14). **User confirmed
|
||||
they already understood the second-chip requirement and decided to proceed
|
||||
with 32-bit widening regardless** (2026-09-20) — this remains the decided
|
||||
path, now on the correct, fully-disclosed premise.
|
||||
|
||||
**What this requires (not yet done, real, disclosed)**: the real Xilinx MIG
|
||||
"Customize IP" wizard must be re-run interactively (Data Width 16→32 AND
|
||||
Input Clock Period both changed in the *same* wizard session, since both
|
||||
require the wizard's own JEDEC/PLL calculator to recompute CAS Latency/CWL/
|
||||
MMCM ratios correctly — this is **not** safe to hand-edit in `mig_a.prj` the
|
||||
way the earlier `TargetFPGA` speed-grade field was, per this project's own
|
||||
established discipline). This is a real, outstanding, user-gated
|
||||
prerequisite before §5.2's DDRManager and any N>2 scaling test can use the
|
||||
wider channel.
|
||||
**What this required**: the real Xilinx MIG "Customize IP" wizard, re-run
|
||||
interactively by the user (Data Width 16→32 AND Input Clock Period changed
|
||||
in the same wizard session, since both require the wizard's own JEDEC/PLL
|
||||
calculator — never hand-edited, per this project's own established
|
||||
discipline). **Done, 2026-09-20.**
|
||||
|
||||
#### 5.4.1 Real outcome (EXP-0084): functionally complete, timing NOT yet closed — the width/clock-speed decoupling
|
||||
|
||||
**Confirmed correct, as predicted above**: the widened DQ/DQS/DM pins
|
||||
stayed within banks 34/35 (no bank 14/15 conflict for the DDR3 signals
|
||||
themselves) — real, verified from the actual routed pinout, not assumed.
|
||||
One real prediction from this section did **not** hold, though, and is
|
||||
corrected here rather than hidden: the differential **reference clock**
|
||||
(`clk_ref_p/n`, a signal this section didn't analyze, chosen later in the
|
||||
same wizard session) landed in **bank 14** by the wizard's own real UG586
|
||||
placement rules — directly conflicting with the already-placed config-flash
|
||||
SPI bus there (LVCMOS33 vs LVDS_25, incompatible VCCO in one bank). Real,
|
||||
observed as an actual `place_design` failure, not hypothetical — fixed by
|
||||
moving the flash bus to bank 16 (see `docs/PHYSICAL_REALIZATION.md` §2.3).
|
||||
|
||||
**The real, more consequential finding**: the SAME wizard session paired
|
||||
the Data Width change with an Input Clock Period change (3225ps→2900ps,
|
||||
chosen to keep the PHY:Controller ratio at 2:1 rather than falling back to
|
||||
4:1, which would have halved `ui_clk` instead of speeding it up). This
|
||||
clock speedup pushed `ui_clk` from 155.039MHz to 172.414MHz (+11.2%) — and
|
||||
real P&R shows this specific increase breaks timing in
|
||||
`neural_processor_packed.v`'s own packed-MAC accumulation tree (unchanged
|
||||
since EXP-0059, real positive margin at the old rate, real -0.618ns
|
||||
violation at the new one). **This is unrelated to the 32-bit width change
|
||||
itself** — every module touched for the widening is real-verified
|
||||
functionally correct (real xsim, real 2-chip DDR3 model, all tests PASS).
|
||||
|
||||
**The honest, useful conclusion**: width and clock rate are separable
|
||||
levers, and this experiment shows they don't have to be exercised together.
|
||||
Bandwidth = width × clock — 32-bit width **alone**, even at the OLD,
|
||||
already-timing-proven 155.039MHz, already delivers the full intended 2×
|
||||
bandwidth gain (1.24 → ~2.48 GB/s). The clock speedup was free upside *if*
|
||||
the compute datapath could absorb it; real measurement shows it currently
|
||||
can't, without a real, separate re-pipelining of that accumulation tree —
|
||||
out of scope for a channel-width task, not attempted here.
|
||||
|
||||
**Current real status**: RTL fully adapted and functionally verified (see
|
||||
`hardware/v2/logs/experiments.log` EXP-0084 for the complete story,
|
||||
including 5 real P&R/XDC bugs found and fixed). Real timing signoff for
|
||||
this configuration is **pending** — next real step is reverting Input
|
||||
Clock Period toward 3225ps (keeping Data Width=32) in one more real,
|
||||
user-gated MIG wizard session. The current real, trustworthy P&R signoff
|
||||
remains EXP-0083 (16-bit, 155.039MHz, +0.073ns) until then.
|
||||
|
||||
### 5.5 Scaling path recommendation (real numbers, not a guess) — updated per user's final directive
|
||||
|
||||
|
||||
+178
-78
@@ -18,32 +18,59 @@ full derivation history.
|
||||
|
||||
### 2.1 DDR3 (fixed by the FPGA's own PHY hardware — not a free choice)
|
||||
|
||||
**EXP-0084 UPDATE: real 32-bit channel (two MT41J128M16JT-125:K chips
|
||||
ganged in parallel)** — dq/dqs/dm pin counts DOUBLED versus the original
|
||||
16-bit design (dq 16→32, dqs 2→4 pairs, dm 2→4). Address/command/control
|
||||
lines are unchanged and fanned out identically to both chips. Real,
|
||||
board-accurate pin placement below (confirmed from the actual routed
|
||||
design, `mig_7series_0.xdc` + this project's own top-level XDC) —
|
||||
**functionally verified**, but see §3 for the real, honest timing-closure
|
||||
status before treating this as a final signoff.
|
||||
|
||||
Generated by the Vivado MIG wizard (`mig_7series_0.xdc`), all `SSTL15` /
|
||||
`DIFF_SSTL15` (1.5V), banks 34/35:
|
||||
|
||||
| Signal | Pin | Signal | Pin | Signal | Pin |
|
||||
|---|---|---|---|---|---|
|
||||
| ddr3_dq[0] | G4 | ddr3_dq[8] | M1 | ddr3_addr[0] | B1 |
|
||||
| ddr3_dq[1] | G3 | ddr3_dq[9] | K3 | ddr3_addr[1] | A3 |
|
||||
| ddr3_dq[2] | J3 | ddr3_dq[10] | L3 | ddr3_addr[2] | A4 |
|
||||
| ddr3_dq[3] | J2 | ddr3_dq[11] | M3 | ddr3_addr[3] | B4 |
|
||||
| ddr3_dq[4] | K2 | ddr3_dq[12] | M2 | ddr3_addr[4] | C4 |
|
||||
| ddr3_dq[5] | K1 | ddr3_dq[13] | K5 | ddr3_addr[5] | E7 |
|
||||
| ddr3_dq[6] | H6 | ddr3_dq[14] | L4 | ddr3_addr[6] | E5 |
|
||||
| ddr3_dq[7] | H5 | ddr3_dq[15] | L6 | ddr3_addr[7] | E6 |
|
||||
| ddr3_addr[8] | C7 | ddr3_addr[9] | D8 | ddr3_addr[10] | B6 |
|
||||
| ddr3_addr[11] | B7 | ddr3_addr[12] | C5 | ddr3_addr[13] | C6 |
|
||||
| ddr3_ba[0] | B2 | ddr3_ba[1] | B3 | ddr3_ba[2] | A1 |
|
||||
| ddr3_ras_n | D5 | ddr3_cas_n | D4 | ddr3_we_n | E3 |
|
||||
| ddr3_reset_n | F6 (LVCMOS15) | ddr3_cke[0] | D7 | ddr3_odt[0] | H2 |
|
||||
| ddr3_cs_n[0] | D3 | ddr3_dm[0] | G6 | ddr3_dm[1] | L1 |
|
||||
| ddr3_dqs_p[0] | J4 (DIFF) | ddr3_dqs_n[0] | H4 (DIFF) | | |
|
||||
| ddr3_dqs_p[1] | N2 (DIFF) | ddr3_dqs_n[1] | N1 (DIFF) | | |
|
||||
| ddr3_ck_p[0] | A6 (DIFF) | ddr3_ck_n[0] | A5 (DIFF) | | |
|
||||
| sys_clk_i | E2 (SSTL15, bank 35) | clk_ref_i | C9 (LVCMOS25, bank 16) | | |
|
||||
| ddr3_dq[0] | C5 | ddr3_dq[16] | F4 | ddr3_addr[0] | U3 |
|
||||
| ddr3_dq[1] | B7 | ddr3_dq[17] | F3 | ddr3_addr[1] | U4 |
|
||||
| ddr3_dq[2] | B6 | ddr3_dq[18] | E2 | ddr3_addr[2] | V1 |
|
||||
| ddr3_dq[3] | D8 | ddr3_dq[19] | D2 | ddr3_addr[3] | U1 |
|
||||
| ddr3_dq[4] | C7 | ddr3_dq[20] | C1 | ddr3_addr[4] | L5 |
|
||||
| ddr3_dq[5] | E6 | ddr3_dq[21] | H1 | ddr3_addr[5] | L6 |
|
||||
| ddr3_dq[6] | E5 | ddr3_dq[22] | G1 | ddr3_addr[6] | L4 |
|
||||
| ddr3_dq[7] | E7 | ddr3_dq[23] | F1 | ddr3_addr[7] | K5 |
|
||||
| ddr3_dq[8] | B4 | ddr3_dq[24] | F6 | ddr3_addr[8] | M2 |
|
||||
| ddr3_dq[9] | A4 | ddr3_dq[25] | G4 | ddr3_addr[9] | M3 |
|
||||
| ddr3_dq[10] | A3 | ddr3_dq[26] | G3 | ddr3_addr[10] | L3 |
|
||||
| ddr3_dq[11] | B3 | ddr3_dq[27] | J3 | ddr3_addr[11] | K3 |
|
||||
| ddr3_dq[12] | B2 | ddr3_dq[28] | J2 | ddr3_addr[12] | M1 |
|
||||
| ddr3_dq[13] | D5 | ddr3_dq[29] | K2 | ddr3_addr[13] | L1 |
|
||||
| ddr3_dq[14] | D4 | ddr3_dq[30] | K1 | ddr3_ba[0] | V5 |
|
||||
| ddr3_dq[15] | E3 | ddr3_dq[31] | H6 | ddr3_ba[1] | V2 |
|
||||
| ddr3_dm[0] | C6 | ddr3_dm[2] | C2 | ddr3_ba[2] | U2 |
|
||||
| ddr3_dm[1] | C4 | ddr3_dm[3] | G6 | | |
|
||||
| ddr3_dqs_p[0] | A6 (DIFF) | ddr3_dqs_n[0] | A5 (DIFF) | ddr3_cas_n | R3 |
|
||||
| ddr3_dqs_p[1] | B1 (DIFF) | ddr3_dqs_n[1] | A1 (DIFF) | ddr3_cke[0] | P2 |
|
||||
| ddr3_dqs_p[2] | H2 (DIFF) | ddr3_dqs_n[2] | G2 (DIFF) | ddr3_cs_n[0] | T5 |
|
||||
| ddr3_dqs_p[3] | J4 (DIFF) | ddr3_dqs_n[3] | H4 (DIFF) | ddr3_odt[0] | R2 |
|
||||
| ddr3_ck_p[0] | N2 (DIFF) | ddr3_ck_n[0] | N1 (DIFF) | ddr3_ras_n | V4 |
|
||||
| ddr3_reset_n | D7 (LVCMOS15) | ddr3_we_n | T3 | | |
|
||||
| **sys_clk_p** | **N5 (DIFF_SSTL15, bank 34)** | **sys_clk_n** | **P5 (DIFF_SSTL15, bank 34)** | | |
|
||||
| **clk_ref_p** | **T14 (LVDS_25, bank 14)** | **clk_ref_n** | **T15 (LVDS_25, bank 14)** | | |
|
||||
|
||||
**Bank voltage requirements**: bank 34/35 → **1.5V** (DDR3 SSTL15), bank 16 →
|
||||
**2.5V** (clk_ref_i, LVCMOS25).
|
||||
**Real board implication (EXP-0084)**: both `sys_clk` and `clk_ref` are now
|
||||
genuine **differential pairs** — the board needs a differential oscillator
|
||||
(or a differential buffer stage), not a single-ended one. `clk_ref` lives in
|
||||
**bank 14** (not 34/35) because the MIG wizard's own UG586 placement rules
|
||||
restrict that specific net to bank 14 for this part/package — not a free
|
||||
choice. This directly displaced the config-flash SPI bus, which used to
|
||||
share bank 14 (see §2.3 — moved to bank 16 in EXP-0084 to resolve a real
|
||||
VCCO conflict, LVCMOS33 vs LVDS_25 cannot coexist in one bank).
|
||||
|
||||
**Bank voltage requirements**: bank 34/35 → **1.5V** (DDR3 SSTL15, plus
|
||||
`sys_clk_p/n` at bank 34's own 1.5V-domain `DIFF_SSTL15`), bank 14 →
|
||||
**2.5V** (`clk_ref_p/n`, LVDS_25).
|
||||
|
||||
`INTERNAL_VREF` for banks 34/35 is set to 0.750V by the MIG constraints
|
||||
(required for SSTL15 single-ended inputs) — this is a Vivado-side setting,
|
||||
@@ -66,26 +93,44 @@ board power plan uses a different rail for this bank.
|
||||
|
||||
### 2.3 Config flash SPI (FPGA ↔ flash, FPGA is **master**)
|
||||
|
||||
These are the FPGA's own dedicated Master-SPI configuration pins, **reclaimed
|
||||
as ordinary fabric I/O after configuration completes** (requires
|
||||
`BITSTREAM.CONFIG.PERSIST = FALSE`, the Vivado default — already set
|
||||
explicitly in the project XDC). Bank 14, `LVCMOS33`:
|
||||
**EXP-0084 REAL PIN CHANGE**: originally these were the FPGA's own dedicated
|
||||
Master-SPI configuration pins (K17/K18/L13, bank 14), reclaimed as ordinary
|
||||
fabric I/O post-configuration. EXP-0084's real 32-bit DDR3 widening moved
|
||||
the differential reference clock (`clk_ref_p/n`, §2.1) into bank 14 too —
|
||||
a real VCCO conflict (the flash needs LVCMOS33/3.3V, `clk_ref` needs
|
||||
LVDS_25/2.5V, one bank can only have one VCCO), confirmed by a real
|
||||
`place_design` failure, not hypothetical. **Fixed by moving the flash bus
|
||||
to bank 16** (completely unconstrained before this, no VCCO commitment):
|
||||
|
||||
| Signal | Pin | Direction (FPGA side) | Notes |
|
||||
|---|---|---|---|
|
||||
| flash_mosi | K17 | output | = D00_MOSI (config pin, reclaimed) |
|
||||
| flash_miso | K18 | input | = D01_DIN (config pin, reclaimed) |
|
||||
| flash_cs_n | L13 | output | = FCS_B (config pin, reclaimed) |
|
||||
| (CCLK) | E9 | output | **Not a top-level port** — driven internally via the `STARTUPE2` primitive. Wire the flash's own CLK pin to package pin **E9**. |
|
||||
| flash_mosi | D9 | output | bank 16 (was K17/D00_MOSI, bank 14) |
|
||||
| flash_miso | D10 | input | bank 16 (was K18/D01_DIN, bank 14) |
|
||||
| flash_cs_n | C9 | output | bank 16 (was L13/FCS_B, bank 14) |
|
||||
| (CCLK) | E9 | output | **Not a top-level port** — driven internally via the `STARTUPE2` primitive. Wire the flash's own CLK pin to package pin **E9**. Unaffected by the bank-14→16 move (CCLK was never a plain port). |
|
||||
|
||||
**Bank 14 VCCO**: assumed **3.3V** (matches the flash's own VCC, typically
|
||||
1.8–3.6V for the W25Q32JV — check its datasheet's exact operating range
|
||||
against whatever VCCO you choose for bank 14).
|
||||
Still requires `BITSTREAM.CONFIG.PERSIST = NO` (the Vivado default — set
|
||||
explicitly in the project XDC; **EXP-0084 note**: the XDC previously had
|
||||
this as `FALSE`, an invalid enum value in this Vivado version — silently
|
||||
not applied at all until fixed to the real `NO`/`YES` enum).
|
||||
|
||||
**Reserved, do not use** (bank 14, same reasons as above but unused by this
|
||||
design — kept clear for any future Quad-SPI/BPI expansion): `L16` (EMCCLK),
|
||||
`R16` (RDWR_B), `V15` (CSI_B). The project's own XDC `PROHIBIT`s these so
|
||||
Vivado's auto-placement never claims them by accident.
|
||||
**Bank 16 VCCO**: **3.3V** (matches the flash's own VCC, typically
|
||||
1.8–3.6V for the W25Q32JV) — chosen freely since bank 16 had no prior VCCO
|
||||
commitment.
|
||||
|
||||
**Real board implication**: since the flash bridge no longer uses the
|
||||
FPGA's own dedicated D00_MOSI/D01_DIN/FCS_B configuration pins at all
|
||||
(K17/K18/L13 are now unused by this design), the flash chip's SPI lines on
|
||||
the PCB connect to D9/D10/C9 instead — a real routing change versus any
|
||||
earlier board layout draft based on the pre-EXP-0084 pinout.
|
||||
|
||||
**Reserved, do not use** (bank 14, unused by this design, kept clear for
|
||||
any future Quad-SPI/BPI expansion — no longer near the flash bus, now just
|
||||
general config-mode-reserved pins): `L16` (EMCCLK), `R16` (RDWR_B), `V15`
|
||||
(CSI_B). The project's own XDC `PROHIBIT`s the underlying **sites** for
|
||||
these (fixed in EXP-0084 — `PROHIBIT` is not a valid property directly on
|
||||
`package_pin` objects in this Vivado version, only on their `site`; this
|
||||
constraint had silently never been enforced before).
|
||||
|
||||
### 2.4 FPGA configuration control (dedicated, bank 0, not negotiable)
|
||||
|
||||
@@ -144,46 +189,93 @@ Signoff history (every real change, same target, in-context P&R):
|
||||
| 0079 (+real activation engine) | +0.030 | 5379 | 16 |
|
||||
| 0082 (+denser activation packing) | +0.068 | 5437 | 16 |
|
||||
| 0083 (+DDRManager phase 1, `ddr_prefetch_mgr.v`) | **+0.073** | 5644 | 16 |
|
||||
| 0084 (32-bit DDR3 widening, 2900ps/172.414MHz) | **-0.618 (FAILS)** | 6418 | 16 |
|
||||
|
||||
### 3.1 EXP-0084: real 32-bit DDR3 widening — functionally verified, timing NOT yet closed
|
||||
|
||||
**Honest status, not glossed over**: the real 32-bit channel (two
|
||||
MT41J128M16JT-125:K chips) is **functionally complete and real-verified**
|
||||
(real xsim against the real 2-chip DDR3 model: `tb_mig_native_adapter.v`
|
||||
12/12 PASS, `tb_n2_system_ddr3.v` 8/8 PASS) — but its own real P&R does
|
||||
**not** close timing at the paired clock speedup (2900ps sys_clk /
|
||||
172.414MHz ui_clk): **WNS = -0.618ns, 213 failing endpoints**. Route
|
||||
completed 100% (a real, physically valid placement/routing), only timing
|
||||
itself fails.
|
||||
|
||||
**Real root cause** (traced to the actual worst path): the violation is
|
||||
inside `neural_processor_packed.v`'s own packed-MAC accumulation tree (a
|
||||
DSP48E1 output through a 4-deep CARRY4 chain), **unchanged since EXP-0059**
|
||||
— it had real positive margin at the old 155.039MHz `ui_clk` (EXP-0083:
|
||||
+0.073ns) but not at the new 172.414MHz (an 11.2% real frequency increase).
|
||||
This is **not caused by the 32-bit width change itself** — every module
|
||||
touched for the widening (adapter, arbiter, activation fetch, etc.) is
|
||||
real-verified functionally correct; the failure is in unrelated, pre-
|
||||
existing compute logic that simply wasn't budgeted for a faster clock.
|
||||
|
||||
**The real, honest decoupling this reveals**: bandwidth = width × clock
|
||||
rate. Widening to 32 bits **alone**, even at the OLD, already-closing
|
||||
3225ps/155.039MHz, already delivers the FULL intended 2× bandwidth gain
|
||||
(1.24 → ~2.48 GB/s physical ceiling) — the clock speedup to 2900ps was a
|
||||
separate, additional optimization stacked on top in the same wizard
|
||||
session, and it is specifically *that* stacking, not the width, that
|
||||
breaks timing.
|
||||
|
||||
**Current real, trustworthy signoff therefore remains EXP-0083** (16-bit,
|
||||
155.039MHz, +0.073ns) until a real P&R closes for the 32-bit configuration
|
||||
— expected once the Input Clock Period is reverted toward 3225ps (keeping
|
||||
Data Width=32), a real, user-gated MIG wizard step, not yet done. See
|
||||
`hardware/v2/logs/experiments.log` EXP-0084 for the complete story,
|
||||
including 5 real P&R/XDC bugs found and fixed along the way (stale
|
||||
differential-clock ports, a real VCCO bank conflict, a stale imported XDC,
|
||||
missing IOSTANDARDs, and two cosmetic but previously-silently-broken XDC
|
||||
property bugs).
|
||||
|
||||
## 4. Real DDR3 memory layout convention
|
||||
|
||||
Both weight data and activation data share the same DDR3 address space
|
||||
(word-addressed, 16-bit words, `BURST_LEN=8` per transaction = 128 bits/burst).
|
||||
Both weight data and activation data share the same DDR3 address space,
|
||||
word-addressed. **EXP-0084**: the native word width is now **32 bits**
|
||||
(`BURST_LEN=8` per transaction = 256 bits/burst, up from 128 bits at the
|
||||
old 16-bit width) — this is the real, current RTL (`hardware/v3/rtl/`),
|
||||
functionally verified; see §3.1 for its pending timing-closure status.
|
||||
|
||||
- **Weights**: one layer's weight set starts at word address `layer_index *
|
||||
WORDS_PER_LAYER` (`WORDS_PER_LAYER = LAYER_BYTES/2`). Densely packed —
|
||||
`layer_prefetch_ctrl.v` reads full bursts sequentially into the on-chip
|
||||
weight buffer once per job.
|
||||
weight buffer once per job. `BYTES_PER_BURST` is now `4*BURST_LEN` (32
|
||||
bytes/burst, up from 16) — the byte-drain logic itself is word-width-
|
||||
agnostic and needed no change beyond this.
|
||||
- **Activations** (real engine, `act_tile_fetch.v`; current layout is the
|
||||
**v2 convention, EXP-0081/0082**): **two consecutive tiles (P_IN=8 INT8
|
||||
values each) share ONE full `BURST_LEN=8`-word (128-bit) burst** — the
|
||||
even-indexed tile occupies the low 64 bits, the odd-indexed tile occupies
|
||||
the high 64 bits. Tile `t`'s burst address is `base + (t>>1)*BURST_LEN`
|
||||
(integer division — two tiles per burst), always burst-aligned by
|
||||
construction. This **halves** real DDR3 bytes-moved-per-useful-byte versus
|
||||
the original EXP-0079 "1 tile = 1 burst" layout, which wasted the upper 64
|
||||
bits of every burst as padding.
|
||||
- **Why this is timing-safe despite selecting a sub-burst half at read
|
||||
time**: the tile index's LSB (which half of the burst a given tile
|
||||
lives in) is known at *request* time, not at response time. It's
|
||||
latched into a register (`sel_lat`) the same cycle the request is
|
||||
accepted — many `ui_clk` cycles before the real DDR3 round-trip
|
||||
completes and `ctrl_rdata` becomes valid. The eventual data-select mux
|
||||
therefore always selects on an already-long-stable registered bit,
|
||||
never a bit racing live read data — this is NOT the runtime-indexed-
|
||||
part-select-on-the-critical-path pattern flagged as a real Fmax risk in
|
||||
EXP-0061 (that pattern is about a select signal arriving *late*,
|
||||
simultaneously with the data it gates). Confirmed timing-safe by real
|
||||
P&R (EXP-0082): margin *improved* from +0.030ns to +0.068ns, not
|
||||
degraded.
|
||||
- Real measured effect: back-to-back same-row DDR3 throughput is a fixed
|
||||
1.24 GB/s (measured, EXP-0080) regardless of packing — this convention
|
||||
doesn't change that ceiling, it changes how much of it is *wasted* on
|
||||
padding, doubling the real achievable useful fraction (see
|
||||
`docs/ARCHITECTURE_ANALYSIS.md` §3 and §5.1).
|
||||
**v3 convention, EXP-0084**): **FOUR consecutive tiles (P_IN=8 INT8
|
||||
values each, 64 bits each) share ONE full `BURST_LEN=8`-word (256-bit)
|
||||
burst** — tile parity 0/1/2/3 (`tcnt[1:0]`) selects bits
|
||||
`[63:0]`/`[127:64]`/`[191:128]`/`[255:192]` of the burst response. Tile
|
||||
`t`'s burst address is `base + (t>>2)*BURST_LEN` (integer division —
|
||||
four tiles per burst), always burst-aligned by construction. This is
|
||||
**not** a further bytes-per-MAC reduction beyond EXP-0081's already-
|
||||
optimal 1 byte/MAC — it's what's required to keep that same 100% packing
|
||||
utilization at the new, larger burst size instead of leaving half of it
|
||||
newly wasted.
|
||||
- **Why this is timing-safe despite selecting a sub-burst quarter at
|
||||
read time**: the tile index's own low 2 bits (which quarter of the
|
||||
burst a given tile lives in) are known at *request* time, not at
|
||||
response time. They're latched into a register (`sel_lat`) the same
|
||||
cycle the request is accepted — many `ui_clk` cycles before the real
|
||||
DDR3 round-trip completes and `ctrl_rdata` becomes valid. The eventual
|
||||
data-select mux is an explicit 4-way `case` on constant byte offsets
|
||||
(not a runtime-indexed part-select expression) — extending the exact
|
||||
same discipline EXP-0081 established for the 1-bit case to 2 bits.
|
||||
This mux is real and functionally verified, but is unrelated to
|
||||
§3.1's own real timing failure (that violation is inside the compute
|
||||
core's accumulation tree, not this fetch path).
|
||||
- Real measured effect: back-to-back same-row DDR3 throughput was a
|
||||
fixed 1.24 GB/s at 16-bit width (measured, EXP-0080); at 32-bit width
|
||||
the real physical ceiling doubles to ~2.48 GB/s, independent of this
|
||||
packing convention — packing controls how much of that ceiling is
|
||||
*wasted*, not the ceiling itself (see `docs/ARCHITECTURE_ANALYSIS.md`
|
||||
§3 and §5.1/§5.4).
|
||||
- `base` (a job's own `x_base_a`/`x_base_b`) is chosen freely by whoever
|
||||
submits jobs (the SPI host) — just keep each position's own activation
|
||||
array in its own non-overlapping `(N_TILES/2) * BURST_LEN`-word region.
|
||||
array in its own non-overlapping `(N_TILES/4) * BURST_LEN`-word region.
|
||||
|
||||
## 5. FPGA configuration (boot) procedure
|
||||
|
||||
@@ -249,19 +341,27 @@ table is a summary/index.
|
||||
engine (currently `result_data_a/b` are literal top-level pins, fine at
|
||||
N=2 but a real pin-budget blocker beyond that) must land first — see
|
||||
`docs/ARCHITECTURE_ANALYSIS.md` §5.3.
|
||||
- The reset pin and other very-low-pin-count signals have no fixed PCB
|
||||
location yet — assign once the rest of the board layout (reset circuit,
|
||||
status LEDs, etc.) is decided.
|
||||
- The §3 timing margin (+0.073ns) is real but thin — do not add logic
|
||||
without a fresh real P&R to confirm it still closes.
|
||||
- `sys_rst` has only a *tentative* real pin (G13, bank 15, EXP-0084 —
|
||||
chosen just to unblock real P&R, not a final board decision) — assign
|
||||
its real, permanent location once the rest of the board layout (reset
|
||||
circuit, status LEDs, etc.) is decided.
|
||||
- **The 32-bit DDR3 channel's own real timing does NOT currently close**
|
||||
(EXP-0084: WNS -0.618ns at 2900ps/172.414MHz) — the current real,
|
||||
trustworthy signoff remains EXP-0083 (16-bit, 155.039MHz, +0.073ns) until
|
||||
the Input Clock Period is reverted toward 3225ps (keeping Data Width=32)
|
||||
in one more real, user-gated MIG wizard session. See §3.1 for the full
|
||||
honest story and real root cause (unrelated to the width change itself).
|
||||
- ESP32-side JTAG bit-banging firmware (§5.1) does not exist yet — it's
|
||||
software work on the host side, not part of this FPGA RTL.
|
||||
- A 32-bit-wide DDR3 channel (decided, see `docs/ARCHITECTURE_ANALYSIS.md`
|
||||
§5.4) requires the user's own real interactive Vivado MIG "Customize IP"
|
||||
wizard session (Data Width + Input Clock Period changed together) —
|
||||
not yet done; every timing/bandwidth number in this document still
|
||||
reflects the current 16-bit channel.
|
||||
- `ddr_prefetch_mgr.v` (DDRManager phase 1, EXP-0083) is real, built, and
|
||||
timing-verified, but its measured benefit (2.86% real reduction in total
|
||||
simulated time) is modest — see `docs/ARCHITECTURE_ANALYSIS.md` §5.2 for
|
||||
the honest number and why. The larger multi-slot DDRManager is not built.
|
||||
timing-verified (at the OLD 16-bit/155MHz signoff), but its measured
|
||||
benefit (2.86% real reduction in total simulated time) is modest — see
|
||||
`docs/ARCHITECTURE_ANALYSIS.md` §5.2 for the honest number and why. The
|
||||
larger multi-slot DDRManager is not built. Its own real benefit should be
|
||||
re-measured once the 32-bit channel's timing actually closes.
|
||||
- User-requested, not yet built: an active-low `data_ready_n`/IRQ pin so
|
||||
the ESP32 can be interrupt-driven (job completion or error pending)
|
||||
instead of polling the STATUS register — planned as a sticky bit (set on
|
||||
`job_out_done`/`dir_error`, cleared when the ESP32 reads STATUS),
|
||||
deliberately deferred until the §3.1 timing-closure work lands so it
|
||||
doesn't further complicate the currently-tight I/O/VCCO budget mid-fix.
|
||||
|
||||
@@ -5330,3 +5330,232 @@ without ddr_prefetch_mgr) against the wider channel to see whether look-
|
||||
ahead's real benefit grows once the physical ceiling is higher; (3) build
|
||||
the result-writeback engine (S5.3, still the real blocker for N>2); (4)
|
||||
real N=2/4/8/16 scaling tests per the user's own final directive.
|
||||
|
||||
EXP-0084 -- real 32-bit DDR3 channel widening: functionally complete
|
||||
and real-verified, but real timing does NOT close at the paired clock
|
||||
speedup -- honest finding, width and clock speed are separable
|
||||
(2026-09-20, same autonomous continuation, user's own direction: "ok
|
||||
sono d'accordo andiamo per un canale fisico a 32 bit... cerchiamo di
|
||||
spremere al massimo il timing")
|
||||
|
||||
CONTEXT: docs/ARCHITECTURE_ANALYSIS.md S5.4 (EXP-0081/0082) recommended
|
||||
32-bit single-channel widening over a second independent DDR3 channel,
|
||||
based on real device data (XC7A100T-CSG324 bank/DQS pin analysis).
|
||||
User ran the real Vivado MIG "Customize IP" wizard themselves: Data
|
||||
Width 16->32 (two MT41J128M16JT-125:K chips ganged in parallel, a real
|
||||
PCB change the user explicitly confirmed they already understood),
|
||||
Input Clock Period 3225ps->2900ps (the fastest value that still keeps
|
||||
PHY:Controller ratio at 2:1, found by the user testing the wizard's
|
||||
own real constraint directly -- below 2900ps the wizard forces 4:1,
|
||||
which would have HALVED ui_clk instead of speeding it up), Differential
|
||||
system clock AND reference clock (both real board decisions -- a
|
||||
differential oscillator, and T14/T15 bank 14 for clk_ref specifically
|
||||
because the wizard's own UG586 placement rules restricted that net to
|
||||
bank 14).
|
||||
|
||||
REAL RTL ADAPTATION (the shared ctrl bus's own native word width
|
||||
changed from 16 to 32 bits system-wide, BURST_LEN=8 unchanged --
|
||||
burst payload 128->256 bits):
|
||||
- mig_native_adapter.v: app_wdf_data/app_rd_data 64->128 bits (real,
|
||||
confirmed against the regenerated mig_7series_0.v: app data width
|
||||
= DataWidth*BURST_LEN/nCK_PER_CLK = 32*8/2 = 128, matches exactly),
|
||||
app_wdf_mask 8->16 bits, ctrl wdata/wmask 32*BURST_LEN/4*BURST_LEN.
|
||||
Beat count (2) and state-machine shape unchanged -- only the
|
||||
per-beat slice widths changed.
|
||||
- sdram_arbiter_n.v, layer_prefetch_ctrl.v (v2, reused -- real,
|
||||
disclosed, deliberate exception to its "unmodified from v2"
|
||||
status, see its own header), host_mem_bridge.v, packed_slot.v,
|
||||
ddr_prefetch_mgr.v, n2_system_ddr3_top.v: mechanical width bump of
|
||||
the shared ctrl_wdata/ctrl_wmask/ctrl_rdata convention throughout.
|
||||
- act_tile_fetch.v: the REAL logic-bearing change. Burst now holds
|
||||
256 bits = FOUR 64-bit tiles (was 128 bits = two, EXP-0081) --
|
||||
sel_lat extended from 1 to 2 registered bits, tile_offset divisor
|
||||
from tcnt>>1 to tcnt>>2, and the 2-way ternary mux replaced with an
|
||||
explicit 4-way `case` on constant byte offsets (not a runtime part-
|
||||
select -- same EXP-0081 discipline, registered select known at
|
||||
request time, extended from 1 to 2 bits). This is NOT a further
|
||||
bytes-per-MAC reduction beyond EXP-0081's already-optimal 1
|
||||
byte/MAC -- it's what's REQUIRED to keep that same 100% packing
|
||||
utilization at the new, larger burst size instead of leaving half
|
||||
of it newly wasted.
|
||||
- host_mem_bridge.v: a real, deliberate ADDRESSING REDESIGN, not a
|
||||
mechanical bump. The host-facing contract (mem_addr as a 16-bit-
|
||||
word address, mem_wdata/mem_rdata 16-bit, mem_lb_n/mem_ub_n byte
|
||||
enables) is kept COMPLETELY UNCHANGED -- spi_host_bridge_v3.v's own
|
||||
WRITE_MEM/READ_MEM opcode payload size, and by extension the ESP32
|
||||
firmware contract, is NOT touched by the DDR3 widening. mem_addr's
|
||||
LSB now additionally selects which 16-bit half of the addressed
|
||||
32-bit ctrl-bus word to target. Real, disclosed limitation: this
|
||||
halves the host's own reachable byte range for a given ADDR_WIDTH
|
||||
-- acceptable for this debug/raw-access path at the project's real
|
||||
current scale, not the compute path.
|
||||
- n2_system_ddr3_top.v: ddr3_dq 16->32 bits, ddr3_dqs_p/n 2->4 bits,
|
||||
ddr3_dm 2->4 bits (real, confirmed against the regenerated
|
||||
mig_7series_0.v wrapper -- address/command/control lines unchanged,
|
||||
shared identically by both chips). app_wdf_data/app_rd_data/
|
||||
app_wdf_mask widths matched to mig_native_adapter.v's own.
|
||||
|
||||
NEW TEST INFRASTRUCTURE: burst_mem_model32.v -- an explicitly SYNTHETIC
|
||||
32-bit-wide burst-memory test model (NOT a real chip model, unlike
|
||||
sdram_controller.v/sdram_model.v which genuinely represent the real
|
||||
AS4C32M16SA x16 SDR part and are correctly, deliberately NOT modified
|
||||
here -- that real chip is inherently 16-bit, shared by 20+ other v2/v3
|
||||
tests, out of scope). Built to unblock the isolated fast (iverilog)
|
||||
tests for modules that now speak the 32-bit convention, matching this
|
||||
project's own "fast backend for glue-logic, real DDR3 backend for the
|
||||
trustworthy number" precedent. Real bug found and fixed during bring-
|
||||
up: the model's first version sized its dense backing array at
|
||||
MEM_ADDR_BITS=16 (65536 entries) -- tb_packed_slot.v's own
|
||||
ACT_MEM_BASE=0x10000 (=65536) SILENTLY WRAPPED to address 0, aliasing
|
||||
the weight and activation regions and producing real, confusing wrong-
|
||||
answer failures (7/9 tests failing with plausible-looking but wrong
|
||||
0/127 values) that took a real root-cause pass to trace to the
|
||||
truncation, not a logic bug. Fixed by widening to MEM_ADDR_BITS=20
|
||||
(~1M entries, ~32MB simulation memory, trivial cost). Also found and
|
||||
fixed: a wmask polarity bug (real DQM convention is 0=write/1=masked,
|
||||
matching sdram_controller.v's own documented convention -- the first
|
||||
draft had it backwards).
|
||||
|
||||
TESTBENCHES UPDATED (all re-verified real, after the burst_mem_model32
|
||||
fixes): tb_act_tile_fetch.v (10/10 PASS, rewritten for 4-tiles/burst),
|
||||
tb_ddr_prefetch_mgr.v (33/33 PASS, PART 3's own same-row-vs-row-switch
|
||||
A/B dropped -- burst_mem_model32's fixed latency doesn't carry that
|
||||
distinction the way the real DDR3 backend does, so it no longer means
|
||||
anything on this backend; EXP-0083's own real-DDR3-backend 2.86%
|
||||
number remains the trustworthy one for that question), tb_host_mem_
|
||||
bridge.v (32/32 PASS, extended to cover all 16 half-word offsets per
|
||||
burst now, was 8), tb_sdram_arbiter_n.v (7/7 PASS), tb_packed_slot.v
|
||||
(9/9 PASS, bit-identical result pattern to EXP-0081 -- confirms zero
|
||||
effect on computed results).
|
||||
|
||||
REAL xsim VERIFICATION (real MIG IP + real ddr3_model.sv, TWO real
|
||||
component instances now, DQ_WIDTH=32/16-per-component -- exact real
|
||||
vendor pattern confirmed by reading the regenerated sim_tb_top.v's own
|
||||
generate block, not assumed):
|
||||
1. tb_mig_native_adapter.v: 12/12 PASS. Found and fixed a real
|
||||
testbench-only bug during bring-up (not an RTL bug): the write-
|
||||
pattern fill loop still used the old 16-bit-word slicing
|
||||
(wpat[k*16+:16]) even after the port widths were bumped -- sed's
|
||||
blanket 16*BURST_LEN->32*BURST_LEN substitution correctly missed
|
||||
this since it's a different expression shape; same class of gap
|
||||
already hit once in tb_sdram_arbiter_n.v this same session.
|
||||
2. tb_n2_system_ddr3.v: 8/8 PASS, 0 errors, 8/8 positions completed,
|
||||
real N=2 system against the real 2-chip DDR3 model, both chips
|
||||
visibly returning DIFFERENT real data in the JEDEC trace
|
||||
(confirms real 32-bit width utilization, not address aliasing).
|
||||
|
||||
REAL P&R -- 5 real bugs found and fixed across iterations, in order:
|
||||
1. n2_system_ddr3_top.v's own top-level MIG instantiation still used
|
||||
the OLD single-ended sys_clk_i/clk_ref_i ports -- real synthesis
|
||||
ERROR ("named port connection 'sys_clk_i' does not exist"). Both
|
||||
sys_clk and clk_ref are now real differential pairs on the
|
||||
regenerated public mig_7series_0.v wrapper (the user's own wizard
|
||||
choice). Fixed: n2_system_ddr3_top.v's own top-level ports changed
|
||||
from sys_clk_i/clk_ref_i to sys_clk_p/sys_clk_n/clk_ref_p/
|
||||
clk_ref_n, matching the real board implication (a differential
|
||||
oscillator, not single-ended).
|
||||
2. Real IO placement failure: "40 unplaced IO Ports vs 10 available
|
||||
pins". Root cause: a real VCCO conflict -- the flash SPI bus
|
||||
(K17/K18/L13) and the differential clk_ref_p/n (T14/T15) both sit
|
||||
in bank 14, needing incompatible voltages (LVCMOS33/3.3V vs
|
||||
LVDS_25/2.5V). This was flagged as a real *risk* when T14/T15 was
|
||||
chosen mid-wizard-session (real device data showed the conflict
|
||||
was possible); this P&R run turned it into a real, observed
|
||||
failure. Fixed: moved the flash bus to bank 16 (D9/D10/C9 --
|
||||
completely unconstrained, no VCCO commitment, real verified-free
|
||||
pins from the actual part database).
|
||||
3. Root cause of why bug #2's own XDC fix didn't take effect on the
|
||||
first re-run: the project's own n2_system_ddr3_top.xdc was a
|
||||
STALE IMPORTED COPY -- the SAME class of bug CLAUDE.md already
|
||||
documents for RTL files (EXP-0078), now confirmed to also apply
|
||||
to constraint files. The imported copy was old enough to still
|
||||
have the PRE-EXP-0077 flash-pin PROHIBIT constraints (predating
|
||||
the real flash bridge entirely). Fixed the same way: removed and
|
||||
re-added as a direct reference.
|
||||
4. With the real XDC now live, a further real placement failure:
|
||||
`sys_rst` (and, on an earlier pass, the result-data/status ports)
|
||||
had no explicit IOSTANDARD, defaulting to LVCMOS18 -- with banks
|
||||
14/15/34/35 now ALL committed to other real voltages (2.5V/3.3V/
|
||||
1.5V/1.5V) by the wider DDR3 interface, there is genuinely no
|
||||
1.8V-compatible bank left. This was already disclosed in docs/
|
||||
PHYSICAL_REALIZATION.md S7 ("sys_rst... no fixed PCB location
|
||||
yet") but the OLD, narrower 16-bit I/O footprint had enough slack
|
||||
for it to silently default-fit somewhere; the wider interface
|
||||
removed that slack. Fixed: explicit LVCMOS33 on all of them,
|
||||
sys_rst placed at G13 (bank 15, real verified-free pin) --
|
||||
NOT a final board decision, still pending the real PCB reset
|
||||
circuit layout.
|
||||
5. Two cosmetic XDC bugs surfaced as Critical Warnings once the real
|
||||
live XDC was actually being read (previously silently ignored
|
||||
from the stale copy): BITSTREAM.CONFIG.PERSIST FALSE is not a
|
||||
valid enum value in this Vivado version (needs NO/YES, not
|
||||
TRUE/FALSE -- real, harmless since NO is also the default, but
|
||||
the property was silently not being set at all before); and
|
||||
PROHIBIT is not a valid property on package_pin objects, only on
|
||||
the underlying site objects (fixed via `get_sites -of_objects`)
|
||||
-- meaning the EMCCLK/RDWR_B/CSI_B PROHIBIT constraint had
|
||||
SILENTLY NEVER WORKED in this project's entire history, only
|
||||
surfacing now because the stale-XDC fix (#3) finally let Vivado
|
||||
actually parse the real file. No real harm came of this (nothing
|
||||
ever auto-placed there), but it was never actually enforced.
|
||||
|
||||
REAL, FINAL P&R RESULT (route_design 100%, 0 placement errors --
|
||||
functionally a complete, real, routed design):
|
||||
Slice LUTs = 6418 (up from EXP-0083's 5644, +774 -- consistent with
|
||||
the wider ctrl-bus muxes/registers throughout the shared memory
|
||||
path: arbiter, act_tile_fetch's 4-way case, ddr_prefetch_mgr's
|
||||
wider ping-pong buffer, host_mem_bridge's wider mask logic,
|
||||
mig_native_adapter's wider beats)
|
||||
DSP48E1 = 16 (UNCHANGED since EXP-0059 -- confirms again the compute
|
||||
datapath itself is untouched by this change)
|
||||
WHS (hold) = +0.048ns (real, closes)
|
||||
WNS (setup) = **-0.618ns -- REAL TIMING FAILURE, 213 failing
|
||||
endpoints, TNS=-61.621ns. Honestly reported, not hidden.**
|
||||
|
||||
ROOT CAUSE OF THE REAL TIMING FAILURE (traced to the actual worst
|
||||
path, not assumed): the violating path is INSIDE `neural_processor_
|
||||
packed.v`'s own packed-MAC accumulation tree (u_slot0/u_np/
|
||||
GEN_MAC_PACKED[5].product, a DSP48E1, through a 4-deep CARRY4 chain,
|
||||
into prodb1_reg[5][15]) -- real data path delay 6.26ns against a
|
||||
5.8ns period budget. This datapath is UNCHANGED since EXP-0059 and
|
||||
had real, positive margin at the OLD ui_clk (155.039MHz, period
|
||||
6.447ns) -- EXP-0083's own real signoff was +0.073ns. The NEW ui_clk
|
||||
(172.414MHz, period 5.8ns, an 11.2% real frequency increase) simply
|
||||
doesn't leave this specific, pre-existing critical path enough time,
|
||||
independent of anything actually changed by the 32-bit width work.
|
||||
|
||||
THE REAL, HONEST DECOUPLING THIS FINDING REVEALS: the 32-bit DATA
|
||||
WIDTH change and the CLOCK PERIOD change were bundled into one wizard
|
||||
session, but they are NOT the same lever. Bandwidth = width x clock
|
||||
rate -- widening from 16 to 32 bits ALONE, even at the OLD 3225ps/
|
||||
310.078MHz sys_clk (155.039MHz ui_clk, already real-proven to close
|
||||
timing with margin), already delivers the FULL intended 2x real
|
||||
bandwidth gain (1.24GB/s -> ~2.48GB/s physical ceiling). The clock
|
||||
speedup to 2900ps/344.828MHz (172.414MHz ui_clk) was a SEPARATE,
|
||||
ADDITIONAL optimization stacked on top -- and it is THAT specific
|
||||
stacking, not the width change, that breaks real timing. This is
|
||||
exactly the kind of "serious, critical, not accondiscendente" finding
|
||||
the user has consistently asked for.
|
||||
|
||||
DECISION: keep all the REAL RTL adaptation work (verified, real,
|
||||
functionally correct via real xsim against real 2-chip DDR3, needed
|
||||
regardless of the final clock choice) -- do NOT revert it. Do NOT
|
||||
claim this P&R is a clean timing signoff -- it is not, and is not
|
||||
being presented as one. The CURRENT real, trustworthy timing signoff
|
||||
remains EXP-0083's own (+0.073ns, 16-bit width, 155.039MHz) until a
|
||||
real P&R closes for the 32-bit configuration.
|
||||
|
||||
next_action: real, user-gated -- re-run the MIG wizard ONE more time,
|
||||
changing ONLY the Input Clock Period back toward 3225ps (keeping Data
|
||||
Width=32), since width alone already delivers the intended bandwidth
|
||||
win without the timing risk the paired clock speedup introduced. Not
|
||||
hand-editable (same real JEDEC/PLL-calculator reasoning as every other
|
||||
MIG timing parameter this project has never hand-edited). Once that
|
||||
real P&R closes, update docs/PHYSICAL_REALIZATION.md and docs/
|
||||
ARCHITECTURE_ANALYSIS.md S5.4 with the REAL final numbers (not these
|
||||
provisional ones). Separately, and out of scope for a channel-width
|
||||
task: if the user wants to keep pushing ui_clk faster in the future,
|
||||
neural_processor_packed.v's own packed-MAC accumulation tree (the
|
||||
real bottleneck identified above, unchanged since EXP-0059) would need
|
||||
real re-pipelining -- a genuinely separate, disclosed, not-yet-
|
||||
attempted optimization.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
// already measured (7.16x real memory-side speedup vs zero-reuse,
|
||||
// same hardware, see that testbench's own header).
|
||||
//
|
||||
// One layer = LAYER_BYTES bytes, fetched as LAYER_BYTES/(2*BURST_LEN)
|
||||
// One layer = LAYER_BYTES bytes, fetched as LAYER_BYTES/BYTES_PER_BURST
|
||||
// back-to-back BURST_LEN-word transactions starting at layer_base
|
||||
// (word address). Sequential -> lands in the SAME open row for any
|
||||
// layer that fits within one row (1024 columns = 256 tile-blocks at
|
||||
@@ -18,7 +18,7 @@
|
||||
// target models use), so this composes directly with EXP-0054's
|
||||
// open-row policy without needing anything special here.
|
||||
//
|
||||
// Each captured burst (ctrl_rdata, 16*BURST_LEN bits) is LATCHED
|
||||
// Each captured burst (ctrl_rdata, 32*BURST_LEN bits) is LATCHED
|
||||
// locally before draining -- does not assume the controller holds
|
||||
// rdata stable beyond the cycle `ready` pulses (its own documented
|
||||
// contract is "valid the same cycle ready pulses", nothing more).
|
||||
@@ -27,6 +27,17 @@
|
||||
// sub-counters to keep in sync, deliberately simpler than a first
|
||||
// draft of this module that tracked them separately and was harder to
|
||||
// convince correct by inspection.
|
||||
//
|
||||
// EXP-0084 (real 32-bit DDR3 channel widening): ctrl_wdata/ctrl_rdata
|
||||
// widened from the old 16-bit-word convention to 32*BURST_LEN bits
|
||||
// (BYTES_PER_BURST from 2 bytes/word*BURST_LEN to 4 bytes/word*
|
||||
// BURST_LEN) -- the ONLY real change needed, since the
|
||||
// byte-drain logic itself was already word-width-agnostic (drains
|
||||
// BYTES_PER_BURST bytes one at a time regardless of the underlying
|
||||
// word size). This is a real, deliberate exception to this file's own
|
||||
// "reused unmodified from v2" status -- the shared ctrl bus's own
|
||||
// width is a real interface contract that changed project-wide, not a
|
||||
// V3-specific choice this file could stay isolated from.
|
||||
// ============================================================
|
||||
module layer_prefetch_ctrl #(
|
||||
parameter DATA_WIDTH = 8,
|
||||
@@ -54,20 +65,20 @@ module layer_prefetch_ctrl #(
|
||||
output reg ctrl_req,
|
||||
output wire ctrl_wr, // always 0: read-only
|
||||
output reg [ADDR_WIDTH-1:0] ctrl_addr,
|
||||
output wire [16*BURST_LEN-1:0] ctrl_wdata, // unused (read-only), tied off
|
||||
output wire [2*BURST_LEN-1:0] ctrl_wmask, // unused (read-only), tied off
|
||||
input wire [16*BURST_LEN-1:0] ctrl_rdata,
|
||||
output wire [32*BURST_LEN-1:0] ctrl_wdata, // unused (read-only), tied off
|
||||
output wire [4*BURST_LEN-1:0] ctrl_wmask, // unused (read-only), tied off
|
||||
input wire [32*BURST_LEN-1:0] ctrl_rdata,
|
||||
input wire ctrl_ready,
|
||||
input wire ctrl_busy
|
||||
);
|
||||
localparam BYTES_PER_BURST = 2*BURST_LEN;
|
||||
localparam BYTES_PER_BURST = 4*BURST_LEN;
|
||||
localparam BURSTS_PER_LAYER = LAYER_BYTES/BYTES_PER_BURST;
|
||||
localparam BIDXW = (BURSTS_PER_LAYER <= 1) ? 1 : $clog2(BURSTS_PER_LAYER);
|
||||
localparam DIDXW = $clog2(BYTES_PER_BURST);
|
||||
|
||||
assign ctrl_wr = 1'b0;
|
||||
assign ctrl_wdata = {(16*BURST_LEN){1'b0}};
|
||||
assign ctrl_wmask = {(2*BURST_LEN){1'b0}};
|
||||
assign ctrl_wdata = {(32*BURST_LEN){1'b0}};
|
||||
assign ctrl_wmask = {(4*BURST_LEN){1'b0}};
|
||||
|
||||
localparam S_IDLE = 3'd0,
|
||||
S_WAIT = 3'd1,
|
||||
@@ -78,7 +89,7 @@ module layer_prefetch_ctrl #(
|
||||
reg [BIDXW-1:0] burst_idx;
|
||||
reg [DIDXW-1:0] drain_cnt;
|
||||
reg [ADDR_WIDTH-1:0] base_lat;
|
||||
reg [16*BURST_LEN-1:0] burst_lat;
|
||||
reg [32*BURST_LEN-1:0] burst_lat;
|
||||
|
||||
// combinational: which byte of the layer is currently being drained
|
||||
wire [BUFADDRW-1:0] cur_fill_addr = burst_idx * BYTES_PER_BURST + drain_cnt;
|
||||
@@ -96,7 +107,7 @@ module layer_prefetch_ctrl #(
|
||||
burst_idx <= {BIDXW{1'b0}};
|
||||
drain_cnt <= {DIDXW{1'b0}};
|
||||
base_lat <= {ADDR_WIDTH{1'b0}};
|
||||
burst_lat <= {(16*BURST_LEN){1'b0}};
|
||||
burst_lat <= {(32*BURST_LEN){1'b0}};
|
||||
end else begin
|
||||
ctrl_req <= 1'b0;
|
||||
fill_we <= 1'b0;
|
||||
|
||||
@@ -15,20 +15,30 @@
|
||||
# when PERSIST is FALSE (the Vivado default -- set explicitly here so
|
||||
# this dependency is self-documenting in the constraints, not just a
|
||||
# silent default someone could flip later without realizing why).
|
||||
set_property BITSTREAM.CONFIG.PERSIST FALSE [current_design]
|
||||
set_property BITSTREAM.CONFIG.PERSIST NO [current_design]
|
||||
|
||||
# ---- config-flash passthrough (-> flash_spi_master.v, EXP-0077):
|
||||
# D00_MOSI/D01_DIN/FCS_B are the SAME physical pins the FPGA's own
|
||||
# dedicated Master-SPI config hardware uses AT BOOT to self-load its
|
||||
# bitstream -- post-configuration they become ordinary fabric I/O
|
||||
# (real Xilinx behavior, PERSIST=FALSE, the Vivado default) and this
|
||||
# design deliberately reclaims them for the flash_spi_master.v bridge
|
||||
# (the board wires the config flash EXCLUSIVELY to the FPGA -- see
|
||||
# that module's own header). CCLK is NOT constrained here -- it's
|
||||
# driven via STARTUPE2 internally, never a plain top-level port.
|
||||
set_property PACKAGE_PIN K17 [get_ports flash_mosi]
|
||||
set_property PACKAGE_PIN K18 [get_ports flash_miso]
|
||||
set_property PACKAGE_PIN L13 [get_ports flash_cs_n]
|
||||
# EXP-0084 REAL, RE-CONFIRMED CONFLICT (not hypothetical any more):
|
||||
# D00_MOSI/D01_DIN/FCS_B's ORIGINAL pins (K17/K18/L13, bank 14) share
|
||||
# bank 14 with the real differential reference clock (clk_ref_p/n,
|
||||
# T14/T15) chosen during the EXP-0084 MIG wizard session -- a single
|
||||
# I/O bank can only have ONE VCCO, and LVCMOS33 (3.3V, what the flash
|
||||
# needs) is incompatible with LVDS_25 (2.5V, what clk_ref needs). This
|
||||
# was flagged as a real *risk* when T14/T15 was chosen (real device
|
||||
# data showed bank 14 already hosted the flash bus); EXP-0084's real
|
||||
# place_design run turned that risk into a real, observed placement
|
||||
# failure ("IO placement is infeasible" -- flash_mosi/flash_cs_n
|
||||
# couldn't be placed at their old LOCs at all). FIX: moved the flash
|
||||
# bus to bank 16 (D9/D10/C9) -- completely unconstrained, no VCCO
|
||||
# commitment, so it can freely be 3.3V with zero conflict. Real,
|
||||
# verified-available pins (queried from the actual part database, not
|
||||
# guessed) -- D9/D10/C9 are ordinary I/O in bank 16, none of them are
|
||||
# the config-mode-reserved EMCCLK/RDWR_B/CSI_B pins (still PROHIBITed
|
||||
# below regardless). CCLK is NOT constrained here -- it's driven via
|
||||
# STARTUPE2 internally, never a plain top-level port.
|
||||
set_property PACKAGE_PIN D9 [get_ports flash_mosi]
|
||||
set_property PACKAGE_PIN D10 [get_ports flash_miso]
|
||||
set_property PACKAGE_PIN C9 [get_ports flash_cs_n]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports flash_mosi]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports flash_miso]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports flash_cs_n]
|
||||
@@ -39,7 +49,21 @@ set_property IOSTANDARD LVCMOS33 [get_ports flash_cs_n]
|
||||
# extra data lines) -- PROHIBITed so Vivado's auto-placement never
|
||||
# lands an unrelated port there by accident (it already had once,
|
||||
# before this constraint existed, on a result-data bit).
|
||||
set_property PROHIBIT true [get_package_pins {L16 R16 V15}]
|
||||
set_property PROHIBIT true [get_sites -of_objects [get_package_pins {L16 R16 V15}]]
|
||||
|
||||
# ---- EXP-0084: remaining top-level ports with no fixed board LOC yet
|
||||
# (result-data debug pins, status signals) default to LVCMOS18 with no
|
||||
# explicit IOSTANDARD set -- real place_design found this real,
|
||||
# concrete: banks 14/15/34/35 are ALL already committed to other real
|
||||
# voltages (2.5V/3.3V/1.5V/1.5V), leaving only bank 16's spare pins as
|
||||
# LVCMOS18-compatible, and there aren't enough of them (40 ports vs 10
|
||||
# pins). Assign these explicitly to LVCMOS33 so they place in bank
|
||||
# 15's own real spare capacity (46 free pins) instead -- a real,
|
||||
# necessary fix, not a workaround; a permanent board LOC for each
|
||||
# should still be assigned once the rest of the board layout is
|
||||
# decided (S7 of docs/PHYSICAL_REALIZATION.md).
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports {s0_result_data_a[*] s0_result_data_b[*] s1_result_data_a[*] s1_result_data_b[*]}]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports {job_out_slot[*] job_out_done init_calib_complete ui_clk_o}]
|
||||
|
||||
# ---- neural-processor management SPI (-> spi_host_bridge_v3.v):
|
||||
# job submission + register file. Bank 15, column A/B (package edge,
|
||||
@@ -57,9 +81,17 @@ set_property IOSTANDARD LVCMOS33 [get_ports mosi]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports miso]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports cs_n]
|
||||
|
||||
# ---- sys_rst: not part of the DDR3 MIG's own pin set (that's
|
||||
# sys_rst too, but MIG's XDC only constrains the DDR3-facing timing,
|
||||
# not necessarily IOSTANDARD for every board variant) -- pin left to
|
||||
# auto-placement for now (low pin-count, no real board decision yet
|
||||
# on where the reset source sits); explicitly constrain once the PCB
|
||||
# layout for the reset circuit (button/supervisor IC) is decided.
|
||||
# ---- sys_rst: EXP-0084 found this real -- with the 32-bit DDR3
|
||||
# interface now committing banks 14/34/35 to 2.5V/1.5V/1.5V and bank
|
||||
# 15 to 3.3V, there is genuinely NO bank left at the 1.8V sys_rst was
|
||||
# silently defaulting to (no explicit IOSTANDARD was ever set) -- real
|
||||
# place_design failure ("IO placement is infeasible... needs 1.8V,
|
||||
# has 0 sites"), not hit before only because the smaller 16-bit I/O
|
||||
# footprint happened to leave enough slack somewhere. Placed here
|
||||
# temporarily in bank 15 alongside the management SPI bus (same real,
|
||||
# already-committed 3.3V) -- NOT a final board decision, still pending
|
||||
# the real PCB layout for the reset circuit (button/supervisor IC),
|
||||
# per S7 of docs/PHYSICAL_REALIZATION.md. Real, verified-free pin
|
||||
# (queried from the actual part database).
|
||||
set_property PACKAGE_PIN G13 [get_ports sys_rst]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports sys_rst]
|
||||
|
||||
@@ -15,30 +15,38 @@
|
||||
// so buffering it on-chip would only add complexity for zero benefit.
|
||||
// This engine reads DIRECTLY from DDR3 per tile instead.
|
||||
//
|
||||
// MEMORY LAYOUT CONVENTION v2 (EXP-0081, real, disclosed, and REQUIRED
|
||||
// MEMORY LAYOUT CONVENTION v3 (EXP-0084, real, disclosed, and REQUIRED
|
||||
// of whoever prepares activation data in DDR3 -- documented in the
|
||||
// physical realization doc too): TWO consecutive tiles (P_IN=8 INT8
|
||||
// values each) share ONE full BURST_LEN=8-word (128-bit) burst -- even
|
||||
// tile index in the LOW 64 bits, odd tile index in the HIGH 64 bits.
|
||||
// Tile t's burst address is `base + (t>>1)*BURST_LEN`. This HALVES
|
||||
// real DDR3 bytes-moved-per-useful-byte versus the original EXP-0079
|
||||
// "1 tile = 1 burst" layout (real measured 1.24GB/s ceiling could only
|
||||
// sustain ~25% of one core's peak DSP throughput under that layout --
|
||||
// see docs/ARCHITECTURE_ANALYSIS.md S3.2 -- this doubles the real
|
||||
// achievable fraction).
|
||||
// physical realization doc too): FOUR consecutive tiles (P_IN=8 INT8
|
||||
// values each, 64 bits each) share ONE full BURST_LEN=8-word burst --
|
||||
// since EXP-0084's real 32-bit DDR3 channel widening, one burst is now
|
||||
// 8*32=256 bits (up from 128 bits at the old 16-bit width), and 4
|
||||
// tiles of 64 bits exactly fill it (100% utilization, same packing
|
||||
// EFFICIENCY as EXP-0081's "2 tiles fill a 128-bit burst" -- this is
|
||||
// NOT a further bytes-per-MAC reduction beyond EXP-0081's already-
|
||||
// optimal 1 byte/MAC, it is what's REQUIRED to keep that same 100%
|
||||
// utilization at the new, larger burst size instead of leaving half
|
||||
// of it newly wasted). Tile index within the burst selects a quarter:
|
||||
// tile parity 0/1/2/3 (tcnt[1:0]) -> bits [63:0]/[127:64]/[191:128]/
|
||||
// [255:192] of the burst response. Tile t's burst address is
|
||||
// `base + (t>>2)*BURST_LEN`.
|
||||
//
|
||||
// WHY THIS IS TIMING-SAFE (the thing EXP-0079 deliberately avoided):
|
||||
// the tile index's own LSB (which half of the burst to use) is known
|
||||
// at REQUEST time, not at response time -- it's registered into
|
||||
// the tile index's own low 2 bits (which quarter of the burst to use)
|
||||
// are known at REQUEST time, not at response time -- registered into
|
||||
// `sel_lat` the SAME cycle `tcnt` is latched, many ui_clk cycles
|
||||
// BEFORE the real DDR3 round-trip completes and `ctrl_rdata` becomes
|
||||
// valid. The eventual data-select mux therefore selects using an
|
||||
// already-long-stable registered bit, never a bit racing the read
|
||||
// data itself -- this is NOT the runtime-indexed-part-select-on-the-
|
||||
// critical-path pattern weight_tile_gather.v's own header (EXP-0061)
|
||||
// warned about; that pattern is about a select signal arriving
|
||||
// LATE/simultaneously with the data it gates. Confirmed via a real
|
||||
// P&R re-check after this change (see the log), not just asserted.
|
||||
// valid. The eventual data-select mux (a real `case` on the registered
|
||||
// 2-bit `sel_lat`, not a runtime-indexed part-select expression --
|
||||
// deliberately written as explicit constant-offset case arms, see
|
||||
// below) therefore selects using an already-long-stable registered
|
||||
// value, never bits racing the read data itself -- this is NOT the
|
||||
// runtime-indexed-part-select-on-the-critical-path pattern weight_
|
||||
// tile_gather.v's own header (EXP-0061) warned about; that pattern is
|
||||
// about a select signal arriving LATE/simultaneously with the data it
|
||||
// gates. Same real discipline EXP-0081 already established for the
|
||||
// 1-bit case, now extended to 2 bits -- confirmed via a real P&R
|
||||
// re-check after this change (see the log), not just asserted.
|
||||
//
|
||||
// PROTOCOL: one request (`req` pulse + base_a/base_b/tcnt) triggers
|
||||
// TWO SEQUENTIAL burst reads (lane A then lane B) over the SAME
|
||||
@@ -74,14 +82,14 @@ module act_tile_fetch #(
|
||||
output reg ctrl_req,
|
||||
output reg ctrl_wr,
|
||||
output reg [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,
|
||||
output wire [32*BURST_LEN-1:0] ctrl_wdata,
|
||||
output wire [4*BURST_LEN-1:0] ctrl_wmask,
|
||||
input wire [32*BURST_LEN-1:0] ctrl_rdata,
|
||||
input wire ctrl_ready,
|
||||
input wire ctrl_busy
|
||||
);
|
||||
assign ctrl_wdata = {(16*BURST_LEN){1'b0}};
|
||||
assign ctrl_wmask = {(2*BURST_LEN){1'b0}}; // read-only engine, mask unused
|
||||
assign ctrl_wdata = {(32*BURST_LEN){1'b0}};
|
||||
assign ctrl_wmask = {(4*BURST_LEN){1'b0}}; // read-only engine, mask unused
|
||||
|
||||
localparam S_IDLE = 3'd0,
|
||||
S_MEMWAIT = 3'd1,
|
||||
@@ -92,12 +100,12 @@ module act_tile_fetch #(
|
||||
reg [2:0] state;
|
||||
reg [ADDR_WIDTH-1:0] base_a_lat, base_b_lat;
|
||||
reg [15:0] tcnt_lat;
|
||||
reg sel_lat; // registered at request time -- see header
|
||||
reg [1:0] sel_lat; // registered at request time -- see header
|
||||
|
||||
assign mem_active = (state != S_IDLE);
|
||||
|
||||
// burst index = tcnt/2 (integer division -- two tiles share one burst)
|
||||
wire [ADDR_WIDTH-1:0] tile_offset = {{(ADDR_WIDTH-15){1'b0}}, tcnt_lat[15:1]} * BURST_LEN[ADDR_WIDTH-1:0];
|
||||
// burst index = tcnt/4 (integer division -- four tiles share one burst)
|
||||
wire [ADDR_WIDTH-1:0] tile_offset = {{(ADDR_WIDTH-14){1'b0}}, tcnt_lat[15:2]} * BURST_LEN[ADDR_WIDTH-1:0];
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
@@ -105,7 +113,7 @@ module act_tile_fetch #(
|
||||
ctrl_req <= 1'b0; ctrl_wr <= 1'b0; ctrl_addr <= {ADDR_WIDTH{1'b0}};
|
||||
valid <= 1'b0; data_a <= {(DATA_WIDTH*P_IN){1'b0}}; data_b <= {(DATA_WIDTH*P_IN){1'b0}};
|
||||
base_a_lat <= {ADDR_WIDTH{1'b0}}; base_b_lat <= {ADDR_WIDTH{1'b0}}; tcnt_lat <= 16'd0;
|
||||
sel_lat <= 1'b0;
|
||||
sel_lat <= 2'd0;
|
||||
end else begin
|
||||
ctrl_req <= 1'b0;
|
||||
valid <= 1'b0;
|
||||
@@ -116,7 +124,7 @@ module act_tile_fetch #(
|
||||
base_a_lat <= base_a;
|
||||
base_b_lat <= base_b;
|
||||
tcnt_lat <= tcnt;
|
||||
sel_lat <= tcnt[0];
|
||||
sel_lat <= tcnt[1:0];
|
||||
state <= S_MEMWAIT;
|
||||
end
|
||||
end
|
||||
@@ -132,8 +140,16 @@ module act_tile_fetch #(
|
||||
|
||||
S_REQ_A: begin
|
||||
if (ctrl_ready) begin
|
||||
data_a <= sel_lat ? ctrl_rdata[DATA_WIDTH*P_IN +: DATA_WIDTH*P_IN]
|
||||
: ctrl_rdata[0 +: DATA_WIDTH*P_IN];
|
||||
// explicit constant-offset case arms, not a
|
||||
// runtime-indexed part-select expression -- see
|
||||
// header (EXP-0084, extends EXP-0081's same
|
||||
// discipline from 1 to 2 select bits).
|
||||
case (sel_lat)
|
||||
2'd0: data_a <= ctrl_rdata[0 +: DATA_WIDTH*P_IN];
|
||||
2'd1: data_a <= ctrl_rdata[64 +: DATA_WIDTH*P_IN];
|
||||
2'd2: data_a <= ctrl_rdata[128 +: DATA_WIDTH*P_IN];
|
||||
2'd3: data_a <= ctrl_rdata[192 +: DATA_WIDTH*P_IN];
|
||||
endcase
|
||||
ctrl_addr <= base_b_lat + tile_offset;
|
||||
ctrl_wr <= 1'b0;
|
||||
state <= S_GAP;
|
||||
@@ -156,8 +172,12 @@ module act_tile_fetch #(
|
||||
|
||||
S_REQ_B: begin
|
||||
if (ctrl_ready) begin
|
||||
data_b <= sel_lat ? ctrl_rdata[DATA_WIDTH*P_IN +: DATA_WIDTH*P_IN]
|
||||
: ctrl_rdata[0 +: DATA_WIDTH*P_IN];
|
||||
case (sel_lat)
|
||||
2'd0: data_b <= ctrl_rdata[0 +: DATA_WIDTH*P_IN];
|
||||
2'd1: data_b <= ctrl_rdata[64 +: DATA_WIDTH*P_IN];
|
||||
2'd2: data_b <= ctrl_rdata[128 +: DATA_WIDTH*P_IN];
|
||||
2'd3: data_b <= ctrl_rdata[192 +: DATA_WIDTH*P_IN];
|
||||
endcase
|
||||
valid <= 1'b1;
|
||||
state <= S_IDLE;
|
||||
end
|
||||
|
||||
@@ -88,9 +88,9 @@ module ddr_prefetch_mgr #(
|
||||
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,
|
||||
output wire [32*BURST_LEN-1:0] ctrl_wdata,
|
||||
output wire [4*BURST_LEN-1:0] ctrl_wmask,
|
||||
input wire [32*BURST_LEN-1:0] ctrl_rdata,
|
||||
input wire ctrl_ready,
|
||||
input wire ctrl_busy
|
||||
);
|
||||
|
||||
@@ -7,20 +7,30 @@
|
||||
// 16-bit-WORD req/wr/addr/wdata/lb_n/ub_n -> rdata/ready port (the
|
||||
// SAME shape as V2's real psram_controller.v / sdram_unified_
|
||||
// backend.v AR port), but V3's shared memory path (sdram_arbiter_n.v
|
||||
// -> mig_native_adapter.v) only understands BURST_LEN=8 (128-bit)
|
||||
// chunks. This module is the translator, matching sdram_unified_
|
||||
// -> mig_native_adapter.v) only understands BURST_LEN=8, 32-bit-word
|
||||
// (256-bit) chunks (EXP-0084's real DDR3 widening -- was 128-bit
|
||||
// before). This module is the translator, matching sdram_unified_
|
||||
// backend.v's own AR-port technique exactly (not reinvented): a
|
||||
// write masks out every word in the burst except the target one
|
||||
// (DQM-style byte masking, already how this project's whole memory
|
||||
// stack works); a read fetches the whole burst and extracts the
|
||||
// target word combinationally.
|
||||
// write masks out every byte in the burst except the target 16-bit
|
||||
// half-word (DQM-style byte masking, already how this project's whole
|
||||
// memory stack works); a read fetches the whole burst and extracts
|
||||
// the target half-word combinationally.
|
||||
//
|
||||
// Sits as one requester on sdram_arbiter_n.v (alongside N packed_
|
||||
// slot.v instances) -- `active` is asserted for the WHOLE single-word
|
||||
// transaction (word-granularity, no multi-burst sequencing needed),
|
||||
// so mem_grant only needs to be observed once before the one-shot
|
||||
// ctrl_req fires, same discipline as packed_slot.v's own S_MEMWAIT
|
||||
// (EXP-0066's real, hard-won lesson).
|
||||
// EXP-0084 ADDRESSING NOTE (real, deliberate design choice, not just a
|
||||
// mechanical width bump): the HOST-facing contract (mem_addr as a
|
||||
// 16-bit-word address, mem_wdata/mem_rdata as 16-bit values,
|
||||
// mem_lb_n/mem_ub_n as byte enables) is kept COMPLETELY UNCHANGED --
|
||||
// this module stays the shock absorber between the host's own fixed
|
||||
// 16-bit-word view (spi_host_bridge_v3.v's WRITE_MEM/READ_MEM opcode
|
||||
// payload size, and by extension the ESP32 firmware contract, is
|
||||
// NOT touched by the DDR3 widening) and the system's now-32-bit-word-
|
||||
// native shared ctrl bus. mem_addr's LSB now additionally selects
|
||||
// WHICH 16-bit half of the addressed 32-bit ctrl-bus word to target;
|
||||
// this halves the host's own reachable byte range for a given
|
||||
// ADDR_WIDTH (a real, honestly-disclosed, non-blocking limitation of
|
||||
// this debug/raw-access path only -- not the compute path, and not
|
||||
// currently a practical constraint at this project's real usage
|
||||
// scale).
|
||||
// ============================================================
|
||||
module host_mem_bridge #(
|
||||
parameter BURST_LEN = 8,
|
||||
@@ -31,10 +41,11 @@ module host_mem_bridge #(
|
||||
|
||||
// ---- host-facing port (matches spi_host_bridge.v's own
|
||||
// mem_req/mem_wr/mem_addr/mem_wdata/mem_lb_n/mem_ub_n ->
|
||||
// mem_rdata/mem_ready convention exactly) ----
|
||||
// mem_rdata/mem_ready convention exactly -- UNCHANGED by EXP-0084,
|
||||
// see header) ----
|
||||
input wire mem_req,
|
||||
input wire mem_wr,
|
||||
input wire [ADDR_WIDTH-1:0] mem_addr, // WORD address (not burst-aligned)
|
||||
input wire [ADDR_WIDTH-1:0] mem_addr, // 16-bit-word address (not burst-aligned)
|
||||
input wire [15:0] mem_wdata,
|
||||
input wire mem_lb_n,
|
||||
input wire mem_ub_n,
|
||||
@@ -49,13 +60,13 @@ module host_mem_bridge #(
|
||||
output reg req_req,
|
||||
output reg req_wr,
|
||||
output reg [ADDR_WIDTH-1:0] req_addr,
|
||||
output reg [16*BURST_LEN-1:0] req_wdata,
|
||||
output reg [2*BURST_LEN-1:0] req_wmask,
|
||||
input wire [16*BURST_LEN-1:0] req_rdata,
|
||||
output reg [32*BURST_LEN-1:0] req_wdata,
|
||||
output reg [4*BURST_LEN-1:0] req_wmask,
|
||||
input wire [32*BURST_LEN-1:0] req_rdata,
|
||||
input wire req_ready,
|
||||
input wire req_busy
|
||||
);
|
||||
localparam ALIGN_BITS = $clog2(BURST_LEN);
|
||||
localparam ALIGN_BITS = $clog2(BURST_LEN); // 3: which of the BURST_LEN 32-bit words in the burst
|
||||
|
||||
localparam S_IDLE = 2'd0,
|
||||
S_MEMWAIT = 2'd1,
|
||||
@@ -63,10 +74,16 @@ module host_mem_bridge #(
|
||||
S_DONE = 2'd3;
|
||||
|
||||
reg [1:0] state;
|
||||
reg [ALIGN_BITS-1:0] word_in_block;
|
||||
reg [ALIGN_BITS-1:0] word_in_block; // which 32-bit word within the burst
|
||||
reg half_sel; // which 16-bit half of that 32-bit word
|
||||
|
||||
assign req_active = (state == S_MEMWAIT) || (state == S_XFER);
|
||||
|
||||
// byte offset (0..4*BURST_LEN-2, even) of the target half-word
|
||||
// within the burst's own byte layout -- word_in_block*4 bytes/word
|
||||
// + half_sel*2 bytes/half.
|
||||
wire [ALIGN_BITS+1:0] byte_offset = {word_in_block, half_sel, 1'b0};
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= S_IDLE;
|
||||
@@ -79,18 +96,30 @@ module host_mem_bridge #(
|
||||
case (state)
|
||||
S_IDLE: begin
|
||||
if (mem_req) begin
|
||||
req_addr <= {mem_addr[ADDR_WIDTH-1:ALIGN_BITS], {ALIGN_BITS{1'b0}}};
|
||||
word_in_block <= mem_addr[ALIGN_BITS-1:0];
|
||||
// mem_addr[0] = which 16-bit half of the 32-bit
|
||||
// ctrl-bus word; mem_addr[ALIGN_BITS:1] = which
|
||||
// of the BURST_LEN 32-bit words in the burst;
|
||||
// the remaining upper bits, re-aligned to
|
||||
// 32-bit-word (ctrl bus) units, form the
|
||||
// burst-aligned req_addr.
|
||||
req_addr <= {1'b0, mem_addr[ADDR_WIDTH-1:ALIGN_BITS+1], {ALIGN_BITS{1'b0}}};
|
||||
word_in_block <= mem_addr[ALIGN_BITS:1];
|
||||
half_sel <= mem_addr[0];
|
||||
req_wr <= mem_wr;
|
||||
if (mem_wr) begin
|
||||
// replicate the target word across the whole
|
||||
// burst; only its own mask bits matter (see
|
||||
// header -- same DQM-style technique as
|
||||
// replicate the target half-word across the
|
||||
// whole burst; only its own mask bits matter
|
||||
// (see header -- same DQM-style technique as
|
||||
// sdram_unified_backend.v's own AR port).
|
||||
req_wdata <= {BURST_LEN{mem_wdata}};
|
||||
req_wmask <= {(2*BURST_LEN){1'b1}} &
|
||||
~(({{(2*BURST_LEN-2){1'b0}}, 2'b11}) << (mem_addr[ALIGN_BITS-1:0]*2)) |
|
||||
(({{(2*BURST_LEN-2){1'b0}}, mem_ub_n, mem_lb_n}) << (mem_addr[ALIGN_BITS-1:0]*2));
|
||||
// byte offset within the burst is computed
|
||||
// directly from the LIVE mem_addr here (not
|
||||
// from word_in_block/half_sel, which are
|
||||
// nonblocking-assigned THIS same cycle and
|
||||
// not yet valid until the next one).
|
||||
req_wdata <= {(4*BURST_LEN/2){mem_wdata}};
|
||||
req_wmask <= ({(4*BURST_LEN){1'b1}} &
|
||||
~(({{(4*BURST_LEN-2){1'b0}}, 2'b11}) << {mem_addr[ALIGN_BITS:0], 1'b0})) |
|
||||
(({{(4*BURST_LEN-2){1'b0}}, mem_ub_n, mem_lb_n}) << {mem_addr[ALIGN_BITS:0], 1'b0});
|
||||
end
|
||||
state <= S_MEMWAIT;
|
||||
end
|
||||
@@ -106,7 +135,7 @@ module host_mem_bridge #(
|
||||
S_XFER: begin
|
||||
if (req_ready) begin
|
||||
if (!req_wr)
|
||||
mem_rdata <= req_rdata[word_in_block*16 +: 16];
|
||||
mem_rdata <= req_rdata[byte_offset*8 +: 16];
|
||||
state <= S_DONE;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
// ============================================================
|
||||
// V3 -- adapter between this project's own established memory-
|
||||
// controller contract (req/wr/addr/wdata/wmask -> rdata/ready/busy,
|
||||
// BURST_LEN=8 16-bit words = 128 bits/transaction, the SAME shape
|
||||
// sdram_controller.v has presented everywhere in this project since
|
||||
// STEP16) and the REAL Xilinx MIG 7-series native "app" user
|
||||
// interface (PG063), generated for this project's actual DDR3 target
|
||||
// (mig_7series_0, XC7A100T, MT41J128M16JT-125:K, PHY:Controller
|
||||
// ratio 2:1).
|
||||
// BURST_LEN=8 32-bit words = 256 bits/transaction since EXP-0084's
|
||||
// real 32-bit DDR3 channel widening -- was 16-bit/128 bits before)
|
||||
// and the REAL Xilinx MIG 7-series native "app" user interface
|
||||
// (PG063), generated for this project's actual DDR3 target
|
||||
// (mig_7series_0, XC7A100T, 2x MT41J128M16JT-125:K ganged to 32-bit,
|
||||
// PHY:Controller ratio 2:1, 2900ps/344.827MHz, EXP-0084).
|
||||
//
|
||||
// Runs entirely in the ui_clk domain -- MIG's own generated clock is
|
||||
// this design's new system clock (the standard way MIG-based designs
|
||||
@@ -17,14 +17,20 @@
|
||||
// synchronized to ui_clk by the caller.
|
||||
//
|
||||
// ADDRESSING (real, derived from THIS project's actual generated MIG
|
||||
// config, not assumed): Data Width=16, Phy:Controller ratio 2:1 =>
|
||||
// nCK_PER_CLK=2 => app data width = 16*8/2 = 64 bits, matching the
|
||||
// real generated mig_7series_0.v port widths exactly (app_wdf_data
|
||||
// [63:0], app_rd_data[63:0]). One app_addr/app_cmd issuance moves a
|
||||
// FULL BURST_LEN=8 (128-bit) DDR3 burst, delivered as TWO 64-bit
|
||||
// beats on the app data bus -- so app_addr increments in the SAME
|
||||
// unit as this project's own existing ctrl_addr (one BURST_LEN=8
|
||||
// chunk per increment), no address scaling needed at this boundary.
|
||||
// config, not assumed, re-verified after EXP-0084's 32-bit
|
||||
// regeneration): Data Width=32, Phy:Controller ratio 2:1 =>
|
||||
// nCK_PER_CLK=2 => app data width = 32*8/2 = 128 bits, matching the
|
||||
// real regenerated mig_7series_0.v port widths exactly (app_wdf_data
|
||||
// [127:0], app_rd_data[127:0], app_wdf_mask[15:0] -- confirmed by
|
||||
// directly reading the real generated wrapper, not assumed from the
|
||||
// old 16-bit numbers). app_addr itself stayed 28 bits (confirmed same
|
||||
// real generated width as before the widening) -- one app_addr/
|
||||
// app_cmd issuance still moves a FULL BURST_LEN=8 chunk per increment,
|
||||
// same as before, just each chunk is now 256 bits (32-bit words) not
|
||||
// 128 bits (16-bit words), delivered as TWO 128-bit beats instead of
|
||||
// two 64-bit beats -- so app_addr increments in the SAME unit as this
|
||||
// project's own existing ctrl_addr, no address scaling needed at this
|
||||
// boundary, same as before.
|
||||
//
|
||||
// Sequencing is deliberately fully sequential, not pipelined
|
||||
// (correctness first): the command is issued and accepted (app_en/
|
||||
@@ -52,9 +58,9 @@ module mig_native_adapter #(
|
||||
input wire req,
|
||||
input wire wr,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
input wire [16*BURST_LEN-1:0] wdata,
|
||||
input wire [2*BURST_LEN-1:0] wmask,
|
||||
output reg [16*BURST_LEN-1:0] rdata,
|
||||
input wire [32*BURST_LEN-1:0] wdata,
|
||||
input wire [4*BURST_LEN-1:0] wmask,
|
||||
output reg [32*BURST_LEN-1:0] rdata,
|
||||
output reg ready,
|
||||
output wire busy,
|
||||
|
||||
@@ -64,15 +70,15 @@ module mig_native_adapter #(
|
||||
output reg app_en,
|
||||
input wire app_rdy,
|
||||
|
||||
output reg [63:0] app_wdf_data,
|
||||
output reg app_wdf_end,
|
||||
output reg [7:0] app_wdf_mask,
|
||||
output reg app_wdf_wren,
|
||||
input wire app_wdf_rdy,
|
||||
output reg [127:0] app_wdf_data,
|
||||
output reg app_wdf_end,
|
||||
output reg [15:0] app_wdf_mask,
|
||||
output reg app_wdf_wren,
|
||||
input wire app_wdf_rdy,
|
||||
|
||||
input wire [63:0] app_rd_data,
|
||||
input wire app_rd_data_end,
|
||||
input wire app_rd_data_valid
|
||||
input wire [127:0] app_rd_data,
|
||||
input wire app_rd_data_end,
|
||||
input wire app_rd_data_valid
|
||||
);
|
||||
localparam CMD_WRITE = 3'b000;
|
||||
localparam CMD_READ = 3'b001;
|
||||
@@ -86,8 +92,8 @@ module mig_native_adapter #(
|
||||
|
||||
reg [2:0] state;
|
||||
reg wr_lat;
|
||||
reg [16*BURST_LEN-1:0] wdata_lat;
|
||||
reg [2*BURST_LEN-1:0] wmask_lat;
|
||||
reg [32*BURST_LEN-1:0] wdata_lat;
|
||||
reg [4*BURST_LEN-1:0] wmask_lat;
|
||||
|
||||
assign busy = (state != S_IDLE);
|
||||
|
||||
@@ -98,11 +104,11 @@ module mig_native_adapter #(
|
||||
app_wdf_wren <= 1'b0;
|
||||
app_wdf_end <= 1'b0;
|
||||
ready <= 1'b0;
|
||||
rdata <= {(16*BURST_LEN){1'b0}};
|
||||
rdata <= {(32*BURST_LEN){1'b0}};
|
||||
app_addr <= 28'h0;
|
||||
app_cmd <= CMD_READ;
|
||||
app_wdf_data <= 64'h0;
|
||||
app_wdf_mask <= 8'h0;
|
||||
app_wdf_data <= 128'h0;
|
||||
app_wdf_mask <= 16'h0;
|
||||
end else begin
|
||||
ready <= 1'b0;
|
||||
|
||||
@@ -123,8 +129,8 @@ module mig_native_adapter #(
|
||||
if (app_rdy) begin
|
||||
app_en <= 1'b0;
|
||||
if (wr_lat) begin
|
||||
app_wdf_data <= wdata_lat[63:0];
|
||||
app_wdf_mask <= wmask_lat[7:0];
|
||||
app_wdf_data <= wdata_lat[127:0];
|
||||
app_wdf_mask <= wmask_lat[15:0];
|
||||
app_wdf_end <= 1'b0;
|
||||
app_wdf_wren <= 1'b1;
|
||||
state <= S_WDF0;
|
||||
@@ -136,8 +142,8 @@ module mig_native_adapter #(
|
||||
|
||||
S_WDF0: begin
|
||||
if (app_wdf_rdy) begin
|
||||
app_wdf_data <= wdata_lat[127:64];
|
||||
app_wdf_mask <= wmask_lat[15:8];
|
||||
app_wdf_data <= wdata_lat[255:128];
|
||||
app_wdf_mask <= wmask_lat[31:16];
|
||||
app_wdf_end <= 1'b1;
|
||||
app_wdf_wren <= 1'b1;
|
||||
state <= S_WDF1;
|
||||
@@ -155,10 +161,10 @@ module mig_native_adapter #(
|
||||
S_RD_WAIT: begin
|
||||
if (app_rd_data_valid) begin
|
||||
if (!app_rd_data_end) begin
|
||||
rdata[63:0] <= app_rd_data;
|
||||
rdata[127:0] <= app_rd_data;
|
||||
end else begin
|
||||
rdata[127:64] <= app_rd_data;
|
||||
state <= S_DONE;
|
||||
rdata[255:128] <= app_rd_data;
|
||||
state <= S_DONE;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,9 +14,16 @@
|
||||
// packed_slot instances, and the SPI bridge itself) runs in the
|
||||
// ui_clk domain, per this project's own standing convention
|
||||
// (mig_native_adapter.v's header) -- ui_clk is generated BY the MIG
|
||||
// from sys_clk_i, so this module only takes sys_clk_i/clk_ref_i/
|
||||
// from sys_clk_p/n, so this module only takes sys_clk_p/n/clk_ref_p/n/
|
||||
// sys_rst as clock/reset inputs, not a separate system clock.
|
||||
//
|
||||
// EXP-0084: sys_clk and clk_ref are now real DIFFERENTIAL pairs (the
|
||||
// user's own wizard choice, confirmed against the real regenerated
|
||||
// public mig_7series_0.v wrapper, which expects sys_clk_p/n and
|
||||
// clk_ref_p/n directly as raw top-level differential pins -- not a
|
||||
// single-ended sys_clk_i/clk_ref_i pair any more). Real board
|
||||
// implication: a differential oscillator, not a single-ended one.
|
||||
//
|
||||
// This is the first REAL (in-context, not out-of-context) P&R target
|
||||
// for V3: previous P&R runs (EXP-0059/63/67) were all out-of-context
|
||||
// synthesis of a sub-block, without the real MIG-generated pin/timing
|
||||
@@ -42,15 +49,25 @@ module n2_system_ddr3_top #(
|
||||
parameter N_SLOTS = 2,
|
||||
parameter QUEUE_DEPTH = 8
|
||||
)(
|
||||
// ---- MIG clock/reset ----
|
||||
input wire sys_clk_i,
|
||||
// ---- MIG clock/reset (EXP-0084: real differential pairs, both
|
||||
// the user's own wizard choice -- confirmed against the real
|
||||
// regenerated public mig_7series_0.v wrapper, which now expects
|
||||
// sys_clk_p/n and clk_ref_p/n directly as raw top-level
|
||||
// differential pins, not a single-ended sys_clk_i/clk_ref_i pair
|
||||
// any more) ----
|
||||
input wire sys_clk_p,
|
||||
input wire sys_clk_n,
|
||||
input wire sys_rst,
|
||||
input wire clk_ref_i,
|
||||
input wire clk_ref_p,
|
||||
input wire clk_ref_n,
|
||||
|
||||
// ---- real DDR3 pins (matches mig_7series_0.xdc's own port names) ----
|
||||
inout wire [15:0] ddr3_dq,
|
||||
inout wire [1:0] ddr3_dqs_n,
|
||||
inout wire [1:0] ddr3_dqs_p,
|
||||
// ---- real DDR3 pins (matches mig_7series_0.xdc's own port names;
|
||||
// dq/dqs/dm widths doubled since EXP-0084's real 32-bit widening --
|
||||
// two MT41J128M16 chips ganged in parallel, confirmed against the
|
||||
// real regenerated mig_7series_0.v wrapper, not assumed) ----
|
||||
inout wire [31:0] ddr3_dq,
|
||||
inout wire [3:0] ddr3_dqs_n,
|
||||
inout wire [3:0] ddr3_dqs_p,
|
||||
output wire [13:0] ddr3_addr,
|
||||
output wire [2:0] ddr3_ba,
|
||||
output wire ddr3_ras_n,
|
||||
@@ -61,7 +78,7 @@ module n2_system_ddr3_top #(
|
||||
output wire [0:0] ddr3_ck_n,
|
||||
output wire [0:0] ddr3_cke,
|
||||
output wire [0:0] ddr3_cs_n,
|
||||
output wire [1:0] ddr3_dm,
|
||||
output wire [3:0] ddr3_dm,
|
||||
output wire [0:0] ddr3_odt,
|
||||
|
||||
// ---- physical SPI host interface (-> spi_host_bridge_v3.v) ----
|
||||
@@ -95,15 +112,15 @@ module n2_system_ddr3_top #(
|
||||
output wire job_out_done,
|
||||
output wire [$clog2(N_SLOTS)-1:0] job_out_slot
|
||||
);
|
||||
wire [27:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en, app_rdy;
|
||||
wire [63:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [7:0] app_wdf_mask;
|
||||
wire app_wdf_wren, app_wdf_rdy;
|
||||
wire [63:0] app_rd_data;
|
||||
wire app_rd_data_end, app_rd_data_valid;
|
||||
wire [27:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en, app_rdy;
|
||||
wire [127:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [15:0] app_wdf_mask;
|
||||
wire app_wdf_wren, app_wdf_rdy;
|
||||
wire [127:0] app_rd_data;
|
||||
wire app_rd_data_end, app_rd_data_valid;
|
||||
wire ui_clk, ui_clk_sync_rst;
|
||||
|
||||
assign ui_clk_o = ui_clk;
|
||||
@@ -120,7 +137,7 @@ module n2_system_ddr3_top #(
|
||||
.ddr3_ck_p(ddr3_ck_p), .ddr3_ck_n(ddr3_ck_n),
|
||||
.ddr3_cke(ddr3_cke), .ddr3_cs_n(ddr3_cs_n),
|
||||
.ddr3_dm(ddr3_dm), .ddr3_odt(ddr3_odt),
|
||||
.sys_clk_i(sys_clk_i), .clk_ref_i(clk_ref_i),
|
||||
.sys_clk_p(sys_clk_p), .sys_clk_n(sys_clk_n), .clk_ref_p(clk_ref_p), .clk_ref_n(clk_ref_n),
|
||||
.app_addr(app_addr), .app_cmd(app_cmd), .app_en(app_en),
|
||||
.app_wdf_data(app_wdf_data), .app_wdf_end(app_wdf_end),
|
||||
.app_wdf_mask(app_wdf_mask), .app_wdf_wren(app_wdf_wren),
|
||||
@@ -136,9 +153,9 @@ module n2_system_ddr3_top #(
|
||||
|
||||
wire adp_req, adp_wr;
|
||||
wire [MEM_ADDR_WIDTH-1:0] adp_addr;
|
||||
wire [16*BURST_LEN-1:0] adp_wdata;
|
||||
wire [2*BURST_LEN-1:0] adp_wmask;
|
||||
wire [16*BURST_LEN-1:0] adp_rdata;
|
||||
wire [32*BURST_LEN-1:0] adp_wdata;
|
||||
wire [4*BURST_LEN-1:0] adp_wmask;
|
||||
wire [32*BURST_LEN-1:0] adp_rdata;
|
||||
wire adp_ready, adp_busy;
|
||||
|
||||
mig_native_adapter #(.BURST_LEN(BURST_LEN), .ADDR_WIDTH(MEM_ADDR_WIDTH)) u_adapter (
|
||||
@@ -156,9 +173,9 @@ module n2_system_ddr3_top #(
|
||||
wire [NUM_REQ-1:0] req_active, req_grant, req_req, req_wr;
|
||||
wire [NUM_REQ-1:0] req_ready, req_busy;
|
||||
wire [NUM_REQ*MEM_ADDR_WIDTH-1:0] req_addr;
|
||||
wire [NUM_REQ*16*BURST_LEN-1:0] req_wdata;
|
||||
wire [NUM_REQ*2*BURST_LEN-1:0] req_wmask;
|
||||
wire [NUM_REQ*16*BURST_LEN-1:0] req_rdata;
|
||||
wire [NUM_REQ*32*BURST_LEN-1:0] req_wdata;
|
||||
wire [NUM_REQ*4*BURST_LEN-1:0] req_wmask;
|
||||
wire [NUM_REQ*32*BURST_LEN-1:0] req_rdata;
|
||||
|
||||
sdram_arbiter_n #(
|
||||
.NUM_REQ(NUM_REQ), .ADDR_WIDTH(MEM_ADDR_WIDTH), .BURST_LEN(BURST_LEN)
|
||||
@@ -251,9 +268,9 @@ module n2_system_ddr3_top #(
|
||||
.req_active(req_active[2]), .req_grant(req_grant[2]),
|
||||
.req_req(req_req[2]), .req_wr(req_wr[2]),
|
||||
.req_addr(req_addr[2*MEM_ADDR_WIDTH +: MEM_ADDR_WIDTH]),
|
||||
.req_wdata(req_wdata[2*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.req_wmask(req_wmask[2*2*BURST_LEN +: 2*BURST_LEN]),
|
||||
.req_rdata(req_rdata[2*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.req_wdata(req_wdata[2*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.req_wmask(req_wmask[2*4*BURST_LEN +: 4*BURST_LEN]),
|
||||
.req_rdata(req_rdata[2*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.req_ready(req_ready[2]), .req_busy(req_busy[2])
|
||||
);
|
||||
|
||||
@@ -288,9 +305,9 @@ module n2_system_ddr3_top #(
|
||||
.mem_active(req_active[0]), .mem_grant(req_grant[0]),
|
||||
.ctrl_req(req_req[0]), .ctrl_wr(req_wr[0]),
|
||||
.ctrl_addr(req_addr[0*MEM_ADDR_WIDTH +: MEM_ADDR_WIDTH]),
|
||||
.ctrl_wdata(req_wdata[0*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wmask(req_wmask[0*2*BURST_LEN +: 2*BURST_LEN]),
|
||||
.ctrl_rdata(req_rdata[0*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wdata(req_wdata[0*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_wmask(req_wmask[0*4*BURST_LEN +: 4*BURST_LEN]),
|
||||
.ctrl_rdata(req_rdata[0*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_ready(req_ready[0]), .ctrl_busy(req_busy[0])
|
||||
);
|
||||
|
||||
@@ -314,9 +331,9 @@ module n2_system_ddr3_top #(
|
||||
.mem_active(req_active[1]), .mem_grant(req_grant[1]),
|
||||
.ctrl_req(req_req[1]), .ctrl_wr(req_wr[1]),
|
||||
.ctrl_addr(req_addr[1*MEM_ADDR_WIDTH +: MEM_ADDR_WIDTH]),
|
||||
.ctrl_wdata(req_wdata[1*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wmask(req_wmask[1*2*BURST_LEN +: 2*BURST_LEN]),
|
||||
.ctrl_rdata(req_rdata[1*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wdata(req_wdata[1*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_wmask(req_wmask[1*4*BURST_LEN +: 4*BURST_LEN]),
|
||||
.ctrl_rdata(req_rdata[1*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_ready(req_ready[1]), .ctrl_busy(req_busy[1])
|
||||
);
|
||||
endmodule
|
||||
|
||||
@@ -107,9 +107,9 @@ module packed_slot #(
|
||||
output wire ctrl_req,
|
||||
output wire ctrl_wr,
|
||||
output wire [ADDR_WIDTH-2: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,
|
||||
output wire [32*BURST_LEN-1:0] ctrl_wdata,
|
||||
output wire [4*BURST_LEN-1:0] ctrl_wmask,
|
||||
input wire [32*BURST_LEN-1:0] ctrl_rdata,
|
||||
input wire ctrl_ready,
|
||||
input wire ctrl_busy
|
||||
);
|
||||
@@ -140,8 +140,8 @@ module packed_slot #(
|
||||
|
||||
wire pf_ctrl_req, pf_ctrl_wr;
|
||||
wire [ADDR_WIDTH-2:0] pf_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] pf_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] pf_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] pf_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] pf_ctrl_wmask;
|
||||
|
||||
layer_prefetch_ctrl #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .LAYER_BYTES(LAYER_BYTES), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH-1)
|
||||
@@ -167,8 +167,8 @@ module packed_slot #(
|
||||
|
||||
wire act_ctrl_req, act_ctrl_wr;
|
||||
wire [ADDR_WIDTH-2:0] act_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] act_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] act_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] act_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] act_ctrl_wmask;
|
||||
|
||||
ddr_prefetch_mgr #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH-1)
|
||||
|
||||
@@ -44,18 +44,18 @@ module sdram_arbiter_n #(
|
||||
input wire [NUM_REQ-1:0] req_req,
|
||||
input wire [NUM_REQ-1:0] req_wr,
|
||||
input wire [NUM_REQ*ADDR_WIDTH-1:0] req_addr,
|
||||
input wire [NUM_REQ*16*BURST_LEN-1:0] req_wdata,
|
||||
input wire [NUM_REQ*2*BURST_LEN-1:0] req_wmask,
|
||||
output wire [NUM_REQ*16*BURST_LEN-1:0] req_rdata,
|
||||
input wire [NUM_REQ*32*BURST_LEN-1:0] req_wdata,
|
||||
input wire [NUM_REQ*4*BURST_LEN-1:0] req_wmask,
|
||||
output wire [NUM_REQ*32*BURST_LEN-1:0] req_rdata,
|
||||
output wire [NUM_REQ-1:0] req_ready,
|
||||
output wire [NUM_REQ-1:0] req_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,
|
||||
output wire [32*BURST_LEN-1:0] ctrl_wdata,
|
||||
output wire [4*BURST_LEN-1:0] ctrl_wmask,
|
||||
input wire [32*BURST_LEN-1:0] ctrl_rdata,
|
||||
input wire ctrl_ready,
|
||||
input wire ctrl_busy
|
||||
);
|
||||
@@ -107,22 +107,22 @@ module sdram_arbiter_n #(
|
||||
// mux request-side signals from the granted requester -> shared ctrl
|
||||
reg m_req, m_wr;
|
||||
reg [ADDR_WIDTH-1:0] m_addr;
|
||||
reg [16*BURST_LEN-1:0] m_wdata;
|
||||
reg [2*BURST_LEN-1:0] m_wmask;
|
||||
reg [32*BURST_LEN-1:0] m_wdata;
|
||||
reg [4*BURST_LEN-1:0] m_wmask;
|
||||
integer mi;
|
||||
always @(*) begin
|
||||
m_req = 1'b0;
|
||||
m_wr = 1'b0;
|
||||
m_addr = {ADDR_WIDTH{1'b0}};
|
||||
m_wdata = {(16*BURST_LEN){1'b0}};
|
||||
m_wmask = {(2*BURST_LEN){1'b0}};
|
||||
m_wdata = {(32*BURST_LEN){1'b0}};
|
||||
m_wmask = {(4*BURST_LEN){1'b0}};
|
||||
for (mi = 0; mi < NUM_REQ; mi = mi + 1) begin
|
||||
if (sel[mi]) begin
|
||||
m_req = req_req[mi];
|
||||
m_wr = req_wr[mi];
|
||||
m_addr = req_addr[mi*ADDR_WIDTH +: ADDR_WIDTH];
|
||||
m_wdata = req_wdata[mi*16*BURST_LEN +: 16*BURST_LEN];
|
||||
m_wmask = req_wmask[mi*2*BURST_LEN +: 2*BURST_LEN];
|
||||
m_wdata = req_wdata[mi*32*BURST_LEN +: 32*BURST_LEN];
|
||||
m_wmask = req_wmask[mi*4*BURST_LEN +: 4*BURST_LEN];
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -137,7 +137,7 @@ module sdram_arbiter_n #(
|
||||
genvar gd;
|
||||
generate
|
||||
for (gd = 0; gd < NUM_REQ; gd = gd + 1) begin : GEN_DEMUX
|
||||
assign req_rdata[gd*16*BURST_LEN +: 16*BURST_LEN] = ctrl_rdata;
|
||||
assign req_rdata[gd*32*BURST_LEN +: 32*BURST_LEN] = ctrl_rdata;
|
||||
assign req_ready[gd] = sel[gd] ? ctrl_ready : 1'b0;
|
||||
assign req_busy[gd] = sel[gd] ? ctrl_busy : 1'b1;
|
||||
end
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// ============================================================
|
||||
// EXP-0084 -- minimal, EXPLICITLY SYNTHETIC 32-bit-wide burst-memory
|
||||
// test model. NOT a real chip model (unlike sdram_controller.v/
|
||||
// sdram_model.v, which genuinely represent the real AS4C32M16SA x16
|
||||
// SDR part this project also uses) -- that real model is inherently
|
||||
// fixed at 16-bit words (a real hardware fact, not a parameter choice)
|
||||
// and is shared by 20+ other testbenches across v2 and v3, so it is
|
||||
// deliberately NOT modified here. This file exists purely so the
|
||||
// isolated, fast (iverilog) testbenches for modules that now speak
|
||||
// this project's real 32-bit ctrl bus convention (EXP-0084's DDR3
|
||||
// widening) have a same-shape, functionally-correct backend to run
|
||||
// against WITHOUT needing the full real MIG IP + ddr3_model.sv (real
|
||||
// xsim, much slower) for every isolated glue-logic check -- matching
|
||||
// this project's own established "verify new glue logic against a
|
||||
// fast backend first" precedent (tb_act_tile_fetch.v's own header),
|
||||
// just re-pointed at a backend that actually matches the current real
|
||||
// bus width. The REAL, trustworthy, board-accurate verification still
|
||||
// comes from tb_n2_system_ddr3.v against the real ddr3_model.sv, same
|
||||
// as always -- this model's own fixed latency is a plausible, but NOT
|
||||
// claimed-real, stand-in.
|
||||
//
|
||||
// Small DENSE backing store (2^MEM_ADDR_BITS entries), not a full
|
||||
// 2^ADDR_WIDTH array -- ADDR_WIDTH=25 would need ~1GB densely
|
||||
// allocated for no reason; every real test in this project only ever
|
||||
// touches small, low addresses. MEM_ADDR_BITS=20 (~1M entries, ~32MB
|
||||
// of simulation memory) comfortably covers any realistic test address
|
||||
// -- including tb_packed_slot.v's own ACT_MEM_BASE=0x10000 region,
|
||||
// which a first version of this model sized at 16 bits (65536
|
||||
// entries) silently WRAPPED to address 0, aliasing weight and
|
||||
// activation data and producing real, confusing wrong-answer failures
|
||||
// (found via real simulation, not by inspection -- see EXP-0084's
|
||||
// log for the full root-cause trace). Staying portable (Icarus's
|
||||
// associative-array support for a packed-vector key type turned out
|
||||
// not to work for this purpose -- found via a real elaboration
|
||||
// error, not assumed).
|
||||
// ============================================================
|
||||
module burst_mem_model32 #(
|
||||
parameter BURST_LEN = 8,
|
||||
parameter ADDR_WIDTH = 25,
|
||||
parameter MEM_ADDR_BITS = 20,
|
||||
parameter LATENCY = 6 // fixed req->ready cycles, a plausible stand-in, not claimed real
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire req,
|
||||
input wire wr,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
input wire [32*BURST_LEN-1:0] wdata,
|
||||
input wire [4*BURST_LEN-1:0] wmask,
|
||||
output reg [32*BURST_LEN-1:0] rdata,
|
||||
output reg ready,
|
||||
output wire busy
|
||||
);
|
||||
reg [32*BURST_LEN-1:0] mem [0:(1<<MEM_ADDR_BITS)-1];
|
||||
|
||||
localparam S_IDLE = 2'd0, S_BUSY = 2'd1, S_DONE = 2'd2;
|
||||
reg [1:0] state;
|
||||
reg [7:0] cnt;
|
||||
reg [ADDR_WIDTH-1:0] addr_lat;
|
||||
reg wr_lat;
|
||||
reg [32*BURST_LEN-1:0] wdata_lat;
|
||||
reg [4*BURST_LEN-1:0] wmask_lat;
|
||||
integer bi;
|
||||
|
||||
assign busy = (state != S_IDLE);
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= S_IDLE;
|
||||
ready <= 1'b0;
|
||||
cnt <= 8'd0;
|
||||
end else begin
|
||||
ready <= 1'b0;
|
||||
case (state)
|
||||
S_IDLE: begin
|
||||
if (req) begin
|
||||
addr_lat <= addr;
|
||||
wr_lat <= wr;
|
||||
wdata_lat <= wdata;
|
||||
wmask_lat <= wmask;
|
||||
cnt <= LATENCY[7:0];
|
||||
state <= S_BUSY;
|
||||
end
|
||||
end
|
||||
S_BUSY: begin
|
||||
if (cnt == 8'd1) state <= S_DONE;
|
||||
else cnt <= cnt - 8'd1;
|
||||
end
|
||||
S_DONE: begin
|
||||
if (wr_lat) begin
|
||||
// real DQM polarity (matches sdram_controller.v's
|
||||
// own convention): 0=write that byte, 1=masked.
|
||||
for (bi = 0; bi < 4*BURST_LEN; bi = bi + 1)
|
||||
if (!wmask_lat[bi])
|
||||
mem[addr_lat[MEM_ADDR_BITS-1:0]][bi*8 +: 8] <= wdata_lat[bi*8 +: 8];
|
||||
end else begin
|
||||
rdata <= mem[addr_lat[MEM_ADDR_BITS-1:0]];
|
||||
end
|
||||
ready <= 1'b1;
|
||||
state <= S_IDLE;
|
||||
end
|
||||
default: state <= S_IDLE;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -1,21 +1,23 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// ============================================================
|
||||
// Isolated correctness test for act_tile_fetch.v -- real SDR SDRAM
|
||||
// placeholder backend (same precedent as tb_host_mem_bridge.v/
|
||||
// tb_sdram_arbiter_n.v: verify new glue logic against the fast
|
||||
// backend first). Checks: (1) both lanes read back bit-exact from
|
||||
// their own burst-aligned tile slot; (2) different tile indices
|
||||
// correctly compute different burst addresses (tile_offset =
|
||||
// tcnt*BURST_LEN); (3) back-to-back requests (multiple tiles in a
|
||||
// row) all stay correct, exercising the S_GAP busy-wait logic.
|
||||
// Isolated correctness test for act_tile_fetch.v -- EXP-0084: re-run
|
||||
// against the new 32-bit-wide ctrl bus / 4-tiles-per-burst layout
|
||||
// (real 32-bit DDR3 channel widening). Backend switched from the real
|
||||
// AS4C32M16SA x16 SDR model (sdram_controller.v/sdram_model.v -- a
|
||||
// REAL chip, genuinely fixed at 16-bit, not reusable here) to
|
||||
// burst_mem_model32.v, an explicitly synthetic 32-bit test-only
|
||||
// burst memory built for exactly this purpose (see its own header).
|
||||
// Checks: (1) all four lanes of a burst read back bit-exact from
|
||||
// their own quarter-slot; (2) different tile indices correctly
|
||||
// compute different burst addresses (tile_offset = (tcnt>>2)*
|
||||
// BURST_LEN); (3) back-to-back requests (multiple tiles in a row,
|
||||
// including crossing a burst boundary) all stay correct, exercising
|
||||
// the S_GAP busy-wait logic.
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS; // 25
|
||||
localparam ADDR_WIDTH = 25;
|
||||
localparam CLK_FREQ_MHZ = 64;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
localparam DATA_WIDTH = 8;
|
||||
@@ -27,30 +29,16 @@ module tb;
|
||||
|
||||
wire ctrl_req, ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [2*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [4*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire ctrl_ready, ctrl_busy;
|
||||
wire cke, cs_n, ras_n, cas_n, we_n;
|
||||
wire [BANK_BITS-1:0] ba;
|
||||
wire [ROW_BITS-1:0] a;
|
||||
wire [15:0] dq;
|
||||
wire [1:0] dqm;
|
||||
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_ctrl (
|
||||
burst_mem_model32 #(
|
||||
.BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) u_mem (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask),
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy),
|
||||
.sdram_cke(cke), .sdram_cs_n(cs_n), .sdram_ras_n(ras_n), .sdram_cas_n(cas_n), .sdram_we_n(we_n),
|
||||
.sdram_ba(ba), .sdram_a(a), .sdram_dq(dq), .sdram_dqm(dqm)
|
||||
);
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_mem (
|
||||
.clk(clk), .cke(cke), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n),
|
||||
.ba(ba), .a(a), .dq(dq), .dqm(dqm)
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy)
|
||||
);
|
||||
|
||||
// single requester -> tie grant = active, same precedent as
|
||||
@@ -66,8 +54,8 @@ module tb;
|
||||
|
||||
wire dut_ctrl_req, dut_ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] dut_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] dut_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] dut_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] dut_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] dut_ctrl_wmask;
|
||||
|
||||
act_tile_fetch #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
@@ -81,27 +69,21 @@ module tb;
|
||||
.ctrl_rdata(ctrl_rdata), .ctrl_ready(ctrl_ready), .ctrl_busy(ctrl_busy)
|
||||
);
|
||||
|
||||
// ---- preload path: direct access to the SDRAM controller,
|
||||
// bypassing act_tile_fetch.v entirely, same "pre_active" mux
|
||||
// pattern as every other testbench in this project ----
|
||||
// ---- preload path: direct access to the memory model, bypassing
|
||||
// act_tile_fetch.v entirely, same "pre_active" mux pattern as
|
||||
// every other testbench in this project ----
|
||||
reg pre_active;
|
||||
reg pre_req, pre_wr;
|
||||
reg [ADDR_WIDTH-1:0] pre_addr;
|
||||
reg [16*BURST_LEN-1:0] pre_wdata;
|
||||
reg [32*BURST_LEN-1:0] pre_wdata;
|
||||
|
||||
// reroute: real DUT ctrl_* wires go through a mux so the testbench
|
||||
// can preload memory directly before act_tile_fetch.v ever runs.
|
||||
// (Re-declare the connection: DUT was wired directly above for
|
||||
// simplicity of the DUT instantiation; use force-free approach by
|
||||
// instead having the DUT's own ctrl_req/wr/addr/wdata feed the mux
|
||||
// inputs below and the mux feed the real controller.)
|
||||
assign ctrl_req = pre_active ? pre_req : dut_ctrl_req;
|
||||
assign ctrl_wr = pre_active ? pre_wr : dut_ctrl_wr;
|
||||
assign ctrl_addr = pre_active ? pre_addr : dut_ctrl_addr;
|
||||
assign ctrl_wdata = pre_active ? pre_wdata : dut_ctrl_wdata;
|
||||
assign ctrl_wmask = pre_active ? {(2*BURST_LEN){1'b0}} : dut_ctrl_wmask;
|
||||
assign ctrl_wmask = pre_active ? {(4*BURST_LEN){1'b0}} : dut_ctrl_wmask;
|
||||
|
||||
task automatic sdram_write_burst(input [ADDR_WIDTH-1:0] word_addr, input [16*BURST_LEN-1:0] data);
|
||||
task automatic mem_write_burst(input [ADDR_WIDTH-1:0] word_addr, input [32*BURST_LEN-1:0] data);
|
||||
begin
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
pre_req = 1'b1; pre_wr = 1'b1; pre_addr = word_addr; pre_wdata = data;
|
||||
@@ -137,7 +119,7 @@ module tb;
|
||||
|
||||
reg signed [DATA_WIDTH*P_IN-1:0] exp_a, exp_b;
|
||||
integer k, wi;
|
||||
reg [16*BURST_LEN-1:0] burst;
|
||||
reg [32*BURST_LEN-1:0] burst;
|
||||
|
||||
initial begin
|
||||
errors = 0; tests = 0;
|
||||
@@ -147,54 +129,62 @@ module tb;
|
||||
rst = 0;
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
|
||||
$display("=== preload 2 bursts/lane, 2 tiles packed per burst (EXP-0081 layout) ===");
|
||||
$display("=== preload 2 bursts/lane, 4 tiles packed per burst (EXP-0084 layout) ===");
|
||||
// lane A base = 0, lane B base = 100 (arbitrary, word-address units).
|
||||
// burst pair p holds tile 2p (low 64 bits) and tile 2p+1 (high 64 bits).
|
||||
for (wi = 0; wi < 2; wi = wi + 1) begin // wi = burst-pair index (0 -> tiles 0/1, 1 -> tiles 2/3)
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[k*16 +: 16] = {act_byte(0, 2*wi, 2*k+1), act_byte(0, 2*wi, 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[(P_IN/2+k)*16 +: 16] = {act_byte(0, 2*wi+1, 2*k+1), act_byte(0, 2*wi+1, 2*k)};
|
||||
sdram_write_burst(0 + wi*BURST_LEN, burst);
|
||||
// burst pair p holds tiles 4p, 4p+1, 4p+2, 4p+3 in quarters 0..3.
|
||||
for (wi = 0; wi < 2; wi = wi + 1) begin // wi = burst index (0 -> tiles 0-3, 1 -> tiles 4-7)
|
||||
for (k = 0; k < 4; k = k + 1)
|
||||
burst[k*64 +: 64] = {act_byte(0, 4*wi+k, 7), act_byte(0, 4*wi+k, 6), act_byte(0, 4*wi+k, 5), act_byte(0, 4*wi+k, 4),
|
||||
act_byte(0, 4*wi+k, 3), act_byte(0, 4*wi+k, 2), act_byte(0, 4*wi+k, 1), act_byte(0, 4*wi+k, 0)};
|
||||
mem_write_burst(0 + wi*BURST_LEN, burst);
|
||||
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[k*16 +: 16] = {act_byte(100, 2*wi, 2*k+1), act_byte(100, 2*wi, 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[(P_IN/2+k)*16 +: 16] = {act_byte(100, 2*wi+1, 2*k+1), act_byte(100, 2*wi+1, 2*k)};
|
||||
sdram_write_burst(100 + wi*BURST_LEN, burst);
|
||||
for (k = 0; k < 4; k = k + 1)
|
||||
burst[k*64 +: 64] = {act_byte(100, 4*wi+k, 7), act_byte(100, 4*wi+k, 6), act_byte(100, 4*wi+k, 5), act_byte(100, 4*wi+k, 4),
|
||||
act_byte(100, 4*wi+k, 3), act_byte(100, 4*wi+k, 2), act_byte(100, 4*wi+k, 1), act_byte(100, 4*wi+k, 0)};
|
||||
mem_write_burst(100 + wi*BURST_LEN, burst);
|
||||
end
|
||||
@(posedge clk);
|
||||
pre_active = 1'b0;
|
||||
|
||||
$display("=== TEST 1: fetch tile 0 (even -> low half), both lanes ===");
|
||||
$display("=== TEST 1: fetch tile 0 (quarter 0), both lanes ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd0);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 0, k);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_b[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(100, 0, k);
|
||||
check(data_a === exp_a, "T1: lane A tile 0 bit-exact");
|
||||
check(data_b === exp_b, "T1: lane B tile 0 bit-exact");
|
||||
|
||||
$display("=== TEST 2: fetch tile 1 (odd -> high half, SAME burst address as tile 0) ===");
|
||||
$display("=== TEST 2: fetch tile 1 (quarter 1, SAME burst address as tile 0) ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd1);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 1, k);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_b[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(100, 1, k);
|
||||
check(data_a === exp_a, "T2: lane A tile 1 bit-exact");
|
||||
check(data_b === exp_b, "T2: lane B tile 1 bit-exact");
|
||||
|
||||
$display("=== TEST 3: fetch tile 2 (even -> low half, NEW burst address) ===");
|
||||
$display("=== TEST 2b: fetch tile 2 (quarter 2, SAME burst) ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd2);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 2, k);
|
||||
check(data_a === exp_a, "T3: lane A tile 2 bit-exact (new burst)");
|
||||
check(data_a === exp_a, "T2b: lane A tile 2 bit-exact");
|
||||
|
||||
$display("=== TEST 4: back-to-back fetches, alternating even/odd tiles ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd0);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 0, k);
|
||||
check(data_a === exp_a, "T4a: back-to-back fetch 1 (tile 0, even), lane A correct");
|
||||
do_fetch(25'd0, 25'd100, 16'd1);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 1, k);
|
||||
check(data_a === exp_a, "T4b: back-to-back fetch 2 (tile 1, odd), lane A correct");
|
||||
$display("=== TEST 2c: fetch tile 3 (quarter 3, SAME burst) ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd3);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 3, k);
|
||||
check(data_a === exp_a, "T4c: back-to-back fetch 3 (tile 3, odd, new burst), lane A correct");
|
||||
check(data_a === exp_a, "T2c: lane A tile 3 bit-exact");
|
||||
|
||||
$display("=== TEST 3: fetch tile 4 (quarter 0, NEW burst address) ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd4);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 4, k);
|
||||
check(data_a === exp_a, "T3: lane A tile 4 bit-exact (new burst)");
|
||||
|
||||
$display("=== TEST 4: back-to-back fetches, cycling through all 4 quarters ===");
|
||||
do_fetch(25'd0, 25'd100, 16'd0);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 0, k);
|
||||
check(data_a === exp_a, "T4a: back-to-back fetch 1 (tile 0, quarter 0), lane A correct");
|
||||
do_fetch(25'd0, 25'd100, 16'd2);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 2, k);
|
||||
check(data_a === exp_a, "T4b: back-to-back fetch 2 (tile 2, quarter 2), lane A correct");
|
||||
do_fetch(25'd0, 25'd100, 16'd7);
|
||||
for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 7, k);
|
||||
check(data_a === exp_a, "T4c: back-to-back fetch 3 (tile 7, quarter 3, new burst), lane A correct");
|
||||
|
||||
$display("=== %0d/%0d tests, %0d errors ===", tests-errors, tests, errors);
|
||||
if (errors == 0) $display("ALL TESTS PASSED (tb_act_tile_fetch)");
|
||||
|
||||
@@ -1,40 +1,33 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// ============================================================
|
||||
// EXP-0083: (1) isolated correctness test for ddr_prefetch_mgr.v --
|
||||
// same real SDR SDRAM placeholder backend precedent as
|
||||
// tb_act_tile_fetch.v -- and (2) a real, measured, honest A/B cycle-
|
||||
// count comparison against the OLD per-tile req/wait/consume loop
|
||||
// packed_slot.v used before EXP-0083, to get a REAL number for the
|
||||
// look-ahead prefetch's benefit instead of asserting one.
|
||||
// EXP-0084: re-run of the EXP-0083 ddr_prefetch_mgr.v isolated
|
||||
// correctness + A/B cycle-count test against the new 32-bit ctrl bus
|
||||
// (real DDR3 channel widening). Backend switched to burst_mem_
|
||||
// model32.v (see its own header -- explicitly synthetic, fixed
|
||||
// latency, not claiming real DDR3 row/bank AC timing) since the real
|
||||
// AS4C32M16SA x16 SDR model this test used before is genuinely fixed
|
||||
// at 16-bit and can't represent the new bus width.
|
||||
//
|
||||
// Both the "baseline" (direct act_tile_fetch.v, one requester per
|
||||
// tile, old packed_slot.v sequencing) and the "prefetch" (ddr_
|
||||
// prefetch_mgr.v, EXP-0083) loops are run against the SAME shared
|
||||
// backend and the SAME preloaded data, back to back, muxed the same
|
||||
// way tb_act_tile_fetch.v's own pre_active mux works -- so the
|
||||
// comparison is apples to apples, not two different simulated
|
||||
// environments.
|
||||
//
|
||||
// Both loops apply the SAME 2-cycle "simulated compute overhead" per
|
||||
// tile (matching packed_slot.v's own real S_TILEREQ + S_OPERAND
|
||||
// single-cycle costs) between a tile becoming available and the next
|
||||
// step being taken -- the honest question this answers is: does
|
||||
// removing the OLD design's serialization of that overhead with the
|
||||
// NEXT tile's DDR3 fetch produce a real, measurable improvement, and
|
||||
// how much.
|
||||
// EXP-0083's own PART 3 (same-row vs row-switch-heavy A/B, exploiting
|
||||
// the old SDR model's own real row/bank timing distinction) is
|
||||
// DROPPED here -- burst_mem_model32.v has a single fixed latency
|
||||
// regardless of address pattern, so that comparison would no longer
|
||||
// carry real meaning on this backend. The trustworthy, real number
|
||||
// for that effect already comes from EXP-0083's own real-DDR3-backend
|
||||
// measurement (tb_n2_system_ddr3.v, 2.86% real reduction) -- this
|
||||
// isolated test's own job is correctness + a basic sanity check that
|
||||
// prefetch is still not slower than the old per-tile loop, not to
|
||||
// re-derive that headline number.
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS; // 25
|
||||
localparam ADDR_WIDTH = 25;
|
||||
localparam CLK_FREQ_MHZ = 64;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
localparam DATA_WIDTH = 8;
|
||||
localparam P_IN = 8;
|
||||
localparam N_TILES = 6; // 3 burst-pairs/lane -- enough to see steady-state behavior
|
||||
localparam N_TILES = 8; // 2 bursts/lane at 4 tiles/burst -- enough to see steady-state behavior
|
||||
|
||||
reg clk = 0;
|
||||
always #(CLK_PERIOD_NS/2.0) clk = ~clk;
|
||||
@@ -42,30 +35,16 @@ module tb;
|
||||
|
||||
wire ctrl_req, ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [2*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [4*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire ctrl_ready, ctrl_busy;
|
||||
wire cke, cs_n, ras_n, cas_n, we_n;
|
||||
wire [BANK_BITS-1:0] ba;
|
||||
wire [ROW_BITS-1:0] a;
|
||||
wire [15:0] dq;
|
||||
wire [1:0] dqm;
|
||||
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_ctrl (
|
||||
burst_mem_model32 #(
|
||||
.BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) u_mem (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask),
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy),
|
||||
.sdram_cke(cke), .sdram_cs_n(cs_n), .sdram_ras_n(ras_n), .sdram_cas_n(cas_n), .sdram_we_n(we_n),
|
||||
.sdram_ba(ba), .sdram_a(a), .sdram_dq(dq), .sdram_dqm(dqm)
|
||||
);
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_mem (
|
||||
.clk(clk), .cke(cke), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n),
|
||||
.ba(ba), .a(a), .dq(dq), .dqm(dqm)
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy)
|
||||
);
|
||||
|
||||
// ---- 3-way mux on the shared backend: preload / baseline DUT / prefetch DUT ----
|
||||
@@ -74,7 +53,7 @@ module tb;
|
||||
|
||||
reg pre_req, pre_wr;
|
||||
reg [ADDR_WIDTH-1:0] pre_addr;
|
||||
reg [16*BURST_LEN-1:0] pre_wdata;
|
||||
reg [32*BURST_LEN-1:0] pre_wdata;
|
||||
|
||||
// ---- baseline DUT: plain act_tile_fetch.v, driven by a per-tile
|
||||
// req/wait/consume loop replicating OLD packed_slot.v sequencing ----
|
||||
@@ -86,8 +65,8 @@ module tb;
|
||||
wire base_mem_active;
|
||||
wire base_ctrl_req, base_ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] base_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] base_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] base_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] base_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] base_ctrl_wmask;
|
||||
|
||||
act_tile_fetch #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
@@ -111,8 +90,8 @@ module tb;
|
||||
wire pf_mem_active;
|
||||
wire pf_ctrl_req, pf_ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] pf_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] pf_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] pf_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] pf_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] pf_ctrl_wmask;
|
||||
|
||||
ddr_prefetch_mgr #(
|
||||
.DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
@@ -130,9 +109,9 @@ module tb;
|
||||
assign ctrl_wr = (sel==SEL_PRELOAD) ? pre_wr : (sel==SEL_BASE) ? base_ctrl_wr : pf_ctrl_wr;
|
||||
assign ctrl_addr = (sel==SEL_PRELOAD) ? pre_addr : (sel==SEL_BASE) ? base_ctrl_addr : pf_ctrl_addr;
|
||||
assign ctrl_wdata = (sel==SEL_PRELOAD) ? pre_wdata : (sel==SEL_BASE) ? base_ctrl_wdata : pf_ctrl_wdata;
|
||||
assign ctrl_wmask = (sel==SEL_BASE) ? base_ctrl_wmask : (sel==SEL_PF) ? pf_ctrl_wmask : {(2*BURST_LEN){1'b0}};
|
||||
assign ctrl_wmask = (sel==SEL_BASE) ? base_ctrl_wmask : (sel==SEL_PF) ? pf_ctrl_wmask : {(4*BURST_LEN){1'b0}};
|
||||
|
||||
task automatic sdram_write_burst(input [ADDR_WIDTH-1:0] word_addr, input [16*BURST_LEN-1:0] data);
|
||||
task automatic mem_write_burst(input [ADDR_WIDTH-1:0] word_addr, input [32*BURST_LEN-1:0] data);
|
||||
begin
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
pre_req = 1'b1; pre_wr = 1'b1; pre_addr = word_addr; pre_wdata = data;
|
||||
@@ -155,8 +134,8 @@ module tb;
|
||||
endtask
|
||||
|
||||
reg signed [DATA_WIDTH*P_IN-1:0] exp_a, exp_b;
|
||||
integer k, wi, cyc;
|
||||
reg [16*BURST_LEN-1:0] burst;
|
||||
integer k, wi, qi;
|
||||
reg [32*BURST_LEN-1:0] burst;
|
||||
integer t_start, t_end, base_cycles, pf_cycles;
|
||||
|
||||
initial begin
|
||||
@@ -169,24 +148,22 @@ module tb;
|
||||
rst = 0;
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
|
||||
$display("=== preload lane A base=0, lane B base=200, %0d tiles (EXP-0081 2-tiles/burst layout) ===", N_TILES);
|
||||
for (wi = 0; wi < N_TILES/2; wi = wi + 1) begin
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[k*16 +: 16] = {act_byte(0, 2*wi, 2*k+1), act_byte(0, 2*wi, 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[(P_IN/2+k)*16 +: 16] = {act_byte(0, 2*wi+1, 2*k+1), act_byte(0, 2*wi+1, 2*k)};
|
||||
sdram_write_burst(0 + wi*BURST_LEN, burst);
|
||||
$display("=== preload lane A base=0, lane B base=200, %0d tiles (EXP-0084 4-tiles/burst layout) ===", N_TILES);
|
||||
for (wi = 0; wi < N_TILES/4; wi = wi + 1) begin
|
||||
for (qi = 0; qi < 4; qi = qi + 1)
|
||||
burst[qi*64 +: 64] = {act_byte(0, 4*wi+qi, 7), act_byte(0, 4*wi+qi, 6), act_byte(0, 4*wi+qi, 5), act_byte(0, 4*wi+qi, 4),
|
||||
act_byte(0, 4*wi+qi, 3), act_byte(0, 4*wi+qi, 2), act_byte(0, 4*wi+qi, 1), act_byte(0, 4*wi+qi, 0)};
|
||||
mem_write_burst(0 + wi*BURST_LEN, burst);
|
||||
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[k*16 +: 16] = {act_byte(200, 2*wi, 2*k+1), act_byte(200, 2*wi, 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst[(P_IN/2+k)*16 +: 16] = {act_byte(200, 2*wi+1, 2*k+1), act_byte(200, 2*wi+1, 2*k)};
|
||||
sdram_write_burst(200 + wi*BURST_LEN, burst);
|
||||
for (qi = 0; qi < 4; qi = qi + 1)
|
||||
burst[qi*64 +: 64] = {act_byte(200, 4*wi+qi, 7), act_byte(200, 4*wi+qi, 6), act_byte(200, 4*wi+qi, 5), act_byte(200, 4*wi+qi, 4),
|
||||
act_byte(200, 4*wi+qi, 3), act_byte(200, 4*wi+qi, 2), act_byte(200, 4*wi+qi, 1), act_byte(200, 4*wi+qi, 0)};
|
||||
mem_write_burst(200 + wi*BURST_LEN, burst);
|
||||
end
|
||||
@(posedge clk);
|
||||
|
||||
// ============================================================
|
||||
// PART 1: baseline correctness + real cycle count, OLD-style
|
||||
// PART 1: baseline correctness + cycle count, OLD-style
|
||||
// per-tile req -> wait valid -> 2-cycle simulated compute -> next req
|
||||
// ============================================================
|
||||
$display("=== PART 1: baseline (direct act_tile_fetch.v, old packed_slot.v loop) ===");
|
||||
@@ -213,10 +190,10 @@ module tb;
|
||||
$display("baseline: %0d tiles in %0d cycles (%0.2f cycles/tile)", N_TILES, base_cycles, base_cycles*1.0/N_TILES);
|
||||
|
||||
// ============================================================
|
||||
// PART 2: prefetch correctness + real cycle count, EXP-0083
|
||||
// job-level start, poll tile_valid, 2-cycle simulated compute, consume
|
||||
// PART 2: prefetch correctness + cycle count, EXP-0083 job-level
|
||||
// start, poll tile_valid, 2-cycle simulated compute, consume
|
||||
// ============================================================
|
||||
$display("=== PART 2: prefetch (ddr_prefetch_mgr.v, EXP-0083) ===");
|
||||
$display("=== PART 2: prefetch (ddr_prefetch_mgr.v, EXP-0083/0084) ===");
|
||||
sel = SEL_PF;
|
||||
@(posedge clk);
|
||||
pf_base_a <= 0; pf_base_b <= 200; pf_n_tiles <= N_TILES[15:0];
|
||||
@@ -230,11 +207,7 @@ module tb;
|
||||
// pulse being sampled by the DUT (both triggered off the same
|
||||
// @(posedge clk)), reading pf_tile_valid/bank_valid BEFORE the
|
||||
// DUT's own nonblocking update for that consume has been
|
||||
// applied -- a real testbench race, not an RTL bug (found via
|
||||
// an iteration-tagged trace: k=1 was reading k=0's still-
|
||||
// unconsumed bank). #1 (real time, 1ns << the 15.625ns clock
|
||||
// period) forces this poll to always run strictly after that
|
||||
// update has settled.
|
||||
// applied -- a real testbench race, not an RTL bug (EXP-0083).
|
||||
#1;
|
||||
while (!pf_tile_valid) @(posedge clk);
|
||||
for (wi = 0; wi < P_IN; wi = wi + 1) begin
|
||||
@@ -252,56 +225,9 @@ module tb;
|
||||
pf_cycles = (t_end - t_start) / CLK_PERIOD_NS;
|
||||
$display("prefetch: %0d tiles in %0d cycles (%0.2f cycles/tile)", N_TILES, pf_cycles, pf_cycles*1.0/N_TILES);
|
||||
|
||||
$display("=== REAL MEASURED COMPARISON (row-switch-heavy, 3 different burst pairs): baseline=%0d cycles, prefetch=%0d cycles, reduction=%0.1f%% ===",
|
||||
base_cycles, pf_cycles, 100.0*(base_cycles-pf_cycles)/base_cycles);
|
||||
check(pf_cycles < base_cycles, "prefetch is real, measurably faster than baseline (row-switch-heavy)");
|
||||
|
||||
// ============================================================
|
||||
// PART 3 (EXP-0083 addendum): best-case, SAME-ROW comparison --
|
||||
// only tiles 0/1 (both already resident in the FIRST preloaded
|
||||
// burst pair, no row activation needed for either), isolating
|
||||
// the look-ahead benefit from row-switch cost entirely. Answers
|
||||
// honestly whether the small PART-1/2 result is because the
|
||||
// benefit is inherently small, or because row-switch cost
|
||||
// dominates and masks it in that scenario.
|
||||
// ============================================================
|
||||
$display("=== PART 3: same-row best case (2 tiles, single burst pair, no row switch) ===");
|
||||
sel = SEL_BASE;
|
||||
@(posedge clk);
|
||||
t_start = $time;
|
||||
for (k = 0; k < 2; k = k + 1) begin
|
||||
@(posedge clk);
|
||||
base_base_a <= 0; base_base_b <= 200; base_tcnt <= k[15:0];
|
||||
base_req <= 1'b1;
|
||||
@(posedge clk);
|
||||
base_req <= 1'b0;
|
||||
while (!base_valid) @(posedge clk);
|
||||
repeat(2) @(posedge clk);
|
||||
end
|
||||
t_end = $time;
|
||||
base_cycles = (t_end - t_start) / CLK_PERIOD_NS;
|
||||
$display("same-row baseline: 2 tiles in %0d cycles", base_cycles);
|
||||
|
||||
sel = SEL_PF;
|
||||
@(posedge clk);
|
||||
pf_base_a <= 0; pf_base_b <= 200; pf_n_tiles <= 16'd2;
|
||||
pf_job_start <= 1'b1;
|
||||
t_start = $time;
|
||||
@(posedge clk);
|
||||
pf_job_start <= 1'b0;
|
||||
for (k = 0; k < 2; k = k + 1) begin
|
||||
#1;
|
||||
while (!pf_tile_valid) @(posedge clk);
|
||||
repeat(2) @(posedge clk);
|
||||
pf_tile_consume <= 1'b1;
|
||||
@(posedge clk);
|
||||
pf_tile_consume <= 1'b0;
|
||||
end
|
||||
t_end = $time;
|
||||
pf_cycles = (t_end - t_start) / CLK_PERIOD_NS;
|
||||
$display("same-row prefetch: 2 tiles in %0d cycles", pf_cycles);
|
||||
$display("=== REAL MEASURED COMPARISON (same-row, best case): baseline=%0d cycles, prefetch=%0d cycles, reduction=%0.1f%% ===",
|
||||
$display("=== cycle comparison on this fixed-latency synthetic backend: baseline=%0d cycles, prefetch=%0d cycles, reduction=%0.1f%% (sanity check only -- the real, trustworthy number is EXP-0083's real-DDR3-backend measurement, 2.86%%) ===",
|
||||
base_cycles, pf_cycles, 100.0*(base_cycles-pf_cycles)/base_cycles);
|
||||
check(pf_cycles <= base_cycles, "prefetch is not slower than baseline (sanity check)");
|
||||
|
||||
$display("=== %0d/%0d tests, %0d errors ===", tests-errors, tests, errors);
|
||||
if (errors == 0) $display("ALL TESTS PASSED (tb_ddr_prefetch_mgr)");
|
||||
|
||||
@@ -4,26 +4,31 @@
|
||||
// Isolated correctness test for host_mem_bridge.v: the word<->burst
|
||||
// translator that closes the "no host raw-memory-access path" gap
|
||||
// found re-auditing spi_host_bridge.v against V3 (EXP-0068's audit).
|
||||
// Uses the cheap SDR SDRAM placeholder backend (sdram_controller.v +
|
||||
// sdram_model.v), same precedent as tb_sdram_arbiter_n.v: verify new
|
||||
// glue logic against the fast backend first, real DDR3 integration
|
||||
// is a separate, later step once this is trusted standalone.
|
||||
//
|
||||
// Checks: (a) single-word write only touches its OWN word inside the
|
||||
// burst (byte masking correctness, lb_n/ub_n both individually and
|
||||
// together) without corrupting neighboring words in the same burst;
|
||||
// (b) single-word read extracts the correct word regardless of its
|
||||
// offset within the burst (all BURST_LEN=8 offsets exercised);
|
||||
// (c) mem_ready pulses exactly once per transaction.
|
||||
// EXP-0084: re-run against the new 32-bit ctrl bus / burst_mem_
|
||||
// model32.v backend (real DDR3 channel widening -- was 16-bit-word-
|
||||
// native before, now 32-bit-word-native, see host_mem_bridge.v's own
|
||||
// header for the real addressing redesign this required: mem_addr
|
||||
// stays a 16-bit-word address for the host's own unchanged protocol,
|
||||
// but now mem_addr[0] additionally selects which half of the
|
||||
// underlying 32-bit ctrl-bus word to target). One burst now spans
|
||||
// 2*BURST_LEN=16 host-side 16-bit-word offsets (was BURST_LEN=8) --
|
||||
// this test now exercises all 16, not just 8, to cover the new
|
||||
// half-select logic across the WHOLE burst, not half of it.
|
||||
//
|
||||
// Checks: (a) single-word write only touches its OWN half-word inside
|
||||
// the burst (byte masking correctness, lb_n/ub_n both individually and
|
||||
// together) without corrupting neighboring half-words in the same
|
||||
// burst; (b) single-word read extracts the correct half-word
|
||||
// regardless of its offset within the burst (all 16 offsets
|
||||
// exercised); (c) mem_ready pulses exactly once per transaction.
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS;
|
||||
localparam ADDR_WIDTH = 25;
|
||||
localparam CLK_FREQ_MHZ = 64;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
localparam BURST_SPAN = 2*BURST_LEN; // 16-bit-word positions per burst, host-side addressing
|
||||
|
||||
reg clk = 0;
|
||||
always #(CLK_PERIOD_NS/2.0) clk = ~clk;
|
||||
@@ -31,30 +36,16 @@ module tb;
|
||||
|
||||
wire ctrl_req, ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [2*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [4*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire ctrl_ready, ctrl_busy;
|
||||
wire cke, cs_n, ras_n, cas_n, we_n;
|
||||
wire [BANK_BITS-1:0] ba;
|
||||
wire [ROW_BITS-1:0] a;
|
||||
wire [15:0] dq;
|
||||
wire [1:0] dqm;
|
||||
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_ctrl (
|
||||
burst_mem_model32 #(
|
||||
.BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) u_mem (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask),
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy),
|
||||
.sdram_cke(cke), .sdram_cs_n(cs_n), .sdram_ras_n(ras_n), .sdram_cas_n(cas_n), .sdram_we_n(we_n),
|
||||
.sdram_ba(ba), .sdram_a(a), .sdram_dq(dq), .sdram_dqm(dqm)
|
||||
);
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_mem (
|
||||
.clk(clk), .cke(cke), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n),
|
||||
.ba(ba), .a(a), .dq(dq), .dqm(dqm)
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy)
|
||||
);
|
||||
|
||||
// single requester -> arbiter isn't even needed for an isolated
|
||||
@@ -112,7 +103,7 @@ module tb;
|
||||
|
||||
reg [15:0] got;
|
||||
integer i;
|
||||
localparam [ADDR_WIDTH-1:0] BASE = 25'd200; // burst-aligned base (200 % 8 == 0)
|
||||
localparam [ADDR_WIDTH-1:0] BASE = 25'd256; // burst-aligned base (256 % 16 == 0)
|
||||
|
||||
initial begin
|
||||
errors = 0; tests = 0;
|
||||
@@ -121,11 +112,11 @@ module tb;
|
||||
rst = 0;
|
||||
@(posedge clk);
|
||||
|
||||
$display("=== TEST 1: write+read every word offset within one burst, verify no cross-word corruption ===");
|
||||
for (i = 0; i < BURST_LEN; i = i + 1) begin
|
||||
$display("=== TEST 1: write+read every half-word offset within one burst (0..%0d), verify no cross-word corruption ===", BURST_SPAN-1);
|
||||
for (i = 0; i < BURST_SPAN; i = i + 1) begin
|
||||
host_write(BASE + i[ADDR_WIDTH-1:0], 16'hA000 + i[15:0], 1'b0, 1'b0);
|
||||
end
|
||||
for (i = 0; i < BURST_LEN; i = i + 1) begin
|
||||
for (i = 0; i < BURST_SPAN; i = i + 1) begin
|
||||
host_read(BASE + i[ADDR_WIDTH-1:0], got);
|
||||
tests = tests + 1;
|
||||
if (got !== (16'hA000 + i[15:0])) begin
|
||||
@@ -136,9 +127,9 @@ module tb;
|
||||
end
|
||||
end
|
||||
|
||||
$display("=== TEST 2: re-write word 3 only, confirm neighbors (0,1,2,4..7) untouched ===");
|
||||
$display("=== TEST 2: re-write offset 3 only, confirm neighbors (0..%0d except 3) untouched ===", BURST_SPAN-1);
|
||||
host_write(BASE + 25'd3, 16'hBEEF, 1'b0, 1'b0);
|
||||
for (i = 0; i < BURST_LEN; i = i + 1) begin
|
||||
for (i = 0; i < BURST_SPAN; i = i + 1) begin
|
||||
host_read(BASE + i[ADDR_WIDTH-1:0], got);
|
||||
tests = tests + 1;
|
||||
if (i == 3) begin
|
||||
|
||||
@@ -16,17 +16,23 @@
|
||||
// real calibration, impractically slow for simulation) and does not
|
||||
// expose this parameter; mig_7series_0_mig.v does. All other
|
||||
// parameters are left at their defaults, which already ARE this
|
||||
// project's real generated configuration (DQ_WIDTH=16, MEM_DENSITY=
|
||||
// 2Gb, MEM_SPEEDGRADE=125, MEM_ADDR_ORDER=BANK_ROW_COLUMN, etc.) --
|
||||
// project's real generated configuration (EXP-0084: DQ_WIDTH=32 now,
|
||||
// two MT41J128M16 components ganged in parallel -- MEM_DENSITY=2Gb,
|
||||
// MEM_SPEEDGRADE=125, MEM_ADDR_ORDER=BANK_ROW_COLUMN unchanged) --
|
||||
// not generic MIG defaults.
|
||||
//
|
||||
// Clock/reset generation and DDR3 pin wiring (WireDelay pass-through,
|
||||
// zero propagation delay) mirror this project's own vendor-shipped
|
||||
// example_design/sim/sim_tb_top.v exactly, per its own real, proven
|
||||
// pattern -- not re-derived from scratch.
|
||||
// pattern -- not re-derived from scratch. EXP-0084: the real vendor
|
||||
// pattern instantiates NUM_COMP=DQ_WIDTH/16 real ddr3_model.sv
|
||||
// components for a >16-bit aggregate width, each wired to its own
|
||||
// 16-bit dq / 2-bit dqs / 2-bit dm slice -- confirmed by reading the
|
||||
// real regenerated sim_tb_top.v (not assumed), replicated exactly
|
||||
// below for DQ_WIDTH=32 (NUM_COMP=2).
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam CLKIN_PERIOD = 3225; // ps, matches this project's real MIG config
|
||||
localparam CLKIN_PERIOD = 2900; // ps, matches this project's real MIG config (EXP-0084)
|
||||
localparam REFCLK_FREQ = 200.0; // MHz
|
||||
localparam real REFCLK_PERIOD = (1000000.0/(2*REFCLK_FREQ));
|
||||
localparam RESET_PERIOD = 200000; // ps
|
||||
@@ -37,41 +43,56 @@ module tb;
|
||||
reg sys_rst_n;
|
||||
wire sys_rst = sys_rst_n; // Active Low, matches mig_7series_0_mig's own default polarity
|
||||
|
||||
// EXP-0084: both system clock AND reference clock are now real
|
||||
// DIFFERENTIAL pairs on the inner mig_7series_0_mig module (the
|
||||
// user's own wizard choice, "Differential" for both) -- confirmed
|
||||
// against the real regenerated mig_7series_0_mig.v port list
|
||||
// (sys_clk_p/n, clk_ref_p/n, no single-ended sys_clk_i/clk_ref_i
|
||||
// ports exist any more). Same real vendor pattern as sim_tb_top.v:
|
||||
// generate a single-ended internal clock, drive the P/N pair as
|
||||
// true/complement of it.
|
||||
reg sys_clk_i = 1'b0;
|
||||
always #(CLKIN_PERIOD/2.0) sys_clk_i = ~sys_clk_i;
|
||||
wire sys_clk_p = sys_clk_i;
|
||||
wire sys_clk_n = ~sys_clk_i;
|
||||
|
||||
reg clk_ref_i = 1'b0;
|
||||
always #REFCLK_PERIOD clk_ref_i = ~clk_ref_i;
|
||||
wire clk_ref_p = clk_ref_i;
|
||||
wire clk_ref_n = ~clk_ref_i;
|
||||
|
||||
initial begin
|
||||
sys_rst_n = 1'b0;
|
||||
#RESET_PERIOD sys_rst_n = 1'b1;
|
||||
end
|
||||
|
||||
// ---- real DDR3 pins ----
|
||||
// ---- real DDR3 pins (dq/dqs/dm widths doubled since EXP-0084's
|
||||
// real 32-bit widening -- two MT41J128M16 chips ganged in
|
||||
// parallel, confirmed against the real regenerated
|
||||
// mig_7series_0.v wrapper) ----
|
||||
wire ddr3_reset_n;
|
||||
wire [15:0] ddr3_dq_fpga;
|
||||
wire [1:0] ddr3_dqs_p_fpga, ddr3_dqs_n_fpga;
|
||||
wire [31:0] ddr3_dq_fpga;
|
||||
wire [3:0] ddr3_dqs_p_fpga, ddr3_dqs_n_fpga;
|
||||
wire [13:0] ddr3_addr_fpga;
|
||||
wire [2:0] ddr3_ba_fpga;
|
||||
wire ddr3_ras_n_fpga, ddr3_cas_n_fpga, ddr3_we_n_fpga;
|
||||
wire [0:0] ddr3_cke_fpga, ddr3_ck_p_fpga, ddr3_ck_n_fpga, ddr3_cs_n_fpga;
|
||||
wire [1:0] ddr3_dm_fpga;
|
||||
wire [3:0] ddr3_dm_fpga;
|
||||
wire [0:0] ddr3_odt_fpga;
|
||||
|
||||
wire [15:0] ddr3_dq_sdram;
|
||||
wire [31:0] ddr3_dq_sdram;
|
||||
reg [13:0] ddr3_addr_sdram;
|
||||
reg [2:0] ddr3_ba_sdram;
|
||||
reg ddr3_ras_n_sdram, ddr3_cas_n_sdram, ddr3_we_n_sdram;
|
||||
wire [0:0] ddr3_cs_n_sdram;
|
||||
wire [0:0] ddr3_odt_sdram;
|
||||
reg [0:0] ddr3_cke_sdram;
|
||||
wire [1:0] ddr3_dm_sdram;
|
||||
wire [1:0] ddr3_dqs_p_sdram, ddr3_dqs_n_sdram;
|
||||
wire [3:0] ddr3_dm_sdram;
|
||||
wire [3:0] ddr3_dqs_p_sdram, ddr3_dqs_n_sdram;
|
||||
reg [0:0] ddr3_ck_p_sdram, ddr3_ck_n_sdram;
|
||||
|
||||
reg [0:0] ddr3_cs_n_sdram_tmp;
|
||||
reg [1:0] ddr3_dm_sdram_tmp;
|
||||
reg [3:0] ddr3_dm_sdram_tmp;
|
||||
reg [0:0] ddr3_odt_sdram_tmp;
|
||||
|
||||
always @(*) begin
|
||||
@@ -93,7 +114,7 @@ module tb;
|
||||
|
||||
genvar dqwd;
|
||||
generate
|
||||
for (dqwd = 0; dqwd < 16; dqwd = dqwd + 1) begin : dq_delay
|
||||
for (dqwd = 0; dqwd < 32; dqwd = dqwd + 1) begin : dq_delay
|
||||
WireDelay #(.Delay_g(0.00), .Delay_rd(0.00), .ERR_INSERT("OFF")) u_delay_dq (
|
||||
.A(ddr3_dq_fpga[dqwd]), .B(ddr3_dq_sdram[dqwd]),
|
||||
.reset(sys_rst_n), .phy_init_done(init_calib_complete)
|
||||
@@ -102,7 +123,7 @@ module tb;
|
||||
endgenerate
|
||||
genvar dqswd;
|
||||
generate
|
||||
for (dqswd = 0; dqswd < 2; dqswd = dqswd + 1) begin : dqs_delay
|
||||
for (dqswd = 0; dqswd < 4; dqswd = dqswd + 1) begin : dqs_delay
|
||||
WireDelay #(.Delay_g(0.00), .Delay_rd(0.00), .ERR_INSERT("OFF")) u_delay_dqs_p (
|
||||
.A(ddr3_dqs_p_fpga[dqswd]), .B(ddr3_dqs_p_sdram[dqswd]),
|
||||
.reset(sys_rst_n), .phy_init_done(init_calib_complete)
|
||||
@@ -114,29 +135,37 @@ module tb;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// ---- real DDR3 behavioral model (single component, DQ_WIDTH=16
|
||||
// matches MEMORY_WIDTH=16 exactly, no splitting needed) ----
|
||||
ddr3_model u_ddr3 (
|
||||
.rst_n(ddr3_reset_n), .ck(ddr3_ck_p_sdram), .ck_n(ddr3_ck_n_sdram),
|
||||
.cke(ddr3_cke_sdram[0]), .cs_n(ddr3_cs_n_sdram[0]),
|
||||
.ras_n(ddr3_ras_n_sdram), .cas_n(ddr3_cas_n_sdram), .we_n(ddr3_we_n_sdram),
|
||||
.dm_tdqs(ddr3_dm_sdram), .ba(ddr3_ba_sdram), .addr(ddr3_addr_sdram),
|
||||
.dq(ddr3_dq_sdram), .dqs(ddr3_dqs_p_sdram), .dqs_n(ddr3_dqs_n_sdram),
|
||||
.tdqs_n(), .odt(ddr3_odt_sdram[0])
|
||||
);
|
||||
// ---- real DDR3 behavioral model (EXP-0084: TWO components now,
|
||||
// DQ_WIDTH=32 / 16 per component -- exact real pattern confirmed
|
||||
// against the real regenerated sim_tb_top.v's own generate block,
|
||||
// not assumed) ----
|
||||
genvar ci;
|
||||
generate
|
||||
for (ci = 0; ci < 2; ci = ci + 1) begin : gen_mem
|
||||
ddr3_model u_comp_ddr3 (
|
||||
.rst_n(ddr3_reset_n), .ck(ddr3_ck_p_sdram), .ck_n(ddr3_ck_n_sdram),
|
||||
.cke(ddr3_cke_sdram[0]), .cs_n(ddr3_cs_n_sdram[0]),
|
||||
.ras_n(ddr3_ras_n_sdram), .cas_n(ddr3_cas_n_sdram), .we_n(ddr3_we_n_sdram),
|
||||
.dm_tdqs(ddr3_dm_sdram[2*ci +: 2]), .ba(ddr3_ba_sdram), .addr(ddr3_addr_sdram),
|
||||
.dq(ddr3_dq_sdram[16*ci +: 16]),
|
||||
.dqs(ddr3_dqs_p_sdram[2*ci +: 2]), .dqs_n(ddr3_dqs_n_sdram[2*ci +: 2]),
|
||||
.tdqs_n(), .odt(ddr3_odt_sdram[0])
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// ---- real MIG controller (inner module, SIM_BYPASS_INIT_CAL
|
||||
// overridden for a real but fast simulation calibration) ----
|
||||
wire [27:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en, app_rdy;
|
||||
wire [63:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [7:0] app_wdf_mask;
|
||||
wire app_wdf_wren, app_wdf_rdy;
|
||||
wire [63:0] app_rd_data;
|
||||
wire app_rd_data_end, app_rd_data_valid;
|
||||
wire ui_clk, ui_clk_sync_rst, init_calib_complete;
|
||||
wire [27:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en, app_rdy;
|
||||
wire [127:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [15:0] app_wdf_mask;
|
||||
wire app_wdf_wren, app_wdf_rdy;
|
||||
wire [127:0] app_rd_data;
|
||||
wire app_rd_data_end, app_rd_data_valid;
|
||||
wire ui_clk, ui_clk_sync_rst, init_calib_complete;
|
||||
|
||||
mig_7series_0_mig #(
|
||||
.SIM_BYPASS_INIT_CAL("FAST")
|
||||
@@ -148,7 +177,7 @@ module tb;
|
||||
.ddr3_ck_p(ddr3_ck_p_fpga), .ddr3_ck_n(ddr3_ck_n_fpga),
|
||||
.ddr3_cke(ddr3_cke_fpga), .ddr3_cs_n(ddr3_cs_n_fpga),
|
||||
.ddr3_dm(ddr3_dm_fpga), .ddr3_odt(ddr3_odt_fpga),
|
||||
.sys_clk_i(sys_clk_i), .clk_ref_i(clk_ref_i),
|
||||
.sys_clk_p(sys_clk_p), .sys_clk_n(sys_clk_n), .clk_ref_p(clk_ref_p), .clk_ref_n(clk_ref_n),
|
||||
.app_addr(app_addr), .app_cmd(app_cmd), .app_en(app_en),
|
||||
.app_wdf_data(app_wdf_data), .app_wdf_end(app_wdf_end),
|
||||
.app_wdf_mask(app_wdf_mask), .app_wdf_wren(app_wdf_wren),
|
||||
@@ -165,9 +194,9 @@ module tb;
|
||||
// ---- adapter under test ----
|
||||
reg req, wr;
|
||||
reg [ADDR_WIDTH-1:0] addr;
|
||||
reg [16*BURST_LEN-1:0] wdata;
|
||||
reg [2*BURST_LEN-1:0] wmask;
|
||||
wire [16*BURST_LEN-1:0] rdata;
|
||||
reg [32*BURST_LEN-1:0] wdata;
|
||||
reg [4*BURST_LEN-1:0] wmask;
|
||||
wire [32*BURST_LEN-1:0] rdata;
|
||||
wire ready, busy;
|
||||
|
||||
mig_native_adapter #(.BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)) u_adapter (
|
||||
@@ -183,13 +212,13 @@ module tb;
|
||||
task automatic do_txn(
|
||||
input t_wr,
|
||||
input [ADDR_WIDTH-1:0] t_addr,
|
||||
input [16*BURST_LEN-1:0] t_wdata,
|
||||
output [16*BURST_LEN-1:0] t_rdata
|
||||
input [32*BURST_LEN-1:0] t_wdata,
|
||||
output [32*BURST_LEN-1:0] t_rdata
|
||||
);
|
||||
begin
|
||||
@(posedge ui_clk);
|
||||
while (busy) @(posedge ui_clk);
|
||||
req = 1'b1; wr = t_wr; addr = t_addr; wdata = t_wdata; wmask = {(2*BURST_LEN){1'b0}};
|
||||
req = 1'b1; wr = t_wr; addr = t_addr; wdata = t_wdata; wmask = {(4*BURST_LEN){1'b0}};
|
||||
@(posedge ui_clk);
|
||||
req = 1'b0;
|
||||
while (!ready) @(posedge ui_clk);
|
||||
@@ -198,15 +227,15 @@ module tb;
|
||||
endtask
|
||||
|
||||
integer errors, tests;
|
||||
reg [16*BURST_LEN-1:0] got, wpat;
|
||||
reg [32*BURST_LEN-1:0] got, wpat;
|
||||
integer k, i;
|
||||
|
||||
task automatic check_addr(input [ADDR_WIDTH-1:0] a, input [15:0] pattern);
|
||||
begin
|
||||
for (k = 0; k < BURST_LEN; k = k + 1)
|
||||
wpat[k*16 +: 16] = pattern + k[15:0];
|
||||
wpat[k*32 +: 32] = {pattern, pattern + k[15:0]};
|
||||
do_txn(1'b1, a, wpat, got);
|
||||
do_txn(1'b0, a, {(16*BURST_LEN){1'b0}}, got);
|
||||
do_txn(1'b0, a, {(32*BURST_LEN){1'b0}}, got);
|
||||
tests = tests + 1;
|
||||
if (got !== wpat) begin
|
||||
$display("FAIL addr=%0d: got=%h expected=%h", a, got, wpat);
|
||||
|
||||
@@ -26,9 +26,19 @@
|
||||
// EXP-0068's own real vendor-shipped fast-calibration simulation
|
||||
// variant), real ddr3_model.sv, real WireDelay pass-through -- same
|
||||
// proven instantiation pattern as tb_mig_native_adapter.v.
|
||||
//
|
||||
// EXP-0084 UPDATE: real 32-bit DDR3 channel widening -- CLKIN_PERIOD
|
||||
// 2900ps, dq/dqs/dm pin widths doubled (two MT41J128M16 chips ganged
|
||||
// in parallel), TWO real ddr3_model.sv components instantiated (one
|
||||
// per chip, exact real pattern confirmed against the real regenerated
|
||||
// sim_tb_top.v), both sys_clk and clk_ref are now real differential
|
||||
// pairs on the inner mig_7series_0_mig module (the user's own wizard
|
||||
// choice), and the weight/activation preload tasks rewritten for the
|
||||
// new BYTES_PER_BURST=4*BURST_LEN / 4-tiles-per-burst real layouts
|
||||
// (same rewrite already verified in tb_packed_slot.v).
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam CLKIN_PERIOD = 3225; // ps, this project's real MIG config
|
||||
localparam CLKIN_PERIOD = 2900; // ps, this project's real MIG config (EXP-0084)
|
||||
localparam REFCLK_FREQ = 200.0; // MHz
|
||||
localparam real REFCLK_PERIOD = (1000000.0/(2*REFCLK_FREQ));
|
||||
localparam RESET_PERIOD = 200000; // ps
|
||||
@@ -55,8 +65,12 @@ module tb;
|
||||
wire sys_rst = sys_rst_n;
|
||||
reg sys_clk_i = 1'b0;
|
||||
always #(CLKIN_PERIOD/2.0) sys_clk_i = ~sys_clk_i;
|
||||
wire sys_clk_p = sys_clk_i;
|
||||
wire sys_clk_n = ~sys_clk_i;
|
||||
reg clk_ref_i = 1'b0;
|
||||
always #REFCLK_PERIOD clk_ref_i = ~clk_ref_i;
|
||||
wire clk_ref_p = clk_ref_i;
|
||||
wire clk_ref_n = ~clk_ref_i;
|
||||
initial begin
|
||||
sys_rst_n = 1'b0;
|
||||
#RESET_PERIOD sys_rst_n = 1'b1;
|
||||
@@ -64,27 +78,27 @@ module tb;
|
||||
|
||||
// ---- real DDR3 pins + model (identical to tb_mig_native_adapter.v) ----
|
||||
wire ddr3_reset_n;
|
||||
wire [15:0] ddr3_dq_fpga;
|
||||
wire [1:0] ddr3_dqs_p_fpga, ddr3_dqs_n_fpga;
|
||||
wire [31:0] ddr3_dq_fpga;
|
||||
wire [3:0] ddr3_dqs_p_fpga, ddr3_dqs_n_fpga;
|
||||
wire [13:0] ddr3_addr_fpga;
|
||||
wire [2:0] ddr3_ba_fpga;
|
||||
wire ddr3_ras_n_fpga, ddr3_cas_n_fpga, ddr3_we_n_fpga;
|
||||
wire [0:0] ddr3_cke_fpga, ddr3_ck_p_fpga, ddr3_ck_n_fpga, ddr3_cs_n_fpga;
|
||||
wire [1:0] ddr3_dm_fpga;
|
||||
wire [3:0] ddr3_dm_fpga;
|
||||
wire [0:0] ddr3_odt_fpga;
|
||||
|
||||
wire [15:0] ddr3_dq_sdram;
|
||||
wire [31:0] ddr3_dq_sdram;
|
||||
reg [13:0] ddr3_addr_sdram;
|
||||
reg [2:0] ddr3_ba_sdram;
|
||||
reg ddr3_ras_n_sdram, ddr3_cas_n_sdram, ddr3_we_n_sdram;
|
||||
wire [0:0] ddr3_cs_n_sdram;
|
||||
wire [0:0] ddr3_odt_sdram;
|
||||
reg [0:0] ddr3_cke_sdram;
|
||||
wire [1:0] ddr3_dm_sdram;
|
||||
wire [1:0] ddr3_dqs_p_sdram, ddr3_dqs_n_sdram;
|
||||
wire [3:0] ddr3_dm_sdram;
|
||||
wire [3:0] ddr3_dqs_p_sdram, ddr3_dqs_n_sdram;
|
||||
reg [0:0] ddr3_ck_p_sdram, ddr3_ck_n_sdram;
|
||||
reg [0:0] ddr3_cs_n_sdram_tmp;
|
||||
reg [1:0] ddr3_dm_sdram_tmp;
|
||||
reg [3:0] ddr3_dm_sdram_tmp;
|
||||
reg [0:0] ddr3_odt_sdram_tmp;
|
||||
|
||||
always @(*) begin
|
||||
@@ -106,7 +120,7 @@ module tb;
|
||||
|
||||
genvar dqwd;
|
||||
generate
|
||||
for (dqwd = 0; dqwd < 16; dqwd = dqwd + 1) begin : dq_delay
|
||||
for (dqwd = 0; dqwd < 32; dqwd = dqwd + 1) begin : dq_delay
|
||||
WireDelay #(.Delay_g(0.00), .Delay_rd(0.00), .ERR_INSERT("OFF")) u_delay_dq (
|
||||
.A(ddr3_dq_fpga[dqwd]), .B(ddr3_dq_sdram[dqwd]),
|
||||
.reset(sys_rst_n), .phy_init_done(init_calib_complete)
|
||||
@@ -115,7 +129,7 @@ module tb;
|
||||
endgenerate
|
||||
genvar dqswd;
|
||||
generate
|
||||
for (dqswd = 0; dqswd < 2; dqswd = dqswd + 1) begin : dqs_delay
|
||||
for (dqswd = 0; dqswd < 4; dqswd = dqswd + 1) begin : dqs_delay
|
||||
WireDelay #(.Delay_g(0.00), .Delay_rd(0.00), .ERR_INSERT("OFF")) u_delay_dqs_p (
|
||||
.A(ddr3_dqs_p_fpga[dqswd]), .B(ddr3_dqs_p_sdram[dqswd]),
|
||||
.reset(sys_rst_n), .phy_init_done(init_calib_complete)
|
||||
@@ -127,25 +141,31 @@ module tb;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
ddr3_model u_ddr3 (
|
||||
.rst_n(ddr3_reset_n), .ck(ddr3_ck_p_sdram), .ck_n(ddr3_ck_n_sdram),
|
||||
.cke(ddr3_cke_sdram[0]), .cs_n(ddr3_cs_n_sdram[0]),
|
||||
.ras_n(ddr3_ras_n_sdram), .cas_n(ddr3_cas_n_sdram), .we_n(ddr3_we_n_sdram),
|
||||
.dm_tdqs(ddr3_dm_sdram), .ba(ddr3_ba_sdram), .addr(ddr3_addr_sdram),
|
||||
.dq(ddr3_dq_sdram), .dqs(ddr3_dqs_p_sdram), .dqs_n(ddr3_dqs_n_sdram),
|
||||
.tdqs_n(), .odt(ddr3_odt_sdram[0])
|
||||
);
|
||||
genvar ci;
|
||||
generate
|
||||
for (ci = 0; ci < 2; ci = ci + 1) begin : gen_mem
|
||||
ddr3_model u_comp_ddr3 (
|
||||
.rst_n(ddr3_reset_n), .ck(ddr3_ck_p_sdram), .ck_n(ddr3_ck_n_sdram),
|
||||
.cke(ddr3_cke_sdram[0]), .cs_n(ddr3_cs_n_sdram[0]),
|
||||
.ras_n(ddr3_ras_n_sdram), .cas_n(ddr3_cas_n_sdram), .we_n(ddr3_we_n_sdram),
|
||||
.dm_tdqs(ddr3_dm_sdram[2*ci +: 2]), .ba(ddr3_ba_sdram), .addr(ddr3_addr_sdram),
|
||||
.dq(ddr3_dq_sdram[16*ci +: 16]),
|
||||
.dqs(ddr3_dqs_p_sdram[2*ci +: 2]), .dqs_n(ddr3_dqs_n_sdram[2*ci +: 2]),
|
||||
.tdqs_n(), .odt(ddr3_odt_sdram[0])
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
wire [27:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en, app_rdy;
|
||||
wire [63:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [7:0] app_wdf_mask;
|
||||
wire app_wdf_wren, app_wdf_rdy;
|
||||
wire [63:0] app_rd_data;
|
||||
wire app_rd_data_end, app_rd_data_valid;
|
||||
wire ui_clk, ui_clk_sync_rst, init_calib_complete;
|
||||
wire [27:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en, app_rdy;
|
||||
wire [127:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [15:0] app_wdf_mask;
|
||||
wire app_wdf_wren, app_wdf_rdy;
|
||||
wire [127:0] app_rd_data;
|
||||
wire app_rd_data_end, app_rd_data_valid;
|
||||
wire ui_clk, ui_clk_sync_rst, init_calib_complete;
|
||||
|
||||
mig_7series_0_mig #(
|
||||
.SIM_BYPASS_INIT_CAL("FAST")
|
||||
@@ -157,7 +177,7 @@ module tb;
|
||||
.ddr3_ck_p(ddr3_ck_p_fpga), .ddr3_ck_n(ddr3_ck_n_fpga),
|
||||
.ddr3_cke(ddr3_cke_fpga), .ddr3_cs_n(ddr3_cs_n_fpga),
|
||||
.ddr3_dm(ddr3_dm_fpga), .ddr3_odt(ddr3_odt_fpga),
|
||||
.sys_clk_i(sys_clk_i), .clk_ref_i(clk_ref_i),
|
||||
.sys_clk_p(sys_clk_p), .sys_clk_n(sys_clk_n), .clk_ref_p(clk_ref_p), .clk_ref_n(clk_ref_n),
|
||||
.app_addr(app_addr), .app_cmd(app_cmd), .app_en(app_en),
|
||||
.app_wdf_data(app_wdf_data), .app_wdf_end(app_wdf_end),
|
||||
.app_wdf_mask(app_wdf_mask), .app_wdf_wren(app_wdf_wren),
|
||||
@@ -178,25 +198,25 @@ module tb;
|
||||
reg pre_active;
|
||||
reg pre_req, pre_wr;
|
||||
reg [MIG_ADDR_WIDTH-1:0] pre_addr;
|
||||
reg [16*BURST_LEN-1:0] pre_wdata;
|
||||
reg [32*BURST_LEN-1:0] pre_wdata;
|
||||
|
||||
wire adp_req, adp_wr;
|
||||
wire [MIG_ADDR_WIDTH-1:0] adp_addr;
|
||||
wire [16*BURST_LEN-1:0] adp_wdata;
|
||||
wire [2*BURST_LEN-1:0] adp_wmask;
|
||||
wire [16*BURST_LEN-1:0] adp_rdata;
|
||||
wire [32*BURST_LEN-1:0] adp_wdata;
|
||||
wire [4*BURST_LEN-1:0] adp_wmask;
|
||||
wire [32*BURST_LEN-1:0] adp_rdata;
|
||||
wire adp_ready, adp_busy;
|
||||
|
||||
wire arb_ctrl_req_o, arb_ctrl_wr_o;
|
||||
wire [MIG_ADDR_WIDTH-1:0] arb_ctrl_addr_o;
|
||||
wire [16*BURST_LEN-1:0] arb_ctrl_wdata_o;
|
||||
wire [2*BURST_LEN-1:0] arb_ctrl_wmask_o;
|
||||
wire [32*BURST_LEN-1:0] arb_ctrl_wdata_o;
|
||||
wire [4*BURST_LEN-1:0] arb_ctrl_wmask_o;
|
||||
|
||||
assign adp_req = pre_active ? pre_req : arb_ctrl_req_o;
|
||||
assign adp_wr = pre_active ? pre_wr : arb_ctrl_wr_o;
|
||||
assign adp_addr = pre_active ? pre_addr : arb_ctrl_addr_o;
|
||||
assign adp_wdata = pre_active ? pre_wdata : arb_ctrl_wdata_o;
|
||||
assign adp_wmask = pre_active ? {(2*BURST_LEN){1'b0}} : arb_ctrl_wmask_o;
|
||||
assign adp_wmask = pre_active ? {(4*BURST_LEN){1'b0}} : arb_ctrl_wmask_o;
|
||||
|
||||
mig_native_adapter #(.BURST_LEN(BURST_LEN), .ADDR_WIDTH(MIG_ADDR_WIDTH)) u_adapter (
|
||||
.clk(ui_clk), .rst(ui_clk_sync_rst),
|
||||
@@ -215,7 +235,7 @@ module tb;
|
||||
input_byte = $signed(8'((li*11 + pos*41 + t*7 + 3) & 8'hFF));
|
||||
endfunction
|
||||
|
||||
task automatic sdram_write_burst(input [MIG_ADDR_WIDTH-1:0] word_addr, input [16*BURST_LEN-1:0] data);
|
||||
task automatic sdram_write_burst(input [MIG_ADDR_WIDTH-1:0] word_addr, input [32*BURST_LEN-1:0] data);
|
||||
begin
|
||||
@(posedge ui_clk); while (adp_busy) @(posedge ui_clk);
|
||||
pre_req = 1'b1; pre_wr = 1'b1; pre_addr = word_addr; pre_wdata = data;
|
||||
@@ -224,15 +244,18 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// EXP-0084: BYTES_PER_BURST = 4*BURST_LEN (32 bytes/burst, up from
|
||||
// 16) -- 4 consecutive weight bytes pack into each 32-bit word now.
|
||||
task automatic preload_sdram_layers;
|
||||
integer li, bi, wb, tt;
|
||||
reg [16*BURST_LEN-1:0] burst_data;
|
||||
reg [32*BURST_LEN-1:0] burst_data;
|
||||
begin
|
||||
for (li = 0; li < L; li = li + 1) begin
|
||||
for (bi = 0; bi < (LAYER_BYTES/(2*BURST_LEN)); bi = bi + 1) begin
|
||||
for (bi = 0; bi < (LAYER_BYTES/(4*BURST_LEN)); bi = bi + 1) begin
|
||||
for (wb = 0; wb < BURST_LEN; wb = wb + 1) begin
|
||||
tt = bi*(2*BURST_LEN) + wb*2;
|
||||
burst_data[wb*16 +: 16] = {weight_byte(li, tt+1), weight_byte(li, tt)};
|
||||
tt = bi*(4*BURST_LEN) + wb*4;
|
||||
burst_data[wb*32 +: 32] = {weight_byte(li, tt+3), weight_byte(li, tt+2),
|
||||
weight_byte(li, tt+1), weight_byte(li, tt)};
|
||||
end
|
||||
sdram_write_burst((li*WORDS_PER_LAYER + bi*BURST_LEN), burst_data);
|
||||
end
|
||||
@@ -240,30 +263,31 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// ---- real activation preload (EXP-0081 layout: TWO consecutive
|
||||
// tiles share one BURST_LEN=8-word burst -- even tile in the low
|
||||
// 64 bits, odd tile in the high 64 bits, see act_tile_fetch.v's
|
||||
// own header). ----
|
||||
// ---- real activation preload (EXP-0084 layout: FOUR consecutive
|
||||
// tiles share one BURST_LEN=8-word (256-bit) burst -- tile parity
|
||||
// 0/1/2/3 -> quarters [63:0]/[127:64]/[191:128]/[255:192], see
|
||||
// act_tile_fetch.v's own header). ----
|
||||
localparam [MIG_ADDR_WIDTH-1:0] ACT_MEM_BASE = 25'h10000;
|
||||
function automatic [ADDR_WIDTH-1:0] act_x_base(input integer li, input integer pos);
|
||||
act_x_base = {{(ADDR_WIDTH-MIG_ADDR_WIDTH){1'b0}}, ACT_MEM_BASE} + (li*M + pos) * ((N_TILES/2)*BURST_LEN);
|
||||
act_x_base = {{(ADDR_WIDTH-MIG_ADDR_WIDTH){1'b0}}, ACT_MEM_BASE} + (li*M + pos) * ((N_TILES/4)*BURST_LEN);
|
||||
endfunction
|
||||
|
||||
task automatic preload_ddr3_activations;
|
||||
integer li, pos, tp, k;
|
||||
reg [16*BURST_LEN-1:0] burst_data;
|
||||
integer li, pos, tq, qi;
|
||||
reg [32*BURST_LEN-1:0] burst_data;
|
||||
reg [ADDR_WIDTH-1:0] base;
|
||||
begin
|
||||
for (li = 0; li < L; li = li + 1) begin
|
||||
for (pos = 0; pos < M; pos = pos + 1) begin
|
||||
base = act_x_base(li, pos);
|
||||
for (tp = 0; tp < N_TILES/2; tp = tp + 1) begin
|
||||
burst_data = {(16*BURST_LEN){1'b0}};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst_data[k*16 +: 16] = {input_byte(li, pos, (2*tp)*P_IN + 2*k+1), input_byte(li, pos, (2*tp)*P_IN + 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst_data[(P_IN/2+k)*16 +: 16] = {input_byte(li, pos, (2*tp+1)*P_IN + 2*k+1), input_byte(li, pos, (2*tp+1)*P_IN + 2*k)};
|
||||
sdram_write_burst(base[MIG_ADDR_WIDTH-1:0] + tp*BURST_LEN, burst_data);
|
||||
for (tq = 0; tq < N_TILES/4; tq = tq + 1) begin
|
||||
burst_data = {(32*BURST_LEN){1'b0}};
|
||||
for (qi = 0; qi < 4; qi = qi + 1)
|
||||
burst_data[qi*64 +: 64] = {input_byte(li, pos, (4*tq+qi)*P_IN + 7), input_byte(li, pos, (4*tq+qi)*P_IN + 6),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 5), input_byte(li, pos, (4*tq+qi)*P_IN + 4),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 3), input_byte(li, pos, (4*tq+qi)*P_IN + 2),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 1), input_byte(li, pos, (4*tq+qi)*P_IN + 0)};
|
||||
sdram_write_burst(base[MIG_ADDR_WIDTH-1:0] + tq*BURST_LEN, burst_data);
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -309,8 +333,8 @@ module tb;
|
||||
wire [1:0] s_ctrl_req, s_ctrl_wr;
|
||||
wire [1:0] s_ctrl_ready, s_ctrl_busy;
|
||||
wire [MIG_ADDR_WIDTH*2-1:0] s_ctrl_addr_flat;
|
||||
wire [16*BURST_LEN*2-1:0] s_ctrl_wdata_flat, s_ctrl_rdata_flat;
|
||||
wire [2*BURST_LEN*2-1:0] s_ctrl_wmask_flat;
|
||||
wire [32*BURST_LEN*2-1:0] s_ctrl_wdata_flat, s_ctrl_rdata_flat;
|
||||
wire [4*BURST_LEN*2-1:0] s_ctrl_wmask_flat;
|
||||
|
||||
sdram_arbiter_n #(.NUM_REQ(2), .ADDR_WIDTH(MIG_ADDR_WIDTH), .BURST_LEN(BURST_LEN)) u_arb (
|
||||
.clk(ui_clk), .rst(ui_clk_sync_rst),
|
||||
@@ -350,9 +374,9 @@ module tb;
|
||||
.mem_active(mem_active[gi]), .mem_grant(mem_grant[gi]),
|
||||
.ctrl_req(s_ctrl_req[gi]), .ctrl_wr(s_ctrl_wr[gi]),
|
||||
.ctrl_addr(s_ctrl_addr_flat[gi*MIG_ADDR_WIDTH +: MIG_ADDR_WIDTH]),
|
||||
.ctrl_wdata(s_ctrl_wdata_flat[gi*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wmask(s_ctrl_wmask_flat[gi*2*BURST_LEN +: 2*BURST_LEN]),
|
||||
.ctrl_rdata(s_ctrl_rdata_flat[gi*16*BURST_LEN +: 16*BURST_LEN]),
|
||||
.ctrl_wdata(s_ctrl_wdata_flat[gi*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_wmask(s_ctrl_wmask_flat[gi*4*BURST_LEN +: 4*BURST_LEN]),
|
||||
.ctrl_rdata(s_ctrl_rdata_flat[gi*32*BURST_LEN +: 32*BURST_LEN]),
|
||||
.ctrl_ready(s_ctrl_ready[gi]), .ctrl_busy(s_ctrl_busy[gi])
|
||||
);
|
||||
end
|
||||
|
||||
@@ -11,17 +11,20 @@
|
||||
//
|
||||
// EXP-0079 UPDATE: packed_slot.v now wraps a REAL act_tile_fetch.v
|
||||
// (real DDR3 reads, no stand-in port left) -- this test now preloads
|
||||
// activation data into the SAME real SDR SDRAM placeholder backend
|
||||
// already used for weights (preload_sdram_activations, matching
|
||||
// act_tile_fetch.v's own real memory layout: one full BURST_LEN=8-word
|
||||
// burst per tile), instead of a combinational behavioral lookup.
|
||||
// activation data into a real burst-memory backend, matching
|
||||
// act_tile_fetch.v's own real memory layout.
|
||||
//
|
||||
// EXP-0084 UPDATE: real 32-bit DDR3 channel widening -- backend
|
||||
// switched to burst_mem_model32.v (the real AS4C32M16SA x16 SDR model
|
||||
// this test used before is genuinely fixed at 16-bit and can't
|
||||
// represent the new bus width, see that model's own header), and both
|
||||
// preload tasks rewritten for the new BYTES_PER_BURST=4*BURST_LEN
|
||||
// (weights, layer_prefetch_ctrl.v) and 4-tiles-per-burst (activations,
|
||||
// act_tile_fetch.v) real layouts.
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam SDRAM_ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS; // 25
|
||||
localparam SDRAM_ADDR_WIDTH = 25;
|
||||
localparam CLK_FREQ_MHZ = 64;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
|
||||
@@ -42,50 +45,36 @@ module tb;
|
||||
integer cyc;
|
||||
always @(posedge clk) if (!rst) cyc <= cyc + 1;
|
||||
|
||||
// ---- real SDRAM controller + model ----
|
||||
// ---- real burst-memory backend ----
|
||||
wire ctrl_req, ctrl_wr;
|
||||
wire [SDRAM_ADDR_WIDTH-1:0] ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [16*BURST_LEN-1:0] ctrl_rdata;
|
||||
wire [32*BURST_LEN-1:0] ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] ctrl_rdata;
|
||||
wire ctrl_ready, ctrl_busy;
|
||||
wire cke, cs_n, ras_n, cas_n, we_n;
|
||||
wire [BANK_BITS-1:0] ba;
|
||||
wire [ROW_BITS-1:0] a;
|
||||
wire [15:0] dq;
|
||||
wire [1:0] dqm;
|
||||
|
||||
reg wpre_req, wpre_wr;
|
||||
reg [SDRAM_ADDR_WIDTH-1:0] wpre_addr;
|
||||
reg [16*BURST_LEN-1:0] wpre_wdata;
|
||||
reg [32*BURST_LEN-1:0] wpre_wdata;
|
||||
reg pre_active;
|
||||
|
||||
wire slot_ctrl_req, slot_ctrl_wr;
|
||||
wire [SDRAM_ADDR_WIDTH-1:0] slot_ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] slot_ctrl_wdata;
|
||||
wire [2*BURST_LEN-1:0] slot_ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] slot_ctrl_wdata;
|
||||
wire [4*BURST_LEN-1:0] slot_ctrl_wmask;
|
||||
|
||||
assign ctrl_req = pre_active ? wpre_req : slot_ctrl_req;
|
||||
assign ctrl_wr = pre_active ? wpre_wr : slot_ctrl_wr;
|
||||
assign ctrl_addr = pre_active ? wpre_addr : slot_ctrl_addr;
|
||||
assign ctrl_wdata = pre_active ? wpre_wdata : slot_ctrl_wdata;
|
||||
assign ctrl_wmask = pre_active ? {(2*BURST_LEN){1'b0}} : slot_ctrl_wmask;
|
||||
assign ctrl_wmask = pre_active ? {(4*BURST_LEN){1'b0}} : slot_ctrl_wmask;
|
||||
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_ctrl (
|
||||
burst_mem_model32 #(
|
||||
.BURST_LEN(BURST_LEN), .ADDR_WIDTH(SDRAM_ADDR_WIDTH)
|
||||
) u_mem (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask),
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy),
|
||||
.sdram_cke(cke), .sdram_cs_n(cs_n), .sdram_ras_n(ras_n), .sdram_cas_n(cas_n), .sdram_we_n(we_n),
|
||||
.sdram_ba(ba), .sdram_a(a), .sdram_dq(dq), .sdram_dqm(dqm)
|
||||
);
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_mem (
|
||||
.clk(clk), .cke(cke), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n),
|
||||
.ba(ba), .a(a), .dq(dq), .dqm(dqm)
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy)
|
||||
);
|
||||
|
||||
function automatic signed [7:0] weight_byte(input integer li, input integer t);
|
||||
@@ -95,7 +84,7 @@ module tb;
|
||||
input_byte = $signed(8'((li*11 + pos*41 + t*7 + 3) & 8'hFF));
|
||||
endfunction
|
||||
|
||||
task automatic sdram_write_burst(input [SDRAM_ADDR_WIDTH-1:0] word_addr, input [16*BURST_LEN-1:0] data);
|
||||
task automatic sdram_write_burst(input [SDRAM_ADDR_WIDTH-1:0] word_addr, input [32*BURST_LEN-1:0] data);
|
||||
begin
|
||||
@(posedge clk); while (ctrl_busy) @(posedge clk);
|
||||
wpre_req = 1'b1; wpre_wr = 1'b1; wpre_addr = word_addr; wpre_wdata = data;
|
||||
@@ -104,15 +93,18 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// EXP-0084: BYTES_PER_BURST = 4*BURST_LEN (32 bytes/burst, up from
|
||||
// 16) -- 4 consecutive weight bytes pack into each 32-bit word now.
|
||||
task automatic preload_sdram_layers;
|
||||
integer li, bi, wb, tt;
|
||||
reg [16*BURST_LEN-1:0] burst_data;
|
||||
reg [32*BURST_LEN-1:0] burst_data;
|
||||
begin
|
||||
for (li = 0; li < L; li = li + 1) begin
|
||||
for (bi = 0; bi < (LAYER_BYTES/(2*BURST_LEN)); bi = bi + 1) begin
|
||||
for (bi = 0; bi < (LAYER_BYTES/(4*BURST_LEN)); bi = bi + 1) begin
|
||||
for (wb = 0; wb < BURST_LEN; wb = wb + 1) begin
|
||||
tt = bi*(2*BURST_LEN) + wb*2;
|
||||
burst_data[wb*16 +: 16] = {weight_byte(li, tt+1), weight_byte(li, tt)};
|
||||
tt = bi*(4*BURST_LEN) + wb*4;
|
||||
burst_data[wb*32 +: 32] = {weight_byte(li, tt+3), weight_byte(li, tt+2),
|
||||
weight_byte(li, tt+1), weight_byte(li, tt)};
|
||||
end
|
||||
sdram_write_burst((li*WORDS_PER_LAYER + bi*BURST_LEN), burst_data);
|
||||
end
|
||||
@@ -120,31 +112,33 @@ module tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
// ---- real activation preload (EXP-0081 layout: TWO consecutive
|
||||
// tiles share one BURST_LEN=8-word burst -- even tile in the low
|
||||
// 64 bits, odd tile in the high 64 bits, see act_tile_fetch.v's
|
||||
// own header). x_base(li,pos) = ACT_MEM_BASE + (li*M+pos)*
|
||||
// (N_TILES/2*BURST_LEN), well clear of the weight region. ----
|
||||
// ---- real activation preload (EXP-0084 layout: FOUR consecutive
|
||||
// tiles share one BURST_LEN=8-word (256-bit) burst -- tile parity
|
||||
// 0/1/2/3 -> quarters [63:0]/[127:64]/[191:128]/[255:192], see
|
||||
// act_tile_fetch.v's own header). x_base(li,pos) = ACT_MEM_BASE +
|
||||
// (li*M+pos)*(N_TILES/4*BURST_LEN), well clear of the weight
|
||||
// region. ----
|
||||
localparam [ADDR_WIDTH-1:0] ACT_MEM_BASE = 26'h10000;
|
||||
function automatic [ADDR_WIDTH-1:0] act_x_base(input integer li, input integer pos);
|
||||
act_x_base = ACT_MEM_BASE + (li*M + pos) * ((N_TILES/2)*BURST_LEN);
|
||||
act_x_base = ACT_MEM_BASE + (li*M + pos) * ((N_TILES/4)*BURST_LEN);
|
||||
endfunction
|
||||
|
||||
task automatic preload_sdram_activations;
|
||||
integer li, pos, tp, k;
|
||||
reg [16*BURST_LEN-1:0] burst_data;
|
||||
integer li, pos, tq, qi;
|
||||
reg [32*BURST_LEN-1:0] burst_data;
|
||||
reg [ADDR_WIDTH-1:0] base;
|
||||
begin
|
||||
for (li = 0; li < L; li = li + 1) begin
|
||||
for (pos = 0; pos < M; pos = pos + 1) begin
|
||||
base = act_x_base(li, pos);
|
||||
for (tp = 0; tp < N_TILES/2; tp = tp + 1) begin // tp = burst-pair index
|
||||
burst_data = {(16*BURST_LEN){1'b0}};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst_data[k*16 +: 16] = {input_byte(li, pos, (2*tp)*P_IN + 2*k+1), input_byte(li, pos, (2*tp)*P_IN + 2*k)};
|
||||
for (k = 0; k < P_IN/2; k = k + 1)
|
||||
burst_data[(P_IN/2+k)*16 +: 16] = {input_byte(li, pos, (2*tp+1)*P_IN + 2*k+1), input_byte(li, pos, (2*tp+1)*P_IN + 2*k)};
|
||||
sdram_write_burst(base[SDRAM_ADDR_WIDTH-1:0] + tp*BURST_LEN, burst_data);
|
||||
for (tq = 0; tq < N_TILES/4; tq = tq + 1) begin // tq = burst-quad index
|
||||
burst_data = {(32*BURST_LEN){1'b0}};
|
||||
for (qi = 0; qi < 4; qi = qi + 1)
|
||||
burst_data[qi*64 +: 64] = {input_byte(li, pos, (4*tq+qi)*P_IN + 7), input_byte(li, pos, (4*tq+qi)*P_IN + 6),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 5), input_byte(li, pos, (4*tq+qi)*P_IN + 4),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 3), input_byte(li, pos, (4*tq+qi)*P_IN + 2),
|
||||
input_byte(li, pos, (4*tq+qi)*P_IN + 1), input_byte(li, pos, (4*tq+qi)*P_IN + 0)};
|
||||
sdram_write_burst(base[SDRAM_ADDR_WIDTH-1:0] + tq*BURST_LEN, burst_data);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,10 +12,7 @@
|
||||
// ============================================================
|
||||
module tb;
|
||||
localparam BURST_LEN = 8;
|
||||
localparam ROW_BITS = 13;
|
||||
localparam COL_BITS = 10;
|
||||
localparam BANK_BITS = 2;
|
||||
localparam ADDR_WIDTH = BANK_BITS + ROW_BITS + COL_BITS;
|
||||
localparam ADDR_WIDTH = 25;
|
||||
localparam CLK_FREQ_MHZ = 64;
|
||||
localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ;
|
||||
localparam NUM_REQ = 3;
|
||||
@@ -26,38 +23,24 @@ module tb;
|
||||
|
||||
wire ctrl_req, ctrl_wr;
|
||||
wire [ADDR_WIDTH-1:0] ctrl_addr;
|
||||
wire [16*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [2*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire [32*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata;
|
||||
wire [4*BURST_LEN-1:0] ctrl_wmask;
|
||||
wire ctrl_ready, ctrl_busy;
|
||||
wire cke, cs_n, ras_n, cas_n, we_n;
|
||||
wire [BANK_BITS-1:0] ba;
|
||||
wire [ROW_BITS-1:0] a;
|
||||
wire [15:0] dq;
|
||||
wire [1:0] dqm;
|
||||
|
||||
sdram_controller #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .BURST_LEN(BURST_LEN),
|
||||
.ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_ctrl (
|
||||
burst_mem_model32 #(
|
||||
.BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH)
|
||||
) u_mem (
|
||||
.clk(clk), .rst(rst),
|
||||
.req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask),
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy),
|
||||
.sdram_cke(cke), .sdram_cs_n(cs_n), .sdram_ras_n(ras_n), .sdram_cas_n(cas_n), .sdram_we_n(we_n),
|
||||
.sdram_ba(ba), .sdram_a(a), .sdram_dq(dq), .sdram_dqm(dqm)
|
||||
);
|
||||
sdram_model #(
|
||||
.CLK_FREQ_MHZ(CLK_FREQ_MHZ), .ROW_BITS(ROW_BITS), .COL_BITS(COL_BITS), .BANK_BITS(BANK_BITS)
|
||||
) u_mem (
|
||||
.clk(clk), .cke(cke), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n),
|
||||
.ba(ba), .a(a), .dq(dq), .dqm(dqm)
|
||||
.rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy)
|
||||
);
|
||||
|
||||
reg [NUM_REQ-1:0] req_active, req_req, req_wr;
|
||||
wire [NUM_REQ-1:0] req_grant, req_ready, req_busy;
|
||||
reg [NUM_REQ*ADDR_WIDTH-1:0] req_addr;
|
||||
reg [NUM_REQ*16*BURST_LEN-1:0] req_wdata;
|
||||
reg [NUM_REQ*2*BURST_LEN-1:0] req_wmask;
|
||||
wire [NUM_REQ*16*BURST_LEN-1:0] req_rdata;
|
||||
reg [NUM_REQ*32*BURST_LEN-1:0] req_wdata;
|
||||
reg [NUM_REQ*4*BURST_LEN-1:0] req_wmask;
|
||||
wire [NUM_REQ*32*BURST_LEN-1:0] req_rdata;
|
||||
|
||||
sdram_arbiter_n #(
|
||||
.NUM_REQ(NUM_REQ), .ADDR_WIDTH(ADDR_WIDTH), .BURST_LEN(BURST_LEN)
|
||||
@@ -80,7 +63,7 @@ module tb;
|
||||
// no retry if lost.
|
||||
task automatic one_shot_txn(
|
||||
input integer slot, input t_wr, input [ADDR_WIDTH-1:0] t_addr,
|
||||
input [16*BURST_LEN-1:0] t_wdata, output [16*BURST_LEN-1:0] t_rdata
|
||||
input [32*BURST_LEN-1:0] t_wdata, output [32*BURST_LEN-1:0] t_rdata
|
||||
);
|
||||
begin
|
||||
@(posedge clk);
|
||||
@@ -88,26 +71,26 @@ module tb;
|
||||
req_req[slot] = 1'b1;
|
||||
req_wr[slot] = t_wr;
|
||||
req_addr[slot*ADDR_WIDTH +: ADDR_WIDTH] = t_addr;
|
||||
req_wdata[slot*16*BURST_LEN +: 16*BURST_LEN] = t_wdata;
|
||||
req_wmask[slot*2*BURST_LEN +: 2*BURST_LEN] = {(2*BURST_LEN){1'b0}};
|
||||
req_wdata[slot*32*BURST_LEN +: 32*BURST_LEN] = t_wdata;
|
||||
req_wmask[slot*4*BURST_LEN +: 4*BURST_LEN] = {(4*BURST_LEN){1'b0}};
|
||||
@(posedge clk);
|
||||
req_req[slot] = 1'b0;
|
||||
while (!req_ready[slot]) @(posedge clk);
|
||||
t_rdata = req_rdata[slot*16*BURST_LEN +: 16*BURST_LEN];
|
||||
t_rdata = req_rdata[slot*32*BURST_LEN +: 32*BURST_LEN];
|
||||
req_active[slot] = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
reg [16*BURST_LEN-1:0] got, wpat;
|
||||
reg [32*BURST_LEN-1:0] got, wpat;
|
||||
integer k;
|
||||
|
||||
task automatic check_slot(input integer slot, input [ADDR_WIDTH-1:0] a, input [15:0] pattern);
|
||||
integer i;
|
||||
begin
|
||||
for (i = 0; i < BURST_LEN; i = i + 1)
|
||||
wpat[i*16 +: 16] = pattern + i[15:0];
|
||||
wpat[i*32 +: 32] = {pattern, pattern + i[15:0]};
|
||||
one_shot_txn(slot, 1'b1, a, wpat, got);
|
||||
one_shot_txn(slot, 1'b0, a, {(16*BURST_LEN){1'b0}}, got);
|
||||
one_shot_txn(slot, 1'b0, a, {(32*BURST_LEN){1'b0}}, got);
|
||||
tests = tests + 1;
|
||||
if (got !== wpat) begin
|
||||
$display("FAIL slot=%0d addr=%0d: got=%h expected=%h", slot, a, got, wpat);
|
||||
@@ -133,19 +116,19 @@ module tb;
|
||||
|
||||
$display("=== TEST 2: simultaneous multi-requester ACTIVATION (the real EXP-0066 risk case) -- each requester fires its OWN one-shot req only once IT sees its OWN grant, exactly matching packed_slot.v's real S_MEMWAIT usage, not a blind simultaneous fire ===");
|
||||
begin : test2
|
||||
reg [16*BURST_LEN-1:0] w0, w1, w2;
|
||||
reg [32*BURST_LEN-1:0] w0, w1, w2;
|
||||
integer kk;
|
||||
for (kk = 0; kk < BURST_LEN; kk = kk + 1) begin
|
||||
w0[kk*16 +: 16] = 16'hD000 + kk[15:0];
|
||||
w1[kk*16 +: 16] = 16'hE000 + kk[15:0];
|
||||
w2[kk*16 +: 16] = 16'hF000 + kk[15:0];
|
||||
w0[kk*32 +: 32] = {16'hD000, 16'hD000 + kk[15:0]};
|
||||
w1[kk*32 +: 32] = {16'hE000, 16'hE000 + kk[15:0]};
|
||||
w2[kk*32 +: 32] = {16'hF000, 16'hF000 + kk[15:0]};
|
||||
end
|
||||
req_addr[0*ADDR_WIDTH +: ADDR_WIDTH] = 25'd100;
|
||||
req_addr[1*ADDR_WIDTH +: ADDR_WIDTH] = 25'd108;
|
||||
req_addr[2*ADDR_WIDTH +: ADDR_WIDTH] = 25'd116;
|
||||
req_wdata[0*16*BURST_LEN +: 16*BURST_LEN] = w0;
|
||||
req_wdata[1*16*BURST_LEN +: 16*BURST_LEN] = w1;
|
||||
req_wdata[2*16*BURST_LEN +: 16*BURST_LEN] = w2;
|
||||
req_wdata[0*32*BURST_LEN +: 32*BURST_LEN] = w0;
|
||||
req_wdata[1*32*BURST_LEN +: 32*BURST_LEN] = w1;
|
||||
req_wdata[2*32*BURST_LEN +: 32*BURST_LEN] = w2;
|
||||
req_wr[0] = 1'b1; req_wr[1] = 1'b1; req_wr[2] = 1'b1;
|
||||
|
||||
// all three raise `active` on the SAME cycle (the real
|
||||
|
||||
Reference in New Issue
Block a user