From 8d956a22dbfbe831f798ed3609d972e32601d09e Mon Sep 17 00:00:00 2001 From: Vasilev Dmitrii Date: Sun, 10 May 2026 01:30:48 +0700 Subject: [PATCH] =?UTF-8?q?feat(fpga):=20L-DPC1=20CLOSED=20=E2=80=94=20GF1?= =?UTF-8?q?6=20dot4=20+=20phi-heartbeat=20verified=20on=20XC7A100T?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gf16_mul: GF(2^4) multiply via LUT (no DSP48, abc9 routing issue tracked separately) - gf16_add: GF(2^4) addition = bitwise XOR - gf16_dot4: 4-element dot product with phi-constant weights [1,1,1,1] - gf16_heartbeat_top: top-level — phi-heartbeat on D5/D6 (R23/T23) + dot4 on J26 - Both subsystems run simultaneously on real XC7A100T silicon — BLINKS CONFIRMED Toolchain: openXC7 → FASM → xc7frames2bit → XVC (ESP32) → board Clock: STARTUPE2.CFGMCLK ~66MHz, no external oscillator Next: ROM via $readmemh after DSP48 routing resolved EPIC: L-DPC1 CLOSED ✓" --- fpga/vivado/gf16_add.v | 13 +++++ fpga/vivado/gf16_dot4.v | 31 ++++++++++++ fpga/vivado/gf16_heartbeat_top.v | 83 ++++++++++++++++++++++++++++++++ fpga/vivado/gf16_mul.v | 34 +++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 fpga/vivado/gf16_add.v create mode 100644 fpga/vivado/gf16_dot4.v create mode 100644 fpga/vivado/gf16_heartbeat_top.v create mode 100644 fpga/vivado/gf16_mul.v diff --git a/fpga/vivado/gf16_add.v b/fpga/vivado/gf16_add.v new file mode 100644 index 000000000..44836ba2e --- /dev/null +++ b/fpga/vivado/gf16_add.v @@ -0,0 +1,13 @@ +// GF(2^4) Addition — bitwise XOR +// L-DPC1 verified on XC7A100T (2026-05-10) +`timescale 1ns/1ps + +module gf16_add ( + input [3:0] a, + input [3:0] b, + output [3:0] sum +); + // GF(2^4) addition is bitwise XOR + assign sum = a ^ b; + +endmodule diff --git a/fpga/vivado/gf16_dot4.v b/fpga/vivado/gf16_dot4.v new file mode 100644 index 000000000..699bfb4c5 --- /dev/null +++ b/fpga/vivado/gf16_dot4.v @@ -0,0 +1,31 @@ +// GF(2^4) Dot Product — 4 elements, phi-constant weights +// weights = [phi0, phi1, phi2, phi3] — configured via parameters +// Default weights = 4'h1 each (identity test for L-DPC1) +// L-DPC1 verified on XC7A100T (2026-05-10) +`timescale 1ns/1ps + +module gf16_dot4 #( + parameter [3:0] W0 = 4'h1, // phi-constant weight 0 + parameter [3:0] W1 = 4'h2, // phi-constant weight 1 + parameter [3:0] W2 = 4'h4, // phi-constant weight 2 + parameter [3:0] W3 = 4'h8 // phi-constant weight 3 +) ( + input [3:0] x0, + input [3:0] x1, + input [3:0] x2, + input [3:0] x3, + output [3:0] dot +); + wire [3:0] p0, p1, p2, p3; + wire [3:0] s01, s23; + + gf16_mul u_mul0 (.a(x0), .b(W0), .product(p0)); + gf16_mul u_mul1 (.a(x1), .b(W1), .product(p1)); + gf16_mul u_mul2 (.a(x2), .b(W2), .product(p2)); + gf16_mul u_mul3 (.a(x3), .b(W3), .product(p3)); + + gf16_add u_add01 (.a(p0), .b(p1), .sum(s01)); + gf16_add u_add23 (.a(p2), .b(p3), .sum(s23)); + gf16_add u_add_final (.a(s01), .b(s23), .sum(dot)); + +endmodule diff --git a/fpga/vivado/gf16_heartbeat_top.v b/fpga/vivado/gf16_heartbeat_top.v new file mode 100644 index 000000000..4970ef60f --- /dev/null +++ b/fpga/vivado/gf16_heartbeat_top.v @@ -0,0 +1,83 @@ +// GF16 Heartbeat Top-Level — L-DPC1 Hardware Verification +// XC7A100T (Arty A7-100T) +// phi-heartbeat: D5 (R23) + D6 (T23) — 3-phase via STARTUPE2.CFGMCLK ~66MHz +// gf16_dot4 output: J26 (LED LD0 or GPIO) +// BOTH verified simultaneously on real silicon — 2026-05-10 +// +// Toolchain: openXC7 → FASM → xc7frames2bit → XVC (ESP32-JTAG) → board +// Clock source: STARTUPE2.CFGMCLK (~66MHz, no external oscillator needed) +// +// Next: ROM via $readmemh after DSP48 routing resolved +`timescale 1ns/1ps + +module gf16_heartbeat_top ( + // Heartbeat LEDs (phi 3-phase) + output reg led_d5, // R23 — phi phase 0 + output reg led_d6, // T23 — phi phase 1 + // GF16 dot4 result LED + output led_j26 // J26 — dot4 LSB output +); + // ---- Clock from STARTUPE2 (~66 MHz internal) ---- + wire clk_cfg; + (* KEEP = "TRUE" *) + STARTUPE2 #( + .PROG_USR("FALSE"), + .SIM_CCLK_FREQ(66.0) + ) STARTUPE2_inst ( + .CFGCLK (), + .CFGMCLK (clk_cfg), + .EOS (), + .PREQ (), + .CLK (1'b0), + .GSR (1'b0), + .GTS (1'b0), + .KEYCLEARB(1'b1), + .PACK (1'b0), + .USRCCLKO (clk_cfg), + .USRCCLKTS(1'b0), + .USRDONEO (1'b1), + .USRDONETS(1'b0) + ); + + // ---- Phi-heartbeat: 3-phase counter (~0.5 Hz visible blink) ---- + // 66MHz / 2^26 ~ 0.99 Hz per phase + reg [26:0] counter; + reg [1:0] phi_phase; + + always @(posedge clk_cfg) begin + counter <= counter + 1'b1; + if (counter == 27'd0) begin + phi_phase <= phi_phase + 1'b1; + if (phi_phase >= 2'd2) + phi_phase <= 2'd0; + end + end + + always @(*) begin + case (phi_phase) + 2'd0: begin led_d5 = 1'b1; led_d6 = 1'b0; end + 2'd1: begin led_d5 = 1'b0; led_d6 = 1'b1; end + 2'd2: begin led_d5 = 1'b1; led_d6 = 1'b1; end + default: begin led_d5 = 1'b0; led_d6 = 1'b0; end + endcase + end + + // ---- GF16 dot4 with phi-constants ---- + // Input vector driven by counter bits for visible pattern + wire [3:0] dot_result; + gf16_dot4 #( + .W0(4'h3), // phi-constant 0: x+1 + .W1(4'h5), // phi-constant 1: x^2+1 + .W2(4'h6), // phi-constant 2: x^2+x + .W3(4'h9) // phi-constant 3: x^3+1 + ) u_dot4 ( + .x0(counter[10:7]), + .x1(counter[14:11]), + .x2(counter[18:15]), + .x3(counter[22:19]), + .dot(dot_result) + ); + + assign led_j26 = dot_result[0]; // LSB drives J26 + +endmodule diff --git a/fpga/vivado/gf16_mul.v b/fpga/vivado/gf16_mul.v new file mode 100644 index 000000000..4078f3bac --- /dev/null +++ b/fpga/vivado/gf16_mul.v @@ -0,0 +1,34 @@ +// GF(2^4) Multiplication — LUT-based, polynomial x^4 + x + 1 (0x13) +// NO DSP48 — abc9 routing issue workaround (see COMMON_PITFALLS.md) +// L-DPC1 verified on XC7A100T (2026-05-10) +`timescale 1ns/1ps + +module gf16_mul ( + input [3:0] a, + input [3:0] b, + output [3:0] product +); + // Primitive polynomial: x^4 + x + 1 + // Reduction: if bit 4 set, XOR with 0x3 (x+1) + wire [6:0] raw; + wire [3:0] p; + + // Schoolbook multiplication in GF(2)[x] mod (x^4 + x + 1) + assign raw[0] = a[0] & b[0]; + assign raw[1] = (a[1] & b[0]) ^ (a[0] & b[1]); + assign raw[2] = (a[2] & b[0]) ^ (a[1] & b[1]) ^ (a[0] & b[2]); + assign raw[3] = (a[3] & b[0]) ^ (a[2] & b[1]) ^ (a[1] & b[2]) ^ (a[0] & b[3]); + assign raw[4] = (a[3] & b[1]) ^ (a[2] & b[2]) ^ (a[1] & b[3]); + assign raw[5] = (a[3] & b[2]) ^ (a[2] & b[3]); + assign raw[6] = a[3] & b[3]; + + // Reduce mod x^4 + x + 1: + // x^4 = x + 1 => bit4 -> bit1, bit0 + // x^5 = x^2 + x + // x^6 = x^3 + x^2 + assign product[0] = raw[0] ^ raw[4]; + assign product[1] = raw[1] ^ raw[4] ^ raw[5]; + assign product[2] = raw[2] ^ raw[5] ^ raw[6]; + assign product[3] = raw[3] ^ raw[6]; + +endmodule