How My One-Pin FPGA Weaver Transmitter Combines I and Q

July 27, 2026

One-pin Weaver radio · exact signal path

My Physical27 transmitter is four quadrature mixers followed by a phase-domain summator. Two baseband streams—SDM(I) and SDM(−Q)—undergo the first +7.5 MHz quadrature Weaver conversion. The resulting two one-bit signals control a four-state selector that realizes the two 216 MHz carrier mixers and their sum on N16.

The phase-domain summator emits one RF phase, not separate analog I and Q voltages. N16 switches that result high or high impedance, and the resonant network turns the timed charge impulses into the narrowband waveform.

4 mixerstwo quadrature pairs
2 × 1 bitintermediate I/Q signals
4 statesfinal RF selector
1 pinphase-domain sum
Physical27 Weaver path with two baseband I/Q sigma-delta streams, a first quadrature mixer pair, two one-bit intermediate signals, and a four-state selector that performs the phase-domain sum of the two 216 megahertz carrier mixers on N16
The active v10 Weaver transformation: four quadrature mixers followed by a phase-domain summator that puts their final sum on one RF pin.

Four Quadrature Mixers Feed One Phase-Domain Summator

A Weaver transmitter contains two quadrature mixer pairs: the first converts baseband I/Q to an intermediate frequency, and the second converts those two results to RF. That is four mixer products. My RTL realizes their signs as coordinated one-bit rotations instead of four standalone multiplier blocks.

Let Q′ denote the selected Q polarity; Physical27 uses Q′ = −Q. For each +7.5 MHz quadrant, the first mixer pair produces (I,Q′), (Q′,−I), (−I,−Q′), or (−Q′,I). These two one-bit intermediate signals control the four-state selector. Each selector state performs the phase-domain sum of the second two mixer products at 216 MHz.

Using the sign convention that reinforces the upper branch, the continuous-time equivalent is I1 = I·cos(IF) − Q′·sin(IF), Q1 = I·sin(IF) + Q′·cos(IF), and s = I1·cos(LO) − Q1·sin(LO). Expanding the products leaves I·cos(LO+IF) − Q′·sin(LO+IF); the LO−IF cross-products cancel.

For Physical27, the 216 MHz carrier, +7.5 MHz whole-word rotation, and +62.5 kHz lane rotation place the wanted signal at 223.5625 MHz. The corresponding true RF conjugate image lies at 208.4375 MHz. The complete physical path measured the wanted lane 29.44 dB above that image.

Measured wanted 223.5625 megahertz lane and true 208.4375 megahertz conjugate image with a 29.44 decibel complete-path ratio
The narrow Physical27/28 wanted and true-image tunings. The tank, cable, pad, tuner, receiver response, and time between sequential captures remain in the 29.44 dB ratio.

Lane 62 Turns One PRBS State Into a Complex Vector

I generate the payload in the FPGA. The 16-bit register starts at 0x1D2B. At each symbol advance, its new low bit is p15 ^ p13 ^ p12 ^ p10:

shared_prbs_q <= {shared_prbs_q[14:0],
    shared_prbs_q[15] ^ shared_prbs_q[13]
    ^ shared_prbs_q[12] ^ shared_prbs_q[10]};

Lane 62 specializes the generated tap formula to {p5 ^ 1, p7}. I assign those two bits to four cardinal states: 0=+I, 1=+Q, 2=−I, and 3=−Q. A symbol therefore adds 0, 8, 16, or 24 to the lane’s five-bit phase, which is exactly a 0°, 90°, 180°, or 270° turn around its 32-point complex waveform.

00

Positive I: the unrotated complex lane.

01

Positive Q: one quarter-turn forward.

10

Negative I: one half-turn.

11

Negative Q: three quarter-turns.

The lane machinery runs at 108 MHz and streams each signed word least-significant bit first across 16 clocks, so its complex sample rate is 6.75 MS/s. In the 25 kHz plan, lane 62 contributes a +62.5 kHz rotation. The QPSK state advances once per 2,048 words, giving 108 MHz / 16 / 2048 = 3,295.8984375 symbols/s.

The five-bit phase selects one of 32 signed I/Q points whose magnitude and initial phase come from compile-time tank-preemphasis constants. Physical27 enables the 17-sample serial moving sum, reconstructs its signed result, divides by 16 with an arithmetic shift, and applies the single-lane gain shift of seven. No host waveform file and no runtime wide multiplier sit in this path.

The PRBS register advances on a symbol tick, while the lane registers consume that advanced state on the following word boundary. I carry a pending flag across that boundary and increment the payload epoch only when the new symbol is actually applied. That small timing distinction is what lets the telemetry name the symbol driving the physical lane rather than a nearby PRBS state.

Read the active lane, PRBS, moving-sum, SDM, and Gray-rotation RTL. The PRBS transition is at lines 394–425, the applied lane symbol at 481–524, and the 32-point waveform selection at 545–598.

Signed I and Q Become Two One-Bit Sigma-Delta Streams

The lane finishes as ordinary signed 16-bit I and Q samples. Each component then enters its own first-order sigma-delta modulator. The modulator decides from the sign of its accumulated error, emits that decision, and subtracts the matching positive or negative full-scale feedback from error + sample:

wire quantizer_d = !error_q[17];
wire signed [18:0] feedback_d = quantizer_d
    ? 19'sd32768 : -19'sd32768;
wire signed [18:0] corrected_d =
    $signed({error_q[17], error_q})
    + $signed({{3{sample_i[15]}}, sample_i})
    - feedback_d;

The same quantizer_d becomes both the emitted bit and the feedback choice for that clock. There is no one-cycle disagreement between what leaves the modulator and what its error accumulator believes it emitted. Over time, the density and pattern of ones carry the signed sample while quantization error is pushed into the switching sequence.

Physical27 inverts Q before the second modulator. After that point the datapath contains only i_sdm_bit and q_sdm_bit, plus their internal error states for telemetry. These are not two analog outputs. They are a two-bit complex phase word waiting to be rotated and mapped onto the RF clocks.

The +7.5 MHz DDS Implements the First Mixer Pair

A 216 MHz phase accumulator advances by 5 modulo 144. Its frequency is therefore 216 MHz × 5 / 144 = 7.5 MHz. Thresholds at 36, 72, and 108 divide that accumulator into four equal quadrants.

Each quadrant permutes or inverts both SDM bits together:

DDS quadrantSelector wordComplex operation
0(I, Q)No additional turn
1(Q, ~I)Quarter-turn
2(~I, ~Q)Half-turn
3(~Q, I)Three-quarter-turn

This whole-word rotation is the first quadrature mixer pair. I do not process I and Q in separate time slots and hope their phase bookkeeping still agrees later. One DDS quadrant transforms the complete two-bit state on every 216 MHz edge. Physical27 leaves the later optional 90° summator rotation disabled.

The arithmetic also makes the target frequency easy to follow: lane 62 contributes +62.5 kHz, the common DDS contributes +7.5 MHz, and the four-phase PLL supplies the nominal 216 MHz carrier. Their sum is the 223.5625 MHz wanted lane.

The Phase-Domain Summator Realizes Two 216 MHz Mixers and Their Sum

The iCE40 PLL produces nominal 216 MHz outputs separated by 90°. Their inversions supply the other two phases. The four-state selector maps the two intermediate bits directly onto those clocks:

wire p1 =  pll_216mhz_i;
wire p2 =  pll_216mhz_90_i;
wire p3 = ~pll_216mhz_i;
wire p4 = ~pll_216mhz_90_i;
assign modulated_rf_o = selector_i_i
    ? (selector_q_i ? p1 : p4)
    : (selector_q_i ? p2 : p3);

11 selects 0°, 01 selects 90°, 00 selects 180°, and 10 selects 270°. Each state equals the algebraic sum of the two 216 MHz carrier-mixer products for that input pair. The selector is therefore the phase-domain summator, and its one output is already the combined RF signal.

The top level derives both PLL phases from the 12 MHz board reference and also divides the in-phase output to make the 108 MHz lane clock. The Physical27 timing declarations used 216.03 MHz for the fast domain; the completed HX8K route reached 244.20 MHz on that constraint.

Read the four-state selector that performs the phase-domain sum at lines 423–437, and read the PLL and telemetry top at lines 140–176.

N16 Switches High or High Impedance

I use the pMOS side of an iCE40 SB_IO. Its data input is tied to one. The phase-selected RF bit controls output enable:

SB_IO #(
    .PIN_TYPE(6'b101000), .IO_STANDARD("SB_LVCMOS"), .PULLUP(1'b0)
) rf_output_io (
    .PACKAGE_PIN(rf_out_o), .D_OUT_0(1'b1),
    .OUTPUT_ENABLE(physical_enable)
);

When output enable is one, N16 drives high. When it is zero, N16 becomes high impedance; it does not pull the tank low. PLL lock, reset, datapath readiness, the one-shot gate, and the selected carrier state all stand between the FPGA image and that enable signal.

The one-shot counter permits 324,000,000 cycles at 108 MHz, nominally three seconds. A configuration-initialized started latch prevents reset or PLL loss from starting a second burst. Outside that bounded interval, the RF pad remains high impedance. Physical27 also left the optional final output-enable complement disabled.

Macro photograph of the HX8K breakout header around the N16 and ground labels
N16 on the actual HX8K breakout header. The Physical27 pin constraints bind RF_OUT to this package pin.
Close photograph of the hand-built resonant network used by the one-pin transmitter family
The hand-built resonant network used by this transmitter family. It receives switched charge from N16; it is part of the computational boundary, not decorative cleanup.

“One pin” means one programmable FPGA output in the RF path. It does not mean a radio made from one conductor. Power, ground, the 12 MHz reference, configuration wiring, telemetry, the resonant network, coupling, and the receiver remain necessary.

Read the one-shot and N16 boundary RTL. The counter is at lines 4–50 and the SB_IO boundary at lines 256–264.

The Tank Completes the RF Waveform

A high/high-impedance square switching sequence contains sharp edges and many frequency components. The tank accepts those impulses, stores energy in its electric and magnetic fields, and favors a narrow region around its resonance. The physical output is therefore a collaboration between RTL timing and resonant matter.

Five NanoVNA sweeps placed the median S21 maximum at 220.783 MHz and the upper half-power crossing at 223.719 MHz. The 223.5625 MHz QPSK lane sits about 156 kHz inside that upper crossing, where interpolated median S21 was about −23.22 dB at the existing calibration plane.

A separate five-repeat 50–300 MHz sweep covered both Weaver products. Its coarse complex interpolation made S21 about 7.95 dB higher at the 223.5625 MHz wanted lane than at the 208.4375 MHz image, so the tuned network clearly favored the wanted frequency. The NanoVNA and SDR used different instruments and reference planes; subtracting that preference from 29.44 dB would not isolate the digital Weaver cancellation.

Five NanoVNA sweeps of the resonant tank with the 223.5625 megahertz QPSK target near the upper half-power crossing
The tank measured five times, with the final target marked. Download the plotted complex-transfer values.

That local S21 measurement does not give calibrated RF power at N16 or at the tank output. The earlier tank sketch notes about −15 dBm beside a 7.5 kΩ coupling value, but it does not name its load, reference plane, or instrument settings; July used a different frequency and recorded coupling value. Removing or substantially changing the tank changes the transmitter.

Physical27 Carried Its Internal States Beside the RF Burst

The v10 telemetry frame includes the payload epoch and exact applied symbol, reconstructed pre-SDM I and Q, both SDM decisions and error states, the +7.5 MHz DDS phase, the two selector bits, burst state, and the real N16 enable. The payload fields come from the same update that drives the lane, so they do not infer the symbol from a later receiver decision.

Ordered Physical27 FPGA telemetry snapshots containing pre-sigma-delta I and Q, I and Q sigma-delta bits, first-mixer DDS quadrant, and phase-domain summator state
One hundred twenty ordered snapshots from the actual Physical27 burst: signed pre-SDM I/Q, both one-bit SDM outputs, first-mixer DDS quadrant, and phase-domain summator state. Download the plotted snapshots.

The horizontal axis follows USB snapshot order. It is not nanoseconds and not a sample-synchronous 216 MHz logic-analyzer trace. USB transport skips enormous numbers of FPGA clocks between records. The plot can connect real internal states in causal order, but it cannot measure RF-edge timing, PLL phase error, or setup and hold behavior at N16.

What I Kept From the April 2025 mod_apr20.sv, and What I Rebuilt

I kept the part that made the original transmitter unusual: four quadrature mixers terminate in a phase-domain summator whose two one-bit controls select the final RF state on a pMOS-only FPGA pad. I rebuilt the baseband and IF implementation so it could carry a finite, internally generated digital message and name the state actually applied to the pin.

StageApril 2025 mod_apr20.svPhysical27 v10
Complex sourceOpposing 32-bit sawtoothsPRBS16 QPSK on a signed 32-phase lane
One-bit conversionTwo threshold accumulatorsTwo signed first-order SDMs with one consistent decision and feedback value
First mixer pairAdjacent phases of a Johnson counter used specifically as the quadrature IF oscillatorOne +7.5 MHz quadrant rotates the complete two-bit word
Phase-domain summatorrfI/rfQ select the summed state of two 216 MHz mixersThe same four-state RF summation
Pad behaviorConstant high data with phase-controlled output enableThe same high/high-impedance pMOS boundary, now behind PLL/reset and one-shot gates
Internal observationNo applied-symbol epochDirect symbol, epoch, I/Q, SDM, DDS, selector, and physical-enable telemetry

The April 2025 source and v10 are architecturally related, not equivalent builds. The baseband SDMs and IF mixer pair are visible at lines 653–725; the phase-domain summator is at lines 711–736; and the pMOS SB_IO is at lines 479–493. V10 replaces the baseband and IF implementation while keeping the four-mixer architecture, summator, and physical boundary.

Read the April 2025 mod_apr20.sv beside the active v10 channelizer and modulation RTL.

Build the Same V10 Logic

The Physical27 build uses Yosys 0.52, nextpnr-ice40 0.7-1+b2, IceStorm icepack, an iCE40HX8K in the CT256 package, and nextpnr seed 1. It reads the same six SystemVerilog files in the same order, applies the Physical27 parameters, checks the 12, 30, 108, and 216.03 MHz domains, and packs the routed design.

From the source directory, the complete command is:

bash scripts/build-v10.sh

The timing-closed result uses 2,127 of 7,680 logic cells, one PLL, no block RAM, three global buffers, and seven I/O cells. The build script creates a local programming image but does not load the FPGA or transmit RF.

Source terms

The files compile with the stated toolchain into the Physical27 routed implementation. I have not granted a separate reuse license.

Read the source notice