diff --git a/.trinity/seals/fpga_FpgaDdr3HeadBandwidth.json b/.trinity/seals/fpga_FpgaDdr3HeadBandwidth.json new file mode 100644 index 0000000000..5f0ead2d37 --- /dev/null +++ b/.trinity/seals/fpga_FpgaDdr3HeadBandwidth.json @@ -0,0 +1,28 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+a86b691b1", + "closure": [], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:5e5da515c467d127f3230cea988388245fcf1ae9daa81d01eb3b76b41911eb5d", + "gen_hash_rust": "sha256:62ef58250c31f22195fca1be94e6ea51b3c2878ba65456b736137437db34a458", + "gen_hash_verilog": "sha256:406c22b582d5a8e113cc3948c2cfc8ad4fdd283b5a136b5bd41df38790b251e7", + "gen_hash_zig": "sha256:825465e68cc9f1a59c3d588fdb208a040f312f03ea4e125672a2c3466173a75f", + "module": "FpgaDdr3HeadBandwidth", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:05:36Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:5ae8e395f6f3be001f54052ae71d11f6093802ed9bad6ccbd32358234edaed6a", + "spec_path": "specs/fpga/ddr3_head_bandwidth.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 11, + "total": 11, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/docs/reports/t27b_expectations.json b/docs/reports/t27b_expectations.json index d160fdeffb..4be3402acd 100644 --- a/docs/reports/t27b_expectations.json +++ b/docs/reports/t27b_expectations.json @@ -296,6 +296,7 @@ {"path": "specs/fpga/coverage.t27", "t27b": "pass"}, {"path": "specs/fpga/crossopt.t27", "t27b": "pass"}, {"path": "specs/fpga/cts.t27", "t27b": "pass"}, + {"path": "specs/fpga/ddr3_head_bandwidth.t27", "t27b": "pass"}, {"path": "specs/fpga/dft.t27", "t27b": "pass"}, {"path": "specs/fpga/die_probe.t27", "t27b": "pass"}, {"path": "specs/fpga/dsp/adders.t27", "t27b": "pass"}, diff --git a/specs/fpga/ddr3_head_bandwidth.t27 b/specs/fpga/ddr3_head_bandwidth.t27 new file mode 100644 index 0000000000..ab7e4f79a8 --- /dev/null +++ b/specs/fpga/ddr3_head_bandwidth.t27 @@ -0,0 +1,1015 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/fpga/ddr3_head_bandwidth.t27 -- how fast the AX7203's DDR3 gives back the head of the model +; Cell 26 of specs/igla/own_model.t27 ("Full model on FPGA, DDR3"), epic gHashTag/t27#10024, route (A) of +; gHashTag/t27#10170. The gate is a read of the head at 2.0 GB/s or more; below 1.5 GB/s the head moves to +; DSP48E1 and this route is killed. +; - +; WHAT THIS IS. A board top, written in t27 and lowered by `t27b verilog`, around a LiteDRAM core +; (litedram_core, generated by litedram_gen for A7DDRPHY, MT41J256M16, sys_clk 100 MHz). The core is the +; DDR3 controller and its calibrating BIOS; everything around it is this file: the pins, the traffic, the +; clock count, the checksum and the receipt on the UART. No constraint file and no Verilog are written by +; hand: every pin's placement and I/O standard is a PIN ATTRIBUTE below (specs/tri/t27b/verilog_plan.t27). +; - +; THE RUN. Wait for the core's init_done. Write the head, HEAD_BEATS beats of 32 bytes at addresses 0 .. +; HEAD_BEATS-1 of the native port, each beat four 64-bit lanes of a pattern no two beats or lanes share +; (lane_word). Then read it back at full rate, one command per clock when the core takes it, and count the +; clocks from the first read command to the last beat in. Every beat read is compared with the pattern +; (err) and folded into a checksum (rsum) in arrival order; the write pass folds the same sum (wsum). A +; 32-step divider turns the clock count into MB/s, and the verdict follows. +; - +; THE ARITHMETIC. The native port is 256 bits = 32 bytes per clock at 100 MHz: a peak of 3.2 GB/s +; (PEAK_MBPS). The head is 2.6M fp32 = 10,400,000 bytes = 325,000 beats. A pass of B bytes in C clocks of +; 10 ns is B * 100 / C MB/s. PASS is B * 100 >= 2000 * C (2.0 GB/s, 62.5 percent of peak); KILL is +; B * 100 < 1500 * C; between the two is SLOW; any beat read back wrong is FAIL, whatever the rate. +; GB here is 10^9 bytes. +; - +; THE RECEIPT. 115200 baud on N15, one line, sent again and again once the run is over: +; T27 DDR3 N=<16 hex> W=<16 hex> R=<16 hex> M=<16 hex> S=<16 hex> C=<16 hex> E=<16 hex> L=<16 hex> PASS +; N beats, W write clocks, R read clocks, M MB/s, S write checksum, C read checksum, E bad beats, L the +; line's number; the verdict word is PASS, SLOW, KILL or FAIL. Until the run is over the UART is the +; core's BIOS, which prints its calibration. +; LEDs (active low): B13 init_done, C13 init_error, D14 PASS, D15 a heartbeat of the 100 MHz clock. +; The button T6 (active low) resets the core and so starts the run again. +; - +; PINS. The DDR3 pinout is litex-boards litex_boards/platforms/alinx_ax7203.py (it cites the ALINX AX7203 +; user manual); clk200 R4/T4 and the UART N15/P20 are the pins fpga/HARDWARE_SSOT.md measured on the board +; (#7788), the LEDs are that file's. Each pin is a 1-bit port of its own, because a placement names one pin. +; - +; WHAT A PASS SAYS. This bitstream read 10,400,000 bytes it had just written to this board's DDR3, all of +; them correct, at the measured rate. WHAT IT DOES NOT SAY. Nothing about a model running: no weight is +; computed on, and the bytes are a pattern, not the head's values. Nothing about any rate but sequential +; reads of one 10.4 MB region after a sequential write of it. +; ASCII only, English only. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module FpgaDdr3HeadBandwidth; + +pub const KIND : str = "board-cell"; +pub const ID : str = "fpga/ddr3_head_bandwidth"; +pub const EPIC : str = "gHashTag/t27#10024"; +pub const BOARD : str = "ALINX AX7203"; +pub const PART : str = "xc7a200tfbg484-2"; + +; --- the run ------------------------------------------------------------------------------------ +pub const HEAD_BYTES : u64 = 10400000; +pub const BEAT_BYTES : u64 = 32; +pub const HEAD_BEATS : u32 = 325000; +pub const SYS_MHZ : u64 = 100; +pub const PEAK_MBPS : u64 = 3200; +pub const GATE_MBPS : u64 = 2000; +pub const KILL_MBPS : u64 = 1500; +pub const LANE_SALT : u64 = 0x9E3779B97F4A7C15; +/// What one beat adds to B * 100, the divider's numerator: BEAT_BYTES * SYS_MHZ. +pub const BEAT_X_MHZ : u32 = 3200; + +pub const V_PASS : u8 = 0; +pub const V_SLOW : u8 = 1; +pub const V_KILL : u8 = 2; +pub const V_FAIL : u8 = 3; + +pub const ST_WAIT : u8 = 0; +pub const ST_WRITE : u8 = 1; +pub const ST_READ : u8 = 2; +pub const ST_DIV : u8 = 3; +pub const ST_REPORT : u8 = 4; + +pub const DIV_STEPS : u8 = 32; +pub const BAUD_DIV : u16 = 868; +pub const LINE_SEGS : u8 = 10; + +; --- the arithmetic ----------------------------------------------------------------------------- + +/// Lane k (0..3) of beat i: (i << 2 | k) spread by two shifts and salted, so no two beats or lanes agree. +pub fn lane_word(i: u32, k: u32) -> u64 { + const w : u64 = ((i as u64) << 2) | ((k & 3) as u64); + return w ^ (w << 21) ^ (w << 42) ^ LANE_SALT; +} + +/// The checksum after one more beat: rotate the sum left by one, then xor in the four lanes, lane k +/// rotated left by 16 * k. Order matters. The lane rotations matter too: the four lanes of a beat of the +/// pattern xor to zero (the salt four times, and (i << 2 | k) for k = 0..3 under every shift), so a plain +/// xor of the lanes adds nothing and every sum is 0. The first bitstream on the board reported exactly +/// that, S=0 C=0. A rotation is wiring; it adds no gate. +pub fn fold(sum: u64, l0: u64, l1: u64, l2: u64, l3: u64) -> u64 { + return ((sum << 1) | (sum >> 63)) ^ l0 ^ ((l1 << 16) | (l1 >> 48)) ^ ((l2 << 32) | (l2 >> 32)) ^ ((l3 << 48) | (l3 >> 16)); +} + +/// The beat's four lanes are the pattern of beat i. +pub fn beat_ok(i: u32, l0: u64, l1: u64, l2: u64, l3: u64) -> bool { + return l0 == lane_word(i, 0) and l1 == lane_word(i, 1) and l2 == lane_word(i, 2) and l3 == lane_word(i, 3); +} + +/// MB/s of `bytes` moved in `cycles` clocks of 10 ns, rounded down (the reference for the divider). +pub fn mbps_of(bytes: u64, cycles: u64) -> u64 { + return (bytes * SYS_MHZ) / cycles; +} + +/// The verdict of a read pass: FAIL on any bad beat, else by the rate against 2.0 and 1.5 GB/s. +pub fn verdict_of(bytes: u64, cycles: u64, bad: u32) -> u8 { + if (bad != 0) { return V_FAIL; } + if (bytes * SYS_MHZ >= GATE_MBPS * cycles) { return V_PASS; } + if (bytes * SYS_MHZ < KILL_MBPS * cycles) { return V_KILL; } + return V_SLOW; +} + +/// The same verdict from the divider's quotient, which is what the board computes: no multiplier on the +/// clock count. B*100 >= 2000*C holds exactly when floor(B*100/C) >= 2000, and B*100 < 1500*C exactly when +/// floor(B*100/C) < 1500, because 2000 and 1500 are whole numbers. +pub fn verdict_by_rate(m: u32, bad: u32) -> u8 { + if (bad != 0) { return V_FAIL; } + if ((m as u64) >= GATE_MBPS) { return V_PASS; } + if ((m as u64) < KILL_MBPS) { return V_KILL; } + return V_SLOW; +} + +/// One step of the restoring divider: the remainder after bringing down the top bit of `num`. +pub fn div_rem(rem: u64, num: u32, den: u32) -> u64 { + const r : u64 = (rem << 1) | ((num >> 31) as u64); + if (r >= (den as u64)) { return r - (den as u64); } + return r; +} + +/// The quotient after the same step. +pub fn div_q(q: u32, rem: u64, num: u32, den: u32) -> u32 { + const r : u64 = (rem << 1) | ((num >> 31) as u64); + if (r >= (den as u64)) { return (q << 1) | 1; } + return q << 1; +} + +; --- the receipt -------------------------------------------------------------------------------- + +/// The ASCII digit of a nibble, 0-9 A-F. +pub fn hex_digit(n: u8) -> u8 { + if ((n & 15) < 10) { return 48 + (n & 15); } + return 55 + (n & 15); +} + +/// The key letter of field s (1..8): N W R M S C E L. +pub fn key_of(s: u8) -> u8 { + if (s == 1) { return 78; } + if (s == 2) { return 87; } + if (s == 3) { return 82; } + if (s == 4) { return 77; } + if (s == 5) { return 83; } + if (s == 6) { return 67; } + if (s == 7) { return 69; } + return 76; +} + +/// Character p of "T27 DDR3". +pub fn prefix_char(p: u8) -> u8 { + if (p == 0) { return 84; } + if (p == 1) { return 50; } + if (p == 2) { return 55; } + if (p == 3) { return 32; } + if (p == 4 or p == 5) { return 68; } + if (p == 6) { return 82; } + return 51; +} + +/// Character p of " \r\n". +pub fn verdict_char(v: u8, p: u8) -> u8 { + if (p == 0) { return 32; } + if (p == 5) { return 13; } + if (p == 6) { return 10; } + if (v == V_PASS) { + if (p == 1) { return 80; } + if (p == 2) { return 65; } + return 83; + } + if (v == V_SLOW) { + if (p == 1) { return 83; } + if (p == 2) { return 76; } + if (p == 3) { return 79; } + return 87; + } + if (v == V_KILL) { + if (p == 1) { return 75; } + if (p == 2) { return 73; } + return 76; + } + if (p == 1) { return 70; } + if (p == 2) { return 65; } + if (p == 3) { return 73; } + return 76; +} + +/// The character at segment s, position p; `fsr` holds the field being written, its next nibble on top. +pub fn line_char(s: u8, p: u8, fsr: u64, v: u8) -> u8 { + if (s == 0) { return prefix_char(p); } + if (s == 9) { return verdict_char(v, p); } + if (p == 0) { return 32; } + if (p == 1) { return key_of(s); } + if (p == 2) { return 61; } + return hex_digit((fsr >> 60) as u8); +} + +/// The last position of segment s. +pub fn seg_last(s: u8) -> u8 { + if (s == 0) { return 7; } + if (s == 9) { return 6; } + return 18; +} + +/// A UART frame, sent low bit first: a start bit 0, the 8 data bits, a stop bit 1. +pub fn frame(ch: u8) -> u16 { + return ((ch as u16) << 1) | 512; +} + +; --- the board ---------------------------------------------------------------------------------- + +/// A differential input buffer (the 200 MHz oscillator on R4/T4). +pub struct IbufdsOut { O: bool } +pub fn IBUFDS(I: bool, IB: bool) -> IbufdsOut {} + +/// The outputs of the LiteDRAM core, as named in its generated Verilog. +pub struct LitedramOut { ddram_a__0: bool, ddram_a__1: bool, ddram_a__2: bool, ddram_a__3: bool, ddram_a__4: bool, ddram_a__5: bool, ddram_a__6: bool, ddram_a__7: bool, ddram_a__8: bool, ddram_a__9: bool, ddram_a__10: bool, ddram_a__11: bool, ddram_a__12: bool, ddram_a__13: bool, ddram_a__14: bool, ddram_ba__0: bool, ddram_ba__1: bool, ddram_ba__2: bool, ddram_dm__0: bool, ddram_dm__1: bool, ddram_dm__2: bool, ddram_dm__3: bool, ddram_cas_n: bool, ddram_cke: bool, ddram_clk_n: bool, ddram_clk_p: bool, ddram_cs_n: bool, ddram_odt: bool, ddram_ras_n: bool, ddram_reset_n: bool, ddram_we_n: bool, init_done: bool, init_error: bool, pll_locked: bool, uart_tx: bool, user_clk: bool, user_port_native_0_cmd_ready: bool, user_port_native_0_rdata_data__0: u64, user_port_native_0_rdata_data__1: u64, user_port_native_0_rdata_data__2: u64, user_port_native_0_rdata_data__3: u64, user_port_native_0_rdata_valid: bool, user_port_native_0_wdata_ready: bool, user_rst: bool } + +/// The LiteDRAM core: a DDR3 controller with its PHY and PLL, a 256-bit native user port, and a BIOS on the UART +/// that calibrates the PHY before it raises init_done. +pub fn litedram_core(clk_: bool, rst: bool, uart_rx_: bool, user_port_native_0_cmd_addr: u25, user_port_native_0_cmd_valid: bool, user_port_native_0_cmd_we: bool, user_port_native_0_rdata_ready: bool, user_port_native_0_wdata_data__0: u64, user_port_native_0_wdata_data__1: u64, user_port_native_0_wdata_data__2: u64, user_port_native_0_wdata_data__3: u64, user_port_native_0_wdata_valid: bool, user_port_native_0_wdata_we: u32, ddram_dq__0: bool, ddram_dq__1: bool, ddram_dq__2: bool, ddram_dq__3: bool, ddram_dq__4: bool, ddram_dq__5: bool, ddram_dq__6: bool, ddram_dq__7: bool, ddram_dq__8: bool, ddram_dq__9: bool, ddram_dq__10: bool, ddram_dq__11: bool, ddram_dq__12: bool, ddram_dq__13: bool, ddram_dq__14: bool, ddram_dq__15: bool, ddram_dq__16: bool, ddram_dq__17: bool, ddram_dq__18: bool, ddram_dq__19: bool, ddram_dq__20: bool, ddram_dq__21: bool, ddram_dq__22: bool, ddram_dq__23: bool, ddram_dq__24: bool, ddram_dq__25: bool, ddram_dq__26: bool, ddram_dq__27: bool, ddram_dq__28: bool, ddram_dq__29: bool, ddram_dq__30: bool, ddram_dq__31: bool, ddram_dqs_n__0: bool, ddram_dqs_n__1: bool, ddram_dqs_n__2: bool, ddram_dqs_n__3: bool, ddram_dqs_p__0: bool, ddram_dqs_p__1: bool, ddram_dqs_p__2: bool, ddram_dqs_p__3: bool) -> LitedramOut {} + +; --- the pins in -------------------------------------------------------------------------------- +const clk200_p : bool = undefined; +const clk200_p_LOC : str = "R4"; +const clk200_p_IOSTANDARD : str = "DIFF_SSTL15"; +const clk200_n : bool = undefined; +const clk200_n_LOC : str = "T4"; +const clk200_n_IOSTANDARD : str = "DIFF_SSTL15"; +const cpu_reset_n : bool = undefined; +const cpu_reset_n_LOC : str = "T6"; +const cpu_reset_n_IOSTANDARD : str = "LVCMOS15"; +const uart_rx : bool = undefined; +const uart_rx_LOC : str = "P20"; +const uart_rx_IOSTANDARD : str = "LVCMOS33"; + +; --- the clock, the core and its reset ---------------------------------------------------------- +const clkbuf : IbufdsOut = IBUFDS(clk200_p, clk200_n); +const clkbuf_O_PERIOD_PS : u32 = 5000; + +const core : LitedramOut = litedram_core(clkbuf.O, !cpu_reset_n, uart_rx, ci as u25, (st == ST_WRITE or st == ST_READ) and ci < n_beats, st == ST_WRITE, true, lane_word(di, 0), lane_word(di, 1), lane_word(di, 2), lane_word(di, 3), st == ST_WRITE and di < n_beats, 0xFFFFFFFF, ddram_dq_0, ddram_dq_1, ddram_dq_2, ddram_dq_3, ddram_dq_4, ddram_dq_5, ddram_dq_6, ddram_dq_7, ddram_dq_8, ddram_dq_9, ddram_dq_10, ddram_dq_11, ddram_dq_12, ddram_dq_13, ddram_dq_14, ddram_dq_15, ddram_dq_16, ddram_dq_17, ddram_dq_18, ddram_dq_19, ddram_dq_20, ddram_dq_21, ddram_dq_22, ddram_dq_23, ddram_dq_24, ddram_dq_25, ddram_dq_26, ddram_dq_27, ddram_dq_28, ddram_dq_29, ddram_dq_30, ddram_dq_31, ddram_dqs_n_0, ddram_dqs_n_1, ddram_dqs_n_2, ddram_dqs_n_3, ddram_dqs_p_0, ddram_dqs_p_1, ddram_dqs_p_2, ddram_dqs_p_3); +const core_user_clk_PERIOD_PS : u32 = 10000; + +const clk : bool = core.user_clk; +const rst_n : bool = !core.user_rst; +const en : bool = true; + +; --- the pins out ------------------------------------------------------------------------------- +pub const uart_tx : bool = (st == ST_REPORT and (tx_sh & 1) == 1) or (st != ST_REPORT and core.uart_tx); +const uart_tx_LOC : str = "N15"; +const uart_tx_IOSTANDARD : str = "LVCMOS33"; +pub const led_0 : bool = !core.init_done; +const led_0_LOC : str = "B13"; +const led_0_IOSTANDARD : str = "LVCMOS33"; +pub const led_1 : bool = !core.init_error; +const led_1_LOC : str = "C13"; +const led_1_IOSTANDARD : str = "LVCMOS33"; +pub const led_2 : bool = !(st == ST_REPORT and verdict == V_PASS); +const led_2_LOC : str = "D14"; +const led_2_IOSTANDARD : str = "LVCMOS33"; +pub const led_3 : bool = ((hb >> 26) & 1) == 1; +const led_3_LOC : str = "D15"; +const led_3_IOSTANDARD : str = "LVCMOS33"; + +; --- the DDR3 pins: outputs of the core, then the bidirectional data and strobes ------------------ +pub const ddram_a_0 : bool = core.ddram_a__0; +const ddram_a_0_LOC : str = "AA4"; +const ddram_a_0_IOSTANDARD : str = "SSTL15"; +const ddram_a_0_SLEW : str = "FAST"; +pub const ddram_a_1 : bool = core.ddram_a__1; +const ddram_a_1_LOC : str = "AB2"; +const ddram_a_1_IOSTANDARD : str = "SSTL15"; +const ddram_a_1_SLEW : str = "FAST"; +pub const ddram_a_2 : bool = core.ddram_a__2; +const ddram_a_2_LOC : str = "AA5"; +const ddram_a_2_IOSTANDARD : str = "SSTL15"; +const ddram_a_2_SLEW : str = "FAST"; +pub const ddram_a_3 : bool = core.ddram_a__3; +const ddram_a_3_LOC : str = "AB5"; +const ddram_a_3_IOSTANDARD : str = "SSTL15"; +const ddram_a_3_SLEW : str = "FAST"; +pub const ddram_a_4 : bool = core.ddram_a__4; +const ddram_a_4_LOC : str = "AB1"; +const ddram_a_4_IOSTANDARD : str = "SSTL15"; +const ddram_a_4_SLEW : str = "FAST"; +pub const ddram_a_5 : bool = core.ddram_a__5; +const ddram_a_5_LOC : str = "U3"; +const ddram_a_5_IOSTANDARD : str = "SSTL15"; +const ddram_a_5_SLEW : str = "FAST"; +pub const ddram_a_6 : bool = core.ddram_a__6; +const ddram_a_6_LOC : str = "W1"; +const ddram_a_6_IOSTANDARD : str = "SSTL15"; +const ddram_a_6_SLEW : str = "FAST"; +pub const ddram_a_7 : bool = core.ddram_a__7; +const ddram_a_7_LOC : str = "T1"; +const ddram_a_7_IOSTANDARD : str = "SSTL15"; +const ddram_a_7_SLEW : str = "FAST"; +pub const ddram_a_8 : bool = core.ddram_a__8; +const ddram_a_8_LOC : str = "V2"; +const ddram_a_8_IOSTANDARD : str = "SSTL15"; +const ddram_a_8_SLEW : str = "FAST"; +pub const ddram_a_9 : bool = core.ddram_a__9; +const ddram_a_9_LOC : str = "U2"; +const ddram_a_9_IOSTANDARD : str = "SSTL15"; +const ddram_a_9_SLEW : str = "FAST"; +pub const ddram_a_10 : bool = core.ddram_a__10; +const ddram_a_10_LOC : str = "Y1"; +const ddram_a_10_IOSTANDARD : str = "SSTL15"; +const ddram_a_10_SLEW : str = "FAST"; +pub const ddram_a_11 : bool = core.ddram_a__11; +const ddram_a_11_LOC : str = "W2"; +const ddram_a_11_IOSTANDARD : str = "SSTL15"; +const ddram_a_11_SLEW : str = "FAST"; +pub const ddram_a_12 : bool = core.ddram_a__12; +const ddram_a_12_LOC : str = "Y2"; +const ddram_a_12_IOSTANDARD : str = "SSTL15"; +const ddram_a_12_SLEW : str = "FAST"; +pub const ddram_a_13 : bool = core.ddram_a__13; +const ddram_a_13_LOC : str = "U1"; +const ddram_a_13_IOSTANDARD : str = "SSTL15"; +const ddram_a_13_SLEW : str = "FAST"; +pub const ddram_a_14 : bool = core.ddram_a__14; +const ddram_a_14_LOC : str = "V3"; +const ddram_a_14_IOSTANDARD : str = "SSTL15"; +const ddram_a_14_SLEW : str = "FAST"; +pub const ddram_ba_0 : bool = core.ddram_ba__0; +const ddram_ba_0_LOC : str = "AA3"; +const ddram_ba_0_IOSTANDARD : str = "SSTL15"; +const ddram_ba_0_SLEW : str = "FAST"; +pub const ddram_ba_1 : bool = core.ddram_ba__1; +const ddram_ba_1_LOC : str = "Y3"; +const ddram_ba_1_IOSTANDARD : str = "SSTL15"; +const ddram_ba_1_SLEW : str = "FAST"; +pub const ddram_ba_2 : bool = core.ddram_ba__2; +const ddram_ba_2_LOC : str = "Y4"; +const ddram_ba_2_IOSTANDARD : str = "SSTL15"; +const ddram_ba_2_SLEW : str = "FAST"; +pub const ddram_ras_n : bool = core.ddram_ras_n; +const ddram_ras_n_LOC : str = "V4"; +const ddram_ras_n_IOSTANDARD : str = "SSTL15"; +const ddram_ras_n_SLEW : str = "FAST"; +pub const ddram_cas_n : bool = core.ddram_cas_n; +const ddram_cas_n_LOC : str = "W4"; +const ddram_cas_n_IOSTANDARD : str = "SSTL15"; +const ddram_cas_n_SLEW : str = "FAST"; +pub const ddram_we_n : bool = core.ddram_we_n; +const ddram_we_n_LOC : str = "AA1"; +const ddram_we_n_IOSTANDARD : str = "SSTL15"; +const ddram_we_n_SLEW : str = "FAST"; +pub const ddram_cs_n : bool = core.ddram_cs_n; +const ddram_cs_n_LOC : str = "AB3"; +const ddram_cs_n_IOSTANDARD : str = "SSTL15"; +const ddram_cs_n_SLEW : str = "FAST"; +pub const ddram_cke : bool = core.ddram_cke; +const ddram_cke_LOC : str = "T5"; +const ddram_cke_IOSTANDARD : str = "SSTL15"; +const ddram_cke_SLEW : str = "FAST"; +pub const ddram_odt : bool = core.ddram_odt; +const ddram_odt_LOC : str = "U5"; +const ddram_odt_IOSTANDARD : str = "SSTL15"; +const ddram_odt_SLEW : str = "FAST"; +pub const ddram_reset_n : bool = core.ddram_reset_n; +const ddram_reset_n_LOC : str = "W6"; +const ddram_reset_n_IOSTANDARD : str = "LVCMOS15"; +const ddram_reset_n_SLEW : str = "FAST"; +pub const ddram_clk_p : bool = core.ddram_clk_p; +const ddram_clk_p_LOC : str = "R3"; +const ddram_clk_p_IOSTANDARD : str = "DIFF_SSTL15"; +const ddram_clk_p_SLEW : str = "FAST"; +pub const ddram_clk_n : bool = core.ddram_clk_n; +const ddram_clk_n_LOC : str = "R2"; +const ddram_clk_n_IOSTANDARD : str = "DIFF_SSTL15"; +const ddram_clk_n_SLEW : str = "FAST"; +pub const ddram_dm_0 : bool = core.ddram_dm__0; +const ddram_dm_0_LOC : str = "D2"; +const ddram_dm_0_IOSTANDARD : str = "SSTL15"; +const ddram_dm_0_SLEW : str = "FAST"; +pub const ddram_dm_1 : bool = core.ddram_dm__1; +const ddram_dm_1_LOC : str = "G2"; +const ddram_dm_1_IOSTANDARD : str = "SSTL15"; +const ddram_dm_1_SLEW : str = "FAST"; +pub const ddram_dm_2 : bool = core.ddram_dm__2; +const ddram_dm_2_LOC : str = "M2"; +const ddram_dm_2_IOSTANDARD : str = "SSTL15"; +const ddram_dm_2_SLEW : str = "FAST"; +pub const ddram_dm_3 : bool = core.ddram_dm__3; +const ddram_dm_3_LOC : str = "M5"; +const ddram_dm_3_IOSTANDARD : str = "SSTL15"; +const ddram_dm_3_SLEW : str = "FAST"; + +pub const ddram_dq_0 : bool = undefined; +const ddram_dq_0_LOC : str = "C2"; +const ddram_dq_0_IOSTANDARD : str = "SSTL15"; +const ddram_dq_0_SLEW : str = "FAST"; +const ddram_dq_0_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_1 : bool = undefined; +const ddram_dq_1_LOC : str = "G1"; +const ddram_dq_1_IOSTANDARD : str = "SSTL15"; +const ddram_dq_1_SLEW : str = "FAST"; +const ddram_dq_1_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_2 : bool = undefined; +const ddram_dq_2_LOC : str = "A1"; +const ddram_dq_2_IOSTANDARD : str = "SSTL15"; +const ddram_dq_2_SLEW : str = "FAST"; +const ddram_dq_2_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_3 : bool = undefined; +const ddram_dq_3_LOC : str = "F3"; +const ddram_dq_3_IOSTANDARD : str = "SSTL15"; +const ddram_dq_3_SLEW : str = "FAST"; +const ddram_dq_3_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_4 : bool = undefined; +const ddram_dq_4_LOC : str = "B2"; +const ddram_dq_4_IOSTANDARD : str = "SSTL15"; +const ddram_dq_4_SLEW : str = "FAST"; +const ddram_dq_4_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_5 : bool = undefined; +const ddram_dq_5_LOC : str = "F1"; +const ddram_dq_5_IOSTANDARD : str = "SSTL15"; +const ddram_dq_5_SLEW : str = "FAST"; +const ddram_dq_5_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_6 : bool = undefined; +const ddram_dq_6_LOC : str = "B1"; +const ddram_dq_6_IOSTANDARD : str = "SSTL15"; +const ddram_dq_6_SLEW : str = "FAST"; +const ddram_dq_6_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_7 : bool = undefined; +const ddram_dq_7_LOC : str = "E2"; +const ddram_dq_7_IOSTANDARD : str = "SSTL15"; +const ddram_dq_7_SLEW : str = "FAST"; +const ddram_dq_7_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_8 : bool = undefined; +const ddram_dq_8_LOC : str = "H3"; +const ddram_dq_8_IOSTANDARD : str = "SSTL15"; +const ddram_dq_8_SLEW : str = "FAST"; +const ddram_dq_8_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_9 : bool = undefined; +const ddram_dq_9_LOC : str = "G3"; +const ddram_dq_9_IOSTANDARD : str = "SSTL15"; +const ddram_dq_9_SLEW : str = "FAST"; +const ddram_dq_9_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_10 : bool = undefined; +const ddram_dq_10_LOC : str = "H2"; +const ddram_dq_10_IOSTANDARD : str = "SSTL15"; +const ddram_dq_10_SLEW : str = "FAST"; +const ddram_dq_10_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_11 : bool = undefined; +const ddram_dq_11_LOC : str = "H5"; +const ddram_dq_11_IOSTANDARD : str = "SSTL15"; +const ddram_dq_11_SLEW : str = "FAST"; +const ddram_dq_11_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_12 : bool = undefined; +const ddram_dq_12_LOC : str = "J1"; +const ddram_dq_12_IOSTANDARD : str = "SSTL15"; +const ddram_dq_12_SLEW : str = "FAST"; +const ddram_dq_12_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_13 : bool = undefined; +const ddram_dq_13_LOC : str = "J5"; +const ddram_dq_13_IOSTANDARD : str = "SSTL15"; +const ddram_dq_13_SLEW : str = "FAST"; +const ddram_dq_13_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_14 : bool = undefined; +const ddram_dq_14_LOC : str = "K1"; +const ddram_dq_14_IOSTANDARD : str = "SSTL15"; +const ddram_dq_14_SLEW : str = "FAST"; +const ddram_dq_14_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_15 : bool = undefined; +const ddram_dq_15_LOC : str = "H4"; +const ddram_dq_15_IOSTANDARD : str = "SSTL15"; +const ddram_dq_15_SLEW : str = "FAST"; +const ddram_dq_15_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_16 : bool = undefined; +const ddram_dq_16_LOC : str = "L4"; +const ddram_dq_16_IOSTANDARD : str = "SSTL15"; +const ddram_dq_16_SLEW : str = "FAST"; +const ddram_dq_16_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_17 : bool = undefined; +const ddram_dq_17_LOC : str = "M3"; +const ddram_dq_17_IOSTANDARD : str = "SSTL15"; +const ddram_dq_17_SLEW : str = "FAST"; +const ddram_dq_17_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_18 : bool = undefined; +const ddram_dq_18_LOC : str = "L3"; +const ddram_dq_18_IOSTANDARD : str = "SSTL15"; +const ddram_dq_18_SLEW : str = "FAST"; +const ddram_dq_18_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_19 : bool = undefined; +const ddram_dq_19_LOC : str = "J6"; +const ddram_dq_19_IOSTANDARD : str = "SSTL15"; +const ddram_dq_19_SLEW : str = "FAST"; +const ddram_dq_19_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_20 : bool = undefined; +const ddram_dq_20_LOC : str = "K3"; +const ddram_dq_20_IOSTANDARD : str = "SSTL15"; +const ddram_dq_20_SLEW : str = "FAST"; +const ddram_dq_20_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_21 : bool = undefined; +const ddram_dq_21_LOC : str = "K6"; +const ddram_dq_21_IOSTANDARD : str = "SSTL15"; +const ddram_dq_21_SLEW : str = "FAST"; +const ddram_dq_21_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_22 : bool = undefined; +const ddram_dq_22_LOC : str = "J4"; +const ddram_dq_22_IOSTANDARD : str = "SSTL15"; +const ddram_dq_22_SLEW : str = "FAST"; +const ddram_dq_22_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_23 : bool = undefined; +const ddram_dq_23_LOC : str = "L5"; +const ddram_dq_23_IOSTANDARD : str = "SSTL15"; +const ddram_dq_23_SLEW : str = "FAST"; +const ddram_dq_23_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_24 : bool = undefined; +const ddram_dq_24_LOC : str = "P1"; +const ddram_dq_24_IOSTANDARD : str = "SSTL15"; +const ddram_dq_24_SLEW : str = "FAST"; +const ddram_dq_24_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_25 : bool = undefined; +const ddram_dq_25_LOC : str = "N4"; +const ddram_dq_25_IOSTANDARD : str = "SSTL15"; +const ddram_dq_25_SLEW : str = "FAST"; +const ddram_dq_25_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_26 : bool = undefined; +const ddram_dq_26_LOC : str = "R1"; +const ddram_dq_26_IOSTANDARD : str = "SSTL15"; +const ddram_dq_26_SLEW : str = "FAST"; +const ddram_dq_26_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_27 : bool = undefined; +const ddram_dq_27_LOC : str = "N2"; +const ddram_dq_27_IOSTANDARD : str = "SSTL15"; +const ddram_dq_27_SLEW : str = "FAST"; +const ddram_dq_27_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_28 : bool = undefined; +const ddram_dq_28_LOC : str = "M6"; +const ddram_dq_28_IOSTANDARD : str = "SSTL15"; +const ddram_dq_28_SLEW : str = "FAST"; +const ddram_dq_28_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_29 : bool = undefined; +const ddram_dq_29_LOC : str = "N5"; +const ddram_dq_29_IOSTANDARD : str = "SSTL15"; +const ddram_dq_29_SLEW : str = "FAST"; +const ddram_dq_29_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_30 : bool = undefined; +const ddram_dq_30_LOC : str = "P6"; +const ddram_dq_30_IOSTANDARD : str = "SSTL15"; +const ddram_dq_30_SLEW : str = "FAST"; +const ddram_dq_30_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dq_31 : bool = undefined; +const ddram_dq_31_LOC : str = "P2"; +const ddram_dq_31_IOSTANDARD : str = "SSTL15"; +const ddram_dq_31_SLEW : str = "FAST"; +const ddram_dq_31_IN_TERM : str = "UNTUNED_SPLIT_50"; +pub const ddram_dqs_p_0 : bool = undefined; +const ddram_dqs_p_0_LOC : str = "E1"; +const ddram_dqs_p_0_IOSTANDARD : str = "DIFF_SSTL15"; +const ddram_dqs_p_0_SLEW : str = "FAST"; +pub const ddram_dqs_p_1 : bool = undefined; +const ddram_dqs_p_1_LOC : str = "K2"; +const ddram_dqs_p_1_IOSTANDARD : str = "DIFF_SSTL15"; +const ddram_dqs_p_1_SLEW : str = "FAST"; +pub const ddram_dqs_p_2 : bool = undefined; +const ddram_dqs_p_2_LOC : str = "M1"; +const ddram_dqs_p_2_IOSTANDARD : str = "DIFF_SSTL15"; +const ddram_dqs_p_2_SLEW : str = "FAST"; +pub const ddram_dqs_p_3 : bool = undefined; +const ddram_dqs_p_3_LOC : str = "P5"; +const ddram_dqs_p_3_IOSTANDARD : str = "DIFF_SSTL15"; +const ddram_dqs_p_3_SLEW : str = "FAST"; +pub const ddram_dqs_n_0 : bool = undefined; +const ddram_dqs_n_0_LOC : str = "D1"; +const ddram_dqs_n_0_IOSTANDARD : str = "DIFF_SSTL15"; +const ddram_dqs_n_0_SLEW : str = "FAST"; +pub const ddram_dqs_n_1 : bool = undefined; +const ddram_dqs_n_1_LOC : str = "J2"; +const ddram_dqs_n_1_IOSTANDARD : str = "DIFF_SSTL15"; +const ddram_dqs_n_1_SLEW : str = "FAST"; +pub const ddram_dqs_n_2 : bool = undefined; +const ddram_dqs_n_2_LOC : str = "L1"; +const ddram_dqs_n_2_IOSTANDARD : str = "DIFF_SSTL15"; +const ddram_dqs_n_2_SLEW : str = "FAST"; +pub const ddram_dqs_n_3 : bool = undefined; +const ddram_dqs_n_3_LOC : str = "P4"; +const ddram_dqs_n_3_IOSTANDARD : str = "DIFF_SSTL15"; +const ddram_dqs_n_3_SLEW : str = "FAST"; + +; --- the registers ------------------------------------------------------------------------------ +var st : u8 = 0; +var n_beats : u32 = 325000; +var ci : u32 = 0; +var di : u32 = 0; +var ri : u32 = 0; +var wcyc : u32 = 0; +var rcyc : u32 = 0; +var wsum : u64 = 0; +var rsum : u64 = 0; +var err : u32 = 0; +/// The beat the core gave on the last clock, checked on this one: the read data reach the compare and the +/// checksum from a register, not straight from the PHY (the first route put that path at 70 MHz). +var rv : bool = false; +var rb0 : u64 = 0; +var rb1 : u64 = 0; +var rb2 : u64 = 0; +var rb3 : u64 = 0; +var mbps : u32 = 0; +var verdict : u8 = 3; +var dnum : u32 = 0; +var drem : u64 = 0; +var dq : u32 = 0; +var dstep : u8 = 0; +var baud_div : u16 = 868; +var tx_cnt : u16 = 0; +var tx_sh : u16 = 1023; +var tx_left : u8 = 0; +var seg : u8 = 0; +var pos : u8 = 0; +var fsr : u64 = 0; +var lines : u32 = 0; +var hb : u32 = 0; + +/// The value of receipt field s (1..8), N W R M S C E L. +pub fn field(s: u8, n: u32, w: u32, r: u32, m: u32, ws: u64, rs: u64, e: u32, l: u32) -> u64 { + if (s == 1) { return n as u64; } + if (s == 2) { return w as u64; } + if (s == 3) { return r as u64; } + if (s == 4) { return m as u64; } + if (s == 5) { return ws; } + if (s == 6) { return rs; } + if (s == 7) { return e as u64; } + return l as u64; +} + +// Every var is read before it is written, so the test's order and the clock edge agree. +fn on_clock(core_init_done: bool, core_init_error: bool, core_user_port_native_0_cmd_ready: bool, core_user_port_native_0_wdata_ready: bool, core_user_port_native_0_rdata_valid: bool, core_user_port_native_0_rdata_data__0: u64, core_user_port_native_0_rdata_data__1: u64, core_user_port_native_0_rdata_data__2: u64, core_user_port_native_0_rdata_data__3: u64) { + hb = hb +% 1; + if (st == ST_WAIT) { + if (core_init_error) { + verdict = V_FAIL; + st = ST_REPORT; + } else if (core_init_done) { + st = ST_WRITE; + } + } else if (st == ST_WRITE) { + if (ci == n_beats and di == n_beats) { + st = ST_READ; + ci = 0; + } else { + wcyc = wcyc + 1; + if (core_user_port_native_0_cmd_ready and ci < n_beats) { ci = ci + 1; } + if (core_user_port_native_0_wdata_ready and di < n_beats) { + wsum = fold(wsum, lane_word(di, 0), lane_word(di, 1), lane_word(di, 2), lane_word(di, 3)); + di = di + 1; + } + } + } else if (st == ST_READ) { + if (ri == n_beats) { + st = ST_DIV; + drem = 0; + dq = 0; + dstep = 0; + } else { + rcyc = rcyc + 1; + if (core_user_port_native_0_cmd_ready and ci < n_beats) { ci = ci + 1; } + if (rv) { + if (!beat_ok(ri, rb0, rb1, rb2, rb3)) { err = err + 1; } + rsum = fold(rsum, rb0, rb1, rb2, rb3); + dnum = dnum + BEAT_X_MHZ; + ri = ri + 1; + } + rv = core_user_port_native_0_rdata_valid; + rb0 = core_user_port_native_0_rdata_data__0; + rb1 = core_user_port_native_0_rdata_data__1; + rb2 = core_user_port_native_0_rdata_data__2; + rb3 = core_user_port_native_0_rdata_data__3; + } + } else if (st == ST_DIV) { + if (dstep == DIV_STEPS) { + mbps = dq; + verdict = verdict_by_rate(dq, err); + st = ST_REPORT; + } else { + dq = div_q(dq, drem, dnum, rcyc); + drem = div_rem(drem, dnum, rcyc); + dnum = dnum << 1; + dstep = dstep + 1; + } + } else { + if (tx_cnt + 1 < baud_div) { + tx_cnt = tx_cnt + 1; + } else { + tx_cnt = 0; + if (tx_left != 0) { + tx_sh = (tx_sh >> 1) | 512; + tx_left = tx_left - 1; + } else { + tx_sh = frame(line_char(seg, pos, fsr, verdict)); + tx_left = 9; + if (pos == seg_last(seg)) { + fsr = field(seg + 1, n_beats, wcyc, rcyc, mbps, wsum, rsum, err, lines); + pos = 0; + if (seg == LINE_SEGS - 1) { + seg = 0; + lines = lines + 1; + } else { + seg = seg + 1; + } + } else { + if (seg != 0 and seg != LINE_SEGS - 1 and pos >= 3) { fsr = fsr << 4; } + pos = pos + 1; + } + } + } + } +} + +; --- the tests ---------------------------------------------------------------------------------- + +test the_head_is_325000_beats_of_32_bytes { + assert_eq((HEAD_BEATS as u64) * BEAT_BYTES, HEAD_BYTES); + assert_eq(BEAT_BYTES * 8, 256); + assert_eq(BEAT_BYTES * SYS_MHZ, PEAK_MBPS); + assert_eq(BEAT_X_MHZ as u64, PEAK_MBPS); + assert_eq(mbps_of(HEAD_BYTES, 325000), PEAK_MBPS); + assert_eq(mbps_of(HEAD_BYTES, 520000), GATE_MBPS); +} + +test the_gate_is_2_gb_s_and_the_kill_1_5 { + assert_eq(verdict_of(HEAD_BYTES, 325000, 0), V_PASS); + assert_eq(verdict_of(HEAD_BYTES, 520000, 0), V_PASS); + assert_eq(verdict_of(HEAD_BYTES, 520001, 0), V_SLOW); + assert_eq(verdict_of(HEAD_BYTES, 693333, 0), V_SLOW); + assert_eq(verdict_of(HEAD_BYTES, 693334, 0), V_KILL); + assert_eq(verdict_of(HEAD_BYTES, 325000, 1), V_FAIL); + assert_eq(verdict_of(HEAD_BYTES, 693334, 7), V_FAIL); + assert_eq(verdict_by_rate(mbps_of(HEAD_BYTES, 325000) as u32, 0), V_PASS); + assert_eq(verdict_by_rate(mbps_of(HEAD_BYTES, 520000) as u32, 0), V_PASS); + assert_eq(verdict_by_rate(mbps_of(HEAD_BYTES, 520001) as u32, 0), V_SLOW); + assert_eq(verdict_by_rate(mbps_of(HEAD_BYTES, 693333) as u32, 0), V_SLOW); + assert_eq(verdict_by_rate(mbps_of(HEAD_BYTES, 693334) as u32, 0), V_KILL); + assert_eq(verdict_by_rate(mbps_of(HEAD_BYTES, 325000) as u32, 1), V_FAIL); + assert_eq(verdict_by_rate(1999, 0), V_SLOW); + assert_eq(verdict_by_rate(1500, 0), V_SLOW); + assert_eq(verdict_by_rate(1499, 0), V_KILL); +} + +test the_serial_divider_is_the_quotient { + var num : u32 = 1040000000; + var den : u32 = 520000; + var q : u32 = 0; + var r : u64 = 0; + var step : u8 = 0; + while (step < DIV_STEPS) { + q = div_q(q, r, num, den); + r = div_rem(r, num, den); + num = num << 1; + step = step + 1; + } + assert_eq(q, 2000); + num = 1040000000; + den = 693334; + q = 0; + r = 0; + step = 0; + while (step < DIV_STEPS) { + q = div_q(q, r, num, den); + r = div_rem(r, num, den); + num = num << 1; + step = step + 1; + } + assert_eq(q, 1499); + assert_eq(r, 1040000000 - 1499 * 693334); + num = 4294967295; + den = 7; + q = 0; + r = 0; + step = 0; + while (step < DIV_STEPS) { + q = div_q(q, r, num, den); + r = div_rem(r, num, den); + num = num << 1; + step = step + 1; + } + assert_eq(q, 613566756); + assert_eq(r, 3); +} + +test no_two_beats_or_lanes_share_a_word { + assert lane_word(0, 0) != lane_word(0, 1); + assert lane_word(0, 3) != lane_word(1, 0); + assert lane_word(1, 0) != lane_word(0, 0); + assert lane_word(324999, 3) != lane_word(324999, 2); + assert lane_word(0, 0) == LANE_SALT; + assert beat_ok(7, lane_word(7, 0), lane_word(7, 1), lane_word(7, 2), lane_word(7, 3)); + assert !beat_ok(7, lane_word(7, 0), lane_word(7, 1), lane_word(7, 2) ^ 1, lane_word(7, 3)); + assert !beat_ok(7, lane_word(7, 1), lane_word(7, 0), lane_word(7, 2), lane_word(7, 3)); + assert !beat_ok(8, lane_word(7, 0), lane_word(7, 1), lane_word(7, 2), lane_word(7, 3)); +} + +test the_checksum_knows_the_order_of_the_beats { + const a : u64 = fold(fold(0, 1, 2, 4, 8), 16, 32, 64, 128); + const b : u64 = fold(fold(0, 16, 32, 64, 128), 1, 2, 4, 8); + assert a != b; + assert_eq(fold(0, 1, 2, 4, 8), 2251816993685505); + assert_eq(fold(9223372036854775808, 0, 0, 0, 0), 1); + assert_eq(lane_word(5, 0) ^ lane_word(5, 1) ^ lane_word(5, 2) ^ lane_word(5, 3), 0); + assert_eq(fold(0, lane_word(0, 0), lane_word(0, 1), lane_word(0, 2), lane_word(0, 3)), 16182248147329805489); +} + +test the_head_folds_to_a_sum_the_board_must_report { + var sum : u64 = 0; + var i : u32 = 0; + while (i < HEAD_BEATS) { + sum = fold(sum, lane_word(i, 0), lane_word(i, 1), lane_word(i, 2), lane_word(i, 3)); + i = i + 1; + } + assert_eq(sum, 0xFFB7236C398EE555); +} + +test a_run_of_three_beats_reads_back_what_it_wrote { + st = ST_WAIT; + n_beats = 3; + ci = 0; + di = 0; + ri = 0; + wcyc = 0; + rcyc = 0; + wsum = 0; + rsum = 0; + err = 0; + dnum = 0; + rv = false; + on_clock(false, false, true, true, false, 0, 0, 0, 0); + assert_eq(st, ST_WAIT); + on_clock(true, false, true, true, false, 0, 0, 0, 0); + assert_eq(st, ST_WRITE); + on_clock(true, false, true, false, false, 0, 0, 0, 0); + assert_eq(ci, 1); + assert_eq(di, 0); + on_clock(true, false, true, true, false, 0, 0, 0, 0); + on_clock(true, false, true, true, false, 0, 0, 0, 0); + on_clock(true, false, false, true, false, 0, 0, 0, 0); + assert_eq(ci, 3); + assert_eq(di, 3); + assert_eq(wcyc, 4); + on_clock(true, false, false, false, false, 0, 0, 0, 0); + assert_eq(st, ST_READ); + assert_eq(ci, 0); + on_clock(true, false, true, false, true, lane_word(0, 0), lane_word(0, 1), lane_word(0, 2), lane_word(0, 3)); + assert_eq(ri, 0); + on_clock(true, false, true, false, true, lane_word(1, 0), lane_word(1, 1), lane_word(1, 2), lane_word(1, 3)); + assert_eq(ri, 1); + on_clock(true, false, true, false, true, lane_word(2, 0), lane_word(2, 1), lane_word(2, 2), lane_word(2, 3)); + on_clock(true, false, false, false, false, 0, 0, 0, 0); + assert_eq(ci, 3); + assert_eq(ri, 3); + assert_eq(rcyc, 4); + assert_eq(dnum, 3 * BEAT_X_MHZ); + on_clock(true, false, false, false, false, 0, 0, 0, 0); + assert_eq(st, ST_DIV); + var k : u8 = 0; + while (k < 33) { + on_clock(true, false, false, false, false, 0, 0, 0, 0); + k = k + 1; + } + assert_eq(st, ST_REPORT); + assert_eq(err, 0); + assert_eq(rsum, wsum); + assert_eq(mbps, 2400); + assert_eq(verdict, V_PASS); +} + +test a_beat_read_back_wrong_is_a_fail_at_any_rate { + st = ST_READ; + n_beats = 2; + ci = 2; + ri = 0; + rcyc = 0; + rsum = 0; + err = 0; + dnum = 0; + rv = false; + wsum = fold(fold(0, lane_word(0, 0), lane_word(0, 1), lane_word(0, 2), lane_word(0, 3)), lane_word(1, 0), lane_word(1, 1), lane_word(1, 2), lane_word(1, 3)); + on_clock(true, false, false, false, true, lane_word(0, 0), lane_word(0, 1), lane_word(0, 2), lane_word(0, 3)); + on_clock(true, false, false, false, true, lane_word(1, 0), lane_word(1, 1), lane_word(1, 2) ^ 4, lane_word(1, 3)); + on_clock(true, false, false, false, false, 0, 0, 0, 0); + on_clock(true, false, false, false, false, 0, 0, 0, 0); + assert_eq(rcyc, 3); + var k : u8 = 0; + while (k < 33) { + on_clock(true, false, false, false, false, 0, 0, 0, 0); + k = k + 1; + } + assert_eq(st, ST_REPORT); + assert_eq(err, 1); + assert rsum != wsum; + assert_eq(mbps, 2133); + assert_eq(verdict, V_FAIL); +} + +test a_slow_read_is_slow_and_a_slower_one_killed { + st = ST_READ; + n_beats = 3; + ci = 3; + ri = 0; + rcyc = 0; + err = 0; + dnum = 0; + rv = false; + on_clock(true, false, false, false, true, lane_word(0, 0), lane_word(0, 1), lane_word(0, 2), lane_word(0, 3)); + on_clock(true, false, false, false, false, 0, 0, 0, 0); + on_clock(true, false, false, false, true, lane_word(1, 0), lane_word(1, 1), lane_word(1, 2), lane_word(1, 3)); + on_clock(true, false, false, false, false, 0, 0, 0, 0); + on_clock(true, false, false, false, true, lane_word(2, 0), lane_word(2, 1), lane_word(2, 2), lane_word(2, 3)); + on_clock(true, false, false, false, false, 0, 0, 0, 0); + var k : u8 = 0; + while (k < 34) { + on_clock(true, false, false, false, false, 0, 0, 0, 0); + k = k + 1; + } + assert_eq(rcyc, 6); + assert_eq(mbps, 1600); + assert_eq(verdict, V_SLOW); + st = ST_READ; + ri = 0; + rcyc = 3; + dnum = 0; + rv = false; + on_clock(true, false, false, false, true, lane_word(0, 0), lane_word(0, 1), lane_word(0, 2), lane_word(0, 3)); + on_clock(true, false, false, false, true, lane_word(1, 0), lane_word(1, 1), lane_word(1, 2), lane_word(1, 3)); + on_clock(true, false, false, false, true, lane_word(2, 0), lane_word(2, 1), lane_word(2, 2), lane_word(2, 3)); + on_clock(true, false, false, false, false, 0, 0, 0, 0); + k = 0; + while (k < 34) { + on_clock(true, false, false, false, false, 0, 0, 0, 0); + k = k + 1; + } + assert_eq(rcyc, 7); + assert_eq(mbps, 1371); + assert_eq(verdict, V_KILL); +} + +test a_failed_calibration_reports_fail { + st = ST_WAIT; + verdict = V_PASS; + on_clock(false, true, false, false, false, 0, 0, 0, 0); + assert_eq(st, ST_REPORT); + assert_eq(verdict, V_FAIL); +} + +test the_receipt_line_says_what_was_measured { + st = ST_REPORT; + n_beats = 3; + wcyc = 4; + rcyc = 4; + mbps = 3200; + wsum = 18364758544493064720; + rsum = 18364758544493064720; + err = 0; + lines = 0; + verdict = V_PASS; + baud_div = 1; + tx_cnt = 0; + tx_sh = 1023; + tx_left = 0; + seg = 0; + pos = 0; + var got : u32 = 0; + var bits : u32 = 0; + var ch : u8 = 0; + var line : u32 = 0; + while (line < 2) { + on_clock(true, false, false, false, false, 0, 0, 0, 0); + bits = bits + 1; + if (tx_left == 9) { + assert_eq(tx_sh & 1, 0); + assert_eq(tx_sh >> 9, 1); + ch = ((tx_sh >> 1) & 255) as u8; + if (line == 0 and got == 0) { assert_eq(ch, 84); } + if (line == 0 and got == 3) { assert_eq(ch, 32); } + if (line == 0 and got == 7) { assert_eq(ch, 51); } + if (line == 0 and got == 9) { assert_eq(ch, 78); } + if (line == 0 and got == 10) { assert_eq(ch, 61); } + if (line == 0 and got == 11) { assert_eq(ch, 48); } + if (line == 0 and got == 26) { assert_eq(ch, 51); } + if (line == 0 and got == 28) { assert_eq(ch, 87); } + if (line == 0 and got == 66) { assert_eq(ch, 77); } + if (line == 0 and got == 81) { assert_eq(ch, 67); } + if (line == 0 and got == 82) { assert_eq(ch, 56); } + if (line == 0 and got == 83) { assert_eq(ch, 48); } + if (line == 0 and got == 87) { assert_eq(ch, 70); } + if (line == 0 and got == 102) { assert_eq(ch, 48); } + if (line == 0 and got == 123) { assert_eq(ch, 69); } + if (line == 0 and got == 159) { assert_eq(ch, 48); } + if (line == 1 and got == 159) { assert_eq(ch, 49); } + if (got == 161) { assert_eq(ch, 80); } + if (got == 164) { assert_eq(ch, 83); } + if (got == 165) { assert_eq(ch, 13); } + if (ch == 10) { + assert_eq(got, 166); + got = 0; + line = line + 1; + } else { + got = got + 1; + } + } + } + assert_eq(lines, 2); + assert_eq(bits, (2 * 167 - 1) * 10 + 1); +} diff --git a/specs/igla/own_model.t27 b/specs/igla/own_model.t27 index 0c4a2986fd..775e6ee175 100644 --- a/specs/igla/own_model.t27 +++ b/specs/igla/own_model.t27 @@ -78,8 +78,19 @@ ; - review: no PR behind any cell has a review, so no DELIVER cell is VERIFIED. #10164 merged on ; 2026-10-18 02:43Z without one. The evidence above is put to review in the PR that adds this ; line, re-taken on the lab at its own base; REVIEWED still waits for a PR that names its review. +; - board on 2026-10-18, cell 26: specs/fpga/ddr3_head_bandwidth.t27 (11/11 tests, 0 vacuous on the +; lab), lowered by t27b verilog around a LiteDRAM core, routed by openXC7 for xc7a200tfbg484-2 and +; flashed on the AX7203 with openFPGALoader -c digilent_hs2. Its UART receipt: 325000 beats +; (10,400,000 bytes, the head) read back in 375548 to 375554 clocks of 10 ns, M = 2769 MB/s +; (86.5 percent of the 3200 peak, gate 2000), 0 bad beats, the read checksum equal to the write +; checksum and to the value the spec's test predicts (FFB7236C398EE555), verdict PASS, the same on +; a second flash. A mutant that checks each beat against the next beat's pattern reported +; E = 325000 of 325000 and FAIL. Not closed: nextpnr puts the 100 MHz clock at 85 to 95 MHz, the +; worst path inside the LiteDRAM core, and 3 of 9 routes pass the BIOS memtest; the receipt is +; from those that do. This is a sequential read of a pattern, no weight on the board and no +; compute from DDR3, so the cell is a draft at 10 percent, a DELIVER step waiting for its review. ; The engine asks an engineering LEARN step for a source link, a PRACTICE step for a passing test -; and a DELIVER step for a review. So: 1 published, 16 verified, 5 drafts, 5 gaps. +; and a DELIVER step for a review. So: 1 published, 16 verified, 6 drafts, 4 gaps. ; PERCENT is a second, separate number per cell: an estimate of how much of that part exists, with ; its basis in SOURCES. It never feeds a status. The lens and overall figures are plain means, every @@ -199,7 +210,7 @@ pub const SOURCES : [27]str = [ "t27c test-report on 17 ML specs: 17 compile", "", "", - "", + "t27 specs/fpga/ddr3_head_bandwidth.t27: head read 2769 MB/s on the AX7203; no weights on it", "t27 specs/igla/own_model.t27" ]; @@ -214,22 +225,22 @@ pub const MISSING : [27]str = [ "", "", "", "", "", "heap allocation and file I/O lowered by t27c: neither exists; ml/arena_heap.t27 and ml/file_io_contract.t27 are the contract it must meet", "a t27c backend that lowers a model spec to a training binary: none exists", - "embeddings, head and weights in DDR3 on the board: they stay on the host", + "", "" ]; /// The measurements per cell (see the header for how each was taken). -pub const GROUNDED : [27]bool = [true, true, true, true, true, true, true, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, true]; +pub const GROUNDED : [27]bool = [true, true, true, true, true, true, true, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, true, true]; pub const TEST_PASS : [27]bool = [false, false, false, true, true, true, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, true, true, false, false, false, false]; pub const REVIEWED : [27]bool = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]; pub const LIVE : [27]bool = [false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false]; /// What a host passes the viewer, per cell: the earned status code and the step number as a label. -pub const STATUS : [27]u8 = [4, 4, 4, 4, 4, 4, 3, 0, 0, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 5, 4, 4, 0, 0, 0, 3]; +pub const STATUS : [27]u8 = [4, 4, 4, 4, 4, 4, 3, 0, 0, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 5, 4, 4, 0, 0, 3, 3]; pub const LABELS : [27]str = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27"]; /// Percent of each part that exists, estimated from its source (see the header). Never a status. -pub const PCT : [27]u8 = [60, 70, 80, 80, 60, 100, 50, 0, 0, 100, 100, 20, 80, 65, 95, 75, 55, 40, 70, 15, 60, 40, 100, 0, 0, 0, 50]; +pub const PCT : [27]u8 = [60, 70, 80, 80, 60, 100, 50, 0, 0, 100, 100, 20, 80, 65, 95, 75, 55, 40, 70, 15, 60, 40, 100, 0, 0, 10, 50]; /// The t27c test-report counts the PRACTICE cells quote. pub const ML_SPECS_MEASURED : i32 = 17; @@ -254,8 +265,8 @@ pub const ROWS : [20]str = [ "In t27: loss, optimizers, HSLM pass", "17 of 17 ML specs compile on the lab", "9 of 9 IGLA CODER specs compile", - "1 published, 16 verified, 5 drafts", - "5 gaps, about 54 percent overall", + "1 published, 16 verified, 6 drafts", + "4 gaps, about 54 percent overall", "Batched f32 HSLM trainer", "Shadow weights for the whole model", "File I/O, heap, --backend ml", @@ -311,7 +322,7 @@ test every_step_is_a_cell { test a_gap_is_named_not_filled { assert gaps_are_named(SOURCES, MISSING, GROUNDED); - assert count_true(GROUNDED) == 22; + assert count_true(GROUNDED) == 23; var c : i32 = 0; while (c < CELL_COUNT) { const i : usize = @intCast(c); @@ -325,11 +336,11 @@ test a_gap_is_named_not_filled { test a_status_is_earned_not_chosen { assert statuses_earned(STATUS, DOMAIN, GROUNDED, TEST_PASS, REVIEWED, LIVE); - // the evidence slide's "1 published, 16 verified, 5 drafts" and "5 gaps" + // the evidence slide's "1 published, 16 verified, 6 drafts" and "4 gaps" assert count_status(STATUS, STATUS_PUBLISHED) == 1; assert count_status(STATUS, STATUS_VERIFIED) == 16; - assert count_status(STATUS, STATUS_DRAFT) == 5; - assert count_status(STATUS, STATUS_GAP) == 5; + assert count_status(STATUS, STATUS_DRAFT) == 6; + assert count_status(STATUS, STATUS_GAP) == 4; assert count_true(REVIEWED) == 0; // every passing test is a PRACTICE step: the t27 bench, MultiPL-E and coder specs (cells 4 to // 6), loss, optimizers, forward pass, matvec, and the ML specs that compile (cell 23) @@ -377,7 +388,7 @@ test the_compile_counts_are_the_measured_ones { test percent_per_lens_is_a_plain_mean { assert mean_pct(0, 9) == 55; assert mean_pct(9, 9) == 70; - assert mean_pct(18, 9) == 37; + assert mean_pct(18, 9) == 38; // the evidence slide's "about 54 percent overall" assert mean_pct(0, CELL_COUNT) == 54; } diff --git a/specs/igla/own_model_board.t27 b/specs/igla/own_model_board.t27 index 450a2acdb4..875f45d3d8 100644 --- a/specs/igla/own_model_board.t27 +++ b/specs/igla/own_model_board.t27 @@ -48,7 +48,7 @@ pub const LENS_CELLS : i32 = 9; pub const JOB : str = "own_model"; pub const STAGE : str = "own_model"; -pub const MSG : str = "1 published, 16 verified, 5 drafts, 5 gaps; 54%"; +pub const MSG : str = "1 published, 16 verified, 6 drafts, 4 gaps; 54%"; /// What MSG says once its numbers are taken out. pub const MSG_WORDS : str = " published, verified, drafts, gaps; %"; pub const OVERALL_PCT : u8 = 54; @@ -59,12 +59,12 @@ pub const KEY_CELLS : str = "cells"; pub const KEY_STATUS : str = "status"; pub const KEY_PCT : str = "pct"; -pub const LENS_PCT : [3]u8 = [55, 70, 37]; -pub const CELL_STATUS : [27]u8 = [4, 4, 4, 4, 4, 4, 3, 0, 0, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 5, 4, 4, 0, 0, 0, 3]; -pub const CELL_PCT : [27]u8 = [60, 70, 80, 80, 60, 100, 50, 0, 0, 100, 100, 20, 80, 65, 95, 75, 55, 40, 70, 15, 60, 40, 100, 0, 0, 0, 50]; +pub const LENS_PCT : [3]u8 = [55, 70, 38]; +pub const CELL_STATUS : [27]u8 = [4, 4, 4, 4, 4, 4, 3, 0, 0, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 5, 4, 4, 0, 0, 3, 3]; +pub const CELL_PCT : [27]u8 = [60, 70, 80, 80, 60, 100, 50, 0, 0, 100, 100, 20, 80, 65, 95, 75, 55, 40, 70, 15, 60, 40, 100, 0, 0, 10, 50]; /// The size of the compact metrics object, in bytes, computed from the keys and values above. -pub const METRICS_BYTES : i32 = 626; +pub const METRICS_BYTES : i32 = 627; pub fn is_digit(ch: u8) -> bool { return ch >= '0' and ch <= '9'; @@ -262,8 +262,8 @@ test the_tally_sums_to_twenty_seven { assert published + verified + drafts + gaps == CELL_COUNT; assert published == 1; assert verified == 16; - assert drafts == 5; - assert gaps == 5; + assert drafts == 6; + assert gaps == 4; } test the_message_is_the_tally { @@ -273,9 +273,9 @@ test the_message_is_the_tally { const n : i32 = @intCast(MSG.len); assert n <= MSG_MAX; // one number wrong, a word changed, or a number added: each refused - assert msg_matches("1 published, 15 verified, 5 drafts, 5 gaps; 54%", STATUS, overall) == false; - assert msg_matches("1 published, 16 verified, 5 drafts, 5 holes; 54%", STATUS, overall) == false; - assert msg_matches("1 published, 16 verified, 5 drafts, 5 gaps; 54% of 100", STATUS, overall) == false; + assert msg_matches("1 published, 15 verified, 6 drafts, 4 gaps; 54%", STATUS, overall) == false; + assert msg_matches("1 published, 16 verified, 6 drafts, 4 holes; 54%", STATUS, overall) == false; + assert msg_matches("1 published, 16 verified, 6 drafts, 4 gaps; 54% of 100", STATUS, overall) == false; assert msg_matches(MSG, STATUS, 48) == false; assert nth_number(MSG, 1) == 16; assert nth_number(MSG, 4) == 54;