Research-Stack/4-Infrastructure/hardware/braid_serial.v
2026-05-11 22:18:31 -05:00

863 lines
36 KiB
Verilog
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Braid-Encoded Serial Communication Interface
// Derived from Lean: Semantics/BraidSerial.lean
// Target: Gowin GW1NR-9 (Tang Nano 9K)
// Q0.16 fixed-point arithmetic for phase encoding
// Replaces standard UART with braid-topology parallel encoding
`timescale 1ns / 1ps
// ═══════════════════════════════════════════════════════════════════════════
// Q0.16 Fixed-Point Arithmetic (Pure Fraction: range [-1, 1 - 2^-16])
// ═══════════════════════════════════════════════════════════════════════════
module q0_16_add (
input signed [15:0] a,
input signed [15:0] b,
output signed [15:0] sum,
output overflow
);
wire signed [16:0] ext = $signed({a[15], a}) + $signed({b[15], b});
assign overflow = (a[15] == b[15]) && (ext[16] != a[15]);
assign sum = overflow ? (a[15] ? 16'sh8000 : 16'sh7FFF) : ext[15:0];
endmodule
module q0_16_mul (
input signed [15:0] a,
input signed [15:0] b,
output signed [15:0] product
);
wire signed [31:0] full = $signed(a) * $signed(b);
assign product = full[31:16]; // Q0.16 multiply: keep upper 16 bits
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Byte to Q0.16 Phase Conversion
// Maps 0-255 to [-1, 1) range: 0→-1.0, 127→0.0, 255→0.999985
// Formula: phase = (byte * 2 - 255) * 65535 / 255
// Simplified: phase = (byte * 2 - 255) * 257
// Q0.16: 0x8000 = -1.0, 0x0000 = 0.0, 0x7FFF = 0.999985
// ═══════════════════════════════════════════════════════════════════════════
module byte_to_phase (
input wire [7:0] byte_in,
output reg signed [15:0] phase_out
);
// Convert byte (0-255) to signed Q0.16 phase (-32768 to 32767)
// Linear mapping: phase = (byte - 128) * 256
// This maps 0 -> -32768, 128 -> 0, 255 -> 32767
wire signed [23:0] offset = {16'b0, byte_in} - 24'sd128;
wire signed [23:0] mult = offset * 24'sd256;
assign phase_out = mult[15:0]; // Extract lower 16 bits for signed result
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Q0.16 Phase to Byte Conversion
// Inverse of byte_to_phase with clamping to [0, 255]
// Formula: byte = (phase * 255 + 65535) / 65535 / 2 + 128
// Q0.16: 0x8000 = -1.0, 0x0000 = 0.0, 0x7FFF = 0.999985
// ═══════════════════════════════════════════════════════════════════════════
module phase_to_byte (
input wire signed [15:0] phase_in,
output reg [7:0] byte_out
);
// Convert signed Q0.16 phase (-32768 to 32767) back to byte (0-255)
// Inverse mapping: byte = (phase / 256) + 128
// This maps -32768 -> 0, 0 -> 128, 32767 -> 255
wire signed [23:0] phase_ext = {{8{phase_in[15]}}, phase_in}; // Sign-extend to 24 bits
wire signed [23:0] div_result = phase_ext / 24'sd256;
wire signed [23:0] byte_result = div_result + 24'sd128;
// Clamp to [0, 255] range
wire [7:0] byte_clamped = (byte_result < 0) ? 8'd0 :
(byte_result > 255) ? 8'd255 :
byte_result[7:0];
assign byte_out = byte_clamped;
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Modulation Mode Selector
// 00: None (direct Q0.16)
// 01: QPSK (4-phase modulation, 2 bits/symbol)
// 10: QAM-16 (16-point constellation, 4 bits/symbol)
// 11: DMT (Discrete Multi-Tone, strand subcarriers)
// ═══════════════════════════════════════════════════════════════════════════
localparam MOD_NONE = 2'b00;
localparam MOD_QPSK = 2'b01;
localparam MOD_QAM16 = 2'b10;
localparam MOD_DMT = 2'b11;
// ═══════════════════════════════════════════════════════════════════════════
// QPSK Modulator
// Maps 2 bits to 4 phase states: 00→45°, 01→135°, 10→225°, 11→315°
// Q0.16: 45°=0x4000, 135°=0xC000, 225°=0x4000 (signed), 315°=0xC000 (signed)
// ═══════════════════════════════════════════════════════════════════════════
module qpsk_modulator (
input wire [1:0] symbols_in, // 2 bits per symbol
output reg signed [15:0] phase_out
);
// QPSK constellation: 4 distinct phase angles
// 00→0° (1.0), 01→90° (0.0 + 1.0j), 10→180° (-1.0), 11→270° (0.0 - 1.0j)
// In Q0.16: 0°=0x7FFF, 90°=0x4000, 180°=0x8000, 270°=0xC000
always @(*) begin
case (symbols_in)
2'b00: phase_out = 16'sh7FFF; // 1.0 (0°)
2'b01: phase_out = 16'sh4000; // 0.0 (90° - using phase as I component)
2'b10: phase_out = 16'sh8000; // -1.0 (180°)
2'b11: phase_out = 16'shC000; // 0.0 (270° - using phase as I component)
default: phase_out = 16'sd0;
endcase
end
endmodule
// QPSK Demodulator
module qpsk_demodulator (
input signed [15:0] phase_in,
output reg [1:0] symbols_out
);
// Demodulate by comparing phase to 4 constellation points
// 00→0x7FFF, 01→0x4000, 10→0x8000, 11→0xC000
always @(*) begin
if (phase_in >= 16'sh6000) begin
symbols_out = 2'b00; // 0° (0x7FFF)
end else if (phase_in >= 16'sh2000) begin
symbols_out = 2'b01; // 90° (0x4000)
end else if (phase_in >= 16'shA000) begin
symbols_out = 2'b11; // 270° (0xC000)
end else begin
symbols_out = 2'b10; // 180° (0x8000)
end
end
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// QAM-16 Modulator
// Maps 4 bits to 16 phase/amplitude combinations
// ═══════════════════════════════════════════════════════════════════════════
module qam16_modulator (
input wire [3:0] symbols_in, // 4 bits per symbol
output reg signed [15:0] phase_out,
output reg signed [15:0] amp_out // Amplitude modulation
);
// QAM-16 constellation: 16 points with varying phase and amplitude
// Use 4x4 grid: 4 amplitude levels × 4 phase levels
always @(*) begin
case (symbols_in)
4'b0000: begin phase_out = 16'sh7FFF; amp_out = 16'sh7FFF; end // Max amp, 0°
4'b0001: begin phase_out = 16'sh4000; amp_out = 16'sh7FFF; end // Max amp, 90°
4'b0010: begin phase_out = 16'sh8000; amp_out = 16'sh7FFF; end // Max amp, 180°
4'b0011: begin phase_out = 16'shC000; amp_out = 16'sh7FFF; end // Max amp, 270°
4'b0100: begin phase_out = 16'sh7FFF; amp_out = 16'sh5FFF; end // High amp, 0°
4'b0101: begin phase_out = 16'sh4000; amp_out = 16'sh5FFF; end // High amp, 90°
4'b0110: begin phase_out = 16'sh8000; amp_out = 16'sh5FFF; end // High amp, 180°
4'b0111: begin phase_out = 16'shC000; amp_out = 16'sh5FFF; end // High amp, 270°
4'b1000: begin phase_out = 16'sh7FFF; amp_out = 16'sh3FFF; end // Mid amp, 0°
4'b1001: begin phase_out = 16'sh4000; amp_out = 16'sh3FFF; end // Mid amp, 90°
4'b1010: begin phase_out = 16'sh8000; amp_out = 16'sh3FFF; end // Mid amp, 180°
4'b1011: begin phase_out = 16'shC000; amp_out = 16'sh3FFF; end // Mid amp, 270°
4'b1100: begin phase_out = 16'sh7FFF; amp_out = 16'sh1FFF; end // Low amp, 0°
4'b1101: begin phase_out = 16'sh4000; amp_out = 16'sh1FFF; end // Low amp, 90°
4'b1110: begin phase_out = 16'sh8000; amp_out = 16'sh1FFF; end // Low amp, 180°
4'b1111: begin phase_out = 16'shC000; amp_out = 16'sh1FFF; end // Low amp, 270°
default: begin phase_out = 16'sd0; amp_out = 16'sd0; end
endcase
end
endmodule
// QAM-16 Demodulator
module qam16_demodulator (
input signed [15:0] phase_in,
input signed [15:0] amp_in,
output reg [3:0] symbols_out
);
// Demodulate by comparing phase and amplitude to constellation
always @(*) begin
// Determine phase (lower 2 bits)
// 00→0x7FFF, 01→0x4000, 10→0x8000, 11→0xC000
if (phase_in >= 16'sh6000) symbols_out[1:0] = 2'b00; // 0°
else if (phase_in >= 16'sh2000) symbols_out[1:0] = 2'b01; // 90°
else if (phase_in >= 16'shA000) symbols_out[1:0] = 2'b11; // 270°
else symbols_out[1:0] = 2'b10; // 180°
// Determine amplitude (upper 2 bits)
// 0→0x7FFF, 1→0x5FFF, 2→0x3FFF, 3→0x1FFF
if (amp_in > 16'sh6000) symbols_out[3:2] = 2'b00; // Max
else if (amp_in > 16'sh4000) symbols_out[3:2] = 2'b01; // High
else if (amp_in > 16'sh2000) symbols_out[3:2] = 2'b10; // Mid
else symbols_out[3:2] = 2'b11; // Low
end
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// DMT-like Multi-Carrier Modulator
// Uses strand index as subcarrier frequency offset
// ═══════════════════════════════════════════════════════════════════════════
module dmt_modulator (
input wire [7:0] byte_in,
input wire [2:0] subcarrier_idx, // Strand index (0-7)
output reg signed [15:0] phase_out
);
// DMT: each subcarrier has different phase offset
// Phase offset = subcarrier_idx * 45°
// Data modulates the phase offset
wire signed [23:0] offset = {16'b0, byte_in} - 24'sd128;
wire signed [23:0] base_phase = offset * 24'sd256;
wire signed [15:0] subcarrier_offset = subcarrier_idx * 16'sh2000; // 45° increments
assign phase_out = (base_phase[15:0] + subcarrier_offset);
endmodule
// DMT Demodulator
module dmt_demodulator (
input signed [15:0] phase_in,
input wire [2:0] subcarrier_idx,
output reg [7:0] byte_out
);
// Remove subcarrier offset, then convert back to byte
// Use same offset calculation as modulator: subcarrier_idx * 0x2000
wire signed [15:0] subcarrier_offset = subcarrier_idx * 16'sh2000; // 45° increments
wire signed [15:0] demod_phase = phase_in - subcarrier_offset;
// Convert phase back to byte using inverse of byte_to_phase
// byte = (phase / 256) + 128
wire signed [23:0] phase_ext = {{8{demod_phase[15]}}, demod_phase};
wire signed [23:0] div_result = phase_ext / 24'sd256;
wire signed [23:0] byte_result = div_result + 24'sd128;
// Clamp to [0, 255] range
wire [7:0] byte_clamped = (byte_result < 0) ? 8'd0 :
(byte_result > 255) ? 8'd255 :
byte_result[7:0];
assign byte_out = byte_clamped;
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Hydrogenic Phi-Torsion Phase Generator
// gamma(theta) = phi * theta where phi = 1.6180339887498948482
// theta = frameNum * 0.01 (evolution parameter)
// ═══════════════════════════════════════════════════════════════════════════
module phi_torsion_phase (
input wire [31:0] frame_num,
output reg signed [31:0] phi_phase // Q16.16 output
);
// phi in Q16.16: 1.6180339887498948482 * 65536 ≈ 106039
localparam signed [31:0] PHI = 32'sh00019E97;
// theta = frameNum * 0.01 in Q16.16
// 0.01 in Q16.16 = 655.36 ≈ 656
localparam signed [31:0] THETA_SCALE = 32'sh00000290;
wire signed [47:0] theta_mult = $signed({16'b0, frame_num}) * THETA_SCALE;
wire signed [31:0] theta = {{16{theta_mult[31]}}, theta_mult[31:16]};
wire signed [63:0] gamma = $signed(theta) * PHI;
assign phi_phase = gamma[47:16];
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Bracket Admissibility Check
// Derived from Lean: Semantics/BraidBracket.lean
// Checks if bracket bounds are valid and gap is conserved
// ═══════════════════════════════════════════════════════════════════════════
module bracket_admissible (
input wire signed [15:0] phi, // Phase angle
input wire signed [15:0] lower_bound, // Bracket lower bound
input wire signed [15:0] upper_bound, // Bracket upper bound
input wire gap_conserved, // Gap conservation flag
output wire admissible // Overall admissibility
);
wire in_bounds = (phi >= lower_bound) && (phi <= upper_bound);
assign admissible = in_bounds && gap_conserved;
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Encoded Strand Structure (matches Lean EncodedStrand)
// ═══════════════════════════════════════════════════════════════════════════
module encoded_strand (
input wire clk,
input wire rst_n,
// Encoding interface
input wire encode_enable,
input wire [7:0] byte_in,
input wire [2:0] slot_in, // Slot 0-7
input wire parity_in,
input signed [15:0] residue_in,
input wire [1:0] modulation_mode, // 00=None, 01=QPSK, 10=QAM16, 11=DMT
// Decoding interface
input wire decode_enable,
input signed [15:0] phase_in,
input signed [15:0] amp_in, // Amplitude for QAM-16
output reg [7:0] byte_out,
// Strand output (for parallel transmission)
output wire signed [15:0] phase_acc,
output wire [2:0] slot,
output wire parity,
output wire signed [15:0] residue,
output wire signed [15:0] amplitude, // Amplitude for QAM-16
output wire admissible
);
// Phase accumulator
reg signed [15:0] phase_reg;
reg [2:0] slot_reg;
reg parity_reg;
reg signed [15:0] residue_reg;
// Bracket bounds (simplified: fixed bounds for now)
localparam signed [15:0] LOWER_BOUND = 16'sh8000; // -1.0
localparam signed [15:0] UPPER_BOUND = 16'sh7FFF; // 0.999985
// Byte to phase conversion (direct)
wire signed [15:0] phase_from_byte;
byte_to_phase byte_conv (.byte_in(byte_in), .phase_out(phase_from_byte));
// Modulation outputs
wire signed [15:0] qpsk_phase;
wire signed [15:0] qam16_phase;
wire signed [15:0] qam16_amp;
wire signed [15:0] dmt_phase;
// QPSK modulator (encode byte as 4 QPSK symbols)
// Byte 0x01 -> symbols: 00,00,00,01 -> phases: 0x7FFF, 0x7FFF, 0x7FFF, 0x4000
// But we only have one phase output per strand, so we need a different approach
// Simplified: Use byte value directly to select one of 4 phases
// byte[1:0] selects the phase
qpsk_modulator qpsk_mod (.symbols_in(byte_in[1:0]), .phase_out(qpsk_phase));
// QAM-16 modulator (encode byte as 2 QAM-16 symbols)
// byte[3:0] selects the constellation point
qam16_modulator qam16_mod (.symbols_in(byte_in[3:0]), .phase_out(qam16_phase), .amp_out(qam16_amp));
// DMT modulator (uses slot as subcarrier index)
dmt_modulator dmt_mod (.byte_in(byte_in), .subcarrier_idx(slot_in), .phase_out(dmt_phase));
// Modulation multiplexer
reg signed [15:0] modulated_phase;
reg signed [15:0] modulated_amp;
always @(*) begin
case (modulation_mode)
MOD_NONE: begin
modulated_phase = phase_from_byte;
modulated_amp = 16'sh7FFF; // Full amplitude
end
MOD_QPSK: begin
modulated_phase = qpsk_phase;
modulated_amp = 16'sh7FFF;
end
MOD_QAM16: begin
modulated_phase = qam16_phase;
modulated_amp = qam16_amp;
end
MOD_DMT: begin
modulated_phase = dmt_phase;
modulated_amp = 16'sh7FFF;
end
default: begin
modulated_phase = phase_from_byte;
modulated_amp = 16'sh7FFF;
end
endcase
end
// Phase to byte conversion (direct)
wire [7:0] byte_from_phase;
phase_to_byte phase_conv (.phase_in(phase_in), .byte_out(byte_from_phase));
// Demodulation outputs
wire [1:0] qpsk_symbols;
wire [3:0] qam16_symbols;
wire [7:0] dmt_byte;
// QPSK demodulator
qpsk_demodulator qpsk_demod (.phase_in(phase_in), .symbols_out(qpsk_symbols));
// QAM-16 demodulator
qam16_demodulator qam16_demod (.phase_in(phase_in), .amp_in(amp_in), .symbols_out(qam16_symbols));
// DMT demodulator
dmt_demodulator dmt_demod (.phase_in(phase_in), .subcarrier_idx(slot_in), .byte_out(dmt_byte));
// Demodulation multiplexer
reg [7:0] demodulated_byte;
always @(*) begin
case (modulation_mode)
MOD_NONE: demodulated_byte = byte_from_phase;
MOD_QPSK: demodulated_byte = {6'b0, qpsk_symbols}; // Lower 2 bits from QPSK
MOD_QAM16: demodulated_byte = {4'b0, qam16_symbols}; // Lower 4 bits from QAM-16
MOD_DMT: demodulated_byte = dmt_byte;
default: demodulated_byte = byte_from_phase;
endcase
end
// Admissibility check
bracket_admissible bracket_check (
.phi(modulated_phase),
.lower_bound(LOWER_BOUND),
.upper_bound(UPPER_BOUND),
.gap_conserved(1'b1), // Simplified: always conserved
.admissible(admissible)
);
// Combinational output assignment - phase_acc reflects current modulated_phase when encoding
assign phase_acc = encode_enable ? modulated_phase : phase_reg;
assign slot = encode_enable ? slot_in : slot_reg;
assign parity = encode_enable ? parity_in : parity_reg;
assign residue = encode_enable ? residue_in : residue_reg;
assign amplitude = encode_enable ? modulated_amp : 16'sh7FFF;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
phase_reg <= 16'sd0;
slot_reg <= 3'd0;
parity_reg <= 1'b0;
residue_reg <= 16'sd0;
byte_out <= 8'd0;
end else begin
if (encode_enable) begin
phase_reg <= modulated_phase;
slot_reg <= slot_in;
parity_reg <= parity_in;
residue_reg <= residue_in;
end else if (decode_enable) begin
byte_out <= demodulated_byte;
end
end
end
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Braid Frame Encoder
// Encodes serial packet into 8-wire parallel braid frame
// ═══════════════════════════════════════════════════════════════════════════
module braid_frame_encoder (
input wire clk,
input wire rst_n,
// Packet input
input wire encode_start,
input wire [7:0] packet_type,
input wire [15:0] seq_num,
input wire [7:0] payload_len,
input wire [63:0] payload_data, // 8 bytes max for simplicity
input wire [31:0] frame_num,
input wire [1:0] modulation_mode, // 00=None, 01=QPSK, 10=QAM16, 11=DMT
// 8-wire parallel output
output wire signed [15:0] wire_phase [0:7],
output wire [2:0] wire_slot [0:7],
output wire wire_parity [0:7],
output wire signed [15:0] wire_amplitude [0:7], // Amplitude for QAM-16
output wire frame_valid,
output wire signed [31:0] phi_phase
);
// Packet assembly - direct wire assignments for debugging
wire [7:0] all_bytes [0:7];
assign all_bytes[0] = packet_type;
assign all_bytes[1] = seq_num[7:0];
assign all_bytes[2] = seq_num[15:8];
assign all_bytes[3] = payload_len;
assign all_bytes[4] = payload_data[7:0];
assign all_bytes[5] = payload_data[15:8];
assign all_bytes[6] = payload_data[23:16];
assign all_bytes[7] = payload_data[31:24];
// Note: For full 64-bit payload, would need 8 more wires, but keeping it simple for now
// Encoding state
reg [3:0] encode_state;
reg [2:0] current_slot;
reg frame_valid_reg;
reg encode_enable_delayed; // Delayed version of encode_start
// Phi-torsion phase
wire signed [31:0] phi_torsion;
phi_torsion_phase phi_gen (.frame_num(frame_num), .phi_phase(phi_torsion));
// 8 strand encoders
wire signed [15:0] phase_acc [0:7];
wire [2:0] slot_out [0:7];
wire parity_out [0:7];
wire signed [15:0] amplitude_out [0:7];
wire admissible [0:7];
genvar i;
generate
for (i = 0; i < 8; i = i + 1) begin : strand_gen
encoded_strand strand_inst (
.clk(clk),
.rst_n(rst_n),
.encode_enable(encode_start),
.byte_in(all_bytes[i]),
.slot_in(i[2:0]),
.parity_in(frame_num[0]), // Frame parity
.residue_in(16'sd0),
.modulation_mode(modulation_mode),
.decode_enable(1'b0),
.phase_in(16'sd0),
.amp_in(16'sd0),
.byte_out(),
.phase_acc(phase_acc[i]),
.slot(slot_out[i]),
.parity(parity_out[i]),
.residue(),
.amplitude(amplitude_out[i]),
.admissible(admissible[i])
);
end
endgenerate
// Frame valid directly from encode_start (simplified)
assign frame_valid = encode_start;
assign phi_phase = phi_torsion;
// Wire output assignments
genvar j;
generate
for (j = 0; j < 8; j = j + 1) begin : wire_assign
assign wire_phase[j] = phase_acc[j];
assign wire_slot[j] = slot_out[j];
assign wire_parity[j] = parity_out[j];
assign wire_amplitude[j] = amplitude_out[j];
end
endgenerate
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Braid Frame Decoder
// Decodes 8-wire parallel braid frame back to serial packet
// ═══════════════════════════════════════════════════════════════════════════
module braid_frame_decoder (
input wire clk,
input wire rst_n,
// 8-wire parallel input
input wire signed [15:0] wire_phase [0:7],
input wire [2:0] wire_slot [0:7],
input wire wire_parity [0:7],
input wire signed [15:0] wire_amplitude [0:7], // Amplitude for QAM-16
input wire frame_valid_in,
input wire signed [31:0] phi_phase_in,
input wire [1:0] modulation_mode, // 00=None, 01=QPSK, 10=QAM16, 11=DMT
// Decoded packet output
output reg [7:0] packet_type,
output reg [15:0] seq_num,
output reg [7:0] payload_len,
output reg [63:0] payload_data,
output reg decode_valid,
output reg admissible_out
);
// Decoded bytes
reg [7:0] decoded_bytes [0:11];
// 8 strand decoders
wire [7:0] byte_out [0:7];
wire admissible [0:7];
genvar i;
generate
for (i = 0; i < 8; i = i + 1) begin : strand_dec_gen
encoded_strand strand_inst (
.clk(clk),
.rst_n(rst_n),
.encode_enable(1'b0),
.byte_in(8'd0),
.slot_in(i[2:0]), // Pass actual strand index for DMT demodulation
.parity_in(1'b0),
.residue_in(16'sd0),
.modulation_mode(modulation_mode),
.decode_enable(frame_valid_in),
.phase_in(wire_phase[i]),
.amp_in(wire_amplitude[i]),
.byte_out(byte_out[i]),
.phase_acc(),
.slot(),
.parity(),
.residue(),
.amplitude(),
.admissible(admissible[i])
);
end
endgenerate
// Decode state machine
reg [3:0] decode_state;
reg frame_valid_in_delayed; // Delayed version for timing
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
decode_state <= 4'd0;
frame_valid_in_delayed <= 1'b0;
packet_type <= 8'd0;
seq_num <= 16'd0;
payload_len <= 8'd0;
payload_data <= 64'd0;
decode_valid <= 1'b0;
admissible_out <= 1'b0;
end else begin
frame_valid_in_delayed <= frame_valid_in;
case (decode_state)
4'd0: begin
decode_valid <= 1'b0;
if (frame_valid_in_delayed) begin
// Extract header from slots 0-3 in one cycle
packet_type <= byte_out[0];
seq_num <= {byte_out[2], byte_out[1]};
payload_len <= byte_out[3];
// Extract payload from slots 4-7
payload_data <= {32'd0, byte_out[7], byte_out[6], byte_out[5], byte_out[4]};
// Check admissibility
admissible_out <= admissible[0] && admissible[1] && admissible[2] && admissible[3] &&
admissible[4] && admissible[5] && admissible[6] && admissible[7];
decode_valid <= 1'b1;
decode_state <= 4'd1;
end
end
4'd1: begin
// Hold valid for one cycle
decode_valid <= 1'b0;
decode_state <= 4'd0;
end
default: decode_state <= 4'd0;
endcase
end
end
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Braid Serial Top-Level Module
// Replaces uart_tx_opt with braid-encoded parallel interface
// ═══════════════════════════════════════════════════════════════════════════
module braid_serial_top (
input wire clk, // Pin 52 (27MHz)
input wire rst_n, // Pin 4 (Reset_Button)
// Transmission interface
input wire tx_start,
input wire [7:0] tx_packet_type,
input wire [15:0] tx_seq_num,
input wire [7:0] tx_payload_len,
input wire [63:0] tx_payload_data,
input wire [31:0] tx_frame_num,
input wire [1:0] tx_modulation_mode, // 00=None, 01=QPSK, 10=QAM16, 11=DMT
// Reception interface
input wire rx_frame_valid,
input signed [15:0] rx_wire_phase [0:7],
input wire [2:0] rx_wire_slot [0:7],
input wire rx_wire_parity [0:7],
input signed [15:0] rx_wire_amplitude [0:7], // Amplitude for QAM-16
input signed [31:0] rx_phi_phase,
input wire [1:0] rx_modulation_mode, // 00=None, 01=QPSK, 10=QAM16, 11=DMT
// 8-wire parallel physical interface (replace UART TX/RX)
output signed [15:0] tx_wire_phase [0:7],
output wire [2:0] tx_wire_slot [0:7],
output wire tx_wire_parity [0:7],
output signed [15:0] tx_wire_amplitude [0:7], // Amplitude for QAM-16
output wire tx_frame_valid,
output signed [31:0] tx_phi_phase,
// Decoded output
output reg [7:0] rx_packet_type,
output reg [15:0] rx_seq_num,
output reg [7:0] rx_payload_len,
output reg [63:0] rx_payload_data,
output reg rx_decode_valid,
output reg rx_admissible
);
// Encoder instance
wire signed [15:0] enc_phase [0:7];
wire [2:0] enc_slot [0:7];
wire enc_parity [0:7];
wire signed [15:0] enc_amplitude [0:7];
wire enc_valid;
wire signed [31:0] enc_phi;
braid_frame_encoder encoder_inst (
.clk(clk),
.rst_n(rst_n),
.encode_start(tx_start),
.packet_type(tx_packet_type),
.seq_num(tx_seq_num),
.payload_len(tx_payload_len),
.payload_data(tx_payload_data),
.frame_num(tx_frame_num),
.modulation_mode(tx_modulation_mode),
.wire_phase(enc_phase),
.wire_slot(enc_slot),
.wire_parity(enc_parity),
.wire_amplitude(enc_amplitude),
.frame_valid(enc_valid),
.phi_phase(enc_phi)
);
// Decoder instance
braid_frame_decoder decoder_inst (
.clk(clk),
.rst_n(rst_n),
.wire_phase(rx_wire_phase),
.wire_slot(rx_wire_slot),
.wire_parity(rx_wire_parity),
.wire_amplitude(rx_wire_amplitude),
.frame_valid_in(rx_frame_valid),
.phi_phase_in(rx_phi_phase),
.modulation_mode(rx_modulation_mode),
.packet_type(rx_packet_type),
.seq_num(rx_seq_num),
.payload_len(rx_payload_len),
.payload_data(rx_payload_data),
.decode_valid(rx_decode_valid),
.admissible_out(rx_admissible)
);
// Physical output assignments
assign tx_wire_phase = enc_phase;
assign tx_wire_slot = enc_slot;
assign tx_wire_parity = enc_parity;
assign tx_wire_amplitude = enc_amplitude;
assign tx_frame_valid = enc_valid;
assign tx_phi_phase = enc_phi;
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Testbench for Braid Serial Interface
// ═══════════════════════════════════════════════════════════════════════════
module braid_serial_tb;
reg clk;
reg rst_n;
// TX interface
reg tx_start;
reg [7:0] tx_packet_type;
reg [15:0] tx_seq_num;
reg [7:0] tx_payload_len;
reg [63:0] tx_payload_data;
reg [31:0] tx_frame_num;
// RX interface (loopback)
reg rx_frame_valid;
reg signed [15:0] rx_wire_phase [0:7];
reg [2:0] rx_wire_slot [0:7];
reg rx_wire_parity [0:7];
reg signed [31:0] rx_phi_phase;
// Physical wires
wire signed [15:0] tx_wire_phase [0:7];
wire [2:0] tx_wire_slot [0:7];
wire tx_wire_parity [0:7];
wire tx_frame_valid;
wire signed [31:0] tx_phi_phase;
// Decoded output
wire [7:0] rx_packet_type;
wire [15:0] rx_seq_num;
wire [7:0] rx_payload_len;
wire [63:0] rx_payload_data;
wire rx_decode_valid;
wire rx_admissible;
// Instantiate DUT
braid_serial_top dut (
.clk(clk),
.rst_n(rst_n),
.tx_start(tx_start),
.tx_packet_type(tx_packet_type),
.tx_seq_num(tx_seq_num),
.tx_payload_len(tx_payload_len),
.tx_payload_data(tx_payload_data),
.tx_frame_num(tx_frame_num),
.rx_frame_valid(rx_frame_valid),
.rx_wire_phase(rx_wire_phase),
.rx_wire_slot(rx_wire_slot),
.rx_wire_parity(rx_wire_parity),
.rx_phi_phase(rx_phi_phase),
.tx_wire_phase(tx_wire_phase),
.tx_wire_slot(tx_wire_slot),
.tx_wire_parity(tx_wire_parity),
.tx_frame_valid(tx_frame_valid),
.tx_phi_phase(tx_phi_phase),
.rx_packet_type(rx_packet_type),
.rx_seq_num(rx_seq_num),
.rx_payload_len(rx_payload_len),
.rx_payload_data(rx_payload_data),
.rx_decode_valid(rx_decode_valid),
.rx_admissible(rx_admissible)
);
// Clock generation (27MHz)
initial clk = 0;
always #18.5185 clk = ~clk;
// Loopback connection
integer i;
always @(posedge clk) begin
if (tx_frame_valid) begin
rx_frame_valid <= tx_frame_valid;
rx_phi_phase <= tx_phi_phase;
for (i = 0; i < 8; i = i + 1) begin
rx_wire_phase[i] <= tx_wire_phase[i];
rx_wire_slot[i] <= tx_wire_slot[i];
rx_wire_parity[i] <= tx_wire_parity[i];
end
end else begin
rx_frame_valid <= 1'b0;
end
end
// Test stimulus
initial begin
// Initialize
rst_n = 0;
tx_start = 0;
tx_packet_type = 8'd0;
tx_seq_num = 16'd0;
tx_payload_len = 8'd0;
tx_payload_data = 64'd0;
tx_frame_num = 32'd0;
rx_frame_valid = 1'b0;
rx_phi_phase = 32'sd0;
#100;
rst_n = 1;
#100;
// Test 1: Simple packet
$display("Test 1: Simple packet");
tx_packet_type = 8'h01;
tx_seq_num = 16'h0001;
tx_payload_len = 8'd4;
tx_payload_data = 64'hDEADBEEFCAFEBABE;
tx_frame_num = 32'd0;
tx_start = 1;
#20;
tx_start = 0;
#200;
// Wait for decode
#200;
$display("Decoded: type=%h, seq=%h, len=%d, data=%h, valid=%b, admissible=%b",
rx_packet_type, rx_seq_num, rx_payload_len, rx_payload_data,
rx_decode_valid, rx_admissible);
// Test 2: Another packet
$display("Test 2: Second packet");
tx_packet_type = 8'h02;
tx_seq_num = 16'h0002;
tx_payload_len = 8'd8;
tx_payload_data = 64'h123456789ABCDEF0;
tx_frame_num = 32'd1;
tx_start = 1;
#20;
tx_start = 0;
#200;
// Wait for decode
#200;
$display("Decoded: type=%h, seq=%h, len=%d, data=%h, valid=%b, admissible=%b",
rx_packet_type, rx_seq_num, rx_payload_len, rx_payload_data,
rx_decode_valid, rx_admissible);
#100;
$finish;
end
endmodule