feat(v2): M3 activation/weight/result buffers, real BRAM mapping

Implements M3: three parametric dual-port buffers for the §12
data-plane (Input/Weight/Result), reusing the proven BRAM-inference
idiom from the frozen hardware/v1/rtl/act_buffer.v (synchronous write,
synchronous REGISTERED read, no reset on the read register -- keeps
Yosys off the LUT-RAM path).

Verified with Verilator: 10/10 tests pass (write-then-read
correctness, extreme INT8 round-tripping, weight_buffer's full 64-bit
tile width round-tripping, undisturbed re-reads).

Real synthesis at two depths per module (6 configs total): 0 CHECK
problems, every configuration correctly infers DP16KD (never
LUT-RAM). Non-obvious real finding: weight_buffer's BRAM cost is
driven by its P_IN*DATA_WIDTH tile width, not its DEPTH -- an 8x depth
reduction (512->64) left DP16KD usage unchanged at 2, while
activation_buffer/result_buffer (byte-wide) scale as naively expected
(2->1). All default-depth configs PASS at 80MHz with large margin
(287-367 MHz) via real nextpnr-ecp5 place&route.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
This commit is contained in:
2026-09-05 14:19:11 +02:00
co-authored by Claude Sonnet 5
parent 3026dcd997
commit 5f0d7f101c
29 changed files with 178330 additions and 2 deletions
@@ -0,0 +1,215 @@
Info: Logic utilisation before packing:
Info: Total LUT4s: 88/43848 0%
Info: logic LUTs: 88/43848 0%
Info: carry LUTs: 0/43848 0%
Info: RAM LUTs: 0/ 5481 0%
Info: RAMW LUTs: 0/10962 0%
Info: Total DFFs: 139/43848 0%
Info: Packing IOs..
Info: Packing constants..
Info: Packing carries...
Info: Packing LUTs...
Info: Packing LUT5-7s...
Info: Packing FFs...
Info: 1 FFs paired with LUTs.
Info: Generating derived timing constraints...
Info: Promoting globals...
Info: promoting clock net clk$TRELLIS_IO_IN to global network
Info: Checksum: 0x8d179719
Info: Device utilisation:
Info: TRELLIS_IO: 148/ 245 60%
Info: DCCA: 1/ 56 1%
Info: DP16KD: 2/ 108 1%
Info: MULT18X18D: 0/ 72 0%
Info: ALU54B: 0/ 36 0%
Info: EHXPLLL: 0/ 4 0%
Info: EXTREFB: 0/ 2 0%
Info: DCUA: 0/ 2 0%
Info: PCSCLKDIV: 0/ 2 0%
Info: IOLOGIC: 0/ 160 0%
Info: SIOLOGIC: 0/ 85 0%
Info: GSR: 0/ 1 0%
Info: JTAGG: 0/ 1 0%
Info: OSCG: 0/ 1 0%
Info: SEDGA: 0/ 1 0%
Info: DTR: 0/ 1 0%
Info: USRMCLK: 0/ 1 0%
Info: CLKDIVF: 0/ 4 0%
Info: ECLKSYNCB: 0/ 10 0%
Info: DLLDELD: 0/ 8 0%
Info: DDRDLL: 0/ 4 0%
Info: DQSBUFM: 0/ 10 0%
Info: TRELLIS_ECLKBUF: 0/ 8 0%
Info: ECLKBRIDGECS: 0/ 2 0%
Info: DCSC: 0/ 2 0%
Info: TRELLIS_FF: 139/ 43848 0%
Info: TRELLIS_COMB: 90/ 43848 0%
Info: TRELLIS_RAMW: 0/ 5481 0%
Info: Placed 0 cells based on constraints.
Info: Creating initial analytic placement for 214 cells, random placement wirelen = 21853.
Info: at initial placer iter 0, wirelen = 8761
Info: at initial placer iter 1, wirelen = 8682
Info: at initial placer iter 2, wirelen = 8664
Info: at initial placer iter 3, wirelen = 8654
Info: Running main analytical placer, max placement attempts per cell = 18050.
Info: at iteration #1, type ALL: wirelen solved = 8649, spread = 9182, legal = 10156; time = 0.00s
Info: HeAP Placer Time: 0.02s
Info: of which solving equations: 0.00s
Info: of which spreading cells: 0.00s
Info: of which strict legalisation: 0.00s
Info: Running simulated annealing placer for refinement.
Info: at iteration #1: temp = 0.000000, timing cost = 85, wirelen = 10156
Info: at iteration #5: temp = 0.000000, timing cost = 31, wirelen = 8157
Info: at iteration #10: temp = 0.000000, timing cost = 12, wirelen = 8054
Info: at iteration #13: temp = 0.000000, timing cost = 10, wirelen = 7961
Info: SA placement time 0.03s
Info: Max frequency for clock '$glbnet$clk$TRELLIS_IO_IN': 366.57 MHz (PASS at 80.00 MHz)
Info: Max delay <async> -> posedge $glbnet$clk$TRELLIS_IO_IN: 7.42 ns
Info: Max delay posedge $glbnet$clk$TRELLIS_IO_IN -> <async> : 10.27 ns
Info: Slack histogram:
Info: legend: * represents 1 endpoint(s)
Info: + represents [1,1) endpoint(s)
Info: [ 9772, 9871) |*
Info: [ 9871, 9970) |
Info: [ 9970, 10069) |*
Info: [ 10069, 10168) |
Info: [ 10168, 10267) |***
Info: [ 10267, 10366) |**
Info: [ 10366, 10465) |**
Info: [ 10465, 10564) |*******
Info: [ 10564, 10663) |****
Info: [ 10663, 10762) |*************
Info: [ 10762, 10861) |*****
Info: [ 10861, 10960) |******
Info: [ 10960, 11059) |**********
Info: [ 11059, 11158) |*******
Info: [ 11158, 11257) |***************
Info: [ 11257, 11356) |******
Info: [ 11356, 11455) |*************************
Info: [ 11455, 11554) |**********
Info: [ 11554, 11653) |*************
Info: [ 11653, 11752) |*************************
Info: Checksum: 0x6f062bf9
Info: Routing globals...
Info: routing clock net $glbnet$clk$TRELLIS_IO_IN using global 0
Info: Routing..
Info: Setting up routing queue.
Info: Routing 568 arcs.
Info: | (re-)routed arcs | delta | remaining| time spent |
Info: IterCnt | w/ripup wo/ripup | w/r wo/r | arcs| batch(sec) total(sec)|
Info: 586 | 18 560 | 18 560 | 0| 0.09 0.09|
Info: Routing complete.
Info: Router1 time 0.09s
Info: Checksum: 0x1cfed5f0
Info: Critical path report for clock '$glbnet$clk$TRELLIS_IO_IN' (posedge -> posedge):
Info: type curr total name
Info: clk-to-q 0.40 0.40 Source mem.0.0_ADA8_TRELLIS_FF_Q.Q
Info: routing 0.67 1.06 Net mem.0.0_ADA8[1] (41,26) -> (41,27)
Info: Sink wr_en_TRELLIS_FF_DI_Q_LUT4_Z.D
Info: Defined in:
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:108.23-108.24
Info: logic 0.18 1.24 Source wr_en_TRELLIS_FF_DI_Q_LUT4_Z.F
Info: routing 0.56 1.80 Net wr_en_TRELLIS_FF_DI_Q[6] (41,27) -> (40,28)
Info: Sink mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_1_D1_L6MUX21_Z_D0_PFUMX_Z_ALUT_LUT4_Z.M
Info: Defined in:
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:108.23-108.24
Info: logic 0.19 2.00 Source mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_1_D1_L6MUX21_Z_D0_PFUMX_Z_ALUT_LUT4_Z.OFX
Info: routing 0.65 2.65 Net mem.0.0_ADA5_LUT4_D_Z[2] (40,28) -> (40,26)
Info: Sink mem.0.0_DOA0_TRELLIS_FF_Q_1_DI_LUT4_Z.C
Info: Defined in:
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:108.23-108.24
Info: logic 0.18 2.83 Source mem.0.0_DOA0_TRELLIS_FF_Q_1_DI_LUT4_Z.F
Info: routing 0.12 2.94 Net mem.0.0_DOA0_TRELLIS_FF_Q_1_DI (40,26) -> (40,26)
Info: Sink mem.0.0_DOA0_TRELLIS_FF_Q_1.DI
Info: setup 0.00 2.94 Source mem.0.0_DOA0_TRELLIS_FF_Q_1.DI
Info: 0.95 ns logic, 2.00 ns routing
Info: Critical path report for cross-domain path '<async>' -> 'posedge $glbnet$clk$TRELLIS_IO_IN':
Info: type curr total name
Info: source 0.00 0.00 Source rd_addr[6]$tr_io.O
Info: routing 4.15 4.15 Net rd_addr[6]$TRELLIS_IO_IN (13,71) -> (41,25)
Info: Sink mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D0_L6MUX21_Z_D0_PFUMX_Z_ALUT_LUT4_Z.A
Info: Defined in:
Info: hardware/v2/rtl/weight_buffer.v:33.48-33.55
Info: logic 0.18 4.33 Source mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D0_L6MUX21_Z_D0_PFUMX_Z_ALUT_LUT4_Z.F
Info: routing 0.00 4.33 Net mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D0_L6MUX21_Z_D0_PFUMX_Z_ALUT (41,25) -> (41,25)
Info: Sink mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D0_L6MUX21_Z_D0_PFUMX_Z_BLUT_LUT4_Z.F1
Info: Defined in:
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:157.22-157.24
Info: logic 0.13 4.45 Source mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D0_L6MUX21_Z_D0_PFUMX_Z_BLUT_LUT4_Z.OFX
Info: routing 0.00 4.45 Net mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D0_L6MUX21_Z_D0 (41,25) -> (41,25)
Info: Sink mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D0_L6MUX21_Z_D1_PFUMX_Z_ALUT_LUT4_Z.FXA
Info: Defined in:
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:157.50-157.52
Info: logic 0.18 4.63 Source mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D0_L6MUX21_Z_D1_PFUMX_Z_ALUT_LUT4_Z.OFX
Info: routing 0.00 4.63 Net mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D0 (41,25) -> (41,25)
Info: Sink mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D1_L6MUX21_Z_D0_PFUMX_Z_ALUT_LUT4_Z.FXA
Info: Defined in:
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:157.66-157.68
Info: logic 0.18 4.81 Source mem.0.0_ADA5_LUT4_D_Z_L6MUX21_Z_D1_L6MUX21_Z_D0_PFUMX_Z_ALUT_LUT4_Z.OFX
Info: routing 0.52 5.34 Net mem.0.0_ADA5_LUT4_D_Z[3] (41,25) -> (40,26)
Info: Sink mem.0.0_DOA0_TRELLIS_FF_Q_1_DI_LUT4_Z.D
Info: Defined in:
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:108.23-108.24
Info: logic 0.18 5.52 Source mem.0.0_DOA0_TRELLIS_FF_Q_1_DI_LUT4_Z.F
Info: routing 0.12 5.63 Net mem.0.0_DOA0_TRELLIS_FF_Q_1_DI (40,26) -> (40,26)
Info: Sink mem.0.0_DOA0_TRELLIS_FF_Q_1.DI
Info: setup 0.00 5.63 Source mem.0.0_DOA0_TRELLIS_FF_Q_1.DI
Info: 0.84 ns logic, 4.79 ns routing
Info: Critical path report for cross-domain path 'posedge $glbnet$clk$TRELLIS_IO_IN' -> '<async>':
Info: type curr total name
Info: clk-to-q 4.26 4.26 Source mem.0.0.DOB10
Info: routing 1.07 5.33 Net mem.0.0_DOB10[0] (37,34) -> (37,35)
Info: Sink rd_data_LUT4_Z_30.B
Info: Defined in:
Info: /opt/homebrew/bin/../share/yosys/lattice/cells_map_trellis.v:108.23-108.24
Info: logic 0.18 5.51 Source rd_data_LUT4_Z_30.F
Info: routing 3.27 8.78 Net rd_data[28]$TRELLIS_IO_OUT (37,35) -> (67,0)
Info: Sink rd_data[28]$tr_io.I
Info: Defined in:
Info: hardware/v2/rtl/weight_buffer.v:34.48-34.55
Info: 4.44 ns logic, 4.33 ns routing
Info: Max frequency for clock '$glbnet$clk$TRELLIS_IO_IN': 339.67 MHz (PASS at 80.00 MHz)
Info: Max delay <async> -> posedge $glbnet$clk$TRELLIS_IO_IN: 5.63 ns
Info: Max delay posedge $glbnet$clk$TRELLIS_IO_IN -> <async> : 8.78 ns
Info: Slack histogram:
Info: legend: * represents 1 endpoint(s)
Info: + represents [1,1) endpoint(s)
Info: [ 9556, 9668) |*
Info: [ 9668, 9780) |
Info: [ 9780, 9892) |
Info: [ 9892, 10004) |
Info: [ 10004, 10116) |
Info: [ 10116, 10228) |
Info: [ 10228, 10340) |
Info: [ 10340, 10452) |
Info: [ 10452, 10564) |
Info: [ 10564, 10676) |****
Info: [ 10676, 10788) |*****
Info: [ 10788, 10900) |*****
Info: [ 10900, 11012) |************************
Info: [ 11012, 11124) |********************
Info: [ 11124, 11236) |***************
Info: [ 11236, 11348) |*****************
Info: [ 11348, 11460) |***********
Info: [ 11460, 11572) |*************
Info: [ 11572, 11684) |***********
Info: [ 11684, 11796) |*****************************
Info: Program finished normally.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff