From e9b69b144c1f41dd8c4c1bc36f5ef26e153fdc04 Mon Sep 17 00:00:00 2001 From: mndza Date: Tue, 31 Mar 2026 12:49:15 +0200 Subject: [PATCH 001/173] gateware: add pstep register in RX extended precision --- firmware/fpga/build/praline_fpga.bin | Bin 201089 -> 200305 bytes firmware/fpga/top/ext_precision_rx.py | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/firmware/fpga/build/praline_fpga.bin b/firmware/fpga/build/praline_fpga.bin index addeb7a744816abe13ed0a71924639a66113384b..90115bedb48a0a491a4476e667ab4ce0322b2ea1 100644 GIT binary patch delta 64790 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
  • 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 Rj8%be%3gi9AAjYC{|Bs;jEn#P delta 65580 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

    1ONTR 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 Date: Mon, 16 Mar 2026 16:55:29 -0400 Subject: [PATCH 002/173] Fix radio_update() usage in sweep mode --- firmware/hackrf_usb/usb_api_sweep.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index 0c5dbfff..782f69cd 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -100,15 +100,12 @@ usb_request_status_t usb_vendor_request_init_sweep( } sweep_freq = (uint64_t) frequencies[0] * FREQ_GRANULARITY; - nvic_disable_irq(NVIC_USB0_IRQ); radio_reg_write( &radio, RADIO_BANK_ACTIVE, RADIO_FREQUENCY_RF, (sweep_freq + offset) * FP_ONE_HZ); usb_transfer_schedule_ack(endpoint->in); - nvic_enable_irq(NVIC_USB0_IRQ); - radio_update(&radio); } return USB_REQUEST_STATUS_OK; } @@ -237,6 +234,7 @@ void sweep_mode(uint32_t seq) RADIO_FREQUENCY_RF, (sweep_freq + offset) * FP_ONE_HZ); nvic_enable_irq(NVIC_USB0_IRQ); + radio_update(&radio); blocks_queued = 0; } From f2a221d0b89ed4e6150bf50b8bca88e43c57c34e Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Wed, 1 Apr 2026 08:11:29 -0400 Subject: [PATCH 003/173] Remove radio_update() call from radio_reg_write() This was intended to be removed in #1648. Registers are typically written from within USB ISRs. Updates are meant to happen outside of ISRs. --- firmware/common/radio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index c714d5e7..cb441a64 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -89,7 +89,6 @@ radio_error_t radio_reg_write( return RADIO_ERR_INVALID_BANK; } - radio_update(radio); return RADIO_OK; } From e5f79913ebc51467e108b79ab4c87e5d50fc720c Mon Sep 17 00:00:00 2001 From: mndza Date: Wed, 1 Apr 2026 17:38:22 +0200 Subject: [PATCH 004/173] gateware: unify resampling factors between gatewares --- firmware/fpga/build/praline_fpga.bin | Bin 200305 -> 200273 bytes firmware/fpga/top/ext_precision_rx.py | 11 +++++++---- firmware/fpga/top/ext_precision_tx.py | 11 +++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/firmware/fpga/build/praline_fpga.bin b/firmware/fpga/build/praline_fpga.bin index 90115bedb48a0a491a4476e667ab4ce0322b2ea1..b327ec0833617821f9e341fbd773c782fcebb524 100644 GIT binary patch delta 125874 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 w9p9TFVekZ+J@jel$NaVAvkN!c%Yeeg* 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%0DX?DcN4M_+GBP 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ð@t?B&DBE9 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 diff --git a/firmware/fpga/top/ext_precision_rx.py b/firmware/fpga/top/ext_precision_rx.py index 6f744c72..3284278a 100644 --- a/firmware/fpga/top/ext_precision_rx.py +++ b/firmware/fpga/top/ext_precision_rx.py @@ -88,9 +88,10 @@ class Top(Elaboratable): # Add control registers. ctrl = spi_regs.add_register(0x01, init=0) - rx_decim = Signal(3, init=2) + rx_decim = Signal(3, init=4) rx_decim_new = Signal(3) rx_decim_stb = Signal() + rx_decim_cic = Signal.like(rx_chain["cic"].factor) spi_regs.add_sfr(0x02, read=rx_decim, write_signal=rx_decim_new, write_strobe=rx_decim_stb) rx_pstep = spi_regs.add_register(0x03, init=0) @@ -104,13 +105,15 @@ class Top(Elaboratable): rx_chain["quarter_shift"].up .eq(rx_pstep[-1]), # RX decimation rate. - rx_chain["cic"].factor .eq(rx_decim), + rx_chain["cic"].factor .eq(rx_decim_cic), ] with m.If(rx_decim_stb): - with m.If(rx_decim_new < 2): - m.d.sync += rx_decim.eq(2) + with m.If(rx_decim_new < 4): + m.d.sync += rx_decim.eq(4) + m.d.sync += rx_decim_cic.eq(2) with m.Else(): m.d.sync += rx_decim.eq(rx_decim_new) + m.d.sync += rx_decim_cic.eq(rx_decim_new-2) return m diff --git a/firmware/fpga/top/ext_precision_tx.py b/firmware/fpga/top/ext_precision_tx.py index 08a3adf0..949a8ce5 100644 --- a/firmware/fpga/top/ext_precision_tx.py +++ b/firmware/fpga/top/ext_precision_tx.py @@ -90,9 +90,10 @@ class Top(Elaboratable): # Add control registers. ctrl = spi_regs.add_register(0x01, init=0) - tx_intrp = Signal(3, init=2) + tx_intrp = Signal(3, init=4) tx_intrp_new = Signal(3) tx_intrp_stb = Signal() + tx_intrp_cic = Signal.like(tx_chain["cic_interpolator"].factor) spi_regs.add_sfr(0x05, read=tx_intrp, write_signal=tx_intrp_new, write_strobe=tx_intrp_stb) m.d.comb += [ @@ -100,13 +101,15 @@ class Top(Elaboratable): mcu_intf.trigger_en .eq(ctrl[7]), ] # TX interpolation rate. - m.submodules.rx_decim_cdc = cdc.FFSynchronizer(tx_intrp, tx_chain["cic_interpolator"].factor, o_domain=dac_clk) + m.submodules.rx_decim_cdc = cdc.FFSynchronizer(tx_intrp_cic, tx_chain["cic_interpolator"].factor, o_domain=dac_clk) with m.If(tx_intrp_stb): - with m.If(tx_intrp_new < 2): - m.d.sync += tx_intrp.eq(2) + with m.If(tx_intrp_new < 4): + m.d.sync += tx_intrp.eq(4) + m.d.sync += tx_intrp_cic.eq(2) with m.Else(): m.d.sync += tx_intrp.eq(tx_intrp_new) + m.d.sync += tx_intrp_cic.eq(tx_intrp_new-2) return m From 68f9ee8b7996b57439b4c49edd19a01a207976d1 Mon Sep 17 00:00:00 2001 From: mndza Date: Wed, 21 Jan 2026 15:33:41 +0100 Subject: [PATCH 005/173] gateware: fix signal direction --- firmware/fpga/dsp/fir_mac16.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/fpga/dsp/fir_mac16.py b/firmware/fpga/dsp/fir_mac16.py index 84c59a89..81667f62 100644 --- a/firmware/fpga/dsp/fir_mac16.py +++ b/firmware/fpga/dsp/fir_mac16.py @@ -468,7 +468,7 @@ class iCE40Multiplier(wiring.Component): "a": In(signed(a_width)), "b": In(signed(b_width)), "valid_in": In(1), - "ready_in": In(1), + "ready_in": Out(1), "p": In(signed(p_width)), "p_load": In(1), "o": Out(signed(o_width)), From b2669ef2fd44e41bffcfdeea306f65b6c4a386a8 Mon Sep 17 00:00:00 2001 From: mndza Date: Fri, 27 Mar 2026 09:18:04 +0100 Subject: [PATCH 006/173] fix NCO phase symmetry --- firmware/fpga/dsp/nco.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/fpga/dsp/nco.py b/firmware/fpga/dsp/nco.py index e464399a..7419e011 100755 --- a/firmware/fpga/dsp/nco.py +++ b/firmware/fpga/dsp/nco.py @@ -54,7 +54,7 @@ class NCO(wiring.Component): addr_width = (self.output_width + 1) - 3 lut_depth = 1 << addr_width lut_scale = (1 << (self.output_width-1)) - 1 - lut_phases = [ i * pi / 4 / lut_depth for i in range(lut_depth) ] + lut_phases = [ (n * pi / 4 + .5) / lut_depth for n in range(lut_depth) ] lut_data = memory.MemoryData( shape=IQSample(self.output_width), depth=lut_depth, From 627c43b27913e0ac9911548fcd4b35c139aae511 Mon Sep 17 00:00:00 2001 From: mndza Date: Fri, 23 Jan 2026 15:44:15 +0100 Subject: [PATCH 007/173] gateware: add complex multiplier --- firmware/fpga/dsp/fir_mac16.py | 27 +++++++----- firmware/fpga/dsp/mixer.py | 80 ++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 firmware/fpga/dsp/mixer.py diff --git a/firmware/fpga/dsp/fir_mac16.py b/firmware/fpga/dsp/fir_mac16.py index 81667f62..57da9605 100644 --- a/firmware/fpga/dsp/fir_mac16.py +++ b/firmware/fpga/dsp/fir_mac16.py @@ -464,18 +464,23 @@ class SerialMAC16(wiring.Component): class iCE40Multiplier(wiring.Component): def __init__(self, a_width=16, b_width=16, p_width=32, o_width=32, always_ready=False): - super().__init__({ + signature = { "a": In(signed(a_width)), "b": In(signed(b_width)), "valid_in": In(1), "ready_in": Out(1), - "p": In(signed(p_width)), - "p_load": In(1), "o": Out(signed(o_width)), "valid_out": Out(1), "ready_out": In(1), - }) + } + if p_width > 0: + signature.update({ + "p": In(signed(p_width)), + "p_load": In(1), + }) + super().__init__(signature) self.always_ready = always_ready + self.p_width = p_width self.o_width = o_width def elaborate(self, platform): @@ -487,15 +492,17 @@ class iCE40Multiplier(wiring.Component): m.d.sync += pipe[i+1].eq(pipe[i]) return pipe - p_load_v = Signal() valid_v = Signal() m.d.comb += valid_v.eq(self.valid_in & self.ready_in) dsp_delay = 3 valid_pipe = pipe(valid_v, dsp_delay) - m.d.comb += p_load_v.eq(self.p_load & valid_v) - p_pipe = pipe(self.p, dsp_delay-1) - p_load_pipe = pipe(p_load_v, dsp_delay - 1) + + if self.p_width > 0: + p_load_v = Signal() + m.d.comb += p_load_v.eq(self.p_load & valid_v) + p_pipe = pipe(self.p, dsp_delay-1) + p_load_pipe = pipe(p_load_v, dsp_delay - 1) # skid buffer if not self.always_ready: @@ -529,10 +536,10 @@ class iCE40Multiplier(wiring.Component): # Inputs. mac.CLK .eq(ClockSignal("sync")), mac.CE .eq(1), - mac.C.as_signed().eq(Mux(p_load_pipe[2], p_pipe[2][16:], mac.O[16:])), + mac.C.as_signed().eq(Mux(p_load_pipe[2], p_pipe[2][16:], mac.O[16:]) if self.p_width > 0 else 0), mac.A.as_signed().eq(self.a), mac.B.as_signed().eq(self.b), - mac.D.as_signed().eq(Mux(p_load_pipe[2], p_pipe[2][:16], mac.O[:16])), + mac.D.as_signed().eq(Mux(p_load_pipe[2], p_pipe[2][:16], mac.O[:16]) if self.p_width > 0 else 0), mac.AHOLD .eq(~valid_pipe[0]), # 0: load mac.BHOLD .eq(~valid_pipe[0]), mac.CHOLD .eq(0), diff --git a/firmware/fpga/dsp/mixer.py b/firmware/fpga/dsp/mixer.py new file mode 100644 index 00000000..e5e599ae --- /dev/null +++ b/firmware/fpga/dsp/mixer.py @@ -0,0 +1,80 @@ +# +# This file is part of HackRF. +# +# Copyright (c) 2025 Great Scott Gadgets +# SPDX-License-Identifier: BSD-3-Clause + +from amaranth import Module +from amaranth.lib import wiring, stream +from amaranth.lib.wiring import In, Out + +from dsp.fir_mac16 import iCE40Multiplier +from dsp.round import convergent_round + +from util import IQSample + + +class ComplexMultiplier(wiring.Component): + + def __init__(self, a_shape, b_shape, c_shape, always_ready=False): + super().__init__({ + "a": In(stream.Signature(a_shape, always_ready=always_ready)), + "b": In(stream.Signature(b_shape, always_ready=always_ready)), + "c": Out(stream.Signature(c_shape, always_ready=always_ready)), + }) + self.always_ready = always_ready + + def elaborate(self, platform): + m = Module() + + A = self.a.p.i + B = self.a.p.q + C = self.b.p.i + D = self.b.p.q + + a_width = A.shape().width + b_width = B.shape().width + c_width = C.shape().width + d_width = D.shape().width + + o_shape = (A * C).shape() + o_width = o_shape.width + + m.submodules.dsp1 = dsp1 = iCE40Multiplier(a_width=a_width, b_width=c_width, p_width=0, o_width=o_width, always_ready=self.always_ready) + m.submodules.dsp2 = dsp2 = iCE40Multiplier(a_width=a_width, b_width=d_width, p_width=0, o_width=o_width, always_ready=self.always_ready) + m.submodules.dsp3 = dsp3 = iCE40Multiplier(a_width=b_width, b_width=d_width, p_width=0, o_width=o_width, always_ready=self.always_ready) + m.submodules.dsp4 = dsp4 = iCE40Multiplier(a_width=b_width, b_width=c_width, p_width=0, o_width=o_width, always_ready=self.always_ready) + + valid_in = self.a.valid & self.b.valid + m.d.comb += [ + dsp1.a.eq(A), + dsp1.b.eq(C), + dsp1.valid_in.eq(valid_in), + dsp2.a.eq(A), + dsp2.b.eq(D), + dsp2.valid_in.eq(valid_in), + dsp3.a.eq(B), + dsp3.b.eq(D), + dsp3.valid_in.eq(valid_in), + dsp4.a.eq(B), + dsp4.b.eq(C), + dsp4.valid_in.eq(valid_in), + ] + + ready_in = dsp1.ready_in + if not self.always_ready: + m.d.comb += self.a.ready.eq(ready_in) + m.d.comb += self.b.ready.eq(ready_in) + + with m.If(~self.c.valid | self.c.ready): + m.d.sync += self.c.valid.eq(dsp1.valid_out) + with m.If(dsp1.valid_out): + m.d.sync += self.c.p.i.eq(convergent_round(dsp1.o - dsp3.o, o_width - self.c.p.i.shape().width - 1)) + m.d.sync += self.c.p.q.eq(convergent_round(dsp2.o + dsp4.o, o_width - self.c.p.q.shape().width - 1)) + m.d.comb += dsp1.ready_out.eq(1) + m.d.comb += dsp2.ready_out.eq(1) + m.d.comb += dsp3.ready_out.eq(1) + m.d.comb += dsp4.ready_out.eq(1) + + return m + From 37ad28a03a78e633ebe4cb64246881b711e92bbd Mon Sep 17 00:00:00 2001 From: mndza Date: Fri, 23 Jan 2026 15:48:50 +0100 Subject: [PATCH 008/173] gateware: add configurable mixer to RX extended precision --- firmware/fpga/build/praline_fpga.bin | Bin 200273 -> 204769 bytes firmware/fpga/dsp/nco.py | 10 ++++-- firmware/fpga/top/ext_precision_rx.py | 44 ++++++++++++++++++-------- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/firmware/fpga/build/praline_fpga.bin b/firmware/fpga/build/praline_fpga.bin index b327ec0833617821f9e341fbd773c782fcebb524..a6b024858f10f08f84cbc6cffa578935c0ae469a 100644 GIT binary patch delta 129252 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

    v$tVy*Gkwfre_!KR9cZn1CAj9=& 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 IZ~5i_0dw;gFaQ7m delta 124644 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�#?3OCTkY{BF_`Z^9^sE*jR@ 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 z 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�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 diff --git a/firmware/fpga/dsp/nco.py b/firmware/fpga/dsp/nco.py index 7419e011..38bae44b 100755 --- a/firmware/fpga/dsp/nco.py +++ b/firmware/fpga/dsp/nco.py @@ -6,7 +6,7 @@ from math import pi, sin, cos -from amaranth import Module, Signal, Mux, Cat +from amaranth import Module, Signal, DomainRenamer, Mux, Cat from amaranth.lib import wiring, memory from amaranth.lib.wiring import In, Out @@ -38,9 +38,10 @@ class NCO(wiring.Component): Returned result for cos(phase), sin(phase). """ - def __init__(self, phase_width=24, output_width=10): + def __init__(self, phase_width=24, output_width=10, domain="sync"): self.phase_width = phase_width self.output_width = output_width + self._domain = domain super().__init__({ "phase": In(phase_width), "en": In(1), @@ -61,7 +62,7 @@ class NCO(wiring.Component): init=({"i": round(lut_scale * cos(x)), "q": round(lut_scale * sin(x))} for x in lut_phases) ) m.submodules.table = table = memory.Memory(data=lut_data) - table_rd = table.read_port(domain="sync") + table_rd = table.read_port(domain=self._domain) # 3 MSBs of the phase word: sign, quadrant, octant. o, q, s = self.phase[-3:] @@ -99,5 +100,8 @@ class NCO(wiring.Component): e_s2.q .eq(Mux(neg_sin, -e_s1.q, e_s1.q)), ] m.d.sync += self.output.eq(e_s2) + + if self._domain != "sync": + m = DomainRenamer(self._domain)(m) return m diff --git a/firmware/fpga/top/ext_precision_rx.py b/firmware/fpga/top/ext_precision_rx.py index 3284278a..3d486519 100644 --- a/firmware/fpga/top/ext_precision_rx.py +++ b/firmware/fpga/top/ext_precision_rx.py @@ -4,7 +4,8 @@ # Copyright (c) 2025 Great Scott Gadgets # SPDX-License-Identifier: BSD-3-Clause -from amaranth import Elaboratable, Module, Cat, DomainRenamer, Signal +from amaranth import Elaboratable, Module, Cat, DomainRenamer, Signal, Mux +from amaranth.lib import cdc from amaranth.lib.wiring import connect from amaranth_future import fixed @@ -15,7 +16,9 @@ from dsp.fir import FIRFilter from dsp.fir_mac16 import HalfBandDecimatorMAC16 from dsp.cic import CICDecimator from dsp.dc_block import DCBlock -from dsp.quarter_shift import QuarterShift +from dsp.nco import NCO +from dsp.mixer import ComplexMultiplier + from util import ClockConverter, IQSample @@ -56,10 +59,21 @@ class Top(Elaboratable): taps_hb2 = [ -6, 0, 19, 0, -44, 0, 89, 0, -163, 0, 278, 0, -452, 0, 711, 0, -1113, 0, 1800, 0, -3298, 0, 10370, 16384, 10370, 0, -3298, 0, 1800, 0, -1113, 0, 711, 0, -452, 0, 278, 0, -163, 0, 89, 0, -44, 0, 19, 0, -6] taps_hb2 = [ tap/16384/2 for tap in taps_hb2 ] + # NCO and mixer. + m.submodules.nco = nco = NCO(phase_width=16, output_width=10, domain=adc_clk) + mixer = DomainRenamer(adc_clk)(ComplexMultiplier(IQSample(8), IQSample(10), IQSample(8), always_ready=True)) + mixer.input = mixer.a + mixer.output = mixer.c + m.d.comb += [ + mixer.b.p.eq(nco.output), + mixer.b.valid.eq(1), + nco.en.eq(1), + ] + rx_chain = { - # DC block and quarter shift. + # DC block and mixer. "dc_block": DCBlock(width=8, num_channels=2, domain=adc_clk), - "quarter_shift": DomainRenamer(adc_clk)(QuarterShift()), + "mixer": mixer, # CIC mandatory first stage with compensator. "cic": CICDecimator(2, 4, (4,8,16,32), width_in=8, width_out=12, num_channels=2, always_ready=True, domain=adc_clk), @@ -93,20 +107,19 @@ class Top(Elaboratable): rx_decim_stb = Signal() rx_decim_cic = Signal.like(rx_chain["cic"].factor) spi_regs.add_sfr(0x02, read=rx_decim, write_signal=rx_decim_new, write_strobe=rx_decim_stb) - rx_pstep = spi_regs.add_register(0x03, init=0) + rx_nco = spi_regs.add_register(0x03, init=0) m.d.comb += [ # Trigger enable. mcu_intf.trigger_en .eq(ctrl[7]), - - # RX settings. - rx_chain["dc_block"].enable .eq(ctrl[0]), - rx_chain["quarter_shift"].enable .eq(rx_pstep[-2]), - rx_chain["quarter_shift"].up .eq(rx_pstep[-1]), - - # RX decimation rate. - rx_chain["cic"].factor .eq(rx_decim_cic), ] + + # RX DC block. + m.submodules.dc_block_cdc = cdc.FFSynchronizer(ctrl[0], rx_chain["dc_block"].enable, o_domain=adc_clk) + + # RX decimation rate. + m.submodules.rx_decim_cdc = cdc.FFSynchronizer(rx_decim_cic, rx_chain["cic"].factor, o_domain=adc_clk) + with m.If(rx_decim_stb): with m.If(rx_decim_new < 4): m.d.sync += rx_decim.eq(4) @@ -115,6 +128,11 @@ class Top(Elaboratable): m.d.sync += rx_decim.eq(rx_decim_new) m.d.sync += rx_decim_cic.eq(rx_decim_new-2) + # NCO control. + rx_nco_ad = Signal(8) + m.submodules.nco_phase_cdc = cdc.FFSynchronizer(rx_nco, rx_nco_ad, o_domain=adc_clk) + m.d[adc_clk] += nco.phase.eq(nco.phase - (rx_nco_ad << 8)) + return m From 020e57b2f5c10214a9cb44a9f8c9bf92c4a62a8a Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Fri, 20 Mar 2026 10:16:09 +0200 Subject: [PATCH 009/173] firmware: replace include guards with #pragma once --- firmware/common/adc.h | 5 +--- firmware/common/bitband.h | 5 +--- firmware/common/clkin.h | 5 +--- firmware/common/cpld_jtag.h | 5 +--- firmware/common/cpld_xc2c.h | 5 +--- firmware/common/crc.h | 5 +--- firmware/common/da7219.h | 5 +--- firmware/common/delay.h | 7 +++--- firmware/common/fault_handler.h | 5 +--- firmware/common/firmware_info.h | 5 +--- firmware/common/fixed_point.h | 5 +--- firmware/common/fpga.h | 5 +--- firmware/common/fpga_regs.def | 7 ++---- firmware/common/gpdma.h | 5 +--- firmware/common/gpio.h | 5 +--- firmware/common/gpio_lpc.h | 5 +--- firmware/common/hackrf_core.h | 5 +--- firmware/common/hackrf_ui.h | 5 +--- firmware/common/i2c_bus.h | 5 +--- firmware/common/i2c_lpc.h | 5 +--- firmware/common/ice40_spi.h | 5 +--- firmware/common/locking.h | 5 +--- firmware/common/lz4_blk.h | 5 +--- firmware/common/lz4_buf.h | 5 +--- firmware/common/m0_state.h | 5 +--- firmware/common/max2831.h | 5 +--- firmware/common/max2831_regs.def | 28 +++++++++++++++++++---- firmware/common/max2831_target.h | 5 +--- firmware/common/max2837.h | 5 +--- firmware/common/max2837_regs.def | 27 ++++++++++++++++++---- firmware/common/max2837_target.h | 5 +--- firmware/common/max2839.h | 5 +--- firmware/common/max2839_regs.def | 27 ++++++++++++++++++---- firmware/common/max2839_target.h | 5 ---- firmware/common/max283x.h | 5 +--- firmware/common/max2871.h | 4 +--- firmware/common/max2871_regs.h | 5 ++-- firmware/common/max5864.h | 5 +--- firmware/common/max5864_target.h | 5 +--- firmware/common/mixer.h | 5 +--- firmware/common/operacake.h | 5 +--- firmware/common/operacake_sctimer.h | 5 +--- firmware/common/platform_detect.h | 5 +--- firmware/common/platform_gpio.h | 5 +--- firmware/common/platform_scu.h | 5 +--- firmware/common/portapack.h | 5 +--- firmware/common/rad1o/decoder.h | 5 +--- firmware/common/rad1o/display.h | 5 +--- firmware/common/rad1o/draw.h | 5 +--- firmware/common/rad1o/fonts.h | 5 +--- firmware/common/rad1o/print.h | 5 +--- firmware/common/rad1o/render.h | 5 ++-- firmware/common/rad1o/smallfonts.h | 6 ++--- firmware/common/rad1o/ubuntu18.h | 5 +--- firmware/common/radio.h | 5 +--- firmware/common/rf_path.h | 5 +--- firmware/common/rffc5071.h | 5 +--- firmware/common/rffc5071_regs.def | 5 +--- firmware/common/rffc5071_spi.h | 5 +--- firmware/common/rom_iap.h | 7 ++---- firmware/common/sct.h | 2 ++ firmware/common/selftest.h | 5 +--- firmware/common/sgpio.h | 5 +--- firmware/common/si5351c.h | 5 +--- firmware/common/spi_bus.h | 5 +--- firmware/common/spi_ssp.h | 5 +--- firmware/common/streaming.h | 5 +--- firmware/common/tune_config.h | 5 +--- firmware/common/tuning.h | 5 +--- firmware/common/ui_portapack.h | 5 +--- firmware/common/ui_rad1o.h | 5 +--- firmware/common/usb.h | 5 +--- firmware/common/usb_queue.h | 4 +--- firmware/common/usb_request.h | 5 +--- firmware/common/usb_standard_request.h | 5 +--- firmware/common/usb_type.h | 5 +--- firmware/common/w25q80bv.h | 5 +--- firmware/common/w25q80bv_target.h | 5 +--- firmware/hackrf_usb/usb_api_adc.h | 5 +--- firmware/hackrf_usb/usb_api_board_info.h | 5 +--- firmware/hackrf_usb/usb_api_cpld.h | 5 +--- firmware/hackrf_usb/usb_api_m0_state.h | 5 +--- firmware/hackrf_usb/usb_api_operacake.h | 5 +--- firmware/hackrf_usb/usb_api_praline.h | 5 +--- firmware/hackrf_usb/usb_api_register.h | 5 +--- firmware/hackrf_usb/usb_api_selftest.h | 5 +--- firmware/hackrf_usb/usb_api_spiflash.h | 5 +--- firmware/hackrf_usb/usb_api_sweep.h | 5 +--- firmware/hackrf_usb/usb_api_transceiver.h | 5 +--- firmware/hackrf_usb/usb_api_ui.h | 5 +--- firmware/hackrf_usb/usb_bulk_buffer.h | 5 +--- firmware/hackrf_usb/usb_descriptor.h | 2 ++ firmware/hackrf_usb/usb_device.h | 5 +--- firmware/hackrf_usb/usb_endpoint.h | 5 +--- 94 files changed, 167 insertions(+), 369 deletions(-) diff --git a/firmware/common/adc.h b/firmware/common/adc.h index 94481678..b74125af 100644 --- a/firmware/common/adc.h +++ b/firmware/common/adc.h @@ -19,12 +19,9 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __ADC_H__ -#define __ADC_H__ +#pragma once #include uint16_t adc_read(uint8_t pin); void adc_off(void); - -#endif // __ADC_H__ diff --git a/firmware/common/bitband.h b/firmware/common/bitband.h index dcf7aefb..8a0afed5 100644 --- a/firmware/common/bitband.h +++ b/firmware/common/bitband.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __BITBAND_H__ -#define __BITBAND_H__ +#pragma once #include @@ -37,5 +36,3 @@ void peripheral_bitband_clear( uint32_t peripheral_bitband_get( volatile void* const peripheral_address, const uint_fast8_t bit_number); - -#endif //__BITBAND_H__ diff --git a/firmware/common/clkin.h b/firmware/common/clkin.h index 2f2fa274..2cef67cf 100644 --- a/firmware/common/clkin.h +++ b/firmware/common/clkin.h @@ -19,12 +19,9 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __CLKIN_H__ -#define __CLKIN_H__ +#pragma once #include void clkin_detect_init(void); uint32_t clkin_frequency(void); - -#endif //__CLKIN_H__ diff --git a/firmware/common/cpld_jtag.h b/firmware/common/cpld_jtag.h index 9219dbcb..c052334d 100644 --- a/firmware/common/cpld_jtag.h +++ b/firmware/common/cpld_jtag.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __CPLD_JTAG_H__ -#define __CPLD_JTAG_H__ +#pragma once #include @@ -59,5 +58,3 @@ int cpld_jtag_program( unsigned char* const buffer, refill_buffer_cb refill); unsigned char cpld_jtag_get_next_byte(void); - -#endif //__CPLD_JTAG_H__ diff --git a/firmware/common/cpld_xc2c.h b/firmware/common/cpld_xc2c.h index 26ac8fa4..c48dae31 100644 --- a/firmware/common/cpld_xc2c.h +++ b/firmware/common/cpld_xc2c.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __CPLD_XC2C_H__ -#define __CPLD_XC2C_H__ +#pragma once #include #include @@ -69,5 +68,3 @@ bool cpld_xc2c64a_jtag_sram_verify( extern const cpld_xc2c64a_program_t cpld_hackrf_program_sram; extern const cpld_xc2c64a_verify_t cpld_hackrf_verify; extern const cpld_xc2c64a_row_addresses_t cpld_hackrf_row_addresses; - -#endif /*__CPLD_XC2C_H__*/ diff --git a/firmware/common/crc.h b/firmware/common/crc.h index f8acf5fc..ee33d7df 100644 --- a/firmware/common/crc.h +++ b/firmware/common/crc.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __CRC_H__ -#define __CRC_H__ +#pragma once #include #include @@ -35,5 +34,3 @@ typedef struct { void crc32_init(crc32_t* const crc); void crc32_update(crc32_t* const crc, const uint8_t* const data, const size_t byte_count); uint32_t crc32_digest(const crc32_t* const crc); - -#endif //__CRC_H__ diff --git a/firmware/common/da7219.h b/firmware/common/da7219.h index f0289240..c81ece20 100644 --- a/firmware/common/da7219.h +++ b/firmware/common/da7219.h @@ -19,13 +19,10 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __DA7219_H -#define __DA7219_H +#pragma once #include #define DA7219_ADDRESS 0x1A bool da7219_detect(void); - -#endif /* __DA7219_H */ diff --git a/firmware/common/delay.h b/firmware/common/delay.h index 123141ba..870ba39e 100644 --- a/firmware/common/delay.h +++ b/firmware/common/delay.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __DELAY_H -#define __DELAY_H +#pragma once #ifdef __cplusplus extern "C" { @@ -31,4 +30,6 @@ extern "C" { void delay(uint32_t duration); void delay_us_at_mhz(uint32_t us, uint32_t mhz); -#endif /* __DELAY_H */ +#ifdef __cplusplus +} +#endif diff --git a/firmware/common/fault_handler.h b/firmware/common/fault_handler.h index efccea24..d0edd6a5 100644 --- a/firmware/common/fault_handler.h +++ b/firmware/common/fault_handler.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __FAULT_HANDLER__ -#define __FAULT_HANDLER__ +#pragma once #include @@ -71,5 +70,3 @@ static armv7m_scb_t* const SCB = (armv7m_scb_t*) SCB_BASE; #define SCB_HFSR_DEBUGEVT (1 << 31) #define SCB_HFSR_FORCED (1 << 30) #define SCB_HFSR_VECTTBL (1 << 1) - -#endif //__FAULT_HANDLER__ diff --git a/firmware/common/firmware_info.h b/firmware/common/firmware_info.h index fb087008..a7ae09f6 100644 --- a/firmware/common/firmware_info.h +++ b/firmware/common/firmware_info.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef FIRMWARE_INFO_H -#define FIRMWARE_INFO_H +#pragma once #include @@ -33,5 +32,3 @@ struct firmware_info_t { } __attribute__((packed, aligned(1))); extern const struct firmware_info_t firmware_info; - -#endif \ No newline at end of file diff --git a/firmware/common/fixed_point.h b/firmware/common/fixed_point.h index 500d2e54..65656e19 100644 --- a/firmware/common/fixed_point.h +++ b/firmware/common/fixed_point.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __FIXED_POINT_H__ -#define __FIXED_POINT_H__ +#pragma once #include @@ -32,5 +31,3 @@ typedef uint64_t fp_40_24_t; /* one in 40.24 fixed-point */ #define FP_ONE_HZ (1 << 24) - -#endif /*__FIXED_POINT_H__*/ diff --git a/firmware/common/fpga.h b/firmware/common/fpga.h index bf78cf08..3ba3b652 100644 --- a/firmware/common/fpga.h +++ b/firmware/common/fpga.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __FPGA_H -#define __FPGA_H +#pragma once #include #include @@ -94,5 +93,3 @@ bool fpga_image_load(struct fpga_loader_t* loader, unsigned int index); bool fpga_spi_selftest(void); bool fpga_sgpio_selftest(void); bool fpga_if_xcvr_selftest(void); - -#endif // __FPGA_H diff --git a/firmware/common/fpga_regs.def b/firmware/common/fpga_regs.def index 5964657b..0a8ce828 100644 --- a/firmware/common/fpga_regs.def +++ b/firmware/common/fpga_regs.def @@ -1,7 +1,7 @@ /* -*- mode: c -*- * * Copyright 2012 Michael Ossmann - * Copyright 2025 Great Scott Gadgets + * Copyright 2025-2026 Great Scott Gadgets * * This file is part of HackRF. * @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __FPGA_REGS_DEF -#define __FPGA_REGS_DEF +#pragma once #define FPGA_REG_SET_CLEAN(_d, _r) (_d->regs_dirty &= ~(1UL<<_r)) #define FPGA_REG_SET_DIRTY(_d, _r) (_d->regs_dirty |= (1UL<<_r)) @@ -71,5 +70,3 @@ __MREG__(FPGA_STANDARD_TX_INTRP, 5, 0, 3) /* REG 06 (6): TX_PSTEP */ __MREG__(FPGA_STANDARD_TX_PSTEP, 6, 0, 8) - -#endif // __FPGA_REGS_DEF diff --git a/firmware/common/gpdma.h b/firmware/common/gpdma.h index 053f83a8..dc469da9 100644 --- a/firmware/common/gpdma.h +++ b/firmware/common/gpdma.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __GPDMA_H__ -#define __GPDMA_H__ +#pragma once #include #include @@ -40,5 +39,3 @@ void gpdma_lli_enable_interrupt(gpdma_lli_t* const lli); void gpdma_lli_create_loop(gpdma_lli_t* const lli, const size_t lli_count); void gpdma_lli_create_oneshot(gpdma_lli_t* const lli, const size_t lli_count); - -#endif /*__GPDMA_H__*/ diff --git a/firmware/common/gpio.h b/firmware/common/gpio.h index 387b91a3..749fa021 100644 --- a/firmware/common/gpio.h +++ b/firmware/common/gpio.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __GPIO_H__ -#define __GPIO_H__ +#pragma once #include @@ -35,5 +34,3 @@ void gpio_output(gpio_t gpio); void gpio_input(gpio_t gpio); void gpio_write(gpio_t gpio, const bool value); bool gpio_read(gpio_t gpio); - -#endif /*__GPIO_H__*/ diff --git a/firmware/common/gpio_lpc.h b/firmware/common/gpio_lpc.h index f018e43c..68905ef9 100644 --- a/firmware/common/gpio_lpc.h +++ b/firmware/common/gpio_lpc.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __GPIO_LPC_H__ -#define __GPIO_LPC_H__ +#pragma once #include @@ -69,5 +68,3 @@ struct gpio { .gpio_w = GPIO_LPC_W(_port_num, _pin_num), \ } // clang-format on - -#endif /*__GPIO_LPC_H__*/ diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 74a171a1..e3396ae2 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __HACKRF_CORE_H -#define __HACKRF_CORE_H +#pragma once #ifdef __cplusplus extern "C" { @@ -146,5 +145,3 @@ void pps_out_set(const uint8_t value); #ifdef __cplusplus } #endif - -#endif /* __HACKRF_CORE_H */ diff --git a/firmware/common/hackrf_ui.h b/firmware/common/hackrf_ui.h index 24d2fa8e..7b08a597 100644 --- a/firmware/common/hackrf_ui.h +++ b/firmware/common/hackrf_ui.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef HACKRF_UI_H -#define HACKRF_UI_H +#pragma once #include #include @@ -71,5 +70,3 @@ typedef struct { */ const hackrf_ui_t* hackrf_ui(void); void hackrf_ui_set_enable(bool); - -#endif diff --git a/firmware/common/i2c_bus.h b/firmware/common/i2c_bus.h index 95431437..1e85e440 100644 --- a/firmware/common/i2c_bus.h +++ b/firmware/common/i2c_bus.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __I2C_BUS_H__ -#define __I2C_BUS_H__ +#pragma once #include #include @@ -51,5 +50,3 @@ void i2c_bus_transfer( const size_t tx_count, uint8_t* const rx, const size_t rx_count); - -#endif /*__I2C_BUS_H__*/ diff --git a/firmware/common/i2c_lpc.h b/firmware/common/i2c_lpc.h index c4f3e8c3..c94ca253 100644 --- a/firmware/common/i2c_lpc.h +++ b/firmware/common/i2c_lpc.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __I2C_LPC_H__ -#define __I2C_LPC_H__ +#pragma once #include #include @@ -43,5 +42,3 @@ void i2c_lpc_transfer( uint8_t* const data_rx, const size_t count_rx); bool i2c_probe(i2c_bus_t* const bus, const uint_fast8_t device_address); - -#endif /*__I2C_LPC_H__*/ diff --git a/firmware/common/ice40_spi.h b/firmware/common/ice40_spi.h index 92ce6280..ef97acc6 100644 --- a/firmware/common/ice40_spi.h +++ b/firmware/common/ice40_spi.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __ICE40_SPI_H -#define __ICE40_SPI_H +#pragma once #include #include @@ -47,5 +46,3 @@ bool ice40_spi_syscfg_program( uint8_t* buf, size_t (*read_block_cb)(void* ctx), void* read_ctx); - -#endif // __ICE40_SPI_H diff --git a/firmware/common/locking.h b/firmware/common/locking.h index 5620f2f9..54ba7a70 100644 --- a/firmware/common/locking.h +++ b/firmware/common/locking.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __LOCKING_H__ -#define __LOCKING_H__ +#pragma once #include @@ -51,5 +50,3 @@ static inline uint32_t store_exclusive(uint32_t val, volatile uint32_t* addr) return 0; } #endif - -#endif /* __LOCKING_H__ */ diff --git a/firmware/common/lz4_blk.h b/firmware/common/lz4_blk.h index 19c8207e..f4e25f7a 100644 --- a/firmware/common/lz4_blk.h +++ b/firmware/common/lz4_blk.h @@ -19,12 +19,9 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __LZ4_BLK_H -#define __LZ4_BLK_H +#pragma once #include #include int lz4_blk_decompress(const uint8_t* src, uint8_t* dst, size_t length); - -#endif \ No newline at end of file diff --git a/firmware/common/lz4_buf.h b/firmware/common/lz4_buf.h index b9dec589..ab073d43 100644 --- a/firmware/common/lz4_buf.h +++ b/firmware/common/lz4_buf.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __LZ4_BUF_H -#define __LZ4_BUF_H +#pragma once #include @@ -31,5 +30,3 @@ extern uint8_t lz4_in_buf[LZ4_BUFFER_SIZE]; extern uint8_t lz4_out_buf[LZ4_BUFFER_SIZE]; - -#endif /*__LZ4_BUF_H */ diff --git a/firmware/common/m0_state.h b/firmware/common/m0_state.h index cd0391fc..b7c26e8b 100644 --- a/firmware/common/m0_state.h +++ b/firmware/common/m0_state.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __M0_STATE_H__ -#define __M0_STATE_H__ +#pragma once #include @@ -60,5 +59,3 @@ enum m0_error { extern volatile struct m0_state m0_state; void m0_set_mode(enum m0_mode mode); - -#endif /*__M0_STATE_H__*/ diff --git a/firmware/common/max2831.h b/firmware/common/max2831.h index 9298247a..a5b58afc 100644 --- a/firmware/common/max2831.h +++ b/firmware/common/max2831.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __MAX2831_H -#define __MAX2831_H +#pragma once #include #include @@ -108,5 +107,3 @@ extern void max2831_tx(max2831_driver_t* const drv); extern void max2831_rx(max2831_driver_t* const drv); extern void max2831_tx_calibration(max2831_driver_t* const drv); extern void max2831_rx_calibration(max2831_driver_t* const drv); - -#endif // __MAX2831_H diff --git a/firmware/common/max2831_regs.def b/firmware/common/max2831_regs.def index fb8fa8a9..fd09707d 100644 --- a/firmware/common/max2831_regs.def +++ b/firmware/common/max2831_regs.def @@ -1,7 +1,27 @@ -/* -*- mode: c -*- */ +/* -*- mode: c -*- + * + * Copyright 2012 Michael Ossmann + * Copyright 2025-2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ -#ifndef __MAX2831_REGS_DEF -#define __MAX2831_REGS_DEF +#pragma once /* Generate static inline accessors that operate on the global * regs. Done this way to (1) allow defs to be scraped out and used @@ -128,5 +148,3 @@ __MREG__(MAX2831_RXIQ_VCM,15,10,2) // RX I/Q output common mode #define MAX2831_RXIQ_VCM_1_2 1 // 1.2V #define MAX2831_RXIQ_VCM_1_3 2 // 1.3V #define MAX2831_RXIQ_VCM_1_45 3 // 1.45V - -#endif // __MAX2831_REGS_DEF \ No newline at end of file diff --git a/firmware/common/max2831_target.h b/firmware/common/max2831_target.h index 11671b73..072faf22 100644 --- a/firmware/common/max2831_target.h +++ b/firmware/common/max2831_target.h @@ -19,12 +19,9 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __MAX2831_TARGET_H -#define __MAX2831_TARGET_H +#pragma once #include "max2831.h" void max2831_target_init(max2831_driver_t* const drv); void max2831_target_set_mode(max2831_driver_t* const drv, const max2831_mode_t new_mode); - -#endif // __MAX2831_TARGET_H diff --git a/firmware/common/max2837.h b/firmware/common/max2837.h index 0c7a65c8..b934b2ed 100644 --- a/firmware/common/max2837.h +++ b/firmware/common/max2837.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __MAX2837_H -#define __MAX2837_H +#pragma once #include #include @@ -91,5 +90,3 @@ bool max2837_set_txvga_gain(max2837_driver_t* const drv, const uint32_t gain_db) extern void max2837_tx(max2837_driver_t* const drv); extern void max2837_rx(max2837_driver_t* const drv); - -#endif // __MAX2837_H diff --git a/firmware/common/max2837_regs.def b/firmware/common/max2837_regs.def index 0aa2f9e5..67eb6dd7 100644 --- a/firmware/common/max2837_regs.def +++ b/firmware/common/max2837_regs.def @@ -1,7 +1,26 @@ -/* -*- mode: c -*- */ +/* -*- mode: c -*- * + * Copyright 2012 Michael Ossmann + * Copyright 2025-2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ -#ifndef __MAX2837_REGS_DEF -#define __MAX2837_REGS_DEF +#pragma once /* Generate static inline accessors that operate on the global * regs. Done this way to (1) allow defs to be scraped out and used @@ -404,5 +423,3 @@ __MREG__(MAX2837_FUSE_RTH,30,9,1) // 0 -> 992/0uA correction, 15 -> 0/992uA correction ... if TX_DCCORR_SPI_EN __MREG__(MAX2837_TX_DCCORR_I,31,4,5) __MREG__(MAX2837_TX_DCCORR_Q,31,9,5) - -#endif // __MAX2837_REGS_DEF diff --git a/firmware/common/max2837_target.h b/firmware/common/max2837_target.h index 75d87698..2910791e 100644 --- a/firmware/common/max2837_target.h +++ b/firmware/common/max2837_target.h @@ -21,12 +21,9 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __MAX2837_TARGET_H -#define __MAX2837_TARGET_H +#pragma once #include "max2837.h" void max2837_target_init(max2837_driver_t* const drv); void max2837_target_set_mode(max2837_driver_t* const drv, const max2837_mode_t new_mode); - -#endif // __MAX2837_TARGET_H diff --git a/firmware/common/max2839.h b/firmware/common/max2839.h index ac238fe2..3647d067 100644 --- a/firmware/common/max2839.h +++ b/firmware/common/max2839.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __MAX2839_H -#define __MAX2839_H +#pragma once #include #include @@ -93,5 +92,3 @@ bool max2839_set_txvga_gain(max2839_driver_t* const drv, const uint32_t gain_db) extern void max2839_tx(max2839_driver_t* const drv); extern void max2839_rx(max2839_driver_t* const drv); - -#endif // __MAX2839_H diff --git a/firmware/common/max2839_regs.def b/firmware/common/max2839_regs.def index bee66846..11b1d80d 100644 --- a/firmware/common/max2839_regs.def +++ b/firmware/common/max2839_regs.def @@ -1,7 +1,26 @@ -/* -*- mode: c -*- */ +/* -*- mode: c -*- * + * Copyright 2012 Michael Ossmann + * Copyright 2025-2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ -#ifndef __MAX2839_REGS_DEF -#define __MAX2839_REGS_DEF +#pragma once /* Generate static inline accessors that operate on the global * regs. Done this way to (1) allow defs to be scraped out and used @@ -255,5 +274,3 @@ __MREG__(MAX2839_PA_DAC_Voltage_Mode_Output_Select,30,9,1) __MREG__(MAX2839_TX_DC_Offset_Correction_QChannel,31,5,6) __MREG__(MAX2839_RESERVED_31_8,31,8,3) __MREG__(MAX2839_PA_DAC_Clk_Divide_Ratio,31,9,1) - -#endif // __MAX2839_REGS_DEF diff --git a/firmware/common/max2839_target.h b/firmware/common/max2839_target.h index 89488243..0c21e98d 100644 --- a/firmware/common/max2839_target.h +++ b/firmware/common/max2839_target.h @@ -21,12 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __MAX2839_TARGET_H -#define __MAX2839_TARGET_H - #include "max2839.h" void max2839_target_init(max2839_driver_t* const drv); void max2839_target_set_mode(max2839_driver_t* const drv, const max2839_mode_t new_mode); - -#endif // __MAX2839_TARGET_H diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h index e67bf8de..a96c908f 100644 --- a/firmware/common/max283x.h +++ b/firmware/common/max283x.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __MAX283x_H -#define __MAX283x_H +#pragma once #include #include @@ -126,5 +125,3 @@ void max283x_set_rx_hpf_frequency( /* Perform MAX2831 TX and RX calibration. */ void max283x_tx_calibration(max283x_driver_t* const drv); void max283x_rx_calibration(max283x_driver_t* const drv); - -#endif // __MAX283x_H diff --git a/firmware/common/max2871.h b/firmware/common/max2871.h index 3cc9bb3a..eb1ecbf0 100644 --- a/firmware/common/max2871.h +++ b/firmware/common/max2871.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef MAX2871_H -#define MAX2871_H +#pragma once #include @@ -39,4 +38,3 @@ extern void max2871_setup(max2871_driver_t* const drv); extern uint64_t max2871_set_frequency(max2871_driver_t* const drv, uint16_t mhz); extern void max2871_enable(max2871_driver_t* const drv); extern void max2871_disable(max2871_driver_t* const drv); -#endif diff --git a/firmware/common/max2871_regs.h b/firmware/common/max2871_regs.h index a18f6217..b0dbdacf 100644 --- a/firmware/common/max2871_regs.h +++ b/firmware/common/max2871_regs.h @@ -19,8 +19,8 @@ * Boston, MA 02110-1301, USA. */ -#ifndef MAX2871_REGS_H -#define MAX2871_REGS_H +#pragma once + #include #define MAX2871_VASA (1 << 9) @@ -75,4 +75,3 @@ void max2871_set_F01(uint32_t v); void max2871_set_LD(uint32_t v); void max2871_set_ADCS(uint32_t v); void max2871_set_ADCM(uint32_t v); -#endif diff --git a/firmware/common/max5864.h b/firmware/common/max5864.h index 0ce2c6e1..fbac7af5 100644 --- a/firmware/common/max5864.h +++ b/firmware/common/max5864.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __MAX5864_H -#define __MAX5864_H +#pragma once #include "spi_bus.h" @@ -41,5 +40,3 @@ void max5864_idle(max5864_driver_t* const drv); void max5864_rx(max5864_driver_t* const drv); void max5864_tx(max5864_driver_t* const drv); void max5864_xcvr(max5864_driver_t* const drv); - -#endif // __MAX5864_H diff --git a/firmware/common/max5864_target.h b/firmware/common/max5864_target.h index 2ddd9ed1..a162e0ac 100644 --- a/firmware/common/max5864_target.h +++ b/firmware/common/max5864_target.h @@ -20,11 +20,8 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __MAX5864_TARGET_H__ -#define __MAX5864_TARGET_H__ +#pragma once #include "max5864.h" void max5864_target_init(max5864_driver_t* const drv); - -#endif /*__MAX5864_TARGET_H__*/ diff --git a/firmware/common/mixer.h b/firmware/common/mixer.h index bfd638bb..d2eb57e9 100644 --- a/firmware/common/mixer.h +++ b/firmware/common/mixer.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __MIXER_H -#define __MIXER_H +#pragma once #if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) #include "rffc5071.h" @@ -41,5 +40,3 @@ extern uint64_t mixer_set_frequency(mixer_driver_t* const mixer, uint64_t hz); extern void mixer_enable(mixer_driver_t* const mixer); extern void mixer_disable(mixer_driver_t* const mixer); extern void mixer_set_gpo(mixer_driver_t* const drv, uint8_t gpo); - -#endif // __MIXER_H diff --git a/firmware/common/operacake.h b/firmware/common/operacake.h index 39da6ef0..b604c513 100644 --- a/firmware/common/operacake.h +++ b/firmware/common/operacake.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __OPERACAKE_H -#define __OPERACAKE_H +#pragma once #ifdef __cplusplus extern "C" { @@ -57,5 +56,3 @@ uint16_t gpio_test(uint8_t address); #ifdef __cplusplus } #endif - -#endif /* __OPERACAKE_H */ diff --git a/firmware/common/operacake_sctimer.h b/firmware/common/operacake_sctimer.h index 805b3cf9..04c86e92 100644 --- a/firmware/common/operacake_sctimer.h +++ b/firmware/common/operacake_sctimer.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __OPERACAKE_SCTIMER_H -#define __OPERACAKE_SCTIMER_H +#pragma once #include #include @@ -37,5 +36,3 @@ void operacake_sctimer_enable(bool enable); void operacake_sctimer_set_dwell_times(struct operacake_dwell_times* times, int n); void operacake_sctimer_stop(void); void operacake_sctimer_reset_state(void); - -#endif /* __OPERACAKE_SCTIMER_H */ diff --git a/firmware/common/platform_detect.h b/firmware/common/platform_detect.h index f02e733b..4f0f0f43 100644 --- a/firmware/common/platform_detect.h +++ b/firmware/common/platform_detect.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __PLATFORM_DETECT_H__ -#define __PLATFORM_DETECT_H__ +#pragma once #include @@ -76,5 +75,3 @@ void detect_hardware_platform(void); board_id_t detected_platform(void); board_rev_t detected_revision(void); uint32_t supported_platform(void); - -#endif //__PLATFORM_DETECT_H__ diff --git a/firmware/common/platform_gpio.h b/firmware/common/platform_gpio.h index e327e4ba..14f0c067 100644 --- a/firmware/common/platform_gpio.h +++ b/firmware/common/platform_gpio.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __PLATFORM_GPIO_H -#define __PLATFORM_GPIO_H +#pragma once #ifdef __cplusplus extern "C" { @@ -369,5 +368,3 @@ static const struct gpio GPIO7_25 = GPIO(7, 25); #ifdef __cplusplus } #endif - -#endif /* __PLATFORM_GPIO_H */ diff --git a/firmware/common/platform_scu.h b/firmware/common/platform_scu.h index 793eefa5..2b4b42f0 100644 --- a/firmware/common/platform_scu.h +++ b/firmware/common/platform_scu.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __PLATFORM_SCU_H -#define __PLATFORM_SCU_H +#pragma once #ifdef __cplusplus extern "C" { @@ -307,5 +306,3 @@ const platform_scu_t* platform_scu(void); #ifdef __cplusplus } #endif - -#endif /* __PLATFORM_SCU_H */ diff --git a/firmware/common/portapack.h b/firmware/common/portapack.h index d4dda66e..1a43af10 100644 --- a/firmware/common/portapack.h +++ b/firmware/common/portapack.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __PORTAPACK_H__ -#define __PORTAPACK_H__ +#pragma once #include #include @@ -85,5 +84,3 @@ void portapack_draw_bitmap( const ui_color_t background); ui_bitmap_t portapack_font_glyph(const ui_font_t* const font, const char c); - -#endif /*__PORTAPACK_H__*/ diff --git a/firmware/common/rad1o/decoder.h b/firmware/common/rad1o/decoder.h index 5f1bcda4..5575ef93 100644 --- a/firmware/common/rad1o/decoder.h +++ b/firmware/common/rad1o/decoder.h @@ -1,8 +1,5 @@ -#ifndef __RAD1O_DECODER_H__ -#define __RAD1O_DECODER_H__ +#pragma once #include uint8_t* rad1o_pk_decode(const uint8_t* data, int* len); - -#endif diff --git a/firmware/common/rad1o/display.h b/firmware/common/rad1o/display.h index 63711dfc..af5da5e1 100644 --- a/firmware/common/rad1o/display.h +++ b/firmware/common/rad1o/display.h @@ -1,5 +1,4 @@ -#ifndef __RAD1O_DISPLAY_H__ -#define __RAD1O_DISPLAY_H__ +#pragma once #include @@ -27,5 +26,3 @@ void rad1o_lcdFill(uint8_t f); void rad1o_lcdDisplay(void); void rad1o_lcdSetPixel(uint8_t x, uint8_t y, uint8_t f); uint8_t* rad1o_lcdGetBuffer(void); - -#endif diff --git a/firmware/common/rad1o/draw.h b/firmware/common/rad1o/draw.h index c8f6708b..dfdaaf68 100644 --- a/firmware/common/rad1o/draw.h +++ b/firmware/common/rad1o/draw.h @@ -1,9 +1,6 @@ -#ifndef __RAD1O_DRAW_H__ -#define __RAD1O_DRAW_H__ +#pragma once #include void rad1o_drawHLine(uint8_t y, uint8_t x1, uint8_t x2, uint8_t color); void rad1o_drawVLine(uint8_t x, uint8_t y1, uint8_t y2, uint8_t color); - -#endif diff --git a/firmware/common/rad1o/fonts.h b/firmware/common/rad1o/fonts.h index 98e0a7ac..bd1ce0b5 100644 --- a/firmware/common/rad1o/fonts.h +++ b/firmware/common/rad1o/fonts.h @@ -1,5 +1,4 @@ -#ifndef __RAD1O_FONTS_H__ -#define __RAD1O_FONTS_H__ +#pragma once #include @@ -31,5 +30,3 @@ typedef const struct FONT_DEF* FONT; #define FONT_DEFAULT 0 #define FONT_INTERNAL 1 #define FONT_EXTERNAL 2 - -#endif diff --git a/firmware/common/rad1o/print.h b/firmware/common/rad1o/print.h index 19ef1631..8981da25 100644 --- a/firmware/common/rad1o/print.h +++ b/firmware/common/rad1o/print.h @@ -1,5 +1,4 @@ -#ifndef __RAD1O_PRINT_H__ -#define __RAD1O_PRINT_H__ +#pragma once #include @@ -9,5 +8,3 @@ void rad1o_lcdClear(void); void rad1o_lcdMoveCrsr(int32_t dx, int32_t dy); void rad1o_lcdSetCrsr(int32_t dx, int32_t dy); void rad1o_setSystemFont(void); - -#endif diff --git a/firmware/common/rad1o/render.h b/firmware/common/rad1o/render.h index 1c623381..8d97b6e1 100644 --- a/firmware/common/rad1o/render.h +++ b/firmware/common/rad1o/render.h @@ -1,5 +1,4 @@ -#ifndef __RENDER_H_ -#define __RENDER_H_ +#pragma once #include @@ -10,4 +9,4 @@ void rad1o_setIntFont(const struct FONT_DEF* font); int rad1o_getFontHeight(void); int rad1o_DoString(int sx, int sy, const char* s); int rad1o_DoChar(int sx, int sy, int c); -#endif + diff --git a/firmware/common/rad1o/smallfonts.h b/firmware/common/rad1o/smallfonts.h index af69bf27..3204e62a 100644 --- a/firmware/common/rad1o/smallfonts.h +++ b/firmware/common/rad1o/smallfonts.h @@ -35,12 +35,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /**************************************************************************/ -#ifndef __RAD1O_SMALLFONTS_H__ -#define __RAD1O_SMALLFONTS_H__ + +#pragma once /* Partially based on original code for the KS0108 by Stephane Rey */ /* Current version by Kevin Townsend */ extern const struct FONT_DEF Font_7x8; - -#endif diff --git a/firmware/common/rad1o/ubuntu18.h b/firmware/common/rad1o/ubuntu18.h index 38b58e76..b8d28b07 100644 --- a/firmware/common/rad1o/ubuntu18.h +++ b/firmware/common/rad1o/ubuntu18.h @@ -1,6 +1,3 @@ -#ifndef __RAD1O_UBUNTU18_H__ -#define __RAD1O_UBUNTU18_H__ +#pragma once extern const struct FONT_DEF Font_Ubuntu18pt; - -#endif diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 7b526a9d..d384f189 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __RF_CONFIG_H__ -#define __RF_CONFIG_H__ +#pragma once #include #include @@ -254,5 +253,3 @@ bool radio_update(radio_t* const radio); * the request bank for the new mode. */ void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode); - -#endif /*__RF_CONFIG_H__*/ diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h index 2d098d86..ff912fe0 100644 --- a/firmware/common/rf_path.h +++ b/firmware/common/rf_path.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __RFPATH_H__ -#define __RFPATH_H__ +#pragma once #include @@ -96,5 +95,3 @@ void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter) void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable); void rf_path_set_antenna(rf_path_t* const rf_path, const uint_fast8_t enable); - -#endif /*__RFPATH_H__*/ diff --git a/firmware/common/rffc5071.h b/firmware/common/rffc5071.h index 506f1a09..1ddf4728 100644 --- a/firmware/common/rffc5071.h +++ b/firmware/common/rffc5071.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __RFFC5071_H -#define __RFFC5071_H +#pragma once #include #include @@ -71,5 +70,3 @@ extern void rffc5071_set_gpo(rffc5071_driver_t* const drv, uint8_t); extern bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state); #endif extern bool rffc5071_check_lock(rffc5071_driver_t* const drv); - -#endif // __RFFC5071_H diff --git a/firmware/common/rffc5071_regs.def b/firmware/common/rffc5071_regs.def index 022010c0..dd352a03 100644 --- a/firmware/common/rffc5071_regs.def +++ b/firmware/common/rffc5071_regs.def @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __RFFC5071_REGS_DEF -#define __RFFC5071_REGS_DEF +#pragma once #define RFFC5071_REG_SET_CLEAN(_d, _r) (_d->regs_dirty &= ~(1UL<<_r)) #define RFFC5071_REG_SET_DIRTY(_d, _r) (_d->regs_dirty |= (1UL<<_r)) @@ -257,5 +256,3 @@ __MREG__(RFFC5071_LFSR,30,9,1) __MREG__(RFFC5071_TSEL,30,10,2) __MREG__(RFFC5071_TMUX,30,12,3) __MREG__(RFFC5071_TEN,30,15,1) - -#endif // __RFFC5071_REGS_DEF diff --git a/firmware/common/rffc5071_spi.h b/firmware/common/rffc5071_spi.h index 7df2d8bf..ab10b47b 100644 --- a/firmware/common/rffc5071_spi.h +++ b/firmware/common/rffc5071_spi.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __RFFC5071_SPI_H -#define __RFFC5071_SPI_H +#pragma once #include @@ -41,5 +40,3 @@ void rffc5071_spi_transfer_gather( spi_bus_t* const bus, const spi_transfer_t* const transfer, const size_t count); - -#endif // __RFFC5071_SPI_H diff --git a/firmware/common/rom_iap.h b/firmware/common/rom_iap.h index 50cfcf93..8b027342 100644 --- a/firmware/common/rom_iap.h +++ b/firmware/common/rom_iap.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __ROM_IAP__ -#define __ROM_IAP__ +#pragma once #include #include @@ -93,7 +92,7 @@ typedef enum INVALID_FLASH_UNIT = 0x00000014, /* Invalid flash unit. */ USER_CODE_CHECKSUM = 0x00000015, ERROR_SETTING_ACTIVE_PARTITION = 0x00000016, - + /* Special Error */ ERROR_IAP_NOT_IMPLEMENTED = 0x00000100 /* IAP is not implemented in this part */ } isp_iap_ret_code_t; @@ -118,5 +117,3 @@ typedef struct { bool iap_is_implemented(void); isp_iap_ret_code_t iap_cmd_call(iap_cmd_res_t* iap_cmd_res); - -#endif //__ROM_IAP__ diff --git a/firmware/common/sct.h b/firmware/common/sct.h index 61c43199..5bedfa75 100644 --- a/firmware/common/sct.h +++ b/firmware/common/sct.h @@ -19,6 +19,8 @@ * Boston, MA 02110-1301, USA. */ +#pragma once + #include #include diff --git a/firmware/common/selftest.h b/firmware/common/selftest.h index 86ee2213..28e02415 100644 --- a/firmware/common/selftest.h +++ b/firmware/common/selftest.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __SELFTEST_H -#define __SELFTEST_H +#pragma once #include #include @@ -76,5 +75,3 @@ typedef struct { } selftest_t; extern selftest_t selftest; - -#endif // __SELFTEST_H diff --git a/firmware/common/sgpio.h b/firmware/common/sgpio.h index 08d7d222..f1cf5a0f 100644 --- a/firmware/common/sgpio.h +++ b/firmware/common/sgpio.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __SGPIO_H__ -#define __SGPIO_H__ +#pragma once #include #include @@ -50,5 +49,3 @@ void sgpio_cpld_stream_disable(sgpio_config_t* const config); bool sgpio_cpld_stream_is_enabled(sgpio_config_t* const config); void sgpio_cpld_set_mixer_invert(sgpio_config_t* const config, uint_fast8_t invert); - -#endif //__SGPIO_H__ diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index d151a74d..8168477e 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __SI5351C_H -#define __SI5351C_H +#pragma once #ifdef __cplusplus extern "C" { @@ -115,5 +114,3 @@ void si5351c_set_phase( #ifdef __cplusplus } #endif - -#endif /* __SI5351C_H */ diff --git a/firmware/common/spi_bus.h b/firmware/common/spi_bus.h index cd885644..4e6caee2 100644 --- a/firmware/common/spi_bus.h +++ b/firmware/common/spi_bus.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __SPI_BUS_H__ -#define __SPI_BUS_H__ +#pragma once #include @@ -52,5 +51,3 @@ void spi_bus_transfer_gather( spi_bus_t* const bus, const spi_transfer_t* const transfers, const size_t count); - -#endif /*__SPI_BUS_H__*/ diff --git a/firmware/common/spi_ssp.h b/firmware/common/spi_ssp.h index 2325f49f..64cd2ee1 100644 --- a/firmware/common/spi_ssp.h +++ b/firmware/common/spi_ssp.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __SPI_SSP_H__ -#define __SPI_SSP_H__ +#pragma once #include #include @@ -46,5 +45,3 @@ void spi_ssp_transfer_gather( spi_bus_t* const bus, const spi_transfer_t* const transfers, const size_t count); - -#endif /*__SPI_SSP_H__*/ diff --git a/firmware/common/streaming.h b/firmware/common/streaming.h index 47f466c1..47af5120 100644 --- a/firmware/common/streaming.h +++ b/firmware/common/streaming.h @@ -21,12 +21,9 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __STREAMING_H__ -#define __STREAMING_H__ +#pragma once #include void baseband_streaming_enable(sgpio_config_t* const sgpio_config); void baseband_streaming_disable(sgpio_config_t* const sgpio_config); - -#endif /*__STREAMING_H__*/ diff --git a/firmware/common/tune_config.h b/firmware/common/tune_config.h index dd21d028..7fc76af9 100644 --- a/firmware/common/tune_config.h +++ b/firmware/common/tune_config.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __TUNE_CONFIG_H__ -#define __TUNE_CONFIG_H__ +#pragma once #ifdef PRALINE #include "fpga.h" @@ -391,5 +390,3 @@ static const tune_config_t praline_tune_config_rx[] = { }; // clang-format on #endif - -#endif /*__TUNE_CONFIG_H__*/ diff --git a/firmware/common/tuning.h b/firmware/common/tuning.h index b76dbcc3..559638ac 100644 --- a/firmware/common/tuning.h +++ b/firmware/common/tuning.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __TUNING_H__ -#define __TUNING_H__ +#pragma once #include #include @@ -46,5 +45,3 @@ bool tuning_set_frequency( const uint64_t freq, const uint32_t offset); #endif - -#endif /*__TUNING_H__*/ diff --git a/firmware/common/ui_portapack.h b/firmware/common/ui_portapack.h index 0565cf6a..e0695839 100644 --- a/firmware/common/ui_portapack.h +++ b/firmware/common/ui_portapack.h @@ -20,11 +20,8 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __UI_PORTAPACK_H__ -#define __UI_PORTAPACK_H__ +#pragma once #include "hackrf_ui.h" const hackrf_ui_t* portapack_hackrf_ui_init(void) __attribute__((weak)); - -#endif /*__UI_PORTAPACK_H__*/ diff --git a/firmware/common/ui_rad1o.h b/firmware/common/ui_rad1o.h index 5b3e7421..73350d11 100644 --- a/firmware/common/ui_rad1o.h +++ b/firmware/common/ui_rad1o.h @@ -20,11 +20,8 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __UI_RAD1O_H__ -#define __UI_RAD1O_H__ +#pragma once #include "hackrf_ui.h" const hackrf_ui_t* rad1o_ui_setup(void) __attribute__((weak)); - -#endif /*__UI_RAD1O_H__*/ diff --git a/firmware/common/usb.h b/firmware/common/usb.h index ee2ff9b6..9445728a 100644 --- a/firmware/common/usb.h +++ b/firmware/common/usb.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_H__ -#define __USB_H__ +#pragma once #include #include @@ -77,5 +76,3 @@ void usb_endpoint_schedule_append( const usb_endpoint_t* const endpoint, usb_transfer_descriptor_t* const tail_td, usb_transfer_descriptor_t* const new_td); - -#endif //__USB_H__ diff --git a/firmware/common/usb_queue.h b/firmware/common/usb_queue.h index 8793558f..66849362 100644 --- a/firmware/common/usb_queue.h +++ b/firmware/common/usb_queue.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_QUEUE_H__ -#define __USB_QUEUE_H__ +#pragma once #include @@ -82,4 +81,3 @@ void usb_queue_init(usb_queue_t* const queue); void usb_queue_transfer_complete(usb_endpoint_t* const endpoint); -#endif //__USB_QUEUE_H__ diff --git a/firmware/common/usb_request.h b/firmware/common/usb_request.h index 40d5013d..c61d77d6 100644 --- a/firmware/common/usb_request.h +++ b/firmware/common/usb_request.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_REQUEST_H__ -#define __USB_REQUEST_H__ +#pragma once #include "usb_type.h" @@ -61,5 +60,3 @@ void usb_setup_complete(usb_endpoint_t* const endpoint); void usb_control_in_complete(usb_endpoint_t* const endpoint); void usb_control_out_complete(usb_endpoint_t* const endpoint); - -#endif //__USB_REQUEST_H__ diff --git a/firmware/common/usb_standard_request.h b/firmware/common/usb_standard_request.h index e9ea9f69..84a8c458 100644 --- a/firmware/common/usb_standard_request.h +++ b/firmware/common/usb_standard_request.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_STANDARD_REQUEST_H__ -#define __USB_STANDARD_REQUEST_H__ +#pragma once #include #include @@ -50,5 +49,3 @@ usb_transfer_type_t usb_endpoint_descriptor_transfer_type( bool usb_set_configuration( usb_device_t* const device, const uint_fast8_t configuration_number); - -#endif //__USB_STANDARD_REQUEST_H__ diff --git a/firmware/common/usb_type.h b/firmware/common/usb_type.h index b268c6bb..2f68b91f 100644 --- a/firmware/common/usb_type.h +++ b/firmware/common/usb_type.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_TYPE_H__ -#define __USB_TYPE_H__ +#pragma once #include @@ -157,5 +156,3 @@ struct usb_endpoint_t { void (*setup_complete)(usb_endpoint_t* const endpoint); void (*transfer_complete)(usb_endpoint_t* const endpoint); }; - -#endif //__USB_TYPE_H__ diff --git a/firmware/common/w25q80bv.h b/firmware/common/w25q80bv.h index f737afe6..4361f4b7 100644 --- a/firmware/common/w25q80bv.h +++ b/firmware/common/w25q80bv.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __W25Q80BV_H__ -#define __W25Q80BV_H__ +#pragma once #include #include @@ -69,5 +68,3 @@ void w25q80bv_read( uint32_t len, uint8_t* const data); void w25q80bv_clear_status(w25q80bv_driver_t* const drv); - -#endif //__W25Q80BV_H__ diff --git a/firmware/common/w25q80bv_target.h b/firmware/common/w25q80bv_target.h index 2fad7aa3..8da28d25 100644 --- a/firmware/common/w25q80bv_target.h +++ b/firmware/common/w25q80bv_target.h @@ -20,11 +20,8 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __W25Q80BV_TARGET_H__ -#define __W25Q80BV_TARGET_H__ +#pragma once #include "w25q80bv.h" void w25q80bv_target_init(w25q80bv_driver_t* const drv); - -#endif /*__W25Q80BV_TARGET_H__*/ diff --git a/firmware/hackrf_usb/usb_api_adc.h b/firmware/hackrf_usb/usb_api_adc.h index e321267e..ed4e7f93 100644 --- a/firmware/hackrf_usb/usb_api_adc.h +++ b/firmware/hackrf_usb/usb_api_adc.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_ADC_H__ -#define __USB_API_ADC_H__ +#pragma once #include #include @@ -28,5 +27,3 @@ usb_request_status_t usb_vendor_request_adc_read( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); - -#endif // __USB_API_ADC_H__ diff --git a/firmware/hackrf_usb/usb_api_board_info.h b/firmware/hackrf_usb/usb_api_board_info.h index 77e4066d..ec50d7ce 100644 --- a/firmware/hackrf_usb/usb_api_board_info.h +++ b/firmware/hackrf_usb/usb_api_board_info.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_BOARD_INFO_H__ -#define __USB_API_BOARD_INFO_H__ +#pragma once #include @@ -52,5 +51,3 @@ usb_request_status_t usb_vendor_request_read_board_rev( usb_request_status_t usb_vendor_request_read_supported_platform( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); - -#endif /* end of include guard: __USB_API_BOARD_INFO_H__ */ diff --git a/firmware/hackrf_usb/usb_api_cpld.h b/firmware/hackrf_usb/usb_api_cpld.h index f508ea5f..786a9160 100644 --- a/firmware/hackrf_usb/usb_api_cpld.h +++ b/firmware/hackrf_usb/usb_api_cpld.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_CPLD_H__ -#define __USB_API_CPLD_H__ +#pragma once #include #include @@ -32,5 +31,3 @@ void cpld_update(void); usb_request_status_t usb_vendor_request_cpld_checksum( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); - -#endif /* end of include guard: __USB_API_CPLD_H__ */ diff --git a/firmware/hackrf_usb/usb_api_m0_state.h b/firmware/hackrf_usb/usb_api_m0_state.h index 4e7ba2a4..a08188d7 100644 --- a/firmware/hackrf_usb/usb_api_m0_state.h +++ b/firmware/hackrf_usb/usb_api_m0_state.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __M0_STATE_USB_H__ -#define __M0_STATE_USB_H__ +#pragma once #include #include @@ -28,5 +27,3 @@ usb_request_status_t usb_vendor_request_get_m0_state( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); - -#endif /*__M0_STATE_USB_H__*/ diff --git a/firmware/hackrf_usb/usb_api_operacake.h b/firmware/hackrf_usb/usb_api_operacake.h index 6e772ec1..824311aa 100644 --- a/firmware/hackrf_usb/usb_api_operacake.h +++ b/firmware/hackrf_usb/usb_api_operacake.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_OPERACAKE_H__ -#define __USB_API_OPERACAKE_H__ +#pragma once #include #include @@ -53,5 +52,3 @@ usb_request_status_t usb_vendor_request_operacake_get_mode( usb_request_status_t usb_vendor_request_operacake_set_dwell_times( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); - -#endif /* end of include guard: __USB_API_OPERACAKE_H__ */ diff --git a/firmware/hackrf_usb/usb_api_praline.h b/firmware/hackrf_usb/usb_api_praline.h index df96c921..97816373 100644 --- a/firmware/hackrf_usb/usb_api_praline.h +++ b/firmware/hackrf_usb/usb_api_praline.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_PRALINE_H__ -#define __USB_API_PRALINE_H__ +#pragma once #include #include @@ -44,5 +43,3 @@ usb_request_status_t usb_vendor_request_set_narrowband_filter( usb_request_status_t usb_vendor_request_set_fpga_bitstream( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); - -#endif /* end of include guard: __USB_API_PRALINE_H__ */ diff --git a/firmware/hackrf_usb/usb_api_register.h b/firmware/hackrf_usb/usb_api_register.h index c94710cb..24825144 100644 --- a/firmware/hackrf_usb/usb_api_register.h +++ b/firmware/hackrf_usb/usb_api_register.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_REGISTER_H__ -#define __USB_API_REGISTER_H__ +#pragma once #include #include @@ -71,5 +70,3 @@ usb_request_status_t usb_vendor_request_write_radio_reg( usb_request_status_t usb_vendor_request_read_radio_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); - -#endif /* end of include guard: __USB_API_REGISTER_H__ */ diff --git a/firmware/hackrf_usb/usb_api_selftest.h b/firmware/hackrf_usb/usb_api_selftest.h index 69478653..7f5af7da 100644 --- a/firmware/hackrf_usb/usb_api_selftest.h +++ b/firmware/hackrf_usb/usb_api_selftest.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_SELFTEST_H -#define __USB_API_SELFTEST_H +#pragma once #include #include @@ -32,5 +31,3 @@ usb_request_status_t usb_vendor_request_read_selftest( usb_request_status_t usb_vendor_request_test_rtc_osc( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); - -#endif // __USB_API_SELFTEST_H diff --git a/firmware/hackrf_usb/usb_api_spiflash.h b/firmware/hackrf_usb/usb_api_spiflash.h index 726734a2..f8e3209f 100644 --- a/firmware/hackrf_usb/usb_api_spiflash.h +++ b/firmware/hackrf_usb/usb_api_spiflash.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_SPIFLASH_H__ -#define __USB_API_SPIFLASH_H__ +#pragma once #include #include @@ -42,5 +41,3 @@ usb_request_status_t usb_vendor_request_spiflash_status( usb_request_status_t usb_vendor_request_spiflash_clear_status( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); - -#endif /* end of include guard: __USB_API_SPIFLASH_H__ */ diff --git a/firmware/hackrf_usb/usb_api_sweep.h b/firmware/hackrf_usb/usb_api_sweep.h index 95cfe848..cf4c84ad 100644 --- a/firmware/hackrf_usb/usb_api_sweep.h +++ b/firmware/hackrf_usb/usb_api_sweep.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_SWEEP_H__ -#define __USB_API_SWEEP_H__ +#pragma once #include @@ -38,5 +37,3 @@ usb_request_status_t usb_vendor_request_init_sweep( const usb_transfer_stage_t stage); void sweep_mode(uint32_t seq); - -#endif /* __USB_API_SWEEP_H__ */ diff --git a/firmware/hackrf_usb/usb_api_transceiver.h b/firmware/hackrf_usb/usb_api_transceiver.h index e28e6bbb..02385acf 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.h +++ b/firmware/hackrf_usb/usb_api_transceiver.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_TRANSCEIVER_H__ -#define __USB_API_TRANSCEIVER_H__ +#pragma once #include @@ -83,5 +82,3 @@ void transceiver_shutdown(void); void rx_mode(uint32_t seq); void tx_mode(uint32_t seq); void off_mode(uint32_t seq); - -#endif /*__USB_API_TRANSCEIVER_H__*/ diff --git a/firmware/hackrf_usb/usb_api_ui.h b/firmware/hackrf_usb/usb_api_ui.h index 62eecfa7..56688ca1 100644 --- a/firmware/hackrf_usb/usb_api_ui.h +++ b/firmware/hackrf_usb/usb_api_ui.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_API_UI_H__ -#define __USB_API_UI_H__ +#pragma once #include #include @@ -29,5 +28,3 @@ usb_request_status_t usb_vendor_request_set_ui_enable( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); - -#endif /* end of include guard: __USB_API_UI_H__ */ diff --git a/firmware/hackrf_usb/usb_bulk_buffer.h b/firmware/hackrf_usb/usb_bulk_buffer.h index f3021bf5..e21a84ff 100644 --- a/firmware/hackrf_usb/usb_bulk_buffer.h +++ b/firmware/hackrf_usb/usb_bulk_buffer.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_BULK_BUFFER_H__ -#define __USB_BULK_BUFFER_H__ +#pragma once #include @@ -34,5 +33,3 @@ * unless you also adjust the ldscripts. */ extern uint8_t usb_bulk_buffer[USB_BULK_BUFFER_SIZE]; - -#endif /*__USB_BULK_BUFFER_H__*/ diff --git a/firmware/hackrf_usb/usb_descriptor.h b/firmware/hackrf_usb/usb_descriptor.h index 2c2990e0..097ccce0 100644 --- a/firmware/hackrf_usb/usb_descriptor.h +++ b/firmware/hackrf_usb/usb_descriptor.h @@ -20,6 +20,8 @@ * Boston, MA 02110-1301, USA. */ +#pragma once + #include extern uint8_t usb_descriptor_device[]; diff --git a/firmware/hackrf_usb/usb_device.h b/firmware/hackrf_usb/usb_device.h index 05008dca..79df298d 100644 --- a/firmware/hackrf_usb/usb_device.h +++ b/firmware/hackrf_usb/usb_device.h @@ -21,11 +21,8 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_DEVICE_H__ -#define __USB_DEVICE_H__ +#pragma once #include extern usb_device_t usb_device; - -#endif /* end of include guard: __USB_DEVICE_H__ */ diff --git a/firmware/hackrf_usb/usb_endpoint.h b/firmware/hackrf_usb/usb_endpoint.h index acb86ab5..e96094f6 100644 --- a/firmware/hackrf_usb/usb_endpoint.h +++ b/firmware/hackrf_usb/usb_endpoint.h @@ -21,8 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_ENDPOINT_H__ -#define __USB_ENDPOINT_H__ +#pragma once #include #include @@ -38,5 +37,3 @@ extern USB_DECLARE_QUEUE(usb_endpoint_bulk_in); extern usb_endpoint_t usb_endpoint_bulk_out; extern USB_DECLARE_QUEUE(usb_endpoint_bulk_out); - -#endif /* end of include guard: __USB_ENDPOINT_H__ */ From 5f8601b8c888a66b9d04683e3f71fdc9ca54faf7 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Fri, 20 Mar 2026 10:18:09 +0200 Subject: [PATCH 010/173] host: replace include guards with #pragma once --- firmware/common/rad1o/render.h | 1 - firmware/common/usb_queue.h | 1 - host/libhackrf/src/hackrf.h | 5 +---- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/firmware/common/rad1o/render.h b/firmware/common/rad1o/render.h index 8d97b6e1..535ee471 100644 --- a/firmware/common/rad1o/render.h +++ b/firmware/common/rad1o/render.h @@ -9,4 +9,3 @@ void rad1o_setIntFont(const struct FONT_DEF* font); int rad1o_getFontHeight(void); int rad1o_DoString(int sx, int sy, const char* s); int rad1o_DoChar(int sx, int sy, int c); - diff --git a/firmware/common/usb_queue.h b/firmware/common/usb_queue.h index 66849362..33f6def2 100644 --- a/firmware/common/usb_queue.h +++ b/firmware/common/usb_queue.h @@ -80,4 +80,3 @@ int usb_transfer_schedule_ack(const usb_endpoint_t* const endpoint); void usb_queue_init(usb_queue_t* const queue); void usb_queue_transfer_complete(usb_endpoint_t* const endpoint); - diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index e00ba3ff..eeca65e0 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -21,8 +21,7 @@ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABI ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __HACKRF_H__ -#define __HACKRF_H__ +#pragma once #include #include @@ -2389,5 +2388,3 @@ extern ADDAPI int ADDCALL hackrf_radio_write_register( #ifdef __cplusplus } // __cplusplus defined. #endif - -#endif /*__HACKRF_H__*/ From e9986d4601624ecc0dc56408a450e9129770c21f Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Wed, 1 Apr 2026 10:33:44 +0200 Subject: [PATCH 011/173] repo: add include-what-you-use mapping file for libopencm3 --- firmware/common/radio.c | 1 - firmware/common/usb.c | 1 - firmware/hackrf_usb/hackrf_usb.c | 3 +-- firmware/hackrf_usb/usb_api_sweep.c | 1 - firmware/libopencm3.imp | 11 +++++++++++ 5 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 firmware/libopencm3.imp diff --git a/firmware/common/radio.c b/firmware/common/radio.c index cb441a64..d22ba700 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -22,7 +22,6 @@ #include #include -#include #include "fixed_point.h" #include "hackrf_core.h" diff --git a/firmware/common/usb.c b/firmware/common/usb.c index 64cd6c08..026b7ec9 100644 --- a/firmware/common/usb.c +++ b/firmware/common/usb.c @@ -25,7 +25,6 @@ #include #include -#include #include #include diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 6170ce6d..3126ac0c 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -25,9 +25,8 @@ #include #include -#include -#include #include +#include #include #include diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index 782f69cd..2ad3203d 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -24,7 +24,6 @@ #include #include -#include #include #include diff --git a/firmware/libopencm3.imp b/firmware/libopencm3.imp new file mode 100644 index 00000000..1cf19631 --- /dev/null +++ b/firmware/libopencm3.imp @@ -0,0 +1,11 @@ +# include-what-you-use mapping file for libopencm3/ +# +# Usage: include-what-you-use -Xiwyu;--mapping_file=libopencm3.imp + +[ + # allow libopencm3 to take responsibility for architecture-specific dispatch of nvic.h includes + { "include": ["", "private", "", "public"] }, + { "include": ["\"libopencm3/lpc43xx/m0/nvic.h\"", "private", "", "public"] }, + { "include": ["", "private", "", "public"] }, + { "include": ["\"libopencm3/lpc43xx/m4/nvic.h\"", "private", "", "public"] }, +] From 1c29079faf82ea4fff80a2b50ea4069222b4cb2f Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Wed, 1 Apr 2026 19:42:32 -0400 Subject: [PATCH 012/173] Reset only sample clock outputs when changing rate --- firmware/common/hackrf_core.c | 4 +- firmware/common/si5351c.c | 77 ++++++++++++++++++++++++++--------- firmware/common/si5351c.h | 14 ++++--- 3 files changed, 69 insertions(+), 26 deletions(-) diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 8db0e855..241c2e3d 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -325,7 +325,7 @@ fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program) } /* Reset PLL to synchronize output clock phase. */ - si5351c_reset_pll(&clock_gen); + si5351c_reset_pll(&clock_gen, SI5351C_PLL_A); #endif if (streaming) { @@ -622,7 +622,7 @@ void clock_gen_init(void) si5351c_configure_clock_control(&clock_gen); si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL); // soft reset - si5351c_reset_pll(&clock_gen); + si5351c_reset_pll(&clock_gen, SI5351C_PLL_BOTH); si5351c_enable_clock_outputs(&clock_gen); } diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 8b6e52fd..ceb9f4a9 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -41,6 +41,7 @@ static enum pll_sources active_clock_source = PLL_SOURCE_UNINITIALIZED; /* External clock output default is deactivated as it creates noise */ static bool clkout_enabled = false; +static uint8_t outputs_disabled = 0xff; /* write to single register */ void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val) @@ -73,7 +74,30 @@ void si5351c_write( /* Disable all CLKx outputs. */ void si5351c_disable_all_outputs(si5351c_driver_t* const drv) { - uint8_t data[] = {3, 0xFF}; + outputs_disabled = 0xff; + uint8_t data[] = {3, outputs_disabled}; + si5351c_write(drv, data, sizeof(data)); +} + +/* Disable all CLKx outputs using selected PLL. */ +void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_t pll) +{ + /* + * Bitmask defines outputs using PLL B. Other outputs are assumed to + * use PLL A. + */ + uint8_t pllb_outputs = 0x00; + if (detected_platform() == BOARD_ID_PRALINE) { + pllb_outputs = 0x30; + } + + if (pll & SI5351C_PLL_A) { + outputs_disabled |= ~pllb_outputs; + } + if (pll & SI5351C_PLL_B) { + outputs_disabled |= pllb_outputs; + } + uint8_t data[] = {3, outputs_disabled}; si5351c_write(drv, data, sizeof(data)); } @@ -156,11 +180,19 @@ void si5351c_configure_pll_multisynth( si5351c_write(drv, data, sizeof(data)); } -void si5351c_reset_pll(si5351c_driver_t* const drv) +void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll) { - si5351c_disable_all_outputs(drv); - /* reset PLLA and PLLB */ - uint8_t data[] = {177, 0xA0}; + uint8_t value = 0; + + if (pll & SI5351C_PLL_A) { + value |= 0x20; + } + if (pll & SI5351C_PLL_B) { + value |= 0x80; + } + + si5351c_disable_pll_outputs(drv, pll); + uint8_t data[] = {177, value}; si5351c_write(drv, data, sizeof(data)); delay_us_at_mhz(2000, 204); si5351c_enable_clock_outputs(drv); @@ -202,11 +234,10 @@ void si5351c_configure_multisynth( void si5351c_configure_clock_control(si5351c_driver_t* const drv) { - const uint8_t pll = SI5351C_CLK_PLL_SRC_A; uint8_t clkout_ctrl; if (clkout_enabled) { - clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | + clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA); } else { @@ -217,20 +248,20 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) /* External clock output is kept in current state */ uint8_t data[] = { 16, - SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(pll) | + SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA), - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | + SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) | SI5351C_CLK_INV, - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | + SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA), clkout_ctrl, - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | + SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA) | SI5351C_CLK_INV, - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | + SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA), SI5351C_CLK_POWERDOWN | @@ -239,10 +270,10 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) SI5351C_CLK_INT_MODE /* not connected, but: PLL B int mode */ }; if (detected_platform() == BOARD_ID_HACKRF1_R9) { - data[1] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC_A | + data[1] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA); - data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC_A | + data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); data[3] = clkout_ctrl; @@ -252,20 +283,27 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) } #ifdef PRALINE /* CLK0: AFE_CLK */ - data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(pll) | + data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); /* CLK1: SCT_CLK and FPGA_CLK */ - data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(pll) | + data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA); + /* CLK3: CLKOUT */ + clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | + SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | + SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA); /* CLK4: XCVR_CLK */ - data[5] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | + data[5] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA) | SI5351C_CLK_INV; + data[6] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | + SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | + SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ - data[3] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(pll) | + data[3] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA); } else { @@ -317,6 +355,7 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) SI5351C_CLK_DISABLE(clkout); uint8_t data[] = {SI5351C_REG_OUTPUT_EN, value}; si5351c_write(drv, data, sizeof(data)); + outputs_disabled = value; #if defined(HACKRF_ONE) if (detected_platform() == BOARD_ID_HACKRF1_R9) { @@ -375,7 +414,7 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source } si5351c_configure_pll_multisynth(drv, source); active_clock_source = source; - si5351c_reset_pll(drv); + si5351c_reset_pll(drv, SI5351C_PLL_BOTH); } bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index d151a74d..52438886 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -39,10 +39,6 @@ extern "C" { #define SI5351C_CLK_INT_MODE (1 << 6) #define SI5351C_CLK_FRAC_MODE (0 << 6) -#define SI5351C_CLK_PLL_SRC(x) (x << 5) -#define SI5351C_CLK_PLL_SRC_A 0 -#define SI5351C_CLK_PLL_SRC_B 1 - #define SI5351C_CLK_INV (1 << 4) #define SI5351C_CLK_SRC(x) (x << 2) @@ -60,6 +56,14 @@ extern "C" { #define SI5351C_LOS (1 << 4) #define SI5351C_REVID 0x03 +typedef enum { + SI5351C_PLL_A = 1, + SI5351C_PLL_B = 2, + SI5351C_PLL_BOTH = 3, +} si5351c_pll_t; + +#define SI5351C_CLK_PLL_SRC(x) ((x & SI5351C_PLL_B) << 4) + enum pll_sources { PLL_SOURCE_UNINITIALIZED = -1, PLL_SOURCE_XTAL = 0, @@ -82,7 +86,7 @@ void si5351c_configure_pll_sources( void si5351c_configure_pll_multisynth( si5351c_driver_t* const drv, const enum pll_sources source); -void si5351c_reset_pll(si5351c_driver_t* const drv); +void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll); void si5351c_configure_multisynth( si5351c_driver_t* const drv, const uint_fast8_t ms_number, From 98bdd30197c92d5a77f66a80c27656a13d8fe35f Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 2 Apr 2026 14:11:33 +0200 Subject: [PATCH 013/173] ci: add a includes-check workflow that runs include-what-you-use on the firmware sources --- .github/workflows/includes-check.yml | 74 ++++++++++++++++++++++++++++ firmware/CMakeLists.txt | 7 +++ firmware/include-what-you-use.cmake | 38 ++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 .github/workflows/includes-check.yml create mode 100644 firmware/include-what-you-use.cmake diff --git a/.github/workflows/includes-check.yml b/.github/workflows/includes-check.yml new file mode 100644 index 00000000..f3848754 --- /dev/null +++ b/.github/workflows/includes-check.yml @@ -0,0 +1,74 @@ +name: Check includes +on: [push, pull_request] + +jobs: + includes-check: + runs-on: ubuntu-latest + strategy: + matrix: + board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE'] + cmake: ['3.10.0', 'latest'] + + # Don't cancel all builds when one fails + fail-fast: false + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v2 + with: + cmake-version: ${{ matrix.cmake }} + + - name: Install Arm GNU Toolchain + uses: carlosperate/arm-none-eabi-gcc-action@v1 + + - name: Install dependencies + run: | + python3 -m venv environment && source environment/bin/activate + python3 -m pip install PyYAML + sudo apt update + sudo apt install iwyu + + - name: Build libopencm3 + shell: bash + working-directory: ${{github.workspace}}/firmware/libopencm3/ + run: | + source ../../environment/bin/activate + make + + - name: Create Build Environment + run: cmake -E make_directory ${{github.workspace}}/firmware/build + + - name: Configure CMake + shell: bash + working-directory: ${{github.workspace}}/firmware/build + run: cmake $GITHUB_WORKSPACE/firmware/ -DCMAKE_BUILD_TYPE=Release -DBOARD=${{ matrix.board }} -DCHECK_INCLUDES=1 + + - name: Build + working-directory: ${{github.workspace}}/firmware/build + shell: bash + run: | + source ../../environment/bin/activate + output="$(cmake --build . --config Release 2>&1)" + while IFS= read -r line + do + if [[ "${line}" == "Warning: include-what-you-use"* ]]; then + exit_code=1 + dump=1 + elif [[ "${line}" == "---" ]]; then + dump=0 + fi + if [[ ${dump} == "1" ]]; then + echo "${line}" + fi + done <<< ${output} + if [[ ${exit_code} == "1" ]]; then + echo "Includes check failed for board target: ${{ matrix.board }}" + else + echo "Includes check succeeded for board target: ${{ matrix.board }}" + fi + echo $(include-what-you-use --version) + exit ${exit_code} diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 10c2d09d..611e7870 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -20,10 +20,17 @@ # Top directory CMake project for HackRF firmware +option(CHECK_INCLUDES + "Check firmware sources for unused includes and transitive dependencies. (Requires iwyu)" OFF) + cmake_minimum_required(VERSION 3.10.0) set(CMAKE_TOOLCHAIN_FILE toolchain-arm-cortex-m.cmake) project (hackrf_firmware_all C) +if(CHECK_INCLUDES) + include(include-what-you-use.cmake) +endif() + add_subdirectory(blinky) add_subdirectory(hackrf_usb) diff --git a/firmware/include-what-you-use.cmake b/firmware/include-what-you-use.cmake new file mode 100644 index 00000000..b5c1bc7e --- /dev/null +++ b/firmware/include-what-you-use.cmake @@ -0,0 +1,38 @@ +# Copyright 2026 Great Scott Gadgets +# +# This file is part of HackRF. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +cmake_minimum_required(VERSION 3.10.0) + +find_program(IWYU_COMMAND NAMES include-what-you-use) +if(NOT IWYU_COMMAND) + message(FATAL_ERROR "Could not find the include-what-you-use executable.") +endif() + +# include-what-you-use arguments: +# +# --no_fwd_decls - Disable forward declaration suggestions. +# --mapping_file - Specify mapping files for excluding suggestions. +# +list(APPEND IWYU_COMMAND + -Xiwyu --no_fwd_decls + -Xiwyu --mapping_file=${CMAKE_SOURCE_DIR}/libopencm3.imp) + +set(CMAKE_C_INCLUDE_WHAT_YOU_USE ${IWYU_COMMAND}) +set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${IWYU_COMMAND}) From a06232270eb06db83255127a8e279884e570d324 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 7 Apr 2026 04:34:20 +0100 Subject: [PATCH 014/173] Fix issues with clang-format version detection. Recent versions include a suffix in brackets, e.g: $ clang-format --version Debian clang-format version 21.1.8 (3+b1) Require a specific version rather than a minimum one, due to differences in output between versions. --- tools/reformat-source.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/reformat-source.sh b/tools/reformat-source.sh index 754717c4..525c9afc 100755 --- a/tools/reformat-source.sh +++ b/tools/reformat-source.sh @@ -1,12 +1,14 @@ #!/bin/bash -VERSION=`clang-format --version | grep -o '[^ ]*$' | cut -d '.' -f 1` -if [ "$VERSION" -ge "14" ]; then +REQUIRED_VERSION=14 + +VERSION=`clang-format --version | grep -o 'version [^ ]' | cut -d ' ' -f 2 | cut -d '.' -f 1` +if [ "$VERSION" -eq "$REQUIRED_VERSION" ]; then CLANG_FORMAT=clang-format -elif clang-format-14 --version > /dev/null; then - CLANG_FORMAT=clang-format-14 +elif command -v clang-format-$REQUIRED_VERSION > /dev/null; then + CLANG_FORMAT=clang-format-$REQUIRED_VERSION else - echo "clang-format version 14 or higher is required." + echo "clang-format version $REQUIRED_VERSION is required." exit 1 fi From b11f6b2f7e21a001e27c762d89c9dbea1a0e5ff8 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Fri, 20 Mar 2026 15:19:45 +0200 Subject: [PATCH 015/173] firmware: standardize struct+typedef usage --- firmware/common/cpld_jtag.h | 4 ++-- firmware/common/fault_handler.h | 6 ++---- firmware/common/fpga.h | 7 ++----- firmware/common/gpio_lpc.h | 2 +- firmware/common/i2c_lpc.h | 2 +- firmware/common/ice40_spi.h | 7 ++----- firmware/common/portapack.c | 2 +- firmware/common/portapack.h | 14 +++++++------- firmware/common/radio.h | 2 +- firmware/common/rf_path.h | 2 +- firmware/common/rffc5071_spi.h | 2 +- firmware/common/sgpio.h | 2 +- firmware/common/spi_ssp.h | 2 +- firmware/common/ui_portapack.c | 2 +- 14 files changed, 24 insertions(+), 32 deletions(-) diff --git a/firmware/common/cpld_jtag.h b/firmware/common/cpld_jtag.h index c052334d..60036759 100644 --- a/firmware/common/cpld_jtag.h +++ b/firmware/common/cpld_jtag.h @@ -25,7 +25,7 @@ #include "gpio.h" -typedef struct jtag_gpio_t { +typedef struct { gpio_t gpio_tck; #ifndef PRALINE gpio_t gpio_tms; @@ -38,7 +38,7 @@ typedef struct jtag_gpio_t { #endif } jtag_gpio_t; -typedef struct jtag_t { +typedef struct { jtag_gpio_t* const gpio; } jtag_t; diff --git a/firmware/common/fault_handler.h b/firmware/common/fault_handler.h index d0edd6a5..0b499acb 100644 --- a/firmware/common/fault_handler.h +++ b/firmware/common/fault_handler.h @@ -29,9 +29,7 @@ // TODO: Move all this to a Cortex-M(?) include file, since these // structures are supposedly the same between processors (to an // undetermined extent). -typedef struct armv7m_scb_t armv7m_scb_t; - -struct armv7m_scb_t { +typedef struct { volatile const uint32_t CPUID; volatile uint32_t ICSR; volatile uint32_t VTOR; @@ -63,7 +61,7 @@ struct armv7m_scb_t { volatile const uint32_t ID_ISAR4; volatile const uint32_t __reserved_0x74_0x87[5]; volatile uint32_t CPACR; -} __attribute__((packed)); +} __attribute__((packed)) armv7m_scb_t; static armv7m_scb_t* const SCB = (armv7m_scb_t*) SCB_BASE; diff --git a/firmware/common/fpga.h b/firmware/common/fpga.h index 3ba3b652..4aa54239 100644 --- a/firmware/common/fpga.h +++ b/firmware/common/fpga.h @@ -36,14 +36,11 @@ typedef enum { FPGA_QUARTER_SHIFT_MODE_DOWN = 0b01, } fpga_quarter_shift_mode_t; -struct fpga_driver_t; // IWYU pragma: keep - fixed in #1704 -typedef struct fpga_driver_t fpga_driver_t; - -struct fpga_driver_t { +typedef struct { ice40_spi_driver_t* bus; uint8_t regs[FPGA_NUM_REGS]; uint8_t regs_dirty; -}; +} fpga_driver_t; struct fpga_loader_t { /* Start address added as an offset to all read() calls. */ diff --git a/firmware/common/gpio_lpc.h b/firmware/common/gpio_lpc.h index 68905ef9..0987537f 100644 --- a/firmware/common/gpio_lpc.h +++ b/firmware/common/gpio_lpc.h @@ -29,7 +29,7 @@ * register #defines and API declarations into separate header files. */ -typedef struct gpio_port { +typedef struct { volatile uint32_t dir; /* +0x000 */ uint32_t _reserved0[31]; volatile uint32_t mask; /* +0x080 */ diff --git a/firmware/common/i2c_lpc.h b/firmware/common/i2c_lpc.h index c94ca253..f5b8230e 100644 --- a/firmware/common/i2c_lpc.h +++ b/firmware/common/i2c_lpc.h @@ -28,7 +28,7 @@ #include "i2c_bus.h" -typedef struct i2c_lpc_config_t { +typedef struct { const uint16_t duty_cycle_count; } i2c_lpc_config_t; diff --git a/firmware/common/ice40_spi.h b/firmware/common/ice40_spi.h index ef97acc6..4ceddffa 100644 --- a/firmware/common/ice40_spi.h +++ b/firmware/common/ice40_spi.h @@ -28,15 +28,12 @@ #include "gpio.h" #include "spi_bus.h" -struct ice40_spi_driver_t; // IWYU pragma: keep - fixed in #1704 -typedef struct ice40_spi_driver_t ice40_spi_driver_t; - -struct ice40_spi_driver_t { +typedef struct { spi_bus_t* const bus; gpio_t gpio_select; gpio_t gpio_creset; gpio_t gpio_cdone; -}; +} ice40_spi_driver_t; void ice40_spi_target_init(ice40_spi_driver_t* const drv); uint8_t ice40_spi_read(ice40_spi_driver_t* const drv, uint8_t r); diff --git a/firmware/common/portapack.c b/firmware/common/portapack.c index fefa2f11..455df871 100644 --- a/firmware/common/portapack.c +++ b/firmware/common/portapack.c @@ -38,7 +38,7 @@ static void portapack_sleep_milliseconds(const uint32_t milliseconds) delay(milliseconds * 40800); } -typedef struct portapack_if_t { +typedef struct { gpio_t gpio_dir; gpio_t gpio_lcd_rdx; gpio_t gpio_lcd_wrx; diff --git a/firmware/common/portapack.h b/firmware/common/portapack.h index 1a43af10..fdc369b8 100644 --- a/firmware/common/portapack.h +++ b/firmware/common/portapack.h @@ -28,31 +28,31 @@ #define ARRAY_SIZEOF(x) (sizeof(x) / sizeof(x[0])) -typedef struct ui_color_t { +typedef struct { uint16_t v; } ui_color_t; -typedef struct ui_point_t { +typedef struct { int16_t x; int16_t y; } ui_point_t; -typedef struct ui_size_t { +typedef struct { int16_t width; int16_t height; } ui_size_t; -typedef struct ui_rect_t { +typedef struct { ui_point_t point; ui_size_t size; } ui_rect_t; -typedef struct ui_bitmap_t { +typedef struct { ui_size_t size; const uint8_t* const data; } ui_bitmap_t; -typedef struct ui_font_t { +typedef struct { const ui_size_t glyph_size; const uint8_t* const data; char c_start; @@ -60,7 +60,7 @@ typedef struct ui_font_t { size_t data_stride; } ui_font_t; -typedef struct portapack_t { +typedef struct { } portapack_t; void portapack_init(void); diff --git a/firmware/common/radio.h b/firmware/common/radio.h index d384f189..0c171d47 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -215,7 +215,7 @@ typedef enum { */ typedef fp_40_24_t (*sample_rate_fn)(const fp_40_24_t sample_rate, const bool program); -typedef struct radio_t { +typedef struct { radio_config_mode_t config_mode; uint64_t config[RADIO_NUM_BANKS][RADIO_NUM_REGS]; volatile uint32_t regs_dirty; diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h index ff912fe0..1ac0e243 100644 --- a/firmware/common/rf_path.h +++ b/firmware/common/rf_path.h @@ -45,7 +45,7 @@ typedef enum { RF_PATH_FILTER_HIGH_PASS = 2, } rf_path_filter_t; -typedef struct rf_path_t { +typedef struct { uint8_t switchctrl; #ifdef HACKRF_ONE gpio_t gpio_hp; diff --git a/firmware/common/rffc5071_spi.h b/firmware/common/rffc5071_spi.h index ab10b47b..552d0769 100644 --- a/firmware/common/rffc5071_spi.h +++ b/firmware/common/rffc5071_spi.h @@ -27,7 +27,7 @@ #include "gpio.h" #include "spi_bus.h" -typedef struct rffc5071_spi_config_t { +typedef struct { gpio_t gpio_select; gpio_t gpio_clock; gpio_t gpio_data; diff --git a/firmware/common/sgpio.h b/firmware/common/sgpio.h index f1cf5a0f..a3b6b2eb 100644 --- a/firmware/common/sgpio.h +++ b/firmware/common/sgpio.h @@ -32,7 +32,7 @@ typedef enum { SGPIO_DIRECTION_TX, } sgpio_direction_t; -typedef struct sgpio_config_t { +typedef struct { gpio_t gpio_q_invert; #ifndef PRALINE gpio_t gpio_trigger_enable; diff --git a/firmware/common/spi_ssp.h b/firmware/common/spi_ssp.h index 64cd2ee1..822772b5 100644 --- a/firmware/common/spi_ssp.h +++ b/firmware/common/spi_ssp.h @@ -30,7 +30,7 @@ #include "gpio.h" #include "spi_bus.h" -typedef struct ssp_config_t { +typedef struct { ssp_datasize_t data_bits; ssp_cpol_cpha_t spi_mode; uint8_t serial_clock_rate; diff --git a/firmware/common/ui_portapack.c b/firmware/common/ui_portapack.c index 8a44db04..aaa96cb0 100644 --- a/firmware/common/ui_portapack.c +++ b/firmware/common/ui_portapack.c @@ -393,7 +393,7 @@ static ui_point_t portapack_lcd_draw_string(ui_point_t point, const char* s) return point; } -typedef struct draw_list_t { +typedef struct { const ui_bitmap_t* bitmap; const ui_point_t point; } draw_list_t; From d49b5b1f13379318f06df71ee74aaf437f39a2d5 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Fri, 20 Mar 2026 10:46:24 +0200 Subject: [PATCH 016/173] firmware: standardize recursive struct+typedef usage --- firmware/common/i2c_bus.h | 13 +++++-------- firmware/common/max2831.h | 13 ++++++------- firmware/common/max2837.h | 13 ++++++------- firmware/common/max2839.h | 13 ++++++------- firmware/common/max5864.h | 9 +++------ firmware/common/spi_bus.h | 18 +++++++++--------- firmware/common/usb_queue.h | 12 +++++------- firmware/common/usb_type.h | 14 ++++++-------- firmware/common/w25q80bv.h | 9 +++------ 9 files changed, 49 insertions(+), 65 deletions(-) diff --git a/firmware/common/i2c_bus.h b/firmware/common/i2c_bus.h index 1e85e440..7519ba14 100644 --- a/firmware/common/i2c_bus.h +++ b/firmware/common/i2c_bus.h @@ -25,21 +25,18 @@ #include #include -struct i2c_bus_t; // IWYU pragma: keep - fixed in #1704 -typedef struct i2c_bus_t i2c_bus_t; - -struct i2c_bus_t { +typedef struct _i2c_bus_t { void* const obj; - void (*start)(i2c_bus_t* const bus, const void* const config); - void (*stop)(i2c_bus_t* const bus); + void (*start)(struct _i2c_bus_t* const bus, const void* const config); + void (*stop)(struct _i2c_bus_t* const bus); void (*transfer)( - i2c_bus_t* const bus, + struct _i2c_bus_t* const bus, const uint_fast8_t peripheral_address, const uint8_t* const tx, const size_t tx_count, uint8_t* const rx, const size_t rx_count); -}; +} i2c_bus_t; void i2c_bus_start(i2c_bus_t* const bus, const void* const config); void i2c_bus_stop(i2c_bus_t* const bus); diff --git a/firmware/common/max2831.h b/firmware/common/max2831.h index a5b58afc..18154649 100644 --- a/firmware/common/max2831.h +++ b/firmware/common/max2831.h @@ -47,22 +47,21 @@ typedef enum { MAX2831_RX_HPF_600_KHZ = 3, } max2831_rx_hpf_freq_t; -struct max2831_driver_t; // IWYU pragma: keep - fixed in #1704 -typedef struct max2831_driver_t max2831_driver_t; - -struct max2831_driver_t { +typedef struct _max2831_driver_t { spi_bus_t* bus; gpio_t gpio_enable; gpio_t gpio_rxtx; gpio_t gpio_rxhp; gpio_t gpio_ld; - void (*target_init)(max2831_driver_t* const drv); - void (*set_mode)(max2831_driver_t* const drv, const max2831_mode_t new_mode); + void (*target_init)(struct _max2831_driver_t* const drv); + void (*set_mode)( + struct _max2831_driver_t* const drv, + const max2831_mode_t new_mode); max2831_mode_t mode; uint16_t regs[MAX2831_NUM_REGS]; uint16_t regs_dirty; uint32_t desired_lpf_bw; -}; +} max2831_driver_t; /* Initialize chip. */ extern void max2831_setup(max2831_driver_t* const drv); diff --git a/firmware/common/max2837.h b/firmware/common/max2837.h index b934b2ed..748bafe0 100644 --- a/firmware/common/max2837.h +++ b/firmware/common/max2837.h @@ -40,20 +40,19 @@ typedef enum { MAX2837_MODE_RX } max2837_mode_t; -struct max2837_driver_t; // IWYU pragma: keep - fixed in #1704 -typedef struct max2837_driver_t max2837_driver_t; - -struct max2837_driver_t { +typedef struct _max2837_driver_t { spi_bus_t* bus; gpio_t gpio_enable; gpio_t gpio_rx_enable; gpio_t gpio_tx_enable; - void (*target_init)(max2837_driver_t* const drv); - void (*set_mode)(max2837_driver_t* const drv, const max2837_mode_t new_mode); + void (*target_init)(struct _max2837_driver_t* const drv); + void (*set_mode)( + struct _max2837_driver_t* const drv, + const max2837_mode_t new_mode); max2837_mode_t mode; uint16_t regs[MAX2837_NUM_REGS]; uint32_t regs_dirty; -}; +} max2837_driver_t; /* Initialize chip. */ extern void max2837_setup(max2837_driver_t* const drv); diff --git a/firmware/common/max2839.h b/firmware/common/max2839.h index 3647d067..b6c21194 100644 --- a/firmware/common/max2839.h +++ b/firmware/common/max2839.h @@ -43,19 +43,18 @@ typedef enum { MAX2839_MODE_CLKOUT, } max2839_mode_t; -struct max2839_driver_t; // IWYU pragma: keep - fixed in #1704 -typedef struct max2839_driver_t max2839_driver_t; - -struct max2839_driver_t { +typedef struct _max2839_driver_t { spi_bus_t* bus; gpio_t gpio_enable; gpio_t gpio_rxtx; - void (*target_init)(max2839_driver_t* const drv); - void (*set_mode)(max2839_driver_t* const drv, const max2839_mode_t new_mode); + void (*target_init)(struct _max2839_driver_t* const drv); + void (*set_mode)( + struct _max2839_driver_t* const drv, + const max2839_mode_t new_mode); max2839_mode_t mode; uint16_t regs[MAX2839_NUM_REGS]; uint32_t regs_dirty; -}; +} max2839_driver_t; /* Initialize chip. */ extern void max2839_setup(max2839_driver_t* const drv); diff --git a/firmware/common/max5864.h b/firmware/common/max5864.h index fbac7af5..56d730cc 100644 --- a/firmware/common/max5864.h +++ b/firmware/common/max5864.h @@ -24,13 +24,10 @@ #include "spi_bus.h" -struct max5864_driver_t; // IWYU pragma: keep - fixed in #1704 -typedef struct max5864_driver_t max5864_driver_t; - -struct max5864_driver_t { +typedef struct _max5864_driver_t { spi_bus_t* const bus; - void (*target_init)(max5864_driver_t* const drv); -}; + void (*target_init)(struct _max5864_driver_t* const drv); +} max5864_driver_t; void max5864_setup(max5864_driver_t* const drv); diff --git a/firmware/common/spi_bus.h b/firmware/common/spi_bus.h index 4e6caee2..07d5787d 100644 --- a/firmware/common/spi_bus.h +++ b/firmware/common/spi_bus.h @@ -29,20 +29,20 @@ typedef struct { const size_t count; } spi_transfer_t; -struct spi_bus_t; // IWYU pragma: keep - fixed in #1704 -typedef struct spi_bus_t spi_bus_t; - -struct spi_bus_t { +typedef struct _spi_bus_t { void* const obj; const void* config; - void (*start)(spi_bus_t* const bus, const void* const config); - void (*stop)(spi_bus_t* const bus); - void (*transfer)(spi_bus_t* const bus, void* const data, const size_t count); + void (*start)(struct _spi_bus_t* const bus, const void* const config); + void (*stop)(struct _spi_bus_t* const bus); + void (*transfer)( + struct _spi_bus_t* const bus, + void* const data, + const size_t count); void (*transfer_gather)( - spi_bus_t* const bus, + struct _spi_bus_t* const bus, const spi_transfer_t* const transfers, const size_t count); -}; +} spi_bus_t; void spi_bus_start(spi_bus_t* const bus, const void* const config); void spi_bus_stop(spi_bus_t* const bus); diff --git a/firmware/common/usb_queue.h b/firmware/common/usb_queue.h index 33f6def2..b6134946 100644 --- a/firmware/common/usb_queue.h +++ b/firmware/common/usb_queue.h @@ -29,27 +29,25 @@ #include "usb_type.h" -typedef struct _usb_transfer_t usb_transfer_t; -typedef struct _usb_queue_t usb_queue_t; typedef void (*transfer_completion_cb)(void*, unsigned int); // This is an opaque datatype. Thou shall not touch these members. -struct _usb_transfer_t { +typedef struct _usb_transfer_t { struct _usb_transfer_t* next; usb_transfer_descriptor_t td ATTR_ALIGNED(64); unsigned int maximum_length; struct _usb_queue_t* queue; transfer_completion_cb completion_cb; void* user_data; -}; +} usb_transfer_t; // This is an opaque datatype. Thou shall not touch these members. -struct _usb_queue_t { - struct usb_endpoint_t* endpoint; +typedef struct _usb_queue_t { + usb_endpoint_t* endpoint; const unsigned int pool_size; usb_transfer_t* volatile free_transfers; usb_transfer_t* volatile active; -}; +} usb_queue_t; #define USB_DECLARE_QUEUE(endpoint_name) struct _usb_queue_t endpoint_name##_queue; #define USB_DEFINE_QUEUE(endpoint_name, _pool_size) \ diff --git a/firmware/common/usb_type.h b/firmware/common/usb_type.h index 2f68b91f..44810de5 100644 --- a/firmware/common/usb_type.h +++ b/firmware/common/usb_type.h @@ -144,15 +144,13 @@ typedef struct { uint8_t* wcid_extended_properties_descriptor; } usb_device_t; -typedef struct usb_endpoint_t usb_endpoint_t; - -struct usb_endpoint_t { +typedef struct _usb_endpoint_t { usb_setup_t setup; uint8_t buffer[32]; // Buffer for use during IN stage. const uint_fast8_t address; usb_device_t* const device; - usb_endpoint_t* const in; - usb_endpoint_t* const out; - void (*setup_complete)(usb_endpoint_t* const endpoint); - void (*transfer_complete)(usb_endpoint_t* const endpoint); -}; + struct _usb_endpoint_t* const in; + struct _usb_endpoint_t* const out; + void (*setup_complete)(struct _usb_endpoint_t* const endpoint); + void (*transfer_complete)(struct _usb_endpoint_t* const endpoint); +} usb_endpoint_t; diff --git a/firmware/common/w25q80bv.h b/firmware/common/w25q80bv.h index 4361f4b7..a43d6445 100644 --- a/firmware/common/w25q80bv.h +++ b/firmware/common/w25q80bv.h @@ -39,18 +39,15 @@ typedef union { uint8_t id_8b[8]; /* 8*8bits 64bits Unique ID */ } w25q80bv_unique_id_t; -struct w25q80bv_driver_t; // IWYU pragma: keep - fixed in #1704 -typedef struct w25q80bv_driver_t w25q80bv_driver_t; - -struct w25q80bv_driver_t { +typedef struct _w25q80bv_driver_t { spi_bus_t* bus; gpio_t gpio_hold; gpio_t gpio_wp; - void (*target_init)(w25q80bv_driver_t* const drv); + void (*target_init)(struct _w25q80bv_driver_t* const drv); size_t page_len; size_t num_pages; size_t num_bytes; -}; +} w25q80bv_driver_t; void w25q80bv_setup(w25q80bv_driver_t* const drv); void w25q80bv_get_full_status(w25q80bv_driver_t* const drv, uint8_t* data); From 8018feb1d03d8cc0d00a1371883520ed392be7db Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Wed, 8 Apr 2026 16:15:21 +0200 Subject: [PATCH 017/173] ci: run includes-check workflow on debian:13 --- .github/workflows/includes-check.yml | 36 ++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/workflows/includes-check.yml b/.github/workflows/includes-check.yml index f3848754..4b3dd400 100644 --- a/.github/workflows/includes-check.yml +++ b/.github/workflows/includes-check.yml @@ -1,9 +1,16 @@ name: Check includes on: [push, pull_request] +defaults: + run: + shell: bash + jobs: includes-check: runs-on: ubuntu-latest + container: + image: "debian:13" + options: --user 0 strategy: matrix: board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE'] @@ -13,7 +20,23 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v4 + + - name: Make it work on debian + run: | + apt update + apt install -y git python3 python3-pip python3-venv nodejs + # actions/checkout insists on putting the checkout in the + # working directory rather than ${{ github.workspace }}. + # + # This may just be because the 'runner' user does not exist + # in the docker image at startup. + # + # also see: https://github.com/actions/runner/issues/878 + useradd -ms /bin/bash runner + ln -s /__w /home/runner/work + + - name: Checkout repository + uses: actions/checkout@v6 with: submodules: true @@ -29,11 +52,10 @@ jobs: run: | python3 -m venv environment && source environment/bin/activate python3 -m pip install PyYAML - sudo apt update - sudo apt install iwyu + apt install -y iwyu + iwyu --version - name: Build libopencm3 - shell: bash working-directory: ${{github.workspace}}/firmware/libopencm3/ run: | source ../../environment/bin/activate @@ -43,13 +65,11 @@ jobs: run: cmake -E make_directory ${{github.workspace}}/firmware/build - name: Configure CMake - shell: bash working-directory: ${{github.workspace}}/firmware/build - run: cmake $GITHUB_WORKSPACE/firmware/ -DCMAKE_BUILD_TYPE=Release -DBOARD=${{ matrix.board }} -DCHECK_INCLUDES=1 + run: cmake ${{github.workspace}}/firmware/ -DCMAKE_BUILD_TYPE=Release -DBOARD=${{ matrix.board }} -DCHECK_INCLUDES=1 - name: Build working-directory: ${{github.workspace}}/firmware/build - shell: bash run: | source ../../environment/bin/activate output="$(cmake --build . --config Release 2>&1)" @@ -58,8 +78,10 @@ jobs: if [[ "${line}" == "Warning: include-what-you-use"* ]]; then exit_code=1 dump=1 + echo elif [[ "${line}" == "---" ]]; then dump=0 + echo fi if [[ ${dump} == "1" ]]; then echo "${line}" From c4bc07836471c70611f3e0b317f8d0c093a44aa1 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 9 Apr 2026 19:40:25 +0100 Subject: [PATCH 018/173] CI: switch from actions-setup-cmake to get-cmake. --- .github/workflows/build.yml | 8 ++++---- .github/workflows/includes-check.yml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eaa1d982..551a35d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,9 +60,9 @@ jobs: - uses: actions/checkout@v4 - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v2 + uses: lukka/get-cmake@latest with: - cmake-version: ${{ matrix.cmake }} + cmakeVersion: ${{ matrix.cmake }} if: matrix.sys.shell != 'msys2' - name: Install dependencies (macOS) @@ -175,9 +175,9 @@ jobs: submodules: true - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v2 + uses: lukka/get-cmake@latest with: - cmake-version: ${{ matrix.cmake }} + cmakeVersion: ${{ matrix.cmake }} if: matrix.os != 'windows' - name: Install Arm GNU Toolchain diff --git a/.github/workflows/includes-check.yml b/.github/workflows/includes-check.yml index 4b3dd400..dfb631bd 100644 --- a/.github/workflows/includes-check.yml +++ b/.github/workflows/includes-check.yml @@ -24,7 +24,7 @@ jobs: - name: Make it work on debian run: | apt update - apt install -y git python3 python3-pip python3-venv nodejs + apt install -y git python3 python3-pip python3-venv nodejs unzip # actions/checkout insists on putting the checkout in the # working directory rather than ${{ github.workspace }}. # @@ -41,9 +41,9 @@ jobs: submodules: true - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v2 + uses: lukka/get-cmake@latest with: - cmake-version: ${{ matrix.cmake }} + cmakeVersion: ${{ matrix.cmake }} - name: Install Arm GNU Toolchain uses: carlosperate/arm-none-eabi-gcc-action@v1 From 0cf5cc229828c5a24542dfb749e26b86872e3385 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Mon, 2 Mar 2026 19:04:43 -0500 Subject: [PATCH 019/173] Fix RX IF gain configuration --- firmware/common/radio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index d22ba700..86e5e190 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -521,13 +521,13 @@ static bool radio_update_gain(radio_t* const radio, uint64_t* bank) gain = bank[RADIO_GAIN_RX_IF]; if ((gain != RADIO_UNSET) && - (gain != radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF])) { + (gain != radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF])) { max283x_set_lna_gain(&max283x, gain); - radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] = gain; + radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] = gain; new_gain = true; - } else if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] == RADIO_UNSET) { + } else if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] == RADIO_UNSET) { max283x_set_lna_gain(&max283x, DEFAULT_GAIN_IF); - radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] = DEFAULT_GAIN_IF; + radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] = DEFAULT_GAIN_IF; new_gain = true; } From 693b7e79f7e1962fb43b7d5f963108cbd7738b3d Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 9 Apr 2026 21:46:48 +0100 Subject: [PATCH 020/173] CI: conserve macOS runners by not building firmware for every board. --- .github/workflows/build.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 551a35d4..07b419e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -164,6 +164,15 @@ jobs: exclude: - os: 'windows' cmake: '3.10.0' + # GitHub doesn't give us many macOS runners, so don't build for every board. + # We just need to know that building firmware works on macOS. Use PRALINE + # since that's the most complicated one due to FPGA bitstreams. + - os: 'macos' + board: 'HACKRF_ONE' + - os: 'macos' + board: 'JAWBREAKER' + - os: 'macos' + board: 'RAD1O' # Don't cancel all builds when one fails fail-fast: false From caa47500c5ff76e7794ec63e0a8c2314bb244f2d Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Fri, 3 Apr 2026 06:41:46 -0400 Subject: [PATCH 021/173] Show 0 in MHz column on PortaPack display The PortaPack UI hides leading zeros in the most significant digits. Previously zeros in the 1 MHz column and above were hidden. Now zeros in the 10 MHz column and above are hidden. --- firmware/common/ui_portapack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/common/ui_portapack.c b/firmware/common/ui_portapack.c index aaa96cb0..0ca2c225 100644 --- a/firmware/common/ui_portapack.c +++ b/firmware/common/ui_portapack.c @@ -525,7 +525,7 @@ static void portapack_ui_set_frequency(uint64_t frequency) char s[10]; for (int i = 0; i < 10; i++) { const char c = '0' + value % 10; - s[i] = ((i >= 6) && (value == 0)) ? ' ' : c; + s[i] = ((i >= 7) && (value == 0)) ? ' ' : c; value /= 10; } From 3f51f407fa17439563d23f63f92e2b21191b0fe7 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Wed, 8 Apr 2026 20:55:20 -0400 Subject: [PATCH 022/173] Move transceiver_mode_t to own header file --- firmware/common/hackrf_ui.c | 1 + firmware/common/hackrf_ui.h | 1 + firmware/common/radio.c | 4 ++- firmware/common/radio.h | 10 +------ firmware/common/transceiver_mode.h | 33 +++++++++++++++++++++++ firmware/common/ui_portapack.c | 1 + firmware/common/ui_rad1o.c | 1 + firmware/hackrf_usb/hackrf_usb.c | 1 + firmware/hackrf_usb/usb_api_sweep.c | 1 + firmware/hackrf_usb/usb_api_transceiver.c | 1 + firmware/hackrf_usb/usb_api_transceiver.h | 2 +- 11 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 firmware/common/transceiver_mode.h diff --git a/firmware/common/hackrf_ui.c b/firmware/common/hackrf_ui.c index dbf1cc24..6e230943 100644 --- a/firmware/common/hackrf_ui.c +++ b/firmware/common/hackrf_ui.c @@ -23,6 +23,7 @@ #include #include "hackrf_ui.h" +#include "transceiver_mode.h" #if defined(PRALINE) || defined(HACKRF_ONE) #include "ui_portapack.h" #endif diff --git a/firmware/common/hackrf_ui.h b/firmware/common/hackrf_ui.h index 7b08a597..1193d1c5 100644 --- a/firmware/common/hackrf_ui.h +++ b/firmware/common/hackrf_ui.h @@ -27,6 +27,7 @@ #include "radio.h" #include "rf_path.h" +#include "transceiver_mode.h" typedef void (*hackrf_ui_init_fn)(void); typedef void (*hackrf_ui_deinit_fn)(void); diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 86e5e190..71818f11 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -19,6 +19,8 @@ * Boston, MA 02110-1301, USA. */ +#include "radio.h" + #include #include @@ -28,8 +30,8 @@ #include "hackrf_ui.h" #include "max283x.h" #include "platform_detect.h" -#include "radio.h" #include "rf_path.h" +#include "transceiver_mode.h" #include "tuning.h" #if defined(PRALINE) #include "fpga.h" diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 0c171d47..e75d2d90 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -27,6 +27,7 @@ #include #include "fixed_point.h" +#include "transceiver_mode.h" typedef enum { RADIO_OK = 1, @@ -60,15 +61,6 @@ typedef enum { CLOCK_SOURCE_PORTAPACK = 2, } clock_source_t; -typedef enum { - TRANSCEIVER_MODE_OFF = 0, - TRANSCEIVER_MODE_RX = 1, - TRANSCEIVER_MODE_TX = 2, - TRANSCEIVER_MODE_SS = 3, - TRANSCEIVER_MODE_CPLD_UPDATE = 4, - TRANSCEIVER_MODE_RX_SWEEP = 5, -} transceiver_mode_t; - /** * Configurable registers stored as uint64_t. Any register may be set to * RADIO_UNSET. When not RADIO_UNSET, some registers are read as a specific type diff --git a/firmware/common/transceiver_mode.h b/firmware/common/transceiver_mode.h new file mode 100644 index 00000000..dccb71b2 --- /dev/null +++ b/firmware/common/transceiver_mode.h @@ -0,0 +1,33 @@ +/* + * Copyright 2012-2026 Great Scott Gadgets + * Copyright 2012 Jared Boone + * Copyright 2013 Benjamin Vernoux + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +typedef enum { + TRANSCEIVER_MODE_OFF = 0, + TRANSCEIVER_MODE_RX = 1, + TRANSCEIVER_MODE_TX = 2, + TRANSCEIVER_MODE_SS = 3, + TRANSCEIVER_MODE_CPLD_UPDATE = 4, + TRANSCEIVER_MODE_RX_SWEEP = 5, +} transceiver_mode_t; diff --git a/firmware/common/ui_portapack.c b/firmware/common/ui_portapack.c index 0ca2c225..4e862ca4 100644 --- a/firmware/common/ui_portapack.c +++ b/firmware/common/ui_portapack.c @@ -29,6 +29,7 @@ #include "portapack.h" #include "radio.h" #include "rf_path.h" +#include "transceiver_mode.h" /* Pixel data within a font or bitmap byte is "reversed": LSB is left-most pixel. */ diff --git a/firmware/common/ui_rad1o.c b/firmware/common/ui_rad1o.c index 977c5205..ce894cdf 100644 --- a/firmware/common/ui_rad1o.c +++ b/firmware/common/ui_rad1o.c @@ -29,6 +29,7 @@ #include "hackrf_core.h" #include "radio.h" #include "rf_path.h" +#include "transceiver_mode.h" #include "rad1o/display.h" #include "rad1o/draw.h" diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 3126ac0c..ee8f897d 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index 2ad3203d..d558a2de 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index ab3d6af4..80750aa4 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_transceiver.h b/firmware/hackrf_usb/usb_api_transceiver.h index 02385acf..df981c1e 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.h +++ b/firmware/hackrf_usb/usb_api_transceiver.h @@ -25,7 +25,7 @@ #include -#include +#include #include #include From 3e256d14154914a6fb376523c609dcf680f9b255 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Apr 2026 00:19:24 -0400 Subject: [PATCH 023/173] Write RADIO_OPMODE when switching operating mode --- firmware/common/radio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 71818f11..7b01805e 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -705,6 +705,7 @@ void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode) } } + radio->config[RADIO_BANK_ACTIVE][RADIO_OPMODE] = mode; mark_dirty(radio, RADIO_OPMODE); nvic_enable_irq(NVIC_USB0_IRQ); radio_update(radio); From 0205dd441d70e677459d0e67f4ce96e1dd69818f Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Wed, 8 Apr 2026 21:11:35 -0400 Subject: [PATCH 024/173] Update UI opportunistically in sweep mode --- firmware/common/radio.c | 20 +++++++++++------ firmware/common/rf_path.c | 10 +++++++-- firmware/common/rf_path.h | 6 +++++- firmware/common/tuning.c | 31 ++++++++++++++++----------- firmware/common/tuning.h | 9 +++++--- firmware/hackrf_usb/usb_api_sweep.c | 33 +++++++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 25 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 7b01805e..39fc5f0a 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -270,6 +270,11 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) bool set_lo = (requested_lo != RADIO_UNSET); bool set_img_reject = (requested_img_reject != RADIO_UNSET); + uint64_t opmode = bank[RADIO_OPMODE]; + if (opmode == RADIO_UNSET) { + opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; + } + if (set_if && set_lo && set_img_reject) { bool new_if = (requested_if != @@ -284,7 +289,8 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) set_freq_explicit( requested_if / FP_ONE_HZ, requested_lo / FP_ONE_HZ, - requested_img_reject); + requested_img_reject, + opmode); radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = requested_if; radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = @@ -323,10 +329,6 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) if (applied_afe_rate == RADIO_UNSET) { return false; } - uint64_t opmode = bank[RADIO_OPMODE]; - if (opmode == RADIO_UNSET) { - opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; - } const tune_config_t* tune_config = select_tune_config(opmode); const tune_config_t* applied_tune_config = select_tune_config(radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]); @@ -349,12 +351,16 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) fp_40_24_t offset = applied_afe_rate / 4; bool new_offset = (applied_offset != offset); if (new_rotation || new_offset || new_config || new_rf) { - tuning_set_frequency(tune_config, requested_rf_hz, offset / FP_ONE_HZ); + tuning_set_frequency( + tune_config, + requested_rf_hz, + offset / FP_ONE_HZ, + opmode); applied_offset = offset; } #else if (new_rf) { - set_freq(requested_rf_hz); + set_freq(requested_rf_hz, opmode); } #endif radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] = requested_rf; diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index 30c8c718..cf77f70c 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -34,6 +34,7 @@ #include "mixer.h" #include "platform_detect.h" #include "sgpio.h" +#include "transceiver_mode.h" #if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE) #include "platform_scu.h" #endif @@ -537,7 +538,10 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d hackrf_ui()->set_direction(direction); } -void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter) +void rf_path_set_filter( + rf_path_t* const rf_path, + const rf_path_filter_t filter, + const transceiver_mode_t opmode) { switch (filter) { default: @@ -560,7 +564,9 @@ void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter) switchctrl_set(rf_path, rf_path->switchctrl); - hackrf_ui()->set_filter(filter); + if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { + hackrf_ui()->set_filter(filter); + } } void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable) diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h index 1ac0e243..d9f45a30 100644 --- a/firmware/common/rf_path.h +++ b/firmware/common/rf_path.h @@ -25,6 +25,7 @@ #include +#include "transceiver_mode.h" #if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE) #include "gpio.h" #endif @@ -91,7 +92,10 @@ void rf_path_init(rf_path_t* const rf_path); void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t direction); -void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter); +void rf_path_set_filter( + rf_path_t* const rf_path, + const rf_path_filter_t filter, + const transceiver_mode_t opmode); void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable); void rf_path_set_antenna(rf_path_t* const rf_path, const uint_fast8_t enable); diff --git a/firmware/common/tuning.c b/firmware/common/tuning.c index 47724e36..b2d49ec4 100644 --- a/firmware/common/tuning.c +++ b/firmware/common/tuning.c @@ -28,6 +28,7 @@ #include "max283x.h" #include "mixer.h" #include "sgpio.h" +#include "transceiver_mode.h" #if defined(PRALINE) || defined(HACKRF_ONE) #include "operacake.h" #endif @@ -79,7 +80,7 @@ static uint32_t max2837_freq_nominal_hz = 2560000000; * hz between 0 to 999999 Hz (not checked) * return false on error or true if success. */ -bool set_freq(const uint64_t freq) +bool set_freq(const uint64_t freq, const transceiver_mode_t opmode) { bool success; uint64_t mixer_freq_hz; @@ -92,7 +93,7 @@ bool set_freq(const uint64_t freq) max283x_mode_t prior_max283x_mode = max283x_mode(&max283x); max283x_set_mode(&max283x, MAX283x_MODE_STANDBY); if (freq_mhz < MAX_LP_FREQ_MHZ) { - rf_path_set_filter(&rf_path, RF_PATH_FILTER_LOW_PASS); + rf_path_set_filter(&rf_path, RF_PATH_FILTER_LOW_PASS, opmode); #ifdef RAD1O max2837_freq_nominal_hz = 2300 * FREQ_ONE_MHZ; #else @@ -105,7 +106,7 @@ bool set_freq(const uint64_t freq) max283x_set_frequency(&max283x, real_mixer_freq_hz - freq); sgpio_cpld_set_mixer_invert(&sgpio_config, 1); } else if ((freq_mhz >= MIN_BYPASS_FREQ_MHZ) && (freq_mhz < MAX_BYPASS_FREQ_MHZ)) { - rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS); + rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS, opmode); /* mixer_freq_mhz <= not used in Bypass mode */ max283x_set_frequency(&max283x, freq); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); @@ -124,7 +125,7 @@ bool set_freq(const uint64_t freq) max2837_freq_nominal_hz = (2500 * FREQ_ONE_MHZ) + ((freq - (MID2_HP_FREQ_MHZ * FREQ_ONE_MHZ)) / 9); } - rf_path_set_filter(&rf_path, RF_PATH_FILTER_HIGH_PASS); + rf_path_set_filter(&rf_path, RF_PATH_FILTER_HIGH_PASS, opmode); mixer_freq_hz = freq - max2837_freq_nominal_hz; /* Set Freq and read real freq */ real_mixer_freq_hz = mixer_set_frequency(&mixer, mixer_freq_hz); @@ -136,7 +137,9 @@ bool set_freq(const uint64_t freq) } max283x_set_mode(&max283x, prior_max283x_mode); if (success) { - hackrf_ui()->set_frequency(freq); + if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { + hackrf_ui()->set_frequency(freq); + } #ifdef HACKRF_ONE operacake_set_range(freq_mhz); #endif @@ -149,7 +152,8 @@ bool set_freq(const uint64_t freq) bool tuning_set_frequency( const tune_config_t* cfg, const uint64_t freq, - const uint32_t offset) + const uint32_t offset, + const transceiver_mode_t opmode) { uint64_t mixer_freq_hz; uint64_t real_mixer_freq_hz; @@ -175,11 +179,11 @@ bool tuning_set_frequency( max283x_set_mode(&max283x, MAX283x_MODE_STANDBY); if (cfg->if_mhz == 0) { - rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS); + rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS, opmode); max283x_set_frequency(&max283x, rf); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } else if (cfg->if_mhz > freq_mhz) { - rf_path_set_filter(&rf_path, RF_PATH_FILTER_LOW_PASS); + rf_path_set_filter(&rf_path, RF_PATH_FILTER_LOW_PASS, opmode); if (cfg->high_lo) { mixer_freq_hz = FREQ_ONE_MHZ * cfg->if_mhz + rf; real_mixer_freq_hz = mixer_set_frequency(&mixer, mixer_freq_hz); @@ -192,7 +196,7 @@ bool tuning_set_frequency( sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } } else { - rf_path_set_filter(&rf_path, RF_PATH_FILTER_HIGH_PASS); + rf_path_set_filter(&rf_path, RF_PATH_FILTER_HIGH_PASS, opmode); mixer_freq_hz = rf - FREQ_ONE_MHZ * cfg->if_mhz; real_mixer_freq_hz = mixer_set_frequency(&mixer, mixer_freq_hz); max283x_set_frequency(&max283x, rf - real_mixer_freq_hz); @@ -200,7 +204,9 @@ bool tuning_set_frequency( } max283x_set_mode(&max283x, prior_max283x_mode); - hackrf_ui()->set_frequency(freq); + if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { + hackrf_ui()->set_frequency(freq); + } operacake_set_range(freq_mhz); return true; } @@ -209,7 +215,8 @@ bool tuning_set_frequency( bool set_freq_explicit( const uint64_t if_freq_hz, const uint64_t lo_freq_hz, - const rf_path_filter_t path) + const rf_path_filter_t path, + const transceiver_mode_t opmode) { if ((if_freq_hz < ((uint64_t) ABS_MIN_BYPASS_FREQ_MHZ * FREQ_ONE_MHZ)) || (if_freq_hz > ((uint64_t) ABS_MAX_BYPASS_FREQ_MHZ * FREQ_ONE_MHZ))) { @@ -225,7 +232,7 @@ bool set_freq_explicit( return false; } - rf_path_set_filter(&rf_path, path); + rf_path_set_filter(&rf_path, path, opmode); max283x_set_frequency(&max283x, if_freq_hz); if (lo_freq_hz > if_freq_hz) { diff --git a/firmware/common/tuning.h b/firmware/common/tuning.h index 559638ac..1c4681db 100644 --- a/firmware/common/tuning.h +++ b/firmware/common/tuning.h @@ -27,21 +27,24 @@ #include #include "rf_path.h" +#include "transceiver_mode.h" #if defined(PRALINE) #include "tune_config.h" #endif #define FREQ_ONE_MHZ (1000ULL * 1000) -bool set_freq(const uint64_t freq); +bool set_freq(const uint64_t freq, const transceiver_mode_t opmode); bool set_freq_explicit( const uint64_t if_freq_hz, const uint64_t lo_freq_hz, - const rf_path_filter_t path); + const rf_path_filter_t path, + const transceiver_mode_t opmode); #ifdef PRALINE bool tuning_set_frequency( const tune_config_t* cfg, const uint64_t freq, - const uint32_t offset); + const uint32_t offset, + const transceiver_mode_t opmode); #endif diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index d558a2de..a9bfc695 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -27,8 +27,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -58,6 +60,31 @@ static uint32_t dwell_blocks = 0; static uint32_t step_width = 0; static uint32_t offset = 0; static enum sweep_style style = LINEAR; +static bool freq_ui_dirty = false; +static bool img_reject_ui_dirty = false; +static rf_path_filter_t img_reject; + +/* + * Opportunistic UI updates are made when time is available. Updates are + * prioritized to prevent a single update from taking too long. Lower priority + * changes to the image reject filter path display are done only if there is + * time remaining after the higher priority update to the frequency display. + */ +void update_ui(void) +{ + if (freq_ui_dirty) { + hackrf_ui()->set_frequency(sweep_freq + offset); + freq_ui_dirty = false; + } else if (img_reject_ui_dirty) { + const uint64_t new_img_reject = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_IMAGE_REJECT); + if (new_img_reject != img_reject) { + img_reject = new_img_reject; + hackrf_ui()->set_filter(img_reject); + } + img_reject_ui_dirty = false; + } +} /* Do this before starting sweep mode with request_transceiver_mode(). */ usb_request_status_t usb_vendor_request_init_sweep( @@ -153,6 +180,9 @@ void sweep_mode(uint32_t seq) uint8_t* buffer; transceiver_startup(TRANSCEIVER_MODE_RX_SWEEP); + hackrf_ui()->set_frequency(sweep_freq + offset); + img_reject = radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_IMAGE_REJECT); + hackrf_ui()->set_filter(img_reject); // Set M0 to RX first buffer, then wait. m0_state.threshold = 0x4000; @@ -235,6 +265,8 @@ void sweep_mode(uint32_t seq) (sweep_freq + offset) * FP_ONE_HZ); nvic_enable_irq(NVIC_USB0_IRQ); radio_update(&radio); + freq_ui_dirty = true; + img_reject_ui_dirty = true; blocks_queued = 0; } @@ -243,6 +275,7 @@ void sweep_mode(uint32_t seq) if (transceiver_request.seq != seq) { goto end; } + update_ui(); } // Set M0 to switch back to WAIT after filling next buffer. From 59c61c9aced28d01ecada28e9311264cfa59e854 Mon Sep 17 00:00:00 2001 From: mndza Date: Wed, 11 Mar 2026 14:07:34 +0100 Subject: [PATCH 025/173] Set optimal fractional divider values (P2, P3) when setting sample rate --- firmware/common/hackrf_core.c | 73 +++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 241c2e3d..012fb948 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -171,6 +171,60 @@ jtag_t jtag_cpld = { .gpio = &jtag_gpio_cpld, }; +/* + * Closest fraction to m/d with denominator <= max_den. + * Returns result in *r / *s with gcd(*r, *s) == 1 and 0 < *s <= max_den. + * Straight port from CPython's fractions, and better documented there. + */ +void limit_denominator( + uint64_t m, + uint64_t d, + const uint64_t max_den, + uint64_t* r, + uint64_t* s) +{ + if (d <= max_den) { + *r = m; + *s = d; + return; + } + + uint64_t p0 = 0, q0 = 1, p1 = 1, q1 = 0; + uint64_t n = m, orig_d = d; + uint64_t tmp; + + while (1) { + uint64_t a = n / d; + uint64_t q2 = q0 + a * q1; + + if (q2 > max_den) + break; + + tmp = p0 + a * p1; + p0 = p1; + q0 = q1; + p1 = tmp; + q1 = q2; + + tmp = n - a * d; + n = d; + d = tmp; + if (d == 0) + break; + } + + uint64_t k = (max_den - q0) / q1; + + /* Return closer candidate. */ + if (2 * d * (q0 + k * q1) <= orig_d) { + *r = p1; + *s = q1; + } else { + *r = p0 + k * p1; + *s = q0 + k * q1; + } +} + /* * Configure clock generator to produce sample clock in units of 1/(2**24) Hz. * Can be called with program=false for a dry run that returns the resultant @@ -207,24 +261,11 @@ fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program) * fixed-point type. */ n = (128 * vco) - (rate * (p1 + 512)); - d = rate / FP_ONE_HZ; - n += (d / 2); - p2 = n / d; - p3 = 1 << 24; + d = rate; - /* Reduce fraction. p3 is 1<<24, so gcd(p2, p3) is a power of 2 */ - unsigned int shift = p2 ? __builtin_ctz(p2) : 24; - p2 >>= shift; - p3 >>= shift; - - /* Convert fraction to valid denominator. */ + /* Reduce fraction. */ const uint64_t p3_max = 0xfffff; - if (p3 > p3_max) { - p2 *= p3_max; - p2 += (p3 / 2); - p2 /= p3; - p3 = p3_max; - } + limit_denominator(n, d, p3_max, &p2, &p3); /* Roll over to next p1 to enable integer mode. */ if (p2 >= p3) { From 5af101e45c8063faec3c36dd9958a3ae77262c93 Mon Sep 17 00:00:00 2001 From: mndza Date: Fri, 10 Apr 2026 17:42:44 +0200 Subject: [PATCH 026/173] Fixed-point format for sample rate with 36 fractional bits Fix overflowing division in sample_rate_set Avoid overflows in round_sample_rate --- firmware/common/fixed_point.h | 9 +++ firmware/common/fpga_selftest.c | 6 +- firmware/common/hackrf_core.c | 69 +++++++++++++++-------- firmware/common/hackrf_core.h | 2 +- firmware/common/radio.c | 31 +++++----- firmware/common/radio.h | 4 +- firmware/hackrf_usb/usb_api_transceiver.c | 18 ++++-- 7 files changed, 91 insertions(+), 48 deletions(-) diff --git a/firmware/common/fixed_point.h b/firmware/common/fixed_point.h index 65656e19..66cabe09 100644 --- a/firmware/common/fixed_point.h +++ b/firmware/common/fixed_point.h @@ -31,3 +31,12 @@ typedef uint64_t fp_40_24_t; /* one in 40.24 fixed-point */ #define FP_ONE_HZ (1 << 24) + +/* 28.36 fixed-point */ +typedef uint64_t fp_28_36_t; + +/* one million in 28.36 fixed-point */ +#define SR_FP_ONE_MHZ ((1000ULL * 1000ULL) << 36) + +/* one in 28.36 fixed-point */ +#define SR_FP_ONE_HZ (1ULL << 36) diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index cc1d8d9c..1e3a9936 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -199,7 +199,7 @@ bool fpga_if_xcvr_selftest(void) max283x_set_frequency(&max283x, 2500000000); // Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF - sample_rate_set(4ULL * FP_ONE_MHZ, true); + sample_rate_set(4ULL * SR_FP_ONE_MHZ, true); delay_us_at_mhz(1000, 204); if (rx_samples(num_samples, 2000000) == -1) { timeout = true; @@ -222,7 +222,7 @@ bool fpga_if_xcvr_selftest(void) // Capture 3: 20 Msps, tone at 5 MHz, narrowband filter OFF fpga_set_tx_nco_pstep(&fpga, 255); - sample_rate_set(20ULL * FP_ONE_MHZ, true); + sample_rate_set(20ULL * SR_FP_ONE_MHZ, true); narrowband_filter_set(0); delay_us_at_mhz(1000, 204); if (rx_samples(num_samples, 2000000) == -1) { @@ -245,7 +245,7 @@ bool fpga_if_xcvr_selftest(void) &selftest.xcvr_measurements[3]); // Restore default settings. - sample_rate_set(10ULL * FP_ONE_MHZ, true); + sample_rate_set(10ULL * SR_FP_ONE_MHZ, true); rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_OFF); narrowband_filter_set(0); fpga_init(&fpga); diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 012fb948..80c73ccc 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -48,6 +48,8 @@ #include "ice40_spi.h" #endif +#include + i2c_bus_t i2c0 = { .obj = (void*) I2C0_BASE, .start = i2c_lpc_start, @@ -226,7 +228,7 @@ void limit_denominator( } /* - * Configure clock generator to produce sample clock in units of 1/(2**24) Hz. + * Configure clock generator to produce sample clock in units of 1/(2**36) Hz. * Can be called with program=false for a dry run that returns the resultant * frequency without actually configuring the clock generator. * @@ -239,12 +241,12 @@ void limit_denominator( * For more information see: * https://www.pa3fwm.nl/technotes/tn42a-si5351-programming.html */ -fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program) +fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) { - const fp_40_24_t vco = 800 * FP_ONE_MHZ; + const uint64_t vco_hz = 800 * 1000ULL * 1000ULL; uint64_t p1, p2, p3; - uint64_t n, d, q; - fp_40_24_t remainder, resultant_rate; + uint64_t n, d, q1, q2, q3, r1, r2; + fp_28_36_t resultant_rate; /* * First double the sample rate so that we can produce a clock at twice @@ -252,15 +254,34 @@ fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program) * and it is divided by two in an output divider to produce the actual * AFE clock. */ - fp_40_24_t rate = sample_rate * 2; + fp_28_36_t rate = sample_rate * 2; - p1 = ((128 * vco) / rate) - 512; - if (vco % rate) { - /* - * Compute numerator (p2) for denominator (p3) matching - * fixed-point type. - */ - n = (128 * vco) - (rate * (p1 + 512)); + /* + * As the numerator exceeds 64 bits, let's compute this as: + * N<<36 / r = ((N<<12) / r) << 24 + ((N<<12) % r) << 24) / r + * The numerator for the second addend may not fit in 64 bits, and + * that's where we take advantage of long division. + */ + const uint64_t A = (128 * vco_hz) << 12; + q1 = A / rate; + r1 = A % rate; + // Remaining bits with long division. + q2 = 0; + r2 = r1; + for (int j = 0; j < 24; j++) { + uint64_t msb = r2 >> 63; + r2 <<= 1; + q2 <<= 1; + if (msb || r2 >= rate) { + r2 -= rate; + q2 |= 1; + } + } + p1 = (q1 << 24) + q2 - 512; + + if (r2) { + /* Use the remainder for the fractional part. */ + n = r2; d = rate; /* Reduce fraction. */ @@ -285,19 +306,21 @@ fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program) if (p2 == 0) { /* Use unity denominator for integer mode. */ p3 = 1; - n = (vco * 128); + n = (128 * vco_hz) << 18; d = (p1 + 512); - n += (d / 2); - resultant_rate = n / d; + q1 = n / d; + r1 = n % d; + q2 = ((r1 << 18) + (d / 2)) / d; + resultant_rate = (q1 << 18) + q2; } else { - const uint64_t vco_hz = vco / FP_ONE_HZ; n = p3 * vco_hz * 128; d = p3 * (p1 + 512) + p2; - const uint64_t rate_hz = n / d; - remainder = (n - (d * rate_hz)) * FP_ONE_HZ; - remainder += (d / 2); - q = remainder / d; - resultant_rate = (rate_hz * FP_ONE_HZ) + q; + q1 = n / d; + r1 = n % d; + q2 = (r1 << 18) / d; + r2 = (r1 << 18) % d; + q3 = ((r2 << 18) + (d / 2)) / d; + resultant_rate = (q1 << 36) + (q2 << 18) + q3; } /* Return MCU sample rate, not AFE clock rate. */ @@ -658,7 +681,7 @@ void clock_gen_init(void) /* MS7/CLK7 is unused. */ /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */ - sample_rate_set(10ULL * FP_ONE_MHZ, true); + sample_rate_set(10ULL * SR_FP_ONE_MHZ, true); si5351c_configure_clock_control(&clock_gen); si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL); diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index e3396ae2..539ce770 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -87,7 +87,7 @@ void enable_1v8_power(void); void disable_1v8_power(void); #endif -fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program); +fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program); clock_source_t activate_best_clock_source(void); diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 39fc5f0a..6dc609d9 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -151,15 +151,15 @@ static inline uint8_t compute_resample_log( return n; } -#define MIN_MCU_RATE (200000ULL * FP_ONE_HZ) -#define MAX_MCU_RATE (21800000ULL * FP_ONE_HZ) -#define DEFAULT_MCU_RATE (10000000ULL * FP_ONE_HZ) +#define MIN_MCU_RATE (200000ULL * SR_FP_ONE_HZ) +#define MAX_MCU_RATE (21800000ULL * SR_FP_ONE_HZ) +#define DEFAULT_MCU_RATE (10000000ULL * SR_FP_ONE_HZ) -static fp_40_24_t applied_afe_rate = RADIO_UNSET; +static fp_28_36_t applied_afe_rate = RADIO_UNSET; static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) { - fp_40_24_t rate, afe_rate; + fp_28_36_t rate, afe_rate; uint64_t previous_n; uint8_t n = 0; bool new_afe_rate = false; @@ -195,7 +195,7 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) switch (opmode) { case TRANSCEIVER_MODE_TX: case TRANSCEIVER_MODE_SS: - n = compute_resample_log(rate / FP_ONE_HZ, requested_n); + n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) { #ifdef PRALINE fpga_set_tx_interpolation_ratio(&fpga, n); @@ -204,7 +204,7 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) } break; default: - n = compute_resample_log(rate / FP_ONE_HZ, requested_n); + n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) { #ifdef PRALINE fpga_set_rx_decimation_ratio(&fpga, n); @@ -231,7 +231,8 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] = rate; if (rate != RADIO_UNSET) { /* Round to the nearest Hz for display. */ - const uint32_t rate_hz = (rate + (FP_ONE_HZ >> 1)) / FP_ONE_HZ; + const uint32_t rate_hz = + (rate + (SR_FP_ONE_HZ >> 1)) / SR_FP_ONE_HZ; hackrf_ui()->set_sample_rate(rate_hz); } } @@ -241,7 +242,7 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) #define DEFAULT_RF (2450ULL * FP_ONE_MHZ) -static fp_40_24_t applied_offset = RADIO_UNSET; +static fp_28_36_t applied_offset = RADIO_UNSET; #ifdef PRALINE static const tune_config_t* select_tune_config(uint64_t opmode) @@ -348,13 +349,13 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = tune_config->shift << 6; } - fp_40_24_t offset = applied_afe_rate / 4; + fp_28_36_t offset = applied_afe_rate / 4; bool new_offset = (applied_offset != offset); if (new_rotation || new_offset || new_config || new_rf) { tuning_set_frequency( tune_config, requested_rf_hz, - offset / FP_ONE_HZ, + offset / SR_FP_ONE_HZ, opmode); applied_offset = offset; } @@ -374,16 +375,16 @@ static uint32_t auto_bandwidth(radio_t* const radio) { uint64_t rotation = radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION]; - uint32_t sample_rate_hz = DEFAULT_MCU_RATE / FP_ONE_HZ; + uint32_t sample_rate_hz = DEFAULT_MCU_RATE / SR_FP_ONE_HZ; if (radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] != RADIO_UNSET) { - sample_rate_hz = - radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] / FP_ONE_HZ; + sample_rate_hz = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] / + SR_FP_ONE_HZ; } uint32_t offset_hz = 0; if ((rotation != 0) && (rotation != RADIO_UNSET) && (applied_offset != RADIO_UNSET)) { - offset_hz = applied_offset / FP_ONE_HZ; + offset_hz = applied_offset / SR_FP_ONE_HZ; } const uint32_t bb_bandwidth = (sample_rate_hz * 3) / 4; diff --git a/firmware/common/radio.h b/firmware/common/radio.h index e75d2d90..2744dd4b 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -96,7 +96,7 @@ typedef enum { */ RADIO_ROTATION = 5, /** - * Sample rate (as seen by MCU/host) in 1/(2**24) Hz. + * Sample rate (as seen by MCU/host) in 1/(2**36) Hz. */ RADIO_SAMPLE_RATE = 6, /** @@ -205,7 +205,7 @@ typedef enum { * execute a dry run, returning the sample rate without configuring clock * generation. */ -typedef fp_40_24_t (*sample_rate_fn)(const fp_40_24_t sample_rate, const bool program); +typedef fp_28_36_t (*sample_rate_fn)(const fp_28_36_t sample_rate, const bool program); typedef struct { radio_config_mode_t config_mode; diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index 80750aa4..d4b47f9f 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -161,15 +161,25 @@ usb_request_status_t usb_vendor_request_set_freq_explicit( } /* - * Convert fractional sample rate to units of 1/(2**24) Hz. + * Convert fractional sample rate to units of 1/(2**36) Hz. */ -static inline fp_40_24_t round_sample_rate(uint64_t num, uint32_t denom) +static inline fp_28_36_t round_sample_rate(uint64_t num, uint32_t denom) { - num *= FP_ONE_HZ; + uint64_t q1, r1, q2, r2, q3; + if (denom == 0) { denom = 1; } - return (num + (denom >> 1)) / denom; + + q1 = num / denom; + r1 = num % denom; + + q2 = (r1 << 32) / denom; + r2 = (r1 << 32) % denom; + + q3 = ((r2 << 4) + (denom >> 1)) / denom; + + return (q1 << 36) + (q2 << 4) + q3; } usb_request_status_t usb_vendor_request_set_sample_rate_frac( From b76c8759bddc48c23281ae838491ba8c8a965011 Mon Sep 17 00:00:00 2001 From: mndza Date: Fri, 10 Apr 2026 17:10:12 +0200 Subject: [PATCH 027/173] Decrease long division cycles in sample_rate_set --- firmware/common/hackrf_core.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 80c73ccc..cca0b541 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -256,19 +256,25 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) */ fp_28_36_t rate = sample_rate * 2; - /* - * As the numerator exceeds 64 bits, let's compute this as: - * N<<36 / r = ((N<<12) / r) << 24 + ((N<<12) % r) << 24) / r - * The numerator for the second addend may not fit in 64 bits, and - * that's where we take advantage of long division. + /* + * Computes p1 = (N << 36) / rate - 512, where N = 128 * vco_hz. + * + * Full numerator (N << 36) is 73 bits, so we split the division: + * + * (N << 36) / rate = ((N << 27) / rate) << 9 + * + (((N << 27) % rate) << 9) / rate + * + * IMPORTANT: Assumes sample rate is in [200e3 << 36, 43.6e6 << 36]. */ - const uint64_t A = (128 * vco_hz) << 12; + const uint64_t A = (128 * vco_hz) << 27; + q1 = A / rate; r1 = A % rate; - // Remaining bits with long division. + + // Remaining 9 bits with long division. q2 = 0; r2 = r1; - for (int j = 0; j < 24; j++) { + for (int j = 0; j < 9; j++) { uint64_t msb = r2 >> 63; r2 <<= 1; q2 <<= 1; @@ -277,7 +283,8 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) q2 |= 1; } } - p1 = (q1 << 24) + q2 - 512; + + p1 = (q1 << 9) + q2 - 512; if (r2) { /* Use the remainder for the fractional part. */ From 1b06ccf0f7b9ecd8b0715e6550d3d9d3a4122cee Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 11 Apr 2026 23:46:55 +0100 Subject: [PATCH 028/173] Add macros to clarify fixed-point constants. --- firmware/common/fixed_point.h | 14 ++++++++++++++ firmware/common/fpga_selftest.c | 6 +++--- firmware/common/hackrf_core.c | 2 +- firmware/common/radio.c | 8 ++++---- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/firmware/common/fixed_point.h b/firmware/common/fixed_point.h index 66cabe09..b6908797 100644 --- a/firmware/common/fixed_point.h +++ b/firmware/common/fixed_point.h @@ -29,14 +29,28 @@ typedef uint64_t fp_40_24_t; /* one million in 40.24 fixed-point */ #define FP_ONE_MHZ ((1000ULL * 1000ULL) << 24) +/* one thousand in 40.24 fixed-point */ +#define FP_ONE_KHZ ((1000ULL) << 24) + /* one in 40.24 fixed-point */ #define FP_ONE_HZ (1 << 24) +#define FP_MHZ(mhz) (mhz##ULL * FP_ONE_MHZ) +#define FP_KHZ(khz) (khz##ULL * FP_ONE_KHZ) +#define FP_HZ(hz) (hz##ULL * FP_ONE_HZ) + /* 28.36 fixed-point */ typedef uint64_t fp_28_36_t; /* one million in 28.36 fixed-point */ #define SR_FP_ONE_MHZ ((1000ULL * 1000ULL) << 36) +/* one thousand in 28.36 fixed-point */ +#define SR_FP_ONE_KHZ (1000ULL << 36) + /* one in 28.36 fixed-point */ #define SR_FP_ONE_HZ (1ULL << 36) + +#define SR_FP_MHZ(mhz) (mhz##ULL * SR_FP_ONE_MHZ) +#define SR_FP_KHZ(khz) (khz##ULL * SR_FP_ONE_KHZ) +#define SR_FP_HZ(hz) (hz##ULL * SR_FP_ONE_HZ) diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index 1e3a9936..e2b773e4 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -199,7 +199,7 @@ bool fpga_if_xcvr_selftest(void) max283x_set_frequency(&max283x, 2500000000); // Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF - sample_rate_set(4ULL * SR_FP_ONE_MHZ, true); + sample_rate_set(SR_FP_MHZ(4), true); delay_us_at_mhz(1000, 204); if (rx_samples(num_samples, 2000000) == -1) { timeout = true; @@ -222,7 +222,7 @@ bool fpga_if_xcvr_selftest(void) // Capture 3: 20 Msps, tone at 5 MHz, narrowband filter OFF fpga_set_tx_nco_pstep(&fpga, 255); - sample_rate_set(20ULL * SR_FP_ONE_MHZ, true); + sample_rate_set(SR_FP_MHZ(20), true); narrowband_filter_set(0); delay_us_at_mhz(1000, 204); if (rx_samples(num_samples, 2000000) == -1) { @@ -245,7 +245,7 @@ bool fpga_if_xcvr_selftest(void) &selftest.xcvr_measurements[3]); // Restore default settings. - sample_rate_set(10ULL * SR_FP_ONE_MHZ, true); + sample_rate_set(SR_FP_MHZ(10), true); rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_OFF); narrowband_filter_set(0); fpga_init(&fpga); diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index cca0b541..0534d480 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -688,7 +688,7 @@ void clock_gen_init(void) /* MS7/CLK7 is unused. */ /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */ - sample_rate_set(10ULL * SR_FP_ONE_MHZ, true); + sample_rate_set(SR_FP_MHZ(10), true); si5351c_configure_clock_control(&clock_gen); si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL); diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 6dc609d9..d8d3a918 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -151,9 +151,9 @@ static inline uint8_t compute_resample_log( return n; } -#define MIN_MCU_RATE (200000ULL * SR_FP_ONE_HZ) -#define MAX_MCU_RATE (21800000ULL * SR_FP_ONE_HZ) -#define DEFAULT_MCU_RATE (10000000ULL * SR_FP_ONE_HZ) +#define MIN_MCU_RATE SR_FP_KHZ(200) +#define MAX_MCU_RATE SR_FP_KHZ(21800) +#define DEFAULT_MCU_RATE SR_FP_KHZ(10000) static fp_28_36_t applied_afe_rate = RADIO_UNSET; @@ -240,7 +240,7 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) return (new_afe_rate || new_rate || new_n); } -#define DEFAULT_RF (2450ULL * FP_ONE_MHZ) +#define DEFAULT_RF FP_MHZ(2450) static fp_28_36_t applied_offset = RADIO_UNSET; From 14d196d3dd634055b7cac50524feb166d0ea6ea9 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Apr 2026 14:11:09 -0400 Subject: [PATCH 029/173] Use fixed-point type for tuning functions --- firmware/common/fpga_selftest.c | 4 +-- firmware/common/max2831.c | 44 ++++++++++-------------- firmware/common/max2831.h | 8 ++--- firmware/common/max2837.c | 59 +++++++++++++++------------------ firmware/common/max2837.h | 8 ++--- firmware/common/max2839.c | 59 +++++++++++++++------------------ firmware/common/max2839.h | 8 ++--- firmware/common/max283x.c | 8 ++--- firmware/common/max283x.h | 8 ++--- firmware/common/max2871.c | 12 ++++--- firmware/common/max2871.h | 7 ++-- firmware/common/mixer.c | 9 ++--- firmware/common/mixer.h | 6 ++-- firmware/common/rffc5071.c | 35 +++++++++---------- firmware/common/rffc5071.h | 7 ++-- firmware/common/tuning.c | 45 ++++++++++++++++--------- 16 files changed, 165 insertions(+), 162 deletions(-) diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index e2b773e4..c61d0692 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -1,5 +1,5 @@ /* - * Copyright 2025 Great Scott Gadgets + * Copyright 2025-2026 Great Scott Gadgets * * This file is part of HackRF. * @@ -196,7 +196,7 @@ bool fpga_if_xcvr_selftest(void) rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_RX_CALIBRATION); max283x_set_lna_gain(&max283x, 16); max283x_set_vga_gain(&max283x, 36); - max283x_set_frequency(&max283x, 2500000000); + max283x_set_frequency(&max283x, FP_MHZ(2500)); // Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF sample_rate_set(SR_FP_MHZ(4), true); diff --git a/firmware/common/max2831.c b/firmware/common/max2831.c index efae9eb6..8d0bf3ff 100644 --- a/firmware/common/max2831.c +++ b/firmware/common/max2831.c @@ -1,5 +1,5 @@ /* - * Copyright 2025 Great Scott Gadgets + * Copyright 2025-2026 Great Scott Gadgets * * This file is part of HackRF. * @@ -30,6 +30,7 @@ #include #include #include "max2831.h" +#include "fixed_point.h" #include "max2831_regs.def" // private register def macros #include "selftest.h" #include "adc.h" @@ -222,35 +223,26 @@ void max2831_stop(max2831_driver_t* const drv) max2831_set_mode(drv, MAX2831_MODE_SHUTDOWN); } -void max2831_set_frequency(max2831_driver_t* const drv, uint32_t freq) +/* Assume 40 MHz reference clock with R divider of 2. */ +#define PFD_FREQ_HZ (20ULL * (1000ULL * 1000ULL)) + +void max2831_set_frequency(max2831_driver_t* const drv, fp_40_24_t freq) { - uint32_t div_frac; - uint32_t div_int; - uint32_t div_rem; - uint32_t div_cmp; - int i; + uint64_t div; - /* ASSUME 40MHz PLL. Ratio = F*R/40,000,000. */ - /* TODO: fixed to R=2. Check if it's worth exploring R=1. */ - freq += (20000000 >> 21); /* round to nearest frequency */ - div_int = freq / 20000000; - div_rem = freq % 20000000; - div_frac = 0; - div_cmp = 20000000; - for (i = 0; i < 20; i++) { - div_frac <<= 1; - div_rem <<= 1; - if (div_rem >= div_cmp) { - div_frac |= 0x1; - div_rem -= div_cmp; - } - } + freq += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */ + div = freq / PFD_FREQ_HZ; + + /* + * Shift from 40.24 fixed-point to 44.20 to match 20-bit fractional + * divider. + */ + div = div >> 4; - /* Write order matters? */ //set_MAX2831_SYN_REF_DIV(drv, MAX2831_SYN_REF_DIV_2); - set_MAX2831_SYN_INT(drv, div_int); - set_MAX2831_SYN_FRAC_HI(drv, (div_frac >> 6) & 0x3fff); - set_MAX2831_SYN_FRAC_LO(drv, div_frac & 0x3f); + set_MAX2831_SYN_INT(drv, (div >> 20) & 0xff); + set_MAX2831_SYN_FRAC_HI(drv, (div >> 6) & 0x3fff); + set_MAX2831_SYN_FRAC_LO(drv, div & 0x3f); max2831_regs_commit(drv); } diff --git a/firmware/common/max2831.h b/firmware/common/max2831.h index 18154649..11ded416 100644 --- a/firmware/common/max2831.h +++ b/firmware/common/max2831.h @@ -1,5 +1,5 @@ /* - * Copyright 2025 Great Scott Gadgets + * Copyright 2025-2026 Great Scott Gadgets * * This file is part of HackRF. * @@ -24,6 +24,7 @@ #include #include +#include "fixed_point.h" #include "gpio.h" #include "spi_bus.h" @@ -86,9 +87,8 @@ void max2831_set_mode(max2831_driver_t* const drv, const max2831_mode_t new_mode extern void max2831_start(max2831_driver_t* const drv); extern void max2831_stop(max2831_driver_t* const drv); -/* Set frequency in Hz. Frequency setting is a multi-step function - * where order of register writes matters. */ -extern void max2831_set_frequency(max2831_driver_t* const drv, uint32_t freq); +/* Set frequency in 1/(2**24) Hz */ +extern void max2831_set_frequency(max2831_driver_t* const drv, fp_40_24_t freq); uint32_t max2831_set_lpf_bandwidth( max2831_driver_t* const drv, const max2831_mode_t mode, diff --git a/firmware/common/max2837.c b/firmware/common/max2837.c index a3e91498..1a4f9648 100644 --- a/firmware/common/max2837.c +++ b/firmware/common/max2837.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Will Code * Copyright 2014 Jared Boone * @@ -32,6 +32,7 @@ #include #include #include "max2837.h" +#include "fixed_point.h" #include "max2837_regs.def" // private register def macros #include "selftest.h" @@ -228,24 +229,23 @@ void max2837_stop(max2837_driver_t* const drv) max2837_set_mode(drv, MAX2837_MODE_SHUTDOWN); } -void max2837_set_frequency(max2837_driver_t* const drv, uint32_t freq) +/* Assume 40 MHz reference clock with R divider of 1. */ +#define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL)) + +void max2837_set_frequency(max2837_driver_t* const drv, fp_40_24_t freq) { uint8_t band; uint8_t lna_band; - uint32_t div_frac; - uint32_t div_int; - uint32_t div_rem; - uint32_t div_cmp; - int i; + uint64_t div; /* Select band. Allow tuning outside specified bands. */ - if (freq < 2400000000U) { + if (freq < FP_MHZ(2400)) { band = MAX2837_LOGEN_BSW_2_3; lna_band = MAX2837_LNAband_2_4; - } else if (freq < 2500000000U) { + } else if (freq < FP_MHZ(2500)) { band = MAX2837_LOGEN_BSW_2_4; lna_band = MAX2837_LNAband_2_4; - } else if (freq < 2600000000U) { + } else if (freq < FP_MHZ(2600)) { band = MAX2837_LOGEN_BSW_2_5; lna_band = MAX2837_LNAband_2_6; } else { @@ -253,34 +253,29 @@ void max2837_set_frequency(max2837_driver_t* const drv, uint32_t freq) lna_band = MAX2837_LNAband_2_6; } - /* ASSUME 40MHz PLL. Ratio = F*(4/3)/40,000,000 = F/30,000,000 */ - freq += (30000000 >> 21); /* round to nearest frequency */ - div_int = freq / 30000000; - div_rem = freq % 30000000; - div_frac = 0; - div_cmp = 30000000; - for (i = 0; i < 20; i++) { - div_frac <<= 1; - div_rem <<= 1; - if (div_rem >= div_cmp) { - div_frac |= 0x1; - div_rem -= div_cmp; - } - } + fp_40_24_t vco = (freq * 4) / 3; + + vco += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */ + div = vco / PFD_FREQ_HZ; + + /* + * Shift from 40.24 fixed-point to 44.20 to match 20-bit fractional + * divider. + */ + div = div >> 4; /* Band settings */ set_MAX2837_LOGEN_BSW(drv, band); set_MAX2837_LNAband(drv, lna_band); - /* Write order matters here, so commit INT and FRAC_HI before - * committing FRAC_LO, which is the trigger for VCO - * auto-select. TODO - it's cleaner this way, but it would be - * faster to explicitly commit the registers explicitly so the - * dirty bits aren't scanned twice. */ - set_MAX2837_SYN_INT(drv, div_int); - set_MAX2837_SYN_FRAC_HI(drv, (div_frac >> 10) & 0x3ff); + /* + * Write order matters here, so commit INT and FRAC_HI before + * committing FRAC_LO, which is the trigger for VCO auto-select. + */ + set_MAX2837_SYN_INT(drv, (div >> 20) & 0xff); + set_MAX2837_SYN_FRAC_HI(drv, (div >> 10) & 0x3ff); max2837_regs_commit(drv); - set_MAX2837_SYN_FRAC_LO(drv, div_frac & 0x3ff); + set_MAX2837_SYN_FRAC_LO(drv, div & 0x3ff); max2837_regs_commit(drv); } diff --git a/firmware/common/max2837.h b/firmware/common/max2837.h index 748bafe0..d90844ce 100644 --- a/firmware/common/max2837.h +++ b/firmware/common/max2837.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Will Code * Copyright 2014 Jared Boone * @@ -26,6 +26,7 @@ #include #include +#include "fixed_point.h" #include "gpio.h" #include "spi_bus.h" @@ -77,9 +78,8 @@ void max2837_set_mode(max2837_driver_t* const drv, const max2837_mode_t new_mode extern void max2837_start(max2837_driver_t* const drv); extern void max2837_stop(max2837_driver_t* const drv); -/* Set frequency in Hz. Frequency setting is a multi-step function - * where order of register writes matters. */ -extern void max2837_set_frequency(max2837_driver_t* const drv, uint32_t freq); +/* Set frequency in 1/(2**24) Hz. */ +extern void max2837_set_frequency(max2837_driver_t* const drv, fp_40_24_t freq); uint32_t max2837_set_lpf_bandwidth( max2837_driver_t* const drv, const uint32_t bandwidth_hz); diff --git a/firmware/common/max2839.c b/firmware/common/max2839.c index 62c5561f..b2ef8ffb 100644 --- a/firmware/common/max2839.c +++ b/firmware/common/max2839.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Will Code * Copyright 2014 Jared Boone * @@ -32,6 +32,7 @@ #include #include #include "max2839.h" +#include "fixed_point.h" #include "max2839_regs.def" // private register def macros #include "selftest.h" @@ -239,53 +240,47 @@ void max2839_stop(max2839_driver_t* const drv) max2839_set_mode(drv, MAX2839_MODE_SHUTDOWN); } -void max2839_set_frequency(max2839_driver_t* const drv, uint32_t freq) +/* Assume 40 MHz reference clock with R divider of 1. */ +#define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL)) + +void max2839_set_frequency(max2839_driver_t* const drv, fp_40_24_t freq) { uint8_t band; - uint32_t div_frac; - uint32_t div_int; - uint32_t div_rem; - uint32_t div_cmp; - int i; + uint64_t div; /* Select band. Allow tuning outside specified bands. */ - if (freq < 2400000000U) { + if (freq < FP_MHZ(2400)) { band = MAX2839_LOGEN_BSW_2_3; - } else if (freq < 2500000000U) { + } else if (freq < FP_MHZ(2500)) { band = MAX2839_LOGEN_BSW_2_4; - } else if (freq < 2600000000U) { + } else if (freq < FP_MHZ(2600)) { band = MAX2839_LOGEN_BSW_2_5; } else { band = MAX2839_LOGEN_BSW_2_6; } - /* ASSUME 40MHz PLL. Ratio = F*(4/3)/40,000,000 = F/30,000,000 */ - freq += (30000000 >> 21); /* round to nearest frequency */ - div_int = freq / 30000000; - div_rem = freq % 30000000; - div_frac = 0; - div_cmp = 30000000; - for (i = 0; i < 20; i++) { - div_frac <<= 1; - div_rem <<= 1; - if (div_rem >= div_cmp) { - div_frac |= 0x1; - div_rem -= div_cmp; - } - } + fp_40_24_t vco = (freq * 4) / 3; + + vco += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */ + div = vco / PFD_FREQ_HZ; + + /* + * Shift from 40.24 fixed-point to 44.20 to match 20-bit fractional + * divider. + */ + div = div >> 4; /* Band settings */ set_MAX2839_LOGEN_BSW(drv, band); - /* Write order matters here, so commit INT and FRAC_HI before - * committing FRAC_LO, which is the trigger for VCO - * auto-select. TODO - it's cleaner this way, but it would be - * faster to explicitly commit the registers explicitly so the - * dirty bits aren't scanned twice. */ - set_MAX2839_SYN_INT(drv, div_int); - set_MAX2839_SYN_FRAC_HI(drv, (div_frac >> 10) & 0x3ff); + /* + * Write order matters here, so commit INT and FRAC_HI before + * committing FRAC_LO, which is the trigger for VCO auto-select. + */ + set_MAX2839_SYN_INT(drv, (div >> 20) & 0xff); + set_MAX2839_SYN_FRAC_HI(drv, (div >> 10) & 0x3ff); max2839_regs_commit(drv); - set_MAX2839_SYN_FRAC_LO(drv, div_frac & 0x3ff); + set_MAX2839_SYN_FRAC_LO(drv, div & 0x3ff); max2839_regs_commit(drv); } diff --git a/firmware/common/max2839.h b/firmware/common/max2839.h index b6c21194..1836bda2 100644 --- a/firmware/common/max2839.h +++ b/firmware/common/max2839.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Will Code * Copyright 2014 Jared Boone * @@ -26,6 +26,7 @@ #include #include +#include "fixed_point.h" #include "gpio.h" #include "spi_bus.h" @@ -79,9 +80,8 @@ void max2839_set_mode(max2839_driver_t* const drv, const max2839_mode_t new_mode extern void max2839_start(max2839_driver_t* const drv); extern void max2839_stop(max2839_driver_t* const drv); -/* Set frequency in Hz. Frequency setting is a multi-step function - * where order of register writes matters. */ -extern void max2839_set_frequency(max2839_driver_t* const drv, uint32_t freq); +/* Set frequency in 1/(2**24) Hz. */ +extern void max2839_set_frequency(max2839_driver_t* const drv, fp_40_24_t freq); uint32_t max2839_set_lpf_bandwidth( max2839_driver_t* const drv, const uint32_t bandwidth_hz); diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index 7c5de380..4a9f39db 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Will Code * Copyright 2014 Jared Boone * @@ -25,6 +25,7 @@ #include "max283x.h" +#include "fixed_point.h" #include "platform_gpio.h" #include "spi_bus.h" #if defined(PRALINE) @@ -288,9 +289,8 @@ void max283x_stop(max283x_driver_t* const drv) } } -/* Set frequency in Hz. Frequency setting is a multi-step function - * where order of register writes matters. */ -void max283x_set_frequency(max283x_driver_t* const drv, uint32_t freq) +/* Set frequency in 1/(2**24) Hz. */ +void max283x_set_frequency(max283x_driver_t* const drv, fp_40_24_t freq) { switch (drv->type) { #ifdef PRALINE diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h index a96c908f..0fd7ac85 100644 --- a/firmware/common/max283x.h +++ b/firmware/common/max283x.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Will Code * Copyright 2014 Jared Boone * @@ -26,6 +26,7 @@ #include #include +#include "fixed_point.h" #if defined(PRALINE) #include "max2831.h" #else @@ -101,9 +102,8 @@ void max283x_set_mode(max283x_driver_t* const drv, const max283x_mode_t new_mode void max283x_start(max283x_driver_t* const drv); void max283x_stop(max283x_driver_t* const drv); -/* Set frequency in Hz. Frequency setting is a multi-step function - * where order of register writes matters. */ -void max283x_set_frequency(max283x_driver_t* const drv, uint32_t freq); +/* Set frequency in 1/(2**24) Hz. */ +void max283x_set_frequency(max283x_driver_t* const drv, fp_40_24_t freq); uint32_t max283x_set_lpf_bandwidth( max283x_driver_t* const drv, const max283x_mode_t mode, diff --git a/firmware/common/max2871.c b/firmware/common/max2871.c index 2d9b0c29..16ba18f1 100644 --- a/firmware/common/max2871.c +++ b/firmware/common/max2871.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 Great Scott Gadgets + * Copyright 2015-2026 Great Scott Gadgets * * This file is part of HackRF. * @@ -24,6 +24,7 @@ #include #include +#include "fixed_point.h" #include "max2871_regs.h" #include "selftest.h" #include "platform_scu.h" @@ -132,7 +133,7 @@ void max2871_setup(max2871_driver_t* const drv) max2871_write_registers(drv); - max2871_set_frequency(drv, 3500); + max2871_set_frequency(drv, FP_MHZ(3500)); } static void delay_ms(int ms) @@ -228,9 +229,10 @@ static void max2871_write_registers(max2871_driver_t* const drv) } } -/* Set frequency (MHz). */ -uint64_t max2871_set_frequency(max2871_driver_t* const drv, uint16_t mhz) +/* Set frequency in 1/(2**24) Hz, rounded to nearest 40 MHz. */ +fp_40_24_t max2871_set_frequency(max2871_driver_t* const drv, fp_40_24_t lo) { + uint16_t mhz = lo / FP_ONE_MHZ; int n = mhz / 40; int diva = 0; @@ -251,7 +253,7 @@ uint64_t max2871_set_frequency(max2871_driver_t* const drv, uint16_t mhz) max2871_set_RFA_EN(1); max2871_write_registers(drv); - return (mhz / 40) * 40 * 1000000; + return (mhz / 40) * FP_MHZ(40); } void max2871_enable(max2871_driver_t* const drv) diff --git a/firmware/common/max2871.h b/firmware/common/max2871.h index eb1ecbf0..1b3e1a13 100644 --- a/firmware/common/max2871.h +++ b/firmware/common/max2871.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Great Scott Gadgets + * Copyright 2017-2026 Great Scott Gadgets * * This file is part of HackRF. * @@ -21,8 +21,7 @@ #pragma once -#include - +#include "fixed_point.h" #include "gpio.h" typedef struct { @@ -35,6 +34,6 @@ typedef struct { } max2871_driver_t; extern void max2871_setup(max2871_driver_t* const drv); -extern uint64_t max2871_set_frequency(max2871_driver_t* const drv, uint16_t mhz); +extern fp_40_24_t max2871_set_frequency(max2871_driver_t* const drv, fp_40_24_t lo); extern void max2871_enable(max2871_driver_t* const drv); extern void max2871_disable(max2871_driver_t* const drv); diff --git a/firmware/common/mixer.c b/firmware/common/mixer.c index cc598701..c663c28c 100644 --- a/firmware/common/mixer.c +++ b/firmware/common/mixer.c @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 Great Scott Gadgets + * Copyright 2017-2026 Great Scott Gadgets * * This file is part of HackRF. * @@ -21,6 +21,7 @@ #include "mixer.h" +#include "fixed_point.h" #include "platform_gpio.h" #if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) #include "rffc5071.h" @@ -84,12 +85,12 @@ void mixer_setup(mixer_driver_t* const mixer) #endif } -uint64_t mixer_set_frequency(mixer_driver_t* const mixer, uint64_t hz) +fp_40_24_t mixer_set_frequency(mixer_driver_t* const mixer, fp_40_24_t lo) { #if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) - return rffc5071_set_frequency(mixer, hz); + return rffc5071_set_frequency(mixer, lo); #elif defined(RAD1O) - return max2871_set_frequency(mixer, hz / 1000000); + return max2871_set_frequency(mixer, lo); #endif } diff --git a/firmware/common/mixer.h b/firmware/common/mixer.h index d2eb57e9..9ef3c602 100644 --- a/firmware/common/mixer.h +++ b/firmware/common/mixer.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2014 Jared Boone * * This file is part of HackRF. @@ -22,6 +22,8 @@ #pragma once +#include "fixed_point.h" + #if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) #include "rffc5071.h" typedef rffc5071_driver_t mixer_driver_t; @@ -35,7 +37,7 @@ extern void mixer_bus_setup(mixer_driver_t* const mixer); extern void mixer_setup(mixer_driver_t* const mixer); /* Set frequency (Hz). */ -extern uint64_t mixer_set_frequency(mixer_driver_t* const mixer, uint64_t hz); +extern fp_40_24_t mixer_set_frequency(mixer_driver_t* const mixer, fp_40_24_t lo); extern void mixer_enable(mixer_driver_t* const mixer); extern void mixer_disable(mixer_driver_t* const mixer); diff --git a/firmware/common/rffc5071.c b/firmware/common/rffc5071.c index 177537f5..c229d417 100644 --- a/firmware/common/rffc5071.c +++ b/firmware/common/rffc5071.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2014 Jared Boone * * This file is part of HackRF. @@ -39,6 +39,7 @@ #endif #include "delay.h" +#include "fixed_point.h" #include "rffc5071.h" #include "rffc5071_regs.def" // private register def macros #include "selftest.h" @@ -153,7 +154,7 @@ void rffc5071_lock_test(rffc5071_driver_t* const drv) for (int i = 0; i < NUM_LOCK_ATTEMPTS; i++) { // Tune to 100MHz. - rffc5071_set_frequency(drv, 100000000); + rffc5071_set_frequency(drv, FP_MHZ(100)); rffc5071_enable(drv); // Wait 1ms. @@ -257,23 +258,23 @@ void rffc5071_enable(rffc5071_driver_t* const drv) } } -#define FREQ_ONE_MHZ (1000ULL * 1000ULL) -#define REF_FREQ (40 * FREQ_ONE_MHZ) -#define LO_MAX (5400 * FREQ_ONE_MHZ) +#define REF_FREQ_HZ (40ULL * (1000ULL * 1000ULL)) +#define MIN_LO FP_KHZ(84375) +#define MAX_LO FP_MHZ(5400) -/* configure frequency synthesizer (lo in Hz) */ -uint64_t rffc5071_config_synth(rffc5071_driver_t* const drv, uint64_t lo) +/* configure frequency synthesizer (lo in 1/(2**24) Hz) */ +fp_40_24_t rffc5071_config_synth(rffc5071_driver_t* const drv, fp_40_24_t lo) { - uint64_t fvco; + fp_40_24_t fvco; uint8_t fbkdivlog; uint16_t n; - uint64_t tune_freq_hz; + fp_40_24_t tune_freq; uint16_t p1nmsb; uint8_t p1nlsb; /* Calculate n_lo (no division) */ uint8_t n_lo = 0; - uint64_t x = LO_MAX >> 1; + fp_40_24_t x = MAX_LO >> 1; while ((x >= lo) && (n_lo < 5)) { n_lo++; x >>= 1; @@ -285,7 +286,7 @@ uint64_t rffc5071_config_synth(rffc5071_driver_t* const drv, uint64_t lo) * Higher charge pump leakage setting and fbkdivlog are required above * 3.2 GHz. */ - if (fvco > (3200 * FREQ_ONE_MHZ)) { + if (fvco > FP_MHZ(3200)) { fbkdivlog = 2; set_RFFC5071_PLLCPL(drv, 3); } else { @@ -293,7 +294,7 @@ uint64_t rffc5071_config_synth(rffc5071_driver_t* const drv, uint64_t lo) set_RFFC5071_PLLCPL(drv, 2); } - uint64_t tmp_n = (fvco << (24ULL - fbkdivlog)) / REF_FREQ; + uint64_t tmp_n = (fvco / REF_FREQ_HZ) >> fbkdivlog; /* Round to nearest step = ref_MHz / 2**s. For s=6, step=625000 Hz */ /* This also ensures the lowest 22-s fractional bits are set to 0. */ @@ -305,7 +306,7 @@ uint64_t rffc5071_config_synth(rffc5071_driver_t* const drv, uint64_t lo) p1nmsb = (tmp_n >> 8ULL) & 0xffff; p1nlsb = tmp_n & 0xff; - tune_freq_hz = (tmp_n * REF_FREQ) >> (24 - fbkdivlog + n_lo); + tune_freq = ((tmp_n * REF_FREQ_HZ) << fbkdivlog) >> n_lo; /* Path 2 */ set_RFFC5071_P2LODIV(drv, n_lo); @@ -319,14 +320,14 @@ uint64_t rffc5071_config_synth(rffc5071_driver_t* const drv, uint64_t lo) rffc5071_regs_commit(drv); - return tune_freq_hz; + return tune_freq; } -uint64_t rffc5071_set_frequency(rffc5071_driver_t* const drv, uint64_t hz) +fp_40_24_t rffc5071_set_frequency(rffc5071_driver_t* const drv, fp_40_24_t lo) { - uint32_t tune_freq; + fp_40_24_t tune_freq; - tune_freq = rffc5071_config_synth(drv, hz); + tune_freq = rffc5071_config_synth(drv, lo); if (enabled) { set_RFFC5071_RELOK(drv, 1); rffc5071_regs_commit(drv); diff --git a/firmware/common/rffc5071.h b/firmware/common/rffc5071.h index 1ddf4728..098f1628 100644 --- a/firmware/common/rffc5071.h +++ b/firmware/common/rffc5071.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2014 Jared Boone * * This file is part of HackRF. @@ -25,6 +25,7 @@ #include #include +#include "fixed_point.h" #include "gpio.h" #include "spi_bus.h" @@ -59,8 +60,8 @@ extern void rffc5071_reg_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t * provided routines for those operations. */ extern void rffc5071_regs_commit(rffc5071_driver_t* const drv); -/* Set frequency (Hz). */ -extern uint64_t rffc5071_set_frequency(rffc5071_driver_t* const drv, uint64_t hz); +/* Set frequency in 1/(2**24) Hz. */ +extern fp_40_24_t rffc5071_set_frequency(rffc5071_driver_t* const drv, fp_40_24_t lo); extern void rffc5071_enable(rffc5071_driver_t* const drv); extern void rffc5071_disable(rffc5071_driver_t* const drv); diff --git a/firmware/common/tuning.c b/firmware/common/tuning.c index b2d49ec4..000ede9d 100644 --- a/firmware/common/tuning.c +++ b/firmware/common/tuning.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * Copyright 2013 Benjamin Vernoux * @@ -23,6 +23,7 @@ #include "tuning.h" +#include "fixed_point.h" #include "hackrf_core.h" #include "hackrf_ui.h" #include "max283x.h" @@ -102,13 +103,15 @@ bool set_freq(const uint64_t freq, const transceiver_mode_t opmode) #endif mixer_freq_hz = max2837_freq_nominal_hz + freq; /* Set Freq and read real freq */ - real_mixer_freq_hz = mixer_set_frequency(&mixer, mixer_freq_hz); - max283x_set_frequency(&max283x, real_mixer_freq_hz - freq); + real_mixer_freq_hz = + mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ) / + FP_ONE_HZ; + max283x_set_frequency(&max283x, (real_mixer_freq_hz - freq) * FP_ONE_HZ); sgpio_cpld_set_mixer_invert(&sgpio_config, 1); } else if ((freq_mhz >= MIN_BYPASS_FREQ_MHZ) && (freq_mhz < MAX_BYPASS_FREQ_MHZ)) { rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS, opmode); /* mixer_freq_mhz <= not used in Bypass mode */ - max283x_set_frequency(&max283x, freq); + max283x_set_frequency(&max283x, freq * FP_ONE_HZ); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } else if ((freq_mhz >= MIN_HP_FREQ_MHZ) && (freq_mhz <= MAX_HP_FREQ_MHZ)) { if (freq_mhz < MID1_HP_FREQ_MHZ) { @@ -128,8 +131,10 @@ bool set_freq(const uint64_t freq, const transceiver_mode_t opmode) rf_path_set_filter(&rf_path, RF_PATH_FILTER_HIGH_PASS, opmode); mixer_freq_hz = freq - max2837_freq_nominal_hz; /* Set Freq and read real freq */ - real_mixer_freq_hz = mixer_set_frequency(&mixer, mixer_freq_hz); - max283x_set_frequency(&max283x, freq - real_mixer_freq_hz); + real_mixer_freq_hz = + mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ) / + FP_ONE_HZ; + max283x_set_frequency(&max283x, (freq - real_mixer_freq_hz) * FP_ONE_HZ); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } else { /* Error freq_mhz too high */ @@ -180,26 +185,36 @@ bool tuning_set_frequency( if (cfg->if_mhz == 0) { rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS, opmode); - max283x_set_frequency(&max283x, rf); + max283x_set_frequency(&max283x, rf * FP_ONE_HZ); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } else if (cfg->if_mhz > freq_mhz) { rf_path_set_filter(&rf_path, RF_PATH_FILTER_LOW_PASS, opmode); if (cfg->high_lo) { mixer_freq_hz = FREQ_ONE_MHZ * cfg->if_mhz + rf; - real_mixer_freq_hz = mixer_set_frequency(&mixer, mixer_freq_hz); - max283x_set_frequency(&max283x, real_mixer_freq_hz - rf); + real_mixer_freq_hz = + mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ) / + FP_ONE_HZ; + max283x_set_frequency( + &max283x, + (real_mixer_freq_hz - rf) * FP_ONE_HZ); sgpio_cpld_set_mixer_invert(&sgpio_config, 1); } else { mixer_freq_hz = FREQ_ONE_MHZ * cfg->if_mhz - rf; - real_mixer_freq_hz = mixer_set_frequency(&mixer, mixer_freq_hz); - max283x_set_frequency(&max283x, real_mixer_freq_hz + rf); + real_mixer_freq_hz = + mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ) / + FP_ONE_HZ; + max283x_set_frequency( + &max283x, + (real_mixer_freq_hz + rf) * FP_ONE_HZ); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } } else { rf_path_set_filter(&rf_path, RF_PATH_FILTER_HIGH_PASS, opmode); mixer_freq_hz = rf - FREQ_ONE_MHZ * cfg->if_mhz; - real_mixer_freq_hz = mixer_set_frequency(&mixer, mixer_freq_hz); - max283x_set_frequency(&max283x, rf - real_mixer_freq_hz); + real_mixer_freq_hz = + mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ) / + FP_ONE_HZ; + max283x_set_frequency(&max283x, (rf - real_mixer_freq_hz) * FP_ONE_HZ); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } @@ -233,7 +248,7 @@ bool set_freq_explicit( } rf_path_set_filter(&rf_path, path, opmode); - max283x_set_frequency(&max283x, if_freq_hz); + max283x_set_frequency(&max283x, if_freq_hz * FP_ONE_HZ); if (lo_freq_hz > if_freq_hz) { sgpio_cpld_set_mixer_invert(&sgpio_config, 1); @@ -241,7 +256,7 @@ bool set_freq_explicit( sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } if (path != RF_PATH_FILTER_BYPASS) { - (void) mixer_set_frequency(&mixer, lo_freq_hz); + (void) mixer_set_frequency(&mixer, lo_freq_hz * FP_ONE_HZ); } return true; } From e84e867a6c1e3a9e27c8367f8bc0ae7cb06232f1 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Apr 2026 14:14:11 -0400 Subject: [PATCH 030/173] Return tuned frequency from tuning functions --- firmware/common/max2831.c | 4 +++- firmware/common/max2831.h | 2 +- firmware/common/max2837.c | 4 +++- firmware/common/max2837.h | 2 +- firmware/common/max2839.c | 4 +++- firmware/common/max2839.h | 2 +- firmware/common/max283x.c | 9 +++++---- firmware/common/max283x.h | 2 +- 8 files changed, 18 insertions(+), 11 deletions(-) diff --git a/firmware/common/max2831.c b/firmware/common/max2831.c index 8d0bf3ff..ee621b23 100644 --- a/firmware/common/max2831.c +++ b/firmware/common/max2831.c @@ -226,7 +226,7 @@ void max2831_stop(max2831_driver_t* const drv) /* Assume 40 MHz reference clock with R divider of 2. */ #define PFD_FREQ_HZ (20ULL * (1000ULL * 1000ULL)) -void max2831_set_frequency(max2831_driver_t* const drv, fp_40_24_t freq) +fp_40_24_t max2831_set_frequency(max2831_driver_t* const drv, fp_40_24_t freq) { uint64_t div; @@ -244,6 +244,8 @@ void max2831_set_frequency(max2831_driver_t* const drv, fp_40_24_t freq) set_MAX2831_SYN_FRAC_HI(drv, (div >> 6) & 0x3fff); set_MAX2831_SYN_FRAC_LO(drv, div & 0x3f); max2831_regs_commit(drv); + + return PFD_FREQ_HZ * (div << 4); } typedef struct { diff --git a/firmware/common/max2831.h b/firmware/common/max2831.h index 11ded416..58b15283 100644 --- a/firmware/common/max2831.h +++ b/firmware/common/max2831.h @@ -88,7 +88,7 @@ extern void max2831_start(max2831_driver_t* const drv); extern void max2831_stop(max2831_driver_t* const drv); /* Set frequency in 1/(2**24) Hz */ -extern void max2831_set_frequency(max2831_driver_t* const drv, fp_40_24_t freq); +extern fp_40_24_t max2831_set_frequency(max2831_driver_t* const drv, fp_40_24_t freq); uint32_t max2831_set_lpf_bandwidth( max2831_driver_t* const drv, const max2831_mode_t mode, diff --git a/firmware/common/max2837.c b/firmware/common/max2837.c index 1a4f9648..c0073226 100644 --- a/firmware/common/max2837.c +++ b/firmware/common/max2837.c @@ -232,7 +232,7 @@ void max2837_stop(max2837_driver_t* const drv) /* Assume 40 MHz reference clock with R divider of 1. */ #define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL)) -void max2837_set_frequency(max2837_driver_t* const drv, fp_40_24_t freq) +fp_40_24_t max2837_set_frequency(max2837_driver_t* const drv, fp_40_24_t freq) { uint8_t band; uint8_t lna_band; @@ -277,6 +277,8 @@ void max2837_set_frequency(max2837_driver_t* const drv, fp_40_24_t freq) max2837_regs_commit(drv); set_MAX2837_SYN_FRAC_LO(drv, div & 0x3ff); max2837_regs_commit(drv); + + return ((PFD_FREQ_HZ * 3) / 4) * (div << 4); } typedef struct { diff --git a/firmware/common/max2837.h b/firmware/common/max2837.h index d90844ce..f348568b 100644 --- a/firmware/common/max2837.h +++ b/firmware/common/max2837.h @@ -79,7 +79,7 @@ extern void max2837_start(max2837_driver_t* const drv); extern void max2837_stop(max2837_driver_t* const drv); /* Set frequency in 1/(2**24) Hz. */ -extern void max2837_set_frequency(max2837_driver_t* const drv, fp_40_24_t freq); +extern fp_40_24_t max2837_set_frequency(max2837_driver_t* const drv, fp_40_24_t freq); uint32_t max2837_set_lpf_bandwidth( max2837_driver_t* const drv, const uint32_t bandwidth_hz); diff --git a/firmware/common/max2839.c b/firmware/common/max2839.c index b2ef8ffb..27db1dee 100644 --- a/firmware/common/max2839.c +++ b/firmware/common/max2839.c @@ -243,7 +243,7 @@ void max2839_stop(max2839_driver_t* const drv) /* Assume 40 MHz reference clock with R divider of 1. */ #define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL)) -void max2839_set_frequency(max2839_driver_t* const drv, fp_40_24_t freq) +fp_40_24_t max2839_set_frequency(max2839_driver_t* const drv, fp_40_24_t freq) { uint8_t band; uint64_t div; @@ -282,6 +282,8 @@ void max2839_set_frequency(max2839_driver_t* const drv, fp_40_24_t freq) max2839_regs_commit(drv); set_MAX2839_SYN_FRAC_LO(drv, div & 0x3ff); max2839_regs_commit(drv); + + return ((PFD_FREQ_HZ * 3) / 4) * (div << 4); } typedef struct { diff --git a/firmware/common/max2839.h b/firmware/common/max2839.h index 1836bda2..7b9c253e 100644 --- a/firmware/common/max2839.h +++ b/firmware/common/max2839.h @@ -81,7 +81,7 @@ extern void max2839_start(max2839_driver_t* const drv); extern void max2839_stop(max2839_driver_t* const drv); /* Set frequency in 1/(2**24) Hz. */ -extern void max2839_set_frequency(max2839_driver_t* const drv, fp_40_24_t freq); +extern fp_40_24_t max2839_set_frequency(max2839_driver_t* const drv, fp_40_24_t freq); uint32_t max2839_set_lpf_bandwidth( max2839_driver_t* const drv, const uint32_t bandwidth_hz); diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index 4a9f39db..1a226ee2 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -290,23 +290,24 @@ void max283x_stop(max283x_driver_t* const drv) } /* Set frequency in 1/(2**24) Hz. */ -void max283x_set_frequency(max283x_driver_t* const drv, fp_40_24_t freq) +fp_40_24_t max283x_set_frequency(max283x_driver_t* const drv, fp_40_24_t freq) { switch (drv->type) { #ifdef PRALINE case MAX2831_VARIANT: - max2831_set_frequency(&drv->drv.max2831, freq); + return max2831_set_frequency(&drv->drv.max2831, freq); break; #else case MAX2837_VARIANT: - max2837_set_frequency(&drv->drv.max2837, freq); + return max2837_set_frequency(&drv->drv.max2837, freq); break; case MAX2839_VARIANT: - max2839_set_frequency(&drv->drv.max2839, freq); + return max2839_set_frequency(&drv->drv.max2839, freq); break; #endif } + return 0; } uint32_t max283x_set_lpf_bandwidth( diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h index 0fd7ac85..4856114f 100644 --- a/firmware/common/max283x.h +++ b/firmware/common/max283x.h @@ -103,7 +103,7 @@ void max283x_start(max283x_driver_t* const drv); void max283x_stop(max283x_driver_t* const drv); /* Set frequency in 1/(2**24) Hz. */ -void max283x_set_frequency(max283x_driver_t* const drv, fp_40_24_t freq); +fp_40_24_t max283x_set_frequency(max283x_driver_t* const drv, fp_40_24_t freq); uint32_t max283x_set_lpf_bandwidth( max283x_driver_t* const drv, const max283x_mode_t mode, From d032dedf9f765d25cdecea0b6a980222357cba31 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Apr 2026 14:18:02 -0400 Subject: [PATCH 031/173] Add program option to tuning functions --- firmware/common/fpga_selftest.c | 2 +- firmware/common/max2831.c | 21 +++++++++----- firmware/common/max2831.h | 7 +++-- firmware/common/max2837.c | 35 +++++++++++++--------- firmware/common/max2837.h | 7 +++-- firmware/common/max2839.c | 33 ++++++++++++--------- firmware/common/max2839.h | 7 +++-- firmware/common/max283x.c | 16 +++++++---- firmware/common/max283x.h | 5 +++- firmware/common/max2871.c | 22 +++++++------- firmware/common/max2871.h | 7 ++++- firmware/common/mixer.c | 8 ++++-- firmware/common/mixer.h | 7 ++++- firmware/common/rffc5071.c | 51 +++++++++++++++++++-------------- firmware/common/rffc5071.h | 5 +++- firmware/common/tuning.c | 47 +++++++++++++++++++----------- 16 files changed, 179 insertions(+), 101 deletions(-) diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index c61d0692..270d3ff4 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -196,7 +196,7 @@ bool fpga_if_xcvr_selftest(void) rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_RX_CALIBRATION); max283x_set_lna_gain(&max283x, 16); max283x_set_vga_gain(&max283x, 36); - max283x_set_frequency(&max283x, FP_MHZ(2500)); + max283x_set_frequency(&max283x, FP_MHZ(2500), true); // Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF sample_rate_set(SR_FP_MHZ(4), true); diff --git a/firmware/common/max2831.c b/firmware/common/max2831.c index ee621b23..f0afaccc 100644 --- a/firmware/common/max2831.c +++ b/firmware/common/max2831.c @@ -27,9 +27,11 @@ * pirate commands to do the same thing. */ +#include "max2831.h" + +#include #include #include -#include "max2831.h" #include "fixed_point.h" #include "max2831_regs.def" // private register def macros #include "selftest.h" @@ -226,7 +228,10 @@ void max2831_stop(max2831_driver_t* const drv) /* Assume 40 MHz reference clock with R divider of 2. */ #define PFD_FREQ_HZ (20ULL * (1000ULL * 1000ULL)) -fp_40_24_t max2831_set_frequency(max2831_driver_t* const drv, fp_40_24_t freq) +fp_40_24_t max2831_set_frequency( + max2831_driver_t* const drv, + fp_40_24_t freq, + bool program) { uint64_t div; @@ -239,11 +244,13 @@ fp_40_24_t max2831_set_frequency(max2831_driver_t* const drv, fp_40_24_t freq) */ div = div >> 4; - //set_MAX2831_SYN_REF_DIV(drv, MAX2831_SYN_REF_DIV_2); - set_MAX2831_SYN_INT(drv, (div >> 20) & 0xff); - set_MAX2831_SYN_FRAC_HI(drv, (div >> 6) & 0x3fff); - set_MAX2831_SYN_FRAC_LO(drv, div & 0x3f); - max2831_regs_commit(drv); + if (program) { + //set_MAX2831_SYN_REF_DIV(drv, MAX2831_SYN_REF_DIV_2); + set_MAX2831_SYN_INT(drv, (div >> 20) & 0xff); + set_MAX2831_SYN_FRAC_HI(drv, (div >> 6) & 0x3fff); + set_MAX2831_SYN_FRAC_LO(drv, div & 0x3f); + max2831_regs_commit(drv); + } return PFD_FREQ_HZ * (div << 4); } diff --git a/firmware/common/max2831.h b/firmware/common/max2831.h index 58b15283..0885fd39 100644 --- a/firmware/common/max2831.h +++ b/firmware/common/max2831.h @@ -21,8 +21,8 @@ #pragma once -#include #include +#include #include "fixed_point.h" #include "gpio.h" @@ -88,7 +88,10 @@ extern void max2831_start(max2831_driver_t* const drv); extern void max2831_stop(max2831_driver_t* const drv); /* Set frequency in 1/(2**24) Hz */ -extern fp_40_24_t max2831_set_frequency(max2831_driver_t* const drv, fp_40_24_t freq); +extern fp_40_24_t max2831_set_frequency( + max2831_driver_t* const drv, + fp_40_24_t freq, + bool program); uint32_t max2831_set_lpf_bandwidth( max2831_driver_t* const drv, const max2831_mode_t mode, diff --git a/firmware/common/max2837.c b/firmware/common/max2837.c index c0073226..b5f09239 100644 --- a/firmware/common/max2837.c +++ b/firmware/common/max2837.c @@ -29,9 +29,11 @@ * pirate commands to do the same thing. */ +#include "max2837.h" + +#include #include #include -#include "max2837.h" #include "fixed_point.h" #include "max2837_regs.def" // private register def macros #include "selftest.h" @@ -232,7 +234,10 @@ void max2837_stop(max2837_driver_t* const drv) /* Assume 40 MHz reference clock with R divider of 1. */ #define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL)) -fp_40_24_t max2837_set_frequency(max2837_driver_t* const drv, fp_40_24_t freq) +fp_40_24_t max2837_set_frequency( + max2837_driver_t* const drv, + fp_40_24_t freq, + bool program) { uint8_t band; uint8_t lna_band; @@ -264,19 +269,21 @@ fp_40_24_t max2837_set_frequency(max2837_driver_t* const drv, fp_40_24_t freq) */ div = div >> 4; - /* Band settings */ - set_MAX2837_LOGEN_BSW(drv, band); - set_MAX2837_LNAband(drv, lna_band); + if (program) { + /* Band settings */ + set_MAX2837_LOGEN_BSW(drv, band); + set_MAX2837_LNAband(drv, lna_band); - /* - * Write order matters here, so commit INT and FRAC_HI before - * committing FRAC_LO, which is the trigger for VCO auto-select. - */ - set_MAX2837_SYN_INT(drv, (div >> 20) & 0xff); - set_MAX2837_SYN_FRAC_HI(drv, (div >> 10) & 0x3ff); - max2837_regs_commit(drv); - set_MAX2837_SYN_FRAC_LO(drv, div & 0x3ff); - max2837_regs_commit(drv); + /* + * Write order matters here, so commit INT and FRAC_HI before + * committing FRAC_LO, which is the trigger for VCO auto-select. + */ + set_MAX2837_SYN_INT(drv, (div >> 20) & 0xff); + set_MAX2837_SYN_FRAC_HI(drv, (div >> 10) & 0x3ff); + max2837_regs_commit(drv); + set_MAX2837_SYN_FRAC_LO(drv, div & 0x3ff); + max2837_regs_commit(drv); + } return ((PFD_FREQ_HZ * 3) / 4) * (div << 4); } diff --git a/firmware/common/max2837.h b/firmware/common/max2837.h index f348568b..f32bde6e 100644 --- a/firmware/common/max2837.h +++ b/firmware/common/max2837.h @@ -23,8 +23,8 @@ #pragma once -#include #include +#include #include "fixed_point.h" #include "gpio.h" @@ -79,7 +79,10 @@ extern void max2837_start(max2837_driver_t* const drv); extern void max2837_stop(max2837_driver_t* const drv); /* Set frequency in 1/(2**24) Hz. */ -extern fp_40_24_t max2837_set_frequency(max2837_driver_t* const drv, fp_40_24_t freq); +extern fp_40_24_t max2837_set_frequency( + max2837_driver_t* const drv, + fp_40_24_t freq, + bool program); uint32_t max2837_set_lpf_bandwidth( max2837_driver_t* const drv, const uint32_t bandwidth_hz); diff --git a/firmware/common/max2839.c b/firmware/common/max2839.c index 27db1dee..a34c2d75 100644 --- a/firmware/common/max2839.c +++ b/firmware/common/max2839.c @@ -29,9 +29,11 @@ * pirate commands to do the same thing. */ +#include "max2839.h" + +#include #include #include -#include "max2839.h" #include "fixed_point.h" #include "max2839_regs.def" // private register def macros #include "selftest.h" @@ -243,7 +245,10 @@ void max2839_stop(max2839_driver_t* const drv) /* Assume 40 MHz reference clock with R divider of 1. */ #define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL)) -fp_40_24_t max2839_set_frequency(max2839_driver_t* const drv, fp_40_24_t freq) +fp_40_24_t max2839_set_frequency( + max2839_driver_t* const drv, + fp_40_24_t freq, + bool program) { uint8_t band; uint64_t div; @@ -270,18 +275,20 @@ fp_40_24_t max2839_set_frequency(max2839_driver_t* const drv, fp_40_24_t freq) */ div = div >> 4; - /* Band settings */ - set_MAX2839_LOGEN_BSW(drv, band); + if (program) { + /* Band settings */ + set_MAX2839_LOGEN_BSW(drv, band); - /* - * Write order matters here, so commit INT and FRAC_HI before - * committing FRAC_LO, which is the trigger for VCO auto-select. - */ - set_MAX2839_SYN_INT(drv, (div >> 20) & 0xff); - set_MAX2839_SYN_FRAC_HI(drv, (div >> 10) & 0x3ff); - max2839_regs_commit(drv); - set_MAX2839_SYN_FRAC_LO(drv, div & 0x3ff); - max2839_regs_commit(drv); + /* + * Write order matters here, so commit INT and FRAC_HI before + * committing FRAC_LO, which is the trigger for VCO auto-select. + */ + set_MAX2839_SYN_INT(drv, (div >> 20) & 0xff); + set_MAX2839_SYN_FRAC_HI(drv, (div >> 10) & 0x3ff); + max2839_regs_commit(drv); + set_MAX2839_SYN_FRAC_LO(drv, div & 0x3ff); + max2839_regs_commit(drv); + } return ((PFD_FREQ_HZ * 3) / 4) * (div << 4); } diff --git a/firmware/common/max2839.h b/firmware/common/max2839.h index 7b9c253e..0e397294 100644 --- a/firmware/common/max2839.h +++ b/firmware/common/max2839.h @@ -23,8 +23,8 @@ #pragma once -#include #include +#include #include "fixed_point.h" #include "gpio.h" @@ -81,7 +81,10 @@ extern void max2839_start(max2839_driver_t* const drv); extern void max2839_stop(max2839_driver_t* const drv); /* Set frequency in 1/(2**24) Hz. */ -extern fp_40_24_t max2839_set_frequency(max2839_driver_t* const drv, fp_40_24_t freq); +extern fp_40_24_t max2839_set_frequency( + max2839_driver_t* const drv, + fp_40_24_t freq, + bool program); uint32_t max2839_set_lpf_bandwidth( max2839_driver_t* const drv, const uint32_t bandwidth_hz); diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index 1a226ee2..c0e912e5 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -21,10 +21,11 @@ * Boston, MA 02110-1301, USA. */ -#include - #include "max283x.h" +#include +#include + #include "fixed_point.h" #include "platform_gpio.h" #include "spi_bus.h" @@ -290,20 +291,23 @@ void max283x_stop(max283x_driver_t* const drv) } /* Set frequency in 1/(2**24) Hz. */ -fp_40_24_t max283x_set_frequency(max283x_driver_t* const drv, fp_40_24_t freq) +fp_40_24_t max283x_set_frequency( + max283x_driver_t* const drv, + fp_40_24_t freq, + bool program) { switch (drv->type) { #ifdef PRALINE case MAX2831_VARIANT: - return max2831_set_frequency(&drv->drv.max2831, freq); + return max2831_set_frequency(&drv->drv.max2831, freq, program); break; #else case MAX2837_VARIANT: - return max2837_set_frequency(&drv->drv.max2837, freq); + return max2837_set_frequency(&drv->drv.max2837, freq, program); break; case MAX2839_VARIANT: - return max2839_set_frequency(&drv->drv.max2839, freq); + return max2839_set_frequency(&drv->drv.max2839, freq, program); break; #endif } diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h index 4856114f..db5a93f8 100644 --- a/firmware/common/max283x.h +++ b/firmware/common/max283x.h @@ -103,7 +103,10 @@ void max283x_start(max283x_driver_t* const drv); void max283x_stop(max283x_driver_t* const drv); /* Set frequency in 1/(2**24) Hz. */ -fp_40_24_t max283x_set_frequency(max283x_driver_t* const drv, fp_40_24_t freq); +fp_40_24_t max283x_set_frequency( + max283x_driver_t* const drv, + fp_40_24_t freq, + bool program); uint32_t max283x_set_lpf_bandwidth( max283x_driver_t* const drv, const max283x_mode_t mode, diff --git a/firmware/common/max2871.c b/firmware/common/max2871.c index 16ba18f1..e783f80f 100644 --- a/firmware/common/max2871.c +++ b/firmware/common/max2871.c @@ -133,7 +133,7 @@ void max2871_setup(max2871_driver_t* const drv) max2871_write_registers(drv); - max2871_set_frequency(drv, FP_MHZ(3500)); + max2871_set_frequency(drv, FP_MHZ(3500), true); } static void delay_ms(int ms) @@ -230,7 +230,7 @@ static void max2871_write_registers(max2871_driver_t* const drv) } /* Set frequency in 1/(2**24) Hz, rounded to nearest 40 MHz. */ -fp_40_24_t max2871_set_frequency(max2871_driver_t* const drv, fp_40_24_t lo) +fp_40_24_t max2871_set_frequency(max2871_driver_t* const drv, fp_40_24_t lo, bool program) { uint16_t mhz = lo / FP_ONE_MHZ; int n = mhz / 40; @@ -241,17 +241,19 @@ fp_40_24_t max2871_set_frequency(max2871_driver_t* const drv, fp_40_24_t lo) diva += 1; } - max2871_set_RFA_EN(0); - max2871_write_registers(drv); + if (program) { + max2871_set_RFA_EN(0); + max2871_write_registers(drv); - max2871_set_N(n); - max2871_set_DIVA(diva); - max2871_write_registers(drv); + max2871_set_N(n); + max2871_set_DIVA(diva); + max2871_write_registers(drv); - while (max2871_spi_read(drv) & MAX2871_VASA) {} + while (max2871_spi_read(drv) & MAX2871_VASA) {} - max2871_set_RFA_EN(1); - max2871_write_registers(drv); + max2871_set_RFA_EN(1); + max2871_write_registers(drv); + } return (mhz / 40) * FP_MHZ(40); } diff --git a/firmware/common/max2871.h b/firmware/common/max2871.h index 1b3e1a13..682ee8c0 100644 --- a/firmware/common/max2871.h +++ b/firmware/common/max2871.h @@ -21,6 +21,8 @@ #pragma once +#include + #include "fixed_point.h" #include "gpio.h" @@ -34,6 +36,9 @@ typedef struct { } max2871_driver_t; extern void max2871_setup(max2871_driver_t* const drv); -extern fp_40_24_t max2871_set_frequency(max2871_driver_t* const drv, fp_40_24_t lo); +extern fp_40_24_t max2871_set_frequency( + max2871_driver_t* const drv, + fp_40_24_t lo, + bool program); extern void max2871_enable(max2871_driver_t* const drv); extern void max2871_disable(max2871_driver_t* const drv); diff --git a/firmware/common/mixer.c b/firmware/common/mixer.c index c663c28c..8623ee6b 100644 --- a/firmware/common/mixer.c +++ b/firmware/common/mixer.c @@ -21,6 +21,8 @@ #include "mixer.h" +#include + #include "fixed_point.h" #include "platform_gpio.h" #if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) @@ -85,12 +87,12 @@ void mixer_setup(mixer_driver_t* const mixer) #endif } -fp_40_24_t mixer_set_frequency(mixer_driver_t* const mixer, fp_40_24_t lo) +fp_40_24_t mixer_set_frequency(mixer_driver_t* const mixer, fp_40_24_t lo, bool program) { #if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) - return rffc5071_set_frequency(mixer, lo); + return rffc5071_set_frequency(mixer, lo, program); #elif defined(RAD1O) - return max2871_set_frequency(mixer, lo); + return max2871_set_frequency(mixer, lo, program); #endif } diff --git a/firmware/common/mixer.h b/firmware/common/mixer.h index 9ef3c602..6ba47d75 100644 --- a/firmware/common/mixer.h +++ b/firmware/common/mixer.h @@ -22,6 +22,8 @@ #pragma once +#include + #include "fixed_point.h" #if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) @@ -37,7 +39,10 @@ extern void mixer_bus_setup(mixer_driver_t* const mixer); extern void mixer_setup(mixer_driver_t* const mixer); /* Set frequency (Hz). */ -extern fp_40_24_t mixer_set_frequency(mixer_driver_t* const mixer, fp_40_24_t lo); +extern fp_40_24_t mixer_set_frequency( + mixer_driver_t* const mixer, + fp_40_24_t lo, + bool program); extern void mixer_enable(mixer_driver_t* const mixer); extern void mixer_disable(mixer_driver_t* const mixer); diff --git a/firmware/common/rffc5071.c b/firmware/common/rffc5071.c index c229d417..c6d12492 100644 --- a/firmware/common/rffc5071.c +++ b/firmware/common/rffc5071.c @@ -31,6 +31,7 @@ * that the RFFC5071 includes a second mixer. */ +#include #include #include @@ -154,7 +155,7 @@ void rffc5071_lock_test(rffc5071_driver_t* const drv) for (int i = 0; i < NUM_LOCK_ATTEMPTS; i++) { // Tune to 100MHz. - rffc5071_set_frequency(drv, FP_MHZ(100)); + rffc5071_set_frequency(drv, FP_MHZ(100), true); rffc5071_enable(drv); // Wait 1ms. @@ -263,7 +264,7 @@ void rffc5071_enable(rffc5071_driver_t* const drv) #define MAX_LO FP_MHZ(5400) /* configure frequency synthesizer (lo in 1/(2**24) Hz) */ -fp_40_24_t rffc5071_config_synth(rffc5071_driver_t* const drv, fp_40_24_t lo) +fp_40_24_t rffc5071_config_synth(rffc5071_driver_t* const drv, fp_40_24_t lo, bool program) { fp_40_24_t fvco; uint8_t fbkdivlog; @@ -271,6 +272,7 @@ fp_40_24_t rffc5071_config_synth(rffc5071_driver_t* const drv, fp_40_24_t lo) fp_40_24_t tune_freq; uint16_t p1nmsb; uint8_t p1nlsb; + uint8_t charge_pump_leakage; /* Calculate n_lo (no division) */ uint8_t n_lo = 0; @@ -288,10 +290,10 @@ fp_40_24_t rffc5071_config_synth(rffc5071_driver_t* const drv, fp_40_24_t lo) */ if (fvco > FP_MHZ(3200)) { fbkdivlog = 2; - set_RFFC5071_PLLCPL(drv, 3); + charge_pump_leakage = 3; } else { fbkdivlog = 1; - set_RFFC5071_PLLCPL(drv, 2); + charge_pump_leakage = 2; } uint64_t tmp_n = (fvco / REF_FREQ_HZ) >> fbkdivlog; @@ -302,33 +304,40 @@ fp_40_24_t rffc5071_config_synth(rffc5071_driver_t* const drv, fp_40_24_t lo) const uint8_t d = (24 - fbkdivlog + n_lo) - s; tmp_n = ((tmp_n + (1 << (d - 1))) >> d) << d; - n = tmp_n >> 24ULL; - p1nmsb = (tmp_n >> 8ULL) & 0xffff; - p1nlsb = tmp_n & 0xff; - tune_freq = ((tmp_n * REF_FREQ_HZ) << fbkdivlog) >> n_lo; - /* Path 2 */ - set_RFFC5071_P2LODIV(drv, n_lo); - set_RFFC5071_P2N(drv, n); - set_RFFC5071_P2PRESC(drv, fbkdivlog); - set_RFFC5071_P2NMSB(drv, p1nmsb); - if (s > 14) { - /* Only set when the step size is small enough. */ - set_RFFC5071_P2NLSB(drv, p1nlsb); - } + if (program) { + set_RFFC5071_PLLCPL(drv, charge_pump_leakage); - rffc5071_regs_commit(drv); + n = tmp_n >> 24ULL; + p1nmsb = (tmp_n >> 8ULL) & 0xffff; + p1nlsb = tmp_n & 0xff; + + /* Path 2 */ + set_RFFC5071_P2LODIV(drv, n_lo); + set_RFFC5071_P2N(drv, n); + set_RFFC5071_P2PRESC(drv, fbkdivlog); + set_RFFC5071_P2NMSB(drv, p1nmsb); + if (s > 14) { + /* Only set when the step size is small enough. */ + set_RFFC5071_P2NLSB(drv, p1nlsb); + } + + rffc5071_regs_commit(drv); + } return tune_freq; } -fp_40_24_t rffc5071_set_frequency(rffc5071_driver_t* const drv, fp_40_24_t lo) +fp_40_24_t rffc5071_set_frequency( + rffc5071_driver_t* const drv, + fp_40_24_t lo, + bool program) { fp_40_24_t tune_freq; - tune_freq = rffc5071_config_synth(drv, lo); - if (enabled) { + tune_freq = rffc5071_config_synth(drv, lo, program); + if (enabled && program) { set_RFFC5071_RELOK(drv, 1); rffc5071_regs_commit(drv); } diff --git a/firmware/common/rffc5071.h b/firmware/common/rffc5071.h index 098f1628..41e0509d 100644 --- a/firmware/common/rffc5071.h +++ b/firmware/common/rffc5071.h @@ -61,7 +61,10 @@ extern void rffc5071_reg_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t extern void rffc5071_regs_commit(rffc5071_driver_t* const drv); /* Set frequency in 1/(2**24) Hz. */ -extern fp_40_24_t rffc5071_set_frequency(rffc5071_driver_t* const drv, fp_40_24_t lo); +extern fp_40_24_t rffc5071_set_frequency( + rffc5071_driver_t* const drv, + fp_40_24_t lo, + bool program); extern void rffc5071_enable(rffc5071_driver_t* const drv); extern void rffc5071_disable(rffc5071_driver_t* const drv); diff --git a/firmware/common/tuning.c b/firmware/common/tuning.c index 000ede9d..3f38fbd4 100644 --- a/firmware/common/tuning.c +++ b/firmware/common/tuning.c @@ -104,14 +104,17 @@ bool set_freq(const uint64_t freq, const transceiver_mode_t opmode) mixer_freq_hz = max2837_freq_nominal_hz + freq; /* Set Freq and read real freq */ real_mixer_freq_hz = - mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ) / + mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ, true) / FP_ONE_HZ; - max283x_set_frequency(&max283x, (real_mixer_freq_hz - freq) * FP_ONE_HZ); + max283x_set_frequency( + &max283x, + (real_mixer_freq_hz - freq) * FP_ONE_HZ, + true); sgpio_cpld_set_mixer_invert(&sgpio_config, 1); } else if ((freq_mhz >= MIN_BYPASS_FREQ_MHZ) && (freq_mhz < MAX_BYPASS_FREQ_MHZ)) { rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS, opmode); /* mixer_freq_mhz <= not used in Bypass mode */ - max283x_set_frequency(&max283x, freq * FP_ONE_HZ); + max283x_set_frequency(&max283x, freq * FP_ONE_HZ, true); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } else if ((freq_mhz >= MIN_HP_FREQ_MHZ) && (freq_mhz <= MAX_HP_FREQ_MHZ)) { if (freq_mhz < MID1_HP_FREQ_MHZ) { @@ -132,9 +135,12 @@ bool set_freq(const uint64_t freq, const transceiver_mode_t opmode) mixer_freq_hz = freq - max2837_freq_nominal_hz; /* Set Freq and read real freq */ real_mixer_freq_hz = - mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ) / + mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ, true) / FP_ONE_HZ; - max283x_set_frequency(&max283x, (freq - real_mixer_freq_hz) * FP_ONE_HZ); + max283x_set_frequency( + &max283x, + (freq - real_mixer_freq_hz) * FP_ONE_HZ, + true); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } else { /* Error freq_mhz too high */ @@ -185,36 +191,45 @@ bool tuning_set_frequency( if (cfg->if_mhz == 0) { rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS, opmode); - max283x_set_frequency(&max283x, rf * FP_ONE_HZ); + max283x_set_frequency(&max283x, rf * FP_ONE_HZ, true); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } else if (cfg->if_mhz > freq_mhz) { rf_path_set_filter(&rf_path, RF_PATH_FILTER_LOW_PASS, opmode); if (cfg->high_lo) { mixer_freq_hz = FREQ_ONE_MHZ * cfg->if_mhz + rf; - real_mixer_freq_hz = - mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ) / + real_mixer_freq_hz = mixer_set_frequency( + &mixer, + mixer_freq_hz * FP_ONE_HZ, + true) / FP_ONE_HZ; max283x_set_frequency( &max283x, - (real_mixer_freq_hz - rf) * FP_ONE_HZ); + (real_mixer_freq_hz - rf) * FP_ONE_HZ, + true); sgpio_cpld_set_mixer_invert(&sgpio_config, 1); } else { mixer_freq_hz = FREQ_ONE_MHZ * cfg->if_mhz - rf; - real_mixer_freq_hz = - mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ) / + real_mixer_freq_hz = mixer_set_frequency( + &mixer, + mixer_freq_hz * FP_ONE_HZ, + true) / FP_ONE_HZ; max283x_set_frequency( &max283x, - (real_mixer_freq_hz + rf) * FP_ONE_HZ); + (real_mixer_freq_hz + rf) * FP_ONE_HZ, + true); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } } else { rf_path_set_filter(&rf_path, RF_PATH_FILTER_HIGH_PASS, opmode); mixer_freq_hz = rf - FREQ_ONE_MHZ * cfg->if_mhz; real_mixer_freq_hz = - mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ) / + mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ, true) / FP_ONE_HZ; - max283x_set_frequency(&max283x, (rf - real_mixer_freq_hz) * FP_ONE_HZ); + max283x_set_frequency( + &max283x, + (rf - real_mixer_freq_hz) * FP_ONE_HZ, + true); sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } @@ -248,7 +263,7 @@ bool set_freq_explicit( } rf_path_set_filter(&rf_path, path, opmode); - max283x_set_frequency(&max283x, if_freq_hz * FP_ONE_HZ); + max283x_set_frequency(&max283x, if_freq_hz * FP_ONE_HZ, true); if (lo_freq_hz > if_freq_hz) { sgpio_cpld_set_mixer_invert(&sgpio_config, 1); @@ -256,7 +271,7 @@ bool set_freq_explicit( sgpio_cpld_set_mixer_invert(&sgpio_config, 0); } if (path != RF_PATH_FILTER_BYPASS) { - (void) mixer_set_frequency(&mixer, lo_freq_hz * FP_ONE_HZ); + (void) mixer_set_frequency(&mixer, lo_freq_hz * FP_ONE_HZ, true); } return true; } From 207ed04f6b0395624933e4e626ea4d3ea1ac2432 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Apr 2026 14:19:09 -0400 Subject: [PATCH 032/173] Restrict tuning frequency in tuning functions --- firmware/common/max2831.c | 7 +++++++ firmware/common/max2837.c | 9 +++++++++ firmware/common/max2839.c | 9 +++++++++ firmware/common/max2871.c | 9 +++++++++ firmware/common/rffc5071.c | 6 ++++++ 5 files changed, 40 insertions(+) diff --git a/firmware/common/max2831.c b/firmware/common/max2831.c index f0afaccc..05d5cb29 100644 --- a/firmware/common/max2831.c +++ b/firmware/common/max2831.c @@ -38,6 +38,7 @@ #include "adc.h" #define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) /* Default register values. */ static const uint16_t max2831_regs_default[MAX2831_NUM_REGS] = { @@ -228,6 +229,9 @@ void max2831_stop(max2831_driver_t* const drv) /* Assume 40 MHz reference clock with R divider of 2. */ #define PFD_FREQ_HZ (20ULL * (1000ULL * 1000ULL)) +#define MIN_FREQ FP_MHZ(2000) +#define MAX_FREQ FP_MHZ(3000) + fp_40_24_t max2831_set_frequency( max2831_driver_t* const drv, fp_40_24_t freq, @@ -235,6 +239,9 @@ fp_40_24_t max2831_set_frequency( { uint64_t div; + freq = MIN(freq, MAX_FREQ); + freq = MAX(freq, MIN_FREQ); + freq += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */ div = freq / PFD_FREQ_HZ; diff --git a/firmware/common/max2837.c b/firmware/common/max2837.c index b5f09239..561a17f9 100644 --- a/firmware/common/max2837.c +++ b/firmware/common/max2837.c @@ -38,6 +38,9 @@ #include "max2837_regs.def" // private register def macros #include "selftest.h" +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + /* Default register values. */ static const uint16_t max2837_regs_default[MAX2837_NUM_REGS] = { 0x150, /* 0 */ @@ -234,6 +237,9 @@ void max2837_stop(max2837_driver_t* const drv) /* Assume 40 MHz reference clock with R divider of 1. */ #define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL)) +#define MIN_FREQ FP_MHZ(2000) +#define MAX_FREQ FP_MHZ(3000) + fp_40_24_t max2837_set_frequency( max2837_driver_t* const drv, fp_40_24_t freq, @@ -243,6 +249,9 @@ fp_40_24_t max2837_set_frequency( uint8_t lna_band; uint64_t div; + freq = MIN(freq, MAX_FREQ); + freq = MAX(freq, MIN_FREQ); + /* Select band. Allow tuning outside specified bands. */ if (freq < FP_MHZ(2400)) { band = MAX2837_LOGEN_BSW_2_3; diff --git a/firmware/common/max2839.c b/firmware/common/max2839.c index a34c2d75..87917ed8 100644 --- a/firmware/common/max2839.c +++ b/firmware/common/max2839.c @@ -38,6 +38,9 @@ #include "max2839_regs.def" // private register def macros #include "selftest.h" +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + static uint8_t requested_lna_gain = 0; static uint8_t requested_vga_gain = 0; @@ -245,6 +248,9 @@ void max2839_stop(max2839_driver_t* const drv) /* Assume 40 MHz reference clock with R divider of 1. */ #define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL)) +#define MIN_FREQ FP_MHZ(2000) +#define MAX_FREQ FP_MHZ(3000) + fp_40_24_t max2839_set_frequency( max2839_driver_t* const drv, fp_40_24_t freq, @@ -253,6 +259,9 @@ fp_40_24_t max2839_set_frequency( uint8_t band; uint64_t div; + freq = MIN(freq, MAX_FREQ); + freq = MAX(freq, MIN_FREQ); + /* Select band. Allow tuning outside specified bands. */ if (freq < FP_MHZ(2400)) { band = MAX2839_LOGEN_BSW_2_3; diff --git a/firmware/common/max2871.c b/firmware/common/max2871.c index e783f80f..4c5a94b2 100644 --- a/firmware/common/max2871.c +++ b/firmware/common/max2871.c @@ -37,6 +37,9 @@ #include #endif +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + static uint32_t max2871_spi_read(max2871_driver_t* const drv); static void max2871_spi_write(max2871_driver_t* const drv, uint8_t r, uint32_t v); static void max2871_write_registers(max2871_driver_t* const drv); @@ -229,9 +232,15 @@ static void max2871_write_registers(max2871_driver_t* const drv) } } +#define MIN_LO FP_MHZ(40) +#define MAX_LO FP_MHZ(6000) + /* Set frequency in 1/(2**24) Hz, rounded to nearest 40 MHz. */ fp_40_24_t max2871_set_frequency(max2871_driver_t* const drv, fp_40_24_t lo, bool program) { + lo = MIN(lo, MAX_LO); + lo = MAX(lo, MIN_LO); + uint16_t mhz = lo / FP_ONE_MHZ; int n = mhz / 40; int diva = 0; diff --git a/firmware/common/rffc5071.c b/firmware/common/rffc5071.c index c6d12492..ffe958c1 100644 --- a/firmware/common/rffc5071.c +++ b/firmware/common/rffc5071.c @@ -48,6 +48,9 @@ #include "platform_scu.h" #endif +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + static bool enabled = false; /* Default register values from vendor documentation or software. */ @@ -274,6 +277,9 @@ fp_40_24_t rffc5071_config_synth(rffc5071_driver_t* const drv, fp_40_24_t lo, bo uint8_t p1nlsb; uint8_t charge_pump_leakage; + lo = MIN(lo, MAX_LO); + lo = MAX(lo, MIN_LO); + /* Calculate n_lo (no division) */ uint8_t n_lo = 0; fp_40_24_t x = MAX_LO >> 1; From 4366b492069ee03a946169ee221f27632fe2a154 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Apr 2026 14:27:27 -0400 Subject: [PATCH 033/173] Refactor radio tuning - Store applied settings in radio registers - Support arbitrary combinations of tuning registers --- firmware/common/radio.c | 388 +++++++++++++++++++++++++++++---------- firmware/common/tuning.c | 282 ++++++---------------------- firmware/common/tuning.h | 36 ++-- 3 files changed, 356 insertions(+), 350 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index d8d3a918..7f274c05 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -28,15 +28,20 @@ #include "fixed_point.h" #include "hackrf_core.h" #include "hackrf_ui.h" +#include "mixer.h" #include "max283x.h" #include "platform_detect.h" #include "rf_path.h" +#include "sgpio.h" #include "transceiver_mode.h" #include "tuning.h" #if defined(PRALINE) #include "fpga.h" #include "tune_config.h" #endif +#if defined(HACKRF_ONE) || defined(PRALINE) + #include "operacake.h" +#endif #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) @@ -240,139 +245,316 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) return (new_afe_rate || new_rate || new_n); } -#define DEFAULT_RF FP_MHZ(2450) - -static fp_28_36_t applied_offset = RADIO_UNSET; - -#ifdef PRALINE -static const tune_config_t* select_tune_config(uint64_t opmode) +/* + * Compute frequency offset in 1/(2**24) Hz for a given rotation. This is an + * unsigned value that does not indicate direction of frequency shift. + */ +static fp_40_24_t compute_offset(radio_t* const radio, uint64_t opmode, uint64_t rotation) { + uint64_t n; + fp_40_24_t offset = 0; + const fp_28_36_t mcu_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]; + switch (opmode) { case TRANSCEIVER_MODE_TX: case TRANSCEIVER_MODE_SS: - return praline_tune_config_tx; - case TRANSCEIVER_MODE_RX_SWEEP: - return praline_tune_config_rx_sweep; + n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]; + break; default: - return praline_tune_config_rx; + n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]; } + if ((rotation != 0) && (rotation != RADIO_UNSET) && (mcu_rate != RADIO_UNSET) && + (n != RADIO_UNSET)) { + const fp_40_24_t afe_rate = mcu_rate >> (12 - n); + if (rotation & 0x80) { + rotation = 0x100 - rotation; + } + offset = (afe_rate * rotation) >> 8; + } + return offset; +} + +/* + * Convert center frequency of analog baseband (as seen by ADC/DAC) to center + * frequency of digital baseband (as seen by MCU). + */ +static fp_40_24_t digital_from_analog_rf( + radio_t* const radio, + const fp_40_24_t arf, + uint64_t opmode, + uint64_t rotation) +{ + fp_40_24_t offset = compute_offset(radio, opmode, rotation); + fp_40_24_t drf = arf; + if (rotation > 0x80) { + if (offset > arf) { + drf = offset - arf; + } else { + drf = arf - offset; + } + } else if ((rotation & 0x7f) > 0x00) { + drf = arf + offset; + } + return drf; +} + +#ifdef PRALINE +/* + * Convert center frequency of digital baseband (as seen by MCU) to center + * frequency of analog baseband (as seen by ADC/DAC). + */ +static fp_40_24_t analog_from_digital_rf( + radio_t* const radio, + const fp_40_24_t drf, + uint64_t opmode, + uint64_t rotation) +{ + fp_40_24_t offset = compute_offset(radio, opmode, rotation); + fp_40_24_t arf = drf; + if (rotation > 0x80) { + arf = drf + offset; + } else if ((rotation & 0x7f) > 0x00) { + if (offset > drf) { + arf = offset - drf; + } else { + arf = drf - offset; + } + } + return arf; +} + +static const tune_config_t* select_tune_config(uint64_t opmode, fp_40_24_t freq_rf) +{ + const tune_config_t* tune_config; + + switch (opmode) { + case TRANSCEIVER_MODE_TX: + case TRANSCEIVER_MODE_SS: + tune_config = praline_tune_config_tx; + break; + case TRANSCEIVER_MODE_RX_SWEEP: + tune_config = praline_tune_config_rx_sweep; + break; + default: + tune_config = praline_tune_config_rx; + } + while ((tune_config->rf_range_end_mhz != 0) || (tune_config->if_mhz != 0)) { + if ((freq_rf == 0) || + (tune_config->rf_range_end_mhz > (freq_rf / FP_ONE_MHZ))) { + break; + } + tune_config++; + } + return tune_config; } #endif static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) { - uint8_t rotation = 0; - uint64_t requested_rf = bank[RADIO_FREQUENCY_RF]; + bool new_freq = false; + bool high_lo = false; + bool invert_spectrum = false; + + const uint64_t requested_rf = bank[RADIO_FREQUENCY_RF]; const uint64_t requested_if = bank[RADIO_FREQUENCY_IF]; const uint64_t requested_lo = bank[RADIO_FREQUENCY_LO]; const uint64_t requested_img_reject = bank[RADIO_IMAGE_REJECT]; + const uint64_t requested_rotation = bank[RADIO_ROTATION]; - bool set_if = (requested_if != RADIO_UNSET); - bool set_lo = (requested_lo != RADIO_UNSET); - bool set_img_reject = (requested_img_reject != RADIO_UNSET); + const uint64_t applied_rf = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF]; + const uint64_t applied_if = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF]; + const uint64_t applied_lo = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO]; + const uint64_t applied_img_reject = + radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT]; + const uint64_t applied_rotation = + radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION]; + + uint64_t freq_rf = applied_rf; + uint64_t analog_rf = applied_rf; + uint64_t freq_if = applied_if; + uint64_t freq_lo = applied_lo; + uint64_t img_reject = applied_img_reject; + uint64_t rotation = applied_rotation; uint64_t opmode = bank[RADIO_OPMODE]; if (opmode == RADIO_UNSET) { opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; } - if (set_if && set_lo && set_img_reject) { - bool new_if = - (requested_if != - radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF]); - bool new_lo = - (requested_lo != - radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO]); - bool new_img_reject = - (requested_img_reject != - radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT]); - if (new_if || new_lo || new_img_reject) { - set_freq_explicit( - requested_if / FP_ONE_HZ, - requested_lo / FP_ONE_HZ, - requested_img_reject, - opmode); - radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = - requested_if; - radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = - requested_lo; - radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT] = - requested_img_reject; - radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] = - RADIO_UNSET; + /* Restrict requested settings to valid ranges. */ + if (requested_img_reject != RADIO_UNSET) { + switch (requested_img_reject) { + case RF_PATH_FILTER_LOW_PASS: + case RF_PATH_FILTER_HIGH_PASS: + img_reject = requested_img_reject; + break; + default: + img_reject = RF_PATH_FILTER_BYPASS; } -#ifdef PRALINE - const uint64_t requested_rotation = bank[RADIO_ROTATION]; + } + if (requested_if != RADIO_UNSET) { + freq_if = max283x_set_frequency(&max283x, requested_if, false); + } + if (requested_lo != RADIO_UNSET) { + freq_lo = mixer_set_frequency(&mixer, freq_lo, false); + } + if (detected_platform() == BOARD_ID_PRALINE) { if (requested_rotation != RADIO_UNSET) { rotation = requested_rotation; - } else if (radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] != RADIO_UNSET) { - return true; } + switch (opmode) { + case TRANSCEIVER_MODE_RX: + case TRANSCEIVER_MODE_RX_SWEEP: + /* Round to nearest supported rotation. */ + rotation = (rotation + 0x20) & 0xc0; + if (rotation == 0x80) { + rotation = 0; + } + break; + default: + rotation = 0; + } + } else { + rotation = 0; + } + + /* Handle requested RF (auto-tune). */ + if (requested_rf != RADIO_UNSET) { + if (requested_img_reject == RADIO_UNSET) { + img_reject = select_img_reject(requested_rf); + } + freq_rf = restrict_rf(requested_rf, img_reject); + + /* Look up settings appropriate for requested RF. */ + if (detected_platform() == BOARD_ID_PRALINE) { +#ifdef PRALINE + const tune_config_t* tune_config = + select_tune_config(opmode, freq_rf); + if (requested_rotation == RADIO_UNSET) { + rotation = (uint8_t) (tune_config->shift) << 6; + } + analog_rf = + analog_from_digital_rf(radio, freq_rf, opmode, rotation); + if (requested_if == RADIO_UNSET) { + if (tune_config->if_mhz == 0) { + freq_if = analog_rf; + } else { + freq_if = tune_config->if_mhz * FP_ONE_MHZ; + } + } + high_lo = tune_config->high_lo; +#endif + } else { + /* Use graduated or fixed IF on older platforms. */ + if (requested_if == RADIO_UNSET) { + freq_if = select_graduated_if(freq_rf, img_reject); + } + high_lo = (img_reject == RF_PATH_FILTER_LOW_PASS); + analog_rf = freq_rf; + } + + /* Compute precise LO. This is done first because the mixer LO is set in coarse steps. */ + if (requested_lo == RADIO_UNSET) { + switch (img_reject) { + case RF_PATH_FILTER_LOW_PASS: + if (high_lo) { + freq_lo = freq_if + analog_rf; + } else { + freq_lo = freq_if - analog_rf; + } + break; + case RF_PATH_FILTER_HIGH_PASS: + freq_lo = analog_rf - freq_if; + break; + default: + freq_lo = RADIO_UNSET; + } + if (freq_lo != RADIO_UNSET) { + freq_lo = mixer_set_frequency(&mixer, freq_lo, false); + } + } + + /* Recompute IF, compensating for rounding of mixer LO. */ + if (requested_if == RADIO_UNSET) { + switch (img_reject) { + case RF_PATH_FILTER_LOW_PASS: + if (high_lo) { + freq_if = freq_lo - analog_rf; + } else { + freq_if = freq_lo + analog_rf; + } + break; + case RF_PATH_FILTER_HIGH_PASS: + freq_if = analog_rf - freq_lo; + } + } + } + + /* Apply settings. */ + if ((freq_if != applied_if) && (freq_if != RADIO_UNSET)) { + freq_if = max283x_set_frequency(&max283x, freq_if, true); + radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = freq_if; + new_freq = true; + } + if ((freq_lo != applied_lo) && (freq_lo != RADIO_UNSET)) { + freq_lo = mixer_set_frequency(&mixer, freq_lo, true); + radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = freq_lo; + new_freq = true; + } + if ((img_reject != applied_img_reject) && (img_reject != RADIO_UNSET)) { + rf_path_set_filter(&rf_path, img_reject, opmode); + radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT] = img_reject; + new_freq = true; + } + if ((rotation != applied_rotation) && (rotation != RADIO_UNSET)) { +#ifdef PRALINE fpga_set_rx_quarter_shift_mode(&fpga, rotation >> 6); #endif radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = rotation; - return true; + new_freq = true; } - if (requested_rf == RADIO_UNSET) { - if (radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] == - RADIO_UNSET) { - requested_rf = DEFAULT_RF; - } else { - return false; - } - } - - bool new_rf = - (radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] != requested_rf); - uint64_t requested_rf_hz = requested_rf / FP_ONE_HZ; -#ifdef PRALINE - if (applied_afe_rate == RADIO_UNSET) { - return false; - } - const tune_config_t* tune_config = select_tune_config(opmode); - const tune_config_t* applied_tune_config = - select_tune_config(radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]); - bool new_config = (applied_tune_config != tune_config); - while ((tune_config->rf_range_end_mhz != 0) || (tune_config->if_mhz != 0)) { - if ((requested_rf_hz == 0) || - (tune_config->rf_range_end_mhz > (requested_rf_hz / FREQ_ONE_MHZ))) { + /* Compute precise RF. */ + if ((img_reject != RADIO_UNSET) && (freq_if != RADIO_UNSET) && + (rotation != RADIO_UNSET) && + ((freq_lo != RADIO_UNSET) || (img_reject == RF_PATH_FILTER_BYPASS))) { + switch (img_reject) { + case RF_PATH_FILTER_LOW_PASS: + if (freq_lo > freq_if) { + analog_rf = freq_lo - freq_if; + invert_spectrum = true; + } else { + analog_rf = freq_if - freq_lo; + } break; + case RF_PATH_FILTER_HIGH_PASS: + analog_rf = freq_if + freq_lo; + break; + default: + analog_rf = freq_if; } - tune_config++; + freq_rf = digital_from_analog_rf(radio, analog_rf, opmode, rotation); } - bool new_rotation = - (radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] != - ((uint64_t) tune_config->shift << 6)); - if (new_rotation) { - fpga_set_rx_quarter_shift_mode(&fpga, tune_config->shift); - radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = - tune_config->shift << 6; - } - fp_28_36_t offset = applied_afe_rate / 4; - bool new_offset = (applied_offset != offset); - if (new_rotation || new_offset || new_config || new_rf) { - tuning_set_frequency( - tune_config, - requested_rf_hz, - offset / SR_FP_ONE_HZ, - opmode); - applied_offset = offset; - } -#else - if (new_rf) { - set_freq(requested_rf_hz, opmode); + if ((freq_rf != applied_rf) && (freq_rf != RADIO_UNSET)) { + radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] = freq_rf; + new_freq = true; } + + if (new_freq) { + sgpio_cpld_set_mixer_invert(&sgpio_config, invert_spectrum); + if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { + hackrf_ui()->set_frequency(freq_rf / FP_ONE_MHZ); + } +#if defined(PRALINE) || defined(HACKRF_ONE) + operacake_set_range(freq_rf / FP_ONE_MHZ); #endif - radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] = requested_rf; - radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = RADIO_UNSET; - radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = RADIO_UNSET; - radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT] = RADIO_UNSET; - return true; + } + return new_freq; } -static uint32_t auto_bandwidth(radio_t* const radio) +static uint32_t auto_bandwidth(radio_t* const radio, uint64_t opmode) { + uint32_t offset_hz = 0; uint64_t rotation = radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION]; uint32_t sample_rate_hz = DEFAULT_MCU_RATE / SR_FP_ONE_HZ; @@ -381,11 +563,8 @@ static uint32_t auto_bandwidth(radio_t* const radio) SR_FP_ONE_HZ; } - uint32_t offset_hz = 0; - if ((rotation != 0) && (rotation != RADIO_UNSET) && - (applied_offset != RADIO_UNSET)) { - offset_hz = applied_offset / SR_FP_ONE_HZ; - } + const fp_40_24_t offset = compute_offset(radio, opmode, rotation); + offset_hz = offset >> 24; const uint32_t bb_bandwidth = (sample_rate_hz * 3) / 4; const uint32_t lpf_bandwidth = bb_bandwidth + offset_hz * 2; @@ -400,10 +579,15 @@ static bool radio_update_bandwidth(radio_t* const radio, uint64_t* bank) { bool new_bw = false; + uint64_t opmode = bank[RADIO_OPMODE]; + if (opmode == RADIO_UNSET) { + opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; + } + #ifdef PRALINE /* Praline legacy mode always sets baseband bandwidth automatically. */ (void) bank; - uint32_t lpf_bandwidth = auto_bandwidth(radio); + uint32_t lpf_bandwidth = auto_bandwidth(radio, opmode); if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_TX, lpf_bandwidth); @@ -450,7 +634,7 @@ static bool radio_update_bandwidth(radio_t* const radio, uint64_t* bank) lpf_bandwidth = radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF]; } if (lpf_bandwidth == RADIO_UNSET) { - lpf_bandwidth = auto_bandwidth(radio); + lpf_bandwidth = auto_bandwidth(radio, opmode); } if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { diff --git a/firmware/common/tuning.c b/firmware/common/tuning.c index 3f38fbd4..7fe48bf0 100644 --- a/firmware/common/tuning.c +++ b/firmware/common/tuning.c @@ -24,254 +24,84 @@ #include "tuning.h" #include "fixed_point.h" -#include "hackrf_core.h" -#include "hackrf_ui.h" -#include "max283x.h" -#include "mixer.h" -#include "sgpio.h" -#include "transceiver_mode.h" -#if defined(PRALINE) || defined(HACKRF_ONE) - #include "operacake.h" -#endif +#include "platform_detect.h" + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + #ifdef PRALINE - #include "fpga.h" -#endif - -#ifndef PRALINE - - #define MIN_LP_FREQ_MHZ (0) - #define MAX_LP_FREQ_MHZ (2170ULL) - - #define ABS_MIN_BYPASS_FREQ_MHZ (2000ULL) - #define MIN_BYPASS_FREQ_MHZ (MAX_LP_FREQ_MHZ) - #define MAX_BYPASS_FREQ_MHZ (2740ULL) - #define ABS_MAX_BYPASS_FREQ_MHZ (3000ULL) - - #define MIN_HP_FREQ_MHZ (MAX_BYPASS_FREQ_MHZ) - #define MID1_HP_FREQ_MHZ (3600ULL) - #define MID2_HP_FREQ_MHZ (5100ULL) - #define MAX_HP_FREQ_MHZ (7250ULL) - - #define MIN_LO_FREQ_HZ (84375000ULL) - #define MAX_LO_FREQ_HZ (5400000000ULL) - + #define MIN_BYPASS_FREQ FP_MHZ(2320) + #define MAX_BYPASS_FREQ FP_MHZ(2580) #else - - #define MIN_LP_FREQ_MHZ (0) - #define MAX_LP_FREQ_MHZ (2320ULL) - - #define ABS_MIN_BYPASS_FREQ_MHZ (2000ULL) - #define MIN_BYPASS_FREQ_MHZ (MAX_LP_FREQ_MHZ) - #define MAX_BYPASS_FREQ_MHZ (2580ULL) - #define ABS_MAX_BYPASS_FREQ_MHZ (3000ULL) - - #define MIN_HP_FREQ_MHZ (MAX_BYPASS_FREQ_MHZ) - #define MAX_HP_FREQ_MHZ (7250ULL) - - #define MIN_LO_FREQ_HZ (84375000ULL) - #define MAX_LO_FREQ_HZ (5400000000ULL) - + #define MIN_BYPASS_FREQ FP_MHZ(2170) + #define MAX_BYPASS_FREQ FP_MHZ(2740) #endif -#ifndef PRALINE -static uint32_t max2837_freq_nominal_hz = 2560000000; +#define MID1_HP_FREQ FP_MHZ(3600) +#define MID2_HP_FREQ FP_MHZ(5100) +#define MAX_HP_FREQ FP_MHZ(7250) -/* - * Set freq/tuning between 0MHz to 7250 MHz (less than 16bits really used) - * hz between 0 to 999999 Hz (not checked) - * return false on error or true if success. - */ -bool set_freq(const uint64_t freq, const transceiver_mode_t opmode) +fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject) { - bool success; - uint64_t mixer_freq_hz; - uint64_t real_mixer_freq_hz; + fp_40_24_t freq_if; - const uint32_t freq_mhz = freq / FREQ_ONE_MHZ; - - success = true; - - max283x_mode_t prior_max283x_mode = max283x_mode(&max283x); - max283x_set_mode(&max283x, MAX283x_MODE_STANDBY); - if (freq_mhz < MAX_LP_FREQ_MHZ) { - rf_path_set_filter(&rf_path, RF_PATH_FILTER_LOW_PASS, opmode); - #ifdef RAD1O - max2837_freq_nominal_hz = 2300 * FREQ_ONE_MHZ; - #else - /* IF is graduated from 2650 MHz to 2340 MHz */ - max2837_freq_nominal_hz = (2650 * FREQ_ONE_MHZ) - (freq / 7); - #endif - mixer_freq_hz = max2837_freq_nominal_hz + freq; - /* Set Freq and read real freq */ - real_mixer_freq_hz = - mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ, true) / - FP_ONE_HZ; - max283x_set_frequency( - &max283x, - (real_mixer_freq_hz - freq) * FP_ONE_HZ, - true); - sgpio_cpld_set_mixer_invert(&sgpio_config, 1); - } else if ((freq_mhz >= MIN_BYPASS_FREQ_MHZ) && (freq_mhz < MAX_BYPASS_FREQ_MHZ)) { - rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS, opmode); - /* mixer_freq_mhz <= not used in Bypass mode */ - max283x_set_frequency(&max283x, freq * FP_ONE_HZ, true); - sgpio_cpld_set_mixer_invert(&sgpio_config, 0); - } else if ((freq_mhz >= MIN_HP_FREQ_MHZ) && (freq_mhz <= MAX_HP_FREQ_MHZ)) { - if (freq_mhz < MID1_HP_FREQ_MHZ) { + switch (img_reject) { + case RF_PATH_FILTER_LOW_PASS: + if (detected_platform() == BOARD_ID_RAD1O) { + freq_if = FP_MHZ(2300); + } else { + /* IF is graduated from 2650 MHz to 2340 MHz */ + freq_if = FP_MHZ(2650) - (freq_rf / 7); + } + break; + case RF_PATH_FILTER_HIGH_PASS: + if (freq_rf < (MID1_HP_FREQ)) { /* IF is graduated from 2170 MHz to 2740 MHz */ - max2837_freq_nominal_hz = (MIN_BYPASS_FREQ_MHZ * FREQ_ONE_MHZ) + - (((freq - (MAX_BYPASS_FREQ_MHZ * FREQ_ONE_MHZ)) * 57) / - 86); - } else if (freq_mhz < MID2_HP_FREQ_MHZ) { + freq_if = (MIN_BYPASS_FREQ) + + (((freq_rf - (MAX_BYPASS_FREQ)) * 57) / 86); + } else if (freq_rf < (MID2_HP_FREQ)) { /* IF is graduated from 2350 MHz to 2650 MHz */ - max2837_freq_nominal_hz = (2350 * FREQ_ONE_MHZ) + - ((freq - (MID1_HP_FREQ_MHZ * FREQ_ONE_MHZ)) / 5); + freq_if = FP_MHZ(2350) + ((freq_rf - (MID1_HP_FREQ)) / 5); } else { /* IF is graduated from 2500 MHz to 2738 MHz */ - max2837_freq_nominal_hz = (2500 * FREQ_ONE_MHZ) + - ((freq - (MID2_HP_FREQ_MHZ * FREQ_ONE_MHZ)) / 9); + freq_if = FP_MHZ(2500) + ((freq_rf - (MID2_HP_FREQ)) / 9); } - rf_path_set_filter(&rf_path, RF_PATH_FILTER_HIGH_PASS, opmode); - mixer_freq_hz = freq - max2837_freq_nominal_hz; - /* Set Freq and read real freq */ - real_mixer_freq_hz = - mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ, true) / - FP_ONE_HZ; - max283x_set_frequency( - &max283x, - (freq - real_mixer_freq_hz) * FP_ONE_HZ, - true); - sgpio_cpld_set_mixer_invert(&sgpio_config, 0); - } else { - /* Error freq_mhz too high */ - success = false; + break; + default: + freq_if = freq_rf; } - max283x_set_mode(&max283x, prior_max283x_mode); - if (success) { - if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { - hackrf_ui()->set_frequency(freq); - } - #ifdef HACKRF_ONE - operacake_set_range(freq_mhz); - #endif - } - return success; + return freq_if; } -#else - -bool tuning_set_frequency( - const tune_config_t* cfg, - const uint64_t freq, - const uint32_t offset, - const transceiver_mode_t opmode) +rf_path_filter_t select_img_reject(fp_40_24_t freq_rf) { - uint64_t mixer_freq_hz; - uint64_t real_mixer_freq_hz; - - if (freq > (MAX_HP_FREQ_MHZ * FREQ_ONE_MHZ)) { - return false; - } - - const uint16_t freq_mhz = freq / FREQ_ONE_MHZ; - - uint64_t rf = freq; - if (cfg->shift == FPGA_QUARTER_SHIFT_MODE_DOWN) { - if (offset > rf) { - rf = offset - rf; - } else { - rf = rf - offset; - } - } else if (cfg->shift == FPGA_QUARTER_SHIFT_MODE_UP) { - rf = rf + offset; - } - - max283x_mode_t prior_max283x_mode = max283x_mode(&max283x); - max283x_set_mode(&max283x, MAX283x_MODE_STANDBY); - - if (cfg->if_mhz == 0) { - rf_path_set_filter(&rf_path, RF_PATH_FILTER_BYPASS, opmode); - max283x_set_frequency(&max283x, rf * FP_ONE_HZ, true); - sgpio_cpld_set_mixer_invert(&sgpio_config, 0); - } else if (cfg->if_mhz > freq_mhz) { - rf_path_set_filter(&rf_path, RF_PATH_FILTER_LOW_PASS, opmode); - if (cfg->high_lo) { - mixer_freq_hz = FREQ_ONE_MHZ * cfg->if_mhz + rf; - real_mixer_freq_hz = mixer_set_frequency( - &mixer, - mixer_freq_hz * FP_ONE_HZ, - true) / - FP_ONE_HZ; - max283x_set_frequency( - &max283x, - (real_mixer_freq_hz - rf) * FP_ONE_HZ, - true); - sgpio_cpld_set_mixer_invert(&sgpio_config, 1); - } else { - mixer_freq_hz = FREQ_ONE_MHZ * cfg->if_mhz - rf; - real_mixer_freq_hz = mixer_set_frequency( - &mixer, - mixer_freq_hz * FP_ONE_HZ, - true) / - FP_ONE_HZ; - max283x_set_frequency( - &max283x, - (real_mixer_freq_hz + rf) * FP_ONE_HZ, - true); - sgpio_cpld_set_mixer_invert(&sgpio_config, 0); - } + if (freq_rf > (MAX_BYPASS_FREQ)) { + return RF_PATH_FILTER_HIGH_PASS; + } else if (freq_rf >= (MIN_BYPASS_FREQ)) { + return RF_PATH_FILTER_BYPASS; } else { - rf_path_set_filter(&rf_path, RF_PATH_FILTER_HIGH_PASS, opmode); - mixer_freq_hz = rf - FREQ_ONE_MHZ * cfg->if_mhz; - real_mixer_freq_hz = - mixer_set_frequency(&mixer, mixer_freq_hz * FP_ONE_HZ, true) / - FP_ONE_HZ; - max283x_set_frequency( - &max283x, - (rf - real_mixer_freq_hz) * FP_ONE_HZ, - true); - sgpio_cpld_set_mixer_invert(&sgpio_config, 0); + return RF_PATH_FILTER_LOW_PASS; } - - max283x_set_mode(&max283x, prior_max283x_mode); - if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { - hackrf_ui()->set_frequency(freq); - } - operacake_set_range(freq_mhz); - return true; } -#endif -bool set_freq_explicit( - const uint64_t if_freq_hz, - const uint64_t lo_freq_hz, - const rf_path_filter_t path, - const transceiver_mode_t opmode) +fp_40_24_t restrict_rf(fp_40_24_t freq_rf, rf_path_filter_t img_reject) { - if ((if_freq_hz < ((uint64_t) ABS_MIN_BYPASS_FREQ_MHZ * FREQ_ONE_MHZ)) || - (if_freq_hz > ((uint64_t) ABS_MAX_BYPASS_FREQ_MHZ * FREQ_ONE_MHZ))) { - return false; - } + fp_40_24_t min_rf = 0; + fp_40_24_t max_rf = MAX_HP_FREQ; - if ((path != RF_PATH_FILTER_BYPASS) && - ((lo_freq_hz < MIN_LO_FREQ_HZ) || (lo_freq_hz > MAX_LO_FREQ_HZ))) { - return false; + switch (img_reject) { + case RF_PATH_FILTER_LOW_PASS: + max_rf = MIN_BYPASS_FREQ; + break; + case RF_PATH_FILTER_HIGH_PASS: + min_rf = MAX_BYPASS_FREQ; + break; + default: + min_rf = MIN_BYPASS_FREQ; + max_rf = MAX_BYPASS_FREQ; } + freq_rf = MIN(freq_rf, max_rf); + freq_rf = MAX(freq_rf, min_rf); - if (path > 2) { - return false; - } - - rf_path_set_filter(&rf_path, path, opmode); - max283x_set_frequency(&max283x, if_freq_hz * FP_ONE_HZ, true); - - if (lo_freq_hz > if_freq_hz) { - sgpio_cpld_set_mixer_invert(&sgpio_config, 1); - } else { - sgpio_cpld_set_mixer_invert(&sgpio_config, 0); - } - if (path != RF_PATH_FILTER_BYPASS) { - (void) mixer_set_frequency(&mixer, lo_freq_hz * FP_ONE_HZ, true); - } - return true; + return freq_rf; } diff --git a/firmware/common/tuning.h b/firmware/common/tuning.h index 1c4681db..a338cd28 100644 --- a/firmware/common/tuning.h +++ b/firmware/common/tuning.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * Copyright 2013 Benjamin Vernoux * @@ -23,28 +23,20 @@ #pragma once -#include -#include - +#include "fixed_point.h" #include "rf_path.h" -#include "transceiver_mode.h" -#if defined(PRALINE) - #include "tune_config.h" -#endif -#define FREQ_ONE_MHZ (1000ULL * 1000) +/** + * Select fixed or graduated IF for a given RF and image reject filter. + */ +fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject); -bool set_freq(const uint64_t freq, const transceiver_mode_t opmode); -bool set_freq_explicit( - const uint64_t if_freq_hz, - const uint64_t lo_freq_hz, - const rf_path_filter_t path, - const transceiver_mode_t opmode); +/** + * Select image reject filter appropriate for a given RF. + */ +rf_path_filter_t select_img_reject(fp_40_24_t freq_rf); -#ifdef PRALINE -bool tuning_set_frequency( - const tune_config_t* cfg, - const uint64_t freq, - const uint32_t offset, - const transceiver_mode_t opmode); -#endif +/** + * Restrict RF to range appropriate for a given image reject filter. + */ +fp_40_24_t restrict_rf(fp_40_24_t freq_rf, rf_path_filter_t img_reject); From 5a82627a2b6735d4b5d6e7a202955937987b8c79 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Apr 2026 14:30:11 -0400 Subject: [PATCH 034/173] Clean up static applied_afe_rate --- firmware/common/radio.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 7f274c05..a551c0ec 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -160,11 +160,9 @@ static inline uint8_t compute_resample_log( #define MAX_MCU_RATE SR_FP_KHZ(21800) #define DEFAULT_MCU_RATE SR_FP_KHZ(10000) -static fp_28_36_t applied_afe_rate = RADIO_UNSET; - static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) { - fp_28_36_t rate, afe_rate; + fp_28_36_t rate, afe_rate, previous_rate, previous_afe_rate; uint64_t previous_n; uint8_t n = 0; bool new_afe_rate = false; @@ -222,16 +220,21 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) if (radio->sample_rate_cb) { afe_rate = rate << n; afe_rate = radio->sample_rate_cb(afe_rate, false); - new_afe_rate = (afe_rate != applied_afe_rate); + previous_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]; + if ((previous_n == RADIO_UNSET) || previous_rate == RADIO_UNSET) { + previous_afe_rate = RADIO_UNSET; + } else { + previous_afe_rate = previous_rate << previous_n; + } + new_afe_rate = (afe_rate != previous_afe_rate); if (new_afe_rate) { afe_rate = radio->sample_rate_cb(afe_rate, true); - applied_afe_rate = afe_rate; } } else { return false; } rate = afe_rate >> n; - new_rate = (rate != radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]); + new_rate = (rate != previous_rate); if (new_rate) { radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] = rate; if (rate != RADIO_UNSET) { From 3d616df7622e71161ac57ecaa69da22c9c269858 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Apr 2026 14:31:10 -0400 Subject: [PATCH 035/173] Store rotation in 32-bit format --- firmware/common/radio.c | 22 +++++++++++----------- firmware/common/radio.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index a551c0ec..0692274c 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -269,10 +269,10 @@ static fp_40_24_t compute_offset(radio_t* const radio, uint64_t opmode, uint64_t if ((rotation != 0) && (rotation != RADIO_UNSET) && (mcu_rate != RADIO_UNSET) && (n != RADIO_UNSET)) { const fp_40_24_t afe_rate = mcu_rate >> (12 - n); - if (rotation & 0x80) { - rotation = 0x100 - rotation; + if (rotation & 0x80000000) { + rotation = 0x100000000 - rotation; } - offset = (afe_rate * rotation) >> 8; + offset = (afe_rate * (rotation >> 24)) >> 8; } return offset; } @@ -289,13 +289,13 @@ static fp_40_24_t digital_from_analog_rf( { fp_40_24_t offset = compute_offset(radio, opmode, rotation); fp_40_24_t drf = arf; - if (rotation > 0x80) { + if (rotation > 0x80000000) { if (offset > arf) { drf = offset - arf; } else { drf = arf - offset; } - } else if ((rotation & 0x7f) > 0x00) { + } else if ((rotation & 0x7fffffff) > 0) { drf = arf + offset; } return drf; @@ -314,9 +314,9 @@ static fp_40_24_t analog_from_digital_rf( { fp_40_24_t offset = compute_offset(radio, opmode, rotation); fp_40_24_t arf = drf; - if (rotation > 0x80) { + if (rotation > 0x80000000) { arf = drf + offset; - } else if ((rotation & 0x7f) > 0x00) { + } else if ((rotation & 0x7fffffff) > 0) { if (offset > drf) { arf = offset - drf; } else { @@ -409,8 +409,8 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) case TRANSCEIVER_MODE_RX: case TRANSCEIVER_MODE_RX_SWEEP: /* Round to nearest supported rotation. */ - rotation = (rotation + 0x20) & 0xc0; - if (rotation == 0x80) { + rotation = (rotation + 0x20000000) & 0xc0000000; + if (rotation == 0x80000000) { rotation = 0; } break; @@ -434,7 +434,7 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) const tune_config_t* tune_config = select_tune_config(opmode, freq_rf); if (requested_rotation == RADIO_UNSET) { - rotation = (uint8_t) (tune_config->shift) << 6; + rotation = (uint64_t) (tune_config->shift) << 30; } analog_rf = analog_from_digital_rf(radio, freq_rf, opmode, rotation); @@ -511,7 +511,7 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) } if ((rotation != applied_rotation) && (rotation != RADIO_UNSET)) { #ifdef PRALINE - fpga_set_rx_quarter_shift_mode(&fpga, rotation >> 6); + fpga_set_rx_quarter_shift_mode(&fpga, rotation >> 30); #endif radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = rotation; new_freq = true; diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 2744dd4b..a0e40730 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -91,7 +91,7 @@ typedef enum { */ RADIO_IMAGE_REJECT = 4, /** - * Digital frequency conversion of type uint8_t in tau/256 steps with + * Digital frequency conversion of type uint8_t in tau/(2**32) steps with * respect to AFE clock. */ RADIO_ROTATION = 5, From 1e69ee09b72c7c3077f94694cb48d62b1311e178 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 14 Apr 2026 12:25:52 +0100 Subject: [PATCH 036/173] Remove some superfluous parentheses. --- firmware/common/tuning.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/firmware/common/tuning.c b/firmware/common/tuning.c index 7fe48bf0..f613ab30 100644 --- a/firmware/common/tuning.c +++ b/firmware/common/tuning.c @@ -55,11 +55,11 @@ fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject) } break; case RF_PATH_FILTER_HIGH_PASS: - if (freq_rf < (MID1_HP_FREQ)) { + if (freq_rf < MID1_HP_FREQ) { /* IF is graduated from 2170 MHz to 2740 MHz */ - freq_if = (MIN_BYPASS_FREQ) + + freq_if = MIN_BYPASS_FREQ + (((freq_rf - (MAX_BYPASS_FREQ)) * 57) / 86); - } else if (freq_rf < (MID2_HP_FREQ)) { + } else if (freq_rf < MID2_HP_FREQ) { /* IF is graduated from 2350 MHz to 2650 MHz */ freq_if = FP_MHZ(2350) + ((freq_rf - (MID1_HP_FREQ)) / 5); } else { @@ -75,9 +75,9 @@ fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject) rf_path_filter_t select_img_reject(fp_40_24_t freq_rf) { - if (freq_rf > (MAX_BYPASS_FREQ)) { + if (freq_rf > MAX_BYPASS_FREQ) { return RF_PATH_FILTER_HIGH_PASS; - } else if (freq_rf >= (MIN_BYPASS_FREQ)) { + } else if (freq_rf >= MIN_BYPASS_FREQ) { return RF_PATH_FILTER_BYPASS; } else { return RF_PATH_FILTER_LOW_PASS; From 59e8670c51f91a1779e730a23e447bc0dffcec6e Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Apr 2026 15:41:12 -0400 Subject: [PATCH 037/173] Ensure correct gains displayed in PortaPack UI Previously it was possible, though rare, for an update of an RX gain setting to be displayed during TX mode or for an update of a TX gain setting to be displayed during RX mode. --- firmware/common/ui_portapack.c | 111 +++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/firmware/common/ui_portapack.c b/firmware/common/ui_portapack.c index 4e862ca4..8912777f 100644 --- a/firmware/common/ui_portapack.c +++ b/firmware/common/ui_portapack.c @@ -495,6 +495,9 @@ static void portapack_radio_path_item_update( static rf_path_direction_t portapack_direction = RF_PATH_DIRECTION_OFF; static bool portapack_lna_on = false; +static uint32_t tx_if_gain = 0; +static uint32_t rx_if_gain = 0; +static uint32_t rx_bb_gain = 0; static void portapack_radio_path_redraw(void) { @@ -569,51 +572,6 @@ static void portapack_ui_set_sample_rate(uint32_t sample_rate) #endif } -static void portapack_ui_set_direction(const rf_path_direction_t direction) -{ - switch (direction) { - case RF_PATH_DIRECTION_TX: - portapack_radio_path_item_update( - RADIO_DRAW_LIST_ITEM_WAVES, - &bitmap_waves_tx); - portapack_radio_path_item_update( - RADIO_DRAW_LIST_ITEM_RF_AMP, - portapack_lna_on ? &bitmap_amp_tx : &bitmap_wire_24); - portapack_radio_path_item_update( - RADIO_DRAW_LIST_ITEM_BB_LNA_AMP, - &bitmap_amp_tx); - portapack_radio_path_item_update( - RADIO_DRAW_LIST_ITEM_BB_VGA_AMP, - &bitmap_wire_24); - portapack_ui_draw_string(RADIO_DRAW_LIST_ITEM_BB_VGA_AMP, " "); - break; - - case RF_PATH_DIRECTION_RX: - portapack_radio_path_item_update( - RADIO_DRAW_LIST_ITEM_WAVES, - &bitmap_waves_rx); - portapack_radio_path_item_update( - RADIO_DRAW_LIST_ITEM_RF_AMP, - portapack_lna_on ? &bitmap_amp_rx : &bitmap_wire_24); - portapack_radio_path_item_update( - RADIO_DRAW_LIST_ITEM_BB_LNA_AMP, - &bitmap_amp_rx); - portapack_radio_path_item_update( - RADIO_DRAW_LIST_ITEM_BB_VGA_AMP, - &bitmap_amp_rx); - break; - - case RF_PATH_DIRECTION_OFF: - default: - portapack_radio_path_item_update( - RADIO_DRAW_LIST_ITEM_WAVES, - &bitmap_blank_24); - break; - } - - portapack_direction = direction; -} - static void portapack_ui_set_filter_bw(uint32_t bandwidth) { portapack_ui_draw_bw_mhz(RADIO_DRAW_LIST_ITEM_BB_FILTER, bandwidth); @@ -634,12 +592,18 @@ static void portapack_ui_set_lna_power(bool lna_on) static void portapack_ui_set_bb_lna_gain(const uint32_t gain_db) { - portapack_ui_draw_db(RADIO_DRAW_LIST_ITEM_BB_LNA_AMP, gain_db); + rx_if_gain = gain_db; + if (portapack_direction == RF_PATH_DIRECTION_RX) { + portapack_ui_draw_db(RADIO_DRAW_LIST_ITEM_BB_LNA_AMP, gain_db); + } } static void portapack_ui_set_bb_vga_gain(const uint32_t gain_db) { - portapack_ui_draw_db(RADIO_DRAW_LIST_ITEM_BB_VGA_AMP, gain_db); + rx_bb_gain = gain_db; + if (portapack_direction == RF_PATH_DIRECTION_RX) { + portapack_ui_draw_db(RADIO_DRAW_LIST_ITEM_BB_VGA_AMP, gain_db); + } } static void portapack_ui_set_bb_tx_vga_gain(const uint32_t gain_db) @@ -648,7 +612,10 @@ static void portapack_ui_set_bb_tx_vga_gain(const uint32_t gain_db) * According to the MAX2837 datasheet diagram, there is no baseband gain in the TX path. * This gets called when the TX IF gain is changed. */ - portapack_ui_draw_db(RADIO_DRAW_LIST_ITEM_BB_LNA_AMP, gain_db); + tx_if_gain = gain_db; + if (portapack_direction == RF_PATH_DIRECTION_TX) { + portapack_ui_draw_db(RADIO_DRAW_LIST_ITEM_BB_LNA_AMP, gain_db); + } } static void portapack_ui_set_first_if_frequency(const uint64_t frequency) @@ -724,6 +691,54 @@ static bool portapack_ui_operacake_gpio_compatible(void) return false; } +static void portapack_ui_set_direction(const rf_path_direction_t direction) +{ + portapack_direction = direction; + + switch (direction) { + case RF_PATH_DIRECTION_TX: + portapack_radio_path_item_update( + RADIO_DRAW_LIST_ITEM_WAVES, + &bitmap_waves_tx); + portapack_radio_path_item_update( + RADIO_DRAW_LIST_ITEM_RF_AMP, + portapack_lna_on ? &bitmap_amp_tx : &bitmap_wire_24); + portapack_radio_path_item_update( + RADIO_DRAW_LIST_ITEM_BB_LNA_AMP, + &bitmap_amp_tx); + portapack_radio_path_item_update( + RADIO_DRAW_LIST_ITEM_BB_VGA_AMP, + &bitmap_wire_24); + portapack_ui_draw_string(RADIO_DRAW_LIST_ITEM_BB_VGA_AMP, " "); + portapack_ui_set_bb_tx_vga_gain(tx_if_gain); + break; + + case RF_PATH_DIRECTION_RX: + portapack_radio_path_item_update( + RADIO_DRAW_LIST_ITEM_WAVES, + &bitmap_waves_rx); + portapack_radio_path_item_update( + RADIO_DRAW_LIST_ITEM_RF_AMP, + portapack_lna_on ? &bitmap_amp_rx : &bitmap_wire_24); + portapack_radio_path_item_update( + RADIO_DRAW_LIST_ITEM_BB_LNA_AMP, + &bitmap_amp_rx); + portapack_radio_path_item_update( + RADIO_DRAW_LIST_ITEM_BB_VGA_AMP, + &bitmap_amp_rx); + portapack_ui_set_bb_vga_gain(rx_bb_gain); + portapack_ui_set_bb_lna_gain(rx_if_gain); + break; + + case RF_PATH_DIRECTION_OFF: + default: + portapack_radio_path_item_update( + RADIO_DRAW_LIST_ITEM_WAVES, + &bitmap_blank_24); + break; + } +} + const hackrf_ui_t portapack_hackrf_ui = { &portapack_ui_init, &portapack_ui_deinit, From 57285507d1c34e64b6e975bdd3a8a4f71d60d001 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Apr 2026 18:44:18 -0400 Subject: [PATCH 038/173] Add radio configuration update callback Co-authored-by: Martin Ling --- firmware/common/radio.c | 208 +++++++++------------- firmware/common/radio.h | 22 +++ firmware/common/rf_path.c | 17 +- firmware/common/rf_path.h | 6 +- firmware/hackrf_usb/hackrf_usb.c | 122 +++++++++++++ firmware/hackrf_usb/usb_api_transceiver.c | 8 - 6 files changed, 233 insertions(+), 150 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 0692274c..ac150ca1 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -27,21 +27,16 @@ #include "fixed_point.h" #include "hackrf_core.h" -#include "hackrf_ui.h" #include "mixer.h" #include "max283x.h" #include "platform_detect.h" #include "rf_path.h" -#include "sgpio.h" #include "transceiver_mode.h" #include "tuning.h" #if defined(PRALINE) #include "fpga.h" #include "tune_config.h" #endif -#if defined(HACKRF_ONE) || defined(PRALINE) - #include "operacake.h" -#endif #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) @@ -106,12 +101,12 @@ uint64_t radio_reg_read( return radio->config[bank][reg]; } -static bool radio_update_direction(radio_t* const radio, uint64_t* bank) +static uint32_t radio_update_direction(radio_t* const radio, uint64_t* bank) { const uint64_t requested = bank[RADIO_OPMODE]; if (requested == RADIO_UNSET) { - return false; + return 0; } rf_path_direction_t direction; @@ -129,7 +124,7 @@ static bool radio_update_direction(radio_t* const radio, uint64_t* bank) } radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE] = requested; rf_path_set_direction(&rf_path, direction); - return true; + return (1 << RADIO_OPMODE); } #define MAX_AFE_RATE_HZ (40000000UL) @@ -160,7 +155,7 @@ static inline uint8_t compute_resample_log( #define MAX_MCU_RATE SR_FP_KHZ(21800) #define DEFAULT_MCU_RATE SR_FP_KHZ(10000) -static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) +static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) { fp_28_36_t rate, afe_rate, previous_rate, previous_afe_rate; uint64_t previous_n; @@ -231,21 +226,15 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) afe_rate = radio->sample_rate_cb(afe_rate, true); } } else { - return false; + return 0; } rate = afe_rate >> n; new_rate = (rate != previous_rate); if (new_rate) { radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] = rate; - if (rate != RADIO_UNSET) { - /* Round to the nearest Hz for display. */ - const uint32_t rate_hz = - (rate + (SR_FP_ONE_HZ >> 1)) / SR_FP_ONE_HZ; - hackrf_ui()->set_sample_rate(rate_hz); - } } - return (new_afe_rate || new_rate || new_n); + return ((new_afe_rate || new_rate || new_n) << RADIO_SAMPLE_RATE); } /* @@ -352,11 +341,10 @@ static const tune_config_t* select_tune_config(uint64_t opmode, fp_40_24_t freq_ } #endif -static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) +static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) { - bool new_freq = false; + uint32_t changed = 0; bool high_lo = false; - bool invert_spectrum = false; const uint64_t requested_rf = bank[RADIO_FREQUENCY_RF]; const uint64_t requested_if = bank[RADIO_FREQUENCY_IF]; @@ -497,24 +485,24 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) if ((freq_if != applied_if) && (freq_if != RADIO_UNSET)) { freq_if = max283x_set_frequency(&max283x, freq_if, true); radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = freq_if; - new_freq = true; + changed |= (1 << RADIO_FREQUENCY_IF); } if ((freq_lo != applied_lo) && (freq_lo != RADIO_UNSET)) { freq_lo = mixer_set_frequency(&mixer, freq_lo, true); radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = freq_lo; - new_freq = true; + changed |= (1 << RADIO_FREQUENCY_LO); } if ((img_reject != applied_img_reject) && (img_reject != RADIO_UNSET)) { - rf_path_set_filter(&rf_path, img_reject, opmode); + rf_path_set_filter(&rf_path, img_reject); radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT] = img_reject; - new_freq = true; + changed |= (1 << RADIO_IMAGE_REJECT); } if ((rotation != applied_rotation) && (rotation != RADIO_UNSET)) { #ifdef PRALINE fpga_set_rx_quarter_shift_mode(&fpga, rotation >> 30); #endif radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = rotation; - new_freq = true; + changed |= (1 << RADIO_ROTATION); } /* Compute precise RF. */ @@ -525,7 +513,6 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) case RF_PATH_FILTER_LOW_PASS: if (freq_lo > freq_if) { analog_rf = freq_lo - freq_if; - invert_spectrum = true; } else { analog_rf = freq_if - freq_lo; } @@ -540,19 +527,10 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) } if ((freq_rf != applied_rf) && (freq_rf != RADIO_UNSET)) { radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] = freq_rf; - new_freq = true; + changed |= (1 << RADIO_FREQUENCY_RF); } - if (new_freq) { - sgpio_cpld_set_mixer_invert(&sgpio_config, invert_spectrum); - if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { - hackrf_ui()->set_frequency(freq_rf / FP_ONE_MHZ); - } -#if defined(PRALINE) || defined(HACKRF_ONE) - operacake_set_range(freq_rf / FP_ONE_MHZ); -#endif - } - return new_freq; + return changed; } static uint32_t auto_bandwidth(radio_t* const radio, uint64_t opmode) @@ -578,9 +556,9 @@ static uint32_t auto_bandwidth(radio_t* const radio, uint64_t opmode) return lpf_bandwidth; } -static bool radio_update_bandwidth(radio_t* const radio, uint64_t* bank) +static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank) { - bool new_bw = false; + uint32_t changed = 0; uint64_t opmode = bank[RADIO_OPMODE]; if (opmode == RADIO_UNSET) { @@ -595,12 +573,12 @@ static bool radio_update_bandwidth(radio_t* const radio, uint64_t* bank) if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_TX, lpf_bandwidth); radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth; - new_bw = true; + changed |= (1 << RADIO_XCVR_TX_LPF); } if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] != lpf_bandwidth) { max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_RX, lpf_bandwidth); radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth; - new_bw = true; + changed |= (1 << RADIO_XCVR_RX_LPF); } bool narrow_lpf_enable = false; bool applied_narrow_lpf_enable = @@ -612,14 +590,14 @@ static bool radio_update_bandwidth(radio_t* const radio, uint64_t* bank) narrowband_filter_set(narrow_lpf_enable); radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF] = narrow_lpf_enable; - new_bw = true; + changed |= (1 << RADIO_RX_NARROW_LPF); } /* Always set HPF bandwidth to 30 kHz for now. */ const max283x_rx_hpf_freq_t hpf_bandwidth = MAX283x_RX_HPF_30_KHZ; if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] != hpf_bandwidth) { max283x_set_rx_hpf_frequency(&max283x, hpf_bandwidth); radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] = hpf_bandwidth; - new_bw = true; + changed |= (1 << RADIO_XCVR_RX_HPF); } #else uint64_t lpf_bandwidth; @@ -646,20 +624,22 @@ static bool radio_update_bandwidth(radio_t* const radio, uint64_t* bank) radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_TX] = lpf_bandwidth; radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth; radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth; - new_bw = true; + changed |= + ((1 << RADIO_BB_BANDWIDTH_RX) | (1 << RADIO_BB_BANDWIDTH_TX) | + (1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF)); } #endif - return new_bw; + return changed; } #define DEFAULT_GAIN_RF (0) #define DEFAULT_GAIN_IF (16) #define DEFAULT_GAIN_BB (12) -static bool radio_update_gain(radio_t* const radio, uint64_t* bank) +static uint32_t radio_update_gain(radio_t* const radio, uint64_t* bank) { - bool new_gain = false; - uint64_t gain; + uint32_t changed = 0; + uint64_t gain, tx_rf_gain, rx_rf_gain; uint64_t opmode = bank[RADIO_OPMODE]; if (opmode == RADIO_UNSET) { opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; @@ -673,34 +653,34 @@ static bool radio_update_gain(radio_t* const radio, uint64_t* bank) switch (opmode) { case TRANSCEIVER_MODE_TX: case TRANSCEIVER_MODE_SS: - gain = bank[RADIO_GAIN_TX_RF]; - if (gain == RADIO_UNSET) { - gain = DEFAULT_GAIN_RF; + tx_rf_gain = bank[RADIO_GAIN_TX_RF]; + if (tx_rf_gain == RADIO_UNSET) { + tx_rf_gain = DEFAULT_GAIN_RF; } - rf_path_set_lna(&rf_path, gain); - if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] != gain) { - new_gain = true; - } - radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] = gain; - radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] = 0; + rf_path_set_lna(&rf_path, tx_rf_gain); + rx_rf_gain = 0; break; case TRANSCEIVER_MODE_RX: case TRANSCEIVER_MODE_RX_SWEEP: - gain = bank[RADIO_GAIN_RX_RF]; - if (gain == RADIO_UNSET) { - gain = DEFAULT_GAIN_RF; + rx_rf_gain = bank[RADIO_GAIN_RX_RF]; + if (rx_rf_gain == RADIO_UNSET) { + rx_rf_gain = DEFAULT_GAIN_RF; } - rf_path_set_lna(&rf_path, gain); - if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] != gain) { - new_gain = true; - } - radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] = gain; - radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] = 0; + rf_path_set_lna(&rf_path, rx_rf_gain); + tx_rf_gain = 0; break; default: rf_path_set_lna(&rf_path, 0); - radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] = 0; - radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] = 0; + tx_rf_gain = 0; + rx_rf_gain = 0; + } + if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] != tx_rf_gain) { + radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] = tx_rf_gain; + changed |= (1 << RADIO_GAIN_TX_RF); + } + if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] != rx_rf_gain) { + radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] = rx_rf_gain; + changed |= (1 << RADIO_GAIN_RX_RF); } gain = bank[RADIO_GAIN_TX_IF]; @@ -708,11 +688,11 @@ static bool radio_update_gain(radio_t* const radio, uint64_t* bank) (gain != radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF])) { max283x_set_txvga_gain(&max283x, gain); radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] = gain; - new_gain = true; + changed |= (1 << RADIO_GAIN_TX_IF); } else if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] == RADIO_UNSET) { max283x_set_txvga_gain(&max283x, DEFAULT_GAIN_IF); radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] = DEFAULT_GAIN_IF; - new_gain = true; + changed |= (1 << RADIO_GAIN_TX_IF); } gain = bank[RADIO_GAIN_RX_IF]; @@ -720,11 +700,11 @@ static bool radio_update_gain(radio_t* const radio, uint64_t* bank) (gain != radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF])) { max283x_set_lna_gain(&max283x, gain); radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] = gain; - new_gain = true; + changed |= (1 << RADIO_GAIN_RX_IF); } else if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] == RADIO_UNSET) { max283x_set_lna_gain(&max283x, DEFAULT_GAIN_IF); radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] = DEFAULT_GAIN_IF; - new_gain = true; + changed |= (1 << RADIO_GAIN_RX_IF); } gain = bank[RADIO_GAIN_RX_BB]; @@ -732,39 +712,39 @@ static bool radio_update_gain(radio_t* const radio, uint64_t* bank) (gain != radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_BB])) { max283x_set_vga_gain(&max283x, gain); radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_BB] = gain; - new_gain = true; + changed |= (1 << RADIO_GAIN_RX_BB); } else if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_BB] == RADIO_UNSET) { max283x_set_vga_gain(&max283x, DEFAULT_GAIN_BB); radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_BB] = DEFAULT_GAIN_BB; - new_gain = true; + changed |= (1 << RADIO_GAIN_RX_BB); } - return new_gain; + return changed; } -static bool radio_update_bias_tee(radio_t* const radio, uint64_t* bank) +static uint32_t radio_update_bias_tee(radio_t* const radio, uint64_t* bank) { if (detected_platform() == BOARD_ID_JAWBREAKER) { - return false; + return 0; } const uint64_t requested = bank[RADIO_BIAS_TEE]; const bool enable = requested; if (requested == RADIO_UNSET) { - return false; + return 0; } if (radio->config[RADIO_BANK_APPLIED][RADIO_BIAS_TEE] == (uint64_t) enable) { - return false; + return 0; } rf_path_set_antenna(&rf_path, enable); radio->config[RADIO_BANK_APPLIED][RADIO_BIAS_TEE] = enable; - return true; + return (1 << RADIO_BIAS_TEE); } -static bool radio_update_trigger(radio_t* const radio, uint64_t* bank) +static uint32_t radio_update_trigger(radio_t* const radio, uint64_t* bank) { const uint64_t requested = bank[RADIO_TRIGGER]; bool enable = requested; @@ -774,20 +754,20 @@ static bool radio_update_trigger(radio_t* const radio, uint64_t* bank) } if (radio->config[RADIO_BANK_APPLIED][RADIO_TRIGGER] == (uint64_t) enable) { - return false; + return 0; } trigger_enable(enable); radio->config[RADIO_BANK_APPLIED][RADIO_TRIGGER] = enable; - return true; + return (1 << RADIO_TRIGGER); } -static bool radio_update_dc_block(radio_t* const radio, uint64_t* bank) +static uint32_t radio_update_dc_block(radio_t* const radio, uint64_t* bank) { #ifndef PRALINE (void) radio; (void) bank; - return false; + return 0; #else const uint64_t requested = bank[RADIO_DC_BLOCK]; bool enable = requested; @@ -797,12 +777,12 @@ static bool radio_update_dc_block(radio_t* const radio, uint64_t* bank) } if (radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] == (uint64_t) enable) { - return false; + return 0; } fpga_set_rx_dc_block_enable(&fpga, enable); radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] = enable; - return true; + return (1 << RADIO_DC_BLOCK); #endif } @@ -811,6 +791,7 @@ bool radio_update(radio_t* const radio) uint64_t tmp_bank[RADIO_NUM_REGS]; nvic_disable_irq(NVIC_USB0_IRQ); uint32_t dirty = radio->regs_dirty; + uint32_t changed = 0; if (dirty == 0) { nvic_enable_irq(NVIC_USB0_IRQ); return false; @@ -819,56 +800,41 @@ bool radio_update(radio_t* const radio) memcpy(&tmp_bank[0], &(radio->config[RADIO_BANK_ACTIVE][0]), sizeof(tmp_bank)); nvic_enable_irq(NVIC_USB0_IRQ); - bool dir = false; - bool rate = false; - bool freq = false; - bool bw = false; - bool gain = false; - bool bias = false; - bool trig = false; - bool dc = false; - - if ((dirty & - ((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_RESAMPLE_TX) | - (1 << RADIO_RESAMPLE_RX))) || + if ((dirty & RADIO_REG_GROUP_RATE) || ((detected_platform() == BOARD_ID_PRALINE) && (dirty & (1 << RADIO_OPMODE)))) { - rate = radio_update_sample_rate(radio, &tmp_bank[0]); + changed |= radio_update_sample_rate(radio, &tmp_bank[0]); } - if ((dirty & - ((1 << RADIO_FREQUENCY_RF) | (1 << RADIO_FREQUENCY_IF) | - (1 << RADIO_FREQUENCY_LO) | (1 << RADIO_IMAGE_REJECT) | - (1 << RADIO_ROTATION))) || + if ((dirty & RADIO_REG_GROUP_FREQ) || ((detected_platform() == BOARD_ID_PRALINE) && - (rate || (dirty & (1 << RADIO_OPMODE))))) { - freq = radio_update_frequency(radio, &tmp_bank[0]); + ((changed & RADIO_REG_GROUP_RATE) || (dirty & (1 << RADIO_OPMODE))))) { + changed |= radio_update_frequency(radio, &tmp_bank[0]); } - if ((dirty & - ((1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_BB_BANDWIDTH_RX) | - (1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF) | - (1 << RADIO_XCVR_RX_HPF) | (1 << RADIO_RX_NARROW_LPF))) || - ((detected_platform() == BOARD_ID_PRALINE) && (rate || freq))) { - bw = radio_update_bandwidth(radio, &tmp_bank[0]); + if ((dirty & RADIO_REG_GROUP_BW) || + ((detected_platform() == BOARD_ID_PRALINE) && + (changed & (RADIO_REG_GROUP_RATE | RADIO_REG_GROUP_FREQ)))) { + changed |= radio_update_bandwidth(radio, &tmp_bank[0]); } - if (dirty & - ((1 << RADIO_GAIN_TX_RF) | (1 << RADIO_GAIN_TX_IF) | (1 << RADIO_GAIN_RX_RF) | - (1 << RADIO_GAIN_RX_IF) | (1 << RADIO_GAIN_RX_BB) | (1 << RADIO_OPMODE))) { - gain = radio_update_gain(radio, &tmp_bank[0]); + if (dirty & (RADIO_REG_GROUP_GAIN | (1 << RADIO_OPMODE))) { + changed |= radio_update_gain(radio, &tmp_bank[0]); } if (dirty & ((1 << RADIO_BIAS_TEE) | (1 << RADIO_OPMODE))) { - bias = radio_update_bias_tee(radio, &tmp_bank[0]); + changed |= radio_update_bias_tee(radio, &tmp_bank[0]); } if (dirty & (1 << RADIO_TRIGGER)) { - trig = radio_update_trigger(radio, &tmp_bank[0]); + changed |= radio_update_trigger(radio, &tmp_bank[0]); } if (dirty & (1 << RADIO_DC_BLOCK)) { - dc = radio_update_dc_block(radio, &tmp_bank[0]); + changed |= radio_update_dc_block(radio, &tmp_bank[0]); } if (dirty & (1 << RADIO_OPMODE)) { - dir = radio_update_direction(radio, &tmp_bank[0]); + changed |= radio_update_direction(radio, &tmp_bank[0]); } - return trig || dir || rate || freq || bw || gain || bias || dc; + if (radio->update_cb) { + radio->update_cb(changed); + } + return (changed != 0); } void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode) diff --git a/firmware/common/radio.h b/firmware/common/radio.h index a0e40730..83f72441 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -177,6 +177,20 @@ typedef enum { #define RADIO_NUM_REGS (23) #define RADIO_UNSET (0xffffffffffffffff) +/* register groups for bitfield convenience */ +#define RADIO_REG_GROUP_RATE \ + ((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_RESAMPLE_TX) | (1 << RADIO_RESAMPLE_RX)) +#define RADIO_REG_GROUP_FREQ \ + ((1 << RADIO_FREQUENCY_RF) | (1 << RADIO_FREQUENCY_IF) | \ + (1 << RADIO_FREQUENCY_LO) | (1 << RADIO_IMAGE_REJECT) | (1 << RADIO_ROTATION)) +#define RADIO_REG_GROUP_BW \ + ((1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_BB_BANDWIDTH_RX) | \ + (1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF) | \ + (1 << RADIO_XCVR_RX_HPF) | (1 << RADIO_RX_NARROW_LPF)) +#define RADIO_REG_GROUP_GAIN \ + ((1 << RADIO_GAIN_TX_RF) | (1 << RADIO_GAIN_TX_IF) | (1 << RADIO_GAIN_RX_RF) | \ + (1 << RADIO_GAIN_RX_IF) | (1 << RADIO_GAIN_RX_BB)) + /** * Register bank RADIO_BANK_ACTIVE stores the active configuration. Active * register settings are copied to the applied register when applied. @@ -207,11 +221,19 @@ typedef enum { */ typedef fp_28_36_t (*sample_rate_fn)(const fp_28_36_t sample_rate, const bool program); +/** + * An optional callback may be provided that is called after each time the + * radio configuration has been updated. The single argument is a bitfield + * indicating registers that have been changed. + */ +typedef void (*update_fn)(const uint32_t changed_regs); + typedef struct { radio_config_mode_t config_mode; uint64_t config[RADIO_NUM_BANKS][RADIO_NUM_REGS]; volatile uint32_t regs_dirty; sample_rate_fn sample_rate_cb; + update_fn update_cb; } radio_t; void radio_init(radio_t* const radio); diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index cf77f70c..9fdb20de 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -28,13 +28,11 @@ #endif #include "hackrf_core.h" -#include "hackrf_ui.h" #include "max283x.h" #include "max5864.h" #include "mixer.h" #include "platform_detect.h" #include "sgpio.h" -#include "transceiver_mode.h" #if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE) #include "platform_scu.h" #endif @@ -534,14 +532,9 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d } switchctrl_set(rf_path, rf_path->switchctrl); - - hackrf_ui()->set_direction(direction); } -void rf_path_set_filter( - rf_path_t* const rf_path, - const rf_path_filter_t filter, - const transceiver_mode_t opmode) +void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter) { switch (filter) { default: @@ -563,10 +556,6 @@ void rf_path_set_filter( } switchctrl_set(rf_path, rf_path->switchctrl); - - if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { - hackrf_ui()->set_filter(filter); - } } void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable) @@ -590,8 +579,6 @@ void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable) } switchctrl_set(rf_path, rf_path->switchctrl); - - hackrf_ui()->set_lna_power(enable); } /* antenna port power control */ @@ -604,6 +591,4 @@ void rf_path_set_antenna(rf_path_t* const rf_path, const uint_fast8_t enable) } switchctrl_set(rf_path, rf_path->switchctrl); - - hackrf_ui()->set_antenna_bias(enable); } diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h index d9f45a30..1ac0e243 100644 --- a/firmware/common/rf_path.h +++ b/firmware/common/rf_path.h @@ -25,7 +25,6 @@ #include -#include "transceiver_mode.h" #if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE) #include "gpio.h" #endif @@ -92,10 +91,7 @@ void rf_path_init(rf_path_t* const rf_path); void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t direction); -void rf_path_set_filter( - rf_path_t* const rf_path, - const rf_path_filter_t filter, - const transceiver_mode_t opmode); +void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter); void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable); void rf_path_set_antenna(rf_path_t* const rf_path, const uint_fast8_t enable); diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index ee8f897d..8a165e19 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -300,6 +302,125 @@ struct fpga_loader_t fpga_loader = { }; #endif +void radio_changed(const uint32_t changed) +{ + const uint64_t opmode = radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_OPMODE); + + if (changed & (1 << RADIO_OPMODE)) { + switch (opmode) { + case TRANSCEIVER_MODE_TX: + case TRANSCEIVER_MODE_SS: + hackrf_ui()->set_direction(RF_PATH_DIRECTION_TX); + break; + case TRANSCEIVER_MODE_RX: + case TRANSCEIVER_MODE_RX_SWEEP: + hackrf_ui()->set_direction(RF_PATH_DIRECTION_RX); + break; + default: + hackrf_ui()->set_direction(RF_PATH_DIRECTION_OFF); + break; + } + } + if (changed & (1 << RADIO_SAMPLE_RATE)) { + const uint64_t rate = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_SAMPLE_RATE); + hackrf_ui()->set_sample_rate(rate / SR_FP_ONE_HZ); + } + if (changed & (RADIO_REG_GROUP_FREQ)) { + const uint64_t img_reject = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_IMAGE_REJECT); + const uint64_t freq_lo = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_FREQUENCY_LO); + const uint64_t freq_if = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_FREQUENCY_IF); + const bool invert_spectrum = + ((img_reject == RF_PATH_FILTER_LOW_PASS) && (freq_lo > freq_if)); + sgpio_cpld_set_mixer_invert(&sgpio_config, invert_spectrum); + } + if (changed & (1 << RADIO_FREQUENCY_RF)) { + const uint64_t freq = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_FREQUENCY_RF); + if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { + hackrf_ui()->set_frequency(freq / FP_ONE_HZ); + } +#if defined(HACKRF_ONE) || defined(PRALINE) + operacake_set_range(freq / FP_ONE_MHZ); +#endif + } + if (changed & (1 << RADIO_IMAGE_REJECT)) { + if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { + const uint64_t img_reject = radio_reg_read( + &radio, + RADIO_BANK_APPLIED, + RADIO_IMAGE_REJECT); + hackrf_ui()->set_filter(img_reject); + } + } + if (changed & + ((1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_BB_BANDWIDTH_RX) | + (1 << RADIO_OPMODE))) { + uint64_t bw; + + switch (opmode) { + case TRANSCEIVER_MODE_TX: + case TRANSCEIVER_MODE_SS: + bw = radio_reg_read( + &radio, + RADIO_BANK_APPLIED, + RADIO_BB_BANDWIDTH_TX); + break; + default: + bw = radio_reg_read( + &radio, + RADIO_BANK_APPLIED, + RADIO_BB_BANDWIDTH_RX); + } + hackrf_ui()->set_filter_bw(bw); + } + if (changed & + ((1 << RADIO_GAIN_TX_RF) | (1 << RADIO_GAIN_RX_RF) | (1 << RADIO_OPMODE))) { + const uint64_t tx_rf = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_GAIN_TX_RF); + const uint64_t rx_rf = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_GAIN_RX_RF); + bool enable; + + switch (opmode) { + case TRANSCEIVER_MODE_TX: + case TRANSCEIVER_MODE_SS: + enable = tx_rf; + break; + case TRANSCEIVER_MODE_RX: + case TRANSCEIVER_MODE_RX_SWEEP: + enable = rx_rf; + break; + default: + enable = false; + } + hackrf_ui()->set_lna_power(enable); + } + if (changed & (1 << RADIO_GAIN_TX_IF)) { + const uint64_t tx_if = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_GAIN_TX_IF); + hackrf_ui()->set_bb_tx_vga_gain(tx_if); + } + if (changed & (1 << RADIO_GAIN_RX_IF)) { + const uint64_t rx_if = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_GAIN_RX_IF); + hackrf_ui()->set_bb_lna_gain(rx_if); + } + if (changed & (1 << RADIO_GAIN_RX_BB)) { + const uint64_t rx_bb = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_GAIN_RX_BB); + hackrf_ui()->set_bb_vga_gain(rx_bb); + } + if (changed & (1 << RADIO_BIAS_TEE)) { + const uint64_t enable = + radio_reg_read(&radio, RADIO_BANK_APPLIED, RADIO_BIAS_TEE); + hackrf_ui()->set_antenna_bias(enable); + } +} + int main(void) { // Copy M0 image from ROM before SPIFI is disabled @@ -369,6 +490,7 @@ int main(void) fpga_spi_selftest(); fpga_sgpio_selftest(); #endif + radio.update_cb = radio_changed; radio_init(&radio); #if (defined HACKRF_ONE || defined PRALINE) diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index d4b47f9f..12c516c9 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -83,7 +82,6 @@ usb_request_status_t usb_vendor_request_set_baseband_filter_bandwidth( RADIO_BANK_ACTIVE, RADIO_BB_BANDWIDTH_RX, bandwidth); - hackrf_ui()->set_filter_bw(bandwidth); usb_transfer_schedule_ack(endpoint->in); } return USB_REQUEST_STATUS_OK; @@ -231,7 +229,6 @@ usb_request_status_t usb_vendor_request_set_lna_gain( uint8_t gain = endpoint->setup.index; radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_GAIN_RX_IF, gain); endpoint->buffer[0] = RADIO_OK; - hackrf_ui()->set_bb_lna_gain(gain); usb_transfer_schedule_block( endpoint->in, &endpoint->buffer, @@ -251,7 +248,6 @@ usb_request_status_t usb_vendor_request_set_vga_gain( uint8_t gain = endpoint->setup.index; radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_GAIN_RX_BB, gain); endpoint->buffer[0] = RADIO_OK; - hackrf_ui()->set_bb_vga_gain(gain); usb_transfer_schedule_block( endpoint->in, &endpoint->buffer, @@ -271,7 +267,6 @@ usb_request_status_t usb_vendor_request_set_txvga_gain( uint8_t gain = endpoint->setup.index; radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_GAIN_TX_IF, gain); endpoint->buffer[0] = RADIO_OK; - hackrf_ui()->set_bb_tx_vga_gain(gain); usb_transfer_schedule_block( endpoint->in, &endpoint->buffer, @@ -333,7 +328,6 @@ void transceiver_shutdown(void) void transceiver_startup(const transceiver_mode_t mode) { radio_switch_opmode(&radio, mode); - hackrf_ui()->set_transceiver_mode(mode); switch (mode) { case TRANSCEIVER_MODE_RX_SWEEP: @@ -486,8 +480,6 @@ void tx_mode(uint32_t seq) void off_mode(uint32_t seq) { - hackrf_ui()->set_transceiver_mode(TRANSCEIVER_MODE_OFF); - while (transceiver_request.seq == seq) { radio_update(&radio); } From 43454dd512c5582cce2629341a3259c0291642dc Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 12 Mar 2026 02:02:59 +0000 Subject: [PATCH 039/173] Move sgpio_configure calls to radio_changed callback. --- firmware/common/rf_path.c | 5 ----- firmware/hackrf_usb/hackrf_usb.c | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index 9fdb20de..4407960a 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -32,7 +32,6 @@ #include "max5864.h" #include "mixer.h" #include "platform_detect.h" -#include "sgpio.h" #if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE) #include "platform_scu.h" #endif @@ -479,7 +478,6 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d max5864_tx(&max5864); ssp1_set_mode_max283x(); max283x_tx(&max283x); - sgpio_configure(&sgpio_config, SGPIO_DIRECTION_TX); break; case RF_PATH_DIRECTION_RX: @@ -497,7 +495,6 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d max5864_rx(&max5864); ssp1_set_mode_max283x(); max283x_rx(&max283x); - sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX); break; #ifdef PRALINE @@ -513,7 +510,6 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d } else { max283x_rx_calibration(&max283x); } - sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX); break; #endif @@ -527,7 +523,6 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d max5864_standby(&max5864); ssp1_set_mode_max283x(); max283x_set_mode(&max283x, MAX283x_MODE_STANDBY); - sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX); break; } diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 8a165e19..c365e9bc 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -310,13 +310,16 @@ void radio_changed(const uint32_t changed) switch (opmode) { case TRANSCEIVER_MODE_TX: case TRANSCEIVER_MODE_SS: + sgpio_configure(&sgpio_config, SGPIO_DIRECTION_TX); hackrf_ui()->set_direction(RF_PATH_DIRECTION_TX); break; case TRANSCEIVER_MODE_RX: case TRANSCEIVER_MODE_RX_SWEEP: + sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX); hackrf_ui()->set_direction(RF_PATH_DIRECTION_RX); break; default: + sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX); hackrf_ui()->set_direction(RF_PATH_DIRECTION_OFF); break; } From 1b37541237139fa8f99a9583ee126e232942cb1d Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 15 Apr 2026 19:02:42 +0100 Subject: [PATCH 040/173] Correct GPIO pin assignments for CPLD JTAG on Jawbreaker. --- firmware/common/hackrf_core.c | 2 +- firmware/common/platform_gpio.c | 9 +++++++-- firmware/common/platform_gpio.h | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 0534d480..1535e1e8 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -947,7 +947,7 @@ void pin_setup(void) #endif jtag_gpio_cpld.gpio_tck = gpio->cpld_tck; -#if defined(HACKRF_ONE) || defined(RAD1O) +#if defined(HACKRF_ONE) || defined(RAD1O) || defined(JAWBREAKER) jtag_gpio_cpld.gpio_tms = gpio->cpld_tms; jtag_gpio_cpld.gpio_tdi = gpio->cpld_tdi; jtag_gpio_cpld.gpio_tdo = gpio->cpld_tdo; diff --git a/firmware/common/platform_gpio.c b/firmware/common/platform_gpio.c index df07fc2a..3e0db2c6 100644 --- a/firmware/common/platform_gpio.c +++ b/firmware/common/platform_gpio.c @@ -131,17 +131,22 @@ const platform_gpio_t* platform_gpio(void) #endif /* CPLD JTAG interface GPIO pins_FPGA config pins in Praline */ - gpio.cpld_tck = &GPIO3_0; + gpio.cpld_tck = &GPIO3_0; #if defined(PRALINE) gpio.fpga_cfg_creset = &GPIO2_11; gpio.fpga_cfg_cdone = &GPIO5_14; gpio.fpga_cfg_spi_cs = &GPIO2_10; +#else + gpio.cpld_tdo = &GPIO5_18; #endif #if defined(HACKRF_ONE) || defined(RAD1O) - gpio.cpld_tdo = &GPIO5_18; gpio.cpld_tms = &GPIO3_4; gpio.cpld_tdi = &GPIO3_1; #endif +#if defined(JAWBREAKER) + gpio.cpld_tms = &GPIO3_1; + gpio.cpld_tdi = &GPIO3_4; +#endif #if defined(HACKRF_ONE) || defined(PRALINE) gpio.cpld_pp_tms = &GPIO1_1; gpio.cpld_pp_tdo = &GPIO1_8; diff --git a/firmware/common/platform_gpio.h b/firmware/common/platform_gpio.h index 14f0c067..9c32925c 100644 --- a/firmware/common/platform_gpio.h +++ b/firmware/common/platform_gpio.h @@ -109,7 +109,7 @@ typedef struct { /* CPLD JTAG interface GPIO pins, FPGA config pins in Praline */ gpio_t cpld_tck; -#if defined(HACKRF_ONE) || defined(RAD1O) +#if defined(HACKRF_ONE) || defined(RAD1O) || defined(JAWBREAKER) gpio_t cpld_tdo; gpio_t cpld_tms; gpio_t cpld_tdi; From 94d1517153ad677e9f349a655ce3f8d9bd1e4025 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Wed, 15 Apr 2026 14:47:08 -0400 Subject: [PATCH 041/173] Correct SCU config for CPLD JTAG on Jawbreaker --- firmware/common/platform_scu.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/firmware/common/platform_scu.c b/firmware/common/platform_scu.c index df611bd2..a4365635 100644 --- a/firmware/common/platform_scu.c +++ b/firmware/common/platform_scu.c @@ -85,11 +85,14 @@ const platform_scu_t* platform_scu(void) scu.PINMUX_FPGA_CRESET = (P5_2); /* GPIO2[11] on P5_2 */ scu.PINMUX_FPGA_CDONE = (P4_10); /* GPIO5[14] */ scu.PINMUX_FPGA_SPI_CS = (P5_1); /* GPIO2[10] */ -#elif defined(RAD1O) || defined(HACKRF_ONE) +#else + scu.PINMUX_CPLD_TDO = (P9_5); /* GPIO5[18] */ +#endif +#if defined(RAD1O) || defined(HACKRF_ONE) scu.PINMUX_CPLD_TMS = (P6_5); /* GPIO3[ 4] */ scu.PINMUX_CPLD_TDI = (P6_2); /* GPIO3[ 1] */ - scu.PINMUX_CPLD_TDO = (P9_5); /* GPIO5[18] */ -#else +#endif +#if defined(JAWBREAKER) scu.PINMUX_CPLD_TMS = (P6_2); /* GPIO3[ 1] */ scu.PINMUX_CPLD_TDI = (P6_5); /* GPIO3[ 4] */ #endif From a36f5ca08ae97c021b42a3b2ab3da61179b944fc Mon Sep 17 00:00:00 2001 From: grvvy Date: Thu, 16 Apr 2026 16:30:16 -0600 Subject: [PATCH 042/173] ci: increase wait time after running spiflash, add support for building firmware for different boards --- Jenkinsfile | 155 +++++++++--------- ...ild_hpro_firmware.sh => build_firmware.sh} | 2 +- ci-scripts/build_h1_firmware.sh | 9 - ci-scripts/hackrf_pro_test.py | 4 +- 4 files changed, 82 insertions(+), 88 deletions(-) rename ci-scripts/{build_hpro_firmware.sh => build_firmware.sh} (80%) delete mode 100755 ci-scripts/build_h1_firmware.sh diff --git a/Jenkinsfile b/Jenkinsfile index 2b03c4de..bf79d51e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,100 +1,101 @@ import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException +def docker_args = '''--group-add=20 --group-add=46 --device-cgroup-rule="c 189:* rmw" \ + --device-cgroup-rule="c 166:* rmw" -v /dev/bus/usb:/dev/bus/usb \ + -v /tmp/req_pipe:/tmp/req_pipe -v /tmp/res_pipe:/tmp/res_pipe''' + +def h1_test = '''python3 ci-scripts/hackrf_test.py --ci --log log \ + --hostdir host/build/hackrf-tools/src/ \ + --fwupdate firmware/hackrf_usb/build/ \ + --tester 0000000000000000325866e629a25623 \ + --eut RunningFromRAM --unattended --rev r4''' + +def hpro_test = '''python3 ci-scripts/hackrf_pro_test.py --ci --log log \ + --hostdir host/build/hackrf-tools/src \ + --fwupdate firmware/hackrf_usb/build \ + --tester 0000000000000000a06063c82338145f \ + --eut RunningFromRAM -p --rev r1.2''' + pipeline { agent any stages { stage('Build Docker Image') { + options { + timeout(time: 20, unit: 'MINUTES') + } steps { sh 'docker build -t hackrf https://github.com/greatscottgadgets/hackrf.git' } } - stage('Test HackRF One') { + stage('Test HackRF One with BOARD=HACKRF_ONE') { agent { docker { image 'hackrf' reuseNode true - args '''--group-add=20 --group-add=46 --device-cgroup-rule="c 189:* rmw" \ - --device-cgroup-rule="c 166:* rmw" -v /dev/bus/usb:/dev/bus/usb \ - -v /tmp/req_pipe:/tmp/req_pipe -v /tmp/res_pipe:/tmp/res_pipe''' + args docker_args } } + options { + timeout(time: 20, unit: 'MINUTES') + } steps { sh './ci-scripts/install_host.sh' - sh './ci-scripts/build_h1_firmware.sh' + sh './ci-scripts/build_firmware.sh HACKRF_ONE' script { - allOff(); + allOff() + reset('h1_eut') } + sh 'sleep 1s' retry(3) { - script { - reset('h1_eut') - } - sh 'sleep 1s' sh './ci-scripts/test_host.sh' } - retry(3) { - script { - reset('h1_tester h1_eut') - } - sh 'sleep 1s' - script { - // Allow 5 minutes for the test to run - runCommand(5, 'MINUTES', "HackRF One Test", - '''python3 ci-scripts/hackrf_test.py --ci --log log \ - --hostdir host/build/hackrf-tools/src/ \ - --fwupdate firmware/hackrf_usb/build/ \ - --tester 0000000000000000325866e629a25623 \ - --eut RunningFromRAM --unattended --rev r4''' - ) - allOff() - } + script { + reset('h1_tester h1_eut') } + sh 'sleep 1s' + script { + // Allow up to 3 retries 5 minutes each for the HIL test + runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test) + } + script { + allOff() + reset('h1_eut') + } + sh 'sleep 1s' retry(3) { - script { - reset('h1_eut') - } - sh 'sleep 1s' sh 'python3 ci-scripts/test_sgpio_debug.py' } } } - stage('Test HackRF Pro') { + stage('Test HackRF Pro with BOARD=PRALINE') { agent { docker { image 'hackrf' reuseNode true - args '''--group-add=20 --group-add=46 --device-cgroup-rule="c 189:* rmw" \ - --device-cgroup-rule="c 166:* rmw" -v /dev/bus/usb:/dev/bus/usb \ - -v /tmp/req_pipe:/tmp/req_pipe -v /tmp/res_pipe:/tmp/res_pipe''' + args "$docker_args" } } + options { + timeout(time: 20, unit: 'MINUTES') + } steps { sh './ci-scripts/install_host.sh' - sh './ci-scripts/build_hpro_firmware.sh' + sh './ci-scripts/build_firmware.sh PRALINE' script { - allOff(); + allOff() + reset('hpro_eut') } + sh 'sleep 1s' retry(3) { - script { - reset('hpro_eut') - } - sh 'sleep 1s' sh './ci-scripts/test_host.sh' } - retry(3) { - script { - reset('hpro_tester hpro_eut') - } - sh 'sleep 1s' - script { - // Allow 5 minutes for the test to run - runCommand(5, 'MINUTES', "HackRF Pro Test", - '''python3 ci-scripts/hackrf_pro_test.py --ci --log log \ - --hostdir host/build/hackrf-tools/src \ - --fwupdate firmware/hackrf_usb/build \ - --tester 0000000000000000a06063c82338145f \ - --eut RunningFromRAM -p --rev r1.2''' - ) - } + script { + reset('hpro_tester hpro_eut') + } + sh 'sleep 1s' + script { + // Allow up to 3 retries 5 minutes each for the HIL test + runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test) } } } @@ -110,32 +111,34 @@ pipeline { } def allOff() { - // Allow 20 seconds for the USB hub port power server to respond - runCommand(20, 'SECONDS', 'USB hub port power server command', "hubs all off") + // Allow up to 3 retries, 20 seconds each, for the USB hub port power server to respond appropriately + runCommand(3, 20, 'SECONDS', 'USB hub port power server command', "hubs all off") } def reset(devices) { - // Allow 20 seconds for the USB hub port power server to respond - runCommand(20, 'SECONDS', 'USB hub port power server command', "hubs ${devices} reset") + // Allow up to 3 retries, 20 seconds each, for the USB hub port power server to respond appropriately + runCommand(3, 20, 'SECONDS', 'USB hub port power server command', "hubs ${devices} reset") } -def runCommand(time, unit, title, cmd) { - try { - timeout(time: time, unit: unit) { - sh "${cmd}" +def runCommand(retries, time, unit, title, cmd) { + retry(retries) { + try { + timeout(time: time, unit: unit) { + sh "${cmd}" + } + } catch (FlowInterruptedException err) { + // Check if the cause was specifically an exceeded timeout + def cause = err.getCauses().get(0) + if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) { + echo "${title} timeout reached." + throw err // Re-throw the exception to fail the build + } else { + echo "Build interrupted for another reason." + throw err // Re-throw the exception to fail the build + } + } catch (Exception err) { + echo "An unrelated error occurred: ${err.getMessage()}" + throw err } - } catch (FlowInterruptedException err) { - // Check if the cause was specifically an exceeded timeout - def cause = err.getCauses().get(0) - if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) { - echo "${title} timeout reached." - throw err // Re-throw the exception to fail the build - } else { - echo "Build interrupted for another reason." - throw err // Re-throw the exception to fail the build - } - } catch (Exception err) { - echo "An unrelated error occurred: ${err.getMessage()}" - throw err } } diff --git a/ci-scripts/build_hpro_firmware.sh b/ci-scripts/build_firmware.sh similarity index 80% rename from ci-scripts/build_hpro_firmware.sh rename to ci-scripts/build_firmware.sh index 830d4bf7..b6fc4ec4 100755 --- a/ci-scripts/build_hpro_firmware.sh +++ b/ci-scripts/build_firmware.sh @@ -4,6 +4,6 @@ git submodule init git submodule update cd firmware/hackrf_usb rm -rf build -cmake -DBOARD=PRALINE -B build +cmake -DBOARD=$1 -B build cmake --build build cd ../.. diff --git a/ci-scripts/build_h1_firmware.sh b/ci-scripts/build_h1_firmware.sh deleted file mode 100755 index 762d64de..00000000 --- a/ci-scripts/build_h1_firmware.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e -git submodule init -git submodule update -cd firmware/hackrf_usb -rm -rf build -cmake -DBOARD=HACKRF_ONE -B build -cmake --build build -cd ../.. diff --git a/ci-scripts/hackrf_pro_test.py b/ci-scripts/hackrf_pro_test.py index 1e9ac9f4..3d0b1c63 100644 --- a/ci-scripts/hackrf_pro_test.py +++ b/ci-scripts/hackrf_pro_test.py @@ -1413,11 +1413,11 @@ def program(bin_dir, fw_dir, serial, unattended=False): if spiflash.returncode != 0: log(spiflash.stdout + spiflash.stderr) fail(70) + time.sleep(3) then = time.time() device_found = False while time.time() < (then + 5): - time.sleep(1) flash_info = subprocess.run([bin_dir + "/hackrf_info"], capture_output=True, timeout=TIMEOUT) if serial == "RunningFromRAM" and not unattended: @@ -1433,7 +1433,7 @@ def program(bin_dir, fw_dir, serial, unattended=False): if serial in flash_info.stdout.decode('utf-8', errors='ignore'): device_found = True break - time.sleep(0.1) + time.sleep(1) if not device_found: fail(75) From 5120383da39f6d8f0a7bdaf67900fbea5b0076dc Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 3 Apr 2026 20:48:53 +0100 Subject: [PATCH 043/173] Universalize: platform_gpio.{c,h} Co-authored-by: Antoine van Gelder --- firmware/common/platform_gpio.c | 381 ++++++++++++++++++++------------ firmware/common/platform_gpio.h | 29 ++- 2 files changed, 255 insertions(+), 155 deletions(-) diff --git a/firmware/common/platform_gpio.c b/firmware/common/platform_gpio.c index 3e0db2c6..f9a44903 100644 --- a/firmware/common/platform_gpio.c +++ b/firmware/common/platform_gpio.c @@ -23,9 +23,7 @@ #include -#if defined(HACKRF_ONE) - #include "platform_detect.h" -#endif +#include "platform_detect.h" // clang-format off @@ -36,180 +34,287 @@ const platform_gpio_t* platform_gpio(void) return _platform_gpio; } + board_id_t board_id = detected_platform(); static platform_gpio_t gpio; /* LEDs */ gpio.led[0] = &GPIO2_1; gpio.led[1] = &GPIO2_2; gpio.led[2] = &GPIO2_8; + switch (board_id) { + case BOARD_ID_RAD1O: #if defined(RAD1O) - gpio.led[3] = &GPIO5_26; -#elif defined(PRALINE) - gpio.led[3] = &GPIO4_6; + gpio.led[3] = &GPIO5_26; #endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + gpio.led[3] = &GPIO4_6; +#endif + break; + default: + break; + } /* Power Supply Control */ -#if defined(PRALINE) - gpio.gpio_1v2_enable = &GPIO4_7; - gpio.gpio_3v3aux_enable_n = &GPIO5_15; -#else - gpio.gpio_1v8_enable = &GPIO3_6; + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + gpio.gpio_1v2_enable = &GPIO4_7; + gpio.gpio_3v3aux_enable_n = &GPIO5_15; #endif + break; + default: + gpio.gpio_1v8_enable = &GPIO3_6; + break; + } /* MAX283x GPIO (XCVR_CTL / CS_XCVR) PinMux */ -#if defined(PRALINE) - gpio.max283x_select = &GPIO6_28; - gpio.max283x_enable = &GPIO7_1; - gpio.max283x_rx_enable = &GPIO7_2; - gpio.max2831_rxhp = &GPIO6_29; - gpio.max2831_ld = &GPIO4_11; -#else - gpio.max283x_select = &GPIO0_15; - gpio.max283x_enable = &GPIO2_6; - gpio.max283x_rx_enable = &GPIO2_5; - gpio.max283x_tx_enable = &GPIO2_4; + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + gpio.max283x_select = &GPIO6_28; + gpio.max283x_enable = &GPIO7_1; + gpio.max283x_rx_enable = &GPIO7_2; + gpio.max2831_rxhp = &GPIO6_29; + gpio.max2831_ld = &GPIO4_11; #endif + break; + default: + gpio.max283x_select = &GPIO0_15; + gpio.max283x_enable = &GPIO2_6; + gpio.max283x_rx_enable = &GPIO2_5; + gpio.max283x_tx_enable = &GPIO2_4; + break; + } /* MAX5864 SPI chip select (AD_CS / CS_AD) GPIO PinMux */ -#if defined(PRALINE) - gpio.max5864_select = &GPIO6_30; -#else - gpio.max5864_select = &GPIO2_7; + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + gpio.max5864_select = &GPIO6_30; #endif + break; + default: + gpio.max5864_select = &GPIO2_7; + break; + } /* RF supply (VAA) control */ -#if defined(HACKRF_ONE) - gpio.vaa_disable = &GPIO2_9; -#elif defined(PRALINE) - gpio.vaa_disable = &GPIO4_1; -#elif defined(RAD1O) - gpio.vaa_enable = &GPIO2_9; + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + gpio.vaa_disable = &GPIO2_9; #endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + gpio.vaa_disable = &GPIO4_1; +#endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + gpio.vaa_enable = &GPIO2_9; +#endif + break; + default: + break; + } /* W25Q80BV Flash */ - gpio.w25q80bv_hold = &GPIO1_14; - gpio.w25q80bv_wp = &GPIO1_15; - gpio.w25q80bv_select = &GPIO5_11; + gpio.w25q80bv_hold = &GPIO1_14; + gpio.w25q80bv_wp = &GPIO1_15; + gpio.w25q80bv_select = &GPIO5_11; /* RF switch control */ -#if defined(HACKRF_ONE) - gpio.hp = &GPIO2_0; - gpio.lp = &GPIO2_10; - gpio.tx_mix_bp = &GPIO2_11; - gpio.no_mix_bypass = &GPIO1_0; - gpio.rx_mix_bp = &GPIO2_12; - gpio.tx_amp = &GPIO2_15; - gpio.tx = &GPIO5_15; - gpio.mix_bypass = &GPIO5_16; - gpio.rx = &GPIO5_5; - gpio.no_tx_amp_pwr = &GPIO3_5; - gpio.amp_bypass = &GPIO0_14; - gpio.rx_amp = &GPIO1_11; - gpio.no_rx_amp_pwr = &GPIO1_12; - // HackRF One rev 9 - gpio.h1r9_rx = &GPIO0_7; - gpio.h1r9_no_ant_pwr = &GPIO2_4; -#elif defined(RAD1O) - gpio.tx_rx_n = &GPIO1_11; - gpio.tx_rx = &GPIO0_14; - gpio.by_mix = &GPIO1_12; - gpio.by_mix_n = &GPIO2_10; - gpio.by_amp = &GPIO1_0; - gpio.by_amp_n = &GPIO5_5; - gpio.mixer_en = &GPIO5_16; - gpio.low_high_filt = &GPIO2_11; - gpio.low_high_filt_n = &GPIO2_12; - gpio.tx_amp = &GPIO2_15; - gpio.rx_lna = &GPIO5_15; -#elif defined(PRALINE) - gpio.tx_en = &GPIO3_4; - gpio.mix_en_n = &GPIO3_2; - gpio.mix_en_n_r1_0 = &GPIO5_6; - gpio.lpf_en = &GPIO4_8; - gpio.rf_amp_en = &GPIO4_9; - gpio.ant_bias_en_n = &GPIO1_12; + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + gpio.hp = &GPIO2_0; + gpio.lp = &GPIO2_10; + gpio.tx_mix_bp = &GPIO2_11; + gpio.no_mix_bypass = &GPIO1_0; + gpio.rx_mix_bp = &GPIO2_12; + gpio.tx_amp = &GPIO2_15; + gpio.tx = &GPIO5_15; + gpio.mix_bypass = &GPIO5_16; + gpio.rx = &GPIO5_5; + gpio.no_tx_amp_pwr = &GPIO3_5; + gpio.amp_bypass = &GPIO0_14; + gpio.rx_amp = &GPIO1_11; + gpio.no_rx_amp_pwr = &GPIO1_12; + // HackRF One rev 9 + gpio.h1r9_rx = &GPIO0_7; + gpio.h1r9_no_ant_pwr = &GPIO2_4; #endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + gpio.tx_rx_n = &GPIO1_11; + gpio.tx_rx = &GPIO0_14; + gpio.by_mix = &GPIO1_12; + gpio.by_mix_n = &GPIO2_10; + gpio.by_amp = &GPIO1_0; + gpio.by_amp_n = &GPIO5_5; + gpio.mixer_en = &GPIO5_16; + gpio.low_high_filt = &GPIO2_11; + gpio.low_high_filt_n = &GPIO2_12; + gpio.tx_amp = &GPIO2_15; + gpio.rx_lna = &GPIO5_15; +#endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + gpio.tx_en = &GPIO3_4; + gpio.mix_en_n = &GPIO3_2; + gpio.mix_en_n_r1_0 = &GPIO5_6; + gpio.lpf_en = &GPIO4_8; + gpio.rf_amp_en = &GPIO4_9; + gpio.ant_bias_en_n = &GPIO1_12; +#endif + break; + case BOARD_ID_JELLYBEAN: + case BOARD_ID_JAWBREAKER: + break; + default: + break; + } /* CPLD JTAG interface GPIO pins_FPGA config pins in Praline */ - gpio.cpld_tck = &GPIO3_0; -#if defined(PRALINE) - gpio.fpga_cfg_creset = &GPIO2_11; - gpio.fpga_cfg_cdone = &GPIO5_14; - gpio.fpga_cfg_spi_cs = &GPIO2_10; -#else - gpio.cpld_tdo = &GPIO5_18; + gpio.cpld_tck = &GPIO3_0; + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + gpio.fpga_cfg_creset = &GPIO2_11; + gpio.fpga_cfg_cdone = &GPIO5_14; + gpio.fpga_cfg_spi_cs = &GPIO2_10; #endif -#if defined(HACKRF_ONE) || defined(RAD1O) - gpio.cpld_tms = &GPIO3_4; - gpio.cpld_tdi = &GPIO3_1; + break; + default: +#if !defined(PRALINE) || defined(UNIVERSAL) + gpio.cpld_tdo = &GPIO5_18; + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_RAD1O: + gpio.cpld_tms = &GPIO3_4; + gpio.cpld_tdi = &GPIO3_1; + break; + case BOARD_ID_JAWBREAKER: + gpio.cpld_tms = &GPIO3_1; + gpio.cpld_tdi = &GPIO3_4; + break; + default: + break; + } #endif -#if defined(JAWBREAKER) - gpio.cpld_tms = &GPIO3_1; - gpio.cpld_tdi = &GPIO3_4; -#endif -#if defined(HACKRF_ONE) || defined(PRALINE) - gpio.cpld_pp_tms = &GPIO1_1; - gpio.cpld_pp_tdo = &GPIO1_8; + break; + } + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + gpio.cpld_pp_tms = &GPIO1_1; + gpio.cpld_pp_tdo = &GPIO1_8; #endif + break; + default: + break; + } /* Other CPLD interface GPIO pins */ -#if !defined(PRALINE) - gpio.trigger_enable = &GPIO5_12; + switch (board_id) { + case BOARD_ID_PRALINE: + break; + default: +#if !defined(PRALINE) || defined(UNIVERSAL) + gpio.trigger_enable = &GPIO5_12; #endif - gpio.q_invert = &GPIO0_13; + break; + } + gpio.q_invert = &GPIO0_13; /* RFFC5071 GPIO serial interface PinMux */ -#if defined(JAWBREAKER) || defined(HACKRF_ONE) - gpio.rffc5072_select = &GPIO2_13; - gpio.rffc5072_clock = &GPIO5_6; - gpio.rffc5072_data = &GPIO3_3; - gpio.rffc5072_reset = &GPIO2_14; -#elif defined(RAD1O) - gpio.vco_ce = &GPIO2_13; - gpio.vco_sclk = &GPIO5_6; - gpio.vco_sdata = &GPIO3_3; - gpio.vco_le = &GPIO2_14; - gpio.vco_mux = &GPIO5_25; - gpio.synt_rfout_en = &GPIO3_5; -#elif defined(PRALINE) - gpio.rffc5072_select = &GPIO2_13; - gpio.rffc5072_clock = &GPIO5_18; - gpio.rffc5072_data = &GPIO4_14; - gpio.rffc5072_reset = &GPIO2_14; - gpio.rffc5072_ld = &GPIO6_25; + switch (board_id) { + case BOARD_ID_JAWBREAKER: + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(UNIVERSAL) + gpio.rffc5072_select = &GPIO2_13; + gpio.rffc5072_clock = &GPIO5_6; + gpio.rffc5072_data = &GPIO3_3; + gpio.rffc5072_reset = &GPIO2_14; #endif - - /* Praline */ -#if defined(PRALINE) - gpio.p2_ctrl0 = &GPIO7_3; - gpio.p2_ctrl1 = &GPIO7_4; - gpio.p1_ctrl0 = &GPIO0_14; - gpio.p1_ctrl1 = &GPIO5_16; - gpio.p1_ctrl2 = &GPIO3_5; - gpio.clkin_ctrl = &GPIO0_15; - gpio.aa_en = &GPIO1_7; - gpio.trigger_in = &GPIO6_26; - gpio.trigger_out = &GPIO5_6; - gpio.pps_out = &GPIO5_5; + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + gpio.vco_ce = &GPIO2_13; + gpio.vco_sclk = &GPIO5_6; + gpio.vco_sdata = &GPIO3_3; + gpio.vco_le = &GPIO2_14; + gpio.vco_mux = &GPIO5_25; + gpio.synt_rfout_en = &GPIO3_5; #endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + gpio.rffc5072_select = &GPIO2_13; + gpio.rffc5072_clock = &GPIO5_18; + gpio.rffc5072_data = &GPIO4_14; + gpio.rffc5072_reset = &GPIO2_14; + gpio.rffc5072_ld = &GPIO6_25; +#endif + break; + default: + break; + } - /* HackRF One r9 */ -#if defined(HACKRF_ONE) - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - /* HackRF One r9 clock control */ - gpio.h1r9_clkin_en = &GPIO5_15; - gpio.h1r9_clkout_en = &GPIO0_9; - gpio.h1r9_mcu_clk_en = &GPIO0_8; - - /* HackRF One r9 power control */ - gpio.h1r9_1v8_enable = &GPIO2_9; - gpio.h1r9_vaa_disable = &GPIO3_6; - - gpio.h1r9_trigger_enable = &GPIO5_5; +#if defined(PRALINE) || defined(UNIVERSAL) + switch (board_id) { + case BOARD_ID_PRALINE: + gpio.p2_ctrl0 = &GPIO7_3; + gpio.p2_ctrl1 = &GPIO7_4; + gpio.p1_ctrl0 = &GPIO0_14; + gpio.p1_ctrl1 = &GPIO5_16; + gpio.p1_ctrl2 = &GPIO3_5; + gpio.clkin_ctrl = &GPIO0_15; + gpio.aa_en = &GPIO1_7; + gpio.trigger_in = &GPIO6_26; + gpio.trigger_out = &GPIO5_6; + gpio.pps_out = &GPIO5_5; + break; + default: + break; } #endif - /* RAD1O */ +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + /* HackRF One r9 clock control */ + switch (board_id) { + case BOARD_ID_HACKRF1_R9: + gpio.h1r9_clkin_en = &GPIO5_15; + gpio.h1r9_clkout_en = &GPIO0_9; + gpio.h1r9_mcu_clk_en = &GPIO0_8; + break; + default: + break; + } + + switch (board_id) { + case BOARD_ID_HACKRF1_R9: + gpio.h1r9_1v8_enable = &GPIO2_9; + gpio.h1r9_vaa_disable = &GPIO3_6; + gpio.h1r9_trigger_enable = &GPIO5_5; + break; + default: + break; + } +#endif + + /* rad1o LCD */ #if defined(RAD1O) gpio.lcd_cs = &GPIO4_12; /* P9_0 */ gpio.lcd_bl_en = &GPIO0_8; /* P1_1 */ @@ -217,11 +322,9 @@ const platform_gpio_t* platform_gpio(void) #endif /* Portapack */ -#if defined(HACKRF_ONE) || defined(PRALINE) +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) gpio.io_stbx = &GPIO5_0; /* P2_0 */ gpio.addr = &GPIO5_1; /* P2_1 */ - gpio.lcd_te = &GPIO5_3; /* P2_3 */ - gpio.unused = &GPIO5_7; /* P2_8 */ gpio.lcd_rdx = &GPIO5_4; /* P2_4 */ gpio.lcd_wrx = &GPIO1_10; /* P2_9 */ gpio.dir = &GPIO1_13; /* P2_13 */ diff --git a/firmware/common/platform_gpio.h b/firmware/common/platform_gpio.h index 9c32925c..16767136 100644 --- a/firmware/common/platform_gpio.h +++ b/firmware/common/platform_gpio.h @@ -30,7 +30,7 @@ extern "C" { typedef struct { /* LEDs */ -#if defined(PRALINE) || defined(RAD1O) +#if defined(PRALINE) || defined(UNIVERSAL) || defined(RAD1O) gpio_t led[4]; #else gpio_t led[3]; @@ -38,7 +38,7 @@ typedef struct { /* Power Supply Control */ gpio_t gpio_1v8_enable; -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) gpio_t gpio_1v2_enable; gpio_t gpio_3v3aux_enable_n; #endif @@ -48,7 +48,7 @@ typedef struct { gpio_t max283x_enable; gpio_t max283x_rx_enable; gpio_t max283x_tx_enable; -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) gpio_t max2831_rxhp; gpio_t max2831_ld; #endif @@ -68,7 +68,7 @@ typedef struct { gpio_t w25q80bv_select; /* RF switch control */ -#if defined(HACKRF_ONE) +#if defined(HACKRF_ONE) || defined(UNIVERSAL) gpio_t hp; gpio_t lp; gpio_t tx_mix_bp; @@ -98,7 +98,7 @@ typedef struct { gpio_t tx_amp; gpio_t rx_lna; #endif -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) gpio_t tx_en; gpio_t mix_en_n; gpio_t mix_en_n_r1_0; @@ -109,23 +109,23 @@ typedef struct { /* CPLD JTAG interface GPIO pins, FPGA config pins in Praline */ gpio_t cpld_tck; -#if defined(HACKRF_ONE) || defined(RAD1O) || defined(JAWBREAKER) - gpio_t cpld_tdo; +#if !defined(PRALINE) || defined(UNIVERSAL) gpio_t cpld_tms; gpio_t cpld_tdi; + gpio_t cpld_tdo; #endif -#if defined(HACKRF_ONE) || defined(PRALINE) +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) gpio_t cpld_pp_tms; gpio_t cpld_pp_tdo; #endif -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) gpio_t fpga_cfg_creset; gpio_t fpga_cfg_cdone; gpio_t fpga_cfg_spi_cs; #endif /* Other CPLD interface GPIO pins */ -#if !defined(PRALINE) +#if !defined(PRALINE) || defined(UNIVERSAL) gpio_t trigger_enable; #endif gpio_t q_invert; @@ -144,7 +144,7 @@ typedef struct { gpio_t synt_rfout_en; // RAD1O /* Praline */ -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) gpio_t p2_ctrl0; gpio_t p2_ctrl1; gpio_t p1_ctrl0; @@ -157,8 +157,7 @@ typedef struct { gpio_t pps_out; #endif - /* HackRF One r9 */ -#if defined(HACKRF_ONE) +#if defined(HACKRF_ONE) || defined(UNIVERSAL) /* HackRF One r9 clock control */ gpio_t h1r9_clkin_en; gpio_t h1r9_clkout_en; @@ -177,11 +176,9 @@ typedef struct { #endif /* Portapack */ -#if defined(HACKRF_ONE) || defined(PRALINE) +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) gpio_t io_stbx; gpio_t addr; - __attribute__((unused)) gpio_t lcd_te; - __attribute__((unused)) gpio_t unused; gpio_t lcd_rdx; gpio_t lcd_wrx; gpio_t dir; From 6176dbcdd52ba72cb4772f998329e1d269b9b90c Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 3 Apr 2026 21:18:01 +0100 Subject: [PATCH 044/173] Universalize: platform_scu.{c,h} Co-authored-by: Antoine van Gelder --- firmware/common/hackrf_core.c | 2 +- firmware/common/platform_scu.c | 597 ++++++++++++++++++++------------- firmware/common/platform_scu.h | 76 +++-- 3 files changed, 405 insertions(+), 270 deletions(-) diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 1535e1e8..25e3b870 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -947,7 +947,7 @@ void pin_setup(void) #endif jtag_gpio_cpld.gpio_tck = gpio->cpld_tck; -#if defined(HACKRF_ONE) || defined(RAD1O) || defined(JAWBREAKER) +#if !defined(PRALINE) || defined(UNIVERSAL) jtag_gpio_cpld.gpio_tms = gpio->cpld_tms; jtag_gpio_cpld.gpio_tdi = gpio->cpld_tdi; jtag_gpio_cpld.gpio_tdo = gpio->cpld_tdo; diff --git a/firmware/common/platform_scu.c b/firmware/common/platform_scu.c index a4365635..edd6ee0e 100644 --- a/firmware/common/platform_scu.c +++ b/firmware/common/platform_scu.c @@ -25,6 +25,8 @@ #include +#include "platform_detect.h" + // clang-format off const platform_scu_t* platform_scu(void) @@ -34,44 +36,68 @@ const platform_scu_t* platform_scu(void) return _platform_scu; } + board_id_t board_id = detected_platform(); static platform_scu_t scu; /* LED PinMux */ scu.PINMUX_LED1 = SCU_PINMUX_LED1; /* GPIO2[1] on P4_1 */ scu.PINMUX_LED2 = SCU_PINMUX_LED2; /* GPIO2[2] on P4_2 */ scu.PINMUX_LED3 = SCU_PINMUX_LED3; /* GPIO2[8] on P6_12 */ -#if defined(PRALINE) - scu.PINMUX_LED4 = (P8_6); /* GPIO4[6] on P8_6 */ -#elif defined(RAD1O) - scu.PINMUX_LED4 = (PB_6); /* GPIO5[26] on PB_6 */ + switch (board_id) { + case BOARD_ID_RAD1O: +#if defined(RAD1O) + scu.PINMUX_LED4 = (PB_6); /* GPIO5[26] on PB_6 */ #endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu.PINMUX_LED4 = (P8_6); /* GPIO4[6] on P8_6 */ +#endif + break; + default: + break; + } /* Power Supply PinMux */ - scu.PINMUX_EN1V8 = (P6_10); /* GPIO3[6] on P6_10 */ - scu.PINMUX_EN1V2 = (P8_7); /* GPIO4[7] on P8_7 */ -#if defined(PRALINE) - scu.PINMUX_EN3V3_AUX_N = (P6_7); /* GPIO5[15] on P6_7 */ - scu.PINMUX_EN3V3_OC_N = (P6_11); /* GPIO3[7] on P6_11 */ + scu.PINMUX_EN1V8 = (P6_10); /* GPIO3[6] on P6_10 */ + scu.PINMUX_EN1V2 = (P8_7); /* GPIO4[7] on P8_7 */ + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu.PINMUX_EN3V3_AUX_N = (P6_7); /* GPIO5[15] on P6_7 */ + scu.PINMUX_EN3V3_OC_N = (P6_11); /* GPIO3[7] on P6_11 */ #endif + break; + default: + break; + } /* GPIO Input PinMux */ scu.PINMUX_BOOT0 = (P1_1); /* GPIO0[8] on P1_1 */ scu.PINMUX_BOOT1 = (P1_2); /* GPIO0[9] on P1_2 */ -#if !defined(HACKRF_ONE) - scu.PINMUX_BOOT2 = (P2_8); /* GPIO5[7] on P2_8 */ - scu.PINMUX_BOOT3 = (P2_9); /* GPIO1[10] on P2_9 */ + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + break; + default: +#if !defined(HACKRF_ONE) || defined(UNIVERSAL) + scu.PINMUX_BOOT2 = (P2_8); /* GPIO5[7] on P2_8 */ + scu.PINMUX_BOOT3 = (P2_9); /* GPIO1[10] on P2_9 */ #endif - scu.PINMUX_PP_LCD_TE = (P2_3); /* GPIO5[3] on P2_3 */ - scu.PINMUX_PP_LCD_RDX = (P2_4); /* GPIO5[4] on P2_4 */ - scu.PINMUX_PP_UNUSED = (P2_8); /* GPIO5[7] on P2_8 */ - scu.PINMUX_PP_LCD_WRX = (P2_9); /* GPIO1[10] on P2_9 */ - scu.PINMUX_PP_DIR = (P2_13); /* GPIO1[13] on P2_13 */ + break; + } /* USB peripheral */ + switch (board_id) { + case BOARD_ID_JAWBREAKER: #if defined(JAWBREAKER) - scu.PINMUX_USB_LED0 = (P6_8); - scu.PINMUX_USB_LED1 = (P6_7); + scu.PINMUX_USB_LED0 = (P6_8); + scu.PINMUX_USB_LED1 = (P6_7); #endif + break; + default: + break; + } /* SSP1 Peripheral PinMux */ scu.SSP1_CIPO = (P1_3); /* P1_3 */ @@ -80,172 +106,242 @@ const platform_scu_t* platform_scu(void) scu.SSP1_CS = (P1_20); /* P1_20 */ /* CPLD JTAG interface */ - scu.PINMUX_CPLD_TCK = (P6_1); /* GPIO3[ 0] */ -#if defined(PRALINE) - scu.PINMUX_FPGA_CRESET = (P5_2); /* GPIO2[11] on P5_2 */ - scu.PINMUX_FPGA_CDONE = (P4_10); /* GPIO5[14] */ - scu.PINMUX_FPGA_SPI_CS = (P5_1); /* GPIO2[10] */ -#else - scu.PINMUX_CPLD_TDO = (P9_5); /* GPIO5[18] */ + scu.PINMUX_CPLD_TCK = (P6_1); /* GPIO3[ 0] */ + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu.PINMUX_FPGA_CRESET = (P5_2); /* GPIO2[11] on P5_2 */ + scu.PINMUX_FPGA_CDONE = (P4_10); /* GPIO5[14] */ + scu.PINMUX_FPGA_SPI_CS = (P5_1); /* GPIO2[10] */ #endif -#if defined(RAD1O) || defined(HACKRF_ONE) - scu.PINMUX_CPLD_TMS = (P6_5); /* GPIO3[ 4] */ - scu.PINMUX_CPLD_TDI = (P6_2); /* GPIO3[ 1] */ -#endif -#if defined(JAWBREAKER) - scu.PINMUX_CPLD_TMS = (P6_2); /* GPIO3[ 1] */ - scu.PINMUX_CPLD_TDI = (P6_5); /* GPIO3[ 4] */ + break; + default: + break; + } +#if !defined(PRALINE) || defined(UNIVERSAL) + scu.PINMUX_CPLD_TDO = (P9_5); /* GPIO5[18] */ + switch (board_id) { + case BOARD_ID_RAD1O: + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + scu.PINMUX_CPLD_TMS = (P6_5); /* GPIO3[ 4] */ + scu.PINMUX_CPLD_TDI = (P6_2); /* GPIO3[ 1] */ + break; + default: + scu.PINMUX_CPLD_TMS = (P6_2); /* GPIO3[ 1] */ + scu.PINMUX_CPLD_TDI = (P6_5); /* GPIO3[ 4] */ + break; + } #endif /* CPLD SGPIO interface */ -#if defined(PRALINE) - scu.PINMUX_SGPIO0 = (P0_0); - scu.PINMUX_SGPIO1 = (P0_1); - scu.PINMUX_SGPIO2 = (P1_15); - scu.PINMUX_SGPIO3 = (P1_16); - scu.PINMUX_SGPIO4 = (P9_4); - scu.PINMUX_SGPIO5 = (P6_6); - scu.PINMUX_SGPIO6 = (P2_2); - scu.PINMUX_SGPIO7 = (P1_0); - scu.PINMUX_SGPIO8 = (P8_0); - scu.PINMUX_SGPIO9 = (P9_3); - scu.PINMUX_SGPIO10 = (P8_2); - scu.PINMUX_SGPIO11 = (P1_17); - scu.PINMUX_SGPIO12 = (P1_18); - scu.PINMUX_SGPIO14 = (P1_18); - scu.PINMUX_SGPIO15 = (P1_18); + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu.PINMUX_SGPIO0 = (P0_0); + scu.PINMUX_SGPIO1 = (P0_1); + scu.PINMUX_SGPIO2 = (P1_15); + scu.PINMUX_SGPIO3 = (P1_16); + scu.PINMUX_SGPIO4 = (P9_4); + scu.PINMUX_SGPIO5 = (P6_6); + scu.PINMUX_SGPIO6 = (P2_2); + scu.PINMUX_SGPIO7 = (P1_0); + scu.PINMUX_SGPIO8 = (P8_0); + scu.PINMUX_SGPIO9 = (P9_3); + scu.PINMUX_SGPIO10 = (P8_2); + scu.PINMUX_SGPIO11 = (P1_17); + scu.PINMUX_SGPIO12 = (P1_18); + scu.PINMUX_SGPIO14 = (P1_18); + scu.PINMUX_SGPIO15 = (P1_18); - scu.PINMUX_SGPIO0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3); - scu.PINMUX_SGPIO1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3); - scu.PINMUX_SGPIO2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); - scu.PINMUX_SGPIO3_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); - scu.PINMUX_SGPIO4_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); - scu.PINMUX_SGPIO5_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); - scu.PINMUX_SGPIO6_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu.PINMUX_SGPIO7_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); - scu.PINMUX_SGPIO8_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.PINMUX_SGPIO9_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); - scu.PINMUX_SGPIO10_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.PINMUX_SGPIO11_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); - scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); -#else - scu.PINMUX_SGPIO0 = (P0_0); - scu.PINMUX_SGPIO1 = (P0_1); - scu.PINMUX_SGPIO2 = (P1_15); - scu.PINMUX_SGPIO3 = (P1_16); - scu.PINMUX_SGPIO4 = (P6_3); - scu.PINMUX_SGPIO5 = (P6_6); - scu.PINMUX_SGPIO6 = (P2_2); - scu.PINMUX_SGPIO7 = (P1_0); - scu.PINMUX_SGPIO8 = (P9_6); - scu.PINMUX_SGPIO9 = (P4_3); - scu.PINMUX_SGPIO10 = (P1_14); - scu.PINMUX_SGPIO11 = (P1_17); - scu.PINMUX_SGPIO12 = (P1_18); - scu.PINMUX_SGPIO14 = (P4_9); - scu.PINMUX_SGPIO15 = (P4_10); - - scu.PINMUX_SGPIO0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3); - scu.PINMUX_SGPIO1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3); - scu.PINMUX_SGPIO2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); - scu.PINMUX_SGPIO3_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); - scu.PINMUX_SGPIO4_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); - scu.PINMUX_SGPIO5_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); - scu.PINMUX_SGPIO6_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu.PINMUX_SGPIO7_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); - scu.PINMUX_SGPIO8_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); - scu.PINMUX_SGPIO9_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION7); - scu.PINMUX_SGPIO10_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); - scu.PINMUX_SGPIO11_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); - scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.PINMUX_SGPIO0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3); + scu.PINMUX_SGPIO1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3); + scu.PINMUX_SGPIO2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); + scu.PINMUX_SGPIO3_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); + scu.PINMUX_SGPIO4_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); + scu.PINMUX_SGPIO5_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); + scu.PINMUX_SGPIO6_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu.PINMUX_SGPIO7_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); + scu.PINMUX_SGPIO8_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.PINMUX_SGPIO9_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); + scu.PINMUX_SGPIO10_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.PINMUX_SGPIO11_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); + scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); #endif + break; + default: +#if !defined(PRALINE) || defined(UNIVERSAL) + scu.PINMUX_SGPIO0 = (P0_0); + scu.PINMUX_SGPIO1 = (P0_1); + scu.PINMUX_SGPIO2 = (P1_15); + scu.PINMUX_SGPIO3 = (P1_16); + scu.PINMUX_SGPIO4 = (P6_3); + scu.PINMUX_SGPIO5 = (P6_6); + scu.PINMUX_SGPIO6 = (P2_2); + scu.PINMUX_SGPIO7 = (P1_0); + scu.PINMUX_SGPIO8 = (P9_6); + scu.PINMUX_SGPIO9 = (P4_3); + scu.PINMUX_SGPIO10 = (P1_14); + scu.PINMUX_SGPIO11 = (P1_17); + scu.PINMUX_SGPIO12 = (P1_18); + scu.PINMUX_SGPIO14 = (P4_9); + scu.PINMUX_SGPIO15 = (P4_10); + + scu.PINMUX_SGPIO0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3); + scu.PINMUX_SGPIO1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3); + scu.PINMUX_SGPIO2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); + scu.PINMUX_SGPIO3_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); + scu.PINMUX_SGPIO4_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); + scu.PINMUX_SGPIO5_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2); + scu.PINMUX_SGPIO6_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu.PINMUX_SGPIO7_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); + scu.PINMUX_SGPIO8_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); + scu.PINMUX_SGPIO9_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION7); + scu.PINMUX_SGPIO10_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); + scu.PINMUX_SGPIO11_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6); + scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); +#endif + break; + } scu.TRIGGER_EN = (P4_8); /* GPIO5[12] on P4_8 */ /* MAX283x GPIO (XCVR_CTL) PinMux */ -#if defined(PRALINE) - scu.XCVR_ENABLE = PE_1; /* GPIO7[1] on PE_1 */ - scu.XCVR_RXENABLE = PE_2; /* GPIO7[2] on PE_2 */ - scu.XCVR_CS = PD_14; /* GPIO6[28] on PD_14 */ - scu.XCVR_RXHP = PD_15; /* GPIO6[29] on PD_15 */ - scu.XCVR_LD = P9_6; /* GPIO4[11] on P9_6 */ - - scu.XCVR_ENABLE_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.XCVR_RXENABLE_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.XCVR_CS_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.XCVR_RXHP_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.XCVR_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0 | - SCU_CONF_EPD_EN_PULLDOWN | SCU_CONF_EPUN_DIS_PULLUP); -#elif defined(JAWBREAKER) || defined(HACKRF_ONE) - scu.XCVR_ENABLE = P4_6; /* GPIO2[6] on P4_6 */ - scu.XCVR_RXENABLE = P4_5; /* GPIO2[5] on P4_5 */ - scu.XCVR_TXENABLE = P4_4; /* GPIO2[4] on P4_4 */ - scu.XCVR_CS = P1_20; /* GPIO0[15] on P1_20 */ - - scu.XCVR_ENABLE_PINCFG = (SCU_GPIO_FAST); - scu.XCVR_RXENABLE_PINCFG = (SCU_GPIO_FAST); - scu.XCVR_TXENABLE_PINCFG = (SCU_GPIO_FAST); - scu.XCVR_CS_PINCFG = (SCU_GPIO_FAST); -#elif defined(RAD1O) - scu.XCVR_RXHP = P8_1; /* GPIO[] on P8_1 */ - scu.XCVR_B6 = P8_2; /* GPIO[] on P8_2 */ - scu.XCVR_B7 = P9_3; /* GPIO[] on P9_3 */ + switch (board_id) { + case BOARD_ID_RAD1O: +#if defined(RAD1O) + scu.XCVR_RXHP = P8_1; /* GPIO[] on P8_1 */ + scu.XCVR_B6 = P8_2; /* GPIO[] on P8_2 */ + scu.XCVR_B7 = P9_3; /* GPIO[] on P9_3 */ #endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu.XCVR_ENABLE = PE_1; /* GPIO7[1] on PE_1 */ + scu.XCVR_RXENABLE = PE_2; /* GPIO7[2] on PE_2 */ + scu.XCVR_CS = PD_14; /* GPIO6[28] on PD_14 */ + scu.XCVR_RXHP = PD_15; /* GPIO6[29] on PD_15 */ + scu.XCVR_LD = P9_6; /* GPIO4[11] on P9_6 */ + + scu.XCVR_ENABLE_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.XCVR_RXENABLE_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.XCVR_CS_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.XCVR_RXHP_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.XCVR_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0 | + SCU_CONF_EPD_EN_PULLDOWN | SCU_CONF_EPUN_DIS_PULLUP); +#endif + break; + case BOARD_ID_JAWBREAKER: + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(UNIVERSAL) + scu.XCVR_ENABLE = P4_6; /* GPIO2[6] on P4_6 */ + scu.XCVR_RXENABLE = P4_5; /* GPIO2[5] on P4_5 */ + scu.XCVR_TXENABLE = P4_4; /* GPIO2[4] on P4_4 */ + scu.XCVR_CS = P1_20; /* GPIO0[15] on P1_20 */ + + scu.XCVR_ENABLE_PINCFG = (SCU_GPIO_FAST); + scu.XCVR_RXENABLE_PINCFG = (SCU_GPIO_FAST); + scu.XCVR_TXENABLE_PINCFG = (SCU_GPIO_FAST); + scu.XCVR_CS_PINCFG = (SCU_GPIO_FAST); +#endif + break; + default: + break; + } /* MAX5864 SPI chip select (AD_CS) GPIO PinMux */ -#if defined(PRALINE) - scu.AD_CS = (PD_16); /* GPIO6[30] on PD_16 */ - scu.AD_CS_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); -#else - scu.AD_CS = (P5_7); /* GPIO2[7] on P5_7 */ - scu.AD_CS_PINCFG = (SCU_GPIO_FAST); + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu.AD_CS = (PD_16); /* GPIO6[30] on PD_16 */ + scu.AD_CS_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); #endif + break; + default: + scu.AD_CS = (P5_7); /* GPIO2[7] on P5_7 */ + scu.AD_CS_PINCFG = (SCU_GPIO_FAST); + break; + } /* RFFC5071 GPIO serial interface PinMux */ -#if defined(PRALINE) - scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */ - scu.MIXER_SCLK = (P9_5); /* GPIO5[18] on P9_5 */ - scu.MIXER_SDATA = (P9_2); /* GPIO4[14] on P9_2 */ - scu.MIXER_RESETX = (P5_5); /* GPIO2[14] on P5_5 */ - scu.MIXER_LD = (PD_11); /* GPIO6[25] on PD_11 */ + switch (board_id) { + case BOARD_ID_JAWBREAKER: + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(UNIVERSAL) + scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */ + scu.MIXER_SCLK = (P2_6); /* GPIO5[6] on P2_6 */ + scu.MIXER_SDATA = (P6_4); /* GPIO3[3] on P6_4 */ + scu.MIXER_RESETX = (P5_5); /* GPIO2[14] on P5_5 */ - scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu.MIXER_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); -#elif defined(JAWBREAKER) || defined(HACKRF_ONE) - scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */ - scu.MIXER_SCLK = (P2_6); /* GPIO5[6] on P2_6 */ - scu.MIXER_SDATA = (P6_4); /* GPIO3[3] on P6_4 */ - scu.MIXER_RESETX = (P5_5); /* GPIO2[14] on P5_5 */ - - scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); -#elif defined(RAD1O) - scu.VCO_CE = (P5_4); /* GPIO2[13] on P5_4 */ - scu.VCO_SCLK = (P2_6); /* GPIO5[6] on P2_6 */ - scu.VCO_SDATA = (P6_4); /* GPIO3[3] on P6_4 */ - scu.VCO_LE = (P5_5); /* GPIO2[14] on P5_5 */ - scu.VCO_MUX = (PB_5); /* GPIO5[25] on PB_5 */ - scu.MIXER_EN = (P6_8); /* GPIO5[16] on P6_8 */ - scu.SYNT_RFOUT_EN = (P6_9); /* GPIO3[5] on P6_9 */ + scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); #endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */ + scu.MIXER_SCLK = (P9_5); /* GPIO5[18] on P9_5 */ + scu.MIXER_SDATA = (P9_2); /* GPIO4[14] on P9_2 */ + scu.MIXER_RESETX = (P5_5); /* GPIO2[14] on P5_5 */ + scu.MIXER_LD = (PD_11); /* GPIO6[25] on PD_11 */ + + scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu.MIXER_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); +#endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + scu.VCO_CE = (P5_4); /* GPIO2[13] on P5_4 */ + scu.VCO_SCLK = (P2_6); /* GPIO5[6] on P2_6 */ + scu.VCO_SDATA = (P6_4); /* GPIO3[3] on P6_4 */ + scu.VCO_LE = (P5_5); /* GPIO2[14] on P5_5 */ + scu.VCO_MUX = (PB_5); /* GPIO5[25] on PB_5 */ + scu.MIXER_EN = (P6_8); /* GPIO5[16] on P6_8 */ + scu.SYNT_RFOUT_EN = (P6_9); /* GPIO3[5] on P6_9 */ +#endif + break; + default: + break; + } /* RF LDO control */ + switch (board_id) { + case BOARD_ID_JAWBREAKER: #if defined(JAWBREAKER) - scu.RF_LDO_ENABLE = (P5_0); /* GPIO2[9] on P5_0 */ + scu.RF_LDO_ENABLE = (P5_0); /* GPIO2[9] on P5_0 */ #endif + break; + default: + break; + } /* RF supply (VAA) control */ -#if defined(PRALINE) - scu.NO_VAA_ENABLE = P8_1; /* GPIO4[1] on P8_1 */ -#elif defined(HACKRF_ONE) - scu.NO_VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */ -#elif defined(RAD1O) - scu.VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */ + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + scu.NO_VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */ #endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu.NO_VAA_ENABLE = P8_1; /* GPIO4[1] on P8_1 */ +#endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + scu.VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */ +#endif + break; + default: + break; + } /* SPI flash */ scu.SSP0_CIPO = (P3_6); @@ -256,79 +352,105 @@ const platform_scu_t* platform_scu(void) scu.FLASH_WP = (P3_5); /* GPIO1[15] on P3_5 */ /* RF switch control */ -#if defined(PRALINE) - scu.TX_EN = P6_5; /* GPIO3[4] on P6_5 */ - scu.MIX_EN_N = P6_3; /* GPIO3[2] on P6_3 */ - scu.MIX_EN_N_R1_0 = P2_6; /* GPIO5[6] on P2_6 */ - scu.LPF_EN = PA_1; /* GPIO4[8] on PA_1 */ - scu.RF_AMP_EN = PA_2; /* GPIO4[9] on PA_2 */ - scu.ANT_BIAS_EN_N = P2_12; /* GPIO1[12] on P2_12 */ - scu.ANT_BIAS_OC_N = P2_11; /* GPIO1[11] on P2_11 */ -#elif defined(HACKRF_ONE) - scu.HP = P4_0; /* GPIO2[0] on P4_0 */ - scu.LP = P5_1; /* GPIO2[10] on P5_1 */ - scu.TX_MIX_BP = P5_2; /* GPIO2[11] on P5_2 */ - scu.NO_MIX_BYPASS = P1_7; /* GPIO1[0] on P1_7 */ - scu.RX_MIX_BP = P5_3; /* GPIO2[12] on P5_3 */ - scu.TX_AMP = P5_6; /* GPIO2[15] on P5_6 */ - scu.TX = P6_7; /* GPIO5[15] on P6_7 */ - scu.MIX_BYPASS = P6_8; /* GPIO5[16] on P6_8 */ - scu.RX = P2_5; /* GPIO5[5] on P2_5 */ - scu.NO_TX_AMP_PWR = P6_9; /* GPIO3[5] on P6_9 */ - scu.AMP_BYPASS = P2_10; /* GPIO0[14] on P2_10 */ - scu.RX_AMP = P2_11; /* GPIO1[11] on P2_11 */ - scu.NO_RX_AMP_PWR = P2_12; /* GPIO1[12] on P2_12 */ -#elif defined(RAD1O) - scu.BY_AMP = P1_7; /* GPIO1[0] on P1_7 */ - scu.BY_AMP_N = P2_5; /* GPIO5[5] on P2_5 */ - scu.TX_RX = P2_10; /* GPIO0[14] on P2_10 */ - scu.TX_RX_N = P2_11; /* GPIO1[11] on P2_11 */ - scu.BY_MIX = P2_12; /* GPIO1[12] on P2_12 */ - scu.BY_MIX_N = P5_1; /* GPIO2[10] on P5_1 */ - scu.LOW_HIGH_FILT = P5_2; /* GPIO2[11] on P5_2 */ - scu.LOW_HIGH_FILT_N = P5_3; /* GPIO2[12] on P5_3 */ - scu.TX_AMP = P5_6; /* GPIO2[15] on P5_6 */ - scu.RX_LNA = P6_7; /* GPIO5[15] on P6_7 */ + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + scu.HP = P4_0; /* GPIO2[0] on P4_0 */ + scu.LP = P5_1; /* GPIO2[10] on P5_1 */ + scu.TX_MIX_BP = P5_2; /* GPIO2[11] on P5_2 */ + scu.NO_MIX_BYPASS = P1_7; /* GPIO1[0] on P1_7 */ + scu.RX_MIX_BP = P5_3; /* GPIO2[12] on P5_3 */ + scu.TX_AMP = P5_6; /* GPIO2[15] on P5_6 */ + scu.TX = P6_7; /* GPIO5[15] on P6_7 */ + scu.MIX_BYPASS = P6_8; /* GPIO5[16] on P6_8 */ + scu.RX = P2_5; /* GPIO5[5] on P2_5 */ + scu.NO_TX_AMP_PWR = P6_9; /* GPIO3[5] on P6_9 */ + scu.AMP_BYPASS = P2_10; /* GPIO0[14] on P2_10 */ + scu.RX_AMP = P2_11; /* GPIO1[11] on P2_11 */ + scu.NO_RX_AMP_PWR = P2_12; /* GPIO1[12] on P2_12 */ #endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + scu.BY_AMP = P1_7; /* GPIO1[0] on P1_7 */ + scu.BY_AMP_N = P2_5; /* GPIO5[5] on P2_5 */ + scu.TX_RX = P2_10; /* GPIO0[14] on P2_10 */ + scu.TX_RX_N = P2_11; /* GPIO1[11] on P2_11 */ + scu.BY_MIX = P2_12; /* GPIO1[12] on P2_12 */ + scu.BY_MIX_N = P5_1; /* GPIO2[10] on P5_1 */ + scu.LOW_HIGH_FILT = P5_2; /* GPIO2[11] on P5_2 */ + scu.LOW_HIGH_FILT_N = P5_3; /* GPIO2[12] on P5_3 */ + scu.TX_AMP = P5_6; /* GPIO2[15] on P5_6 */ + scu.RX_LNA = P6_7; /* GPIO5[15] on P6_7 */ +#endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu.TX_EN = P6_5; /* GPIO3[4] on P6_5 */ + scu.MIX_EN_N = P6_3; /* GPIO3[2] on P6_3 */ + scu.MIX_EN_N_R1_0 = P2_6; /* GPIO5[6] on P2_6 */ + scu.LPF_EN = PA_1; /* GPIO4[8] on PA_1 */ + scu.RF_AMP_EN = PA_2; /* GPIO4[9] on PA_2 */ + scu.ANT_BIAS_EN_N = P2_12; /* GPIO1[12] on P2_12 */ + scu.ANT_BIAS_OC_N = P2_11; /* GPIO1[11] on P2_11 */ +#endif + break; + default: + break; + } /* Praline */ -#if defined(PRALINE) - scu.P2_CTRL0 = (PE_3); /* GPIO7[3] on PE_3 */ - scu.P2_CTRL1 = (PE_4); /* GPIO7[4] on PE_4 */ - scu.P1_CTRL0 = (P2_10); /* GPIO0[14] on P2_10 */ - scu.P1_CTRL1 = (P6_8); /* GPIO5[16] on P6_8 */ - scu.P1_CTRL2 = (P6_9); /* GPIO3[5] on P6_9 */ - scu.CLKIN_CTRL = (P1_20); /* GPIO0[15] on P1_20 */ - scu.AA_EN = (P1_14); /* GPIO1[7] on P1_14 */ - scu.TRIGGER_IN = (PD_12); /* GPIO6[26] on PD_12 */ - scu.TRIGGER_OUT = (P2_6); /* GPIO5[6] on P2_6 */ - scu.PPS_OUT = (P2_5); /* GPIO5[5] on P2_5 */ +#if defined(PRALINE) || defined(UNIVERSAL) + switch (board_id) { + case BOARD_ID_PRALINE: + scu.P2_CTRL0 = (PE_3); /* GPIO7[3] on PE_3 */ + scu.P2_CTRL1 = (PE_4); /* GPIO7[4] on PE_4 */ + scu.P1_CTRL0 = (P2_10); /* GPIO0[14] on P2_10 */ + scu.P1_CTRL1 = (P6_8); /* GPIO5[16] on P6_8 */ + scu.P1_CTRL2 = (P6_9); /* GPIO3[5] on P6_9 */ + scu.CLKIN_CTRL = (P1_20); /* GPIO0[15] on P1_20 */ + scu.AA_EN = (P1_14); /* GPIO1[7] on P1_14 */ + scu.TRIGGER_IN = (PD_12); /* GPIO6[26] on PD_12 */ + scu.TRIGGER_OUT = (P2_6); /* GPIO5[6] on P2_6 */ + scu.PPS_OUT = (P2_5); /* GPIO5[5] on P2_5 */ - scu.P2_CTRL0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.P2_CTRL1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.P1_CTRL0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu.P1_CTRL1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.P1_CTRL2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu.CLKIN_CTRL_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu.AA_EN_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu.TRIGGER_IN_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.TRIGGER_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu.PPS_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.P2_CTRL0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.P2_CTRL1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.P1_CTRL0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu.P1_CTRL1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.P1_CTRL2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu.CLKIN_CTRL_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu.AA_EN_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu.TRIGGER_IN_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.TRIGGER_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.PPS_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + break; + default: + break; + } #endif /* HackRF One r9 */ -#if defined(HACKRF_ONE) - scu.H1R9_CLKIN_EN = P6_7; /* GPIO5[15] on P6_7 */ - scu.H1R9_CLKOUT_EN = P1_2; /* GPIO0[9] on P1_2 = has boot pull-down; */ - scu.H1R9_MCU_CLK_EN = P1_1; /* GPIO0[8] on P1_1 = has boot pull-up; */ - scu.H1R9_RX = P2_7; /* GPIO0[7] on P4_4 = has boot pull-up; */ - scu.H1R9_NO_ANT_PWR = P4_4; /* GPIO2[4] on P4_4 */ - scu.H1R9_EN1V8 = P5_0; /* GPIO2[9] on P5_0 */ - scu.H1R9_NO_VAA_EN = P6_10; /* GPIO3[6] on P6_10 */ - scu.H1R9_TRIGGER_EN = P2_5; /* GPIO5[5] on P2_5 */ +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + switch (board_id) { + case BOARD_ID_HACKRF1_R9: + scu.H1R9_CLKIN_EN = P6_7; /* GPIO5[15] on P6_7 */ + scu.H1R9_CLKOUT_EN = P1_2; /* GPIO0[9] on P1_2 = has boot pull-down; */ + scu.H1R9_MCU_CLK_EN = P1_1; /* GPIO0[8] on P1_1 = has boot pull-up; */ + scu.H1R9_RX = P2_7; /* GPIO0[7] on P4_4 = has boot pull-up; */ + scu.H1R9_NO_ANT_PWR = P4_4; /* GPIO2[4] on P4_4 */ + scu.H1R9_EN1V8 = P5_0; /* GPIO2[9] on P5_0 */ + scu.H1R9_NO_VAA_EN = P6_10; /* GPIO3[6] on P6_10 */ + scu.H1R9_TRIGGER_EN = P2_5; /* GPIO5[5] on P2_5 */ + break; + default: + break; + } #endif - /* Miscellaneous */ + /* Expansion headers */ +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) scu.PINMUX_PP_D0 = (P7_0); /* GPIO3[8] */ scu.PINMUX_PP_D1 = (P7_1); /* GPIO3[9] */ scu.PINMUX_PP_D2 = (P7_2); /* GPIO3[10] */ @@ -338,16 +460,11 @@ const platform_scu_t* platform_scu(void) scu.PINMUX_PP_D6 = (P7_6); /* GPIO3[14] */ scu.PINMUX_PP_D7 = (P7_7); /* GPIO3[15] */ - /* TODO add other Pins */ - - scu.PINMUX_GPIO3_8 = (P7_0); /* GPIO3[8] */ - scu.PINMUX_GPIO3_9 = (P7_1); /* GPIO3[9] */ - scu.PINMUX_GPIO3_10 = (P7_2); /* GPIO3[10] */ - scu.PINMUX_GPIO3_11 = (P7_3); /* GPIO3[11] */ - scu.PINMUX_GPIO3_12 = (P7_4); /* GPIO3[12] */ - scu.PINMUX_GPIO3_13 = (P7_5); /* GPIO3[13] */ - scu.PINMUX_GPIO3_14 = (P7_6); /* GPIO3[14] */ - scu.PINMUX_GPIO3_15 = (P7_7); /* GPIO3[15] */ + scu.PINMUX_PP_LCD_TE = (P2_3); /* GPIO5[3] on P2_3 */ + scu.PINMUX_PP_LCD_RDX = (P2_4); /* GPIO5[4] on P2_4 */ + scu.PINMUX_PP_UNUSED = (P2_8); /* GPIO5[7] on P2_8 */ + scu.PINMUX_PP_LCD_WRX = (P2_9); /* GPIO1[10] on P2_9 */ + scu.PINMUX_PP_DIR = (P2_13); /* GPIO1[13] on P2_13 */ scu.PINMUX_PP_TDO = (P1_5); /* GPIO1[8] */ scu.PINMUX_SD_POW = (P1_5); /* GPIO1[8] */ @@ -365,6 +482,16 @@ const platform_scu_t* platform_scu(void) scu.PINMUX_U0_TXD = (P2_0); /* GPIO5[0] */ scu.PINMUX_U0_RXD = (P2_1); /* GPIO5[1] */ + scu.PINMUX_GPIO3_8 = (P7_0); /* GPIO3[8] */ + scu.PINMUX_GPIO3_9 = (P7_1); /* GPIO3[9] */ + scu.PINMUX_GPIO3_10 = (P7_2); /* GPIO3[10] */ + scu.PINMUX_GPIO3_11 = (P7_3); /* GPIO3[11] */ + scu.PINMUX_GPIO3_12 = (P7_4); /* GPIO3[12] */ + scu.PINMUX_GPIO3_13 = (P7_5); /* GPIO3[13] */ + scu.PINMUX_GPIO3_14 = (P7_6); /* GPIO3[14] */ + scu.PINMUX_GPIO3_15 = (P7_7); /* GPIO3[15] */ +#endif + scu.PINMUX_ISP = (P2_7); /* GPIO0[7] */ scu.PINMUX_GP_CLKIN = (P4_7); diff --git a/firmware/common/platform_scu.h b/firmware/common/platform_scu.h index 2b4b42f0..2c5365e3 100644 --- a/firmware/common/platform_scu.h +++ b/firmware/common/platform_scu.h @@ -34,18 +34,17 @@ extern "C" { */ typedef struct { - /* LED PinMux */ + /* GPIO Output PinMux */ scu_grp_pin_t PINMUX_LED1; scu_grp_pin_t PINMUX_LED2; scu_grp_pin_t PINMUX_LED3; -#if defined(RAD1O) || defined(PRALINE) +#if defined(RAD1O) || defined(PRALINE) || defined(UNIVERSAL) scu_grp_pin_t PINMUX_LED4; #endif - /* Power Supply PinMux */ scu_grp_pin_t PINMUX_EN1V8; scu_grp_pin_t PINMUX_EN1V2; -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) scu_grp_pin_t PINMUX_EN3V3_AUX_N; scu_grp_pin_t PINMUX_EN3V3_OC_N; #endif @@ -53,7 +52,7 @@ typedef struct { /* GPIO Input PinMux */ scu_grp_pin_t PINMUX_BOOT0; scu_grp_pin_t PINMUX_BOOT1; -#if !defined(HACKRF_ONE) +#if !defined(HACKRF_ONE) || defined(UNIVERSAL) scu_grp_pin_t PINMUX_BOOT2; scu_grp_pin_t PINMUX_BOOT3; #endif @@ -76,16 +75,15 @@ typedef struct { scu_grp_pin_t SSP1_CS; /* CPLD JTAG interface */ -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) scu_grp_pin_t PINMUX_FPGA_CRESET; scu_grp_pin_t PINMUX_FPGA_CDONE; scu_grp_pin_t PINMUX_FPGA_SPI_CS; -#else +#endif scu_grp_pin_t PINMUX_CPLD_TDO; + scu_grp_pin_t PINMUX_CPLD_TCK; scu_grp_pin_t PINMUX_CPLD_TMS; scu_grp_pin_t PINMUX_CPLD_TDI; -#endif - scu_grp_pin_t PINMUX_CPLD_TCK; /* CPLD SGPIO interface */ scu_grp_pin_t PINMUX_SGPIO0; @@ -121,36 +119,45 @@ typedef struct { scu_grp_pin_t TRIGGER_EN; /* MAX283x GPIO (XCVR_CTL) PinMux */ - scu_grp_pin_t XCVR_RXHP; // RAD1O, PRALINE - scu_grp_pin_t XCVR_B6; // RAD1O - scu_grp_pin_t XCVR_B7; // RAD1O - scu_grp_pin_t XCVR_ENABLE; // PRALINE, HACKRF_ONE - scu_grp_pin_t XCVR_RXENABLE; // PRALINE, HACKRF_ONE - scu_grp_pin_t XCVR_CS; // PRALINE, HACKRF_ONE - scu_grp_pin_t XCVR_LD; // PRALINE - uint32_t XCVR_ENABLE_PINCFG; // PRALINE, HACKRF_ONE - uint32_t XCVR_RXENABLE_PINCFG; // PRALINE, HACKRF_ONE - uint32_t XCVR_CS_PINCFG; // PRALINE, HACKRF_ONE - uint32_t XCVR_RXHP_PINCFG; // PRALINE - uint32_t XCVR_LD_PINCFG; // PRALINE - scu_grp_pin_t XCVR_TXENABLE; // HACKRF_ONE - uint32_t XCVR_TXENABLE_PINCFG; // HACKRF_ONE + scu_grp_pin_t XCVR_ENABLE; + scu_grp_pin_t XCVR_RXENABLE; + scu_grp_pin_t XCVR_TXENABLE; + scu_grp_pin_t XCVR_CS; + uint32_t XCVR_ENABLE_PINCFG; + uint32_t XCVR_RXENABLE_PINCFG; + uint32_t XCVR_TXENABLE_PINCFG; + uint32_t XCVR_CS_PINCFG; +#if defined(PRALINE) || defined(UNIVERSAL) + scu_grp_pin_t XCVR_LD; + uint32_t XCVR_LD_PINCFG; + uint32_t XCVR_RXHP_PINCFG; +#endif +#if defined(RAD1O) || defined(PRALINE) || defined(UNIVERSAL) + scu_grp_pin_t XCVR_RXHP; +#endif +#if defined(RAD1O) + scu_grp_pin_t XCVR_B6; + scu_grp_pin_t XCVR_B7; +#endif /* MAX5864 SPI chip select (AD_CS) GPIO PinMux */ scu_grp_pin_t AD_CS; scu_grp_pin_t AD_CS_PINCFG; /* RFFC5071 GPIO serial interface PinMux */ +#if !defined(RAD1O) scu_grp_pin_t MIXER_ENX; scu_grp_pin_t MIXER_SCLK; scu_grp_pin_t MIXER_SDATA; scu_grp_pin_t MIXER_RESETX; uint32_t MIXER_SCLK_PINCFG; uint32_t MIXER_SDATA_PINCFG; -#if defined(PRALINE) +#endif +#if defined(PRALINE) || defined(UNIVERSAL) scu_grp_pin_t MIXER_LD; uint32_t MIXER_LD_PINCFG; -#elif defined(RAD1O) +#endif +#if defined(RAD1O) scu_grp_pin_t VCO_CE; scu_grp_pin_t VCO_SCLK; scu_grp_pin_t VCO_SDATA; @@ -166,9 +173,10 @@ typedef struct { #endif /* RF supply (VAA) control */ -#if defined(PRALINE) || defined(HACKRF_ONE) +#if defined(PRALINE) || defined(HACKRF_ONE) || defined(UNIVERSAL) scu_grp_pin_t NO_VAA_ENABLE; -#elif defined(RAD1O) +#endif +#if defined(RAD1O) scu_grp_pin_t VAA_ENABLE; #endif @@ -181,7 +189,7 @@ typedef struct { scu_grp_pin_t FLASH_WP; /* RF switch control */ -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) scu_grp_pin_t TX_EN; scu_grp_pin_t MIX_EN_N; scu_grp_pin_t MIX_EN_N_R1_0; @@ -189,7 +197,8 @@ typedef struct { scu_grp_pin_t RF_AMP_EN; scu_grp_pin_t ANT_BIAS_EN_N; scu_grp_pin_t ANT_BIAS_OC_N; -#elif defined(HACKRF_ONE) +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) scu_grp_pin_t HP; scu_grp_pin_t LP; scu_grp_pin_t TX_MIX_BP; @@ -203,7 +212,8 @@ typedef struct { scu_grp_pin_t AMP_BYPASS; scu_grp_pin_t RX_AMP; scu_grp_pin_t NO_RX_AMP_PWR; -#elif defined(RAD1O) +#endif +#if defined(RAD1O) scu_grp_pin_t BY_AMP; scu_grp_pin_t BY_AMP_N; scu_grp_pin_t TX_RX; @@ -217,7 +227,7 @@ typedef struct { #endif /* Praline */ -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) scu_grp_pin_t P2_CTRL0; scu_grp_pin_t P2_CTRL1; scu_grp_pin_t P1_CTRL0; @@ -242,7 +252,7 @@ typedef struct { #endif /* HackRF One r9 */ -#if defined(HACKRF_ONE) +#if defined(HACKRF_ONE) || defined(UNIVERSAL) scu_grp_pin_t H1R9_CLKIN_EN; scu_grp_pin_t H1R9_CLKOUT_EN; scu_grp_pin_t H1R9_MCU_CLK_EN; @@ -262,9 +272,7 @@ typedef struct { scu_grp_pin_t PINMUX_PP_D5; scu_grp_pin_t PINMUX_PP_D6; scu_grp_pin_t PINMUX_PP_D7; - /* TODO add other Pins */ - scu_grp_pin_t PINMUX_GPIO3_8; scu_grp_pin_t PINMUX_GPIO3_9; scu_grp_pin_t PINMUX_GPIO3_10; From d0ac3183f597974b13daf2c5e81dfb9520c5cf25 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 3 Apr 2026 21:37:14 +0100 Subject: [PATCH 045/173] Universalize: cpld_jtag.{c,h} Co-authored-by: Antoine van Gelder --- firmware/common/cpld_jtag.c | 85 ++++++++++++++++++++++++++----------- firmware/common/cpld_jtag.h | 4 +- 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/firmware/common/cpld_jtag.c b/firmware/common/cpld_jtag.c index 4ef9ce5d..21991db0 100644 --- a/firmware/common/cpld_jtag.c +++ b/firmware/common/cpld_jtag.c @@ -19,16 +19,17 @@ * Boston, MA 02110-1301, USA. */ -#if !defined(PRALINE) +#if !defined(PRALINE) || defined(UNIVERSAL) #include #endif #include "cpld_jtag.h" -#if !defined(PRALINE) +#include "platform_detect.h" +#if !defined(PRALINE) || defined(UNIVERSAL) #include "xapp058/micro.h" #endif -#ifndef PRALINE +#if !defined(PRALINE) || defined(UNIVERSAL) static refill_buffer_cb refill_buffer; static uint32_t xsvf_buffer_len, xsvf_pos; static unsigned char* xsvf_buffer; @@ -38,52 +39,86 @@ void cpld_jtag_take(jtag_t* const jtag) { const jtag_gpio_t* const gpio = jtag->gpio; + board_id_t board_id = detected_platform(); + /* Set initial GPIO state to the voltages of the internal or external pull-ups/downs, * to avoid any glitches. */ -#if (defined HACKRF_ONE || defined PRALINE) - gpio_set(gpio->gpio_pp_tms); + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + gpio_set(gpio->gpio_pp_tms); #endif + break; + default: + break; + } gpio_clear(gpio->gpio_tck); -#ifndef PRALINE - gpio_set(gpio->gpio_tms); - gpio_set(gpio->gpio_tdi); + if (board_id != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + gpio_set(gpio->gpio_tms); + gpio_set(gpio->gpio_tdi); #endif + } -#if (defined HACKRF_ONE || defined PRALINE) - /* Do not drive PortaPack-specific TMS pin initially, just to be cautious. */ - gpio_input(gpio->gpio_pp_tms); - gpio_input(gpio->gpio_pp_tdo); + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + /* Do not drive PortaPack-specific TMS pin initially, just to be cautious. */ + gpio_input(gpio->gpio_pp_tms); + gpio_input(gpio->gpio_pp_tdo); #endif + break; + default: + break; + } gpio_output(gpio->gpio_tck); -#ifndef PRALINE - gpio_output(gpio->gpio_tms); - gpio_output(gpio->gpio_tdi); - gpio_input(gpio->gpio_tdo); + if (board_id != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + gpio_output(gpio->gpio_tms); + gpio_output(gpio->gpio_tdi); + gpio_input(gpio->gpio_tdo); #endif + } } void cpld_jtag_release(jtag_t* const jtag) { const jtag_gpio_t* const gpio = jtag->gpio; + board_id_t board_id = detected_platform(); + /* Make all pins inputs when JTAG interface not active. * Let the pull-ups/downs do the work. */ -#if (defined HACKRF_ONE || defined PRALINE) - /* Do not drive PortaPack-specific pins, initially, just to be cautious. */ - gpio_input(gpio->gpio_pp_tms); - gpio_input(gpio->gpio_pp_tdo); + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + /* Do not drive PortaPack-specific pins, initially, just to be cautious. */ + gpio_input(gpio->gpio_pp_tms); + gpio_input(gpio->gpio_pp_tdo); #endif + break; + default: + break; + } gpio_input(gpio->gpio_tck); -#ifndef PRALINE - gpio_input(gpio->gpio_tms); - gpio_input(gpio->gpio_tdi); - gpio_input(gpio->gpio_tdo); + if (board_id != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + gpio_input(gpio->gpio_tms); + gpio_input(gpio->gpio_tdi); + gpio_input(gpio->gpio_tdo); #endif + } } -#ifndef PRALINE +#if !defined(PRALINE) || defined(UNIVERSAL) /* return 0 if success else return error code see xsvfExecute() */ int cpld_jtag_program( jtag_t* const jtag, diff --git a/firmware/common/cpld_jtag.h b/firmware/common/cpld_jtag.h index 60036759..939e5d94 100644 --- a/firmware/common/cpld_jtag.h +++ b/firmware/common/cpld_jtag.h @@ -27,12 +27,12 @@ typedef struct { gpio_t gpio_tck; -#ifndef PRALINE +#if !defined(PRALINE) || defined(UNIVERSAL) gpio_t gpio_tms; gpio_t gpio_tdi; gpio_t gpio_tdo; #endif -#if (defined HACKRF_ONE || defined PRALINE) +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) gpio_t gpio_pp_tms; gpio_t gpio_pp_tdo; #endif From 8d4570d2cbc6e820da9381865402414713e08f53 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 3 Apr 2026 21:39:02 +0100 Subject: [PATCH 046/173] Universalize: max283x.{c,h} Co-authored-by: Antoine van Gelder --- firmware/common/max283x.c | 270 ++++++++++++++++++++++---------------- firmware/common/max283x.h | 21 ++- firmware/common/rf_path.c | 8 +- 3 files changed, 181 insertions(+), 118 deletions(-) diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index c0e912e5..54b596bc 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -29,31 +29,36 @@ #include "fixed_point.h" #include "platform_gpio.h" #include "spi_bus.h" -#if defined(PRALINE) - #include "max2831.h" + +#if defined(PRALINE) || defined(UNIVERSAL) #include "max2831_target.h" -#else - #include "max2837.h" +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) #include "max2837_target.h" - #include "max2839.h" +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) #include "max2839_target.h" #endif extern spi_bus_t spi_bus_ssp1; -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) max2831_driver_t max2831 = { .bus = &spi_bus_ssp1, .target_init = max2831_target_init, .set_mode = max2831_target_set_mode, }; -#else +#endif + +#if !defined(PRALINE) || defined(UNIVERSAL) max2837_driver_t max2837 = { .bus = &spi_bus_ssp1, .target_init = max2837_target_init, .set_mode = max2837_target_set_mode, }; +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) max2839_driver_t max2839 = { .bus = &spi_bus_ssp1, .target_init = max2839_target_init, @@ -69,36 +74,43 @@ void max283x_setup(max283x_driver_t* const drv, max283x_variant_t type) const platform_gpio_t* gpio = platform_gpio(); /* MAX283x GPIO PinMux */ -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) max2831.gpio_enable = gpio->max283x_enable; max2831.gpio_rxtx = gpio->max283x_rx_enable; max2831.gpio_rxhp = gpio->max2831_rxhp; max2831.gpio_ld = gpio->max2831_ld; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) max2837.gpio_enable = gpio->max283x_enable; max2837.gpio_rx_enable = gpio->max283x_rx_enable; max2837.gpio_tx_enable = gpio->max283x_tx_enable; +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) max2839.gpio_enable = gpio->max283x_enable; max2839.gpio_rxtx = gpio->max283x_rx_enable; #endif - switch (type) { -#ifdef PRALINE + switch (drv->type) { +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: memcpy(&drv->drv.max2831, &max2831, sizeof(max2831)); max2831_setup(&drv->drv.max2831); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: memcpy(&drv->drv.max2837, &max2837, sizeof(max2837)); max2837_setup(&drv->drv.max2837); break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: memcpy(&drv->drv.max2839, &max2839, sizeof(max2839)); max2839_setup(&drv->drv.max2839); break; #endif + default: + break; } } @@ -106,44 +118,42 @@ void max283x_setup(max283x_driver_t* const drv, max283x_variant_t type) uint16_t max283x_num_regs(max283x_driver_t* const drv) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: return MAX2831_NUM_REGS; - break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: return MAX2837_NUM_REGS; - break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: return MAX2839_NUM_REGS; - break; #endif + default: + return 0; } - - return 0; } /* Returns the maximum data register value supported by the driver. */ uint16_t max283x_data_regs_max_value(max283x_driver_t* const drv) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: return MAX2831_DATA_REGS_MAX_VALUE; - break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: return MAX2837_DATA_REGS_MAX_VALUE; - break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: return MAX2839_DATA_REGS_MAX_VALUE; - break; #endif + default: + return 0; } - - return 0; } /* Read a register via SPI. Save a copy to memory and return @@ -151,22 +161,21 @@ uint16_t max283x_data_regs_max_value(max283x_driver_t* const drv) uint16_t max283x_reg_read(max283x_driver_t* const drv, uint8_t r) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: return max2831_reg_read(&drv->drv.max2831, r); - break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: return max2837_reg_read(&drv->drv.max2837, r); - break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: return max2839_reg_read(&drv->drv.max2839, r); - break; #endif + default: + return 0; } - - return 0; } /* Write value to register via SPI and save a copy to memory. Mark @@ -174,19 +183,23 @@ uint16_t max283x_reg_read(max283x_driver_t* const drv, uint8_t r) void max283x_reg_write(max283x_driver_t* const drv, uint8_t r, uint16_t v) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: max2831_reg_write(&drv->drv.max2831, r, v); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: max2837_reg_write(&drv->drv.max2837, r, v); break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: max2839_reg_write(&drv->drv.max2839, r, v); break; #endif + default: + break; } } @@ -196,97 +209,113 @@ void max283x_reg_write(max283x_driver_t* const drv, uint8_t r, uint16_t v) void max283x_regs_commit(max283x_driver_t* const drv) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: max2831_regs_commit(&drv->drv.max2831); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: max2837_regs_commit(&drv->drv.max2837); break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: max2839_regs_commit(&drv->drv.max2839); break; #endif + default: + break; } } void max283x_set_mode(max283x_driver_t* const drv, const max283x_mode_t new_mode) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: max2831_set_mode(&drv->drv.max2831, (max2831_mode_t) new_mode); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: max2837_set_mode(&drv->drv.max2837, (max2837_mode_t) new_mode); break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: max2839_set_mode(&drv->drv.max2839, (max2839_mode_t) new_mode); break; #endif + default: + break; } } max283x_mode_t max283x_mode(max283x_driver_t* const drv) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: return (max283x_mode_t) max2831_mode(&drv->drv.max2831); - break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: return (max283x_mode_t) max2837_mode(&drv->drv.max2837); - break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: return (max283x_mode_t) max2839_mode(&drv->drv.max2839); - break; #endif + default: + return 0; } - return 0; } /* Turn on/off all chip functions. Does not control oscillator and CLKOUT */ void max283x_start(max283x_driver_t* const drv) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: max2831_start(&drv->drv.max2831); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: max2837_start(&drv->drv.max2837); break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: max2839_start(&drv->drv.max2839); break; #endif + default: + break; } } void max283x_stop(max283x_driver_t* const drv) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: max2831_stop(&drv->drv.max2831); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: max2837_stop(&drv->drv.max2837); break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: max2839_stop(&drv->drv.max2839); break; #endif + default: + break; } } @@ -297,19 +326,23 @@ fp_40_24_t max283x_set_frequency( bool program) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: return max2831_set_frequency(&drv->drv.max2831, freq, program); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: return max2837_set_frequency(&drv->drv.max2837, freq, program); break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: return max2839_set_frequency(&drv->drv.max2839, freq, program); break; #endif + default: + break; } return 0; } @@ -324,189 +357,206 @@ uint32_t max283x_set_lpf_bandwidth( #endif switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: return max2831_set_lpf_bandwidth( &drv->drv.max2831, (max2831_mode_t) mode, bandwidth_hz); - break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: return max2837_set_lpf_bandwidth(&drv->drv.max2837, bandwidth_hz); - break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: return max2839_set_lpf_bandwidth(&drv->drv.max2839, bandwidth_hz); - break; #endif + default: + return 0; } - - return 0; } bool max283x_set_lna_gain(max283x_driver_t* const drv, const uint32_t gain_db) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: return max2831_set_lna_gain(&drv->drv.max2831, gain_db); - break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: return max2837_set_lna_gain(&drv->drv.max2837, gain_db); - break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: return max2839_set_lna_gain(&drv->drv.max2839, gain_db); - break; #endif + default: + return false; } - - return false; } bool max283x_set_vga_gain(max283x_driver_t* const drv, const uint32_t gain_db) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: return max2831_set_vga_gain(&drv->drv.max2831, gain_db); - break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: return max2837_set_vga_gain(&drv->drv.max2837, gain_db); - break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: return max2839_set_vga_gain(&drv->drv.max2839, gain_db); - break; #endif + default: + return false; } - - return false; } bool max283x_set_txvga_gain(max283x_driver_t* const drv, const uint32_t gain_db) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: return max2831_set_txvga_gain(&drv->drv.max2831, gain_db); - break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: return max2837_set_txvga_gain(&drv->drv.max2837, gain_db); - break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: return max2839_set_txvga_gain(&drv->drv.max2839, gain_db); - break; #endif + default: + return false; } - - return false; } void max283x_tx(max283x_driver_t* const drv) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: max2831_tx(&drv->drv.max2831); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: max2837_tx(&drv->drv.max2837); break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: max2839_tx(&drv->drv.max2839); break; #endif + default: + break; } } void max283x_rx(max283x_driver_t* const drv) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: max2831_rx(&drv->drv.max2831); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: max2837_rx(&drv->drv.max2837); break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: max2839_rx(&drv->drv.max2839); break; #endif + default: + break; } } +/* Set MAX2831 receiver high-pass filter corner frequency in Hz */ void max283x_set_rx_hpf_frequency( max283x_driver_t* const drv, const max283x_rx_hpf_freq_t freq) { -#ifndef PRALINE +#if !defined(PRALINE) (void) freq; #endif switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: max2831_set_rx_hpf_frequency( &drv->drv.max2831, (max2831_rx_hpf_freq_t) freq); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: // unsupported break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: // unsupported break; #endif + default: + break; } } void max283x_tx_calibration(max283x_driver_t* const drv) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: max2831_tx_calibration(&drv->drv.max2831); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: // unsupported - use max283x_set_mode instead break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: // unsupported - use max283x_set_mode instead break; #endif + default: + break; } } void max283x_rx_calibration(max283x_driver_t* const drv) { switch (drv->type) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case MAX2831_VARIANT: max2831_rx_calibration(&drv->drv.max2831); break; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) case MAX2837_VARIANT: // unsupported - use max283x_set_mode instead break; - +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) case MAX2839_VARIANT: // unsupported - use max283x_set_mode instead break; #endif + default: + break; } } diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h index db5a93f8..87da59b4 100644 --- a/firmware/common/max283x.h +++ b/firmware/common/max283x.h @@ -27,10 +27,13 @@ #include #include "fixed_point.h" -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) #include "max2831.h" -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) #include "max2837.h" +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) #include "max2839.h" #endif @@ -52,10 +55,13 @@ typedef enum { } max283x_rx_hpf_freq_t; typedef enum { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) MAX2831_VARIANT, -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) MAX2837_VARIANT, +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) MAX2839_VARIANT, #endif } max283x_variant_t; @@ -64,10 +70,13 @@ typedef struct { max283x_variant_t type; union { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) max2831_driver_t max2831; -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) max2837_driver_t max2837; +#endif +#if defined(HACKRF_ONE) || defined(UNIVERSAL) max2839_driver_t max2839; #endif } drv; diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index 4407960a..bce921ea 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -31,7 +31,9 @@ #include "max283x.h" #include "max5864.h" #include "mixer.h" -#include "platform_detect.h" +#if defined(HACKRF_ONE) || defined(PRALINE) + #include "platform_detect.h" +#endif #if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE) #include "platform_scu.h" #endif @@ -441,12 +443,14 @@ void rf_path_init(rf_path_t* const rf_path) ssp1_set_mode_max283x(); #ifdef PRALINE max283x_setup(&max283x, MAX2831_VARIANT); -#else +#elif defined(HACKRF_ONE) if (detected_platform() == BOARD_ID_HACKRF1_R9) { max283x_setup(&max283x, MAX2839_VARIANT); } else { max283x_setup(&max283x, MAX2837_VARIANT); } +#else + max283x_setup(&max283x, MAX2837_VARIANT); #endif max283x_start(&max283x); From c913e81ed619703a62d06de302166c2ec9fbce2d Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 3 Apr 2026 22:00:17 +0100 Subject: [PATCH 047/173] Universalize: rffc5071.{c,h} Co-authored-by: Antoine van Gelder --- firmware/common/rffc5071.c | 59 +++++++++++++++++++++++++++----------- firmware/common/rffc5071.h | 4 +-- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/firmware/common/rffc5071.c b/firmware/common/rffc5071.c index ffe958c1..89befe98 100644 --- a/firmware/common/rffc5071.c +++ b/firmware/common/rffc5071.c @@ -35,16 +35,17 @@ #include #include -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) #include #endif #include "delay.h" #include "fixed_point.h" +#include "platform_detect.h" #include "rffc5071.h" #include "rffc5071_regs.def" // private register def macros #include "selftest.h" -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) #include "platform_scu.h" #endif @@ -109,15 +110,19 @@ void rffc5071_init(rffc5071_driver_t* const drv) */ void rffc5071_setup(rffc5071_driver_t* const drv) { + board_id_t board_id = detected_platform(); + gpio_set(drv->gpio_reset); gpio_output(drv->gpio_reset); -#ifdef PRALINE - /* Configure mixer PLL lock detect pin */ - const platform_scu_t* scu = platform_scu(); - scu_pinmux(scu->MIXER_LD, scu->MIXER_LD_PINCFG); - gpio_input(drv->gpio_ld); + if (board_id == BOARD_ID_PRALINE) { +#if defined(PRALINE) || defined(UNIVERSAL) + /* Configure mixer PLL lock detect pin */ + const platform_scu_t* scu = platform_scu(); + scu_pinmux(scu->MIXER_LD, scu->MIXER_LD_PINCFG); + gpio_input(drv->gpio_ld); #endif + } rffc5071_init(drv); @@ -135,10 +140,16 @@ void rffc5071_setup(rffc5071_driver_t* const drv) set_RFFC5071_FULLD(drv, 0); set_RFFC5071_MODE(drv, 1); -#if defined(PRALINE) || defined(HACKRF_ONE) - /* Enable GPO Lock output signal */ - set_RFFC5071_LOCK(drv, 1); -#endif + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: + /* Enable GPO Lock output signal */ + set_RFFC5071_LOCK(drv, 1); + break; + default: + break; + } /* Enable reference oscillator standby */ set_RFFC5071_REFST(drv, 1); @@ -181,13 +192,17 @@ void rffc5071_lock_test(rffc5071_driver_t* const drv) bool rffc5071_check_lock(rffc5071_driver_t* const drv) { -#ifdef PRALINE - return gpio_read(drv->gpio_ld); + if (detected_platform() == BOARD_ID_PRALINE) { +#if defined(PRALINE) || defined(UNIVERSAL) + return gpio_read(drv->gpio_ld); #else - set_RFFC5071_READSEL(drv, 0b0001); - rffc5071_regs_commit(drv); - return !!(rffc5071_reg_read(drv, RFFC5071_READBACK_REG) & 0x8000); + return false; #endif + } else { + set_RFFC5071_READSEL(drv, 0b0001); + rffc5071_regs_commit(drv); + return !!(rffc5071_reg_read(drv, RFFC5071_READBACK_REG) & 0x8000); + } } static uint16_t rffc5071_spi_read(rffc5071_driver_t* const drv, uint8_t r) @@ -360,9 +375,17 @@ void rffc5071_set_gpo(rffc5071_driver_t* const drv, uint8_t gpo) rffc5071_regs_commit(drv); } -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state) { + // This is only supported on Praline hardware. + // + // For all other boards we'll just return true to avoid a situation where a + // a caller is waiting for a lock signal that will never be detected. + if (detected_platform() != BOARD_ID_PRALINE) { + return true; + } + // The RFFC5072 can be configured to output PLL lock status on // GPO4. The lock detect signal is produced by a window detector // on the VCO tuning voltage. It goes high to show PLL lock when @@ -428,7 +451,9 @@ bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state) uint8_t rsm_state = (rb >> 11) & 0b11111; // get gpo4 lock detect signal + #if defined(PRALINE) || defined(UNIVERSAL) bool gpo4_ld = gpio_read(drv->gpio_ld); + #endif // parse state switch (rsm_state) { diff --git a/firmware/common/rffc5071.h b/firmware/common/rffc5071.h index 41e0509d..6a0c4ce2 100644 --- a/firmware/common/rffc5071.h +++ b/firmware/common/rffc5071.h @@ -35,7 +35,7 @@ typedef struct { spi_bus_t* const bus; gpio_t gpio_reset; -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) gpio_t gpio_ld; #endif uint16_t regs[RFFC5071_NUM_REGS]; @@ -70,7 +70,7 @@ extern void rffc5071_enable(rffc5071_driver_t* const drv); extern void rffc5071_disable(rffc5071_driver_t* const drv); extern void rffc5071_set_gpo(rffc5071_driver_t* const drv, uint8_t); -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) extern bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state); #endif extern bool rffc5071_check_lock(rffc5071_driver_t* const drv); From cfd729597c883a2ec5917852c33bf7b2df5d4e35 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 3 Apr 2026 22:04:35 +0100 Subject: [PATCH 048/173] Universalize: si5351.c Co-authored-by: Antoine van Gelder --- firmware/common/si5351c.c | 119 ++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index ceb9f4a9..b50aad81 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -23,7 +23,7 @@ #include #include -#if defined(HACKRF_ONE) +#if defined(HACKRF_ONE) || defined(UNIVERSAL) #include #endif @@ -32,7 +32,7 @@ #include "platform_detect.h" #include "selftest.h" #include "si5351c.h" -#if defined(HACKRF_ONE) +#if defined(HACKRF_ONE) || defined(UNIVERSAL) #include "gpio.h" #include "platform_gpio.h" #include "platform_scu.h" @@ -235,6 +235,7 @@ void si5351c_configure_multisynth( void si5351c_configure_clock_control(si5351c_driver_t* const drv) { uint8_t clkout_ctrl; + board_id_t board_id = detected_platform(); if (clkout_enabled) { clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | @@ -269,7 +270,8 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE /* not connected, but: PLL B int mode */ }; - if (detected_platform() == BOARD_ID_HACKRF1_R9) { + if (board_id == BOARD_ID_HACKRF1_R9) { +#if defined(HACKRF_ONE) || defined(UNIVERSAL) data[1] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA); @@ -280,37 +282,41 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) data[4] = SI5351C_CLK_POWERDOWN; data[5] = SI5351C_CLK_POWERDOWN; data[6] = SI5351C_CLK_POWERDOWN; +#endif } -#ifdef PRALINE - /* CLK0: AFE_CLK */ - data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); - /* CLK1: SCT_CLK and FPGA_CLK */ - data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA); - /* CLK3: CLKOUT */ - clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA); - /* CLK4: XCVR_CLK */ - data[5] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA) | SI5351C_CLK_INV; - data[6] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); - if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { - /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ - data[3] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | + if (board_id == BOARD_ID_PRALINE) { +#if defined(PRALINE) || defined(UNIVERSAL) + /* CLK0: AFE_CLK */ + data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | + SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | + SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); + /* CLK1: SCT_CLK and FPGA_CLK */ + data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA); - } else { - /* CLK2: MCU_CLK */ - data[3] = SI5351C_CLK_POWERDOWN; - } + /* CLK3: CLKOUT */ + clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | + SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | + SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA); + /* CLK4: XCVR_CLK */ + data[5] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | + SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | + SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA) | SI5351C_CLK_INV; + data[6] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | + SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | + SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); + if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { + /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ + data[3] = SI5351C_CLK_FRAC_MODE | + SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | + SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | + SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA); + } else { + /* CLK2: MCU_CLK */ + data[3] = SI5351C_CLK_POWERDOWN; + } #endif + } si5351c_write(drv, data, sizeof(data)); } @@ -325,30 +331,34 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) /* 7: Clock to CPU is deactivated as it is not used and creates noise */ /* 3: External clock output is deactivated by default */ -#ifndef PRALINE - uint8_t value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | - SI5351C_CLK_ENABLE(2) | SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) | - SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); -#else - uint8_t value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | - SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) | - SI5351C_CLK_DISABLE(7); - if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { - /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ - value |= SI5351C_CLK_ENABLE(2); - } else { - value |= SI5351C_CLK_DISABLE(2); - } -#endif + board_id_t board_id = detected_platform(); uint8_t clkout = 3; + uint8_t value; - /* HackRF One r9 has only three clock generator outputs. */ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - clkout = 2; + if (board_id == BOARD_ID_PRALINE) { value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | - SI5351C_CLK_DISABLE(3) | SI5351C_CLK_DISABLE(4) | - SI5351C_CLK_DISABLE(5) | SI5351C_CLK_DISABLE(6) | + SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) | + SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); + if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { + /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ + value |= SI5351C_CLK_ENABLE(2); + } else { + value |= SI5351C_CLK_DISABLE(2); + } + } else { + value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | + SI5351C_CLK_ENABLE(2) | SI5351C_CLK_ENABLE(4) | + SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); + + /* HackRF One r9 has only three clock generator outputs. */ + if (board_id == BOARD_ID_HACKRF1_R9) { + clkout = 2; + value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | + SI5351C_CLK_DISABLE(3) | SI5351C_CLK_DISABLE(4) | + SI5351C_CLK_DISABLE(5) | SI5351C_CLK_DISABLE(6) | + SI5351C_CLK_DISABLE(7); + } } value |= (clkout_enabled) ? SI5351C_CLK_ENABLE(clkout) : @@ -357,16 +367,16 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) si5351c_write(drv, data, sizeof(data)); outputs_disabled = value; -#if defined(HACKRF_ONE) if (detected_platform() == BOARD_ID_HACKRF1_R9) { +#if defined(HACKRF_ONE) || defined(UNIVERSAL) const platform_gpio_t* gpio = platform_gpio(); if (clkout_enabled) { gpio_set(gpio->h1r9_clkout_en); } else { gpio_clear(gpio->h1r9_clkout_en); } - } #endif + } } void si5351c_set_int_mode( @@ -466,8 +476,8 @@ void si5351c_init(si5351c_driver_t* const drv) selftest.report.pass = false; } -#if defined(HACKRF_ONE) if (detected_platform() == BOARD_ID_HACKRF1_R9) { +#if defined(HACKRF_ONE) || defined(UNIVERSAL) const platform_gpio_t* gpio = platform_gpio(); const platform_scu_t* scu = platform_scu(); @@ -485,9 +495,8 @@ void si5351c_init(si5351c_driver_t* const drv) scu_pinmux(scu->H1R9_MCU_CLK_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); gpio_clear(gpio->h1r9_mcu_clk_en); gpio_output(gpio->h1r9_mcu_clk_en); - } #endif - (void) drv; + } } /* From cdc17d5e6791b918e4f0afa0f0eaa44334e9bbf1 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 3 Apr 2026 22:05:13 +0100 Subject: [PATCH 049/173] Universalize: rf_path.{c,h} Co-authored-by: Antoine van Gelder --- firmware/common/rf_path.c | 354 ++++++++++++++++++++++---------------- firmware/common/rf_path.h | 77 +++++---- 2 files changed, 243 insertions(+), 188 deletions(-) diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index bce921ea..a361f442 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -23,7 +23,7 @@ #include "rf_path.h" -#if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE) +#if !defined(JAWBREAKER) #include #endif @@ -31,10 +31,8 @@ #include "max283x.h" #include "max5864.h" #include "mixer.h" -#if defined(HACKRF_ONE) || defined(PRALINE) - #include "platform_detect.h" -#endif -#if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE) +#include "platform_detect.h" +#if !defined(JAWBREAKER) #include "platform_scu.h" #endif @@ -94,17 +92,18 @@ #define SWITCHCTRL_ANT_PWR (1 << 6) /* turn on antenna port power */ -#ifdef HACKRF_ONE - +#if defined(HACKRF_ONE) || defined(UNIVERSAL) static void switchctrl_set_hackrf_one(rf_path_t* const rf_path, uint8_t ctrl) { + board_id_t board_id = detected_platform(); + if (ctrl & SWITCHCTRL_TX) { - if (detected_platform() != BOARD_ID_HACKRF1_R9) { + if (board_id != BOARD_ID_HACKRF1_R9) { gpio_set(rf_path->gpio_tx); } gpio_clear(rf_path->gpio_rx); } else { - if (detected_platform() != BOARD_ID_HACKRF1_R9) { + if (board_id != BOARD_ID_HACKRF1_R9) { gpio_clear(rf_path->gpio_tx); } gpio_set(rf_path->gpio_rx); @@ -167,7 +166,7 @@ static void switchctrl_set_hackrf_one(rf_path_t* const rf_path, uint8_t ctrl) gpio_set(rf_path->gpio_no_rx_amp_pwr); } - if (detected_platform() == BOARD_ID_HACKRF1_R9) { + if (board_id == BOARD_ID_HACKRF1_R9) { if (ctrl & SWITCHCTRL_ANT_PWR) { gpio_clear(rf_path->gpio_h1r9_no_ant_pwr); } else { @@ -187,7 +186,7 @@ static void switchctrl_set_hackrf_one(rf_path_t* const rf_path, uint8_t ctrl) } #endif -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) static void switchctrl_set_praline(rf_path_t* const rf_path, uint8_t ctrl) { if (ctrl & SWITCHCTRL_TX) { @@ -228,7 +227,7 @@ static void switchctrl_set_praline(rf_path_t* const rf_path, uint8_t ctrl) } #endif -#ifdef RAD1O +#if defined(RAD1O) static void switchctrl_set_rad1o(rf_path_t* const rf_path, uint8_t ctrl) { if (ctrl & SWITCHCTRL_TX) { @@ -295,169 +294,220 @@ static void switchctrl_set_rad1o(rf_path_t* const rf_path, uint8_t ctrl) static void switchctrl_set(rf_path_t* const rf_path, const uint8_t gpo) { -#ifdef JAWBREAKER - (void) rf_path; /* silence unused param warning */ - mixer_set_gpo(&mixer, gpo); -#elif HACKRF_ONE - switchctrl_set_hackrf_one(rf_path, gpo); -#elif PRALINE - switchctrl_set_praline(rf_path, gpo); -#elif RAD1O - switchctrl_set_rad1o(rf_path, gpo); -#else - (void) gpo; +#if defined(JAWBREAKER) + (void) rf_path; #endif + + switch (detected_platform()) { + case BOARD_ID_JAWBREAKER: +#if defined(JAWBREAKER) + mixer_set_gpo(&mixer, gpo); +#endif + break; + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + switchctrl_set_hackrf_one(rf_path, gpo); +#endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + switchctrl_set_praline(rf_path, gpo); +#endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + switchctrl_set_rad1o(rf_path, gpo); +#endif + break; + default: + break; + } } void rf_path_pin_setup(rf_path_t* const rf_path) { +#if defined(JAWBREAKER) + (void) rf_path; +#endif + + board_id_t board_id = detected_platform(); #if !defined(JAWBREAKER) const platform_scu_t* scu = platform_scu(); #endif -#ifdef HACKRF_ONE - /* Configure RF switch control signals */ - // clang-format off - scu_pinmux(scu->HP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->LP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->TX_MIX_BP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->NO_MIX_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->RX_MIX_BP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->TX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->MIX_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu_pinmux(scu->NO_TX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->AMP_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->RX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->NO_RX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - // clang-format on - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - scu_pinmux(scu->H1R9_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->H1R9_NO_ANT_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - gpio_clear(rf_path->gpio_h1r9_no_ant_pwr); - gpio_output(rf_path->gpio_h1r9_no_ant_pwr); - scu_pinmux(scu->H1R9_NO_VAA_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - } else { - scu_pinmux(scu->TX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu_pinmux(scu->RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - gpio_output(rf_path->gpio_tx); - scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - } + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + /* Configure RF switch control signals */ + // clang-format off + scu_pinmux(scu->HP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->LP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->TX_MIX_BP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->NO_MIX_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->RX_MIX_BP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->TX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->MIX_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu_pinmux(scu->NO_TX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->AMP_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->RX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->NO_RX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + // clang-format on + if (board_id == BOARD_ID_HACKRF1_R9) { + scu_pinmux(scu->H1R9_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux( + scu->H1R9_NO_ANT_PWR, + SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + gpio_clear(rf_path->gpio_h1r9_no_ant_pwr); + gpio_output(rf_path->gpio_h1r9_no_ant_pwr); + scu_pinmux( + scu->H1R9_NO_VAA_EN, + SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + } else { + scu_pinmux(scu->TX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu_pinmux(scu->RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + gpio_output(rf_path->gpio_tx); + scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + } - /* - * Safe (initial) switch settings turn off both amplifiers and antenna port - * power and enable both amp bypass and mixer bypass. - */ - switchctrl_set(rf_path, SWITCHCTRL_SAFE); + /* + * Safe (initial) switch settings turn off both amplifiers and antenna port + * power and enable both amp bypass and mixer bypass. + */ + switchctrl_set(rf_path, SWITCHCTRL_SAFE); - /* Configure RF switch control signals as outputs */ - gpio_output(rf_path->gpio_amp_bypass); - gpio_output(rf_path->gpio_no_mix_bypass); - gpio_output(rf_path->gpio_rx_amp); - gpio_output(rf_path->gpio_no_rx_amp_pwr); - gpio_output(rf_path->gpio_hp); - gpio_output(rf_path->gpio_lp); - gpio_output(rf_path->gpio_tx_mix_bp); - gpio_output(rf_path->gpio_rx_mix_bp); - gpio_output(rf_path->gpio_tx_amp); - gpio_output(rf_path->gpio_no_tx_amp_pwr); - gpio_output(rf_path->gpio_mix_bypass); - gpio_output(rf_path->gpio_rx); -#elif RAD1O - /* Configure RF switch control signals */ - // clang-format off - scu_pinmux(scu->BY_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->BY_AMP_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu_pinmux(scu->TX_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->TX_RX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->BY_MIX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->BY_MIX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->LOW_HIGH_FILT, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->LOW_HIGH_FILT_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->TX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->RX_LNA, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - scu_pinmux(scu->MIXER_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - // clang-format on - - /* Configure RF power supply (VAA) switch */ - scu_pinmux(scu->VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - - /* - * Safe (initial) switch settings turn off both amplifiers and antenna port - * power and enable both amp bypass and mixer bypass. - */ - switchctrl_set(rf_path, SWITCHCTRL_SAFE); - - /* Configure RF switch control signals as outputs */ - gpio_output(rf_path->gpio_tx_rx_n); - gpio_output(rf_path->gpio_tx_rx); - gpio_output(rf_path->gpio_by_mix); - gpio_output(rf_path->gpio_by_mix_n); - gpio_output(rf_path->gpio_by_amp); - gpio_output(rf_path->gpio_by_amp_n); - gpio_output(rf_path->gpio_mixer_en); - gpio_output(rf_path->gpio_low_high_filt); - gpio_output(rf_path->gpio_low_high_filt_n); - gpio_output(rf_path->gpio_tx_amp); - gpio_output(rf_path->gpio_rx_lna); -#elif PRALINE - /* Configure RF switch control signals */ - scu_pinmux(scu->TX_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - board_rev_t rev = detected_revision(); - if ((rev == BOARD_REV_PRALINE_R1_0) || (rev == BOARD_REV_GSG_PRALINE_R1_0)) { - scu_pinmux(scu->MIX_EN_N_R1_0, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - } else { - scu_pinmux(scu->MIX_EN_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - } - scu_pinmux(scu->LPF_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->RF_AMP_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - - /* Configure antenna port power control signal */ - scu_pinmux(scu->ANT_BIAS_EN_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - - /* Configure RF power supply (VAA) switch */ - scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - - /* - * Safe (initial) switch settings turn off both amplifiers and antenna port - * power and enable both amp bypass and mixer bypass. - */ - switchctrl_set(rf_path, SWITCHCTRL_SAFE); - - /* Configure RF switch control signals as outputs */ - gpio_output(rf_path->gpio_ant_bias_en_n); - gpio_output(rf_path->gpio_tx_en); - gpio_output(rf_path->gpio_mix_en_n); - gpio_output(rf_path->gpio_lpf_en); - gpio_output(rf_path->gpio_rf_amp_en); -#else - (void) rf_path; /* silence unused param warning */ + /* Configure RF switch control signals as outputs */ + gpio_output(rf_path->gpio_amp_bypass); + gpio_output(rf_path->gpio_no_mix_bypass); + gpio_output(rf_path->gpio_rx_amp); + gpio_output(rf_path->gpio_no_rx_amp_pwr); + gpio_output(rf_path->gpio_hp); + gpio_output(rf_path->gpio_lp); + gpio_output(rf_path->gpio_tx_mix_bp); + gpio_output(rf_path->gpio_rx_mix_bp); + gpio_output(rf_path->gpio_tx_amp); + gpio_output(rf_path->gpio_no_tx_amp_pwr); + gpio_output(rf_path->gpio_mix_bypass); + gpio_output(rf_path->gpio_rx); #endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + /* Configure RF switch control signals */ + // clang-format off + scu_pinmux(scu->BY_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->BY_AMP_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu_pinmux(scu->TX_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->TX_RX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->BY_MIX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->BY_MIX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->LOW_HIGH_FILT, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->LOW_HIGH_FILT_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->TX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->RX_LNA, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu_pinmux(scu->MIXER_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + // clang-format on + + /* Configure RF power supply (VAA) switch */ + scu_pinmux(scu->VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + + /* + * Safe (initial) switch settings turn off both amplifiers and antenna port + * power and enable both amp bypass and mixer bypass. + */ + switchctrl_set(rf_path, SWITCHCTRL_SAFE); + + /* Configure RF switch control signals as outputs */ + gpio_output(rf_path->gpio_tx_rx_n); + gpio_output(rf_path->gpio_tx_rx); + gpio_output(rf_path->gpio_by_mix); + gpio_output(rf_path->gpio_by_mix_n); + gpio_output(rf_path->gpio_by_amp); + gpio_output(rf_path->gpio_by_amp_n); + gpio_output(rf_path->gpio_mixer_en); + gpio_output(rf_path->gpio_low_high_filt); + gpio_output(rf_path->gpio_low_high_filt_n); + gpio_output(rf_path->gpio_tx_amp); + gpio_output(rf_path->gpio_rx_lna); +#endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + /* Configure RF switch control signals */ + scu_pinmux(scu->TX_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + board_rev_t rev = detected_revision(); + if ((rev == BOARD_REV_PRALINE_R1_0) || + (rev == BOARD_REV_GSG_PRALINE_R1_0)) { + scu_pinmux(scu->MIX_EN_N_R1_0, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + } else { + scu_pinmux(scu->MIX_EN_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + } + scu_pinmux(scu->LPF_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->RF_AMP_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + + /* Configure antenna port power control signal */ + scu_pinmux(scu->ANT_BIAS_EN_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + + /* Configure RF power supply (VAA) switch */ + scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + + /* + * Safe (initial) switch settings turn off both amplifiers and antenna port + * power and enable both amp bypass and mixer bypass. + */ + switchctrl_set(rf_path, SWITCHCTRL_SAFE); + + /* Configure RF switch control signals as outputs */ + gpio_output(rf_path->gpio_ant_bias_en_n); + gpio_output(rf_path->gpio_tx_en); + gpio_output(rf_path->gpio_mix_en_n); + gpio_output(rf_path->gpio_lpf_en); + gpio_output(rf_path->gpio_rf_amp_en); +#endif + break; + default: + break; + } } void rf_path_init(rf_path_t* const rf_path) { + board_id_t board_id = detected_platform(); + ssp1_set_mode_max5864(); max5864_setup(&max5864); max5864_shutdown(&max5864); ssp1_set_mode_max283x(); -#ifdef PRALINE - max283x_setup(&max283x, MAX2831_VARIANT); -#elif defined(HACKRF_ONE) - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - max283x_setup(&max283x, MAX2839_VARIANT); - } else { - max283x_setup(&max283x, MAX2837_VARIANT); - } -#else - max283x_setup(&max283x, MAX2837_VARIANT); + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + max283x_setup(&max283x, MAX2831_VARIANT); #endif + break; + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + max283x_setup(&max283x, MAX2839_VARIANT); +#endif + break; + default: +#if !defined(PRALINE) || defined(UNIVERSAL) + max283x_setup(&max283x, MAX2837_VARIANT); +#endif + break; + } max283x_start(&max283x); - // On HackRF One, the mixer is now set up earlier in boot. -#ifndef HACKRF_ONE - mixer_setup(&mixer); -#endif + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + // On HackRF One, the mixer is now set up earlier in boot. + break; + default: + mixer_setup(&mixer); + break; + } rf_path->switchctrl = SWITCHCTRL_SAFE; } @@ -501,7 +551,7 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d max283x_rx(&max283x); break; -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) case RF_PATH_DIRECTION_TX_CALIBRATION: case RF_PATH_DIRECTION_RX_CALIBRATION: rf_path->switchctrl &= ~SWITCHCTRL_TX; diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h index 1ac0e243..64634b3d 100644 --- a/firmware/common/rf_path.h +++ b/firmware/common/rf_path.h @@ -25,7 +25,7 @@ #include -#if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE) +#if !defined(JAWBREAKER) #include "gpio.h" #endif @@ -33,7 +33,7 @@ typedef enum { RF_PATH_DIRECTION_OFF, RF_PATH_DIRECTION_RX, RF_PATH_DIRECTION_TX, -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) RF_PATH_DIRECTION_TX_CALIBRATION, RF_PATH_DIRECTION_RX_CALIBRATION, #endif @@ -47,43 +47,48 @@ typedef enum { typedef struct { uint8_t switchctrl; -#ifdef HACKRF_ONE - gpio_t gpio_hp; - gpio_t gpio_lp; - gpio_t gpio_tx_mix_bp; - gpio_t gpio_no_mix_bypass; - gpio_t gpio_rx_mix_bp; - gpio_t gpio_tx_amp; - gpio_t gpio_tx; - gpio_t gpio_mix_bypass; - gpio_t gpio_rx; - gpio_t gpio_no_tx_amp_pwr; - gpio_t gpio_amp_bypass; - gpio_t gpio_rx_amp; - gpio_t gpio_no_rx_amp_pwr; - // In HackRF One r9 this control signal has been moved to the microcontroller. - gpio_t gpio_h1r9_no_ant_pwr; + + struct { +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + gpio_t gpio_hp; + gpio_t gpio_lp; + gpio_t gpio_tx_mix_bp; + gpio_t gpio_no_mix_bypass; + gpio_t gpio_rx_mix_bp; + gpio_t gpio_tx_amp; + gpio_t gpio_tx; + gpio_t gpio_mix_bypass; + gpio_t gpio_rx; + gpio_t gpio_no_tx_amp_pwr; + gpio_t gpio_amp_bypass; + gpio_t gpio_rx_amp; + gpio_t gpio_no_rx_amp_pwr; + // In HackRF One r9 this control signal has been moved to the microcontroller. + gpio_t gpio_h1r9_no_ant_pwr; #endif -#ifdef RAD1O - gpio_t gpio_tx_rx_n; - gpio_t gpio_tx_rx; - gpio_t gpio_by_mix; - gpio_t gpio_by_mix_n; - gpio_t gpio_by_amp; - gpio_t gpio_by_amp_n; - gpio_t gpio_mixer_en; - gpio_t gpio_low_high_filt; - gpio_t gpio_low_high_filt_n; - gpio_t gpio_tx_amp; - gpio_t gpio_rx_lna; + +#if defined(RAD1O) + gpio_t gpio_tx_rx_n; + gpio_t gpio_tx_rx; + gpio_t gpio_by_mix; + gpio_t gpio_by_mix_n; + gpio_t gpio_by_amp; + gpio_t gpio_by_amp_n; + gpio_t gpio_mixer_en; + gpio_t gpio_low_high_filt; + gpio_t gpio_low_high_filt_n; + gpio_t gpio_tx_amp; + gpio_t gpio_rx_lna; #endif -#ifdef PRALINE - gpio_t gpio_tx_en; - gpio_t gpio_mix_en_n; - gpio_t gpio_lpf_en; - gpio_t gpio_rf_amp_en; - gpio_t gpio_ant_bias_en_n; + +#if defined(PRALINE) || defined(UNIVERSAL) + gpio_t gpio_tx_en; + gpio_t gpio_mix_en_n; + gpio_t gpio_lpf_en; + gpio_t gpio_rf_amp_en; + gpio_t gpio_ant_bias_en_n; #endif + }; } rf_path_t; void rf_path_pin_setup(rf_path_t* const rf_path); From 3aaa7185bd49d27a918fc7f810d5e5a1572245fe Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 4 Apr 2026 00:32:50 +0100 Subject: [PATCH 050/173] Universalize: mixer.{c,h}, usb_api_register.c Co-authored-by: Antoine van Gelder --- firmware/common/mixer.c | 157 +++++++++++++++++-------- firmware/common/mixer.h | 27 ++++- firmware/common/rf_path.c | 9 +- firmware/hackrf_usb/hackrf_usb.c | 8 +- firmware/hackrf_usb/usb_api_register.c | 52 ++++++-- 5 files changed, 185 insertions(+), 68 deletions(-) diff --git a/firmware/common/mixer.c b/firmware/common/mixer.c index 8623ee6b..9e1afb32 100644 --- a/firmware/common/mixer.c +++ b/firmware/common/mixer.c @@ -25,101 +25,164 @@ #include "fixed_point.h" #include "platform_gpio.h" -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) +#if defined(RAD1O) + #include "max2871.h" +#else + #include "platform_detect.h" #include "rffc5071.h" #include "rffc5071_spi.h" #include "spi_bus.h" -#elif defined(RAD1O) - #include "max2871.h" #endif -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) +#if !defined(RAD1O) static rffc5071_spi_config_t rffc5071_spi_config; -spi_bus_t spi_bus_rffc5071 = { +static spi_bus_t spi_bus_rffc5071 = { .config = &rffc5071_spi_config, .start = rffc5071_spi_start, .stop = rffc5071_spi_stop, .transfer = rffc5071_spi_transfer, .transfer_gather = rffc5071_spi_transfer_gather, }; +#endif mixer_driver_t mixer = { - .bus = &spi_bus_rffc5071, -}; -#elif defined(RAD1O) -mixer_driver_t mixer = {}; +#if !defined(RAD1O) + .rffc5071.bus = &spi_bus_rffc5071, #endif +}; void mixer_bus_setup(mixer_driver_t* const mixer) { +#if defined(RAD1O) (void) mixer; - -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) +#else const platform_gpio_t* gpio = platform_gpio(); - rffc5071_spi_config = (rffc5071_spi_config_t){ - .gpio_select = gpio->rffc5072_select, - .gpio_clock = gpio->rffc5072_clock, - .gpio_data = gpio->rffc5072_data, - }; - spi_bus_start(&spi_bus_rffc5071, &rffc5071_spi_config); #endif + + switch (mixer->type) { + case RFFC5071_VARIANT: +#if !defined(RAD1O) + rffc5071_spi_config = (rffc5071_spi_config_t){ + .gpio_select = gpio->rffc5072_select, + .gpio_clock = gpio->rffc5072_clock, + .gpio_data = gpio->rffc5072_data, + }; + spi_bus_start(&spi_bus_rffc5071, &rffc5071_spi_config); +#endif + break; + case MAX2871_VARIANT: + break; + } } -void mixer_setup(mixer_driver_t* const mixer) +void mixer_setup(mixer_driver_t* const mixer, mixer_variant_t type) { + mixer->type = type; + const platform_gpio_t* gpio = platform_gpio(); -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) - mixer->gpio_reset = gpio->rffc5072_reset; - #if defined(PRALINE) - mixer->gpio_ld = gpio->rffc5072_ld; + /* Mixer GPIO serial interface PinMux */ + switch (mixer->type) { + case RFFC5071_VARIANT: +#if !defined(RAD1O) + mixer->rffc5071.gpio_reset = gpio->rffc5072_reset; + if (detected_platform() == BOARD_ID_PRALINE) { + #if defined(PRALINE) || defined(UNIVERSAL) + mixer->rffc5071.gpio_ld = gpio->rffc5072_ld; #endif - rffc5071_setup(mixer); -#elif defined(RAD1O) - mixer->gpio_vco_ce = gpio->vco_ce; - mixer->gpio_vco_sclk = gpio->vco_sclk; - mixer->gpio_vco_sdata = gpio->vco_sdata; - mixer->gpio_vco_le = gpio->vco_le; - mixer->gpio_synt_rfout_en = gpio->synt_rfout_en; - mixer->gpio_vco_mux = gpio->vco_mux; - max2871_setup(mixer); + } #endif + break; + case MAX2871_VARIANT: +#if defined(RAD1O) + mixer->max2871.gpio_vco_ce = gpio->vco_ce; + mixer->max2871.gpio_vco_sclk = gpio->vco_sclk; + mixer->max2871.gpio_vco_sdata = gpio->vco_sdata; + mixer->max2871.gpio_vco_le = gpio->vco_le; + mixer->max2871.gpio_synt_rfout_en = gpio->synt_rfout_en; + mixer->max2871.gpio_vco_mux = gpio->vco_mux; +#endif + break; + } + + switch (mixer->type) { + case RFFC5071_VARIANT: +#if !defined(RAD1O) + rffc5071_setup(&mixer->rffc5071); +#endif + break; + case MAX2871_VARIANT: +#if defined(RAD1O) + max2871_setup(&mixer->max2871); +#endif + break; + } } fp_40_24_t mixer_set_frequency(mixer_driver_t* const mixer, fp_40_24_t lo, bool program) { -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) - return rffc5071_set_frequency(mixer, lo, program); -#elif defined(RAD1O) - return max2871_set_frequency(mixer, lo, program); + switch (mixer->type) { + case RFFC5071_VARIANT: +#if !defined(RAD1O) + return rffc5071_set_frequency(&mixer->rffc5071, lo, program); #endif + break; + case MAX2871_VARIANT: +#if defined(RAD1O) + return max2871_set_frequency(&mixer->max2871, lo, program); +#endif + break; + } + + return 0; } void mixer_enable(mixer_driver_t* const mixer) { -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) - rffc5071_enable(mixer); -#elif defined(RAD1O) - max2871_enable(mixer); + switch (mixer->type) { + case RFFC5071_VARIANT: +#if !defined(RAD1O) + rffc5071_enable(&mixer->rffc5071); #endif + break; + case MAX2871_VARIANT: +#if defined(RAD1O) + max2871_enable(&mixer->max2871); +#endif + break; + } } void mixer_disable(mixer_driver_t* const mixer) { -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) - rffc5071_disable(mixer); -#elif defined(RAD1O) - max2871_disable(mixer); + switch (mixer->type) { + case RFFC5071_VARIANT: +#if !defined(RAD1O) + rffc5071_disable(&mixer->rffc5071); #endif + break; + case MAX2871_VARIANT: +#if defined(RAD1O) + max2871_disable(&mixer->max2871); +#endif + break; + } } void mixer_set_gpo(mixer_driver_t* const mixer, uint8_t gpo) { -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) - rffc5071_set_gpo(mixer, gpo); -#elif defined(RAD1O) - (void) mixer; +#if defined(RAD1O) (void) gpo; #endif + + switch (mixer->type) { + case RFFC5071_VARIANT: +#if !defined(RAD1O) + rffc5071_set_gpo(&mixer->rffc5071, gpo); +#endif + break; + case MAX2871_VARIANT: + break; + } } diff --git a/firmware/common/mixer.h b/firmware/common/mixer.h index 6ba47d75..0afacb6b 100644 --- a/firmware/common/mixer.h +++ b/firmware/common/mixer.h @@ -26,17 +26,32 @@ #include "fixed_point.h" -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) - #include "rffc5071.h" -typedef rffc5071_driver_t mixer_driver_t; -#elif defined(RAD1O) +#if defined(RAD1O) #include "max2871.h" -typedef max2871_driver_t mixer_driver_t; +#else + #include "rffc5071.h" #endif +typedef enum { + RFFC5071_VARIANT, + MAX2871_VARIANT, +} mixer_variant_t; + +typedef struct { + mixer_variant_t type; + + union { +#if defined(RAD1O) + max2871_driver_t max2871; +#else + rffc5071_driver_t rffc5071; +#endif + }; +} mixer_driver_t; + #include extern void mixer_bus_setup(mixer_driver_t* const mixer); -extern void mixer_setup(mixer_driver_t* const mixer); +extern void mixer_setup(mixer_driver_t* const mixer, mixer_variant_t type); /* Set frequency (Hz). */ extern fp_40_24_t mixer_set_frequency( diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index a361f442..5e892668 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -504,8 +504,15 @@ void rf_path_init(rf_path_t* const rf_path) case BOARD_ID_HACKRF1_R9: // On HackRF One, the mixer is now set up earlier in boot. break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + mixer_setup(&mixer, MAX2871_VARIANT); +#endif + break; default: - mixer_setup(&mixer); +#if !defined(RAD1O) + mixer_setup(&mixer, RFFC5071_VARIANT); +#endif break; } rf_path->switchctrl = SWITCHCTRL_SAFE; diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index c365e9bc..93308cda 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -47,7 +47,7 @@ #include #include #include -#if defined(HACKRF_ONE) +#if !defined(RAD1O) #include #endif #if defined(PRALINE) || defined(HACKRF_ONE) @@ -432,7 +432,9 @@ int main(void) // This will be cleared if any self-test check fails. selftest.report.pass = true; + // Detect hardware platform before we do anything else. detect_hardware_platform(); + pin_shutdown(); #ifndef RAD1O clock_gen_shutdown(); @@ -464,7 +466,7 @@ int main(void) #ifdef HACKRF_ONE // Set up mixer before enabling RF power, because its // GPO is used to control the antenna bias tee. - mixer_setup(&mixer); + mixer_setup(&mixer, RFFC5071_VARIANT); #endif #if (defined HACKRF_ONE || defined RAD1O) enable_rf_power(); @@ -526,7 +528,7 @@ int main(void) rf_path_init(&rf_path); #ifndef RAD1O - rffc5071_lock_test(&mixer); + rffc5071_lock_test(&mixer.rffc5071); #endif #ifdef PRALINE diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index 01d9fa8f..b74589ec 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -30,17 +30,19 @@ #include #include +#include #include #include #include #include #include -#if defined(PRALINE) || defined(HACKRF_ONE) || defined(JAWBREAKER) - #include -#endif -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) #include #endif +#if !defined(RAD1O) + #include + #include +#endif usb_request_status_t usb_vendor_request_write_max283x( usb_endpoint_t* const endpoint, @@ -136,20 +138,29 @@ usb_request_status_t usb_vendor_request_read_si5351c( } } -#ifndef RAD1O usb_request_status_t usb_vendor_request_write_rffc5071( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { +#if defined(RAD1O) + (void) endpoint; + (void) stage; + if (detected_platform() == BOARD_ID_RAD1O) { + return USB_REQUEST_STATUS_STALL; + } +#endif + if (stage == USB_TRANSFER_STAGE_SETUP) { +#if !defined(RAD1O) if (endpoint->setup.index < RFFC5071_NUM_REGS) { rffc5071_reg_write( - &mixer, + &mixer.rffc5071, endpoint->setup.index, endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } +#endif return USB_REQUEST_STATUS_STALL; } else { return USB_REQUEST_STATUS_OK; @@ -160,10 +171,19 @@ usb_request_status_t usb_vendor_request_read_rffc5071( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { - uint16_t value; +#if defined(RAD1O) + (void) endpoint; + (void) stage; + if (detected_platform() == BOARD_ID_RAD1O) { + return USB_REQUEST_STATUS_STALL; + } +#endif + if (stage == USB_TRANSFER_STAGE_SETUP) { +#if !defined(RAD1O) + uint16_t value; if (endpoint->setup.index < RFFC5071_NUM_REGS) { - value = rffc5071_reg_read(&mixer, endpoint->setup.index); + value = rffc5071_reg_read(&mixer.rffc5071, endpoint->setup.index); endpoint->buffer[0] = value & 0xff; endpoint->buffer[1] = value >> 8; usb_transfer_schedule_block( @@ -175,12 +195,12 @@ usb_request_status_t usb_vendor_request_read_rffc5071( usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } +#endif return USB_REQUEST_STATUS_STALL; } else { return USB_REQUEST_STATUS_OK; } } -#endif usb_request_status_t usb_vendor_request_set_clkout_enable( usb_endpoint_t* const endpoint, @@ -266,13 +286,18 @@ usb_request_status_t usb_vendor_request_user_config_set_bias_t_opts( return USB_REQUEST_STATUS_OK; } -#ifdef PRALINE usb_request_status_t usb_vendor_request_write_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (detected_platform() != BOARD_ID_PRALINE) { + return USB_REQUEST_STATUS_STALL; + } + if (stage == USB_TRANSFER_STAGE_SETUP) { +#if defined(PRALINE) || defined(UNIVERSAL) fpga_reg_write(&fpga, endpoint->setup.index, endpoint->setup.value); +#endif usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } @@ -283,7 +308,12 @@ usb_request_status_t usb_vendor_request_read_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (detected_platform() != BOARD_ID_PRALINE) { + return USB_REQUEST_STATUS_STALL; + } + if (stage == USB_TRANSFER_STAGE_SETUP) { +#if defined(PRALINE) || defined(UNIVERSAL) const uint8_t value = fpga_reg_read(&fpga, endpoint->setup.index); endpoint->buffer[0] = value; usb_transfer_schedule_block( @@ -292,11 +322,11 @@ usb_request_status_t usb_vendor_request_read_fpga_reg( 1, NULL, NULL); +#endif usb_transfer_schedule_ack(endpoint->out); } return USB_REQUEST_STATUS_OK; } -#endif /* * Each register is transferred as a uint8_t register number followed by a From 275b75fc8558cb0ab7e72f2c47e714ab9e44d920 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 4 Apr 2026 00:51:41 +0100 Subject: [PATCH 051/173] Universalize: tuning.{c,h}, tune_config.h Co-authored-by: Antoine van Gelder --- firmware/common/tune_config.h | 2 +- firmware/common/tuning.c | 37 ++++++++++++++++++-------------- firmware/common/tuning.h | 2 ++ firmware/hackrf_usb/hackrf_usb.c | 4 +++- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/firmware/common/tune_config.h b/firmware/common/tune_config.h index 7fc76af9..34c692e6 100644 --- a/firmware/common/tune_config.h +++ b/firmware/common/tune_config.h @@ -21,7 +21,7 @@ #pragma once -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) #include "fpga.h" typedef struct { diff --git a/firmware/common/tuning.c b/firmware/common/tuning.c index f613ab30..9c863beb 100644 --- a/firmware/common/tuning.c +++ b/firmware/common/tuning.c @@ -29,18 +29,23 @@ #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) -#ifdef PRALINE - #define MIN_BYPASS_FREQ FP_MHZ(2320) - #define MAX_BYPASS_FREQ FP_MHZ(2580) -#else - #define MIN_BYPASS_FREQ FP_MHZ(2170) - #define MAX_BYPASS_FREQ FP_MHZ(2740) -#endif - #define MID1_HP_FREQ FP_MHZ(3600) #define MID2_HP_FREQ FP_MHZ(5100) #define MAX_HP_FREQ FP_MHZ(7250) +static fp_40_24_t min_bypass_freq = FP_MHZ(2170); +static fp_40_24_t max_bypass_freq = FP_MHZ(2740); + +void tuning_setup(void) +{ +#if defined(PRALINE) || defined(UNIVERSAL) + if (detected_platform() == BOARD_ID_PRALINE) { + min_bypass_freq = FP_MHZ(2320); + max_bypass_freq = FP_MHZ(2580); + } +#endif +} + fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject) { fp_40_24_t freq_if; @@ -57,8 +62,8 @@ fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject) case RF_PATH_FILTER_HIGH_PASS: if (freq_rf < MID1_HP_FREQ) { /* IF is graduated from 2170 MHz to 2740 MHz */ - freq_if = MIN_BYPASS_FREQ + - (((freq_rf - (MAX_BYPASS_FREQ)) * 57) / 86); + freq_if = min_bypass_freq + + (((freq_rf - (max_bypass_freq)) * 57) / 86); } else if (freq_rf < MID2_HP_FREQ) { /* IF is graduated from 2350 MHz to 2650 MHz */ freq_if = FP_MHZ(2350) + ((freq_rf - (MID1_HP_FREQ)) / 5); @@ -75,9 +80,9 @@ fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject) rf_path_filter_t select_img_reject(fp_40_24_t freq_rf) { - if (freq_rf > MAX_BYPASS_FREQ) { + if (freq_rf > max_bypass_freq) { return RF_PATH_FILTER_HIGH_PASS; - } else if (freq_rf >= MIN_BYPASS_FREQ) { + } else if (freq_rf >= min_bypass_freq) { return RF_PATH_FILTER_BYPASS; } else { return RF_PATH_FILTER_LOW_PASS; @@ -91,14 +96,14 @@ fp_40_24_t restrict_rf(fp_40_24_t freq_rf, rf_path_filter_t img_reject) switch (img_reject) { case RF_PATH_FILTER_LOW_PASS: - max_rf = MIN_BYPASS_FREQ; + max_rf = min_bypass_freq; break; case RF_PATH_FILTER_HIGH_PASS: - min_rf = MAX_BYPASS_FREQ; + min_rf = max_bypass_freq; break; default: - min_rf = MIN_BYPASS_FREQ; - max_rf = MAX_BYPASS_FREQ; + min_rf = min_bypass_freq; + max_rf = max_bypass_freq; } freq_rf = MIN(freq_rf, max_rf); freq_rf = MAX(freq_rf, min_rf); diff --git a/firmware/common/tuning.h b/firmware/common/tuning.h index a338cd28..db123ee3 100644 --- a/firmware/common/tuning.h +++ b/firmware/common/tuning.h @@ -26,6 +26,8 @@ #include "fixed_point.h" #include "rf_path.h" +void tuning_setup(void); + /** * Select fixed or graduated IF for a given RF and image reject filter. */ diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 93308cda..3238ca0e 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -40,8 +40,9 @@ #include #include #include -#include #include +#include +#include #include #include #include @@ -463,6 +464,7 @@ int main(void) clock_gen_init(); #endif #endif + tuning_setup(); #ifdef HACKRF_ONE // Set up mixer before enabling RF power, because its // GPO is used to control the antenna bias tee. From 233951c9c9fae3dd36af8e62edd44dc35997764c Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 4 Apr 2026 00:53:20 +0100 Subject: [PATCH 052/173] Universalize: hackrf_ui.c Co-authored-by: Antoine van Gelder --- firmware/common/hackrf_ui.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/firmware/common/hackrf_ui.c b/firmware/common/hackrf_ui.c index 6e230943..ac16a444 100644 --- a/firmware/common/hackrf_ui.c +++ b/firmware/common/hackrf_ui.c @@ -23,8 +23,9 @@ #include #include "hackrf_ui.h" +#include "platform_detect.h" #include "transceiver_mode.h" -#if defined(PRALINE) || defined(HACKRF_ONE) +#if defined(PRALINE) || defined(HACKRF_ONE) || defined(UNIVERSAL) #include "ui_portapack.h" #endif #if defined(RAD1O) @@ -82,16 +83,26 @@ const hackrf_ui_t* hackrf_ui(void) { /* Detect on first use. If no UI hardware is detected, use a stub function table. */ if (ui == NULL && ui_enabled) { -#if (defined HACKRF_ONE || defined PRALINE) - if (portapack_hackrf_ui_init) { - ui = portapack_hackrf_ui_init(); - } + switch (detected_platform()) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + if (portapack_hackrf_ui_init) { + ui = portapack_hackrf_ui_init(); + } #endif -#ifdef RAD1O - if (rad1o_ui_setup) { - ui = rad1o_ui_setup(); - } + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + if (rad1o_ui_setup) { + ui = rad1o_ui_setup(); + } #endif + break; + default: + break; + } } if (ui == NULL) { From 9285573f0bbdec44e586daf14be92c68f7ac72cd Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 4 Apr 2026 00:54:12 +0100 Subject: [PATCH 053/173] Universalize: operacake_sctimer.c Co-authored-by: Antoine van Gelder --- firmware/common/operacake_sctimer.c | 40 +++++++++++++++++------------ 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/firmware/common/operacake_sctimer.c b/firmware/common/operacake_sctimer.c index 9ae37567..21ce3d7e 100644 --- a/firmware/common/operacake_sctimer.c +++ b/firmware/common/operacake_sctimer.c @@ -30,6 +30,8 @@ #include #endif +#include + #include "delay.h" #include "sct.h" @@ -95,25 +97,31 @@ void operacake_sctimer_init(void) P7_0, SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1); -#ifndef PRALINE - // Configure the SGPIO to output the clock (f=2 * sample clock) on pin 12 - SGPIO_OUT_MUX_CFG12 = SGPIO_OUT_MUX_CFG_P_OUT_CFG(0x08) | // clkout output mode - SGPIO_OUT_MUX_CFG_P_OE_CFG(0); // gpio_oe - SGPIO_GPIO_OENREG |= BIT12; + uint8_t sct_clock_input; + if (detected_platform() != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + // Configure the SGPIO to output the clock (f=2 * sample clock) on pin 12 + SGPIO_OUT_MUX_CFG12 = + SGPIO_OUT_MUX_CFG_P_OUT_CFG(0x08) | // clkout output mode + SGPIO_OUT_MUX_CFG_P_OE_CFG(0); // gpio_oe + SGPIO_GPIO_OENREG |= BIT12; - // Use the GIMA to connect the SGPIO clock to the SCTimer - GIMA_CTIN_1_IN = 0x2 << 4; // Route SGPIO12 to SCTIN1 + // Use the GIMA to connect the SGPIO clock to the SCTimer + GIMA_CTIN_1_IN = 0x2 << 4; // Route SGPIO12 to SCTIN1 - uint8_t sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_1; -#else - // Configure pin P6_4 as SCT_IN_6 - scu_pinmux(P6_4, SCU_CLK_IN | SCU_CONF_FUNCTION1); - - // Use the GIMA to connect MS0/CLK1 (SCT_CLK) on pin P6_4 to the SCTimer - GIMA_CTIN_6_IN = 0x0 << 4; - - uint8_t sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_6; + sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_1; #endif + } else { +#if defined(PRALINE) || defined(UNIVERSAL) + // Configure pin P6_4 as SCT_IN_6 + scu_pinmux(P6_4, SCU_CLK_IN | SCU_CONF_FUNCTION1); + + // Use the GIMA to connect MS0/CLK1 (SCT_CLK) on pin P6_4 to the SCTimer + GIMA_CTIN_6_IN = 0x0 << 4; + + sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_6; +#endif + } // We configure this register first, because the user manual says to SCT_CONFIG |= SCT_CONFIG_UNIFY_32_BIT | SCT_CONFIG_CLKMODE_PRESCALED_BUS_CLOCK | From ed1a4d0e9e0938cddb83636f5a079c9a4ca30b9b Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 4 Apr 2026 00:55:40 +0100 Subject: [PATCH 054/173] Universalize: w25q80bv.c Co-authored-by: Antoine van Gelder --- firmware/common/w25q80bv.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/firmware/common/w25q80bv.c b/firmware/common/w25q80bv.c index 9e7e1acd..f569bedf 100644 --- a/firmware/common/w25q80bv.c +++ b/firmware/common/w25q80bv.c @@ -27,10 +27,11 @@ * programming the flash. */ -#include "w25q80bv.h" - -#include #include +#include + +#include "platform_detect.h" +#include "w25q80bv.h" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) @@ -59,13 +60,13 @@ void w25q80bv_setup(w25q80bv_driver_t* const drv) uint8_t device_id; drv->page_len = 256U; -#ifdef PRALINE - drv->num_pages = 16384U; - drv->num_bytes = 4194304U; -#else - drv->num_pages = 4096U; - drv->num_bytes = 1048576U; -#endif + if (detected_platform() == BOARD_ID_PRALINE) { + drv->num_pages = 16384U; + drv->num_bytes = 4194304U; + } else { + drv->num_pages = 4096U; + drv->num_bytes = 1048576U; + } drv->target_init(drv); From 54dfdd9174f2bf90ad2179b3b74e611986373961 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 4 Apr 2026 00:56:31 +0100 Subject: [PATCH 055/173] Universalize: sgpio.{c,h} Co-authored-by: Antoine van Gelder --- firmware/common/sgpio.c | 14 ++++++++------ firmware/common/sgpio.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index f364d7b0..ac755d51 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -58,7 +58,7 @@ void sgpio_configure_pin_functions(sgpio_config_t* const config) scu_pinmux(scu->PINMUX_SGPIO15, scu->PINMUX_SGPIO15_PINCFG); /* GPIO5[14] */ if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#if defined(HACKRF_ONE) +#if defined(HACKRF_ONE) || defined(UNIVERSAL) scu_pinmux( scu->H1R9_TRIGGER_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[5] */ @@ -72,10 +72,12 @@ void sgpio_configure_pin_functions(sgpio_config_t* const config) sgpio_cpld_set_mixer_invert(config, 0); gpio_output(config->gpio_q_invert); -#ifndef PRALINE - trigger_enable(false); - gpio_output(config->gpio_trigger_enable); + if (detected_platform() != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + trigger_enable(false); + gpio_output(config->gpio_trigger_enable); #endif + } } void sgpio_set_slice_mode(sgpio_config_t* const config, const bool multi_slice) @@ -157,11 +159,11 @@ void sgpio_configure(sgpio_config_t* const config, const sgpio_direction_t direc SGPIO_OUT_MUX_CFG_P_OE_CFG(0) // gpio_oe (state set by GPIO_OEREG) | SGPIO_OUT_MUX_CFG_P_OUT_CFG(0) // dout_doutm1 (1-bit mode) ; - SGPIO_OUT_MUX_CFG(10) = // GPIO10: Output: disable + SGPIO_OUT_MUX_CFG(10) = // GPIO10: Output: disable SGPIO_OUT_MUX_CFG_P_OE_CFG(0) // gpio_oe (state set by GPIO_OEREG) | SGPIO_OUT_MUX_CFG_P_OUT_CFG(4) // gpio_out (level set by GPIO_OUTREG) ; - SGPIO_OUT_MUX_CFG(11) = // GPIO11: Output: direction + SGPIO_OUT_MUX_CFG(11) = // GPIO11: Output: direction SGPIO_OUT_MUX_CFG_P_OE_CFG(0) // gpio_oe (state set by GPIO_OEREG) | SGPIO_OUT_MUX_CFG_P_OUT_CFG(4) // gpio_out (level set by GPIO_OUTREG) ; diff --git a/firmware/common/sgpio.h b/firmware/common/sgpio.h index a3b6b2eb..f6fdf213 100644 --- a/firmware/common/sgpio.h +++ b/firmware/common/sgpio.h @@ -34,7 +34,7 @@ typedef enum { typedef struct { gpio_t gpio_q_invert; -#ifndef PRALINE +#if !defined(PRALINE) || defined(UNIVERSAL) gpio_t gpio_trigger_enable; #endif bool slice_mode_multislice; From 6c4023e1791883d51d83a4e91ca8897556e90b3d Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 4 Apr 2026 00:57:58 +0100 Subject: [PATCH 056/173] Universalize: radio.c Co-authored-by: Antoine van Gelder --- firmware/common/radio.c | 184 +++++++++++++++++++++------------------- 1 file changed, 96 insertions(+), 88 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index ac150ca1..ff97a814 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -33,7 +33,7 @@ #include "rf_path.h" #include "transceiver_mode.h" #include "tuning.h" -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) #include "fpga.h" #include "tune_config.h" #endif @@ -195,18 +195,22 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) case TRANSCEIVER_MODE_SS: n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) { -#ifdef PRALINE - fpga_set_tx_interpolation_ratio(&fpga, n); + if (detected_platform() == BOARD_ID_PRALINE) { +#if defined(PRALINE) || defined(UNIVERSAL) + fpga_set_tx_interpolation_ratio(&fpga, n); #endif + } radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX] = n; } break; default: n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) { -#ifdef PRALINE - fpga_set_rx_decimation_ratio(&fpga, n); + if (detected_platform() == BOARD_ID_PRALINE) { +#if defined(PRALINE) || defined(UNIVERSAL) + fpga_set_rx_decimation_ratio(&fpga, n); #endif + } radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX] = n; } } @@ -290,7 +294,7 @@ static fp_40_24_t digital_from_analog_rf( return drf; } -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) /* * Convert center frequency of digital baseband (as seen by MCU) to center * frequency of analog baseband (as seen by ADC/DAC). @@ -418,7 +422,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) /* Look up settings appropriate for requested RF. */ if (detected_platform() == BOARD_ID_PRALINE) { -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) const tune_config_t* tune_config = select_tune_config(opmode, freq_rf); if (requested_rotation == RADIO_UNSET) { @@ -498,9 +502,11 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) changed |= (1 << RADIO_IMAGE_REJECT); } if ((rotation != applied_rotation) && (rotation != RADIO_UNSET)) { -#ifdef PRALINE - fpga_set_rx_quarter_shift_mode(&fpga, rotation >> 30); + if (detected_platform() == BOARD_ID_PRALINE) { +#if defined(PRALINE) || defined(UNIVERSAL) + fpga_set_rx_quarter_shift_mode(&fpga, rotation >> 30); #endif + } radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = rotation; changed |= (1 << RADIO_ROTATION); } @@ -565,70 +571,74 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank) opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; } -#ifdef PRALINE - /* Praline legacy mode always sets baseband bandwidth automatically. */ - (void) bank; - uint32_t lpf_bandwidth = auto_bandwidth(radio, opmode); + if (detected_platform() == BOARD_ID_PRALINE) { +#if defined(PRALINE) || defined(UNIVERSAL) + /* Praline legacy mode always sets baseband bandwidth automatically. */ + (void) bank; + uint32_t lpf_bandwidth = auto_bandwidth(radio, opmode); - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { - max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_TX, lpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth; - changed |= (1 << RADIO_XCVR_TX_LPF); - } - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] != lpf_bandwidth) { - max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_RX, lpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth; - changed |= (1 << RADIO_XCVR_RX_LPF); - } - bool narrow_lpf_enable = false; - bool applied_narrow_lpf_enable = - radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF]; - if (lpf_bandwidth <= 1750000) { - narrow_lpf_enable = true; - } - if (applied_narrow_lpf_enable != narrow_lpf_enable) { - narrowband_filter_set(narrow_lpf_enable); - radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF] = - narrow_lpf_enable; - changed |= (1 << RADIO_RX_NARROW_LPF); - } - /* Always set HPF bandwidth to 30 kHz for now. */ - const max283x_rx_hpf_freq_t hpf_bandwidth = MAX283x_RX_HPF_30_KHZ; - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] != hpf_bandwidth) { - max283x_set_rx_hpf_frequency(&max283x, hpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] = hpf_bandwidth; - changed |= (1 << RADIO_XCVR_RX_HPF); - } -#else - uint64_t lpf_bandwidth; - lpf_bandwidth = bank[RADIO_XCVR_TX_LPF]; - if (lpf_bandwidth == RADIO_UNSET) { - lpf_bandwidth = bank[RADIO_XCVR_RX_LPF]; - } - if (lpf_bandwidth == RADIO_UNSET) { - lpf_bandwidth = bank[RADIO_BB_BANDWIDTH_TX]; - } - if (lpf_bandwidth == RADIO_UNSET) { - lpf_bandwidth = bank[RADIO_BB_BANDWIDTH_RX]; - } - if (lpf_bandwidth == RADIO_UNSET) { - lpf_bandwidth = radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF]; - } - if (lpf_bandwidth == RADIO_UNSET) { - lpf_bandwidth = auto_bandwidth(radio, opmode); - } - - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { - max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_TX, lpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_RX] = lpf_bandwidth; - radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_TX] = lpf_bandwidth; - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth; - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth; - changed |= - ((1 << RADIO_BB_BANDWIDTH_RX) | (1 << RADIO_BB_BANDWIDTH_TX) | - (1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF)); - } + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { + max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_TX, lpf_bandwidth); + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth; + changed |= (1 << RADIO_XCVR_TX_LPF); + } + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] != lpf_bandwidth) { + max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_RX, lpf_bandwidth); + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth; + changed |= (1 << RADIO_XCVR_RX_LPF); + } + bool narrow_lpf_enable = false; + bool applied_narrow_lpf_enable = + radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF]; + if (lpf_bandwidth <= 1750000) { + narrow_lpf_enable = true; + } + if (applied_narrow_lpf_enable != narrow_lpf_enable) { + narrowband_filter_set(narrow_lpf_enable); + radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF] = + narrow_lpf_enable; + changed |= (1 << RADIO_RX_NARROW_LPF); + } + /* Always set HPF bandwidth to 30 kHz for now. */ + const max283x_rx_hpf_freq_t hpf_bandwidth = MAX283x_RX_HPF_30_KHZ; + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] != hpf_bandwidth) { + max283x_set_rx_hpf_frequency(&max283x, hpf_bandwidth); + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] = hpf_bandwidth; + changed |= (1 << RADIO_XCVR_RX_HPF); + } #endif + } else { +#if !defined(PRALINE) || defined(UNIVERSAL) + uint64_t lpf_bandwidth; + lpf_bandwidth = bank[RADIO_XCVR_TX_LPF]; + if (lpf_bandwidth == RADIO_UNSET) { + lpf_bandwidth = bank[RADIO_XCVR_RX_LPF]; + } + if (lpf_bandwidth == RADIO_UNSET) { + lpf_bandwidth = bank[RADIO_BB_BANDWIDTH_TX]; + } + if (lpf_bandwidth == RADIO_UNSET) { + lpf_bandwidth = bank[RADIO_BB_BANDWIDTH_RX]; + } + if (lpf_bandwidth == RADIO_UNSET) { + lpf_bandwidth = radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF]; + } + if (lpf_bandwidth == RADIO_UNSET) { + lpf_bandwidth = auto_bandwidth(radio, opmode); + } + + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { + max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_TX, lpf_bandwidth); + radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_RX] = lpf_bandwidth; + radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_TX] = lpf_bandwidth; + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth; + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth; + changed |= + ((1 << RADIO_BB_BANDWIDTH_RX) | (1 << RADIO_BB_BANDWIDTH_TX) | + (1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF)); + } +#endif + } return changed; } @@ -764,26 +774,24 @@ static uint32_t radio_update_trigger(radio_t* const radio, uint64_t* bank) static uint32_t radio_update_dc_block(radio_t* const radio, uint64_t* bank) { -#ifndef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) + if (detected_platform() == BOARD_ID_PRALINE) { + const uint64_t requested = bank[RADIO_DC_BLOCK]; + bool enable = requested; + + if (radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] == (uint64_t) enable) { + return 0; + } + + fpga_set_rx_dc_block_enable(&fpga, enable); + radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] = enable; + return (1 << RADIO_DC_BLOCK); + } +#else (void) radio; (void) bank; - return 0; -#else - const uint64_t requested = bank[RADIO_DC_BLOCK]; - bool enable = requested; - - if (requested == RADIO_UNSET) { - enable = true; - } - - if (radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] == (uint64_t) enable) { - return 0; - } - - fpga_set_rx_dc_block_enable(&fpga, enable); - radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] = enable; - return (1 << RADIO_DC_BLOCK); #endif + return 0; } bool radio_update(radio_t* const radio) From 938c020f3b5b146e322c88402325c831e7c5cb8a Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 4 Apr 2026 01:09:30 +0100 Subject: [PATCH 057/173] Universalize: hackrf_core.{c,h}, selftest.h Co-authored-by: Antoine van Gelder --- firmware/common/hackrf_core.c | 781 +++++++++++++++++++++------------- firmware/common/hackrf_core.h | 18 +- firmware/common/selftest.h | 9 +- 3 files changed, 504 insertions(+), 304 deletions(-) diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 25e3b870..c8b66443 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -25,7 +25,7 @@ #include #include #include -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE) +#if !defined(RAD1O) #include #endif @@ -41,10 +41,10 @@ #include "platform_scu.h" #include "spi_bus.h" #include "w25q80bv_target.h" -#if defined(HACKRF_ONE) || defined(PRALINE) +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) #include "portapack.h" #endif -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) #include "ice40_spi.h" #endif @@ -124,7 +124,7 @@ ssp_config_t ssp_config_w25q80bv = { .clock_prescale_rate = 2, }; -spi_bus_t spi_bus_ssp0 = { +static spi_bus_t spi_bus_ssp0 = { .obj = (void*) SSP0_BASE, .config = &ssp_config_w25q80bv, .start = spi_ssp_start, @@ -142,8 +142,8 @@ sgpio_config_t sgpio_config = { .slice_mode_multislice = true, }; -#ifdef PRALINE -ssp_config_t ssp_config_ice40_fpga = { +#if defined(PRALINE) || defined(UNIVERSAL) +static ssp_config_t ssp_config_ice40_fpga = { .data_bits = SSP_DATA_8BITS, .spi_mode = SSP_CPOL_1_CPHA_1, .serial_clock_rate = 21, @@ -163,11 +163,9 @@ radio_t radio = { .sample_rate_cb = sample_rate_set, }; -rf_path_t rf_path = { - .switchctrl = 0, -}; +rf_path_t rf_path; -jtag_gpio_t jtag_gpio_cpld = {}; +jtag_gpio_t jtag_gpio_cpld; jtag_t jtag_cpld = { .gpio = &jtag_gpio_cpld, @@ -343,61 +341,77 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) sgpio_cpld_stream_disable(&sgpio_config); } -#ifndef PRALINE - /* Integer mode can be enabled if p1 is even and p2 is zero. */ - if (p1 & 0x1 || p2) { - si5351c_set_int_mode(&clock_gen, 0, 0); - } else { - si5351c_set_int_mode(&clock_gen, 0, 1); - } + if (detected_platform() != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + /* Integer mode can be enabled if p1 is even and p2 is zero. */ + if (p1 & 0x1 || p2) { + si5351c_set_int_mode(&clock_gen, 0, 0); + } else { + si5351c_set_int_mode(&clock_gen, 0, 1); + } - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - /* - * On HackRF One r9 all sample clocks are externally derived - * from MS1/CLK1 operating at twice the sample rate. - */ - si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + /* + * On HackRF One r9 all sample clocks are externally derived + * from MS1/CLK1 operating at twice the sample rate. + */ + si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); + } else { + /* + * On other platforms the clock generator produces three + * different sample clocks, all derived from multisynth 0. + */ + /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ + si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); + + /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ + si5351c_configure_multisynth( + &clock_gen, + 1, + 0, + 0, + 0, + 0); //p1 doesn't matter + + /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ + si5351c_configure_multisynth( + &clock_gen, + 2, + 0, + 0, + 0, + 0); //p1 doesn't matter + } +#endif } else { - /* - * On other platforms the clock generator produces three - * different sample clocks, all derived from multisynth 0. - */ - /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ +#if defined(PRALINE) || defined(UNIVERSAL) + /* MS0/CLK0 is the source for the MAX5864 (AFE_CLK). */ si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); - /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ - si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); //p1 doesn't matter + /* MS1/CLK1 is the source for the FPGA (FPGA_CLK and SCT_CLK). */ + si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 1); - /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ - si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter - } -#else - /* MS0/CLK0 is the source for the MAX5864 (AFE_CLK). */ - si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); + /* Delay FPGA_CLK relative to AFE_CLK. */ + uint8_t phase_offset = 0; + if (p1 < 2100) { + phase_offset = (p1 >> 4) - 6; + } + si5351c_set_phase(&clock_gen, 1, phase_offset); - /* MS1/CLK1 is the source for the FPGA (FPGA_CLK and SCT_CLK). */ - si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 1); + if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { + /* + * On older boards FPGA_CLK is on CLK2 while SCT_CLK is on + * CLK1. We configure both so that behavior is consistent with + * newer boards that use CLK1 for both FPGA_CLK and SCT_CLK. + */ + si5351c_configure_multisynth(&clock_gen, 2, p1, p2, p3, 1); + si5351c_set_phase(&clock_gen, 2, phase_offset); + } - /* Delay FPGA_CLK relative to AFE_CLK. */ - uint8_t phase_offset = 0; - if (p1 < 2100) { - phase_offset = (p1 >> 4) - 6; - } - si5351c_set_phase(&clock_gen, 1, phase_offset); - - if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { - /* - * On older boards FPGA_CLK is on CLK2 while SCT_CLK is on - * CLK1. We configure both so that behavior is consistent with - * newer boards that use CLK1 for both FPGA_CLK and SCT_CLK. - */ - si5351c_configure_multisynth(&clock_gen, 2, p1, p2, p3, 1); - si5351c_set_phase(&clock_gen, 2, phase_offset); - } - - /* Reset PLL to synchronize output clock phase. */ - si5351c_reset_pll(&clock_gen, SI5351C_PLL_A); + /* Reset PLL to synchronize output clock phase. */ + si5351c_reset_pll(&clock_gen, SI5351C_PLL_A); #endif + } if (streaming) { sgpio_cpld_stream_enable(&sgpio_config); @@ -549,7 +563,7 @@ void cpu_clock_init(void) CGU_BASE_SSP1_CLK = CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1); -#if (defined JAWBREAKER || defined HACKRF_ONE || defined PRALINE) +#if !defined(RAD1O) /* Disable unused clocks */ /* Start with PLLs */ CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); @@ -707,12 +721,20 @@ void clock_gen_shutdown(void) clock_source_t activate_best_clock_source(void) { -#if (defined HACKRF_ONE || defined PRALINE) - /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ - if (portapack_reference_oscillator && portapack()) { - portapack_reference_oscillator(false); - } + switch (detected_platform()) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ + if (portapack_reference_oscillator && portapack()) { + portapack_reference_oscillator(false); + } #endif + break; + default: + break; + } clock_source_t source = CLOCK_SOURCE_HACKRF; @@ -720,18 +742,26 @@ clock_source_t activate_best_clock_source(void) if (si5351c_clkin_signal_valid(&clock_gen)) { source = CLOCK_SOURCE_EXTERNAL; } else { -#if (defined HACKRF_ONE || defined PRALINE) - /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ - if (portapack_reference_oscillator && portapack()) { - portapack_reference_oscillator(true); - delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */ - if (si5351c_clkin_signal_valid(&clock_gen)) { - source = CLOCK_SOURCE_PORTAPACK; - } else { - portapack_reference_oscillator(false); + switch (detected_platform()) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ + if (portapack_reference_oscillator && portapack()) { + portapack_reference_oscillator(true); + delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */ + if (si5351c_clkin_signal_valid(&clock_gen)) { + source = CLOCK_SOURCE_PORTAPACK; + } else { + portapack_reference_oscillator(false); + } } - } #endif + break; + default: + break; + } /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ } @@ -753,7 +783,7 @@ void ssp1_set_mode_max5864(void) spi_bus_start(max5864.bus, &ssp_config_max5864); } -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) void ssp1_set_mode_ice40(void) { spi_bus_start(&spi_bus_ssp1, &ssp_config_ice40_fpga); @@ -762,12 +792,14 @@ void ssp1_set_mode_ice40(void) void pin_shutdown(void) { - const platform_gpio_t* gpio = platform_gpio(); - const platform_scu_t* scu = platform_scu(); - /* Configure all GPIO as Input (safe state) */ gpio_init(); + /* Detect Platform */ + board_id_t board_id = detected_platform(); + const platform_gpio_t* gpio = platform_gpio(); + const platform_scu_t* scu = platform_scu(); + /* TDI and TMS pull-ups are required in all JTAG-compliant devices. * * The HackRF CPLD is always present, so let the CPLD pull up its TDI and TMS. @@ -782,119 +814,159 @@ void pin_shutdown(void) * * LPC43xx pull-up and pull-down resistors are approximately 53K. */ -#if (defined HACKRF_ONE || defined PRALINE) - scu_pinmux(scu->PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + scu_pinmux(scu->PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); #endif + break; + default: + break; + } scu_pinmux(scu->PINMUX_CPLD_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); -#ifndef PRALINE - scu_pinmux(scu->PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + if (board_id != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + scu_pinmux(scu->PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); #endif + } /* Configure SCU Pin Mux as GPIO */ scu_pinmux(scu->PINMUX_LED1, SCU_GPIO_NOPULL); scu_pinmux(scu->PINMUX_LED2, SCU_GPIO_NOPULL); scu_pinmux(scu->PINMUX_LED3, SCU_GPIO_NOPULL); -#ifdef RAD1O - scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4); + switch (board_id) { + case BOARD_ID_RAD1O: +#if defined(RAD1O) + scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4); #endif -#ifdef PRALINE - scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); #endif + break; + default: + break; + } /* Configure USB indicators */ -#ifdef JAWBREAKER - scu_pinmux(scu->PINMUX_USB_LED0, SCU_CONF_FUNCTION3); - scu_pinmux(scu->PINMUX_USB_LED1, SCU_CONF_FUNCTION3); + if (board_id == BOARD_ID_JAWBREAKER) { +#if defined(JAWBREAKER) + scu_pinmux(scu->PINMUX_USB_LED0, SCU_CONF_FUNCTION3); + scu_pinmux(scu->PINMUX_USB_LED1, SCU_CONF_FUNCTION3); #endif - -#ifdef PRALINE - disable_1v2_power(); - disable_3v3aux_power(); - gpio_output(gpio->gpio_1v2_enable); - gpio_output(gpio->gpio_3v3aux_enable_n); - scu_pinmux(scu->PINMUX_EN1V2, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_EN3V3_AUX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); -#else - disable_1v8_power(); - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - #ifdef HACKRF_ONE - gpio_output(gpio->h1r9_1v8_enable); - scu_pinmux(scu->H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - #endif - } else { - gpio_output(gpio->gpio_1v8_enable); - scu_pinmux(scu->PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); } + + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + disable_1v2_power(); + disable_3v3aux_power(); + gpio_output(gpio->gpio_1v2_enable); + gpio_output(gpio->gpio_3v3aux_enable_n); + scu_pinmux(scu->PINMUX_EN1V2, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_EN3V3_AUX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); #endif - -#if (defined HACKRF_ONE || defined PRALINE) - /* Safe state: start with VAA turned off: */ - disable_rf_power(); - - /* Configure RF power supply (VAA) switch control signal as output */ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - #ifdef HACKRF_ONE - gpio_output(gpio->h1r9_vaa_disable); - #endif - } else { - gpio_output(gpio->vaa_disable); + break; + default: + disable_1v8_power(); + if (detected_platform() == BOARD_ID_HACKRF1_R9) { +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + gpio_output(gpio->h1r9_1v8_enable); + scu_pinmux(scu->H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); +#endif + } else { + gpio_output(gpio->gpio_1v8_enable); + scu_pinmux(scu->PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + } + break; } + + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + /* Safe state: start with VAA turned off: */ + disable_rf_power(); + + /* Configure RF power supply (VAA) switch control signal as output */ + if (board_id == BOARD_ID_HACKRF1_R9) { + #if defined(HACKRF_ONE) || defined(UNIVERSAL) + gpio_output(gpio->h1r9_vaa_disable); + #endif + } else { + gpio_output(gpio->vaa_disable); + } + break; #endif + case BOARD_ID_RAD1O: +#if defined(RAD1O) + /* Safe state: start with VAA turned off: */ + disable_rf_power(); -#ifdef RAD1O - /* Safe state: start with VAA turned off: */ - disable_rf_power(); + /* Configure RF power supply (VAA) switch control signal as output */ + gpio_output(gpio->vaa_enable); - /* Configure RF power supply (VAA) switch control signal as output */ - gpio_output(gpio->vaa_enable); + /* Disable unused clock outputs. They generate noise. */ + scu_pinmux(CLK0, SCU_CLK_IN | SCU_CONF_FUNCTION7); + scu_pinmux(CLK2, SCU_CLK_IN | SCU_CONF_FUNCTION7); - /* Disable unused clock outputs. They generate noise. */ - scu_pinmux(CLK0, SCU_CLK_IN | SCU_CONF_FUNCTION7); - scu_pinmux(CLK2, SCU_CLK_IN | SCU_CONF_FUNCTION7); - - scu_pinmux(scu->PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); #endif + break; + default: + break; + } -#ifdef PRALINE - scu_pinmux(scu->P2_CTRL0, scu->P2_CTRL0_PINCFG); - scu_pinmux(scu->P2_CTRL1, scu->P2_CTRL1_PINCFG); - scu_pinmux(scu->P1_CTRL0, scu->P1_CTRL0_PINCFG); - scu_pinmux(scu->P1_CTRL1, scu->P1_CTRL1_PINCFG); - scu_pinmux(scu->P1_CTRL2, scu->P1_CTRL2_PINCFG); - scu_pinmux(scu->CLKIN_CTRL, scu->CLKIN_CTRL_PINCFG); - scu_pinmux(scu->AA_EN, scu->AA_EN_PINCFG); - scu_pinmux(scu->TRIGGER_IN, scu->TRIGGER_IN_PINCFG); - scu_pinmux(scu->TRIGGER_OUT, scu->TRIGGER_OUT_PINCFG); - scu_pinmux(scu->PPS_OUT, scu->PPS_OUT_PINCFG); - scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PUP | SCU_CONF_FUNCTION4); - scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + switch (board_id) { + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + scu_pinmux(scu->P2_CTRL0, scu->P2_CTRL0_PINCFG); + scu_pinmux(scu->P2_CTRL1, scu->P2_CTRL1_PINCFG); + scu_pinmux(scu->P1_CTRL0, scu->P1_CTRL0_PINCFG); + scu_pinmux(scu->P1_CTRL1, scu->P1_CTRL1_PINCFG); + scu_pinmux(scu->P1_CTRL2, scu->P1_CTRL2_PINCFG); + scu_pinmux(scu->CLKIN_CTRL, scu->CLKIN_CTRL_PINCFG); + scu_pinmux(scu->AA_EN, scu->AA_EN_PINCFG); + scu_pinmux(scu->TRIGGER_IN, scu->TRIGGER_IN_PINCFG); + scu_pinmux(scu->TRIGGER_OUT, scu->TRIGGER_OUT_PINCFG); + scu_pinmux(scu->PPS_OUT, scu->PPS_OUT_PINCFG); + scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PUP | SCU_CONF_FUNCTION4); + scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - p2_ctrl_set(P2_SIGNAL_CLK3); - p1_ctrl_set(P1_SIGNAL_CLKIN); - narrowband_filter_set(0); - clkin_ctrl_set(CLKIN_SIGNAL_P22); + p2_ctrl_set(P2_SIGNAL_CLK3); + p1_ctrl_set(P1_SIGNAL_CLKIN); + narrowband_filter_set(0); + clkin_ctrl_set(CLKIN_SIGNAL_P22); - gpio_output(gpio->p2_ctrl0); - gpio_output(gpio->p2_ctrl1); - gpio_output(gpio->p1_ctrl0); - gpio_output(gpio->p1_ctrl1); - gpio_output(gpio->p1_ctrl2); - gpio_output(gpio->clkin_ctrl); - gpio_output(gpio->pps_out); - gpio_output(gpio->aa_en); - gpio_input(gpio->trigger_in); - gpio_input(gpio->trigger_out); - gpio_clear(gpio->fpga_cfg_spi_cs); - gpio_output(gpio->fpga_cfg_spi_cs); - gpio_clear(gpio->fpga_cfg_creset); - gpio_output(gpio->fpga_cfg_creset); - gpio_input(gpio->fpga_cfg_cdone); + gpio_output(gpio->p2_ctrl0); + gpio_output(gpio->p2_ctrl1); + gpio_output(gpio->p1_ctrl0); + gpio_output(gpio->p1_ctrl1); + gpio_output(gpio->p1_ctrl2); + gpio_output(gpio->clkin_ctrl); + gpio_output(gpio->pps_out); + gpio_output(gpio->aa_en); + gpio_input(gpio->trigger_in); + gpio_input(gpio->trigger_out); + gpio_clear(gpio->fpga_cfg_spi_cs); + gpio_output(gpio->fpga_cfg_spi_cs); + gpio_clear(gpio->fpga_cfg_creset); + gpio_output(gpio->fpga_cfg_creset); + gpio_input(gpio->fpga_cfg_cdone); #endif + break; + default: + break; + } /* enable input on SCL and SDA pins */ SCU_SFSI2C0 = SCU_I2C0_NOMINAL; @@ -903,30 +975,43 @@ void pin_shutdown(void) /* Run after pin_shutdown() and prior to enabling power supplies. */ void pin_setup(void) { + /* Detect Platform */ + board_id_t board_id = detected_platform(); const platform_gpio_t* gpio = platform_gpio(); const platform_scu_t* scu = platform_scu(); + /* Configure LEDs */ led_off(0); led_off(1); led_off(2); -#ifdef RAD1O - led_off(3); -#endif + switch (board_id) { + case BOARD_ID_RAD1O: + led_off(3); + default: + break; + } gpio_output(gpio->led[0]); gpio_output(gpio->led[1]); gpio_output(gpio->led[2]); -#if (defined RAD1O || defined PRALINE) - gpio_output(gpio->led[3]); + switch (board_id) { + case BOARD_ID_RAD1O: + case BOARD_ID_PRALINE: +#if defined(RAD1O) || defined(PRALINE) || defined(UNIVERSAL) + gpio_output(gpio->led[3]); #endif + break; + default: + break; + } /* Configure drivers and driver pins */ ssp_config_max283x.gpio_select = gpio->max283x_select; -#if defined(PRALINE) - ssp_config_max283x.data_bits = SSP_DATA_9BITS; // send 2 words -#else - ssp_config_max283x.data_bits = SSP_DATA_16BITS; -#endif + if (board_id == BOARD_ID_PRALINE) { + ssp_config_max283x.data_bits = SSP_DATA_9BITS; // send 2 words + } else { + ssp_config_max283x.data_bits = SSP_DATA_16BITS; + } ssp_config_max5864.gpio_select = gpio->max5864_select; @@ -935,11 +1020,13 @@ void pin_setup(void) spi_flash.gpio_wp = gpio->w25q80bv_wp; sgpio_config.gpio_q_invert = gpio->q_invert; -#if !defined(PRALINE) - sgpio_config.gpio_trigger_enable = gpio->trigger_enable; + if (board_id != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + sgpio_config.gpio_trigger_enable = gpio->trigger_enable; #endif + } -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) ssp_config_ice40_fpga.gpio_select = gpio->fpga_cfg_spi_cs; ice40.gpio_select = gpio->fpga_cfg_spi_cs; ice40.gpio_creset = gpio->fpga_cfg_creset; @@ -952,73 +1039,102 @@ void pin_setup(void) jtag_gpio_cpld.gpio_tdi = gpio->cpld_tdi; jtag_gpio_cpld.gpio_tdo = gpio->cpld_tdo; #endif -#if defined(HACKRF_ONE) || defined(PRALINE) - jtag_gpio_cpld.gpio_pp_tms = gpio->cpld_pp_tms; - jtag_gpio_cpld.gpio_pp_tdo = gpio->cpld_pp_tdo; + if (board_id != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + jtag_gpio_cpld.gpio_tms = gpio->cpld_tms; + jtag_gpio_cpld.gpio_tdi = gpio->cpld_tdi; + jtag_gpio_cpld.gpio_tdo = gpio->cpld_tdo; #endif + } + switch (board_id) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + jtag_gpio_cpld.gpio_pp_tms = gpio->cpld_pp_tms; + jtag_gpio_cpld.gpio_pp_tdo = gpio->cpld_pp_tdo; +#endif + break; + default: + break; + } ssp1_set_mode_max283x(); mixer_bus_setup(&mixer); -#if defined(HACKRF_ONE) if (detected_platform() == BOARD_ID_HACKRF1_R9) { - rf_path.gpio_rx = gpio->h1r9_rx; +#if defined(HACKRF_ONE) || defined(UNIVERSAL) sgpio_config.gpio_trigger_enable = gpio->h1r9_trigger_enable; - } #endif + } - /* Configure RF path */ -#if defined(HACKRF_ONE) - rf_path = (rf_path_t){ - .switchctrl = 0, - .gpio_hp = gpio->hp, - .gpio_lp = gpio->lp, - .gpio_tx_mix_bp = gpio->tx_mix_bp, - .gpio_no_mix_bypass = gpio->no_mix_bypass, - .gpio_rx_mix_bp = gpio->rx_mix_bp, - .gpio_tx_amp = gpio->tx_amp, - .gpio_tx = gpio->tx, - .gpio_mix_bypass = gpio->mix_bypass, - .gpio_rx = gpio->rx, - .gpio_no_tx_amp_pwr = gpio->no_tx_amp_pwr, - .gpio_amp_bypass = gpio->amp_bypass, - .gpio_rx_amp = gpio->rx_amp, - .gpio_no_rx_amp_pwr = gpio->no_rx_amp_pwr, - }; - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - rf_path.gpio_rx = gpio->h1r9_rx; - rf_path.gpio_h1r9_no_ant_pwr = gpio->h1r9_no_ant_pwr; - } -#elif defined(RAD1O) - rf_path = (rf_path_t){ - .switchctrl = 0, - .gpio_tx_rx_n = gpio->tx_rx_n, - .gpio_tx_rx = gpio->tx_rx, - .gpio_by_mix = gpio->by_mix, - .gpio_by_mix_n = gpio->by_mix_n, - .gpio_by_amp = gpio->by_amp, - .gpio_by_amp_n = gpio->by_amp_n, - .gpio_mixer_en = gpio->mixer_en, - .gpio_low_high_filt = gpio->low_high_filt, - .gpio_low_high_filt_n = gpio->low_high_filt_n, - .gpio_tx_amp = gpio->tx_amp, - .gpio_rx_lna = gpio->rx_lna, - }; -#elif defined(PRALINE) - rf_path = (rf_path_t){ - .switchctrl = 0, - .gpio_tx_en = gpio->tx_en, - .gpio_mix_en_n = gpio->mix_en_n, - .gpio_lpf_en = gpio->lpf_en, - .gpio_rf_amp_en = gpio->rf_amp_en, - .gpio_ant_bias_en_n = gpio->ant_bias_en_n, - }; - board_rev_t rev = detected_revision(); - if ((rev == BOARD_REV_PRALINE_R1_0) || (rev == BOARD_REV_GSG_PRALINE_R1_0)) { - rf_path.gpio_mix_en_n = gpio->mix_en_n_r1_0; - } + // initialize rf_path struct and assign gpio's + switch (board_id) { + case BOARD_ID_JAWBREAKER: + break; + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + rf_path = (rf_path_t){ + .switchctrl = 0, + .gpio_hp = gpio->hp, + .gpio_lp = gpio->lp, + .gpio_tx_mix_bp = gpio->tx_mix_bp, + .gpio_no_mix_bypass = gpio->no_mix_bypass, + .gpio_rx_mix_bp = gpio->rx_mix_bp, + .gpio_tx_amp = gpio->tx_amp, + .gpio_tx = gpio->tx, + .gpio_mix_bypass = gpio->mix_bypass, + .gpio_rx = gpio->rx, + .gpio_no_tx_amp_pwr = gpio->no_tx_amp_pwr, + .gpio_amp_bypass = gpio->amp_bypass, + .gpio_rx_amp = gpio->rx_amp, + .gpio_no_rx_amp_pwr = gpio->no_rx_amp_pwr, + }; + if (board_id == BOARD_ID_HACKRF1_R9) { + rf_path.gpio_rx = gpio->h1r9_rx; + rf_path.gpio_h1r9_no_ant_pwr = gpio->h1r9_no_ant_pwr; + } #endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + rf_path = (rf_path_t){ + .switchctrl = 0, + .gpio_tx_rx_n = gpio->tx_rx_n, + .gpio_tx_rx = gpio->tx_rx, + .gpio_by_mix = gpio->by_mix, + .gpio_by_mix_n = gpio->by_mix_n, + .gpio_by_amp = gpio->by_amp, + .gpio_by_amp_n = gpio->by_amp_n, + .gpio_mixer_en = gpio->mixer_en, + .gpio_low_high_filt = gpio->low_high_filt, + .gpio_low_high_filt_n = gpio->low_high_filt_n, + .gpio_tx_amp = gpio->tx_amp, + .gpio_rx_lna = gpio->rx_lna, + }; +#endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + rf_path = (rf_path_t){ + .switchctrl = 0, + .gpio_tx_en = gpio->tx_en, + .gpio_mix_en_n = gpio->mix_en_n, + .gpio_lpf_en = gpio->lpf_en, + .gpio_rf_amp_en = gpio->rf_amp_en, + .gpio_ant_bias_en_n = gpio->ant_bias_en_n, + }; + if ((detected_revision() == BOARD_REV_PRALINE_R1_0) || + (detected_revision() == BOARD_REV_GSG_PRALINE_R1_0)) { + rf_path.gpio_mix_en_n = gpio->mix_en_n_r1_0; + } +#endif + break; + default: + break; + } rf_path_pin_setup(&rf_path); /* Configure external clock in */ @@ -1027,52 +1143,86 @@ void pin_setup(void) sgpio_configure_pin_functions(&sgpio_config); } -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) void enable_1v2_power(void) { - gpio_set(platform_gpio()->gpio_1v2_enable); + switch (detected_platform()) { + case BOARD_ID_PRALINE: + gpio_set(platform_gpio()->gpio_1v2_enable); + break; + default: + break; + } } void disable_1v2_power(void) { - gpio_clear(platform_gpio()->gpio_1v2_enable); + switch (detected_platform()) { + case BOARD_ID_PRALINE: + gpio_clear(platform_gpio()->gpio_1v2_enable); + break; + default: + break; + } } void enable_3v3aux_power(void) { - gpio_clear(platform_gpio()->gpio_3v3aux_enable_n); + switch (detected_platform()) { + case BOARD_ID_PRALINE: + gpio_clear(platform_gpio()->gpio_3v3aux_enable_n); + break; + default: + break; + } } void disable_3v3aux_power(void) { - gpio_set(platform_gpio()->gpio_3v3aux_enable_n); + switch (detected_platform()) { + case BOARD_ID_PRALINE: + gpio_set(platform_gpio()->gpio_3v3aux_enable_n); + break; + default: + break; + } } -#else +#endif + void enable_1v8_power(void) { - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - #ifdef HACKRF_ONE + switch (detected_platform()) { + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) gpio_set(platform_gpio()->h1r9_1v8_enable); - #endif - } else { +#endif + break; + case BOARD_ID_PRALINE: + break; + default: gpio_set(platform_gpio()->gpio_1v8_enable); + break; } } void disable_1v8_power(void) { - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - #ifdef HACKRF_ONE + switch (detected_platform()) { + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) gpio_clear(platform_gpio()->h1r9_1v8_enable); - #endif - } else { +#endif + break; + case BOARD_ID_PRALINE: + break; + default: gpio_clear(platform_gpio()->gpio_1v8_enable); + break; } } -#endif -#ifdef HACKRF_ONE -void enable_rf_power(void) +#if defined(HACKRF_ONE) || defined(UNIVERSAL) +static inline void enable_rf_power_hackrf_one(void) { const platform_gpio_t* gpio = platform_gpio(); uint32_t i; @@ -1089,7 +1239,7 @@ void enable_rf_power(void) } } -void disable_rf_power(void) +static inline void disable_rf_power_hackrf_one(void) { if (detected_platform() == BOARD_ID_HACKRF1_R9) { gpio_set(platform_gpio()->h1r9_vaa_disable); @@ -1099,8 +1249,8 @@ void disable_rf_power(void) } #endif -#ifdef PRALINE -void enable_rf_power(void) +#if defined(PRALINE) || defined(UNIVERSAL) +static inline void enable_rf_power_praline(void) { gpio_clear(platform_gpio()->vaa_disable); @@ -1108,14 +1258,14 @@ void enable_rf_power(void) delay(1000000); } -void disable_rf_power(void) +static inline void disable_rf_power_praline(void) { gpio_set(platform_gpio()->vaa_disable); } #endif -#ifdef RAD1O -void enable_rf_power(void) +#if defined(RAD1O) +static inline void enable_rf_power_rad1o(void) { gpio_set(platform_gpio()->vaa_enable); @@ -1123,33 +1273,77 @@ void enable_rf_power(void) delay(1000000); } -void disable_rf_power(void) +static inline void disable_rf_power_rad1o(void) { gpio_clear(platform_gpio()->vaa_enable); } #endif -#ifdef PRALINE -void led_on(const led_t led) +void enable_rf_power(void) { - gpio_clear(platform_gpio()->led[led]); -} - -void led_off(const led_t led) -{ - gpio_set(platform_gpio()->led[led]); -} -#else -void led_on(const led_t led) -{ - gpio_set(platform_gpio()->led[led]); -} - -void led_off(const led_t led) -{ - gpio_clear(platform_gpio()->led[led]); -} + switch (detected_platform()) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + enable_rf_power_hackrf_one(); #endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + enable_rf_power_praline(); +#endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + enable_rf_power_rad1o(); +#endif + break; + default: + break; + } +} + +void disable_rf_power(void) +{ + switch (detected_platform()) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + disable_rf_power_hackrf_one(); +#endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + disable_rf_power_praline(); +#endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + disable_rf_power_rad1o(); +#endif + break; + default: + break; + } +} + +void led_on(const led_t led) +{ + if (detected_platform() == BOARD_ID_PRALINE) { + gpio_clear(platform_gpio()->led[led]); + } else { + gpio_set(platform_gpio()->led[led]); + } +} + +void led_off(const led_t led) +{ + if (detected_platform() == BOARD_ID_PRALINE) { + gpio_set(platform_gpio()->led[led]); + } else { + gpio_clear(platform_gpio()->led[led]); + } +} void led_toggle(const led_t led) { @@ -1158,26 +1352,33 @@ void led_toggle(const led_t led) void set_leds(const uint8_t state) { + board_id_t board_id = detected_platform(); + int num_leds = 3; -#if (defined RAD1O || defined PRALINE) - num_leds = 4; -#endif + if (board_id == BOARD_ID_RAD1O || board_id == BOARD_ID_PRALINE) { + num_leds = 4; + } + for (int i = 0; i < num_leds; i++) { -#ifdef PRALINE - gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 0); -#else - gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 1); -#endif + if (board_id == BOARD_ID_PRALINE) { + gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 0); + } else { + gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 1); + } } } void trigger_enable(const bool enable) { -#ifndef PRALINE - gpio_write(sgpio_config.gpio_trigger_enable, enable); -#else - fpga_set_trigger_enable(&fpga, enable); + if (detected_platform() != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + gpio_write(sgpio_config.gpio_trigger_enable, enable); #endif + } else { +#if defined(PRALINE) || defined(UNIVERSAL) + fpga_set_trigger_enable(&fpga, enable); +#endif + } } void halt_and_flash(const uint32_t duration) @@ -1195,7 +1396,7 @@ void halt_and_flash(const uint32_t duration) } } -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) void p1_ctrl_set(const p1_ctrl_signal_t signal) { const platform_gpio_t* gpio = platform_gpio(); diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 539ce770..93bc44c9 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Benjamin Vernoux * Copyright 2012 Jared Boone * @@ -42,7 +42,7 @@ extern "C" { #include "si5351c.h" #include "spi_ssp.h" #include "w25q80bv.h" -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) #include "fpga.h" #include "ice40_spi.h" #endif @@ -51,7 +51,8 @@ extern "C" { extern si5351c_driver_t clock_gen; extern ssp_config_t ssp_config_w25q80bv; -#ifdef PRALINE +extern max283x_driver_t max283x; +#if defined(PRALINE) || defined(UNIVERSAL) extern ice40_spi_driver_t ice40; extern fpga_driver_t fpga; #endif @@ -70,28 +71,27 @@ void clock_gen_init(void); void clock_gen_shutdown(void); void ssp1_set_mode_max283x(void); void ssp1_set_mode_max5864(void); -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) void ssp1_set_mode_ice40(void); #endif void pin_shutdown(void); void pin_setup(void); -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) void enable_1v2_power(void); void disable_1v2_power(void); void enable_3v3aux_power(void); void disable_3v3aux_power(void); -#else +#endif void enable_1v8_power(void); void disable_1v8_power(void); -#endif fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program); clock_source_t activate_best_clock_source(void); -#if (defined HACKRF_ONE || defined RAD1O || defined PRALINE) +#if defined(RAD1O) || defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) void enable_rf_power(void); void disable_rf_power(void); #endif @@ -112,7 +112,7 @@ void trigger_enable(const bool enable); void halt_and_flash(const uint32_t duration); -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) typedef enum { P1_SIGNAL_TRIGGER_IN = 0, P1_SIGNAL_AUX_CLK1 = 1, diff --git a/firmware/common/selftest.h b/firmware/common/selftest.h index 28e02415..8223a582 100644 --- a/firmware/common/selftest.h +++ b/firmware/common/selftest.h @@ -37,23 +37,22 @@ typedef uint8_t test_result_t; typedef struct { uint16_t mixer_id; -#ifndef RAD1O +#if !defined(RAD1O) bool mixer_locks[NUM_LOCK_ATTEMPTS]; #endif -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) uint16_t max2831_mux_rssi_1; uint16_t max2831_mux_temp; uint16_t max2831_mux_rssi_2; bool max2831_mux_test_ok; -#else +#endif uint16_t max283x_readback_bad_value; uint16_t max283x_readback_expected_value; uint8_t max283x_readback_register_count; uint8_t max283x_readback_total_registers; -#endif uint8_t si5351_rev_id; bool si5351_readback_ok; -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) test_result_t fpga_image_load; test_result_t fpga_spi; test_result_t sgpio_rx; From 85dfacf6efa420955c949aa426282ad7a1681d23 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 4 Apr 2026 01:12:34 +0100 Subject: [PATCH 058/173] Universalize: firmware/hackrf_usb Co-authored-by: Antoine van Gelder --- firmware/hackrf_usb/hackrf_usb.c | 192 ++++++++++++++-------- firmware/hackrf_usb/usb_api_board_info.c | 15 +- firmware/hackrf_usb/usb_api_cpld.c | 10 ++ firmware/hackrf_usb/usb_api_praline.c | 21 +++ firmware/hackrf_usb/usb_api_register.h | 2 +- firmware/hackrf_usb/usb_api_selftest.c | 170 ++++++++++--------- firmware/hackrf_usb/usb_api_sweep.c | 19 ++- firmware/hackrf_usb/usb_api_transceiver.c | 11 ++ firmware/hackrf_usb/usb_descriptor.c | 132 +++++++++++---- firmware/hackrf_usb/usb_descriptor.h | 28 +++- firmware/hackrf_usb/usb_device.c | 43 ++++- firmware/hackrf_usb/usb_device.h | 13 ++ 12 files changed, 453 insertions(+), 203 deletions(-) diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 3238ca0e..4952f85d 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -48,23 +48,22 @@ #include #include #include -#if !defined(RAD1O) - #include -#endif -#if defined(PRALINE) || defined(HACKRF_ONE) +#if defined(PRALINE) || defined(HACKRF_ONE) || defined(UNIVERSAL) #include #endif -#if defined(PRALINE) || defined(HACKRF_ONE) || defined(JAWBREAKER) +#if !defined(RAD1O) + #include #include #endif -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) #include #if !(defined(DFU_MODE) || defined(RAM_MODE)) #include #include #include #endif -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) #include #include #endif @@ -82,9 +81,10 @@ #include "usb_descriptor.h" #include "usb_device.h" #include "usb_endpoint.h" -#if defined(PRALINE) +#if defined(PRALINE) || defined(UNIVERSAL) #include "usb_api_praline.h" -#else +#endif +#if !defined(PRALINE) || defined(UNIVERSAL) #include "usb_api_cpld.h" #endif @@ -102,12 +102,12 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_read_si5351c, usb_vendor_request_set_sample_rate_frac, usb_vendor_request_set_baseband_filter_bandwidth, -#ifdef RAD1O - NULL, // write_rffc5071 not used - NULL, // read_rffc5071 not used -#else +#if !defined(RAD1O) usb_vendor_request_write_rffc5071, usb_vendor_request_read_rffc5071, +#else + NULL, // write_rffc5071 not used + NULL, // read_rffc5071 not used #endif usb_vendor_request_erase_spiflash, usb_vendor_request_write_spiflash, @@ -122,7 +122,7 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_set_vga_gain, usb_vendor_request_set_txvga_gain, NULL, // was set_if_freq -#if (defined HACKRF_ONE || defined PRALINE) +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) usb_vendor_request_set_antenna_enable, #else NULL, @@ -139,7 +139,7 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_spiflash_status, usb_vendor_request_spiflash_clear_status, usb_vendor_request_operacake_gpio_test, -#ifdef HACKRF_ONE +#if defined(HACKRF_ONE) || defined(UNIVERSAL) usb_vendor_request_cpld_checksum, #else NULL, @@ -156,7 +156,7 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_read_supported_platform, usb_vendor_request_set_leds, usb_vendor_request_user_config_set_bias_t_opts, -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) usb_vendor_request_write_fpga_reg, usb_vendor_request_read_fpga_reg, usb_vendor_request_p2_ctrl, @@ -252,7 +252,7 @@ void usb_set_descriptor_by_serial_number(void) } } -#ifndef PRALINE +#if !defined(PRALINE) || defined(UNIVERSAL) static bool cpld_jtag_sram_load(jtag_t* const jtag) { cpld_jtag_take(jtag); @@ -280,7 +280,7 @@ static void m0_rom_to_ram(void) memcpy(dest, (uint32_t*) (base + src), len); } -#if defined(PRALINE) && !(defined(DFU_MODE) || defined(RAM_MODE)) +#if (defined(PRALINE) || defined(UNIVERSAL)) && !(defined(DFU_MODE) || defined(RAM_MODE)) extern uint32_t _binary_fpga_bin_start; void fpga_loader_setup(void) @@ -347,7 +347,7 @@ void radio_changed(const uint32_t changed) if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { hackrf_ui()->set_frequency(freq / FP_ONE_HZ); } -#if defined(HACKRF_ONE) || defined(PRALINE) +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) operacake_set_range(freq / FP_ONE_MHZ); #endif } @@ -435,74 +435,91 @@ int main(void) // Detect hardware platform before we do anything else. detect_hardware_platform(); + board_id_t board_id = detected_platform(); pin_shutdown(); -#ifndef RAD1O - clock_gen_shutdown(); -#endif + if (board_id != BOARD_ID_RAD1O) { + clock_gen_shutdown(); + } delay_us_at_mhz(10000, 96); pin_setup(); -#ifndef PRALINE - enable_1v8_power(); - #ifndef RAD1O - /* - * On rad1o, the clock generator power supply comes from the RF supply - * which is enabled later. On H1 and Jawbreaker, the clock generator is - * on the main 3V3 supply. - */ - clock_gen_init(); - #endif -#else - enable_3v3aux_power(); + if (board_id != BOARD_ID_PRALINE) { + enable_1v8_power(); + if (board_id != BOARD_ID_RAD1O) { + /* + * On rad1o, the clock generator power supply comes from the RF supply + * which is enabled later. On H1 and Jawbreaker, the clock generator is + * on the main 3V3 supply. + */ + clock_gen_init(); + } + } else { +#if defined(PRALINE) || defined(UNIVERSAL) + enable_3v3aux_power(); #if !defined(DFU_MODE) && !defined(RAM_MODE) - enable_1v2_power(); - enable_rf_power(); - /* - * On Praline, the clock generator power supply comes from 3V3FPGA - * which is enabled when 1V2FPGA is turned on. - */ - clock_gen_init(); + enable_1v2_power(); + enable_rf_power(); + /* + * On Praline, the clock generator power supply comes from 3V3FPGA + * which is enabled when 1V2FPGA is turned on. + */ + clock_gen_init(); #endif #endif + } tuning_setup(); -#ifdef HACKRF_ONE - // Set up mixer before enabling RF power, because its - // GPO is used to control the antenna bias tee. - mixer_setup(&mixer, RFFC5071_VARIANT); + if (board_id == BOARD_ID_HACKRF1_OG || board_id == BOARD_ID_HACKRF1_R9) { +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + // Set up mixer before enabling RF power, because its + // GPO is used to control the antenna bias tee. + mixer_setup(&mixer, RFFC5071_VARIANT); #endif -#if (defined HACKRF_ONE || defined RAD1O) - enable_rf_power(); -#endif -#ifdef RAD1O - clock_gen_init(); + } + if (board_id == BOARD_ID_HACKRF1_OG || board_id == BOARD_ID_HACKRF1_R9 || + board_id == BOARD_ID_RAD1O) { +#if defined(RAD1O) || defined(HACKRF_ONE) || defined(UNIVERSAL) + enable_rf_power(); #endif + } + if (board_id == BOARD_ID_RAD1O) { + clock_gen_init(); + } cpu_clock_init(); /* Wake the M0 */ ipc_halt_m0(); ipc_start_m0((uint32_t) &__ram_m0_start__); -#ifndef PRALINE - if (!cpld_jtag_sram_load(&jtag_cpld)) { - halt_and_flash(6000000); +#if !defined(PRALINE) || defined(UNIVERSAL) + if (board_id != BOARD_ID_PRALINE) { + if (!cpld_jtag_sram_load(&jtag_cpld)) { + halt_and_flash(6000000); + } } -#else - #if defined(DFU_MODE) || defined(RAM_MODE) - selftest.fpga_image_load = SKIPPED; - selftest.report.pass = false; - #else - fpga_image_load(&fpga_loader, 0); - #endif - delay_us_at_mhz(100, 204); - fpga_spi_selftest(); - fpga_sgpio_selftest(); #endif +#if defined(PRALINE) || defined(UNIVERSAL) + if (board_id == BOARD_ID_PRALINE) { + #if defined(DFU_MODE) || defined(RAM_MODE) + selftest.fpga_image_load = SKIPPED; + selftest.report.pass = false; + #else + fpga_image_load(&fpga_loader, 0); + #endif + delay_us_at_mhz(100, 204); + fpga_spi_selftest(); + fpga_sgpio_selftest(); + } +#endif + radio.update_cb = radio_changed; radio_init(&radio); -#if (defined HACKRF_ONE || defined PRALINE) - portapack_init(); + if (board_id == BOARD_ID_HACKRF1_OG || board_id == BOARD_ID_HACKRF1_R9 || + board_id == BOARD_ID_PRALINE) { +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + portapack_init(); #endif + } #ifndef DFU_MODE usb_set_descriptor_by_serial_number(); @@ -511,6 +528,35 @@ int main(void) usb_set_configuration_changed_cb(usb_configuration_changed); usb_peripheral_reset(); + switch (detected_platform()) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + memcpy(&usb_device, + &usb_device_hackrf_one, + sizeof(usb_device_hackrf_one)); +#endif + break; + case BOARD_ID_JAWBREAKER: +#if defined(JAWBREAKER) + memcpy(&usb_device, + &usb_device_jawbreaker, + sizeof(usb_device_jawbreaker)); +#endif + break; + case BOARD_ID_RAD1O: +#if defined(RAD1O) + memcpy(&usb_device, &usb_device_rad1o, sizeof(usb_device_rad1o)); +#endif + break; + case BOARD_ID_PRALINE: +#if defined(PRALINE) || defined(UNIVERSAL) + memcpy(&usb_device, &usb_device_praline, sizeof(usb_device_praline)); +#endif + break; + default: + break; + }; usb_device_init(0, &usb_device); usb_queue_init(&usb_endpoint_control_out_queue); @@ -529,13 +575,15 @@ int main(void) rf_path_init(&rf_path); -#ifndef RAD1O +#if !defined(RAD1O) rffc5071_lock_test(&mixer.rffc5071); #endif -#ifdef PRALINE - fpga_if_xcvr_selftest(); + if (board_id == BOARD_ID_PRALINE) { +#if defined(PRALINE) || defined(UNIVERSAL) + fpga_if_xcvr_selftest(); #endif + } if (da7219_detect()) { operacake_skip_i2c_address(DA7219_ADDRESS); @@ -581,11 +629,13 @@ int main(void) case TRANSCEIVER_MODE_RX_SWEEP: sweep_mode(request.seq); break; -#ifndef PRALINE case TRANSCEIVER_MODE_CPLD_UPDATE: - cpld_update(); - break; + if (board_id != BOARD_ID_PRALINE) { +#if !defined(PRALINE) || defined(UNIVERSAL) + cpld_update(); #endif + } + break; default: break; } diff --git a/firmware/hackrf_usb/usb_api_board_info.c b/firmware/hackrf_usb/usb_api_board_info.c index a1ad3bce..8e64ad7f 100644 --- a/firmware/hackrf_usb/usb_api_board_info.c +++ b/firmware/hackrf_usb/usb_api_board_info.c @@ -34,7 +34,7 @@ #include #include #include -#ifdef HACKRF_ONE +#if defined(HACKRF_ONE) || defined(UNIVERSAL) #include #include #endif @@ -131,18 +131,19 @@ usb_request_status_t usb_vendor_request_reset( if (stage == USB_TRANSFER_STAGE_SETUP) { pin_shutdown(); clock_gen_shutdown(); -#ifdef HACKRF_ONE - /* - * Set boot pins as inputs so that the bootloader reads them - * correctly after the reset. - */ if (detected_platform() == BOARD_ID_HACKRF1_R9) { +#if defined(HACKRF_ONE) || defined(UNIVERSAL) + /* + * Set boot pins as inputs so that the bootloader reads them + * correctly after the reset. + */ const platform_gpio_t* gpio = platform_gpio(); gpio_input(gpio->h1r9_mcu_clk_en); gpio_input(gpio->h1r9_clkout_en); gpio_input(gpio->h1r9_rx); - } #endif + } + wwdt_reset(100000); usb_transfer_schedule_ack(endpoint->in); diff --git a/firmware/hackrf_usb/usb_api_cpld.c b/firmware/hackrf_usb/usb_api_cpld.c index 7d8200c3..05636a0b 100644 --- a/firmware/hackrf_usb/usb_api_cpld.c +++ b/firmware/hackrf_usb/usb_api_cpld.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +91,15 @@ usb_request_status_t usb_vendor_request_cpld_checksum( static uint32_t cpld_crc; uint8_t length; + switch (detected_platform()) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + // supported + break; + default: + return USB_REQUEST_STATUS_STALL; + } + if (stage == USB_TRANSFER_STAGE_SETUP) { cpld_jtag_take(&jtag_cpld); const bool checksum_success = cpld_xc2c64a_jtag_checksum( diff --git a/firmware/hackrf_usb/usb_api_praline.c b/firmware/hackrf_usb/usb_api_praline.c index 6a44da60..2a41b839 100644 --- a/firmware/hackrf_usb/usb_api_praline.c +++ b/firmware/hackrf_usb/usb_api_praline.c @@ -24,6 +24,7 @@ #include "usb_api_praline.h" #include +#include #include #include #include @@ -35,6 +36,10 @@ usb_request_status_t usb_vendor_request_p1_ctrl( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (detected_platform() != BOARD_ID_PRALINE) { + return USB_REQUEST_STATUS_STALL; + } + if (stage == USB_TRANSFER_STAGE_SETUP) { p1_ctrl_set(endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); @@ -46,6 +51,10 @@ usb_request_status_t usb_vendor_request_p2_ctrl( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (detected_platform() != BOARD_ID_PRALINE) { + return USB_REQUEST_STATUS_STALL; + } + if (stage == USB_TRANSFER_STAGE_SETUP) { p2_ctrl_set(endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); @@ -57,6 +66,10 @@ usb_request_status_t usb_vendor_request_clkin_ctrl( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (detected_platform() != BOARD_ID_PRALINE) { + return USB_REQUEST_STATUS_STALL; + } + if (stage == USB_TRANSFER_STAGE_SETUP) { clkin_ctrl_set(endpoint->setup.value & 1); usb_transfer_schedule_ack(endpoint->in); @@ -68,6 +81,10 @@ usb_request_status_t usb_vendor_request_set_narrowband_filter( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (detected_platform() != BOARD_ID_PRALINE) { + return USB_REQUEST_STATUS_STALL; + } + if (stage == USB_TRANSFER_STAGE_SETUP) { narrowband_filter_set(endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); @@ -86,6 +103,10 @@ usb_request_status_t usb_vendor_request_set_fpga_bitstream( #else extern struct fpga_loader_t fpga_loader; + if (detected_platform() != BOARD_ID_PRALINE) { + return USB_REQUEST_STATUS_STALL; + } + if (stage == USB_TRANSFER_STAGE_SETUP) { if (!fpga_image_load(&fpga_loader, endpoint->setup.value)) { return USB_REQUEST_STATUS_STALL; diff --git a/firmware/hackrf_usb/usb_api_register.h b/firmware/hackrf_usb/usb_api_register.h index 24825144..58a84aff 100644 --- a/firmware/hackrf_usb/usb_api_register.h +++ b/firmware/hackrf_usb/usb_api_register.h @@ -56,7 +56,7 @@ usb_request_status_t usb_vendor_request_set_leds( usb_request_status_t usb_vendor_request_user_config_set_bias_t_opts( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) usb_request_status_t usb_vendor_request_write_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); diff --git a/firmware/hackrf_usb/usb_api_selftest.c b/firmware/hackrf_usb/usb_api_selftest.c index 4fe2e628..fa5267b5 100644 --- a/firmware/hackrf_usb/usb_api_selftest.c +++ b/firmware/hackrf_usb/usb_api_selftest.c @@ -21,20 +21,18 @@ #include #include -#if defined(PRALINE) || defined(HACKRF_ONE) || defined(JAWBREAKER) +#if !defined(RAD1O) #include #endif #include #include +#include #include #include #include #include -#if !defined(PRALINE) - #include -#endif #include "usb_api_selftest.h" @@ -63,7 +61,7 @@ void append(char** dest, size_t* capacity, const char* str) } } -#ifdef PRALINE +#if defined(PRALINE) || defined(UNIVERSAL) static const char* test_result_to_str(test_result_t result) { switch (result) { @@ -82,91 +80,105 @@ static const char* test_result_to_str(test_result_t result) void generate_selftest_report(void) { + board_id_t board_id = detected_platform(); + char* s = &selftest.report.msg[0]; size_t c = sizeof(selftest.report.msg); -#ifdef RAD1O - append(&s, &c, "Mixer: MAX2871, ID: "); - append(&s, &c, itoa(selftest.mixer_id, 10)); - append(&s, &c, "\n"); -#else - append(&s, &c, "Mixer: RFFC5072, ID: "); - append(&s, &c, itoa(selftest.mixer_id >> 3, 10)); - append(&s, &c, ", Rev: "); - append(&s, &c, itoa(selftest.mixer_id & 0x7, 10)); - append(&s, &c, ", Locks: "); - bool lock; - for (int i = 0; i < NUM_LOCK_ATTEMPTS; i++) { - lock = selftest.mixer_locks[i]; - append(&s, &c, itoa(lock, 2)); - } - if (lock) { - append(&s, &c, " (PASS)"); - } else { - append(&s, &c, " (FAIL)"); - } - append(&s, &c, "\n"); + if (board_id == BOARD_ID_RAD1O) { +#if defined(RAD1O) + append(&s, &c, "Mixer: MAX2871, ID: "); + append(&s, &c, itoa(selftest.mixer_id, 10)); + append(&s, &c, "\n"); #endif + } else { +#if !defined(RAD1O) + append(&s, &c, "Mixer: RFFC5072, ID: "); + append(&s, &c, itoa(selftest.mixer_id >> 3, 10)); + append(&s, &c, ", Rev: "); + append(&s, &c, itoa(selftest.mixer_id & 0x7, 10)); + append(&s, &c, ", Locks: "); + bool lock; + for (int i = 0; i < NUM_LOCK_ATTEMPTS; i++) { + lock = selftest.mixer_locks[i]; + append(&s, &c, itoa(lock, 2)); + } + if (lock) { + append(&s, &c, " (PASS)"); + } else { + append(&s, &c, " (FAIL)"); + } + append(&s, &c, "\n"); +#endif + } + append(&s, &c, "Clock: Si5351"); append(&s, &c, ", Rev: "); append(&s, &c, itoa(selftest.si5351_rev_id, 10)); append(&s, &c, ", readback: "); append(&s, &c, selftest.si5351_readback_ok ? "OK" : "FAIL"); append(&s, &c, "\n"); -#ifdef PRALINE - append(&s, &c, "Transceiver: MAX2831, RSSI mux test: "); - append(&s, &c, selftest.max2831_mux_test_ok ? "PASS" : "FAIL"); - append(&s, &c, "\n"); -#else - append(&s, &c, "Transceiver: "); - append(&s, - &c, - (detected_platform() == BOARD_ID_HACKRF1_R9 ? "MAX2839" : "MAX2837")); - append(&s, &c, ", readback success: "); - append(&s, &c, itoa(selftest.max283x_readback_register_count, 10)); - append(&s, &c, "/"); - append(&s, &c, itoa(selftest.max283x_readback_total_registers, 10)); - if (selftest.max283x_readback_register_count < - selftest.max283x_readback_total_registers) { - append(&s, &c, ", bad value: 0x"); - append(&s, &c, itoa(selftest.max283x_readback_bad_value, 10)); - append(&s, &c, ", expected: 0x"); - append(&s, &c, itoa(selftest.max283x_readback_expected_value, 10)); - } - append(&s, &c, "\n"); -#endif -#ifdef PRALINE - append(&s, &c, "FPGA configuration: "); - append(&s, &c, test_result_to_str(selftest.fpga_image_load)); - append(&s, &c, "\n"); - append(&s, &c, "FPGA SPI: "); - append(&s, &c, test_result_to_str(selftest.fpga_spi)); - append(&s, &c, "\n"); - append(&s, &c, "SGPIO RX test: "); - append(&s, &c, test_result_to_str(selftest.sgpio_rx)); - append(&s, &c, "\n"); - append(&s, &c, "Loopback test: "); - append(&s, &c, test_result_to_str(selftest.xcvr_loopback)); - append(&s, &c, "\n"); - // Dump transceiver loopback measurements. - for (int i = 0; i < 4; ++i) { - struct xcvr_measurements* m = &selftest.xcvr_measurements[i]; - append(&s, &c, " "); - append(&s, &c, itoa(i, 10)); - append(&s, &c, ":"); - append(&s, &c, itoa(m->zcs_i, 10)); - append(&s, &c, ","); - append(&s, &c, itoa(m->zcs_q, 10)); - append(&s, &c, ","); - append(&s, &c, itoa(m->max_mag_i, 10)); - append(&s, &c, ","); - append(&s, &c, itoa(m->max_mag_q, 10)); - append(&s, &c, ","); - append(&s, &c, itoa(m->avg_mag_sq_i, 10)); - append(&s, &c, ","); - append(&s, &c, itoa(m->avg_mag_sq_q, 10)); + + if (board_id == BOARD_ID_PRALINE) { +#if defined(PRALINE) || defined(UNIVERSAL) + append(&s, &c, "Transceiver: MAX2831, RSSI mux test: "); + append(&s, &c, selftest.max2831_mux_test_ok ? "PASS" : "FAIL"); append(&s, &c, "\n"); - } #endif + } else { +#if !defined(PRALINE) || defined(UNIVERSAL) + append(&s, &c, "Transceiver: "); + append(&s, &c, (board_id == BOARD_ID_HACKRF1_R9 ? "MAX2839" : "MAX2837")); + append(&s, &c, ", readback success: "); + append(&s, &c, itoa(selftest.max283x_readback_register_count, 10)); + append(&s, &c, "/"); + append(&s, &c, itoa(selftest.max283x_readback_total_registers, 10)); + if (selftest.max283x_readback_register_count < + selftest.max283x_readback_total_registers) { + append(&s, &c, ", bad value: 0x"); + append(&s, &c, itoa(selftest.max283x_readback_bad_value, 10)); + append(&s, &c, ", expected: 0x"); + append(&s, + &c, + itoa(selftest.max283x_readback_expected_value, 10)); + } + append(&s, &c, "\n"); +#endif + } + if (board_id == BOARD_ID_PRALINE) { +#if defined(PRALINE) || defined(UNIVERSAL) + append(&s, &c, "FPGA configuration: "); + append(&s, &c, test_result_to_str(selftest.fpga_image_load)); + append(&s, &c, "\n"); + append(&s, &c, "FPGA SPI: "); + append(&s, &c, test_result_to_str(selftest.fpga_spi)); + append(&s, &c, "\n"); + append(&s, &c, "SGPIO RX test: "); + append(&s, &c, test_result_to_str(selftest.sgpio_rx)); + append(&s, &c, "\n"); + append(&s, &c, "Loopback test: "); + append(&s, &c, test_result_to_str(selftest.xcvr_loopback)); + append(&s, &c, "\n"); + // Dump transceiver loopback measurements. + for (int i = 0; i < 4; ++i) { + struct xcvr_measurements* m = &selftest.xcvr_measurements[i]; + append(&s, &c, " "); + append(&s, &c, itoa(i, 10)); + append(&s, &c, ":"); + append(&s, &c, itoa(m->zcs_i, 10)); + append(&s, &c, ","); + append(&s, &c, itoa(m->zcs_q, 10)); + append(&s, &c, ","); + append(&s, &c, itoa(m->max_mag_i, 10)); + append(&s, &c, ","); + append(&s, &c, itoa(m->max_mag_q, 10)); + append(&s, &c, ","); + append(&s, &c, itoa(m->avg_mag_sq_i, 10)); + append(&s, &c, ","); + append(&s, &c, itoa(m->avg_mag_sq_q, 10)); + append(&s, &c, "\n"); + } +#endif + } } usb_request_status_t usb_vendor_request_read_selftest( diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index a9bfc695..4f7d8e57 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -46,11 +47,6 @@ #define MAX(x, y) ((x) > (y) ? (x) : (y)) #define FREQ_GRANULARITY 1000000 #define MAX_RANGES 10 -#ifndef PRALINE - #define THROWAWAY_BUFFERS 2 -#else - #define THROWAWAY_BUFFERS 1 -#endif static uint64_t sweep_freq; static uint16_t frequencies[MAX_RANGES * 2]; @@ -59,6 +55,7 @@ static uint16_t num_ranges = 0; static uint32_t dwell_blocks = 0; static uint32_t step_width = 0; static uint32_t offset = 0; +static uint32_t throwaway_buffers = 0; static enum sweep_style style = LINEAR; static bool freq_ui_dirty = false; static bool img_reject_ui_dirty = false; @@ -91,6 +88,12 @@ usb_request_status_t usb_vendor_request_init_sweep( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (detected_platform() == BOARD_ID_PRALINE) { + throwaway_buffers = 1; + } else { + throwaway_buffers = 2; + } + uint32_t num_bytes; int i; if (stage == USB_TRANSFER_STAGE_SETUP) { @@ -144,7 +147,7 @@ void sweep_bulk_transfer_complete(void* user_data, unsigned int bytes_transferre // For each buffer transferred, we need to bump the count to allow // for the buffer(s) that are to be discarded. - m0_state.m4_count += (THROWAWAY_BUFFERS + 1) * 0x4000; + m0_state.m4_count += (throwaway_buffers + 1) * 0x4000; } void sweep_mode(uint32_t seq) @@ -200,7 +203,7 @@ void sweep_mode(uint32_t seq) // Set M0 to switch back to RX after we have received our // discard buffers. - m0_state.threshold += (0x4000 * THROWAWAY_BUFFERS); + m0_state.threshold += (0x4000 * throwaway_buffers); m0_state.next_mode = M0_MODE_RX; // Write metadata to buffer. @@ -225,7 +228,7 @@ void sweep_mode(uint32_t seq) NULL); // Use other buffer next time. - phase = (phase + 1) % THROWAWAY_BUFFERS; + phase = (phase + 1) % throwaway_buffers; if (++blocks_queued == dwell_blocks) { // Calculate next sweep frequency. diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index 12c516c9..be16b1be 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -282,6 +283,16 @@ usb_request_status_t usb_vendor_request_set_antenna_enable( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + switch (detected_platform()) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: + // supported + break; + default: + return USB_REQUEST_STATUS_STALL; + } + if (stage == USB_TRANSFER_STAGE_SETUP) { radio_reg_write( &radio, diff --git a/firmware/hackrf_usb/usb_descriptor.c b/firmware/hackrf_usb/usb_descriptor.c index 7c98f0ca..f213f484 100644 --- a/firmware/hackrf_usb/usb_descriptor.c +++ b/firmware/hackrf_usb/usb_descriptor.c @@ -51,7 +51,8 @@ #define USB_STRING_LANGID (0x0409) -uint8_t usb_descriptor_device[] = { +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) +uint8_t usb_descriptor_device_hackrf[] = { 18, // bLength USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType USB_WORD(0x0200), // bcdUSB @@ -60,13 +61,50 @@ uint8_t usb_descriptor_device[] = { 0x00, // bDeviceProtocol USB_MAX_PACKET0, // bMaxPacketSize0 USB_WORD(USB_VENDOR_ID), // idVendor - USB_WORD(USB_PRODUCT_ID), // idProduct + USB_WORD(0x6089), // idProduct USB_WORD(USB_API_VERSION), // bcdDevice 0x01, // iManufacturer 0x02, // iProduct 0x04, // iSerialNumber 0x01 // bNumConfigurations }; +#endif +#if defined(JAWBREAKER) +uint8_t usb_descriptor_device_jawbreaker[] = { + 18, // bLength + USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + USB_MAX_PACKET0, // bMaxPacketSize0 + USB_WORD(USB_VENDOR_ID), // idVendor + USB_WORD(0x604B), // idProduct + USB_WORD(USB_API_VERSION), // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x04, // iSerialNumber + 0x01 // bNumConfigurations +}; +#endif +#if defined(RAD1O) +uint8_t usb_descriptor_device_rad1o[] = { + 18, // bLength + USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + USB_MAX_PACKET0, // bMaxPacketSize0 + USB_WORD(USB_VENDOR_ID), // idVendor + USB_WORD(0xCC15), // idProduct + USB_WORD(USB_API_VERSION), // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x04, // iSerialNumber + 0x01 // bNumConfigurations +}; +#endif uint8_t usb_descriptor_device_qualifier[] = { 10, // bLength @@ -185,8 +223,8 @@ uint8_t usb_descriptor_string_manufacturer[] = { 's', 0x00, }; -uint8_t usb_descriptor_string_product[] = { -#ifdef HACKRF_ONE +#if defined(HACKRF_ONE) || defined(UNIVERSAL) +uint8_t usb_descriptor_string_product_hackrf_one[] = { 22, // bLength USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType 'H', 0x00, @@ -199,7 +237,26 @@ uint8_t usb_descriptor_string_product[] = { 'O', 0x00, 'n', 0x00, 'e', 0x00, -#elif JAWBREAKER +}; +#endif +#if defined(PRALINE) || defined(UNIVERSAL) +uint8_t usb_descriptor_string_product_praline[] = { + 22, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'H', 0x00, + 'a', 0x00, + 'c', 0x00, + 'k', 0x00, + 'R', 0x00, + 'F', 0x00, + ' ', 0x00, + 'P', 0x00, + 'r', 0x00, + 'o', 0x00, +}; +#endif +#if defined(JAWBREAKER) +uint8_t usb_descriptor_string_product_jawbreaker[] = { 36, // bLength USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType 'H', 0x00, @@ -219,7 +276,10 @@ uint8_t usb_descriptor_string_product[] = { 'k', 0x00, 'e', 0x00, 'r', 0x00, -#elif RAD1O +}; +#endif +#if defined(RAD1O) +uint8_t usb_descriptor_string_product_rad1o[] = { 12, // bLength USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType 'r', 0x00, @@ -227,30 +287,8 @@ uint8_t usb_descriptor_string_product[] = { 'd', 0x00, '1', 0x00, 'o', 0x00, -#elif PRALINE - 22, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - 'H', 0x00, - 'a', 0x00, - 'c', 0x00, - 'k', 0x00, - 'R', 0x00, - 'F', 0x00, - ' ', 0x00, - 'P', 0x00, - 'r', 0x00, - 'o', 0x00, -#else - 14, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - 'H', 0x00, - 'a', 0x00, - 'c', 0x00, - 'k', 0x00, - 'R', 0x00, - 'F', 0x00, -#endif }; +#endif uint8_t usb_descriptor_string_config_description[] = { 24, // bLength @@ -291,14 +329,46 @@ uint8_t usb_descriptor_string_serial_number[] = { uint8_t usb_descriptor_string_serial_number[USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN]; #endif -uint8_t* usb_descriptor_strings[] = { +#if defined(HACKRF_ONE) || defined(UNIVERSAL) +uint8_t* usb_descriptor_strings_hackrf_one[] = { usb_descriptor_string_languages, usb_descriptor_string_manufacturer, - usb_descriptor_string_product, + usb_descriptor_string_product_hackrf_one, usb_descriptor_string_config_description, usb_descriptor_string_serial_number, 0, // TERMINATOR }; +#endif +#if defined(PRALINE) || defined(UNIVERSAL) +uint8_t* usb_descriptor_strings_praline[] = { + usb_descriptor_string_languages, + usb_descriptor_string_manufacturer, + usb_descriptor_string_product_praline, + usb_descriptor_string_config_description, + usb_descriptor_string_serial_number, + 0, // TERMINATOR +}; +#endif +#if defined(JAWBREAKER) +uint8_t* usb_descriptor_strings_jawbreaker[] = { + usb_descriptor_string_languages, + usb_descriptor_string_manufacturer, + usb_descriptor_string_product_jawbreaker, + usb_descriptor_string_config_description, + usb_descriptor_string_serial_number, + 0, // TERMINATOR +}; +#endif +#if defined(RAD1O) +uint8_t* usb_descriptor_strings_rad1o[] = { + usb_descriptor_string_languages, + usb_descriptor_string_manufacturer, + usb_descriptor_string_product_rad1o, + usb_descriptor_string_config_description, + usb_descriptor_string_serial_number, + 0, // TERMINATOR +}; +#endif uint8_t wcid_string_descriptor[] = { 18, // bLength diff --git a/firmware/hackrf_usb/usb_descriptor.h b/firmware/hackrf_usb/usb_descriptor.h index 097ccce0..93f3eb78 100644 --- a/firmware/hackrf_usb/usb_descriptor.h +++ b/firmware/hackrf_usb/usb_descriptor.h @@ -24,20 +24,42 @@ #include -extern uint8_t usb_descriptor_device[]; +#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) +extern uint8_t usb_descriptor_device_hackrf[]; +#endif +#if defined(JAWBREAKER) +extern uint8_t usb_descriptor_device_jawbreaker[]; +#endif +#if defined(RAD1O) +extern uint8_t usb_descriptor_device_rad1o[]; +#endif extern uint8_t usb_descriptor_device_qualifier[]; extern uint8_t usb_descriptor_configuration_full_speed[]; extern uint8_t usb_descriptor_configuration_high_speed[]; extern uint8_t usb_descriptor_string_languages[]; extern uint8_t usb_descriptor_string_manufacturer[]; -extern uint8_t usb_descriptor_string_product[]; +#if defined(HACKRF_ONE) || defined(UNIVERSAL) +extern uint8_t usb_descriptor_string_product_hackrf_one[]; +#endif +#if defined(PRALINE) || defined(UNIVERSAL) +extern uint8_t usb_descriptor_string_product_praline[]; +#endif +#if defined(JAWBREAKER) +extern uint8_t usb_descriptor_string_product_jawbreaker[]; +#endif +#if defined(RAD1O) +extern uint8_t usb_descriptor_string_product_rad1o[]; +#endif #define USB_DESCRIPTOR_STRING_SERIAL_LEN 32 #define USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN \ (USB_DESCRIPTOR_STRING_SERIAL_LEN * 2 + 2) /* UTF-16LE */ extern uint8_t usb_descriptor_string_serial_number[]; -extern uint8_t* usb_descriptor_strings[]; +extern uint8_t* usb_descriptor_strings_hackrf_one[]; +extern uint8_t* usb_descriptor_strings_jawbreaker[]; +extern uint8_t* usb_descriptor_strings_rad1o[]; +extern uint8_t* usb_descriptor_strings_praline[]; #define USB_WCID_VENDOR_REQ 0x19 extern uint8_t wcid_string_descriptor[]; diff --git a/firmware/hackrf_usb/usb_device.c b/firmware/hackrf_usb/usb_device.c index b689ce9f..4ad46424 100644 --- a/firmware/hackrf_usb/usb_device.c +++ b/firmware/hackrf_usb/usb_device.c @@ -45,12 +45,49 @@ usb_configuration_t* usb_configurations[] = { 0, }; -usb_device_t usb_device = { - .descriptor = usb_descriptor_device, - .descriptor_strings = usb_descriptor_strings, +usb_device_t usb_device; + +#if defined(HACKRF_ONE) || defined(UNIVERSAL) +const usb_device_t usb_device_hackrf_one = { + .descriptor = usb_descriptor_device_hackrf, + .descriptor_strings = usb_descriptor_strings_hackrf_one, .qualifier_descriptor = usb_descriptor_device_qualifier, .configurations = &usb_configurations, .configuration = 0, .wcid_string_descriptor = wcid_string_descriptor, .wcid_feature_descriptor = wcid_feature_descriptor, }; +#endif +#if defined(PRALINE) || defined(UNIVERSAL) +const usb_device_t usb_device_praline = { + .descriptor = usb_descriptor_device_hackrf, + .descriptor_strings = usb_descriptor_strings_praline, + .qualifier_descriptor = usb_descriptor_device_qualifier, + .configurations = &usb_configurations, + .configuration = 0, + .wcid_string_descriptor = wcid_string_descriptor, + .wcid_feature_descriptor = wcid_feature_descriptor, +}; +#endif +#if defined(JAWBREAKER) +const usb_device_t usb_device_jawbreaker = { + .descriptor = usb_descriptor_device_jawbreaker, + .descriptor_strings = usb_descriptor_strings_jawbreaker, + .qualifier_descriptor = usb_descriptor_device_qualifier, + .configurations = &usb_configurations, + .configuration = 0, + .wcid_string_descriptor = wcid_string_descriptor, + .wcid_feature_descriptor = wcid_feature_descriptor, +}; +#endif +#if defined(RAD1O) +const usb_device_t usb_device_rad1o = { + .descriptor = usb_descriptor_device_rad1o, + .descriptor_strings = usb_descriptor_strings_rad1o, + .qualifier_descriptor = usb_descriptor_device_qualifier, + .configurations = &usb_configurations, + .configuration = 0, + .wcid_string_descriptor = wcid_string_descriptor, + .wcid_feature_descriptor = wcid_feature_descriptor, +}; +#endif diff --git a/firmware/hackrf_usb/usb_device.h b/firmware/hackrf_usb/usb_device.h index 79df298d..528d084d 100644 --- a/firmware/hackrf_usb/usb_device.h +++ b/firmware/hackrf_usb/usb_device.h @@ -26,3 +26,16 @@ #include extern usb_device_t usb_device; + +#if defined(HACKRF_ONE) || defined(UNIVERSAL) +extern const usb_device_t usb_device_hackrf_one; +#endif +#if defined(PRALINE) || defined(UNIVERSAL) +extern const usb_device_t usb_device_praline; +#endif +#if defined(JAWBREAKER) +extern const usb_device_t usb_device_jawbreaker; +#endif +#if defined(RAD1O) +extern const usb_device_t usb_device_rad1o; +#endif From 3faa91ef85336b4f989b8fa1a8fb519bcf34c99a Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 4 Apr 2026 01:13:09 +0100 Subject: [PATCH 059/173] Add UNIVERSAL board target. --- firmware/common/firmware_info.c | 3 +++ firmware/hackrf-common.cmake | 21 +++++++++++++++++---- firmware/hackrf_usb/CMakeLists.txt | 24 ++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/firmware/common/firmware_info.c b/firmware/common/firmware_info.c index 1ac02074..527dd19b 100644 --- a/firmware/common/firmware_info.c +++ b/firmware/common/firmware_info.c @@ -30,6 +30,9 @@ #define SUPPORTED_PLATFORM PLATFORM_RAD1O #elif PRALINE #define SUPPORTED_PLATFORM PLATFORM_PRALINE +#elif UNIVERSAL + #define SUPPORTED_PLATFORM \ + (PLATFORM_PRALINE | PLATFORM_HACKRF1_OG | PLATFORM_HACKRF1_R9) #else #define SUPPORTED_PLATFORM 0 #endif diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index 26a24d6e..9a349b1e 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -74,7 +74,7 @@ if(NOT DEFINED BOARD) set(BOARD HACKRF_ONE) endif() -if(BOARD STREQUAL "HACKRF_ONE" OR BOARD STREQUAL "PRALINE") +if(BOARD STREQUAL "HACKRF_ONE" OR BOARD STREQUAL "PRALINE" OR BOARD STREQUAL "UNIVERSAL") set(MCU_PARTNO LPC4320) else() set(MCU_PARTNO LPC4330) @@ -195,7 +195,8 @@ macro(DeclareTargets) ${PATH_HACKRF_FIRMWARE_COMMON}/m0_state.c ${PATH_HACKRF_FIRMWARE_COMMON}/adc.c ${PATH_HACKRF_FIRMWARE_COMMON}/da7219.c - ) + ${PATH_HACKRF_FIRMWARE_COMMON}/max283x.c + ) if(BOARD STREQUAL "RAD1O") SET(SRC_M4 @@ -216,14 +217,26 @@ macro(DeclareTargets) ${SRC_M4} ${PATH_HACKRF_FIRMWARE_COMMON}/fpga.c ${PATH_HACKRF_FIRMWARE_COMMON}/ice40_spi.c - ${PATH_HACKRF_FIRMWARE_COMMON}/max283x.c ${PATH_HACKRF_FIRMWARE_COMMON}/max2831.c ${PATH_HACKRF_FIRMWARE_COMMON}/max2831_target.c ) else() SET(SRC_M4 ${SRC_M4} - ${PATH_HACKRF_FIRMWARE_COMMON}/max283x.c + ${PATH_HACKRF_FIRMWARE_COMMON}/max2837.c + ${PATH_HACKRF_FIRMWARE_COMMON}/max2837_target.c + ${PATH_HACKRF_FIRMWARE_COMMON}/max2839.c + ${PATH_HACKRF_FIRMWARE_COMMON}/max2839_target.c + ) + endif() + + if(BOARD STREQUAL "UNIVERSAL") + SET(SRC_M4 + ${SRC_M4} + ${PATH_HACKRF_FIRMWARE_COMMON}/fpga.c + ${PATH_HACKRF_FIRMWARE_COMMON}/ice40_spi.c + ${PATH_HACKRF_FIRMWARE_COMMON}/max2831.c + ${PATH_HACKRF_FIRMWARE_COMMON}/max2831_target.c ${PATH_HACKRF_FIRMWARE_COMMON}/max2837.c ${PATH_HACKRF_FIRMWARE_COMMON}/max2837_target.c ${PATH_HACKRF_FIRMWARE_COMMON}/max2839.c diff --git a/firmware/hackrf_usb/CMakeLists.txt b/firmware/hackrf_usb/CMakeLists.txt index 3ab1beed..3fd3ea87 100644 --- a/firmware/hackrf_usb/CMakeLists.txt +++ b/firmware/hackrf_usb/CMakeLists.txt @@ -35,7 +35,7 @@ add_custom_command( OUTPUT ${PATH_PRALINE_FPGA_OBJ} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy ${PATH_PRALINE_FPGA_BIN} "fpga.bin" - COMMAND ${CMAKE_OBJCOPY} + COMMAND ${CMAKE_OBJCOPY} -I binary -O elf32-littlearm -B arm @@ -75,6 +75,26 @@ set(SRC_M4 set(SRC_M0 sgpio_m0.s) +if(BOARD STREQUAL "UNIVERSAL") + SET(SRC_M4 + ${SRC_M4} + usb_api_cpld.c + "${PATH_HACKRF_FIRMWARE_COMMON}/cpld_xc2c.c" + "${PATH_HACKRF_CPLD_DATA_C}" + "${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/lenval.c" + "${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/micro.c" + "${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/ports.c" + + "${PATH_HACKRF_FIRMWARE_COMMON}/lz4_blk.c" + "${PATH_HACKRF_FIRMWARE_COMMON}/fpga_image.c" + "${PATH_HACKRF_FIRMWARE_COMMON}/fpga_selftest.c" + usb_api_praline.c + ) + SET(OBJ_M4 + ${PATH_PRALINE_FPGA_OBJ} + ) +endif() + if(BOARD STREQUAL "PRALINE") SET(SRC_M4 ${SRC_M4} @@ -98,7 +118,7 @@ else() ) endif() -if(BOARD STREQUAL "HACKRF_ONE" OR BOARD STREQUAL "PRALINE") +if(BOARD STREQUAL "PRALINE" OR BOARD STREQUAL "HACKRF_ONE" OR BOARD STREQUAL "UNIVERSAL") SET(SRC_M4 ${SRC_M4} "${PATH_HACKRF_FIRMWARE_COMMON}/portapack.c" From 6387a0b1006a2492a4cb90b95b84756bb736a23e Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 2 Apr 2026 04:48:00 +0100 Subject: [PATCH 060/173] Add UNIVERSAL firmware build to GitHub Actions. --- .github/workflows/build.yml | 2 +- .github/workflows/includes-check.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 07b419e5..a93a27c1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -159,7 +159,7 @@ jobs: strategy: matrix: os: ['macos', 'ubuntu', 'windows'] - board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE'] + board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE', 'UNIVERSAL'] cmake: ['3.10.0', 'latest'] exclude: - os: 'windows' diff --git a/.github/workflows/includes-check.yml b/.github/workflows/includes-check.yml index dfb631bd..11640069 100644 --- a/.github/workflows/includes-check.yml +++ b/.github/workflows/includes-check.yml @@ -13,7 +13,7 @@ jobs: options: --user 0 strategy: matrix: - board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE'] + board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE', 'UNIVERSAL'] cmake: ['3.10.0', 'latest'] # Don't cancel all builds when one fails From 421af497447a9e2632f4ca7dad88229af1da6300 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 2 Apr 2026 07:45:08 +0100 Subject: [PATCH 061/173] Jenkinsfile: add tests with BOARD=UNIVERSAL. --- Jenkinsfile | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index bf79d51e..b41d6714 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -67,6 +67,46 @@ pipeline { } } } + stage('Test HackRF One with BOARD=UNIVERSAL') { + agent { + docker { + image 'hackrf' + reuseNode true + args docker_args + } + } + options { + timeout(time: 20, unit: 'MINUTES') + } + steps { + sh './ci-scripts/install_host.sh' + sh './ci-scripts/build_firmware.sh UNIVERSAL' + script { + allOff() + reset('h1_eut') + } + sh 'sleep 1s' + retry(3) { + sh './ci-scripts/test_host.sh' + } + script { + reset('h1_tester h1_eut') + } + sh 'sleep 1s' + script { + // Allow up to 3 retries 5 minutes each for the HIL test + runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test) + } + script { + allOff() + reset('h1_eut') + } + sh 'sleep 1s' + retry(3) { + sh 'python3 ci-scripts/test_sgpio_debug.py' + } + } + } stage('Test HackRF Pro with BOARD=PRALINE') { agent { docker { @@ -99,6 +139,38 @@ pipeline { } } } + stage('Test HackRF Pro with BOARD=UNIVERSAL') { + agent { + docker { + image 'hackrf' + reuseNode true + args "$docker_args" + } + } + options { + timeout(time: 20, unit: 'MINUTES') + } + steps { + sh './ci-scripts/install_host.sh' + sh './ci-scripts/build_firmware.sh UNIVERSAL' + script { + allOff() + reset('hpro_eut') + } + sh 'sleep 1s' + retry(3) { + sh './ci-scripts/test_host.sh' + } + script { + reset('hpro_tester hpro_eut') + } + sh 'sleep 1s' + script { + // Allow up to 3 retries 5 minutes each for the HIL test + runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test) + } + } + } } post { always { From 9b5f342be024fce5ab0c421116af720a9ccf44c4 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 6 Apr 2026 21:21:01 +0100 Subject: [PATCH 062/173] max283x: Move platform switch inside max283x_setup(). --- firmware/common/max283x.c | 51 +++++++++++++++++---------------------- firmware/common/max283x.h | 2 +- firmware/common/rf_path.c | 18 +------------- 3 files changed, 24 insertions(+), 47 deletions(-) diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index 54b596bc..9c64d668 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -27,6 +27,7 @@ #include #include "fixed_point.h" +#include "platform_detect.h" #include "platform_gpio.h" #include "spi_bus.h" @@ -67,49 +68,41 @@ max2839_driver_t max2839 = { #endif /* Initialize chip. */ -void max283x_setup(max283x_driver_t* const drv, max283x_variant_t type) +void max283x_setup(max283x_driver_t* const drv) { - drv->type = type; - const platform_gpio_t* gpio = platform_gpio(); /* MAX283x GPIO PinMux */ + switch (detected_platform()) { + case BOARD_ID_PRALINE: #if defined(PRALINE) || defined(UNIVERSAL) - max2831.gpio_enable = gpio->max283x_enable; - max2831.gpio_rxtx = gpio->max283x_rx_enable; - max2831.gpio_rxhp = gpio->max2831_rxhp; - max2831.gpio_ld = gpio->max2831_ld; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - max2837.gpio_enable = gpio->max283x_enable; - max2837.gpio_rx_enable = gpio->max283x_rx_enable; - max2837.gpio_tx_enable = gpio->max283x_tx_enable; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - max2839.gpio_enable = gpio->max283x_enable; - max2839.gpio_rxtx = gpio->max283x_rx_enable; -#endif - - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: + drv->type = MAX2831_VARIANT; + max2831.gpio_enable = gpio->max283x_enable; + max2831.gpio_rxtx = gpio->max283x_rx_enable; + max2831.gpio_rxhp = gpio->max2831_rxhp; + max2831.gpio_ld = gpio->max2831_ld; memcpy(&drv->drv.max2831, &max2831, sizeof(max2831)); max2831_setup(&drv->drv.max2831); - break; #endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - memcpy(&drv->drv.max2837, &max2837, sizeof(max2837)); - max2837_setup(&drv->drv.max2837); break; -#endif + case BOARD_ID_HACKRF1_R9: #if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: + drv->type = MAX2839_VARIANT; + max2839.gpio_enable = gpio->max283x_enable; + max2839.gpio_rxtx = gpio->max283x_rx_enable; memcpy(&drv->drv.max2839, &max2839, sizeof(max2839)); max2839_setup(&drv->drv.max2839); - break; #endif + break; default: +#if !defined(PRALINE) || defined(UNIVERSAL) + drv->type = MAX2837_VARIANT; + max2837.gpio_enable = gpio->max283x_enable; + max2837.gpio_rx_enable = gpio->max283x_rx_enable; + max2837.gpio_tx_enable = gpio->max283x_tx_enable; + memcpy(&drv->drv.max2837, &max2837, sizeof(max2837)); + max2837_setup(&drv->drv.max2837); +#endif break; } } diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h index 87da59b4..bcafff06 100644 --- a/firmware/common/max283x.h +++ b/firmware/common/max283x.h @@ -83,7 +83,7 @@ typedef struct { } max283x_driver_t; /* Initialize chip. */ -void max283x_setup(max283x_driver_t* const drv, max283x_variant_t type); +void max283x_setup(max283x_driver_t* const drv); /* Returns the number of registers supported by the driver. */ uint16_t max283x_num_regs(max283x_driver_t* const drv); diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index 5e892668..f2234413 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -480,23 +480,7 @@ void rf_path_init(rf_path_t* const rf_path) max5864_shutdown(&max5864); ssp1_set_mode_max283x(); - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) - max283x_setup(&max283x, MAX2831_VARIANT); -#endif - break; - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - max283x_setup(&max283x, MAX2839_VARIANT); -#endif - break; - default: -#if !defined(PRALINE) || defined(UNIVERSAL) - max283x_setup(&max283x, MAX2837_VARIANT); -#endif - break; - } + max283x_setup(&max283x); max283x_start(&max283x); switch (board_id) { From d4b2091b128d7b413e3e334fabcea89e5a39d08e Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 6 Apr 2026 22:58:34 +0100 Subject: [PATCH 063/173] max283x: Add macros to simplify variant-specific dispatch. --- firmware/common/max283x.c | 442 +++++++++----------------------------- 1 file changed, 100 insertions(+), 342 deletions(-) diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index 9c64d668..6a6d5f26 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -107,93 +107,95 @@ void max283x_setup(max283x_driver_t* const drv) } } +/* Macros to simplify dispatch of variant-specific operations. */ + +/* clang-format off */ + +#if defined(UNIVERSAL) + // UNIVERSAL: all variants + #define DISPATCH(_drv, _max2831, _max2837, _max2839) \ + if (_drv->type == MAX2831_VARIANT) { \ + _max2831; \ + } else if (_drv->type == MAX2837_VARIANT) { \ + _max2837; \ + } else { \ + _max2839; \ + } +#elif defined(HACKRF_ONE) + // HACKRF_ONE: MAX2837 and MAX2839 only + #define DISPATCH(_drv, _max2831, _max2837, _max2839) \ + if (_drv->type == MAX2837_VARIANT) { \ + _max2837; \ + } else { \ + _max2839; \ + } +#elif defined(PRALINE) + // PRALINE: MAX2831 only + #define DISPATCH(_drv, _max2831, _max2837, _max2839) \ + (void) drv; \ + _max2831 +#else + // JAWBREAKER, RAD1O: MAX2837 only + #define DISPATCH(_drv, _max2831, _max2837, _max2839) \ + (void) drv; \ + _max2837 +#endif + +#define CALL(_drv, _func, ...) \ + DISPATCH(drv, \ + max2831_ ## _func(&_drv->drv.max2831, ##__VA_ARGS__), \ + max2837_ ## _func(&_drv->drv.max2837, ##__VA_ARGS__), \ + max2839_ ## _func(&_drv->drv.max2839, ##__VA_ARGS__) \ + ); \ + +#define RESULT(_drv, _type, _func, ...) ({ \ + _type _result; \ + DISPATCH(drv, \ + _result = max2831_ ## _func(&_drv->drv.max2831, ##__VA_ARGS__), \ + _result = max2837_ ## _func(&_drv->drv.max2837, ##__VA_ARGS__), \ + _result = max2839_ ## _func(&_drv->drv.max2839, ##__VA_ARGS__) \ + ); \ + _result; \ +}) + +#define CONSTANT(_drv, _type, _name) ({\ + _type value; \ + DISPATCH(drv, \ + value = MAX2831_ ## _name, \ + value = MAX2837_ ## _name, \ + value = MAX2839_ ## _name \ + ); \ + value; \ +}) + +#define PRALINE_ONLY(_drv, _max2831) DISPATCH(_drv, _max2831, , ) + +/* clang-format on */ + /* Returns the number of registers supported by the driver. */ uint16_t max283x_num_regs(max283x_driver_t* const drv) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - return MAX2831_NUM_REGS; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - return MAX2837_NUM_REGS; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - return MAX2839_NUM_REGS; -#endif - default: - return 0; - } + return CONSTANT(drv, uint16_t, NUM_REGS); } /* Returns the maximum data register value supported by the driver. */ uint16_t max283x_data_regs_max_value(max283x_driver_t* const drv) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - return MAX2831_DATA_REGS_MAX_VALUE; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - return MAX2837_DATA_REGS_MAX_VALUE; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - return MAX2839_DATA_REGS_MAX_VALUE; -#endif - default: - return 0; - } + return CONSTANT(drv, uint16_t, DATA_REGS_MAX_VALUE); } /* Read a register via SPI. Save a copy to memory and return * value. Mark clean. */ uint16_t max283x_reg_read(max283x_driver_t* const drv, uint8_t r) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - return max2831_reg_read(&drv->drv.max2831, r); -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - return max2837_reg_read(&drv->drv.max2837, r); -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - return max2839_reg_read(&drv->drv.max2839, r); -#endif - default: - return 0; - } + return RESULT(drv, uint16_t, reg_read, r); } /* Write value to register via SPI and save a copy to memory. Mark * clean. */ void max283x_reg_write(max283x_driver_t* const drv, uint8_t r, uint16_t v) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - max2831_reg_write(&drv->drv.max2831, r, v); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - max2837_reg_write(&drv->drv.max2837, r, v); - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - max2839_reg_write(&drv->drv.max2839, r, v); - break; -#endif - default: - break; - } + CALL(drv, reg_write, r, v); } /* Write all dirty registers via SPI from memory. Mark all clean. Some @@ -201,115 +203,36 @@ void max283x_reg_write(max283x_driver_t* const drv, uint8_t r, uint16_t v) * provided routines for those operations. */ void max283x_regs_commit(max283x_driver_t* const drv) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - max2831_regs_commit(&drv->drv.max2831); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - max2837_regs_commit(&drv->drv.max2837); - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - max2839_regs_commit(&drv->drv.max2839); - break; -#endif - default: - break; - } + CALL(drv, regs_commit); } void max283x_set_mode(max283x_driver_t* const drv, const max283x_mode_t new_mode) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - max2831_set_mode(&drv->drv.max2831, (max2831_mode_t) new_mode); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - max2837_set_mode(&drv->drv.max2837, (max2837_mode_t) new_mode); - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - max2839_set_mode(&drv->drv.max2839, (max2839_mode_t) new_mode); - break; -#endif - default: - break; - } + DISPATCH( + drv, + max2831_set_mode(&drv->drv.max2831, (max2831_mode_t) new_mode), + max2837_set_mode(&drv->drv.max2837, (max2837_mode_t) new_mode), + max2839_set_mode(&drv->drv.max2839, (max2839_mode_t) new_mode)); } max283x_mode_t max283x_mode(max283x_driver_t* const drv) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - return (max283x_mode_t) max2831_mode(&drv->drv.max2831); -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - return (max283x_mode_t) max2837_mode(&drv->drv.max2837); -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - return (max283x_mode_t) max2839_mode(&drv->drv.max2839); -#endif - default: - return 0; - } + DISPATCH( + drv, + return (max283x_mode_t) max2831_mode(&drv->drv.max2831), + return (max283x_mode_t) max2837_mode(&drv->drv.max2837), + return (max283x_mode_t) max2839_mode(&drv->drv.max2839)); } /* Turn on/off all chip functions. Does not control oscillator and CLKOUT */ void max283x_start(max283x_driver_t* const drv) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - max2831_start(&drv->drv.max2831); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - max2837_start(&drv->drv.max2837); - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - max2839_start(&drv->drv.max2839); - break; -#endif - default: - break; - } + CALL(drv, start); } void max283x_stop(max283x_driver_t* const drv) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - max2831_stop(&drv->drv.max2831); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - max2837_stop(&drv->drv.max2837); - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - max2839_stop(&drv->drv.max2839); - break; -#endif - default: - break; - } + CALL(drv, stop); } /* Set frequency in 1/(2**24) Hz. */ @@ -318,26 +241,7 @@ fp_40_24_t max283x_set_frequency( fp_40_24_t freq, bool program) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - return max2831_set_frequency(&drv->drv.max2831, freq, program); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - return max2837_set_frequency(&drv->drv.max2837, freq, program); - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - return max2839_set_frequency(&drv->drv.max2839, freq, program); - break; -#endif - default: - break; - } - return 0; + return RESULT(drv, fp_40_24_t, set_frequency, freq, program); } uint32_t max283x_set_lpf_bandwidth( @@ -345,135 +249,42 @@ uint32_t max283x_set_lpf_bandwidth( const max283x_mode_t mode, const uint32_t bandwidth_hz) { -#ifndef PRALINE +#if !defined(PRALINE) (void) mode; #endif - - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: + DISPATCH( + drv, return max2831_set_lpf_bandwidth( &drv->drv.max2831, (max2831_mode_t) mode, - bandwidth_hz); -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - return max2837_set_lpf_bandwidth(&drv->drv.max2837, bandwidth_hz); -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - return max2839_set_lpf_bandwidth(&drv->drv.max2839, bandwidth_hz); -#endif - default: - return 0; - } + bandwidth_hz), + return max2837_set_lpf_bandwidth(&drv->drv.max2837, bandwidth_hz), + return max2839_set_lpf_bandwidth(&drv->drv.max2839, bandwidth_hz)); } bool max283x_set_lna_gain(max283x_driver_t* const drv, const uint32_t gain_db) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - return max2831_set_lna_gain(&drv->drv.max2831, gain_db); -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - return max2837_set_lna_gain(&drv->drv.max2837, gain_db); -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - return max2839_set_lna_gain(&drv->drv.max2839, gain_db); -#endif - default: - return false; - } + return RESULT(drv, bool, set_lna_gain, gain_db); } bool max283x_set_vga_gain(max283x_driver_t* const drv, const uint32_t gain_db) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - return max2831_set_vga_gain(&drv->drv.max2831, gain_db); -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - return max2837_set_vga_gain(&drv->drv.max2837, gain_db); -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - return max2839_set_vga_gain(&drv->drv.max2839, gain_db); -#endif - default: - return false; - } + return RESULT(drv, bool, set_vga_gain, gain_db); } bool max283x_set_txvga_gain(max283x_driver_t* const drv, const uint32_t gain_db) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - return max2831_set_txvga_gain(&drv->drv.max2831, gain_db); -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - return max2837_set_txvga_gain(&drv->drv.max2837, gain_db); -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - return max2839_set_txvga_gain(&drv->drv.max2839, gain_db); -#endif - default: - return false; - } + return RESULT(drv, bool, set_txvga_gain, gain_db); } void max283x_tx(max283x_driver_t* const drv) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - max2831_tx(&drv->drv.max2831); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - max2837_tx(&drv->drv.max2837); - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - max2839_tx(&drv->drv.max2839); - break; -#endif - default: - break; - } + CALL(drv, tx); } void max283x_rx(max283x_driver_t* const drv) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - max2831_rx(&drv->drv.max2831); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - max2837_rx(&drv->drv.max2837); - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - max2839_rx(&drv->drv.max2839); - break; -#endif - default: - break; - } + CALL(drv, rx); } /* Set MAX2831 receiver high-pass filter corner frequency in Hz */ @@ -481,75 +292,22 @@ void max283x_set_rx_hpf_frequency( max283x_driver_t* const drv, const max283x_rx_hpf_freq_t freq) { -#if !defined(PRALINE) +#if !defined(PRALINE) && !defined(UNIVERSAL) (void) freq; #endif - - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: + PRALINE_ONLY( + drv, max2831_set_rx_hpf_frequency( &drv->drv.max2831, - (max2831_rx_hpf_freq_t) freq); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - // unsupported - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - // unsupported - break; -#endif - default: - break; - } + (max2831_rx_hpf_freq_t) freq)); } void max283x_tx_calibration(max283x_driver_t* const drv) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - max2831_tx_calibration(&drv->drv.max2831); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - // unsupported - use max283x_set_mode instead - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - // unsupported - use max283x_set_mode instead - break; -#endif - default: - break; - } + PRALINE_ONLY(drv, max2831_tx_calibration(&drv->drv.max2831)); } void max283x_rx_calibration(max283x_driver_t* const drv) { - switch (drv->type) { -#if defined(PRALINE) || defined(UNIVERSAL) - case MAX2831_VARIANT: - max2831_rx_calibration(&drv->drv.max2831); - break; -#endif -#if !defined(PRALINE) || defined(UNIVERSAL) - case MAX2837_VARIANT: - // unsupported - use max283x_set_mode instead - break; -#endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - case MAX2839_VARIANT: - // unsupported - use max283x_set_mode instead - break; -#endif - default: - break; - } + PRALINE_ONLY(drv, max2831_rx_calibration(&drv->drv.max2831)); } From 439f6fd3554dda31782fb3db66dd6f061cd08ece Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 15 Apr 2026 02:05:24 +0100 Subject: [PATCH 064/173] firmware: Use macros to simplify platform-specific code. --- firmware/common/cpld_jtag.c | 64 +-- firmware/common/hackrf_core.c | 549 +++++++++-------------- firmware/common/hackrf_ui.c | 18 +- firmware/common/max283x.c | 44 +- firmware/common/mixer.c | 121 ++--- firmware/common/operacake_sctimer.c | 11 +- firmware/common/platform_detect.h | 111 +++++ firmware/common/platform_gpio.c | 235 +++------- firmware/common/platform_scu.c | 300 ++++--------- firmware/common/radio.c | 116 ++--- firmware/common/rf_path.c | 90 ++-- firmware/common/rffc5071.c | 37 +- firmware/common/sgpio.c | 15 +- firmware/common/si5351c.c | 48 +- firmware/common/tuning.c | 13 +- firmware/hackrf_usb/hackrf_usb.c | 118 ++--- firmware/hackrf_usb/usb_api_board_info.c | 6 +- firmware/hackrf_usb/usb_api_register.c | 138 +++--- firmware/hackrf_usb/usb_api_selftest.c | 37 +- 19 files changed, 822 insertions(+), 1249 deletions(-) diff --git a/firmware/common/cpld_jtag.c b/firmware/common/cpld_jtag.c index 21991db0..58359051 100644 --- a/firmware/common/cpld_jtag.c +++ b/firmware/common/cpld_jtag.c @@ -39,83 +39,55 @@ void cpld_jtag_take(jtag_t* const jtag) { const jtag_gpio_t* const gpio = jtag->gpio; - board_id_t board_id = detected_platform(); - /* Set initial GPIO state to the voltages of the internal or external pull-ups/downs, * to avoid any glitches. */ - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + IF_EXPANSION_COMPATIBLE ( gpio_set(gpio->gpio_pp_tms); -#endif - break; - default: - break; - } + ) + gpio_clear(gpio->gpio_tck); - if (board_id != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) + + IF_NOT_PRALINE ( gpio_set(gpio->gpio_tms); gpio_set(gpio->gpio_tdi); -#endif - } + ) - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + IF_EXPANSION_COMPATIBLE ( /* Do not drive PortaPack-specific TMS pin initially, just to be cautious. */ gpio_input(gpio->gpio_pp_tms); gpio_input(gpio->gpio_pp_tdo); -#endif - break; - default: - break; - } + ) + gpio_output(gpio->gpio_tck); - if (board_id != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) + + IF_NOT_PRALINE ( gpio_output(gpio->gpio_tms); gpio_output(gpio->gpio_tdi); gpio_input(gpio->gpio_tdo); -#endif - } + ) } void cpld_jtag_release(jtag_t* const jtag) { const jtag_gpio_t* const gpio = jtag->gpio; - board_id_t board_id = detected_platform(); - /* Make all pins inputs when JTAG interface not active. * Let the pull-ups/downs do the work. */ - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + IF_EXPANSION_COMPATIBLE ( /* Do not drive PortaPack-specific pins, initially, just to be cautious. */ gpio_input(gpio->gpio_pp_tms); gpio_input(gpio->gpio_pp_tdo); -#endif - break; - default: - break; - } + ) + gpio_input(gpio->gpio_tck); - if (board_id != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) + + IF_NOT_PRALINE ( gpio_input(gpio->gpio_tms); gpio_input(gpio->gpio_tdi); gpio_input(gpio->gpio_tdo); -#endif - } + ) } #if !defined(PRALINE) || defined(UNIVERSAL) diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index c8b66443..689a2a30 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -341,8 +341,7 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) sgpio_cpld_stream_disable(&sgpio_config); } - if (detected_platform() != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) + IF_NOT_PRALINE ( /* Integer mode can be enabled if p1 is even and p2 is zero. */ if (p1 & 0x1 || p2) { si5351c_set_int_mode(&clock_gen, 0, 0); @@ -350,13 +349,14 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) si5351c_set_int_mode(&clock_gen, 0, 1); } - if (detected_platform() == BOARD_ID_HACKRF1_R9) { + IF_H1_R9 ( /* * On HackRF One r9 all sample clocks are externally derived * from MS1/CLK1 operating at twice the sample rate. */ si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); - } else { + ) + IF_NOT_H1_R9 ( /* * On other platforms the clock generator produces three * different sample clocks, all derived from multisynth 0. @@ -381,10 +381,9 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) 0, 0, 0); //p1 doesn't matter - } -#endif - } else { -#if defined(PRALINE) || defined(UNIVERSAL) + ) + ) + IF_PRALINE ( /* MS0/CLK0 is the source for the MAX5864 (AFE_CLK). */ si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); @@ -410,8 +409,7 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) /* Reset PLL to synchronize output clock phase. */ si5351c_reset_pll(&clock_gen, SI5351C_PLL_A); -#endif - } + ) if (streaming) { sgpio_cpld_stream_enable(&sgpio_config); @@ -563,73 +561,75 @@ void cpu_clock_init(void) CGU_BASE_SSP1_CLK = CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1); -#if !defined(RAD1O) - /* Disable unused clocks */ - /* Start with PLLs */ - CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); + IF_NOT_RAD1O ( + /* Disable unused clocks */ + /* Start with PLLs */ + CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); - /* Dividers */ - CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1); - CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1); - CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1); - CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1); - CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1); + /* Dividers */ + CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1); + CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1); + CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1); + CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1); + CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1); - /* Base clocks */ - CGU_BASE_SPIFI_CLK = CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */ - CGU_BASE_USB1_CLK = CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */ - CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1); - CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1); - CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1); - CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1); - CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(1); - CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1); - CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1); - CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1); - CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1); - CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1); - CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1); - CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1); - CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1); + /* Base clocks */ + CGU_BASE_SPIFI_CLK = + CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */ + CGU_BASE_USB1_CLK = + CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */ + CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1); + CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1); + CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1); + CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1); + CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(1); + CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1); + CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1); + CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1); + CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1); + CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1); + CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1); + CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1); + CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1); - /* Disable unused peripheral clocks */ - CCU1_CLK_APB1_CAN1_CFG = 0; - CCU1_CLK_APB1_I2S_CFG = 0; - CCU1_CLK_APB1_MOTOCONPWM_CFG = 0; - //CCU1_CLK_APB3_ADC0_CFG = 0; - CCU1_CLK_APB3_ADC1_CFG = 0; - CCU1_CLK_APB3_CAN0_CFG = 0; - CCU1_CLK_APB3_DAC_CFG = 0; - //CCU1_CLK_M4_DMA_CFG = 0; - CCU1_CLK_M4_EMC_CFG = 0; - CCU1_CLK_M4_EMCDIV_CFG = 0; - CCU1_CLK_M4_ETHERNET_CFG = 0; - CCU1_CLK_M4_LCD_CFG = 0; - CCU1_CLK_M4_QEI_CFG = 0; - CCU1_CLK_M4_RITIMER_CFG = 0; - // CCU1_CLK_M4_SCT_CFG = 0; - CCU1_CLK_M4_SDIO_CFG = 0; - CCU1_CLK_M4_SPIFI_CFG = 0; - CCU1_CLK_M4_TIMER0_CFG = 0; - //CCU1_CLK_M4_TIMER1_CFG = 0; - //CCU1_CLK_M4_TIMER2_CFG = 0; - CCU1_CLK_M4_TIMER3_CFG = 0; - CCU1_CLK_M4_UART1_CFG = 0; - CCU1_CLK_M4_USART0_CFG = 0; - CCU1_CLK_M4_USART2_CFG = 0; - CCU1_CLK_M4_USART3_CFG = 0; - CCU1_CLK_M4_USB1_CFG = 0; - CCU1_CLK_M4_VADC_CFG = 0; - // CCU1_CLK_SPIFI_CFG = 0; - // CCU1_CLK_USB1_CFG = 0; - // CCU1_CLK_VADC_CFG = 0; - // CCU2_CLK_APB0_UART1_CFG = 0; - // CCU2_CLK_APB0_USART0_CFG = 0; - // CCU2_CLK_APB2_USART2_CFG = 0; - // CCU2_CLK_APB2_USART3_CFG = 0; - // CCU2_CLK_APLL_CFG = 0; - // CCU2_CLK_SDIO_CFG = 0; -#endif + /* Disable unused peripheral clocks */ + CCU1_CLK_APB1_CAN1_CFG = 0; + CCU1_CLK_APB1_I2S_CFG = 0; + CCU1_CLK_APB1_MOTOCONPWM_CFG = 0; + //CCU1_CLK_APB3_ADC0_CFG = 0; + CCU1_CLK_APB3_ADC1_CFG = 0; + CCU1_CLK_APB3_CAN0_CFG = 0; + CCU1_CLK_APB3_DAC_CFG = 0; + //CCU1_CLK_M4_DMA_CFG = 0; + CCU1_CLK_M4_EMC_CFG = 0; + CCU1_CLK_M4_EMCDIV_CFG = 0; + CCU1_CLK_M4_ETHERNET_CFG = 0; + CCU1_CLK_M4_LCD_CFG = 0; + CCU1_CLK_M4_QEI_CFG = 0; + CCU1_CLK_M4_RITIMER_CFG = 0; + // CCU1_CLK_M4_SCT_CFG = 0; + CCU1_CLK_M4_SDIO_CFG = 0; + CCU1_CLK_M4_SPIFI_CFG = 0; + CCU1_CLK_M4_TIMER0_CFG = 0; + //CCU1_CLK_M4_TIMER1_CFG = 0; + //CCU1_CLK_M4_TIMER2_CFG = 0; + CCU1_CLK_M4_TIMER3_CFG = 0; + CCU1_CLK_M4_UART1_CFG = 0; + CCU1_CLK_M4_USART0_CFG = 0; + CCU1_CLK_M4_USART2_CFG = 0; + CCU1_CLK_M4_USART3_CFG = 0; + CCU1_CLK_M4_USB1_CFG = 0; + CCU1_CLK_M4_VADC_CFG = 0; + // CCU1_CLK_SPIFI_CFG = 0; + // CCU1_CLK_USB1_CFG = 0; + // CCU1_CLK_VADC_CFG = 0; + // CCU2_CLK_APB0_UART1_CFG = 0; + // CCU2_CLK_APB0_USART0_CFG = 0; + // CCU2_CLK_APB2_USART2_CFG = 0; + // CCU2_CLK_APB2_USART3_CFG = 0; + // CCU2_CLK_APLL_CFG = 0; + // CCU2_CLK_SDIO_CFG = 0; + ) } void clock_gen_init(void) @@ -670,7 +670,7 @@ void clock_gen_init(void) * CLK7 -> AUX_CLK2 */ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { + IF_H1_R9 ( /* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */ si5351c_configure_multisynth( &clock_gen, @@ -679,7 +679,8 @@ void clock_gen_init(void) 0, 1, 0); /* 800/20 = 40MHz */ - } else { + ) + IF_NOT_H1_R9 ( /* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */ si5351c_configure_multisynth( &clock_gen, @@ -696,7 +697,7 @@ void clock_gen_init(void) 0, 1, 0); /* 800/20 = 40MHz */ - } + ) /* MS6/CLK6 is unused. */ /* MS7/CLK7 is unused. */ @@ -721,20 +722,12 @@ void clock_gen_shutdown(void) clock_source_t activate_best_clock_source(void) { - switch (detected_platform()) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + IF_EXPANSION_COMPATIBLE ( /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ if (portapack_reference_oscillator && portapack()) { portapack_reference_oscillator(false); } -#endif - break; - default: - break; - } + ) clock_source_t source = CLOCK_SOURCE_HACKRF; @@ -742,11 +735,7 @@ clock_source_t activate_best_clock_source(void) if (si5351c_clkin_signal_valid(&clock_gen)) { source = CLOCK_SOURCE_EXTERNAL; } else { - switch (detected_platform()) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + IF_EXPANSION_COMPATIBLE ( /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ if (portapack_reference_oscillator && portapack()) { portapack_reference_oscillator(true); @@ -757,11 +746,7 @@ clock_source_t activate_best_clock_source(void) portapack_reference_oscillator(false); } } -#endif - break; - default: - break; - } + ) /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ } @@ -796,7 +781,6 @@ void pin_shutdown(void) gpio_init(); /* Detect Platform */ - board_id_t board_id = detected_platform(); const platform_gpio_t* gpio = platform_gpio(); const platform_scu_t* scu = platform_scu(); @@ -814,99 +798,69 @@ void pin_shutdown(void) * * LPC43xx pull-up and pull-down resistors are approximately 53K. */ - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + IF_EXPANSION_COMPATIBLE ( scu_pinmux(scu->PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); scu_pinmux(scu->PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); -#endif - break; - default: - break; - } + ) scu_pinmux(scu->PINMUX_CPLD_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - if (board_id != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) + IF_NOT_PRALINE ( scu_pinmux(scu->PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); scu_pinmux(scu->PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); scu_pinmux(scu->PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); -#endif - } + ) /* Configure SCU Pin Mux as GPIO */ scu_pinmux(scu->PINMUX_LED1, SCU_GPIO_NOPULL); scu_pinmux(scu->PINMUX_LED2, SCU_GPIO_NOPULL); scu_pinmux(scu->PINMUX_LED3, SCU_GPIO_NOPULL); - switch (board_id) { - case BOARD_ID_RAD1O: -#if defined(RAD1O) + IF_RAD1O ( scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4); -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); -#endif - break; - default: - break; - } + ) /* Configure USB indicators */ - if (board_id == BOARD_ID_JAWBREAKER) { -#if defined(JAWBREAKER) + IF_JAWBREAKER ( scu_pinmux(scu->PINMUX_USB_LED0, SCU_CONF_FUNCTION3); scu_pinmux(scu->PINMUX_USB_LED1, SCU_CONF_FUNCTION3); -#endif - } + ) - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( disable_1v2_power(); disable_3v3aux_power(); gpio_output(gpio->gpio_1v2_enable); gpio_output(gpio->gpio_3v3aux_enable_n); scu_pinmux(scu->PINMUX_EN1V2, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu_pinmux(scu->PINMUX_EN3V3_AUX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); -#endif - break; - default: + ) + + IF_NOT_PRALINE ( disable_1v8_power(); - if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_H1_R9 ( gpio_output(gpio->h1r9_1v8_enable); scu_pinmux(scu->H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); -#endif - } else { + ) + IF_NOT_H1_R9 ( gpio_output(gpio->gpio_1v8_enable); scu_pinmux(scu->PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - } - break; - } + ) + ) - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + IF_H1_OR_PRALINE ( /* Safe state: start with VAA turned off: */ disable_rf_power(); /* Configure RF power supply (VAA) switch control signal as output */ - if (board_id == BOARD_ID_HACKRF1_R9) { - #if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_H1_R9 ( gpio_output(gpio->h1r9_vaa_disable); - #endif - } else { + ) + IF_NOT_H1_R9 ( gpio_output(gpio->vaa_disable); - } - break; -#endif - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + ) + + IF_RAD1O ( /* Safe state: start with VAA turned off: */ disable_rf_power(); @@ -919,15 +873,9 @@ void pin_shutdown(void) scu_pinmux(scu->PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); scu_pinmux(scu->PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); -#endif - break; - default: - break; - } + ) - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( scu_pinmux(scu->P2_CTRL0, scu->P2_CTRL0_PINCFG); scu_pinmux(scu->P2_CTRL1, scu->P2_CTRL1_PINCFG); scu_pinmux(scu->P1_CTRL0, scu->P1_CTRL0_PINCFG); @@ -962,11 +910,7 @@ void pin_shutdown(void) gpio_clear(gpio->fpga_cfg_creset); gpio_output(gpio->fpga_cfg_creset); gpio_input(gpio->fpga_cfg_cdone); -#endif - break; - default: - break; - } + ) /* enable input on SCL and SDA pins */ SCU_SFSI2C0 = SCU_I2C0_NOMINAL; @@ -976,7 +920,6 @@ void pin_shutdown(void) void pin_setup(void) { /* Detect Platform */ - board_id_t board_id = detected_platform(); const platform_gpio_t* gpio = platform_gpio(); const platform_scu_t* scu = platform_scu(); @@ -984,34 +927,26 @@ void pin_setup(void) led_off(0); led_off(1); led_off(2); - switch (board_id) { - case BOARD_ID_RAD1O: + IF_FOUR_LEDS ( led_off(3); - default: - break; - } + ) gpio_output(gpio->led[0]); gpio_output(gpio->led[1]); gpio_output(gpio->led[2]); - switch (board_id) { - case BOARD_ID_RAD1O: - case BOARD_ID_PRALINE: -#if defined(RAD1O) || defined(PRALINE) || defined(UNIVERSAL) + + IF_FOUR_LEDS ( gpio_output(gpio->led[3]); -#endif - break; - default: - break; - } + ) /* Configure drivers and driver pins */ ssp_config_max283x.gpio_select = gpio->max283x_select; - if (board_id == BOARD_ID_PRALINE) { - ssp_config_max283x.data_bits = SSP_DATA_9BITS; // send 2 words - } else { + IF_NOT_PRALINE ( ssp_config_max283x.data_bits = SSP_DATA_16BITS; - } + ) + IF_PRALINE ( + ssp_config_max283x.data_bits = SSP_DATA_9BITS; // send 2 words + ) ssp_config_max5864.gpio_select = gpio->max5864_select; @@ -1020,62 +955,41 @@ void pin_setup(void) spi_flash.gpio_wp = gpio->w25q80bv_wp; sgpio_config.gpio_q_invert = gpio->q_invert; - if (board_id != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) - sgpio_config.gpio_trigger_enable = gpio->trigger_enable; -#endif - } -#if defined(PRALINE) || defined(UNIVERSAL) - ssp_config_ice40_fpga.gpio_select = gpio->fpga_cfg_spi_cs; - ice40.gpio_select = gpio->fpga_cfg_spi_cs; - ice40.gpio_creset = gpio->fpga_cfg_creset; - ice40.gpio_cdone = gpio->fpga_cfg_cdone; -#endif + IF_NOT_PRALINE ( + sgpio_config.gpio_trigger_enable = gpio->trigger_enable; + ) + + IF_PRALINE ( + ssp_config_ice40_fpga.gpio_select = gpio->fpga_cfg_spi_cs; + ice40.gpio_select = gpio->fpga_cfg_spi_cs; + ice40.gpio_creset = gpio->fpga_cfg_creset; + ice40.gpio_cdone = gpio->fpga_cfg_cdone; + ) jtag_gpio_cpld.gpio_tck = gpio->cpld_tck; -#if !defined(PRALINE) || defined(UNIVERSAL) - jtag_gpio_cpld.gpio_tms = gpio->cpld_tms; - jtag_gpio_cpld.gpio_tdi = gpio->cpld_tdi; - jtag_gpio_cpld.gpio_tdo = gpio->cpld_tdo; -#endif - if (board_id != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) + + IF_NOT_PRALINE ( jtag_gpio_cpld.gpio_tms = gpio->cpld_tms; jtag_gpio_cpld.gpio_tdi = gpio->cpld_tdi; jtag_gpio_cpld.gpio_tdo = gpio->cpld_tdo; -#endif - } - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + ) + + IF_EXPANSION_COMPATIBLE ( jtag_gpio_cpld.gpio_pp_tms = gpio->cpld_pp_tms; jtag_gpio_cpld.gpio_pp_tdo = gpio->cpld_pp_tdo; -#endif - break; - default: - break; - } + ) ssp1_set_mode_max283x(); mixer_bus_setup(&mixer); - if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_H1_R9 ( sgpio_config.gpio_trigger_enable = gpio->h1r9_trigger_enable; -#endif - } + ) // initialize rf_path struct and assign gpio's - switch (board_id) { - case BOARD_ID_JAWBREAKER: - break; - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_HACKRF_ONE ( rf_path = (rf_path_t){ .switchctrl = 0, .gpio_hp = gpio->hp, @@ -1092,14 +1006,13 @@ void pin_setup(void) .gpio_rx_amp = gpio->rx_amp, .gpio_no_rx_amp_pwr = gpio->no_rx_amp_pwr, }; - if (board_id == BOARD_ID_HACKRF1_R9) { + IF_H1_R9 ( rf_path.gpio_rx = gpio->h1r9_rx; rf_path.gpio_h1r9_no_ant_pwr = gpio->h1r9_no_ant_pwr; - } -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + ) + + IF_RAD1O ( rf_path = (rf_path_t){ .switchctrl = 0, .gpio_tx_rx_n = gpio->tx_rx_n, @@ -1114,10 +1027,9 @@ void pin_setup(void) .gpio_tx_amp = gpio->tx_amp, .gpio_rx_lna = gpio->rx_lna, }; -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + + IF_PRALINE ( rf_path = (rf_path_t){ .switchctrl = 0, .gpio_tx_en = gpio->tx_en, @@ -1130,11 +1042,8 @@ void pin_setup(void) (detected_revision() == BOARD_REV_GSG_PRALINE_R1_0)) { rf_path.gpio_mix_en_n = gpio->mix_en_n_r1_0; } -#endif - break; - default: - break; - } + ) + rf_path_pin_setup(&rf_path); /* Configure external clock in */ @@ -1146,79 +1055,55 @@ void pin_setup(void) #if defined(PRALINE) || defined(UNIVERSAL) void enable_1v2_power(void) { - switch (detected_platform()) { - case BOARD_ID_PRALINE: + IF_PRALINE ( gpio_set(platform_gpio()->gpio_1v2_enable); - break; - default: - break; - } + ) } void disable_1v2_power(void) { - switch (detected_platform()) { - case BOARD_ID_PRALINE: + IF_PRALINE ( gpio_clear(platform_gpio()->gpio_1v2_enable); - break; - default: - break; - } + ) } void enable_3v3aux_power(void) { - switch (detected_platform()) { - case BOARD_ID_PRALINE: + IF_PRALINE ( gpio_clear(platform_gpio()->gpio_3v3aux_enable_n); - break; - default: - break; - } + ) } void disable_3v3aux_power(void) { - switch (detected_platform()) { - case BOARD_ID_PRALINE: + IF_PRALINE ( gpio_set(platform_gpio()->gpio_3v3aux_enable_n); - break; - default: - break; - } + ) } #endif void enable_1v8_power(void) { - switch (detected_platform()) { - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - gpio_set(platform_gpio()->h1r9_1v8_enable); -#endif - break; - case BOARD_ID_PRALINE: - break; - default: - gpio_set(platform_gpio()->gpio_1v8_enable); - break; - } + IF_NOT_PRALINE ( + IF_H1_R9 ( + gpio_set(platform_gpio()->h1r9_1v8_enable); + ) + IF_NOT_H1_R9 ( + gpio_set(platform_gpio()->gpio_1v8_enable); + ) + ) } void disable_1v8_power(void) { - switch (detected_platform()) { - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - gpio_clear(platform_gpio()->h1r9_1v8_enable); -#endif - break; - case BOARD_ID_PRALINE: - break; - default: - gpio_clear(platform_gpio()->gpio_1v8_enable); - break; - } + IF_NOT_PRALINE ( + IF_H1_R9 ( + gpio_clear(platform_gpio()->h1r9_1v8_enable); + ) + IF_NOT_H1_R9 ( + gpio_clear(platform_gpio()->gpio_1v8_enable); + ) + ) } #if defined(HACKRF_ONE) || defined(UNIVERSAL) @@ -1281,68 +1166,48 @@ static inline void disable_rf_power_rad1o(void) void enable_rf_power(void) { - switch (detected_platform()) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_HACKRF_ONE ( enable_rf_power_hackrf_one(); -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( enable_rf_power_praline(); -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( enable_rf_power_rad1o(); -#endif - break; - default: - break; - } + ) } void disable_rf_power(void) { - switch (detected_platform()) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_HACKRF_ONE ( disable_rf_power_hackrf_one(); -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( disable_rf_power_praline(); -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( disable_rf_power_rad1o(); -#endif - break; - default: - break; - } + ) } void led_on(const led_t led) { - if (detected_platform() == BOARD_ID_PRALINE) { + IF_PRALINE ( gpio_clear(platform_gpio()->led[led]); - } else { + ) + IF_NOT_PRALINE ( gpio_set(platform_gpio()->led[led]); - } + ) } void led_off(const led_t led) { - if (detected_platform() == BOARD_ID_PRALINE) { + IF_PRALINE ( gpio_set(platform_gpio()->led[led]); - } else { + ) + IF_NOT_PRALINE ( gpio_clear(platform_gpio()->led[led]); - } + ) } void led_toggle(const led_t led) @@ -1352,33 +1217,29 @@ void led_toggle(const led_t led) void set_leds(const uint8_t state) { - board_id_t board_id = detected_platform(); - int num_leds = 3; - if (board_id == BOARD_ID_RAD1O || board_id == BOARD_ID_PRALINE) { + IF_FOUR_LEDS ( num_leds = 4; - } + ) for (int i = 0; i < num_leds; i++) { - if (board_id == BOARD_ID_PRALINE) { + IF_PRALINE ( gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 0); - } else { + ) + IF_NOT_PRALINE ( gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 1); - } + ) } } void trigger_enable(const bool enable) { - if (detected_platform() != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) + IF_NOT_PRALINE ( gpio_write(sgpio_config.gpio_trigger_enable, enable); -#endif - } else { -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( fpga_set_trigger_enable(&fpga, enable); -#endif - } + ) } void halt_and_flash(const uint32_t duration) diff --git a/firmware/common/hackrf_ui.c b/firmware/common/hackrf_ui.c index ac16a444..092ec12b 100644 --- a/firmware/common/hackrf_ui.c +++ b/firmware/common/hackrf_ui.c @@ -83,26 +83,16 @@ const hackrf_ui_t* hackrf_ui(void) { /* Detect on first use. If no UI hardware is detected, use a stub function table. */ if (ui == NULL && ui_enabled) { - switch (detected_platform()) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + IF_EXPANSION_COMPATIBLE ( if (portapack_hackrf_ui_init) { ui = portapack_hackrf_ui_init(); } -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( if (rad1o_ui_setup) { ui = rad1o_ui_setup(); } -#endif - break; - default: - break; - } + ) } if (ui == NULL) { diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index 6a6d5f26..feb7837d 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -73,9 +73,7 @@ void max283x_setup(max283x_driver_t* const drv) const platform_gpio_t* gpio = platform_gpio(); /* MAX283x GPIO PinMux */ - switch (detected_platform()) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( drv->type = MAX2831_VARIANT; max2831.gpio_enable = gpio->max283x_enable; max2831.gpio_rxtx = gpio->max283x_rx_enable; @@ -83,28 +81,24 @@ void max283x_setup(max283x_driver_t* const drv) max2831.gpio_ld = gpio->max2831_ld; memcpy(&drv->drv.max2831, &max2831, sizeof(max2831)); max2831_setup(&drv->drv.max2831); -#endif - break; - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - drv->type = MAX2839_VARIANT; - max2839.gpio_enable = gpio->max283x_enable; - max2839.gpio_rxtx = gpio->max283x_rx_enable; - memcpy(&drv->drv.max2839, &max2839, sizeof(max2839)); - max2839_setup(&drv->drv.max2839); -#endif - break; - default: -#if !defined(PRALINE) || defined(UNIVERSAL) - drv->type = MAX2837_VARIANT; - max2837.gpio_enable = gpio->max283x_enable; - max2837.gpio_rx_enable = gpio->max283x_rx_enable; - max2837.gpio_tx_enable = gpio->max283x_tx_enable; - memcpy(&drv->drv.max2837, &max2837, sizeof(max2837)); - max2837_setup(&drv->drv.max2837); -#endif - break; - } + ) + IF_NOT_PRALINE ( + IF_H1_R9 ( + drv->type = MAX2839_VARIANT; + max2839.gpio_enable = gpio->max283x_enable; + max2839.gpio_rxtx = gpio->max283x_rx_enable; + memcpy(&drv->drv.max2839, &max2839, sizeof(max2839)); + max2839_setup(&drv->drv.max2839); + ) + IF_NOT_H1_R9 ( + drv->type = MAX2837_VARIANT; + max2837.gpio_enable = gpio->max283x_enable; + max2837.gpio_rx_enable = gpio->max283x_rx_enable; + max2837.gpio_tx_enable = gpio->max283x_tx_enable; + memcpy(&drv->drv.max2837, &max2837, sizeof(max2837)); + max2837_setup(&drv->drv.max2837); + ) + ) } /* Macros to simplify dispatch of variant-specific operations. */ diff --git a/firmware/common/mixer.c b/firmware/common/mixer.c index 9e1afb32..0706170b 100644 --- a/firmware/common/mixer.c +++ b/firmware/common/mixer.c @@ -24,11 +24,11 @@ #include #include "fixed_point.h" +#include "platform_detect.h" #include "platform_gpio.h" #if defined(RAD1O) #include "max2871.h" #else - #include "platform_detect.h" #include "rffc5071.h" #include "rffc5071_spi.h" #include "spi_bus.h" @@ -54,26 +54,18 @@ mixer_driver_t mixer = { void mixer_bus_setup(mixer_driver_t* const mixer) { -#if defined(RAD1O) (void) mixer; -#else - const platform_gpio_t* gpio = platform_gpio(); -#endif - switch (mixer->type) { - case RFFC5071_VARIANT: -#if !defined(RAD1O) + IF_NOT_RAD1O ( + const platform_gpio_t* gpio = platform_gpio(); + rffc5071_spi_config = (rffc5071_spi_config_t){ .gpio_select = gpio->rffc5072_select, .gpio_clock = gpio->rffc5072_clock, .gpio_data = gpio->rffc5072_data, }; spi_bus_start(&spi_bus_rffc5071, &rffc5071_spi_config); -#endif - break; - case MAX2871_VARIANT: - break; - } + ) } void mixer_setup(mixer_driver_t* const mixer, mixer_variant_t type) @@ -83,106 +75,67 @@ void mixer_setup(mixer_driver_t* const mixer, mixer_variant_t type) const platform_gpio_t* gpio = platform_gpio(); /* Mixer GPIO serial interface PinMux */ - switch (mixer->type) { - case RFFC5071_VARIANT: -#if !defined(RAD1O) + IF_PRALINE ( + mixer->rffc5071.gpio_ld = gpio->rffc5072_ld + ); + + IF_NOT_RAD1O ( mixer->rffc5071.gpio_reset = gpio->rffc5072_reset; - if (detected_platform() == BOARD_ID_PRALINE) { - #if defined(PRALINE) || defined(UNIVERSAL) - mixer->rffc5071.gpio_ld = gpio->rffc5072_ld; - #endif - } -#endif - break; - case MAX2871_VARIANT: -#if defined(RAD1O) + rffc5071_setup(&mixer->rffc5071); + ) + + IF_RAD1O ( mixer->max2871.gpio_vco_ce = gpio->vco_ce; mixer->max2871.gpio_vco_sclk = gpio->vco_sclk; mixer->max2871.gpio_vco_sdata = gpio->vco_sdata; mixer->max2871.gpio_vco_le = gpio->vco_le; mixer->max2871.gpio_synt_rfout_en = gpio->synt_rfout_en; mixer->max2871.gpio_vco_mux = gpio->vco_mux; -#endif - break; - } - - switch (mixer->type) { - case RFFC5071_VARIANT: -#if !defined(RAD1O) - rffc5071_setup(&mixer->rffc5071); -#endif - break; - case MAX2871_VARIANT: -#if defined(RAD1O) max2871_setup(&mixer->max2871); -#endif - break; - } + ) } fp_40_24_t mixer_set_frequency(mixer_driver_t* const mixer, fp_40_24_t lo, bool program) { - switch (mixer->type) { - case RFFC5071_VARIANT: -#if !defined(RAD1O) + IF_NOT_RAD1O ( return rffc5071_set_frequency(&mixer->rffc5071, lo, program); -#endif - break; - case MAX2871_VARIANT: -#if defined(RAD1O) - return max2871_set_frequency(&mixer->max2871, lo, program); -#endif - break; - } + ) - return 0; + IF_RAD1O ( + return max2871_set_frequency(&mixer->max2871, lo, program); + ) } void mixer_enable(mixer_driver_t* const mixer) { - switch (mixer->type) { - case RFFC5071_VARIANT: -#if !defined(RAD1O) + IF_NOT_RAD1O ( rffc5071_enable(&mixer->rffc5071); -#endif - break; - case MAX2871_VARIANT: -#if defined(RAD1O) + ) + + IF_RAD1O ( max2871_enable(&mixer->max2871); -#endif - break; - } + ) } void mixer_disable(mixer_driver_t* const mixer) { - switch (mixer->type) { - case RFFC5071_VARIANT: -#if !defined(RAD1O) + IF_NOT_RAD1O ( rffc5071_disable(&mixer->rffc5071); -#endif - break; - case MAX2871_VARIANT: -#if defined(RAD1O) + ) + + IF_RAD1O ( max2871_disable(&mixer->max2871); -#endif - break; - } + ) } void mixer_set_gpo(mixer_driver_t* const mixer, uint8_t gpo) { -#if defined(RAD1O) - (void) gpo; -#endif - - switch (mixer->type) { - case RFFC5071_VARIANT: -#if !defined(RAD1O) + IF_NOT_RAD1O ( rffc5071_set_gpo(&mixer->rffc5071, gpo); -#endif - break; - case MAX2871_VARIANT: - break; - } + ) + + IF_RAD1O ( + (void) mixer; + (void) gpo; + ) } diff --git a/firmware/common/operacake_sctimer.c b/firmware/common/operacake_sctimer.c index 21ce3d7e..82bb6400 100644 --- a/firmware/common/operacake_sctimer.c +++ b/firmware/common/operacake_sctimer.c @@ -98,8 +98,7 @@ void operacake_sctimer_init(void) SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1); uint8_t sct_clock_input; - if (detected_platform() != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) + IF_NOT_PRALINE ( // Configure the SGPIO to output the clock (f=2 * sample clock) on pin 12 SGPIO_OUT_MUX_CFG12 = SGPIO_OUT_MUX_CFG_P_OUT_CFG(0x08) | // clkout output mode @@ -110,9 +109,8 @@ void operacake_sctimer_init(void) GIMA_CTIN_1_IN = 0x2 << 4; // Route SGPIO12 to SCTIN1 sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_1; -#endif - } else { -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( // Configure pin P6_4 as SCT_IN_6 scu_pinmux(P6_4, SCU_CLK_IN | SCU_CONF_FUNCTION1); @@ -120,8 +118,7 @@ void operacake_sctimer_init(void) GIMA_CTIN_6_IN = 0x0 << 4; sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_6; -#endif - } + ) // We configure this register first, because the user manual says to SCT_CONFIG |= SCT_CONFIG_UNIFY_32_BIT | SCT_CONFIG_CLKMODE_PRESCALED_BUS_CLOCK | diff --git a/firmware/common/platform_detect.h b/firmware/common/platform_detect.h index 4f0f0f43..f8bc53b9 100644 --- a/firmware/common/platform_detect.h +++ b/firmware/common/platform_detect.h @@ -31,6 +31,117 @@ #define PLATFORM_HACKRF1_R9 (1 << 3) #define PLATFORM_PRALINE (1 << 4) +/* clang-format off */ + +/* Helper macros for platform-specific code. */ +#if defined(UNIVERSAL) + #define IF_PRALINE(...) \ + if (detected_platform() == BOARD_ID_PRALINE) { __VA_ARGS__; } + #define IF_NOT_PRALINE(...) \ + if (detected_platform() != BOARD_ID_PRALINE) { __VA_ARGS__; } + #define IF_HACKRF_ONE(...) \ + switch (detected_platform()) { \ + case BOARD_ID_HACKRF1_OG: \ + case BOARD_ID_HACKRF1_R9: \ + { __VA_ARGS__; } \ + break; \ + default: \ + break; \ + } + #define IF_NOT_HACKRF_ONE(...) \ + switch (detected_platform()) { \ + case BOARD_ID_HACKRF1_OG: \ + case BOARD_ID_HACKRF1_R9: \ + break; \ + default: \ + { __VA_ARGS__; } \ + break; \ + } + #define IF_H1_R9(...) \ + if (detected_platform() == BOARD_ID_HACKRF1_R9) { __VA_ARGS__; } + #define IF_NOT_H1_R9(...) \ + if (detected_platform() != BOARD_ID_HACKRF1_R9) { __VA_ARGS__; } + #define IF_RAD1O(...) + #define IF_NOT_RAD1O(...) { __VA_ARGS__; } + #define IF_JAWBREAKER(...) + #define IF_NOT_JAWBREAKER(...) { __VA_ARGS__; } + #define IF_H1_OR_PRALINE(...) { __VA_ARGS__; } + #define IF_H1_OR_RAD1O(...) IF_HACKRF_ONE(__VA_ARGS__) + #define IF_H1_OR_JAWBREAKER(...) IF_HACKRF_ONE(__VA_ARGS__) + #define IF_FOUR_LEDS(...) IF_PRALINE(__VA_ARGS__) + #define IF_EXPANSION_COMPATIBLE(...) { __VA_ARGS__; } +#elif defined(HACKRF_ONE) + #define IF_PRALINE(...) + #define IF_NOT_PRALINE(...) { __VA_ARGS__; } + #define IF_HACKRF_ONE(...) { __VA_ARGS__; } + #define IF_NOT_HACKRF_ONE(...) + #define IF_H1_R9(...) \ + if (detected_platform() == BOARD_ID_HACKRF1_R9) { __VA_ARGS__; } + #define IF_NOT_H1_R9(...) \ + if (detected_platform() != BOARD_ID_HACKRF1_R9) { __VA_ARGS__; } + #define IF_RAD1O(...) + #define IF_NOT_RAD1O(...) { __VA_ARGS__; } + #define IF_JAWBREAKER(...) + #define IF_NOT_JAWBREAKER(...) { __VA_ARGS__; } + #define IF_H1_OR_PRALINE(...) { __VA_ARGS__; } + #define IF_H1_OR_RAD1O(...) { __VA_ARGS__; } + #define IF_H1_OR_JAWBREAKER(...) { __VA_ARGS__; } + #define IF_FOUR_LEDS(...) + #define IF_EXPANSION_COMPATIBLE(...) { __VA_ARGS__; } +#elif defined(PRALINE) + #define IF_PRALINE(...) { __VA_ARGS__; } + #define IF_NOT_PRALINE(...) + #define IF_HACKRF_ONE(...) + #define IF_NOT_HACKRF_ONE(...) { __VA_ARGS__; } + #define IF_H1_R9(...) + #define IF_NOT_H1_R9(...) { __VA_ARGS__; } + #define IF_RAD1O(...) + #define IF_NOT_RAD1O(...) { __VA_ARGS__; } + #define IF_JAWBREAKER(...) + #define IF_NOT_JAWBREAKER(...) { __VA_ARGS__; } + #define IF_H1_OR_PRALINE(...) { __VA_ARGS__; } + #define IF_H1_OR_RAD1O(...) + #define IF_H1_OR_JAWBREAKER(...) + #define IF_FOUR_LEDS(...) + #define IF_EXPANSION_COMPATIBLE(...) { __VA_ARGS__; } +#elif defined(RAD1O) + #define IF_PRALINE(...) + #define IF_NOT_PRALINE(...) { __VA_ARGS__; } + #define IF_HACKRF_ONE(...) + #define IF_NOT_HACKRF_ONE(...) { __VA_ARGS__; } + #define IF_H1_R9(...) + #define IF_NOT_H1_R9(...) { __VA_ARGS__; } + #define IF_RAD1O(...) { __VA_ARGS__; } + #define IF_NOT_RAD1O(...) + #define IF_JAWBREAKER(...) + #define IF_NOT_JAWBREAKER(...) { __VA_ARGS__; } + #define IF_H1_OR_PRALINE(...) + #define IF_H1_OR_RAD1O(...) { __VA_ARGS__; } + #define IF_H1_OR_JAWBREAKER(...) + #define IF_FOUR_LEDS(...) { __VA_ARGS__; } + #define IF_EXPANSION_COMPATIBLE(...) +#elif defined(JAWBREAKER) + #define IF_PRALINE(...) + #define IF_NOT_PRALINE(...) { __VA_ARGS__; } + #define IF_HACKRF_ONE(...) + #define IF_NOT_HACKRF_ONE(...) { __VA_ARGS__; } + #define IF_H1_R9(...) + #define IF_NOT_H1_R9(...) { __VA_ARGS__; } + #define IF_RAD1O(...) + #define IF_NOT_RAD1O(...) { __VA_ARGS__; } + #define IF_JAWBREAKER(...) { __VA_ARGS__; } + #define IF_NOT_JAWBREAKER(...) + #define IF_H1_OR_PRALINE(...) + #define IF_H1_OR_RAD1O(...) + #define IF_H1_OR_JAWBREAKER(...) { __VA_ARGS__; } + #define IF_FOUR_LEDS(...) + #define IF_EXPANSION_COMPATIBLE(...) +#else + #error "No recognised platform defined" +#endif + +/* clang-format on */ + typedef enum { BOARD_ID_JELLYBEAN = 0, BOARD_ID_JAWBREAKER = 1, diff --git a/firmware/common/platform_gpio.c b/firmware/common/platform_gpio.c index f9a44903..7f773b20 100644 --- a/firmware/common/platform_gpio.c +++ b/firmware/common/platform_gpio.c @@ -34,93 +34,61 @@ const platform_gpio_t* platform_gpio(void) return _platform_gpio; } - board_id_t board_id = detected_platform(); static platform_gpio_t gpio; /* LEDs */ gpio.led[0] = &GPIO2_1; gpio.led[1] = &GPIO2_2; gpio.led[2] = &GPIO2_8; - switch (board_id) { - case BOARD_ID_RAD1O: -#if defined(RAD1O) + IF_RAD1O ( gpio.led[3] = &GPIO5_26; -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( gpio.led[3] = &GPIO4_6; -#endif - break; - default: - break; - } + ) /* Power Supply Control */ - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( gpio.gpio_1v2_enable = &GPIO4_7; gpio.gpio_3v3aux_enable_n = &GPIO5_15; -#endif - break; - default: + ) + IF_NOT_PRALINE ( gpio.gpio_1v8_enable = &GPIO3_6; - break; - } + ) /* MAX283x GPIO (XCVR_CTL / CS_XCVR) PinMux */ - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( gpio.max283x_select = &GPIO6_28; gpio.max283x_enable = &GPIO7_1; gpio.max283x_rx_enable = &GPIO7_2; gpio.max2831_rxhp = &GPIO6_29; gpio.max2831_ld = &GPIO4_11; -#endif - break; - default: + ) + IF_NOT_PRALINE ( gpio.max283x_select = &GPIO0_15; gpio.max283x_enable = &GPIO2_6; gpio.max283x_rx_enable = &GPIO2_5; gpio.max283x_tx_enable = &GPIO2_4; - break; - } + ) /* MAX5864 SPI chip select (AD_CS / CS_AD) GPIO PinMux */ - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( gpio.max5864_select = &GPIO6_30; -#endif - break; - default: + ) + IF_NOT_PRALINE ( gpio.max5864_select = &GPIO2_7; - break; - } + ) /* RF supply (VAA) control */ - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_HACKRF_ONE ( gpio.vaa_disable = &GPIO2_9; -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( gpio.vaa_disable = &GPIO4_1; -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( gpio.vaa_enable = &GPIO2_9; -#endif - break; - default: - break; - } + ) /* W25Q80BV Flash */ gpio.w25q80bv_hold = &GPIO1_14; @@ -128,10 +96,7 @@ const platform_gpio_t* platform_gpio(void) gpio.w25q80bv_select = &GPIO5_11; /* RF switch control */ - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_HACKRF_ONE ( gpio.hp = &GPIO2_0; gpio.lp = &GPIO2_10; gpio.tx_mix_bp = &GPIO2_11; @@ -145,13 +110,12 @@ const platform_gpio_t* platform_gpio(void) gpio.amp_bypass = &GPIO0_14; gpio.rx_amp = &GPIO1_11; gpio.no_rx_amp_pwr = &GPIO1_12; - // HackRF One rev 9 - gpio.h1r9_rx = &GPIO0_7; - gpio.h1r9_no_ant_pwr = &GPIO2_4; -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + IF_H1_R9 ( + gpio.h1r9_rx = &GPIO0_7; + gpio.h1r9_no_ant_pwr = &GPIO2_4; + ) + ) + IF_RAD1O ( gpio.tx_rx_n = &GPIO1_11; gpio.tx_rx = &GPIO0_14; gpio.by_mix = &GPIO1_12; @@ -163,118 +127,70 @@ const platform_gpio_t* platform_gpio(void) gpio.low_high_filt_n = &GPIO2_12; gpio.tx_amp = &GPIO2_15; gpio.rx_lna = &GPIO5_15; -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( gpio.tx_en = &GPIO3_4; gpio.mix_en_n = &GPIO3_2; gpio.mix_en_n_r1_0 = &GPIO5_6; gpio.lpf_en = &GPIO4_8; gpio.rf_amp_en = &GPIO4_9; gpio.ant_bias_en_n = &GPIO1_12; -#endif - break; - case BOARD_ID_JELLYBEAN: - case BOARD_ID_JAWBREAKER: - break; - default: - break; - } + ) /* CPLD JTAG interface GPIO pins_FPGA config pins in Praline */ gpio.cpld_tck = &GPIO3_0; - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( gpio.fpga_cfg_creset = &GPIO2_11; gpio.fpga_cfg_cdone = &GPIO5_14; gpio.fpga_cfg_spi_cs = &GPIO2_10; -#endif - break; - default: -#if !defined(PRALINE) || defined(UNIVERSAL) + ) + IF_NOT_PRALINE ( gpio.cpld_tdo = &GPIO5_18; - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_RAD1O: + IF_H1_OR_RAD1O ( gpio.cpld_tms = &GPIO3_4; gpio.cpld_tdi = &GPIO3_1; - break; - case BOARD_ID_JAWBREAKER: + ) + IF_JAWBREAKER ( gpio.cpld_tms = &GPIO3_1; gpio.cpld_tdi = &GPIO3_4; - break; - default: - break; - } -#endif - break; - } - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + ) + ) + IF_EXPANSION_COMPATIBLE ( gpio.cpld_pp_tms = &GPIO1_1; gpio.cpld_pp_tdo = &GPIO1_8; -#endif - break; - default: - break; - } + ) /* Other CPLD interface GPIO pins */ - switch (board_id) { - case BOARD_ID_PRALINE: - break; - default: -#if !defined(PRALINE) || defined(UNIVERSAL) + IF_NOT_PRALINE ( gpio.trigger_enable = &GPIO5_12; -#endif - break; - } + ) gpio.q_invert = &GPIO0_13; /* RFFC5071 GPIO serial interface PinMux */ - switch (board_id) { - case BOARD_ID_JAWBREAKER: - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_NOT_RAD1O ( gpio.rffc5072_select = &GPIO2_13; gpio.rffc5072_clock = &GPIO5_6; gpio.rffc5072_data = &GPIO3_3; gpio.rffc5072_reset = &GPIO2_14; -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( gpio.vco_ce = &GPIO2_13; gpio.vco_sclk = &GPIO5_6; gpio.vco_sdata = &GPIO3_3; gpio.vco_le = &GPIO2_14; gpio.vco_mux = &GPIO5_25; gpio.synt_rfout_en = &GPIO3_5; -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( gpio.rffc5072_select = &GPIO2_13; gpio.rffc5072_clock = &GPIO5_18; gpio.rffc5072_data = &GPIO4_14; gpio.rffc5072_reset = &GPIO2_14; gpio.rffc5072_ld = &GPIO6_25; -#endif - break; - default: - break; - } + ) -#if defined(PRALINE) || defined(UNIVERSAL) - switch (board_id) { - case BOARD_ID_PRALINE: + /* Praline */ + IF_PRALINE ( gpio.p2_ctrl0 = &GPIO7_3; gpio.p2_ctrl1 = &GPIO7_4; gpio.p1_ctrl0 = &GPIO0_14; @@ -285,50 +201,33 @@ const platform_gpio_t* platform_gpio(void) gpio.trigger_in = &GPIO6_26; gpio.trigger_out = &GPIO5_6; gpio.pps_out = &GPIO5_5; - break; - default: - break; - } -#endif + ) -#if defined(HACKRF_ONE) || defined(UNIVERSAL) /* HackRF One r9 clock control */ - switch (board_id) { - case BOARD_ID_HACKRF1_R9: + IF_H1_R9 ( gpio.h1r9_clkin_en = &GPIO5_15; gpio.h1r9_clkout_en = &GPIO0_9; gpio.h1r9_mcu_clk_en = &GPIO0_8; - break; - default: - break; - } - - switch (board_id) { - case BOARD_ID_HACKRF1_R9: gpio.h1r9_1v8_enable = &GPIO2_9; gpio.h1r9_vaa_disable = &GPIO3_6; gpio.h1r9_trigger_enable = &GPIO5_5; - break; - default: - break; - } -#endif + ) /* rad1o LCD */ -#if defined(RAD1O) - gpio.lcd_cs = &GPIO4_12; /* P9_0 */ - gpio.lcd_bl_en = &GPIO0_8; /* P1_1 */ - gpio.lcd_reset = &GPIO5_17; /* P9_4 */ -#endif + IF_RAD1O ( + gpio.lcd_cs = &GPIO4_12; /* P9_0 */ + gpio.lcd_bl_en = &GPIO0_8; /* P1_1 */ + gpio.lcd_reset = &GPIO5_17; /* P9_4 */ + ) /* Portapack */ -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) - gpio.io_stbx = &GPIO5_0; /* P2_0 */ - gpio.addr = &GPIO5_1; /* P2_1 */ - gpio.lcd_rdx = &GPIO5_4; /* P2_4 */ - gpio.lcd_wrx = &GPIO1_10; /* P2_9 */ - gpio.dir = &GPIO1_13; /* P2_13 */ -#endif + IF_EXPANSION_COMPATIBLE ( + gpio.io_stbx = &GPIO5_0; /* P2_0 */ + gpio.addr = &GPIO5_1; /* P2_1 */ + gpio.lcd_rdx = &GPIO5_4; /* P2_4 */ + gpio.lcd_wrx = &GPIO1_10; /* P2_9 */ + gpio.dir = &GPIO1_13; /* P2_13 */ + ) _platform_gpio = &gpio; diff --git a/firmware/common/platform_scu.c b/firmware/common/platform_scu.c index edd6ee0e..9d3b0c5d 100644 --- a/firmware/common/platform_scu.c +++ b/firmware/common/platform_scu.c @@ -36,68 +36,40 @@ const platform_scu_t* platform_scu(void) return _platform_scu; } - board_id_t board_id = detected_platform(); static platform_scu_t scu; /* LED PinMux */ scu.PINMUX_LED1 = SCU_PINMUX_LED1; /* GPIO2[1] on P4_1 */ scu.PINMUX_LED2 = SCU_PINMUX_LED2; /* GPIO2[2] on P4_2 */ scu.PINMUX_LED3 = SCU_PINMUX_LED3; /* GPIO2[8] on P6_12 */ - switch (board_id) { - case BOARD_ID_RAD1O: -#if defined(RAD1O) + IF_RAD1O ( scu.PINMUX_LED4 = (PB_6); /* GPIO5[26] on PB_6 */ -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( scu.PINMUX_LED4 = (P8_6); /* GPIO4[6] on P8_6 */ -#endif - break; - default: - break; - } + ) /* Power Supply PinMux */ scu.PINMUX_EN1V8 = (P6_10); /* GPIO3[6] on P6_10 */ scu.PINMUX_EN1V2 = (P8_7); /* GPIO4[7] on P8_7 */ - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( scu.PINMUX_EN3V3_AUX_N = (P6_7); /* GPIO5[15] on P6_7 */ scu.PINMUX_EN3V3_OC_N = (P6_11); /* GPIO3[7] on P6_11 */ -#endif - break; - default: - break; - } + ) /* GPIO Input PinMux */ scu.PINMUX_BOOT0 = (P1_1); /* GPIO0[8] on P1_1 */ scu.PINMUX_BOOT1 = (P1_2); /* GPIO0[9] on P1_2 */ - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - break; - default: -#if !defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_NOT_HACKRF_ONE ( scu.PINMUX_BOOT2 = (P2_8); /* GPIO5[7] on P2_8 */ scu.PINMUX_BOOT3 = (P2_9); /* GPIO1[10] on P2_9 */ -#endif - break; - } + ) /* USB peripheral */ - switch (board_id) { - case BOARD_ID_JAWBREAKER: -#if defined(JAWBREAKER) + IF_JAWBREAKER ( scu.PINMUX_USB_LED0 = (P6_8); scu.PINMUX_USB_LED1 = (P6_7); -#endif - break; - default: - break; - } + ) /* SSP1 Peripheral PinMux */ scu.SSP1_CIPO = (P1_3); /* P1_3 */ @@ -107,37 +79,25 @@ const platform_scu_t* platform_scu(void) /* CPLD JTAG interface */ scu.PINMUX_CPLD_TCK = (P6_1); /* GPIO3[ 0] */ - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( scu.PINMUX_FPGA_CRESET = (P5_2); /* GPIO2[11] on P5_2 */ scu.PINMUX_FPGA_CDONE = (P4_10); /* GPIO5[14] */ scu.PINMUX_FPGA_SPI_CS = (P5_1); /* GPIO2[10] */ -#endif - break; - default: - break; - } -#if !defined(PRALINE) || defined(UNIVERSAL) - scu.PINMUX_CPLD_TDO = (P9_5); /* GPIO5[18] */ - switch (board_id) { - case BOARD_ID_RAD1O: - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: + ) + IF_NOT_PRALINE ( + scu.PINMUX_CPLD_TDO = (P9_5); /* GPIO5[18] */ + ) + IF_H1_OR_RAD1O ( scu.PINMUX_CPLD_TMS = (P6_5); /* GPIO3[ 4] */ scu.PINMUX_CPLD_TDI = (P6_2); /* GPIO3[ 1] */ - break; - default: + ) + IF_JAWBREAKER ( scu.PINMUX_CPLD_TMS = (P6_2); /* GPIO3[ 1] */ scu.PINMUX_CPLD_TDI = (P6_5); /* GPIO3[ 4] */ - break; - } -#endif + ) /* CPLD SGPIO interface */ - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( scu.PINMUX_SGPIO0 = (P0_0); scu.PINMUX_SGPIO1 = (P0_1); scu.PINMUX_SGPIO2 = (P1_15); @@ -169,10 +129,8 @@ const platform_scu_t* platform_scu(void) scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); -#endif - break; - default: -#if !defined(PRALINE) || defined(UNIVERSAL) + ) + IF_NOT_PRALINE ( scu.PINMUX_SGPIO0 = (P0_0); scu.PINMUX_SGPIO1 = (P0_1); scu.PINMUX_SGPIO2 = (P1_15); @@ -204,22 +162,16 @@ const platform_scu_t* platform_scu(void) scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); -#endif - break; - } + ) scu.TRIGGER_EN = (P4_8); /* GPIO5[12] on P4_8 */ /* MAX283x GPIO (XCVR_CTL) PinMux */ - switch (board_id) { - case BOARD_ID_RAD1O: -#if defined(RAD1O) + IF_RAD1O ( scu.XCVR_RXHP = P8_1; /* GPIO[] on P8_1 */ scu.XCVR_B6 = P8_2; /* GPIO[] on P8_2 */ scu.XCVR_B7 = P9_3; /* GPIO[] on P9_3 */ -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( scu.XCVR_ENABLE = PE_1; /* GPIO7[1] on PE_1 */ scu.XCVR_RXENABLE = PE_2; /* GPIO7[2] on PE_2 */ scu.XCVR_CS = PD_14; /* GPIO6[28] on PD_14 */ @@ -232,12 +184,8 @@ const platform_scu_t* platform_scu(void) scu.XCVR_RXHP_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.XCVR_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0 | SCU_CONF_EPD_EN_PULLDOWN | SCU_CONF_EPUN_DIS_PULLUP); -#endif - break; - case BOARD_ID_JAWBREAKER: - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(UNIVERSAL) + ) + IF_H1_OR_JAWBREAKER ( scu.XCVR_ENABLE = P4_6; /* GPIO2[6] on P4_6 */ scu.XCVR_RXENABLE = P4_5; /* GPIO2[5] on P4_5 */ scu.XCVR_TXENABLE = P4_4; /* GPIO2[4] on P4_4 */ @@ -247,32 +195,20 @@ const platform_scu_t* platform_scu(void) scu.XCVR_RXENABLE_PINCFG = (SCU_GPIO_FAST); scu.XCVR_TXENABLE_PINCFG = (SCU_GPIO_FAST); scu.XCVR_CS_PINCFG = (SCU_GPIO_FAST); -#endif - break; - default: - break; - } + ) /* MAX5864 SPI chip select (AD_CS) GPIO PinMux */ - switch (board_id) { - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( scu.AD_CS = (PD_16); /* GPIO6[30] on PD_16 */ scu.AD_CS_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); -#endif - break; - default: + ) + IF_NOT_PRALINE ( scu.AD_CS = (P5_7); /* GPIO2[7] on P5_7 */ scu.AD_CS_PINCFG = (SCU_GPIO_FAST); - break; - } + ) /* RFFC5071 GPIO serial interface PinMux */ - switch (board_id) { - case BOARD_ID_JAWBREAKER: - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_H1_OR_JAWBREAKER ( scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */ scu.MIXER_SCLK = (P2_6); /* GPIO5[6] on P2_6 */ scu.MIXER_SDATA = (P6_4); /* GPIO3[3] on P6_4 */ @@ -280,10 +216,8 @@ const platform_scu_t* platform_scu(void) scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */ scu.MIXER_SCLK = (P9_5); /* GPIO5[18] on P9_5 */ scu.MIXER_SDATA = (P9_2); /* GPIO4[14] on P9_2 */ @@ -293,10 +227,8 @@ const platform_scu_t* platform_scu(void) scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu.MIXER_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( scu.VCO_CE = (P5_4); /* GPIO2[13] on P5_4 */ scu.VCO_SCLK = (P2_6); /* GPIO5[6] on P2_6 */ scu.VCO_SDATA = (P6_4); /* GPIO3[3] on P6_4 */ @@ -304,44 +236,23 @@ const platform_scu_t* platform_scu(void) scu.VCO_MUX = (PB_5); /* GPIO5[25] on PB_5 */ scu.MIXER_EN = (P6_8); /* GPIO5[16] on P6_8 */ scu.SYNT_RFOUT_EN = (P6_9); /* GPIO3[5] on P6_9 */ -#endif - break; - default: - break; - } + ) /* RF LDO control */ - switch (board_id) { - case BOARD_ID_JAWBREAKER: -#if defined(JAWBREAKER) + IF_JAWBREAKER ( scu.RF_LDO_ENABLE = (P5_0); /* GPIO2[9] on P5_0 */ -#endif - break; - default: - break; - } + ) /* RF supply (VAA) control */ - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_HACKRF_ONE ( scu.NO_VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */ -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( scu.NO_VAA_ENABLE = P8_1; /* GPIO4[1] on P8_1 */ -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( scu.VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */ -#endif - break; - default: - break; - } + ) /* SPI flash */ scu.SSP0_CIPO = (P3_6); @@ -352,10 +263,7 @@ const platform_scu_t* platform_scu(void) scu.FLASH_WP = (P3_5); /* GPIO1[15] on P3_5 */ /* RF switch control */ - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_HACKRF_ONE ( scu.HP = P4_0; /* GPIO2[0] on P4_0 */ scu.LP = P5_1; /* GPIO2[10] on P5_1 */ scu.TX_MIX_BP = P5_2; /* GPIO2[11] on P5_2 */ @@ -369,10 +277,8 @@ const platform_scu_t* platform_scu(void) scu.AMP_BYPASS = P2_10; /* GPIO0[14] on P2_10 */ scu.RX_AMP = P2_11; /* GPIO1[11] on P2_11 */ scu.NO_RX_AMP_PWR = P2_12; /* GPIO1[12] on P2_12 */ -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( scu.BY_AMP = P1_7; /* GPIO1[0] on P1_7 */ scu.BY_AMP_N = P2_5; /* GPIO5[5] on P2_5 */ scu.TX_RX = P2_10; /* GPIO0[14] on P2_10 */ @@ -383,10 +289,8 @@ const platform_scu_t* platform_scu(void) scu.LOW_HIGH_FILT_N = P5_3; /* GPIO2[12] on P5_3 */ scu.TX_AMP = P5_6; /* GPIO2[15] on P5_6 */ scu.RX_LNA = P6_7; /* GPIO5[15] on P6_7 */ -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( scu.TX_EN = P6_5; /* GPIO3[4] on P6_5 */ scu.MIX_EN_N = P6_3; /* GPIO3[2] on P6_3 */ scu.MIX_EN_N_R1_0 = P2_6; /* GPIO5[6] on P2_6 */ @@ -394,16 +298,10 @@ const platform_scu_t* platform_scu(void) scu.RF_AMP_EN = PA_2; /* GPIO4[9] on PA_2 */ scu.ANT_BIAS_EN_N = P2_12; /* GPIO1[12] on P2_12 */ scu.ANT_BIAS_OC_N = P2_11; /* GPIO1[11] on P2_11 */ -#endif - break; - default: - break; - } + ) /* Praline */ -#if defined(PRALINE) || defined(UNIVERSAL) - switch (board_id) { - case BOARD_ID_PRALINE: + IF_PRALINE ( scu.P2_CTRL0 = (PE_3); /* GPIO7[3] on PE_3 */ scu.P2_CTRL1 = (PE_4); /* GPIO7[4] on PE_4 */ scu.P1_CTRL0 = (P2_10); /* GPIO0[14] on P2_10 */ @@ -425,16 +323,10 @@ const platform_scu_t* platform_scu(void) scu.TRIGGER_IN_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.TRIGGER_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.PPS_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - break; - default: - break; - } -#endif + ) /* HackRF One r9 */ -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - switch (board_id) { - case BOARD_ID_HACKRF1_R9: + IF_H1_R9 ( scu.H1R9_CLKIN_EN = P6_7; /* GPIO5[15] on P6_7 */ scu.H1R9_CLKOUT_EN = P1_2; /* GPIO0[9] on P1_2 = has boot pull-down; */ scu.H1R9_MCU_CLK_EN = P1_1; /* GPIO0[8] on P1_1 = has boot pull-up; */ @@ -443,54 +335,50 @@ const platform_scu_t* platform_scu(void) scu.H1R9_EN1V8 = P5_0; /* GPIO2[9] on P5_0 */ scu.H1R9_NO_VAA_EN = P6_10; /* GPIO3[6] on P6_10 */ scu.H1R9_TRIGGER_EN = P2_5; /* GPIO5[5] on P2_5 */ - break; - default: - break; - } -#endif + ) /* Expansion headers */ -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) - scu.PINMUX_PP_D0 = (P7_0); /* GPIO3[8] */ - scu.PINMUX_PP_D1 = (P7_1); /* GPIO3[9] */ - scu.PINMUX_PP_D2 = (P7_2); /* GPIO3[10] */ - scu.PINMUX_PP_D3 = (P7_3); /* GPIO3[11] */ - scu.PINMUX_PP_D4 = (P7_4); /* GPIO3[12] */ - scu.PINMUX_PP_D5 = (P7_5); /* GPIO3[13] */ - scu.PINMUX_PP_D6 = (P7_6); /* GPIO3[14] */ - scu.PINMUX_PP_D7 = (P7_7); /* GPIO3[15] */ + IF_EXPANSION_COMPATIBLE ( + scu.PINMUX_PP_D0 = (P7_0); /* GPIO3[8] */ + scu.PINMUX_PP_D1 = (P7_1); /* GPIO3[9] */ + scu.PINMUX_PP_D2 = (P7_2); /* GPIO3[10] */ + scu.PINMUX_PP_D3 = (P7_3); /* GPIO3[11] */ + scu.PINMUX_PP_D4 = (P7_4); /* GPIO3[12] */ + scu.PINMUX_PP_D5 = (P7_5); /* GPIO3[13] */ + scu.PINMUX_PP_D6 = (P7_6); /* GPIO3[14] */ + scu.PINMUX_PP_D7 = (P7_7); /* GPIO3[15] */ - scu.PINMUX_PP_LCD_TE = (P2_3); /* GPIO5[3] on P2_3 */ - scu.PINMUX_PP_LCD_RDX = (P2_4); /* GPIO5[4] on P2_4 */ - scu.PINMUX_PP_UNUSED = (P2_8); /* GPIO5[7] on P2_8 */ - scu.PINMUX_PP_LCD_WRX = (P2_9); /* GPIO1[10] on P2_9 */ - scu.PINMUX_PP_DIR = (P2_13); /* GPIO1[13] on P2_13 */ + scu.PINMUX_PP_LCD_TE = (P2_3); /* GPIO5[3] on P2_3 */ + scu.PINMUX_PP_LCD_RDX = (P2_4); /* GPIO5[4] on P2_4 */ + scu.PINMUX_PP_UNUSED = (P2_8); /* GPIO5[7] on P2_8 */ + scu.PINMUX_PP_LCD_WRX = (P2_9); /* GPIO1[10] on P2_9 */ + scu.PINMUX_PP_DIR = (P2_13); /* GPIO1[13] on P2_13 */ - scu.PINMUX_PP_TDO = (P1_5); /* GPIO1[8] */ - scu.PINMUX_SD_POW = (P1_5); /* GPIO1[8] */ - scu.PINMUX_SD_CMD = (P1_6); /* GPIO1[9] */ - scu.PINMUX_PP_TMS = (P1_8); /* GPIO1[1] */ - scu.PINMUX_SD_VOLT0 = (P1_8); /* GPIO1[1] */ - scu.PINMUX_SD_DAT0 = (P1_9); /* GPIO1[2] */ - scu.PINMUX_SD_DAT1 = (P1_10); /* GPIO1[3] */ - scu.PINMUX_SD_DAT2 = (P1_11); /* GPIO1[4] */ - scu.PINMUX_SD_DAT3 = (P1_12); /* GPIO1[5] */ - scu.PINMUX_SD_CD = (P1_13); /* GPIO1[6] */ + scu.PINMUX_PP_TDO = (P1_5); /* GPIO1[8] */ + scu.PINMUX_SD_POW = (P1_5); /* GPIO1[8] */ + scu.PINMUX_SD_CMD = (P1_6); /* GPIO1[9] */ + scu.PINMUX_PP_TMS = (P1_8); /* GPIO1[1] */ + scu.PINMUX_SD_VOLT0 = (P1_8); /* GPIO1[1] */ + scu.PINMUX_SD_DAT0 = (P1_9); /* GPIO1[2] */ + scu.PINMUX_SD_DAT1 = (P1_10); /* GPIO1[3] */ + scu.PINMUX_SD_DAT2 = (P1_11); /* GPIO1[4] */ + scu.PINMUX_SD_DAT3 = (P1_12); /* GPIO1[5] */ + scu.PINMUX_SD_CD = (P1_13); /* GPIO1[6] */ - scu.PINMUX_PP_IO_STBX = (P2_0); /* GPIO5[0] */ - scu.PINMUX_PP_ADDR = (P2_1); /* GPIO5[1] */ - scu.PINMUX_U0_TXD = (P2_0); /* GPIO5[0] */ - scu.PINMUX_U0_RXD = (P2_1); /* GPIO5[1] */ + scu.PINMUX_PP_IO_STBX = (P2_0); /* GPIO5[0] */ + scu.PINMUX_PP_ADDR = (P2_1); /* GPIO5[1] */ + scu.PINMUX_U0_TXD = (P2_0); /* GPIO5[0] */ + scu.PINMUX_U0_RXD = (P2_1); /* GPIO5[1] */ - scu.PINMUX_GPIO3_8 = (P7_0); /* GPIO3[8] */ - scu.PINMUX_GPIO3_9 = (P7_1); /* GPIO3[9] */ - scu.PINMUX_GPIO3_10 = (P7_2); /* GPIO3[10] */ - scu.PINMUX_GPIO3_11 = (P7_3); /* GPIO3[11] */ - scu.PINMUX_GPIO3_12 = (P7_4); /* GPIO3[12] */ - scu.PINMUX_GPIO3_13 = (P7_5); /* GPIO3[13] */ - scu.PINMUX_GPIO3_14 = (P7_6); /* GPIO3[14] */ - scu.PINMUX_GPIO3_15 = (P7_7); /* GPIO3[15] */ -#endif + scu.PINMUX_GPIO3_8 = (P7_0); /* GPIO3[8] */ + scu.PINMUX_GPIO3_9 = (P7_1); /* GPIO3[9] */ + scu.PINMUX_GPIO3_10 = (P7_2); /* GPIO3[10] */ + scu.PINMUX_GPIO3_11 = (P7_3); /* GPIO3[11] */ + scu.PINMUX_GPIO3_12 = (P7_4); /* GPIO3[12] */ + scu.PINMUX_GPIO3_13 = (P7_5); /* GPIO3[13] */ + scu.PINMUX_GPIO3_14 = (P7_6); /* GPIO3[14] */ + scu.PINMUX_GPIO3_15 = (P7_7); /* GPIO3[15] */ + ) scu.PINMUX_ISP = (P2_7); /* GPIO0[7] */ diff --git a/firmware/common/radio.c b/firmware/common/radio.c index ff97a814..a4d41c68 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -195,22 +195,18 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) case TRANSCEIVER_MODE_SS: n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) { - if (detected_platform() == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( fpga_set_tx_interpolation_ratio(&fpga, n); -#endif - } + ) radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX] = n; } break; default: n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) { - if (detected_platform() == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( fpga_set_rx_decimation_ratio(&fpga, n); -#endif - } + ) radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX] = n; } } @@ -354,7 +350,9 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) const uint64_t requested_if = bank[RADIO_FREQUENCY_IF]; const uint64_t requested_lo = bank[RADIO_FREQUENCY_LO]; const uint64_t requested_img_reject = bank[RADIO_IMAGE_REJECT]; +#if defined(PRALINE) || defined(UNIVERSAL) const uint64_t requested_rotation = bank[RADIO_ROTATION]; +#endif const uint64_t applied_rf = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF]; const uint64_t applied_if = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF]; @@ -393,7 +391,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) if (requested_lo != RADIO_UNSET) { freq_lo = mixer_set_frequency(&mixer, freq_lo, false); } - if (detected_platform() == BOARD_ID_PRALINE) { + IF_PRALINE ( if (requested_rotation != RADIO_UNSET) { rotation = requested_rotation; } @@ -409,9 +407,10 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) default: rotation = 0; } - } else { + ) + IF_NOT_PRALINE ( rotation = 0; - } + ) /* Handle requested RF (auto-tune). */ if (requested_rf != RADIO_UNSET) { @@ -421,8 +420,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) freq_rf = restrict_rf(requested_rf, img_reject); /* Look up settings appropriate for requested RF. */ - if (detected_platform() == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( const tune_config_t* tune_config = select_tune_config(opmode, freq_rf); if (requested_rotation == RADIO_UNSET) { @@ -438,15 +436,15 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) } } high_lo = tune_config->high_lo; -#endif - } else { + ) + IF_NOT_PRALINE ( /* Use graduated or fixed IF on older platforms. */ if (requested_if == RADIO_UNSET) { freq_if = select_graduated_if(freq_rf, img_reject); } high_lo = (img_reject == RF_PATH_FILTER_LOW_PASS); analog_rf = freq_rf; - } + ) /* Compute precise LO. This is done first because the mixer LO is set in coarse steps. */ if (requested_lo == RADIO_UNSET) { @@ -502,11 +500,9 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) changed |= (1 << RADIO_IMAGE_REJECT); } if ((rotation != applied_rotation) && (rotation != RADIO_UNSET)) { - if (detected_platform() == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( fpga_set_rx_quarter_shift_mode(&fpga, rotation >> 30); -#endif - } + ) radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = rotation; changed |= (1 << RADIO_ROTATION); } @@ -571,20 +567,29 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank) opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; } - if (detected_platform() == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( /* Praline legacy mode always sets baseband bandwidth automatically. */ (void) bank; uint32_t lpf_bandwidth = auto_bandwidth(radio, opmode); - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { - max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_TX, lpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth; + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != + lpf_bandwidth) { + max283x_set_lpf_bandwidth( + &max283x, + MAX283x_MODE_TX, + lpf_bandwidth); + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = + lpf_bandwidth; changed |= (1 << RADIO_XCVR_TX_LPF); } - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] != lpf_bandwidth) { - max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_RX, lpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth; + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] != + lpf_bandwidth) { + max283x_set_lpf_bandwidth( + &max283x, + MAX283x_MODE_RX, + lpf_bandwidth); + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = + lpf_bandwidth; changed |= (1 << RADIO_XCVR_RX_LPF); } bool narrow_lpf_enable = false; @@ -601,14 +606,15 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank) } /* Always set HPF bandwidth to 30 kHz for now. */ const max283x_rx_hpf_freq_t hpf_bandwidth = MAX283x_RX_HPF_30_KHZ; - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] != hpf_bandwidth) { + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] != + hpf_bandwidth) { max283x_set_rx_hpf_frequency(&max283x, hpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] = hpf_bandwidth; + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] = + hpf_bandwidth; changed |= (1 << RADIO_XCVR_RX_HPF); } -#endif - } else { -#if !defined(PRALINE) || defined(UNIVERSAL) + ) + IF_NOT_PRALINE ( uint64_t lpf_bandwidth; lpf_bandwidth = bank[RADIO_XCVR_TX_LPF]; if (lpf_bandwidth == RADIO_UNSET) { @@ -621,24 +627,33 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank) lpf_bandwidth = bank[RADIO_BB_BANDWIDTH_RX]; } if (lpf_bandwidth == RADIO_UNSET) { - lpf_bandwidth = radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF]; + lpf_bandwidth = + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF]; } if (lpf_bandwidth == RADIO_UNSET) { lpf_bandwidth = auto_bandwidth(radio, opmode); } - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { - max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_TX, lpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_RX] = lpf_bandwidth; - radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_TX] = lpf_bandwidth; - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth; - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth; + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != + lpf_bandwidth) { + max283x_set_lpf_bandwidth( + &max283x, + MAX283x_MODE_TX, + lpf_bandwidth); + radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_RX] = + lpf_bandwidth; + radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_TX] = + lpf_bandwidth; + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = + lpf_bandwidth; + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = + lpf_bandwidth; changed |= - ((1 << RADIO_BB_BANDWIDTH_RX) | (1 << RADIO_BB_BANDWIDTH_TX) | - (1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF)); + ((1 << RADIO_BB_BANDWIDTH_RX) | + (1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_XCVR_TX_LPF) | + (1 << RADIO_XCVR_RX_LPF)); } -#endif - } + ) return changed; } @@ -774,24 +789,23 @@ static uint32_t radio_update_trigger(radio_t* const radio, uint64_t* bank) static uint32_t radio_update_dc_block(radio_t* const radio, uint64_t* bank) { -#if defined(PRALINE) || defined(UNIVERSAL) - if (detected_platform() == BOARD_ID_PRALINE) { + IF_PRALINE ( const uint64_t requested = bank[RADIO_DC_BLOCK]; bool enable = requested; - if (radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] == (uint64_t) enable) { + if (radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] == + (uint64_t) enable) { return 0; } fpga_set_rx_dc_block_enable(&fpga, enable); radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] = enable; return (1 << RADIO_DC_BLOCK); - } -#else + ) + (void) radio; (void) bank; -#endif - return 0; + return false; } bool radio_update(radio_t* const radio) diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index f2234413..a757eceb 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -294,35 +294,19 @@ static void switchctrl_set_rad1o(rf_path_t* const rf_path, uint8_t ctrl) static void switchctrl_set(rf_path_t* const rf_path, const uint8_t gpo) { -#if defined(JAWBREAKER) - (void) rf_path; -#endif - - switch (detected_platform()) { - case BOARD_ID_JAWBREAKER: -#if defined(JAWBREAKER) + IF_JAWBREAKER ( + (void) rf_path; mixer_set_gpo(&mixer, gpo); -#endif - break; - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + ) + IF_HACKRF_ONE ( switchctrl_set_hackrf_one(rf_path, gpo); -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( switchctrl_set_praline(rf_path, gpo); -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( switchctrl_set_rad1o(rf_path, gpo); -#endif - break; - default: - break; - } + ) } void rf_path_pin_setup(rf_path_t* const rf_path) @@ -331,15 +315,11 @@ void rf_path_pin_setup(rf_path_t* const rf_path) (void) rf_path; #endif - board_id_t board_id = detected_platform(); #if !defined(JAWBREAKER) const platform_scu_t* scu = platform_scu(); #endif - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_HACKRF_ONE ( /* Configure RF switch control signals */ // clang-format off scu_pinmux(scu->HP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); @@ -354,7 +334,7 @@ void rf_path_pin_setup(rf_path_t* const rf_path) scu_pinmux(scu->RX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu_pinmux(scu->NO_RX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); // clang-format on - if (board_id == BOARD_ID_HACKRF1_R9) { + IF_H1_R9 ( scu_pinmux(scu->H1R9_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu_pinmux( scu->H1R9_NO_ANT_PWR, @@ -364,12 +344,13 @@ void rf_path_pin_setup(rf_path_t* const rf_path) scu_pinmux( scu->H1R9_NO_VAA_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - } else { + ) + IF_NOT_H1_R9 ( scu_pinmux(scu->TX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu_pinmux(scu->RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); gpio_output(rf_path->gpio_tx); scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - } + ) /* * Safe (initial) switch settings turn off both amplifiers and antenna port @@ -390,10 +371,8 @@ void rf_path_pin_setup(rf_path_t* const rf_path) gpio_output(rf_path->gpio_no_tx_amp_pwr); gpio_output(rf_path->gpio_mix_bypass); gpio_output(rf_path->gpio_rx); -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( /* Configure RF switch control signals */ // clang-format off scu_pinmux(scu->BY_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); @@ -430,10 +409,9 @@ void rf_path_pin_setup(rf_path_t* const rf_path) gpio_output(rf_path->gpio_low_high_filt_n); gpio_output(rf_path->gpio_tx_amp); gpio_output(rf_path->gpio_rx_lna); -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + + IF_PRALINE ( /* Configure RF switch control signals */ scu_pinmux(scu->TX_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); board_rev_t rev = detected_revision(); @@ -464,17 +442,11 @@ void rf_path_pin_setup(rf_path_t* const rf_path) gpio_output(rf_path->gpio_mix_en_n); gpio_output(rf_path->gpio_lpf_en); gpio_output(rf_path->gpio_rf_amp_en); -#endif - break; - default: - break; - } + ) } void rf_path_init(rf_path_t* const rf_path) { - board_id_t board_id = detected_platform(); - ssp1_set_mode_max5864(); max5864_setup(&max5864); max5864_shutdown(&max5864); @@ -483,22 +455,14 @@ void rf_path_init(rf_path_t* const rf_path) max283x_setup(&max283x); max283x_start(&max283x); - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - // On HackRF One, the mixer is now set up earlier in boot. - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + IF_RAD1O ( mixer_setup(&mixer, MAX2871_VARIANT); -#endif - break; - default: -#if !defined(RAD1O) - mixer_setup(&mixer, RFFC5071_VARIANT); -#endif - break; - } + ) + IF_NOT_RAD1O ( + IF_NOT_HACKRF_ONE ( + mixer_setup(&mixer, RFFC5071_VARIANT); + ) + ) rf_path->switchctrl = SWITCHCTRL_SAFE; } diff --git a/firmware/common/rffc5071.c b/firmware/common/rffc5071.c index 89befe98..fbeb2d93 100644 --- a/firmware/common/rffc5071.c +++ b/firmware/common/rffc5071.c @@ -110,19 +110,15 @@ void rffc5071_init(rffc5071_driver_t* const drv) */ void rffc5071_setup(rffc5071_driver_t* const drv) { - board_id_t board_id = detected_platform(); - gpio_set(drv->gpio_reset); gpio_output(drv->gpio_reset); - if (board_id == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( /* Configure mixer PLL lock detect pin */ const platform_scu_t* scu = platform_scu(); scu_pinmux(scu->MIXER_LD, scu->MIXER_LD_PINCFG); gpio_input(drv->gpio_ld); -#endif - } + ) rffc5071_init(drv); @@ -140,16 +136,9 @@ void rffc5071_setup(rffc5071_driver_t* const drv) set_RFFC5071_FULLD(drv, 0); set_RFFC5071_MODE(drv, 1); - switch (board_id) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: - case BOARD_ID_PRALINE: - /* Enable GPO Lock output signal */ + IF_H1_OR_PRALINE ( set_RFFC5071_LOCK(drv, 1); - break; - default: - break; - } + ) /* Enable reference oscillator standby */ set_RFFC5071_REFST(drv, 1); @@ -192,17 +181,15 @@ void rffc5071_lock_test(rffc5071_driver_t* const drv) bool rffc5071_check_lock(rffc5071_driver_t* const drv) { - if (detected_platform() == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( return gpio_read(drv->gpio_ld); -#else - return false; -#endif - } else { + ) + IF_NOT_PRALINE ( set_RFFC5071_READSEL(drv, 0b0001); rffc5071_regs_commit(drv); return !!(rffc5071_reg_read(drv, RFFC5071_READBACK_REG) & 0x8000); - } + ) + return false; } static uint16_t rffc5071_spi_read(rffc5071_driver_t* const drv, uint8_t r) @@ -382,9 +369,9 @@ bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state) // // For all other boards we'll just return true to avoid a situation where a // a caller is waiting for a lock signal that will never be detected. - if (detected_platform() != BOARD_ID_PRALINE) { + IF_NOT_PRALINE ( return true; - } + ) // The RFFC5072 can be configured to output PLL lock status on // GPO4. The lock detect signal is produced by a window detector @@ -451,9 +438,7 @@ bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state) uint8_t rsm_state = (rb >> 11) & 0b11111; // get gpo4 lock detect signal - #if defined(PRALINE) || defined(UNIVERSAL) bool gpo4_ld = gpio_read(drv->gpio_ld); - #endif // parse state switch (rsm_state) { diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index ac755d51..86b6c58e 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -57,27 +57,24 @@ void sgpio_configure_pin_functions(sgpio_config_t* const config) scu_pinmux(scu->PINMUX_SGPIO14, scu->PINMUX_SGPIO14_PINCFG); /* GPIO5[13] */ scu_pinmux(scu->PINMUX_SGPIO15, scu->PINMUX_SGPIO15_PINCFG); /* GPIO5[14] */ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_H1_R9 ( scu_pinmux( scu->H1R9_TRIGGER_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[5] */ -#endif - } else { + ) + IF_NOT_H1_R9 ( scu_pinmux( scu->TRIGGER_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[12] */ - } + ) sgpio_cpld_set_mixer_invert(config, 0); gpio_output(config->gpio_q_invert); - if (detected_platform() != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) + IF_NOT_PRALINE ( trigger_enable(false); gpio_output(config->gpio_trigger_enable); -#endif - } + ) } void sgpio_set_slice_mode(sgpio_config_t* const config, const bool multi_slice) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index b50aad81..dbedddf3 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -235,7 +235,6 @@ void si5351c_configure_multisynth( void si5351c_configure_clock_control(si5351c_driver_t* const drv) { uint8_t clkout_ctrl; - board_id_t board_id = detected_platform(); if (clkout_enabled) { clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | @@ -270,8 +269,7 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE /* not connected, but: PLL B int mode */ }; - if (board_id == BOARD_ID_HACKRF1_R9) { -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_H1_R9 ( data[1] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA); @@ -282,10 +280,8 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) data[4] = SI5351C_CLK_POWERDOWN; data[5] = SI5351C_CLK_POWERDOWN; data[6] = SI5351C_CLK_POWERDOWN; -#endif - } - if (board_id == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( /* CLK0: AFE_CLK */ data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | @@ -315,8 +311,7 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) /* CLK2: MCU_CLK */ data[3] = SI5351C_CLK_POWERDOWN; } -#endif - } + ) si5351c_write(drv, data, sizeof(data)); } @@ -331,11 +326,10 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) /* 7: Clock to CPU is deactivated as it is not used and creates noise */ /* 3: External clock output is deactivated by default */ - board_id_t board_id = detected_platform(); uint8_t clkout = 3; - uint8_t value; + uint8_t value = 0; - if (board_id == BOARD_ID_PRALINE) { + IF_PRALINE ( value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); @@ -345,21 +339,22 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) } else { value |= SI5351C_CLK_DISABLE(2); } - } else { + ) + IF_NOT_PRALINE ( value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | SI5351C_CLK_ENABLE(2) | SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); /* HackRF One r9 has only three clock generator outputs. */ - if (board_id == BOARD_ID_HACKRF1_R9) { + IF_H1_R9 ( clkout = 2; value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | SI5351C_CLK_DISABLE(3) | SI5351C_CLK_DISABLE(4) | SI5351C_CLK_DISABLE(5) | SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); - } - } + ) + ) value |= (clkout_enabled) ? SI5351C_CLK_ENABLE(clkout) : SI5351C_CLK_DISABLE(clkout); @@ -367,16 +362,14 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) si5351c_write(drv, data, sizeof(data)); outputs_disabled = value; - if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_H1_R9 ( const platform_gpio_t* gpio = platform_gpio(); if (clkout_enabled) { gpio_set(gpio->h1r9_clkout_en); } else { gpio_clear(gpio->h1r9_clkout_en); } -#endif - } + ) } void si5351c_set_int_mode( @@ -406,8 +399,7 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source return; } si5351c_disable_all_outputs(drv); - if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#if defined(HACKRF_ONE) + IF_H1_R9 ( /* * HackRF One r9 always uses PLL A on the XTAL input * but externally switches that input to CLKIN. @@ -418,10 +410,10 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source } else { gpio_clear(platform_gpio()->h1r9_clkin_en); } -#endif - } else { + ) + IF_NOT_H1_R9 ( si5351c_configure_pll_sources(drv, source); - } + ) si5351c_configure_pll_multisynth(drv, source); active_clock_source = source; si5351c_reset_pll(drv, SI5351C_PLL_BOTH); @@ -476,8 +468,7 @@ void si5351c_init(si5351c_driver_t* const drv) selftest.report.pass = false; } - if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_H1_R9 ( const platform_gpio_t* gpio = platform_gpio(); const platform_scu_t* scu = platform_scu(); @@ -495,8 +486,7 @@ void si5351c_init(si5351c_driver_t* const drv) scu_pinmux(scu->H1R9_MCU_CLK_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); gpio_clear(gpio->h1r9_mcu_clk_en); gpio_output(gpio->h1r9_mcu_clk_en); -#endif - } + ) } /* diff --git a/firmware/common/tuning.c b/firmware/common/tuning.c index 9c863beb..347be0ae 100644 --- a/firmware/common/tuning.c +++ b/firmware/common/tuning.c @@ -38,12 +38,10 @@ static fp_40_24_t max_bypass_freq = FP_MHZ(2740); void tuning_setup(void) { -#if defined(PRALINE) || defined(UNIVERSAL) - if (detected_platform() == BOARD_ID_PRALINE) { + IF_PRALINE ( min_bypass_freq = FP_MHZ(2320); max_bypass_freq = FP_MHZ(2580); - } -#endif + ) } fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject) @@ -52,12 +50,13 @@ fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject) switch (img_reject) { case RF_PATH_FILTER_LOW_PASS: - if (detected_platform() == BOARD_ID_RAD1O) { + IF_RAD1O ( freq_if = FP_MHZ(2300); - } else { + ) + IF_NOT_RAD1O ( /* IF is graduated from 2650 MHz to 2340 MHz */ freq_if = FP_MHZ(2650) - (freq_rf / 7); - } + ) break; case RF_PATH_FILTER_HIGH_PASS: if (freq_rf < MID1_HP_FREQ) { diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 4952f85d..97a04737 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -347,9 +347,9 @@ void radio_changed(const uint32_t changed) if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { hackrf_ui()->set_frequency(freq / FP_ONE_HZ); } -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) - operacake_set_range(freq / FP_ONE_MHZ); -#endif + IF_EXPANSION_COMPATIBLE ( + operacake_set_range(freq / FP_ONE_MHZ); + ) } if (changed & (1 << RADIO_IMAGE_REJECT)) { if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { @@ -443,20 +443,9 @@ int main(void) } delay_us_at_mhz(10000, 96); pin_setup(); - if (board_id != BOARD_ID_PRALINE) { - enable_1v8_power(); - if (board_id != BOARD_ID_RAD1O) { - /* - * On rad1o, the clock generator power supply comes from the RF supply - * which is enabled later. On H1 and Jawbreaker, the clock generator is - * on the main 3V3 supply. - */ - clock_gen_init(); - } - } else { -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( enable_3v3aux_power(); - #if !defined(DFU_MODE) && !defined(RAM_MODE) +#if !defined(DFU_MODE) && !defined(RAM_MODE) enable_1v2_power(); enable_rf_power(); /* @@ -464,62 +453,60 @@ int main(void) * which is enabled when 1V2FPGA is turned on. */ clock_gen_init(); - #endif #endif - } + ) + IF_NOT_PRALINE ( + enable_1v8_power(); + IF_NOT_RAD1O ( + /* + * On rad1o, the clock generator power supply comes from the RF supply + * which is enabled later. On H1 and Jawbreaker, the clock generator is + * on the main 3V3 supply. + */ + clock_gen_init(); + ) + ) tuning_setup(); - if (board_id == BOARD_ID_HACKRF1_OG || board_id == BOARD_ID_HACKRF1_R9) { -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_HACKRF_ONE ( // Set up mixer before enabling RF power, because its // GPO is used to control the antenna bias tee. mixer_setup(&mixer, RFFC5071_VARIANT); -#endif - } - if (board_id == BOARD_ID_HACKRF1_OG || board_id == BOARD_ID_HACKRF1_R9 || - board_id == BOARD_ID_RAD1O) { -#if defined(RAD1O) || defined(HACKRF_ONE) || defined(UNIVERSAL) + ) + IF_H1_OR_RAD1O ( enable_rf_power(); -#endif - } - if (board_id == BOARD_ID_RAD1O) { + ) + IF_RAD1O ( clock_gen_init(); - } + ) cpu_clock_init(); /* Wake the M0 */ ipc_halt_m0(); ipc_start_m0((uint32_t) &__ram_m0_start__); -#if !defined(PRALINE) || defined(UNIVERSAL) - if (board_id != BOARD_ID_PRALINE) { + IF_NOT_PRALINE ( if (!cpld_jtag_sram_load(&jtag_cpld)) { halt_and_flash(6000000); } - } -#endif -#if defined(PRALINE) || defined(UNIVERSAL) - if (board_id == BOARD_ID_PRALINE) { - #if defined(DFU_MODE) || defined(RAM_MODE) + ) + IF_PRALINE ( +#if defined(DFU_MODE) || defined(RAM_MODE) selftest.fpga_image_load = SKIPPED; selftest.report.pass = false; - #else +#else fpga_image_load(&fpga_loader, 0); - #endif +#endif delay_us_at_mhz(100, 204); fpga_spi_selftest(); fpga_sgpio_selftest(); - } -#endif + ) radio.update_cb = radio_changed; radio_init(&radio); - if (board_id == BOARD_ID_HACKRF1_OG || board_id == BOARD_ID_HACKRF1_R9 || - board_id == BOARD_ID_PRALINE) { -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) + IF_EXPANSION_COMPATIBLE ( portapack_init(); -#endif - } + ) #ifndef DFU_MODE usb_set_descriptor_by_serial_number(); @@ -528,35 +515,22 @@ int main(void) usb_set_configuration_changed_cb(usb_configuration_changed); usb_peripheral_reset(); - switch (detected_platform()) { - case BOARD_ID_HACKRF1_OG: - case BOARD_ID_HACKRF1_R9: -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_HACKRF_ONE ( memcpy(&usb_device, &usb_device_hackrf_one, sizeof(usb_device_hackrf_one)); -#endif - break; - case BOARD_ID_JAWBREAKER: -#if defined(JAWBREAKER) + ) + IF_JAWBREAKER ( memcpy(&usb_device, &usb_device_jawbreaker, sizeof(usb_device_jawbreaker)); -#endif - break; - case BOARD_ID_RAD1O: -#if defined(RAD1O) + ) + IF_RAD1O ( memcpy(&usb_device, &usb_device_rad1o, sizeof(usb_device_rad1o)); -#endif - break; - case BOARD_ID_PRALINE: -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( memcpy(&usb_device, &usb_device_praline, sizeof(usb_device_praline)); -#endif - break; - default: - break; - }; + ) usb_device_init(0, &usb_device); usb_queue_init(&usb_endpoint_control_out_queue); @@ -579,11 +553,9 @@ int main(void) rffc5071_lock_test(&mixer.rffc5071); #endif - if (board_id == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( fpga_if_xcvr_selftest(); -#endif - } + ) if (da7219_detect()) { operacake_skip_i2c_address(DA7219_ADDRESS); @@ -630,11 +602,9 @@ int main(void) sweep_mode(request.seq); break; case TRANSCEIVER_MODE_CPLD_UPDATE: - if (board_id != BOARD_ID_PRALINE) { -#if !defined(PRALINE) || defined(UNIVERSAL) + IF_NOT_PRALINE ( cpld_update(); -#endif - } + ) break; default: break; diff --git a/firmware/hackrf_usb/usb_api_board_info.c b/firmware/hackrf_usb/usb_api_board_info.c index 8e64ad7f..eb91e5c9 100644 --- a/firmware/hackrf_usb/usb_api_board_info.c +++ b/firmware/hackrf_usb/usb_api_board_info.c @@ -131,8 +131,7 @@ usb_request_status_t usb_vendor_request_reset( if (stage == USB_TRANSFER_STAGE_SETUP) { pin_shutdown(); clock_gen_shutdown(); - if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#if defined(HACKRF_ONE) || defined(UNIVERSAL) + IF_H1_R9 ( /* * Set boot pins as inputs so that the bootloader reads them * correctly after the reset. @@ -141,8 +140,7 @@ usb_request_status_t usb_vendor_request_reset( gpio_input(gpio->h1r9_mcu_clk_en); gpio_input(gpio->h1r9_clkout_en); gpio_input(gpio->h1r9_rx); -#endif - } + ) wwdt_reset(100000); diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index b74589ec..bf7e4985 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -142,64 +142,60 @@ usb_request_status_t usb_vendor_request_write_rffc5071( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { -#if defined(RAD1O) - (void) endpoint; - (void) stage; - if (detected_platform() == BOARD_ID_RAD1O) { + IF_RAD1O ( + (void) endpoint; + (void) stage; return USB_REQUEST_STATUS_STALL; - } -#endif + ) - if (stage == USB_TRANSFER_STAGE_SETUP) { -#if !defined(RAD1O) - if (endpoint->setup.index < RFFC5071_NUM_REGS) { - rffc5071_reg_write( - &mixer.rffc5071, - endpoint->setup.index, - endpoint->setup.value); - usb_transfer_schedule_ack(endpoint->in); - return USB_REQUEST_STATUS_OK; + IF_NOT_RAD1O ( + if (stage == USB_TRANSFER_STAGE_SETUP) { + if (endpoint->setup.index < RFFC5071_NUM_REGS) { + rffc5071_reg_write( + &mixer.rffc5071, + endpoint->setup.index, + endpoint->setup.value); + usb_transfer_schedule_ack(endpoint->in); + return USB_REQUEST_STATUS_OK; + } + return USB_REQUEST_STATUS_STALL; } -#endif - return USB_REQUEST_STATUS_STALL; - } else { return USB_REQUEST_STATUS_OK; - } + ) } usb_request_status_t usb_vendor_request_read_rffc5071( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { -#if defined(RAD1O) - (void) endpoint; - (void) stage; - if (detected_platform() == BOARD_ID_RAD1O) { + IF_RAD1O ( + (void) endpoint; + (void) stage; return USB_REQUEST_STATUS_STALL; - } -#endif + ) - if (stage == USB_TRANSFER_STAGE_SETUP) { -#if !defined(RAD1O) + IF_NOT_RAD1O ( uint16_t value; - if (endpoint->setup.index < RFFC5071_NUM_REGS) { - value = rffc5071_reg_read(&mixer.rffc5071, endpoint->setup.index); - endpoint->buffer[0] = value & 0xff; - endpoint->buffer[1] = value >> 8; - usb_transfer_schedule_block( - endpoint->in, - &endpoint->buffer, - 2, - NULL, - NULL); - usb_transfer_schedule_ack(endpoint->out); - return USB_REQUEST_STATUS_OK; + if (stage == USB_TRANSFER_STAGE_SETUP) { + if (endpoint->setup.index < RFFC5071_NUM_REGS) { + value = rffc5071_reg_read( + &mixer.rffc5071, + endpoint->setup.index); + endpoint->buffer[0] = value & 0xff; + endpoint->buffer[1] = value >> 8; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 2, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + return USB_REQUEST_STATUS_OK; + } + return USB_REQUEST_STATUS_STALL; } -#endif - return USB_REQUEST_STATUS_STALL; - } else { return USB_REQUEST_STATUS_OK; - } + ) } usb_request_status_t usb_vendor_request_set_clkout_enable( @@ -286,47 +282,47 @@ usb_request_status_t usb_vendor_request_user_config_set_bias_t_opts( return USB_REQUEST_STATUS_OK; } +#if defined(PRALINE) || defined(UNIVERSAL) usb_request_status_t usb_vendor_request_write_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { - if (detected_platform() != BOARD_ID_PRALINE) { - return USB_REQUEST_STATUS_STALL; - } - - if (stage == USB_TRANSFER_STAGE_SETUP) { -#if defined(PRALINE) || defined(UNIVERSAL) - fpga_reg_write(&fpga, endpoint->setup.index, endpoint->setup.value); -#endif - usb_transfer_schedule_ack(endpoint->in); + IF_PRALINE ( + if (stage == USB_TRANSFER_STAGE_SETUP) { + fpga_reg_write( + &fpga, + endpoint->setup.index, + endpoint->setup.value); + usb_transfer_schedule_ack(endpoint->in); + } return USB_REQUEST_STATUS_OK; - } - return USB_REQUEST_STATUS_OK; + ) + + return USB_REQUEST_STATUS_STALL; } usb_request_status_t usb_vendor_request_read_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { - if (detected_platform() != BOARD_ID_PRALINE) { - return USB_REQUEST_STATUS_STALL; - } + IF_PRALINE ( + if (stage == USB_TRANSFER_STAGE_SETUP) { + const uint8_t value = fpga_reg_read(&fpga, endpoint->setup.index); + endpoint->buffer[0] = value; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 1, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + } + return USB_REQUEST_STATUS_OK; + ) - if (stage == USB_TRANSFER_STAGE_SETUP) { -#if defined(PRALINE) || defined(UNIVERSAL) - const uint8_t value = fpga_reg_read(&fpga, endpoint->setup.index); - endpoint->buffer[0] = value; - usb_transfer_schedule_block( - endpoint->in, - &endpoint->buffer, - 1, - NULL, - NULL); -#endif - usb_transfer_schedule_ack(endpoint->out); - } - return USB_REQUEST_STATUS_OK; + return USB_REQUEST_STATUS_STALL; } +#endif /* * Each register is transferred as a uint8_t register number followed by a diff --git a/firmware/hackrf_usb/usb_api_selftest.c b/firmware/hackrf_usb/usb_api_selftest.c index fa5267b5..e25f387a 100644 --- a/firmware/hackrf_usb/usb_api_selftest.c +++ b/firmware/hackrf_usb/usb_api_selftest.c @@ -80,18 +80,14 @@ static const char* test_result_to_str(test_result_t result) void generate_selftest_report(void) { - board_id_t board_id = detected_platform(); - char* s = &selftest.report.msg[0]; size_t c = sizeof(selftest.report.msg); - if (board_id == BOARD_ID_RAD1O) { -#if defined(RAD1O) + IF_RAD1O ( append(&s, &c, "Mixer: MAX2871, ID: "); append(&s, &c, itoa(selftest.mixer_id, 10)); append(&s, &c, "\n"); -#endif - } else { -#if !defined(RAD1O) + ) + IF_NOT_RAD1O ( append(&s, &c, "Mixer: RFFC5072, ID: "); append(&s, &c, itoa(selftest.mixer_id >> 3, 10)); append(&s, &c, ", Rev: "); @@ -108,8 +104,7 @@ void generate_selftest_report(void) append(&s, &c, " (FAIL)"); } append(&s, &c, "\n"); -#endif - } + ) append(&s, &c, "Clock: Si5351"); append(&s, &c, ", Rev: "); @@ -118,16 +113,19 @@ void generate_selftest_report(void) append(&s, &c, selftest.si5351_readback_ok ? "OK" : "FAIL"); append(&s, &c, "\n"); - if (board_id == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + IF_PRALINE ( append(&s, &c, "Transceiver: MAX2831, RSSI mux test: "); append(&s, &c, selftest.max2831_mux_test_ok ? "PASS" : "FAIL"); append(&s, &c, "\n"); -#endif - } else { -#if !defined(PRALINE) || defined(UNIVERSAL) + ) + IF_NOT_PRALINE ( append(&s, &c, "Transceiver: "); - append(&s, &c, (board_id == BOARD_ID_HACKRF1_R9 ? "MAX2839" : "MAX2837")); + IF_H1_R9 ( + append(&s, &c, "MAX2839"); + ) + IF_NOT_H1_R9 ( + append(&s, &c, "MAX2837"); + ) append(&s, &c, ", readback success: "); append(&s, &c, itoa(selftest.max283x_readback_register_count, 10)); append(&s, &c, "/"); @@ -142,10 +140,8 @@ void generate_selftest_report(void) itoa(selftest.max283x_readback_expected_value, 10)); } append(&s, &c, "\n"); -#endif - } - if (board_id == BOARD_ID_PRALINE) { -#if defined(PRALINE) || defined(UNIVERSAL) + ) + IF_PRALINE ( append(&s, &c, "FPGA configuration: "); append(&s, &c, test_result_to_str(selftest.fpga_image_load)); append(&s, &c, "\n"); @@ -177,8 +173,7 @@ void generate_selftest_report(void) append(&s, &c, itoa(m->avg_mag_sq_q, 10)); append(&s, &c, "\n"); } -#endif - } + ) } usb_request_status_t usb_vendor_request_read_selftest( From 7a6b09962402836745d74e133d46a5d95102a232 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 9 Apr 2026 18:27:46 +0100 Subject: [PATCH 065/173] firmware: Separate compile-time and runtime platform checks. --- firmware/common/cpld_jtag.c | 36 ++- firmware/common/hackrf_core.c | 366 +++++++++++++++-------- firmware/common/hackrf_ui.c | 16 +- firmware/common/max283x.c | 24 +- firmware/common/mixer.c | 74 +++-- firmware/common/operacake_sctimer.c | 12 +- firmware/common/platform_detect.h | 145 +++------ firmware/common/platform_gpio.c | 176 +++++++---- firmware/common/platform_scu.c | 176 +++++++---- firmware/common/radio.c | 60 ++-- firmware/common/rf_path.c | 72 +++-- firmware/common/rffc5071.c | 30 +- firmware/common/sgpio.c | 18 +- firmware/common/si5351c.c | 54 ++-- firmware/common/tuning.c | 18 +- firmware/hackrf_usb/hackrf_usb.c | 98 +++--- firmware/hackrf_usb/usb_api_board_info.c | 6 +- firmware/hackrf_usb/usb_api_register.c | 36 ++- firmware/hackrf_usb/usb_api_selftest.c | 42 ++- 19 files changed, 921 insertions(+), 538 deletions(-) diff --git a/firmware/common/cpld_jtag.c b/firmware/common/cpld_jtag.c index 58359051..b277b462 100644 --- a/firmware/common/cpld_jtag.c +++ b/firmware/common/cpld_jtag.c @@ -42,30 +42,38 @@ void cpld_jtag_take(jtag_t* const jtag) /* Set initial GPIO state to the voltages of the internal or external pull-ups/downs, * to avoid any glitches. */ - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { gpio_set(gpio->gpio_pp_tms); - ) + } +#endif gpio_clear(gpio->gpio_tck); - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio_set(gpio->gpio_tms); gpio_set(gpio->gpio_tdi); - ) + } +#endif - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { /* Do not drive PortaPack-specific TMS pin initially, just to be cautious. */ gpio_input(gpio->gpio_pp_tms); gpio_input(gpio->gpio_pp_tdo); - ) + } +#endif gpio_output(gpio->gpio_tck); - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio_output(gpio->gpio_tms); gpio_output(gpio->gpio_tdi); gpio_input(gpio->gpio_tdo); - ) + } +#endif } void cpld_jtag_release(jtag_t* const jtag) @@ -75,19 +83,23 @@ void cpld_jtag_release(jtag_t* const jtag) /* Make all pins inputs when JTAG interface not active. * Let the pull-ups/downs do the work. */ - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { /* Do not drive PortaPack-specific pins, initially, just to be cautious. */ gpio_input(gpio->gpio_pp_tms); gpio_input(gpio->gpio_pp_tdo); - ) + } +#endif gpio_input(gpio->gpio_tck); - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio_input(gpio->gpio_tms); gpio_input(gpio->gpio_tdi); gpio_input(gpio->gpio_tdo); - ) + } +#endif } #if !defined(PRALINE) || defined(UNIVERSAL) diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 689a2a30..0ac7ae65 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -341,7 +341,8 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) sgpio_cpld_stream_disable(&sgpio_config); } - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { /* Integer mode can be enabled if p1 is even and p2 is zero. */ if (p1 & 0x1 || p2) { si5351c_set_int_mode(&clock_gen, 0, 0); @@ -349,14 +350,17 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) si5351c_set_int_mode(&clock_gen, 0, 1); } - IF_H1_R9 ( + #ifdef IS_H1_R9 + if (IS_H1_R9) { /* * On HackRF One r9 all sample clocks are externally derived * from MS1/CLK1 operating at twice the sample rate. */ si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); - ) - IF_NOT_H1_R9 ( + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { /* * On other platforms the clock generator produces three * different sample clocks, all derived from multisynth 0. @@ -381,9 +385,12 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) 0, 0, 0); //p1 doesn't matter - ) - ) - IF_PRALINE ( + } + #endif + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { /* MS0/CLK0 is the source for the MAX5864 (AFE_CLK). */ si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); @@ -409,7 +416,8 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) /* Reset PLL to synchronize output clock phase. */ si5351c_reset_pll(&clock_gen, SI5351C_PLL_A); - ) + } +#endif if (streaming) { sgpio_cpld_stream_enable(&sgpio_config); @@ -561,7 +569,8 @@ void cpu_clock_init(void) CGU_BASE_SSP1_CLK = CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1); - IF_NOT_RAD1O ( +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { /* Disable unused clocks */ /* Start with PLLs */ CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); @@ -629,7 +638,8 @@ void cpu_clock_init(void) // CCU2_CLK_APB2_USART3_CFG = 0; // CCU2_CLK_APLL_CFG = 0; // CCU2_CLK_SDIO_CFG = 0; - ) + } +#endif } void clock_gen_init(void) @@ -670,7 +680,8 @@ void clock_gen_init(void) * CLK7 -> AUX_CLK2 */ - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { /* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */ si5351c_configure_multisynth( &clock_gen, @@ -679,8 +690,10 @@ void clock_gen_init(void) 0, 1, 0); /* 800/20 = 40MHz */ - ) - IF_NOT_H1_R9 ( + } +#endif +#ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { /* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */ si5351c_configure_multisynth( &clock_gen, @@ -697,7 +710,8 @@ void clock_gen_init(void) 0, 1, 0); /* 800/20 = 40MHz */ - ) + } +#endif /* MS6/CLK6 is unused. */ /* MS7/CLK7 is unused. */ @@ -722,12 +736,14 @@ void clock_gen_shutdown(void) clock_source_t activate_best_clock_source(void) { - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ if (portapack_reference_oscillator && portapack()) { portapack_reference_oscillator(false); } - ) + } +#endif clock_source_t source = CLOCK_SOURCE_HACKRF; @@ -735,7 +751,8 @@ clock_source_t activate_best_clock_source(void) if (si5351c_clkin_signal_valid(&clock_gen)) { source = CLOCK_SOURCE_EXTERNAL; } else { - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ if (portapack_reference_oscillator && portapack()) { portapack_reference_oscillator(true); @@ -746,7 +763,8 @@ clock_source_t activate_best_clock_source(void) portapack_reference_oscillator(false); } } - ) + } +#endif /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ } @@ -798,69 +816,94 @@ void pin_shutdown(void) * * LPC43xx pull-up and pull-down resistors are approximately 53K. */ - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { scu_pinmux(scu->PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); scu_pinmux(scu->PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - ) + } +#endif scu_pinmux(scu->PINMUX_CPLD_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { scu_pinmux(scu->PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); scu_pinmux(scu->PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); scu_pinmux(scu->PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); - ) + } +#endif /* Configure SCU Pin Mux as GPIO */ scu_pinmux(scu->PINMUX_LED1, SCU_GPIO_NOPULL); scu_pinmux(scu->PINMUX_LED2, SCU_GPIO_NOPULL); scu_pinmux(scu->PINMUX_LED3, SCU_GPIO_NOPULL); - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4); - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - ) + } +#endif /* Configure USB indicators */ - IF_JAWBREAKER ( +#ifdef IS_JAWBREAKER + if (IS_JAWBREAKER) { scu_pinmux(scu->PINMUX_USB_LED0, SCU_CONF_FUNCTION3); scu_pinmux(scu->PINMUX_USB_LED1, SCU_CONF_FUNCTION3); - ) + } +#endif - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { disable_1v2_power(); disable_3v3aux_power(); gpio_output(gpio->gpio_1v2_enable); gpio_output(gpio->gpio_3v3aux_enable_n); scu_pinmux(scu->PINMUX_EN1V2, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu_pinmux(scu->PINMUX_EN3V3_AUX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - ) + } +#endif - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { disable_1v8_power(); - IF_H1_R9 ( + #ifdef IS_H1_R9 + if (IS_H1_R9) { gpio_output(gpio->h1r9_1v8_enable); scu_pinmux(scu->H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - ) - IF_NOT_H1_R9 ( + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { gpio_output(gpio->gpio_1v8_enable); scu_pinmux(scu->PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - ) - ) + } + #endif + } +#endif - IF_H1_OR_PRALINE ( +#ifdef IS_H1_OR_PRALINE + if (IS_H1_OR_PRALINE) { /* Safe state: start with VAA turned off: */ disable_rf_power(); /* Configure RF power supply (VAA) switch control signal as output */ - IF_H1_R9 ( + #ifdef IS_H1_R9 + if (IS_H1_R9) { gpio_output(gpio->h1r9_vaa_disable); - ) - IF_NOT_H1_R9 ( + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { gpio_output(gpio->vaa_disable); - ) - ) + } + #endif + } +#endif - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { /* Safe state: start with VAA turned off: */ disable_rf_power(); @@ -873,9 +916,11 @@ void pin_shutdown(void) scu_pinmux(scu->PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); scu_pinmux(scu->PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - ) + } +#endif - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { scu_pinmux(scu->P2_CTRL0, scu->P2_CTRL0_PINCFG); scu_pinmux(scu->P2_CTRL1, scu->P2_CTRL1_PINCFG); scu_pinmux(scu->P1_CTRL0, scu->P1_CTRL0_PINCFG); @@ -910,7 +955,8 @@ void pin_shutdown(void) gpio_clear(gpio->fpga_cfg_creset); gpio_output(gpio->fpga_cfg_creset); gpio_input(gpio->fpga_cfg_cdone); - ) + } +#endif /* enable input on SCL and SDA pins */ SCU_SFSI2C0 = SCU_I2C0_NOMINAL; @@ -927,26 +973,34 @@ void pin_setup(void) led_off(0); led_off(1); led_off(2); - IF_FOUR_LEDS ( +#ifdef IS_FOUR_LEDS + if (IS_FOUR_LEDS) { led_off(3); - ) + } +#endif gpio_output(gpio->led[0]); gpio_output(gpio->led[1]); gpio_output(gpio->led[2]); - IF_FOUR_LEDS ( +#ifdef IS_FOUR_LEDS + if (IS_FOUR_LEDS) { gpio_output(gpio->led[3]); - ) + } +#endif /* Configure drivers and driver pins */ ssp_config_max283x.gpio_select = gpio->max283x_select; - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { ssp_config_max283x.data_bits = SSP_DATA_16BITS; - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { ssp_config_max283x.data_bits = SSP_DATA_9BITS; // send 2 words - ) + } +#endif ssp_config_max5864.gpio_select = gpio->max5864_select; @@ -956,40 +1010,51 @@ void pin_setup(void) sgpio_config.gpio_q_invert = gpio->q_invert; - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { sgpio_config.gpio_trigger_enable = gpio->trigger_enable; - ) + } +#endif - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { ssp_config_ice40_fpga.gpio_select = gpio->fpga_cfg_spi_cs; ice40.gpio_select = gpio->fpga_cfg_spi_cs; ice40.gpio_creset = gpio->fpga_cfg_creset; ice40.gpio_cdone = gpio->fpga_cfg_cdone; - ) + } +#endif jtag_gpio_cpld.gpio_tck = gpio->cpld_tck; - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { jtag_gpio_cpld.gpio_tms = gpio->cpld_tms; jtag_gpio_cpld.gpio_tdi = gpio->cpld_tdi; jtag_gpio_cpld.gpio_tdo = gpio->cpld_tdo; - ) + } +#endif - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { jtag_gpio_cpld.gpio_pp_tms = gpio->cpld_pp_tms; jtag_gpio_cpld.gpio_pp_tdo = gpio->cpld_pp_tdo; - ) + } +#endif ssp1_set_mode_max283x(); mixer_bus_setup(&mixer); - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { sgpio_config.gpio_trigger_enable = gpio->h1r9_trigger_enable; - ) + } +#endif // initialize rf_path struct and assign gpio's - IF_HACKRF_ONE ( +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { rf_path = (rf_path_t){ .switchctrl = 0, .gpio_hp = gpio->hp, @@ -1006,13 +1071,17 @@ void pin_setup(void) .gpio_rx_amp = gpio->rx_amp, .gpio_no_rx_amp_pwr = gpio->no_rx_amp_pwr, }; - IF_H1_R9 ( + #ifdef IS_H1_R9 + if (IS_H1_R9) { rf_path.gpio_rx = gpio->h1r9_rx; rf_path.gpio_h1r9_no_ant_pwr = gpio->h1r9_no_ant_pwr; - ) - ) + } + #endif + } +#endif - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { rf_path = (rf_path_t){ .switchctrl = 0, .gpio_tx_rx_n = gpio->tx_rx_n, @@ -1027,9 +1096,11 @@ void pin_setup(void) .gpio_tx_amp = gpio->tx_amp, .gpio_rx_lna = gpio->rx_lna, }; - ) + } +#endif - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { rf_path = (rf_path_t){ .switchctrl = 0, .gpio_tx_en = gpio->tx_en, @@ -1042,7 +1113,8 @@ void pin_setup(void) (detected_revision() == BOARD_REV_GSG_PRALINE_R1_0)) { rf_path.gpio_mix_en_n = gpio->mix_en_n_r1_0; } - ) + } +#endif rf_path_pin_setup(&rf_path); @@ -1055,55 +1127,75 @@ void pin_setup(void) #if defined(PRALINE) || defined(UNIVERSAL) void enable_1v2_power(void) { - IF_PRALINE ( + #ifdef IS_PRALINE + if (IS_PRALINE) { gpio_set(platform_gpio()->gpio_1v2_enable); - ) + } + #endif } void disable_1v2_power(void) { - IF_PRALINE ( + #ifdef IS_PRALINE + if (IS_PRALINE) { gpio_clear(platform_gpio()->gpio_1v2_enable); - ) + } + #endif } void enable_3v3aux_power(void) { - IF_PRALINE ( + #ifdef IS_PRALINE + if (IS_PRALINE) { gpio_clear(platform_gpio()->gpio_3v3aux_enable_n); - ) + } + #endif } void disable_3v3aux_power(void) { - IF_PRALINE ( + #ifdef IS_PRALINE + if (IS_PRALINE) { gpio_set(platform_gpio()->gpio_3v3aux_enable_n); - ) + } + #endif } #endif void enable_1v8_power(void) { - IF_NOT_PRALINE ( - IF_H1_R9 ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + #ifdef IS_H1_R9 + if (IS_H1_R9) { gpio_set(platform_gpio()->h1r9_1v8_enable); - ) - IF_NOT_H1_R9 ( + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { gpio_set(platform_gpio()->gpio_1v8_enable); - ) - ) + } + #endif + } +#endif } void disable_1v8_power(void) { - IF_NOT_PRALINE ( - IF_H1_R9 ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + #ifdef IS_H1_R9 + if (IS_H1_R9) { gpio_clear(platform_gpio()->h1r9_1v8_enable); - ) - IF_NOT_H1_R9 ( + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { gpio_clear(platform_gpio()->gpio_1v8_enable); - ) - ) + } + #endif + } +#endif } #if defined(HACKRF_ONE) || defined(UNIVERSAL) @@ -1166,48 +1258,68 @@ static inline void disable_rf_power_rad1o(void) void enable_rf_power(void) { - IF_HACKRF_ONE ( +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { enable_rf_power_hackrf_one(); - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { enable_rf_power_praline(); - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { enable_rf_power_rad1o(); - ) + } +#endif } void disable_rf_power(void) { - IF_HACKRF_ONE ( +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { disable_rf_power_hackrf_one(); - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { disable_rf_power_praline(); - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { disable_rf_power_rad1o(); - ) + } +#endif } void led_on(const led_t led) { - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio_clear(platform_gpio()->led[led]); - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio_set(platform_gpio()->led[led]); - ) + } +#endif } void led_off(const led_t led) { - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio_set(platform_gpio()->led[led]); - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio_clear(platform_gpio()->led[led]); - ) + } +#endif } void led_toggle(const led_t led) @@ -1218,28 +1330,38 @@ void led_toggle(const led_t led) void set_leds(const uint8_t state) { int num_leds = 3; - IF_FOUR_LEDS ( +#ifdef IS_FOUR_LEDS + if (IS_FOUR_LEDS) { num_leds = 4; - ) + } +#endif for (int i = 0; i < num_leds; i++) { - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 0); - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 1); - ) + } +#endif } } void trigger_enable(const bool enable) { - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio_write(sgpio_config.gpio_trigger_enable, enable); - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { fpga_set_trigger_enable(&fpga, enable); - ) + } +#endif } void halt_and_flash(const uint32_t duration) diff --git a/firmware/common/hackrf_ui.c b/firmware/common/hackrf_ui.c index 092ec12b..b90ccc14 100644 --- a/firmware/common/hackrf_ui.c +++ b/firmware/common/hackrf_ui.c @@ -23,8 +23,10 @@ #include #include "hackrf_ui.h" -#include "platform_detect.h" #include "transceiver_mode.h" +#if !defined(JAWBREAKER) + #include "platform_detect.h" +#endif #if defined(PRALINE) || defined(HACKRF_ONE) || defined(UNIVERSAL) #include "ui_portapack.h" #endif @@ -83,16 +85,20 @@ const hackrf_ui_t* hackrf_ui(void) { /* Detect on first use. If no UI hardware is detected, use a stub function table. */ if (ui == NULL && ui_enabled) { - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { if (portapack_hackrf_ui_init) { ui = portapack_hackrf_ui_init(); } - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { if (rad1o_ui_setup) { ui = rad1o_ui_setup(); } - ) + } +#endif } if (ui == NULL) { diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index feb7837d..856c5d99 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -73,7 +73,8 @@ void max283x_setup(max283x_driver_t* const drv) const platform_gpio_t* gpio = platform_gpio(); /* MAX283x GPIO PinMux */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { drv->type = MAX2831_VARIANT; max2831.gpio_enable = gpio->max283x_enable; max2831.gpio_rxtx = gpio->max283x_rx_enable; @@ -81,24 +82,31 @@ void max283x_setup(max283x_driver_t* const drv) max2831.gpio_ld = gpio->max2831_ld; memcpy(&drv->drv.max2831, &max2831, sizeof(max2831)); max2831_setup(&drv->drv.max2831); - ) - IF_NOT_PRALINE ( - IF_H1_R9 ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + #ifdef IS_H1_R9 + if (IS_H1_R9) { drv->type = MAX2839_VARIANT; max2839.gpio_enable = gpio->max283x_enable; max2839.gpio_rxtx = gpio->max283x_rx_enable; memcpy(&drv->drv.max2839, &max2839, sizeof(max2839)); max2839_setup(&drv->drv.max2839); - ) - IF_NOT_H1_R9 ( + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { drv->type = MAX2837_VARIANT; max2837.gpio_enable = gpio->max283x_enable; max2837.gpio_rx_enable = gpio->max283x_rx_enable; max2837.gpio_tx_enable = gpio->max283x_tx_enable; memcpy(&drv->drv.max2837, &max2837, sizeof(max2837)); max2837_setup(&drv->drv.max2837); - ) - ) + } + #endif + } +#endif } /* Macros to simplify dispatch of variant-specific operations. */ diff --git a/firmware/common/mixer.c b/firmware/common/mixer.c index 0706170b..fb921350 100644 --- a/firmware/common/mixer.c +++ b/firmware/common/mixer.c @@ -56,7 +56,8 @@ void mixer_bus_setup(mixer_driver_t* const mixer) { (void) mixer; - IF_NOT_RAD1O ( +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { const platform_gpio_t* gpio = platform_gpio(); rffc5071_spi_config = (rffc5071_spi_config_t){ @@ -65,7 +66,8 @@ void mixer_bus_setup(mixer_driver_t* const mixer) .gpio_data = gpio->rffc5072_data, }; spi_bus_start(&spi_bus_rffc5071, &rffc5071_spi_config); - ) + } +#endif } void mixer_setup(mixer_driver_t* const mixer, mixer_variant_t type) @@ -75,16 +77,21 @@ void mixer_setup(mixer_driver_t* const mixer, mixer_variant_t type) const platform_gpio_t* gpio = platform_gpio(); /* Mixer GPIO serial interface PinMux */ - IF_PRALINE ( - mixer->rffc5071.gpio_ld = gpio->rffc5072_ld - ); +#ifdef IS_PRALINE + if (IS_PRALINE) { + mixer->rffc5071.gpio_ld = gpio->rffc5072_ld; + } +#endif - IF_NOT_RAD1O ( +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { mixer->rffc5071.gpio_reset = gpio->rffc5072_reset; rffc5071_setup(&mixer->rffc5071); - ) + } +#endif - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { mixer->max2871.gpio_vco_ce = gpio->vco_ce; mixer->max2871.gpio_vco_sclk = gpio->vco_sclk; mixer->max2871.gpio_vco_sdata = gpio->vco_sdata; @@ -92,50 +99,67 @@ void mixer_setup(mixer_driver_t* const mixer, mixer_variant_t type) mixer->max2871.gpio_synt_rfout_en = gpio->synt_rfout_en; mixer->max2871.gpio_vco_mux = gpio->vco_mux; max2871_setup(&mixer->max2871); - ) + } +#endif } fp_40_24_t mixer_set_frequency(mixer_driver_t* const mixer, fp_40_24_t lo, bool program) { - IF_NOT_RAD1O ( +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { return rffc5071_set_frequency(&mixer->rffc5071, lo, program); - ) + } +#endif - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { return max2871_set_frequency(&mixer->max2871, lo, program); - ) + } +#endif } void mixer_enable(mixer_driver_t* const mixer) { - IF_NOT_RAD1O ( +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { rffc5071_enable(&mixer->rffc5071); - ) + } +#endif - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { max2871_enable(&mixer->max2871); - ) + } +#endif } void mixer_disable(mixer_driver_t* const mixer) { - IF_NOT_RAD1O ( +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { rffc5071_disable(&mixer->rffc5071); - ) + } +#endif - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { max2871_disable(&mixer->max2871); - ) + } +#endif } void mixer_set_gpo(mixer_driver_t* const mixer, uint8_t gpo) { - IF_NOT_RAD1O ( +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { rffc5071_set_gpo(&mixer->rffc5071, gpo); - ) + } +#endif - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { (void) mixer; (void) gpo; - ) + } +#endif } diff --git a/firmware/common/operacake_sctimer.c b/firmware/common/operacake_sctimer.c index 82bb6400..2dbe07d9 100644 --- a/firmware/common/operacake_sctimer.c +++ b/firmware/common/operacake_sctimer.c @@ -98,7 +98,8 @@ void operacake_sctimer_init(void) SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1); uint8_t sct_clock_input; - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { // Configure the SGPIO to output the clock (f=2 * sample clock) on pin 12 SGPIO_OUT_MUX_CFG12 = SGPIO_OUT_MUX_CFG_P_OUT_CFG(0x08) | // clkout output mode @@ -109,8 +110,10 @@ void operacake_sctimer_init(void) GIMA_CTIN_1_IN = 0x2 << 4; // Route SGPIO12 to SCTIN1 sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_1; - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { // Configure pin P6_4 as SCT_IN_6 scu_pinmux(P6_4, SCU_CLK_IN | SCU_CONF_FUNCTION1); @@ -118,7 +121,8 @@ void operacake_sctimer_init(void) GIMA_CTIN_6_IN = 0x0 << 4; sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_6; - ) + } +#endif // We configure this register first, because the user manual says to SCT_CONFIG |= SCT_CONFIG_UNIFY_32_BIT | SCT_CONFIG_CLKMODE_PRESCALED_BUS_CLOCK | diff --git a/firmware/common/platform_detect.h b/firmware/common/platform_detect.h index f8bc53b9..0331fe9c 100644 --- a/firmware/common/platform_detect.h +++ b/firmware/common/platform_detect.h @@ -21,6 +21,7 @@ #pragma once +#include #include #define BOARD_REV_GSG (0x80) @@ -35,107 +36,57 @@ /* Helper macros for platform-specific code. */ #if defined(UNIVERSAL) - #define IF_PRALINE(...) \ - if (detected_platform() == BOARD_ID_PRALINE) { __VA_ARGS__; } - #define IF_NOT_PRALINE(...) \ - if (detected_platform() != BOARD_ID_PRALINE) { __VA_ARGS__; } - #define IF_HACKRF_ONE(...) \ - switch (detected_platform()) { \ - case BOARD_ID_HACKRF1_OG: \ - case BOARD_ID_HACKRF1_R9: \ - { __VA_ARGS__; } \ - break; \ - default: \ - break; \ - } - #define IF_NOT_HACKRF_ONE(...) \ - switch (detected_platform()) { \ - case BOARD_ID_HACKRF1_OG: \ - case BOARD_ID_HACKRF1_R9: \ - break; \ - default: \ - { __VA_ARGS__; } \ - break; \ - } - #define IF_H1_R9(...) \ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { __VA_ARGS__; } - #define IF_NOT_H1_R9(...) \ - if (detected_platform() != BOARD_ID_HACKRF1_R9) { __VA_ARGS__; } - #define IF_RAD1O(...) - #define IF_NOT_RAD1O(...) { __VA_ARGS__; } - #define IF_JAWBREAKER(...) - #define IF_NOT_JAWBREAKER(...) { __VA_ARGS__; } - #define IF_H1_OR_PRALINE(...) { __VA_ARGS__; } - #define IF_H1_OR_RAD1O(...) IF_HACKRF_ONE(__VA_ARGS__) - #define IF_H1_OR_JAWBREAKER(...) IF_HACKRF_ONE(__VA_ARGS__) - #define IF_FOUR_LEDS(...) IF_PRALINE(__VA_ARGS__) - #define IF_EXPANSION_COMPATIBLE(...) { __VA_ARGS__; } + #define IS_PRALINE (detected_platform() == BOARD_ID_PRALINE) + #define IS_NOT_PRALINE (!IS_PRALINE) + #define IS_HACKRF_ONE ( \ + detected_platform() == BOARD_ID_HACKRF1_OG || \ + detected_platform() == BOARD_ID_HACKRF1_R9 \ + ) + #define IS_NOT_HACKRF_ONE (!IS_HACKRF_ONE) + #define IS_H1_R9 (detected_platform() == BOARD_ID_HACKRF1_R9) + #define IS_NOT_H1_R9 (!IS_H1_R9) + #define IS_NOT_RAD1O true + #define IS_NOT_JAWBREAKER true + #define IS_H1_OR_PRALINE true + #define IS_H1_OR_RAD1O IS_HACKRF_ONE + #define IS_H1_OR_JAWBREAKER IS_HACKRF_ONE + #define IS_FOUR_LEDS IS_PRALINE + #define IS_EXPANSION_COMPATIBLE true #elif defined(HACKRF_ONE) - #define IF_PRALINE(...) - #define IF_NOT_PRALINE(...) { __VA_ARGS__; } - #define IF_HACKRF_ONE(...) { __VA_ARGS__; } - #define IF_NOT_HACKRF_ONE(...) - #define IF_H1_R9(...) \ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { __VA_ARGS__; } - #define IF_NOT_H1_R9(...) \ - if (detected_platform() != BOARD_ID_HACKRF1_R9) { __VA_ARGS__; } - #define IF_RAD1O(...) - #define IF_NOT_RAD1O(...) { __VA_ARGS__; } - #define IF_JAWBREAKER(...) - #define IF_NOT_JAWBREAKER(...) { __VA_ARGS__; } - #define IF_H1_OR_PRALINE(...) { __VA_ARGS__; } - #define IF_H1_OR_RAD1O(...) { __VA_ARGS__; } - #define IF_H1_OR_JAWBREAKER(...) { __VA_ARGS__; } - #define IF_FOUR_LEDS(...) - #define IF_EXPANSION_COMPATIBLE(...) { __VA_ARGS__; } + #define IS_NOT_PRALINE true + #define IS_HACKRF_ONE true + #define IS_H1_R9 (detected_platform() == BOARD_ID_HACKRF1_R9) + #define IS_NOT_H1_R9 (!IS_H1_R9) + #define IS_NOT_RAD1O true + #define IS_NOT_JAWBREAKER true + #define IS_H1_OR_PRALINE true + #define IS_H1_OR_RAD1O true + #define IS_H1_OR_JAWBREAKER true + #define IS_EXPANSION_COMPATIBLE true #elif defined(PRALINE) - #define IF_PRALINE(...) { __VA_ARGS__; } - #define IF_NOT_PRALINE(...) - #define IF_HACKRF_ONE(...) - #define IF_NOT_HACKRF_ONE(...) { __VA_ARGS__; } - #define IF_H1_R9(...) - #define IF_NOT_H1_R9(...) { __VA_ARGS__; } - #define IF_RAD1O(...) - #define IF_NOT_RAD1O(...) { __VA_ARGS__; } - #define IF_JAWBREAKER(...) - #define IF_NOT_JAWBREAKER(...) { __VA_ARGS__; } - #define IF_H1_OR_PRALINE(...) { __VA_ARGS__; } - #define IF_H1_OR_RAD1O(...) - #define IF_H1_OR_JAWBREAKER(...) - #define IF_FOUR_LEDS(...) - #define IF_EXPANSION_COMPATIBLE(...) { __VA_ARGS__; } + #define IS_PRALINE true + #define IS_NOT_HACKRF_ONE true + #define IS_NOT_H1_R9 true + #define IS_NOT_RAD1O true + #define IS_NOT_JAWBREAKER true + #define IS_H1_OR_PRALINE true + #define IS_FOUR_LEDS true + #define IS_EXPANSION_COMPATIBLE true #elif defined(RAD1O) - #define IF_PRALINE(...) - #define IF_NOT_PRALINE(...) { __VA_ARGS__; } - #define IF_HACKRF_ONE(...) - #define IF_NOT_HACKRF_ONE(...) { __VA_ARGS__; } - #define IF_H1_R9(...) - #define IF_NOT_H1_R9(...) { __VA_ARGS__; } - #define IF_RAD1O(...) { __VA_ARGS__; } - #define IF_NOT_RAD1O(...) - #define IF_JAWBREAKER(...) - #define IF_NOT_JAWBREAKER(...) { __VA_ARGS__; } - #define IF_H1_OR_PRALINE(...) - #define IF_H1_OR_RAD1O(...) { __VA_ARGS__; } - #define IF_H1_OR_JAWBREAKER(...) - #define IF_FOUR_LEDS(...) { __VA_ARGS__; } - #define IF_EXPANSION_COMPATIBLE(...) + #define IS_NOT_PRALINE true + #define IS_NOT_HACKRF_ONE true + #define IS_NOT_H1_R9 true + #define IS_RAD1O true + #define IS_NOT_JAWBREAKER true + #define IS_H1_OR_RAD1O true + #define IS_FOUR_LEDS true #elif defined(JAWBREAKER) - #define IF_PRALINE(...) - #define IF_NOT_PRALINE(...) { __VA_ARGS__; } - #define IF_HACKRF_ONE(...) - #define IF_NOT_HACKRF_ONE(...) { __VA_ARGS__; } - #define IF_H1_R9(...) - #define IF_NOT_H1_R9(...) { __VA_ARGS__; } - #define IF_RAD1O(...) - #define IF_NOT_RAD1O(...) { __VA_ARGS__; } - #define IF_JAWBREAKER(...) { __VA_ARGS__; } - #define IF_NOT_JAWBREAKER(...) - #define IF_H1_OR_PRALINE(...) - #define IF_H1_OR_RAD1O(...) - #define IF_H1_OR_JAWBREAKER(...) { __VA_ARGS__; } - #define IF_FOUR_LEDS(...) - #define IF_EXPANSION_COMPATIBLE(...) + #define IS_NOT_PRALINE true + #define IS_NOT_HACKRF_ONE true + #define IS_NOT_H1_R9 true + #define IS_NOT_RAD1O true + #define IS_JAWBREAKER true + #define IS_H1_OR_JAWBREAKER true #else #error "No recognised platform defined" #endif diff --git a/firmware/common/platform_gpio.c b/firmware/common/platform_gpio.c index 7f773b20..a5b892e8 100644 --- a/firmware/common/platform_gpio.c +++ b/firmware/common/platform_gpio.c @@ -40,55 +40,77 @@ const platform_gpio_t* platform_gpio(void) gpio.led[0] = &GPIO2_1; gpio.led[1] = &GPIO2_2; gpio.led[2] = &GPIO2_8; - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { gpio.led[3] = &GPIO5_26; - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio.led[3] = &GPIO4_6; - ) + } +#endif /* Power Supply Control */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio.gpio_1v2_enable = &GPIO4_7; gpio.gpio_3v3aux_enable_n = &GPIO5_15; - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio.gpio_1v8_enable = &GPIO3_6; - ) + } +#endif /* MAX283x GPIO (XCVR_CTL / CS_XCVR) PinMux */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio.max283x_select = &GPIO6_28; gpio.max283x_enable = &GPIO7_1; gpio.max283x_rx_enable = &GPIO7_2; gpio.max2831_rxhp = &GPIO6_29; gpio.max2831_ld = &GPIO4_11; - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio.max283x_select = &GPIO0_15; gpio.max283x_enable = &GPIO2_6; gpio.max283x_rx_enable = &GPIO2_5; gpio.max283x_tx_enable = &GPIO2_4; - ) + } +#endif /* MAX5864 SPI chip select (AD_CS / CS_AD) GPIO PinMux */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio.max5864_select = &GPIO6_30; - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio.max5864_select = &GPIO2_7; - ) + } +#endif /* RF supply (VAA) control */ - IF_HACKRF_ONE ( +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { gpio.vaa_disable = &GPIO2_9; - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio.vaa_disable = &GPIO4_1; - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { gpio.vaa_enable = &GPIO2_9; - ) + } +#endif /* W25Q80BV Flash */ gpio.w25q80bv_hold = &GPIO1_14; @@ -96,7 +118,8 @@ const platform_gpio_t* platform_gpio(void) gpio.w25q80bv_select = &GPIO5_11; /* RF switch control */ - IF_HACKRF_ONE ( +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { gpio.hp = &GPIO2_0; gpio.lp = &GPIO2_10; gpio.tx_mix_bp = &GPIO2_11; @@ -110,12 +133,16 @@ const platform_gpio_t* platform_gpio(void) gpio.amp_bypass = &GPIO0_14; gpio.rx_amp = &GPIO1_11; gpio.no_rx_amp_pwr = &GPIO1_12; - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { gpio.h1r9_rx = &GPIO0_7; gpio.h1r9_no_ant_pwr = &GPIO2_4; - ) - ) - IF_RAD1O ( + } +#endif + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { gpio.tx_rx_n = &GPIO1_11; gpio.tx_rx = &GPIO0_14; gpio.by_mix = &GPIO1_12; @@ -127,70 +154,92 @@ const platform_gpio_t* platform_gpio(void) gpio.low_high_filt_n = &GPIO2_12; gpio.tx_amp = &GPIO2_15; gpio.rx_lna = &GPIO5_15; - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio.tx_en = &GPIO3_4; gpio.mix_en_n = &GPIO3_2; gpio.mix_en_n_r1_0 = &GPIO5_6; gpio.lpf_en = &GPIO4_8; gpio.rf_amp_en = &GPIO4_9; gpio.ant_bias_en_n = &GPIO1_12; - ) + } +#endif /* CPLD JTAG interface GPIO pins_FPGA config pins in Praline */ gpio.cpld_tck = &GPIO3_0; - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio.fpga_cfg_creset = &GPIO2_11; gpio.fpga_cfg_cdone = &GPIO5_14; gpio.fpga_cfg_spi_cs = &GPIO2_10; - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio.cpld_tdo = &GPIO5_18; - IF_H1_OR_RAD1O ( - gpio.cpld_tms = &GPIO3_4; - gpio.cpld_tdi = &GPIO3_1; - ) - IF_JAWBREAKER ( - gpio.cpld_tms = &GPIO3_1; - gpio.cpld_tdi = &GPIO3_4; - ) - ) - IF_EXPANSION_COMPATIBLE ( + } +#endif +#ifdef IS_H1_OR_RAD1O + if (IS_H1_OR_RAD1O) { + gpio.cpld_tms = &GPIO3_4; + gpio.cpld_tdi = &GPIO3_1; + } +#endif +#ifdef IS_JAWBREAKER + if (IS_JAWBREAKER) { + gpio.cpld_tms = &GPIO3_1; + gpio.cpld_tdi = &GPIO3_4; + } +#endif +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { gpio.cpld_pp_tms = &GPIO1_1; gpio.cpld_pp_tdo = &GPIO1_8; - ) + } +#endif /* Other CPLD interface GPIO pins */ - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { gpio.trigger_enable = &GPIO5_12; - ) + } +#endif gpio.q_invert = &GPIO0_13; /* RFFC5071 GPIO serial interface PinMux */ - IF_NOT_RAD1O ( +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { gpio.rffc5072_select = &GPIO2_13; gpio.rffc5072_clock = &GPIO5_6; gpio.rffc5072_data = &GPIO3_3; gpio.rffc5072_reset = &GPIO2_14; - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { gpio.vco_ce = &GPIO2_13; gpio.vco_sclk = &GPIO5_6; gpio.vco_sdata = &GPIO3_3; gpio.vco_le = &GPIO2_14; gpio.vco_mux = &GPIO5_25; gpio.synt_rfout_en = &GPIO3_5; - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio.rffc5072_select = &GPIO2_13; gpio.rffc5072_clock = &GPIO5_18; gpio.rffc5072_data = &GPIO4_14; gpio.rffc5072_reset = &GPIO2_14; gpio.rffc5072_ld = &GPIO6_25; - ) + } +#endif /* Praline */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { gpio.p2_ctrl0 = &GPIO7_3; gpio.p2_ctrl1 = &GPIO7_4; gpio.p1_ctrl0 = &GPIO0_14; @@ -201,33 +250,40 @@ const platform_gpio_t* platform_gpio(void) gpio.trigger_in = &GPIO6_26; gpio.trigger_out = &GPIO5_6; gpio.pps_out = &GPIO5_5; - ) + } +#endif /* HackRF One r9 clock control */ - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { gpio.h1r9_clkin_en = &GPIO5_15; gpio.h1r9_clkout_en = &GPIO0_9; gpio.h1r9_mcu_clk_en = &GPIO0_8; gpio.h1r9_1v8_enable = &GPIO2_9; gpio.h1r9_vaa_disable = &GPIO3_6; gpio.h1r9_trigger_enable = &GPIO5_5; - ) + } +#endif /* rad1o LCD */ - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { gpio.lcd_cs = &GPIO4_12; /* P9_0 */ gpio.lcd_bl_en = &GPIO0_8; /* P1_1 */ gpio.lcd_reset = &GPIO5_17; /* P9_4 */ - ) + } +#endif /* Portapack */ - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { gpio.io_stbx = &GPIO5_0; /* P2_0 */ gpio.addr = &GPIO5_1; /* P2_1 */ gpio.lcd_rdx = &GPIO5_4; /* P2_4 */ gpio.lcd_wrx = &GPIO1_10; /* P2_9 */ gpio.dir = &GPIO1_13; /* P2_13 */ - ) + } +#endif _platform_gpio = &gpio; diff --git a/firmware/common/platform_scu.c b/firmware/common/platform_scu.c index 9d3b0c5d..ef894745 100644 --- a/firmware/common/platform_scu.c +++ b/firmware/common/platform_scu.c @@ -42,34 +42,44 @@ const platform_scu_t* platform_scu(void) scu.PINMUX_LED1 = SCU_PINMUX_LED1; /* GPIO2[1] on P4_1 */ scu.PINMUX_LED2 = SCU_PINMUX_LED2; /* GPIO2[2] on P4_2 */ scu.PINMUX_LED3 = SCU_PINMUX_LED3; /* GPIO2[8] on P6_12 */ - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { scu.PINMUX_LED4 = (PB_6); /* GPIO5[26] on PB_6 */ - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { scu.PINMUX_LED4 = (P8_6); /* GPIO4[6] on P8_6 */ - ) + } +#endif /* Power Supply PinMux */ scu.PINMUX_EN1V8 = (P6_10); /* GPIO3[6] on P6_10 */ scu.PINMUX_EN1V2 = (P8_7); /* GPIO4[7] on P8_7 */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { scu.PINMUX_EN3V3_AUX_N = (P6_7); /* GPIO5[15] on P6_7 */ scu.PINMUX_EN3V3_OC_N = (P6_11); /* GPIO3[7] on P6_11 */ - ) + } +#endif /* GPIO Input PinMux */ scu.PINMUX_BOOT0 = (P1_1); /* GPIO0[8] on P1_1 */ scu.PINMUX_BOOT1 = (P1_2); /* GPIO0[9] on P1_2 */ - IF_NOT_HACKRF_ONE ( +#ifdef IS_NOT_HACKRF_ONE + if (IS_NOT_HACKRF_ONE) { scu.PINMUX_BOOT2 = (P2_8); /* GPIO5[7] on P2_8 */ scu.PINMUX_BOOT3 = (P2_9); /* GPIO1[10] on P2_9 */ - ) + } +#endif /* USB peripheral */ - IF_JAWBREAKER ( +#ifdef IS_JAWBREAKER + if (IS_JAWBREAKER) { scu.PINMUX_USB_LED0 = (P6_8); scu.PINMUX_USB_LED1 = (P6_7); - ) + } +#endif /* SSP1 Peripheral PinMux */ scu.SSP1_CIPO = (P1_3); /* P1_3 */ @@ -79,25 +89,34 @@ const platform_scu_t* platform_scu(void) /* CPLD JTAG interface */ scu.PINMUX_CPLD_TCK = (P6_1); /* GPIO3[ 0] */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { scu.PINMUX_FPGA_CRESET = (P5_2); /* GPIO2[11] on P5_2 */ scu.PINMUX_FPGA_CDONE = (P4_10); /* GPIO5[14] */ scu.PINMUX_FPGA_SPI_CS = (P5_1); /* GPIO2[10] */ - ) - IF_NOT_PRALINE ( - scu.PINMUX_CPLD_TDO = (P9_5); /* GPIO5[18] */ - ) - IF_H1_OR_RAD1O ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + scu.PINMUX_CPLD_TDO = (P9_5); /* GPIO5[18] */ + } +#endif +#ifdef IS_H1_OR_RAD1O + if (IS_H1_OR_RAD1O) { scu.PINMUX_CPLD_TMS = (P6_5); /* GPIO3[ 4] */ scu.PINMUX_CPLD_TDI = (P6_2); /* GPIO3[ 1] */ - ) - IF_JAWBREAKER ( + } +#endif +#ifdef IS_JAWBREAKER + if (IS_JAWBREAKER) { scu.PINMUX_CPLD_TMS = (P6_2); /* GPIO3[ 1] */ scu.PINMUX_CPLD_TDI = (P6_5); /* GPIO3[ 4] */ - ) + } +#endif /* CPLD SGPIO interface */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { scu.PINMUX_SGPIO0 = (P0_0); scu.PINMUX_SGPIO1 = (P0_1); scu.PINMUX_SGPIO2 = (P1_15); @@ -129,8 +148,10 @@ const platform_scu_t* platform_scu(void) scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { scu.PINMUX_SGPIO0 = (P0_0); scu.PINMUX_SGPIO1 = (P0_1); scu.PINMUX_SGPIO2 = (P1_15); @@ -162,16 +183,20 @@ const platform_scu_t* platform_scu(void) scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - ) + } +#endif scu.TRIGGER_EN = (P4_8); /* GPIO5[12] on P4_8 */ /* MAX283x GPIO (XCVR_CTL) PinMux */ - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { scu.XCVR_RXHP = P8_1; /* GPIO[] on P8_1 */ scu.XCVR_B6 = P8_2; /* GPIO[] on P8_2 */ scu.XCVR_B7 = P9_3; /* GPIO[] on P9_3 */ - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { scu.XCVR_ENABLE = PE_1; /* GPIO7[1] on PE_1 */ scu.XCVR_RXENABLE = PE_2; /* GPIO7[2] on PE_2 */ scu.XCVR_CS = PD_14; /* GPIO6[28] on PD_14 */ @@ -184,8 +209,10 @@ const platform_scu_t* platform_scu(void) scu.XCVR_RXHP_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.XCVR_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0 | SCU_CONF_EPD_EN_PULLDOWN | SCU_CONF_EPUN_DIS_PULLUP); - ) - IF_H1_OR_JAWBREAKER ( + } +#endif +#ifdef IS_H1_OR_JAWBREAKER + if (IS_H1_OR_JAWBREAKER) { scu.XCVR_ENABLE = P4_6; /* GPIO2[6] on P4_6 */ scu.XCVR_RXENABLE = P4_5; /* GPIO2[5] on P4_5 */ scu.XCVR_TXENABLE = P4_4; /* GPIO2[4] on P4_4 */ @@ -195,20 +222,26 @@ const platform_scu_t* platform_scu(void) scu.XCVR_RXENABLE_PINCFG = (SCU_GPIO_FAST); scu.XCVR_TXENABLE_PINCFG = (SCU_GPIO_FAST); scu.XCVR_CS_PINCFG = (SCU_GPIO_FAST); - ) + } +#endif /* MAX5864 SPI chip select (AD_CS) GPIO PinMux */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { scu.AD_CS = (PD_16); /* GPIO6[30] on PD_16 */ scu.AD_CS_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { scu.AD_CS = (P5_7); /* GPIO2[7] on P5_7 */ scu.AD_CS_PINCFG = (SCU_GPIO_FAST); - ) + } +#endif /* RFFC5071 GPIO serial interface PinMux */ - IF_H1_OR_JAWBREAKER ( +#ifdef IS_H1_OR_JAWBREAKER + if (IS_H1_OR_JAWBREAKER) { scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */ scu.MIXER_SCLK = (P2_6); /* GPIO5[6] on P2_6 */ scu.MIXER_SDATA = (P6_4); /* GPIO3[3] on P6_4 */ @@ -216,8 +249,10 @@ const platform_scu_t* platform_scu(void) scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */ scu.MIXER_SCLK = (P9_5); /* GPIO5[18] on P9_5 */ scu.MIXER_SDATA = (P9_2); /* GPIO4[14] on P9_2 */ @@ -227,8 +262,10 @@ const platform_scu_t* platform_scu(void) scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu.MIXER_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { scu.VCO_CE = (P5_4); /* GPIO2[13] on P5_4 */ scu.VCO_SCLK = (P2_6); /* GPIO5[6] on P2_6 */ scu.VCO_SDATA = (P6_4); /* GPIO3[3] on P6_4 */ @@ -236,23 +273,32 @@ const platform_scu_t* platform_scu(void) scu.VCO_MUX = (PB_5); /* GPIO5[25] on PB_5 */ scu.MIXER_EN = (P6_8); /* GPIO5[16] on P6_8 */ scu.SYNT_RFOUT_EN = (P6_9); /* GPIO3[5] on P6_9 */ - ) + } +#endif /* RF LDO control */ - IF_JAWBREAKER ( +#ifdef IS_JAWBREAKER + if (IS_JAWBREAKER) { scu.RF_LDO_ENABLE = (P5_0); /* GPIO2[9] on P5_0 */ - ) + } +#endif /* RF supply (VAA) control */ - IF_HACKRF_ONE ( +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { scu.NO_VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */ - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { scu.NO_VAA_ENABLE = P8_1; /* GPIO4[1] on P8_1 */ - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { scu.VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */ - ) + } +#endif /* SPI flash */ scu.SSP0_CIPO = (P3_6); @@ -263,7 +309,8 @@ const platform_scu_t* platform_scu(void) scu.FLASH_WP = (P3_5); /* GPIO1[15] on P3_5 */ /* RF switch control */ - IF_HACKRF_ONE ( +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { scu.HP = P4_0; /* GPIO2[0] on P4_0 */ scu.LP = P5_1; /* GPIO2[10] on P5_1 */ scu.TX_MIX_BP = P5_2; /* GPIO2[11] on P5_2 */ @@ -277,8 +324,10 @@ const platform_scu_t* platform_scu(void) scu.AMP_BYPASS = P2_10; /* GPIO0[14] on P2_10 */ scu.RX_AMP = P2_11; /* GPIO1[11] on P2_11 */ scu.NO_RX_AMP_PWR = P2_12; /* GPIO1[12] on P2_12 */ - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { scu.BY_AMP = P1_7; /* GPIO1[0] on P1_7 */ scu.BY_AMP_N = P2_5; /* GPIO5[5] on P2_5 */ scu.TX_RX = P2_10; /* GPIO0[14] on P2_10 */ @@ -289,8 +338,10 @@ const platform_scu_t* platform_scu(void) scu.LOW_HIGH_FILT_N = P5_3; /* GPIO2[12] on P5_3 */ scu.TX_AMP = P5_6; /* GPIO2[15] on P5_6 */ scu.RX_LNA = P6_7; /* GPIO5[15] on P6_7 */ - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { scu.TX_EN = P6_5; /* GPIO3[4] on P6_5 */ scu.MIX_EN_N = P6_3; /* GPIO3[2] on P6_3 */ scu.MIX_EN_N_R1_0 = P2_6; /* GPIO5[6] on P2_6 */ @@ -298,10 +349,12 @@ const platform_scu_t* platform_scu(void) scu.RF_AMP_EN = PA_2; /* GPIO4[9] on PA_2 */ scu.ANT_BIAS_EN_N = P2_12; /* GPIO1[12] on P2_12 */ scu.ANT_BIAS_OC_N = P2_11; /* GPIO1[11] on P2_11 */ - ) + } +#endif /* Praline */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { scu.P2_CTRL0 = (PE_3); /* GPIO7[3] on PE_3 */ scu.P2_CTRL1 = (PE_4); /* GPIO7[4] on PE_4 */ scu.P1_CTRL0 = (P2_10); /* GPIO0[14] on P2_10 */ @@ -323,10 +376,12 @@ const platform_scu_t* platform_scu(void) scu.TRIGGER_IN_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.TRIGGER_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.PPS_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - ) + } +#endif /* HackRF One r9 */ - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { scu.H1R9_CLKIN_EN = P6_7; /* GPIO5[15] on P6_7 */ scu.H1R9_CLKOUT_EN = P1_2; /* GPIO0[9] on P1_2 = has boot pull-down; */ scu.H1R9_MCU_CLK_EN = P1_1; /* GPIO0[8] on P1_1 = has boot pull-up; */ @@ -335,10 +390,12 @@ const platform_scu_t* platform_scu(void) scu.H1R9_EN1V8 = P5_0; /* GPIO2[9] on P5_0 */ scu.H1R9_NO_VAA_EN = P6_10; /* GPIO3[6] on P6_10 */ scu.H1R9_TRIGGER_EN = P2_5; /* GPIO5[5] on P2_5 */ - ) + } +#endif /* Expansion headers */ - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { scu.PINMUX_PP_D0 = (P7_0); /* GPIO3[8] */ scu.PINMUX_PP_D1 = (P7_1); /* GPIO3[9] */ scu.PINMUX_PP_D2 = (P7_2); /* GPIO3[10] */ @@ -378,7 +435,8 @@ const platform_scu_t* platform_scu(void) scu.PINMUX_GPIO3_13 = (P7_5); /* GPIO3[13] */ scu.PINMUX_GPIO3_14 = (P7_6); /* GPIO3[14] */ scu.PINMUX_GPIO3_15 = (P7_7); /* GPIO3[15] */ - ) + } +#endif scu.PINMUX_ISP = (P2_7); /* GPIO0[7] */ diff --git a/firmware/common/radio.c b/firmware/common/radio.c index a4d41c68..a98587f4 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -195,18 +195,22 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) case TRANSCEIVER_MODE_SS: n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) { - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { fpga_set_tx_interpolation_ratio(&fpga, n); - ) + } +#endif radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX] = n; } break; default: n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) { - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { fpga_set_rx_decimation_ratio(&fpga, n); - ) + } +#endif radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX] = n; } } @@ -391,7 +395,8 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) if (requested_lo != RADIO_UNSET) { freq_lo = mixer_set_frequency(&mixer, freq_lo, false); } - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { if (requested_rotation != RADIO_UNSET) { rotation = requested_rotation; } @@ -407,10 +412,13 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) default: rotation = 0; } - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { rotation = 0; - ) + } +#endif /* Handle requested RF (auto-tune). */ if (requested_rf != RADIO_UNSET) { @@ -420,7 +428,8 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) freq_rf = restrict_rf(requested_rf, img_reject); /* Look up settings appropriate for requested RF. */ - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { const tune_config_t* tune_config = select_tune_config(opmode, freq_rf); if (requested_rotation == RADIO_UNSET) { @@ -436,15 +445,18 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) } } high_lo = tune_config->high_lo; - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { /* Use graduated or fixed IF on older platforms. */ if (requested_if == RADIO_UNSET) { freq_if = select_graduated_if(freq_rf, img_reject); } high_lo = (img_reject == RF_PATH_FILTER_LOW_PASS); analog_rf = freq_rf; - ) + } +#endif /* Compute precise LO. This is done first because the mixer LO is set in coarse steps. */ if (requested_lo == RADIO_UNSET) { @@ -500,9 +512,11 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) changed |= (1 << RADIO_IMAGE_REJECT); } if ((rotation != applied_rotation) && (rotation != RADIO_UNSET)) { - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { fpga_set_rx_quarter_shift_mode(&fpga, rotation >> 30); - ) + } +#endif radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = rotation; changed |= (1 << RADIO_ROTATION); } @@ -567,7 +581,8 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank) opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; } - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { /* Praline legacy mode always sets baseband bandwidth automatically. */ (void) bank; uint32_t lpf_bandwidth = auto_bandwidth(radio, opmode); @@ -613,8 +628,10 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank) hpf_bandwidth; changed |= (1 << RADIO_XCVR_RX_HPF); } - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { uint64_t lpf_bandwidth; lpf_bandwidth = bank[RADIO_XCVR_TX_LPF]; if (lpf_bandwidth == RADIO_UNSET) { @@ -653,7 +670,8 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank) (1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF)); } - ) + } +#endif return changed; } @@ -789,7 +807,8 @@ static uint32_t radio_update_trigger(radio_t* const radio, uint64_t* bank) static uint32_t radio_update_dc_block(radio_t* const radio, uint64_t* bank) { - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { const uint64_t requested = bank[RADIO_DC_BLOCK]; bool enable = requested; @@ -801,7 +820,8 @@ static uint32_t radio_update_dc_block(radio_t* const radio, uint64_t* bank) fpga_set_rx_dc_block_enable(&fpga, enable); radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] = enable; return (1 << RADIO_DC_BLOCK); - ) + } +#endif (void) radio; (void) bank; diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index a757eceb..5043bfe8 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -294,19 +294,27 @@ static void switchctrl_set_rad1o(rf_path_t* const rf_path, uint8_t ctrl) static void switchctrl_set(rf_path_t* const rf_path, const uint8_t gpo) { - IF_JAWBREAKER ( +#ifdef IS_JAWBREAKER + if (IS_JAWBREAKER) { (void) rf_path; mixer_set_gpo(&mixer, gpo); - ) - IF_HACKRF_ONE ( + } +#endif +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { switchctrl_set_hackrf_one(rf_path, gpo); - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { switchctrl_set_praline(rf_path, gpo); - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { switchctrl_set_rad1o(rf_path, gpo); - ) + } +#endif } void rf_path_pin_setup(rf_path_t* const rf_path) @@ -319,7 +327,8 @@ void rf_path_pin_setup(rf_path_t* const rf_path) const platform_scu_t* scu = platform_scu(); #endif - IF_HACKRF_ONE ( +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { /* Configure RF switch control signals */ // clang-format off scu_pinmux(scu->HP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); @@ -334,7 +343,8 @@ void rf_path_pin_setup(rf_path_t* const rf_path) scu_pinmux(scu->RX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu_pinmux(scu->NO_RX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); // clang-format on - IF_H1_R9 ( + #ifdef IS_H1_R9 + if (IS_H1_R9) { scu_pinmux(scu->H1R9_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu_pinmux( scu->H1R9_NO_ANT_PWR, @@ -344,13 +354,16 @@ void rf_path_pin_setup(rf_path_t* const rf_path) scu_pinmux( scu->H1R9_NO_VAA_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - ) - IF_NOT_H1_R9 ( + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { scu_pinmux(scu->TX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu_pinmux(scu->RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); gpio_output(rf_path->gpio_tx); scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - ) + } + #endif /* * Safe (initial) switch settings turn off both amplifiers and antenna port @@ -371,8 +384,10 @@ void rf_path_pin_setup(rf_path_t* const rf_path) gpio_output(rf_path->gpio_no_tx_amp_pwr); gpio_output(rf_path->gpio_mix_bypass); gpio_output(rf_path->gpio_rx); - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { /* Configure RF switch control signals */ // clang-format off scu_pinmux(scu->BY_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); @@ -409,9 +424,11 @@ void rf_path_pin_setup(rf_path_t* const rf_path) gpio_output(rf_path->gpio_low_high_filt_n); gpio_output(rf_path->gpio_tx_amp); gpio_output(rf_path->gpio_rx_lna); - ) + } +#endif - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { /* Configure RF switch control signals */ scu_pinmux(scu->TX_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); board_rev_t rev = detected_revision(); @@ -442,7 +459,8 @@ void rf_path_pin_setup(rf_path_t* const rf_path) gpio_output(rf_path->gpio_mix_en_n); gpio_output(rf_path->gpio_lpf_en); gpio_output(rf_path->gpio_rf_amp_en); - ) + } +#endif } void rf_path_init(rf_path_t* const rf_path) @@ -455,14 +473,20 @@ void rf_path_init(rf_path_t* const rf_path) max283x_setup(&max283x); max283x_start(&max283x); - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { mixer_setup(&mixer, MAX2871_VARIANT); - ) - IF_NOT_RAD1O ( - IF_NOT_HACKRF_ONE ( + } +#endif +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { + #ifdef IS_NOT_HACKRF_ONE + if (IS_NOT_HACKRF_ONE) { mixer_setup(&mixer, RFFC5071_VARIANT); - ) - ) + } + #endif + } +#endif rf_path->switchctrl = SWITCHCTRL_SAFE; } diff --git a/firmware/common/rffc5071.c b/firmware/common/rffc5071.c index fbeb2d93..ab0717ea 100644 --- a/firmware/common/rffc5071.c +++ b/firmware/common/rffc5071.c @@ -113,12 +113,14 @@ void rffc5071_setup(rffc5071_driver_t* const drv) gpio_set(drv->gpio_reset); gpio_output(drv->gpio_reset); - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { /* Configure mixer PLL lock detect pin */ const platform_scu_t* scu = platform_scu(); scu_pinmux(scu->MIXER_LD, scu->MIXER_LD_PINCFG); gpio_input(drv->gpio_ld); - ) + } +#endif rffc5071_init(drv); @@ -136,9 +138,11 @@ void rffc5071_setup(rffc5071_driver_t* const drv) set_RFFC5071_FULLD(drv, 0); set_RFFC5071_MODE(drv, 1); - IF_H1_OR_PRALINE ( +#ifdef IS_H1_OR_PRALINE + if (IS_H1_OR_PRALINE) { set_RFFC5071_LOCK(drv, 1); - ) + } +#endif /* Enable reference oscillator standby */ set_RFFC5071_REFST(drv, 1); @@ -181,14 +185,18 @@ void rffc5071_lock_test(rffc5071_driver_t* const drv) bool rffc5071_check_lock(rffc5071_driver_t* const drv) { - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { return gpio_read(drv->gpio_ld); - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { set_RFFC5071_READSEL(drv, 0b0001); rffc5071_regs_commit(drv); return !!(rffc5071_reg_read(drv, RFFC5071_READBACK_REG) & 0x8000); - ) + } +#endif return false; } @@ -369,9 +377,11 @@ bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state) // // For all other boards we'll just return true to avoid a situation where a // a caller is waiting for a lock signal that will never be detected. - IF_NOT_PRALINE ( + #ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { return true; - ) + } + #endif // The RFFC5072 can be configured to output PLL lock status on // GPO4. The lock detect signal is produced by a window detector diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index 86b6c58e..4aa98265 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -57,24 +57,30 @@ void sgpio_configure_pin_functions(sgpio_config_t* const config) scu_pinmux(scu->PINMUX_SGPIO14, scu->PINMUX_SGPIO14_PINCFG); /* GPIO5[13] */ scu_pinmux(scu->PINMUX_SGPIO15, scu->PINMUX_SGPIO15_PINCFG); /* GPIO5[14] */ - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { scu_pinmux( scu->H1R9_TRIGGER_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[5] */ - ) - IF_NOT_H1_R9 ( + } +#endif +#ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { scu_pinmux( scu->TRIGGER_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[12] */ - ) + } +#endif sgpio_cpld_set_mixer_invert(config, 0); gpio_output(config->gpio_q_invert); - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { trigger_enable(false); gpio_output(config->gpio_trigger_enable); - ) + } +#endif } void sgpio_set_slice_mode(sgpio_config_t* const config, const bool multi_slice) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index dbedddf3..a1466393 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -269,7 +269,8 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE /* not connected, but: PLL B int mode */ }; - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { data[1] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA); @@ -280,8 +281,10 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) data[4] = SI5351C_CLK_POWERDOWN; data[5] = SI5351C_CLK_POWERDOWN; data[6] = SI5351C_CLK_POWERDOWN; - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { /* CLK0: AFE_CLK */ data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | @@ -311,7 +314,8 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) /* CLK2: MCU_CLK */ data[3] = SI5351C_CLK_POWERDOWN; } - ) + } +#endif si5351c_write(drv, data, sizeof(data)); } @@ -329,7 +333,8 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) uint8_t clkout = 3; uint8_t value = 0; - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); @@ -339,22 +344,27 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) } else { value |= SI5351C_CLK_DISABLE(2); } - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | SI5351C_CLK_ENABLE(2) | SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); /* HackRF One r9 has only three clock generator outputs. */ - IF_H1_R9 ( + #ifdef IS_H1_R9 + if (IS_H1_R9) { clkout = 2; value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | SI5351C_CLK_DISABLE(3) | SI5351C_CLK_DISABLE(4) | SI5351C_CLK_DISABLE(5) | SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); - ) - ) + } + #endif + } +#endif value |= (clkout_enabled) ? SI5351C_CLK_ENABLE(clkout) : SI5351C_CLK_DISABLE(clkout); @@ -362,14 +372,16 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) si5351c_write(drv, data, sizeof(data)); outputs_disabled = value; - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { const platform_gpio_t* gpio = platform_gpio(); if (clkout_enabled) { gpio_set(gpio->h1r9_clkout_en); } else { gpio_clear(gpio->h1r9_clkout_en); } - ) + } +#endif } void si5351c_set_int_mode( @@ -399,7 +411,8 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source return; } si5351c_disable_all_outputs(drv); - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { /* * HackRF One r9 always uses PLL A on the XTAL input * but externally switches that input to CLKIN. @@ -410,10 +423,13 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source } else { gpio_clear(platform_gpio()->h1r9_clkin_en); } - ) - IF_NOT_H1_R9 ( + } +#endif +#ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { si5351c_configure_pll_sources(drv, source); - ) + } +#endif si5351c_configure_pll_multisynth(drv, source); active_clock_source = source; si5351c_reset_pll(drv, SI5351C_PLL_BOTH); @@ -468,7 +484,8 @@ void si5351c_init(si5351c_driver_t* const drv) selftest.report.pass = false; } - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { const platform_gpio_t* gpio = platform_gpio(); const platform_scu_t* scu = platform_scu(); @@ -486,7 +503,8 @@ void si5351c_init(si5351c_driver_t* const drv) scu_pinmux(scu->H1R9_MCU_CLK_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); gpio_clear(gpio->h1r9_mcu_clk_en); gpio_output(gpio->h1r9_mcu_clk_en); - ) + } +#endif } /* diff --git a/firmware/common/tuning.c b/firmware/common/tuning.c index 347be0ae..9e3c41ba 100644 --- a/firmware/common/tuning.c +++ b/firmware/common/tuning.c @@ -38,10 +38,12 @@ static fp_40_24_t max_bypass_freq = FP_MHZ(2740); void tuning_setup(void) { - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { min_bypass_freq = FP_MHZ(2320); max_bypass_freq = FP_MHZ(2580); - ) + } +#endif } fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject) @@ -50,13 +52,17 @@ fp_40_24_t select_graduated_if(fp_40_24_t freq_rf, rf_path_filter_t img_reject) switch (img_reject) { case RF_PATH_FILTER_LOW_PASS: - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { freq_if = FP_MHZ(2300); - ) - IF_NOT_RAD1O ( + } +#endif +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { /* IF is graduated from 2650 MHz to 2340 MHz */ freq_if = FP_MHZ(2650) - (freq_rf / 7); - ) + } +#endif break; case RF_PATH_FILTER_HIGH_PASS: if (freq_rf < MID1_HP_FREQ) { diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 97a04737..653a477a 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -443,9 +443,10 @@ int main(void) } delay_us_at_mhz(10000, 96); pin_setup(); - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { enable_3v3aux_power(); -#if !defined(DFU_MODE) && !defined(RAM_MODE) + #if !defined(DFU_MODE) && !defined(RAM_MODE) enable_1v2_power(); enable_rf_power(); /* @@ -453,60 +454,77 @@ int main(void) * which is enabled when 1V2FPGA is turned on. */ clock_gen_init(); + #endif + } #endif - ) - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { enable_1v8_power(); - IF_NOT_RAD1O ( + #ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { /* * On rad1o, the clock generator power supply comes from the RF supply * which is enabled later. On H1 and Jawbreaker, the clock generator is * on the main 3V3 supply. */ clock_gen_init(); - ) - ) + } + #endif + } +#endif tuning_setup(); - IF_HACKRF_ONE ( +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { // Set up mixer before enabling RF power, because its // GPO is used to control the antenna bias tee. mixer_setup(&mixer, RFFC5071_VARIANT); - ) - IF_H1_OR_RAD1O ( + } +#endif +#ifdef IS_H1_OR_RAD1O + if (IS_H1_OR_RAD1O) { enable_rf_power(); - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { clock_gen_init(); - ) + } +#endif cpu_clock_init(); /* Wake the M0 */ ipc_halt_m0(); ipc_start_m0((uint32_t) &__ram_m0_start__); - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { if (!cpld_jtag_sram_load(&jtag_cpld)) { halt_and_flash(6000000); } - ) - IF_PRALINE ( -#if defined(DFU_MODE) || defined(RAM_MODE) + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { + #if defined(DFU_MODE) || defined(RAM_MODE) selftest.fpga_image_load = SKIPPED; selftest.report.pass = false; -#else + #else fpga_image_load(&fpga_loader, 0); -#endif + #endif delay_us_at_mhz(100, 204); fpga_spi_selftest(); fpga_sgpio_selftest(); - ) + } +#endif radio.update_cb = radio_changed; radio_init(&radio); - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { portapack_init(); - ) + } +#endif #ifndef DFU_MODE usb_set_descriptor_by_serial_number(); @@ -515,22 +533,30 @@ int main(void) usb_set_configuration_changed_cb(usb_configuration_changed); usb_peripheral_reset(); - IF_HACKRF_ONE ( +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { memcpy(&usb_device, &usb_device_hackrf_one, sizeof(usb_device_hackrf_one)); - ) - IF_JAWBREAKER ( + } +#endif +#ifdef IS_JAWBREAKER + if (IS_JAWBREAKER) { memcpy(&usb_device, &usb_device_jawbreaker, sizeof(usb_device_jawbreaker)); - ) - IF_RAD1O ( + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { memcpy(&usb_device, &usb_device_rad1o, sizeof(usb_device_rad1o)); - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { memcpy(&usb_device, &usb_device_praline, sizeof(usb_device_praline)); - ) + } +#endif usb_device_init(0, &usb_device); usb_queue_init(&usb_endpoint_control_out_queue); @@ -553,9 +579,11 @@ int main(void) rffc5071_lock_test(&mixer.rffc5071); #endif - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { fpga_if_xcvr_selftest(); - ) + } +#endif if (da7219_detect()) { operacake_skip_i2c_address(DA7219_ADDRESS); @@ -602,9 +630,11 @@ int main(void) sweep_mode(request.seq); break; case TRANSCEIVER_MODE_CPLD_UPDATE: - IF_NOT_PRALINE ( +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { cpld_update(); - ) + } +#endif break; default: break; diff --git a/firmware/hackrf_usb/usb_api_board_info.c b/firmware/hackrf_usb/usb_api_board_info.c index eb91e5c9..393b102d 100644 --- a/firmware/hackrf_usb/usb_api_board_info.c +++ b/firmware/hackrf_usb/usb_api_board_info.c @@ -131,7 +131,8 @@ usb_request_status_t usb_vendor_request_reset( if (stage == USB_TRANSFER_STAGE_SETUP) { pin_shutdown(); clock_gen_shutdown(); - IF_H1_R9 ( +#ifdef IS_H1_R9 + if (IS_H1_R9) { /* * Set boot pins as inputs so that the bootloader reads them * correctly after the reset. @@ -140,7 +141,8 @@ usb_request_status_t usb_vendor_request_reset( gpio_input(gpio->h1r9_mcu_clk_en); gpio_input(gpio->h1r9_clkout_en); gpio_input(gpio->h1r9_rx); - ) + } +#endif wwdt_reset(100000); diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index bf7e4985..d17a87d8 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -142,13 +142,16 @@ usb_request_status_t usb_vendor_request_write_rffc5071( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { (void) endpoint; (void) stage; return USB_REQUEST_STATUS_STALL; - ) + } +#endif - IF_NOT_RAD1O ( +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { if (stage == USB_TRANSFER_STAGE_SETUP) { if (endpoint->setup.index < RFFC5071_NUM_REGS) { rffc5071_reg_write( @@ -161,20 +164,24 @@ usb_request_status_t usb_vendor_request_write_rffc5071( return USB_REQUEST_STATUS_STALL; } return USB_REQUEST_STATUS_OK; - ) + } +#endif } usb_request_status_t usb_vendor_request_read_rffc5071( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { (void) endpoint; (void) stage; return USB_REQUEST_STATUS_STALL; - ) + } +#endif - IF_NOT_RAD1O ( +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { uint16_t value; if (stage == USB_TRANSFER_STAGE_SETUP) { if (endpoint->setup.index < RFFC5071_NUM_REGS) { @@ -195,7 +202,8 @@ usb_request_status_t usb_vendor_request_read_rffc5071( return USB_REQUEST_STATUS_STALL; } return USB_REQUEST_STATUS_OK; - ) + } +#endif } usb_request_status_t usb_vendor_request_set_clkout_enable( @@ -287,7 +295,8 @@ usb_request_status_t usb_vendor_request_write_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { - IF_PRALINE ( + #ifdef IS_PRALINE + if (IS_PRALINE) { if (stage == USB_TRANSFER_STAGE_SETUP) { fpga_reg_write( &fpga, @@ -296,7 +305,8 @@ usb_request_status_t usb_vendor_request_write_fpga_reg( usb_transfer_schedule_ack(endpoint->in); } return USB_REQUEST_STATUS_OK; - ) + } + #endif return USB_REQUEST_STATUS_STALL; } @@ -305,7 +315,8 @@ usb_request_status_t usb_vendor_request_read_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { - IF_PRALINE ( + #ifdef IS_PRALINE + if (IS_PRALINE) { if (stage == USB_TRANSFER_STAGE_SETUP) { const uint8_t value = fpga_reg_read(&fpga, endpoint->setup.index); endpoint->buffer[0] = value; @@ -318,7 +329,8 @@ usb_request_status_t usb_vendor_request_read_fpga_reg( usb_transfer_schedule_ack(endpoint->out); } return USB_REQUEST_STATUS_OK; - ) + } + #endif return USB_REQUEST_STATUS_STALL; } diff --git a/firmware/hackrf_usb/usb_api_selftest.c b/firmware/hackrf_usb/usb_api_selftest.c index e25f387a..403f3753 100644 --- a/firmware/hackrf_usb/usb_api_selftest.c +++ b/firmware/hackrf_usb/usb_api_selftest.c @@ -82,12 +82,15 @@ void generate_selftest_report(void) { char* s = &selftest.report.msg[0]; size_t c = sizeof(selftest.report.msg); - IF_RAD1O ( +#ifdef IS_RAD1O + if (IS_RAD1O) { append(&s, &c, "Mixer: MAX2871, ID: "); append(&s, &c, itoa(selftest.mixer_id, 10)); append(&s, &c, "\n"); - ) - IF_NOT_RAD1O ( + } +#endif +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { append(&s, &c, "Mixer: RFFC5072, ID: "); append(&s, &c, itoa(selftest.mixer_id >> 3, 10)); append(&s, &c, ", Rev: "); @@ -104,7 +107,8 @@ void generate_selftest_report(void) append(&s, &c, " (FAIL)"); } append(&s, &c, "\n"); - ) + } +#endif append(&s, &c, "Clock: Si5351"); append(&s, &c, ", Rev: "); @@ -113,19 +117,26 @@ void generate_selftest_report(void) append(&s, &c, selftest.si5351_readback_ok ? "OK" : "FAIL"); append(&s, &c, "\n"); - IF_PRALINE ( +#ifdef IS_PRALINE + if (IS_PRALINE) { append(&s, &c, "Transceiver: MAX2831, RSSI mux test: "); append(&s, &c, selftest.max2831_mux_test_ok ? "PASS" : "FAIL"); append(&s, &c, "\n"); - ) - IF_NOT_PRALINE ( + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { append(&s, &c, "Transceiver: "); - IF_H1_R9 ( + #ifdef IS_H1_R9 + if (IS_H1_R9) { append(&s, &c, "MAX2839"); - ) - IF_NOT_H1_R9 ( + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { append(&s, &c, "MAX2837"); - ) + } + #endif append(&s, &c, ", readback success: "); append(&s, &c, itoa(selftest.max283x_readback_register_count, 10)); append(&s, &c, "/"); @@ -140,8 +151,10 @@ void generate_selftest_report(void) itoa(selftest.max283x_readback_expected_value, 10)); } append(&s, &c, "\n"); - ) - IF_PRALINE ( + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { append(&s, &c, "FPGA configuration: "); append(&s, &c, test_result_to_str(selftest.fpga_image_load)); append(&s, &c, "\n"); @@ -173,7 +186,8 @@ void generate_selftest_report(void) append(&s, &c, itoa(m->avg_mag_sq_q, 10)); append(&s, &c, "\n"); } - ) + } +#endif } usb_request_status_t usb_vendor_request_read_selftest( From f7a3d434075d0fa5cdedcc546e2a426c3a628f30 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 14 Apr 2026 11:52:21 +0100 Subject: [PATCH 066/173] Use IS macros for all conditional compilation. --- firmware/common/cpld_jtag.c | 13 ++++---- firmware/common/cpld_jtag.h | 5 +-- firmware/common/hackrf_core.c | 32 ++++++++----------- firmware/common/hackrf_core.h | 13 ++++---- firmware/common/hackrf_ui.c | 8 ++--- firmware/common/max283x.c | 22 ++++++------- firmware/common/max283x.h | 20 ++++++------ firmware/common/max2871.c | 5 +-- firmware/common/mixer.c | 9 +++--- firmware/common/mixer.h | 13 +++++--- firmware/common/operacake_sctimer.c | 12 +++---- firmware/common/platform_gpio.h | 33 +++++++++---------- firmware/common/platform_scu.h | 40 +++++++++++++----------- firmware/common/radio.c | 6 ++-- firmware/common/rf_path.c | 21 +++++-------- firmware/common/rf_path.h | 12 ++++--- firmware/common/rffc5071.c | 9 ++---- firmware/common/rffc5071.h | 5 +-- firmware/common/selftest.h | 8 +++-- firmware/common/sgpio.c | 2 +- firmware/common/sgpio.h | 3 +- firmware/common/si5351c.c | 7 ++--- firmware/common/tune_config.h | 4 ++- firmware/hackrf_usb/hackrf_usb.c | 32 ++++++++++--------- firmware/hackrf_usb/usb_api_board_info.c | 3 +- firmware/hackrf_usb/usb_api_register.c | 7 ++--- firmware/hackrf_usb/usb_api_register.h | 4 ++- firmware/hackrf_usb/usb_api_selftest.c | 11 ++++--- firmware/hackrf_usb/usb_descriptor.c | 34 ++++++++------------ firmware/hackrf_usb/usb_descriptor.h | 16 +++++----- firmware/hackrf_usb/usb_device.c | 9 +++--- firmware/hackrf_usb/usb_device.h | 10 +++--- 32 files changed, 212 insertions(+), 216 deletions(-) diff --git a/firmware/common/cpld_jtag.c b/firmware/common/cpld_jtag.c index b277b462..034ba196 100644 --- a/firmware/common/cpld_jtag.c +++ b/firmware/common/cpld_jtag.c @@ -19,17 +19,16 @@ * Boston, MA 02110-1301, USA. */ -#if !defined(PRALINE) || defined(UNIVERSAL) - #include -#endif - #include "cpld_jtag.h" + #include "platform_detect.h" -#if !defined(PRALINE) || defined(UNIVERSAL) + +#ifdef IS_NOT_PRALINE + #include #include "xapp058/micro.h" #endif -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE static refill_buffer_cb refill_buffer; static uint32_t xsvf_buffer_len, xsvf_pos; static unsigned char* xsvf_buffer; @@ -102,7 +101,7 @@ void cpld_jtag_release(jtag_t* const jtag) #endif } -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE /* return 0 if success else return error code see xsvfExecute() */ int cpld_jtag_program( jtag_t* const jtag, diff --git a/firmware/common/cpld_jtag.h b/firmware/common/cpld_jtag.h index 939e5d94..0994c858 100644 --- a/firmware/common/cpld_jtag.h +++ b/firmware/common/cpld_jtag.h @@ -24,15 +24,16 @@ #include #include "gpio.h" +#include "platform_detect.h" typedef struct { gpio_t gpio_tck; -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE gpio_t gpio_tms; gpio_t gpio_tdi; gpio_t gpio_tdo; #endif -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_EXPANSION_COMPATIBLE gpio_t gpio_pp_tms; gpio_t gpio_pp_tdo; #endif diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 0ac7ae65..d5679942 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -25,7 +25,10 @@ #include #include #include -#if !defined(RAD1O) + +#include "platform_detect.h" + +#ifdef IS_NOT_RAD1O #include #endif @@ -36,15 +39,14 @@ #include "i2c_lpc.h" #include "max283x.h" #include "max5864_target.h" -#include "platform_detect.h" #include "platform_gpio.h" #include "platform_scu.h" #include "spi_bus.h" #include "w25q80bv_target.h" -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_EXPANSION_COMPATIBLE #include "portapack.h" #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE #include "ice40_spi.h" #endif @@ -142,7 +144,7 @@ sgpio_config_t sgpio_config = { .slice_mode_multislice = true, }; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE static ssp_config_t ssp_config_ice40_fpga = { .data_bits = SSP_DATA_8BITS, .spi_mode = SSP_CPOL_1_CPHA_1, @@ -786,7 +788,7 @@ void ssp1_set_mode_max5864(void) spi_bus_start(max5864.bus, &ssp_config_max5864); } -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE void ssp1_set_mode_ice40(void) { spi_bus_start(&spi_bus_ssp1, &ssp_config_ice40_fpga); @@ -1124,41 +1126,33 @@ void pin_setup(void) sgpio_configure_pin_functions(&sgpio_config); } -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE void enable_1v2_power(void) { - #ifdef IS_PRALINE if (IS_PRALINE) { gpio_set(platform_gpio()->gpio_1v2_enable); } - #endif } void disable_1v2_power(void) { - #ifdef IS_PRALINE if (IS_PRALINE) { gpio_clear(platform_gpio()->gpio_1v2_enable); } - #endif } void enable_3v3aux_power(void) { - #ifdef IS_PRALINE if (IS_PRALINE) { gpio_clear(platform_gpio()->gpio_3v3aux_enable_n); } - #endif } void disable_3v3aux_power(void) { - #ifdef IS_PRALINE if (IS_PRALINE) { gpio_set(platform_gpio()->gpio_3v3aux_enable_n); } - #endif } #endif @@ -1198,7 +1192,7 @@ void disable_1v8_power(void) #endif } -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE static inline void enable_rf_power_hackrf_one(void) { const platform_gpio_t* gpio = platform_gpio(); @@ -1226,7 +1220,7 @@ static inline void disable_rf_power_hackrf_one(void) } #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE static inline void enable_rf_power_praline(void) { gpio_clear(platform_gpio()->vaa_disable); @@ -1241,7 +1235,7 @@ static inline void disable_rf_power_praline(void) } #endif -#if defined(RAD1O) +#ifdef IS_RAD1O static inline void enable_rf_power_rad1o(void) { gpio_set(platform_gpio()->vaa_enable); @@ -1379,7 +1373,7 @@ void halt_and_flash(const uint32_t duration) } } -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE void p1_ctrl_set(const p1_ctrl_signal_t signal) { const platform_gpio_t* gpio = platform_gpio(); diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 93bc44c9..56221f6b 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -36,13 +36,14 @@ extern "C" { #include "max283x.h" #include "max5864.h" #include "mixer.h" +#include "platform_detect.h" // IWYU pragma: keep #include "radio.h" #include "rf_path.h" #include "sgpio.h" #include "si5351c.h" #include "spi_ssp.h" #include "w25q80bv.h" -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE #include "fpga.h" #include "ice40_spi.h" #endif @@ -52,7 +53,7 @@ extern si5351c_driver_t clock_gen; extern ssp_config_t ssp_config_w25q80bv; extern max283x_driver_t max283x; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE extern ice40_spi_driver_t ice40; extern fpga_driver_t fpga; #endif @@ -71,14 +72,14 @@ void clock_gen_init(void); void clock_gen_shutdown(void); void ssp1_set_mode_max283x(void); void ssp1_set_mode_max5864(void); -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE void ssp1_set_mode_ice40(void); #endif void pin_shutdown(void); void pin_setup(void); -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE void enable_1v2_power(void); void disable_1v2_power(void); void enable_3v3aux_power(void); @@ -91,7 +92,7 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program); clock_source_t activate_best_clock_source(void); -#if defined(RAD1O) || defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) +#if defined(IS_RAD1O) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE) void enable_rf_power(void); void disable_rf_power(void); #endif @@ -112,7 +113,7 @@ void trigger_enable(const bool enable); void halt_and_flash(const uint32_t duration); -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE typedef enum { P1_SIGNAL_TRIGGER_IN = 0, P1_SIGNAL_AUX_CLK1 = 1, diff --git a/firmware/common/hackrf_ui.c b/firmware/common/hackrf_ui.c index b90ccc14..4224d40f 100644 --- a/firmware/common/hackrf_ui.c +++ b/firmware/common/hackrf_ui.c @@ -23,14 +23,12 @@ #include #include "hackrf_ui.h" +#include "platform_detect.h" // IWYU pragma: keep #include "transceiver_mode.h" -#if !defined(JAWBREAKER) - #include "platform_detect.h" -#endif -#if defined(PRALINE) || defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_EXPANSION_COMPATIBLE #include "ui_portapack.h" #endif -#if defined(RAD1O) +#ifdef IS_RAD1O #include "ui_rad1o.h" #endif diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index 856c5d99..3b49ab6e 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -31,19 +31,19 @@ #include "platform_gpio.h" #include "spi_bus.h" -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE #include "max2831_target.h" #endif -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE #include "max2837_target.h" #endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_H1_R9 #include "max2839_target.h" #endif extern spi_bus_t spi_bus_ssp1; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE max2831_driver_t max2831 = { .bus = &spi_bus_ssp1, .target_init = max2831_target_init, @@ -51,7 +51,7 @@ max2831_driver_t max2831 = { }; #endif -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE max2837_driver_t max2837 = { .bus = &spi_bus_ssp1, .target_init = max2837_target_init, @@ -59,7 +59,7 @@ max2837_driver_t max2837 = { }; #endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE max2839_driver_t max2839 = { .bus = &spi_bus_ssp1, .target_init = max2839_target_init, @@ -113,7 +113,7 @@ void max283x_setup(max283x_driver_t* const drv) /* clang-format off */ -#if defined(UNIVERSAL) +#if UNIVERSAL // UNIVERSAL: all variants #define DISPATCH(_drv, _max2831, _max2837, _max2839) \ if (_drv->type == MAX2831_VARIANT) { \ @@ -123,7 +123,7 @@ void max283x_setup(max283x_driver_t* const drv) } else { \ _max2839; \ } -#elif defined(HACKRF_ONE) +#elif HACKRF_ONE // HACKRF_ONE: MAX2837 and MAX2839 only #define DISPATCH(_drv, _max2831, _max2837, _max2839) \ if (_drv->type == MAX2837_VARIANT) { \ @@ -131,7 +131,7 @@ void max283x_setup(max283x_driver_t* const drv) } else { \ _max2839; \ } -#elif defined(PRALINE) +#elif PRALINE // PRALINE: MAX2831 only #define DISPATCH(_drv, _max2831, _max2837, _max2839) \ (void) drv; \ @@ -251,9 +251,7 @@ uint32_t max283x_set_lpf_bandwidth( const max283x_mode_t mode, const uint32_t bandwidth_hz) { -#if !defined(PRALINE) (void) mode; -#endif DISPATCH( drv, return max2831_set_lpf_bandwidth( @@ -294,9 +292,7 @@ void max283x_set_rx_hpf_frequency( max283x_driver_t* const drv, const max283x_rx_hpf_freq_t freq) { -#if !defined(PRALINE) && !defined(UNIVERSAL) (void) freq; -#endif PRALINE_ONLY( drv, max2831_set_rx_hpf_frequency( diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h index bcafff06..01bb40b0 100644 --- a/firmware/common/max283x.h +++ b/firmware/common/max283x.h @@ -27,13 +27,15 @@ #include #include "fixed_point.h" -#if defined(PRALINE) || defined(UNIVERSAL) +#include "platform_detect.h" + +#ifdef IS_PRALINE #include "max2831.h" #endif -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE #include "max2837.h" #endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_H1_R9 #include "max2839.h" #endif @@ -55,13 +57,13 @@ typedef enum { } max283x_rx_hpf_freq_t; typedef enum { -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE MAX2831_VARIANT, #endif -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE MAX2837_VARIANT, #endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_H1_R9 MAX2839_VARIANT, #endif } max283x_variant_t; @@ -70,13 +72,13 @@ typedef struct { max283x_variant_t type; union { -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE max2831_driver_t max2831; #endif -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE max2837_driver_t max2837; #endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_H1_R9 max2839_driver_t max2839; #endif } drv; diff --git a/firmware/common/max2871.c b/firmware/common/max2871.c index 4c5a94b2..05840678 100644 --- a/firmware/common/max2871.c +++ b/firmware/common/max2871.c @@ -24,17 +24,18 @@ #include #include +#include + #include "fixed_point.h" #include "max2871_regs.h" -#include "selftest.h" #include "platform_scu.h" +#include "selftest.h" #if (defined DEBUG) #include #define LOG printf #else #define LOG(x, ...) - #include #endif #define MIN(x, y) ((x) < (y) ? (x) : (y)) diff --git a/firmware/common/mixer.c b/firmware/common/mixer.c index fb921350..e38c28bf 100644 --- a/firmware/common/mixer.c +++ b/firmware/common/mixer.c @@ -26,15 +26,16 @@ #include "fixed_point.h" #include "platform_detect.h" #include "platform_gpio.h" -#if defined(RAD1O) +#ifdef IS_RAD1O #include "max2871.h" -#else +#endif +#ifdef IS_NOT_RAD1O #include "rffc5071.h" #include "rffc5071_spi.h" #include "spi_bus.h" #endif -#if !defined(RAD1O) +#ifdef IS_NOT_RAD1O static rffc5071_spi_config_t rffc5071_spi_config; static spi_bus_t spi_bus_rffc5071 = { @@ -47,7 +48,7 @@ static spi_bus_t spi_bus_rffc5071 = { #endif mixer_driver_t mixer = { -#if !defined(RAD1O) +#ifdef IS_NOT_RAD1O .rffc5071.bus = &spi_bus_rffc5071, #endif }; diff --git a/firmware/common/mixer.h b/firmware/common/mixer.h index 0afacb6b..f7861725 100644 --- a/firmware/common/mixer.h +++ b/firmware/common/mixer.h @@ -23,12 +23,15 @@ #pragma once #include +#include #include "fixed_point.h" +#include "platform_detect.h" -#if defined(RAD1O) +#ifdef IS_RAD1O #include "max2871.h" -#else +#endif +#ifdef IS_NOT_RAD1O #include "rffc5071.h" #endif @@ -41,15 +44,15 @@ typedef struct { mixer_variant_t type; union { -#if defined(RAD1O) +#ifdef IS_RAD1O max2871_driver_t max2871; -#else +#endif +#ifdef IS_NOT_RAD1O rffc5071_driver_t rffc5071; #endif }; } mixer_driver_t; -#include extern void mixer_bus_setup(mixer_driver_t* const mixer); extern void mixer_setup(mixer_driver_t* const mixer, mixer_variant_t type); diff --git a/firmware/common/operacake_sctimer.c b/firmware/common/operacake_sctimer.c index 2dbe07d9..3b95f6a1 100644 --- a/firmware/common/operacake_sctimer.c +++ b/firmware/common/operacake_sctimer.c @@ -25,16 +25,16 @@ #include #include #include -#if !defined(PRALINE) + +#include "delay.h" +#include "platform_detect.h" +#include "sct.h" + +#ifdef IS_NOT_PRALINE #include #include #endif -#include - -#include "delay.h" -#include "sct.h" - #define U1CTRL_SET SCT_OUT14_SET #define U1CTRL_CLR SCT_OUT14_CLR #define U2CTRL0_SET SCT_OUT13_SET diff --git a/firmware/common/platform_gpio.h b/firmware/common/platform_gpio.h index 16767136..eeb37b05 100644 --- a/firmware/common/platform_gpio.h +++ b/firmware/common/platform_gpio.h @@ -27,10 +27,11 @@ extern "C" { #include "gpio.h" #include "gpio_lpc.h" +#include "platform_detect.h" typedef struct { /* LEDs */ -#if defined(PRALINE) || defined(UNIVERSAL) || defined(RAD1O) +#ifdef IS_FOUR_LEDS gpio_t led[4]; #else gpio_t led[3]; @@ -38,7 +39,7 @@ typedef struct { /* Power Supply Control */ gpio_t gpio_1v8_enable; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE gpio_t gpio_1v2_enable; gpio_t gpio_3v3aux_enable_n; #endif @@ -48,7 +49,7 @@ typedef struct { gpio_t max283x_enable; gpio_t max283x_rx_enable; gpio_t max283x_tx_enable; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE gpio_t max2831_rxhp; gpio_t max2831_ld; #endif @@ -58,7 +59,7 @@ typedef struct { /* RF supply (VAA) control */ gpio_t vaa_disable; -#if defined(RAD1O) +#ifdef IS_RAD1O gpio_t vaa_enable; #endif @@ -68,7 +69,7 @@ typedef struct { gpio_t w25q80bv_select; /* RF switch control */ -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE gpio_t hp; gpio_t lp; gpio_t tx_mix_bp; @@ -85,7 +86,7 @@ typedef struct { gpio_t h1r9_no_ant_pwr; // HACKRF_ONE r9 gpio_t h1r9_rx; // HACKRF_ONE r9 #endif -#if defined(RAD1O) +#ifdef IS_RAD1O gpio_t tx_rx_n; gpio_t tx_rx; gpio_t by_mix; @@ -98,7 +99,7 @@ typedef struct { gpio_t tx_amp; gpio_t rx_lna; #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE gpio_t tx_en; gpio_t mix_en_n; gpio_t mix_en_n_r1_0; @@ -109,23 +110,23 @@ typedef struct { /* CPLD JTAG interface GPIO pins, FPGA config pins in Praline */ gpio_t cpld_tck; -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE + gpio_t cpld_tdo; gpio_t cpld_tms; gpio_t cpld_tdi; - gpio_t cpld_tdo; #endif -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_EXPANSION_COMPATIBLE gpio_t cpld_pp_tms; gpio_t cpld_pp_tdo; #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE gpio_t fpga_cfg_creset; gpio_t fpga_cfg_cdone; gpio_t fpga_cfg_spi_cs; #endif /* Other CPLD interface GPIO pins */ -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE gpio_t trigger_enable; #endif gpio_t q_invert; @@ -144,7 +145,7 @@ typedef struct { gpio_t synt_rfout_en; // RAD1O /* Praline */ -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE gpio_t p2_ctrl0; gpio_t p2_ctrl1; gpio_t p1_ctrl0; @@ -157,7 +158,7 @@ typedef struct { gpio_t pps_out; #endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE /* HackRF One r9 clock control */ gpio_t h1r9_clkin_en; gpio_t h1r9_clkout_en; @@ -169,14 +170,14 @@ typedef struct { #endif /* RAD1O */ -#if defined(RAD1O) +#ifdef IS_RAD1O gpio_t lcd_cs; gpio_t lcd_bl_en; gpio_t lcd_reset; #endif /* Portapack */ -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_EXPANSION_COMPATIBLE gpio_t io_stbx; gpio_t addr; gpio_t lcd_rdx; diff --git a/firmware/common/platform_scu.h b/firmware/common/platform_scu.h index 2c5365e3..1ffb224c 100644 --- a/firmware/common/platform_scu.h +++ b/firmware/common/platform_scu.h @@ -29,6 +29,8 @@ extern "C" { #include +#include "platform_detect.h" + /* * SCU PinMux */ @@ -38,13 +40,13 @@ typedef struct { scu_grp_pin_t PINMUX_LED1; scu_grp_pin_t PINMUX_LED2; scu_grp_pin_t PINMUX_LED3; -#if defined(RAD1O) || defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_FOUR_LEDS scu_grp_pin_t PINMUX_LED4; #endif scu_grp_pin_t PINMUX_EN1V8; scu_grp_pin_t PINMUX_EN1V2; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE scu_grp_pin_t PINMUX_EN3V3_AUX_N; scu_grp_pin_t PINMUX_EN3V3_OC_N; #endif @@ -52,7 +54,7 @@ typedef struct { /* GPIO Input PinMux */ scu_grp_pin_t PINMUX_BOOT0; scu_grp_pin_t PINMUX_BOOT1; -#if !defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_NOT_HACKRF_ONE scu_grp_pin_t PINMUX_BOOT2; scu_grp_pin_t PINMUX_BOOT3; #endif @@ -63,7 +65,7 @@ typedef struct { scu_grp_pin_t PINMUX_PP_DIR; /* USB peripheral */ -#if defined(JAWBREAKER) +#ifdef IS_JAWBREAKER scu_grp_pin_t PINMUX_USB_LED0; scu_grp_pin_t PINMUX_USB_LED1; #endif @@ -75,7 +77,7 @@ typedef struct { scu_grp_pin_t SSP1_CS; /* CPLD JTAG interface */ -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE scu_grp_pin_t PINMUX_FPGA_CRESET; scu_grp_pin_t PINMUX_FPGA_CDONE; scu_grp_pin_t PINMUX_FPGA_SPI_CS; @@ -127,15 +129,15 @@ typedef struct { uint32_t XCVR_RXENABLE_PINCFG; uint32_t XCVR_TXENABLE_PINCFG; uint32_t XCVR_CS_PINCFG; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE scu_grp_pin_t XCVR_LD; uint32_t XCVR_LD_PINCFG; uint32_t XCVR_RXHP_PINCFG; #endif -#if defined(RAD1O) || defined(PRALINE) || defined(UNIVERSAL) +#if defined(IS_RAD1O) || defined(IS_PRALINE) scu_grp_pin_t XCVR_RXHP; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O scu_grp_pin_t XCVR_B6; scu_grp_pin_t XCVR_B7; #endif @@ -145,7 +147,7 @@ typedef struct { scu_grp_pin_t AD_CS_PINCFG; /* RFFC5071 GPIO serial interface PinMux */ -#if !defined(RAD1O) +#ifdef IS_NOT_RAD1O scu_grp_pin_t MIXER_ENX; scu_grp_pin_t MIXER_SCLK; scu_grp_pin_t MIXER_SDATA; @@ -153,11 +155,11 @@ typedef struct { uint32_t MIXER_SCLK_PINCFG; uint32_t MIXER_SDATA_PINCFG; #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE scu_grp_pin_t MIXER_LD; uint32_t MIXER_LD_PINCFG; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O scu_grp_pin_t VCO_CE; scu_grp_pin_t VCO_SCLK; scu_grp_pin_t VCO_SDATA; @@ -168,15 +170,15 @@ typedef struct { #endif /* RF LDO control */ -#if defined(JAWBREAKER) +#ifdef IS_JAWBREAKER scu_grp_pin_t RF_LDO_ENABLE; #endif /* RF supply (VAA) control */ -#if defined(PRALINE) || defined(HACKRF_ONE) || defined(UNIVERSAL) +#if defined(IS_PRALINE) || defined(IS_HACKRF_ONE) scu_grp_pin_t NO_VAA_ENABLE; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O scu_grp_pin_t VAA_ENABLE; #endif @@ -189,7 +191,7 @@ typedef struct { scu_grp_pin_t FLASH_WP; /* RF switch control */ -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE scu_grp_pin_t TX_EN; scu_grp_pin_t MIX_EN_N; scu_grp_pin_t MIX_EN_N_R1_0; @@ -198,7 +200,7 @@ typedef struct { scu_grp_pin_t ANT_BIAS_EN_N; scu_grp_pin_t ANT_BIAS_OC_N; #endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE scu_grp_pin_t HP; scu_grp_pin_t LP; scu_grp_pin_t TX_MIX_BP; @@ -213,7 +215,7 @@ typedef struct { scu_grp_pin_t RX_AMP; scu_grp_pin_t NO_RX_AMP_PWR; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O scu_grp_pin_t BY_AMP; scu_grp_pin_t BY_AMP_N; scu_grp_pin_t TX_RX; @@ -227,7 +229,7 @@ typedef struct { #endif /* Praline */ -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE scu_grp_pin_t P2_CTRL0; scu_grp_pin_t P2_CTRL1; scu_grp_pin_t P1_CTRL0; @@ -252,7 +254,7 @@ typedef struct { #endif /* HackRF One r9 */ -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE scu_grp_pin_t H1R9_CLKIN_EN; scu_grp_pin_t H1R9_CLKOUT_EN; scu_grp_pin_t H1R9_MCU_CLK_EN; diff --git a/firmware/common/radio.c b/firmware/common/radio.c index a98587f4..760351ac 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -33,7 +33,7 @@ #include "rf_path.h" #include "transceiver_mode.h" #include "tuning.h" -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE #include "fpga.h" #include "tune_config.h" #endif @@ -294,7 +294,7 @@ static fp_40_24_t digital_from_analog_rf( return drf; } -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE /* * Convert center frequency of digital baseband (as seen by MCU) to center * frequency of analog baseband (as seen by ADC/DAC). @@ -354,7 +354,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) const uint64_t requested_if = bank[RADIO_FREQUENCY_IF]; const uint64_t requested_lo = bank[RADIO_FREQUENCY_LO]; const uint64_t requested_img_reject = bank[RADIO_IMAGE_REJECT]; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE const uint64_t requested_rotation = bank[RADIO_ROTATION]; #endif diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index 5043bfe8..6a8e3caf 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -23,16 +23,13 @@ #include "rf_path.h" -#if !defined(JAWBREAKER) - #include -#endif - #include "hackrf_core.h" #include "max283x.h" #include "max5864.h" #include "mixer.h" #include "platform_detect.h" -#if !defined(JAWBREAKER) +#ifdef IS_NOT_JAWBREAKER + #include #include "platform_scu.h" #endif @@ -92,7 +89,7 @@ #define SWITCHCTRL_ANT_PWR (1 << 6) /* turn on antenna port power */ -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE static void switchctrl_set_hackrf_one(rf_path_t* const rf_path, uint8_t ctrl) { board_id_t board_id = detected_platform(); @@ -186,7 +183,7 @@ static void switchctrl_set_hackrf_one(rf_path_t* const rf_path, uint8_t ctrl) } #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE static void switchctrl_set_praline(rf_path_t* const rf_path, uint8_t ctrl) { if (ctrl & SWITCHCTRL_TX) { @@ -227,7 +224,7 @@ static void switchctrl_set_praline(rf_path_t* const rf_path, uint8_t ctrl) } #endif -#if defined(RAD1O) +#ifdef IS_RAD1O static void switchctrl_set_rad1o(rf_path_t* const rf_path, uint8_t ctrl) { if (ctrl & SWITCHCTRL_TX) { @@ -319,11 +316,9 @@ static void switchctrl_set(rf_path_t* const rf_path, const uint8_t gpo) void rf_path_pin_setup(rf_path_t* const rf_path) { -#if defined(JAWBREAKER) +#ifdef JAWBREAKER (void) rf_path; -#endif - -#if !defined(JAWBREAKER) +#else const platform_scu_t* scu = platform_scu(); #endif @@ -530,7 +525,7 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d max283x_rx(&max283x); break; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE case RF_PATH_DIRECTION_TX_CALIBRATION: case RF_PATH_DIRECTION_RX_CALIBRATION: rf_path->switchctrl &= ~SWITCHCTRL_TX; diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h index 64634b3d..42dd91cb 100644 --- a/firmware/common/rf_path.h +++ b/firmware/common/rf_path.h @@ -25,7 +25,9 @@ #include -#if !defined(JAWBREAKER) +#include "platform_detect.h" // IWYU pragma: keep + +#ifdef IS_NOT_JAWBREAKER #include "gpio.h" #endif @@ -33,7 +35,7 @@ typedef enum { RF_PATH_DIRECTION_OFF, RF_PATH_DIRECTION_RX, RF_PATH_DIRECTION_TX, -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE RF_PATH_DIRECTION_TX_CALIBRATION, RF_PATH_DIRECTION_RX_CALIBRATION, #endif @@ -49,7 +51,7 @@ typedef struct { uint8_t switchctrl; struct { -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE gpio_t gpio_hp; gpio_t gpio_lp; gpio_t gpio_tx_mix_bp; @@ -67,7 +69,7 @@ typedef struct { gpio_t gpio_h1r9_no_ant_pwr; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O gpio_t gpio_tx_rx_n; gpio_t gpio_tx_rx; gpio_t gpio_by_mix; @@ -81,7 +83,7 @@ typedef struct { gpio_t gpio_rx_lna; #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE gpio_t gpio_tx_en; gpio_t gpio_mix_en_n; gpio_t gpio_lpf_en; diff --git a/firmware/common/rffc5071.c b/firmware/common/rffc5071.c index ab0717ea..2a1866d2 100644 --- a/firmware/common/rffc5071.c +++ b/firmware/common/rffc5071.c @@ -35,17 +35,14 @@ #include #include -#if defined(PRALINE) || defined(UNIVERSAL) - #include -#endif - #include "delay.h" #include "fixed_point.h" #include "platform_detect.h" #include "rffc5071.h" #include "rffc5071_regs.def" // private register def macros #include "selftest.h" -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE + #include #include "platform_scu.h" #endif @@ -370,7 +367,7 @@ void rffc5071_set_gpo(rffc5071_driver_t* const drv, uint8_t gpo) rffc5071_regs_commit(drv); } -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state) { // This is only supported on Praline hardware. diff --git a/firmware/common/rffc5071.h b/firmware/common/rffc5071.h index 6a0c4ce2..c6b2c0c2 100644 --- a/firmware/common/rffc5071.h +++ b/firmware/common/rffc5071.h @@ -27,6 +27,7 @@ #include "fixed_point.h" #include "gpio.h" +#include "platform_detect.h" // IWYU pragma: keep #include "spi_bus.h" /* 31 registers, each containing 16 bits of data. */ @@ -35,7 +36,7 @@ typedef struct { spi_bus_t* const bus; gpio_t gpio_reset; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE gpio_t gpio_ld; #endif uint16_t regs[RFFC5071_NUM_REGS]; @@ -70,7 +71,7 @@ extern void rffc5071_enable(rffc5071_driver_t* const drv); extern void rffc5071_disable(rffc5071_driver_t* const drv); extern void rffc5071_set_gpo(rffc5071_driver_t* const drv, uint8_t); -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE extern bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state); #endif extern bool rffc5071_check_lock(rffc5071_driver_t* const drv); diff --git a/firmware/common/selftest.h b/firmware/common/selftest.h index 8223a582..9fca53a6 100644 --- a/firmware/common/selftest.h +++ b/firmware/common/selftest.h @@ -24,6 +24,8 @@ #include #include +#include "platform_detect.h" // IWYU pragma: keep + #define NUM_LOCK_ATTEMPTS 3 enum { @@ -37,10 +39,10 @@ typedef uint8_t test_result_t; typedef struct { uint16_t mixer_id; -#if !defined(RAD1O) +#ifdef IS_NOT_RAD1O bool mixer_locks[NUM_LOCK_ATTEMPTS]; #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE uint16_t max2831_mux_rssi_1; uint16_t max2831_mux_temp; uint16_t max2831_mux_rssi_2; @@ -52,7 +54,7 @@ typedef struct { uint8_t max283x_readback_total_registers; uint8_t si5351_rev_id; bool si5351_readback_ok; -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE test_result_t fpga_image_load; test_result_t fpga_spi; test_result_t sgpio_rx; diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index 4aa98265..9d03924a 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -31,7 +31,7 @@ #include "platform_detect.h" #include "platform_scu.h" #include "sgpio.h" -#if !defined(PRALINE) +#ifdef IS_NOT_PRALINE #include "hackrf_core.h" #endif diff --git a/firmware/common/sgpio.h b/firmware/common/sgpio.h index f6fdf213..409cef0c 100644 --- a/firmware/common/sgpio.h +++ b/firmware/common/sgpio.h @@ -26,6 +26,7 @@ #include #include "gpio.h" +#include "platform_detect.h" // IWYU pragma: keep typedef enum { SGPIO_DIRECTION_RX, @@ -34,7 +35,7 @@ typedef enum { typedef struct { gpio_t gpio_q_invert; -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE gpio_t gpio_trigger_enable; #endif bool slice_mode_multislice; diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index a1466393..0acff63e 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -23,16 +23,13 @@ #include #include -#if defined(HACKRF_ONE) || defined(UNIVERSAL) - #include -#endif - #include "clkin.h" #include "delay.h" #include "platform_detect.h" #include "selftest.h" #include "si5351c.h" -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE + #include #include "gpio.h" #include "platform_gpio.h" #include "platform_scu.h" diff --git a/firmware/common/tune_config.h b/firmware/common/tune_config.h index 34c692e6..dd81f62d 100644 --- a/firmware/common/tune_config.h +++ b/firmware/common/tune_config.h @@ -21,7 +21,9 @@ #pragma once -#if defined(PRALINE) || defined(UNIVERSAL) +#include "platform_detect.h" + +#ifdef IS_PRALINE #include "fpga.h" typedef struct { diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 653a477a..b79dcdec 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -48,14 +48,14 @@ #include #include #include -#if defined(PRALINE) || defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_EXPANSION_COMPATIBLE #include #endif -#if !defined(RAD1O) +#ifdef IS_NOT_RAD1O #include #include #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE #include #if !(defined(DFU_MODE) || defined(RAM_MODE)) #include @@ -63,7 +63,7 @@ #include #endif #endif -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE #include #include #endif @@ -81,10 +81,10 @@ #include "usb_descriptor.h" #include "usb_device.h" #include "usb_endpoint.h" -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE #include "usb_api_praline.h" #endif -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE #include "usb_api_cpld.h" #endif @@ -102,7 +102,7 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_read_si5351c, usb_vendor_request_set_sample_rate_frac, usb_vendor_request_set_baseband_filter_bandwidth, -#if !defined(RAD1O) +#ifdef IS_NOT_RAD1O usb_vendor_request_write_rffc5071, usb_vendor_request_read_rffc5071, #else @@ -122,7 +122,7 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_set_vga_gain, usb_vendor_request_set_txvga_gain, NULL, // was set_if_freq -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) +#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE) usb_vendor_request_set_antenna_enable, #else NULL, @@ -139,7 +139,7 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_spiflash_status, usb_vendor_request_spiflash_clear_status, usb_vendor_request_operacake_gpio_test, -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE usb_vendor_request_cpld_checksum, #else NULL, @@ -156,7 +156,7 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_read_supported_platform, usb_vendor_request_set_leds, usb_vendor_request_user_config_set_bias_t_opts, -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE usb_vendor_request_write_fpga_reg, usb_vendor_request_read_fpga_reg, usb_vendor_request_p2_ctrl, @@ -252,7 +252,7 @@ void usb_set_descriptor_by_serial_number(void) } } -#if !defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_NOT_PRALINE static bool cpld_jtag_sram_load(jtag_t* const jtag) { cpld_jtag_take(jtag); @@ -280,7 +280,7 @@ static void m0_rom_to_ram(void) memcpy(dest, (uint32_t*) (base + src), len); } -#if (defined(PRALINE) || defined(UNIVERSAL)) && !(defined(DFU_MODE) || defined(RAM_MODE)) +#if defined(IS_PRALINE) && !(defined(DFU_MODE) || defined(RAM_MODE)) extern uint32_t _binary_fpga_bin_start; void fpga_loader_setup(void) @@ -347,9 +347,11 @@ void radio_changed(const uint32_t changed) if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { hackrf_ui()->set_frequency(freq / FP_ONE_HZ); } - IF_EXPANSION_COMPATIBLE ( +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { operacake_set_range(freq / FP_ONE_MHZ); - ) + } +#endif } if (changed & (1 << RADIO_IMAGE_REJECT)) { if (opmode != TRANSCEIVER_MODE_RX_SWEEP) { @@ -575,7 +577,7 @@ int main(void) rf_path_init(&rf_path); -#if !defined(RAD1O) +#ifdef IS_NOT_RAD1O rffc5071_lock_test(&mixer.rffc5071); #endif diff --git a/firmware/hackrf_usb/usb_api_board_info.c b/firmware/hackrf_usb/usb_api_board_info.c index 393b102d..920ad749 100644 --- a/firmware/hackrf_usb/usb_api_board_info.c +++ b/firmware/hackrf_usb/usb_api_board_info.c @@ -20,7 +20,6 @@ * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ - #include "usb_api_board_info.h" #include @@ -34,7 +33,7 @@ #include #include #include -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE #include #include #endif diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index d17a87d8..b9498dab 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -21,7 +21,6 @@ * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ - #include "usb_api_register.h" #include @@ -36,10 +35,10 @@ #include #include #include -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE #include #endif -#if !defined(RAD1O) +#ifdef IS_NOT_RAD1O #include #include #endif @@ -290,7 +289,7 @@ usb_request_status_t usb_vendor_request_user_config_set_bias_t_opts( return USB_REQUEST_STATUS_OK; } -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE usb_request_status_t usb_vendor_request_write_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) diff --git a/firmware/hackrf_usb/usb_api_register.h b/firmware/hackrf_usb/usb_api_register.h index 58a84aff..aef02d03 100644 --- a/firmware/hackrf_usb/usb_api_register.h +++ b/firmware/hackrf_usb/usb_api_register.h @@ -23,6 +23,8 @@ #pragma once +#include "platform_detect.h" // IWYU pragma: keep + #include #include @@ -56,7 +58,7 @@ usb_request_status_t usb_vendor_request_set_leds( usb_request_status_t usb_vendor_request_user_config_set_bias_t_opts( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE usb_request_status_t usb_vendor_request_write_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); diff --git a/firmware/hackrf_usb/usb_api_selftest.c b/firmware/hackrf_usb/usb_api_selftest.c index 403f3753..4c812366 100644 --- a/firmware/hackrf_usb/usb_api_selftest.c +++ b/firmware/hackrf_usb/usb_api_selftest.c @@ -19,11 +19,10 @@ * Boston, MA 02110-1301, USA. */ +#include "usb_api_selftest.h" + #include #include -#if !defined(RAD1O) - #include -#endif #include #include @@ -34,7 +33,9 @@ #include #include -#include "usb_api_selftest.h" +#ifdef IS_NOT_RAD1O + #include +#endif static char* itoa(int val, int base) { @@ -61,7 +62,7 @@ void append(char** dest, size_t* capacity, const char* str) } } -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE static const char* test_result_to_str(test_result_t result) { switch (result) { diff --git a/firmware/hackrf_usb/usb_descriptor.c b/firmware/hackrf_usb/usb_descriptor.c index f213f484..36531ebc 100644 --- a/firmware/hackrf_usb/usb_descriptor.c +++ b/firmware/hackrf_usb/usb_descriptor.c @@ -26,17 +26,9 @@ #include -#define USB_VENDOR_ID (0x1D50) +#include "platform_detect.h" -#if (defined HACKRF_ONE || defined PRALINE) - #define USB_PRODUCT_ID (0x6089) -#elif JAWBREAKER - #define USB_PRODUCT_ID (0x604B) -#elif RAD1O - #define USB_PRODUCT_ID (0xCC15) -#else - #define USB_PRODUCT_ID (0xFFFF) -#endif +#define USB_VENDOR_ID (0x1D50) #define USB_API_VERSION (0x0111) @@ -51,7 +43,7 @@ #define USB_STRING_LANGID (0x0409) -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) +#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE) uint8_t usb_descriptor_device_hackrf[] = { 18, // bLength USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType @@ -69,7 +61,7 @@ uint8_t usb_descriptor_device_hackrf[] = { 0x01 // bNumConfigurations }; #endif -#if defined(JAWBREAKER) +#ifdef IS_JAWBREAKER uint8_t usb_descriptor_device_jawbreaker[] = { 18, // bLength USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType @@ -87,7 +79,7 @@ uint8_t usb_descriptor_device_jawbreaker[] = { 0x01 // bNumConfigurations }; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O uint8_t usb_descriptor_device_rad1o[] = { 18, // bLength USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType @@ -223,7 +215,7 @@ uint8_t usb_descriptor_string_manufacturer[] = { 's', 0x00, }; -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE uint8_t usb_descriptor_string_product_hackrf_one[] = { 22, // bLength USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType @@ -239,7 +231,7 @@ uint8_t usb_descriptor_string_product_hackrf_one[] = { 'e', 0x00, }; #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE uint8_t usb_descriptor_string_product_praline[] = { 22, // bLength USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType @@ -255,7 +247,7 @@ uint8_t usb_descriptor_string_product_praline[] = { 'o', 0x00, }; #endif -#if defined(JAWBREAKER) +#ifdef IS_JAWBREAKER uint8_t usb_descriptor_string_product_jawbreaker[] = { 36, // bLength USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType @@ -278,7 +270,7 @@ uint8_t usb_descriptor_string_product_jawbreaker[] = { 'r', 0x00, }; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O uint8_t usb_descriptor_string_product_rad1o[] = { 12, // bLength USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType @@ -329,7 +321,7 @@ uint8_t usb_descriptor_string_serial_number[] = { uint8_t usb_descriptor_string_serial_number[USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN]; #endif -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE uint8_t* usb_descriptor_strings_hackrf_one[] = { usb_descriptor_string_languages, usb_descriptor_string_manufacturer, @@ -339,7 +331,7 @@ uint8_t* usb_descriptor_strings_hackrf_one[] = { 0, // TERMINATOR }; #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE uint8_t* usb_descriptor_strings_praline[] = { usb_descriptor_string_languages, usb_descriptor_string_manufacturer, @@ -349,7 +341,7 @@ uint8_t* usb_descriptor_strings_praline[] = { 0, // TERMINATOR }; #endif -#if defined(JAWBREAKER) +#ifdef IS_JAWBREAKER uint8_t* usb_descriptor_strings_jawbreaker[] = { usb_descriptor_string_languages, usb_descriptor_string_manufacturer, @@ -359,7 +351,7 @@ uint8_t* usb_descriptor_strings_jawbreaker[] = { 0, // TERMINATOR }; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O uint8_t* usb_descriptor_strings_rad1o[] = { usb_descriptor_string_languages, usb_descriptor_string_manufacturer, diff --git a/firmware/hackrf_usb/usb_descriptor.h b/firmware/hackrf_usb/usb_descriptor.h index 93f3eb78..ef41d6ac 100644 --- a/firmware/hackrf_usb/usb_descriptor.h +++ b/firmware/hackrf_usb/usb_descriptor.h @@ -24,13 +24,15 @@ #include -#if defined(HACKRF_ONE) || defined(PRALINE) || defined(UNIVERSAL) +#include "platform_detect.h" + +#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE) extern uint8_t usb_descriptor_device_hackrf[]; #endif -#if defined(JAWBREAKER) +#ifdef IS_JAWBREAKER extern uint8_t usb_descriptor_device_jawbreaker[]; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O extern uint8_t usb_descriptor_device_rad1o[]; #endif extern uint8_t usb_descriptor_device_qualifier[]; @@ -38,16 +40,16 @@ extern uint8_t usb_descriptor_configuration_full_speed[]; extern uint8_t usb_descriptor_configuration_high_speed[]; extern uint8_t usb_descriptor_string_languages[]; extern uint8_t usb_descriptor_string_manufacturer[]; -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE extern uint8_t usb_descriptor_string_product_hackrf_one[]; #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE extern uint8_t usb_descriptor_string_product_praline[]; #endif -#if defined(JAWBREAKER) +#ifdef IS_JAWBREAKER extern uint8_t usb_descriptor_string_product_jawbreaker[]; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O extern uint8_t usb_descriptor_string_product_rad1o[]; #endif diff --git a/firmware/hackrf_usb/usb_device.c b/firmware/hackrf_usb/usb_device.c index 4ad46424..a2af1158 100644 --- a/firmware/hackrf_usb/usb_device.c +++ b/firmware/hackrf_usb/usb_device.c @@ -25,6 +25,7 @@ #include +#include "platform_detect.h" #include "usb_descriptor.h" usb_configuration_t usb_configuration_high_speed = { @@ -47,7 +48,7 @@ usb_configuration_t* usb_configurations[] = { usb_device_t usb_device; -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE const usb_device_t usb_device_hackrf_one = { .descriptor = usb_descriptor_device_hackrf, .descriptor_strings = usb_descriptor_strings_hackrf_one, @@ -58,7 +59,7 @@ const usb_device_t usb_device_hackrf_one = { .wcid_feature_descriptor = wcid_feature_descriptor, }; #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE const usb_device_t usb_device_praline = { .descriptor = usb_descriptor_device_hackrf, .descriptor_strings = usb_descriptor_strings_praline, @@ -69,7 +70,7 @@ const usb_device_t usb_device_praline = { .wcid_feature_descriptor = wcid_feature_descriptor, }; #endif -#if defined(JAWBREAKER) +#ifdef IS_JAWBREAKER const usb_device_t usb_device_jawbreaker = { .descriptor = usb_descriptor_device_jawbreaker, .descriptor_strings = usb_descriptor_strings_jawbreaker, @@ -80,7 +81,7 @@ const usb_device_t usb_device_jawbreaker = { .wcid_feature_descriptor = wcid_feature_descriptor, }; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O const usb_device_t usb_device_rad1o = { .descriptor = usb_descriptor_device_rad1o, .descriptor_strings = usb_descriptor_strings_rad1o, diff --git a/firmware/hackrf_usb/usb_device.h b/firmware/hackrf_usb/usb_device.h index 528d084d..f442db37 100644 --- a/firmware/hackrf_usb/usb_device.h +++ b/firmware/hackrf_usb/usb_device.h @@ -25,17 +25,19 @@ #include +#include "platform_detect.h" + extern usb_device_t usb_device; -#if defined(HACKRF_ONE) || defined(UNIVERSAL) +#ifdef IS_HACKRF_ONE extern const usb_device_t usb_device_hackrf_one; #endif -#if defined(PRALINE) || defined(UNIVERSAL) +#ifdef IS_PRALINE extern const usb_device_t usb_device_praline; #endif -#if defined(JAWBREAKER) +#ifdef IS_JAWBREAKER extern const usb_device_t usb_device_jawbreaker; #endif -#if defined(RAD1O) +#ifdef IS_RAD1O extern const usb_device_t usb_device_rad1o; #endif From f2962ce9b10e85d8d709aa05cd425bea48fd50c1 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Tue, 21 Apr 2026 14:24:41 -0400 Subject: [PATCH 067/173] By default, turn off bias tee when radio idle This matches the behavior before #1648. --- firmware/common/radio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 760351ac..a0aafb4c 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -53,6 +53,7 @@ void radio_init(radio_t* const radio) radio->config[RADIO_BANK_IDLE][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF; radio->config[RADIO_BANK_RX][RADIO_OPMODE] = TRANSCEIVER_MODE_RX; radio->config[RADIO_BANK_TX][RADIO_OPMODE] = TRANSCEIVER_MODE_TX; + radio->config[RADIO_BANK_IDLE][RADIO_BIAS_TEE] = false; radio->regs_dirty = 0; } From 4bd414194a5ead5b4adcec23afdcab2a0f33534b Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Tue, 21 Apr 2026 17:11:31 -0400 Subject: [PATCH 068/173] Fix explicit TX interpolation --- firmware/common/radio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 760351ac..6c932ec9 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -158,14 +158,13 @@ static inline uint8_t compute_resample_log( static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) { fp_28_36_t rate, afe_rate, previous_rate, previous_afe_rate; - uint64_t previous_n; + uint64_t previous_n, requested_n; uint8_t n = 0; bool new_afe_rate = false; bool new_rate = false; bool new_n = false; const uint64_t requested_rate = bank[RADIO_SAMPLE_RATE]; - const uint64_t requested_n = bank[RADIO_RESAMPLE_RX]; if (requested_rate != RADIO_UNSET) { rate = MIN(requested_rate, MAX_MCU_RATE); @@ -193,6 +192,7 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) switch (opmode) { case TRANSCEIVER_MODE_TX: case TRANSCEIVER_MODE_SS: + requested_n = bank[RADIO_RESAMPLE_TX]; n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) { #ifdef IS_PRALINE @@ -204,6 +204,7 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) } break; default: + requested_n = bank[RADIO_RESAMPLE_RX]; n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) { #ifdef IS_PRALINE From 230bfbc36f16f15acb2a9380c9e7e814e490200f Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Wed, 22 Apr 2026 10:51:54 -0400 Subject: [PATCH 069/173] Allow explicit resampling ratio of 1 --- firmware/common/radio.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 6c932ec9..4f5eb683 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -127,26 +127,39 @@ static uint32_t radio_update_direction(radio_t* const radio, uint64_t* bank) return (1 << RADIO_OPMODE); } -#define MAX_AFE_RATE_HZ (40000000UL) +#define ABSOLUTE_MIN_AFE_RATE SR_FP_KHZ(200) +#define ABSOLUTE_MAX_AFE_RATE SR_FP_KHZ(43600) +#define MAX_SUPPORTED_AFE_RATE SR_FP_KHZ(40000) static inline uint8_t compute_resample_log( - const uint32_t sample_rate_hz, + const fp_28_36_t sample_rate, const uint64_t requested_n) { if (detected_platform() != BOARD_ID_PRALINE) { return 0; } - const uint8_t min_n = 1; - uint8_t max_n = 5; + uint8_t n = 0; // resampling ratio is 2**n + const uint8_t max_n = 5; + fp_28_36_t afe_rate = 0; - if (requested_n != RADIO_UNSET) { - max_n = MIN(max_n, requested_n); - } - uint8_t n = min_n; // resampling ratio is 2**n - uint32_t afe_rate_x2 = 4 * sample_rate_hz; - while ((afe_rate_x2 <= MAX_AFE_RATE_HZ) && (n < max_n)) { - afe_rate_x2 <<= 1; - n++; + if (requested_n == RADIO_UNSET) { + /* Find highest supported resampling ratio of at least 2 (n=1). */ + while (((afe_rate * 2) <= MAX_SUPPORTED_AFE_RATE) && (n < max_n)) { + n++; + afe_rate = sample_rate << n; + } + } else { + /* Restrict requested resampling ratio within allowable limits. */ + n = MIN(max_n, requested_n); + afe_rate = sample_rate << n; + while ((afe_rate < ABSOLUTE_MIN_AFE_RATE) && (n < max_n)) { + n++; + afe_rate <<= 1; + } + while ((afe_rate > ABSOLUTE_MAX_AFE_RATE) && (n > 0)) { + n--; + afe_rate >>= 1; + } } return n; } @@ -193,7 +206,7 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) case TRANSCEIVER_MODE_TX: case TRANSCEIVER_MODE_SS: requested_n = bank[RADIO_RESAMPLE_TX]; - n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); + n = compute_resample_log(rate, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) { #ifdef IS_PRALINE if (IS_PRALINE) { @@ -205,7 +218,7 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) break; default: requested_n = bank[RADIO_RESAMPLE_RX]; - n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n); + n = compute_resample_log(rate, requested_n); if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) { #ifdef IS_PRALINE if (IS_PRALINE) { From 902ae831bfe5b93277a816d012b44e2d61e7b61c Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 19 Mar 2026 11:30:54 +0200 Subject: [PATCH 070/173] firmware: move cpu clock logic to cpu_clock.h|c --- firmware/common/cpu_clock.c | 252 +++++++++++++++++++++++++++++++ firmware/common/cpu_clock.h | 34 +++++ firmware/common/hackrf_core.c | 225 +-------------------------- firmware/common/hackrf_core.h | 3 +- firmware/hackrf-common.cmake | 1 + firmware/hackrf_usb/hackrf_usb.c | 1 + 6 files changed, 291 insertions(+), 225 deletions(-) create mode 100644 firmware/common/cpu_clock.c create mode 100644 firmware/common/cpu_clock.h diff --git a/firmware/common/cpu_clock.c b/firmware/common/cpu_clock.c new file mode 100644 index 00000000..1bbd9571 --- /dev/null +++ b/firmware/common/cpu_clock.c @@ -0,0 +1,252 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "cpu_clock.h" + +#include + +#include +#if defined(IS_JAWBREAKER) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE) + #include +#endif + +#include "delay.h" +#include "hackrf_core.h" +#include "i2c_bus.h" +#ifdef IS_NOT_RAD1O + #include "platform_detect.h" +#endif + +/* +Configure PLL1 (Main MCU Clock) to max speed (204MHz). +Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. +This function shall be called after cpu_clock_init(). +*/ +static void cpu_clock_pll1_max_speed(void) +{ + uint32_t reg_val; + + /* This function implements the sequence recommended in: + * UM10503 Rev 2.4 (Aug 2018), section 13.2.1.1, page 167. */ + + /* 1. Select the IRC as BASE_M4_CLK source. */ + reg_val = CGU_BASE_M4_CLK; + reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; + reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_IRC) | CGU_BASE_M4_CLK_AUTOBLOCK(1); + CGU_BASE_M4_CLK = reg_val; + + /* 2. Enable the crystal oscillator. */ + CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK; + + /* 3. Wait 250us. */ + delay_us_at_mhz(250, 12); + + /* 4. Set the AUTOBLOCK bit. */ + CGU_PLL1_CTRL |= CGU_PLL1_CTRL_AUTOBLOCK(1); + + /* 5. Reconfigure PLL1 to produce the final output frequency, with the + * crystal oscillator as clock source. */ + reg_val = CGU_PLL1_CTRL; + // clang-format off + reg_val &= ~( CGU_PLL1_CTRL_CLK_SEL_MASK | + CGU_PLL1_CTRL_PD_MASK | + CGU_PLL1_CTRL_FBSEL_MASK | + CGU_PLL1_CTRL_BYPASS_MASK | + CGU_PLL1_CTRL_DIRECT_MASK | + CGU_PLL1_CTRL_PSEL_MASK | + CGU_PLL1_CTRL_MSEL_MASK | + CGU_PLL1_CTRL_NSEL_MASK ); + /* Set PLL1 up to 12MHz * 17 = 204MHz. + * Direct mode: FCLKOUT = FCCO = M*(FCLKIN/N) */ + reg_val |= CGU_PLL1_CTRL_CLK_SEL(CGU_SRC_XTAL) | + CGU_PLL1_CTRL_PSEL(0) | + CGU_PLL1_CTRL_NSEL(0) | + CGU_PLL1_CTRL_MSEL(16) | + CGU_PLL1_CTRL_FBSEL(0) | + CGU_PLL1_CTRL_DIRECT(1); + // clang-format on + CGU_PLL1_CTRL = reg_val; + + /* 6. Wait for PLL1 to lock. */ + while (!(CGU_PLL1_STAT & CGU_PLL1_STAT_LOCK_MASK)) {} + + /* 7. Set the PLL1 P-divider to divide by 2 (DIRECT=0, PSEL=0). */ + CGU_PLL1_CTRL &= ~CGU_PLL1_CTRL_DIRECT_MASK; + + /* 8. Select PLL1 as BASE_M4_CLK source. */ + reg_val = CGU_BASE_M4_CLK; + reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; + reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_PLL1); + CGU_BASE_M4_CLK = reg_val; + + /* 9. Wait 50us. */ + delay_us_at_mhz(50, 102); + + /* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */ + CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK; +} + +/* clock startup for LPC4320 configure PLL1 to max speed (204MHz). +Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. */ +void cpu_clock_init(void) +{ + /* use IRC as clock source for APB1 (including I2C0) */ + CGU_BASE_APB1_CLK = CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_IRC); + + /* use IRC as clock source for APB3 */ + CGU_BASE_APB3_CLK = CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_IRC); + + //FIXME disable I2C + /* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */ + i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); + + /* + * 12MHz clock is entering LPC XTAL1/OSC input now. + * On HackRF One and Jawbreaker, there is a 12 MHz crystal at the LPC. + * Set up PLL1 to run from XTAL1 input. + */ + + //FIXME a lot of the details here should be in a CGU driver + + /* set xtal oscillator to low frequency mode */ + CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_HF_MASK; + + cpu_clock_pll1_max_speed(); + + /* use XTAL_OSC as clock source for APB1 */ + CGU_BASE_APB1_CLK = + CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_XTAL); + + /* use XTAL_OSC as clock source for APB3 */ + CGU_BASE_APB3_CLK = + CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_XTAL); + + /* use XTAL_OSC as clock source for PLL0USB */ + CGU_PLL0USB_CTRL = CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_AUTOBLOCK(1) | + CGU_PLL0USB_CTRL_CLK_SEL(CGU_SRC_XTAL); + while (CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK) {} + + /* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */ + /* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */ + CGU_PLL0USB_MDIV = 0x06167FFA; + CGU_PLL0USB_NP_DIV = 0x00302062; + CGU_PLL0USB_CTRL |= + (CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_DIRECTI(1) | + CGU_PLL0USB_CTRL_DIRECTO(1) | CGU_PLL0USB_CTRL_CLKEN(1)); + + /* power on PLL0USB and wait until stable */ + CGU_PLL0USB_CTRL &= ~CGU_PLL0USB_CTRL_PD_MASK; + while (!(CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK)) {} + + /* use PLL0USB as clock source for USB0 */ + CGU_BASE_USB0_CLK = CGU_BASE_USB0_CLK_AUTOBLOCK(1) | + CGU_BASE_USB0_CLK_CLK_SEL(CGU_SRC_PLL0USB); + + /* Switch peripheral clock over to use PLL1 (204MHz) */ + CGU_BASE_PERIPH_CLK = CGU_BASE_PERIPH_CLK_AUTOBLOCK(1) | + CGU_BASE_PERIPH_CLK_CLK_SEL(CGU_SRC_PLL1); + + /* Switch APB1 clock over to use PLL1 (204MHz) */ + CGU_BASE_APB1_CLK = + CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_PLL1); + + /* Switch APB3 clock over to use PLL1 (204MHz) */ + CGU_BASE_APB3_CLK = + CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_PLL1); + + CGU_BASE_SSP0_CLK = + CGU_BASE_SSP0_CLK_AUTOBLOCK(1) | CGU_BASE_SSP0_CLK_CLK_SEL(CGU_SRC_PLL1); + + CGU_BASE_SSP1_CLK = + CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1); + +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { + /* Disable unused clocks */ + /* Start with PLLs */ + CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); + + /* Dividers */ + CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1); + CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1); + CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1); + CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1); + CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1); + + /* Base clocks */ + CGU_BASE_SPIFI_CLK = + CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */ + CGU_BASE_USB1_CLK = + CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */ + CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1); + CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1); + CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1); + CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1); + CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(1); + CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1); + CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1); + CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1); + CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1); + CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1); + CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1); + CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1); + CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1); + + /* Disable unused peripheral clocks */ + CCU1_CLK_APB1_CAN1_CFG = 0; + CCU1_CLK_APB1_I2S_CFG = 0; + CCU1_CLK_APB1_MOTOCONPWM_CFG = 0; + //CCU1_CLK_APB3_ADC0_CFG = 0; + CCU1_CLK_APB3_ADC1_CFG = 0; + CCU1_CLK_APB3_CAN0_CFG = 0; + CCU1_CLK_APB3_DAC_CFG = 0; + //CCU1_CLK_M4_DMA_CFG = 0; + CCU1_CLK_M4_EMC_CFG = 0; + CCU1_CLK_M4_EMCDIV_CFG = 0; + CCU1_CLK_M4_ETHERNET_CFG = 0; + CCU1_CLK_M4_LCD_CFG = 0; + CCU1_CLK_M4_QEI_CFG = 0; + CCU1_CLK_M4_RITIMER_CFG = 0; + // CCU1_CLK_M4_SCT_CFG = 0; + CCU1_CLK_M4_SDIO_CFG = 0; + CCU1_CLK_M4_SPIFI_CFG = 0; + CCU1_CLK_M4_TIMER0_CFG = 0; + //CCU1_CLK_M4_TIMER1_CFG = 0; + //CCU1_CLK_M4_TIMER2_CFG = 0; + CCU1_CLK_M4_TIMER3_CFG = 0; + CCU1_CLK_M4_UART1_CFG = 0; + CCU1_CLK_M4_USART0_CFG = 0; + CCU1_CLK_M4_USART2_CFG = 0; + CCU1_CLK_M4_USART3_CFG = 0; + CCU1_CLK_M4_USB1_CFG = 0; + CCU1_CLK_M4_VADC_CFG = 0; + // CCU1_CLK_SPIFI_CFG = 0; + // CCU1_CLK_USB1_CFG = 0; + // CCU1_CLK_VADC_CFG = 0; + // CCU2_CLK_APB0_UART1_CFG = 0; + // CCU2_CLK_APB0_USART0_CFG = 0; + // CCU2_CLK_APB2_USART2_CFG = 0; + // CCU2_CLK_APB2_USART3_CFG = 0; + // CCU2_CLK_APLL_CFG = 0; + // CCU2_CLK_SDIO_CFG = 0; + } +#endif +} diff --git a/firmware/common/cpu_clock.h b/firmware/common/cpu_clock.h new file mode 100644 index 00000000..b0393081 --- /dev/null +++ b/firmware/common/cpu_clock.h @@ -0,0 +1,34 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "platform_detect.h" // IWYU pragma: keep + +void cpu_clock_init(void); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index d5679942..e9bc4c2e 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -21,17 +21,12 @@ * Boston, MA 02110-1301, USA. */ -#include #include #include #include #include "platform_detect.h" -#ifdef IS_NOT_RAD1O - #include -#endif - #include "delay.h" #include "gpio.h" #include "hackrf_core.h" @@ -44,14 +39,12 @@ #include "spi_bus.h" #include "w25q80bv_target.h" #ifdef IS_EXPANSION_COMPATIBLE - #include "portapack.h" + #include "portapack.h" #endif #ifdef IS_PRALINE #include "ice40_spi.h" #endif -#include - i2c_bus_t i2c0 = { .obj = (void*) I2C0_BASE, .start = i2c_lpc_start, @@ -428,222 +421,6 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) return resultant_rate; } -/* -Configure PLL1 (Main MCU Clock) to max speed (204MHz). -Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. -This function shall be called after cpu_clock_init(). -*/ -static void cpu_clock_pll1_max_speed(void) -{ - uint32_t reg_val; - - /* This function implements the sequence recommended in: - * UM10503 Rev 2.4 (Aug 2018), section 13.2.1.1, page 167. */ - - /* 1. Select the IRC as BASE_M4_CLK source. */ - reg_val = CGU_BASE_M4_CLK; - reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; - reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_IRC) | CGU_BASE_M4_CLK_AUTOBLOCK(1); - CGU_BASE_M4_CLK = reg_val; - - /* 2. Enable the crystal oscillator. */ - CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK; - - /* 3. Wait 250us. */ - delay_us_at_mhz(250, 12); - - /* 4. Set the AUTOBLOCK bit. */ - CGU_PLL1_CTRL |= CGU_PLL1_CTRL_AUTOBLOCK(1); - - /* 5. Reconfigure PLL1 to produce the final output frequency, with the - * crystal oscillator as clock source. */ - reg_val = CGU_PLL1_CTRL; - // clang-format off - reg_val &= ~( CGU_PLL1_CTRL_CLK_SEL_MASK | - CGU_PLL1_CTRL_PD_MASK | - CGU_PLL1_CTRL_FBSEL_MASK | - CGU_PLL1_CTRL_BYPASS_MASK | - CGU_PLL1_CTRL_DIRECT_MASK | - CGU_PLL1_CTRL_PSEL_MASK | - CGU_PLL1_CTRL_MSEL_MASK | - CGU_PLL1_CTRL_NSEL_MASK ); - /* Set PLL1 up to 12MHz * 17 = 204MHz. - * Direct mode: FCLKOUT = FCCO = M*(FCLKIN/N) */ - reg_val |= CGU_PLL1_CTRL_CLK_SEL(CGU_SRC_XTAL) | - CGU_PLL1_CTRL_PSEL(0) | - CGU_PLL1_CTRL_NSEL(0) | - CGU_PLL1_CTRL_MSEL(16) | - CGU_PLL1_CTRL_FBSEL(0) | - CGU_PLL1_CTRL_DIRECT(1); - // clang-format on - CGU_PLL1_CTRL = reg_val; - - /* 6. Wait for PLL1 to lock. */ - while (!(CGU_PLL1_STAT & CGU_PLL1_STAT_LOCK_MASK)) {} - - /* 7. Set the PLL1 P-divider to divide by 2 (DIRECT=0, PSEL=0). */ - CGU_PLL1_CTRL &= ~CGU_PLL1_CTRL_DIRECT_MASK; - - /* 8. Select PLL1 as BASE_M4_CLK source. */ - reg_val = CGU_BASE_M4_CLK; - reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; - reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_PLL1); - CGU_BASE_M4_CLK = reg_val; - - /* 9. Wait 50us. */ - delay_us_at_mhz(50, 102); - - /* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */ - CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK; -} - -/* clock startup for LPC4320 configure PLL1 to max speed (204MHz). -Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. */ -void cpu_clock_init(void) -{ - /* use IRC as clock source for APB1 (including I2C0) */ - CGU_BASE_APB1_CLK = CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_IRC); - - /* use IRC as clock source for APB3 */ - CGU_BASE_APB3_CLK = CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_IRC); - - //FIXME disable I2C - /* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */ - i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); - - /* - * 12MHz clock is entering LPC XTAL1/OSC input now. - * On HackRF One and Jawbreaker, there is a 12 MHz crystal at the LPC. - * Set up PLL1 to run from XTAL1 input. - */ - - //FIXME a lot of the details here should be in a CGU driver - - /* set xtal oscillator to low frequency mode */ - CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_HF_MASK; - - cpu_clock_pll1_max_speed(); - - /* use XTAL_OSC as clock source for APB1 */ - CGU_BASE_APB1_CLK = - CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_XTAL); - - /* use XTAL_OSC as clock source for APB3 */ - CGU_BASE_APB3_CLK = - CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_XTAL); - - /* use XTAL_OSC as clock source for PLL0USB */ - CGU_PLL0USB_CTRL = CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_AUTOBLOCK(1) | - CGU_PLL0USB_CTRL_CLK_SEL(CGU_SRC_XTAL); - while (CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK) {} - - /* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */ - /* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */ - CGU_PLL0USB_MDIV = 0x06167FFA; - CGU_PLL0USB_NP_DIV = 0x00302062; - CGU_PLL0USB_CTRL |= - (CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_DIRECTI(1) | - CGU_PLL0USB_CTRL_DIRECTO(1) | CGU_PLL0USB_CTRL_CLKEN(1)); - - /* power on PLL0USB and wait until stable */ - CGU_PLL0USB_CTRL &= ~CGU_PLL0USB_CTRL_PD_MASK; - while (!(CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK)) {} - - /* use PLL0USB as clock source for USB0 */ - CGU_BASE_USB0_CLK = CGU_BASE_USB0_CLK_AUTOBLOCK(1) | - CGU_BASE_USB0_CLK_CLK_SEL(CGU_SRC_PLL0USB); - - /* Switch peripheral clock over to use PLL1 (204MHz) */ - CGU_BASE_PERIPH_CLK = CGU_BASE_PERIPH_CLK_AUTOBLOCK(1) | - CGU_BASE_PERIPH_CLK_CLK_SEL(CGU_SRC_PLL1); - - /* Switch APB1 clock over to use PLL1 (204MHz) */ - CGU_BASE_APB1_CLK = - CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_PLL1); - - /* Switch APB3 clock over to use PLL1 (204MHz) */ - CGU_BASE_APB3_CLK = - CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_PLL1); - - CGU_BASE_SSP0_CLK = - CGU_BASE_SSP0_CLK_AUTOBLOCK(1) | CGU_BASE_SSP0_CLK_CLK_SEL(CGU_SRC_PLL1); - - CGU_BASE_SSP1_CLK = - CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1); - -#ifdef IS_NOT_RAD1O - if (IS_NOT_RAD1O) { - /* Disable unused clocks */ - /* Start with PLLs */ - CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); - - /* Dividers */ - CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1); - CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1); - CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1); - CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1); - CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1); - - /* Base clocks */ - CGU_BASE_SPIFI_CLK = - CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */ - CGU_BASE_USB1_CLK = - CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */ - CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1); - CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1); - CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1); - CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1); - CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(1); - CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1); - CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1); - CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1); - CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1); - CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1); - CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1); - CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1); - CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1); - - /* Disable unused peripheral clocks */ - CCU1_CLK_APB1_CAN1_CFG = 0; - CCU1_CLK_APB1_I2S_CFG = 0; - CCU1_CLK_APB1_MOTOCONPWM_CFG = 0; - //CCU1_CLK_APB3_ADC0_CFG = 0; - CCU1_CLK_APB3_ADC1_CFG = 0; - CCU1_CLK_APB3_CAN0_CFG = 0; - CCU1_CLK_APB3_DAC_CFG = 0; - //CCU1_CLK_M4_DMA_CFG = 0; - CCU1_CLK_M4_EMC_CFG = 0; - CCU1_CLK_M4_EMCDIV_CFG = 0; - CCU1_CLK_M4_ETHERNET_CFG = 0; - CCU1_CLK_M4_LCD_CFG = 0; - CCU1_CLK_M4_QEI_CFG = 0; - CCU1_CLK_M4_RITIMER_CFG = 0; - // CCU1_CLK_M4_SCT_CFG = 0; - CCU1_CLK_M4_SDIO_CFG = 0; - CCU1_CLK_M4_SPIFI_CFG = 0; - CCU1_CLK_M4_TIMER0_CFG = 0; - //CCU1_CLK_M4_TIMER1_CFG = 0; - //CCU1_CLK_M4_TIMER2_CFG = 0; - CCU1_CLK_M4_TIMER3_CFG = 0; - CCU1_CLK_M4_UART1_CFG = 0; - CCU1_CLK_M4_USART0_CFG = 0; - CCU1_CLK_M4_USART2_CFG = 0; - CCU1_CLK_M4_USART3_CFG = 0; - CCU1_CLK_M4_USB1_CFG = 0; - CCU1_CLK_M4_VADC_CFG = 0; - // CCU1_CLK_SPIFI_CFG = 0; - // CCU1_CLK_USB1_CFG = 0; - // CCU1_CLK_VADC_CFG = 0; - // CCU2_CLK_APB0_UART1_CFG = 0; - // CCU2_CLK_APB0_USART0_CFG = 0; - // CCU2_CLK_APB2_USART2_CFG = 0; - // CCU2_CLK_APB2_USART3_CFG = 0; - // CCU2_CLK_APLL_CFG = 0; - // CCU2_CLK_SDIO_CFG = 0; - } -#endif -} - void clock_gen_init(void) { i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 56221f6b..334d74a0 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -33,6 +33,7 @@ extern "C" { #include "cpld_jtag.h" #include "fixed_point.h" #include "i2c_bus.h" +#include "i2c_lpc.h" #include "max283x.h" #include "max5864.h" #include "mixer.h" @@ -49,6 +50,7 @@ extern "C" { #endif /* TODO: Hide these configurations */ +extern const i2c_lpc_config_t i2c_config_si5351c_fast_clock; extern si5351c_driver_t clock_gen; extern ssp_config_t ssp_config_w25q80bv; @@ -67,7 +69,6 @@ extern rf_path_t rf_path; extern jtag_t jtag_cpld; extern i2c_bus_t i2c0; -void cpu_clock_init(void); void clock_gen_init(void); void clock_gen_shutdown(void); void ssp1_set_mode_max283x(void); diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index 9a349b1e..a2823405 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -168,6 +168,7 @@ endmacro() macro(DeclareTargets) SET(SRC_M4 ${SRC_M4} + ${PATH_HACKRF_FIRMWARE_COMMON}/cpu_clock.c ${PATH_HACKRF_FIRMWARE_COMMON}/delay.c ${PATH_HACKRF_FIRMWARE_COMMON}/hackrf_core.c ${PATH_HACKRF_FIRMWARE_COMMON}/sgpio.c diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index b79dcdec..dba9a39e 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include From b8cd1d51a7e3d605b238a68f6051c74c5e1bc8ca Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 19 Mar 2026 11:04:47 +0200 Subject: [PATCH 071/173] firmware: move clock generator logic to clock_gen.h|c --- firmware/common/clock_gen.c | 452 ++++++++++++++++++++++ firmware/common/clock_gen.h | 76 ++++ firmware/common/fpga_selftest.c | 1 + firmware/common/hackrf_core.c | 416 +------------------- firmware/common/hackrf_core.h | 27 -- firmware/common/hackrf_ui.h | 2 +- firmware/common/radio.h | 7 - firmware/common/ui_portapack.c | 2 +- firmware/common/ui_rad1o.c | 5 +- firmware/hackrf-common.cmake | 1 + firmware/hackrf_usb/hackrf_usb.c | 1 + firmware/hackrf_usb/usb_api_board_info.c | 1 + firmware/hackrf_usb/usb_api_praline.c | 1 + firmware/hackrf_usb/usb_api_transceiver.c | 1 + 14 files changed, 541 insertions(+), 452 deletions(-) create mode 100644 firmware/common/clock_gen.c create mode 100644 firmware/common/clock_gen.h diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c new file mode 100644 index 00000000..e29e5a91 --- /dev/null +++ b/firmware/common/clock_gen.c @@ -0,0 +1,452 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "clock_gen.h" + +#include + +#include "hackrf_core.h" +#include "hackrf_ui.h" +#include "i2c_bus.h" +#include "platform_detect.h" +#include "sgpio.h" +#include "si5351c.h" +#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE) + #include "delay.h" + #include "portapack.h" +#endif +#if defined(IS_PRALINE) + #include "gpio.h" + #include "platform_gpio.h" +#endif + +void clock_gen_init(void) +{ + i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); + + si5351c_init(&clock_gen); + si5351c_disable_all_outputs(&clock_gen); + si5351c_disable_oeb_pin_control(&clock_gen); + si5351c_power_down_all_clocks(&clock_gen); + si5351c_set_crystal_configuration(&clock_gen); + si5351c_enable_xo_and_ms_fanout(&clock_gen); + + /* + * Clocks on HackRF One r9: + * CLK0 -> MAX5864/CPLD/SGPIO (sample clocks) + * CLK1 -> RFFC5072/MAX2839 + * CLK2 -> External Clock Output/LPC43xx (power down at boot) + * + * Clocks on other platforms: + * CLK0 -> MAX5864/CPLD + * CLK1 -> CPLD + * CLK2 -> SGPIO + * CLK3 -> External Clock Output (power down at boot) + * CLK4 -> RFFC5072 (MAX2837 on rad1o) + * CLK5 -> MAX2837 (MAX2871 on rad1o) + * CLK6 -> none + * CLK7 -> LPC43xx (uses a 12MHz crystal by default) + * + * Clocks on Praline: + * CLK0 -> AFE_CLK (MAX5864/FPGA) + * CLK1 -> SCT_CLK + * CLK2 -> MCU_CLK (uses a 12MHz crystal by default) + * CLK3 -> External Clock Output (power down at boot) + * CLK4 -> XCVR_CLK (MAX2837) + * CLK5 -> MIX_CLK (RFFC5072) + * CLK6 -> AUX_CLK1 + * CLK7 -> AUX_CLK2 + */ + +#ifdef IS_H1_R9 + if (IS_H1_R9) { + /* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */ + si5351c_configure_multisynth( + &clock_gen, + 0, + 20 * 128 - 512, + 0, + 1, + 0); /* 800/20 = 40MHz */ + } +#endif +#ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { + /* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */ + si5351c_configure_multisynth( + &clock_gen, + 4, + 20 * 128 - 512, + 0, + 1, + 0); /* 800/20 = 40MHz */ + /* MS5/CLK5 is the source for the MAX2837 clock input (MAX2871 on rad1o). */ + si5351c_configure_multisynth( + &clock_gen, + 5, + 20 * 128 - 512, + 0, + 1, + 0); /* 800/20 = 40MHz */ + } +#endif + + /* MS6/CLK6 is unused. */ + /* MS7/CLK7 is unused. */ + + /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */ + sample_rate_set(SR_FP_MHZ(10), true); + + si5351c_configure_clock_control(&clock_gen); + si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL); + // soft reset + si5351c_reset_pll(&clock_gen, SI5351C_PLL_BOTH); + si5351c_enable_clock_outputs(&clock_gen); +} + +void clock_gen_shutdown(void) +{ + i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); + si5351c_disable_all_outputs(&clock_gen); + si5351c_disable_oeb_pin_control(&clock_gen); + si5351c_power_down_all_clocks(&clock_gen); +} + +clock_source_t activate_best_clock_source(void) +{ +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { + /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ + if (portapack_reference_oscillator && portapack()) { + portapack_reference_oscillator(false); + } + } +#endif + + clock_source_t source = CLOCK_SOURCE_HACKRF; + + /* Check for external clock input. */ + if (si5351c_clkin_signal_valid(&clock_gen)) { + source = CLOCK_SOURCE_EXTERNAL; + } else { +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { + /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ + if (portapack_reference_oscillator && portapack()) { + portapack_reference_oscillator(true); + delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */ + if (si5351c_clkin_signal_valid(&clock_gen)) { + source = CLOCK_SOURCE_PORTAPACK; + } else { + portapack_reference_oscillator(false); + } + } + } +#endif + /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ + } + + si5351c_set_clock_source( + &clock_gen, + (source == CLOCK_SOURCE_HACKRF) ? PLL_SOURCE_XTAL : PLL_SOURCE_CLKIN); + hackrf_ui()->set_clock_source(source); + + return source; +} + +/* + * Closest fraction to m/d with denominator <= max_den. + * Returns result in *r / *s with gcd(*r, *s) == 1 and 0 < *s <= max_den. + * Straight port from CPython's fractions, and better documented there. + */ +void limit_denominator( + uint64_t m, + uint64_t d, + const uint64_t max_den, + uint64_t* r, + uint64_t* s) +{ + if (d <= max_den) { + *r = m; + *s = d; + return; + } + + uint64_t p0 = 0, q0 = 1, p1 = 1, q1 = 0; + uint64_t n = m, orig_d = d; + uint64_t tmp; + + while (1) { + uint64_t a = n / d; + uint64_t q2 = q0 + a * q1; + + if (q2 > max_den) + break; + + tmp = p0 + a * p1; + p0 = p1; + q0 = q1; + p1 = tmp; + q1 = q2; + + tmp = n - a * d; + n = d; + d = tmp; + if (d == 0) + break; + } + + uint64_t k = (max_den - q0) / q1; + + /* Return closer candidate. */ + if (2 * d * (q0 + k * q1) <= orig_d) { + *r = p1; + *s = q1; + } else { + *r = p0 + k * p1; + *s = q0 + k * q1; + } +} + +/* + * Configure clock generator to produce sample clock in units of 1/(2**36) Hz. + * Can be called with program=false for a dry run that returns the resultant + * frequency without actually configuring the clock generator. + * + * The clock generator output frequency is: + * + * fs = 128 * vco / (512 + p1 + p2/p3)) + * + * where p1, p2, and p3 are register values. + * + * For more information see: + * https://www.pa3fwm.nl/technotes/tn42a-si5351-programming.html + */ +fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) +{ + const uint64_t vco_hz = 800 * 1000ULL * 1000ULL; + uint64_t p1, p2, p3; + uint64_t n, d, q1, q2, q3, r1, r2; + fp_28_36_t resultant_rate; + + /* + * First double the sample rate so that we can produce a clock at twice + * the intended sample rate. The 2x clock is sometimes used directly, + * and it is divided by two in an output divider to produce the actual + * AFE clock. + */ + fp_28_36_t rate = sample_rate * 2; + + /* + * Computes p1 = (N << 36) / rate - 512, where N = 128 * vco_hz. + * + * Full numerator (N << 36) is 73 bits, so we split the division: + * + * (N << 36) / rate = ((N << 27) / rate) << 9 + * + (((N << 27) % rate) << 9) / rate + * + * IMPORTANT: Assumes sample rate is in [200e3 << 36, 43.6e6 << 36]. + */ + const uint64_t A = (128 * vco_hz) << 27; + + q1 = A / rate; + r1 = A % rate; + + // Remaining 9 bits with long division. + q2 = 0; + r2 = r1; + for (int j = 0; j < 9; j++) { + uint64_t msb = r2 >> 63; + r2 <<= 1; + q2 <<= 1; + if (msb || r2 >= rate) { + r2 -= rate; + q2 |= 1; + } + } + + p1 = (q1 << 9) + q2 - 512; + + if (r2) { + /* Use the remainder for the fractional part. */ + n = r2; + d = rate; + + /* Reduce fraction. */ + const uint64_t p3_max = 0xfffff; + limit_denominator(n, d, p3_max, &p2, &p3); + + /* Roll over to next p1 to enable integer mode. */ + if (p2 >= p3) { + p1++; + p2 = 0; + } + } else { + p2 = 0; + } + + /* Maximum: (128 * 2048) - 512 */ + if (p1 > 0x3fe00) { + p1 = 0x3fe00; + p2 = 0; + } + + if (p2 == 0) { + /* Use unity denominator for integer mode. */ + p3 = 1; + n = (128 * vco_hz) << 18; + d = (p1 + 512); + q1 = n / d; + r1 = n % d; + q2 = ((r1 << 18) + (d / 2)) / d; + resultant_rate = (q1 << 18) + q2; + } else { + n = p3 * vco_hz * 128; + d = p3 * (p1 + 512) + p2; + q1 = n / d; + r1 = n % d; + q2 = (r1 << 18) / d; + r2 = (r1 << 18) % d; + q3 = ((r2 << 18) + (d / 2)) / d; + resultant_rate = (q1 << 36) + (q2 << 18) + q3; + } + + /* Return MCU sample rate, not AFE clock rate. */ + resultant_rate = (resultant_rate + 1) / 2; + + if (!program) { + return resultant_rate; + } + + bool streaming = sgpio_cpld_stream_is_enabled(&sgpio_config); + + if (streaming) { + sgpio_cpld_stream_disable(&sgpio_config); + } + +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + /* Integer mode can be enabled if p1 is even and p2 is zero. */ + if (p1 & 0x1 || p2) { + si5351c_set_int_mode(&clock_gen, 0, 0); + } else { + si5351c_set_int_mode(&clock_gen, 0, 1); + } + + #ifdef IS_H1_R9 + if (IS_H1_R9) { + /* + * On HackRF One r9 all sample clocks are externally derived + * from MS1/CLK1 operating at twice the sample rate. + */ + si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { + /* + * On other platforms the clock generator produces three + * different sample clocks, all derived from multisynth 0. + */ + /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ + si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); + + /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ + si5351c_configure_multisynth( + &clock_gen, + 1, + 0, + 0, + 0, + 0); //p1 doesn't matter + + /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ + si5351c_configure_multisynth( + &clock_gen, + 2, + 0, + 0, + 0, + 0); //p1 doesn't matter + } + #endif + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { + /* MS0/CLK0 is the source for the MAX5864 (AFE_CLK). */ + si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); + + /* MS1/CLK1 is the source for the FPGA (FPGA_CLK and SCT_CLK). */ + si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 1); + + /* Delay FPGA_CLK relative to AFE_CLK. */ + uint8_t phase_offset = 0; + if (p1 < 2100) { + phase_offset = (p1 >> 4) - 6; + } + si5351c_set_phase(&clock_gen, 1, phase_offset); + + if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { + /* + * On older boards FPGA_CLK is on CLK2 while SCT_CLK is on + * CLK1. We configure both so that behavior is consistent with + * newer boards that use CLK1 for both FPGA_CLK and SCT_CLK. + */ + si5351c_configure_multisynth(&clock_gen, 2, p1, p2, p3, 1); + si5351c_set_phase(&clock_gen, 2, phase_offset); + } + + /* Reset PLL to synchronize output clock phase. */ + si5351c_reset_pll(&clock_gen, SI5351C_PLL_A); + } +#endif + + if (streaming) { + sgpio_cpld_stream_enable(&sgpio_config); + } + + return resultant_rate; +} + +#ifdef IS_PRALINE +void p1_ctrl_set(const p1_ctrl_signal_t signal) +{ + const platform_gpio_t* gpio = platform_gpio(); + + gpio_write(gpio->p1_ctrl0, signal & 1); + gpio_write(gpio->p1_ctrl1, (signal >> 1) & 1); + gpio_write(gpio->p1_ctrl2, (signal >> 2) & 1); +} + +void p2_ctrl_set(const p2_ctrl_signal_t signal) +{ + const platform_gpio_t* gpio = platform_gpio(); + + gpio_write(gpio->p2_ctrl0, signal & 1); + gpio_write(gpio->p2_ctrl1, (signal >> 1) & 1); +} + +void pps_out_set(const uint8_t value) +{ + gpio_write(platform_gpio()->pps_out, value & 1); +} +#endif diff --git a/firmware/common/clock_gen.h b/firmware/common/clock_gen.h new file mode 100644 index 00000000..48fb6f20 --- /dev/null +++ b/firmware/common/clock_gen.h @@ -0,0 +1,76 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "platform_detect.h" // IWYU pragma: keep + +#include +#ifdef IS_PRALINE + #include +#endif + +#include "fixed_point.h" + +typedef enum { + CLOCK_SOURCE_HACKRF = 0, + CLOCK_SOURCE_EXTERNAL = 1, + CLOCK_SOURCE_PORTAPACK = 2, +} clock_source_t; + +void clock_gen_init(void); +void clock_gen_shutdown(void); + +clock_source_t activate_best_clock_source(void); + +fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program); + +#ifdef IS_PRALINE +typedef enum { + P1_SIGNAL_TRIGGER_IN = 0, + P1_SIGNAL_AUX_CLK1 = 1, + P1_SIGNAL_CLKIN = 2, + P1_SIGNAL_TRIGGER_OUT = 3, + P1_SIGNAL_P22_CLKIN = 4, + P1_SIGNAL_P2_5 = 5, + P1_SIGNAL_NC = 6, + P1_SIGNAL_AUX_CLK2 = 7, +} p1_ctrl_signal_t; + +typedef enum { + P2_SIGNAL_CLK3 = 0, + P2_SIGNAL_TRIGGER_IN = 2, + P2_SIGNAL_TRIGGER_OUT = 3, +} p2_ctrl_signal_t; + +void p1_ctrl_set(const p1_ctrl_signal_t signal); +void p2_ctrl_set(const p2_ctrl_signal_t signal); + +void pps_out_set(const uint8_t value); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index 270d3ff4..fe0a96f9 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -23,6 +23,7 @@ #include #include +#include "clock_gen.h" #include "delay.h" #include "fixed_point.h" #include "fpga.h" diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index e9bc4c2e..c8b12842 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -27,10 +27,10 @@ #include "platform_detect.h" +#include "clock_gen.h" #include "delay.h" #include "gpio.h" #include "hackrf_core.h" -#include "hackrf_ui.h" #include "i2c_lpc.h" #include "max283x.h" #include "max5864_target.h" @@ -38,9 +38,6 @@ #include "platform_scu.h" #include "spi_bus.h" #include "w25q80bv_target.h" -#ifdef IS_EXPANSION_COMPATIBLE - #include "portapack.h" -#endif #ifdef IS_PRALINE #include "ice40_spi.h" #endif @@ -166,395 +163,6 @@ jtag_t jtag_cpld = { .gpio = &jtag_gpio_cpld, }; -/* - * Closest fraction to m/d with denominator <= max_den. - * Returns result in *r / *s with gcd(*r, *s) == 1 and 0 < *s <= max_den. - * Straight port from CPython's fractions, and better documented there. - */ -void limit_denominator( - uint64_t m, - uint64_t d, - const uint64_t max_den, - uint64_t* r, - uint64_t* s) -{ - if (d <= max_den) { - *r = m; - *s = d; - return; - } - - uint64_t p0 = 0, q0 = 1, p1 = 1, q1 = 0; - uint64_t n = m, orig_d = d; - uint64_t tmp; - - while (1) { - uint64_t a = n / d; - uint64_t q2 = q0 + a * q1; - - if (q2 > max_den) - break; - - tmp = p0 + a * p1; - p0 = p1; - q0 = q1; - p1 = tmp; - q1 = q2; - - tmp = n - a * d; - n = d; - d = tmp; - if (d == 0) - break; - } - - uint64_t k = (max_den - q0) / q1; - - /* Return closer candidate. */ - if (2 * d * (q0 + k * q1) <= orig_d) { - *r = p1; - *s = q1; - } else { - *r = p0 + k * p1; - *s = q0 + k * q1; - } -} - -/* - * Configure clock generator to produce sample clock in units of 1/(2**36) Hz. - * Can be called with program=false for a dry run that returns the resultant - * frequency without actually configuring the clock generator. - * - * The clock generator output frequency is: - * - * fs = 128 * vco / (512 + p1 + p2/p3)) - * - * where p1, p2, and p3 are register values. - * - * For more information see: - * https://www.pa3fwm.nl/technotes/tn42a-si5351-programming.html - */ -fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) -{ - const uint64_t vco_hz = 800 * 1000ULL * 1000ULL; - uint64_t p1, p2, p3; - uint64_t n, d, q1, q2, q3, r1, r2; - fp_28_36_t resultant_rate; - - /* - * First double the sample rate so that we can produce a clock at twice - * the intended sample rate. The 2x clock is sometimes used directly, - * and it is divided by two in an output divider to produce the actual - * AFE clock. - */ - fp_28_36_t rate = sample_rate * 2; - - /* - * Computes p1 = (N << 36) / rate - 512, where N = 128 * vco_hz. - * - * Full numerator (N << 36) is 73 bits, so we split the division: - * - * (N << 36) / rate = ((N << 27) / rate) << 9 - * + (((N << 27) % rate) << 9) / rate - * - * IMPORTANT: Assumes sample rate is in [200e3 << 36, 43.6e6 << 36]. - */ - const uint64_t A = (128 * vco_hz) << 27; - - q1 = A / rate; - r1 = A % rate; - - // Remaining 9 bits with long division. - q2 = 0; - r2 = r1; - for (int j = 0; j < 9; j++) { - uint64_t msb = r2 >> 63; - r2 <<= 1; - q2 <<= 1; - if (msb || r2 >= rate) { - r2 -= rate; - q2 |= 1; - } - } - - p1 = (q1 << 9) + q2 - 512; - - if (r2) { - /* Use the remainder for the fractional part. */ - n = r2; - d = rate; - - /* Reduce fraction. */ - const uint64_t p3_max = 0xfffff; - limit_denominator(n, d, p3_max, &p2, &p3); - - /* Roll over to next p1 to enable integer mode. */ - if (p2 >= p3) { - p1++; - p2 = 0; - } - } else { - p2 = 0; - } - - /* Maximum: (128 * 2048) - 512 */ - if (p1 > 0x3fe00) { - p1 = 0x3fe00; - p2 = 0; - } - - if (p2 == 0) { - /* Use unity denominator for integer mode. */ - p3 = 1; - n = (128 * vco_hz) << 18; - d = (p1 + 512); - q1 = n / d; - r1 = n % d; - q2 = ((r1 << 18) + (d / 2)) / d; - resultant_rate = (q1 << 18) + q2; - } else { - n = p3 * vco_hz * 128; - d = p3 * (p1 + 512) + p2; - q1 = n / d; - r1 = n % d; - q2 = (r1 << 18) / d; - r2 = (r1 << 18) % d; - q3 = ((r2 << 18) + (d / 2)) / d; - resultant_rate = (q1 << 36) + (q2 << 18) + q3; - } - - /* Return MCU sample rate, not AFE clock rate. */ - resultant_rate = (resultant_rate + 1) / 2; - - if (!program) { - return resultant_rate; - } - - bool streaming = sgpio_cpld_stream_is_enabled(&sgpio_config); - - if (streaming) { - sgpio_cpld_stream_disable(&sgpio_config); - } - -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - /* Integer mode can be enabled if p1 is even and p2 is zero. */ - if (p1 & 0x1 || p2) { - si5351c_set_int_mode(&clock_gen, 0, 0); - } else { - si5351c_set_int_mode(&clock_gen, 0, 1); - } - - #ifdef IS_H1_R9 - if (IS_H1_R9) { - /* - * On HackRF One r9 all sample clocks are externally derived - * from MS1/CLK1 operating at twice the sample rate. - */ - si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); - } - #endif - #ifdef IS_NOT_H1_R9 - if (IS_NOT_H1_R9) { - /* - * On other platforms the clock generator produces three - * different sample clocks, all derived from multisynth 0. - */ - /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ - si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); - - /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ - si5351c_configure_multisynth( - &clock_gen, - 1, - 0, - 0, - 0, - 0); //p1 doesn't matter - - /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ - si5351c_configure_multisynth( - &clock_gen, - 2, - 0, - 0, - 0, - 0); //p1 doesn't matter - } - #endif - } -#endif -#ifdef IS_PRALINE - if (IS_PRALINE) { - /* MS0/CLK0 is the source for the MAX5864 (AFE_CLK). */ - si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); - - /* MS1/CLK1 is the source for the FPGA (FPGA_CLK and SCT_CLK). */ - si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 1); - - /* Delay FPGA_CLK relative to AFE_CLK. */ - uint8_t phase_offset = 0; - if (p1 < 2100) { - phase_offset = (p1 >> 4) - 6; - } - si5351c_set_phase(&clock_gen, 1, phase_offset); - - if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { - /* - * On older boards FPGA_CLK is on CLK2 while SCT_CLK is on - * CLK1. We configure both so that behavior is consistent with - * newer boards that use CLK1 for both FPGA_CLK and SCT_CLK. - */ - si5351c_configure_multisynth(&clock_gen, 2, p1, p2, p3, 1); - si5351c_set_phase(&clock_gen, 2, phase_offset); - } - - /* Reset PLL to synchronize output clock phase. */ - si5351c_reset_pll(&clock_gen, SI5351C_PLL_A); - } -#endif - - if (streaming) { - sgpio_cpld_stream_enable(&sgpio_config); - } - - return resultant_rate; -} - -void clock_gen_init(void) -{ - i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); - - si5351c_init(&clock_gen); - si5351c_disable_all_outputs(&clock_gen); - si5351c_disable_oeb_pin_control(&clock_gen); - si5351c_power_down_all_clocks(&clock_gen); - si5351c_set_crystal_configuration(&clock_gen); - si5351c_enable_xo_and_ms_fanout(&clock_gen); - - /* - * Clocks on HackRF One r9: - * CLK0 -> MAX5864/CPLD/SGPIO (sample clocks) - * CLK1 -> RFFC5072/MAX2839 - * CLK2 -> External Clock Output/LPC43xx (power down at boot) - * - * Clocks on other platforms: - * CLK0 -> MAX5864/CPLD - * CLK1 -> CPLD - * CLK2 -> SGPIO - * CLK3 -> External Clock Output (power down at boot) - * CLK4 -> RFFC5072 (MAX2837 on rad1o) - * CLK5 -> MAX2837 (MAX2871 on rad1o) - * CLK6 -> none - * CLK7 -> LPC43xx (uses a 12MHz crystal by default) - * - * Clocks on Praline: - * CLK0 -> AFE_CLK (MAX5864/FPGA) - * CLK1 -> SCT_CLK - * CLK2 -> MCU_CLK (uses a 12MHz crystal by default) - * CLK3 -> External Clock Output (power down at boot) - * CLK4 -> XCVR_CLK (MAX2837) - * CLK5 -> MIX_CLK (RFFC5072) - * CLK6 -> AUX_CLK1 - * CLK7 -> AUX_CLK2 - */ - -#ifdef IS_H1_R9 - if (IS_H1_R9) { - /* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */ - si5351c_configure_multisynth( - &clock_gen, - 0, - 20 * 128 - 512, - 0, - 1, - 0); /* 800/20 = 40MHz */ - } -#endif -#ifdef IS_NOT_H1_R9 - if (IS_NOT_H1_R9) { - /* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */ - si5351c_configure_multisynth( - &clock_gen, - 4, - 20 * 128 - 512, - 0, - 1, - 0); /* 800/20 = 40MHz */ - /* MS5/CLK5 is the source for the MAX2837 clock input (MAX2871 on rad1o). */ - si5351c_configure_multisynth( - &clock_gen, - 5, - 20 * 128 - 512, - 0, - 1, - 0); /* 800/20 = 40MHz */ - } -#endif - - /* MS6/CLK6 is unused. */ - /* MS7/CLK7 is unused. */ - - /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */ - sample_rate_set(SR_FP_MHZ(10), true); - - si5351c_configure_clock_control(&clock_gen); - si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL); - // soft reset - si5351c_reset_pll(&clock_gen, SI5351C_PLL_BOTH); - si5351c_enable_clock_outputs(&clock_gen); -} - -void clock_gen_shutdown(void) -{ - i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); - si5351c_disable_all_outputs(&clock_gen); - si5351c_disable_oeb_pin_control(&clock_gen); - si5351c_power_down_all_clocks(&clock_gen); -} - -clock_source_t activate_best_clock_source(void) -{ -#ifdef IS_EXPANSION_COMPATIBLE - if (IS_EXPANSION_COMPATIBLE) { - /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ - if (portapack_reference_oscillator && portapack()) { - portapack_reference_oscillator(false); - } - } -#endif - - clock_source_t source = CLOCK_SOURCE_HACKRF; - - /* Check for external clock input. */ - if (si5351c_clkin_signal_valid(&clock_gen)) { - source = CLOCK_SOURCE_EXTERNAL; - } else { -#ifdef IS_EXPANSION_COMPATIBLE - if (IS_EXPANSION_COMPATIBLE) { - /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ - if (portapack_reference_oscillator && portapack()) { - portapack_reference_oscillator(true); - delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */ - if (si5351c_clkin_signal_valid(&clock_gen)) { - source = CLOCK_SOURCE_PORTAPACK; - } else { - portapack_reference_oscillator(false); - } - } - } -#endif - /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ - } - - si5351c_set_clock_source( - &clock_gen, - (source == CLOCK_SOURCE_HACKRF) ? PLL_SOURCE_XTAL : PLL_SOURCE_CLKIN); - hackrf_ui()->set_clock_source(source); - - return source; -} - void ssp1_set_mode_max283x(void) { spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); @@ -1151,33 +759,11 @@ void halt_and_flash(const uint32_t duration) } #ifdef IS_PRALINE -void p1_ctrl_set(const p1_ctrl_signal_t signal) -{ - const platform_gpio_t* gpio = platform_gpio(); - - gpio_write(gpio->p1_ctrl0, signal & 1); - gpio_write(gpio->p1_ctrl1, (signal >> 1) & 1); - gpio_write(gpio->p1_ctrl2, (signal >> 2) & 1); -} - -void p2_ctrl_set(const p2_ctrl_signal_t signal) -{ - const platform_gpio_t* gpio = platform_gpio(); - - gpio_write(gpio->p2_ctrl0, signal & 1); - gpio_write(gpio->p2_ctrl1, (signal >> 1) & 1); -} - void clkin_ctrl_set(const clkin_signal_t signal) { gpio_write(platform_gpio()->clkin_ctrl, signal & 1); } -void pps_out_set(const uint8_t value) -{ - gpio_write(platform_gpio()->pps_out, value & 1); -} - void narrowband_filter_set(const uint8_t value) { gpio_write(platform_gpio()->aa_en, value & 1); diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 334d74a0..d38ce387 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -31,7 +31,6 @@ extern "C" { #include #include "cpld_jtag.h" -#include "fixed_point.h" #include "i2c_bus.h" #include "i2c_lpc.h" #include "max283x.h" @@ -69,8 +68,6 @@ extern rf_path_t rf_path; extern jtag_t jtag_cpld; extern i2c_bus_t i2c0; -void clock_gen_init(void); -void clock_gen_shutdown(void); void ssp1_set_mode_max283x(void); void ssp1_set_mode_max5864(void); #ifdef IS_PRALINE @@ -89,10 +86,6 @@ void disable_3v3aux_power(void); void enable_1v8_power(void); void disable_1v8_power(void); -fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program); - -clock_source_t activate_best_clock_source(void); - #if defined(IS_RAD1O) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE) void enable_rf_power(void); void disable_rf_power(void); @@ -115,33 +108,13 @@ void trigger_enable(const bool enable); void halt_and_flash(const uint32_t duration); #ifdef IS_PRALINE -typedef enum { - P1_SIGNAL_TRIGGER_IN = 0, - P1_SIGNAL_AUX_CLK1 = 1, - P1_SIGNAL_CLKIN = 2, - P1_SIGNAL_TRIGGER_OUT = 3, - P1_SIGNAL_P22_CLKIN = 4, - P1_SIGNAL_P2_5 = 5, - P1_SIGNAL_NC = 6, - P1_SIGNAL_AUX_CLK2 = 7, -} p1_ctrl_signal_t; - -typedef enum { - P2_SIGNAL_CLK3 = 0, - P2_SIGNAL_TRIGGER_IN = 2, - P2_SIGNAL_TRIGGER_OUT = 3, -} p2_ctrl_signal_t; - typedef enum { CLKIN_SIGNAL_P1 = 0, CLKIN_SIGNAL_P22 = 1, } clkin_signal_t; -void p1_ctrl_set(const p1_ctrl_signal_t signal); -void p2_ctrl_set(const p2_ctrl_signal_t signal); void narrowband_filter_set(const uint8_t value); void clkin_ctrl_set(const clkin_signal_t value); -void pps_out_set(const uint8_t value); #endif #ifdef __cplusplus diff --git a/firmware/common/hackrf_ui.h b/firmware/common/hackrf_ui.h index 1193d1c5..7b7d67e4 100644 --- a/firmware/common/hackrf_ui.h +++ b/firmware/common/hackrf_ui.h @@ -25,7 +25,7 @@ #include #include -#include "radio.h" +#include "clock_gen.h" #include "rf_path.h" #include "transceiver_mode.h" diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 83f72441..7ab46c80 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -54,13 +54,6 @@ typedef struct { uint32_t hz; } radio_sample_rate_t; -// legacy type, moved from hackrf_core -typedef enum { - CLOCK_SOURCE_HACKRF = 0, - CLOCK_SOURCE_EXTERNAL = 1, - CLOCK_SOURCE_PORTAPACK = 2, -} clock_source_t; - /** * Configurable registers stored as uint64_t. Any register may be set to * RADIO_UNSET. When not RADIO_UNSET, some registers are read as a specific type diff --git a/firmware/common/ui_portapack.c b/firmware/common/ui_portapack.c index 8912777f..85ffa705 100644 --- a/firmware/common/ui_portapack.c +++ b/firmware/common/ui_portapack.c @@ -26,8 +26,8 @@ #include #include +#include "clock_gen.h" #include "portapack.h" -#include "radio.h" #include "rf_path.h" #include "transceiver_mode.h" diff --git a/firmware/common/ui_rad1o.c b/firmware/common/ui_rad1o.c index ce894cdf..878e9e74 100644 --- a/firmware/common/ui_rad1o.c +++ b/firmware/common/ui_rad1o.c @@ -26,10 +26,13 @@ #include #include +#include "clock_gen.h" #include "hackrf_core.h" -#include "radio.h" #include "rf_path.h" #include "transceiver_mode.h" +#ifdef IS_NOT_RAD1O + #include "radio.h" +#endif #include "rad1o/display.h" #include "rad1o/draw.h" diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index a2823405..0183948f 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -174,6 +174,7 @@ macro(DeclareTargets) ${PATH_HACKRF_FIRMWARE_COMMON}/sgpio.c ${PATH_HACKRF_FIRMWARE_COMMON}/rf_path.c ${PATH_HACKRF_FIRMWARE_COMMON}/si5351c.c + ${PATH_HACKRF_FIRMWARE_COMMON}/clock_gen.c ${PATH_HACKRF_FIRMWARE_COMMON}/max5864.c ${PATH_HACKRF_FIRMWARE_COMMON}/max5864_target.c ${PATH_HACKRF_FIRMWARE_COMMON}/mixer.c diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index dba9a39e..661d6791 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_board_info.c b/firmware/hackrf_usb/usb_api_board_info.c index 920ad749..c4634605 100644 --- a/firmware/hackrf_usb/usb_api_board_info.c +++ b/firmware/hackrf_usb/usb_api_board_info.c @@ -26,6 +26,7 @@ #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_praline.c b/firmware/hackrf_usb/usb_api_praline.c index 2a41b839..47b59446 100644 --- a/firmware/hackrf_usb/usb_api_praline.c +++ b/firmware/hackrf_usb/usb_api_praline.c @@ -23,6 +23,7 @@ #include "usb_api_praline.h" +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index be16b1be..3d3ded45 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include From 92476260edde8dae7a1d9814c3a3acd10141501c Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Tue, 24 Mar 2026 11:00:56 +0200 Subject: [PATCH 072/173] firmware: move power logic to power.h|c --- firmware/blinky/blinky.c | 9 +- firmware/common/hackrf_core.c | 163 +------------------------ firmware/common/hackrf_core.h | 14 --- firmware/common/power.c | 197 +++++++++++++++++++++++++++++++ firmware/common/power.h | 46 ++++++++ firmware/hackrf-common.cmake | 1 + firmware/hackrf_usb/hackrf_usb.c | 1 + 7 files changed, 251 insertions(+), 180 deletions(-) create mode 100644 firmware/common/power.c create mode 100644 firmware/common/power.h diff --git a/firmware/blinky/blinky.c b/firmware/blinky/blinky.c index 6d071c97..e82f4007 100644 --- a/firmware/blinky/blinky.c +++ b/firmware/blinky/blinky.c @@ -19,9 +19,10 @@ * Boston, MA 02110-1301, USA. */ +#include "delay.h" #include "hackrf_core.h" #include "platform_detect.h" -#include "delay.h" +#include "power.h" int main(void) { @@ -37,18 +38,18 @@ int main(void) #endif /* Blink LED1/2/3 on the board. */ - while (1) + while (1) { led_on(LED1); led_on(LED2); led_on(LED3); delay(2000000); - + led_off(LED1); led_off(LED2); led_off(LED3); - + delay(2000000); } diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index c8b12842..b0b8a563 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -36,6 +36,7 @@ #include "max5864_target.h" #include "platform_gpio.h" #include "platform_scu.h" +#include "power.h" #include "spi_bus.h" #include "w25q80bv_target.h" #ifdef IS_PRALINE @@ -511,168 +512,6 @@ void pin_setup(void) sgpio_configure_pin_functions(&sgpio_config); } -#ifdef IS_PRALINE -void enable_1v2_power(void) -{ - if (IS_PRALINE) { - gpio_set(platform_gpio()->gpio_1v2_enable); - } -} - -void disable_1v2_power(void) -{ - if (IS_PRALINE) { - gpio_clear(platform_gpio()->gpio_1v2_enable); - } -} - -void enable_3v3aux_power(void) -{ - if (IS_PRALINE) { - gpio_clear(platform_gpio()->gpio_3v3aux_enable_n); - } -} - -void disable_3v3aux_power(void) -{ - if (IS_PRALINE) { - gpio_set(platform_gpio()->gpio_3v3aux_enable_n); - } -} -#endif - -void enable_1v8_power(void) -{ -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - #ifdef IS_H1_R9 - if (IS_H1_R9) { - gpio_set(platform_gpio()->h1r9_1v8_enable); - } - #endif - #ifdef IS_NOT_H1_R9 - if (IS_NOT_H1_R9) { - gpio_set(platform_gpio()->gpio_1v8_enable); - } - #endif - } -#endif -} - -void disable_1v8_power(void) -{ -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - #ifdef IS_H1_R9 - if (IS_H1_R9) { - gpio_clear(platform_gpio()->h1r9_1v8_enable); - } - #endif - #ifdef IS_NOT_H1_R9 - if (IS_NOT_H1_R9) { - gpio_clear(platform_gpio()->gpio_1v8_enable); - } - #endif - } -#endif -} - -#ifdef IS_HACKRF_ONE -static inline void enable_rf_power_hackrf_one(void) -{ - const platform_gpio_t* gpio = platform_gpio(); - uint32_t i; - - /* many short pulses to avoid one big voltage glitch */ - for (i = 0; i < 1000; i++) { - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - gpio_set(gpio->h1r9_vaa_disable); - gpio_clear(gpio->h1r9_vaa_disable); - } else { - gpio_set(gpio->vaa_disable); - gpio_clear(gpio->vaa_disable); - } - } -} - -static inline void disable_rf_power_hackrf_one(void) -{ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - gpio_set(platform_gpio()->h1r9_vaa_disable); - } else { - gpio_set(platform_gpio()->vaa_disable); - } -} -#endif - -#ifdef IS_PRALINE -static inline void enable_rf_power_praline(void) -{ - gpio_clear(platform_gpio()->vaa_disable); - - /* Let the voltage stabilize */ - delay(1000000); -} - -static inline void disable_rf_power_praline(void) -{ - gpio_set(platform_gpio()->vaa_disable); -} -#endif - -#ifdef IS_RAD1O -static inline void enable_rf_power_rad1o(void) -{ - gpio_set(platform_gpio()->vaa_enable); - - /* Let the voltage stabilize */ - delay(1000000); -} - -static inline void disable_rf_power_rad1o(void) -{ - gpio_clear(platform_gpio()->vaa_enable); -} -#endif - -void enable_rf_power(void) -{ -#ifdef IS_HACKRF_ONE - if (IS_HACKRF_ONE) { - enable_rf_power_hackrf_one(); - } -#endif -#ifdef IS_PRALINE - if (IS_PRALINE) { - enable_rf_power_praline(); - } -#endif -#ifdef IS_RAD1O - if (IS_RAD1O) { - enable_rf_power_rad1o(); - } -#endif -} - -void disable_rf_power(void) -{ -#ifdef IS_HACKRF_ONE - if (IS_HACKRF_ONE) { - disable_rf_power_hackrf_one(); - } -#endif -#ifdef IS_PRALINE - if (IS_PRALINE) { - disable_rf_power_praline(); - } -#endif -#ifdef IS_RAD1O - if (IS_RAD1O) { - disable_rf_power_rad1o(); - } -#endif -} - void led_on(const led_t led) { #ifdef IS_PRALINE diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index d38ce387..1d783236 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -77,20 +77,6 @@ void ssp1_set_mode_ice40(void); void pin_shutdown(void); void pin_setup(void); -#ifdef IS_PRALINE -void enable_1v2_power(void); -void disable_1v2_power(void); -void enable_3v3aux_power(void); -void disable_3v3aux_power(void); -#endif -void enable_1v8_power(void); -void disable_1v8_power(void); - -#if defined(IS_RAD1O) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE) -void enable_rf_power(void); -void disable_rf_power(void); -#endif - typedef enum { LED1 = 0, LED2 = 1, diff --git a/firmware/common/power.c b/firmware/common/power.c new file mode 100644 index 00000000..25424aab --- /dev/null +++ b/firmware/common/power.c @@ -0,0 +1,197 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "power.h" + +#if defined(IS_HACKRF_ONE) + #include +#endif +#if defined(IS_HACKRF_ONE) || defined(IS_RAD1O) || defined(IS_JAWBREAKER) + #include "platform_detect.h" +#endif +#if defined(IS_PRALINE) || defined(IS_RAD1O) + #include "delay.h" +#endif + +#include "gpio.h" +#include "platform_gpio.h" + +#ifdef IS_PRALINE +void enable_1v2_power(void) +{ + if (IS_PRALINE) { + gpio_set(platform_gpio()->gpio_1v2_enable); + } +} + +void disable_1v2_power(void) +{ + if (IS_PRALINE) { + gpio_clear(platform_gpio()->gpio_1v2_enable); + } +} + +void enable_3v3aux_power(void) +{ + if (IS_PRALINE) { + gpio_clear(platform_gpio()->gpio_3v3aux_enable_n); + } +} + +void disable_3v3aux_power(void) +{ + if (IS_PRALINE) { + gpio_set(platform_gpio()->gpio_3v3aux_enable_n); + } +} +#endif + +void enable_1v8_power(void) +{ +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + #ifdef IS_H1_R9 + if (IS_H1_R9) { + gpio_set(platform_gpio()->h1r9_1v8_enable); + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { + gpio_set(platform_gpio()->gpio_1v8_enable); + } + #endif + } +#endif +} + +void disable_1v8_power(void) +{ +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + #ifdef IS_H1_R9 + if (IS_H1_R9) { + gpio_clear(platform_gpio()->h1r9_1v8_enable); + } + #endif + #ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { + gpio_clear(platform_gpio()->gpio_1v8_enable); + } + #endif + } +#endif +} + +#ifdef IS_HACKRF_ONE +static inline void enable_rf_power_hackrf_one(void) +{ + const platform_gpio_t* gpio = platform_gpio(); + uint32_t i; + + /* many short pulses to avoid one big voltage glitch */ + for (i = 0; i < 1000; i++) { + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + gpio_set(gpio->h1r9_vaa_disable); + gpio_clear(gpio->h1r9_vaa_disable); + } else { + gpio_set(gpio->vaa_disable); + gpio_clear(gpio->vaa_disable); + } + } +} + +static inline void disable_rf_power_hackrf_one(void) +{ + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + gpio_set(platform_gpio()->h1r9_vaa_disable); + } else { + gpio_set(platform_gpio()->vaa_disable); + } +} +#endif + +#ifdef IS_PRALINE +static inline void enable_rf_power_praline(void) +{ + gpio_clear(platform_gpio()->vaa_disable); + + /* Let the voltage stabilize */ + delay(1000000); +} + +static inline void disable_rf_power_praline(void) +{ + gpio_set(platform_gpio()->vaa_disable); +} +#endif + +#ifdef IS_RAD1O +static inline void enable_rf_power_rad1o(void) +{ + gpio_set(platform_gpio()->vaa_enable); + + /* Let the voltage stabilize */ + delay(1000000); +} + +static inline void disable_rf_power_rad1o(void) +{ + gpio_clear(platform_gpio()->vaa_enable); +} +#endif + +void enable_rf_power(void) +{ +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { + enable_rf_power_hackrf_one(); + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { + enable_rf_power_praline(); + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { + enable_rf_power_rad1o(); + } +#endif +} + +void disable_rf_power(void) +{ +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { + disable_rf_power_hackrf_one(); + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { + disable_rf_power_praline(); + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { + disable_rf_power_rad1o(); + } +#endif +} diff --git a/firmware/common/power.h b/firmware/common/power.h new file mode 100644 index 00000000..22b2345e --- /dev/null +++ b/firmware/common/power.h @@ -0,0 +1,46 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "platform_detect.h" // IWYU pragma: keep + +#ifdef IS_PRALINE +void enable_1v2_power(void); +void disable_1v2_power(void); +void enable_3v3aux_power(void); +void disable_3v3aux_power(void); +#endif +void enable_1v8_power(void); +void disable_1v8_power(void); + +#if defined(IS_RAD1O) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE) +void enable_rf_power(void); +void disable_rf_power(void); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index 0183948f..1a55400f 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -168,6 +168,7 @@ endmacro() macro(DeclareTargets) SET(SRC_M4 ${SRC_M4} + ${PATH_HACKRF_FIRMWARE_COMMON}/power.c ${PATH_HACKRF_FIRMWARE_COMMON}/cpu_clock.c ${PATH_HACKRF_FIRMWARE_COMMON}/delay.c ${PATH_HACKRF_FIRMWARE_COMMON}/hackrf_core.c diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 661d6791..eff69298 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include From 799af35b5fd7f94221e0d37db477bd5b3e0bbc1b Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Tue, 24 Mar 2026 14:03:55 +0200 Subject: [PATCH 073/173] firmware: move led logic to leds.h|c --- firmware/blinky/blinky.c | 1 + firmware/common/hackrf_core.c | 73 +--------------- firmware/common/hackrf_core.h | 18 +--- firmware/common/leds.c | 100 ++++++++++++++++++++++ firmware/common/leds.h | 46 ++++++++++ firmware/common/platform_detect.c | 2 +- firmware/hackrf-common.cmake | 1 + firmware/hackrf_usb/hackrf_usb.c | 1 + firmware/hackrf_usb/usb_api_cpld.c | 1 + firmware/hackrf_usb/usb_api_register.c | 1 + firmware/hackrf_usb/usb_api_transceiver.c | 1 + 11 files changed, 157 insertions(+), 88 deletions(-) create mode 100644 firmware/common/leds.c create mode 100644 firmware/common/leds.h diff --git a/firmware/blinky/blinky.c b/firmware/blinky/blinky.c index e82f4007..0bf89bf8 100644 --- a/firmware/blinky/blinky.c +++ b/firmware/blinky/blinky.c @@ -21,6 +21,7 @@ #include "delay.h" #include "hackrf_core.h" +#include "leds.h" #include "platform_detect.h" #include "power.h" diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index b0b8a563..c6429f6d 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -28,10 +28,10 @@ #include "platform_detect.h" #include "clock_gen.h" -#include "delay.h" #include "gpio.h" #include "hackrf_core.h" #include "i2c_lpc.h" +#include "leds.h" #include "max283x.h" #include "max5864_target.h" #include "platform_gpio.h" @@ -512,62 +512,6 @@ void pin_setup(void) sgpio_configure_pin_functions(&sgpio_config); } -void led_on(const led_t led) -{ -#ifdef IS_PRALINE - if (IS_PRALINE) { - gpio_clear(platform_gpio()->led[led]); - } -#endif -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - gpio_set(platform_gpio()->led[led]); - } -#endif -} - -void led_off(const led_t led) -{ -#ifdef IS_PRALINE - if (IS_PRALINE) { - gpio_set(platform_gpio()->led[led]); - } -#endif -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - gpio_clear(platform_gpio()->led[led]); - } -#endif -} - -void led_toggle(const led_t led) -{ - gpio_toggle(platform_gpio()->led[led]); -} - -void set_leds(const uint8_t state) -{ - int num_leds = 3; -#ifdef IS_FOUR_LEDS - if (IS_FOUR_LEDS) { - num_leds = 4; - } -#endif - - for (int i = 0; i < num_leds; i++) { -#ifdef IS_PRALINE - if (IS_PRALINE) { - gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 0); - } -#endif -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 1); - } -#endif - } -} - void trigger_enable(const bool enable) { #ifdef IS_NOT_PRALINE @@ -582,21 +526,6 @@ void trigger_enable(const bool enable) #endif } -void halt_and_flash(const uint32_t duration) -{ - /* blink LED1, LED2, and LED3 */ - while (1) { - led_on(LED1); - led_on(LED2); - led_on(LED3); - delay(duration); - led_off(LED1); - led_off(LED2); - led_off(LED3); - delay(duration); - } -} - #ifdef IS_PRALINE void clkin_ctrl_set(const clkin_signal_t signal) { diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 1d783236..7b6e1336 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -28,7 +28,9 @@ extern "C" { #endif #include -#include +#ifdef IS_PRALINE + #include +#endif #include "cpld_jtag.h" #include "i2c_bus.h" @@ -77,22 +79,8 @@ void ssp1_set_mode_ice40(void); void pin_shutdown(void); void pin_setup(void); -typedef enum { - LED1 = 0, - LED2 = 1, - LED3 = 2, - LED4 = 3, -} led_t; - -void led_on(const led_t led); -void led_off(const led_t led); -void led_toggle(const led_t led); -void set_leds(const uint8_t state); - void trigger_enable(const bool enable); -void halt_and_flash(const uint32_t duration); - #ifdef IS_PRALINE typedef enum { CLKIN_SIGNAL_P1 = 0, diff --git a/firmware/common/leds.c b/firmware/common/leds.c new file mode 100644 index 00000000..ab9b56fb --- /dev/null +++ b/firmware/common/leds.c @@ -0,0 +1,100 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "leds.h" + +#include + +#include "delay.h" +#include "gpio.h" +#include "platform_detect.h" +#include "platform_gpio.h" + +void led_on(const led_t led) +{ +#ifdef IS_PRALINE + if (IS_PRALINE) { + gpio_clear(platform_gpio()->led[led]); + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + gpio_set(platform_gpio()->led[led]); + } +#endif +} + +void led_off(const led_t led) +{ +#ifdef IS_PRALINE + if (IS_PRALINE) { + gpio_set(platform_gpio()->led[led]); + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + gpio_clear(platform_gpio()->led[led]); + } +#endif +} + +void led_toggle(const led_t led) +{ + gpio_toggle(platform_gpio()->led[led]); +} + +void set_leds(const uint8_t state) +{ + int num_leds = 3; +#ifdef IS_FOUR_LEDS + if (IS_FOUR_LEDS) { + num_leds = 4; + } +#endif + + for (int i = 0; i < num_leds; i++) { +#ifdef IS_PRALINE + if (IS_PRALINE) { + gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 0); + } +#endif +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 1); + } +#endif + } +} + +void halt_and_flash(const uint32_t duration) +{ + /* blink LED1, LED2, and LED3 */ + while (1) { + led_on(LED1); + led_on(LED2); + led_on(LED3); + delay(duration); + led_off(LED1); + led_off(LED2); + led_off(LED3); + delay(duration); + } +} diff --git a/firmware/common/leds.h b/firmware/common/leds.h new file mode 100644 index 00000000..a552dca6 --- /dev/null +++ b/firmware/common/leds.h @@ -0,0 +1,46 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef enum { + LED1 = 0, + LED2 = 1, + LED3 = 2, + LED4 = 3, +} led_t; + +void led_on(const led_t led); +void led_off(const led_t led); +void led_toggle(const led_t led); +void set_leds(const uint8_t state); + +void halt_and_flash(const uint32_t duration); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/common/platform_detect.c b/firmware/common/platform_detect.c index b3903035..7c9fb53d 100644 --- a/firmware/common/platform_detect.c +++ b/firmware/common/platform_detect.c @@ -26,7 +26,7 @@ #include "firmware_info.h" #include "gpio.h" #include "gpio_lpc.h" -#include "hackrf_core.h" +#include "leds.h" #include "platform_detect.h" #include "platform_scu.h" diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index 1a55400f..92a905f4 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -172,6 +172,7 @@ macro(DeclareTargets) ${PATH_HACKRF_FIRMWARE_COMMON}/cpu_clock.c ${PATH_HACKRF_FIRMWARE_COMMON}/delay.c ${PATH_HACKRF_FIRMWARE_COMMON}/hackrf_core.c + ${PATH_HACKRF_FIRMWARE_COMMON}/leds.c ${PATH_HACKRF_FIRMWARE_COMMON}/sgpio.c ${PATH_HACKRF_FIRMWARE_COMMON}/rf_path.c ${PATH_HACKRF_FIRMWARE_COMMON}/si5351c.c diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index eff69298..4cd68852 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_cpld.c b/firmware/hackrf_usb/usb_api_cpld.c index 05636a0b..7381601d 100644 --- a/firmware/hackrf_usb/usb_api_cpld.c +++ b/firmware/hackrf_usb/usb_api_cpld.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index b9498dab..632ce0e5 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index 3d3ded45..153b760b 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include From 634bd605b92770defb63959a5736682951ec8d05 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Wed, 25 Mar 2026 14:53:00 +0200 Subject: [PATCH 074/173] firmware: move clkin_ctrl_set to clkin.h|c --- firmware/common/clkin.c | 13 ++++++++++++- firmware/common/clkin.h | 11 +++++++++++ firmware/common/hackrf_core.c | 6 +----- firmware/common/hackrf_core.h | 6 ------ firmware/hackrf_usb/usb_api_praline.c | 1 + 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/firmware/common/clkin.c b/firmware/common/clkin.c index 9f63681c..f5af1723 100644 --- a/firmware/common/clkin.c +++ b/firmware/common/clkin.c @@ -30,6 +30,10 @@ #include #include "gpdma.h" +#ifdef IS_PRALINE + #include "gpio.h" + #include "platform_gpio.h" +#endif #define CLOCK_CYCLES_1_MS (204000) #define MEASUREMENT_WINDOW_MS (50) @@ -114,4 +118,11 @@ void clkin_detect_init(void) uint32_t clkin_frequency(void) { return TIMER2_CR3 * (1000 / MEASUREMENT_WINDOW_MS); -}; +} + +#ifdef IS_PRALINE +void clkin_ctrl_set(const clkin_signal_t signal) +{ + gpio_write(platform_gpio()->clkin_ctrl, signal & 1); +} +#endif diff --git a/firmware/common/clkin.h b/firmware/common/clkin.h index 2cef67cf..53379013 100644 --- a/firmware/common/clkin.h +++ b/firmware/common/clkin.h @@ -23,5 +23,16 @@ #include +#include "platform_detect.h" // IWYU pragma: keep + void clkin_detect_init(void); uint32_t clkin_frequency(void); + +#ifdef IS_PRALINE +typedef enum { + CLKIN_SIGNAL_P1 = 0, + CLKIN_SIGNAL_P22 = 1, +} clkin_signal_t; + +void clkin_ctrl_set(const clkin_signal_t value); +#endif diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index c6429f6d..29ca3820 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -40,6 +40,7 @@ #include "spi_bus.h" #include "w25q80bv_target.h" #ifdef IS_PRALINE + #include "clkin.h" #include "ice40_spi.h" #endif @@ -527,11 +528,6 @@ void trigger_enable(const bool enable) } #ifdef IS_PRALINE -void clkin_ctrl_set(const clkin_signal_t signal) -{ - gpio_write(platform_gpio()->clkin_ctrl, signal & 1); -} - void narrowband_filter_set(const uint8_t value) { gpio_write(platform_gpio()->aa_en, value & 1); diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 7b6e1336..e0253458 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -82,13 +82,7 @@ void pin_setup(void); void trigger_enable(const bool enable); #ifdef IS_PRALINE -typedef enum { - CLKIN_SIGNAL_P1 = 0, - CLKIN_SIGNAL_P22 = 1, -} clkin_signal_t; - void narrowband_filter_set(const uint8_t value); -void clkin_ctrl_set(const clkin_signal_t value); #endif #ifdef __cplusplus diff --git a/firmware/hackrf_usb/usb_api_praline.c b/firmware/hackrf_usb/usb_api_praline.c index 47b59446..434418a5 100644 --- a/firmware/hackrf_usb/usb_api_praline.c +++ b/firmware/hackrf_usb/usb_api_praline.c @@ -23,6 +23,7 @@ #include "usb_api_praline.h" +#include #include #include #include From e2741090ad36b9aac6243fa25abad5eed9404ddd Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Fri, 27 Mar 2026 14:46:12 +0200 Subject: [PATCH 075/173] firmware: move narrowband_filter_set to rf_path.h|c --- firmware/common/fpga.h | 2 ++ firmware/common/hackrf_core.c | 7 ------- firmware/common/hackrf_core.h | 7 ------- firmware/common/rf_path.c | 10 ++++++++++ firmware/common/rf_path.h | 4 ++++ firmware/hackrf_usb/usb_api_praline.c | 4 ++-- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/firmware/common/fpga.h b/firmware/common/fpga.h index 4aa54239..65826b21 100644 --- a/firmware/common/fpga.h +++ b/firmware/common/fpga.h @@ -90,3 +90,5 @@ bool fpga_image_load(struct fpga_loader_t* loader, unsigned int index); bool fpga_spi_selftest(void); bool fpga_sgpio_selftest(void); bool fpga_if_xcvr_selftest(void); + +//void narrowband_filter_set(const uint8_t value); diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 29ca3820..58499442 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -526,10 +526,3 @@ void trigger_enable(const bool enable) } #endif } - -#ifdef IS_PRALINE -void narrowband_filter_set(const uint8_t value) -{ - gpio_write(platform_gpio()->aa_en, value & 1); -} -#endif diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index e0253458..3755ee2e 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -28,9 +28,6 @@ extern "C" { #endif #include -#ifdef IS_PRALINE - #include -#endif #include "cpld_jtag.h" #include "i2c_bus.h" @@ -81,10 +78,6 @@ void pin_setup(void); void trigger_enable(const bool enable); -#ifdef IS_PRALINE -void narrowband_filter_set(const uint8_t value); -#endif - #ifdef __cplusplus } #endif diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index 6a8e3caf..6cadea1c 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -32,6 +32,9 @@ #include #include "platform_scu.h" #endif +#ifdef IS_PRALINE + #include "platform_gpio.h" +#endif /* * RF switches on Jawbreaker are controlled by General Purpose Outputs (GPO) on @@ -615,3 +618,10 @@ void rf_path_set_antenna(rf_path_t* const rf_path, const uint_fast8_t enable) switchctrl_set(rf_path, rf_path->switchctrl); } + +#ifdef IS_PRALINE +void narrowband_filter_set(const uint8_t value) +{ + gpio_write(platform_gpio()->aa_en, value & 1); +} +#endif diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h index 42dd91cb..12f83b53 100644 --- a/firmware/common/rf_path.h +++ b/firmware/common/rf_path.h @@ -102,3 +102,7 @@ void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter) void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable); void rf_path_set_antenna(rf_path_t* const rf_path, const uint_fast8_t enable); + +#ifdef IS_PRALINE +void narrowband_filter_set(const uint8_t value); +#endif diff --git a/firmware/hackrf_usb/usb_api_praline.c b/firmware/hackrf_usb/usb_api_praline.c index 434418a5..262e66df 100644 --- a/firmware/hackrf_usb/usb_api_praline.c +++ b/firmware/hackrf_usb/usb_api_praline.c @@ -25,12 +25,12 @@ #include #include -#include #include +#include #include #include #include -#if !(defined(DFU_MODE) || defined(RAM_MODE)) +#if !defined(DFU_MODE) && !defined(RAM_MODE) #include #endif From 5672be9d00a67a5ad8c2488580325d686b8365ef Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Tue, 31 Mar 2026 11:25:14 +0200 Subject: [PATCH 076/173] firmware: move clock-associated gpio functions to clkin.x and rename to clock_io.h|c --- firmware/common/clock_gen.c | 28 --------------- firmware/common/clock_gen.h | 27 --------------- firmware/common/{clkin.c => clock_io.c} | 45 +++++++++++++++++++++++-- firmware/common/{clkin.h => clock_io.h} | 26 +++++++++++++- firmware/common/hackrf_core.c | 18 ++-------- firmware/common/hackrf_core.h | 4 --- firmware/common/radio.c | 1 + firmware/common/sgpio.c | 2 +- firmware/common/si5351c.c | 2 +- firmware/hackrf-common.cmake | 2 +- firmware/hackrf_usb/hackrf_usb.c | 2 +- firmware/hackrf_usb/usb_api_praline.c | 3 +- 12 files changed, 76 insertions(+), 84 deletions(-) rename firmware/common/{clkin.c => clock_io.c} (81%) rename firmware/common/{clkin.h => clock_io.h} (64%) diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index e29e5a91..3080c436 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -33,10 +33,6 @@ #include "delay.h" #include "portapack.h" #endif -#if defined(IS_PRALINE) - #include "gpio.h" - #include "platform_gpio.h" -#endif void clock_gen_init(void) { @@ -426,27 +422,3 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) return resultant_rate; } - -#ifdef IS_PRALINE -void p1_ctrl_set(const p1_ctrl_signal_t signal) -{ - const platform_gpio_t* gpio = platform_gpio(); - - gpio_write(gpio->p1_ctrl0, signal & 1); - gpio_write(gpio->p1_ctrl1, (signal >> 1) & 1); - gpio_write(gpio->p1_ctrl2, (signal >> 2) & 1); -} - -void p2_ctrl_set(const p2_ctrl_signal_t signal) -{ - const platform_gpio_t* gpio = platform_gpio(); - - gpio_write(gpio->p2_ctrl0, signal & 1); - gpio_write(gpio->p2_ctrl1, (signal >> 1) & 1); -} - -void pps_out_set(const uint8_t value) -{ - gpio_write(platform_gpio()->pps_out, value & 1); -} -#endif diff --git a/firmware/common/clock_gen.h b/firmware/common/clock_gen.h index 48fb6f20..0bc6439b 100644 --- a/firmware/common/clock_gen.h +++ b/firmware/common/clock_gen.h @@ -28,9 +28,6 @@ extern "C" { #include "platform_detect.h" // IWYU pragma: keep #include -#ifdef IS_PRALINE - #include -#endif #include "fixed_point.h" @@ -47,30 +44,6 @@ clock_source_t activate_best_clock_source(void); fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program); -#ifdef IS_PRALINE -typedef enum { - P1_SIGNAL_TRIGGER_IN = 0, - P1_SIGNAL_AUX_CLK1 = 1, - P1_SIGNAL_CLKIN = 2, - P1_SIGNAL_TRIGGER_OUT = 3, - P1_SIGNAL_P22_CLKIN = 4, - P1_SIGNAL_P2_5 = 5, - P1_SIGNAL_NC = 6, - P1_SIGNAL_AUX_CLK2 = 7, -} p1_ctrl_signal_t; - -typedef enum { - P2_SIGNAL_CLK3 = 0, - P2_SIGNAL_TRIGGER_IN = 2, - P2_SIGNAL_TRIGGER_OUT = 3, -} p2_ctrl_signal_t; - -void p1_ctrl_set(const p1_ctrl_signal_t signal); -void p2_ctrl_set(const p2_ctrl_signal_t signal); - -void pps_out_set(const uint8_t value); -#endif - #ifdef __cplusplus } #endif diff --git a/firmware/common/clkin.c b/firmware/common/clock_io.c similarity index 81% rename from firmware/common/clkin.c rename to firmware/common/clock_io.c index f5af1723..0a8b48d9 100644 --- a/firmware/common/clkin.c +++ b/firmware/common/clock_io.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 Great Scott Gadgets + * Copyright 2022-2026 Great Scott Gadgets * * This file is part of HackRF. * @@ -19,8 +19,9 @@ * Boston, MA 02110-1301, USA. */ -#include "clkin.h" +#include "clock_io.h" +#include #include #include @@ -30,8 +31,10 @@ #include #include "gpdma.h" +#include "gpio.h" +#include "hackrf_core.h" #ifdef IS_PRALINE - #include "gpio.h" + #include "fpga.h" #include "platform_gpio.h" #endif @@ -120,9 +123,45 @@ uint32_t clkin_frequency(void) return TIMER2_CR3 * (1000 / MEASUREMENT_WINDOW_MS); } +void trigger_enable(const bool enable) +{ +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + gpio_write(sgpio_config.gpio_trigger_enable, enable); + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { + fpga_set_trigger_enable(&fpga, enable); + } +#endif +} + #ifdef IS_PRALINE void clkin_ctrl_set(const clkin_signal_t signal) { gpio_write(platform_gpio()->clkin_ctrl, signal & 1); } + +void p1_ctrl_set(const p1_ctrl_signal_t signal) +{ + const platform_gpio_t* gpio = platform_gpio(); + + gpio_write(gpio->p1_ctrl0, signal & 1); + gpio_write(gpio->p1_ctrl1, (signal >> 1) & 1); + gpio_write(gpio->p1_ctrl2, (signal >> 2) & 1); +} + +void p2_ctrl_set(const p2_ctrl_signal_t signal) +{ + const platform_gpio_t* gpio = platform_gpio(); + + gpio_write(gpio->p2_ctrl0, signal & 1); + gpio_write(gpio->p2_ctrl1, (signal >> 1) & 1); +} + +void pps_out_set(const uint8_t value) +{ + gpio_write(platform_gpio()->pps_out, value & 1); +} #endif diff --git a/firmware/common/clkin.h b/firmware/common/clock_io.h similarity index 64% rename from firmware/common/clkin.h rename to firmware/common/clock_io.h index 53379013..df540022 100644 --- a/firmware/common/clkin.h +++ b/firmware/common/clock_io.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Great Scott Gadgets + * Copyright 2022-2026 Great Scott Gadgets * * This file is part of HackRF. * @@ -21,6 +21,7 @@ #pragma once +#include #include #include "platform_detect.h" // IWYU pragma: keep @@ -28,11 +29,34 @@ void clkin_detect_init(void); uint32_t clkin_frequency(void); +void trigger_enable(const bool enable); + #ifdef IS_PRALINE typedef enum { CLKIN_SIGNAL_P1 = 0, CLKIN_SIGNAL_P22 = 1, } clkin_signal_t; +typedef enum { + P1_SIGNAL_TRIGGER_IN = 0, + P1_SIGNAL_AUX_CLK1 = 1, + P1_SIGNAL_CLKIN = 2, + P1_SIGNAL_TRIGGER_OUT = 3, + P1_SIGNAL_P22_CLKIN = 4, + P1_SIGNAL_P2_5 = 5, + P1_SIGNAL_NC = 6, + P1_SIGNAL_AUX_CLK2 = 7, +} p1_ctrl_signal_t; + +typedef enum { + P2_SIGNAL_CLK3 = 0, + P2_SIGNAL_TRIGGER_IN = 2, + P2_SIGNAL_TRIGGER_OUT = 3, +} p2_ctrl_signal_t; + void clkin_ctrl_set(const clkin_signal_t value); +void p1_ctrl_set(const p1_ctrl_signal_t signal); +void p2_ctrl_set(const p2_ctrl_signal_t signal); + +void pps_out_set(const uint8_t value); #endif diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 58499442..086e3961 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -21,6 +21,8 @@ * Boston, MA 02110-1301, USA. */ +#include + #include #include #include @@ -40,7 +42,7 @@ #include "spi_bus.h" #include "w25q80bv_target.h" #ifdef IS_PRALINE - #include "clkin.h" + #include "clock_io.h" #include "ice40_spi.h" #endif @@ -512,17 +514,3 @@ void pin_setup(void) sgpio_configure_pin_functions(&sgpio_config); } - -void trigger_enable(const bool enable) -{ -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - gpio_write(sgpio_config.gpio_trigger_enable, enable); - } -#endif -#ifdef IS_PRALINE - if (IS_PRALINE) { - fpga_set_trigger_enable(&fpga, enable); - } -#endif -} diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 3755ee2e..6e0c1cc0 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -27,8 +27,6 @@ extern "C" { #endif -#include - #include "cpld_jtag.h" #include "i2c_bus.h" #include "i2c_lpc.h" @@ -76,8 +74,6 @@ void ssp1_set_mode_ice40(void); void pin_shutdown(void); void pin_setup(void); -void trigger_enable(const bool enable); - #ifdef __cplusplus } #endif diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 760351ac..7191f1ff 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -25,6 +25,7 @@ #include +#include "clock_io.h" #include "fixed_point.h" #include "hackrf_core.h" #include "mixer.h" diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index 9d03924a..568f1208 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -32,7 +32,7 @@ #include "platform_scu.h" #include "sgpio.h" #ifdef IS_NOT_PRALINE - #include "hackrf_core.h" + #include "clock_io.h" #endif static void update_q_invert(sgpio_config_t* const config); diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 0acff63e..c9767405 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -23,7 +23,7 @@ #include #include -#include "clkin.h" +#include "clock_io.h" #include "delay.h" #include "platform_detect.h" #include "selftest.h" diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index 92a905f4..6384acdc 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -192,7 +192,7 @@ macro(DeclareTargets) ${PATH_HACKRF_FIRMWARE_COMMON}/platform_gpio.c ${PATH_HACKRF_FIRMWARE_COMMON}/platform_scu.c ${PATH_HACKRF_FIRMWARE_COMMON}/firmware_info.c - ${PATH_HACKRF_FIRMWARE_COMMON}/clkin.c + ${PATH_HACKRF_FIRMWARE_COMMON}/clock_io.c ${PATH_HACKRF_FIRMWARE_COMMON}/gpdma.c ${PATH_HACKRF_FIRMWARE_COMMON}/radio.c ${PATH_HACKRF_FIRMWARE_COMMON}/selftest.c diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 4cd68852..bd2c5964 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -28,8 +28,8 @@ #include #include -#include #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_praline.c b/firmware/hackrf_usb/usb_api_praline.c index 262e66df..d889f5da 100644 --- a/firmware/hackrf_usb/usb_api_praline.c +++ b/firmware/hackrf_usb/usb_api_praline.c @@ -23,8 +23,7 @@ #include "usb_api_praline.h" -#include -#include +#include #include #include #include From adf83cbac2396c568145e28bad48549b4e89561a Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Tue, 31 Mar 2026 10:59:18 +0200 Subject: [PATCH 077/173] firmware: move hackrf_core drivers & configurations to drivers.h|c --- firmware/common/clock_gen.c | 2 +- firmware/common/clock_io.c | 2 +- firmware/common/cpu_clock.c | 2 +- firmware/common/da7219.c | 2 +- firmware/common/drivers.c | 178 ++++++++++++++++++++++ firmware/common/drivers.h | 77 ++++++++++ firmware/common/fpga.c | 4 +- firmware/common/fpga_image.c | 2 +- firmware/common/fpga_selftest.c | 2 +- firmware/common/hackrf_core.c | 158 +------------------ firmware/common/hackrf_core.h | 44 ------ firmware/common/operacake.c | 2 +- firmware/common/portapack.c | 2 +- firmware/common/radio.c | 4 +- firmware/common/rf_path.c | 2 +- firmware/common/rom_iap.c | 2 +- firmware/common/ui_rad1o.c | 2 +- firmware/hackrf-common.cmake | 1 + firmware/hackrf_usb/hackrf_usb.c | 1 + firmware/hackrf_usb/usb_api_cpld.c | 2 +- firmware/hackrf_usb/usb_api_register.c | 2 +- firmware/hackrf_usb/usb_api_spiflash.c | 2 +- firmware/hackrf_usb/usb_api_sweep.c | 2 +- firmware/hackrf_usb/usb_api_transceiver.c | 2 +- 24 files changed, 284 insertions(+), 215 deletions(-) create mode 100644 firmware/common/drivers.c create mode 100644 firmware/common/drivers.h diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index 3080c436..b62308ce 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -23,7 +23,7 @@ #include -#include "hackrf_core.h" +#include "drivers.h" #include "hackrf_ui.h" #include "i2c_bus.h" #include "platform_detect.h" diff --git a/firmware/common/clock_io.c b/firmware/common/clock_io.c index 0a8b48d9..279eb8c5 100644 --- a/firmware/common/clock_io.c +++ b/firmware/common/clock_io.c @@ -30,9 +30,9 @@ #include #include +#include "drivers.h" #include "gpdma.h" #include "gpio.h" -#include "hackrf_core.h" #ifdef IS_PRALINE #include "fpga.h" #include "platform_gpio.h" diff --git a/firmware/common/cpu_clock.c b/firmware/common/cpu_clock.c index 1bbd9571..b69f0773 100644 --- a/firmware/common/cpu_clock.c +++ b/firmware/common/cpu_clock.c @@ -29,7 +29,7 @@ #endif #include "delay.h" -#include "hackrf_core.h" +#include "drivers.h" #include "i2c_bus.h" #ifdef IS_NOT_RAD1O #include "platform_detect.h" diff --git a/firmware/common/da7219.c b/firmware/common/da7219.c index 376e04a5..5af022d2 100644 --- a/firmware/common/da7219.c +++ b/firmware/common/da7219.c @@ -22,7 +22,7 @@ #include #include "da7219.h" -#include "hackrf_core.h" +#include "drivers.h" #include "i2c_bus.h" #define DA7219_REG_CHIP_ID1 0x81 diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c new file mode 100644 index 00000000..a6bd536a --- /dev/null +++ b/firmware/common/drivers.c @@ -0,0 +1,178 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "drivers.h" + +#include + +#include +#include + +#include "clock_gen.h" +#include "i2c_lpc.h" +#include "max283x.h" +#include "max5864_target.h" +#include "spi_bus.h" +#include "w25q80bv_target.h" +#if defined(IS_PRALINE) + #include "fpga.h" + #include "ice40_spi.h" +#endif + +i2c_bus_t i2c0 = { + .obj = (void*) I2C0_BASE, + .start = i2c_lpc_start, + .stop = i2c_lpc_stop, + .transfer = i2c_lpc_transfer, +}; + +i2c_bus_t i2c1 = { + .obj = (void*) I2C1_BASE, + .start = i2c_lpc_start, + .stop = i2c_lpc_stop, + .transfer = i2c_lpc_transfer, +}; + +// const i2c_lpc_config_t i2c_config_si5351c_slow_clock = { +// .duty_cycle_count = 15, +// }; + +const i2c_lpc_config_t i2c_config_si5351c_fast_clock = { + .duty_cycle_count = 255, +}; + +si5351c_driver_t clock_gen = { + .bus = &i2c0, + .i2c_address = 0x60, +}; + +ssp_config_t ssp_config_max283x = { + /* FIXME speed up once everything is working reliably */ + /* + // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + const uint8_t serial_clock_rate = 32; + const uint8_t clock_prescale_rate = 128; + */ + // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + .serial_clock_rate = 21, + .clock_prescale_rate = 2, +}; + +max283x_driver_t max283x = {}; + +ssp_config_t ssp_config_max5864 = { + /* FIXME speed up once everything is working reliably */ + /* + // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + const uint8_t serial_clock_rate = 32; + const uint8_t clock_prescale_rate = 128; + */ + // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + .data_bits = SSP_DATA_8BITS, + .serial_clock_rate = 21, + .clock_prescale_rate = 2, +}; + +spi_bus_t spi_bus_ssp1 = { + .obj = (void*) SSP1_BASE, + .config = &ssp_config_max5864, + .start = spi_ssp_start, + .stop = spi_ssp_stop, + .transfer = spi_ssp_transfer, + .transfer_gather = spi_ssp_transfer_gather, +}; + +max5864_driver_t max5864 = { + .bus = &spi_bus_ssp1, + .target_init = max5864_target_init, +}; + +ssp_config_t ssp_config_w25q80bv = { + .data_bits = SSP_DATA_8BITS, + .serial_clock_rate = 2, + .clock_prescale_rate = 2, +}; + +spi_bus_t spi_bus_ssp0 = { + .obj = (void*) SSP0_BASE, + .config = &ssp_config_w25q80bv, + .start = spi_ssp_start, + .stop = spi_ssp_stop, + .transfer = spi_ssp_transfer, + .transfer_gather = spi_ssp_transfer_gather, +}; + +w25q80bv_driver_t spi_flash = { + .bus = &spi_bus_ssp0, + .target_init = w25q80bv_target_init, +}; + +sgpio_config_t sgpio_config = { + .slice_mode_multislice = true, +}; + +#ifdef IS_PRALINE +ssp_config_t ssp_config_ice40_fpga = { + .data_bits = SSP_DATA_8BITS, + .spi_mode = SSP_CPOL_1_CPHA_1, + .serial_clock_rate = 21, + .clock_prescale_rate = 2, +}; + +ice40_spi_driver_t ice40 = { + .bus = &spi_bus_ssp1, +}; + +fpga_driver_t fpga = { + .bus = &ice40, +}; +#endif + +radio_t radio = { + .sample_rate_cb = sample_rate_set, +}; + +rf_path_t rf_path = { + .switchctrl = 0, +}; + +jtag_gpio_t jtag_gpio_cpld = {}; + +jtag_t jtag_cpld = { + .gpio = &jtag_gpio_cpld, +}; + +void ssp1_set_mode_max283x(void) +{ + spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); +} + +void ssp1_set_mode_max5864(void) +{ + spi_bus_start(max5864.bus, &ssp_config_max5864); +} + +#ifdef IS_PRALINE +void ssp1_set_mode_ice40(void) +{ + spi_bus_start(&spi_bus_ssp1, &ssp_config_ice40_fpga); +} +#endif diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h new file mode 100644 index 00000000..6aa45f87 --- /dev/null +++ b/firmware/common/drivers.h @@ -0,0 +1,77 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cpld_jtag.h" +#include "i2c_bus.h" +#include "i2c_lpc.h" +#include "max283x.h" +#include "max5864.h" +#include "mixer.h" +#include "platform_detect.h" // IWYU pragma: keep +#include "radio.h" +#include "rf_path.h" +#include "sgpio.h" +#include "si5351c.h" +#include "spi_ssp.h" +#include "w25q80bv.h" +#if defined(IS_PRALINE) + #include "fpga.h" + #include "ice40_spi.h" +#endif + +/* TODO: Hide these configurations */ +extern const i2c_lpc_config_t i2c_config_si5351c_fast_clock; +extern si5351c_driver_t clock_gen; +extern ssp_config_t ssp_config_max283x; +extern ssp_config_t ssp_config_max5864; +extern ssp_config_t ssp_config_w25q80bv; + +#if defined(IS_PRALINE) +extern ssp_config_t ssp_config_ice40_fpga; +extern ice40_spi_driver_t ice40; +extern fpga_driver_t fpga; +#endif +extern max283x_driver_t max283x; +extern max5864_driver_t max5864; +extern mixer_driver_t mixer; +extern w25q80bv_driver_t spi_flash; +extern sgpio_config_t sgpio_config; +extern radio_t radio; +extern rf_path_t rf_path; +extern jtag_gpio_t jtag_gpio_cpld; +extern jtag_t jtag_cpld; +extern i2c_bus_t i2c0; + +void ssp1_set_mode_max283x(void); +void ssp1_set_mode_max5864(void); +#if defined(IS_PRALINE) +void ssp1_set_mode_ice40(void); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/firmware/common/fpga.c b/firmware/common/fpga.c index 9c4d55f2..172810a8 100644 --- a/firmware/common/fpga.c +++ b/firmware/common/fpga.c @@ -20,11 +20,11 @@ */ #include "fpga.h" +#include "fpga_regs.def" #include -#include "fpga_regs.def" -#include "hackrf_core.h" +#include "drivers.h" #include "ice40_spi.h" /* Set up all registers according to the loaded bitstream's defaults. */ diff --git a/firmware/common/fpga_image.c b/firmware/common/fpga_image.c index dc88556b..989d16b2 100644 --- a/firmware/common/fpga_image.c +++ b/firmware/common/fpga_image.c @@ -23,8 +23,8 @@ #include #include +#include "drivers.h" #include "fpga.h" -#include "hackrf_core.h" #include "ice40_spi.h" #include "lz4_blk.h" #include "selftest.h" diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index fe0a96f9..30e70ea2 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -25,9 +25,9 @@ #include "clock_gen.h" #include "delay.h" +#include "drivers.h" #include "fixed_point.h" #include "fpga.h" -#include "hackrf_core.h" #include "ice40_spi.h" #include "m0_state.h" #include "max283x.h" diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 086e3961..2a7bc376 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -21,167 +21,23 @@ * Boston, MA 02110-1301, USA. */ -#include +#include "hackrf_core.h" -#include #include #include -#include "platform_detect.h" - -#include "clock_gen.h" +#include "drivers.h" #include "gpio.h" -#include "hackrf_core.h" -#include "i2c_lpc.h" #include "leds.h" -#include "max283x.h" -#include "max5864_target.h" +#include "mixer.h" +#include "platform_detect.h" #include "platform_gpio.h" #include "platform_scu.h" #include "power.h" -#include "spi_bus.h" -#include "w25q80bv_target.h" -#ifdef IS_PRALINE +#include "rf_path.h" +#include "sgpio.h" +#if defined(IS_PRALINE) #include "clock_io.h" - #include "ice40_spi.h" -#endif - -i2c_bus_t i2c0 = { - .obj = (void*) I2C0_BASE, - .start = i2c_lpc_start, - .stop = i2c_lpc_stop, - .transfer = i2c_lpc_transfer, -}; - -i2c_bus_t i2c1 = { - .obj = (void*) I2C1_BASE, - .start = i2c_lpc_start, - .stop = i2c_lpc_stop, - .transfer = i2c_lpc_transfer, -}; - -// const i2c_lpc_config_t i2c_config_si5351c_slow_clock = { -// .duty_cycle_count = 15, -// }; - -const i2c_lpc_config_t i2c_config_si5351c_fast_clock = { - .duty_cycle_count = 255, -}; - -si5351c_driver_t clock_gen = { - .bus = &i2c0, - .i2c_address = 0x60, -}; - -static ssp_config_t ssp_config_max283x = { - /* FIXME speed up once everything is working reliably */ - /* - // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - const uint8_t serial_clock_rate = 32; - const uint8_t clock_prescale_rate = 128; - */ - // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - .serial_clock_rate = 21, - .clock_prescale_rate = 2, -}; - -max283x_driver_t max283x = {}; - -static ssp_config_t ssp_config_max5864 = { - /* FIXME speed up once everything is working reliably */ - /* - // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - const uint8_t serial_clock_rate = 32; - const uint8_t clock_prescale_rate = 128; - */ - // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - .data_bits = SSP_DATA_8BITS, - .serial_clock_rate = 21, - .clock_prescale_rate = 2, -}; - -spi_bus_t spi_bus_ssp1 = { - .obj = (void*) SSP1_BASE, - .config = &ssp_config_max5864, - .start = spi_ssp_start, - .stop = spi_ssp_stop, - .transfer = spi_ssp_transfer, - .transfer_gather = spi_ssp_transfer_gather, -}; - -max5864_driver_t max5864 = { - .bus = &spi_bus_ssp1, - .target_init = max5864_target_init, -}; - -ssp_config_t ssp_config_w25q80bv = { - .data_bits = SSP_DATA_8BITS, - .serial_clock_rate = 2, - .clock_prescale_rate = 2, -}; - -static spi_bus_t spi_bus_ssp0 = { - .obj = (void*) SSP0_BASE, - .config = &ssp_config_w25q80bv, - .start = spi_ssp_start, - .stop = spi_ssp_stop, - .transfer = spi_ssp_transfer, - .transfer_gather = spi_ssp_transfer_gather, -}; - -w25q80bv_driver_t spi_flash = { - .bus = &spi_bus_ssp0, - .target_init = w25q80bv_target_init, -}; - -sgpio_config_t sgpio_config = { - .slice_mode_multislice = true, -}; - -#ifdef IS_PRALINE -static ssp_config_t ssp_config_ice40_fpga = { - .data_bits = SSP_DATA_8BITS, - .spi_mode = SSP_CPOL_1_CPHA_1, - .serial_clock_rate = 21, - .clock_prescale_rate = 2, -}; - -ice40_spi_driver_t ice40 = { - .bus = &spi_bus_ssp1, -}; - -fpga_driver_t fpga = { - .bus = &ice40, -}; -#endif - -radio_t radio = { - .sample_rate_cb = sample_rate_set, -}; - -rf_path_t rf_path; - -jtag_gpio_t jtag_gpio_cpld; - -jtag_t jtag_cpld = { - .gpio = &jtag_gpio_cpld, -}; - -void ssp1_set_mode_max283x(void) -{ - spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); -} - -void ssp1_set_mode_max5864(void) -{ - spi_bus_start(max5864.bus, &ssp_config_max5864); -} - -#ifdef IS_PRALINE -void ssp1_set_mode_ice40(void) -{ - spi_bus_start(&spi_bus_ssp1, &ssp_config_ice40_fpga); -} #endif void pin_shutdown(void) diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 6e0c1cc0..20ae76f0 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -27,50 +27,6 @@ extern "C" { #endif -#include "cpld_jtag.h" -#include "i2c_bus.h" -#include "i2c_lpc.h" -#include "max283x.h" -#include "max5864.h" -#include "mixer.h" -#include "platform_detect.h" // IWYU pragma: keep -#include "radio.h" -#include "rf_path.h" -#include "sgpio.h" -#include "si5351c.h" -#include "spi_ssp.h" -#include "w25q80bv.h" -#ifdef IS_PRALINE - #include "fpga.h" - #include "ice40_spi.h" -#endif - -/* TODO: Hide these configurations */ -extern const i2c_lpc_config_t i2c_config_si5351c_fast_clock; -extern si5351c_driver_t clock_gen; -extern ssp_config_t ssp_config_w25q80bv; - -extern max283x_driver_t max283x; -#ifdef IS_PRALINE -extern ice40_spi_driver_t ice40; -extern fpga_driver_t fpga; -#endif -extern max283x_driver_t max283x; -extern max5864_driver_t max5864; -extern mixer_driver_t mixer; -extern w25q80bv_driver_t spi_flash; -extern sgpio_config_t sgpio_config; -extern radio_t radio; -extern rf_path_t rf_path; -extern jtag_t jtag_cpld; -extern i2c_bus_t i2c0; - -void ssp1_set_mode_max283x(void); -void ssp1_set_mode_max5864(void); -#ifdef IS_PRALINE -void ssp1_set_mode_ice40(void); -#endif - void pin_shutdown(void); void pin_setup(void); diff --git a/firmware/common/operacake.c b/firmware/common/operacake.c index 61d78512..24ca6541 100644 --- a/firmware/common/operacake.c +++ b/firmware/common/operacake.c @@ -26,9 +26,9 @@ #include +#include "drivers.h" #include "gpio.h" #include "gpio_lpc.h" -#include "hackrf_core.h" #include "i2c_bus.h" #include "operacake_sctimer.h" #include "platform_scu.h" diff --git a/firmware/common/portapack.c b/firmware/common/portapack.c index 455df871..f25a4a6e 100644 --- a/firmware/common/portapack.c +++ b/firmware/common/portapack.c @@ -26,9 +26,9 @@ #include "cpld_jtag.h" #include "delay.h" +#include "drivers.h" #include "gpio.h" #include "gpio_lpc.h" -#include "hackrf_core.h" #include "platform_gpio.h" #include "platform_scu.h" diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 65412c4b..2d3ce8c3 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -26,10 +26,10 @@ #include #include "clock_io.h" +#include "drivers.h" #include "fixed_point.h" -#include "hackrf_core.h" -#include "mixer.h" #include "max283x.h" +#include "mixer.h" #include "platform_detect.h" #include "rf_path.h" #include "transceiver_mode.h" diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index 6cadea1c..0506a3bf 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -23,7 +23,7 @@ #include "rf_path.h" -#include "hackrf_core.h" +#include "drivers.h" #include "max283x.h" #include "max5864.h" #include "mixer.h" diff --git a/firmware/common/rom_iap.c b/firmware/common/rom_iap.c index a8f185ad..09da0b7c 100644 --- a/firmware/common/rom_iap.c +++ b/firmware/common/rom_iap.c @@ -22,7 +22,7 @@ #include -#include "hackrf_core.h" +#include "drivers.h" #include "rom_iap.h" #include "spi_bus.h" #include "w25q80bv.h" diff --git a/firmware/common/ui_rad1o.c b/firmware/common/ui_rad1o.c index 878e9e74..757e11f1 100644 --- a/firmware/common/ui_rad1o.c +++ b/firmware/common/ui_rad1o.c @@ -27,7 +27,7 @@ #include #include "clock_gen.h" -#include "hackrf_core.h" +#include "drivers.h" #include "rf_path.h" #include "transceiver_mode.h" #ifdef IS_NOT_RAD1O diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index 6384acdc..664f23f1 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -168,6 +168,7 @@ endmacro() macro(DeclareTargets) SET(SRC_M4 ${SRC_M4} + ${PATH_HACKRF_FIRMWARE_COMMON}/drivers.c ${PATH_HACKRF_FIRMWARE_COMMON}/power.c ${PATH_HACKRF_FIRMWARE_COMMON}/cpu_clock.c ${PATH_HACKRF_FIRMWARE_COMMON}/delay.c diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index bd2c5964..982a84bf 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_cpld.c b/firmware/hackrf_usb/usb_api_cpld.c index 7381601d..ed2beeda 100644 --- a/firmware/hackrf_usb/usb_api_cpld.c +++ b/firmware/hackrf_usb/usb_api_cpld.c @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index 632ce0e5..58c1684c 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_spiflash.c b/firmware/hackrf_usb/usb_api_spiflash.c index d422836e..2c944a10 100644 --- a/firmware/hackrf_usb/usb_api_spiflash.c +++ b/firmware/hackrf_usb/usb_api_spiflash.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index 4f7d8e57..ae3b399d 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -25,8 +25,8 @@ #include +#include #include -#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index 153b760b..b6c6782b 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -27,8 +27,8 @@ #include #include +#include #include -#include #include #include #include From 679179eb63976260389b98d958550608a52471fa Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Fri, 24 Apr 2026 09:03:47 +0200 Subject: [PATCH 078/173] firmware: rename hackrf_core.x to pins.x --- firmware/blinky/blinky.c | 4 ++-- firmware/common/{hackrf_core.c => pins.c} | 8 ++++---- firmware/common/{hackrf_core.h => pins.h} | 6 ++++-- firmware/common/rf_path.c | 2 +- firmware/hackrf-common.cmake | 2 +- firmware/hackrf_usb/hackrf_usb.c | 6 +++--- firmware/hackrf_usb/usb_api_board_info.c | 4 ++-- 7 files changed, 17 insertions(+), 15 deletions(-) rename firmware/common/{hackrf_core.c => pins.c} (98%) rename firmware/common/{hackrf_core.h => pins.h} (91%) diff --git a/firmware/blinky/blinky.c b/firmware/blinky/blinky.c index 0bf89bf8..fdaf3ef7 100644 --- a/firmware/blinky/blinky.c +++ b/firmware/blinky/blinky.c @@ -20,15 +20,15 @@ */ #include "delay.h" -#include "hackrf_core.h" #include "leds.h" +#include "pins.h" #include "platform_detect.h" #include "power.h" int main(void) { detect_hardware_platform(); - pin_setup(); + pins_setup(); #ifndef PRALINE /* enable 1V8 power supply so that the 1V8 LED lights up */ diff --git a/firmware/common/hackrf_core.c b/firmware/common/pins.c similarity index 98% rename from firmware/common/hackrf_core.c rename to firmware/common/pins.c index 2a7bc376..35b77838 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/pins.c @@ -21,7 +21,7 @@ * Boston, MA 02110-1301, USA. */ -#include "hackrf_core.h" +#include "pins.h" #include #include @@ -40,7 +40,7 @@ #include "clock_io.h" #endif -void pin_shutdown(void) +void pins_shutdown(void) { /* Configure all GPIO as Input (safe state) */ gpio_init(); @@ -209,8 +209,8 @@ void pin_shutdown(void) SCU_SFSI2C0 = SCU_I2C0_NOMINAL; } -/* Run after pin_shutdown() and prior to enabling power supplies. */ -void pin_setup(void) +/* Run after pins_shutdown() and prior to enabling power supplies. */ +void pins_setup(void) { /* Detect Platform */ const platform_gpio_t* gpio = platform_gpio(); diff --git a/firmware/common/hackrf_core.h b/firmware/common/pins.h similarity index 91% rename from firmware/common/hackrf_core.h rename to firmware/common/pins.h index 20ae76f0..b9454d18 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/pins.h @@ -27,8 +27,10 @@ extern "C" { #endif -void pin_shutdown(void); -void pin_setup(void); +#include "platform_detect.h" // IWYU pragma: keep + +void pins_shutdown(void); +void pins_setup(void); #ifdef __cplusplus } diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index 0506a3bf..ea1c0d45 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -319,7 +319,7 @@ static void switchctrl_set(rf_path_t* const rf_path, const uint8_t gpo) void rf_path_pin_setup(rf_path_t* const rf_path) { -#ifdef JAWBREAKER +#ifdef IS_JAWBREAKER (void) rf_path; #else const platform_scu_t* scu = platform_scu(); diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index 664f23f1..04d8b1a3 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -172,7 +172,7 @@ macro(DeclareTargets) ${PATH_HACKRF_FIRMWARE_COMMON}/power.c ${PATH_HACKRF_FIRMWARE_COMMON}/cpu_clock.c ${PATH_HACKRF_FIRMWARE_COMMON}/delay.c - ${PATH_HACKRF_FIRMWARE_COMMON}/hackrf_core.c + ${PATH_HACKRF_FIRMWARE_COMMON}/pins.c ${PATH_HACKRF_FIRMWARE_COMMON}/leds.c ${PATH_HACKRF_FIRMWARE_COMMON}/sgpio.c ${PATH_HACKRF_FIRMWARE_COMMON}/rf_path.c diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 982a84bf..54431ff3 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -35,10 +35,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -444,12 +444,12 @@ int main(void) detect_hardware_platform(); board_id_t board_id = detected_platform(); - pin_shutdown(); + pins_shutdown(); if (board_id != BOARD_ID_RAD1O) { clock_gen_shutdown(); } delay_us_at_mhz(10000, 96); - pin_setup(); + pins_setup(); #ifdef IS_PRALINE if (IS_PRALINE) { enable_3v3aux_power(); diff --git a/firmware/hackrf_usb/usb_api_board_info.c b/firmware/hackrf_usb/usb_api_board_info.c index c4634605..2d017378 100644 --- a/firmware/hackrf_usb/usb_api_board_info.c +++ b/firmware/hackrf_usb/usb_api_board_info.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include #include @@ -129,7 +129,7 @@ usb_request_status_t usb_vendor_request_reset( const usb_transfer_stage_t stage) { if (stage == USB_TRANSFER_STAGE_SETUP) { - pin_shutdown(); + pins_shutdown(); clock_gen_shutdown(); #ifdef IS_H1_R9 if (IS_H1_R9) { From db376ad1946b6f14b8c00bb5856d18869192f231 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Tue, 17 Mar 2026 17:08:49 -0400 Subject: [PATCH 079/173] Change Praline CLKIN default to P1_CLKIN Previously the CLKIN switch defaulted to P22_CLKIN, enabling usage of the clock input on pin header P22 but not on SMA port P1. This change enables use of clock input on P1 and disables P22_CLKIN by default. --- ci-scripts/hackrf_pro_test.py | 3 --- firmware/common/pins.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/ci-scripts/hackrf_pro_test.py b/ci-scripts/hackrf_pro_test.py index 3d0b1c63..44b6eb56 100644 --- a/ci-scripts/hackrf_pro_test.py +++ b/ci-scripts/hackrf_pro_test.py @@ -663,9 +663,6 @@ class HackRF: self.rf_test_cases = praline_r110_rf_test_cases def clkin(self): - if self.name == "EUT" and self.id == "5": - debug = subprocess.run([self.bin_dir + "/hackrf_debug", "-d", self.serial, - "-C", "0"]) command = subprocess.run([self.bin_dir + "/hackrf_clock", "-i", "-d", self.serial], capture_output=True, encoding="utf-8", timeout=TIMEOUT) diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 35b77838..6f8123cb 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -185,7 +185,7 @@ void pins_shutdown(void) p2_ctrl_set(P2_SIGNAL_CLK3); p1_ctrl_set(P1_SIGNAL_CLKIN); narrowband_filter_set(0); - clkin_ctrl_set(CLKIN_SIGNAL_P22); + clkin_ctrl_set(CLKIN_SIGNAL_P1); gpio_output(gpio->p2_ctrl0); gpio_output(gpio->p2_ctrl1); From 3e1542261a64b333d246b5e162ed88fe15caa28c Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Tue, 21 Apr 2026 10:21:40 -0400 Subject: [PATCH 080/173] Add CLKIN control option to hackrf_clock --- host/hackrf-tools/src/hackrf_clock.c | 37 ++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_clock.c b/host/hackrf-tools/src/hackrf_clock.c index 36f066c1..01c2c800 100644 --- a/host/hackrf-tools/src/hackrf_clock.c +++ b/host/hackrf-tools/src/hackrf_clock.c @@ -103,6 +103,19 @@ int parse_p2_ctrl_signal(char* s, enum p2_ctrl_signal* const signal) return HACKRF_SUCCESS; } +int parse_clkin_ctrl_signal(char* s, enum clkin_ctrl_signal* const signal) +{ + if (strcasecmp("p1", s) == 0) { + *signal = CLKIN_SIGNAL_P1; + } else if (strcasecmp("p22", s) == 0) { + *signal = CLKIN_SIGNAL_P22; + } else { + fprintf(stderr, "Invalid signal '%s'\n", s); + return HACKRF_ERROR_INVALID_PARAM; + } + return HACKRF_SUCCESS; +} + int si5351c_read_register(hackrf_device* device, const uint16_t register_number) { uint16_t register_value; @@ -295,6 +308,8 @@ static void usage() printf("\tone of: clkin, trigger_in, trigger_out, p22_clkin, pps_out, aux_clk1, aux_clk2, off\n"); printf("\t-2, --p2 : select the signal for the HackRF Pro P2 SMA connector (default: clkout)\n"); printf("\tone of: clkout, trigger_in, trigger_out\n"); + printf("\t-c, --clkin-ctrl : select the HackRF Pro CLKIN signal (default: p1)\n"); + printf("\tone of: p1, p22\n"); printf("\t-d, --device : Serial number of desired HackRF.\n"); printf("\nExamples:\n"); printf("\thackrf_clock -r 3 : prints settings for CLKOUT\n"); @@ -308,6 +323,7 @@ static struct option long_options[] = { {"clkout", required_argument, 0, 'o'}, {"p1", required_argument, 0, '1'}, {"p2", required_argument, 0, '2'}, + {"clkin-ctrl", required_argument, 0, 'c'}, {"device", required_argument, 0, 'd'}, {0, 0, 0, 0}, }; @@ -324,8 +340,10 @@ int main(int argc, char** argv) uint8_t clkin_status; bool p1_ctrl = false; bool p2_ctrl = false; + bool clkin_ctrl = false; enum p1_ctrl_signal p1_signal = P1_SIGNAL_CLKIN; enum p2_ctrl_signal p2_signal = P2_SIGNAL_CLK3; + enum clkin_ctrl_signal clkin_signal = CLKIN_SIGNAL_P1; const char* serial_number = NULL; int result = hackrf_init(); @@ -339,7 +357,7 @@ int main(int argc, char** argv) while ((opt = getopt_long( argc, argv, - "r:aio:1:2:d:h?", + "r:aio:1:2:c:d:h?", long_options, &option_index)) != EOF) { switch (opt) { @@ -371,6 +389,11 @@ int main(int argc, char** argv) result = parse_p2_ctrl_signal(optarg, &p2_signal); break; + case 'c': + clkin_ctrl = true; + result = parse_clkin_ctrl_signal(optarg, &clkin_signal); + break; + case 'd': serial_number = optarg; break; @@ -394,7 +417,7 @@ int main(int argc, char** argv) } } - if (!clkin && !clkout && !read && !p1_ctrl && !p2_ctrl) { + if (!clkin && !clkout && !read && !p1_ctrl && !p2_ctrl && !clkin_ctrl) { fprintf(stderr, "An operation must be specified.\n"); usage(); return EXIT_FAILURE; @@ -460,6 +483,16 @@ int main(int argc, char** argv) } } + if (clkin_ctrl) { + result = hackrf_set_clkin_ctrl(device, clkin_signal); + if (result != HACKRF_SUCCESS) { + printf("hackrf_set_clkin_ctrl() failed: %s (%d)\n", + hackrf_error_name(result), + result); + return EXIT_FAILURE; + } + } + result = hackrf_close(device); if (result) { printf("hackrf_close() failed: %s (%d)\n", From fd0c7cc5f1f416f8097ede8e03790845086a1dfa Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Tue, 21 Apr 2026 13:01:58 -0400 Subject: [PATCH 081/173] Document Praline P1, P2, and CLKIN control --- docs/source/external_clock_interface.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/source/external_clock_interface.rst b/docs/source/external_clock_interface.rst index fc3f3b1d..9c09ca66 100644 --- a/docs/source/external_clock_interface.rst +++ b/docs/source/external_clock_interface.rst @@ -9,12 +9,18 @@ HackRF Pro HackRF Pro has two configurable SMA ports, P1 and P2. By default, P1 is configured as CLKIN and P2 as CLKOUT. The default behaviour of these signals is as described for HackRF One below. +A second CLKIN signal is available on header P22 pin 2. Unlike HackRF One, HackRF Pro's P22_CLKIN is a separate signal from P1_CLKIN. To enable P22_CLKIN instead of P1_CLKIN use ``hackrf_clock -c p22``. + +Various internal signals can be connected to P1 or P2 instead of the default CLKIN and CLKOUT signals. Use ``hackrf_clock -1`` or ``hackrf_clock -2`` to select a different signal. + HackRF One ~~~~~~~~~~ -HackRF One produces a 10 MHz clock signal on CLKOUT. The signal is a 3.3 V, 10 MHz square wave intended for a high impedance load. +HackRF One produces a 10 MHz clock signal on the CLKOUT SMA port. The signal is a 3.3 V, 10 MHz square wave intended for a high impedance load. -The CLKIN port on HackRF One is a high impedance input that expects 3.3 V square wave at 10 MHz. Do not exceed 3.3 V or drop below 0 V on this input. Do not connect a clock signal at a frequency other than 10 MHz (unless you modify the firmware to support this). You may directly connect the CLKOUT port of one HackRF One to the CLKIN port of another HackRF One. +The CLKIN SMA port on HackRF One is a high impedance input that expects 3.3 V square wave at 10 MHz. Do not exceed 3.3 V or drop below 0 V on this input. Do not connect a clock signal at a frequency other than 10 MHz (unless you modify the firmware to support this). You may directly connect the CLKOUT port of one HackRF One to the CLKIN port of another HackRF. + +The CLKIN signal is also connected to header P22 pin 2. Unlike HackRF Pro, HackRF One has only one CLKIN signal shared between P22 pin 2 and the CLKIN port. Do not connect input signals to both CLKIN and P22 pin 2 simultaneously. HackRF One uses CLKIN instead of the internal crystal when a clock signal is detected on CLKIN. The switch to or from CLKIN only happens when a transmit or receive operation begins. From ba5fe7f7daa9ee487df8950134896a8df35a54cb Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 18 Oct 2025 23:08:12 +0100 Subject: [PATCH 082/173] Rename usb_bulk_buffer to usb_samp_buffer. --- firmware/common/LPC43xx_M4_memory.ld | 4 ++-- firmware/common/fpga_selftest.c | 20 +++++++++---------- firmware/hackrf_usb/usb_api_sweep.c | 4 ++-- firmware/hackrf_usb/usb_api_transceiver.c | 10 +++++----- .../{usb_bulk_buffer.h => usb_buffer.h} | 8 ++++---- 5 files changed, 23 insertions(+), 23 deletions(-) rename firmware/hackrf_usb/{usb_bulk_buffer.h => usb_buffer.h} (85%) diff --git a/firmware/common/LPC43xx_M4_memory.ld b/firmware/common/LPC43xx_M4_memory.ld index 933fa854..d37542f9 100644 --- a/firmware/common/LPC43xx_M4_memory.ld +++ b/firmware/common/LPC43xx_M4_memory.ld @@ -35,11 +35,11 @@ MEMORY * to get performance benefit of having two USB buffers addressable * simultaneously (on two different buses of the AHB multilayer matrix) */ - ram_usb (rwx) : ORIGIN = 0x20008000, LENGTH = 32K + ram_samp (rwx) : ORIGIN = 0x20008000, LENGTH = 32K } -usb_bulk_buffer = ORIGIN(ram_usb); lz4_in_buf = ORIGIN(ram_lz4_in); lz4_out_buf = ORIGIN(ram_lz4_out); +usb_samp_buffer = ORIGIN(ram_samp); m0_state = ORIGIN(ram_shared); PROVIDE(__ram_m0_start__ = ORIGIN(ram_m0)); diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index 30e70ea2..b23be525 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -37,8 +37,8 @@ #include "streaming.h" // USB buffer used during selftests. -#define USB_BULK_BUFFER_SIZE 0x8000 -extern uint8_t usb_bulk_buffer[USB_BULK_BUFFER_SIZE]; +#define USB_SAMP_BUFFER_SIZE 0x8000 +extern uint8_t usb_samp_buffer[USB_SAMP_BUFFER_SIZE]; static int rx_samples(const unsigned int num_samples, uint32_t max_cycles) { @@ -115,10 +115,10 @@ bool fpga_sgpio_selftest(void) fpga_set_prbs_enable(&fpga, false); // Generate sequence from first value and compare. - bool seq_in_sync = (usb_bulk_buffer[0] != 0); - uint8_t seq = lfsr_advance(usb_bulk_buffer[0]); + bool seq_in_sync = (usb_samp_buffer[0] != 0); + uint8_t seq = lfsr_advance(usb_samp_buffer[0]); for (int i = 1; i < 512; ++i) { - if (usb_bulk_buffer[i] != seq) { + if (usb_samp_buffer[i] != seq) { seq_in_sync = false; break; } @@ -189,7 +189,7 @@ bool fpga_if_xcvr_selftest(void) return false; } - const size_t num_samples = USB_BULK_BUFFER_SIZE / 2; + const size_t num_samples = USB_SAMP_BUFFER_SIZE / 2; // Set common RX path and gateware settings for the measurements. fpga_set_tx_nco_pstep(&fpga, 64); // NCO phase increment @@ -206,7 +206,7 @@ bool fpga_if_xcvr_selftest(void) timeout = true; } measure_tone( - (int8_t*) usb_bulk_buffer, + (int8_t*) usb_samp_buffer, num_samples, &selftest.xcvr_measurements[0]); @@ -217,7 +217,7 @@ bool fpga_if_xcvr_selftest(void) timeout = true; } measure_tone( - (int8_t*) usb_bulk_buffer, + (int8_t*) usb_samp_buffer, num_samples, &selftest.xcvr_measurements[1]); @@ -230,7 +230,7 @@ bool fpga_if_xcvr_selftest(void) timeout = true; } measure_tone( - (int8_t*) usb_bulk_buffer, + (int8_t*) usb_samp_buffer, num_samples, &selftest.xcvr_measurements[2]); @@ -241,7 +241,7 @@ bool fpga_if_xcvr_selftest(void) timeout = true; } measure_tone( - (int8_t*) usb_bulk_buffer, + (int8_t*) usb_samp_buffer, num_samples, &selftest.xcvr_measurements[3]); diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index ae3b399d..eee74228 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -40,7 +40,7 @@ #include "usb_api_sweep.h" #include "usb_api_transceiver.h" -#include "usb_bulk_buffer.h" +#include "usb_buffer.h" #include "usb_endpoint.h" #define MIN(x, y) ((x) < (y) ? (x) : (y)) @@ -207,7 +207,7 @@ void sweep_mode(uint32_t seq) m0_state.next_mode = M0_MODE_RX; // Write metadata to buffer. - buffer = &usb_bulk_buffer[phase * 0x4000]; + buffer = &usb_samp_buffer[phase * 0x4000]; *buffer = 0x7f; *(buffer + 1) = 0x7f; *(buffer + 2) = sweep_freq & 0xff; diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index b6c6782b..f272dac5 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -41,7 +41,7 @@ #include #include -#include "usb_bulk_buffer.h" +#include "usb_buffer.h" #include "usb_endpoint.h" #define USB_TRANSFER_SIZE 0x4000 @@ -442,7 +442,7 @@ void rx_mode(uint32_t seq) if ((m0_state.m0_count - usb_count) >= USB_TRANSFER_SIZE) { usb_transfer_schedule_block( &usb_endpoint_bulk_in, - &usb_bulk_buffer[usb_count & USB_BULK_BUFFER_MASK], + &usb_samp_buffer[usb_count & USB_SAMP_BUFFER_MASK], USB_TRANSFER_SIZE, transceiver_bulk_transfer_complete, NULL); @@ -464,14 +464,14 @@ void tx_mode(uint32_t seq) // Set up OUT transfer of buffer 0. usb_transfer_schedule_block( &usb_endpoint_bulk_out, - &usb_bulk_buffer[0x0000], + &usb_samp_buffer[0x0000], USB_TRANSFER_SIZE, transceiver_bulk_transfer_complete, NULL); usb_count += USB_TRANSFER_SIZE; while (transceiver_request.seq == seq) { - if (!started && (m0_state.m4_count == USB_BULK_BUFFER_SIZE)) { + if (!started && (m0_state.m4_count == USB_SAMP_BUFFER_SIZE)) { // Buffer is now full, start streaming. baseband_streaming_enable(&sgpio_config); started = true; @@ -479,7 +479,7 @@ void tx_mode(uint32_t seq) if ((usb_count - m0_state.m0_count) <= USB_TRANSFER_SIZE) { usb_transfer_schedule_block( &usb_endpoint_bulk_out, - &usb_bulk_buffer[usb_count & USB_BULK_BUFFER_MASK], + &usb_samp_buffer[usb_count & USB_SAMP_BUFFER_MASK], USB_TRANSFER_SIZE, transceiver_bulk_transfer_complete, NULL); diff --git a/firmware/hackrf_usb/usb_bulk_buffer.h b/firmware/hackrf_usb/usb_buffer.h similarity index 85% rename from firmware/hackrf_usb/usb_bulk_buffer.h rename to firmware/hackrf_usb/usb_buffer.h index e21a84ff..5aecdb67 100644 --- a/firmware/hackrf_usb/usb_bulk_buffer.h +++ b/firmware/hackrf_usb/usb_buffer.h @@ -25,11 +25,11 @@ #include -#define USB_BULK_BUFFER_SIZE 0x8000 -#define USB_BULK_BUFFER_MASK 0x7FFF +#define USB_SAMP_BUFFER_SIZE 0x8000 +#define USB_SAMP_BUFFER_MASK 0x7FFF -/* Address of usb_bulk_buffer is set in ldscripts. If you change the name of this +/* Address of usb_samp_buffer is set in ldscripts. If you change the name of this * variable, it won't be where it needs to be in the processor's address space, * unless you also adjust the ldscripts. */ -extern uint8_t usb_bulk_buffer[USB_BULK_BUFFER_SIZE]; +extern uint8_t usb_samp_buffer[USB_SAMP_BUFFER_SIZE]; From d222e0188e3bf0ecdcde15f30cc6fb0251ba8978 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sun, 19 Oct 2025 22:39:10 +0100 Subject: [PATCH 083/173] Add new usb_bulk_buffer. --- firmware/common/LPC4320_M4_memory.ld | 3 ++- firmware/common/LPC4330_M4_memory.ld | 3 ++- firmware/common/LPC43xx_M4_memory.ld | 1 + firmware/hackrf_usb/usb_buffer.h | 10 +++++++--- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/firmware/common/LPC4320_M4_memory.ld b/firmware/common/LPC4320_M4_memory.ld index a5997f32..48be94da 100644 --- a/firmware/common/LPC4320_M4_memory.ld +++ b/firmware/common/LPC4320_M4_memory.ld @@ -26,7 +26,8 @@ MEMORY { /* rom is really the shadow region that points to SPI flash or elsewhere */ rom (rx) : ORIGIN = 0x00000000, LENGTH = 1M - ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 96K + ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 64K + ram_usb (rw) : ORIGIN = 0x10010000, LENGTH = 32K ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 32K ram_sleep (rwx) : ORIGIN = 0x10088000, LENGTH = 8K } diff --git a/firmware/common/LPC4330_M4_memory.ld b/firmware/common/LPC4330_M4_memory.ld index 1e886d3a..1d42ebee 100644 --- a/firmware/common/LPC4330_M4_memory.ld +++ b/firmware/common/LPC4330_M4_memory.ld @@ -26,7 +26,8 @@ MEMORY { /* rom is really the shadow region that points to SPI flash or elsewhere */ rom (rx) : ORIGIN = 0x00000000, LENGTH = 128K - ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 128K + ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 96K + ram_usb(rw) : ORIGIN = 0x10018000, LENGTH = 32K ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 64K ram_sleep (rwx) : ORIGIN = 0x10090000, LENGTH = 8K } diff --git a/firmware/common/LPC43xx_M4_memory.ld b/firmware/common/LPC43xx_M4_memory.ld index d37542f9..fc7dcee7 100644 --- a/firmware/common/LPC43xx_M4_memory.ld +++ b/firmware/common/LPC43xx_M4_memory.ld @@ -41,5 +41,6 @@ MEMORY lz4_in_buf = ORIGIN(ram_lz4_in); lz4_out_buf = ORIGIN(ram_lz4_out); usb_samp_buffer = ORIGIN(ram_samp); +usb_bulk_buffer = ORIGIN(ram_usb); m0_state = ORIGIN(ram_shared); PROVIDE(__ram_m0_start__ = ORIGIN(ram_m0)); diff --git a/firmware/hackrf_usb/usb_buffer.h b/firmware/hackrf_usb/usb_buffer.h index 5aecdb67..07543025 100644 --- a/firmware/hackrf_usb/usb_buffer.h +++ b/firmware/hackrf_usb/usb_buffer.h @@ -28,8 +28,12 @@ #define USB_SAMP_BUFFER_SIZE 0x8000 #define USB_SAMP_BUFFER_MASK 0x7FFF -/* Address of usb_samp_buffer is set in ldscripts. If you change the name of this - * variable, it won't be where it needs to be in the processor's address space, - * unless you also adjust the ldscripts. +#define USB_BULK_BUFFER_SIZE 0x8000 +#define USB_BULK_BUFFER_MASK 0x7FFF + +/* Addresses of usb_samp_buffer and usb_bulk_buffer are set in ldscripts. If + * you change the name of these variables, they won't be where they need to + * be in the processor's address space, unless you also adjust the ldscripts. */ extern uint8_t usb_samp_buffer[USB_SAMP_BUFFER_SIZE]; +extern uint8_t usb_bulk_buffer[USB_BULK_BUFFER_SIZE]; From 40a8dc53dc9f0f7f858c911eac47186f16127ce8 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sun, 19 Oct 2025 22:39:23 +0100 Subject: [PATCH 084/173] Use both buffers, with memcpy in place of DMA for now. --- firmware/hackrf_usb/usb_api_transceiver.c | 179 +++++++++++++++++----- 1 file changed, 142 insertions(+), 37 deletions(-) diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index f272dac5..148741eb 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -45,6 +46,11 @@ #include "usb_endpoint.h" #define USB_TRANSFER_SIZE 0x4000 +#define DMA_TRANSFER_SIZE 0x4000 + +#define BUF_HALF_MASK (USB_SAMP_BUFFER_SIZE >> 1) + +volatile uint32_t dma_started, dma_pending, usb_started, usb_completed; typedef struct { uint32_t freq_mhz; @@ -68,6 +74,8 @@ typedef struct { set_sample_r_params_t set_sample_r_params; +void dma_isr(void); + usb_request_status_t usb_vendor_request_set_baseband_filter_bandwidth( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) @@ -340,6 +348,11 @@ void transceiver_shutdown(void) void transceiver_startup(const transceiver_mode_t mode) { + dma_started = 0; + dma_pending = 0; + usb_started = 0; + usb_completed = 0; + radio_switch_opmode(&radio, mode); switch (mode) { @@ -424,30 +437,115 @@ usb_request_status_t usb_vendor_request_set_rx_overrun_limit( return USB_REQUEST_STATUS_OK; } +void transceiver_start_dma(void* src, void* dest, size_t size) +{ + dma_pending = size; + memcpy(dest, src, size); + dma_isr(); +} + +void dma_isr(void) +{ + m0_state.m4_count += dma_pending; + dma_pending = 0; +} + void transceiver_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) { (void) user_data; - m0_state.m4_count += bytes_transferred; + usb_completed += bytes_transferred; +} + +typedef enum { + DIRECTION_RX, + DIRECTION_TX, +} direction_t; + +void start_dma_if_possible(direction_t direction, size_t size) +{ + if (dma_pending) { + return; + } + + uint32_t sampling_completed = m0_state.m0_count; + uint32_t dma_completed = m0_state.m4_count; + uint32_t samp_offset = dma_started & USB_SAMP_BUFFER_MASK; + uint32_t bulk_offset = dma_started & USB_BULK_BUFFER_MASK; + uint32_t data_available, space_in_use, space_available, samp_buf_margin; + uint8_t *dest, *src; + + if (direction == DIRECTION_RX) { + data_available = sampling_completed - dma_started; + space_in_use = usb_completed - dma_completed; + space_available = USB_BULK_BUFFER_SIZE - space_in_use; + samp_buf_margin = USB_SAMP_BUFFER_SIZE - data_available; + src = &usb_samp_buffer[samp_offset]; + dest = &usb_bulk_buffer[bulk_offset]; + } else { + data_available = usb_completed - dma_started; + space_in_use = dma_completed - sampling_completed; + space_available = USB_SAMP_BUFFER_SIZE - space_in_use; + samp_buf_margin = space_in_use; + src = &usb_bulk_buffer[bulk_offset]; + dest = &usb_samp_buffer[samp_offset]; + } + + if (data_available < size || size > space_available) { + return; + } + + uint32_t m0_buf_half = sampling_completed & BUF_HALF_MASK; + uint32_t dma_buf_half = dma_started & BUF_HALF_MASK; + bool same_buf_half = m0_buf_half == dma_buf_half; + + if (same_buf_half && samp_buf_margin >= (USB_SAMP_BUFFER_SIZE / 2)) { + return; + } + + transceiver_start_dma(src, dest, size); + + dma_started += size; +} + +void start_usb_if_possible(direction_t direction) +{ + uint32_t bulk_offset = usb_started & USB_BULK_BUFFER_MASK; + uint32_t dma_completed = m0_state.m4_count; + uint32_t bytes_available; + usb_endpoint_t* usb_endpoint; + + if (direction == DIRECTION_RX) { + bytes_available = dma_completed - usb_started; + usb_endpoint = &usb_endpoint_bulk_in; + } else { + uint32_t space_used = usb_started - dma_completed; + bytes_available = USB_BULK_BUFFER_SIZE - space_used; + usb_endpoint = &usb_endpoint_bulk_out; + } + + if (bytes_available < USB_TRANSFER_SIZE) { + return; + } + + usb_transfer_schedule_block( + usb_endpoint, + &usb_bulk_buffer[bulk_offset], + USB_TRANSFER_SIZE, + transceiver_bulk_transfer_complete, + NULL); + + usb_started += USB_TRANSFER_SIZE; } void rx_mode(uint32_t seq) { - uint32_t usb_count = 0; - transceiver_startup(TRANSCEIVER_MODE_RX); baseband_streaming_enable(&sgpio_config); while (transceiver_request.seq == seq) { - if ((m0_state.m0_count - usb_count) >= USB_TRANSFER_SIZE) { - usb_transfer_schedule_block( - &usb_endpoint_bulk_in, - &usb_samp_buffer[usb_count & USB_SAMP_BUFFER_MASK], - USB_TRANSFER_SIZE, - transceiver_bulk_transfer_complete, - NULL); - usb_count += USB_TRANSFER_SIZE; - } + start_dma_if_possible(DIRECTION_RX, DMA_TRANSFER_SIZE); + start_usb_if_possible(DIRECTION_RX); radio_update(&radio); } @@ -456,35 +554,42 @@ void rx_mode(uint32_t seq) void tx_mode(uint32_t seq) { - unsigned int usb_count = 0; - bool started = false; - transceiver_startup(TRANSCEIVER_MODE_TX); - // Set up OUT transfer of buffer 0. - usb_transfer_schedule_block( - &usb_endpoint_bulk_out, - &usb_samp_buffer[0x0000], - USB_TRANSFER_SIZE, - transceiver_bulk_transfer_complete, - NULL); - usb_count += USB_TRANSFER_SIZE; + // First, make transfers directly into the sample buffer to fill it. + for (int i = 0; i < (USB_SAMP_BUFFER_SIZE / USB_TRANSFER_SIZE); i++) { + // Set up transfer. + usb_transfer_schedule_block( + &usb_endpoint_bulk_out, + &usb_samp_buffer[usb_started], + USB_TRANSFER_SIZE, + transceiver_bulk_transfer_complete, + NULL); + usb_started += USB_TRANSFER_SIZE; + // Wait for the transfer to complete. + while (usb_completed < usb_started) { + // Handle the host switching modes before filling the buffer. + if (transceiver_request.seq != seq) { + transceiver_shutdown(); + return; + } + + radio_update(&radio); + } + } + + // Sample buffer is now full. Update DMA counters accordingly. + dma_started = USB_SAMP_BUFFER_SIZE; + m0_state.m4_count = USB_SAMP_BUFFER_SIZE; + + // Start transmitting samples. + baseband_streaming_enable(&sgpio_config); + + // Continue feeding samples to the sample buffer. while (transceiver_request.seq == seq) { - if (!started && (m0_state.m4_count == USB_SAMP_BUFFER_SIZE)) { - // Buffer is now full, start streaming. - baseband_streaming_enable(&sgpio_config); - started = true; - } - if ((usb_count - m0_state.m0_count) <= USB_TRANSFER_SIZE) { - usb_transfer_schedule_block( - &usb_endpoint_bulk_out, - &usb_samp_buffer[usb_count & USB_SAMP_BUFFER_MASK], - USB_TRANSFER_SIZE, - transceiver_bulk_transfer_complete, - NULL); - usb_count += USB_TRANSFER_SIZE; - } + start_dma_if_possible(DIRECTION_TX, DMA_TRANSFER_SIZE); + start_usb_if_possible(DIRECTION_TX); radio_update(&radio); } From 0fb7b1c8c0ecb7854fa2d8a5b45bda05b8a84040 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sun, 19 Oct 2025 19:40:28 +0100 Subject: [PATCH 085/173] Reduce memory transfer size to 8KB. --- firmware/hackrf_usb/usb_api_transceiver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index 148741eb..32efbc71 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -46,7 +46,7 @@ #include "usb_endpoint.h" #define USB_TRANSFER_SIZE 0x4000 -#define DMA_TRANSFER_SIZE 0x4000 +#define DMA_TRANSFER_SIZE 0x2000 #define BUF_HALF_MASK (USB_SAMP_BUFFER_SIZE >> 1) From cebf0c7ce1ef4d2c1699408fc7723d883f2886e1 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 1 Dec 2025 23:24:34 +0000 Subject: [PATCH 086/173] Flush both buffers before leaving TX mode. --- firmware/common/usb.h | 2 + firmware/hackrf_usb/usb_api_transceiver.c | 58 +++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/firmware/common/usb.h b/firmware/common/usb.h index 9445728a..2b8c6c8c 100644 --- a/firmware/common/usb.h +++ b/firmware/common/usb.h @@ -31,6 +31,8 @@ #include "usb_type.h" +usb_queue_head_t* usb_queue_head(const uint_fast8_t endpoint_address); + void usb_peripheral_reset(void); void usb_phy_enable(void); diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index 32efbc71..2112fe07 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -27,6 +27,8 @@ #include #include +#include + #include #include #include @@ -593,6 +595,62 @@ void tx_mode(uint32_t seq) radio_update(&radio); } + // Host has now requested to stop TX. We won't initiate any further USB + // transfers into the bulk buffer. However, we should make sure all + // data currently in the USB bulk buffer reaches the sample buffer. + + if ((usb_started - usb_completed) > 0) { + // We were part way through a 16KB firmware-side transfer when + // the transceiver mode change request to stop TX was received. + // + // We want to include the contents of that partial transfer in + // the data we move to the sample buffer. + // + // The transfer was already stopped by usb_endpoint_flush(), + // which was called from request_transceiver_mode(). + // + // We will not have had a callback, and the transfer descriptor + // (dTD) will not have been updated, since the transfer did not + // complete. + // + // However, as long as we haven't started a new transfer, we + // can retrieve the partial byte count from the transfer + // overlay in the endpoint queue head (dQH) (UM10503 25.9.1). + + usb_queue_head_t* const qh = + usb_queue_head(usb_endpoint_bulk_out.address); + unsigned int bytes_remaining = + (qh->total_bytes & USB_TD_DTD_TOKEN_TOTAL_BYTES_MASK) >> + USB_TD_DTD_TOKEN_TOTAL_BYTES_SHIFT; + unsigned int bytes_transferred = USB_TRANSFER_SIZE - bytes_remaining; + usb_completed += bytes_transferred; + } + + // Feed the remaining data from the bulk buffer to the sample buffer. + // At this point, we also need to handle the case where there is less data + // to be transferred to the sample buffer than a full-sized DMA transfer. + + // Any remainder of less than 4 bytes will be ignored; this is the chunk + // size of our DMA transfers. + while ((usb_completed - m0_state.m4_count) >= 4) { + uint32_t data_available = usb_completed - dma_started; + if (data_available > DMA_TRANSFER_SIZE) { + start_dma_if_possible(DIRECTION_TX, DMA_TRANSFER_SIZE); + } else { + start_dma_if_possible(DIRECTION_TX, data_available); + } + radio_update(&radio); + } + + // Wait for the data in the sample buffer to be transmitted. + + // Any remainder of less than 32 bytes will be ignored; this is + // the chunk size used by the M0 core to transfer samples to SGPIO. + while ((m0_state.m4_count - m0_state.m0_count) >= 32) { + radio_update(&radio); + } + + // All data received from the host has now been transmitted. transceiver_shutdown(); } From ebe5a5acdffbe8e2ec8a3a273bf4b7e87317ee25 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 8 Dec 2025 16:05:09 +0000 Subject: [PATCH 087/173] Read USB API version in hackrf_open() and cache it. --- host/libhackrf/src/hackrf.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 48a25868..14c34ab2 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -143,6 +143,7 @@ typedef enum { struct hackrf_device { libusb_device_handle* usb_device; + uint16_t usb_api_version; struct libusb_transfer** transfers; hackrf_sample_block_cb_fn callback; volatile bool @@ -187,10 +188,8 @@ static const max2837_ft_t max2837_ft[] = { {28000000}, {0}}; -#define USB_API_REQUIRED(device, version) \ - uint16_t usb_version = 0; \ - hackrf_usb_api_version_read(device, &usb_version); \ - if (usb_version < version) \ +#define USB_API_REQUIRED(device, version) \ + if (device->usb_api_version < version) \ return HACKRF_ERROR_USB_API_VERSION; static const uint16_t hackrf_usb_vid = 0x1d50; @@ -713,6 +712,13 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d { int result; hackrf_device* lib_device; + struct libusb_device_descriptor device_descriptor; + libusb_device* dev = libusb_get_device(usb_device); + result = libusb_get_device_descriptor(dev, &device_descriptor); + if (result < 0) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } //int speed = libusb_get_device_speed(usb_device); // TODO: Error or warning if not high speed USB? @@ -746,6 +752,7 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d #endif lib_device->usb_device = usb_device; + lib_device->usb_api_version = device_descriptor.bcdDevice; lib_device->transfers = NULL; lib_device->callback = NULL; lib_device->transfer_thread_started = false; @@ -1700,17 +1707,7 @@ extern ADDAPI int ADDCALL hackrf_usb_api_version_read( hackrf_device* device, uint16_t* version) { - int result; - libusb_device* dev; - struct libusb_device_descriptor desc; - dev = libusb_get_device(device->usb_device); - result = libusb_get_device_descriptor(dev, &desc); - if (result < 0) { - last_libusb_error = result; - return HACKRF_ERROR_LIBUSB; - } - - *version = desc.bcdDevice; + *version = device->usb_api_version; return HACKRF_SUCCESS; } From 3517d0adfc3bdb34f806a997a862d2d75e3508ff Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 9 Dec 2025 16:32:14 +0000 Subject: [PATCH 088/173] NAK the request that takes us out of TX mode until buffer flushed. --- firmware/hackrf_usb/usb_api_transceiver.c | 14 ++++++++++++++ firmware/hackrf_usb/usb_api_transceiver.h | 2 ++ firmware/hackrf_usb/usb_endpoint.c | 3 ++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index 2112fe07..cad75cd3 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -324,6 +324,17 @@ volatile transceiver_request_t transceiver_request = { .seq = 0, }; +void transceiver_usb_setup_complete(usb_endpoint_t* const endpoint) +{ + if (transceiver_request.mode == TRANSCEIVER_MODE_TX && + endpoint->setup.request == 1) { + // This is a request to leave TX mode. Do so but NAK for now. + request_transceiver_mode(endpoint->setup.value); + } else { + usb_setup_complete(endpoint); + } +} + // Must be called from an atomic context (normally USB ISR) void request_transceiver_mode(transceiver_mode_t mode) { @@ -651,6 +662,9 @@ void tx_mode(uint32_t seq) } // All data received from the host has now been transmitted. + // Now we can ACK the control request that took us out of TX mode. + usb_transfer_schedule_ack(usb_endpoint_control_in.in); + transceiver_shutdown(); } diff --git a/firmware/hackrf_usb/usb_api_transceiver.h b/firmware/hackrf_usb/usb_api_transceiver.h index df981c1e..383be18c 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.h +++ b/firmware/hackrf_usb/usb_api_transceiver.h @@ -36,6 +36,8 @@ typedef struct { extern volatile transceiver_request_t transceiver_request; +void transceiver_usb_setup_complete(usb_endpoint_t* const endpoint); + usb_request_status_t usb_vendor_request_set_transceiver_mode( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); diff --git a/firmware/hackrf_usb/usb_endpoint.c b/firmware/hackrf_usb/usb_endpoint.c index a8a35f0d..81251f1e 100644 --- a/firmware/hackrf_usb/usb_endpoint.c +++ b/firmware/hackrf_usb/usb_endpoint.c @@ -28,13 +28,14 @@ #include #include "usb_device.h" +#include "usb_api_transceiver.h" usb_endpoint_t usb_endpoint_control_out = { .address = 0x00, .device = &usb_device, .in = &usb_endpoint_control_in, .out = &usb_endpoint_control_out, - .setup_complete = usb_setup_complete, + .setup_complete = transceiver_usb_setup_complete, .transfer_complete = usb_control_out_complete, }; USB_DEFINE_QUEUE(usb_endpoint_control_out, 4); From 9584416b49ecd5e3b792288d2ffd5a64f7cfc60f Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 18 Dec 2025 07:21:58 +0000 Subject: [PATCH 089/173] Add vendor request to retrieve buffer size. --- firmware/hackrf_usb/hackrf_usb.c | 1 + firmware/hackrf_usb/usb_api_transceiver.c | 22 ++++++++++++++++++++++ firmware/hackrf_usb/usb_api_transceiver.h | 3 +++ firmware/hackrf_usb/usb_descriptor.c | 2 +- 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 54431ff3..cabf0d11 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -183,6 +183,7 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_test_rtc_osc, usb_vendor_request_write_radio_reg, usb_vendor_request_read_radio_reg, + usb_vendor_request_get_buffer_size, }; static const uint32_t vendor_request_handler_count = diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index cad75cd3..1aca654e 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -450,6 +450,28 @@ usb_request_status_t usb_vendor_request_set_rx_overrun_limit( return USB_REQUEST_STATUS_OK; } +usb_request_status_t usb_vendor_request_get_buffer_size( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint32_t value = USB_SAMP_BUFFER_SIZE + USB_BULK_BUFFER_SIZE; + endpoint->buffer[0] = value & 0xff; + endpoint->buffer[1] = (value & 0xff00) >> 8; + endpoint->buffer[2] = (value & 0xff0000) >> 16; + endpoint->buffer[3] = (value & 0xff000000) >> 24; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 4, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + return USB_REQUEST_STATUS_OK; + } + return USB_REQUEST_STATUS_OK; +} + void transceiver_start_dma(void* src, void* dest, size_t size) { dma_pending = size; diff --git a/firmware/hackrf_usb/usb_api_transceiver.h b/firmware/hackrf_usb/usb_api_transceiver.h index 383be18c..a29dec11 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.h +++ b/firmware/hackrf_usb/usb_api_transceiver.h @@ -77,6 +77,9 @@ usb_request_status_t usb_vendor_request_set_tx_underrun_limit( usb_request_status_t usb_vendor_request_set_rx_overrun_limit( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_get_buffer_size( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); void request_transceiver_mode(transceiver_mode_t mode); void transceiver_startup(transceiver_mode_t mode); diff --git a/firmware/hackrf_usb/usb_descriptor.c b/firmware/hackrf_usb/usb_descriptor.c index 36531ebc..067317b3 100644 --- a/firmware/hackrf_usb/usb_descriptor.c +++ b/firmware/hackrf_usb/usb_descriptor.c @@ -30,7 +30,7 @@ #define USB_VENDOR_ID (0x1D50) -#define USB_API_VERSION (0x0111) +#define USB_API_VERSION (0x0112) #define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF) From 90625818adc367759a94e90a3007beecd91e64aa Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 18 Dec 2025 07:36:58 +0000 Subject: [PATCH 090/173] For API >= 1.12, get buffer size when opening device. --- host/libhackrf/src/hackrf.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 14c34ab2..da3c0f15 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -115,6 +115,7 @@ typedef enum { HACKRF_VENDOR_REQUEST_TEST_RTC_OSC = 58, HACKRF_VENDOR_REQUEST_RADIO_WRITE_REG = 59, HACKRF_VENDOR_REQUEST_RADIO_READ_REG = 60, + HACKRF_VENDOR_REQUEST_GET_BUFFER_SIZE = 61, } hackrf_vendor_request; #define USB_CONFIG_STANDARD 0x1 @@ -138,7 +139,6 @@ typedef enum { #define TRANSFER_COUNT 4 #define TRANSFER_BUFFER_SIZE 262144 -#define DEVICE_BUFFER_SIZE 32768 #define USB_MAX_SERIAL_LENGTH 32 struct hackrf_device { @@ -163,6 +163,7 @@ struct hackrf_device { hackrf_flush_cb_fn flush_callback; hackrf_tx_block_complete_cb_fn tx_completion_callback; void* flush_ctx; + uint32_t buffer_size; }; typedef struct { @@ -712,6 +713,7 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d { int result; hackrf_device* lib_device; + uint32_t buffer_size; struct libusb_device_descriptor device_descriptor; libusb_device* dev = libusb_get_device(usb_device); result = libusb_get_device_descriptor(dev, &device_descriptor); @@ -765,6 +767,30 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d lib_device->flush_ctx = NULL; lib_device->tx_completion_callback = NULL; + if (lib_device->usb_api_version >= 0x0112) { + // Fetch buffer size from device so we know how many bytes to flush TX with. + result = libusb_control_transfer( + lib_device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_GET_BUFFER_SIZE, + 0, + 0, + (unsigned char*) &buffer_size, + 4, + 0); + + if (result < 4) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } + + lib_device->buffer_size = FROM_LE32(buffer_size); + } else { + // All older firmware uses a fixed 32KB buffer size. + lib_device->buffer_size = 32768; + } + result = pthread_mutex_init(&lib_device->transfer_lock, NULL); if (result != 0) { free(lib_device); @@ -2371,8 +2397,8 @@ ADDAPI int ADDCALL hackrf_enable_tx_flush( device->flush_transfer, device->usb_device, TX_ENDPOINT_ADDRESS, - calloc(1, DEVICE_BUFFER_SIZE), - DEVICE_BUFFER_SIZE, + calloc(1, device->buffer_size), + device->buffer_size, hackrf_libusb_flush_callback, device, 0); From 5afdd6dfef9f7d83885e09750d3a46acab0e9e8a Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 18 Dec 2025 07:49:31 +0000 Subject: [PATCH 091/173] If the host knows our buffer size, don't auto-flush. --- firmware/hackrf_usb/usb_api_transceiver.c | 26 +++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index 1aca654e..ca9445d1 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -52,6 +52,11 @@ #define BUF_HALF_MASK (USB_SAMP_BUFFER_SIZE >> 1) +// Unless we know the host knows our buffer size, we'll avoid leaving TX +// until we've transmitted all bytes sent by the host. This flag is cleared +// when the host requests our buffer size. +bool auto_tx_flush = true; + volatile uint32_t dma_started, dma_pending, usb_started, usb_completed; typedef struct { @@ -327,7 +332,7 @@ volatile transceiver_request_t transceiver_request = { void transceiver_usb_setup_complete(usb_endpoint_t* const endpoint) { if (transceiver_request.mode == TRANSCEIVER_MODE_TX && - endpoint->setup.request == 1) { + endpoint->setup.request == 1 && auto_tx_flush) { // This is a request to leave TX mode. Do so but NAK for now. request_transceiver_mode(endpoint->setup.value); } else { @@ -467,6 +472,11 @@ usb_request_status_t usb_vendor_request_get_buffer_size( NULL, NULL); usb_transfer_schedule_ack(endpoint->out); + + // We now know the host is aware of our buffer size, so it + // can make its own decisions about flushing the buffer. + auto_tx_flush = false; + return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_OK; @@ -628,9 +638,17 @@ void tx_mode(uint32_t seq) radio_update(&radio); } - // Host has now requested to stop TX. We won't initiate any further USB - // transfers into the bulk buffer. However, we should make sure all - // data currently in the USB bulk buffer reaches the sample buffer. + // Host has now requested to stop TX. If we're not auto-flushing, we + // should now stop TX immediately. + + if (!auto_tx_flush) { + transceiver_shutdown(); + return; + } + + // Otherwise, we should now ensure all bytes sent by the host are + // transmitted before we leave TX. First, we should make sure all data + // currently in the USB bulk buffer reaches the sample buffer. if ((usb_started - usb_completed) > 0) { // We were part way through a 16KB firmware-side transfer when From 03119000a4a8543f9f39f5727cad42ce32df6a61 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sun, 19 Oct 2025 16:41:34 +0100 Subject: [PATCH 092/173] Use DMA for transfers. --- firmware/hackrf_usb/usb_api_transceiver.c | 64 ++++++++++++++++++++--- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index ca9445d1..fe2b5405 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -25,13 +25,15 @@ #include #include -#include +#include +#include #include #include #include #include +#include #include #include #include @@ -81,7 +83,7 @@ typedef struct { set_sample_r_params_t set_sample_r_params; -void dma_isr(void); +void transceiver_dma_setup(void); usb_request_status_t usb_vendor_request_set_baseband_filter_bandwidth( usb_endpoint_t* const endpoint, @@ -371,6 +373,8 @@ void transceiver_startup(const transceiver_mode_t mode) usb_started = 0; usb_completed = 0; + transceiver_dma_setup(); + radio_switch_opmode(&radio, mode); switch (mode) { @@ -482,15 +486,63 @@ usb_request_status_t usb_vendor_request_get_buffer_size( return USB_REQUEST_STATUS_OK; } -void transceiver_start_dma(void* src, void* dest, size_t size) +/* clang-format off */ + +// Which GPDMA channel to use. +const uint32_t DMA_CHANNEL = 1; + +// GPDMA CCONFIG register setting. +const uint32_t DMA_CONFIG = + GPDMA_CCONFIG_FLOWCNTRL(0) // memory-to-memory + | GPDMA_CCONFIG_IE(0) // no error interrupt + | GPDMA_CCONFIG_ITC(1) // terminal count interrupt + | GPDMA_CCONFIG_L(0) // do not lock + | GPDMA_CCONFIG_H(0); // do not halt + +// GPDMA CCONTROL register setting (excluding TRANSFERSIZE field). +const uint32_t DMA_CONTROL = + GPDMA_CCONTROL_SBSIZE(7) // 256-transfer src bursts + | GPDMA_CCONTROL_DBSIZE(7) // 256-transfer dst bursts + | GPDMA_CCONTROL_SWIDTH(2) // 32-bit src transfers + | GPDMA_CCONTROL_DWIDTH(2) // 32-bit dst transfers + | GPDMA_CCONTROL_S(0) // AHB Master 0 + | GPDMA_CCONTROL_D(1) // AHB Master 1 + | GPDMA_CCONTROL_SI(1) // increment source + | GPDMA_CCONTROL_DI(1) // increment destination + | GPDMA_CCONTROL_PROT1(0) // user mode + | GPDMA_CCONTROL_PROT2(0) // not bufferable + | GPDMA_CCONTROL_PROT3(0) // not cacheable + | GPDMA_CCONTROL_I(1); // interrupt enabled + +/* clang-format on */ + +// Called before any sequence of DMA transfers. +void transceiver_dma_setup(void) { - dma_pending = size; - memcpy(dest, src, size); - dma_isr(); + gpdma_controller_enable(); + GPDMA_CCONFIG(DMA_CHANNEL) = DMA_CONFIG; + GPDMA_CCONTROL(DMA_CHANNEL) = DMA_CONTROL; + GPDMA_CLLI(DMA_CHANNEL) = 0; + GPDMA_INTTCCLEAR = (1 << DMA_CHANNEL); + nvic_enable_irq(NVIC_DMA_IRQ); } +// Called to start each DMA transfer. +void transceiver_start_dma(void* src, void* dest, size_t size) +{ + uint32_t num_transfers = size >> 2; + GPDMA_CCONTROL(DMA_CHANNEL) = DMA_CONTROL | num_transfers; + GPDMA_CSRCADDR(DMA_CHANNEL) = (uint32_t) src; + GPDMA_CDESTADDR(DMA_CHANNEL) = (uint32_t) dest; + dma_pending = size; + gpdma_channel_enable(DMA_CHANNEL); +} + +// Called when a DMA transfer completes. void dma_isr(void) { + gpdma_channel_disable(DMA_CHANNEL); + GPDMA_INTTCCLEAR = (1 << DMA_CHANNEL); m0_state.m4_count += dma_pending; dma_pending = 0; } From f1ece3769c30de6ef319ab640e1b0eaecd4c2208 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 09:48:52 +0200 Subject: [PATCH 093/173] firmware: move radio_t object instance to hackrf_usb binary --- firmware/common/drivers.c | 5 ----- firmware/common/drivers.h | 2 -- firmware/common/radio.h | 8 ++++++++ firmware/hackrf_usb/hackrf_usb.c | 4 ++++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index a6bd536a..a9f00509 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -26,7 +26,6 @@ #include #include -#include "clock_gen.h" #include "i2c_lpc.h" #include "max283x.h" #include "max5864_target.h" @@ -146,10 +145,6 @@ fpga_driver_t fpga = { }; #endif -radio_t radio = { - .sample_rate_cb = sample_rate_set, -}; - rf_path_t rf_path = { .switchctrl = 0, }; diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index 6aa45f87..b5fea0a5 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -32,7 +32,6 @@ extern "C" { #include "max5864.h" #include "mixer.h" #include "platform_detect.h" // IWYU pragma: keep -#include "radio.h" #include "rf_path.h" #include "sgpio.h" #include "si5351c.h" @@ -60,7 +59,6 @@ extern max5864_driver_t max5864; extern mixer_driver_t mixer; extern w25q80bv_driver_t spi_flash; extern sgpio_config_t sgpio_config; -extern radio_t radio; extern rf_path_t rf_path; extern jtag_gpio_t jtag_gpio_cpld; extern jtag_t jtag_cpld; diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 7ab46c80..b785c281 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -260,3 +260,11 @@ bool radio_update(radio_t* const radio); * the request bank for the new mode. */ void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode); + +/** + * Driver instance. + * + * This needs to be configured and provided by the application + * binary. e.g. firmware/hackrf_usb/hackrf_usb.c + */ +extern radio_t radio; diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 54431ff3..d02ea279 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -98,6 +98,10 @@ extern uint32_t __m0_end__; extern uint32_t __ram_m0_start__; extern uint32_t _etext_ram, _text_ram, _etext_rom; +radio_t radio = { + .sample_rate_cb = sample_rate_set, +}; + static usb_request_handler_fn vendor_request_handler[] = { NULL, usb_vendor_request_set_transceiver_mode, From 1af5055720d454518b44ab262cb51526a69e5d27 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 14:35:46 +0200 Subject: [PATCH 094/173] firmware: move spi_bus_ssp0, spi_bus_ssp1 driver instances to spi_bus.x --- firmware/common/drivers.c | 18 ------------------ firmware/common/spi_bus.c | 22 ++++++++++++++++++++++ firmware/common/spi_bus.h | 4 ++++ 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index a9f00509..a5b568af 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -90,15 +90,6 @@ ssp_config_t ssp_config_max5864 = { .clock_prescale_rate = 2, }; -spi_bus_t spi_bus_ssp1 = { - .obj = (void*) SSP1_BASE, - .config = &ssp_config_max5864, - .start = spi_ssp_start, - .stop = spi_ssp_stop, - .transfer = spi_ssp_transfer, - .transfer_gather = spi_ssp_transfer_gather, -}; - max5864_driver_t max5864 = { .bus = &spi_bus_ssp1, .target_init = max5864_target_init, @@ -110,15 +101,6 @@ ssp_config_t ssp_config_w25q80bv = { .clock_prescale_rate = 2, }; -spi_bus_t spi_bus_ssp0 = { - .obj = (void*) SSP0_BASE, - .config = &ssp_config_w25q80bv, - .start = spi_ssp_start, - .stop = spi_ssp_stop, - .transfer = spi_ssp_transfer, - .transfer_gather = spi_ssp_transfer_gather, -}; - w25q80bv_driver_t spi_flash = { .bus = &spi_bus_ssp0, .target_init = w25q80bv_target_init, diff --git a/firmware/common/spi_bus.c b/firmware/common/spi_bus.c index a26b4bab..a5332699 100644 --- a/firmware/common/spi_bus.c +++ b/firmware/common/spi_bus.c @@ -22,8 +22,30 @@ #include "spi_bus.h" +#include + +#include "spi_ssp.h" + +/* Driver instances. */ +spi_bus_t spi_bus_ssp0 = { + .obj = (void*) SSP0_BASE, + .start = spi_ssp_start, + .stop = spi_ssp_stop, + .transfer = spi_ssp_transfer, + .transfer_gather = spi_ssp_transfer_gather, +}; + +spi_bus_t spi_bus_ssp1 = { + .obj = (void*) SSP1_BASE, + .start = spi_ssp_start, + .stop = spi_ssp_stop, + .transfer = spi_ssp_transfer, + .transfer_gather = spi_ssp_transfer_gather, +}; + void spi_bus_start(spi_bus_t* const bus, const void* const config) { + bus->config = config; bus->start(bus, config); } diff --git a/firmware/common/spi_bus.h b/firmware/common/spi_bus.h index 07d5787d..23b3107b 100644 --- a/firmware/common/spi_bus.h +++ b/firmware/common/spi_bus.h @@ -51,3 +51,7 @@ void spi_bus_transfer_gather( spi_bus_t* const bus, const spi_transfer_t* const transfers, const size_t count); + +/* Driver instances. */ +extern spi_bus_t spi_bus_ssp0; +extern spi_bus_t spi_bus_ssp1; From 858ffd268a4cf3a6c4941fdacb6a252599adbd5e Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 14:40:37 +0200 Subject: [PATCH 095/173] firmware: move i2c_bus0, i2c_bus1 driver instances to i2c_bus.x --- firmware/common/da7219.c | 1 - firmware/common/drivers.c | 16 +--------------- firmware/common/drivers.h | 2 -- firmware/common/i2c_bus.c | 19 +++++++++++++++++++ firmware/common/i2c_bus.h | 4 ++++ firmware/common/operacake.c | 1 - 6 files changed, 24 insertions(+), 19 deletions(-) diff --git a/firmware/common/da7219.c b/firmware/common/da7219.c index 5af022d2..7ae916cb 100644 --- a/firmware/common/da7219.c +++ b/firmware/common/da7219.c @@ -22,7 +22,6 @@ #include #include "da7219.h" -#include "drivers.h" #include "i2c_bus.h" #define DA7219_REG_CHIP_ID1 0x81 diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index a5b568af..9d42000b 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -23,9 +23,9 @@ #include -#include #include +#include "i2c_bus.h" #include "i2c_lpc.h" #include "max283x.h" #include "max5864_target.h" @@ -36,20 +36,6 @@ #include "ice40_spi.h" #endif -i2c_bus_t i2c0 = { - .obj = (void*) I2C0_BASE, - .start = i2c_lpc_start, - .stop = i2c_lpc_stop, - .transfer = i2c_lpc_transfer, -}; - -i2c_bus_t i2c1 = { - .obj = (void*) I2C1_BASE, - .start = i2c_lpc_start, - .stop = i2c_lpc_stop, - .transfer = i2c_lpc_transfer, -}; - // const i2c_lpc_config_t i2c_config_si5351c_slow_clock = { // .duty_cycle_count = 15, // }; diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index b5fea0a5..111c7103 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -26,7 +26,6 @@ extern "C" { #endif #include "cpld_jtag.h" -#include "i2c_bus.h" #include "i2c_lpc.h" #include "max283x.h" #include "max5864.h" @@ -62,7 +61,6 @@ extern sgpio_config_t sgpio_config; extern rf_path_t rf_path; extern jtag_gpio_t jtag_gpio_cpld; extern jtag_t jtag_cpld; -extern i2c_bus_t i2c0; void ssp1_set_mode_max283x(void); void ssp1_set_mode_max5864(void); diff --git a/firmware/common/i2c_bus.c b/firmware/common/i2c_bus.c index 6d8dcd25..c0d5b79d 100644 --- a/firmware/common/i2c_bus.c +++ b/firmware/common/i2c_bus.c @@ -22,6 +22,25 @@ #include "i2c_bus.h" +#include + +#include "i2c_lpc.h" + +/* Driver instances. */ +i2c_bus_t i2c0 = { + .obj = (void*) I2C0_BASE, + .start = i2c_lpc_start, + .stop = i2c_lpc_stop, + .transfer = i2c_lpc_transfer, +}; + +i2c_bus_t i2c1 = { + .obj = (void*) I2C1_BASE, + .start = i2c_lpc_start, + .stop = i2c_lpc_stop, + .transfer = i2c_lpc_transfer, +}; + void i2c_bus_start(i2c_bus_t* const bus, const void* const config) { bus->start(bus, config); diff --git a/firmware/common/i2c_bus.h b/firmware/common/i2c_bus.h index 7519ba14..fa1211c1 100644 --- a/firmware/common/i2c_bus.h +++ b/firmware/common/i2c_bus.h @@ -47,3 +47,7 @@ void i2c_bus_transfer( const size_t tx_count, uint8_t* const rx, const size_t rx_count); + +/* Driver instances. */ +extern i2c_bus_t i2c0; +extern i2c_bus_t i2c1; diff --git a/firmware/common/operacake.c b/firmware/common/operacake.c index 24ca6541..f50c4f7e 100644 --- a/firmware/common/operacake.c +++ b/firmware/common/operacake.c @@ -26,7 +26,6 @@ #include -#include "drivers.h" #include "gpio.h" #include "gpio_lpc.h" #include "i2c_bus.h" From 4f44917f685337cdccbe5fd6b930883c2ac503c2 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 14:48:24 +0200 Subject: [PATCH 096/173] firmware: move w25q80vc driver instance to w25q80bv.x --- firmware/common/drivers.c | 12 ------------ firmware/common/drivers.h | 3 --- firmware/common/pins.c | 1 + firmware/common/rom_iap.c | 1 - firmware/common/w25q80bv.c | 18 +++++++++++++++++- firmware/common/w25q80bv.h | 5 +++++ firmware/hackrf_usb/usb_api_spiflash.c | 1 - 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index 9d42000b..b83a1a80 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -30,7 +30,6 @@ #include "max283x.h" #include "max5864_target.h" #include "spi_bus.h" -#include "w25q80bv_target.h" #if defined(IS_PRALINE) #include "fpga.h" #include "ice40_spi.h" @@ -81,17 +80,6 @@ max5864_driver_t max5864 = { .target_init = max5864_target_init, }; -ssp_config_t ssp_config_w25q80bv = { - .data_bits = SSP_DATA_8BITS, - .serial_clock_rate = 2, - .clock_prescale_rate = 2, -}; - -w25q80bv_driver_t spi_flash = { - .bus = &spi_bus_ssp0, - .target_init = w25q80bv_target_init, -}; - sgpio_config_t sgpio_config = { .slice_mode_multislice = true, }; diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index 111c7103..5d903e5b 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -35,7 +35,6 @@ extern "C" { #include "sgpio.h" #include "si5351c.h" #include "spi_ssp.h" -#include "w25q80bv.h" #if defined(IS_PRALINE) #include "fpga.h" #include "ice40_spi.h" @@ -46,7 +45,6 @@ extern const i2c_lpc_config_t i2c_config_si5351c_fast_clock; extern si5351c_driver_t clock_gen; extern ssp_config_t ssp_config_max283x; extern ssp_config_t ssp_config_max5864; -extern ssp_config_t ssp_config_w25q80bv; #if defined(IS_PRALINE) extern ssp_config_t ssp_config_ice40_fpga; @@ -56,7 +54,6 @@ extern fpga_driver_t fpga; extern max283x_driver_t max283x; extern max5864_driver_t max5864; extern mixer_driver_t mixer; -extern w25q80bv_driver_t spi_flash; extern sgpio_config_t sgpio_config; extern rf_path_t rf_path; extern jtag_gpio_t jtag_gpio_cpld; diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 6f8123cb..425d6d74 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -36,6 +36,7 @@ #include "power.h" #include "rf_path.h" #include "sgpio.h" +#include "w25q80bv.h" #if defined(IS_PRALINE) #include "clock_io.h" #endif diff --git a/firmware/common/rom_iap.c b/firmware/common/rom_iap.c index 09da0b7c..25dfa269 100644 --- a/firmware/common/rom_iap.c +++ b/firmware/common/rom_iap.c @@ -22,7 +22,6 @@ #include -#include "drivers.h" #include "rom_iap.h" #include "spi_bus.h" #include "w25q80bv.h" diff --git a/firmware/common/w25q80bv.c b/firmware/common/w25q80bv.c index f569bedf..11c6d5c0 100644 --- a/firmware/common/w25q80bv.c +++ b/firmware/common/w25q80bv.c @@ -27,11 +27,15 @@ * programming the flash. */ +#include "w25q80bv.h" + #include #include +#include + #include "platform_detect.h" -#include "w25q80bv.h" +#include "w25q80bv_target.h" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) @@ -51,6 +55,18 @@ #define W25Q80BV_DEVICE_ID_RES 0x13 /* Expected device_id for W25Q80BV */ +/* Driver instance. */ +ssp_config_t ssp_config_w25q80bv = { + .data_bits = SSP_DATA_8BITS, + .serial_clock_rate = 2, + .clock_prescale_rate = 2, +}; + +w25q80bv_driver_t spi_flash = { + .bus = &spi_bus_ssp0, + .target_init = w25q80bv_target_init, +}; + /* * Set up pins for GPIO and SPI control, configure SSP0 peripheral for SPI. * SSP0_CS is controlled by GPIO in order to handle various transfer lengths. diff --git a/firmware/common/w25q80bv.h b/firmware/common/w25q80bv.h index a43d6445..21d0814b 100644 --- a/firmware/common/w25q80bv.h +++ b/firmware/common/w25q80bv.h @@ -28,6 +28,7 @@ #include "gpio.h" #include "spi_bus.h" +#include "spi_ssp.h" #define W25Q80BV_DEVICE_ID_RES 0x13 /* Expected device_id for W25Q80BV */ #define W25Q16DV_DEVICE_ID_RES 0x14 /* Expected device_id for W25Q16DV */ @@ -65,3 +66,7 @@ void w25q80bv_read( uint32_t len, uint8_t* const data); void w25q80bv_clear_status(w25q80bv_driver_t* const drv); + +/* Driver instance. */ +extern ssp_config_t ssp_config_w25q80bv; +extern w25q80bv_driver_t spi_flash; diff --git a/firmware/hackrf_usb/usb_api_spiflash.c b/firmware/hackrf_usb/usb_api_spiflash.c index 2c944a10..d490eb0e 100644 --- a/firmware/hackrf_usb/usb_api_spiflash.c +++ b/firmware/hackrf_usb/usb_api_spiflash.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include From e5c8dc235012cc44ee4e6eab813a267b43680c02 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 14:54:22 +0200 Subject: [PATCH 097/173] firmware: move max283x driver instance to max283x.x --- firmware/common/drivers.c | 20 -------------------- firmware/common/drivers.h | 4 ---- firmware/common/fpga.c | 1 + firmware/common/fpga_image.c | 1 + firmware/common/max283x.c | 20 +++++++++++++++++++- firmware/common/max283x.h | 6 ++++++ firmware/common/pins.c | 1 + firmware/common/ui_rad1o.c | 2 +- 8 files changed, 29 insertions(+), 26 deletions(-) diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index b83a1a80..38f41a9d 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -27,7 +27,6 @@ #include "i2c_bus.h" #include "i2c_lpc.h" -#include "max283x.h" #include "max5864_target.h" #include "spi_bus.h" #if defined(IS_PRALINE) @@ -48,20 +47,6 @@ si5351c_driver_t clock_gen = { .i2c_address = 0x60, }; -ssp_config_t ssp_config_max283x = { - /* FIXME speed up once everything is working reliably */ - /* - // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - const uint8_t serial_clock_rate = 32; - const uint8_t clock_prescale_rate = 128; - */ - // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - .serial_clock_rate = 21, - .clock_prescale_rate = 2, -}; - -max283x_driver_t max283x = {}; - ssp_config_t ssp_config_max5864 = { /* FIXME speed up once everything is working reliably */ /* @@ -111,11 +96,6 @@ jtag_t jtag_cpld = { .gpio = &jtag_gpio_cpld, }; -void ssp1_set_mode_max283x(void) -{ - spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); -} - void ssp1_set_mode_max5864(void) { spi_bus_start(max5864.bus, &ssp_config_max5864); diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index 5d903e5b..d4792d75 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -27,7 +27,6 @@ extern "C" { #include "cpld_jtag.h" #include "i2c_lpc.h" -#include "max283x.h" #include "max5864.h" #include "mixer.h" #include "platform_detect.h" // IWYU pragma: keep @@ -43,7 +42,6 @@ extern "C" { /* TODO: Hide these configurations */ extern const i2c_lpc_config_t i2c_config_si5351c_fast_clock; extern si5351c_driver_t clock_gen; -extern ssp_config_t ssp_config_max283x; extern ssp_config_t ssp_config_max5864; #if defined(IS_PRALINE) @@ -51,7 +49,6 @@ extern ssp_config_t ssp_config_ice40_fpga; extern ice40_spi_driver_t ice40; extern fpga_driver_t fpga; #endif -extern max283x_driver_t max283x; extern max5864_driver_t max5864; extern mixer_driver_t mixer; extern sgpio_config_t sgpio_config; @@ -59,7 +56,6 @@ extern rf_path_t rf_path; extern jtag_gpio_t jtag_gpio_cpld; extern jtag_t jtag_cpld; -void ssp1_set_mode_max283x(void); void ssp1_set_mode_max5864(void); #if defined(IS_PRALINE) void ssp1_set_mode_ice40(void); diff --git a/firmware/common/fpga.c b/firmware/common/fpga.c index 172810a8..3bdd636e 100644 --- a/firmware/common/fpga.c +++ b/firmware/common/fpga.c @@ -26,6 +26,7 @@ #include "drivers.h" #include "ice40_spi.h" +#include "max283x.h" /* Set up all registers according to the loaded bitstream's defaults. */ void fpga_init(fpga_driver_t* const drv) diff --git a/firmware/common/fpga_image.c b/firmware/common/fpga_image.c index 989d16b2..a9f46aee 100644 --- a/firmware/common/fpga_image.c +++ b/firmware/common/fpga_image.c @@ -27,6 +27,7 @@ #include "fpga.h" #include "ice40_spi.h" #include "lz4_blk.h" +#include "max283x.h" #include "selftest.h" struct fpga_image_read_ctx { diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index 3b49ab6e..e7221bb0 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -41,7 +41,25 @@ #include "max2839_target.h" #endif -extern spi_bus_t spi_bus_ssp1; +/* Driver instance. */ +ssp_config_t ssp_config_max283x = { + /* FIXME speed up once everything is working reliably */ + /* + // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + const uint8_t serial_clock_rate = 32; + const uint8_t clock_prescale_rate = 128; + */ + // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + .serial_clock_rate = 21, + .clock_prescale_rate = 2, +}; + +max283x_driver_t max283x = {}; + +void ssp1_set_mode_max283x(void) +{ + spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); +} #ifdef IS_PRALINE max2831_driver_t max2831 = { diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h index 01bb40b0..b4e44620 100644 --- a/firmware/common/max283x.h +++ b/firmware/common/max283x.h @@ -28,6 +28,7 @@ #include "fixed_point.h" #include "platform_detect.h" +#include "spi_ssp.h" #ifdef IS_PRALINE #include "max2831.h" @@ -139,3 +140,8 @@ void max283x_set_rx_hpf_frequency( /* Perform MAX2831 TX and RX calibration. */ void max283x_tx_calibration(max283x_driver_t* const drv); void max283x_rx_calibration(max283x_driver_t* const drv); + +/* Driver instance. */ +extern ssp_config_t ssp_config_max283x; +extern max283x_driver_t max283x; +void ssp1_set_mode_max283x(void); diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 425d6d74..bced06d4 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -29,6 +29,7 @@ #include "drivers.h" #include "gpio.h" #include "leds.h" +#include "max283x.h" #include "mixer.h" #include "platform_detect.h" #include "platform_gpio.h" diff --git a/firmware/common/ui_rad1o.c b/firmware/common/ui_rad1o.c index 757e11f1..378798b0 100644 --- a/firmware/common/ui_rad1o.c +++ b/firmware/common/ui_rad1o.c @@ -27,7 +27,7 @@ #include #include "clock_gen.h" -#include "drivers.h" +#include "max283x.h" #include "rf_path.h" #include "transceiver_mode.h" #ifdef IS_NOT_RAD1O From ac93a68a028d7921aa6e37626f1b8d18be23ac80 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 30 Apr 2026 12:45:15 +0100 Subject: [PATCH 098/173] ui_rad1o: remove unnecessary include. --- firmware/common/ui_rad1o.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/firmware/common/ui_rad1o.c b/firmware/common/ui_rad1o.c index 757e11f1..4f0a3636 100644 --- a/firmware/common/ui_rad1o.c +++ b/firmware/common/ui_rad1o.c @@ -30,9 +30,6 @@ #include "drivers.h" #include "rf_path.h" #include "transceiver_mode.h" -#ifdef IS_NOT_RAD1O - #include "radio.h" -#endif #include "rad1o/display.h" #include "rad1o/draw.h" From f655bec11f3dad1ebf659bf2cd2e798fbf1f5202 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 30 Apr 2026 14:01:36 +0100 Subject: [PATCH 099/173] ui_rad1o: use a helper function for printing frequencies. --- firmware/common/ui_rad1o.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/firmware/common/ui_rad1o.c b/firmware/common/ui_rad1o.c index 4f0a3636..c1041fad 100644 --- a/firmware/common/ui_rad1o.c +++ b/firmware/common/ui_rad1o.c @@ -58,20 +58,21 @@ static bool enabled = false; #define WHITE 0b11111111 #define GREY 0b01001101 +static void print_hz(char* tmp, char* fmt, uint64_t hz) +{ + uint32_t mhz, khz; + mhz = hz / 1000000; + khz = (hz - mhz * 1000000) / 1000; + sprintf(tmp, fmt, (unsigned int) mhz, (unsigned int) khz); + rad1o_lcdPrint(tmp); +} + static void draw_frequency(void) { char tmp[100]; - uint32_t mhz; - uint32_t khz; - - mhz = freq / 1000000; - khz = (freq - mhz * 1000000) / 1000; - rad1o_setTextColor(BLACK, GREEN); rad1o_setIntFont(&Font_Ubuntu18pt); - sprintf(tmp, "%4u.%03u", (unsigned int) mhz, (unsigned int) khz); - rad1o_lcdPrint(tmp); - + print_hz(tmp, "%4u.%03u", freq); rad1o_setIntFont(&Font_7x8); rad1o_lcdMoveCrsr(1, 18 - 7); rad1o_lcdPrint("MHz"); @@ -112,8 +113,6 @@ static void draw_tx_rx(void) static void ui_update(void) { char tmp[100]; - uint32_t mhz; - uint32_t khz; if (!enabled) { return; @@ -154,18 +153,12 @@ static void ui_update(void) rad1o_setTextColor(BLACK, WHITE); rad1o_lcdSetCrsr(2, 71); rad1o_lcdPrint("Rate: "); - mhz = sample_rate / 1000000; - khz = (sample_rate - mhz * 1000000) / 1000; - sprintf(tmp, "%2u.%03u MHz", (unsigned int) mhz, (unsigned int) khz); - rad1o_lcdPrint(tmp); + print_hz(tmp, "%2u.%03u MHz", sample_rate); rad1o_lcdNl(); rad1o_lcdMoveCrsr(2, 0); rad1o_lcdPrint("Filter: "); - mhz = filter_bw / 1000000; - khz = (filter_bw - mhz * 1000000) / 1000; - sprintf(tmp, "%2u.%03u MHz", (unsigned int) mhz, (unsigned int) khz); - rad1o_lcdPrint(tmp); + print_hz(tmp, "%2u.%03u MHz", filter_bw); rad1o_lcdNl(); rad1o_drawHLine(88, 0, RESX - 1, WHITE); From 5d7b6c33cf199dc57a138b44e490b3caf2e5cc88 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 30 Apr 2026 14:02:33 +0100 Subject: [PATCH 100/173] ui_rad1o: Round displayed frequencies to nearest kHz. --- firmware/common/ui_rad1o.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/firmware/common/ui_rad1o.c b/firmware/common/ui_rad1o.c index c1041fad..8df630bd 100644 --- a/firmware/common/ui_rad1o.c +++ b/firmware/common/ui_rad1o.c @@ -58,11 +58,14 @@ static bool enabled = false; #define WHITE 0b11111111 #define GREY 0b01001101 +#define DIV_ROUND_CLOSEST(n, d) ((n + (d / 2)) / d) + static void print_hz(char* tmp, char* fmt, uint64_t hz) { uint32_t mhz, khz; - mhz = hz / 1000000; - khz = (hz - mhz * 1000000) / 1000; + mhz = DIV_ROUND_CLOSEST(hz, 1000000); + hz -= mhz * 1000000; + khz = DIV_ROUND_CLOSEST(hz, 1000); sprintf(tmp, fmt, (unsigned int) mhz, (unsigned int) khz); rad1o_lcdPrint(tmp); } From 01a3b345422c2102721c2c8e3e9a42beb55a92d9 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 15:07:58 +0200 Subject: [PATCH 101/173] firmware: move si5351c driver instance 'clock_gen' to si5351c.x --- firmware/common/cpu_clock.c | 2 +- firmware/common/drivers.c | 11 ----------- firmware/common/drivers.h | 4 ---- firmware/common/si5351c.c | 13 +++++++++++++ firmware/common/si5351c.h | 5 +++++ 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/firmware/common/cpu_clock.c b/firmware/common/cpu_clock.c index b69f0773..5a4ab4f6 100644 --- a/firmware/common/cpu_clock.c +++ b/firmware/common/cpu_clock.c @@ -29,8 +29,8 @@ #endif #include "delay.h" -#include "drivers.h" #include "i2c_bus.h" +#include "si5351c.h" #ifdef IS_NOT_RAD1O #include "platform_detect.h" #endif diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index 38f41a9d..bd601a1c 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -25,8 +25,6 @@ #include -#include "i2c_bus.h" -#include "i2c_lpc.h" #include "max5864_target.h" #include "spi_bus.h" #if defined(IS_PRALINE) @@ -38,15 +36,6 @@ // .duty_cycle_count = 15, // }; -const i2c_lpc_config_t i2c_config_si5351c_fast_clock = { - .duty_cycle_count = 255, -}; - -si5351c_driver_t clock_gen = { - .bus = &i2c0, - .i2c_address = 0x60, -}; - ssp_config_t ssp_config_max5864 = { /* FIXME speed up once everything is working reliably */ /* diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index d4792d75..2bb8db8e 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -26,13 +26,11 @@ extern "C" { #endif #include "cpld_jtag.h" -#include "i2c_lpc.h" #include "max5864.h" #include "mixer.h" #include "platform_detect.h" // IWYU pragma: keep #include "rf_path.h" #include "sgpio.h" -#include "si5351c.h" #include "spi_ssp.h" #if defined(IS_PRALINE) #include "fpga.h" @@ -40,8 +38,6 @@ extern "C" { #endif /* TODO: Hide these configurations */ -extern const i2c_lpc_config_t i2c_config_si5351c_fast_clock; -extern si5351c_driver_t clock_gen; extern ssp_config_t ssp_config_max5864; #if defined(IS_PRALINE) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index c9767405..53b025d8 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -25,6 +25,7 @@ #include "clock_io.h" #include "delay.h" +#include "i2c_lpc.h" #include "platform_detect.h" #include "selftest.h" #include "si5351c.h" @@ -35,6 +36,18 @@ #include "platform_scu.h" #endif +/* Driver instance. */ +// const i2c_lpc_config_t i2c_config_si5351c_slow_clock = { +// .duty_cycle_count = 15, +// }; +const i2c_lpc_config_t i2c_config_si5351c_fast_clock = { + .duty_cycle_count = 255, +}; +si5351c_driver_t clock_gen = { + .bus = &i2c0, + .i2c_address = 0x60, +}; + static enum pll_sources active_clock_source = PLL_SOURCE_UNINITIALIZED; /* External clock output default is deactivated as it creates noise */ static bool clkout_enabled = false; diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index 9e6b9ab8..f7a7da90 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -31,6 +31,7 @@ extern "C" { #include #include "i2c_bus.h" +#include "i2c_lpc.h" #define SI_INTDIV(x) (x * 128 - 512) @@ -115,6 +116,10 @@ void si5351c_set_phase( const uint8_t ms_number, const uint8_t offset); +/* Driver Instance. */ +extern const i2c_lpc_config_t i2c_config_si5351c_fast_clock; +extern si5351c_driver_t clock_gen; + #ifdef __cplusplus } #endif From 76252016b6a813b7c4ad661d6a8820521965a4d8 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 15:19:15 +0200 Subject: [PATCH 102/173] firmware: rename si5351c driver instance 'clock_gen' to 'si5351c' --- firmware/common/clock_gen.c | 66 +++++++++++++------------- firmware/common/cpu_clock.c | 2 +- firmware/common/si5351c.c | 2 +- firmware/common/si5351c.h | 2 +- firmware/hackrf_usb/usb_api_register.c | 10 ++-- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index b62308ce..642fc61b 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -36,14 +36,14 @@ void clock_gen_init(void) { - i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); + i2c_bus_start(si5351c.bus, &i2c_config_si5351c_fast_clock); - si5351c_init(&clock_gen); - si5351c_disable_all_outputs(&clock_gen); - si5351c_disable_oeb_pin_control(&clock_gen); - si5351c_power_down_all_clocks(&clock_gen); - si5351c_set_crystal_configuration(&clock_gen); - si5351c_enable_xo_and_ms_fanout(&clock_gen); + si5351c_init(&si5351c); + si5351c_disable_all_outputs(&si5351c); + si5351c_disable_oeb_pin_control(&si5351c); + si5351c_power_down_all_clocks(&si5351c); + si5351c_set_crystal_configuration(&si5351c); + si5351c_enable_xo_and_ms_fanout(&si5351c); /* * Clocks on HackRF One r9: @@ -76,7 +76,7 @@ void clock_gen_init(void) if (IS_H1_R9) { /* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */ si5351c_configure_multisynth( - &clock_gen, + &si5351c, 0, 20 * 128 - 512, 0, @@ -88,7 +88,7 @@ void clock_gen_init(void) if (IS_NOT_H1_R9) { /* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */ si5351c_configure_multisynth( - &clock_gen, + &si5351c, 4, 20 * 128 - 512, 0, @@ -96,7 +96,7 @@ void clock_gen_init(void) 0); /* 800/20 = 40MHz */ /* MS5/CLK5 is the source for the MAX2837 clock input (MAX2871 on rad1o). */ si5351c_configure_multisynth( - &clock_gen, + &si5351c, 5, 20 * 128 - 512, 0, @@ -111,19 +111,19 @@ void clock_gen_init(void) /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */ sample_rate_set(SR_FP_MHZ(10), true); - si5351c_configure_clock_control(&clock_gen); - si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL); + si5351c_configure_clock_control(&si5351c); + si5351c_set_clock_source(&si5351c, PLL_SOURCE_XTAL); // soft reset - si5351c_reset_pll(&clock_gen, SI5351C_PLL_BOTH); - si5351c_enable_clock_outputs(&clock_gen); + si5351c_reset_pll(&si5351c, SI5351C_PLL_BOTH); + si5351c_enable_clock_outputs(&si5351c); } void clock_gen_shutdown(void) { - i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); - si5351c_disable_all_outputs(&clock_gen); - si5351c_disable_oeb_pin_control(&clock_gen); - si5351c_power_down_all_clocks(&clock_gen); + i2c_bus_start(si5351c.bus, &i2c_config_si5351c_fast_clock); + si5351c_disable_all_outputs(&si5351c); + si5351c_disable_oeb_pin_control(&si5351c); + si5351c_power_down_all_clocks(&si5351c); } clock_source_t activate_best_clock_source(void) @@ -140,7 +140,7 @@ clock_source_t activate_best_clock_source(void) clock_source_t source = CLOCK_SOURCE_HACKRF; /* Check for external clock input. */ - if (si5351c_clkin_signal_valid(&clock_gen)) { + if (si5351c_clkin_signal_valid(&si5351c)) { source = CLOCK_SOURCE_EXTERNAL; } else { #ifdef IS_EXPANSION_COMPATIBLE @@ -149,7 +149,7 @@ clock_source_t activate_best_clock_source(void) if (portapack_reference_oscillator && portapack()) { portapack_reference_oscillator(true); delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */ - if (si5351c_clkin_signal_valid(&clock_gen)) { + if (si5351c_clkin_signal_valid(&si5351c)) { source = CLOCK_SOURCE_PORTAPACK; } else { portapack_reference_oscillator(false); @@ -161,7 +161,7 @@ clock_source_t activate_best_clock_source(void) } si5351c_set_clock_source( - &clock_gen, + &si5351c, (source == CLOCK_SOURCE_HACKRF) ? PLL_SOURCE_XTAL : PLL_SOURCE_CLKIN); hackrf_ui()->set_clock_source(source); @@ -342,9 +342,9 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) if (IS_NOT_PRALINE) { /* Integer mode can be enabled if p1 is even and p2 is zero. */ if (p1 & 0x1 || p2) { - si5351c_set_int_mode(&clock_gen, 0, 0); + si5351c_set_int_mode(&si5351c, 0, 0); } else { - si5351c_set_int_mode(&clock_gen, 0, 1); + si5351c_set_int_mode(&si5351c, 0, 1); } #ifdef IS_H1_R9 @@ -353,7 +353,7 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) * On HackRF One r9 all sample clocks are externally derived * from MS1/CLK1 operating at twice the sample rate. */ - si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); + si5351c_configure_multisynth(&si5351c, 1, p1, p2, p3, 0); } #endif #ifdef IS_NOT_H1_R9 @@ -363,11 +363,11 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) * different sample clocks, all derived from multisynth 0. */ /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ - si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); + si5351c_configure_multisynth(&si5351c, 0, p1, p2, p3, 1); /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ si5351c_configure_multisynth( - &clock_gen, + &si5351c, 1, 0, 0, @@ -376,7 +376,7 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ si5351c_configure_multisynth( - &clock_gen, + &si5351c, 2, 0, 0, @@ -389,17 +389,17 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) #ifdef IS_PRALINE if (IS_PRALINE) { /* MS0/CLK0 is the source for the MAX5864 (AFE_CLK). */ - si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); + si5351c_configure_multisynth(&si5351c, 0, p1, p2, p3, 1); /* MS1/CLK1 is the source for the FPGA (FPGA_CLK and SCT_CLK). */ - si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 1); + si5351c_configure_multisynth(&si5351c, 1, p1, p2, p3, 1); /* Delay FPGA_CLK relative to AFE_CLK. */ uint8_t phase_offset = 0; if (p1 < 2100) { phase_offset = (p1 >> 4) - 6; } - si5351c_set_phase(&clock_gen, 1, phase_offset); + si5351c_set_phase(&si5351c, 1, phase_offset); if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { /* @@ -407,12 +407,12 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) * CLK1. We configure both so that behavior is consistent with * newer boards that use CLK1 for both FPGA_CLK and SCT_CLK. */ - si5351c_configure_multisynth(&clock_gen, 2, p1, p2, p3, 1); - si5351c_set_phase(&clock_gen, 2, phase_offset); + si5351c_configure_multisynth(&si5351c, 2, p1, p2, p3, 1); + si5351c_set_phase(&si5351c, 2, phase_offset); } /* Reset PLL to synchronize output clock phase. */ - si5351c_reset_pll(&clock_gen, SI5351C_PLL_A); + si5351c_reset_pll(&si5351c, SI5351C_PLL_A); } #endif diff --git a/firmware/common/cpu_clock.c b/firmware/common/cpu_clock.c index 5a4ab4f6..aa4ac910 100644 --- a/firmware/common/cpu_clock.c +++ b/firmware/common/cpu_clock.c @@ -116,7 +116,7 @@ void cpu_clock_init(void) //FIXME disable I2C /* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */ - i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); + i2c_bus_start(si5351c.bus, &i2c_config_si5351c_fast_clock); /* * 12MHz clock is entering LPC XTAL1/OSC input now. diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 53b025d8..a7b605f5 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -43,7 +43,7 @@ const i2c_lpc_config_t i2c_config_si5351c_fast_clock = { .duty_cycle_count = 255, }; -si5351c_driver_t clock_gen = { +si5351c_driver_t si5351c = { .bus = &i2c0, .i2c_address = 0x60, }; diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index f7a7da90..b38eac90 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -118,7 +118,7 @@ void si5351c_set_phase( /* Driver Instance. */ extern const i2c_lpc_config_t i2c_config_si5351c_fast_clock; -extern si5351c_driver_t clock_gen; +extern si5351c_driver_t si5351c; #ifdef __cplusplus } diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index 58c1684c..e88d0016 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -40,6 +39,7 @@ #include #endif #ifdef IS_NOT_RAD1O + #include #include #include #endif @@ -101,7 +101,7 @@ usb_request_status_t usb_vendor_request_write_si5351c( if (endpoint->setup.index < 256) { if (endpoint->setup.value < 256) { si5351c_write_single( - &clock_gen, + &si5351c, endpoint->setup.index, endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); @@ -121,7 +121,7 @@ usb_request_status_t usb_vendor_request_read_si5351c( if (stage == USB_TRANSFER_STAGE_SETUP) { if (endpoint->setup.index < 256) { const uint8_t value = - si5351c_read_single(&clock_gen, endpoint->setup.index); + si5351c_read_single(&si5351c, endpoint->setup.index); endpoint->buffer[0] = value; usb_transfer_schedule_block( endpoint->in, @@ -211,7 +211,7 @@ usb_request_status_t usb_vendor_request_set_clkout_enable( const usb_transfer_stage_t stage) { if (stage == USB_TRANSFER_STAGE_SETUP) { - si5351c_clkout_enable(&clock_gen, endpoint->setup.value); + si5351c_clkout_enable(&si5351c, endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); } return USB_REQUEST_STATUS_OK; @@ -222,7 +222,7 @@ usb_request_status_t usb_vendor_request_get_clkin_status( const usb_transfer_stage_t stage) { if (stage == USB_TRANSFER_STAGE_SETUP) { - endpoint->buffer[0] = si5351c_clkin_signal_valid(&clock_gen); + endpoint->buffer[0] = si5351c_clkin_signal_valid(&si5351c); usb_transfer_schedule_block( endpoint->in, &endpoint->buffer, From 3efc8bdbb6a2ccee3bbccfb0f2f7443fd1b34b94 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 15:30:00 +0200 Subject: [PATCH 103/173] firmware: move max5864 driver instance to max5864.x --- firmware/common/drivers.c | 33 ++------------------------------- firmware/common/drivers.h | 8 +------- firmware/common/max5864.c | 29 +++++++++++++++++++++++++++++ firmware/common/max5864.h | 6 ++++++ firmware/common/pins.c | 1 + 5 files changed, 39 insertions(+), 38 deletions(-) diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index bd601a1c..769013b0 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -23,37 +23,13 @@ #include -#include - -#include "max5864_target.h" -#include "spi_bus.h" #if defined(IS_PRALINE) + #include #include "fpga.h" #include "ice40_spi.h" + #include "spi_bus.h" #endif -// const i2c_lpc_config_t i2c_config_si5351c_slow_clock = { -// .duty_cycle_count = 15, -// }; - -ssp_config_t ssp_config_max5864 = { - /* FIXME speed up once everything is working reliably */ - /* - // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - const uint8_t serial_clock_rate = 32; - const uint8_t clock_prescale_rate = 128; - */ - // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - .data_bits = SSP_DATA_8BITS, - .serial_clock_rate = 21, - .clock_prescale_rate = 2, -}; - -max5864_driver_t max5864 = { - .bus = &spi_bus_ssp1, - .target_init = max5864_target_init, -}; - sgpio_config_t sgpio_config = { .slice_mode_multislice = true, }; @@ -85,11 +61,6 @@ jtag_t jtag_cpld = { .gpio = &jtag_gpio_cpld, }; -void ssp1_set_mode_max5864(void) -{ - spi_bus_start(max5864.bus, &ssp_config_max5864); -} - #ifdef IS_PRALINE void ssp1_set_mode_ice40(void) { diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index 2bb8db8e..423e46a8 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -26,33 +26,27 @@ extern "C" { #endif #include "cpld_jtag.h" -#include "max5864.h" #include "mixer.h" #include "platform_detect.h" // IWYU pragma: keep #include "rf_path.h" #include "sgpio.h" -#include "spi_ssp.h" #if defined(IS_PRALINE) #include "fpga.h" #include "ice40_spi.h" + #include "spi_ssp.h" #endif -/* TODO: Hide these configurations */ -extern ssp_config_t ssp_config_max5864; - #if defined(IS_PRALINE) extern ssp_config_t ssp_config_ice40_fpga; extern ice40_spi_driver_t ice40; extern fpga_driver_t fpga; #endif -extern max5864_driver_t max5864; extern mixer_driver_t mixer; extern sgpio_config_t sgpio_config; extern rf_path_t rf_path; extern jtag_gpio_t jtag_gpio_cpld; extern jtag_t jtag_cpld; -void ssp1_set_mode_max5864(void); #if defined(IS_PRALINE) void ssp1_set_mode_ice40(void); #endif diff --git a/firmware/common/max5864.c b/firmware/common/max5864.c index b91a1cd1..88d781b9 100644 --- a/firmware/common/max5864.c +++ b/firmware/common/max5864.c @@ -24,6 +24,35 @@ #include +#include + +#include "max5864_target.h" +#include "spi_bus.h" + +/* Driver instance. */ +ssp_config_t ssp_config_max5864 = { + /* FIXME speed up once everything is working reliably */ + /* + // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + const uint8_t serial_clock_rate = 32; + const uint8_t clock_prescale_rate = 128; + */ + // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + .data_bits = SSP_DATA_8BITS, + .serial_clock_rate = 21, + .clock_prescale_rate = 2, +}; + +max5864_driver_t max5864 = { + .bus = &spi_bus_ssp1, + .target_init = max5864_target_init, +}; + +void ssp1_set_mode_max5864(void) +{ + spi_bus_start(max5864.bus, &ssp_config_max5864); +} + static void max5864_write(max5864_driver_t* const drv, uint8_t value) { spi_bus_transfer(drv->bus, &value, 1); diff --git a/firmware/common/max5864.h b/firmware/common/max5864.h index 56d730cc..a617cdf0 100644 --- a/firmware/common/max5864.h +++ b/firmware/common/max5864.h @@ -23,6 +23,7 @@ #pragma once #include "spi_bus.h" +#include "spi_ssp.h" typedef struct _max5864_driver_t { spi_bus_t* const bus; @@ -37,3 +38,8 @@ void max5864_idle(max5864_driver_t* const drv); void max5864_rx(max5864_driver_t* const drv); void max5864_tx(max5864_driver_t* const drv); void max5864_xcvr(max5864_driver_t* const drv); + +/* Driver instance. */ +extern ssp_config_t ssp_config_max5864; +extern max5864_driver_t max5864; +void ssp1_set_mode_max5864(void); diff --git a/firmware/common/pins.c b/firmware/common/pins.c index bced06d4..24aeb033 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -30,6 +30,7 @@ #include "gpio.h" #include "leds.h" #include "max283x.h" +#include "max5864.h" #include "mixer.h" #include "platform_detect.h" #include "platform_gpio.h" From c8a0b02d85da760b7d87da0b382acc6ea2f90204 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 15:37:23 +0200 Subject: [PATCH 104/173] firmware: move mixer object's extern instance to mixer.h --- firmware/common/drivers.h | 2 -- firmware/common/mixer.h | 3 +++ firmware/common/rf_path.c | 1 - firmware/hackrf_usb/usb_api_register.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index 423e46a8..461ada2c 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -26,7 +26,6 @@ extern "C" { #endif #include "cpld_jtag.h" -#include "mixer.h" #include "platform_detect.h" // IWYU pragma: keep #include "rf_path.h" #include "sgpio.h" @@ -41,7 +40,6 @@ extern ssp_config_t ssp_config_ice40_fpga; extern ice40_spi_driver_t ice40; extern fpga_driver_t fpga; #endif -extern mixer_driver_t mixer; extern sgpio_config_t sgpio_config; extern rf_path_t rf_path; extern jtag_gpio_t jtag_gpio_cpld; diff --git a/firmware/common/mixer.h b/firmware/common/mixer.h index f7861725..35c0f047 100644 --- a/firmware/common/mixer.h +++ b/firmware/common/mixer.h @@ -65,3 +65,6 @@ extern fp_40_24_t mixer_set_frequency( extern void mixer_enable(mixer_driver_t* const mixer); extern void mixer_disable(mixer_driver_t* const mixer); extern void mixer_set_gpo(mixer_driver_t* const drv, uint8_t gpo); + +/* Mixer instance. */ +extern mixer_driver_t mixer; diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index ea1c0d45..721d647c 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -23,7 +23,6 @@ #include "rf_path.h" -#include "drivers.h" #include "max283x.h" #include "max5864.h" #include "mixer.h" diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index e88d0016..88a4de53 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -36,10 +36,10 @@ #include #include #ifdef IS_PRALINE + #include #include #endif #ifdef IS_NOT_RAD1O - #include #include #include #endif From 9a3abb6413912ab8fc55251e504421121f83e0a4 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 15:40:48 +0200 Subject: [PATCH 105/173] firmware: move rf_path object instance to rf_path.x --- firmware/common/drivers.c | 4 ---- firmware/common/drivers.h | 2 -- firmware/common/radio.c | 2 +- firmware/common/rf_path.c | 5 +++++ firmware/common/rf_path.h | 3 +++ 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index 769013b0..85a17746 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -51,10 +51,6 @@ fpga_driver_t fpga = { }; #endif -rf_path_t rf_path = { - .switchctrl = 0, -}; - jtag_gpio_t jtag_gpio_cpld = {}; jtag_t jtag_cpld = { diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index 461ada2c..11ec20a2 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -27,7 +27,6 @@ extern "C" { #include "cpld_jtag.h" #include "platform_detect.h" // IWYU pragma: keep -#include "rf_path.h" #include "sgpio.h" #if defined(IS_PRALINE) #include "fpga.h" @@ -41,7 +40,6 @@ extern ice40_spi_driver_t ice40; extern fpga_driver_t fpga; #endif extern sgpio_config_t sgpio_config; -extern rf_path_t rf_path; extern jtag_gpio_t jtag_gpio_cpld; extern jtag_t jtag_cpld; diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 8cf73cc8..17b7486c 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -26,7 +26,6 @@ #include #include "clock_io.h" -#include "drivers.h" #include "fixed_point.h" #include "max283x.h" #include "mixer.h" @@ -35,6 +34,7 @@ #include "transceiver_mode.h" #include "tuning.h" #ifdef IS_PRALINE + #include "drivers.h" #include "fpga.h" #include "tune_config.h" #endif diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index 721d647c..e3292532 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -35,6 +35,11 @@ #include "platform_gpio.h" #endif +/* RF Path instance. */ +rf_path_t rf_path = { + .switchctrl = 0, +}; + /* * RF switches on Jawbreaker are controlled by General Purpose Outputs (GPO) on * the RFFC5072. diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h index 12f83b53..ca7641e4 100644 --- a/firmware/common/rf_path.h +++ b/firmware/common/rf_path.h @@ -106,3 +106,6 @@ void rf_path_set_antenna(rf_path_t* const rf_path, const uint_fast8_t enable); #ifdef IS_PRALINE void narrowband_filter_set(const uint8_t value); #endif + +/* RF Path instance. */ +extern rf_path_t rf_path; From 47f91d29cf313c03ded1989498de7519f62d0a3f Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 16:19:05 +0200 Subject: [PATCH 106/173] firmware: move sgpio_config configuration instance to sgpio.x --- firmware/common/clock_gen.c | 1 - firmware/common/clock_io.c | 5 ++++- firmware/common/drivers.c | 6 ------ firmware/common/drivers.h | 2 -- firmware/common/sgpio.c | 5 +++++ firmware/common/sgpio.h | 3 +++ firmware/hackrf_usb/hackrf_usb.c | 2 +- firmware/hackrf_usb/usb_api_sweep.c | 2 +- firmware/hackrf_usb/usb_api_transceiver.c | 2 +- 9 files changed, 15 insertions(+), 13 deletions(-) diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index 642fc61b..0f0085c2 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -23,7 +23,6 @@ #include -#include "drivers.h" #include "hackrf_ui.h" #include "i2c_bus.h" #include "platform_detect.h" diff --git a/firmware/common/clock_io.c b/firmware/common/clock_io.c index 279eb8c5..c68d470f 100644 --- a/firmware/common/clock_io.c +++ b/firmware/common/clock_io.c @@ -30,10 +30,13 @@ #include #include -#include "drivers.h" #include "gpdma.h" #include "gpio.h" +#ifdef IS_NOT_PRALINE + #include "sgpio.h" +#endif #ifdef IS_PRALINE + #include "drivers.h" #include "fpga.h" #include "platform_gpio.h" #endif diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index 85a17746..58b30cb2 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -21,8 +21,6 @@ #include "drivers.h" -#include - #if defined(IS_PRALINE) #include #include "fpga.h" @@ -30,10 +28,6 @@ #include "spi_bus.h" #endif -sgpio_config_t sgpio_config = { - .slice_mode_multislice = true, -}; - #ifdef IS_PRALINE ssp_config_t ssp_config_ice40_fpga = { .data_bits = SSP_DATA_8BITS, diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index 11ec20a2..d0427e93 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -27,7 +27,6 @@ extern "C" { #include "cpld_jtag.h" #include "platform_detect.h" // IWYU pragma: keep -#include "sgpio.h" #if defined(IS_PRALINE) #include "fpga.h" #include "ice40_spi.h" @@ -39,7 +38,6 @@ extern ssp_config_t ssp_config_ice40_fpga; extern ice40_spi_driver_t ice40; extern fpga_driver_t fpga; #endif -extern sgpio_config_t sgpio_config; extern jtag_gpio_t jtag_gpio_cpld; extern jtag_t jtag_cpld; diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index 568f1208..e6151e46 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -35,6 +35,11 @@ #include "clock_io.h" #endif +/* Driver configuration instance. */ +sgpio_config_t sgpio_config = { + .slice_mode_multislice = true, +}; + static void update_q_invert(sgpio_config_t* const config); void sgpio_configure_pin_functions(sgpio_config_t* const config) diff --git a/firmware/common/sgpio.h b/firmware/common/sgpio.h index 409cef0c..767d6f48 100644 --- a/firmware/common/sgpio.h +++ b/firmware/common/sgpio.h @@ -50,3 +50,6 @@ void sgpio_cpld_stream_disable(sgpio_config_t* const config); bool sgpio_cpld_stream_is_enabled(sgpio_config_t* const config); void sgpio_cpld_set_mixer_invert(sgpio_config_t* const config, uint_fast8_t invert); + +/* Driver configuration instance. */ +extern sgpio_config_t sgpio_config; diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index d02ea279..031f2001 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -71,6 +70,7 @@ #ifdef IS_NOT_PRALINE #include #include + #include #endif #include "usb_api_adc.h" diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index ae3b399d..72ff2467 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -25,13 +25,13 @@ #include -#include #include #include #include #include #include #include +#include #include #include #include diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index b6c6782b..7f6f9473 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -27,13 +27,13 @@ #include #include -#include #include #include #include #include #include #include +#include #include #include #include From a426b7518d5ebb267f7895ecbf41d009223725d0 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Mon, 27 Apr 2026 16:56:20 -0400 Subject: [PATCH 107/173] Improve pins_shutdown() --- firmware/common/operacake_sctimer.c | 27 ++++++----------- firmware/common/pins.c | 34 +++++++++++++++++++-- firmware/common/platform_gpio.c | 1 + firmware/common/platform_gpio.h | 1 + firmware/common/platform_scu.c | 12 ++++++++ firmware/common/platform_scu.h | 12 ++++++++ firmware/common/rf_path.c | 27 +++++++++++++++++ firmware/common/rf_path.h | 1 + firmware/common/sgpio.c | 47 +++++++++++++++++++++++++++++ firmware/common/sgpio.h | 1 + 10 files changed, 143 insertions(+), 20 deletions(-) diff --git a/firmware/common/operacake_sctimer.c b/firmware/common/operacake_sctimer.c index 3b95f6a1..1268114f 100644 --- a/firmware/common/operacake_sctimer.c +++ b/firmware/common/operacake_sctimer.c @@ -28,6 +28,7 @@ #include "delay.h" #include "platform_detect.h" +#include "platform_scu.h" #include "sct.h" #ifdef IS_NOT_PRALINE @@ -62,6 +63,8 @@ static uint32_t default_output = 0; */ void operacake_sctimer_init(void) { + const platform_scu_t* scu = platform_scu(); + // We start by resetting the SCTimer RESET_CTRL1 = RESET_CTRL1_SCT_RST; @@ -77,25 +80,15 @@ void operacake_sctimer_init(void) // Pin definitions for the HackRF // U2CTRL0 - scu_pinmux( - P7_4, - SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1); + scu_pinmux(scu->CTOUT_13, scu->CTOUT_PINCFG); // U2CTRL1 - scu_pinmux( - P7_5, - SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1); + scu_pinmux(scu->CTOUT_12, scu->CTOUT_PINCFG); // U3CTRL0 - scu_pinmux( - P7_6, - SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1); + scu_pinmux(scu->CTOUT_11, scu->CTOUT_PINCFG); // U3CTRL1 - scu_pinmux( - P7_7, - SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1); + scu_pinmux(scu->CTOUT_8, scu->CTOUT_PINCFG); // U1CTRL - scu_pinmux( - P7_0, - SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1); + scu_pinmux(scu->CTOUT_14, scu->CTOUT_PINCFG); uint8_t sct_clock_input; #ifdef IS_NOT_PRALINE @@ -114,8 +107,8 @@ void operacake_sctimer_init(void) #endif #ifdef IS_PRALINE if (IS_PRALINE) { - // Configure pin P6_4 as SCT_IN_6 - scu_pinmux(P6_4, SCU_CLK_IN | SCU_CONF_FUNCTION1); + // Configure pin P6_4 as CTIN_6 + scu_pinmux(scu->SCT_CLK, scu->SCT_CLK_PINCFG); // Use the GIMA to connect MS0/CLK1 (SCT_CLK) on pin P6_4 to the SCTimer GIMA_CTIN_6_IN = 0x0 << 4; diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 6f8123cb..eb445fc3 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -178,9 +178,30 @@ void pins_shutdown(void) scu_pinmux(scu->TRIGGER_IN, scu->TRIGGER_IN_PINCFG); scu_pinmux(scu->TRIGGER_OUT, scu->TRIGGER_OUT_PINCFG); scu_pinmux(scu->PPS_OUT, scu->PPS_OUT_PINCFG); - scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PUP | SCU_CONF_FUNCTION4); - scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(scu->SCT_CLK, scu->SCT_CLK_PINCFG); + + scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + scu_pinmux(scu->SSP1_CIPO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->SSP1_COPI, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->SSP1_SCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION2); + + scu_pinmux(scu->XCVR_ENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->XCVR_RXENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->XCVR_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->XCVR_RXHP, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->XCVR_LD, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + scu_pinmux(scu->MIXER_LD, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->MIXER_SCLK, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->MIXER_SDATA, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->MIXER_ENX, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->MIXER_RESETX, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->MIXER_ENBL, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + scu_pinmux(scu->AD_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); p2_ctrl_set(P2_SIGNAL_CLK3); p1_ctrl_set(P1_SIGNAL_CLKIN); @@ -202,8 +223,12 @@ void pins_shutdown(void) gpio_clear(gpio->fpga_cfg_creset); gpio_output(gpio->fpga_cfg_creset); gpio_input(gpio->fpga_cfg_cdone); + gpio_input(gpio->max5864_select); + + rf_path_pin_shutdown(); } #endif + sgpio_pin_shutdown(&sgpio_config); /* enable input on SCL and SDA pins */ SCU_SFSI2C0 = SCU_I2C0_NOMINAL; @@ -360,6 +385,9 @@ void pins_setup(void) (detected_revision() == BOARD_REV_GSG_PRALINE_R1_0)) { rf_path.gpio_mix_en_n = gpio->mix_en_n_r1_0; } + scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PUP | SCU_CONF_FUNCTION4); + scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); } #endif diff --git a/firmware/common/platform_gpio.c b/firmware/common/platform_gpio.c index a5b892e8..eeecd90e 100644 --- a/firmware/common/platform_gpio.c +++ b/firmware/common/platform_gpio.c @@ -234,6 +234,7 @@ const platform_gpio_t* platform_gpio(void) gpio.rffc5072_data = &GPIO4_14; gpio.rffc5072_reset = &GPIO2_14; gpio.rffc5072_ld = &GPIO6_25; + gpio.rffc5072_enbl = &GPIO4_12; } #endif diff --git a/firmware/common/platform_gpio.h b/firmware/common/platform_gpio.h index eeb37b05..a7e68bc9 100644 --- a/firmware/common/platform_gpio.h +++ b/firmware/common/platform_gpio.h @@ -137,6 +137,7 @@ typedef struct { gpio_t rffc5072_data; gpio_t rffc5072_reset; gpio_t rffc5072_ld; // PRALINE + gpio_t rffc5072_enbl; // PRALINE gpio_t vco_ce; // RAD1O gpio_t vco_sclk; // RAD1O gpio_t vco_sdata; // RAD1O diff --git a/firmware/common/platform_scu.c b/firmware/common/platform_scu.c index ef894745..c9e468e9 100644 --- a/firmware/common/platform_scu.c +++ b/firmware/common/platform_scu.c @@ -258,10 +258,12 @@ const platform_scu_t* platform_scu(void) scu.MIXER_SDATA = (P9_2); /* GPIO4[14] on P9_2 */ scu.MIXER_RESETX = (P5_5); /* GPIO2[14] on P5_5 */ scu.MIXER_LD = (PD_11); /* GPIO6[25] on PD_11 */ + scu.MIXER_ENBL = (P9_0); /* GPIO4[12] on P9_0 */ scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); scu.MIXER_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.MIXER_ENBL_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); } #endif #ifdef IS_RAD1O @@ -365,6 +367,7 @@ const platform_scu_t* platform_scu(void) scu.TRIGGER_IN = (PD_12); /* GPIO6[26] on PD_12 */ scu.TRIGGER_OUT = (P2_6); /* GPIO5[6] on P2_6 */ scu.PPS_OUT = (P2_5); /* GPIO5[5] on P2_5 */ + scu.SCT_CLK = (P6_4); /* CTIN_6 on P6_4 */ scu.P2_CTRL0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.P2_CTRL1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); @@ -376,6 +379,7 @@ const platform_scu_t* platform_scu(void) scu.TRIGGER_IN_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.TRIGGER_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); scu.PPS_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + scu.SCT_CLK_PINCFG = (SCU_CLK_IN | SCU_CONF_FUNCTION1); } #endif @@ -438,6 +442,14 @@ const platform_scu_t* platform_scu(void) } #endif + /* SCT */ + scu.CTOUT_8 = (P7_7); + scu.CTOUT_11 = (P7_6); + scu.CTOUT_12 = (P7_5); + scu.CTOUT_13 = (P7_4); + scu.CTOUT_14 = (P7_0); + scu.CTOUT_PINCFG = (SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1); + scu.PINMUX_ISP = (P2_7); /* GPIO0[7] */ scu.PINMUX_GP_CLKIN = (P4_7); diff --git a/firmware/common/platform_scu.h b/firmware/common/platform_scu.h index 1ffb224c..71c0f742 100644 --- a/firmware/common/platform_scu.h +++ b/firmware/common/platform_scu.h @@ -157,7 +157,9 @@ typedef struct { #endif #ifdef IS_PRALINE scu_grp_pin_t MIXER_LD; + scu_grp_pin_t MIXER_ENBL; uint32_t MIXER_LD_PINCFG; + uint32_t MIXER_ENBL_PINCFG; #endif #ifdef IS_RAD1O scu_grp_pin_t VCO_CE; @@ -240,6 +242,7 @@ typedef struct { scu_grp_pin_t TRIGGER_IN; scu_grp_pin_t TRIGGER_OUT; scu_grp_pin_t PPS_OUT; + scu_grp_pin_t SCT_CLK; scu_grp_pin_t P2_CTRL0_PINCFG; scu_grp_pin_t P2_CTRL1_PINCFG; @@ -251,6 +254,7 @@ typedef struct { scu_grp_pin_t TRIGGER_IN_PINCFG; scu_grp_pin_t TRIGGER_OUT_PINCFG; scu_grp_pin_t PPS_OUT_PINCFG; + scu_grp_pin_t SCT_CLK_PINCFG; #endif /* HackRF One r9 */ @@ -300,6 +304,14 @@ typedef struct { scu_grp_pin_t PINMUX_U0_TXD; scu_grp_pin_t PINMUX_U0_RXD; + /* SCT */ + scu_grp_pin_t CTOUT_8; + scu_grp_pin_t CTOUT_11; + scu_grp_pin_t CTOUT_12; + scu_grp_pin_t CTOUT_13; + scu_grp_pin_t CTOUT_14; + scu_grp_pin_t CTOUT_PINCFG; + scu_grp_pin_t PINMUX_ISP; scu_grp_pin_t PINMUX_GP_CLKIN; diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index ea1c0d45..8fc071a7 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -317,6 +317,33 @@ static void switchctrl_set(rf_path_t* const rf_path, const uint8_t gpo) #endif } +void rf_path_pin_shutdown(void) +{ +#ifdef IS_PRALINE + if (IS_PRALINE) { + const platform_scu_t* scu = platform_scu(); + + /* Configure RF switch control signals */ + scu_pinmux(scu->TX_EN, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + board_rev_t rev = detected_revision(); + if ((rev == BOARD_REV_PRALINE_R1_0) || + (rev == BOARD_REV_GSG_PRALINE_R1_0)) { + scu_pinmux(scu->MIX_EN_N_R1_0, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + } else { + scu_pinmux(scu->MIX_EN_N, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + } + scu_pinmux(scu->LPF_EN, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->RF_AMP_EN, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + /* Configure antenna port power control signal */ + scu_pinmux(scu->ANT_BIAS_EN_N, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + /* Configure RF power supply (VAA) switch */ + scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + } +#endif +} + void rf_path_pin_setup(rf_path_t* const rf_path) { #ifdef IS_JAWBREAKER diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h index 12f83b53..a1e91b4c 100644 --- a/firmware/common/rf_path.h +++ b/firmware/common/rf_path.h @@ -93,6 +93,7 @@ typedef struct { }; } rf_path_t; +void rf_path_pin_shutdown(void); void rf_path_pin_setup(rf_path_t* const rf_path); void rf_path_init(rf_path_t* const rf_path); diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index 568f1208..a88290db 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -37,6 +37,53 @@ static void update_q_invert(sgpio_config_t* const config); +void sgpio_pin_shutdown(sgpio_config_t* const config) +{ + const platform_scu_t* scu = platform_scu(); + +#ifdef IS_PRALINE + if (IS_PRALINE) { + scu_pinmux(scu->PINMUX_SGPIO0, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_SGPIO1, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_SGPIO2, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_SGPIO3, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_SGPIO4, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->PINMUX_SGPIO5, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_SGPIO6, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->PINMUX_SGPIO7, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_SGPIO8, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_SGPIO9, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_SGPIO10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_SGPIO11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_SGPIO12, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + } +#endif + +#ifdef IS_H1_R9 + if (IS_H1_R9) { + scu_pinmux( + scu->H1R9_TRIGGER_EN, + SCU_GPIO_PDN | SCU_CONF_FUNCTION4); /* GPIO5[5] */ + } +#endif +#ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { + scu_pinmux( + scu->TRIGGER_EN, + SCU_GPIO_PDN | SCU_CONF_FUNCTION4); /* GPIO5[12] */ + } +#endif + + gpio_input(config->gpio_q_invert); + +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + trigger_enable(false); + gpio_output(config->gpio_trigger_enable); + } +#endif +} + void sgpio_configure_pin_functions(sgpio_config_t* const config) { const platform_scu_t* scu = platform_scu(); diff --git a/firmware/common/sgpio.h b/firmware/common/sgpio.h index 409cef0c..392209da 100644 --- a/firmware/common/sgpio.h +++ b/firmware/common/sgpio.h @@ -41,6 +41,7 @@ typedef struct { bool slice_mode_multislice; } sgpio_config_t; +void sgpio_pin_shutdown(sgpio_config_t* const config); void sgpio_configure_pin_functions(sgpio_config_t* const config); void sgpio_test_interface(sgpio_config_t* const config); void sgpio_set_slice_mode(sgpio_config_t* const config, const bool multi_slice); From e8cfb86e10bf06a2bd1d8a61f19747ccffaecc06 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 28 Apr 2026 14:52:13 +0100 Subject: [PATCH 108/173] Add picoprintf to RAD1O firmware build. Code taken from https://github.com/rom-p/picoprintf @ dfb7f93. --- firmware/common/picobool.h | 20 ++ firmware/common/picoprintf.c | 342 +++++++++++++++++++++++++++++ firmware/common/picoprintf.h | 43 ++++ firmware/hackrf_usb/CMakeLists.txt | 1 + 4 files changed, 406 insertions(+) create mode 100644 firmware/common/picobool.h create mode 100644 firmware/common/picoprintf.c create mode 100644 firmware/common/picoprintf.h diff --git a/firmware/common/picobool.h b/firmware/common/picobool.h new file mode 100644 index 00000000..3f0324c8 --- /dev/null +++ b/firmware/common/picobool.h @@ -0,0 +1,20 @@ +#ifndef __picobool_h_INCLUDED__ +#define __picobool_h_INCLUDED__ + +// this file provides a bool type which might or might not be available on your platform + +#ifndef __cplusplus // C++: use standard bool + #ifndef __STDC_VERSION__ // Pre-C99: define bool manually + typedef unsigned char bool; + #define true 1 + #define false 0 + #elif __STDC_VERSION__ < 199901L // C90/C95: define bool manually + typedef unsigned char bool; + #define true 1 + #define false 0 + #else // C99+: use standard bool + #include + #endif +#endif + +#endif // __picobool_h_INCLUDED__ diff --git a/firmware/common/picoprintf.c b/firmware/common/picoprintf.c new file mode 100644 index 00000000..18ef565c --- /dev/null +++ b/firmware/common/picoprintf.c @@ -0,0 +1,342 @@ +#include "picoprintf.h" +#include "picobool.h" + +#include // fabs() +#include // llabs() + + +static void flip(char *pLeft, char *pRight) { + pRight--; + while (pLeft < pRight) { + char tmp = *pLeft; + *pLeft = *pRight; + *pRight = tmp; + pLeft++; + pRight--; + } +} + +// using #define over `inline` for enabling porting to old C +#if !defined(MIN) +#define MIN(left, right) (((left) < (right)) ? (left) : (right)) +#endif +#if !defined(MAX) +#define MAX(left, right) (((left) > (right)) ? (left) : (right)) +#endif + + +// returns the pointer to the null-terminating character of the filled string +int pico_vsnprintf(char *pDest, size_t cbDest, const char *pFormat, va_list vl) { + const char *pLowercaseNumberDigits = "0123456789abcdef"; + const char *pUppercaseNumberDigits = "0123456789ABCDEF"; + const char *pOctalDigits = pLowercaseNumberDigits; + const char *pBinaryDigits = pLowercaseNumberDigits; + char *pStart = pDest; + for (char *pEnd = pDest + cbDest - 1; *pFormat && pDest < pEnd; ) { + if (*pFormat != '%') { + *pDest++ = *pFormat++; + } else { // format starts here + pFormat++; // skipping the '%' + if (*pFormat == '%') { // unless it's indeed a '%' + *pDest++ = *pFormat++; + } else { // first, collect the format + char format = '\0'; + int bits_per_digit = 0; // valid in 'b', 'o', 'p' and 'x'/'X' modes only + int whole_chars = 0; // number of chars in the whole part of the number + int decimal_chars = -1; // if not specified, %f are rendered with 6, while %g are rendered with 0 + #ifdef __aarch64__ // these platforms benefit from packing flags into a single variable + struct { + unsigned force_sign:1; + unsigned fill_zeros:1; + unsigned left_align:1; + unsigned seen_period:1; + unsigned seen_numbers:1; + unsigned treat_as_unsigned:1; + unsigned treat_as_long:1; + unsigned render_in_lowercase:1; + } flags = {0}; + #define FLAGS flags. + + #else // other platforms do not benefit from struct packing + unsigned force_sign = 0; + unsigned fill_zeros = 0; + unsigned left_align = 0; + unsigned seen_period = 0; + unsigned seen_numbers = 0; + unsigned treat_as_unsigned = 0; + unsigned treat_as_long = 0; + unsigned render_in_lowercase = 0; + #define FLAGS + #endif // struct packing platforms + for (; *pFormat && '\0' == format; pFormat++) { + switch (*pFormat) { + #ifdef PICOFORMAT_HANDLE_FORCEDSIGN + case '+': + FLAGS force_sign = 1; + break; + #endif // PICOFORMAT_HANDLE_FORCEDSIGN + #ifdef PICOFORMAT_HANDLE_FILL + case '-': // left-align flag + FLAGS left_align = 1; + break; + #endif // PICOFORMAT_HANDLE_FILL + case '0': + if (!FLAGS seen_numbers) { + FLAGS fill_zeros = 1; + break; + } + // fall through + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + *(FLAGS seen_period ? &decimal_chars : &whole_chars ) *= 10; + *(FLAGS seen_period ? &decimal_chars : &whole_chars) += *pFormat - '0'; + FLAGS seen_numbers = 1; + break; + case '.': + FLAGS seen_numbers = FLAGS seen_period = 1; + decimal_chars = 0; + break; + case '*': // dynamic width/precision: read value from arg list + *(FLAGS seen_period ? &decimal_chars : &whole_chars) = va_arg(vl, int); + FLAGS seen_numbers = 1; + break; + case 'l': // long modifier + FLAGS treat_as_long = 1; + break; + case 'u': // unsigned decimal integer + FLAGS treat_as_unsigned = 1; + format = 'd'; + break; + #ifdef PICOFORMAT_HANDLE_BIN + case 'b': + bits_per_digit = 0; + FLAGS treat_as_unsigned = 1; + format = 'b'; + break; + #endif // PICOFORMAT_HANDLE_BIN + #ifdef PICOFORMAT_HANDLE_OCT + case 'o': // octal integer + bits_per_digit = 3; + FLAGS treat_as_unsigned = 1; + format = 'b'; + break; + #endif // PICOFORMAT_HANDLE_OCT + #ifdef PICOFORMAT_HANDLE_HEX + case 'x': // hexadecimal integer + case 'p': // pointer + bits_per_digit = 4; + FLAGS treat_as_unsigned = 1; + FLAGS render_in_lowercase = 1; + format = 'b'; + break; + case 'X': // hexadecimal integer, uppercase + bits_per_digit = 4; + FLAGS treat_as_unsigned = 1; + format = 'b'; + break; + #endif // PICOFORMAT_HANDLE_HEX + #ifdef PICOFORMAT_HANDLE_FLOATS + case 'a': + #ifdef PICOFORMAT_HANDLE_EXPONENTS + case 'e': // floating point, exponent format + #endif // PICOFORMAT_HANDLE_EXPONENTS + case 'f': + FLAGS render_in_lowercase = 1; + // fall through + case 'F': + #endif // PICOFORMAT_HANDLE_FLOATS + case 'c': // single char: always supported + case 'd': // integer: always supported + case 'i': // integer: always supported + case 's': // string of native `char`s: always supported + format = *pFormat; + break; + default: + FORMAT_ERROR_DELEGATE("detected unhandled format specifier: %c", *pFormat); + break; + } + } + + // format is parsed, now render the value + char *pParamStarts = pDest; + switch (format) { + case 'c': // single char + *pDest++ = va_arg(vl, int) & 0xff; + break; + case 's': { // null-terminated string + #ifdef PICOFORMAT_HANDLE_FILL + int len = 0; // effective length, bounded by precision if set + const char *pStr = va_arg(vl, const char*); + for (; pStr[len] && (decimal_chars < 0 || len < decimal_chars); len++); + if (!FLAGS left_align) { // right-align: pad on the left + #ifdef PICOFORMAT_CLANG_QUIRK // clang's non-standard: '0' flag zero-pads strings + char chFill = FLAGS fill_zeros ? '0' : ' '; + #else // PICOFORMAT_CLANG_QUIRK // standard C: '0' flag is undefined for %s, use spaces + char chFill = ' '; + #endif // PICOFORMAT_CLANG_QUIRK + for (; pDest < pEnd && whole_chars > len; whole_chars--) { + *pDest++ = chFill; + } + } + for (int ii = 0; ii < len && pDest < pEnd; ii++) { + *pDest++ = pStr[ii]; + } + if (FLAGS left_align) { // left-align: pad on the right (always spaces; '-' flag overrides '0') + for (; pDest < pEnd && whole_chars > len; whole_chars--) { + *pDest++ = ' '; + } + } + #else // PICOFORMAT_HANDLE_FILL + for (const char *pStr = va_arg(vl, const char*); *pStr && pDest < pEnd; ) { + *pDest++ = *pStr++; + } + #endif // PICOFORMAT_HANDLE_FILL + } + break; + #if defined(PICOFORMAT_HANDLE_HEX) || defined(PICOFORMAT_HANDLE_OCT) || defined(PICOFORMAT_HANDLE_BIN) + case 'b': { // binary, oct, or hex integer, always unsigned + long long int val = 0; + if (FLAGS treat_as_long) { + val = va_arg(vl, unsigned long long int); + } else { + val = va_arg(vl, unsigned); + } + unsigned mask = bits_per_digit == 4 ? 0x0f : bits_per_digit == 3 ? 0x07 : 0x01; + #if defined(PICOFORMAT_HANDLE_HEX) + const char* chars = FLAGS render_in_lowercase ? pLowercaseNumberDigits : pUppercaseNumberDigits; + #elif defined(PICOFORMAT_HANDLE_OCT) + const char* chars = pOctalDigits; + #else + const char* chars = pBinaryDigits; + #endif // individual non-decimal formats + while (pDest < pEnd + && (pDest == pParamStarts + || val + || pDest - pParamStarts < whole_chars)) { + char ch; + if (pDest == pParamStarts || 0 != val) { // if first char or there's still meaningful digits + ch = chars[val & mask]; + val >>= bits_per_digit; + } else { + ch = FLAGS fill_zeros ? '0' : ' '; + } + *pDest++ = ch; + } + flip(pParamStarts, pDest); + } + break; + #endif // defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || defined(PICOFORMAT_HANDLE_HEX) + case 'd': // decimal integer + case 'i': { + long long int val = 0; + if (FLAGS treat_as_long) { + if (FLAGS treat_as_unsigned) { + val = va_arg(vl, unsigned long long int); + } else { + val = va_arg(vl, long long int); + } + } else { + if (FLAGS treat_as_unsigned) { + val = va_arg(vl, unsigned); + } else { + val = va_arg(vl, int); + } + } + char chSign = '\0'; + if ((FLAGS force_sign || val < 0) && pDest < pEnd) { + chSign = val < 0 ? '-' : '+'; + val = llabs(val); + whole_chars--; + } + while (pDest < pEnd + && (pDest == pParamStarts + || val + || pDest - pParamStarts < whole_chars)) { + char ch; + if (pDest == pParamStarts || 0 != val) { // if first digit (i.e. zero) or there's still non-zero digits to write + ch = val % 10 + '0'; + val /= 10; + } else { + if ('\0' != chSign && !FLAGS fill_zeros) { // write a sign if filling with spaces, not with zeros + *pDest++ = chSign; + whole_chars++; + chSign = '\0'; // prevent the sign from being written twice + } + #ifdef PICOFORMAT_HANDLE_FILL + ch = FLAGS fill_zeros ? '0' : ' '; + #else // PICOFORMAT_HANDLE_FILL + break; + #endif // PICOFORMAT_HANDLE_FILL + } + *pDest++ = ch; + } + if ('\0' != chSign) { // write a sign if it wasn't written before + *pDest++ = chSign; + } + flip(pParamStarts, pDest); + } + break; + #ifdef PICOFORMAT_HANDLE_FLOATS + case 'f': case 'F': { + double val = va_arg(vl, double); + if (FLAGS force_sign || val < 0.f) { + *pDest++ = val < 0.f ? '-' : '+'; + val = fabs(val); + pParamStarts = pDest; + } + if (val == INFINITY || val == -INFINITY || isnan(val)) { + for (const char *pszVal = isnan(val) ? FLAGS render_in_lowercase ? "nan" : "NAN": FLAGS render_in_lowercase ? "inf" : "INF" + ; pDest < pEnd && *pszVal + ; *pDest++ = *pszVal++); + } else { + if (decimal_chars == -1) { + if (format == 'g') { + decimal_chars = 0; + } else { + decimal_chars = 6; + } + } + whole_chars = MAX(0, whole_chars - decimal_chars); + whole_chars = MAX(whole_chars, pDest - pParamStarts + 1); // at least sign (if present) and first char + for (float digit = 1.f + ; pDest < pEnd && (val >= digit || pDest - pParamStarts < whole_chars) + ; digit *= 10.f) { + char ch = (int)(val / digit) % 10 + '0'; + *pDest++ = ch; + } + flip(pParamStarts, pDest); + if (decimal_chars && pDest < pEnd) { + *pDest++ = '.'; + val += .5f * pow(10.f, -decimal_chars); // compensating for rounding error + for (size_t digit = 0; pDest < pEnd && digit < decimal_chars; digit++) { + val = (val - (int)(val)) * 10.f; + *pDest++ = (int)(val) + '0'; + } + } + } + } + break; + #endif // PICOFORMAT_HANDLE_FLOATS + } + } + } + } + *pDest = '\0'; + return pDest - pStart; +} + + +int pico_snprintf(char *pDest, size_t cbDest, const char *pFormat, ...) { + va_list vl; + va_start(vl, pFormat); + int result = pico_vsnprintf(pDest, cbDest, pFormat, vl); + va_end(vl); + return result; +} diff --git a/firmware/common/picoprintf.h b/firmware/common/picoprintf.h new file mode 100644 index 00000000..82796502 --- /dev/null +++ b/firmware/common/picoprintf.h @@ -0,0 +1,43 @@ +#ifndef __picoprintf_h_INCLUDED__ +#define __picoprintf_h_INCLUDED__ + +#include // size_t +#include // va_* + + +int pico_snprintf(char *pDest, size_t cbDest, const char *pFormat, ...); +int pico_vsnprintf(char *pDest, size_t cbDest, const char *pFormat, va_list vl); + +// PLEASE use `pico_snprintf()` instead!!! This function is vulnerable to buffer overflows +inline int pico_sprintf(char *pDest, const char *pFormat, ...) { + va_list vl; + va_start(vl, pFormat); + int result = pico_vsnprintf(pDest, -1, pFormat, vl); + va_end(vl); + return result; +} + + +// +// IMPORTANT!!! +// comment/uncomment the following lines corresponding to the features you need +// +// #define PICOFORMAT_HANDLE_FILL // uncomment this line to handle "%6i" and "%04d" -- the fill with zeroes or spaces +// #define PICOFORMAT_HANDLE_FORCEDSIGN // uncomment this line to handle "%+d" -- the forced sign placement +// #define PICOFORMAT_HANDLE_BIN // uncomment this line to handle "%b" -- binary representation +// #define PICOFORMAT_HANDLE_OCT // uncomment this line to handle "%o" +// #define PICOFORMAT_HANDLE_HEX // uncomment this line to handle "%x" and "%X" +// #define PICOFORMAT_HANDLE_FLOATS // uncomment this line to handle the "%f" +// #define PICOFORMAT_CLANG_QUIRK // uncomment this line to match clang's non-standard "%010s" behavior (zero-pad strings when both '0' flag and width are set) + + +// by default, the debug builds (determined by `#define _DEBUG`) will real-time print errors when a feature is used that is not enabled above +#ifndef FORMAT_ERROR_DELEGATE + #ifdef _DEBUG + #define FORMAT_ERROR_DELEGATE(__message, __arg) printf(__message, __arg); printf("\n"); + #else // _DEBUG + #define FORMAT_ERROR_DELEGATE(__message, __arg) + #endif // _DEBUG +#endif // FORMAT_ERROR_DELEGATE + +#endif // __picoprintf_h_INCLUDED__ diff --git a/firmware/hackrf_usb/CMakeLists.txt b/firmware/hackrf_usb/CMakeLists.txt index 3fd3ea87..dba0120e 100644 --- a/firmware/hackrf_usb/CMakeLists.txt +++ b/firmware/hackrf_usb/CMakeLists.txt @@ -136,6 +136,7 @@ if(BOARD STREQUAL "RAD1O") "${PATH_HACKRF_FIRMWARE_COMMON}/rad1o/smallfonts.c" "${PATH_HACKRF_FIRMWARE_COMMON}/rad1o/draw.c" "${PATH_HACKRF_FIRMWARE_COMMON}/rad1o/ubuntu18.c" + "${PATH_HACKRF_FIRMWARE_COMMON}/picoprintf.c" "${PATH_HACKRF_FIRMWARE_COMMON}/ui_rad1o.c" ) endif() From 58334a565b2a93a5b33663732709512289f33471 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 28 Apr 2026 15:42:18 +0100 Subject: [PATCH 109/173] picoprintf: remove unnecessary picobool.h. We don't require workarounds for ancient C environments. --- firmware/common/picobool.h | 20 -------------------- firmware/common/picoprintf.c | 1 - 2 files changed, 21 deletions(-) delete mode 100644 firmware/common/picobool.h diff --git a/firmware/common/picobool.h b/firmware/common/picobool.h deleted file mode 100644 index 3f0324c8..00000000 --- a/firmware/common/picobool.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __picobool_h_INCLUDED__ -#define __picobool_h_INCLUDED__ - -// this file provides a bool type which might or might not be available on your platform - -#ifndef __cplusplus // C++: use standard bool - #ifndef __STDC_VERSION__ // Pre-C99: define bool manually - typedef unsigned char bool; - #define true 1 - #define false 0 - #elif __STDC_VERSION__ < 199901L // C90/C95: define bool manually - typedef unsigned char bool; - #define true 1 - #define false 0 - #else // C99+: use standard bool - #include - #endif -#endif - -#endif // __picobool_h_INCLUDED__ diff --git a/firmware/common/picoprintf.c b/firmware/common/picoprintf.c index 18ef565c..185ccdfb 100644 --- a/firmware/common/picoprintf.c +++ b/firmware/common/picoprintf.c @@ -1,5 +1,4 @@ #include "picoprintf.h" -#include "picobool.h" #include // fabs() #include // llabs() From 5b4392f609633877ac64b351a6aa362cf73cedb3 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 28 Apr 2026 15:44:09 +0100 Subject: [PATCH 110/173] picoprintf: add LICENSE text to source & header files. --- firmware/common/picoprintf.c | 23 +++++++++++++++++++++++ firmware/common/picoprintf.h | 22 ++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/firmware/common/picoprintf.c b/firmware/common/picoprintf.c index 185ccdfb..f71af487 100644 --- a/firmware/common/picoprintf.c +++ b/firmware/common/picoprintf.c @@ -1,3 +1,26 @@ +/* MIT License + * + * Copyright (c) 2023-2026 rom-p + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "picoprintf.h" #include // fabs() diff --git a/firmware/common/picoprintf.h b/firmware/common/picoprintf.h index 82796502..96ac9b14 100644 --- a/firmware/common/picoprintf.h +++ b/firmware/common/picoprintf.h @@ -1,3 +1,25 @@ +/* + * Copyright (c) 2023-2026 rom-p + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #ifndef __picoprintf_h_INCLUDED__ #define __picoprintf_h_INCLUDED__ From 83fd6a00a81dac8863423346788ecc35b848e725 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 28 Apr 2026 15:45:16 +0100 Subject: [PATCH 111/173] picoprintf: Fix unused variable warnings with GCC 15.2. --- firmware/common/picoprintf.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/firmware/common/picoprintf.c b/firmware/common/picoprintf.c index f71af487..64446c3c 100644 --- a/firmware/common/picoprintf.c +++ b/firmware/common/picoprintf.c @@ -23,9 +23,11 @@ #include "picoprintf.h" -#include // fabs() #include // llabs() +#ifdef PICOFORMAT_HANDLE_FLOATS + #include // fabs() +#endif static void flip(char *pLeft, char *pRight) { pRight--; @@ -49,10 +51,16 @@ static void flip(char *pLeft, char *pRight) { // returns the pointer to the null-terminating character of the filled string int pico_vsnprintf(char *pDest, size_t cbDest, const char *pFormat, va_list vl) { +#if defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || defined(PICOFORMAT_HANDLE_HEX) const char *pLowercaseNumberDigits = "0123456789abcdef"; +#endif +#if defined(PICOFORMAT_HANDLE_HEX) const char *pUppercaseNumberDigits = "0123456789ABCDEF"; +#elif defined(PICOFORMAT_HANDLE_OCT) const char *pOctalDigits = pLowercaseNumberDigits; +#elif defined(PICOFORMAT_HANDLE_BIN) const char *pBinaryDigits = pLowercaseNumberDigits; +#endif char *pStart = pDest; for (char *pEnd = pDest + cbDest - 1; *pFormat && pDest < pEnd; ) { if (*pFormat != '%') { @@ -63,7 +71,9 @@ int pico_vsnprintf(char *pDest, size_t cbDest, const char *pFormat, va_list vl) *pDest++ = *pFormat++; } else { // first, collect the format char format = '\0'; + #if defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || defined(PICOFORMAT_HANDLE_HEX) int bits_per_digit = 0; // valid in 'b', 'o', 'p' and 'x'/'X' modes only + #endif int whole_chars = 0; // number of chars in the whole part of the number int decimal_chars = -1; // if not specified, %f are rendered with 6, while %g are rendered with 0 #ifdef __aarch64__ // these platforms benefit from packing flags into a single variable @@ -82,12 +92,16 @@ int pico_vsnprintf(char *pDest, size_t cbDest, const char *pFormat, va_list vl) #else // other platforms do not benefit from struct packing unsigned force_sign = 0; unsigned fill_zeros = 0; + #ifdef PICOFORMAT_HANDLE_FILL unsigned left_align = 0; + #endif unsigned seen_period = 0; unsigned seen_numbers = 0; unsigned treat_as_unsigned = 0; unsigned treat_as_long = 0; + #if defined(PICOFORMAT_HANDLE_HEX) || defined(PICOFORMAT_HANDLE_FLOATS) unsigned render_in_lowercase = 0; + #endif #define FLAGS #endif // struct packing platforms for (; *pFormat && '\0' == format; pFormat++) { @@ -235,7 +249,7 @@ int pico_vsnprintf(char *pDest, size_t cbDest, const char *pFormat, va_list vl) const char* chars = FLAGS render_in_lowercase ? pLowercaseNumberDigits : pUppercaseNumberDigits; #elif defined(PICOFORMAT_HANDLE_OCT) const char* chars = pOctalDigits; - #else + #elif defined(PICOFORMAT_HANDLE_BIN) const char* chars = pBinaryDigits; #endif // individual non-decimal formats while (pDest < pEnd From a20da328ba03eb3f06aac33eda372f1e061e6d32 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 28 Apr 2026 15:45:57 +0100 Subject: [PATCH 112/173] picoprintf: use #pragma once. --- firmware/common/picoprintf.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/firmware/common/picoprintf.h b/firmware/common/picoprintf.h index 96ac9b14..0fb5438b 100644 --- a/firmware/common/picoprintf.h +++ b/firmware/common/picoprintf.h @@ -20,8 +20,7 @@ * SOFTWARE. */ -#ifndef __picoprintf_h_INCLUDED__ -#define __picoprintf_h_INCLUDED__ +#pragma once #include // size_t #include // va_* @@ -61,5 +60,3 @@ inline int pico_sprintf(char *pDest, const char *pFormat, ...) { #define FORMAT_ERROR_DELEGATE(__message, __arg) #endif // _DEBUG #endif // FORMAT_ERROR_DELEGATE - -#endif // __picoprintf_h_INCLUDED__ From f970722195856b5f910f393b8871becef6061d4f Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 28 Apr 2026 16:36:14 +0100 Subject: [PATCH 113/173] picoprintf: Standardize code style. --- firmware/common/picoprintf.c | 715 +++++++++++++++++++---------------- firmware/common/picoprintf.h | 38 +- 2 files changed, 404 insertions(+), 349 deletions(-) diff --git a/firmware/common/picoprintf.c b/firmware/common/picoprintf.c index 64446c3c..1959edb5 100644 --- a/firmware/common/picoprintf.c +++ b/firmware/common/picoprintf.c @@ -23,356 +23,411 @@ #include "picoprintf.h" -#include // llabs() +#include // llabs() #ifdef PICOFORMAT_HANDLE_FLOATS - #include // fabs() + #include // fabs() #endif -static void flip(char *pLeft, char *pRight) { - pRight--; - while (pLeft < pRight) { - char tmp = *pLeft; - *pLeft = *pRight; - *pRight = tmp; - pLeft++; - pRight--; - } +static void flip(char* pLeft, char* pRight) +{ + pRight--; + while (pLeft < pRight) { + char tmp = *pLeft; + *pLeft = *pRight; + *pRight = tmp; + pLeft++; + pRight--; + } } // using #define over `inline` for enabling porting to old C #if !defined(MIN) -#define MIN(left, right) (((left) < (right)) ? (left) : (right)) + #define MIN(left, right) (((left) < (right)) ? (left) : (right)) #endif #if !defined(MAX) -#define MAX(left, right) (((left) > (right)) ? (left) : (right)) + #define MAX(left, right) (((left) > (right)) ? (left) : (right)) #endif - // returns the pointer to the null-terminating character of the filled string -int pico_vsnprintf(char *pDest, size_t cbDest, const char *pFormat, va_list vl) { -#if defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || defined(PICOFORMAT_HANDLE_HEX) - const char *pLowercaseNumberDigits = "0123456789abcdef"; +int pico_vsnprintf(char* pDest, size_t cbDest, const char* pFormat, va_list vl) +{ +#if defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || \ + defined(PICOFORMAT_HANDLE_HEX) + const char* pLowercaseNumberDigits = "0123456789abcdef"; #endif #if defined(PICOFORMAT_HANDLE_HEX) - const char *pUppercaseNumberDigits = "0123456789ABCDEF"; + const char* pUppercaseNumberDigits = "0123456789ABCDEF"; #elif defined(PICOFORMAT_HANDLE_OCT) - const char *pOctalDigits = pLowercaseNumberDigits; + const char* pOctalDigits = pLowercaseNumberDigits; #elif defined(PICOFORMAT_HANDLE_BIN) - const char *pBinaryDigits = pLowercaseNumberDigits; + const char* pBinaryDigits = pLowercaseNumberDigits; #endif - char *pStart = pDest; - for (char *pEnd = pDest + cbDest - 1; *pFormat && pDest < pEnd; ) { - if (*pFormat != '%') { - *pDest++ = *pFormat++; - } else { // format starts here - pFormat++; // skipping the '%' - if (*pFormat == '%') { // unless it's indeed a '%' - *pDest++ = *pFormat++; - } else { // first, collect the format - char format = '\0'; - #if defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || defined(PICOFORMAT_HANDLE_HEX) - int bits_per_digit = 0; // valid in 'b', 'o', 'p' and 'x'/'X' modes only - #endif - int whole_chars = 0; // number of chars in the whole part of the number - int decimal_chars = -1; // if not specified, %f are rendered with 6, while %g are rendered with 0 - #ifdef __aarch64__ // these platforms benefit from packing flags into a single variable - struct { - unsigned force_sign:1; - unsigned fill_zeros:1; - unsigned left_align:1; - unsigned seen_period:1; - unsigned seen_numbers:1; - unsigned treat_as_unsigned:1; - unsigned treat_as_long:1; - unsigned render_in_lowercase:1; - } flags = {0}; - #define FLAGS flags. + char* pStart = pDest; + for (char* pEnd = pDest + cbDest - 1; *pFormat && pDest < pEnd;) { + if (*pFormat != '%') { + *pDest++ = *pFormat++; + } else { // format starts here + pFormat++; // skipping the '%' + if (*pFormat == '%') { // unless it's indeed a '%' + *pDest++ = *pFormat++; + } else { // first, collect the format + char format = '\0'; +#if defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || \ + defined(PICOFORMAT_HANDLE_HEX) + int bits_per_digit = + 0; // valid in 'b', 'o', 'p' and 'x'/'X' modes only +#endif + int whole_chars = + 0; // number of chars in the whole part of the number + int decimal_chars = + -1; // if not specified, %f are rendered with 6, while %g are rendered with 0 +#ifdef __aarch64__ // these platforms benefit from packing flags into a single variable + struct { + unsigned force_sign : 1; + unsigned fill_zeros : 1; + unsigned left_align : 1; + unsigned seen_period : 1; + unsigned seen_numbers : 1; + unsigned treat_as_unsigned : 1; + unsigned treat_as_long : 1; + unsigned render_in_lowercase : 1; + } flags = {0}; - #else // other platforms do not benefit from struct packing - unsigned force_sign = 0; - unsigned fill_zeros = 0; - #ifdef PICOFORMAT_HANDLE_FILL - unsigned left_align = 0; - #endif - unsigned seen_period = 0; - unsigned seen_numbers = 0; - unsigned treat_as_unsigned = 0; - unsigned treat_as_long = 0; - #if defined(PICOFORMAT_HANDLE_HEX) || defined(PICOFORMAT_HANDLE_FLOATS) - unsigned render_in_lowercase = 0; - #endif - #define FLAGS - #endif // struct packing platforms - for (; *pFormat && '\0' == format; pFormat++) { - switch (*pFormat) { - #ifdef PICOFORMAT_HANDLE_FORCEDSIGN - case '+': - FLAGS force_sign = 1; - break; - #endif // PICOFORMAT_HANDLE_FORCEDSIGN - #ifdef PICOFORMAT_HANDLE_FILL - case '-': // left-align flag - FLAGS left_align = 1; - break; - #endif // PICOFORMAT_HANDLE_FILL - case '0': - if (!FLAGS seen_numbers) { - FLAGS fill_zeros = 1; - break; - } - // fall through - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - *(FLAGS seen_period ? &decimal_chars : &whole_chars ) *= 10; - *(FLAGS seen_period ? &decimal_chars : &whole_chars) += *pFormat - '0'; - FLAGS seen_numbers = 1; - break; - case '.': - FLAGS seen_numbers = FLAGS seen_period = 1; - decimal_chars = 0; - break; - case '*': // dynamic width/precision: read value from arg list - *(FLAGS seen_period ? &decimal_chars : &whole_chars) = va_arg(vl, int); - FLAGS seen_numbers = 1; - break; - case 'l': // long modifier - FLAGS treat_as_long = 1; - break; - case 'u': // unsigned decimal integer - FLAGS treat_as_unsigned = 1; - format = 'd'; - break; - #ifdef PICOFORMAT_HANDLE_BIN - case 'b': - bits_per_digit = 0; - FLAGS treat_as_unsigned = 1; - format = 'b'; - break; - #endif // PICOFORMAT_HANDLE_BIN - #ifdef PICOFORMAT_HANDLE_OCT - case 'o': // octal integer - bits_per_digit = 3; - FLAGS treat_as_unsigned = 1; - format = 'b'; - break; - #endif // PICOFORMAT_HANDLE_OCT - #ifdef PICOFORMAT_HANDLE_HEX - case 'x': // hexadecimal integer - case 'p': // pointer - bits_per_digit = 4; - FLAGS treat_as_unsigned = 1; - FLAGS render_in_lowercase = 1; - format = 'b'; - break; - case 'X': // hexadecimal integer, uppercase - bits_per_digit = 4; - FLAGS treat_as_unsigned = 1; - format = 'b'; - break; - #endif // PICOFORMAT_HANDLE_HEX - #ifdef PICOFORMAT_HANDLE_FLOATS - case 'a': - #ifdef PICOFORMAT_HANDLE_EXPONENTS - case 'e': // floating point, exponent format - #endif // PICOFORMAT_HANDLE_EXPONENTS - case 'f': - FLAGS render_in_lowercase = 1; - // fall through - case 'F': - #endif // PICOFORMAT_HANDLE_FLOATS - case 'c': // single char: always supported - case 'd': // integer: always supported - case 'i': // integer: always supported - case 's': // string of native `char`s: always supported - format = *pFormat; - break; - default: - FORMAT_ERROR_DELEGATE("detected unhandled format specifier: %c", *pFormat); - break; - } - } + #define FLAGS flags. - // format is parsed, now render the value - char *pParamStarts = pDest; - switch (format) { - case 'c': // single char - *pDest++ = va_arg(vl, int) & 0xff; - break; - case 's': { // null-terminated string - #ifdef PICOFORMAT_HANDLE_FILL - int len = 0; // effective length, bounded by precision if set - const char *pStr = va_arg(vl, const char*); - for (; pStr[len] && (decimal_chars < 0 || len < decimal_chars); len++); - if (!FLAGS left_align) { // right-align: pad on the left - #ifdef PICOFORMAT_CLANG_QUIRK // clang's non-standard: '0' flag zero-pads strings - char chFill = FLAGS fill_zeros ? '0' : ' '; - #else // PICOFORMAT_CLANG_QUIRK // standard C: '0' flag is undefined for %s, use spaces - char chFill = ' '; - #endif // PICOFORMAT_CLANG_QUIRK - for (; pDest < pEnd && whole_chars > len; whole_chars--) { - *pDest++ = chFill; - } - } - for (int ii = 0; ii < len && pDest < pEnd; ii++) { - *pDest++ = pStr[ii]; - } - if (FLAGS left_align) { // left-align: pad on the right (always spaces; '-' flag overrides '0') - for (; pDest < pEnd && whole_chars > len; whole_chars--) { - *pDest++ = ' '; - } - } - #else // PICOFORMAT_HANDLE_FILL - for (const char *pStr = va_arg(vl, const char*); *pStr && pDest < pEnd; ) { - *pDest++ = *pStr++; - } - #endif // PICOFORMAT_HANDLE_FILL - } - break; - #if defined(PICOFORMAT_HANDLE_HEX) || defined(PICOFORMAT_HANDLE_OCT) || defined(PICOFORMAT_HANDLE_BIN) - case 'b': { // binary, oct, or hex integer, always unsigned - long long int val = 0; - if (FLAGS treat_as_long) { - val = va_arg(vl, unsigned long long int); - } else { - val = va_arg(vl, unsigned); - } - unsigned mask = bits_per_digit == 4 ? 0x0f : bits_per_digit == 3 ? 0x07 : 0x01; - #if defined(PICOFORMAT_HANDLE_HEX) - const char* chars = FLAGS render_in_lowercase ? pLowercaseNumberDigits : pUppercaseNumberDigits; - #elif defined(PICOFORMAT_HANDLE_OCT) - const char* chars = pOctalDigits; - #elif defined(PICOFORMAT_HANDLE_BIN) - const char* chars = pBinaryDigits; - #endif // individual non-decimal formats - while (pDest < pEnd - && (pDest == pParamStarts - || val - || pDest - pParamStarts < whole_chars)) { - char ch; - if (pDest == pParamStarts || 0 != val) { // if first char or there's still meaningful digits - ch = chars[val & mask]; - val >>= bits_per_digit; - } else { - ch = FLAGS fill_zeros ? '0' : ' '; - } - *pDest++ = ch; - } - flip(pParamStarts, pDest); - } - break; - #endif // defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || defined(PICOFORMAT_HANDLE_HEX) - case 'd': // decimal integer - case 'i': { - long long int val = 0; - if (FLAGS treat_as_long) { - if (FLAGS treat_as_unsigned) { - val = va_arg(vl, unsigned long long int); - } else { - val = va_arg(vl, long long int); - } - } else { - if (FLAGS treat_as_unsigned) { - val = va_arg(vl, unsigned); - } else { - val = va_arg(vl, int); - } - } - char chSign = '\0'; - if ((FLAGS force_sign || val < 0) && pDest < pEnd) { - chSign = val < 0 ? '-' : '+'; - val = llabs(val); - whole_chars--; - } - while (pDest < pEnd - && (pDest == pParamStarts - || val - || pDest - pParamStarts < whole_chars)) { - char ch; - if (pDest == pParamStarts || 0 != val) { // if first digit (i.e. zero) or there's still non-zero digits to write - ch = val % 10 + '0'; - val /= 10; - } else { - if ('\0' != chSign && !FLAGS fill_zeros) { // write a sign if filling with spaces, not with zeros - *pDest++ = chSign; - whole_chars++; - chSign = '\0'; // prevent the sign from being written twice - } - #ifdef PICOFORMAT_HANDLE_FILL - ch = FLAGS fill_zeros ? '0' : ' '; - #else // PICOFORMAT_HANDLE_FILL - break; - #endif // PICOFORMAT_HANDLE_FILL - } - *pDest++ = ch; - } - if ('\0' != chSign) { // write a sign if it wasn't written before - *pDest++ = chSign; - } - flip(pParamStarts, pDest); - } - break; - #ifdef PICOFORMAT_HANDLE_FLOATS - case 'f': case 'F': { - double val = va_arg(vl, double); - if (FLAGS force_sign || val < 0.f) { - *pDest++ = val < 0.f ? '-' : '+'; - val = fabs(val); - pParamStarts = pDest; - } - if (val == INFINITY || val == -INFINITY || isnan(val)) { - for (const char *pszVal = isnan(val) ? FLAGS render_in_lowercase ? "nan" : "NAN": FLAGS render_in_lowercase ? "inf" : "INF" - ; pDest < pEnd && *pszVal - ; *pDest++ = *pszVal++); - } else { - if (decimal_chars == -1) { - if (format == 'g') { - decimal_chars = 0; - } else { - decimal_chars = 6; - } - } - whole_chars = MAX(0, whole_chars - decimal_chars); - whole_chars = MAX(whole_chars, pDest - pParamStarts + 1); // at least sign (if present) and first char - for (float digit = 1.f - ; pDest < pEnd && (val >= digit || pDest - pParamStarts < whole_chars) - ; digit *= 10.f) { - char ch = (int)(val / digit) % 10 + '0'; - *pDest++ = ch; - } - flip(pParamStarts, pDest); - if (decimal_chars && pDest < pEnd) { - *pDest++ = '.'; - val += .5f * pow(10.f, -decimal_chars); // compensating for rounding error - for (size_t digit = 0; pDest < pEnd && digit < decimal_chars; digit++) { - val = (val - (int)(val)) * 10.f; - *pDest++ = (int)(val) + '0'; - } - } - } - } - break; - #endif // PICOFORMAT_HANDLE_FLOATS - } - } - } - } - *pDest = '\0'; - return pDest - pStart; +#else // other platforms do not benefit from struct packing + unsigned force_sign = 0; + unsigned fill_zeros = 0; + #ifdef PICOFORMAT_HANDLE_FILL + unsigned left_align = 0; + #endif + unsigned seen_period = 0; + unsigned seen_numbers = 0; + unsigned treat_as_unsigned = 0; + unsigned treat_as_long = 0; + #if defined(PICOFORMAT_HANDLE_HEX) || defined(PICOFORMAT_HANDLE_FLOATS) + unsigned render_in_lowercase = 0; + #endif + #define FLAGS +#endif // struct packing platforms + for (; *pFormat && '\0' == format; pFormat++) { + switch (*pFormat) { +#ifdef PICOFORMAT_HANDLE_FORCEDSIGN + case '+': + FLAGS force_sign = 1; + break; +#endif // PICOFORMAT_HANDLE_FORCEDSIGN +#ifdef PICOFORMAT_HANDLE_FILL + case '-': // left-align flag + FLAGS left_align = 1; + break; +#endif // PICOFORMAT_HANDLE_FILL + case '0': + if (!FLAGS seen_numbers) { + FLAGS fill_zeros = 1; + break; + } + // fall through + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + *(FLAGS seen_period ? &decimal_chars : + &whole_chars) *= 10; + *(FLAGS seen_period ? &decimal_chars : + &whole_chars) += + *pFormat - '0'; + FLAGS seen_numbers = 1; + break; + case '.': + FLAGS seen_numbers = FLAGS seen_period = + 1; + decimal_chars = 0; + break; + case '*': // dynamic width/precision: read value from arg list + *(FLAGS seen_period ? &decimal_chars : + &whole_chars) = + va_arg(vl, int); + FLAGS seen_numbers = 1; + break; + case 'l': // long modifier + FLAGS treat_as_long = 1; + break; + case 'u': // unsigned decimal integer + FLAGS treat_as_unsigned = 1; + format = 'd'; + break; +#ifdef PICOFORMAT_HANDLE_BIN + case 'b': + bits_per_digit = 0; + FLAGS treat_as_unsigned = 1; + format = 'b'; + break; +#endif // PICOFORMAT_HANDLE_BIN +#ifdef PICOFORMAT_HANDLE_OCT + case 'o': // octal integer + bits_per_digit = 3; + FLAGS treat_as_unsigned = 1; + format = 'b'; + break; +#endif // PICOFORMAT_HANDLE_OCT +#ifdef PICOFORMAT_HANDLE_HEX + case 'x': // hexadecimal integer + case 'p': // pointer + bits_per_digit = 4; + FLAGS treat_as_unsigned = 1; + FLAGS render_in_lowercase = 1; + format = 'b'; + break; + case 'X': // hexadecimal integer, uppercase + bits_per_digit = 4; + FLAGS treat_as_unsigned = 1; + format = 'b'; + break; +#endif // PICOFORMAT_HANDLE_HEX +#ifdef PICOFORMAT_HANDLE_FLOATS + case 'a': + #ifdef PICOFORMAT_HANDLE_EXPONENTS + case 'e': // floating point, exponent format + #endif // PICOFORMAT_HANDLE_EXPONENTS + case 'f': + FLAGS render_in_lowercase = 1; + // fall through + case 'F': +#endif // PICOFORMAT_HANDLE_FLOATS + case 'c': // single char: always supported + case 'd': // integer: always supported + case 'i': // integer: always supported + case 's': // string of native `char`s: always supported + format = *pFormat; + break; + default: + FORMAT_ERROR_DELEGATE( + "detected unhandled format specifier: %c", + *pFormat); + break; + } + } + + // format is parsed, now render the value + char* pParamStarts = pDest; + switch (format) { + case 'c': // single char + *pDest++ = va_arg(vl, int) & 0xff; + break; + case 's': { // null-terminated string +#ifdef PICOFORMAT_HANDLE_FILL + int len = + 0; // effective length, bounded by precision if set + const char* pStr = va_arg(vl, const char*); + for (; pStr[len] && + (decimal_chars < 0 || len < decimal_chars); + len++) + ; + if (!FLAGS left_align) { // right-align: pad on the left + #ifdef PICOFORMAT_CLANG_QUIRK // clang's non-standard: '0' flag zero-pads strings + char chFill = + FLAGS fill_zeros ? '0' : ' '; + #else // PICOFORMAT_CLANG_QUIRK // standard C: '0' flag is undefined for %s, use spaces + char chFill = ' '; + #endif // PICOFORMAT_CLANG_QUIRK + for (; pDest < pEnd && whole_chars > len; + whole_chars--) { + *pDest++ = chFill; + } + } + for (int ii = 0; ii < len && pDest < pEnd; ii++) { + *pDest++ = pStr[ii]; + } + if (FLAGS left_align) { // left-align: pad on the right (always spaces; '-' flag overrides '0') + for (; pDest < pEnd && whole_chars > len; + whole_chars--) { + *pDest++ = ' '; + } + } +#else // PICOFORMAT_HANDLE_FILL + for (const char* pStr = va_arg(vl, const char*); + *pStr && pDest < pEnd;) { + *pDest++ = *pStr++; + } +#endif // PICOFORMAT_HANDLE_FILL + } break; +#if defined(PICOFORMAT_HANDLE_HEX) || defined(PICOFORMAT_HANDLE_OCT) || \ + defined(PICOFORMAT_HANDLE_BIN) + case 'b': { // binary, oct, or hex integer, always unsigned + long long int val = 0; + if (FLAGS treat_as_long) { + val = va_arg(vl, unsigned long long int); + } else { + val = va_arg(vl, unsigned); + } + unsigned mask = bits_per_digit == 4 ? 0x0f : + bits_per_digit == 3 ? 0x07 : + 0x01; + #if defined(PICOFORMAT_HANDLE_HEX) + const char* chars = FLAGS render_in_lowercase ? + pLowercaseNumberDigits : + pUppercaseNumberDigits; + #elif defined(PICOFORMAT_HANDLE_OCT) + const char* chars = pOctalDigits; + #elif defined(PICOFORMAT_HANDLE_BIN) + const char* chars = pBinaryDigits; + #endif // individual non-decimal formats + while (pDest < pEnd && + (pDest == pParamStarts || val || + pDest - pParamStarts < whole_chars)) { + char ch; + if (pDest == pParamStarts || + 0 != val) { // if first char or there's still meaningful digits + ch = chars[val & mask]; + val >>= bits_per_digit; + } else { + ch = FLAGS fill_zeros ? '0' : ' '; + } + *pDest++ = ch; + } + flip(pParamStarts, pDest); + } break; +#endif // defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || defined(PICOFORMAT_HANDLE_HEX) + case 'd': // decimal integer + case 'i': { + long long int val = 0; + if (FLAGS treat_as_long) { + if (FLAGS treat_as_unsigned) { + val = va_arg( + vl, + unsigned long long int); + } else { + val = va_arg(vl, long long int); + } + } else { + if (FLAGS treat_as_unsigned) { + val = va_arg(vl, unsigned); + } else { + val = va_arg(vl, int); + } + } + char chSign = '\0'; + if ((FLAGS force_sign || val < 0) && + pDest < pEnd) { + chSign = val < 0 ? '-' : '+'; + val = llabs(val); + whole_chars--; + } + while (pDest < pEnd && + (pDest == pParamStarts || val || + pDest - pParamStarts < whole_chars)) { + char ch; + if (pDest == pParamStarts || + 0 != val) { // if first digit (i.e. zero) or there's still non-zero digits to write + ch = val % 10 + '0'; + val /= 10; + } else { + if ('\0' != chSign && + !FLAGS fill_zeros) { // write a sign if filling with spaces, not with zeros + *pDest++ = chSign; + whole_chars++; + chSign = + '\0'; // prevent the sign from being written twice + } +#ifdef PICOFORMAT_HANDLE_FILL + ch = FLAGS fill_zeros ? '0' : ' '; +#else // PICOFORMAT_HANDLE_FILL + break; +#endif // PICOFORMAT_HANDLE_FILL + } + *pDest++ = ch; + } + if ('\0' != + chSign) { // write a sign if it wasn't written before + *pDest++ = chSign; + } + flip(pParamStarts, pDest); + } break; +#ifdef PICOFORMAT_HANDLE_FLOATS + case 'f': + case 'F': { + double val = va_arg(vl, double); + if (FLAGS force_sign || val < 0.f) { + *pDest++ = val < 0.f ? '-' : '+'; + val = fabs(val); + pParamStarts = pDest; + } + if (val == INFINITY || val == -INFINITY || + isnan(val)) { + for (const char* pszVal = isnan(val) ? + FLAGS render_in_lowercase ? + "nan" : + "NAN" : + FLAGS render_in_lowercase ? + "inf" : + "INF"; + pDest < pEnd && *pszVal; + *pDest++ = *pszVal++) + ; + } else { + if (decimal_chars == -1) { + if (format == 'g') { + decimal_chars = 0; + } else { + decimal_chars = 6; + } + } + whole_chars = + MAX(0, + whole_chars - decimal_chars); + whole_chars = MAX( + whole_chars, + pDest - pParamStarts + + 1); // at least sign (if present) and first char + for (float digit = 1.f; pDest < pEnd && + (val >= digit || + pDest - pParamStarts < whole_chars); + digit *= 10.f) { + char ch = + (int) (val / digit) % 10 + + '0'; + *pDest++ = ch; + } + flip(pParamStarts, pDest); + if (decimal_chars && pDest < pEnd) { + *pDest++ = '.'; + val += .5f * + pow(10.f, + -decimal_chars); // compensating for rounding error + for (size_t digit = 0; + pDest < pEnd && + digit < decimal_chars; + digit++) { + val = (val - + (int) (val)) * + 10.f; + *pDest++ = + (int) (val) + '0'; + } + } + } + } break; +#endif // PICOFORMAT_HANDLE_FLOATS + } + } + } + } + *pDest = '\0'; + return pDest - pStart; } - -int pico_snprintf(char *pDest, size_t cbDest, const char *pFormat, ...) { - va_list vl; - va_start(vl, pFormat); - int result = pico_vsnprintf(pDest, cbDest, pFormat, vl); - va_end(vl); - return result; +int pico_snprintf(char* pDest, size_t cbDest, const char* pFormat, ...) +{ + va_list vl; + va_start(vl, pFormat); + int result = pico_vsnprintf(pDest, cbDest, pFormat, vl); + va_end(vl); + return result; } diff --git a/firmware/common/picoprintf.h b/firmware/common/picoprintf.h index 0fb5438b..aadb27ea 100644 --- a/firmware/common/picoprintf.h +++ b/firmware/common/picoprintf.h @@ -22,23 +22,22 @@ #pragma once -#include // size_t -#include // va_* +#include // size_t +#include // va_* - -int pico_snprintf(char *pDest, size_t cbDest, const char *pFormat, ...); -int pico_vsnprintf(char *pDest, size_t cbDest, const char *pFormat, va_list vl); +int pico_snprintf(char* pDest, size_t cbDest, const char* pFormat, ...); +int pico_vsnprintf(char* pDest, size_t cbDest, const char* pFormat, va_list vl); // PLEASE use `pico_snprintf()` instead!!! This function is vulnerable to buffer overflows -inline int pico_sprintf(char *pDest, const char *pFormat, ...) { - va_list vl; - va_start(vl, pFormat); - int result = pico_vsnprintf(pDest, -1, pFormat, vl); - va_end(vl); - return result; +inline int pico_sprintf(char* pDest, const char* pFormat, ...) +{ + va_list vl; + va_start(vl, pFormat); + int result = pico_vsnprintf(pDest, -1, pFormat, vl); + va_end(vl); + return result; } - // // IMPORTANT!!! // comment/uncomment the following lines corresponding to the features you need @@ -51,12 +50,13 @@ inline int pico_sprintf(char *pDest, const char *pFormat, ...) { // #define PICOFORMAT_HANDLE_FLOATS // uncomment this line to handle the "%f" // #define PICOFORMAT_CLANG_QUIRK // uncomment this line to match clang's non-standard "%010s" behavior (zero-pad strings when both '0' flag and width are set) - // by default, the debug builds (determined by `#define _DEBUG`) will real-time print errors when a feature is used that is not enabled above #ifndef FORMAT_ERROR_DELEGATE - #ifdef _DEBUG - #define FORMAT_ERROR_DELEGATE(__message, __arg) printf(__message, __arg); printf("\n"); - #else // _DEBUG - #define FORMAT_ERROR_DELEGATE(__message, __arg) - #endif // _DEBUG -#endif // FORMAT_ERROR_DELEGATE + #ifdef _DEBUG + #define FORMAT_ERROR_DELEGATE(__message, __arg) \ + printf(__message, __arg); \ + printf("\n"); + #else // _DEBUG + #define FORMAT_ERROR_DELEGATE(__message, __arg) + #endif // _DEBUG +#endif // FORMAT_ERROR_DELEGATE From ec1ff486b44e3c7f9a69aa293b0f5a6642047087 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 28 Apr 2026 15:22:06 +0100 Subject: [PATCH 114/173] picoprintf: Enable additional features. --- firmware/common/picoprintf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/common/picoprintf.h b/firmware/common/picoprintf.h index aadb27ea..313ee91d 100644 --- a/firmware/common/picoprintf.h +++ b/firmware/common/picoprintf.h @@ -42,11 +42,11 @@ inline int pico_sprintf(char* pDest, const char* pFormat, ...) // IMPORTANT!!! // comment/uncomment the following lines corresponding to the features you need // -// #define PICOFORMAT_HANDLE_FILL // uncomment this line to handle "%6i" and "%04d" -- the fill with zeroes or spaces +#define PICOFORMAT_HANDLE_FILL // uncomment this line to handle "%6i" and "%04d" -- the fill with zeroes or spaces // #define PICOFORMAT_HANDLE_FORCEDSIGN // uncomment this line to handle "%+d" -- the forced sign placement // #define PICOFORMAT_HANDLE_BIN // uncomment this line to handle "%b" -- binary representation // #define PICOFORMAT_HANDLE_OCT // uncomment this line to handle "%o" -// #define PICOFORMAT_HANDLE_HEX // uncomment this line to handle "%x" and "%X" +#define PICOFORMAT_HANDLE_HEX // uncomment this line to handle "%x" and "%X" // #define PICOFORMAT_HANDLE_FLOATS // uncomment this line to handle the "%f" // #define PICOFORMAT_CLANG_QUIRK // uncomment this line to match clang's non-standard "%010s" behavior (zero-pad strings when both '0' flag and width are set) From f73c2b8e6a4ab1666fa387e82994fe24f2bfb842 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 30 Apr 2026 14:44:29 +0100 Subject: [PATCH 115/173] rad1o: Add formatting support to rad1o_lcdPrint. --- firmware/common/rad1o/print.c | 18 ++++++++++++++++-- firmware/common/rad1o/print.h | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/firmware/common/rad1o/print.c b/firmware/common/rad1o/print.c index d279f4e7..345af5e0 100644 --- a/firmware/common/rad1o/print.c +++ b/firmware/common/rad1o/print.c @@ -1,5 +1,11 @@ #include "print.h" +#include +#include +#include + +#include + #include "display.h" #include "render.h" #include "smallfonts.h" @@ -7,9 +13,17 @@ static int32_t x = 0; static int32_t y = 0; -void rad1o_lcdPrint(const char* string) +void rad1o_lcdPrint(const char* fmt, ...) { - x = rad1o_DoString(x, y, string); + va_list args1, args2; + va_start(args1, fmt); + va_copy(args2, args1); + int buflen = pico_vsnprintf(NULL, 0, fmt, args1) + 1; + va_end(args1); + char* buf = alloca(buflen); + pico_vsnprintf(buf, buflen, fmt, args2); + va_end(args2); + x = rad1o_DoString(x, y, buf); } void rad1o_lcdNl(void) diff --git a/firmware/common/rad1o/print.h b/firmware/common/rad1o/print.h index 8981da25..858f1b22 100644 --- a/firmware/common/rad1o/print.h +++ b/firmware/common/rad1o/print.h @@ -2,7 +2,7 @@ #include -void rad1o_lcdPrint(const char* string); +void rad1o_lcdPrint(const char* string, ...); void rad1o_lcdNl(void); void rad1o_lcdClear(void); void rad1o_lcdMoveCrsr(int32_t dx, int32_t dy); From 9688caaf1c3aa0de92d6bac6d611af46bbaa73ff Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 30 Apr 2026 14:45:27 +0100 Subject: [PATCH 116/173] ui_rad1o: Eliminate use of sprintf. --- firmware/common/ui_rad1o.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/firmware/common/ui_rad1o.c b/firmware/common/ui_rad1o.c index 8df630bd..a157b6ef 100644 --- a/firmware/common/ui_rad1o.c +++ b/firmware/common/ui_rad1o.c @@ -24,7 +24,6 @@ #include #include -#include #include "clock_gen.h" #include "drivers.h" @@ -60,22 +59,20 @@ static bool enabled = false; #define DIV_ROUND_CLOSEST(n, d) ((n + (d / 2)) / d) -static void print_hz(char* tmp, char* fmt, uint64_t hz) +static void print_hz(char* fmt, uint64_t hz) { uint32_t mhz, khz; mhz = DIV_ROUND_CLOSEST(hz, 1000000); hz -= mhz * 1000000; khz = DIV_ROUND_CLOSEST(hz, 1000); - sprintf(tmp, fmt, (unsigned int) mhz, (unsigned int) khz); - rad1o_lcdPrint(tmp); + rad1o_lcdPrint(fmt, (unsigned int) mhz, (unsigned int) khz); } static void draw_frequency(void) { - char tmp[100]; rad1o_setTextColor(BLACK, GREEN); rad1o_setIntFont(&Font_Ubuntu18pt); - print_hz(tmp, "%4u.%03u", freq); + print_hz("%4u.%03u", freq); rad1o_setIntFont(&Font_7x8); rad1o_lcdMoveCrsr(1, 18 - 7); rad1o_lcdPrint("MHz"); @@ -115,8 +112,6 @@ static void draw_tx_rx(void) static void ui_update(void) { - char tmp[100]; - if (!enabled) { return; } @@ -156,12 +151,12 @@ static void ui_update(void) rad1o_setTextColor(BLACK, WHITE); rad1o_lcdSetCrsr(2, 71); rad1o_lcdPrint("Rate: "); - print_hz(tmp, "%2u.%03u MHz", sample_rate); + print_hz("%2u.%03u MHz", sample_rate); rad1o_lcdNl(); rad1o_lcdMoveCrsr(2, 0); rad1o_lcdPrint("Filter: "); - print_hz(tmp, "%2u.%03u MHz", filter_bw); + print_hz("%2u.%03u MHz", filter_bw); rad1o_lcdNl(); rad1o_drawHLine(88, 0, RESX - 1, WHITE); @@ -185,8 +180,7 @@ static void ui_update(void) if (direction == RF_PATH_DIRECTION_TX) { rad1o_setTextColor(BLACK, RED); } - sprintf(tmp, " TX: %u dB", (unsigned int) bbtxvga_gain); - rad1o_lcdPrint(tmp); + rad1o_lcdPrint(" TX: %u dB", (unsigned int) bbtxvga_gain); rad1o_lcdNl(); rad1o_lcdMoveCrsr(2, 0); @@ -194,12 +188,10 @@ static void ui_update(void) if (direction == RF_PATH_DIRECTION_RX) { rad1o_setTextColor(BLACK, GREEN); } - sprintf(tmp, "LNA: %2u dB", (unsigned int) bblna_gain); - rad1o_lcdPrint(tmp); + rad1o_lcdPrint("LNA: %2u dB", (unsigned int) bblna_gain); rad1o_lcdNl(); rad1o_lcdMoveCrsr(2, 0); - sprintf(tmp, "VGA: %2u dB", (unsigned int) bbvga_gain); - rad1o_lcdPrint(tmp); + rad1o_lcdPrint("VGA: %2u dB", (unsigned int) bbvga_gain); rad1o_lcdNl(); rad1o_lcdDisplay(); From 90ab84ba28be0e32b99af42e663d78f0445ff1a8 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 16:24:25 +0200 Subject: [PATCH 117/173] firmware: move jtag_cpld driver instance to cpld_jtag.x --- firmware/common/cpld_jtag.c | 7 +++++++ firmware/common/cpld_jtag.h | 4 ++++ firmware/common/drivers.c | 6 ------ firmware/common/drivers.h | 3 --- firmware/common/pins.c | 3 ++- firmware/common/portapack.c | 1 - firmware/hackrf-common.cmake | 1 + firmware/hackrf_usb/CMakeLists.txt | 1 - firmware/hackrf_usb/hackrf_usb.c | 1 - firmware/hackrf_usb/usb_api_cpld.c | 1 - 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/firmware/common/cpld_jtag.c b/firmware/common/cpld_jtag.c index 034ba196..92f89f27 100644 --- a/firmware/common/cpld_jtag.c +++ b/firmware/common/cpld_jtag.c @@ -34,6 +34,13 @@ static uint32_t xsvf_buffer_len, xsvf_pos; static unsigned char* xsvf_buffer; #endif +/* Driver instance. */ +jtag_gpio_t jtag_gpio_cpld = {}; + +jtag_t jtag_cpld = { + .gpio = &jtag_gpio_cpld, +}; + void cpld_jtag_take(jtag_t* const jtag) { const jtag_gpio_t* const gpio = jtag->gpio; diff --git a/firmware/common/cpld_jtag.h b/firmware/common/cpld_jtag.h index 0994c858..14f70437 100644 --- a/firmware/common/cpld_jtag.h +++ b/firmware/common/cpld_jtag.h @@ -59,3 +59,7 @@ int cpld_jtag_program( unsigned char* const buffer, refill_buffer_cb refill); unsigned char cpld_jtag_get_next_byte(void); + +/* Driver instance. */ +extern jtag_gpio_t jtag_gpio_cpld; +extern jtag_t jtag_cpld; diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index 58b30cb2..1444534c 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -45,12 +45,6 @@ fpga_driver_t fpga = { }; #endif -jtag_gpio_t jtag_gpio_cpld = {}; - -jtag_t jtag_cpld = { - .gpio = &jtag_gpio_cpld, -}; - #ifdef IS_PRALINE void ssp1_set_mode_ice40(void) { diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index d0427e93..1fe7d72c 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -25,7 +25,6 @@ extern "C" { #endif -#include "cpld_jtag.h" #include "platform_detect.h" // IWYU pragma: keep #if defined(IS_PRALINE) #include "fpga.h" @@ -38,8 +37,6 @@ extern ssp_config_t ssp_config_ice40_fpga; extern ice40_spi_driver_t ice40; extern fpga_driver_t fpga; #endif -extern jtag_gpio_t jtag_gpio_cpld; -extern jtag_t jtag_cpld; #if defined(IS_PRALINE) void ssp1_set_mode_ice40(void); diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 24aeb033..f695d1ef 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -26,7 +26,7 @@ #include #include -#include "drivers.h" +#include "cpld_jtag.h" #include "gpio.h" #include "leds.h" #include "max283x.h" @@ -41,6 +41,7 @@ #include "w25q80bv.h" #if defined(IS_PRALINE) #include "clock_io.h" + #include "drivers.h" #endif void pins_shutdown(void) diff --git a/firmware/common/portapack.c b/firmware/common/portapack.c index f25a4a6e..d3d848fc 100644 --- a/firmware/common/portapack.c +++ b/firmware/common/portapack.c @@ -26,7 +26,6 @@ #include "cpld_jtag.h" #include "delay.h" -#include "drivers.h" #include "gpio.h" #include "gpio_lpc.h" #include "platform_gpio.h" diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index 04d8b1a3..d31cd6c0 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -172,6 +172,7 @@ macro(DeclareTargets) ${PATH_HACKRF_FIRMWARE_COMMON}/power.c ${PATH_HACKRF_FIRMWARE_COMMON}/cpu_clock.c ${PATH_HACKRF_FIRMWARE_COMMON}/delay.c + ${PATH_HACKRF_FIRMWARE_COMMON}/cpld_jtag.c ${PATH_HACKRF_FIRMWARE_COMMON}/pins.c ${PATH_HACKRF_FIRMWARE_COMMON}/leds.c ${PATH_HACKRF_FIRMWARE_COMMON}/sgpio.c diff --git a/firmware/hackrf_usb/CMakeLists.txt b/firmware/hackrf_usb/CMakeLists.txt index 3fd3ea87..fe392f49 100644 --- a/firmware/hackrf_usb/CMakeLists.txt +++ b/firmware/hackrf_usb/CMakeLists.txt @@ -66,7 +66,6 @@ set(SRC_M4 usb_api_adc.c "${PATH_HACKRF_FIRMWARE_COMMON}/usb_queue.c" "${PATH_HACKRF_FIRMWARE_COMMON}/fault_handler.c" - "${PATH_HACKRF_FIRMWARE_COMMON}/cpld_jtag.c" "${PATH_HACKRF_FIRMWARE_COMMON}/crc.c" "${PATH_HACKRF_FIRMWARE_COMMON}/rom_iap.c" "${PATH_HACKRF_FIRMWARE_COMMON}/operacake.c" diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 031f2001..7419d063 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -70,7 +70,6 @@ #ifdef IS_NOT_PRALINE #include #include - #include #endif #include "usb_api_adc.h" diff --git a/firmware/hackrf_usb/usb_api_cpld.c b/firmware/hackrf_usb/usb_api_cpld.c index ed2beeda..391b9366 100644 --- a/firmware/hackrf_usb/usb_api_cpld.c +++ b/firmware/hackrf_usb/usb_api_cpld.c @@ -29,7 +29,6 @@ #include #include -#include #include #include #include From d0f61abcd791a792428b765a3c01540acc28ecbb Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 16:31:14 +0200 Subject: [PATCH 118/173] firmware: move ice40 driver instance to ice40_spi.x --- firmware/common/drivers.c | 20 -------------------- firmware/common/drivers.h | 8 -------- firmware/common/fpga.c | 1 - firmware/common/fpga_image.c | 1 - firmware/common/ice40_spi.c | 17 +++++++++++++++++ firmware/common/ice40_spi.h | 6 ++++++ firmware/common/pins.c | 2 +- 7 files changed, 24 insertions(+), 31 deletions(-) diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index 1444534c..65ca10a9 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -22,32 +22,12 @@ #include "drivers.h" #if defined(IS_PRALINE) - #include #include "fpga.h" #include "ice40_spi.h" - #include "spi_bus.h" #endif #ifdef IS_PRALINE -ssp_config_t ssp_config_ice40_fpga = { - .data_bits = SSP_DATA_8BITS, - .spi_mode = SSP_CPOL_1_CPHA_1, - .serial_clock_rate = 21, - .clock_prescale_rate = 2, -}; - -ice40_spi_driver_t ice40 = { - .bus = &spi_bus_ssp1, -}; - fpga_driver_t fpga = { .bus = &ice40, }; #endif - -#ifdef IS_PRALINE -void ssp1_set_mode_ice40(void) -{ - spi_bus_start(&spi_bus_ssp1, &ssp_config_ice40_fpga); -} -#endif diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index 1fe7d72c..1128a2dc 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -28,20 +28,12 @@ extern "C" { #include "platform_detect.h" // IWYU pragma: keep #if defined(IS_PRALINE) #include "fpga.h" - #include "ice40_spi.h" - #include "spi_ssp.h" #endif #if defined(IS_PRALINE) -extern ssp_config_t ssp_config_ice40_fpga; -extern ice40_spi_driver_t ice40; extern fpga_driver_t fpga; #endif -#if defined(IS_PRALINE) -void ssp1_set_mode_ice40(void); -#endif - #ifdef __cplusplus } #endif diff --git a/firmware/common/fpga.c b/firmware/common/fpga.c index 3bdd636e..b339fc15 100644 --- a/firmware/common/fpga.c +++ b/firmware/common/fpga.c @@ -24,7 +24,6 @@ #include -#include "drivers.h" #include "ice40_spi.h" #include "max283x.h" diff --git a/firmware/common/fpga_image.c b/firmware/common/fpga_image.c index a9f46aee..3ebadad8 100644 --- a/firmware/common/fpga_image.c +++ b/firmware/common/fpga_image.c @@ -23,7 +23,6 @@ #include #include -#include "drivers.h" #include "fpga.h" #include "ice40_spi.h" #include "lz4_blk.h" diff --git a/firmware/common/ice40_spi.c b/firmware/common/ice40_spi.c index c8364b8a..2685092a 100644 --- a/firmware/common/ice40_spi.c +++ b/firmware/common/ice40_spi.c @@ -28,6 +28,23 @@ #include "delay.h" #include "platform_scu.h" +/* Driver instance. */ +ssp_config_t ssp_config_ice40_fpga = { + .data_bits = SSP_DATA_8BITS, + .spi_mode = SSP_CPOL_1_CPHA_1, + .serial_clock_rate = 21, + .clock_prescale_rate = 2, +}; + +ice40_spi_driver_t ice40 = { + .bus = &spi_bus_ssp1, +}; + +void ssp1_set_mode_ice40(void) +{ + spi_bus_start(&spi_bus_ssp1, &ssp_config_ice40_fpga); +} + void ice40_spi_target_init(ice40_spi_driver_t* const drv) { const platform_scu_t* scu = platform_scu(); diff --git a/firmware/common/ice40_spi.h b/firmware/common/ice40_spi.h index 4ceddffa..a1362033 100644 --- a/firmware/common/ice40_spi.h +++ b/firmware/common/ice40_spi.h @@ -27,6 +27,7 @@ #include "gpio.h" #include "spi_bus.h" +#include "spi_ssp.h" typedef struct { spi_bus_t* const bus; @@ -43,3 +44,8 @@ bool ice40_spi_syscfg_program( uint8_t* buf, size_t (*read_block_cb)(void* ctx), void* read_ctx); + +/* Driver instance. */ +extern ssp_config_t ssp_config_ice40_fpga; +extern ice40_spi_driver_t ice40; +void ssp1_set_mode_ice40(void); diff --git a/firmware/common/pins.c b/firmware/common/pins.c index f695d1ef..c51de383 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -41,7 +41,7 @@ #include "w25q80bv.h" #if defined(IS_PRALINE) #include "clock_io.h" - #include "drivers.h" + #include "ice40_spi.h" #endif void pins_shutdown(void) From 8569c1dfd26d55f4f2e6df6b4265ef11018c9e60 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 16:35:44 +0200 Subject: [PATCH 119/173] firmware: move fpga object instance to fpga.x --- firmware/common/clock_io.c | 1 - firmware/common/drivers.c | 11 ----------- firmware/common/drivers.h | 9 --------- firmware/common/fpga.c | 5 +++++ firmware/common/fpga.h | 3 ++- firmware/common/fpga_selftest.c | 1 - firmware/common/radio.c | 1 - firmware/hackrf_usb/usb_api_register.c | 1 - 8 files changed, 7 insertions(+), 25 deletions(-) diff --git a/firmware/common/clock_io.c b/firmware/common/clock_io.c index c68d470f..b0904f89 100644 --- a/firmware/common/clock_io.c +++ b/firmware/common/clock_io.c @@ -36,7 +36,6 @@ #include "sgpio.h" #endif #ifdef IS_PRALINE - #include "drivers.h" #include "fpga.h" #include "platform_gpio.h" #endif diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c index 65ca10a9..739f9b1f 100644 --- a/firmware/common/drivers.c +++ b/firmware/common/drivers.c @@ -20,14 +20,3 @@ */ #include "drivers.h" - -#if defined(IS_PRALINE) - #include "fpga.h" - #include "ice40_spi.h" -#endif - -#ifdef IS_PRALINE -fpga_driver_t fpga = { - .bus = &ice40, -}; -#endif diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h index 1128a2dc..0071a0be 100644 --- a/firmware/common/drivers.h +++ b/firmware/common/drivers.h @@ -25,15 +25,6 @@ extern "C" { #endif -#include "platform_detect.h" // IWYU pragma: keep -#if defined(IS_PRALINE) - #include "fpga.h" -#endif - -#if defined(IS_PRALINE) -extern fpga_driver_t fpga; -#endif - #ifdef __cplusplus } #endif diff --git a/firmware/common/fpga.c b/firmware/common/fpga.c index b339fc15..f68b6525 100644 --- a/firmware/common/fpga.c +++ b/firmware/common/fpga.c @@ -27,6 +27,11 @@ #include "ice40_spi.h" #include "max283x.h" +/* Driver instance. */ +fpga_driver_t fpga = { + .bus = &ice40, +}; + /* Set up all registers according to the loaded bitstream's defaults. */ void fpga_init(fpga_driver_t* const drv) { diff --git a/firmware/common/fpga.h b/firmware/common/fpga.h index 65826b21..1765fcdd 100644 --- a/firmware/common/fpga.h +++ b/firmware/common/fpga.h @@ -91,4 +91,5 @@ bool fpga_spi_selftest(void); bool fpga_sgpio_selftest(void); bool fpga_if_xcvr_selftest(void); -//void narrowband_filter_set(const uint8_t value); +/* Driver instance. */ +extern fpga_driver_t fpga; diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index 30e70ea2..c51dd008 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -25,7 +25,6 @@ #include "clock_gen.h" #include "delay.h" -#include "drivers.h" #include "fixed_point.h" #include "fpga.h" #include "ice40_spi.h" diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 17b7486c..eb01b5de 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -34,7 +34,6 @@ #include "transceiver_mode.h" #include "tuning.h" #ifdef IS_PRALINE - #include "drivers.h" #include "fpga.h" #include "tune_config.h" #endif diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index 88a4de53..30d1d493 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -36,7 +36,6 @@ #include #include #ifdef IS_PRALINE - #include #include #endif #ifdef IS_NOT_RAD1O From 8bdefc2d899e4a26cca2c37b30faf0b454ad5f20 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 30 Apr 2026 16:38:10 +0200 Subject: [PATCH 120/173] firmware: remove drivers.x --- firmware/common/drivers.c | 22 ---------------------- firmware/common/drivers.h | 30 ------------------------------ firmware/hackrf-common.cmake | 1 - 3 files changed, 53 deletions(-) delete mode 100644 firmware/common/drivers.c delete mode 100644 firmware/common/drivers.h diff --git a/firmware/common/drivers.c b/firmware/common/drivers.c deleted file mode 100644 index 739f9b1f..00000000 --- a/firmware/common/drivers.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2026 Great Scott Gadgets - * - * This file is part of HackRF. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#include "drivers.h" diff --git a/firmware/common/drivers.h b/firmware/common/drivers.h deleted file mode 100644 index 0071a0be..00000000 --- a/firmware/common/drivers.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2026 Great Scott Gadgets - * - * This file is part of HackRF. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index d31cd6c0..eb1d5716 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -168,7 +168,6 @@ endmacro() macro(DeclareTargets) SET(SRC_M4 ${SRC_M4} - ${PATH_HACKRF_FIRMWARE_COMMON}/drivers.c ${PATH_HACKRF_FIRMWARE_COMMON}/power.c ${PATH_HACKRF_FIRMWARE_COMMON}/cpu_clock.c ${PATH_HACKRF_FIRMWARE_COMMON}/delay.c From 9c81954b1b31cd7166f0d8df345c24803a9b4c06 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Fri, 24 Apr 2026 10:43:00 +0200 Subject: [PATCH 121/173] firmware: move platform preprocessor definitions to cmake --- firmware/common/platform_detect.h | 62 ---------------------- firmware/hackrf-common.cmake | 2 + firmware/platform-detect.cmake | 87 +++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 62 deletions(-) create mode 100644 firmware/platform-detect.cmake diff --git a/firmware/common/platform_detect.h b/firmware/common/platform_detect.h index 0331fe9c..4f0f0f43 100644 --- a/firmware/common/platform_detect.h +++ b/firmware/common/platform_detect.h @@ -21,7 +21,6 @@ #pragma once -#include #include #define BOARD_REV_GSG (0x80) @@ -32,67 +31,6 @@ #define PLATFORM_HACKRF1_R9 (1 << 3) #define PLATFORM_PRALINE (1 << 4) -/* clang-format off */ - -/* Helper macros for platform-specific code. */ -#if defined(UNIVERSAL) - #define IS_PRALINE (detected_platform() == BOARD_ID_PRALINE) - #define IS_NOT_PRALINE (!IS_PRALINE) - #define IS_HACKRF_ONE ( \ - detected_platform() == BOARD_ID_HACKRF1_OG || \ - detected_platform() == BOARD_ID_HACKRF1_R9 \ - ) - #define IS_NOT_HACKRF_ONE (!IS_HACKRF_ONE) - #define IS_H1_R9 (detected_platform() == BOARD_ID_HACKRF1_R9) - #define IS_NOT_H1_R9 (!IS_H1_R9) - #define IS_NOT_RAD1O true - #define IS_NOT_JAWBREAKER true - #define IS_H1_OR_PRALINE true - #define IS_H1_OR_RAD1O IS_HACKRF_ONE - #define IS_H1_OR_JAWBREAKER IS_HACKRF_ONE - #define IS_FOUR_LEDS IS_PRALINE - #define IS_EXPANSION_COMPATIBLE true -#elif defined(HACKRF_ONE) - #define IS_NOT_PRALINE true - #define IS_HACKRF_ONE true - #define IS_H1_R9 (detected_platform() == BOARD_ID_HACKRF1_R9) - #define IS_NOT_H1_R9 (!IS_H1_R9) - #define IS_NOT_RAD1O true - #define IS_NOT_JAWBREAKER true - #define IS_H1_OR_PRALINE true - #define IS_H1_OR_RAD1O true - #define IS_H1_OR_JAWBREAKER true - #define IS_EXPANSION_COMPATIBLE true -#elif defined(PRALINE) - #define IS_PRALINE true - #define IS_NOT_HACKRF_ONE true - #define IS_NOT_H1_R9 true - #define IS_NOT_RAD1O true - #define IS_NOT_JAWBREAKER true - #define IS_H1_OR_PRALINE true - #define IS_FOUR_LEDS true - #define IS_EXPANSION_COMPATIBLE true -#elif defined(RAD1O) - #define IS_NOT_PRALINE true - #define IS_NOT_HACKRF_ONE true - #define IS_NOT_H1_R9 true - #define IS_RAD1O true - #define IS_NOT_JAWBREAKER true - #define IS_H1_OR_RAD1O true - #define IS_FOUR_LEDS true -#elif defined(JAWBREAKER) - #define IS_NOT_PRALINE true - #define IS_NOT_HACKRF_ONE true - #define IS_NOT_H1_R9 true - #define IS_NOT_RAD1O true - #define IS_JAWBREAKER true - #define IS_H1_OR_JAWBREAKER true -#else - #error "No recognised platform defined" -#endif - -/* clang-format on */ - typedef enum { BOARD_ID_JELLYBEAN = 0, BOARD_ID_JAWBREAKER = 1, diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index eb1d5716..dfc67d9e 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -74,6 +74,8 @@ if(NOT DEFINED BOARD) set(BOARD HACKRF_ONE) endif() +include(${PATH_HACKRF_FIRMWARE}/platform-detect.cmake) + if(BOARD STREQUAL "HACKRF_ONE" OR BOARD STREQUAL "PRALINE" OR BOARD STREQUAL "UNIVERSAL") set(MCU_PARTNO LPC4320) else() diff --git a/firmware/platform-detect.cmake b/firmware/platform-detect.cmake new file mode 100644 index 00000000..dcaeba58 --- /dev/null +++ b/firmware/platform-detect.cmake @@ -0,0 +1,87 @@ +# Copyright 2026 Great Scott Gadgets +# +# This file is part of HackRF. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +cmake_minimum_required(VERSION 3.12.0) + +if(BOARD STREQUAL "UNIVERSAL") + add_compile_definitions( + "IS_PRALINE=(detected_platform() == BOARD_ID_PRALINE)" + "IS_NOT_PRALINE=(!IS_PRALINE)" + "IS_HACKRF_ONE=( \ + detected_platform() == BOARD_ID_HACKRF1_OG || \ + detected_platform() == BOARD_ID_HACKRF1_R9 \ + )" + "IS_NOT_HACKRF_ONE=(!IS_HACKRF_ONE)" + "IS_H1_R9=(detected_platform() == BOARD_ID_HACKRF1_R9)" + "IS_NOT_H1_R9=(!IS_H1_R9)" + "IS_NOT_RAD1O=1" + "IS_NOT_JAWBREAKER=1" + "IS_H1_OR_PRALINE=1" + "IS_H1_OR_RAD1O=IS_HACKRF_ONE" + "IS_H1_OR_JAWBREAKER=IS_HACKRF_ONE" + "IS_FOUR_LEDS=IS_PRALINE" + "IS_EXPANSION_COMPATIBLE=1" + ) +elseif(BOARD STREQUAL "HACKRF_ONE") + add_compile_definitions( + "IS_NOT_PRALINE=1" + "IS_HACKRF_ONE=1" + "IS_H1_R9=(detected_platform() == BOARD_ID_HACKRF1_R9)" + "IS_NOT_H1_R9=(!IS_H1_R9)" + "IS_NOT_RAD1O=1" + "IS_NOT_JAWBREAKER=1" + "IS_H1_OR_PRALINE=1" + "IS_H1_OR_RAD1O=1" + "IS_H1_OR_JAWBREAKER=1" + "IS_EXPANSION_COMPATIBLE=1" + ) +elseif(BOARD STREQUAL "PRALINE") + add_compile_definitions( + "IS_PRALINE=1" + "IS_NOT_HACKRF_ONE=1" + "IS_NOT_H1_R9=1" + "IS_NOT_RAD1O=1" + "IS_NOT_JAWBREAKER=1" + "IS_H1_OR_PRALINE=1" + "IS_FOUR_LEDS=1" + "IS_EXPANSION_COMPATIBLE=1" + ) +elseif(BOARD STREQUAL "RAD1O") + add_compile_definitions( + "IS_NOT_PRALINE=1" + "IS_NOT_HACKRF_ONE=1" + "IS_NOT_H1_R9=1" + "IS_RAD1O=1" + "IS_NOT_JAWBREAKER=1" + "IS_H1_OR_RAD1O=1" + "IS_FOUR_LEDS=1" + ) +elseif(BOARD STREQUAL "JAWBREAKER") + add_compile_definitions( + "IS_NOT_PRALINE=1" + "IS_NOT_HACKRF_ONE=1" + "IS_NOT_H1_R9=1" + "IS_NOT_RAD1O=1" + "IS_JAWBREAKER=1" + "IS_H1_OR_JAWBREAKER=1" + ) +else() + message(FATAL_ERROR "No recognized platform defined: ${BOARD}") +endif() From 01ffff68eea4db1f39b76bff1ab64f1edeb11d6b Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Fri, 24 Apr 2026 11:55:25 +0200 Subject: [PATCH 122/173] firmware: remove spurious platform_detect.h includes --- firmware/common/clock_gen.h | 2 -- firmware/common/clock_io.c | 1 + firmware/common/clock_io.h | 2 -- firmware/common/cpld_jtag.c | 1 - firmware/common/cpld_jtag.h | 1 - firmware/common/cpu_clock.c | 3 --- firmware/common/cpu_clock.h | 2 -- firmware/common/hackrf_ui.c | 1 - firmware/common/max283x.h | 1 - firmware/common/mixer.h | 2 -- firmware/common/operacake_sctimer.c | 5 ++++- firmware/common/pins.c | 2 +- firmware/common/pins.h | 2 -- firmware/common/platform_gpio.h | 1 - firmware/common/platform_scu.h | 2 -- firmware/common/power.c | 10 ++++------ firmware/common/power.h | 2 -- firmware/common/rf_path.h | 2 -- firmware/common/rffc5071.h | 1 - firmware/common/selftest.h | 2 -- firmware/common/sgpio.h | 1 - firmware/hackrf_usb/usb_api_register.c | 1 + firmware/hackrf_usb/usb_api_register.h | 2 -- firmware/hackrf_usb/usb_api_selftest.c | 7 +++---- firmware/hackrf_usb/usb_descriptor.c | 2 -- firmware/hackrf_usb/usb_descriptor.h | 2 -- firmware/hackrf_usb/usb_device.c | 1 - firmware/hackrf_usb/usb_device.h | 2 -- firmware/include-what-you-use.cmake | 18 +++++++++++++++++- 29 files changed, 31 insertions(+), 50 deletions(-) diff --git a/firmware/common/clock_gen.h b/firmware/common/clock_gen.h index 0bc6439b..fa1d3da0 100644 --- a/firmware/common/clock_gen.h +++ b/firmware/common/clock_gen.h @@ -25,8 +25,6 @@ extern "C" { #endif -#include "platform_detect.h" // IWYU pragma: keep - #include #include "fixed_point.h" diff --git a/firmware/common/clock_io.c b/firmware/common/clock_io.c index b0904f89..d4d14af0 100644 --- a/firmware/common/clock_io.c +++ b/firmware/common/clock_io.c @@ -32,6 +32,7 @@ #include "gpdma.h" #include "gpio.h" +#include "platform_detect.h" #ifdef IS_NOT_PRALINE #include "sgpio.h" #endif diff --git a/firmware/common/clock_io.h b/firmware/common/clock_io.h index df540022..4868b2d9 100644 --- a/firmware/common/clock_io.h +++ b/firmware/common/clock_io.h @@ -24,8 +24,6 @@ #include #include -#include "platform_detect.h" // IWYU pragma: keep - void clkin_detect_init(void); uint32_t clkin_frequency(void); diff --git a/firmware/common/cpld_jtag.c b/firmware/common/cpld_jtag.c index 92f89f27..441fefe8 100644 --- a/firmware/common/cpld_jtag.c +++ b/firmware/common/cpld_jtag.c @@ -22,7 +22,6 @@ #include "cpld_jtag.h" #include "platform_detect.h" - #ifdef IS_NOT_PRALINE #include #include "xapp058/micro.h" diff --git a/firmware/common/cpld_jtag.h b/firmware/common/cpld_jtag.h index 14f70437..f49f1317 100644 --- a/firmware/common/cpld_jtag.h +++ b/firmware/common/cpld_jtag.h @@ -24,7 +24,6 @@ #include #include "gpio.h" -#include "platform_detect.h" typedef struct { gpio_t gpio_tck; diff --git a/firmware/common/cpu_clock.c b/firmware/common/cpu_clock.c index aa4ac910..37445e51 100644 --- a/firmware/common/cpu_clock.c +++ b/firmware/common/cpu_clock.c @@ -31,9 +31,6 @@ #include "delay.h" #include "i2c_bus.h" #include "si5351c.h" -#ifdef IS_NOT_RAD1O - #include "platform_detect.h" -#endif /* Configure PLL1 (Main MCU Clock) to max speed (204MHz). diff --git a/firmware/common/cpu_clock.h b/firmware/common/cpu_clock.h index b0393081..92ad6441 100644 --- a/firmware/common/cpu_clock.h +++ b/firmware/common/cpu_clock.h @@ -25,8 +25,6 @@ extern "C" { #endif -#include "platform_detect.h" // IWYU pragma: keep - void cpu_clock_init(void); #ifdef __cplusplus diff --git a/firmware/common/hackrf_ui.c b/firmware/common/hackrf_ui.c index 4224d40f..ac243890 100644 --- a/firmware/common/hackrf_ui.c +++ b/firmware/common/hackrf_ui.c @@ -23,7 +23,6 @@ #include #include "hackrf_ui.h" -#include "platform_detect.h" // IWYU pragma: keep #include "transceiver_mode.h" #ifdef IS_EXPANSION_COMPATIBLE #include "ui_portapack.h" diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h index b4e44620..4db5edfe 100644 --- a/firmware/common/max283x.h +++ b/firmware/common/max283x.h @@ -27,7 +27,6 @@ #include #include "fixed_point.h" -#include "platform_detect.h" #include "spi_ssp.h" #ifdef IS_PRALINE diff --git a/firmware/common/mixer.h b/firmware/common/mixer.h index 35c0f047..15afe35e 100644 --- a/firmware/common/mixer.h +++ b/firmware/common/mixer.h @@ -26,8 +26,6 @@ #include #include "fixed_point.h" -#include "platform_detect.h" - #ifdef IS_RAD1O #include "max2871.h" #endif diff --git a/firmware/common/operacake_sctimer.c b/firmware/common/operacake_sctimer.c index 1268114f..9325f65d 100644 --- a/firmware/common/operacake_sctimer.c +++ b/firmware/common/operacake_sctimer.c @@ -30,12 +30,15 @@ #include "platform_detect.h" #include "platform_scu.h" #include "sct.h" - #ifdef IS_NOT_PRALINE #include #include #endif +#include "delay.h" +#include "platform_detect.h" +#include "sct.h" + #define U1CTRL_SET SCT_OUT14_SET #define U1CTRL_CLR SCT_OUT14_CLR #define U2CTRL0_SET SCT_OUT13_SET diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 48479f95..3010cd9f 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -39,7 +39,7 @@ #include "rf_path.h" #include "sgpio.h" #include "w25q80bv.h" -#if defined(IS_PRALINE) +#ifdef IS_PRALINE #include "clock_io.h" #include "ice40_spi.h" #endif diff --git a/firmware/common/pins.h b/firmware/common/pins.h index b9454d18..bc109e5b 100644 --- a/firmware/common/pins.h +++ b/firmware/common/pins.h @@ -27,8 +27,6 @@ extern "C" { #endif -#include "platform_detect.h" // IWYU pragma: keep - void pins_shutdown(void); void pins_setup(void); diff --git a/firmware/common/platform_gpio.h b/firmware/common/platform_gpio.h index a7e68bc9..c57df96e 100644 --- a/firmware/common/platform_gpio.h +++ b/firmware/common/platform_gpio.h @@ -27,7 +27,6 @@ extern "C" { #include "gpio.h" #include "gpio_lpc.h" -#include "platform_detect.h" typedef struct { /* LEDs */ diff --git a/firmware/common/platform_scu.h b/firmware/common/platform_scu.h index 71c0f742..a3702bf7 100644 --- a/firmware/common/platform_scu.h +++ b/firmware/common/platform_scu.h @@ -29,8 +29,6 @@ extern "C" { #include -#include "platform_detect.h" - /* * SCU PinMux */ diff --git a/firmware/common/power.c b/firmware/common/power.c index 25424aab..1256e595 100644 --- a/firmware/common/power.c +++ b/firmware/common/power.c @@ -24,16 +24,14 @@ #if defined(IS_HACKRF_ONE) #include #endif -#if defined(IS_HACKRF_ONE) || defined(IS_RAD1O) || defined(IS_JAWBREAKER) - #include "platform_detect.h" -#endif + +#include "gpio.h" +#include "platform_detect.h" +#include "platform_gpio.h" #if defined(IS_PRALINE) || defined(IS_RAD1O) #include "delay.h" #endif -#include "gpio.h" -#include "platform_gpio.h" - #ifdef IS_PRALINE void enable_1v2_power(void) { diff --git a/firmware/common/power.h b/firmware/common/power.h index 22b2345e..fc8af67a 100644 --- a/firmware/common/power.h +++ b/firmware/common/power.h @@ -25,8 +25,6 @@ extern "C" { #endif -#include "platform_detect.h" // IWYU pragma: keep - #ifdef IS_PRALINE void enable_1v2_power(void); void disable_1v2_power(void); diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h index 3b97ac62..a1796f13 100644 --- a/firmware/common/rf_path.h +++ b/firmware/common/rf_path.h @@ -25,8 +25,6 @@ #include -#include "platform_detect.h" // IWYU pragma: keep - #ifdef IS_NOT_JAWBREAKER #include "gpio.h" #endif diff --git a/firmware/common/rffc5071.h b/firmware/common/rffc5071.h index c6b2c0c2..40971061 100644 --- a/firmware/common/rffc5071.h +++ b/firmware/common/rffc5071.h @@ -27,7 +27,6 @@ #include "fixed_point.h" #include "gpio.h" -#include "platform_detect.h" // IWYU pragma: keep #include "spi_bus.h" /* 31 registers, each containing 16 bits of data. */ diff --git a/firmware/common/selftest.h b/firmware/common/selftest.h index 9fca53a6..cea60da2 100644 --- a/firmware/common/selftest.h +++ b/firmware/common/selftest.h @@ -24,8 +24,6 @@ #include #include -#include "platform_detect.h" // IWYU pragma: keep - #define NUM_LOCK_ATTEMPTS 3 enum { diff --git a/firmware/common/sgpio.h b/firmware/common/sgpio.h index 58a30c27..0aebdb5f 100644 --- a/firmware/common/sgpio.h +++ b/firmware/common/sgpio.h @@ -26,7 +26,6 @@ #include #include "gpio.h" -#include "platform_detect.h" // IWYU pragma: keep typedef enum { SGPIO_DIRECTION_RX, diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index 30d1d493..1a9f0238 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -21,6 +21,7 @@ * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ + #include "usb_api_register.h" #include diff --git a/firmware/hackrf_usb/usb_api_register.h b/firmware/hackrf_usb/usb_api_register.h index aef02d03..a5a36550 100644 --- a/firmware/hackrf_usb/usb_api_register.h +++ b/firmware/hackrf_usb/usb_api_register.h @@ -23,8 +23,6 @@ #pragma once -#include "platform_detect.h" // IWYU pragma: keep - #include #include diff --git a/firmware/hackrf_usb/usb_api_selftest.c b/firmware/hackrf_usb/usb_api_selftest.c index 4c812366..7fde6f68 100644 --- a/firmware/hackrf_usb/usb_api_selftest.c +++ b/firmware/hackrf_usb/usb_api_selftest.c @@ -23,6 +23,9 @@ #include #include +#ifdef IS_NOT_RAD1O + #include +#endif #include #include @@ -33,10 +36,6 @@ #include #include -#ifdef IS_NOT_RAD1O - #include -#endif - static char* itoa(int val, int base) { static char buf[32] = {0}; diff --git a/firmware/hackrf_usb/usb_descriptor.c b/firmware/hackrf_usb/usb_descriptor.c index 067317b3..005babb3 100644 --- a/firmware/hackrf_usb/usb_descriptor.c +++ b/firmware/hackrf_usb/usb_descriptor.c @@ -26,8 +26,6 @@ #include -#include "platform_detect.h" - #define USB_VENDOR_ID (0x1D50) #define USB_API_VERSION (0x0112) diff --git a/firmware/hackrf_usb/usb_descriptor.h b/firmware/hackrf_usb/usb_descriptor.h index ef41d6ac..bd43f4aa 100644 --- a/firmware/hackrf_usb/usb_descriptor.h +++ b/firmware/hackrf_usb/usb_descriptor.h @@ -24,8 +24,6 @@ #include -#include "platform_detect.h" - #if defined(IS_HACKRF_ONE) || defined(IS_PRALINE) extern uint8_t usb_descriptor_device_hackrf[]; #endif diff --git a/firmware/hackrf_usb/usb_device.c b/firmware/hackrf_usb/usb_device.c index a2af1158..90347e17 100644 --- a/firmware/hackrf_usb/usb_device.c +++ b/firmware/hackrf_usb/usb_device.c @@ -25,7 +25,6 @@ #include -#include "platform_detect.h" #include "usb_descriptor.h" usb_configuration_t usb_configuration_high_speed = { diff --git a/firmware/hackrf_usb/usb_device.h b/firmware/hackrf_usb/usb_device.h index f442db37..5ef53360 100644 --- a/firmware/hackrf_usb/usb_device.h +++ b/firmware/hackrf_usb/usb_device.h @@ -25,8 +25,6 @@ #include -#include "platform_detect.h" - extern usb_device_t usb_device; #ifdef IS_HACKRF_ONE diff --git a/firmware/include-what-you-use.cmake b/firmware/include-what-you-use.cmake index b5c1bc7e..291cfdab 100644 --- a/firmware/include-what-you-use.cmake +++ b/firmware/include-what-you-use.cmake @@ -28,11 +28,27 @@ endif() # include-what-you-use arguments: # # --no_fwd_decls - Disable forward declaration suggestions. +# --keep - Always keep the specified include. # --mapping_file - Specify mapping files for excluding suggestions. +# -w - Disable spurious clang warnings. +# +# NOTE: We specify '--keep platform_detect.h' due to iwyu 0.23 not +# being able to detect usage of symbols within preprocessor +# macros. This results in false positives for the removal of the +# include. +# +# The alternate solution is to use "// IWYU pragma: always-keep" +# within "platform_detect.h" itself but this triggers the +# angle-quote-curse on 0.23. (e.g. https://github.com/include-what-you-use/include-what-you-use/issues/1087) +# +# iwyu versions from at least 0.26 onwards do not suffer from these +# problems which will allow us to remove it in future. # list(APPEND IWYU_COMMAND -Xiwyu --no_fwd_decls - -Xiwyu --mapping_file=${CMAKE_SOURCE_DIR}/libopencm3.imp) + -Xiwyu --keep ${CMAKE_SOURCE_DIR}/common/platform_detect.h + -Xiwyu --mapping_file=${CMAKE_SOURCE_DIR}/libopencm3.imp + -w) set(CMAKE_C_INCLUDE_WHAT_YOU_USE ${IWYU_COMMAND}) set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${IWYU_COMMAND}) From 23104c7bd9fa4dddda0bc5c6db512679b5626cc7 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 28 Apr 2026 11:58:18 +0100 Subject: [PATCH 123/173] firmware: bump CMake requirement to 3.12. --- .github/workflows/build.yml | 4 ++-- .github/workflows/includes-check.yml | 2 +- firmware/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a93a27c1..02239f82 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -160,10 +160,10 @@ jobs: matrix: os: ['macos', 'ubuntu', 'windows'] board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE', 'UNIVERSAL'] - cmake: ['3.10.0', 'latest'] + cmake: ['3.12.0', 'latest'] exclude: - os: 'windows' - cmake: '3.10.0' + cmake: '3.12.0' # GitHub doesn't give us many macOS runners, so don't build for every board. # We just need to know that building firmware works on macOS. Use PRALINE # since that's the most complicated one due to FPGA bitstreams. diff --git a/.github/workflows/includes-check.yml b/.github/workflows/includes-check.yml index 11640069..f16800fe 100644 --- a/.github/workflows/includes-check.yml +++ b/.github/workflows/includes-check.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE', 'UNIVERSAL'] - cmake: ['3.10.0', 'latest'] + cmake: ['3.12.0', 'latest'] # Don't cancel all builds when one fails fail-fast: false diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 611e7870..844ae708 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -23,7 +23,7 @@ option(CHECK_INCLUDES "Check firmware sources for unused includes and transitive dependencies. (Requires iwyu)" OFF) -cmake_minimum_required(VERSION 3.10.0) +cmake_minimum_required(VERSION 3.12.0) set(CMAKE_TOOLCHAIN_FILE toolchain-arm-cortex-m.cmake) project (hackrf_firmware_all C) From 6ff7f8ed6346a396e74d85ed7e7b5b9049052806 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 7 May 2026 18:56:53 -0400 Subject: [PATCH 124/173] Fix IWYU --keep syntax for compatibility with 0.22 --- firmware/include-what-you-use.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/include-what-you-use.cmake b/firmware/include-what-you-use.cmake index 291cfdab..37e2a2a6 100644 --- a/firmware/include-what-you-use.cmake +++ b/firmware/include-what-you-use.cmake @@ -46,7 +46,7 @@ endif() # list(APPEND IWYU_COMMAND -Xiwyu --no_fwd_decls - -Xiwyu --keep ${CMAKE_SOURCE_DIR}/common/platform_detect.h + -Xiwyu --keep=${CMAKE_SOURCE_DIR}/common/platform_detect.h -Xiwyu --mapping_file=${CMAKE_SOURCE_DIR}/libopencm3.imp -w) From db44436fda46150bc443b0c1f65b1e70803d7cb5 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 7 May 2026 19:30:30 -0400 Subject: [PATCH 125/173] Remove redundant includes --- firmware/common/operacake_sctimer.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/firmware/common/operacake_sctimer.c b/firmware/common/operacake_sctimer.c index 9325f65d..76453e57 100644 --- a/firmware/common/operacake_sctimer.c +++ b/firmware/common/operacake_sctimer.c @@ -35,10 +35,6 @@ #include #endif -#include "delay.h" -#include "platform_detect.h" -#include "sct.h" - #define U1CTRL_SET SCT_OUT14_SET #define U1CTRL_CLR SCT_OUT14_CLR #define U2CTRL0_SET SCT_OUT13_SET From fe060fd04b1c18a1755cd800632c45bcf9b5fa2b Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 11 May 2026 12:19:29 +0100 Subject: [PATCH 126/173] libhackrf: Set timeouts for all USB transfers. --- host/libhackrf/src/hackrf.c | 129 ++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index da3c0f15..7bafd5f3 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -54,6 +54,10 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI #define FROM_LE64(x) x #endif +#define DEFAULT_REQUEST_TIMEOUT 100 +#define CPLD_WRITE_TIMEOUT 10000 +#define SPIFLASH_WRITE_TIMEOUT 50000 // W25Q32JV max chip erase time + // TODO: Factor this into a shared #include so that firmware can use // the same values. typedef enum { @@ -778,7 +782,7 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d 0, (unsigned char*) &buffer_size, 4, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 4) { last_libusb_error = result; @@ -948,7 +952,7 @@ int ADDCALL hackrf_set_transceiver_mode( 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -977,7 +981,7 @@ int ADDCALL hackrf_max2837_read( register_number, (unsigned char*) value, 2, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 2) { last_libusb_error = result; @@ -1006,7 +1010,7 @@ int ADDCALL hackrf_max2831_read( register_number, (unsigned char*) value, 2, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 2) { last_libusb_error = result; @@ -1039,7 +1043,7 @@ int ADDCALL hackrf_max2837_write( register_number, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1072,7 +1076,7 @@ int ADDCALL hackrf_max2831_write( register_number, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1103,7 +1107,7 @@ int ADDCALL hackrf_si5351c_read( register_number, (unsigned char*) &temp_value, 1, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 1) { last_libusb_error = result; @@ -1137,7 +1141,7 @@ int ADDCALL hackrf_si5351c_write( register_number, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1161,7 +1165,7 @@ int ADDCALL hackrf_set_baseband_filter_bandwidth( bandwidth_hz >> 16, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1190,7 +1194,7 @@ int ADDCALL hackrf_rffc5071_read( register_number, (unsigned char*) value, 2, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 2) { last_libusb_error = result; @@ -1220,7 +1224,7 @@ int ADDCALL hackrf_rffc5071_write( register_number, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1246,7 +1250,7 @@ int ADDCALL hackrf_fpga_read_register( register_number, (unsigned char*) value, 1, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 1) { last_libusb_error = result; @@ -1273,7 +1277,7 @@ int ADDCALL hackrf_fpga_write_register( register_number, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1297,7 +1301,7 @@ int ADDCALL hackrf_read_selftest(hackrf_device* device, hackrf_selftest* selftes 0, (unsigned char*) selftest, sizeof(hackrf_selftest), - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 2) { last_libusb_error = result; @@ -1429,7 +1433,7 @@ int ADDCALL hackrf_read_adc(hackrf_device* device, uint8_t adc_channel, uint16_t adc_channel, (unsigned char*) value, 2, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 2) { last_libusb_error = result; @@ -1453,7 +1457,7 @@ int ADDCALL hackrf_get_m0_state(hackrf_device* device, hackrf_m0_state* state) 0, (unsigned char*) state, sizeof(hackrf_m0_state), - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < sizeof(hackrf_m0_state)) { last_libusb_error = result; @@ -1488,7 +1492,7 @@ int ADDCALL hackrf_set_tx_underrun_limit(hackrf_device* device, uint32_t value) value >> 16, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1512,7 +1516,7 @@ int ADDCALL hackrf_set_rx_overrun_limit(hackrf_device* device, uint32_t value) value >> 16, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1534,7 +1538,7 @@ int ADDCALL hackrf_spiflash_erase(hackrf_device* device) 0, NULL, 0, - 0); + SPIFLASH_WRITE_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1565,7 +1569,7 @@ int ADDCALL hackrf_spiflash_write( address & 0xFFFF, data, length, - 0); + SPIFLASH_WRITE_TIMEOUT); if (result < length) { last_libusb_error = result; @@ -1595,7 +1599,7 @@ int ADDCALL hackrf_spiflash_read( address & 0xFFFF, data, length, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < length) { last_libusb_error = result; @@ -1618,7 +1622,7 @@ int ADDCALL hackrf_spiflash_status(hackrf_device* device, uint8_t* data) 0, data, 2, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 1) { last_libusb_error = result; @@ -1641,7 +1645,7 @@ int ADDCALL hackrf_spiflash_clear_status(hackrf_device* device) 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1671,8 +1675,7 @@ int ADDCALL hackrf_cpld_write( &data[i], chunk_size, &transferred, - 10000 // long timeout to allow for CPLD programming - ); + CPLD_WRITE_TIMEOUT); if (result != LIBUSB_SUCCESS) { last_libusb_error = result; @@ -1694,7 +1697,7 @@ int ADDCALL hackrf_board_id_read(hackrf_device* device, uint8_t* value) 0, value, 1, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 1) { last_libusb_error = result; @@ -1718,7 +1721,7 @@ int ADDCALL hackrf_version_string_read( 0, (unsigned char*) version, length, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 0) { last_libusb_error = result; @@ -1769,7 +1772,7 @@ int ADDCALL hackrf_set_freq(hackrf_device* device, const uint64_t freq_hz) 0, (unsigned char*) &set_freq_params, length, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < length) { last_libusb_error = result; @@ -1829,7 +1832,7 @@ int ADDCALL hackrf_set_freq_explicit( 0, (unsigned char*) ¶ms, length, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < length) { last_libusb_error = result; @@ -1871,7 +1874,7 @@ int ADDCALL hackrf_set_sample_rate_manual( 0, (unsigned char*) &set_fracrate_params, length, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < length) { last_libusb_error = result; @@ -1943,7 +1946,7 @@ int ADDCALL hackrf_set_amp_enable(hackrf_device* device, const uint8_t value) 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -1969,7 +1972,7 @@ int ADDCALL hackrf_board_partid_serialno_read( 0, (unsigned char*) read_partid_serialno, length, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < length) { last_libusb_error = result; @@ -2010,7 +2013,7 @@ int ADDCALL hackrf_set_lna_gain(hackrf_device* device, uint32_t value) value, &retval, 1, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 1 || !retval) { return HACKRF_ERROR_INVALID_PARAM; @@ -2037,7 +2040,7 @@ int ADDCALL hackrf_set_vga_gain(hackrf_device* device, uint32_t value) value, &retval, 1, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 1 || !retval) { return HACKRF_ERROR_INVALID_PARAM; @@ -2063,7 +2066,7 @@ int ADDCALL hackrf_set_txvga_gain(hackrf_device* device, uint32_t value) value, &retval, 1, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 1 || !retval) { return HACKRF_ERROR_INVALID_PARAM; @@ -2084,7 +2087,7 @@ int ADDCALL hackrf_set_antenna_enable(hackrf_device* device, const uint8_t value 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -2401,7 +2404,7 @@ ADDAPI int ADDCALL hackrf_enable_tx_flush( device->buffer_size, hackrf_libusb_flush_callback, device, - 0); + DEFAULT_REQUEST_TIMEOUT); device->flush_transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; @@ -2669,7 +2672,7 @@ int ADDCALL hackrf_set_hw_sync_mode(hackrf_device* device, const uint8_t value) 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -2750,7 +2753,7 @@ int ADDCALL hackrf_init_sweep( (num_bytes >> 16) & 0xffff, data, size, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < size) { last_libusb_error = result; @@ -2784,7 +2787,7 @@ int ADDCALL hackrf_get_operacake_boards(hackrf_device* device, uint8_t* boards) 0, boards, 8, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 8) { last_libusb_error = result; @@ -2823,7 +2826,7 @@ int ADDCALL hackrf_set_operacake_mode( (uint8_t) mode, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -2862,7 +2865,7 @@ int ADDCALL hackrf_get_operacake_mode( 0, &buf, 1, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 1) { last_libusb_error = result; @@ -2905,7 +2908,7 @@ int ADDCALL hackrf_set_operacake_ports( port_a | (port_b << 8), NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -2927,7 +2930,7 @@ int ADDCALL hackrf_reset(hackrf_device* device) 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -2957,7 +2960,7 @@ int ADDCALL hackrf_set_operacake_ranges( 0, ranges, len_ranges, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < len_ranges) { last_libusb_error = result; @@ -2998,7 +3001,7 @@ int ADDCALL hackrf_set_operacake_freq_ranges( 0, range_bytes, len_ranges, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < len_ranges) { last_libusb_error = result; @@ -3040,7 +3043,7 @@ int ADDCALL hackrf_set_operacake_dwell_times( 0, dwell_data, data_len, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < data_len) { last_libusb_error = result; @@ -3063,7 +3066,7 @@ int ADDCALL hackrf_set_clkout_enable(hackrf_device* device, const uint8_t value) 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -3085,7 +3088,7 @@ int ADDCALL hackrf_get_clkin_status(hackrf_device* device, uint8_t* status) 0, status, 1, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 1) { last_libusb_error = result; return HACKRF_ERROR_LIBUSB; @@ -3114,7 +3117,7 @@ int ADDCALL hackrf_operacake_gpio_test( 0, (unsigned char*) test_result, 2, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 1) { last_libusb_error = result; @@ -3140,7 +3143,7 @@ int ADDCALL hackrf_cpld_checksum(hackrf_device* device, uint32_t* crc) 0, (unsigned char*) crc, length, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < length) { last_libusb_error = result; @@ -3165,7 +3168,7 @@ int ADDCALL hackrf_set_ui_enable(hackrf_device* device, const uint8_t value) 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -3223,7 +3226,7 @@ int ADDCALL hackrf_board_rev_read(hackrf_device* device, uint8_t* value) 0, value, 1, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 1) { last_libusb_error = result; @@ -3307,7 +3310,7 @@ int ADDCALL hackrf_supported_platform_read(hackrf_device* device, uint32_t* valu 0, &data[0], 4, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 1) { last_libusb_error = result; @@ -3330,7 +3333,7 @@ int ADDCALL hackrf_set_leds(hackrf_device* device, const uint8_t state) 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -3376,7 +3379,7 @@ int ADDCALL hackrf_set_user_bias_t_opts( 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -3399,7 +3402,7 @@ int ADDCALL hackrf_set_p1_ctrl(hackrf_device* device, const enum p1_ctrl_signal 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -3422,7 +3425,7 @@ int ADDCALL hackrf_set_p2_ctrl(hackrf_device* device, const enum p2_ctrl_signal 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -3447,7 +3450,7 @@ int ADDCALL hackrf_set_clkin_ctrl( 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -3470,7 +3473,7 @@ int ADDCALL hackrf_set_narrowband_filter(hackrf_device* device, const uint8_t va 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -3493,7 +3496,7 @@ int ADDCALL hackrf_set_fpga_bitstream(hackrf_device* device, const uint8_t index 0, NULL, 0, - 0); + DEFAULT_REQUEST_TIMEOUT); if (result != 0) { last_libusb_error = result; @@ -3521,7 +3524,7 @@ int ADDCALL hackrf_radio_read_register( bank, (unsigned char*) value, length, - 0); + DEFAULT_REQUEST_TIMEOUT); *value = FROM_LE64(*value); if (result < length) { @@ -3561,7 +3564,7 @@ int ADDCALL hackrf_radio_write_register( bank, &data[0], sizeof(data), - 0); + DEFAULT_REQUEST_TIMEOUT); if (result < 9) { last_libusb_error = result; From b4600148116b0aaeb91291d3c8bd7c50977dbde2 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 13 May 2026 21:52:51 +0100 Subject: [PATCH 127/173] Move cpld_jtag_sram_load() into cpld_jtag.c. --- firmware/common/cpld_jtag.c | 13 +++++++++++++ firmware/common/cpld_jtag.h | 1 + firmware/hackrf_usb/hackrf_usb.c | 15 --------------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/firmware/common/cpld_jtag.c b/firmware/common/cpld_jtag.c index 441fefe8..4d313d01 100644 --- a/firmware/common/cpld_jtag.c +++ b/firmware/common/cpld_jtag.c @@ -21,6 +21,7 @@ #include "cpld_jtag.h" +#include "cpld_xc2c.h" #include "platform_detect.h" #ifdef IS_NOT_PRALINE #include @@ -138,4 +139,16 @@ unsigned char cpld_jtag_get_next_byte(void) xsvf_pos++; return byte; } + +bool cpld_jtag_sram_load(jtag_t* const jtag) +{ + cpld_jtag_take(jtag); + cpld_xc2c64a_jtag_sram_write(jtag, &cpld_hackrf_program_sram); + const bool success = cpld_xc2c64a_jtag_sram_verify( + jtag, + &cpld_hackrf_program_sram, + &cpld_hackrf_verify); + cpld_jtag_release(jtag); + return success; +} #endif diff --git a/firmware/common/cpld_jtag.h b/firmware/common/cpld_jtag.h index f49f1317..b9a4b52f 100644 --- a/firmware/common/cpld_jtag.h +++ b/firmware/common/cpld_jtag.h @@ -58,6 +58,7 @@ int cpld_jtag_program( unsigned char* const buffer, refill_buffer_cb refill); unsigned char cpld_jtag_get_next_byte(void); +bool cpld_jtag_sram_load(jtag_t* const jtag); /* Driver instance. */ extern jtag_gpio_t jtag_gpio_cpld; diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 97863876..eb7f85c5 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -69,7 +69,6 @@ #endif #ifdef IS_NOT_PRALINE #include - #include #endif #include "usb_api_adc.h" @@ -261,20 +260,6 @@ void usb_set_descriptor_by_serial_number(void) } } -#ifdef IS_NOT_PRALINE -static bool cpld_jtag_sram_load(jtag_t* const jtag) -{ - cpld_jtag_take(jtag); - cpld_xc2c64a_jtag_sram_write(jtag, &cpld_hackrf_program_sram); - const bool success = cpld_xc2c64a_jtag_sram_verify( - jtag, - &cpld_hackrf_program_sram, - &cpld_hackrf_verify); - cpld_jtag_release(jtag); - return success; -} -#endif - static void m0_rom_to_ram(void) { uint32_t* dest = &__ram_m0_start__; From d00dae3835f15a398915d764393a4c0b3e256ff0 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sun, 3 May 2026 09:03:37 +0100 Subject: [PATCH 128/173] si5351c: API: change terminology to refer to XTAL/CLKIN as "inputs". There are multiple similar but different things called a "source" or "clock source", or that are set in a register called "SRC", in the context of the Si5351 and its usage in HackRF. One place we can be unambiguous is that there are only two inputs to the Si5351C itself: XTAL and CLKIN. Let's have a type for that alone, and name it as such when we use it. There should be no functional changes in this commit. --- firmware/common/clock_gen.c | 9 +++++---- firmware/common/si5351c.c | 28 ++++++++++++++-------------- firmware/common/si5351c.h | 17 +++++++---------- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index 0f0085c2..f1f0bc60 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -111,7 +111,7 @@ void clock_gen_init(void) sample_rate_set(SR_FP_MHZ(10), true); si5351c_configure_clock_control(&si5351c); - si5351c_set_clock_source(&si5351c, PLL_SOURCE_XTAL); + si5351c_change_input(&si5351c, SI5351C_INPUT_XTAL); // soft reset si5351c_reset_pll(&si5351c, SI5351C_PLL_BOTH); si5351c_enable_clock_outputs(&si5351c); @@ -159,9 +159,10 @@ clock_source_t activate_best_clock_source(void) /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ } - si5351c_set_clock_source( - &si5351c, - (source == CLOCK_SOURCE_HACKRF) ? PLL_SOURCE_XTAL : PLL_SOURCE_CLKIN); + si5351c_input_t input = (source == CLOCK_SOURCE_HACKRF) ? SI5351C_INPUT_XTAL : + SI5351C_INPUT_CLKIN; + si5351c_change_input(&si5351c, input); + hackrf_ui()->set_clock_source(source); return source; diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index a7b605f5..23d18e93 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -48,7 +48,8 @@ si5351c_driver_t si5351c = { .i2c_address = 0x60, }; -static enum pll_sources active_clock_source = PLL_SOURCE_UNINITIALIZED; +static bool input_initialized = false; +static si5351c_input_t active_input; /* External clock output default is deactivated as it creates noise */ static bool clkout_enabled = false; static uint8_t outputs_disabled = 0xff; @@ -160,13 +161,11 @@ void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv) * CLKIN_DIV=0 (Divide by 1) * Set both PLLA_SRC and PLLB_SRC */ -void si5351c_configure_pll_sources( - si5351c_driver_t* const drv, - const enum pll_sources source) +void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input) { uint8_t data[] = {15, 0x00}; - if (source == PLL_SOURCE_CLKIN) { + if (input == SI5351C_INPUT_CLKIN) { data[1] = 0x0c; } si5351c_write(drv, data, sizeof(data)); @@ -175,11 +174,11 @@ void si5351c_configure_pll_sources( /* MultiSynth NA (PLLA) and NB (PLLB) */ void si5351c_configure_pll_multisynth( si5351c_driver_t* const drv, - const enum pll_sources source) + const si5351c_input_t input) { /* XTAL: 25 MHz * (0x0e00 + 512) / 128 = 800 MHz, integer mode */ uint8_t data[] = {26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00}; - if (source == PLL_SOURCE_CLKIN) { + if (input == SI5351C_INPUT_CLKIN) { /* CLKIN: 10 MHz * (0x2600 + 512) / 128 = 800 MHz, integer mode */ data[4] = 0x26; } @@ -415,9 +414,9 @@ void si5351c_set_int_mode( } } -void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source) +void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input) { - if (source == active_clock_source) { + if (input_initialized && input == active_input) { return; } si5351c_disable_all_outputs(drv); @@ -427,8 +426,8 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source * HackRF One r9 always uses PLL A on the XTAL input * but externally switches that input to CLKIN. */ - si5351c_configure_pll_sources(drv, PLL_SOURCE_XTAL); - if (source == PLL_SOURCE_CLKIN) { + si5351c_configure_inputs(drv, SI5351C_INPUT_XTAL); + if (input == SI5351C_INPUT_CLKIN) { gpio_set(platform_gpio()->h1r9_clkin_en); } else { gpio_clear(platform_gpio()->h1r9_clkin_en); @@ -437,11 +436,12 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source #endif #ifdef IS_NOT_H1_R9 if (IS_NOT_H1_R9) { - si5351c_configure_pll_sources(drv, source); + si5351c_configure_inputs(drv, input); } #endif - si5351c_configure_pll_multisynth(drv, source); - active_clock_source = source; + si5351c_configure_pll_multisynth(drv, input); + active_input = input; + input_initialized = true; si5351c_reset_pll(drv, SI5351C_PLL_BOTH); } diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index b38eac90..aedc7d05 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -64,11 +64,10 @@ typedef enum { #define SI5351C_CLK_PLL_SRC(x) ((x & SI5351C_PLL_B) << 4) -enum pll_sources { - PLL_SOURCE_UNINITIALIZED = -1, - PLL_SOURCE_XTAL = 0, - PLL_SOURCE_CLKIN = 1, -}; +typedef enum { + SI5351C_INPUT_XTAL = 0, + SI5351C_INPUT_CLKIN = 1, +} si5351c_input_t; typedef struct { i2c_bus_t* const bus; @@ -80,12 +79,10 @@ void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv); void si5351c_power_down_all_clocks(si5351c_driver_t* const drv); void si5351c_set_crystal_configuration(si5351c_driver_t* const drv); void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv); -void si5351c_configure_pll_sources( - si5351c_driver_t* const drv, - const enum pll_sources source); +void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input); void si5351c_configure_pll_multisynth( si5351c_driver_t* const drv, - const enum pll_sources source); + const si5351c_input_t input); void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll); void si5351c_configure_multisynth( si5351c_driver_t* const drv, @@ -100,7 +97,7 @@ void si5351c_set_int_mode( si5351c_driver_t* const drv, const uint_fast8_t ms_number, const uint_fast8_t on); -void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source); +void si5351c_change_input(si5351c_driver_t* const drv, const si5351c_input_t input); bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv); void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val); From 098b9dd9f20fd971efbd84429a8904d633092d12 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sun, 3 May 2026 09:19:45 +0100 Subject: [PATCH 129/173] si5351c: API: distinguish between PLL IDs and PLL masks. We're going to want a type that just identifies one PLL, and is consistent with how the PLLs are numbered in register settings. The current si5351c_pll_t is a bitmask which allows referring to both PLLs. Let's make that a different type, and name things more clearly when using it. --- firmware/common/clock_gen.c | 4 ++-- firmware/common/si5351c.c | 16 ++++++++-------- firmware/common/si5351c.h | 15 ++++++++++----- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index f1f0bc60..e91e3822 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -113,7 +113,7 @@ void clock_gen_init(void) si5351c_configure_clock_control(&si5351c); si5351c_change_input(&si5351c, SI5351C_INPUT_XTAL); // soft reset - si5351c_reset_pll(&si5351c, SI5351C_PLL_BOTH); + si5351c_reset_plls(&si5351c, SI5351C_PLL_MASK_BOTH); si5351c_enable_clock_outputs(&si5351c); } @@ -412,7 +412,7 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) } /* Reset PLL to synchronize output clock phase. */ - si5351c_reset_pll(&si5351c, SI5351C_PLL_A); + si5351c_reset_plls(&si5351c, SI5351C_PLL_MASK_A); } #endif diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 23d18e93..30c23129 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -91,7 +91,7 @@ void si5351c_disable_all_outputs(si5351c_driver_t* const drv) } /* Disable all CLKx outputs using selected PLL. */ -void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_t pll) +void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_mask_t mask) { /* * Bitmask defines outputs using PLL B. Other outputs are assumed to @@ -102,10 +102,10 @@ void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_t pll) pllb_outputs = 0x30; } - if (pll & SI5351C_PLL_A) { + if (mask & SI5351C_PLL_MASK_A) { outputs_disabled |= ~pllb_outputs; } - if (pll & SI5351C_PLL_B) { + if (mask & SI5351C_PLL_MASK_B) { outputs_disabled |= pllb_outputs; } uint8_t data[] = {3, outputs_disabled}; @@ -189,18 +189,18 @@ void si5351c_configure_pll_multisynth( si5351c_write(drv, data, sizeof(data)); } -void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll) +void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask) { uint8_t value = 0; - if (pll & SI5351C_PLL_A) { + if (mask & SI5351C_PLL_MASK_A) { value |= 0x20; } - if (pll & SI5351C_PLL_B) { + if (mask & SI5351C_PLL_MASK_B) { value |= 0x80; } - si5351c_disable_pll_outputs(drv, pll); + si5351c_disable_pll_outputs(drv, mask); uint8_t data[] = {177, value}; si5351c_write(drv, data, sizeof(data)); delay_us_at_mhz(2000, 204); @@ -442,7 +442,7 @@ void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input) si5351c_configure_pll_multisynth(drv, input); active_input = input; input_initialized = true; - si5351c_reset_pll(drv, SI5351C_PLL_BOTH); + si5351c_reset_plls(drv, SI5351C_PLL_MASK_BOTH); } bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index aedc7d05..491266cf 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -57,12 +57,17 @@ extern "C" { #define SI5351C_REVID 0x03 typedef enum { - SI5351C_PLL_A = 1, - SI5351C_PLL_B = 2, - SI5351C_PLL_BOTH = 3, + SI5351C_PLL_A = 0, + SI5351C_PLL_B = 1, } si5351c_pll_t; -#define SI5351C_CLK_PLL_SRC(x) ((x & SI5351C_PLL_B) << 4) +#define SI5351C_CLK_PLL_SRC(x) ((x & SI5351C_PLL_B) << 5) + +typedef enum { + SI5351C_PLL_MASK_A = 1, + SI5351C_PLL_MASK_B = 2, + SI5351C_PLL_MASK_BOTH = 3, +} si5351c_pll_mask_t; typedef enum { SI5351C_INPUT_XTAL = 0, @@ -83,7 +88,7 @@ void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t void si5351c_configure_pll_multisynth( si5351c_driver_t* const drv, const si5351c_input_t input); -void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll); +void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask); void si5351c_configure_multisynth( si5351c_driver_t* const drv, const uint_fast8_t ms_number, From a1cdbaddbf3bf20d74a3da7819093826d6d2a475 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sun, 3 May 2026 09:32:03 +0100 Subject: [PATCH 130/173] si5351c: internal: Add register definitions and helpers. --- firmware/common/si5351c_regs.def | 494 +++++++++++++++++++++++++++++++ 1 file changed, 494 insertions(+) create mode 100644 firmware/common/si5351c_regs.def diff --git a/firmware/common/si5351c_regs.def b/firmware/common/si5351c_regs.def new file mode 100644 index 00000000..5c3a6774 --- /dev/null +++ b/firmware/common/si5351c_regs.def @@ -0,0 +1,494 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +static inline void si5351c_reg_set_clean(si5351c_driver_t* drv, int reg) +{ + drv->regs_dirty[reg / 32] &= ~(1 << (reg % 32)); +} + +static inline void si5351c_reg_set_dirty(si5351c_driver_t* drv, int reg) +{ + drv->regs_dirty[reg / 32] |= (1 << (reg % 32)); +} + +static inline bool si5351c_reg_is_dirty(si5351c_driver_t* drv, int reg) +{ + return (drv->regs_dirty[reg / 32] & (1 << (reg % 32))) ? true : false; +} + +/* Generate static inline accessors that operate on the global + * regs. Done this way to (1) allow defs to be scraped out and used + * elsewhere, e.g. in scripts, (2) to avoid dealing with endian + * (structs). This may be used in firmware, or on host predefined + * register loads. */ + +/* On set_, register is always set dirty, even if nothing + * changed. This makes sure that writes that have side effects, + * e.g. frequency setting, are not skipped. */ + +/* n=name, r=regnum, o=offset (bits from LSB) of LSB of field, + * l=length (bits) */ +#define __MREG__(n,r,o,l) \ +static inline uint8_t get_##n(si5351c_driver_t* const _d) { \ + return (_d->regs[r] >> o) & ((1L<regs[r] &= (uint8_t)(~(((1L<regs[r] |= (uint8_t)(((v&((1L<> 8); \ + set_##n##_BOT(_d, v); \ +} + +#define __3PART__(n) \ +static inline uint32_t get_##n(si5351c_driver_t* const _d) { \ + return (get_##n##_TOP(_d) << 16) | (get_##n##_MID(_d) << 8) | get_##n##_BOT(_d); \ +} \ +static inline void set_##n(si5351c_driver_t* const _d, uint32_t v) { \ + set_##n##_TOP(_d, v >> 16); \ + set_##n##_MID(_d, v >> 8); \ + set_##n##_BOT(_d, v); \ +} + +/* Generate accessors for per-PLL and per-multisynth registers */ +/* n=name, p=prefix, s=suffix */ + +#define __PER_PLL__(n, p, s) \ +static inline uint32_t get_##n(si5351c_driver_t* const _d, si5351c_pll_t pll) { \ + return (pll == SI5351C_PLL_A) ? get_##p##A##s(_d) : get_##p##B##s(_d); \ +} \ +static inline void set_##n(si5351c_driver_t* const _d, si5351c_pll_t pll, uint32_t v) { \ + if (pll == SI5351C_PLL_A) { \ + set_##p##A##s(_d, v); \ + } else { \ + set_##p##B##s(_d, v); \ + } \ +} + +#define __PER_MS__(n, p, s) \ +static inline uint32_t get_##n(si5351c_driver_t* const _d, uint8_t i) { \ + switch (i) { \ + case 0: return get_##p##0##s(_d); \ + case 1: return get_##p##1##s(_d); \ + case 2: return get_##p##2##s(_d); \ + case 3: return get_##p##3##s(_d); \ + case 4: return get_##p##4##s(_d); \ + case 5: return get_##p##5##s(_d); \ + case 6: return get_##p##6##s(_d); \ + case 7: return get_##p##7##s(_d); \ + default: return 0; \ + } \ +} \ +static inline void set_##n(si5351c_driver_t* const _d, uint8_t i, uint32_t v) { \ + switch (i) { \ + case 0: set_##p##0##s(_d, v); return; \ + case 1: set_##p##1##s(_d, v); return; \ + case 2: set_##p##2##s(_d, v); return; \ + case 3: set_##p##3##s(_d, v); return; \ + case 4: set_##p##4##s(_d, v); return; \ + case 5: set_##p##5##s(_d, v); return; \ + case 6: set_##p##6##s(_d, v); return; \ + case 7: set_##p##7##s(_d, v); return; \ + } \ +} \ +static inline void set_all_##n(si5351c_driver_t* const _d, uint32_t v) { \ + set_##p##0##s(_d, v); \ + set_##p##1##s(_d, v); \ + set_##p##2##s(_d, v); \ + set_##p##3##s(_d, v); \ + set_##p##4##s(_d, v); \ + set_##p##5##s(_d, v); \ + set_##p##6##s(_d, v); \ + set_##p##7##s(_d, v); \ +} + +#define __PER_FRAC_MS__(n, p, s) \ +static inline void set_##n(si5351c_driver_t* const _d, uint8_t i, uint32_t v) { \ + switch (i) { \ + case 0: set_##p##0##s(_d, v); return; \ + case 1: set_##p##1##s(_d, v); return; \ + case 2: set_##p##2##s(_d, v); return; \ + case 3: set_##p##3##s(_d, v); return; \ + case 4: set_##p##4##s(_d, v); return; \ + case 5: set_##p##5##s(_d, v); return; \ + } \ +} + +/* Register 0: Device Status */ +__MREG__(SYS_INIT, 0, 7, 1) +__MREG__(LOL_B, 0, 6, 1) +__MREG__(LOL_A, 0, 5, 1) +__MREG__(LOS_CLKIN, 0, 4, 1) +__MREG__(LOS_XTAL, 0, 3, 1) +__MREG__(REVID, 0, 0, 2) + +/* Register 1: Interrupt Status Sticky */ +__MREG__(SYS_INIT_STKY, 1, 7, 1) +__MREG__(LOL_B_STKY, 1, 6, 1) +__MREG__(LOL_A_STKY, 1, 5, 1) +__MREG__(LOS_CLKIN_STKY, 1, 4, 1) +__MREG__(LOS_XTAL_STKY, 1, 3, 1) + +/* Register 2: Interrupt Status Mask */ +__MREG__(SYS_INIT_MASK, 2, 7, 1) +__MREG__(LOL_B_MASK, 2, 6, 1) +__MREG__(LOL_A_MASK, 2, 5, 1) +__MREG__(LOS_CLKIN_MASK, 2, 4, 1) +__MREG__(LOS_XTAL_MASK, 2, 3, 1) + +/* Register 3: Output Enable Control */ +__MREG__(CLK7_OEB, 3, 7, 1) +__MREG__(CLK6_OEB, 3, 6, 1) +__MREG__(CLK5_OEB, 3, 5, 1) +__MREG__(CLK4_OEB, 3, 4, 1) +__MREG__(CLK3_OEB, 3, 3, 1) +__MREG__(CLK2_OEB, 3, 2, 1) +__MREG__(CLK1_OEB, 3, 1, 1) +__MREG__(CLK0_OEB, 3, 0, 1) + +/* Register 4: OEB Pin Enable Control */ +__MREG__(OEB_CLK7, 4, 7, 1) +__MREG__(OEB_CLK6, 4, 6, 1) +__MREG__(OEB_CLK5, 4, 5, 1) +__MREG__(OEB_CLK4, 4, 4, 1) +__MREG__(OEB_CLK3, 4, 3, 1) +__MREG__(OEB_CLK2, 4, 2, 1) +__MREG__(OEB_CLK1, 4, 1, 1) +__MREG__(OEB_CLK0, 4, 0, 1) + +/* Register 9: OEB Pin Enable Control Mask */ +__MREG__(OEB_MASK7, 9, 7, 1) +__MREG__(OEB_MASK6, 9, 6, 1) +__MREG__(OEB_MASK5, 9, 5, 1) +__MREG__(OEB_MASK4, 9, 4, 1) +__MREG__(OEB_MASK3, 9, 3, 1) +__MREG__(OEB_MASK2, 9, 2, 1) +__MREG__(OEB_MASK1, 9, 1, 1) +__MREG__(OEB_MASK0, 9, 0, 1) + +/* Register 15: PLL Input Source */ +__MREG__(CLKIN_DIV, 15, 6, 2) +__MREG__(PLLB_SRC, 15, 3, 1) +__MREG__(PLLA_SRC, 15, 2, 1) + +/* Register 16: CLK0 Control */ +__MREG__(CLK0_PDN, 16, 7, 1) +__MREG__(MS0_INT, 16, 6, 1) +__MREG__(MS0_SRC, 16, 5, 1) +__MREG__(CLK0_INV, 16, 4, 1) +__MREG__(CLK0_SRC, 16, 2, 2) +__MREG__(CLK0_IDRV, 16, 0, 2) + +/* Register 17: CLK1 Control */ +__MREG__(CLK1_PDN, 17, 7, 1) +__MREG__(MS1_INT, 17, 6, 1) +__MREG__(MS1_SRC, 17, 5, 1) +__MREG__(CLK1_INV, 17, 4, 1) +__MREG__(CLK1_SRC, 17, 2, 2) +__MREG__(CLK1_IDRV, 17, 0, 2) + +/* Register 18: CLK2 Control */ +__MREG__(CLK2_PDN, 18, 7, 1) +__MREG__(MS2_INT, 18, 6, 1) +__MREG__(MS2_SRC, 18, 5, 1) +__MREG__(CLK2_INV, 18, 4, 1) +__MREG__(CLK2_SRC, 18, 2, 2) +__MREG__(CLK2_IDRV, 18, 0, 2) + +/* Register 19: CLK3 Control */ +__MREG__(CLK3_PDN, 19, 7, 1) +__MREG__(MS3_INT, 19, 6, 1) +__MREG__(MS3_SRC, 19, 5, 1) +__MREG__(CLK3_INV, 19, 4, 1) +__MREG__(CLK3_SRC, 19, 2, 2) +__MREG__(CLK3_IDRV, 19, 0, 2) + +/* Register 20: CLK4 Control */ +__MREG__(CLK4_PDN, 20, 7, 1) +__MREG__(MS4_INT, 20, 6, 1) +__MREG__(MS4_SRC, 20, 5, 1) +__MREG__(CLK4_INV, 20, 4, 1) +__MREG__(CLK4_SRC, 20, 2, 2) +__MREG__(CLK4_IDRV, 20, 0, 2) + +/* Register 21: CLK5 Control */ +__MREG__(CLK5_PDN, 21, 7, 1) +__MREG__(MS5_INT, 21, 6, 1) +__MREG__(MS5_SRC, 21, 5, 1) +__MREG__(CLK5_INV, 21, 4, 1) +__MREG__(CLK5_SRC, 21, 2, 2) +__MREG__(CLK5_IDRV, 21, 0, 2) + +/* Register 22: CLK6 Control */ +__MREG__(CLK6_PDN, 22, 7, 1) +__MREG__(FBA_INT, 22, 6, 1) +__MREG__(MS6_SRC, 22, 5, 1) +__MREG__(CLK6_INV, 22, 4, 1) +__MREG__(CLK6_SRC, 22, 2, 2) +__MREG__(CLK6_IDRV, 22, 0, 2) + +/* Register 23: CLK7 Control */ +__MREG__(CLK7_PDN, 23, 7, 1) +__MREG__(FBB_INT, 23, 6, 1) +__MREG__(MS7_SRC, 23, 5, 1) +__MREG__(CLK7_INV, 23, 4, 1) +__MREG__(CLK7_SRC, 23, 2, 2) +__MREG__(CLK7_IDRV, 23, 0, 2) + +/* Register 24: CLK0-CLK3 Disable State */ +__MREG__(CLK3_DIS_STATE, 24, 6, 2) +__MREG__(CLK2_DIS_STATE, 24, 4, 2) +__MREG__(CLK1_DIS_STATE, 24, 2, 2) +__MREG__(CLK0_DIS_STATE, 24, 0, 2) + +/* Register 25: CLK4-CLK7 Disable State */ +__MREG__(CLK7_DIS_STATE, 25, 6, 2) +__MREG__(CLK6_DIS_STATE, 25, 4, 2) +__MREG__(CLK5_DIS_STATE, 25, 2, 2) +__MREG__(CLK4_DIS_STATE, 25, 0, 2) + +/* Registers 26-33: Multisynth NA Parameters */ +__MREG__(MSNA_P3_MID, 26, 0, 8) +__MREG__(MSNA_P3_BOT, 27, 0, 8) +__MREG__(MSNA_P1_TOP, 28, 0, 2) +__MREG__(MSNA_P1_MID, 29, 0, 8) +__MREG__(MSNA_P1_BOT, 30, 0, 8) +__MREG__(MSNA_P3_TOP, 31, 4, 4) +__MREG__(MSNA_P2_TOP, 31, 0, 4) +__MREG__(MSNA_P2_MID, 32, 0, 8) +__MREG__(MSNA_P2_BOT, 33, 0, 8) + +/* Registers 34-41: Multisynth NB Parameters */ +__MREG__(MSNB_P3_MID, 34, 0, 8) +__MREG__(MSNB_P3_BOT, 35, 0, 8) +__MREG__(MSNB_P1_TOP, 36, 0, 2) +__MREG__(MSNB_P1_MID, 37, 0, 8) +__MREG__(MSNB_P1_BOT, 38, 0, 8) +__MREG__(MSNB_P3_TOP, 39, 4, 4) +__MREG__(MSNB_P2_TOP, 39, 0, 4) +__MREG__(MSNB_P2_MID, 40, 0, 8) +__MREG__(MSNB_P2_BOT, 41, 0, 8) + +/* Registers 42-49: Multisynth 0 Parameters */ +__MREG__(MS0_P3_MID, 42, 0, 8) +__MREG__(MS0_P3_BOT, 43, 0, 8) +__MREG__(R0_DIV, 44, 4, 3) +__MREG__(MS0_DIVBY4, 44, 2, 2) +__MREG__(MS0_P1_TOP, 44, 0, 2) +__MREG__(MS0_P1_MID, 45, 0, 8) +__MREG__(MS0_P1_BOT, 46, 0, 8) +__MREG__(MS0_P3_TOP, 47, 4, 4) +__MREG__(MS0_P2_TOP, 47, 0, 4) +__MREG__(MS0_P2_MID, 48, 0, 8) +__MREG__(MS0_P2_BOT, 49, 0, 8) + +/* Registers 50-57: Multisynth 1 Parameters */ +__MREG__(MS1_P3_MID, 50, 0, 8) +__MREG__(MS1_P3_BOT, 51, 0, 8) +__MREG__(R1_DIV, 52, 4, 3) +__MREG__(MS1_DIVBY4, 52, 2, 2) +__MREG__(MS1_P1_TOP, 52, 0, 2) +__MREG__(MS1_P1_MID, 53, 0, 8) +__MREG__(MS1_P1_BOT, 54, 0, 8) +__MREG__(MS1_P3_TOP, 55, 4, 4) +__MREG__(MS1_P2_TOP, 55, 0, 4) +__MREG__(MS1_P2_MID, 56, 0, 8) +__MREG__(MS1_P2_BOT, 57, 0, 8) + +/* Registers 58-65: Multisynth 2 Parameters */ +__MREG__(MS2_P3_MID, 58, 0, 8) +__MREG__(MS2_P3_BOT, 59, 0, 8) +__MREG__(R2_DIV, 60, 4, 3) +__MREG__(MS2_DIVBY4, 60, 2, 2) +__MREG__(MS2_P1_TOP, 60, 0, 2) +__MREG__(MS2_P1_MID, 61, 0, 8) +__MREG__(MS2_P1_BOT, 62, 0, 8) +__MREG__(MS2_P3_TOP, 63, 4, 4) +__MREG__(MS2_P2_TOP, 63, 0, 4) +__MREG__(MS2_P2_MID, 64, 0, 8) +__MREG__(MS2_P2_BOT, 65, 0, 8) + +/* Registers 66-73: Multisynth 3 Parameters */ +__MREG__(MS3_P3_MID, 66, 0, 8) +__MREG__(MS3_P3_BOT, 67, 0, 8) +__MREG__(R3_DIV, 68, 4, 3) +__MREG__(MS3_DIVBY4, 68, 2, 2) +__MREG__(MS3_P1_TOP, 68, 0, 2) +__MREG__(MS3_P1_MID, 69, 0, 8) +__MREG__(MS3_P1_BOT, 70, 0, 8) +__MREG__(MS3_P3_TOP, 71, 4, 4) +__MREG__(MS3_P2_TOP, 71, 0, 4) +__MREG__(MS3_P2_MID, 72, 0, 8) +__MREG__(MS3_P2_BOT, 73, 0, 8) + +/* Registers 74-81: Multisynth 4 Parameters */ +__MREG__(MS4_P3_MID, 74, 0, 8) +__MREG__(MS4_P3_BOT, 75, 0, 8) +__MREG__(R4_DIV, 76, 4, 3) +__MREG__(MS4_DIVBY4, 76, 2, 2) +__MREG__(MS4_P1_TOP, 76, 0, 2) +__MREG__(MS4_P1_MID, 77, 0, 8) +__MREG__(MS4_P1_BOT, 78, 0, 8) +__MREG__(MS4_P3_TOP, 79, 4, 4) +__MREG__(MS4_P2_TOP, 79, 0, 4) +__MREG__(MS4_P2_MID, 80, 0, 8) +__MREG__(MS4_P2_BOT, 81, 0, 8) + +/* Registers 82-89: Multisynth 5 Parameters */ +__MREG__(MS5_P3_MID, 82, 0, 8) +__MREG__(MS5_P3_BOT, 83, 0, 8) +__MREG__(R5_DIV, 84, 4, 3) +__MREG__(MS5_DIVBY4, 84, 2, 2) +__MREG__(MS5_P1_TOP, 84, 0, 2) +__MREG__(MS5_P1_MID, 85, 0, 8) +__MREG__(MS5_P1_BOT, 86, 0, 8) +__MREG__(MS5_P3_TOP, 87, 4, 4) +__MREG__(MS5_P2_TOP, 87, 0, 4) +__MREG__(MS5_P2_MID, 88, 0, 8) +__MREG__(MS5_P2_BOT, 89, 0, 8) + +/* Register 90: Multisynth 6 Parameters */ +__MREG__(MS6_P1, 90, 0, 8) + +/* Register 91: Multisynth 7 Parameters */ +__MREG__(MS7_P1, 91, 0, 8) + +/* Register 92: Clock 6 and 7 Output Divider */ +__MREG__(R7_DIV, 92, 4, 3) +__MREG__(R6_DIV, 92, 0, 3) + +/* Registers 149-161: Spread Spectrum Parameters */ +__MREG__(SSC_EN, 149, 7, 1) +__MREG__(SSDN_P2_TOP, 149, 0, 7) +__MREG__(SSDN_P2_BOT, 150, 0, 8) +__MREG__(SSC_MODE, 151, 7, 1) +__MREG__(SSDN_P3_TOP, 151, 0, 7) +__MREG__(SSDN_P3_BOT, 152, 0, 8) +__MREG__(SSDN_P1_BOT, 153, 0, 8) +__MREG__(SSUDP_TOP, 154, 4, 4) +__MREG__(SSDN_P1_TOP, 154, 0, 4) +__MREG__(SSUDP_BOT, 155, 0, 8) +__MREG__(SSUP_P2_TOP, 156, 0, 7) +__MREG__(SSUP_P2_BOT, 157, 0, 8) +__MREG__(SSUP_P3_TOP, 158, 0, 8) +__MREG__(SSUP_P3_BOT, 159, 0, 8) +__MREG__(SSUP_P1_BOT, 160, 0, 8) +__MREG__(SS_NCLK, 161, 4, 4) +__MREG__(SSUP_P1_TOP, 161, 0, 4) + +/* Registers 162-163: VCXO Parameter */ +__MREG__(VCXO_PARAM_BOT, 162, 0, 8) +__MREG__(VCXO_PARAM_MID, 163, 0, 8) +__MREG__(VCXO_PARAM_TOP, 164, 0, 6) + +/* Registers 165-170: CLK0-CLK5 Initial Phase Offsets */ +__MREG__(CLK0_PHOFF, 165, 0, 7) +__MREG__(CLK1_PHOFF, 166, 0, 7) +__MREG__(CLK2_PHOFF, 167, 0, 7) +__MREG__(CLK3_PHOFF, 168, 0, 7) +__MREG__(CLK4_PHOFF, 169, 0, 7) +__MREG__(CLK5_PHOFF, 170, 0, 7) + +/* Register 177: PLL Reset */ +__MREG__(PLLB_RST, 177, 7, 1) +__MREG__(PLLA_RST, 177, 5, 1) + +/* Register 183: Crystal Internal Load Capacitance */ +__MREG__(XTAL_CL, 183, 6, 2) + +/* Register 187: Fanout Enable */ +__MREG__(CLKIN_FANOUT_EN, 187, 7, 1) +__MREG__(XO_FANOUT_EN, 187, 6, 1) +__MREG__(MS_FANOUT_EN, 187, 4, 1) + +/* 3-part parameters */ +__3PART__(MSNA_P1) +__3PART__(MSNA_P2) +__3PART__(MSNA_P3) +__3PART__(MSNB_P1) +__3PART__(MSNB_P2) +__3PART__(MSNB_P3) +__3PART__(MS0_P1) +__3PART__(MS0_P2) +__3PART__(MS0_P3) +__3PART__(MS1_P1) +__3PART__(MS1_P2) +__3PART__(MS1_P3) +__3PART__(MS2_P1) +__3PART__(MS2_P2) +__3PART__(MS2_P3) +__3PART__(MS3_P1) +__3PART__(MS3_P2) +__3PART__(MS3_P3) +__3PART__(MS4_P1) +__3PART__(MS4_P2) +__3PART__(MS4_P3) +__3PART__(MS5_P1) +__3PART__(MS5_P2) +__3PART__(MS5_P3) + +/* 2-part parameters */ +__2PART__(SSDN_P1) +__2PART__(SSDN_P2) +__2PART__(SSDN_P3) +__2PART__(SSUP_P1) +__2PART__(SSUP_P2) +__2PART__(SSUP_P3) +__2PART__(SSUDP) + +/* Per-PLL register groups */ +__PER_PLL__(LOL, LOL_, ) +__PER_PLL__(LOL_STKY, LOL_, _STKY) +__PER_PLL__(LOL_MASK, LOL_, _MASK) +__PER_PLL__(PLL_SRC, PLL, _SRC) +__PER_PLL__(MSN_P1, MSN, _P1) +__PER_PLL__(MSN_P2, MSN, _P2) +__PER_PLL__(MSN_P3, MSN, _P3) +__PER_PLL__(PLL_RST, PLL, _RST) + +/* Per-multisynth register groups */ +__PER_MS__(CLK_OEB, CLK, _OEB) +__PER_MS__(OEB_CLK, OEB_CLK, ) +__PER_MS__(OEB_MASK, OEB_MASK, ) +__PER_MS__(CLK_PDN, CLK, _PDN) +__PER_MS__(MS_SRC, MS, _SRC) +__PER_MS__(CLK_INV, CLK, _INV) +__PER_MS__(CLK_SRC, CLK, _SRC) +__PER_MS__(CLK_IDRV, CLK, _IDRV) +__PER_MS__(CLK_DIS_STATE, CLK, _DIS_STATE) +__PER_MS__(R_DIV, R, _DIV) +__PER_MS__(MS_P1, MS, _P1) +__PER_FRAC_MS__(MS_INT, MS, _INT) +__PER_FRAC_MS__(MS_P2, MS, _P2) +__PER_FRAC_MS__(MS_P3, MS, _P3) +__PER_FRAC_MS__(MS_DIVBY4, MS, _DIVBY4) +__PER_FRAC_MS__(CLK_PHOFF, CLK, _PHOFF) From 016910567bc236fcfb14ebb741185c468416cda8 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sun, 3 May 2026 09:32:58 +0100 Subject: [PATCH 131/173] si5351c: internal: reimplement using register helpers. --- firmware/common/si5351c.c | 480 +++++++++++++++++++++++--------------- firmware/common/si5351c.h | 72 ++++-- 2 files changed, 336 insertions(+), 216 deletions(-) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 30c23129..1f27d8db 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * * This file is part of HackRF. @@ -22,6 +22,7 @@ #include #include +#include #include "clock_io.h" #include "delay.h" @@ -36,6 +37,8 @@ #include "platform_scu.h" #endif +#include "si5351c_regs.def" + /* Driver instance. */ // const i2c_lpc_config_t i2c_config_si5351c_slow_clock = { // .duty_cycle_count = 15, @@ -52,13 +55,16 @@ static bool input_initialized = false; static si5351c_input_t active_input; /* External clock output default is deactivated as it creates noise */ static bool clkout_enabled = false; -static uint8_t outputs_disabled = 0xff; /* write to single register */ void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val) { const uint8_t data_tx[] = {reg, val}; - si5351c_write(drv, data_tx, 2); + i2c_bus_transfer(drv->bus, drv->i2c_address, data_tx, 2, NULL, 0); + if (reg < SI5351C_CACHED_REGS) { + drv->regs[reg] = val; + si5351c_reg_set_clean(drv, reg); + } } /* read single register */ @@ -67,72 +73,100 @@ uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg) const uint8_t data_tx[] = {reg}; uint8_t data_rx[] = {0x00}; i2c_bus_transfer(drv->bus, drv->i2c_address, data_tx, 1, data_rx, 1); - return data_rx[0]; + uint8_t val = data_rx[0]; + if (reg < SI5351C_CACHED_REGS) { + drv->regs[reg] = val; + si5351c_reg_set_clean(drv, reg); + } + return val; } -/* - * Write to one or more contiguous registers. data[0] should be the first - * register number, one or more values follow. - */ -void si5351c_write( - si5351c_driver_t* const drv, - const uint8_t* const data, - const size_t data_count) +/* Commit changes to register values. */ +void si5351c_regs_commit(si5351c_driver_t* drv) { - i2c_bus_transfer(drv->bus, drv->i2c_address, data, data_count, NULL, 0); + for (int start = 0; start < SI5351C_CACHED_REGS; start++) { + if (si5351c_reg_is_dirty(drv, start)) { + if (start == SI5351C_CACHED_REGS - 1) { + si5351c_write_single(drv, start, drv->regs[start]); + } else { + int end; + for (end = start + 1; end < 256; end++) { + if (!si5351c_reg_is_dirty(drv, end)) + break; + } + size_t len = 1 + (end - start); + uint8_t data_tx[len]; + data_tx[0] = start; + for (int i = 0; i < (end - start); i++) { + data_tx[1 + i] = drv->regs[start + i]; + } + i2c_bus_transfer( + drv->bus, + drv->i2c_address, + data_tx, + len, + NULL, + 0); + for (int i = start; i < end; i++) { + si5351c_reg_set_clean(drv, i); + } + start = end; + } + } + } + + /* Reset bits are self-clearing. */ + drv->regs[177] = 0; } /* Disable all CLKx outputs. */ void si5351c_disable_all_outputs(si5351c_driver_t* const drv) { - outputs_disabled = 0xff; - uint8_t data[] = {3, outputs_disabled}; - si5351c_write(drv, data, sizeof(data)); + set_all_CLK_OEB(drv, SI5351C_OUTPUT_DISABLE); + si5351c_regs_commit(drv); } /* Disable all CLKx outputs using selected PLL. */ void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_mask_t mask) { - /* - * Bitmask defines outputs using PLL B. Other outputs are assumed to - * use PLL A. - */ - uint8_t pllb_outputs = 0x00; - if (detected_platform() == BOARD_ID_PRALINE) { - pllb_outputs = 0x30; + /* For each CLKx output, check if it is using the specified PLL. */ + for (int op = 0; op < 8; op++) { + int ms; + /* First check which multisynth is used by this output. */ + switch (get_CLK_SRC(drv, op)) { + case SI5351C_SRC_MULTISYNTH_SELF: + ms = op; + break; + case SI5351C_SRC_MULTISYNTH_0_4: + ms = (op < 4) ? 0 : 4; + break; + default: + /* This output is not using any PLL */ + continue; + } + /* Now check which PLL is used by that multisynth. */ + if (mask & (1 << get_MS_SRC(drv, ms))) { + /* This output depends on the specified PLL; disable it. */ + set_CLK_OEB(drv, op, SI5351C_OUTPUT_DISABLE); + } } - - if (mask & SI5351C_PLL_MASK_A) { - outputs_disabled |= ~pllb_outputs; - } - if (mask & SI5351C_PLL_MASK_B) { - outputs_disabled |= pllb_outputs; - } - uint8_t data[] = {3, outputs_disabled}; - si5351c_write(drv, data, sizeof(data)); + si5351c_regs_commit(drv); } /* Turn off OEB pin control for all CLKx */ void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv) { - uint8_t data[] = {9, 0xFF}; - si5351c_write(drv, data, sizeof(data)); + set_all_OEB_MASK(drv, true); + si5351c_regs_commit(drv); } /* Power down all CLKx */ void si5351c_power_down_all_clocks(si5351c_driver_t* const drv) { - uint8_t data[] = { - 16, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE, - SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE}; - si5351c_write(drv, data, sizeof(data)); + set_all_CLK_PDN(drv, true); + set_FBA_INT(drv, true); + set_FBB_INT(drv, true); + si5351c_regs_commit(drv); } /* @@ -142,8 +176,8 @@ void si5351c_power_down_all_clocks(si5351c_driver_t* const drv) */ void si5351c_set_crystal_configuration(si5351c_driver_t* const drv) { - uint8_t data[] = {183, 0x80}; - si5351c_write(drv, data, sizeof(data)); + set_XTAL_CL(drv, SI5351C_XTAL_8PF); + si5351c_regs_commit(drv); } /* @@ -152,8 +186,10 @@ void si5351c_set_crystal_configuration(si5351c_driver_t* const drv) */ void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv) { - uint8_t data[] = {187, 0xD0}; - si5351c_write(drv, data, sizeof(data)); + set_CLKIN_FANOUT_EN(drv, true); + set_XO_FANOUT_EN(drv, true); + set_MS_FANOUT_EN(drv, true); + si5351c_regs_commit(drv); } /* @@ -163,12 +199,10 @@ void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv) */ void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input) { - uint8_t data[] = {15, 0x00}; - - if (input == SI5351C_INPUT_CLKIN) { - data[1] = 0x0c; - } - si5351c_write(drv, data, sizeof(data)); + set_CLKIN_DIV(drv, SI5351C_DIV_1); + set_PLLA_SRC(drv, input); + set_PLLB_SRC(drv, input); + si5351c_regs_commit(drv); } /* MultiSynth NA (PLLA) and NB (PLLB) */ @@ -176,33 +210,26 @@ void si5351c_configure_pll_multisynth( si5351c_driver_t* const drv, const si5351c_input_t input) { - /* XTAL: 25 MHz * (0x0e00 + 512) / 128 = 800 MHz, integer mode */ - uint8_t data[] = {26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00}; - if (input == SI5351C_INPUT_CLKIN) { - /* CLKIN: 10 MHz * (0x2600 + 512) / 128 = 800 MHz, integer mode */ - data[4] = 0x26; + for (si5351c_pll_t pll = SI5351C_PLL_A; pll <= SI5351C_PLL_B; pll++) { + if (input == SI5351C_INPUT_CLKIN) { + /* CLKIN: 10 MHz * (0x2600 + 512) / 128 = 800 MHz, integer mode */ + set_MSN_P1(drv, pll, 0x2600); + } else { + /* XTAL: 25 MHz * (0x0e00 + 512) / 128 = 800 MHz, integer mode */ + set_MSN_P1(drv, pll, 0x0E00); + } + set_MSN_P2(drv, pll, 0); + set_MSN_P3(drv, pll, 1); } - si5351c_write(drv, data, sizeof(data)); - - /* Apply same configuration to PLL B. */ - data[0] = 34; - si5351c_write(drv, data, sizeof(data)); + si5351c_regs_commit(drv); } void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask) { - uint8_t value = 0; - - if (mask & SI5351C_PLL_MASK_A) { - value |= 0x20; - } - if (mask & SI5351C_PLL_MASK_B) { - value |= 0x80; - } - si5351c_disable_pll_outputs(drv, mask); - uint8_t data[] = {177, value}; - si5351c_write(drv, data, sizeof(data)); + set_PLLA_RST(drv, (mask & SI5351C_PLL_MASK_A) ? true : false); + set_PLLB_RST(drv, (mask & SI5351C_PLL_MASK_B) ? true : false); + si5351c_regs_commit(drv); delay_us_at_mhz(2000, 204); si5351c_enable_clock_outputs(drv); } @@ -227,110 +254,166 @@ void si5351c_configure_multisynth( * ... * 7 means divide by 128 */ - const uint_fast8_t register_number = 42 + (ms_number * 8); - uint8_t data[] = { - register_number, - (p3 >> 8) & 0xFF, - (p3 >> 0) & 0xFF, - (r_div << 4) | (0 << 2) | ((p1 >> 16) & 0x3), - (p1 >> 8) & 0xFF, - (p1 >> 0) & 0xFF, - (((p3 >> 16) & 0xF) << 4) | (((p2 >> 16) & 0xF) << 0), - (p2 >> 8) & 0xFF, - (p2 >> 0) & 0xFF}; - si5351c_write(drv, data, sizeof(data)); + set_MS_P1(drv, ms_number, p1); + set_MS_P2(drv, ms_number, p2); + set_MS_P3(drv, ms_number, p3); + set_R_DIV(drv, ms_number, r_div); + si5351c_regs_commit(drv); } void si5351c_configure_clock_control(si5351c_driver_t* const drv) { - uint8_t clkout_ctrl; + si5351c_clk_ctrl_t clkout = { + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_8MA, + }; - if (clkout_enabled) { - clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA); - } else { - clkout_ctrl = SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE; + si5351c_clk_ctrl_t powered_down = { + .power_down = true, + .mode = SI5351C_MODE_INT, + }; + + if (!clkout_enabled) { + clkout = powered_down; } + si5351c_clk_ctrl_t clk[8]; + /* Clock to CPU is deactivated as it is not used and creates noise */ /* External clock output is kept in current state */ - uint8_t data[] = { - 16, - SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA), - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) | SI5351C_CLK_INV, - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA), - clkout_ctrl, - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA) | SI5351C_CLK_INV, - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA), - SI5351C_CLK_POWERDOWN | - SI5351C_CLK_INT_MODE, /* not connected, but: PLL A int mode */ - SI5351C_CLK_POWERDOWN | - SI5351C_CLK_INT_MODE /* not connected, but: PLL B int mode */ + + /* CLK0: MAX5864/CPLD */ + clk[0] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_8MA, }; + /* CLK1: CPLD */ + clk[1] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_0_4, + .drive = SI5351C_DRIVE_2MA, + .invert = true, + }; + /* CLK2: SGPIO */ + clk[2] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_0_4, + .drive = SI5351C_DRIVE_2MA, + }; + /* CLK3: CLKOUT */ + clk[3] = clkout; + /* CLK4: RFFC5072 (MAX2837 on rad1o) */ + clk[4] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_6MA, + .invert = true, + }; + /* CLK5: MAX2837 (MAX2871 on rad1o) */ + clk[5] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; + /* CLK6: none */ + clk[6] = powered_down; + /* CLK7: LPC43xx */ + clk[7] = powered_down; + #ifdef IS_H1_R9 if (IS_H1_R9) { - data[1] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA); - data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); - data[3] = clkout_ctrl; - data[4] = SI5351C_CLK_POWERDOWN; - data[5] = SI5351C_CLK_POWERDOWN; - data[6] = SI5351C_CLK_POWERDOWN; + /* CLK0: MAX5864/CPLD/SGPIO (sample clocks) */ + clk[0] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_6MA, + }; + /* CLK1: RFFC5072/MAX2839 */ + clk[1] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; + /* CLK2: CLKOUT/LPC4320 */ + clk[2] = clkout; + /* Other outputs not present on Si5351A */ + clk[3] = powered_down; + clk[4] = powered_down; + clk[5] = powered_down; + clk[6] = powered_down; + clk[7] = powered_down; } #endif #ifdef IS_PRALINE if (IS_PRALINE) { /* CLK0: AFE_CLK */ - data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); + clk[0] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; /* CLK1: SCT_CLK and FPGA_CLK */ - data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA); + clk[1] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_2MA, + }; /* CLK3: CLKOUT */ - clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA); + clkout.pll = SI5351C_PLL_B; + clk[3] = clkout; /* CLK4: XCVR_CLK */ - data[5] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA) | SI5351C_CLK_INV; - data[6] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); + clk[4] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_B, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + .invert = true, + }; + /* CLK5: MIX_CLK */ + clk[5] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_B, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ - data[3] = SI5351C_CLK_FRAC_MODE | - SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA); + clk[2] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_2MA, + }; } else { /* CLK2: MCU_CLK */ - data[3] = SI5351C_CLK_POWERDOWN; + clk[2] = powered_down; } } #endif - si5351c_write(drv, data, sizeof(data)); -} -#define SI5351C_CLK_ENABLE(x) (0 << x) -#define SI5351C_CLK_DISABLE(x) (1 << x) -#define SI5351C_REG_OUTPUT_EN (3) + for (int i = 0; i < 8; i++) { + set_CLK_PDN(drv, i, clk[i].power_down); + set_MS_INT(drv, i, clk[i].mode); + set_MS_SRC(drv, i, clk[i].pll); + set_CLK_SRC(drv, i, clk[i].source); + set_CLK_IDRV(drv, i, clk[i].drive); + set_CLK_INV(drv, i, clk[i].invert); + } + + si5351c_regs_commit(drv); +} void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) { @@ -340,46 +423,61 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) /* 3: External clock output is deactivated by default */ uint8_t clkout = 3; - uint8_t value = 0; + + bool enable[8]; #ifdef IS_PRALINE if (IS_PRALINE) { - value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | - SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) | - SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); + enable[0] = true; + enable[1] = true; if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ - value |= SI5351C_CLK_ENABLE(2); + enable[2] = true; } else { - value |= SI5351C_CLK_DISABLE(2); + enable[2] = false; } + enable[3] = false; + enable[4] = true; + enable[5] = true; + enable[6] = false; + enable[7] = false; } #endif #ifdef IS_NOT_PRALINE if (IS_NOT_PRALINE) { - value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | - SI5351C_CLK_ENABLE(2) | SI5351C_CLK_ENABLE(4) | - SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) | - SI5351C_CLK_DISABLE(7); + enable[0] = true; + enable[1] = true; + enable[2] = true; + enable[3] = false; + enable[4] = true; + enable[5] = true; + enable[6] = false; + enable[7] = false; /* HackRF One r9 has only three clock generator outputs. */ #ifdef IS_H1_R9 if (IS_H1_R9) { clkout = 2; - value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | - SI5351C_CLK_DISABLE(3) | SI5351C_CLK_DISABLE(4) | - SI5351C_CLK_DISABLE(5) | SI5351C_CLK_DISABLE(6) | - SI5351C_CLK_DISABLE(7); + enable[0] = true; + enable[1] = true; + enable[3] = false; + enable[4] = false; + enable[5] = false; + enable[6] = false; + enable[7] = false; } #endif } #endif + enable[clkout] = clkout_enabled; - value |= (clkout_enabled) ? SI5351C_CLK_ENABLE(clkout) : - SI5351C_CLK_DISABLE(clkout); - uint8_t data[] = {SI5351C_REG_OUTPUT_EN, value}; - si5351c_write(drv, data, sizeof(data)); - outputs_disabled = value; + for (int i = 0; i < 8; i++) { + set_CLK_OEB( + drv, + i, + enable[i] ? SI5351C_OUTPUT_ENABLE : SI5351C_OUTPUT_DISABLE); + } + si5351c_regs_commit(drv); #ifdef IS_H1_R9 if (IS_H1_R9) { @@ -398,20 +496,8 @@ void si5351c_set_int_mode( const uint_fast8_t ms_number, const uint_fast8_t on) { - uint8_t data[] = {16, 0}; - - if (ms_number < 8) { - data[0] = 16 + ms_number; - data[1] = si5351c_read_single(drv, data[0]); - - if (on) { - data[1] |= SI5351C_CLK_INT_MODE; - } else { - data[1] &= ~(SI5351C_CLK_INT_MODE); - } - - si5351c_write(drv, data, 2); - } + set_MS_INT(drv, ms_number, on); + si5351c_regs_commit(drv); } void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input) @@ -451,7 +537,9 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) uint32_t f = clkin_frequency(); return (f > 9000000) && (f < 11000000); } else { - return (si5351c_read_single(drv, 0) & SI5351C_LOS) == 0; + si5351c_read_single(drv, LOS_CLKIN); + bool los_clkin = get_LOS_CLKIN(drv); + return !los_clkin; } } @@ -475,7 +563,8 @@ void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable) void si5351c_init(si5351c_driver_t* const drv) { /* Read revision ID */ - selftest.si5351_rev_id = si5351c_read_single(drv, 0) & SI5351C_REVID; + si5351c_read_single(drv, REVID); + selftest.si5351_rev_id = get_REVID(drv); /* Read back interrupt status mask register, flip the mask bits and verify. */ uint8_t int_mask = si5351c_read_single(drv, 2); @@ -494,6 +583,17 @@ void si5351c_init(si5351c_driver_t* const drv) selftest.report.pass = false; } + /* Cache all current register values. */ + uint8_t data_tx[] = {0}; + i2c_bus_transfer( + drv->bus, + drv->i2c_address, + data_tx, + 1, + drv->regs, + sizeof(drv->regs)); + memset(drv->regs_dirty, 0, sizeof(drv->regs_dirty)); + #ifdef IS_H1_R9 if (IS_H1_R9) { const platform_gpio_t* gpio = platform_gpio(); @@ -529,8 +629,6 @@ void si5351c_set_phase( const uint8_t ms_number, const uint8_t offset) { - const uint8_t address = 165 + ms_number; - if (ms_number < 8) { - si5351c_write_single(drv, address, offset & 0x7f); - } + set_CLK_PHOFF(drv, ms_number, offset); + si5351c_regs_commit(drv); } diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index 491266cf..5be31788 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * * This file is part of HackRF. @@ -28,47 +28,62 @@ extern "C" { #include #include -#include #include "i2c_bus.h" #include "i2c_lpc.h" #define SI_INTDIV(x) (x * 128 - 512) -#define SI5351C_CLK_POWERDOWN (1 << 7) -#define SI5351C_CLK_INT_MODE (1 << 6) -#define SI5351C_CLK_FRAC_MODE (0 << 6) +#define SI5351C_CACHED_REGS 188 -#define SI5351C_CLK_INV (1 << 4) +typedef enum { + SI5351C_MODE_FRAC = 0, + SI5351C_MODE_INT = 1, +} si5351c_mode_t; -#define SI5351C_CLK_SRC(x) (x << 2) -#define SI5351C_CLK_SRC_XTAL 0 -#define SI5351C_CLK_SRC_CLKIN 1 -#define SI5351C_CLK_SRC_MULTISYNTH_0_4 2 -#define SI5351C_CLK_SRC_MULTISYNTH_SELF 3 +typedef enum { + SI5351C_SRC_XTAL = 0, + SI5351C_SRC_CLKIN = 1, + SI5351C_SRC_MULTISYNTH_0_4 = 2, + SI5351C_SRC_MULTISYNTH_SELF = 3, +} si5351c_src_t; -#define SI5351C_CLK_IDRV(x) (x << 0) -#define SI5351C_CLK_IDRV_2MA 0 -#define SI5351C_CLK_IDRV_4MA 1 -#define SI5351C_CLK_IDRV_6MA 2 -#define SI5351C_CLK_IDRV_8MA 3 - -#define SI5351C_LOS (1 << 4) -#define SI5351C_REVID 0x03 +typedef enum { + SI5351C_DRIVE_2MA = 0, + SI5351C_DRIVE_4MA = 1, + SI5351C_DRIVE_6MA = 2, + SI5351C_DRIVE_8MA = 3, +} si5351c_drive_t; typedef enum { SI5351C_PLL_A = 0, SI5351C_PLL_B = 1, } si5351c_pll_t; -#define SI5351C_CLK_PLL_SRC(x) ((x & SI5351C_PLL_B) << 5) - typedef enum { SI5351C_PLL_MASK_A = 1, SI5351C_PLL_MASK_B = 2, SI5351C_PLL_MASK_BOTH = 3, } si5351c_pll_mask_t; +typedef enum { + SI5351C_XTAL_6PF = 1, + SI5351C_XTAL_8PF = 2, + SI5351C_XTAL_10PF = 3, +} si5351c_xtal_t; + +typedef enum { + SI5351C_OUTPUT_ENABLE = 0, + SI5351C_OUTPUT_DISABLE = 1, +} si5351c_output_t; + +typedef enum { + SI5351C_DIV_1 = 0, + SI5351C_DIV_2 = 1, + SI5351C_DIV_4 = 2, + SI5351C_DIV_8 = 3, +} si5351c_div_t; + typedef enum { SI5351C_INPUT_XTAL = 0, SI5351C_INPUT_CLKIN = 1, @@ -77,8 +92,19 @@ typedef enum { typedef struct { i2c_bus_t* const bus; uint8_t i2c_address; + uint8_t regs[SI5351C_CACHED_REGS]; + uint32_t regs_dirty[(SI5351C_CACHED_REGS + 31) / 32]; } si5351c_driver_t; +typedef struct { + bool power_down; + si5351c_mode_t mode; + si5351c_pll_t pll; + si5351c_src_t source; + si5351c_drive_t drive; + bool invert; +} si5351c_clk_ctrl_t; + void si5351c_disable_all_outputs(si5351c_driver_t* const drv); void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv); void si5351c_power_down_all_clocks(si5351c_driver_t* const drv); @@ -107,10 +133,6 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv); void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val); uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg); -void si5351c_write( - si5351c_driver_t* const drv, - const uint8_t* const data, - const size_t data_count); void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable); void si5351c_init(si5351c_driver_t* const drv); void si5351c_set_phase( From 478a520d65f2f822bac6baf9a7387c19720e3c12 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 18 May 2026 14:42:16 +0100 Subject: [PATCH 132/173] Add script to extract font data to an image file for editing. --- firmware/tools/edit-font.py | 107 ++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 firmware/tools/edit-font.py diff --git a/firmware/tools/edit-font.py b/firmware/tools/edit-font.py new file mode 100644 index 00000000..9e03467a --- /dev/null +++ b/firmware/tools/edit-font.py @@ -0,0 +1,107 @@ +# Tool to extract font data, save to bitmap file, and apply modifications +# to source code after editing the bitmap. + +from __future__ import print_function +from matplotlib.pyplot import imread, imsave, imshow, show +import numpy as np +import sys +import re + +if len(sys.argv) != 3: + print("Usage: %s { show | extract | insert}" % sys.argv[0]) + sys.exit(1) + +font_name = sys.argv[1] +mode = sys.argv[2] + +# Where to find the font data - may need updated if code has changed. +source_file = 'firmware/common/ui_portapack.c' +data_marker = f'static const uint8_t {font_name}_glyph_data[] = {{' +data_offset = 1 # Data starts this number of lines after start marker. +end_pattern = '(.*)};$' # Indicates end of font data. +def_marker = f'static const ui_font_t {font_name} =' +def_offset = 1 +def_pattern = '^\t{{(?P\\d+), (?P\\d+)}' + +# Image filename used in extract and insert modes. +image_file = f'{font_name}.png' + +# Get the literals used to populate the font array in the source file +lines = [line.rstrip() for line in open(source_file).readlines()] +try: + start = lines.index(data_marker) + data_offset +except ValueError: + print(f"Could not find glyph data for font '{font_name}'") + sys.exit(1) + +data = str() +for line in lines[start:]: + if match := re.match(end_pattern, line): + data += match.group(1) + break + else: + data += line + +# Get the width and height from the font definition. +try: + def_start = lines.index(def_marker) +except ValueError: + print(f"Could not find definition for font '{font_name}'") + sys.exit(1) + +def_match = re.match(def_pattern, lines[def_start + def_offset]) + +if def_match is None: + print(f"Failed to parse definition of '{font_name}'") + +width = int(def_match.group('width')) +height = int(def_match.group('height')) + +# Eval the literals to get the values into a numpy array +packed = eval("np.array([%s], np.uint8)" % data) + +# Reorganise into a monochrome image. +chars_per_row = 5 +unpacked = np.unpackbits(packed) +bitmaps = (unpacked + .reshape(-1, height, width // 8, 8)[:,:,::-1,:] + .reshape(-1, height, width)[:,:,::-1] +) +indices = np.arange(len(bitmaps)).reshape(-1, chars_per_row) +image = np.block([[bitmaps[idx] for idx in row] for row in indices]) + +if mode == 'show': + # Display font image + imshow(image, cmap='binary') + show() + +elif mode == 'extract': + # Save font image + imsave(image_file, image, format='png', cmap='binary') + +elif mode == 'insert': + # Read in modified font image + image = imread(image_file)[:,:,0].astype(bool) + + # Reorganise back to original order + rows = image.reshape(-1, height, image.shape[1]) + single_row_image = np.hstack(rows) + num_chars = single_row_image.shape[1] // width + bitmaps = np.array(np.split(single_row_image, num_chars, 1)) + unpacked = (bitmaps + [:,:,::-1].reshape(-1, height, width // 8, 8) + [:,:,::-1,:].reshape(-1) + ) + packed = ~np.packbits(unpacked) + + # Replace lines of file in same format as used before + bytes_per_row = 13 + split_indices = np.arange(bytes_per_row, len(packed), bytes_per_row) + for i, group in enumerate(np.split(packed, split_indices)): + line = ("\t0x%02x," + " 0x%02x," * (len(group) - 1)) % tuple(group) + if len(group) < bytes_per_row: + line = line[:-1] + "};" + lines[start + i] = line + + # Write out modified source file + open(source_file, 'w').writelines([line + "\n" for line in lines]) From dc255468b1ecaf8141d68067addb1e5c03f452ce Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 18 May 2026 17:26:27 +0100 Subject: [PATCH 133/173] Fix up IWYU issues in cpld_jtag.{c,h}. --- firmware/common/cpld_jtag.c | 3 ++- firmware/common/cpld_jtag.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/firmware/common/cpld_jtag.c b/firmware/common/cpld_jtag.c index 4d313d01..e6413ba4 100644 --- a/firmware/common/cpld_jtag.c +++ b/firmware/common/cpld_jtag.c @@ -21,11 +21,12 @@ #include "cpld_jtag.h" -#include "cpld_xc2c.h" #include "platform_detect.h" #ifdef IS_NOT_PRALINE + #include #include #include "xapp058/micro.h" + #include "cpld_xc2c.h" #endif #ifdef IS_NOT_PRALINE diff --git a/firmware/common/cpld_jtag.h b/firmware/common/cpld_jtag.h index b9a4b52f..c1f707d4 100644 --- a/firmware/common/cpld_jtag.h +++ b/firmware/common/cpld_jtag.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include "gpio.h" From 50ce90908f6a6cbbe03778b21f9c0a564242bf09 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 9 May 2026 06:21:44 +0100 Subject: [PATCH 134/173] si5351c: Wait for SYS_INIT flag to clear during startup. --- firmware/common/si5351c.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 1f27d8db..f356f320 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -583,6 +583,11 @@ void si5351c_init(si5351c_driver_t* const drv) selftest.report.pass = false; } + /* Wait for on-chip initialization to complete. */ + while (get_SYS_INIT(drv)) { + si5351c_read_single(drv, SYS_INIT); + } + /* Cache all current register values. */ uint8_t data_tx[] = {0}; i2c_bus_transfer( From 4c69adb9dab1f0df34d93a6ea2a4715fb92f1ec1 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 8 May 2026 14:54:03 +0100 Subject: [PATCH 135/173] si5351c: API: change argument of si5351c_clkout_enable to bool. --- firmware/common/si5351c.c | 4 ++-- firmware/common/si5351c.h | 2 +- firmware/hackrf_usb/usb_api_register.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index f356f320..d053d601 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -543,9 +543,9 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) } } -void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable) +void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable) { - clkout_enabled = (enable > 0); + clkout_enabled = enable; uint8_t clkout = 3; diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index 5be31788..c357442f 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -133,7 +133,7 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv); void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val); uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg); -void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable); +void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable); void si5351c_init(si5351c_driver_t* const drv); void si5351c_set_phase( si5351c_driver_t* const drv, diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index 1a9f0238..626c7508 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -211,7 +211,7 @@ usb_request_status_t usb_vendor_request_set_clkout_enable( const usb_transfer_stage_t stage) { if (stage == USB_TRANSFER_STAGE_SETUP) { - si5351c_clkout_enable(&si5351c, endpoint->setup.value); + si5351c_clkout_enable(&si5351c, endpoint->setup.value > 0); usb_transfer_schedule_ack(endpoint->in); } return USB_REQUEST_STATUS_OK; From 9423133c4b94957665991c3dfa30c93ed474e045 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 8 May 2026 14:40:28 +0100 Subject: [PATCH 136/173] si5351c: Make clk array persistent, setup in init, include OE flags. --- firmware/common/si5351c.c | 365 +++++++++++++++++--------------------- firmware/common/si5351c.h | 3 +- 2 files changed, 161 insertions(+), 207 deletions(-) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index d053d601..50d39583 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -51,10 +51,12 @@ si5351c_driver_t si5351c = { .i2c_address = 0x60, }; +si5351c_clk_t clk[8]; + +uint8_t clkout_id; + static bool input_initialized = false; static si5351c_input_t active_input; -/* External clock output default is deactivated as it creates noise */ -static bool clkout_enabled = false; /* write to single register */ void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val) @@ -263,146 +265,6 @@ void si5351c_configure_multisynth( void si5351c_configure_clock_control(si5351c_driver_t* const drv) { - si5351c_clk_ctrl_t clkout = { - .mode = SI5351C_MODE_INT, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_8MA, - }; - - si5351c_clk_ctrl_t powered_down = { - .power_down = true, - .mode = SI5351C_MODE_INT, - }; - - if (!clkout_enabled) { - clkout = powered_down; - } - - si5351c_clk_ctrl_t clk[8]; - - /* Clock to CPU is deactivated as it is not used and creates noise */ - /* External clock output is kept in current state */ - - /* CLK0: MAX5864/CPLD */ - clk[0] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_FRAC, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_8MA, - }; - /* CLK1: CPLD */ - clk[1] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_INT, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_0_4, - .drive = SI5351C_DRIVE_2MA, - .invert = true, - }; - /* CLK2: SGPIO */ - clk[2] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_INT, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_0_4, - .drive = SI5351C_DRIVE_2MA, - }; - /* CLK3: CLKOUT */ - clk[3] = clkout; - /* CLK4: RFFC5072 (MAX2837 on rad1o) */ - clk[4] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_INT, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_6MA, - .invert = true, - }; - /* CLK5: MAX2837 (MAX2871 on rad1o) */ - clk[5] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_INT, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_4MA, - }; - /* CLK6: none */ - clk[6] = powered_down; - /* CLK7: LPC43xx */ - clk[7] = powered_down; - -#ifdef IS_H1_R9 - if (IS_H1_R9) { - /* CLK0: MAX5864/CPLD/SGPIO (sample clocks) */ - clk[0] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_INT, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_6MA, - }; - /* CLK1: RFFC5072/MAX2839 */ - clk[1] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_FRAC, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_4MA, - }; - /* CLK2: CLKOUT/LPC4320 */ - clk[2] = clkout; - /* Other outputs not present on Si5351A */ - clk[3] = powered_down; - clk[4] = powered_down; - clk[5] = powered_down; - clk[6] = powered_down; - clk[7] = powered_down; - } -#endif -#ifdef IS_PRALINE - if (IS_PRALINE) { - /* CLK0: AFE_CLK */ - clk[0] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_FRAC, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_4MA, - }; - /* CLK1: SCT_CLK and FPGA_CLK */ - clk[1] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_FRAC, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_2MA, - }; - /* CLK3: CLKOUT */ - clkout.pll = SI5351C_PLL_B; - clk[3] = clkout; - /* CLK4: XCVR_CLK */ - clk[4] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_INT, - .pll = SI5351C_PLL_B, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_4MA, - .invert = true, - }; - /* CLK5: MIX_CLK */ - clk[5] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_INT, - .pll = SI5351C_PLL_B, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_4MA, - }; - if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { - /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ - clk[2] = (si5351c_clk_ctrl_t){ - .mode = SI5351C_MODE_FRAC, - .pll = SI5351C_PLL_A, - .source = SI5351C_SRC_MULTISYNTH_SELF, - .drive = SI5351C_DRIVE_2MA, - }; - } else { - /* CLK2: MCU_CLK */ - clk[2] = powered_down; - } - } -#endif - for (int i = 0; i < 8; i++) { set_CLK_PDN(drv, i, clk[i].power_down); set_MS_INT(drv, i, clk[i].mode); @@ -417,72 +279,19 @@ void si5351c_configure_clock_control(si5351c_driver_t* const drv) void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) { - /* Enable CLK outputs 0, 1, 2, 4, 5 only. */ - /* Praline: enable 0, 1, 4, 5 only. */ - /* 7: Clock to CPU is deactivated as it is not used and creates noise */ - /* 3: External clock output is deactivated by default */ - - uint8_t clkout = 3; - - bool enable[8]; - -#ifdef IS_PRALINE - if (IS_PRALINE) { - enable[0] = true; - enable[1] = true; - if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { - /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ - enable[2] = true; - } else { - enable[2] = false; - } - enable[3] = false; - enable[4] = true; - enable[5] = true; - enable[6] = false; - enable[7] = false; - } -#endif -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - enable[0] = true; - enable[1] = true; - enable[2] = true; - enable[3] = false; - enable[4] = true; - enable[5] = true; - enable[6] = false; - enable[7] = false; - - /* HackRF One r9 has only three clock generator outputs. */ - #ifdef IS_H1_R9 - if (IS_H1_R9) { - clkout = 2; - enable[0] = true; - enable[1] = true; - enable[3] = false; - enable[4] = false; - enable[5] = false; - enable[6] = false; - enable[7] = false; - } - #endif - } -#endif - enable[clkout] = clkout_enabled; - for (int i = 0; i < 8; i++) { set_CLK_OEB( drv, i, - enable[i] ? SI5351C_OUTPUT_ENABLE : SI5351C_OUTPUT_DISABLE); + clk[i].output_enable ? SI5351C_OUTPUT_ENABLE : + SI5351C_OUTPUT_DISABLE); } si5351c_regs_commit(drv); #ifdef IS_H1_R9 if (IS_H1_R9) { const platform_gpio_t* gpio = platform_gpio(); - if (clkout_enabled) { + if (clk[clkout_id].output_enable) { gpio_set(gpio->h1r9_clkout_en); } else { gpio_clear(gpio->h1r9_clkout_en); @@ -545,16 +354,11 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable) { - clkout_enabled = enable; + clk[clkout_id].output_enable = enable; + clk[clkout_id].power_down = !enable; - uint8_t clkout = 3; - - /* HackRF One r9 has only three clock generator outputs. */ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - clkout = 2; - } /* Configure clock to 10MHz */ - si5351c_configure_multisynth(drv, clkout, 80 * 128 - 512, 0, 1, 0); + si5351c_configure_multisynth(drv, clkout_id, 80 * 128 - 512, 0, 1, 0); si5351c_configure_clock_control(drv); si5351c_enable_clock_outputs(drv); @@ -620,6 +424,155 @@ void si5351c_init(si5351c_driver_t* const drv) gpio_output(gpio->h1r9_mcu_clk_en); } #endif + + si5351c_clk_t clkout = { + .output_enable = false, + .power_down = true, + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_8MA, + }; + + si5351c_clk_t powered_down = { + .output_enable = false, + .power_down = true, + .mode = SI5351C_MODE_INT, + }; + + /* CLK0: MAX5864/CPLD */ + clk[0] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_8MA, + }; + /* CLK1: CPLD */ + clk[1] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_0_4, + .drive = SI5351C_DRIVE_2MA, + .invert = true, + }; + /* CLK2: SGPIO */ + clk[2] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_0_4, + .drive = SI5351C_DRIVE_2MA, + }; + /* CLK3: CLKOUT */ + clk[3] = clkout; + clkout_id = 3; + /* CLK4: RFFC5072 (MAX2837 on rad1o) */ + clk[4] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_6MA, + .invert = true, + }; + /* CLK5: MAX2837 (MAX2871 on rad1o) */ + clk[5] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; + /* CLK6: none */ + clk[6] = powered_down; + /* CLK7: LPC43xx */ + clk[7] = powered_down; + +#ifdef IS_H1_R9 + if (IS_H1_R9) { + /* CLK0: MAX5864/CPLD/SGPIO (sample clocks) */ + clk[0] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_6MA, + }; + /* CLK1: RFFC5072/MAX2839 */ + clk[1] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; + /* CLK2: CLKOUT/LPC4320 */ + clk[2] = clkout; + clkout_id = 2; + /* Other outputs not present on Si5351A */ + clk[3] = powered_down; + clk[4] = powered_down; + clk[5] = powered_down; + clk[6] = powered_down; + clk[7] = powered_down; + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { + /* CLK0: AFE_CLK */ + clk[0] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; + /* CLK1: SCT_CLK and FPGA_CLK */ + clk[1] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_2MA, + }; + /* CLK3: CLKOUT */ + clkout.pll = SI5351C_PLL_B; + clk[3] = clkout; + clkout_id = 3; + /* CLK4: XCVR_CLK */ + clk[4] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_B, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + .invert = true, + }; + /* CLK5: MIX_CLK */ + clk[5] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_B, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; + if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { + /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ + clk[2] = (si5351c_clk_t){ + .output_enable = true, + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_2MA, + }; + } else { + /* CLK2: MCU_CLK */ + clk[2] = powered_down; + } + } +#endif } /* diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index c357442f..9691b896 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -97,13 +97,14 @@ typedef struct { } si5351c_driver_t; typedef struct { + bool output_enable; bool power_down; si5351c_mode_t mode; si5351c_pll_t pll; si5351c_src_t source; si5351c_drive_t drive; bool invert; -} si5351c_clk_ctrl_t; +} si5351c_clk_t; void si5351c_disable_all_outputs(si5351c_driver_t* const drv); void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv); From 835e7b618a55865aeeb7846d00b3f378b74ff3d5 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 19 May 2026 06:47:45 +0100 Subject: [PATCH 137/173] Move LPC43xx I2C instances and configs to ipc_lpc.{c,h}. --- firmware/common/clock_gen.c | 5 +++-- firmware/common/cpu_clock.c | 3 ++- firmware/common/da7219.c | 1 + firmware/common/i2c_bus.c | 19 ------------------- firmware/common/i2c_bus.h | 4 ---- firmware/common/i2c_lpc.c | 20 ++++++++++++++++++++ firmware/common/i2c_lpc.h | 5 +++++ firmware/common/operacake.c | 1 + firmware/common/si5351c.c | 6 ------ firmware/common/si5351c.h | 2 -- 10 files changed, 32 insertions(+), 34 deletions(-) diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index e91e3822..22f461c2 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -25,6 +25,7 @@ #include "hackrf_ui.h" #include "i2c_bus.h" +#include "i2c_lpc.h" #include "platform_detect.h" #include "sgpio.h" #include "si5351c.h" @@ -35,7 +36,7 @@ void clock_gen_init(void) { - i2c_bus_start(si5351c.bus, &i2c_config_si5351c_fast_clock); + i2c_bus_start(si5351c.bus, &i2c_config_fast_clock); si5351c_init(&si5351c); si5351c_disable_all_outputs(&si5351c); @@ -119,7 +120,7 @@ void clock_gen_init(void) void clock_gen_shutdown(void) { - i2c_bus_start(si5351c.bus, &i2c_config_si5351c_fast_clock); + i2c_bus_start(si5351c.bus, &i2c_config_fast_clock); si5351c_disable_all_outputs(&si5351c); si5351c_disable_oeb_pin_control(&si5351c); si5351c_power_down_all_clocks(&si5351c); diff --git a/firmware/common/cpu_clock.c b/firmware/common/cpu_clock.c index 37445e51..325de76f 100644 --- a/firmware/common/cpu_clock.c +++ b/firmware/common/cpu_clock.c @@ -30,6 +30,7 @@ #include "delay.h" #include "i2c_bus.h" +#include "i2c_lpc.h" #include "si5351c.h" /* @@ -113,7 +114,7 @@ void cpu_clock_init(void) //FIXME disable I2C /* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */ - i2c_bus_start(si5351c.bus, &i2c_config_si5351c_fast_clock); + i2c_bus_start(si5351c.bus, &i2c_config_fast_clock); /* * 12MHz clock is entering LPC XTAL1/OSC input now. diff --git a/firmware/common/da7219.c b/firmware/common/da7219.c index 7ae916cb..de85d2b8 100644 --- a/firmware/common/da7219.c +++ b/firmware/common/da7219.c @@ -23,6 +23,7 @@ #include "da7219.h" #include "i2c_bus.h" +#include "i2c_lpc.h" #define DA7219_REG_CHIP_ID1 0x81 #define DA7219_REG_CHIP_ID2 0x82 diff --git a/firmware/common/i2c_bus.c b/firmware/common/i2c_bus.c index c0d5b79d..6d8dcd25 100644 --- a/firmware/common/i2c_bus.c +++ b/firmware/common/i2c_bus.c @@ -22,25 +22,6 @@ #include "i2c_bus.h" -#include - -#include "i2c_lpc.h" - -/* Driver instances. */ -i2c_bus_t i2c0 = { - .obj = (void*) I2C0_BASE, - .start = i2c_lpc_start, - .stop = i2c_lpc_stop, - .transfer = i2c_lpc_transfer, -}; - -i2c_bus_t i2c1 = { - .obj = (void*) I2C1_BASE, - .start = i2c_lpc_start, - .stop = i2c_lpc_stop, - .transfer = i2c_lpc_transfer, -}; - void i2c_bus_start(i2c_bus_t* const bus, const void* const config) { bus->start(bus, config); diff --git a/firmware/common/i2c_bus.h b/firmware/common/i2c_bus.h index fa1211c1..7519ba14 100644 --- a/firmware/common/i2c_bus.h +++ b/firmware/common/i2c_bus.h @@ -47,7 +47,3 @@ void i2c_bus_transfer( const size_t tx_count, uint8_t* const rx, const size_t rx_count); - -/* Driver instances. */ -extern i2c_bus_t i2c0; -extern i2c_bus_t i2c1; diff --git a/firmware/common/i2c_lpc.c b/firmware/common/i2c_lpc.c index 05631f05..9d6499fa 100644 --- a/firmware/common/i2c_lpc.c +++ b/firmware/common/i2c_lpc.c @@ -23,6 +23,26 @@ #include "i2c_lpc.h" #include +#include + +/* Driver instances. */ +i2c_bus_t i2c0 = { + .obj = (void*) I2C0_BASE, + .start = i2c_lpc_start, + .stop = i2c_lpc_stop, + .transfer = i2c_lpc_transfer, +}; + +i2c_bus_t i2c1 = { + .obj = (void*) I2C1_BASE, + .start = i2c_lpc_start, + .stop = i2c_lpc_stop, + .transfer = i2c_lpc_transfer, +}; + +const i2c_lpc_config_t i2c_config_fast_clock = { + .duty_cycle_count = 255, +}; /* FIXME return i2c0 status from each function */ diff --git a/firmware/common/i2c_lpc.h b/firmware/common/i2c_lpc.h index f5b8230e..e71cb362 100644 --- a/firmware/common/i2c_lpc.h +++ b/firmware/common/i2c_lpc.h @@ -42,3 +42,8 @@ void i2c_lpc_transfer( uint8_t* const data_rx, const size_t count_rx); bool i2c_probe(i2c_bus_t* const bus, const uint_fast8_t device_address); + +/* Driver instances. */ +extern const i2c_lpc_config_t i2c_config_fast_clock; +extern i2c_bus_t i2c0; +extern i2c_bus_t i2c1; diff --git a/firmware/common/operacake.c b/firmware/common/operacake.c index f50c4f7e..bce51b76 100644 --- a/firmware/common/operacake.c +++ b/firmware/common/operacake.c @@ -29,6 +29,7 @@ #include "gpio.h" #include "gpio_lpc.h" #include "i2c_bus.h" +#include "i2c_lpc.h" #include "operacake_sctimer.h" #include "platform_scu.h" diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 1f27d8db..77d77703 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -40,12 +40,6 @@ #include "si5351c_regs.def" /* Driver instance. */ -// const i2c_lpc_config_t i2c_config_si5351c_slow_clock = { -// .duty_cycle_count = 15, -// }; -const i2c_lpc_config_t i2c_config_si5351c_fast_clock = { - .duty_cycle_count = 255, -}; si5351c_driver_t si5351c = { .bus = &i2c0, .i2c_address = 0x60, diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index 5be31788..b99bec14 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -30,7 +30,6 @@ extern "C" { #include #include "i2c_bus.h" -#include "i2c_lpc.h" #define SI_INTDIV(x) (x * 128 - 512) @@ -141,7 +140,6 @@ void si5351c_set_phase( const uint8_t offset); /* Driver Instance. */ -extern const i2c_lpc_config_t i2c_config_si5351c_fast_clock; extern si5351c_driver_t si5351c; #ifdef __cplusplus From 62cdff34483c34dadabae43850f8b99577444820 Mon Sep 17 00:00:00 2001 From: grvvy Date: Fri, 24 Apr 2026 10:58:55 -0600 Subject: [PATCH 138/173] Jenkinsfile: add resource locks to HIL USB hubs to allow for concurrent builds --- Jenkinsfile | 160 +++++++++++++++++++++++++++------------------------- 1 file changed, 84 insertions(+), 76 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index b41d6714..2d856531 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -41,29 +41,31 @@ pipeline { steps { sh './ci-scripts/install_host.sh' sh './ci-scripts/build_firmware.sh HACKRF_ONE' - script { - allOff() - reset('h1_eut') - } - sh 'sleep 1s' - retry(3) { - sh './ci-scripts/test_host.sh' - } - script { - reset('h1_tester h1_eut') - } - sh 'sleep 1s' - script { - // Allow up to 3 retries 5 minutes each for the HIL test - runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test) - } - script { - allOff() - reset('h1_eut') - } - sh 'sleep 1s' - retry(3) { - sh 'python3 ci-scripts/test_sgpio_debug.py' + lock('HIL_hubs') { + script { + allOff() + reset('h1_eut') + } + sh 'sleep 1s' + retry(3) { + sh './ci-scripts/test_host.sh' + } + script { + reset('h1_tester h1_eut') + } + sh 'sleep 1s' + script { + // Allow up to 3 retries 5 minutes each for the HIL test + runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test) + } + script { + allOff() + reset('h1_eut') + } + sh 'sleep 1s' + retry(3) { + sh 'python3 ci-scripts/test_sgpio_debug.py' + } } } } @@ -81,29 +83,31 @@ pipeline { steps { sh './ci-scripts/install_host.sh' sh './ci-scripts/build_firmware.sh UNIVERSAL' - script { - allOff() - reset('h1_eut') - } - sh 'sleep 1s' - retry(3) { - sh './ci-scripts/test_host.sh' - } - script { - reset('h1_tester h1_eut') - } - sh 'sleep 1s' - script { - // Allow up to 3 retries 5 minutes each for the HIL test - runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test) - } - script { - allOff() - reset('h1_eut') - } - sh 'sleep 1s' - retry(3) { - sh 'python3 ci-scripts/test_sgpio_debug.py' + lock('HIL_hubs') { + script { + allOff() + reset('h1_eut') + } + sh 'sleep 1s' + retry(3) { + sh './ci-scripts/test_host.sh' + } + script { + reset('h1_tester h1_eut') + } + sh 'sleep 1s' + script { + // Allow up to 3 retries 5 minutes each for the HIL test + runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test) + } + script { + allOff() + reset('h1_eut') + } + sh 'sleep 1s' + retry(3) { + sh 'python3 ci-scripts/test_sgpio_debug.py' + } } } } @@ -121,21 +125,23 @@ pipeline { steps { sh './ci-scripts/install_host.sh' sh './ci-scripts/build_firmware.sh PRALINE' - script { - allOff() - reset('hpro_eut') - } - sh 'sleep 1s' - retry(3) { - sh './ci-scripts/test_host.sh' - } - script { - reset('hpro_tester hpro_eut') - } - sh 'sleep 1s' - script { - // Allow up to 3 retries 5 minutes each for the HIL test - runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test) + lock('HIL_hubs') { + script { + allOff() + reset('hpro_eut') + } + sh 'sleep 1s' + retry(3) { + sh './ci-scripts/test_host.sh' + } + script { + reset('hpro_tester hpro_eut') + } + sh 'sleep 1s' + script { + // Allow up to 3 retries 5 minutes each for the HIL test + runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test) + } } } } @@ -153,21 +159,23 @@ pipeline { steps { sh './ci-scripts/install_host.sh' sh './ci-scripts/build_firmware.sh UNIVERSAL' - script { - allOff() - reset('hpro_eut') - } - sh 'sleep 1s' - retry(3) { - sh './ci-scripts/test_host.sh' - } - script { - reset('hpro_tester hpro_eut') - } - sh 'sleep 1s' - script { - // Allow up to 3 retries 5 minutes each for the HIL test - runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test) + lock('HIL_hubs') { + script { + allOff() + reset('hpro_eut') + } + sh 'sleep 1s' + retry(3) { + sh './ci-scripts/test_host.sh' + } + script { + reset('hpro_tester hpro_eut') + } + sh 'sleep 1s' + script { + // Allow up to 3 retries 5 minutes each for the HIL test + runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test) + } } } } From 6c01a078d52c76d58619881669d84c94ad4c5194 Mon Sep 17 00:00:00 2001 From: grvvy Date: Tue, 19 May 2026 14:44:37 -0600 Subject: [PATCH 139/173] Jenkinsfile: ensure device resets occur between test runs --- Jenkinsfile | 111 +++++++++++++++------------------------------------- 1 file changed, 32 insertions(+), 79 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2d856531..dcfd9ad8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -39,32 +39,14 @@ pipeline { timeout(time: 20, unit: 'MINUTES') } steps { - sh './ci-scripts/install_host.sh' - sh './ci-scripts/build_firmware.sh HACKRF_ONE' + runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES') + runCommand("Build HackRF One Firmware", './ci-scripts/build_firmware.sh HACKRF_ONE', 3, 1, 'MINUTES') lock('HIL_hubs') { script { allOff() - reset('h1_eut') - } - sh 'sleep 1s' - retry(3) { - sh './ci-scripts/test_host.sh' - } - script { - reset('h1_tester h1_eut') - } - sh 'sleep 1s' - script { - // Allow up to 3 retries 5 minutes each for the HIL test - runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test) - } - script { - allOff() - reset('h1_eut') - } - sh 'sleep 1s' - retry(3) { - sh 'python3 ci-scripts/test_sgpio_debug.py' + runTest("Check Host", 'h1_eut', './ci-scripts/test_host.sh') + runTest("HackRF One HIL Test", 'h1_tester h1_eut', h1_test) + runTest("SGPIO Debug Test", 'h1_eut', 'python3 ci-scripts/test_sgpio_debug.py') } } } @@ -81,34 +63,17 @@ pipeline { timeout(time: 20, unit: 'MINUTES') } steps { - sh './ci-scripts/install_host.sh' - sh './ci-scripts/build_firmware.sh UNIVERSAL' + runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES') + runCommand("Build Universal Firmware", './ci-scripts/build_firmware.sh UNIVERSAL', 3, 1, 'MINUTES') lock('HIL_hubs') { script { allOff() - reset('h1_eut') - } - sh 'sleep 1s' - retry(3) { - sh './ci-scripts/test_host.sh' - } - script { - reset('h1_tester h1_eut') - } - sh 'sleep 1s' - script { - // Allow up to 3 retries 5 minutes each for the HIL test - runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test) - } - script { - allOff() - reset('h1_eut') - } - sh 'sleep 1s' - retry(3) { - sh 'python3 ci-scripts/test_sgpio_debug.py' + runTest("Check Host", 'h1_eut', './ci-scripts/test_host.sh') + runTest("HackRF One HIL Test", 'h1_tester h1_eut', h1_test) + runTest("SGPIO Debug Test", 'h1_eut', 'python3 ci-scripts/test_sgpio_debug.py') } } + } } stage('Test HackRF Pro with BOARD=PRALINE') { @@ -123,24 +88,13 @@ pipeline { timeout(time: 20, unit: 'MINUTES') } steps { - sh './ci-scripts/install_host.sh' - sh './ci-scripts/build_firmware.sh PRALINE' + runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES') + runCommand("Build Praline Firmware", './ci-scripts/build_firmware.sh PRALINE', 3, 1, 'MINUTES') lock('HIL_hubs') { script { allOff() - reset('hpro_eut') - } - sh 'sleep 1s' - retry(3) { - sh './ci-scripts/test_host.sh' - } - script { - reset('hpro_tester hpro_eut') - } - sh 'sleep 1s' - script { - // Allow up to 3 retries 5 minutes each for the HIL test - runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test) + runTest("Check Host", 'hpro_eut', './ci-scripts/test_host.sh') + runTest("HackRF Pro HIL Test", 'hpro_tester hpro_eut', hpro_test) } } } @@ -157,24 +111,13 @@ pipeline { timeout(time: 20, unit: 'MINUTES') } steps { - sh './ci-scripts/install_host.sh' - sh './ci-scripts/build_firmware.sh UNIVERSAL' + runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES') + runCommand("Build Universal Firmware", './ci-scripts/build_firmware.sh UNIVERSAL', 3, 1, 'MINUTES') lock('HIL_hubs') { script { allOff() - reset('hpro_eut') - } - sh 'sleep 1s' - retry(3) { - sh './ci-scripts/test_host.sh' - } - script { - reset('hpro_tester hpro_eut') - } - sh 'sleep 1s' - script { - // Allow up to 3 retries 5 minutes each for the HIL test - runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test) + runTest("Check Host", 'hpro_eut', './ci-scripts/test_host.sh') + runTest("HackRF Pro HIL Test", 'hpro_tester hpro_eut', hpro_test) } } } @@ -192,15 +135,15 @@ pipeline { def allOff() { // Allow up to 3 retries, 20 seconds each, for the USB hub port power server to respond appropriately - runCommand(3, 20, 'SECONDS', 'USB hub port power server command', "hubs all off") + runCommand('USB hub port power server command', "hubs all off", 3, 20, 'SECONDS') } def reset(devices) { // Allow up to 3 retries, 20 seconds each, for the USB hub port power server to respond appropriately - runCommand(3, 20, 'SECONDS', 'USB hub port power server command', "hubs ${devices} reset") + runCommand('USB hub port power server command', "hubs ${devices} reset", 3, 20, 'SECONDS') } -def runCommand(retries, time, unit, title, cmd) { +def runCommand(title, cmd, retries, time, unit) { retry(retries) { try { timeout(time: time, unit: unit) { @@ -222,3 +165,13 @@ def runCommand(retries, time, unit, title, cmd) { } } } + +def runTest(title, devices, cmd) { + retry(3) { + // reset() retains it's own internal retries + reset(devices) + sh 'sleep 1s' + // run the test with 0 internal retries and 3 external retries to ensure resets between runs + runCommand(title, cmd, 0, 5, 'MINUTES') + } +} From c2262ccda1a73e00e153e35f5bb0b2902d0de92d Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 19 May 2026 16:57:30 +0100 Subject: [PATCH 140/173] si5351c: Add control of MCU_CLKIN output. --- firmware/common/si5351c.c | 51 +++++++++++++++++++++++++++++++++++++-- firmware/common/si5351c.h | 1 + 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 50d39583..353a4d2e 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -54,6 +54,7 @@ si5351c_driver_t si5351c = { si5351c_clk_t clk[8]; uint8_t clkout_id; +uint8_t mcu_clkin_id; static bool input_initialized = false; static si5351c_input_t active_input; @@ -364,6 +365,40 @@ void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable) si5351c_enable_clock_outputs(drv); } +void si5351c_mcu_clkin_enable(si5351c_driver_t* const drv, bool enable) +{ +#ifdef IS_H1_R9 + if (IS_H1_R9) { + /* MCU clock is shared with CLKOUT. */ + si5351c_clkout_enable(drv, enable); + } +#endif +#ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { + clk[mcu_clkin_id].output_enable = enable; + clk[mcu_clkin_id].power_down = !enable; + + /* Configure clock to 40MHz */ + if (mcu_clkin_id >= 6) { + // MCU_CLKIN on CLK6 or CLK7, integer only MS. + si5351c_configure_multisynth(drv, mcu_clkin_id, 20, 0, 0, 0); + } else { + // MCU_CLKIN on CLK0 to CLK5, fractional-capable MS. + si5351c_configure_multisynth( + drv, + mcu_clkin_id, + 20 * 128 - 512, + 0, + 1, + 0); + } + + si5351c_configure_clock_control(drv); + si5351c_enable_clock_outputs(drv); + } +#endif +} + void si5351c_init(si5351c_driver_t* const drv) { /* Read revision ID */ @@ -434,6 +469,15 @@ void si5351c_init(si5351c_driver_t* const drv) .drive = SI5351C_DRIVE_8MA, }; + si5351c_clk_t mcu_clkin = { + .output_enable = false, + .power_down = true, + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_2MA, + }; + si5351c_clk_t powered_down = { .output_enable = false, .power_down = true, @@ -488,7 +532,8 @@ void si5351c_init(si5351c_driver_t* const drv) /* CLK6: none */ clk[6] = powered_down; /* CLK7: LPC43xx */ - clk[7] = powered_down; + clk[7] = mcu_clkin; + mcu_clkin_id = 7; #ifdef IS_H1_R9 if (IS_H1_R9) { @@ -511,6 +556,7 @@ void si5351c_init(si5351c_driver_t* const drv) /* CLK2: CLKOUT/LPC4320 */ clk[2] = clkout; clkout_id = 2; + mcu_clkin_id = 2; /* Other outputs not present on Si5351A */ clk[3] = powered_down; clk[4] = powered_down; @@ -569,7 +615,8 @@ void si5351c_init(si5351c_driver_t* const drv) }; } else { /* CLK2: MCU_CLK */ - clk[2] = powered_down; + clk[2] = mcu_clkin; + mcu_clkin_id = 2; } } #endif diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index 9691b896..dad74a17 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -135,6 +135,7 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv); void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val); uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg); void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable); +void si5351c_mcu_clkin_enable(si5351c_driver_t* const drv, bool enable); void si5351c_init(si5351c_driver_t* const drv); void si5351c_set_phase( si5351c_driver_t* const drv, From 62229fdf73e96943f95c623144258d9708982beb Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 21 May 2026 18:40:44 +0100 Subject: [PATCH 141/173] Remove sample rate callback, move radio instance to radio.c. --- firmware/common/radio.c | 29 +++++++++++++++-------------- firmware/common/radio.h | 4 ---- firmware/hackrf_usb/hackrf_usb.c | 4 ---- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index eb01b5de..f8b2e514 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -25,6 +25,7 @@ #include +#include "clock_gen.h" #include "clock_io.h" #include "fixed_point.h" #include "max283x.h" @@ -41,6 +42,9 @@ #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) +/* Driver instance. */ +radio_t radio; + void radio_init(radio_t* const radio) { for (uint8_t bank = 0; bank < RADIO_NUM_BANKS; bank++) { @@ -231,22 +235,19 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) } new_n = (n != previous_n); - if (radio->sample_rate_cb) { - afe_rate = rate << n; - afe_rate = radio->sample_rate_cb(afe_rate, false); - previous_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]; - if ((previous_n == RADIO_UNSET) || previous_rate == RADIO_UNSET) { - previous_afe_rate = RADIO_UNSET; - } else { - previous_afe_rate = previous_rate << previous_n; - } - new_afe_rate = (afe_rate != previous_afe_rate); - if (new_afe_rate) { - afe_rate = radio->sample_rate_cb(afe_rate, true); - } + afe_rate = rate << n; + afe_rate = sample_rate_set(afe_rate, false); + previous_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]; + if ((previous_n == RADIO_UNSET) || previous_rate == RADIO_UNSET) { + previous_afe_rate = RADIO_UNSET; } else { - return 0; + previous_afe_rate = previous_rate << previous_n; } + new_afe_rate = (afe_rate != previous_afe_rate); + if (new_afe_rate) { + afe_rate = sample_rate_set(afe_rate, true); + } + rate = afe_rate >> n; new_rate = (rate != previous_rate); if (new_rate) { diff --git a/firmware/common/radio.h b/firmware/common/radio.h index b785c281..821c7fea 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -225,7 +225,6 @@ typedef struct { radio_config_mode_t config_mode; uint64_t config[RADIO_NUM_BANKS][RADIO_NUM_REGS]; volatile uint32_t regs_dirty; - sample_rate_fn sample_rate_cb; update_fn update_cb; } radio_t; @@ -263,8 +262,5 @@ void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode); /** * Driver instance. - * - * This needs to be configured and provided by the application - * binary. e.g. firmware/hackrf_usb/hackrf_usb.c */ extern radio_t radio; diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index eb7f85c5..5d71fa80 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -96,10 +96,6 @@ extern uint32_t __m0_end__; extern uint32_t __ram_m0_start__; extern uint32_t _etext_ram, _text_ram, _etext_rom; -radio_t radio = { - .sample_rate_cb = sample_rate_set, -}; - static usb_request_handler_fn vendor_request_handler[] = { NULL, usb_vendor_request_set_transceiver_mode, From 7cce66a7fa8449a3c64e55d62ba1bda8005d5c63 Mon Sep 17 00:00:00 2001 From: mndza Date: Fri, 22 May 2026 08:18:17 +0200 Subject: [PATCH 142/173] Fix reads with hackrf_spiflash when size is a multiple of 64 bytes. --- firmware/hackrf_usb/usb_api_spiflash.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/firmware/hackrf_usb/usb_api_spiflash.c b/firmware/hackrf_usb/usb_api_spiflash.c index d490eb0e..c698d599 100644 --- a/firmware/hackrf_usb/usb_api_spiflash.c +++ b/firmware/hackrf_usb/usb_api_spiflash.c @@ -111,6 +111,7 @@ usb_request_status_t usb_vendor_request_read_spiflash( len, NULL, NULL); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } } else if (stage == USB_TRANSFER_STAGE_DATA) { @@ -121,7 +122,6 @@ usb_request_status_t usb_vendor_request_read_spiflash( ((addr + len) > spi_flash.num_bytes)) { return USB_REQUEST_STATUS_STALL; } else { - usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } } else { @@ -141,13 +141,9 @@ usb_request_status_t usb_vendor_request_spiflash_status( 2, NULL, NULL); - return USB_REQUEST_STATUS_OK; - } else if (stage == USB_TRANSFER_STAGE_DATA) { usb_transfer_schedule_ack(endpoint->out); - return USB_REQUEST_STATUS_OK; - } else { - return USB_REQUEST_STATUS_OK; } + return USB_REQUEST_STATUS_OK; } usb_request_status_t usb_vendor_request_spiflash_clear_status( From eb2e6a1c05a2a2080bec6964242c7e00c0734e24 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 28 May 2026 16:37:18 +0100 Subject: [PATCH 143/173] Move SSP0 and SSP1 bus instances to spi_ssp.{c,h}. --- firmware/common/spi_bus.c | 21 --------------------- firmware/common/spi_bus.h | 4 ---- firmware/common/spi_ssp.c | 17 +++++++++++++++++ firmware/common/spi_ssp.h | 4 ++++ 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/firmware/common/spi_bus.c b/firmware/common/spi_bus.c index a5332699..1b05c298 100644 --- a/firmware/common/spi_bus.c +++ b/firmware/common/spi_bus.c @@ -22,27 +22,6 @@ #include "spi_bus.h" -#include - -#include "spi_ssp.h" - -/* Driver instances. */ -spi_bus_t spi_bus_ssp0 = { - .obj = (void*) SSP0_BASE, - .start = spi_ssp_start, - .stop = spi_ssp_stop, - .transfer = spi_ssp_transfer, - .transfer_gather = spi_ssp_transfer_gather, -}; - -spi_bus_t spi_bus_ssp1 = { - .obj = (void*) SSP1_BASE, - .start = spi_ssp_start, - .stop = spi_ssp_stop, - .transfer = spi_ssp_transfer, - .transfer_gather = spi_ssp_transfer_gather, -}; - void spi_bus_start(spi_bus_t* const bus, const void* const config) { bus->config = config; diff --git a/firmware/common/spi_bus.h b/firmware/common/spi_bus.h index 23b3107b..07d5787d 100644 --- a/firmware/common/spi_bus.h +++ b/firmware/common/spi_bus.h @@ -51,7 +51,3 @@ void spi_bus_transfer_gather( spi_bus_t* const bus, const spi_transfer_t* const transfers, const size_t count); - -/* Driver instances. */ -extern spi_bus_t spi_bus_ssp0; -extern spi_bus_t spi_bus_ssp1; diff --git a/firmware/common/spi_ssp.c b/firmware/common/spi_ssp.c index 7c288d51..868556be 100644 --- a/firmware/common/spi_ssp.c +++ b/firmware/common/spi_ssp.c @@ -28,6 +28,23 @@ #include #include +/* Driver instances. */ +spi_bus_t spi_bus_ssp0 = { + .obj = (void*) SSP0_BASE, + .start = spi_ssp_start, + .stop = spi_ssp_stop, + .transfer = spi_ssp_transfer, + .transfer_gather = spi_ssp_transfer_gather, +}; + +spi_bus_t spi_bus_ssp1 = { + .obj = (void*) SSP1_BASE, + .start = spi_ssp_start, + .stop = spi_ssp_stop, + .transfer = spi_ssp_transfer, + .transfer_gather = spi_ssp_transfer_gather, +}; + void spi_ssp_start(spi_bus_t* const bus, const void* const _config) { const ssp_config_t* const config = _config; diff --git a/firmware/common/spi_ssp.h b/firmware/common/spi_ssp.h index 822772b5..5e754dc4 100644 --- a/firmware/common/spi_ssp.h +++ b/firmware/common/spi_ssp.h @@ -45,3 +45,7 @@ void spi_ssp_transfer_gather( spi_bus_t* const bus, const spi_transfer_t* const transfers, const size_t count); + +/* Driver instances. */ +extern spi_bus_t spi_bus_ssp0; +extern spi_bus_t spi_bus_ssp1; From b86b13930574a1ff97d82901bb8d55d489783404 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 29 May 2026 09:02:01 +0100 Subject: [PATCH 144/173] Express all delays as delay_us() or delay_ms(). --- firmware/blinky/blinky.c | 4 ++-- firmware/common/clock_gen.c | 2 +- firmware/common/cpu_clock.c | 16 ++++++++++++++-- firmware/common/cpu_clock.h | 3 +++ firmware/common/delay.c | 21 ++++++++++++--------- firmware/common/delay.h | 4 ++-- firmware/common/fpga_selftest.c | 8 ++++---- firmware/common/ice40_spi.c | 4 ++-- firmware/common/leds.c | 6 +++--- firmware/common/operacake_sctimer.c | 2 +- firmware/common/platform_detect.c | 20 ++++++++++---------- firmware/common/portapack.c | 14 ++++---------- firmware/common/power.c | 4 ++-- firmware/common/rad1o/display.c | 12 +++--------- firmware/common/rffc5071.c | 4 ++-- firmware/common/si5351c.c | 2 +- firmware/hackrf_usb/hackrf_usb.c | 6 +++--- firmware/hackrf_usb/usb_api_cpld.c | 2 +- 18 files changed, 70 insertions(+), 64 deletions(-) diff --git a/firmware/blinky/blinky.c b/firmware/blinky/blinky.c index fdaf3ef7..4bd11106 100644 --- a/firmware/blinky/blinky.c +++ b/firmware/blinky/blinky.c @@ -45,13 +45,13 @@ int main(void) led_on(LED2); led_on(LED3); - delay(2000000); + delay_ms(150); led_off(LED1); led_off(LED2); led_off(LED3); - delay(2000000); + delay_ms(150); } return 0; diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index 22f461c2..8a75074f 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -148,7 +148,7 @@ clock_source_t activate_best_clock_source(void) /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ if (portapack_reference_oscillator && portapack()) { portapack_reference_oscillator(true); - delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */ + delay_ms(18); // for oscillator to enable. if (si5351c_clkin_signal_valid(&si5351c)) { source = CLOCK_SOURCE_PORTAPACK; } else { diff --git a/firmware/common/cpu_clock.c b/firmware/common/cpu_clock.c index 325de76f..32fd012c 100644 --- a/firmware/common/cpu_clock.c +++ b/firmware/common/cpu_clock.c @@ -33,6 +33,9 @@ #include "i2c_lpc.h" #include "si5351c.h" +/* We start with the CPU clock at 96MHz */ +unsigned int cpu_clock_mhz = 96; + /* Configure PLL1 (Main MCU Clock) to max speed (204MHz). Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. @@ -51,11 +54,14 @@ static void cpu_clock_pll1_max_speed(void) reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_IRC) | CGU_BASE_M4_CLK_AUTOBLOCK(1); CGU_BASE_M4_CLK = reg_val; + /* CPU is now at 12MHz */ + cpu_clock_mhz = 12; + /* 2. Enable the crystal oscillator. */ CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK; /* 3. Wait 250us. */ - delay_us_at_mhz(250, 12); + delay_us(250); /* 4. Set the AUTOBLOCK bit. */ CGU_PLL1_CTRL |= CGU_PLL1_CTRL_AUTOBLOCK(1); @@ -95,11 +101,17 @@ static void cpu_clock_pll1_max_speed(void) reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_PLL1); CGU_BASE_M4_CLK = reg_val; + /* CPU is now at 102MHz */ + cpu_clock_mhz = 102; + /* 9. Wait 50us. */ - delay_us_at_mhz(50, 102); + delay_us(50); /* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */ CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK; + + /* CPU is now at 204MHz */ + cpu_clock_mhz = 204; } /* clock startup for LPC4320 configure PLL1 to max speed (204MHz). diff --git a/firmware/common/cpu_clock.h b/firmware/common/cpu_clock.h index 92ad6441..04929643 100644 --- a/firmware/common/cpu_clock.h +++ b/firmware/common/cpu_clock.h @@ -27,6 +27,9 @@ extern "C" { void cpu_clock_init(void); +// Current clock speed in MHz, updated on clock changes. +extern unsigned int cpu_clock_mhz; + #ifdef __cplusplus } #endif diff --git a/firmware/common/delay.c b/firmware/common/delay.c index ceb7693e..b74389b2 100644 --- a/firmware/common/delay.c +++ b/firmware/common/delay.c @@ -21,16 +21,9 @@ #include "delay.h" -void delay(uint32_t duration) -{ - uint32_t i; +#include "cpu_clock.h" - for (i = 0; i < duration; i++) { - __asm__("nop"); - } -} - -void delay_us_at_mhz(uint32_t us, uint32_t mhz) +static void delay_us_at_mhz(uint32_t us, uint32_t mhz) { #if defined(LPC43XX_M4) // The loop below takes 3 cycles per iteration. @@ -52,3 +45,13 @@ void delay_us_at_mhz(uint32_t us, uint32_t mhz) #error "No delay loop implementation" #endif } + +void delay_us(uint32_t us) +{ + delay_us_at_mhz(us, cpu_clock_mhz); +} + +void delay_ms(uint32_t ms) +{ + delay_us_at_mhz(ms * 1000, cpu_clock_mhz); +} diff --git a/firmware/common/delay.h b/firmware/common/delay.h index 870ba39e..eed806bb 100644 --- a/firmware/common/delay.h +++ b/firmware/common/delay.h @@ -27,8 +27,8 @@ extern "C" { #include -void delay(uint32_t duration); -void delay_us_at_mhz(uint32_t us, uint32_t mhz); +void delay_us(uint32_t us); +void delay_ms(uint32_t ms); #ifdef __cplusplus } diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index 06f2e8ee..7170422c 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -200,7 +200,7 @@ bool fpga_if_xcvr_selftest(void) // Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF sample_rate_set(SR_FP_MHZ(4), true); - delay_us_at_mhz(1000, 204); + delay_ms(1); if (rx_samples(num_samples, 2000000) == -1) { timeout = true; } @@ -211,7 +211,7 @@ bool fpga_if_xcvr_selftest(void) // Capture 2: 4 Msps, tone at 0.5 MHz, narrowband filter ON narrowband_filter_set(1); - delay_us_at_mhz(1000, 204); + delay_ms(1); if (rx_samples(num_samples, 2000000) == -1) { timeout = true; } @@ -224,7 +224,7 @@ bool fpga_if_xcvr_selftest(void) fpga_set_tx_nco_pstep(&fpga, 255); sample_rate_set(SR_FP_MHZ(20), true); narrowband_filter_set(0); - delay_us_at_mhz(1000, 204); + delay_ms(1); if (rx_samples(num_samples, 2000000) == -1) { timeout = true; } @@ -235,7 +235,7 @@ bool fpga_if_xcvr_selftest(void) // Capture 4: 20 Msps, tone at 5 MHz, narrowband filter ON narrowband_filter_set(1); - delay_us_at_mhz(1000, 204); + delay_ms(1); if (rx_samples(num_samples, 2000000) == -1) { timeout = true; } diff --git a/firmware/common/ice40_spi.c b/firmware/common/ice40_spi.c index 2685092a..880e0be2 100644 --- a/firmware/common/ice40_spi.c +++ b/firmware/common/ice40_spi.c @@ -112,14 +112,14 @@ bool ice40_spi_syscfg_program( gpio_clear(drv->gpio_select); // Wait a minimum of 200 ns. - delay_us_at_mhz(1, 204 / 4); // 250 ns. + delay_us(1); // Release CRESET_B or drive CRESET_B = 1. gpio_set(drv->gpio_creset); // Wait a minimum of 1200 μs to clear internal configuration memory. // Testing showed us that we need to wait longer. Let's wait 1800 μs. - delay_us_at_mhz(1800, 204); + delay_us(1800); // Set SPI_SS = 1, Send 8 dummy clocks. gpio_set(drv->gpio_select); diff --git a/firmware/common/leds.c b/firmware/common/leds.c index ab9b56fb..e1fd507b 100644 --- a/firmware/common/leds.c +++ b/firmware/common/leds.c @@ -84,17 +84,17 @@ void set_leds(const uint8_t state) } } -void halt_and_flash(const uint32_t duration) +void halt_and_flash(const uint32_t period_ms) { /* blink LED1, LED2, and LED3 */ while (1) { led_on(LED1); led_on(LED2); led_on(LED3); - delay(duration); + delay_ms(period_ms / 2); led_off(LED1); led_off(LED2); led_off(LED3); - delay(duration); + delay_ms(period_ms / 2); } } diff --git a/firmware/common/operacake_sctimer.c b/firmware/common/operacake_sctimer.c index 76453e57..c5ec8a3b 100644 --- a/firmware/common/operacake_sctimer.c +++ b/firmware/common/operacake_sctimer.c @@ -75,7 +75,7 @@ void operacake_sctimer_init(void) // there are additional instructions that fill the time. If the duration of // the actions from here to the first access to the SCTimer is changed, then // this delay may need to be increased. - delay(8); + delay_us(1); // Pin definitions for the HackRF // U2CTRL0 diff --git a/firmware/common/platform_detect.c b/firmware/common/platform_detect.c index 7c9fb53d..475ad1e1 100644 --- a/firmware/common/platform_detect.c +++ b/firmware/common/platform_detect.c @@ -194,11 +194,11 @@ void detect_hardware_platform(void) /* activate internal pull-down */ scu_pinmux(P5_0, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); scu_pinmux(P6_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - delay_us_at_mhz(4, 96); + delay_us(4); /* tri-state for a moment before testing input */ scu_pinmux(P5_0, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); scu_pinmux(P6_10, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - delay_us_at_mhz(4, 96); + delay_us(4); /* if input rose quickly, there must be an external pull-up */ detected_resistors |= (gpio_read(&gpio2_9_on_P5_0)) ? P5_0_PUP : 0; detected_resistors |= (gpio_read(&gpio3_6_on_P6_10)) ? P6_10_PUP : 0; @@ -207,12 +207,12 @@ void detect_hardware_platform(void) scu_pinmux(P5_0, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); scu_pinmux(P6_10, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); scu_pinmux(P6_5, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); - delay_us_at_mhz(4, 96); + delay_us(4); /* tri-state for a moment before testing input */ scu_pinmux(P5_0, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); scu_pinmux(P6_10, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); scu_pinmux(P6_5, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - delay_us_at_mhz(4, 96); + delay_us(4); /* if input fell quickly, there must be an external pull-down */ detected_resistors |= (gpio_read(&gpio2_9_on_P5_0)) ? 0 : P5_0_PDN; detected_resistors |= (gpio_read(&gpio3_6_on_P6_10)) ? 0 : P6_10_PDN; @@ -221,37 +221,37 @@ void detect_hardware_platform(void) switch (detected_resistors) { case JAWBREAKER_RESISTORS: if (!(supported_platform() & PLATFORM_JAWBREAKER)) { - halt_and_flash(3000000); + halt_and_flash(500); } platform = BOARD_ID_JAWBREAKER; return; case RAD1O_RESISTORS: if (!(supported_platform() & PLATFORM_RAD1O)) { - halt_and_flash(3000000); + halt_and_flash(500); } platform = BOARD_ID_RAD1O; return; case HACKRF1_OG_RESISTORS: if (!(supported_platform() & PLATFORM_HACKRF1_OG)) { - halt_and_flash(3000000); + halt_and_flash(500); } platform = BOARD_ID_HACKRF1_OG; break; case HACKRF1_R9_RESISTORS: if (!(supported_platform() & PLATFORM_HACKRF1_R9)) { - halt_and_flash(3000000); + halt_and_flash(500); } platform = BOARD_ID_HACKRF1_R9; break; case PRALINE_RESISTORS: if (!(supported_platform() & PLATFORM_PRALINE)) { - halt_and_flash(3000000); + halt_and_flash(500); } platform = BOARD_ID_PRALINE; break; default: platform = BOARD_ID_UNRECOGNIZED; - halt_and_flash(1000000); + halt_and_flash(150); } uint32_t adc0_3 = check_pin_strap(3); diff --git a/firmware/common/portapack.c b/firmware/common/portapack.c index d3d848fc..0c3cbce2 100644 --- a/firmware/common/portapack.c +++ b/firmware/common/portapack.c @@ -31,12 +31,6 @@ #include "platform_gpio.h" #include "platform_scu.h" -static void portapack_sleep_milliseconds(const uint32_t milliseconds) -{ - /* NOTE: Naively assumes 204 MHz instruction cycle clock and five instructions per count */ - delay(milliseconds * 40800); -} - typedef struct { gpio_t gpio_dir; gpio_t gpio_lcd_rdx; @@ -244,7 +238,7 @@ static void portapack_lcd_sleep_out(void) // "It will be necessary to wait 120msec after sending Sleep Out // command (when in Sleep In Mode) before Sleep In command can be // sent." - portapack_sleep_milliseconds(120); + delay_ms(120); } static void portapack_lcd_display_on(void) @@ -310,11 +304,11 @@ static void portapack_lcd_wake(void) static void portapack_lcd_reset(void) { portapack_lcd_reset_state(false); - portapack_sleep_milliseconds(1); + delay_ms(1); portapack_lcd_reset_state(true); - portapack_sleep_milliseconds(10); + delay_ms(10); portapack_lcd_reset_state(false); - portapack_sleep_milliseconds(120); + delay_ms(120); } static void portapack_lcd_init(void) diff --git a/firmware/common/power.c b/firmware/common/power.c index 1256e595..70c8c0f1 100644 --- a/firmware/common/power.c +++ b/firmware/common/power.c @@ -132,7 +132,7 @@ static inline void enable_rf_power_praline(void) gpio_clear(platform_gpio()->vaa_disable); /* Let the voltage stabilize */ - delay(1000000); + delay_ms(35); } static inline void disable_rf_power_praline(void) @@ -147,7 +147,7 @@ static inline void enable_rf_power_rad1o(void) gpio_set(platform_gpio()->vaa_enable); /* Let the voltage stabilize */ - delay(1000000); + delay_ms(35); } static inline void disable_rf_power_rad1o(void) diff --git a/firmware/common/rad1o/display.c b/firmware/common/rad1o/display.c index 91813aab..c0c090b3 100644 --- a/firmware/common/rad1o/display.c +++ b/firmware/common/rad1o/display.c @@ -10,12 +10,6 @@ #include "gpio_lpc.h" #include "delay.h" -static void delayms(const uint32_t milliseconds) -{ - /* NOTE: Naively assumes 204 MHz instruction cycle clock and five instructions per count */ - delay(milliseconds * 40800); -} - static struct gpio gpio_lcd_cs = GPIO(4, 12); /* P9_0 */ static struct gpio gpio_lcd_bl_en = GPIO(0, 8); /* P1_1 */ static struct gpio gpio_lcd_reset = GPIO(5, 17); /* P9_4 */ @@ -85,9 +79,9 @@ void rad1o_lcdInit(void) // Reset the display gpio_clear(&gpio_lcd_reset); - delayms(100); + delay_ms(100); gpio_set(&gpio_lcd_reset); - delayms(100); + delay_ms(100); select(); @@ -115,7 +109,7 @@ void rad1o_lcdInit(void) (1 << 12) | (0 << 13) | (0 << 14) | 0); write(0, 0x01); /* most color displays need the pause */ - delayms(10); + delay_ms(10); size_t i = 0; while (i < sizeof(initseq_d)) { diff --git a/firmware/common/rffc5071.c b/firmware/common/rffc5071.c index 2a1866d2..df25987c 100644 --- a/firmware/common/rffc5071.c +++ b/firmware/common/rffc5071.c @@ -163,13 +163,13 @@ void rffc5071_lock_test(rffc5071_driver_t* const drv) rffc5071_enable(drv); // Wait 1ms. - delay_us_at_mhz(1000, 204); + delay_ms(1); // Check for lock. lock = rffc5071_check_lock(drv); rffc5071_disable(drv); - delay_us_at_mhz(100, 204); + delay_us(100); selftest.mixer_locks[i] = lock; } diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 77d77703..6672b3b0 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -224,7 +224,7 @@ void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask) set_PLLA_RST(drv, (mask & SI5351C_PLL_MASK_A) ? true : false); set_PLLB_RST(drv, (mask & SI5351C_PLL_MASK_B) ? true : false); si5351c_regs_commit(drv); - delay_us_at_mhz(2000, 204); + delay_ms(2); si5351c_enable_clock_outputs(drv); } diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 5d71fa80..f9a4e40b 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -433,7 +433,7 @@ int main(void) if (board_id != BOARD_ID_RAD1O) { clock_gen_shutdown(); } - delay_us_at_mhz(10000, 96); + delay_ms(10); pins_setup(); #ifdef IS_PRALINE if (IS_PRALINE) { @@ -491,7 +491,7 @@ int main(void) #ifdef IS_NOT_PRALINE if (IS_NOT_PRALINE) { if (!cpld_jtag_sram_load(&jtag_cpld)) { - halt_and_flash(6000000); + halt_and_flash(1000); } } #endif @@ -503,7 +503,7 @@ int main(void) #else fpga_image_load(&fpga_loader, 0); #endif - delay_us_at_mhz(100, 204); + delay_us(100); fpga_spi_selftest(); fpga_sgpio_selftest(); } diff --git a/firmware/hackrf_usb/usb_api_cpld.c b/firmware/hackrf_usb/usb_api_cpld.c index 391b9366..38103c81 100644 --- a/firmware/hackrf_usb/usb_api_cpld.c +++ b/firmware/hackrf_usb/usb_api_cpld.c @@ -76,7 +76,7 @@ void cpld_update(void) cpld_xsvf_buffer, refill_cpld_buffer); if (error == 0) { - halt_and_flash(6000000); + halt_and_flash(1000); } else { /* LED3 (Red) steady on error */ led_on(LED3); From 4f9f6a4c7425144ea43348f424c52d150ec67fa3 Mon Sep 17 00:00:00 2001 From: Dreg Date: Wed, 3 Jun 2026 02:41:37 +0200 Subject: [PATCH 145/173] Add SigDigger support information to documentation --- docs/source/software_support.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/source/software_support.rst b/docs/source/software_support.rst index e8487652..66594e6b 100644 --- a/docs/source/software_support.rst +++ b/docs/source/software_support.rst @@ -43,6 +43,11 @@ Software That Has Direct Support For HackRF * `https://xakcop.com/aprs-sdr `__ +* SigDigger (Windows/Linux/macOS) + + * `https://github.com/BatchDrake/SigDigger `__ + * Supports HackRF through SoapySDR / SoapyHackRF + Software That Can Use Data From HackRF @@ -74,4 +79,4 @@ Troubleshooting Recommendations Many of these tools require libhackrf and at times HackRF Tools. It may help you to have updated libhackrf and HackRF Tools when troubleshooting these applications. -It is also strongly suggested, and usually required, that your HackRF Tools and HackRF firmware match. \ No newline at end of file +It is also strongly suggested, and usually required, that your HackRF Tools and HackRF firmware match. From 5d9b6bf4119228c65ea2b72991926e956bca5c4c Mon Sep 17 00:00:00 2001 From: mndza Date: Thu, 28 May 2026 14:05:13 +0200 Subject: [PATCH 146/173] add gateware documentation --- docs/images/gateware-basic-block-diagram.png | Bin 0 -> 195902 bytes docs/images/gateware-basic-block-diagram.svg | 4 + .../images/gateware-ext-prec-rx-dsp-chain.png | Bin 0 -> 70790 bytes .../images/gateware-ext-prec-rx-dsp-chain.svg | 4 + .../images/gateware-ext-prec-tx-dsp-chain.png | Bin 0 -> 51198 bytes .../images/gateware-ext-prec-tx-dsp-chain.svg | 4 + docs/images/gateware-halfprec-dsp-chain.png | Bin 0 -> 79385 bytes docs/images/gateware-halfprec-dsp-chain.svg | 4 + .../gateware-rx-only-basic-block-diagram.png | Bin 0 -> 138600 bytes .../gateware-rx-only-basic-block-diagram.svg | 4 + docs/images/gateware-standard-dsp-chain.png | Bin 0 -> 130043 bytes docs/images/gateware-standard-dsp-chain.svg | 4 + .../gateware-tx-only-basic-block-diagram.png | Bin 0 -> 137152 bytes .../gateware-tx-only-basic-block-diagram.svg | 4 + docs/source/gateware.rst | 118 ++++++++++++++++++ docs/source/hackrf_pro.rst | 2 + docs/source/index.rst | 7 ++ 17 files changed, 155 insertions(+) create mode 100644 docs/images/gateware-basic-block-diagram.png create mode 100644 docs/images/gateware-basic-block-diagram.svg create mode 100644 docs/images/gateware-ext-prec-rx-dsp-chain.png create mode 100644 docs/images/gateware-ext-prec-rx-dsp-chain.svg create mode 100644 docs/images/gateware-ext-prec-tx-dsp-chain.png create mode 100644 docs/images/gateware-ext-prec-tx-dsp-chain.svg create mode 100644 docs/images/gateware-halfprec-dsp-chain.png create mode 100644 docs/images/gateware-halfprec-dsp-chain.svg create mode 100644 docs/images/gateware-rx-only-basic-block-diagram.png create mode 100644 docs/images/gateware-rx-only-basic-block-diagram.svg create mode 100644 docs/images/gateware-standard-dsp-chain.png create mode 100644 docs/images/gateware-standard-dsp-chain.svg create mode 100644 docs/images/gateware-tx-only-basic-block-diagram.png create mode 100644 docs/images/gateware-tx-only-basic-block-diagram.svg create mode 100644 docs/source/gateware.rst diff --git a/docs/images/gateware-basic-block-diagram.png b/docs/images/gateware-basic-block-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..58f99e7c6a7622edbb820923cbd6d4834d2190e9 GIT binary patch literal 195902 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%0DQwoE@m47=`rp~=S*1%crihTn3-FbFJmlvKOR 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/h8H1cdTAwMWR0qrL284aN9729+/ZcdFx1MDAwN1x1MDAwNrG02VxmeD0x//1kyTaS0Fx1MDAwMrLFmPbAxHR0I6moUuXyfVWZWf/9Y2lpefjcc5f/vbTsPlXKrWa1X35cXP7Tfv/g9lx1MDAwN81uXHUwMDA3L4H370H3vl/x7mxcZoe9wb//9S//XHSn0m2/PuW23LbbXHUwMDE5XHUwMDBl8L7/x38vLf3X+1x1MDAxM680q/bZy8Y57Dw/XFzXXG6lm63rWsttNODMe9S76b0zfbcyLHfqLde/9ITfXHUwMDBiplx1MDAxZC6lJpRcdTAwMWJQIOTo6jNeLVxi4kiqqZLMXGIpXHUwMDA1gdHlx2Z12MBbuGGOJMZQwyhhyojRXHUwMDFkXHK3WW9cZu0tXHUwMDAyb9GMXHSuXHJlknM+uue1R/9eXCKjb1x1MDAwNsN+99Zd7ba6fdvt/6Ou/c/v9E25clvvd+87Vf+emvfx76k1W63j4bPXMr5lfKPLY+2fv/VcdTAwMWXGvk96XG5/sN7ouINBqK/dXrnSXHUwMDFj2lx1MDAxN0WJ/63tXW+r6k3Yf/w+9cttd8vOWOe+1Vx1MDAxYX3d7FRdO1x1MDAwZstXw9Cvdapvv1x1MDAxNrp74LpV74Vcblx0kjL/PfqiRSnR41/vdzuenFx1MDAwMVGMXHUwMDEzQYnxf39QQlx1MDAwMVx1MDAxYnrN1sqtgeu/aNuJtYDw+WO571XLr49QpYxWlFBcclKNrreandvxrre6lVv/V7xv//ozTp77va6QzeKPYbn93H/aboq7X5t3U8szyrCjXHUwMDE4MYJzqViiPFx1MDAwYq1cdTAwMDRII6iIXGI0UOJwbjTBh1x1MDAxNSdU6DiJXHUwMDA2x1DBOGgjlFxuXGZ9IdAjgX6cXqCpJGiCXHUwMDE4XHUwMDFholx1MDAxOInWPFwi5+9cdTAwMDJNJTVAgc2xON+d3pTkbu38+fjxeO30x+GAtqE2tThcdTAwMWKiXHUwMDFkTZRcdTAwMDLzKs5cIizOlDl4UVx1MDAxOM5cdFx1MDAwMEHZ11x1MDAxMXGmXHUwMDAwjqZCXHUwMDFhRjRcdTAwMTeKU1x1MDAxYVx1MDAxNWctUZpcdTAwMDHbodaQa5m3fS6Lqv7txflpY3p5NtpcdTAwMTBuqDIx4mxcdTAwMDLiOi7OIIxC4aNcdTAwMWZcdTAwMTLot1x1MDAwYr5cdTAwMDRcdTAwMDaksLr1dFN5Wi109tpnJXq4Uz68a5vReEKSWO73u4/Loyt//ZnWLu+drJUu9tdXKrtcdTAwMWJseHhr9lZcdTAwMGZcdTAwMGbj21x1MDAxZLpPw0Czb3+bpIU6XHUwMDFmLUzvZ7iPYVx1MDAwNWTEUVx1MDAxYYBTKVxmXHRaXHUwMDA1K1bCYUorjW5cdTAwMDS1XHUwMDA3b4jCI4X6K/GjcV6pojrGmVxikq+6XHL75c6gV+6jXFz87ip3lsGFIPrU+Js8ol3WwIlEXHUwMDE3gu7DoFx1MDAxN6FCfUTn8lx1MDAxNF9fXHUwMDFhrVx1MDAxNOL4i6XVn51mZ+j2a+VKYM5r3c7wuPnyiuhC366X282Wffky1Fix1azb17BcXHFta8vBdzFsXCLlXHUwMDE43dBuVqtBXHUwMDFmVMFGy82O29+axpl1+816s1NunfhcdTAwMDNYiul/+X7Y/eFcdTAwMGVeRzDs37vBl+RuvutcdTAwMDV1QKSo9EGZXGbW9eXOpTxfLz5fXHUwMDFmXFxcdTAwMGW2V1x1MDAxZad2rIiSlcNcdTAwMTVcdTAwMTNcdTAwMDIo/i+1b6rDQFx1MDAxMalcdTAwMGZcdTAwMTGKcP+NRoBcIpNEMJzwWOrzXHUwMDBlXHUwMDE0lWZCcl+5XHUwMDE2QHGk5s/Ta7lcdTAwMTTANVx1MDAxMTpcdTAwMGUnUqLE+Ncjz6qRMFmOO8/Mxz0s3Ynmw01p0NpbP+akqzZLMUz+1UGHpFlcdTAwMWHuXGJASSaaIezgYZjIqEZcdTAwMWZmXHUwMDE0gJJSXHRCorKMJN6qXHUwMDAxXHUwMDE4fFxcXCKLZ1FRXHUwMDA2plx1MDAxZKRVQiiNnJ+agG2bLMtaatfIL3Fb1fKg4f69XHUwMDAy/TK9QKNcdTAwMTFcdTAwMTFaK4g6KG9WXCJcdTAwMDAyXHUwMDAwXHUwMDE1XHUwMDA1XGL0a7NcdTAwMTBoTTNcYvTo6163OY5E/b8t+e/a+8fo7//5M/ZuLlx1MDAxZMlQmNF4Wsn1efznny5cdTAwMDA1XHUwMDBlRUhGXHUwMDE5YUC5MJOaS9SOmTRcdTAwMTfRM7+9XGJoXHUwMDFlXGbL/eFcbkpes1PHN+8j9Pdlw1e/Te7Of1xmLnuuu71XP9m5X19bXHUwMDFmnq5cdTAwMDVg+nK7W/WUpdu/aVx1MDAwZYNcdTAwMTdqzSe3emhnNjSxS+idXHQzdj1cdTAwMGZcdTAwMTiSSuIvOtlcdTAwMGY6RqNcdTAwMTFcdTAwMTbjXHUwMDFmXHUwMDFh/1BcdTAwMTJcImNcdTAwMTjximW3U508guur45dcdTAwMWQlXHUwMDBipdvqxd7dkOzp9lnxcyOwnTSMXHTKuUTYSKVSf4ZcdTAwMDcosftEKoJcZpyjXHUwMDBmT1x1MDAxOYM3XHUwMDBmRWuYXHUwMDFibjliXHUwMDE5cITBa2jqmmOQxG3ddFx1MDAxZj2dXG7hIa/vx259XHUwMDEyx3uzPFx1MDAxMOz/a5/GxjxBNYKqXHUwMDE1XHUwMDEy39dBZG0sRTX8l5hCMd+GxT4/rPSeTFx1MDAxY2eqxk4/znj13Vx1MDAxYVx1MDAxY/fcSrPcilhU7ErkYlxufrgvXHUwMDBmyo9rZ5f9XHUwMDBlb/bO13ZcdTAwMGau8P6pXHUwMDExMaJYh0lBJOfK4IBYmOratSaDKFxu2SxeQs5rokun/lpcdTAwMTPlaFx1MDAxZGhcdTAwMDBU+zBcdTAwMDK0Q6lWyvcx+UDhXHUwMDFiqMHNze9ccoXLXHUwMDA30yNcdTAwMDfFXGZcdTAwMDGUd/83g0umdPxbXHUwMDFmOFx1MDAxOFBcdTAwMDKn5kPAIUFRPfHbXFzbKdS7e9Qt1I6u3EpVN0QhjzWmo+Lx1UOrddXacMU+2dgtdfZXrqZaY0ptdqUn6V79cqO92qn1n1bbJ7/Ory+n625Ej2e6dpU+/pS1K9RAR1x1MDAxMlx0XFxgd1x1MDAwNKNhVoD8V9idXHUwMDEy6+KQOlx1MDAwNCTmXZ2tQbBcdTAwMWYgkiokXHUwMDBlMWtXIPLV4m+zdlU+nF6TcXKEZuhX4jiANMmkXHUwMDE2XGYxYDjMglx1MDAwMyiSQXp9YXxb+TneONxcbtiyr1uxmuBcdTAwMTPHV6zG+p3PSlU6dp3kl1x1MDAxNVx1MDAwNU9RhUK3ySlcdTAwMWJbqEK3XGZcblxiIYogV5CBvbKRV1x1MDAxNoh0iZZcdTAwMWPVXHUwMDFjXbuKW6dCY0CJolJIdPxIOfJep/pcdTAwMGU7QOWjXGbOWUlGXHRcdTAwMDdcYlxu7ptGM1x1MDAxNXHZ71x1MDAxYc3RXHUwMDE2U5wkf4Zy8s3pS0shMczkm2HlobHdbFx1MDAxZWzxl5PTx3Jzc/C0df553zyr7arS3p5unD5etEFcdTAwMGWO+Fx1MDAxMWlcXD33e/Po89Pfa4rPR+rjXGKJqoxcdTAwMDZDIFx1MDAwMVx1MDAwZvt8qlx1MDAxY4VcdTAwMDBdXHUwMDAzKrti1PhcdTAwMTDfh/Dc4dbpcyaIQJTv25OF059sITJsWGmpXHUwMDA1XHUwMDEwXHUwMDA1cUvZLFx1MDAxOb8rbXcluJrFdtXnfP6Pi6XS8eFSpYHud1x1MDAxZVxc/1x1MDAwNLc77vrju59cdTAwMGZcdTAwMDJwz55qvc06O+a7l8eX5uCktlH8kVx0XHUwMDAxKIluXHUwMDFjfbNgwMZ0XHUwMDFhlENcdTAwMDDhvaHUbjLFbELPXHUwMDAzXHUwMDAyqNUqpmJ+c/0+nl69mdSccFx1MDAxZavegieG6EkphcZZyFx1MDAxYlx1MDAwMNBcdTAwMTV1uK86xeLwR+vxviV27s9rjVx1MDAxYzzqzWDQ6pdZof1jyG/qLWJWSi/Dz1x1MDAwM4Dn/e1cbu3tt3bL7ZWD+52Dx63CeUKzmbq7/oOd9n+121x1MDAxN6o9XHUwMDFjlipcdTAwMDf7dyu7c0n6099rKlx1MDAwMJBcdTAwMGWzLp5cdEC+wHzt8XRAaIcobdCUgFx1MDAxMujk/Y0+XHUwMDFmXHUwMDAxMIchnyCIIOz+t45cdFx1MDAxOFsggCRcdTAwMGJxkiFIXGZsXHUwMDE4qlFcdTAwMTFbYIlANHTMj3m0XHUwMDBitERcdTAwMDUgwrwggJM5Q1x1MDAwMFx1MDAxM9zuOFx1MDAwMojvfk7RKpX2rSno2/XLwu456TUua7Xn8keiVSxcdTAwMDQggcjv90VcdTAwMDDNXHUwMDEx9nN0XHUwMDFk2saDxsSBToNcdTAwMDFcdTAwMTZxoJM1/CaDhlx1MDAxYuBGiyjft3aaJ2u4UZJ7QUV5o4DaRfual3a2iqq1Vzu77lV/XGZkZypv/fd6v/R+png/Sjl1UIS1kppcdTAwMTLJXHUwMDAzkbRv/Jda6ouqpIlmLMp/OXNQ9o3RXHUwMDFjXHIs0uRcdTAwMDX/zaJcdTAwMWKVXGaL3lxcMMNcdTAwMTTEXHUwMDAyZClcdTAwMTOVgyM5YYhs5s/7XHUwMDE1S6vz4PQmeJqYXHUwMDE4zbx9XePm/PHa7PRKtfvhk7o5XZVX3Ytsvk4hXHUwMDE1RXCE2qzHXHUwMDExLOJbg1x1MDAxN1xyXHUwMDAz0IIyXHUwMDE23bfKRHdtXHUwMDAyT5BSL9juSJur02sz4ERcdTAwMTh847FhbGhLk8EssSSEm9xd3Uuh9munXFzefNp7pCuVlave3vlqwor3l7q69H5OcnVcdTAwMTI05YZgh1x1MDAxNFx1MDAwZqe6eUxcdTAwMGaQwdm9W6ZETNAnXHUwMDA3R9jcXHUwMDA0IbnhlFx1MDAwN+5YuLqJyuFmcHVUXHUwMDFhZG3BXHL0XHUwMDAwXHUwMDBlVFx1MDAxMVx1MDAwN/iuXHUwMDFjTFBE9EzNJL3tU5lcdKXiXFy4ulx0jmbc1YV6nZOr2yj21l+u6NMludx+qOtOsdIoTaO9fp4q06ihvlx1MDAxM/P4nPVOQmj0dYpcdTAwMWFcdTAwMDJcdTAwMTBccrXiwlx1MDAwMYtTXHIwRZAqxLi4he4m6W4zg+6i4WSII2TcTi5Pic9W0jCNNOTLc1Mjyru3epqH8rbc2jBFdYfdXpLehjo7rqSh3uWjpMVCf3ejfK97PXq2s3lz8uNwcPWUKS5SU2RcdTAwMWRcXFxuXHUwMDBlJLBcXGdfXHUwMDBlaONIw7lcdTAwMDaG01wilIqJv1x1MDAwMPTTXHUwMDAyXHUwMDA0wk1OhM1cdTAwMTiP6qowjkJnTYBLyuxeTt6A9FtER/6aXm+5VNJcdTAwMTKIuKVcdTAwMTdk9Ynbq1xiZJFeasHyXHUwMDBmj9zYPN1erz5t9y/JVePlWP56gFx1MDAxY3ZK+Fx1MDAwNdTF2epq79I9JSdrXHUwMDA3zy/P9Vp8s1+7o5E+/lx1MDAxNFeJwPR17ZOCje/mYT7IiHJsglx1MDAxZcNcdTAwMWJcdTAwMTS3YVx1MDAwYlx1MDAxMVx1MDAwNWRvcYyow1x1MDAwNrFcdTAwMTgsNjQy6Nzt9DonKFVM0Vx1MDAwNJ1jiXGMgFx1MDAxNFx1MDAxZDWOzGJJ53M49/hwa1x1MDAxZXDuXHUwMDA0XHUwMDA3XHUwMDE2XHRiXGb2Olx1MDAxZlx1MDAxN5qeQDRNkSGKfo2gd+NMqfCiLCiB+ol+z1x1MDAxONRjQaNEdVx1MDAxZeJcdTAwMTe+hVx1MDAwM+1kXHUwMDAwvlx1MDAxMnDahGJxpFXqxFx1MDAxNVx1MDAxZCFcYlx1MDAwNfPBXHUwMDEyQ2mO7nCvWd+H++NVpuBcdTAwMDW2f1x1MDAxZLfbv3hcdTAwMGUhXHUwMDAxs4qMmFXI5dlFvchcdTAwMWae6XqtWNi/b9bldVx1MDAxMe7/QThcIn38KThCSOZozblhTKGRXGLbIGOvjVKkI1x1MDAxNsgsynh83Op0p7c6klx1MDAxOC5s2FSc0ZHJy8goXlx1MDAxMslcdTAwMTWbXHUwMDA121aQQXDjIMSSdef9butnp+/Wm1x1MDAwM4RcdTAwMDKDeUBcdTAwMTVcdTAwMTN8elxmqnhcdTAwMWbIUsw48sFcdTAwMTnpxnjWJVx1MDAxMJA9SIVcdTAwMWNfXHUwMDBirnjMxi9aXHUwMDAxaXeLNFx1MDAwMSNcdTAwMTDrZomP+EdVQLghJMtOsLSp5ELE8Vx1MDAwNkheXHUwMDFlp1RcdTAwMTjgdEbpTyYov19SXHUwMDAyIVEgP/90IVwiyX5zXHUwMDExb/6PqTGQXHUwMDFlLvahXHUwMDExRGpcZvxcdTAwMTlcdTAwMWVeuMJA2lxi/qZcblx1MDAwM2FcdTAwMDVcdTAwMWRPT1x1MDAxZv/N8Wspln3/8mFtX93frJpB8aBeLojn1pqJWvaJXGZcdTAwMTLtO1qKaPybl1x1MDAwNEdBXHUwMDEyKVx1MDAxOYnZMMmWm1x1MDAwZVx1MDAxYWFcdTAwMWWhi1XYONvOp7ftRmgk5Do2xlx1MDAxNbuSuH2CRJ5cdTAwMTG0TrmHXHUwMDA1zIpFvlx1MDAxNFx1MDAwZlulW1a7u9u/bT+QXHUwMDAzXtzY2P88K/tEknpqu59IhEtt97eJr0+frzRcdTAwMTbJuWNQMqkhVFx1MDAxYaXDVTKUQJipmOZMXHUwMDE5XHUwMDAzXHUwMDEwXHUwMDAzMynxSoUgXaHAXHUwMDA0XHUwMDBiLnctqORk2yOy4Epcbmh6XGKLw5WGJlfIIHYhkbM5rFxi6aWnL5U71Z+dWqv7+M7H5oFNTvDvsYn2diRL8Vx1MDAwM8mHTqabzzQ6yTWqKaIyYZeKJKVcdTAwMTCOrjLGwW+1QCShXHUwMDE0XHUwMDA0QrlcdTAwMDOxVYaB4lx1MDAxNuDZqkExe07EQSlcdTAwMDNEM4pcdMlcdTAwMDSC9YXWJ2i9zKD1OCH4SmVsJFwiqJRIRFx1MDAwZcBQ9WdcdTAwMTJY/CGtz7egXpJAek9HRNFvLuKXp+SD6ZU3PspcdTAwMDc5Uj5mw+nAmLGigJZq2T1iXHUwMDAwjuPkRIrIIDJcdTAwMTLCdJv2oSFcdTAwMTTI2Fx1MDAxOFx1MDAwNKFcdTAwMWZcdTAwMWZFhFx1MDAxNMZcdTAwMTG/XGbUMCx/+XLD0bV298Hda76+ncF5c9h4U6QpUFv6JkW6PffOOeGKKsNcdTAwMTWTMmzPXHUwMDE5NfjiLcFcdTAwMDQ0XHUwMDA0jETzpzhz8GnCkFx1MDAxOVx1MDAxYYnTomP2IIkjwFJQramWXHUwMDA2mM5SXCL1n2XRi1x1MDAxOSw6cnWlNfC4TFx1MDAxMZqyKVx1MDAwMIpSbmZcdTAwMTNXoD+UKpKvRU+USPspRIXx8zY9PVx1MDAxMOFDXHUwMDA2kThm7Fx1MDAxMzaHglx1MDAxMPpZOz6DpUmSqZeztdOfMKjpyyGp+y12zY0qxDZcbqRcbsa2e/up0kyK6WCOQrFVaJNfXHUwMDE3oWNcdTAwMTAyV462K6eCI9RG6rSoOJ1oT1cy2FOcNqS3sYl3QJKrzTHEj0ZcdTAwMGI6XHUwMDBiWjxcdTAwMDfmNHR3XHUwMDAxuFx1MDAwMyh5mttQXCJcdTAwMDVcdTAwMDBi0vOFZImeUYtcdTAwMTH1+LyBn4WlTNtcdTAwMDKxcHisynKUenw9avfGgC9ZKWortEk/JMB+kJaAsTtcdTAwMGZInFx1MDAwNFx1MDAxMqjvvI2z/djZfJG7W1x1MDAwN72Ty5eSXFw3m/x5Olx1MDAwNE5cdJNcdTAwMGVRXvo157ZKc8hjUPRcdTAwMThGS+RENl3NXHUwMDA0XHUwMDE3yt5dhiBcdTAwMGVFZ8KVRoxcdTAwMWVKXGZcciBwXCKNd9iD5lx1MDAwNEGjWUDwRJexkXCgVUCv3qZcdTAwMTOiYvxW5lAghZWxeW1cXKfkxtiUJ1x1MDAwMfpDVY5mssCaqydJlFPv6aiETm+2x1x1MDAxNTfhSn5GJjeguXVx+HJ5Vjhw60ebp1x1MDAxN9u6eqzPnqe0XHUwMDFiSN3tijzRRkJwXHUwMDA17zVwz+FcdTAwMWPdXG4om2KJnnWi2YhLlbOVz1x1MDAxNTf2hD2cXHUwMDE1rSDnXHUwMDAzXHUwMDFkv5PVSChxmMFq2FO68IO8Po7Qq+TzS5HOS4pOQs9PRmyuhqOQKKre1aiU/lx1MDAxMyxHei3YNMthXGKEXHUwMDBlz1xmXHUwMDA3jdisIMWVrZJcdTAwMDaKXHUwMDEzo6OZe4w5KFx1MDAxN1x1MDAwNGxNXpQ9XHUwMDFlXHUwMDEzMkIspFGIbilcYiDYXHUwMDFjyblo0jcyXHUwMDFkm1x1MDAxOeKAQTGrXHUwMDAzcXHAMcdq+shC2JM3P1Y4af73cFxuiVx1MDAwMuldjZHF6VxyRFx1MDAxMptKP/LvY2wqjIfyWOGbwfFANFMv59BwptfQTDOcXHUwMDFhuZhccn+z5eRcYoWxaDu8YuuOXGK7X8pcclx1MDAxOFx1MDAxMa2ihXKqXHUwMDE46lwiNSijwCiLXHRyIY7dbuXcxnba80jEYvs7yXBuT284XHUwMDExXHUwMDFmW1x1MDAwM1x1MDAxMLv5nVxmrHCmOMOpmMne99dHUieLo/d4RFx1MDAxMD9vNWdcdTAwMTBKnM1cdTAwMWX9Vlx1MDAxMC89XHUwMDFhMc1SKVwiU1x1MDAwZcdAiFx1MDAwN4JcYips4mgoXHUwMDAzeWSojCM5s2XupeE26DTeUKG5XHUwMDAzyoRWqFqC6Vx1MDAwNTtMslQ7XHUwMDE5LFx1MDAxNTFEXGJcdTAwMTmbX1xuJFx1MDAwMvz84pj4kFx1MDAxNCC/adZHIVkkvcsx0vh5czWL01x1MDAxNfNcdTAwMDd5M1nY14iW0V96tetsenm421RrZZNMmEBSgaz890KA6dHYqbu7UjpcYu28SrTaXHUwMDA0z7F5RYDUwUtWXGKptPWpokdcdTAwMGWgXHUwMDE0a4Z+lYNGXHUwMDE1x1x1MDAxN5xcdTAwMTAuY5SR9qBTZY/IXtjVJLu6m2F7V9pVZUhcYpdJrnxDXHUwMDEx61x1MDAxM2aPt5yBYZ2D/d1kibSfQlRcdTAwMTg/b1ZnZLBcYpNApFeYzOixcJnwVVx1MDAxM1x1MDAxOcNcdTAwMWOkxP3uvPqsdCbg4Gylen3SfKhXXHUwMDFlz39cdTAwMWScvUStanJ5W+lwavNdgVxigLHAXHUwMDE5zsBcdTAwMTGM2NPDXGLYglx1MDAwM1x1MDAxMctcbsi+KVx1MDAxMlx1MDAxNkWEttluMYaVKsfYwDBAeGWDXHUwMDFhM1x1MDAxY7VcdTAwMWFrM79rLfdcdTAwMWJyOb1d5UZcImJlJra6XHUwMDExXHJcdTAwMTSwjYQhXCLx1kLCl6eTpMj0odxs883DXfLyfFC63ylcdTAwMWT1e49TXHUwMDE156KUolx1MDAwZSvQiuAwXHUwMDExXHUwMDE0hFx1MDAxOVx1MDAxOFx1MDAxYVtHeWdcdTAwMTLYXHUwMDE1XHUwMDA2Q2Kq49mzlW3YXHUwMDFmol1cckGkMVx1MDAxMme1KM6VJL/1LGG00mLZuFx1MDAxNXVtXHUwMDEy6Vx1MDAxNlBg6DbFXHUwMDFjXHUwMDE25yqWVpcq9p6larddbnZ+dkrF8W+2bFJTp9xcbn2dR7rUjKpf5jCmfDKnXGZpbm1dXHUwMDBmfvXPa/fd44vtl6Z4gGlcZoLN1LZ1OIywXHUwMDAxXHUwMDBlJlx1MDAxY2Y/KmzLiJdcdTAwMWbJosRBwGulXHUwMDFkrpGSKS5cdTAwMTfF+rLYg0ZcdTAwMDZ7YJhBXHUwMDA0XHUwMDExXHUwMDFmXHUwMDA3ynXiWrGHkdlcdTAwMDfPXHUwMDBmn6lBWD/cKM6xboe7l4+a7lZa5cpg82b7onL1eFcv6MrBVUy9nFx1MDAxOL/tR9VQXHUwMDAzXFyIXHUwMDA0RVx1MDAwNcmNlzBcdTAwMTN13Fx1MDAwNFx1MDAxY7Cng1x1MDAxYm+H3cR57oWmJmpqllx1MDAxYdRcdTAwMTZcdTAwMWFcdTAwMTlccrFcdTAwMDVyZDSKxkeejFx1MDAwYjs1c6epJ3ZcdTAwMWUrbvMhmHE8d1xuXHUwMDFi28ucXHUwMDBlXHUwMDA0vT0o7Jw97YrCY6l+L3dcdTAwMTivXHUwMDE0N6J6m8AhXHLRjrEn+FwiIbF+NKS6o/NAcaao3fGKOVx1MDAwZnRUXHUwMDBihWguXHUwMDE0j8u9mPVZYN/hhJRcdTAwMWKa4bxfJrVdkolcdTAwMWX75XVFJfJHe667srvxXHUwMDFm0eK3XHUwMDBisbU6NrZ3bju1lcu14+vm1vUjOT+UVb9wbEhcdTAwMGW/9ISU9H6m+DhcdTAwMTRvXHUwMDA3XHRcdTAwMGZw1Fx1MDAxM2NcdTAwMGL/hPWEXHUwMDBix1x1MDAwNjhcdTAwMThbO5CK4DFcdTAwMWLveqJcdTAwMTZVXHUwMDFmP6FcdTAwMWLn0+uGXHUwMDAxZcNcdTAwMWVlZHF6Ob1MXHUwMDEwTlx1MDAxYlx1MDAxMEQgM8lJ+vRcdTAwMTEpPztNS9Vq5Yqbh5f79GmY6T4n5rSUpZj+5+P/XHUwMDFlaiedo6fHw1bhrrzz3KtcXJfPiyeZ/Fx1MDAxZqNSXG5qfZfSY9hcdTAwMTXVnlx1MDAwMOFGSiOIXHKjjTvm3lx1MDAxZXIrUepcdTAwMTRIyoMxxz54lcRRjNlcdTAwMTRcdTAwMDdGSDBbYaHpYU2/yIBl0Vx1MDAxNiOxMFx1MDAxMMkztC6SJFx1MDAxN3tEsmqPdfzYYUhpbnBzbadQ7+5Rt1A7unIrVd1cdTAwMTBcdTAwMDVcdTAwMTPvXHUwMDA2M5XCOrr/Je5uh8f99v7m1uauW6qyJzmH7jW9n2nuXHUwMDE1jENcdTAwMTQh9uBcdTAwMDV7SlVcdTAwMTiGauIwbrNcXEBcdTAwMWGjILrwq6mjJ1x1MDAxYzPNXHUwMDE3zjVR5TIgT1x1MDAxMIYjXHUwMDAy4rHeNaqIvsrZwzKk/tCGcKhcdTAwMWazZZA/O+1u9b5cdTAwMTXvY1x1MDAwM5nnf4ePneDXUlx1MDAxOObS+CDycbTp1i21Yoo9tkghtqLEXHUwMDFlXHUwMDBmKGDs1Fx1MDAwNiC2XCIn2mUuJFM05iR5wbVcclx1MDAxM7FFKFx1MDAwNFx1MDAwZorRSMGZxJ/QNqLLxt4gzcy5ojKyTPvJVdm/ILufZtimtCVS7SuNi1x1MDAwMFx1MDAxNjKygjTa5/GssJjJWYOBPae5SO6PSt2kx5MkOf/WqHSEMdowZZNkkd5Eyz/NQyWu8YR4qUKBczYjXlx1MDAwMlVeqI1cdTAwMTEmXHUwMDFhS5MxXHUwMDBlJd2ufmZcYoZQLkBcdTAwMDD4SYXvI2A298FcIlx1MDAxNMGViqbS/O1J/Ukw9s1OhNKMX/s0NuQxPUiV3ZD0vo5cItJamiBPajxcdTAwMDXuRupcdTAwMTWEXHLDWMGCSfh2cEsutsrlo9OXe1edNjZpoSdcdTAwMDdRXHUwMDBmmFx1MDAxNq5cdTAwMDPcXHUwMDAyITBcXGkxXHUwMDE23yCUXHUwMDAzzNZTQSaqKYkyzTlcdNf5XHUwMDFly61XXHUwMDE5QC+1oas0elCRXHUwMDA3emky0SRcbmVZqC+Pd0hcdTAwMTHpXHUwMDE3/VLq9s5cdTAwMWFcdTAwMGZ8d4dcZpvq7rh2dPdcdTAwMDGRXHUwMDE2ilj9XHUwMDFji0BDYmYrRVx1MDAxYeWleFGIXHUwMDAyuzmR6W9RTZ2WM8i04XZeoqfOvrK15Fx1MDAxMDRCXGYxTMCXXHUwMDE33HhcdTAwMTXqP96s/XK51ztcdTAwMWVikyPPv/zQdFx1MDAxZleSgfxcdTAwMWZvKmHn2vXKXHUwMDBl/PXHX/9cdTAwMDPhIXiWIn0=ADCinterfaceSGPIORX DSP chainTX DSP chainADCDACMCUSPISPI controlregistersSGPIO andflow controlADC clock domainDAC clock domainInternal clock domainFPGATransceiverDACinterfaceTransceivermodule \ No newline at end of file diff --git a/docs/images/gateware-ext-prec-rx-dsp-chain.png b/docs/images/gateware-ext-prec-rx-dsp-chain.png new file mode 100644 index 0000000000000000000000000000000000000000..6dc4833047e0d36ad14078439d60bc4cde95db25 GIT binary patch literal 70790 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 IWBcI$1SF;TBme*a literal 0 HcmV?d00001 diff --git a/docs/images/gateware-ext-prec-rx-dsp-chain.svg b/docs/images/gateware-ext-prec-rx-dsp-chain.svg new file mode 100644 index 00000000..af05bf82 --- /dev/null +++ b/docs/images/gateware-ext-prec-rx-dsp-chain.svg @@ -0,0 +1,4 @@ + + +eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1cXGtX2tpcdTAwMTb93l/h8H6tOfv96DdcdTAwMTRbrbVS0YLee8ZcdTAwMTlcdTAwMDFcdTAwMDJEIGBcYr7O6H+/K1x1MDAxMUnIi6Dx0XOkY3RAnjt7rznnmnuv+PeHjY1N73ZibX7a2LRu2ubQ7rjm9eZHf/uV5U7tsVx1MDAwM7tI8Hs6nrnt4Mi+502mn/74IzzDaI9H92dZQ2tkOd5cdTAwMTSO+y/83tj4O/hcdTAwMWb22Fx1MDAxZP/cXHUwMDAxXHUwMDFi1UjnR52cWiNcdTAwMTfzztGX47NqcGpw0ENjXFyr7ZlOb2iFu25gO1XIUIJhJTinVCC92HtcdTAwMGJ7XHUwMDA1XHUwMDE2hmSUK0RcdTAwMTknSMvF3mu74/X9I6Q2JMaUI6mJxpoujuhbdq/vwSGYcINIjCjVcFx1MDAxNVxuX1x1MDAxNsfcN+jTXHUwMDA2WmyZeu54YO2Mh2PXb/V/sOX/XHUwMDBi29wy24OeO545nfBcdTAwMTiTd1S3XHUwMDFiXHUwMDFl07WHw7p3XHUwMDFiXFxcdTAwMTk6XHUwMDE5OnQzdv3GvPUktj3rLLhhr+9Y0+lSW8dcdTAwMTOzbXt+P2FcdTAwMTRu9Vs32e9cdTAwMDTj9WfYJtdcdTAwMWNZ+/6AObPhcLHZdjqWP1xmmy28fDunM7/d0uFTy/IvgSXCkispwvaHscVcdTAwMTCOb/0+doI4w0RKIbnkLGzAtFxuXHUwMDAx5lx1MDAwNZftmsOpXHUwMDE19rTfiN1I8IVcdTAwMGYzm3RMb95cdTAwMTKFkIBBZUiEXHUwMDE3XHUwMDFk2s4g3vThuD1cYu9cdTAwMTJs/fUxLZ57s9aoWzl1XHUwMDFiqGI3XHUwMDFid1edm+Z1L1x1MDAxOc+edeNlhTLTXHUwMDFjIyaWQlx1MDAxOWLYkJxzLVx1MDAxNYFwXHUwMDE2XCJcdTAwMTHKmDCDUfhAwCOuuGbJWCa83Nj1XFzTmU5MXHUwMDE3+vh3j9/RXHUwMDFh8UuEkppTiVLiVyiaXHUwMDE1v5Ip4Fx1MDAxOVx1MDAxZVx0+1dcbt8wXHUwMDFh/SiExz9ublTrtY1237SdyECOXHUwMDFkr27f+U0naGnrZ3NkXHUwMDBmXHUwMDAzhl26VGVo9/w+2Fx1MDAxY1rdSEBAN3g2KMNitzeehHvbcD24reUmh2fs2j3bMYcnuc00Z9742JreN9RzZ1a0J6y9XHUwMDA1j1x1MDAxYoTn4PZscHg7Q9VcdTAwMTG9uFx1MDAxZTc+XHUwMDFm1Vpm87BZWIc0Jlx1MDAwNlwimkqCKVxmXHUwMDA2XHUwMDBmh9jvJSmlIbVcdTAwMTZUXHUwMDEzXHUwMDA0YYPDvVx1MDAwZuBV2Fx1MDAxMIJcYo65XHUwMDEyRFx1MDAwYlx1MDAxZPbrXHUwMDAyu1x1MDAxNFx1MDAxOVxiXHUwMDAxNSDKXHUwMDE5QJxHWHA1lpVQllx1MDAxNvk61FxyPr87jq/Tcfww2uFQ0vmWX3F4c4A2YErJaFx1MDAwMD+gm2SiXHUwMDFiXHUwMDEzXCKVxlH8r1x1MDAwMe9F88KGZqlFJGhcdTAwMWLt4/rB972qvVe56Fx1MDAxZrpH9i69+2tzcdyvj+mXvT+ZnDZPyOi0zr7efLeOr26mXYYry3d5uL/puuPryHXn37I4iVx0TVx1MDAxNWZcdTAwMTF+fJKkpj9lXHUwMDAxSdVEXHUwMDFhWjNEIcOD9FxysWVUKmIoRDBmVFx1MDAxMa50XHUwMDAylFxmckNfUKmfqzDBVJqiondcdTAwMTSmoPCmuJpcbn9kqMA6XHUwMDA1bkwlti7ghlx1MDAxMFChZlx1MDAxYT9cdTAwMDZuZUZuQk13XHUwMDFks1x1MDAxNVx1MDAxNYiIjmKxtHWVjrahvZabo6Qju9OJ3mpZTFeJWlxcX+Ncci9HWe2rs7Z73WPfKLOP7trbtdu2OCmsrEoxcGhaXHRcZiktwTrm8LgwYKNQioA3k1wiXGaGRVpMOXBA5Fx1MDAxM9L6XHUwMDAyxFx1MDAwMHRcdTAwMTY9RIXDUY7F+2eA+naNXHUwMDE0WSsmMWRDaahcdTAwMDanknB+XHUwMDBmsOaQ43Ck9Fx1MDAwYopos950K4NWt1t1rratXHUwMDFmg8Fs0Jq8gIjmXrd+cHgzkzY7bc+8Kq3v3V5cdTAwMWXJy1x1MDAxMq77rXV8e3nuNr80rquN8eR0cnP4xStL9Fx1MDAxOVwiiNEoSTxe9NNHpYjoY5xHXHUwMDE4Qlx1MDAxOdDSXHUwMDFjwlBhbP6TfHP4oOWSwl1xUoBUXHUwMDBiLDBcdTAwMDbrnEZcbogkNi+0nvpcdTAwMTTPXHUwMDExXHS5u1x1MDAxY61nXGJcdTAwMTH2JOdcXN3ZaPmHlOGan6r2K4Q2rvbJppej907vroKtXHUwMDFkfjGhSDly9HnQ/bZXWO+Fkr6iz+FLYjn7g95rVKbeh4e8y/1cdTAwMDLZXHUwMDA0rYFs5HtmjlGaZ4ZdmcDmgGtcdEP5cmo/aX5v91x1MDAxYs27Rnt/n8krp7ZdZWdF1bN71G25jVx1MDAwYqfDL6fyYLfROoFcdTAwMTF6XbVfrcpUc0jEylHl9N4roMqSXCJcdTAwMDODXHUwMDEx19yfsNRcdTAwMTIvw3quytmw5sggK734u0pnYVx1MDAxOa+RumPEgXlcdTAwMTlPrMNsXHUwMDA2M5mZWNbUXHUwMDFmXypcdTAwMWWVuj+rIT+0b6K6+npcbr1CXHUwMDFh41xuXHUwMDFka3c58jz7flBxZ5XmUbd/dT447rZofeBcdTAwMTSWZ0ZQJLsmkcXQYnacIG6g6CdMz7Pt+Ls8p0CarDHFxqnyeTfVjDOWWMZ6gDSBMSScs/C8Z5fnqz103t/+2mh1f17QqkNqfFTfem15lmdnPycnP05ml1x1MDAwN9Ci3Z81+vlgtzzTrJSQUSg/Xp7Te6+APDOq82C90jRjkZJmv+txJnjpOqvNjFx1MDAxMK0pTdNjTJKL0OGCXHUwMDE0435dXHUwMDBiKts1P12Qq1bbXHUwMDFlmVx1MDAxZTR2Y+qZPWv6XHUwMDE2xHmFMCbsc/YzlCPU+VOLMcZZQrPmxFBcXHJNsUIkOt9yv1x1MDAxYY2XVqOTaIZUXHUwMDFkXHUwMDEzJVx1MDAxMCQpWlxiIWlEglx1MDAxN+hcdTAwMTbIgOCCfF5z0OiIRrzZdbCOOe1bL6zSbFx1MDAxZKBcdTAwMDOsqNJpOGfJrYtcdTAwMTlzzZRmUXEvcW6MRyO3KMonYzueXHUwMDAzhN82wq5cdTAwMGV+LL7/+TH16K2cYFxm9sfjMLxgQntcdTAwMDGnrrdccqNjO73NpZqAeZVikVUzOHo07lx1MDAwNFx1MDAwMTV2W3Y0v4AgvbE6Nf/xl55cdTAwMWVcdTAwMWXT4CgsbIs9+PxbWIhgOZ3Vrcyf7SuplXg5WYeQyGl20L1cdTAwMTWfjvqWmVx1MDAwMFx1MDAwNTxUdFx1MDAxZqi5XHUwMDFk81x1MDAxN9awNb5OiaegvXWr91x1MDAxMLXL4PFvuj+tT4COzWHKTTP3jcZX1qF931x1MDAxMdOG7fXnwCiQZ+1/nX3bObn2zveG28dfL88vqtK9TFJzhoeSSFx1MDAxOJhLTSSFeCZ6udLPL1x1MDAxNlx1MDAwMlx1MDAwMHKNKGVcZstcdTAwMTCCXHUwMDBm9KyZoZHiXHUwMDE4ICE5XHUwMDExJLVY6O3T8St4Jv7k4iDFKVFKpk50YsQyZ0eIX/cnMX1BK3Xa3MV1tFO7MpuXuq/GX4Zd77So5dlcdTAwMTlhd3+G735sfz2vkcb0wu10XHUwMDBm3mJxUPpTXHUwMDE2sDwwXHUwMDE4hoJcZlx1MDAxOVx1MDAwYoxcdTAwMTRcZkysZC8oXHUwMDBlykOh1Fx1MDAwNvcnJFx0mFwiiFx1MDAwN5RSOv5eXHUwMDFjlIpCUTwnooQz8KXpcCNUZcFccmNIXHUwMDE4YGRKXzF8uvep9c2pXHUwMDA1lsGKrGa/XoXQXG4li5uetMaX43byXHQnz+1ITlxySUBcdTAwMGKl0r5PTrqdfCBDjoM51YJpKZAmNGKI4maHaUjPpcJ8nZmNf5HbkcWRzVx1MDAwNEHCdzwpyFY5NfSKXHUwMDBiTVx1MDAwNH47VX+lup3sWPQ/W4kwTGTja5udfFx1MDAwMnhps5NM7f9cdTAwMTV+Yq/RPN5pnHi1o2mjentTV2fbzXHxNVx1MDAxOaZcciRcdTAwMTBTWEmCo4XroZ/QnFx1MDAwNS9cdTAwMGZA8pokQMZccq6IYEiAq1x1MDAxMJHpolx1MDAwNf/x90wmje/Uk/1cdTAwMDRcdTAwMTZcblx1MDAxMc2oTJv10dlLM5hcYimYovxcdTAwMDVLJyr7jf3rn6je2Fx1MDAxN5O9mTPtXHUwMDFkkT1eeFxypfZcdTAwMDVXd2rNu8pgq+38PK//dXzlvEVDkf6UXHUwMDA1XGZcdTAwMDVcdTAwMTNcdTAwMDKA5s+4alx0Y0NTXzfIxSFcdTAwMDZ6XHUwMDBmXHUwMDE2PJlkmKTlIexcdTAwMWSHaTjUa8yySjB8XHUwMDFhYiZ1OVx1MDAwNdHsXHUwMDE3XHUwMDBlqNBAkoi/ndTjwVJ8Ntve2J1++p9cdTAwMDPuXHUwMDAxPNPHXHLBPlx1MDAwMkWot+AwVmhb3GFcdTAwMTR4lnJcZkc+IeVcdTAwMTlcdTAwMGVOsKFcdTAwMTnnXGa0UlxuLmI4XHUwMDBmXGZHiPPkm7rI8F82p0RcdTAwMTDwI5gqnlJx7Gd6imnIXHUwMDA2NXhcdTAwMWKy1kvn/1wiw1FZXHUwMDAz+FpcYlxuakvTqo+5znRcdTAwMWMwxkpcdTAwMTCFy15feSOOIztcdTAwMTj9z1ZcIlx1MDAwZVx1MDAxM5n82o4jn1x1MDAxMN5cdTAwMWTHiziO/FKZPPZcdTAwMTNcbudcdTAwMTVoK2RoycBLICA/zpKvOoNZSTFcdTAwMTjofqFcdIPrlVxc+G/RXHUwMDAyWOU6rzhcdTAwMTer/Grrtv7ds53tNUiPXHUwMDAyov0107Rkh2TnOkQyf4qBPWb6dKlcdTAwMWQp1ch+Pcur095WNFxmg1xyXHUwMDE5XHUwMDEx+HS+yy/6fFx1MDAxY98tPWtAfkX5LquV+dUvj2RlXHUwMDFk+6zV7JLJOIdcZvPL7/PIUEmV97LZnFxmqWJ+NqiSlTaxUlfOVlIj4lxmnD9cdTAwMDTnOzemceNOcW6UfsVcYqMsjVx1MDAxYiPKk1hZ4lx1MDAwNJJ6Rlx1MDAxZjPxkk+Nb6XiJjsog/MzwvHpRPlcdTAwMWNcdTAwMTUt5Vx1MDAxM+Uz0HmyyuY1ibKM9DL/NeM8RsWIiGgpclx1MDAwNqFmXHUwMDE1XCJLf1Ffcq7nPZnGp+/EmUKcu8WJU1x1MDAwMN6ha5OvXHUwMDAy+YKY89fbuO9cbtSzXHUwMDEwp4j+yaXXXCLOzNiLnf2G+PGJzLOGk07jnWVmWmatwslb/stcdTAwMTl5VMMwy3vpYTXXMFx1MDAwM8KOMTzvupRXXHUwMDEy37kmjWs+r2FgfTj5r0CkOlic/SdcdTAwMDNcdTAwMDQlkj+HgWWYM1x1MDAxYy7evFqWllx1MDAxOX2x01x1MDAxZss2z2FcYp+LXFzKcIZcdTAwMWbmfbRpTiZ1XHUwMDBmxnzRN5tXtnW9nT2J/mHealx1MDAxZo1WsP7768Ov/1x1MDAwM1x1MDAxNahInyJ9RX DSP chainEnableDC blockMixerDecimation stagesPhase stepFactors:16, 32, 64, 128 \ No newline at end of file diff --git a/docs/images/gateware-ext-prec-tx-dsp-chain.png b/docs/images/gateware-ext-prec-tx-dsp-chain.png new file mode 100644 index 0000000000000000000000000000000000000000..d0a971902e8a42668aa4f1a0fa997d104a26b6e3 GIT binary patch literal 51198 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/yxjrxXGaLwVAwSyolW42tIEtGkiFL8e9zWzZabNlcdTAwMTiGQCZcdTAwMTNRRcm93r59zrm3W1/XXHUwMDFhjWb6eWSab1x1MDAxYk3zqedcdTAwMDa+XHUwMDE3u7fNdVt+Y+LEj0KoXCLZ7yRcdTAwMWHHvazlIE1Hyds3b4pcdTAwMWVOL1x1MDAxYU56mcBcZk2YJtDuL/jdaHzN/kON79m+p8zEvJNcdTAwMWOP37VGZ6fB2a3Z+bibdc1cdTAwMWHdXHUwMDFiXHUwMDEzm17qhv3AXHUwMDE0VZ+gnFHqaMFcdTAwMTjHSHHKXHUwMDE5y2s/Q60m2sFcdTAwMTRrLrDgnGic1976XjqAXHUwMDE2QmpHYkw5ktBcdTAwMThrmrdcdTAwMThcdTAwMTi/P0ihXHQm3CFcdTAwMTIjSjVlnMJL3mZi0NtcdTAwMDbKS5I0jq7Mdlx1MDAxNESxtfpcdTAwMTds7F9hc9ftXfXjaFx1MDAxY3pFm8vLnu7pos2lXHUwMDFmXHUwMDA0nfRzNjI4XHUwMDE5XHUwMDFj2pxcdTAwMTn/fGo9mSlf1Fx1MDAwYibsXHUwMDBmQpMkXHUwMDE1W6OR2/NT6yeMilJr3ajlZfv1vrApdoemZTcsXHUwMDFjXHUwMDA3QV7sh56x29Ds4s3KdKE3na7SPDHGXHUwMDBlgaVcdTAwMTaKXHStZV5TYItcdTAwMTEyW3pcdTAwMTiFXHUwMDE5zjChkjKCS/38ZFx1MDAwN1x1MDAwMJZmw166QWJcbk9bI3ZL4CtcdTAwMTYzXHUwMDFleW46tURqhYTmXHUwMDFjoWLQwFx1MDAwZq9mTVx1MDAwZqLeVTFLVnq3Xofnrcs+v1x0qdy49kLe0ofph/Pj7sp4tnhcdTAwMDSEcSVcdTAwMDRRiGNRxbOQXHUwMDBl4JAjzlx1MDAxNEWU6Tk8XHUwMDEzxFx1MDAxZFR+9DygmXaYLj3q2fFsn/86nvdcdTAwMWWBZ4awJKRcdTAwMWXPXHUwMDE49nEhoCVTXHUwMDE20SXZeVx1MDAwNKCnXHUwMDE1XHUwMDA1XHUwMDAyK1x1MDAwMEvNpzRfQlx0n2r3ZGe/e8A628Ptsf6yd9GSO2Ezb3e3Xj/spPPg3UmqWu3u79t70YfBvt44P9w/r85yP79cdTAwMWLH0e2q435s6+OtUXRxe33UPVx1MDAxZW7GbX1w/G61cadvL8Tueu/Nsbvi/Fx0sTV2iCU2RCpcdTAwMDJokVViXHUwMDAz7YlaRmyskKMsoSmoguRSzfOa8Oclclx1MDAxYbthMnJjwNtLkVx1MDAxOX8jMv/6XGIyXHUwMDBixJGGXHUwMDEwVUtmTOaK78lMXHUwMDEwg1RcdTAwMDNpwZ9C5ufEb1x1MDAwMUdcdTAwMGJDcEArTE08ilx1MDAwMjdcdTAwMDV7XHUwMDFiSer2TVLa0ihMO/6XyVx1MDAxYSqle+7QXHUwMDBm7Fx1MDAxZYjKkJuB37feaPaMXHUwMDFkuFn2SOpDXG6YN1x1MDAxOPqeV1x1MDAwZXE9XHUwMDE41PVDXHUwMDEzt1aJlVHs9/3QXHJOV1qGO06jXHUwMDEzk0xcdTAwMTaSxmNT9ph5l6dzXHUwMDBl4UtcYv6ZXGbD06M/bvg1XHUwMDFhjLzNXHUwMDEzwlx1MDAwZY9OVyF4KVx1MDAxMyVcdTAwMTJpVcDfOlBp6mBGtaRcdTAwMDJcdTAwMTjORVGbXHUwMDEznFCHllx0XnDhR2L4t1xu1789guFEMK0gXHUwMDA15WXgTlx1MDAxOS5L6f1stFx1MDAwNiaCRENa9t1cdTAwMTH89KKx02k3elx1MDAwMyDXc1x1MDAxMDswl+lcdTAwMTJap9FoXHUwMDExpytWz1x1MDAxMrjezOchbnt89Sc6iL4kvyc4XHUwMDE4dM8vTkQsV867JVKOwoRcYiFcdTAwMTjSxVx1MDAwNk/gqSA6cySYkJRcYlWKvTl5XHUwMDE5d7hcdTAwMDJcXCFAlcXJPHd5sVx1MDAwM1x1MDAwZnNXXHRltPg/pNnH9bwtcsT7naPTkrvC+Hs6w2FJg/d1XHUwMDE5sFM6g9YuojOnQlx1MDAwMp/Jk9j8tNw7PPvY9sjw7GDbhF2yMVx1MDAxNPu7x19WzZHjLd7ekPuD8Ijs6tPt3vl1lOx9jzly/SpXXGKhklxih8OWKCmZYJKRXHUwMDE5XHUwMDFhavZcdTAwMTBccjFyUHakhd6Y4JpcdTAwMTDKftKwjoYnq4dPgimmXGLcX8c3jcRcIr5cdFwiJIYkmTyFb/XQhVx1MDAwM5FcdTAwMDBcdTAwMDJDykRcdTAwMWVcdTAwMDHduei55/bSKE7e/lx1MDAxZGKx3qBkvSHYutWV2lBaupN5iVx1MDAxY/mBuDZcdTAwMWJiV1jL88Tb5XI0I0VVmivsXGJtOcwg2ipZ4GhcdTAwMWFt6Vx1MDAwMzRHjr2shUrCsMSIlSCV81xcgFx1MDAxMiimXHUwMDEx5lpcdTAwMTJKXHUwMDFldWn7SrT33GRgXpj3nUfwXnFifVl3LubzpXnWrCiVXHUwMDEyjjWvfmmbXHUwMDE3j1wifzaMXHUwMDE3b43C19mP/P39en3rhWC0z8ZcdTAwMWNcdTAwMGWL8eaiMpxo43RcdTAwMGJ2x1x1MDAwZvvVXHUwMDFjaPpcdTAwMTFlXHUwMDE1RYDWw8jLXHUwMDAwXHUwMDE1xV2/nIdcdTAwMDBIP1x1MDAxOa9tV19ZvF1cdTAwMDT4XHUwMDEyL1j3XFziZUKvsLJcblx1MDAxNbuCTUv5gXHncFx1MDAwN/3KdXD89GdScVx1MDAxM3Sj25pcdTAwMWTLXGbvmP49LuYnbSWdken5blAz6cK6YXRjXHUwMDBl/IlHknM/XHUwMDFkTKG3QpKz/CZ0mfyBrFWu+GduXHUwMDAyNXZAIC2WtMKKXHUwMDE1upVf8UvhXGJE4PyqkKZKXHUwMDE1/XPxQ1x1MDAwZSWMXHTFYFx1MDAxY8GZZj8/WdWJ3+nq4lx1MDAwNykl4japrFx1MDAxMT86f/LIxY9Km6Dw0oXuMyQ9TGjYUy6LjOO1xG9jIVx1MDAxOLPaeVx1MDAxY/579Vt+Z/g09ausOZPC51G/OoWramBVqqrqt0R9ln8vWaY+XHUwMDE4c+5cdTAwMTBFJLdwXHUwMDA2LapRXHUwMDFmxfjkO0RxNM7FR3BcdTAwMDdLjWG/IXmDo0Dxwb1Qn59iUyM2Z4/ItFx1MDAxMFdYXHUwMDEwKetOWISw2dJ7tbE5NaGodOz9wcRmIfhm+j+kLbNcdMKrqFx1MDAwZa5+qEeokjg+KEPfRGzWpk5ruqNRJ1x1MDAwNVx1MDAxNOQuad745nZr8UFobWqbZafJbvDu1u7+XHUwMDAxkVx1MDAxMOB/In0=Interpolation stagesTX DSP chainFactors:16, 32, 64, 128 \ No newline at end of file diff --git a/docs/images/gateware-halfprec-dsp-chain.png b/docs/images/gateware-halfprec-dsp-chain.png new file mode 100644 index 0000000000000000000000000000000000000000..a0b6c5ed1feaf3eb37319391ca81f6e431238458 GIT binary patch literal 79385 zcmeFZc|4T;|2ACqB_vWLq%4&s*^2BTB&4z%k}>vu4^2X)QfTaZ_HD|(Q%Pd%41*Eb zx3Q0HW}eUVyT7mB{kvbT=lTD+uRpH2RCAur^Rv9)@8ftM$LE#)JxwMCE{0>rjxlN7 zxqbiGF&dF$$0)PuXu#hPq21lbj`1DSx~=lS&vJR3wtDdP6mi`^Bv07K!iYMy!rH{- z>dvo%YMJF~hMfxE)~|X-&6eo!lGo>3Ytvu8X7IVK&U#wC75jmopYG7Vj5LO7N8J=^ z*Ydb7G~v&~yN!KveXp}-E>+$Y3QfJ~8JWTN>=*^zKYe(!$44}0zS}qFI!<*``Pk7P z+{Y;xE$RO89&~*AzQ-vdYkiM1{nH)Dw+8p1{P!OJJ<9)%$NyY#|BlBI;T}hd zKH5%w7_qLFJuw1{{<%qEGG3n3IJ(ugc9knkLHS@WRM-pxB`PL?6%)Kqa!e>Mv z3gzU`ABbzP;|V!*m$d#$Rkl53mXzXyX%{K)QLS;2e5-a;IBV7bf#_Ty3O&mxUf(nJ zbQAR%cRO@cA#g`3gs=qZ=%QJJ{7DaGzP@idaNVl;nCJdNu53!>uPcS|c4bd;6EwPD2 zz~wwFd}OO<5CTKqav5=NJD;L_=ZF904UF42CRCIXab9YHC1f|$e{UwvX2i~2&oJk% z#5vdc&*0CHL@{)gE5x(;IPDX6{}T97(Ofx$icW+pz0UFcun+x^nX7_lzpm`f(4Rz) zi6Hzfz6u*FN2-K<=b*U?^0gEWQk$wBZQQ1h}59ALTKl)ut zMr!7n)G^XRZdzih3rnpxN~(IPr0#Achp2XAu@K64*qU~ey!wJHC|C6q_db=CKd87K zwC`1Ruv#aZB$1x&Q+~^EEc$`*A4{d>{!LQ~ZAr=05aM2QPKUS%GHoWP?C?du732Y~UD&IaW zp#(O)TN2Bnp4>+#)EDf0NILqDbKed_cM=6MN;W9;hN_x~T`k2FivxjDIc?4{)+o*nmQVG^fw4 z9J2$4dd@>l+fp-_BQv(t>tk?r%B?5EDhKIigKX1@Hz!vw|K{0j7fL(pJiRQ=Rjoie z+|+4&+9}Z%yR0**GzVV6rE>X$j9BAj_&Ep5oIFGzubfw2nSZtK(92Pum5R^pf=N`3 za?cTteLrRh``e5aknab$CfD-hp{yvy#eq_jD~?5{VZI7j_tjF}7{t8EQ^ufR~rSp#!anIRK z2~BSI7rjvy7SY?urm~P@8*|idHk%}*Grp-wIIW5vWJmAA0e#uHd{bEUjLQ>I~RU5knXBbya;up@x(CCoQQi^K@&$^2=Hl9hyZ zy2)5Ug7;=9P}_ zts}OutnzvIaby_ueYQ+0x@P7zMvNBKwcefB+bB`nh~SNhS?qN;z?`i8%>DAHmwuK_ zLB)aaNv;~vI)TA~!4r@?@zv>8G1I4mofR{Fznu{~c^!?0z1bubRLH4`u+tUZqY`NI zeI@+T>UhHrSecHi)qmba=hUzNIHysjSwHifQS2Ith;K@2{Pgyx^b&u2^lhsQPBBxO zUuHZ3^A|SbnhvvPa~w!asMubOWgwD{NPSkioAT_ z$K6N@`UVc^LiL$0d7PIFgalGp3Tfz#0BLqd?p{aspx!SE8DGrH*dG*N>?Ck_3Ox#4W&29Oq6WB2;h!1 z#f=A|(oHzHl85*zs>VFK6=sXdx~}XcuIiq16?zy=Cn9(^{`ZN*z(~a0{vTe_*}8

    5c`^?!9B)kj6=8~j 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/hRd+CTs3DuVx1MDAwM8yUwzyEoftfLNmWsYItXHUwMDE5WYbAv3Lzv0a/XT9J73KIJWsoPIhAcnBWsohKkmvY37eH2rv491x1MDAxZrXaUvzY95b+qi15X5tu129F7sPSsrl+70VcdTAwMDM/XGagiYz+P1xih1FzdGcnjvuDv/78M3nCaYa97095Xa/nXHUwMDA18Vx1MDAwMO77b/h/rfbv0b/Q4rfMs6fMi/jJ4Gi4vdM/O+2ePXj1L1x1MDAxYqNHRzf96EzkNWM3uOl6SdNXuM4odbRgjGOkOOWMjVtcdTAwMWahVVx1MDAxM+1gijVcdTAwMTdYcE40XHUwMDFltz74rbhcdTAwMDN3XGKpXHUwMDFkiTHlSMLNWNPxXHUwMDFkXHUwMDFkz7/pxHBcdTAwMGIm3CFcdTAwMTIjSjVlnMJcdTAwMGbje7536K9cdTAwMWFcdTAwMWFfXHUwMDE5xFF4662H3TAyvf4v7Jk/SZ9cdTAwMWJu8/YmXG6HQSu5p91u6qZO7mn73e5J/Dh6M0wyTOhS5v3nz70nmetlT8FcdTAwMTfedFx1MDAwMm8wmOhr2HebfmzmXHSj5KrpXX+nNVqvfyV9ityet2NcdTAwMTYsXHUwMDE4drvjy37Q8swyLDXw6sTXXHUwMDA1reevm7h94HnmXHUwMDE1WGqhmNBajltcdTAwMTLZYoRlr+6HwUjOMMZSaiZZso7+oFx1MDAwZVx1MDAwMlx1MDAxNo9e23a7XHUwMDAzL5lp04mNlPAlg1x1MDAxOfZbbvzcXHUwMDEzqZVEmktcdTAwMTCfcXvXXHUwMDBmbrNd74bN2+RbRle/LVx1MDAxN8nzWvuG31x1MDAwN1Su3LVcdTAwMDK+o/fj6/OjxtTybORcdTAwMTEkjCshiEJcdTAwMWOLSXlcdTAwMTbSXHUwMDAxOeSIM0VcdTAwMTFlOifPXHUwMDA0cVx1MDAwN6U/Oi/QTDtMpz6qcnk2n19dnjdnkGeGsCSkWJ4xUaRMoFx1MDAxNVx1MDAxMrCKSuh55Pm5IVx1MDAxMcCUXHUwMDEwqo3j+m5jj52s99aH+mnzYkfWg/GAJlx1MDAwNDH2vsZL44Zvy7bXdraPY7Vz2Ph7fTO87uzqlfP93fPi17pRXHUwMDE0Pkz73i+H+mitXHUwMDFmXjzcXHUwMDFkNI56q9Gh3jvanu69zz/9JHDb53VyTidxrbFDXGauQVFcdTAwMTFcdTAwMTBcdTAwMTY5iWtAPVE2XFxjhVx1MDAxY2XwTIFcdTAwMTQklypcdTAwMGZrwqvFcVx1MDAxY7nBoO9GIG8/XHUwMDBiy/iVsLw1XHUwMDAzllx1MDAwNeJIg4YqxDImulxmy1xcaIWVlPNAuUrpTYTRXGIhXGZ/J4i9qFx1MDAxZnbdXHUwMDE4eltcdTAwMWLE7o03SC1oXHUwMDE4xCf+kzfSXHUwMDFkXHUwMDEzVzfdnt81KyAmXrna9W/MXFwsNT3z4qX0fMQ+2H/jXHUwMDFien6rldZvTXip61x1MDAwN160M42iXGYj/8ZcdTAwMGbc7ulUw3CHcXjsXHK+XHUwMDBmJI6GXnrGvO2xLedcdTAwMTBugfcj6Vx1MDAwNadcdTAwMDeX9/xcdTAwMGV1+q3VY8L2XHUwMDBmTqeBd8pcZiWwZinGN1x1MDAxM6g0dTCjWlJcdTAwMDH45lwiaVx1MDAxZMObUIem4Z0g4XfC92vp6k8z4JtcYqZcdTAwMTXYnzwtuM/4lih3dWx7XCIw/Vx1MDAwNVx1MDAxMjS5471cdTAwMDD89KJWPzmsNTtcdTAwMDCuKoDd9dqxXHUwMDA11nHYL8P0RK+zXHUwMDAwLu5mNcA9XHUwMDFj3l6hvfBp8PdcdTAwMDB3O43zi2NcdTAwMTHJqY1uWFx1MDAwMEdhQoRcdTAwMTBcZtZjXHUwMDAyu+BXgm7mSDAhKVx1MDAxMSqlecfgZdzhXG7kXG6BVGGResFcdTAwMTi7PFmBl7GrhPK0+CfY2EfFuE0sxFx1MDAxZitHn698Szr/XHUwMDAzzuApaZj9nF42vEtzSnxcZmdcdNa6UmQ+fW2zZYOzL4ct0jvbW/eCXHUwMDA2WemJ3Y2jp8VN72iNXHUwMDFmrsjdTnBANvTpevP8Llx1MDAxY2y+R1x1MDAxM9k+fotcdTAwMGWVoFx1MDAwM1x1MDAxOTcmMuNScK4zONTsJVx1MDAxY2Lxolx1MDAxMmVcdTAwMWZALFx1MDAwMuLx9FxulGCKKYL5L0RcXCrElkVcdTAwMWPhsG6a8/dnIW+6zTiMXHUwMDA2f/1PgJdhWWpsuaaWwVx1MDAxM1iuUVKoUFNhmZ9hKb+g3bKKdrrhVKN47bSUoaRJuCvsXGJtsMxA7abx/Kx26Vx1MDAwYnBHjlx02UIjYVhiXFzoXHUwMDEzXHUwMDBi5CDFNMJcXFx1MDAwM72QmUK3b4T+ljvoeD9cdTAwMTn+JzPAX3Fi5rLIPeYpds2gX1xiglx1MDAxOWbiNVwit3ou8PdDP6vNk59qyVSP/jP++V/LxXeXyqL5rOTEMHlfTjmDZ1x1MDAxYsVrsDh+cDNpXHUwMDBiPe+kTMNcdHB3L2yN5CmMXHUwMDFhfpxuaPtfvdahXHUwMDE5/cTgzSA4Qrhk3DlcdTAwMDPMXHUwMDBiWkkvJyXFjGDVIL7juTmxg+fSbeCG+lx1MDAxOZPc6zbCh4JcdTAwMTVcdTAwMWJ1/MS7+SFcdTAwMTf5L91cdTAwMTmc9L2m73ZcdTAwMGK+tLStXHUwMDE33nt7/vdcdTAwMTlcdTAwMTmc+3HnWfSmsHXs8VBcdTAwMWL7XHUwMDAxq9ni/Fx1MDAxYTvAj0aWTFAptas1jvNL4VxiRMCPVUhTpZLnx9yHXHUwMDFjSlx1MDAxOFx1MDAxM1xuYIdcdTAwMDVcdTAwMTeo+jD/77BtdTo99Wlccis1sfuUUFx1MDAxZqWlQX4wS5HkYDQlymdh7lNcYlx1MDAxNlYzmtaLb8V9K6WyOGrNieHi3GePXHUwMDFjzsd9XHUwMDEzQ1x1MDAxZVx1MDAxMWE13FfEb5NcZjhJVCXct1x1MDAwMEnZN1dsJFx1MDAwNYqMO1x1MDAwNJxkbsRcdTAwMWUoq4CkXHUwMDE04983LVx1MDAxMiU85ijBXHUwMDFk8LMxyFx1MDAwNTP6P0FIwlFcdTAwMWacVMBJZzOYY4grLFxiXHUwMDE4Q1x1MDAwNaRESGn8Q2IlXGJcdTAwMDVe+l05qUz2Mo+/REFZK+JNyFx0T27pIzQ5mpfY6lx1MDAxZHLS7Tq5XHUwMDE41Fl8NVx1MDAxY6qDcO3pcvUpKNhoKYnXgqZxXHUwMDE4xVx1MDAwNHxcdTAwMDBcdTAwMGVOXHUwMDAwy+y2YPBZJUNcdTAwMTgxijjIeI6YjOcpQEIwmF+gtnSB8UTBYkdMc2RyiihcdTAwMDeOe/+e41x1MDAxYjDV54VcdTAwMDO4RIF1ZPbEXHUwMDBi+Fx1MDAwYlxm5PJwXHUwMDEyo0A1Ku2IVlx1MDAxNMDtKD+6blx1MDAxY1x1MDAwN4/bW9dbu7c7X/Vnd23xXHUwMDAwrvs5OOycrq9tnZBe/bS+XHUwMDFl99TaVvFrZ1xu4Fx1MDAxYdrVwPRcdTAwMTQnKmChXHUwMDAwrn38llx1MDAwMK5WzKGaKylcdTAwMDCWWGuWgaVcdTAwMDBYXG6mwFx1MDAwNpSSqbxPw7QjqfmYVzCRviXZXHUwMDA0/YjfXHUwMDE24fB8eotBwdowpFx1MDAwYr1cdTAwMTiVz3pcdTAwMTjjTYNcdTAwMTmPXHUwMDE4mStVqUrJTVx1MDAwNPE53LlcdTAwMTG4jbSGeLs47Vx1MDAwYlotXHUwMDFip812vJqI7Omd6oDBs7Xh9tXjdeess3Z54s+eT1x1MDAwYlx1MDAxMkJoemVG88aRI8Gs4lx1MDAwNINiJTqP4feQT+vylvr1IX0xi1x1MDAxM1x1MDAwMI6aJunko1x1MDAwNNNC0ezVZEtGgzFcdJZSxZGJXHUwMDExqFNMvdh+4uOOwndt9Zntb39cIlx1MDAxN+7R6lE0nN5UZNgxhlwiXHUwMDAyjcNcdTAwMTHJiLPSjtBcZnHNJDLZ4zlxXHUwMDA2QXbSqbIpfixPp1x1MDAxNVx1MDAxNcvz76GiLmfJwdOMM5DNolx1MDAxY1x1MDAxZFxmdnuZQFx1MDAxM1xmsmzqXHUwMDAwKjdcbsVcdTAwMDV5XHUwMDFhbqjO5sHaIKQqfFqNXHUwMDA2f7+pUWh975eocbZ23FhZP4zu7878XHUwMDBidiGu9qs0NqtCt31ebcYm+IClwJZcYjua2YCtXG6CUb9Bgt1rJdBeTVx1MDAwZl7gQi4oxoX5s6w8TFx1MDAwZUyNtEjn5b2RLspcdTAwMTmY9fVaw9zyXHUwMDFlkmZf0IZZXHUwMDEzM9/1aozMvZZUXHUwMDE3+nz1VNdcdTAwMDP/PNLxlUc70+fbMepcYoNd9Vx1MDAxZLuJ1P7Qylx1MDAwNrXoXHUwMDE5vIlxsohWTm75UMpcdK7dXHUwMDE5lLJSXHUwMDAyLHtVXHUwMDE4qFx1MDAxMaxcdTAwMTTXYJxcbsxcdTAwMTWuXidcdTAwMGauVp+6nd7h54N24+q6fbPbXHUwMDFjXHUwMDA07cV18lx1MDAwMrrT+t6bu83Nk0/tLbGFVm56+497V+crjVxudTKZKe5uXHUwMDAxt31ebVx1MDAxOXySOoQqKimVmCM6WeQyUsrchmspXHUwMDFjKYSQXGJGg8CD/ChymVx1MDAwNcuNXHUwMDE5sIw0kuD6XHUwMDE1OoyYcVx1MDAwYpjhISTFa4SBZpPfnJZu/99//vdPVlx1MDAxYnT8dPb622nqXHUwMDE3VGRWU1x1MDAxN3e/XHUwMDFhbb3d7O5ds6143+1cZleaq2ir8XnFnVpbMyknLO1MrkrWic7D+qMmtSqIN6eHONec4lx0rZtCuCYqezlcdTAwMWRcdTAwMTNcdTAwMTJEcl29ut5e94afXHUwMDFlb3tbjYvL/fud/kEjOlhcXF0voFat763rXnSng/teXHUwMDE339w/tL8+tHdcdTAwMWb33qNcdTAwMGJtn1db0ZpcdTAwMTY2YGed6Fx1MDAwMjtcXFx1MDAxNFx1MDAxON5cdTAwMWZcdTAwMWG6XHUwMDE0vq1cdTAwMTk0NEVcdTAwMDLULCneXHUwMDE3leUhMKwolpKQ+WrKX9eP9pp+771Vob6gXHUwMDFic1x1MDAwZXX5XHUwMDE4qtHVvZOzzue7/k33cmd3Z79/XHUwMDExn8SbfCo0U+IgwUA2lGJIcj5ZxyYwc8xcdTAwMTFcdTAwMDVCXHUwMDBiU2IjXG5i3YQ5zGy/MlOIrrgu2n799cH9Wrr5Zlx1MDAwNt3MYY1Eusw/wbampVx1MDAxNeagzFx1MDAwNVZcdTAwMTN+VUXQxohcIpZcdTAwMTbXWaF9/GuUoFx1MDAxNnezXHUwMDFh4NpD+dY0S7CI4d0mdc5cdTAwMWNcYpJO5lx1MDAxZSVOIP5CPlx1MDAxM3KwqYOERVRaXGKNMC3IaDI1XGJSa440XHUwMDA3+5pcdTAwMTP2XHUwMDBiZDS9RS3Ml1n2qThlSqNCXHUwMDFjI8sxRlxmXHUwMDAxiLV4P6HuapMvLfI4as+LYvLKnO08ZU64Pfei9pPrYcp6aVx1MDAwZt9X1Mt8uqil2/+cYp2jT+s+2vXq3Wh4dsc+3fH11bDAsio9I4CZ2KY2me5cdTAwMTRcdTAwMDPuJzna5Jxqjlx1MDAwNdZaUc5cdTAwMTP+XHUwMDFkXHUwMDA3QcD+MlXNnCmEwfxcdTAwMTJcdTAwMDWZMVx1MDAxZqdcdTAwMDRcdTAwMTQy8m0xI89ySlx1MDAwMMVmd5FcdTAwMTduXlx1MDAxMExLqVphXHUwMDA15DWnMzXuX0F0YYWc7+1cdTAwMWNHT/XHK1x1MDAxMoWXfuvk6SxNV1lTf9xgXHUwMDBmWlx1MDAxY7VcdTAwMDdb267Hn64/0/OGqp/fXHUwMDA3XHUwMDE36j3uMdjHb9tjwMSh0lx1MDAwNC0kpzqd6TFOMrXiXHUwMDEwa+VcdTAwMTRcdTAwMWPQ8XEuQCH0utNcdTAwMWJD4FmC36h0LtvMXGYpjaDsuVmKw3yiKlx1MDAwZs6qaEdhXHUwMDBmdLGpol977LuDwfL32Hxt2P/xUyt8KHZ2fnLa6Vx1MDAwYoot61x1MDAwNM02rGqcIzsv2Zwjo1lcdTAwMTlh5rRcdTAwMTYhKUZcdTAwMTm9a3wju951sGJwh2ImNX1cIvCROyWAUHCwXHUwMDA0YrNcdTAwMDQ5/kGeUW+GJCBcdTAwMDGeqix0jLBkpVx0qdRcdTAwMWPlIVx1MDAwNHpcdTAwMDXHiMi5kswrrkorXHUwMDE1xlFzTlx1MDAwZXN2+8xukZ1cdTAwMWJ+tlv0XHUwMDBmPSagcY3r9Vhuy8ujrcbGWexdf5LTXHUwMDFmXHTMgeOYIJRIpqUmkmfCQ1x1MDAxOI3OXG5cdTAwMDDsXHUwMDEwhNXH8WTVXHUwMDExXlCB46E4XHUwMDAzNFx1MDAxN2cyp8srcuVthFHCOa58XHUwMDFi1ttcdTAwMTen3FtcdTAwMGKlqIefLlx1MDAxYfuPO/Hd+uKeR/9cdTAwMTI3TlZ2j46C/WPqtY+e3G1cdTAwMTlX6HnQ1E7YQp6HffxcdTAwMTbPg1x1MDAxM2SO7DCuO4a/uVxiXHUwMDAwf1x1MDAwMYZcdTAwMWank82Lw3CWLEVEwPJghcd7YipLM5ukyXug88HtVSyPX+Vwslx1MDAxN5TbW1x1MDAxZU5m5ySb11x1MDAwMfB0sJFcIqE1XHUwMDE2XFxlvVx1MDAwZWbHOnI0ZVxuXHUwMDEzxFx1MDAxOFwiWOmiYF/mTCj84XRcdTAwMTRivz899lx0XHUwMDA1L1HwwpQnbUG+xliZqqHqoT+b0nolp6NcXFx1MDAxOEfNOTnMXHUwMDE58zM7XHUwMDFkdkr4cDp+itPRf1x1MDAxNP2VePf0yX98Wlx0XHUwMDA2N5u0j6Y7mVx1MDAxMSNcdTAwMDXESkyxXHUwMDBlXHUwMDE3xnGYZD+JmcNMXHUwMDFltzJcdTAwMGWJkoV53FgrRTHlXHUwMDFhcEVcdTAwMGLYb3S8XHUwMDA2Zlx1MDAxOFMwpaSpXHUwMDA0Rlx1MDAxNVdo/EapJXfT+iEkh4Txb9xcdTAwMDHvXHUwMDBmVlx1MDAxM1x1MDAxNUVnYaFF9vLYXHUwMDBmMedzmi2vytlx7pyTStmxVFRHT1x1MDAxN1xi6fTsWH500Ls/McxaXHUwMDAxY6NcdTAwMGVNuaUqXHUwMDEziINaqzJ/ZIGr5yTwgkRxWFx1MDAxM4zJOFdcdTAwMWMx/vH7uFxuSSOa3nDS5uQuc+pKXHUwMDAxOVx1MDAxMFp6pivmmChN6Fxc3DDRj3ebyVIqkKPHXHUwMDBiRHFx4+lcdTAwMTVSRMBcdTAwMDRcdTAwMDTjXHUwMDBmYylcZvDMoUmcTVx1MDAwZcREXHUwMDE2U3/yLDdjnou9+GWuQeDsXHUwMDE4lHVcYvljJl8j52XCNa3WXHUwMDA2tPCzveTBeug2lZbK21x1MDAxMUFbK/RcdTAwMDBcdTAwMGb8e1x0z+gjeOG5s1x1MDAwNlx1MDAxNvxH8lx1MDAxMVapKOdcdTAwMDdBJ1x1MDAwNFx1MDAxZM9A0NpQbXpjMkXQurSWR4HZXHUwMDA3xs1cXMV6v1xmP5fI46i5QFx1MDAxNFx1MDAxNyfoV+C2mVx02sZuU1x1MDAxMbS97GFegiaagMP4/E9ytmjRXHUwMDEwVH4lflx1MDAxZoK2147ZXGKaSWytyTK+tzlcdTAwMTmnlKFcdTAwMTVytPltlIhQsOlAklxuXGL6g45cdTAwMGLo+H6WTVx1MDAwNkaEQMWJ3+lcdTAwMDT9XGZcdTAwMWYzs2RIonl+XHTCr8HHpdKXeXxe7n1cdTAwMDXaerWTvyuKLE7nx//xPJlLbr9/XHUwMDEyg3CMJ3Hp3vdcdTAwMWXWyjdcYv54XHUwMDFlnoGtN1xuK33749v/XHUwMDAzf9FPsyJ9Interpolation stagesTX DSP chainFactors:1, 2, 4, 8, 16, 32EnableDC blockfâ‚›/4 shiftDecimation stagesRX DSP chainModes:Bypass, shift up, shift downFactors:1, 2, 4, 8, 16, 32 \ No newline at end of file diff --git a/docs/images/gateware-tx-only-basic-block-diagram.png b/docs/images/gateware-tx-only-basic-block-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..fbd1639cb80bcd1972f0980927a186a5711fcd7e GIT binary patch literal 137152 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 OhulR)>1@f{?*9jG19NNu literal 0 HcmV?d00001 diff --git a/docs/images/gateware-tx-only-basic-block-diagram.svg b/docs/images/gateware-tx-only-basic-block-diagram.svg new file mode 100644 index 00000000..61aea1e2 --- /dev/null +++ b/docs/images/gateware-tx-only-basic-block-diagram.svg @@ -0,0 +1,4 @@ + + +eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1dWVfbyFx1MDAxMn7Pr+BwX2NNV+89b1x1MDAwZWZcdTAwMGbgXHUwMDA0wnbnXHUwMDFljrBlW8FcdTAwMWK2WefMf7/VYpFkLVi2SFx1MDAxOFx1MDAwZc5cdTAwMTlcdTAwMDYsud0tVdX31db6+9PS0vLkbugt/7m07N023K7fXHUwMDFjuTfLn+37195o7Fx1MDAwZvp4iFx1MDAwNn+PXHUwMDA3V6NGcGZnMlx1MDAxOY7//OOP8Fx1MDAxM05j0Hv4lNf1el5/Msbz/ot/Ly39XHUwMDFk/MQjftN+9qRzRLfvrs9aldr55lmr63U69DD4aHDS02RGXmPi9ttdLzx0i+9cdTAwMGKmXHUwMDFkLqUmwFxyVVTI56N3eLTCtUOEMvjPXHUwMDFl44w+XHUwMDFmvvGbk1x1MDAwZZ7CXHJzJDFcdTAwMDZcZlx1MDAwM8KUXHUwMDExz2d0PL/dmdjVanBcdTAwMDRcdTAwMTBgYLTSXFzw51NcdTAwMWUm9OdcdTAwMTJ5fmc8XHUwMDE5XHIuvJVBdzCys/5cdTAwMGZ49l8453O3cdFcdTAwMWVccq76zfCcVvBcbs9p+d3u/uQuXHUwMDE4XHUwMDE5LzJe0OWp8Y9cdTAwMWUnT6fez/pcdTAwMTR+YbvT98bj2FxcXHUwMDA3Q7fhT+x1XHUwMDAyXHUwMDEyvmtnN9xsXHUwMDA29+t/4ZxGbs/btDesf9XtPr/t95uevVxyy6eT2Lf1m4/fXHUwMDE2O3vseXZcdTAwMDQuhKRcdTAwMTJYeFx1MDAxZEPJXHUwMDAyzlx1MDAxM2/vXHUwMDBl+oGYUaY5lYqFc/XHNVx1MDAxNK9JMGrL7Y698DrbOaxGRC9cXMrVsOk+fFx1MDAwNJQmRHIuNZXht3b9/sX0zLuDxkX4LcG7/3xOk+bRcCCkX/0+cXt3o9stX1xc/ty4nFmaUYJcdTAwMWRcXJ9cdTAwMTE4J8VcdTAwMTLSLIgjQYNcdTAwMTJaXHQqjVx1MDAwMJFcdTAwMTBnXG7E4dxoglx1MDAxZlacgNBp8sxcdTAwMWSiXGKj6nGcXHUwMDBmeU7K883s8lxmkqBcdTAwMDFiaIZSXHUwMDA0WmuWJc/AlVJGXHUwMDEySstcdTAwMTZohWpcdTAwMDTlXGL05Y/zmvzaOrrbv9lf/fG9PoZcdTAwMWVtzSzQhmhHXHUwMDEzNL7mQaBFXFyggaEgKmE4x4tAUPp1QqCBUkeDkIZcdTAwMTE0vYpcdTAwMDMkXHUwMDA1WkvHXHUwMDAwxXHAXHUwMDFhci3LN9BcctMw/3KBvl2fXaKNNoRcdTAwMWJQJkWgXHUwMDExZjNccrRCRbDGdFx1MDAxZXl+PFx1MDAxMFxuYERcYpubt+eN25VKf6d3WIP6tlu/7IX3IyaI7mg0uFl+PvLP57xx+fBgtXa8u/al8XWdTepcdTAwMTdmZ6VeT1x1MDAxZnfi3U5mXHUwMDFkdmN1u9JcdTAwMWXsgFdpfTv1XHUwMDFhTd1cdTAwMTGVXHUwMDE5p/v4W45yS1xuXHUwMDAwpFx1MDAxY+XOX3987XG9ZsRRmlJcdTAwMGVSXHUwMDE4gqBcdTAwMWPTa+EwhFx1MDAxNo34hEqJJyRZl0KzIPGlXHUwMDE1XHUwMDAxUKBTUEqQcrV4MnL746E7Qnn7t2vyYVx1MDAwMWxCUqvxO3lcdTAwMDKFXHUwMDAyu1x0WapcZlx1MDAwNJmEQcqr59HlPPFFaKJFxDeURiuFuP5adeWvvt+feKOW24jc89agP9n371x1MDAwM0NEYu+uuT2/ay++jFxyVu36bXtcdTAwMTmWXHUwMDFinlx1MDAxZG05ei0mPnoyzyf0/GYzXG5tXHJcdTAwMWPU9fveaHNcdTAwMTaMXHUwMDFjjPy233e7XHUwMDA34Vx1MDAwMpZS5u9eTVx1MDAwNt+98cNcbiajKy96kbyNJ71cdTAwMDCHilx1MDAxY5Xec8l4TZ9sn8ijterd2d7JeOvLzcx4XHJEK4crJlx1MDAwNFx1MDAwNfxP6lx1MDAxMFx1MDAwMeJcZlRJho5cdTAwMTXh4Vx1MDAxNU0wUCaJYFxuSKpH9cRAn8b5oKBJPb+bXc2loFxcXHUwMDEzodNcdTAwMTgoRKcypeZcZum/Mni3SmegPDroQiDl1WuXwr8+r427O2v7nFxm1EYtJUDwgKQxaZbGilx1MDAxOaM4XHUwMDE1hmyGx9knopOD+IRXTkmpXHUwMDA0ISohy0FsgFx1MDAwMyP4ccmUSeGe6LEhe2XCaG5cdTAwMThcdTAwMThpXG5IMnIlz8jfglpNd9zxfq04388uzmhDhNaKitRcdTAwMTBcdTAwMDFVmbBlPyiFQlwiWrZAS4hqV1x1MDAwMdhcdTAwMWFcdTAwMGX8aYZcdTAwMWL+tlx1MDAxNF7s4I/n3//3OfXsTIFcXPzTlYQkh8MlWOl44o4mX/De+v02Li3kwE/hvlx1MDAwN2Akl0ffxydDz9vaaVx1MDAxZmxfra2uTX6sRojwcm/QXGbEcTA69yfRXHUwMDAzLf/Wa9btpYtduSWEP5y9RmZCXHUwMDE5OoOE6NhcdTAwMTVwUNWRd+JcdTAwMGZtg3WSJtbwzNyXvX7z5Vx1MDAxNZyd7t9vK1mpXTSPdy4nZEf3XHUwMDBlq4utwE7SMCaQXGLh3WAglfpcdTAwMWNfoMTpXHUwMDEzqVxies5cXDCes4bgPlSt5et4bkL3cIXRY2hM/CnM97rng5tAaGOEI5j7vtd+0oy4jtrv3Fx1MDAxY+9cdTAwMGa9hu92U75z+liObb9yx+7N6uHJqM/94dHq171TVJ2Z2VxuMlxmh0lcdTAwMTHgjaFgWNxccrHhXHUwMDA1oyVFT1x1MDAwM1x1MDAwZqE/YpLxsjC8XHUwMDAw3KpFhPCE4Vx1MDAwNeVoXG5cXMDDMKFcdTAwMTUoh6uc01x1MDAxNj0//3dzXHUwMDE1d292465cdTAwMTh6jMZAgpRcdTAwMDRcdTAwMWWJnn732SNcdTAwMTFcdTAwMDSdTaUjYeOSolx1MDAwYt+q+6fX3e5pd91cdTAwMTO7ZP1rrb/75XTxKMCXoYSd9sl6b6XfXHUwMDFh3a70XHUwMDBlflx1MDAxZZ2dlFx1MDAxMLR41ehcdTAwMDLapWhcdTAwMGVkIeKWf11zolx1MDAwYqiHXGJUknK85VowmOJt6KFQzvEkPFx1MDAwNCTqPDxptbVcdTAwMGL2RYlcdTAwMDSF3C4tXGJectT73YRcdTAwMTfc+uyqjHdHaIZUXCKNpymaXHUwMDFkXWBKU/wknytSWKb4htL46Jzvr9c3I8bs91x1MDAwNVx1MDAxNV6AxumgwtS8y1x0JuSzn5fgWVx1MDAwMXWEsv49gEbaOVx1MDAxNUtAdKaKXHUwMDEy9LtcYrJNyZNqjCbf4UTbmCc6aUalhVx1MDAxMlx1MDAxMJzRXHUwMDA2IDJIxH8jdahIXHUwMDFmsf9nlf5WXHUwMDAwnZVkQHgkJ1x1MDAxNWp0ip6H8UK0tpJxKFx1MDAxZJ3pl+vOlu/vbfL7g1x1MDAxZjeuvzG+3TxaXHUwMDFjnV8rpVDb2dGdXHUwMDFmN8c9Ksff+DfSOb1cdTAwMWJccktcdTAwMTg3P1xukjvuy6hfZk4h/37loL6i0kHf3qZcdTAwMGVcYnqgLJ78RtRXSNSRiFx1MDAxM6XQPU1cdTAwMDZrgDKHoTkhaHFshFKnRGs+UD/TRFx1MDAxNEgqIEtcdTAwMTeUKJpcdTAwMTZtZJEo27SN0Fx1MDAxY1x1MDAxMVx1MDAxYUWuZNCXXHUwMDAwplC6O1x1MDAwMfpcdTAwMDfHS7X9+lKjg/j7XHUwMDE2sP9cdTAwMDXcncb+9OmXlE9o9C5MRV+snVS+XHUwMDFlkWHnpNW6c4vlXHUwMDEzmKRcYiqGXHRGWLJcdTAwMDBcdTAwMDCUhXZcclLbQoCUXHUwMDAygFlIwEdcdTAwMDHAy1x1MDAxYX4+u4JcdTAwMWJjY+ZCpbnoSmXm/1x1MDAwMZDOqVfgXHUwMDAwrePeXHUwMDE5r21vVlV3p3V4Nmx+XHUwMDFmy/5MXHUwMDFjYFx1MDAxNuwzlJWUqsifZ1x1MDAwZfZcdTAwMDFw1EPNlEDmRY1JeLxcdTAwMDH2gWKaaMaS2MeRadt8upBcdTAwMWNNIScyqSNcdTAwMWbQl6VcdTAwMTiNXHUwMDAyXHUwMDBlL1x1MDAxN2jIXHUwMDE0TYU+aVx1MDAxMvUyYalcdTAwMTf6J8Bp6bWLhaU3gX216spbgLxcdTAwMTdwJiWHXjbSddarw7X7U7g9ISdb123drzY6tVmUN6zaZFpCJDlcdTAwMTVAnHVQXHUwMDA16i6VXG5cZqE0XHUwMDE5g+bCQZXHXHUwMDE3ZYpIXCLSXHUwMDEy5lx1MDAxZsqbobx+XHUwMDAx5Vx1MDAxNURcIlx1MDAxN6EyzbeNXHUwMDE2TCaiVUYh9aCR9O/CyquMkYZwXHUwMDFkLVx1MDAwZp1DeXdWfpShvF2vNclR3clgmKW3sclOK2lsduUoabUy+rruXunhXHUwMDEwXHUwMDBltzfOXHUwMDBmvtfHp7fFXHUwMDEzRviDo1x1MDAwN1x1MDAxM1x1MDAxYc5AXHUwMDFh0YFcdTAwMDRNqOZcZpVVKJVcdTAwMTKRouBIQVx1MDAwNVFcYrKC6rR2XHUwMDAxYVx1MDAxY0VcdTAwMDVcdTAwMTCKjlx0cl4ovb76XSSMfs6ut1xcKim0Sk1cdTAwMThBNFx1MDAxMjCtt9JmWVx1MDAxMSfnqq/O46NcdTAwMWLrXHUwMDFiP7bWmrdbo1x1MDAxM3Laud+XP6/pZCY++jlvWH5M2+JwZWV44v0gXHUwMDA3q3t393ftVvqwxWM83FxubEldXHUwMDBl+evPgUpkpo5VXGZBKENPjdO4XHUwMDAycuKgXmomhFCcccZcdTAwMTJcbshcdTAwMWUzO1xmzSeApFx1MDAxZjGeXHUwMDAyOncxu85cdEBnQ0GGzrFcdTAwMDSCPumcsE4+46JsnovSi+Z5XHUwMDExqNyvb75cdTAwMDWe+1x1MDAwMoAl0jrRWZdcdTAwMDOh+UU5LzfcKUei30GYXHUwMDA0zqJcdTAwMTj5IKTa1kqgXHUwMDBmaoRcIlx1MDAwMmSy9PstJHXeXHUwMDA1gvZcdTAwMGIwX0lcdTAwMDXTQrFEdYW92FwiU5tcdTAwMDG5kP3ofFx1MDAxZEp5UHd43K7y6ztYa1Uru1d+W55V6dXiXGK6QJbktZA5d1xctbk7+tm567fO6u6WafRcdTAwMDa1853GW0T8/PuVg/hCMocjoFvnXHUwMDE2tXmKcCuHRStwk3hvPlx1MDAxYUXmt1x1MDAxMINcdTAwMDJcdTAwMDXkxCBoc57mXHUwMDFha8h2jVx1MDAwNUXfWMvS20RcbstuXHUwMDFh3i9Z7Fx1MDAxZFxyun/1R17bXHUwMDFmI26P31x1MDAwMlx1MDAwNXhcdTAwMDGAUyjA00KWUtZRXHUwMDBlKdg9uV7dVVfnK2ZcXN1ru1x1MDAxNXHXXTVcdTAwMDVIgXaQXHUwMDE1olx1MDAwMFx0ooUgcVLwXFzpXHUwMDAxVFx1MDAxMilcdTAwMTlJiYHNWIdcdTAwMTmwXHUwMDAy+ThM2Vme98Bcbs5cdC+Q5lx1MDAxMVx1MDAxYVx1MDAxOZZWaawgr2tcdTAwMDTQgdNcZq136Yme+2q9W7tgrcvL3YveNdnj1fX13bdbivlaxVx1MDAxZa9KXHUwMDBiXHUwMDE43vBIw+ZCtCD/fuXRXHUwMDAyzlx1MDAxZMM0XHUwMDAxQ0BcdTAwMWGlp1x1MDAxYkhpkO/S0iD0c5PkXHUwMDA1QIxcdTAwMDOWXHUwMDE4cHRcdTAwMGJcdTAwMDVDT+ODXHUwMDE4XHUwMDE0MFx1MDAxMqJIylx1MDAwYijaXGLC0kJcdTAwMDHGZFeESVx1MDAxYlx1MDAwN2DczGUkcms8XHREMtPz1nguuf3mX/1Wd3DzhK5vgVx1MDAxYryAw6lVn3YlS+lcdTAwMGIph1x1MDAxY+Sbz7xcdTAwMGU8ro1ccqnbflEwXHUwMDEygMaLuqhxtFx1MDAwNlx0RFx1MDAxMqJcdTAwMTghPFx1MDAxOS7gXHUwMDE07Vx1MDAwNFwije2JMUhcZlJcdTAwMDJ+xGFCcVx1MDAwM0QowZlBb6JkZvCOXHUwMDE0X1x1MDAxNlB8o9A3UzK1XHSPmeyeUlx1MDAwNYozWXpcdTAwMGKe3SbEzFx1MDAxNVx1MDAwMiy1XHUwMDA1L1Mgg08nRTFcdTAwMWMvXHUwMDAxzTN20eVXgi/N2UXHtVx1MDAxNkxcdTAwMWGQttxExlx1MDAxN8HxLcRVSjkulFx1MDAxMylcdTAwMTKLKNhGl2/W5lpChUytQVx1MDAxMJh/XHUwMDE15TXSxUWv3E6652O9wbW34z9cXJfxkT/pPOrQXGaULT+UlW/MtUNsZS1Rhismp7KnXHUwMDAyXHUwMDE1Qyn8XHUwMDFmXHUwMDA1XHUwMDA0aZKs5OPMkdx2/WvEXHUwMDAy9O9Dzlx1MDAxNzHlglovUVx1MDAwN9xcdTAwMGaoXGLN/Yclj1vyalx1MDAwMUuO7rTSmvLUgl3I3KBKck1RwUTZXGZu/mROuZY8S1x1MDAxZe2rklx1MDAxNMXFLXl+8mcuM0hcdTAwMWMz9YpcdTAwMWJBQUhy4lx1MDAwNa33K7Rxk0KzTFjnNFx1MDAwYjy3jV7AmG5cdTAwMWXX709cdTAwMGUre17728aP4y3d3NeHdzNcdTAwMTlTQMFcbsJZRFx1MDAxYkmpijvASIxRSTjyXGZcdTAwMDQ3uz9h0ppcbuKgXHUwMDFiJLgtnea2XHUwMDExL5VcdTAwMThLZCN24zXNiVaRnbs+rGncmq5ntD9EVOLxZtKkdD5aWfRf8Fx1MDAwNZBcdTAwMWFOU5CdZbNmUUP5SXM0XqDnXG6il2pnK5miXHUwMDFhXHUwMDFjTUrp7IZ2mjBlXHUwMDFjmcN+xEcu33Lk95/lWVx1MDAwZUNobE/FeLDdXHUwMDE2yCiuXHUwMDEwuzSyXmJ0soiNMUcjsFFcdFx1MDAxNKVcdTAwMTWdqjTLgZZJMYFcdTAwMWW7oFx1MDAwNIcrO9j+jkzHRoEsXHUwMDFiVczqQFqWXHJyNlxu1XaLPVx1MDAxNt0y4X1cdTAwMTmITIlcZo6mXGLj4lQsf8+2X8Bpftf2M1Bolm/QcuYnXfIsp1wiMtqQnjCc1G5MgpRLWv812WTDjGOzJ1x1MDAwMu0u+r+gU1JcdTAwMGVg5dgoqShcdTAwMTAweKJX+eBcXFmGc3t2w0mJIULI1PolJjI7TjXj2lD5bolVpkRcdTAwMDZH04RxccP5XHUwMDFhO2KVbzhfIeD4rzech7VDQfdcdTAwMGW/NM9cdTAwMGX863bj5ujn3uF90nBmt/Liolx1MDAxNWF2/1ZBpzenp1x1MDAwMl1Wg/ydXHUwMDEyxHEpk1x1MDAwZSu1e4dcdTAwMDa5O6FtXHUwMDAxR4rDXG7KMTYgg4Mj22FFon+pdvG99vKek5PZbSc3XHUwMDEyrWdKwsZOJVx1MDAxYadNsE6Bt5lqVnq/PouFXCJcdTAwMTYqQKjLjVx1MDAxZd+ofyX3d3u1q+3at9HwZqZOXHUwMDA0XHUwMDAwwlx1MDAxZEVcdTAwMTlcdTAwMTOGojU301x1MDAwMVx1MDAxOIFUgTMpKDrmeO1SWoFcZnPAxtvQ9GpcdTAwMWHdMOWjXHUwMDAw4WX5bVx1MDAxN4leS22j1ynia0y29FL7XHUwMDEwhPkqlHJlV5DFNpiyuz837DlLzUHP9ft/9TdtyUDf7cbeLqNcdTAwMTjhldr6XG6voJwqXHUwMDA0Q/zNzbPxz9FR62qwf7x174trOlOtXHUwMDEx047NSlx1MDAxOSE1J2bqoSpP/bnI+I2hNOVcdTAwMTFBgj7UIHOtJUqU/Og5KqLpnVwiXHUwMDE1XHUwMDA3zCA3YKnd9Ty74lx1MDAwMFDZXHSa8TfYoLtWX6++YVWOT69cdTAwMWM9/drouo3xxvnWceP05rJd0Y29085MkFx1MDAxY6ZEwFD7lKh0TaWSmyBcdTAwMDGdxGRCXHUwMDFkavd9NEF41KSB8oeqZqpqkV56y3qMplwijVVKmq2qXHUwMDAy7EbalIZB57eiqlx1MDAwN/ZGNjz/Olq/9+Y0NnWW5Shu/sayuZUhti3ebpCApNpwXHUwMDEwkeS+vUJKOsCUJFx1MDAwMlx1MDAwNEHf0SRcdTAwMTVXcO2gX4lcdTAwMTiLzlxukvE0iGVcdTAwMGXRyNaNsU+mYEZcdTAwMTZpXG6cYaf913hmxK/fZf9cdTAwMWNcbniGtl9DaZO+yz4kg23PSqzwXHUwMDE2oN9UOrvmaFx1MDAxZFwi24T+rrha7Oyk3L308UxhfoXhmCONwnshhELHXHUwMDA0UVGGw32aXHUwMDFh9jfWXHUwMDFiXHUwMDA2xXighVKgiVx1MDAwNClD0bEvcFxilVx1MDAxNGwnXHUwMDE5Q3k0yTBlwfDfa2R3okuwu1xmi6lcdTAwMTUgXHUwMDA2MXRcdTAwMGZcdTAwMTgwQDeR5SzhXHUwMDE37dqf1WnzaCtodP5cdTAwMGZzmlrylCbkXG5vTHxcdTAwMWZWkVx1MDAxOC1Xkl9cdTAwMWH9+fdki0uijjJuXHUwMDFiplxuKV/sadH3tcHwsHPNv26Tia8u91vfZn+EZ1x1MDAxOCZliqAsTz3y0D75SNoqXGKl7VZvkNJcbvdG4qTvolx1MDAxYlx1MDAwZdzZ0Vx1MDAxMFx1MDAxZFx1MDAwNqqMYmlcdTAwMGY9RI3O3NtNMmmE3Wq+ZDBcdTAwMTTAlSmpfTu/oazIM5Sm9nuYfoZSSpyUSUdcbtD2cSyE23BqUp6fXHUwMDFmPfM4StnU7lx1MDAxNWT5d1C7XHUwMDAyO3hcdTAwMDJcdTAwMTgpiEhNmNLszk6BUkeUnOtcdJ6xaaQ2dLPfnzLldIHTK1x1MDAwMlx1MDAxMUpwu/s0XHUwMDAx9GTgRe5WQe1JXHUwMDE3/ldcdTAwMTkvoUeLk8Ff8FxiJ1x1MDAxNb8nU89wQlx1MDAxMFxclFxyvkIyeJrQxtdcdTAwMTB5hlx1MDAxMyOESfX22SCLzj+DXHLGtSFXfmehg/nC/NLwr8FcdTAwMDc/PY6y7Fx1MDAwZYf7XHUwMDEzNGDPQrV87Xs3X7KjXHUwMDA2n1x1MDAxZe+ohVx1MDAxNi8oXHUwMDFj/ufTP/9cdTAwMDf5elfzIn0=DACinterfaceSGPIOTX DSP chainDACMCUSPISPI controlregistersSGPIO andflow controlDAC clock domainInternal clock domainFPGATransceiver \ No newline at end of file diff --git a/docs/source/gateware.rst b/docs/source/gateware.rst new file mode 100644 index 00000000..f560b583 --- /dev/null +++ b/docs/source/gateware.rst @@ -0,0 +1,118 @@ +================================================ +Gateware +================================================ + +One of the significant hardware changes in :ref:`HackRF Pro ` is the replacement of the CPLD with a FPGA. While the older CPLD primarily provided glue logic between the MCU and RF front end, the FPGA in HackRF Pro introduces more logic and DSP capability. This enables offloading digital signal processing tasks from the MCU. + +FPGAs are highly flexible devices whose behavior is defined by *gateware*: hardware descriptions that configure the internal logic fabric. HackRF Pro gateware is written in `Amaranth HDL `__, a Python-based hardware description language. + +The specific FPGA device used in HackRF Pro is the Lattice iCE40UP5K, which features +5280 LUT4s and 8 dedicated DSP (multiply-accumulate) blocks. We rely on the `open-source iCE40 FPGA toolchain `__ to build the required bitstreams that are bundled in the firmware. + +All gateware source code lives under `firmware/fpga/` in the HackRF repository. Top-level designs reside in `firmware/fpga/top/` and are the primary entry points for different operational modes. + +By default, a standard gateware configuration is loaded at boot. However, the firmware can dynamically reconfigure the FPGA at runtime to switch between different gateware variants. + + +Standard gateware +~~~~~~~~~~~~~~~~~ + +The standard gateware is used by default when the firmware has not requested an alternative bitstream. + +The standard gateware provides a balanced configuration optimized for general-purpose operation. It implements configurable digital signal processing paths for the reception and transmission paths, capable of (limited) frequency translation and supporting a wide range of sample rates. + +Block diagram +^^^^^^^^^^^^^ + +.. image:: ../images/gateware-basic-block-diagram.png + :align: center + +.. image:: ../images/gateware-standard-dsp-chain.png + :align: center + :width: 75% + +Features +^^^^^^^^ +* 8-bit I, 8-bit Q data format +* Receiver signal chain: + * Optional DC offset removal (DC blocker) + * Configurable fs/4 shifter (quarter sample rate): bypass, shift up or shift down + * Configurable decimation rates: 1x, 2x, 4x, 8x, 16x, 32x +* Transmitter signal chain: + * Configurable interpolation rates: 1x, 2x, 4x, 8x, 16x, 32x +* SPI control interface for register configuration +* Double data rate (DDR) interface to RF transceiver +* Interface to MCU (SGPIO) + + +Half-precision gateware +~~~~~~~~~~~~~~~~~~~~~~~ + +The half-precision gateware reduces sample width to 4 bits per I/Q component, enabling higher throughput within the constraints of the USB interface (up to 40 Msps). + +This configuration is intended for applications where bandwidth is more critical than dynamic range, such as wideband spectrum monitoring. + +Block diagram +^^^^^^^^^^^^^ + +.. image:: ../images/gateware-basic-block-diagram.png + :align: center + +.. image:: ../images/gateware-halfprec-dsp-chain.png + :align: center + :width: 75% + +Features +^^^^^^^^ +* 4-bit I, 4-bit Q data format +* Receiver signal chain: + * Optional DC offset removal (DC blocker) + * Round to 4-bit I/Q +* Transmitter signal chain: + * Extend width to 8-bit I/Q +* SPI control interface for register configuration +* Double data rate (DDR) interface to RF transceiver +* Interface to MCU (SGPIO) + +Extended-precision gateware (RX and TX) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The extended-precision gateware increases internal signal processing precision and output sample width to improve signal quality. The main drawback is that the minimum decimation or interpolation factor is 16x. Due to increased logic requirements, this gateware is split in two top-level designs (RX and TX). + +Samples are 16-bit I/Q, while the effective number of bits (ENOB) depends on the selected configuration and typically ranges between 9 and 11 bits. + +The increased dynamic range of the output makes it particularly useful for weak and/or narrowband signals. + +Block diagram (RX) +^^^^^^^^^^^^^^^^^^ + +.. image:: ../images/gateware-rx-only-basic-block-diagram.png + :align: center + +.. image:: ../images/gateware-ext-prec-rx-dsp-chain.png + :align: center + :width: 75% + +Block diagram (TX) +^^^^^^^^^^^^^^^^^^ + +.. image:: ../images/gateware-tx-only-basic-block-diagram.png + :align: center + +.. image:: ../images/gateware-ext-prec-tx-dsp-chain.png + :align: center + :width: 75% + +Features +^^^^^^^^ +* 16-bit I, 16-bit Q data format +* Receiver signal chain (RX extended-precision gateware): + * Optional DC offset removal (DC blocker) + * Configurable mixer (in fs/128 steps) + * Configurable decimation rates: 16x, 32x, 64x, 128x +* Transmitter signal chain (TX extended-precision gateware): + * Configurable interpolation rates: 16x, 32x, 64x, 128x +* SPI control interface for register configuration +* Double data rate (DDR) interface to RF transceiver +* Interface to MCU (SGPIO) + diff --git a/docs/source/hackrf_pro.rst b/docs/source/hackrf_pro.rst index 18e4a151..e47dbb0b 100644 --- a/docs/source/hackrf_pro.rst +++ b/docs/source/hackrf_pro.rst @@ -2,6 +2,8 @@ HackRF Pro ================================================ +.. _hackrf_pro: + .. image:: ../images/hackrf-pro-preliminary-photo.jpg :alt: HackRF Pro diff --git a/docs/source/index.rst b/docs/source/index.rst index ce845e66..401fbb3b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -57,6 +57,13 @@ Welcome to HackRF's documentation! setting_gain virtual_machines + +.. toctree:: + :maxdepth: 2 + :caption: Gateware + + gateware + .. toctree:: :maxdepth: 2 :caption: Opera Cake Documentation From e32695e24fde864b46a9664ccddf42a2b91594d0 Mon Sep 17 00:00:00 2001 From: mndza Date: Wed, 3 Jun 2026 10:12:36 +0200 Subject: [PATCH 147/173] gateware: CIC defaults to the minimum resampling factor --- firmware/fpga/build/praline_fpga.bin | Bin 204769 -> 204382 bytes firmware/fpga/dsp/cic.py | 5 ++++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/firmware/fpga/build/praline_fpga.bin b/firmware/fpga/build/praline_fpga.bin index a6b024858f10f08f84cbc6cffa578935c0ae469a..64dd9b4b9bc6f457a2d440b385132e270e0e864b 100644 GIT binary patch literal 204382 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

    8B0P02b};H!|8ra@ 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 KlPVz#ME?NOoQ1mp literal 204769 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 diff --git a/firmware/fpga/dsp/cic.py b/firmware/fpga/dsp/cic.py index fa0f3fa0..99f5376a 100644 --- a/firmware/fpga/dsp/cic.py +++ b/firmware/fpga/dsp/cic.py @@ -240,6 +240,7 @@ class ProgrammableShift(wiring.Component): # Implement the map itself (should it be done outside?) max_shift = max(self.shift_map.values()) + min_shift = min(self.shift_map.values()) value_scaled = [ Signal(signed(self.width_in + max_shift)) for _ in range(self.num_channels) ] scaled_valid = Signal() @@ -255,7 +256,9 @@ class ProgrammableShift(wiring.Component): for k, v in self.shift_map.items(): with m.Case(k): m.d.sync += value_scaled[c].eq(self.input.payload[c] << (max_shift - v)) - + with m.Default(): + m.d.sync += value_scaled[c].eq(self.input.payload[c] << (max_shift - min_shift)) + with m.If(~self.output.valid | self.output.ready): m.d.comb += scaled_ready.eq(1) m.d.sync += self.output.valid.eq(scaled_valid) From 595ea6c072fdbed6e77b1ddf9cbba7dc0342eaaf Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 29 May 2026 06:30:57 +0100 Subject: [PATCH 148/173] Make switching SPI bus modes automatic. --- firmware/common/fpga.c | 5 ----- firmware/common/fpga_image.c | 3 --- firmware/common/fpga_selftest.c | 2 -- firmware/common/ice40_spi.c | 11 ++++------ firmware/common/ice40_spi.h | 1 - firmware/common/max2831.c | 2 +- firmware/common/max2831.h | 1 + firmware/common/max2837.c | 4 ++-- firmware/common/max2837.h | 1 + firmware/common/max2839.c | 4 ++-- firmware/common/max2839.h | 1 + firmware/common/max283x.c | 9 +++------ firmware/common/max283x.h | 1 - firmware/common/max5864.c | 7 +------ firmware/common/max5864.h | 1 - firmware/common/pins.c | 2 -- firmware/common/rf_path.c | 10 --------- firmware/common/rffc5071.c | 4 ++-- firmware/common/spi_bus.c | 14 ++++++++++++- firmware/common/spi_bus.h | 7 ++++++- firmware/common/ui_rad1o.c | 6 ------ firmware/common/w25q80bv.c | 36 ++++++++++++++++++++++++--------- 22 files changed, 63 insertions(+), 69 deletions(-) diff --git a/firmware/common/fpga.c b/firmware/common/fpga.c index f68b6525..ec4ff2ec 100644 --- a/firmware/common/fpga.c +++ b/firmware/common/fpga.c @@ -25,7 +25,6 @@ #include #include "ice40_spi.h" -#include "max283x.h" /* Driver instance. */ fpga_driver_t fpga = { @@ -57,9 +56,7 @@ void fpga_setup(fpga_driver_t* const drv) uint8_t fpga_reg_read(fpga_driver_t* const drv, uint8_t r) { uint8_t v; - ssp1_set_mode_ice40(); v = ice40_spi_read(drv->bus, r); - ssp1_set_mode_max283x(); drv->regs[r] = v; return v; } @@ -67,9 +64,7 @@ uint8_t fpga_reg_read(fpga_driver_t* const drv, uint8_t r) void fpga_reg_write(fpga_driver_t* const drv, uint8_t r, uint8_t v) { drv->regs[r] = v; - ssp1_set_mode_ice40(); ice40_spi_write(drv->bus, r, v); - ssp1_set_mode_max283x(); FPGA_REG_SET_CLEAN(drv, r); } diff --git a/firmware/common/fpga_image.c b/firmware/common/fpga_image.c index 3ebadad8..6b440337 100644 --- a/firmware/common/fpga_image.c +++ b/firmware/common/fpga_image.c @@ -26,7 +26,6 @@ #include "fpga.h" #include "ice40_spi.h" #include "lz4_blk.h" -#include "max283x.h" #include "selftest.h" struct fpga_image_read_ctx { @@ -86,7 +85,6 @@ bool fpga_image_load(struct fpga_loader_t* loader, unsigned int index) // A callback function is used by the FPGA programmer // to obtain consecutive gateware chunks. ice40_spi_target_init(&ice40); - ssp1_set_mode_ice40(); struct fpga_image_read_ctx fpga_image_ctx = { .loader = loader, .addr = loader->start_addr + bitstream_offset, @@ -96,7 +94,6 @@ bool fpga_image_load(struct fpga_loader_t* loader, unsigned int index) loader->out_buffer, fpga_image_read_block_cb, &fpga_image_ctx); - ssp1_set_mode_max283x(); // Update selftest result. selftest.fpga_image_load = success ? PASSED : FAILED; diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index 06f2e8ee..b9d7025e 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -71,10 +71,8 @@ bool fpga_spi_selftest(void) // Test writing a register and reading it back. uint8_t reg = 6; uint8_t write_value = 0xA5; - ssp1_set_mode_ice40(); ice40_spi_write(&ice40, reg, write_value); uint8_t read_value = ice40_spi_read(&ice40, reg); - ssp1_set_mode_max283x(); // Update selftest result. selftest.fpga_spi = (read_value == write_value) ? PASSED : FAILED; diff --git a/firmware/common/ice40_spi.c b/firmware/common/ice40_spi.c index 2685092a..ce646fd6 100644 --- a/firmware/common/ice40_spi.c +++ b/firmware/common/ice40_spi.c @@ -40,11 +40,6 @@ ice40_spi_driver_t ice40 = { .bus = &spi_bus_ssp1, }; -void ssp1_set_mode_ice40(void) -{ - spi_bus_start(&spi_bus_ssp1, &ssp_config_ice40_fpga); -} - void ice40_spi_target_init(ice40_spi_driver_t* const drv) { const platform_scu_t* scu = platform_scu(); @@ -67,14 +62,14 @@ void ice40_spi_target_init(ice40_spi_driver_t* const drv) uint8_t ice40_spi_read(ice40_spi_driver_t* const drv, uint8_t r) { uint8_t value[3] = {r & 0x7F, 0, 0}; - spi_bus_transfer(drv->bus, value, 3); + spi_bus_transfer(drv->bus, &ssp_config_ice40_fpga, value, 3); return value[2]; } void ice40_spi_write(ice40_spi_driver_t* const drv, uint8_t r, uint16_t v) { uint8_t value[3] = {(r & 0x7F) | 0x80, v, 0}; - spi_bus_transfer(drv->bus, value, 3); + spi_bus_transfer(drv->bus, &ssp_config_ice40_fpga, value, 3); } static void spi_ssp1_wait_for_tx_fifo_not_full(void) @@ -107,6 +102,8 @@ bool ice40_spi_syscfg_program( size_t (*read_block_cb)(void* ctx), void* read_ctx) { + spi_bus_start(drv->bus, &ssp_config_ice40_fpga); + // Drive CRESET_B = 0, SPI_SS = 0, SPI_SCK = 1. gpio_clear(drv->gpio_creset); gpio_clear(drv->gpio_select); diff --git a/firmware/common/ice40_spi.h b/firmware/common/ice40_spi.h index a1362033..331814aa 100644 --- a/firmware/common/ice40_spi.h +++ b/firmware/common/ice40_spi.h @@ -48,4 +48,3 @@ bool ice40_spi_syscfg_program( /* Driver instance. */ extern ssp_config_t ssp_config_ice40_fpga; extern ice40_spi_driver_t ice40; -void ssp1_set_mode_ice40(void); diff --git a/firmware/common/max2831.c b/firmware/common/max2831.c index 05d5cb29..08e59a59 100644 --- a/firmware/common/max2831.c +++ b/firmware/common/max2831.c @@ -109,7 +109,7 @@ static void max2831_write(max2831_driver_t* const drv, uint8_t r, uint16_t v) { uint32_t word = (((uint32_t) v & 0x3fff) << 4) | (r & 0xf); uint16_t values[2] = {word >> 9, word & 0x1ff}; - spi_bus_transfer(drv->bus, values, 2); + spi_bus_transfer(drv->bus, drv->config, values, 2); } uint16_t max2831_reg_read(max2831_driver_t* const drv, uint8_t r) diff --git a/firmware/common/max2831.h b/firmware/common/max2831.h index 0885fd39..f1a28c54 100644 --- a/firmware/common/max2831.h +++ b/firmware/common/max2831.h @@ -50,6 +50,7 @@ typedef enum { typedef struct _max2831_driver_t { spi_bus_t* bus; + void* config; gpio_t gpio_enable; gpio_t gpio_rxtx; gpio_t gpio_rxhp; diff --git a/firmware/common/max2837.c b/firmware/common/max2837.c index 561a17f9..f512c6af 100644 --- a/firmware/common/max2837.c +++ b/firmware/common/max2837.c @@ -156,14 +156,14 @@ void max2837_setup(max2837_driver_t* const drv) static uint16_t max2837_read(max2837_driver_t* const drv, uint8_t r) { uint16_t value = (1 << 15) | (r << 10); - spi_bus_transfer(drv->bus, &value, 1); + spi_bus_transfer(drv->bus, drv->config, &value, 1); return value & 0x3ff; } static void max2837_write(max2837_driver_t* const drv, uint8_t r, uint16_t v) { uint16_t value = (r << 10) | (v & 0x3ff); - spi_bus_transfer(drv->bus, &value, 1); + spi_bus_transfer(drv->bus, drv->config, &value, 1); } uint16_t max2837_reg_read(max2837_driver_t* const drv, uint8_t r) diff --git a/firmware/common/max2837.h b/firmware/common/max2837.h index f32bde6e..a33179ac 100644 --- a/firmware/common/max2837.h +++ b/firmware/common/max2837.h @@ -43,6 +43,7 @@ typedef enum { typedef struct _max2837_driver_t { spi_bus_t* bus; + void* config; gpio_t gpio_enable; gpio_t gpio_rx_enable; gpio_t gpio_tx_enable; diff --git a/firmware/common/max2839.c b/firmware/common/max2839.c index 87917ed8..a938b733 100644 --- a/firmware/common/max2839.c +++ b/firmware/common/max2839.c @@ -164,14 +164,14 @@ void max2839_setup(max2839_driver_t* const drv) static uint16_t max2839_read(max2839_driver_t* const drv, uint8_t r) { uint16_t value = (1 << 15) | (r << 10); - spi_bus_transfer(drv->bus, &value, 1); + spi_bus_transfer(drv->bus, drv->config, &value, 1); return value & 0x3ff; } static void max2839_write(max2839_driver_t* const drv, uint8_t r, uint16_t v) { uint16_t value = (r << 10) | (v & 0x3ff); - spi_bus_transfer(drv->bus, &value, 1); + spi_bus_transfer(drv->bus, drv->config, &value, 1); } uint16_t max2839_reg_read(max2839_driver_t* const drv, uint8_t r) diff --git a/firmware/common/max2839.h b/firmware/common/max2839.h index 0e397294..6430ce37 100644 --- a/firmware/common/max2839.h +++ b/firmware/common/max2839.h @@ -46,6 +46,7 @@ typedef enum { typedef struct _max2839_driver_t { spi_bus_t* bus; + void* config; gpio_t gpio_enable; gpio_t gpio_rxtx; void (*target_init)(struct _max2839_driver_t* const drv); diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index e7221bb0..c0478ae0 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -29,7 +29,6 @@ #include "fixed_point.h" #include "platform_detect.h" #include "platform_gpio.h" -#include "spi_bus.h" #ifdef IS_PRALINE #include "max2831_target.h" @@ -56,14 +55,10 @@ ssp_config_t ssp_config_max283x = { max283x_driver_t max283x = {}; -void ssp1_set_mode_max283x(void) -{ - spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); -} - #ifdef IS_PRALINE max2831_driver_t max2831 = { .bus = &spi_bus_ssp1, + .config = &ssp_config_max283x, .target_init = max2831_target_init, .set_mode = max2831_target_set_mode, }; @@ -72,6 +67,7 @@ max2831_driver_t max2831 = { #ifdef IS_NOT_PRALINE max2837_driver_t max2837 = { .bus = &spi_bus_ssp1, + .config = &ssp_config_max283x, .target_init = max2837_target_init, .set_mode = max2837_target_set_mode, }; @@ -80,6 +76,7 @@ max2837_driver_t max2837 = { #ifdef IS_HACKRF_ONE max2839_driver_t max2839 = { .bus = &spi_bus_ssp1, + .config = &ssp_config_max283x, .target_init = max2839_target_init, .set_mode = max2839_target_set_mode, }; diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h index 4db5edfe..7c9c03dd 100644 --- a/firmware/common/max283x.h +++ b/firmware/common/max283x.h @@ -143,4 +143,3 @@ void max283x_rx_calibration(max283x_driver_t* const drv); /* Driver instance. */ extern ssp_config_t ssp_config_max283x; extern max283x_driver_t max283x; -void ssp1_set_mode_max283x(void); diff --git a/firmware/common/max5864.c b/firmware/common/max5864.c index 88d781b9..5f3417fc 100644 --- a/firmware/common/max5864.c +++ b/firmware/common/max5864.c @@ -48,14 +48,9 @@ max5864_driver_t max5864 = { .target_init = max5864_target_init, }; -void ssp1_set_mode_max5864(void) -{ - spi_bus_start(max5864.bus, &ssp_config_max5864); -} - static void max5864_write(max5864_driver_t* const drv, uint8_t value) { - spi_bus_transfer(drv->bus, &value, 1); + spi_bus_transfer(drv->bus, &ssp_config_max5864, &value, 1); } static void max5864_init(max5864_driver_t* const drv) diff --git a/firmware/common/max5864.h b/firmware/common/max5864.h index a617cdf0..0f561685 100644 --- a/firmware/common/max5864.h +++ b/firmware/common/max5864.h @@ -42,4 +42,3 @@ void max5864_xcvr(max5864_driver_t* const drv); /* Driver instance. */ extern ssp_config_t ssp_config_max5864; extern max5864_driver_t max5864; -void ssp1_set_mode_max5864(void); diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 3010cd9f..1f98fd9a 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -318,8 +318,6 @@ void pins_setup(void) } #endif - ssp1_set_mode_max283x(); - mixer_bus_setup(&mixer); #ifdef IS_H1_R9 diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index b497a197..27dd548f 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -494,11 +494,9 @@ void rf_path_pin_setup(rf_path_t* const rf_path) void rf_path_init(rf_path_t* const rf_path) { - ssp1_set_mode_max5864(); max5864_setup(&max5864); max5864_shutdown(&max5864); - ssp1_set_mode_max283x(); max283x_setup(&max283x); max283x_start(&max283x); @@ -536,9 +534,7 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d } else { mixer_enable(&mixer); } - ssp1_set_mode_max5864(); max5864_tx(&max5864); - ssp1_set_mode_max283x(); max283x_tx(&max283x); break; @@ -553,9 +549,7 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d } else { mixer_enable(&mixer); } - ssp1_set_mode_max5864(); max5864_rx(&max5864); - ssp1_set_mode_max283x(); max283x_rx(&max283x); break; @@ -564,9 +558,7 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d case RF_PATH_DIRECTION_RX_CALIBRATION: rf_path->switchctrl &= ~SWITCHCTRL_TX; mixer_disable(&mixer); - ssp1_set_mode_max5864(); max5864_xcvr(&max5864); - ssp1_set_mode_max283x(); if (direction == RF_PATH_DIRECTION_TX_CALIBRATION) { max283x_tx_calibration(&max283x); } else { @@ -581,9 +573,7 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d /* Set RF path to receive direction when "off" */ rf_path->switchctrl &= ~SWITCHCTRL_TX; mixer_disable(&mixer); - ssp1_set_mode_max5864(); max5864_standby(&max5864); - ssp1_set_mode_max283x(); max283x_set_mode(&max283x, MAX283x_MODE_STANDBY); break; } diff --git a/firmware/common/rffc5071.c b/firmware/common/rffc5071.c index 2a1866d2..0644a1fa 100644 --- a/firmware/common/rffc5071.c +++ b/firmware/common/rffc5071.c @@ -202,7 +202,7 @@ static uint16_t rffc5071_spi_read(rffc5071_driver_t* const drv, uint8_t r) (void) drv; uint16_t data[] = {0x80 | (r & 0x7f), 0xffff}; - spi_bus_transfer(drv->bus, data, 2); + spi_bus_transfer(drv->bus, drv->bus->config, data, 2); return data[1]; } @@ -211,7 +211,7 @@ static void rffc5071_spi_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t (void) drv; uint16_t data[] = {0x00 | (r & 0x7f), v}; - spi_bus_transfer(drv->bus, data, 2); + spi_bus_transfer(drv->bus, drv->bus->config, data, 2); } uint16_t rffc5071_reg_read(rffc5071_driver_t* const drv, uint8_t r) diff --git a/firmware/common/spi_bus.c b/firmware/common/spi_bus.c index 1b05c298..a667db8a 100644 --- a/firmware/common/spi_bus.c +++ b/firmware/common/spi_bus.c @@ -31,17 +31,29 @@ void spi_bus_start(spi_bus_t* const bus, const void* const config) void spi_bus_stop(spi_bus_t* const bus) { bus->stop(bus); + bus->config = NULL; } -void spi_bus_transfer(spi_bus_t* const bus, void* const data, const size_t count) +void spi_bus_transfer( + spi_bus_t* const bus, + const void* const config, + void* const data, + const size_t count) { + if (config != bus->config) { + spi_bus_start(bus, config); + } bus->transfer(bus, data, count); } void spi_bus_transfer_gather( spi_bus_t* const bus, + const void* const config, const spi_transfer_t* const transfers, const size_t count) { + if (config != bus->config) { + spi_bus_start(bus, config); + } bus->transfer_gather(bus, transfers, count); } diff --git a/firmware/common/spi_bus.h b/firmware/common/spi_bus.h index 07d5787d..bc9c7dee 100644 --- a/firmware/common/spi_bus.h +++ b/firmware/common/spi_bus.h @@ -46,8 +46,13 @@ typedef struct _spi_bus_t { void spi_bus_start(spi_bus_t* const bus, const void* const config); void spi_bus_stop(spi_bus_t* const bus); -void spi_bus_transfer(spi_bus_t* const bus, void* const data, const size_t count); +void spi_bus_transfer( + spi_bus_t* const bus, + const void* const config, + void* const data, + const size_t count); void spi_bus_transfer_gather( spi_bus_t* const bus, + const void* const config, const spi_transfer_t* const transfers, const size_t count); diff --git a/firmware/common/ui_rad1o.c b/firmware/common/ui_rad1o.c index acaa2153..a6d3e544 100644 --- a/firmware/common/ui_rad1o.c +++ b/firmware/common/ui_rad1o.c @@ -26,7 +26,6 @@ #include #include "clock_gen.h" -#include "max283x.h" #include "rf_path.h" #include "transceiver_mode.h" @@ -195,9 +194,6 @@ static void ui_update(void) rad1o_lcdNl(); rad1o_lcdDisplay(); - - // Don't ask... - ssp1_set_mode_max283x(); } static void rad1o_ui_init(void) @@ -211,8 +207,6 @@ static void rad1o_ui_deinit(void) { rad1o_lcdDeInit(); enabled = false; - // Don't ask... - ssp1_set_mode_max283x(); } static void rad1o_ui_set_frequency(uint64_t frequency) diff --git a/firmware/common/w25q80bv.c b/firmware/common/w25q80bv.c index 11c6d5c0..9afa9bea 100644 --- a/firmware/common/w25q80bv.c +++ b/firmware/common/w25q80bv.c @@ -67,6 +67,22 @@ w25q80bv_driver_t spi_flash = { .target_init = w25q80bv_target_init, }; +static void w25q80bv_transfer( + w25q80bv_driver_t* const drv, + void* const data, + const size_t count) +{ + spi_bus_transfer(drv->bus, &ssp_config_w25q80bv, data, count); +} + +static void w25q80bv_transfer_gather( + w25q80bv_driver_t* const drv, + const spi_transfer_t* const transfers, + size_t size) +{ + spi_bus_transfer_gather(drv->bus, &ssp_config_w25q80bv, transfers, size); +} + /* * Set up pins for GPIO and SPI control, configure SSP0 peripheral for SPI. * SSP0_CS is controlled by GPIO in order to handle various transfer lengths. @@ -96,7 +112,7 @@ void w25q80bv_setup(w25q80bv_driver_t* const drv) uint8_t w25q80bv_get_status(w25q80bv_driver_t* const drv) { uint8_t data[] = {W25Q80BV_READ_STATUS1, 0xFF}; - spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data)); + w25q80bv_transfer(drv, data, ARRAY_SIZE(data)); return data[1]; } @@ -104,7 +120,7 @@ uint8_t w25q80bv_get_status(w25q80bv_driver_t* const drv) uint8_t w25q80bv_get_device_id(w25q80bv_driver_t* const drv) { uint8_t data[] = {W25Q80BV_DEVICE_ID, 0xFF, 0xFF, 0xFF, 0xFF}; - spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data)); + w25q80bv_transfer(drv, data, ARRAY_SIZE(data)); return data[4]; } @@ -124,7 +140,7 @@ void w25q80bv_get_unique_id(w25q80bv_driver_t* const drv, w25q80bv_unique_id_t* 0xFF, 0xFF, 0xFF}; - spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data)); + w25q80bv_transfer(drv, data, ARRAY_SIZE(data)); for (size_t i = 0; i < 8; i++) { unique_id->id_8b[i] = data[5 + i]; @@ -141,7 +157,7 @@ void w25q80bv_write_enable(w25q80bv_driver_t* const drv) w25q80bv_wait_while_busy(drv); uint8_t data[] = {W25Q80BV_WRITE_ENABLE}; - spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data)); + w25q80bv_transfer(drv, data, ARRAY_SIZE(data)); while (!(w25q80bv_get_status(drv) & W25Q80BV_STATUS_WEL)) {} } @@ -159,7 +175,7 @@ void w25q80bv_chip_erase(w25q80bv_driver_t* const drv) w25q80bv_write_enable(drv); uint8_t data[] = {W25Q80BV_CHIP_ERASE}; - spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data)); + w25q80bv_transfer(drv, data, ARRAY_SIZE(data)); } /* write up a 256 byte page or partial page */ @@ -190,7 +206,7 @@ static void w25q80bv_page_program( const spi_transfer_t transfers[] = {{header, ARRAY_SIZE(header)}, {data, len}}; - spi_bus_transfer_gather(drv->bus, transfers, ARRAY_SIZE(transfers)); + w25q80bv_transfer_gather(drv, transfers, ARRAY_SIZE(transfers)); } /* write an arbitrary number of bytes */ @@ -265,7 +281,7 @@ void w25q80bv_read( const spi_transfer_t transfers[] = {{header, ARRAY_SIZE(header)}, {data, len}}; - spi_bus_transfer_gather(drv->bus, transfers, ARRAY_SIZE(transfers)); + w25q80bv_transfer_gather(drv, transfers, ARRAY_SIZE(transfers)); } void w25q80bv_clear_status(w25q80bv_driver_t* const drv) @@ -273,16 +289,16 @@ void w25q80bv_clear_status(w25q80bv_driver_t* const drv) w25q80bv_wait_while_busy(drv); w25q80bv_write_enable(drv); uint8_t data[] = {W25Q80BV_WRITE_STATUS, 0x00, 0x00}; - spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data)); + w25q80bv_transfer(drv, data, ARRAY_SIZE(data)); } void w25q80bv_get_full_status(w25q80bv_driver_t* const drv, uint8_t* data) { uint8_t cmd[] = {W25Q80BV_READ_STATUS1, 0xFF}; - spi_bus_transfer(drv->bus, cmd, ARRAY_SIZE(cmd)); + w25q80bv_transfer(drv, cmd, ARRAY_SIZE(cmd)); data[0] = cmd[1]; cmd[0] = W25Q80BV_READ_STATUS2; cmd[1] = 0xFF; - spi_bus_transfer(drv->bus, cmd, ARRAY_SIZE(cmd)); + w25q80bv_transfer(drv, cmd, ARRAY_SIZE(cmd)); data[1] = cmd[1]; } From ff052af3526dd915ac8a0728a2196ab58fc0cf16 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 8 Jun 2026 16:17:11 +0100 Subject: [PATCH 149/173] si5351c: Move the clk array into the driver struct. --- firmware/common/si5351c.c | 74 +++++++++++++++++++-------------------- firmware/common/si5351c.h | 15 ++++---- 2 files changed, 44 insertions(+), 45 deletions(-) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 353a4d2e..2fe487a3 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -51,8 +51,6 @@ si5351c_driver_t si5351c = { .i2c_address = 0x60, }; -si5351c_clk_t clk[8]; - uint8_t clkout_id; uint8_t mcu_clkin_id; @@ -267,12 +265,12 @@ void si5351c_configure_multisynth( void si5351c_configure_clock_control(si5351c_driver_t* const drv) { for (int i = 0; i < 8; i++) { - set_CLK_PDN(drv, i, clk[i].power_down); - set_MS_INT(drv, i, clk[i].mode); - set_MS_SRC(drv, i, clk[i].pll); - set_CLK_SRC(drv, i, clk[i].source); - set_CLK_IDRV(drv, i, clk[i].drive); - set_CLK_INV(drv, i, clk[i].invert); + set_CLK_PDN(drv, i, drv->clk[i].power_down); + set_MS_INT(drv, i, drv->clk[i].mode); + set_MS_SRC(drv, i, drv->clk[i].pll); + set_CLK_SRC(drv, i, drv->clk[i].source); + set_CLK_IDRV(drv, i, drv->clk[i].drive); + set_CLK_INV(drv, i, drv->clk[i].invert); } si5351c_regs_commit(drv); @@ -284,15 +282,15 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) set_CLK_OEB( drv, i, - clk[i].output_enable ? SI5351C_OUTPUT_ENABLE : - SI5351C_OUTPUT_DISABLE); + drv->clk[i].output_enable ? SI5351C_OUTPUT_ENABLE : + SI5351C_OUTPUT_DISABLE); } si5351c_regs_commit(drv); #ifdef IS_H1_R9 if (IS_H1_R9) { const platform_gpio_t* gpio = platform_gpio(); - if (clk[clkout_id].output_enable) { + if (drv->clk[clkout_id].output_enable) { gpio_set(gpio->h1r9_clkout_en); } else { gpio_clear(gpio->h1r9_clkout_en); @@ -355,8 +353,8 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable) { - clk[clkout_id].output_enable = enable; - clk[clkout_id].power_down = !enable; + drv->clk[clkout_id].output_enable = enable; + drv->clk[clkout_id].power_down = !enable; /* Configure clock to 10MHz */ si5351c_configure_multisynth(drv, clkout_id, 80 * 128 - 512, 0, 1, 0); @@ -375,8 +373,8 @@ void si5351c_mcu_clkin_enable(si5351c_driver_t* const drv, bool enable) #endif #ifdef IS_NOT_H1_R9 if (IS_NOT_H1_R9) { - clk[mcu_clkin_id].output_enable = enable; - clk[mcu_clkin_id].power_down = !enable; + drv->clk[mcu_clkin_id].output_enable = enable; + drv->clk[mcu_clkin_id].power_down = !enable; /* Configure clock to 40MHz */ if (mcu_clkin_id >= 6) { @@ -485,7 +483,7 @@ void si5351c_init(si5351c_driver_t* const drv) }; /* CLK0: MAX5864/CPLD */ - clk[0] = (si5351c_clk_t){ + drv->clk[0] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_FRAC, .pll = SI5351C_PLL_A, @@ -493,7 +491,7 @@ void si5351c_init(si5351c_driver_t* const drv) .drive = SI5351C_DRIVE_8MA, }; /* CLK1: CPLD */ - clk[1] = (si5351c_clk_t){ + drv->clk[1] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_INT, .pll = SI5351C_PLL_A, @@ -502,7 +500,7 @@ void si5351c_init(si5351c_driver_t* const drv) .invert = true, }; /* CLK2: SGPIO */ - clk[2] = (si5351c_clk_t){ + drv->clk[2] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_INT, .pll = SI5351C_PLL_A, @@ -510,10 +508,10 @@ void si5351c_init(si5351c_driver_t* const drv) .drive = SI5351C_DRIVE_2MA, }; /* CLK3: CLKOUT */ - clk[3] = clkout; + drv->clk[3] = clkout; clkout_id = 3; /* CLK4: RFFC5072 (MAX2837 on rad1o) */ - clk[4] = (si5351c_clk_t){ + drv->clk[4] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_INT, .pll = SI5351C_PLL_A, @@ -522,7 +520,7 @@ void si5351c_init(si5351c_driver_t* const drv) .invert = true, }; /* CLK5: MAX2837 (MAX2871 on rad1o) */ - clk[5] = (si5351c_clk_t){ + drv->clk[5] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_INT, .pll = SI5351C_PLL_A, @@ -530,15 +528,15 @@ void si5351c_init(si5351c_driver_t* const drv) .drive = SI5351C_DRIVE_4MA, }; /* CLK6: none */ - clk[6] = powered_down; + drv->clk[6] = powered_down; /* CLK7: LPC43xx */ - clk[7] = mcu_clkin; + drv->clk[7] = mcu_clkin; mcu_clkin_id = 7; #ifdef IS_H1_R9 if (IS_H1_R9) { /* CLK0: MAX5864/CPLD/SGPIO (sample clocks) */ - clk[0] = (si5351c_clk_t){ + drv->clk[0] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_INT, .pll = SI5351C_PLL_A, @@ -546,7 +544,7 @@ void si5351c_init(si5351c_driver_t* const drv) .drive = SI5351C_DRIVE_6MA, }; /* CLK1: RFFC5072/MAX2839 */ - clk[1] = (si5351c_clk_t){ + drv->clk[1] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_FRAC, .pll = SI5351C_PLL_A, @@ -554,21 +552,21 @@ void si5351c_init(si5351c_driver_t* const drv) .drive = SI5351C_DRIVE_4MA, }; /* CLK2: CLKOUT/LPC4320 */ - clk[2] = clkout; + drv->clk[2] = clkout; clkout_id = 2; mcu_clkin_id = 2; /* Other outputs not present on Si5351A */ - clk[3] = powered_down; - clk[4] = powered_down; - clk[5] = powered_down; - clk[6] = powered_down; - clk[7] = powered_down; + drv->clk[3] = powered_down; + drv->clk[4] = powered_down; + drv->clk[5] = powered_down; + drv->clk[6] = powered_down; + drv->clk[7] = powered_down; } #endif #ifdef IS_PRALINE if (IS_PRALINE) { /* CLK0: AFE_CLK */ - clk[0] = (si5351c_clk_t){ + drv->clk[0] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_FRAC, .pll = SI5351C_PLL_A, @@ -576,7 +574,7 @@ void si5351c_init(si5351c_driver_t* const drv) .drive = SI5351C_DRIVE_4MA, }; /* CLK1: SCT_CLK and FPGA_CLK */ - clk[1] = (si5351c_clk_t){ + drv->clk[1] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_FRAC, .pll = SI5351C_PLL_A, @@ -585,10 +583,10 @@ void si5351c_init(si5351c_driver_t* const drv) }; /* CLK3: CLKOUT */ clkout.pll = SI5351C_PLL_B; - clk[3] = clkout; + drv->clk[3] = clkout; clkout_id = 3; /* CLK4: XCVR_CLK */ - clk[4] = (si5351c_clk_t){ + drv->clk[4] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_INT, .pll = SI5351C_PLL_B, @@ -597,7 +595,7 @@ void si5351c_init(si5351c_driver_t* const drv) .invert = true, }; /* CLK5: MIX_CLK */ - clk[5] = (si5351c_clk_t){ + drv->clk[5] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_INT, .pll = SI5351C_PLL_B, @@ -606,7 +604,7 @@ void si5351c_init(si5351c_driver_t* const drv) }; if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ - clk[2] = (si5351c_clk_t){ + drv->clk[2] = (si5351c_clk_t){ .output_enable = true, .mode = SI5351C_MODE_FRAC, .pll = SI5351C_PLL_A, @@ -615,7 +613,7 @@ void si5351c_init(si5351c_driver_t* const drv) }; } else { /* CLK2: MCU_CLK */ - clk[2] = mcu_clkin; + drv->clk[2] = mcu_clkin; mcu_clkin_id = 2; } } diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index dad74a17..c3a0558e 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -89,13 +89,6 @@ typedef enum { SI5351C_INPUT_CLKIN = 1, } si5351c_input_t; -typedef struct { - i2c_bus_t* const bus; - uint8_t i2c_address; - uint8_t regs[SI5351C_CACHED_REGS]; - uint32_t regs_dirty[(SI5351C_CACHED_REGS + 31) / 32]; -} si5351c_driver_t; - typedef struct { bool output_enable; bool power_down; @@ -106,6 +99,14 @@ typedef struct { bool invert; } si5351c_clk_t; +typedef struct { + i2c_bus_t* const bus; + uint8_t i2c_address; + si5351c_clk_t clk[8]; + uint8_t regs[SI5351C_CACHED_REGS]; + uint32_t regs_dirty[(SI5351C_CACHED_REGS + 31) / 32]; +} si5351c_driver_t; + void si5351c_disable_all_outputs(si5351c_driver_t* const drv); void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv); void si5351c_power_down_all_clocks(si5351c_driver_t* const drv); From a716b642eca1247a2e41a68fc594ecaa59be1f47 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 8 Jun 2026 16:24:15 +0100 Subject: [PATCH 150/173] si5351c: Move other globals into driver struct. --- firmware/common/si5351c.c | 42 +++++++++++++++++---------------------- firmware/common/si5351c.h | 4 ++++ 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 2fe487a3..9fbe8764 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -51,12 +51,6 @@ si5351c_driver_t si5351c = { .i2c_address = 0x60, }; -uint8_t clkout_id; -uint8_t mcu_clkin_id; - -static bool input_initialized = false; -static si5351c_input_t active_input; - /* write to single register */ void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val) { @@ -290,7 +284,7 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) #ifdef IS_H1_R9 if (IS_H1_R9) { const platform_gpio_t* gpio = platform_gpio(); - if (drv->clk[clkout_id].output_enable) { + if (drv->clk[drv->clkout_id].output_enable) { gpio_set(gpio->h1r9_clkout_en); } else { gpio_clear(gpio->h1r9_clkout_en); @@ -310,7 +304,7 @@ void si5351c_set_int_mode( void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input) { - if (input_initialized && input == active_input) { + if (drv->input_initialized && input == drv->active_input) { return; } si5351c_disable_all_outputs(drv); @@ -334,8 +328,8 @@ void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input) } #endif si5351c_configure_pll_multisynth(drv, input); - active_input = input; - input_initialized = true; + drv->active_input = input; + drv->input_initialized = true; si5351c_reset_plls(drv, SI5351C_PLL_MASK_BOTH); } @@ -353,11 +347,11 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable) { - drv->clk[clkout_id].output_enable = enable; - drv->clk[clkout_id].power_down = !enable; + drv->clk[drv->clkout_id].output_enable = enable; + drv->clk[drv->clkout_id].power_down = !enable; /* Configure clock to 10MHz */ - si5351c_configure_multisynth(drv, clkout_id, 80 * 128 - 512, 0, 1, 0); + si5351c_configure_multisynth(drv, drv->clkout_id, 80 * 128 - 512, 0, 1, 0); si5351c_configure_clock_control(drv); si5351c_enable_clock_outputs(drv); @@ -373,18 +367,18 @@ void si5351c_mcu_clkin_enable(si5351c_driver_t* const drv, bool enable) #endif #ifdef IS_NOT_H1_R9 if (IS_NOT_H1_R9) { - drv->clk[mcu_clkin_id].output_enable = enable; - drv->clk[mcu_clkin_id].power_down = !enable; + drv->clk[drv->mcu_clkin_id].output_enable = enable; + drv->clk[drv->mcu_clkin_id].power_down = !enable; /* Configure clock to 40MHz */ - if (mcu_clkin_id >= 6) { + if (drv->mcu_clkin_id >= 6) { // MCU_CLKIN on CLK6 or CLK7, integer only MS. - si5351c_configure_multisynth(drv, mcu_clkin_id, 20, 0, 0, 0); + si5351c_configure_multisynth(drv, drv->mcu_clkin_id, 20, 0, 0, 0); } else { // MCU_CLKIN on CLK0 to CLK5, fractional-capable MS. si5351c_configure_multisynth( drv, - mcu_clkin_id, + drv->mcu_clkin_id, 20 * 128 - 512, 0, 1, @@ -509,7 +503,7 @@ void si5351c_init(si5351c_driver_t* const drv) }; /* CLK3: CLKOUT */ drv->clk[3] = clkout; - clkout_id = 3; + drv->clkout_id = 3; /* CLK4: RFFC5072 (MAX2837 on rad1o) */ drv->clk[4] = (si5351c_clk_t){ .output_enable = true, @@ -531,7 +525,7 @@ void si5351c_init(si5351c_driver_t* const drv) drv->clk[6] = powered_down; /* CLK7: LPC43xx */ drv->clk[7] = mcu_clkin; - mcu_clkin_id = 7; + drv->mcu_clkin_id = 7; #ifdef IS_H1_R9 if (IS_H1_R9) { @@ -553,8 +547,8 @@ void si5351c_init(si5351c_driver_t* const drv) }; /* CLK2: CLKOUT/LPC4320 */ drv->clk[2] = clkout; - clkout_id = 2; - mcu_clkin_id = 2; + drv->clkout_id = 2; + drv->mcu_clkin_id = 2; /* Other outputs not present on Si5351A */ drv->clk[3] = powered_down; drv->clk[4] = powered_down; @@ -584,7 +578,7 @@ void si5351c_init(si5351c_driver_t* const drv) /* CLK3: CLKOUT */ clkout.pll = SI5351C_PLL_B; drv->clk[3] = clkout; - clkout_id = 3; + drv->clkout_id = 3; /* CLK4: XCVR_CLK */ drv->clk[4] = (si5351c_clk_t){ .output_enable = true, @@ -614,7 +608,7 @@ void si5351c_init(si5351c_driver_t* const drv) } else { /* CLK2: MCU_CLK */ drv->clk[2] = mcu_clkin; - mcu_clkin_id = 2; + drv->mcu_clkin_id = 2; } } #endif diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index c3a0558e..666a79f4 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -103,6 +103,10 @@ typedef struct { i2c_bus_t* const bus; uint8_t i2c_address; si5351c_clk_t clk[8]; + uint8_t clkout_id; + uint8_t mcu_clkin_id; + bool input_initialized; + si5351c_input_t active_input; uint8_t regs[SI5351C_CACHED_REGS]; uint32_t regs_dirty[(SI5351C_CACHED_REGS + 31) / 32]; } si5351c_driver_t; From c77c962b7185593cf975134e924e066c33791d7d Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 18 May 2026 18:25:25 +0100 Subject: [PATCH 151/173] Move max283x-related bits out of pins_setup. --- firmware/common/max283x.c | 5 +++++ firmware/common/pins.c | 14 -------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c index c0478ae0..f9486921 100644 --- a/firmware/common/max283x.c +++ b/firmware/common/max283x.c @@ -26,6 +26,8 @@ #include #include +#include + #include "fixed_point.h" #include "platform_detect.h" #include "platform_gpio.h" @@ -88,8 +90,10 @@ void max283x_setup(max283x_driver_t* const drv) const platform_gpio_t* gpio = platform_gpio(); /* MAX283x GPIO PinMux */ + ssp_config_max283x.gpio_select = gpio->max283x_select; #ifdef IS_PRALINE if (IS_PRALINE) { + ssp_config_max283x.data_bits = SSP_DATA_9BITS; drv->type = MAX2831_VARIANT; max2831.gpio_enable = gpio->max283x_enable; max2831.gpio_rxtx = gpio->max283x_rx_enable; @@ -101,6 +105,7 @@ void max283x_setup(max283x_driver_t* const drv) #endif #ifdef IS_NOT_PRALINE if (IS_NOT_PRALINE) { + ssp_config_max283x.data_bits = SSP_DATA_16BITS; #ifdef IS_H1_R9 if (IS_H1_R9) { drv->type = MAX2839_VARIANT; diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 1f98fd9a..3fcbf037 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -24,12 +24,10 @@ #include "pins.h" #include -#include #include "cpld_jtag.h" #include "gpio.h" #include "leds.h" -#include "max283x.h" #include "max5864.h" #include "mixer.h" #include "platform_detect.h" @@ -266,18 +264,6 @@ void pins_setup(void) #endif /* Configure drivers and driver pins */ - ssp_config_max283x.gpio_select = gpio->max283x_select; -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - ssp_config_max283x.data_bits = SSP_DATA_16BITS; - } -#endif -#ifdef IS_PRALINE - if (IS_PRALINE) { - ssp_config_max283x.data_bits = SSP_DATA_9BITS; // send 2 words - } -#endif - ssp_config_max5864.gpio_select = gpio->max5864_select; ssp_config_w25q80bv.gpio_select = gpio->w25q80bv_select; From 9d6e43a1cb611fc9bbd1917915745adae38bb23f Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 13 May 2026 23:11:47 +0100 Subject: [PATCH 152/173] Move max5864-related bits out of pins_setup. --- firmware/common/max5864.c | 3 +++ firmware/common/pins.c | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/common/max5864.c b/firmware/common/max5864.c index 5f3417fc..0b825fcd 100644 --- a/firmware/common/max5864.c +++ b/firmware/common/max5864.c @@ -27,6 +27,7 @@ #include #include "max5864_target.h" +#include "platform_gpio.h" #include "spi_bus.h" /* Driver instance. */ @@ -60,6 +61,8 @@ static void max5864_init(max5864_driver_t* const drv) void max5864_setup(max5864_driver_t* const drv) { + ssp_config_max5864.gpio_select = platform_gpio()->max5864_select; + max5864_init(drv); } diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 3fcbf037..d917f7d2 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -28,7 +28,6 @@ #include "cpld_jtag.h" #include "gpio.h" #include "leds.h" -#include "max5864.h" #include "mixer.h" #include "platform_detect.h" #include "platform_gpio.h" @@ -264,8 +263,6 @@ void pins_setup(void) #endif /* Configure drivers and driver pins */ - ssp_config_max5864.gpio_select = gpio->max5864_select; - ssp_config_w25q80bv.gpio_select = gpio->w25q80bv_select; spi_flash.gpio_hold = gpio->w25q80bv_hold; spi_flash.gpio_wp = gpio->w25q80bv_wp; From a7925bd74ea4229e8437991cd61af77bc306dd63 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 18 May 2026 16:31:54 +0100 Subject: [PATCH 153/173] Remove some superfluous spi_bus_setup calls. --- firmware/common/rom_iap.c | 2 -- firmware/hackrf_usb/hackrf_usb.c | 1 - firmware/hackrf_usb/usb_api_spiflash.c | 3 --- 3 files changed, 6 deletions(-) diff --git a/firmware/common/rom_iap.c b/firmware/common/rom_iap.c index 25dfa269..ce1f113b 100644 --- a/firmware/common/rom_iap.c +++ b/firmware/common/rom_iap.c @@ -23,7 +23,6 @@ #include #include "rom_iap.h" -#include "spi_bus.h" #include "w25q80bv.h" #define ROM_IAP_ADDR (0x10400100) @@ -77,7 +76,6 @@ isp_iap_ret_code_t iap_cmd_call(iap_cmd_res_t* iap_cmd_res) Alternative way to retrieve Part Id on MCU with no IAP Read Serial No => Read Unique ID in SPIFI (only compatible with W25Q80BV */ - spi_bus_start(spi_flash.bus, &ssp_config_w25q80bv); w25q80bv_setup(&spi_flash); switch (iap_cmd_res->cmd_param.command_code) { diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 5d71fa80..197ffb8a 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -275,7 +275,6 @@ extern uint32_t _binary_fpga_bin_start; void fpga_loader_setup(void) { - spi_bus_start(spi_flash.bus, &ssp_config_w25q80bv); w25q80bv_setup(&spi_flash); } diff --git a/firmware/hackrf_usb/usb_api_spiflash.c b/firmware/hackrf_usb/usb_api_spiflash.c index c698d599..21370bd0 100644 --- a/firmware/hackrf_usb/usb_api_spiflash.c +++ b/firmware/hackrf_usb/usb_api_spiflash.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -40,7 +39,6 @@ usb_request_status_t usb_vendor_request_erase_spiflash( const usb_transfer_stage_t stage) { if (stage == USB_TRANSFER_STAGE_SETUP) { - spi_bus_start(spi_flash.bus, &ssp_config_w25q80bv); w25q80bv_setup(&spi_flash); /* only chip erase is implemented */ w25q80bv_chip_erase(&spi_flash); @@ -69,7 +67,6 @@ usb_request_status_t usb_vendor_request_write_spiflash( len, NULL, NULL); - spi_bus_start(spi_flash.bus, &ssp_config_w25q80bv); w25q80bv_setup(&spi_flash); return USB_REQUEST_STATUS_OK; } From 9c0a211f1362b57a40f09f2a99ad5f9deee33516 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 18 May 2026 16:34:24 +0100 Subject: [PATCH 154/173] Move w25q80bv-related bits out of pins_setup. --- firmware/common/pins.c | 5 ----- firmware/common/w25q80bv.c | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/firmware/common/pins.c b/firmware/common/pins.c index d917f7d2..3ce27b8d 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -35,7 +35,6 @@ #include "power.h" #include "rf_path.h" #include "sgpio.h" -#include "w25q80bv.h" #ifdef IS_PRALINE #include "clock_io.h" #include "ice40_spi.h" @@ -263,10 +262,6 @@ void pins_setup(void) #endif /* Configure drivers and driver pins */ - ssp_config_w25q80bv.gpio_select = gpio->w25q80bv_select; - spi_flash.gpio_hold = gpio->w25q80bv_hold; - spi_flash.gpio_wp = gpio->w25q80bv_wp; - sgpio_config.gpio_q_invert = gpio->q_invert; #ifdef IS_NOT_PRALINE diff --git a/firmware/common/w25q80bv.c b/firmware/common/w25q80bv.c index 9afa9bea..abd18624 100644 --- a/firmware/common/w25q80bv.c +++ b/firmware/common/w25q80bv.c @@ -35,6 +35,7 @@ #include #include "platform_detect.h" +#include "platform_gpio.h" #include "w25q80bv_target.h" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) @@ -90,6 +91,11 @@ static void w25q80bv_transfer_gather( void w25q80bv_setup(w25q80bv_driver_t* const drv) { uint8_t device_id; + const platform_gpio_t* gpio = platform_gpio(); + + ssp_config_w25q80bv.gpio_select = gpio->w25q80bv_select; + spi_flash.gpio_hold = gpio->w25q80bv_hold; + spi_flash.gpio_wp = gpio->w25q80bv_wp; drv->page_len = 256U; if (detected_platform() == BOARD_ID_PRALINE) { From b533e1c5dce61c2a34571844737d6290af4433ef Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 13 May 2026 23:29:39 +0100 Subject: [PATCH 155/173] Move sgpio-related bits out of pins_setup. --- firmware/common/pins.c | 18 ------------------ firmware/common/sgpio.c | 16 ++++++++++++++++ firmware/hackrf_usb/hackrf_usb.c | 2 ++ firmware/hackrf_usb/usb_api_board_info.c | 2 ++ 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 3ce27b8d..2bd135a3 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -34,7 +34,6 @@ #include "platform_scu.h" #include "power.h" #include "rf_path.h" -#include "sgpio.h" #ifdef IS_PRALINE #include "clock_io.h" #include "ice40_spi.h" @@ -228,7 +227,6 @@ void pins_shutdown(void) rf_path_pin_shutdown(); } #endif - sgpio_pin_shutdown(&sgpio_config); /* enable input on SCL and SDA pins */ SCU_SFSI2C0 = SCU_I2C0_NOMINAL; @@ -262,14 +260,6 @@ void pins_setup(void) #endif /* Configure drivers and driver pins */ - sgpio_config.gpio_q_invert = gpio->q_invert; - -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - sgpio_config.gpio_trigger_enable = gpio->trigger_enable; - } -#endif - #ifdef IS_PRALINE if (IS_PRALINE) { ssp_config_ice40_fpga.gpio_select = gpio->fpga_cfg_spi_cs; @@ -298,12 +288,6 @@ void pins_setup(void) mixer_bus_setup(&mixer); -#ifdef IS_H1_R9 - if (IS_H1_R9) { - sgpio_config.gpio_trigger_enable = gpio->h1r9_trigger_enable; - } -#endif - // initialize rf_path struct and assign gpio's #ifdef IS_HACKRF_ONE if (IS_HACKRF_ONE) { @@ -375,6 +359,4 @@ void pins_setup(void) /* Configure external clock in */ scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1); - - sgpio_configure_pin_functions(&sgpio_config); } diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index 09a5d8cf..1528933a 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -29,6 +29,7 @@ #include #include "platform_detect.h" +#include "platform_gpio.h" #include "platform_scu.h" #include "sgpio.h" #ifdef IS_NOT_PRALINE @@ -91,8 +92,23 @@ void sgpio_pin_shutdown(sgpio_config_t* const config) void sgpio_configure_pin_functions(sgpio_config_t* const config) { + const platform_gpio_t* gpio = platform_gpio(); const platform_scu_t* scu = platform_scu(); + config->gpio_q_invert = gpio->q_invert; + +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + config->gpio_trigger_enable = gpio->trigger_enable; + } +#endif + +#ifdef IS_H1_R9 + if (IS_H1_R9) { + config->gpio_trigger_enable = gpio->h1r9_trigger_enable; + } +#endif + scu_pinmux(scu->PINMUX_SGPIO0, scu->PINMUX_SGPIO0_PINCFG); scu_pinmux(scu->PINMUX_SGPIO1, scu->PINMUX_SGPIO1_PINCFG); scu_pinmux(scu->PINMUX_SGPIO2, scu->PINMUX_SGPIO2_PINCFG); diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 197ffb8a..944b9556 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -429,11 +429,13 @@ int main(void) board_id_t board_id = detected_platform(); pins_shutdown(); + sgpio_pin_shutdown(&sgpio_config); if (board_id != BOARD_ID_RAD1O) { clock_gen_shutdown(); } delay_us_at_mhz(10000, 96); pins_setup(); + sgpio_configure_pin_functions(&sgpio_config); #ifdef IS_PRALINE if (IS_PRALINE) { enable_3v3aux_power(); diff --git a/firmware/hackrf_usb/usb_api_board_info.c b/firmware/hackrf_usb/usb_api_board_info.c index 2d017378..671cb862 100644 --- a/firmware/hackrf_usb/usb_api_board_info.c +++ b/firmware/hackrf_usb/usb_api_board_info.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -130,6 +131,7 @@ usb_request_status_t usb_vendor_request_reset( { if (stage == USB_TRANSFER_STAGE_SETUP) { pins_shutdown(); + sgpio_pin_shutdown(&sgpio_config); clock_gen_shutdown(); #ifdef IS_H1_R9 if (IS_H1_R9) { From e3ffbf68f0121bfa752577113c087691263a405b Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 13 May 2026 23:46:48 +0100 Subject: [PATCH 156/173] Move rf_path-related bits out of pins_setup. --- firmware/common/pins.c | 74 ------------------------ firmware/common/rf_path.c | 74 +++++++++++++++++++++++- firmware/hackrf_usb/hackrf_usb.c | 2 + firmware/hackrf_usb/usb_api_board_info.c | 2 + 4 files changed, 76 insertions(+), 76 deletions(-) diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 2bd135a3..97f3f413 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -33,7 +33,6 @@ #include "platform_gpio.h" #include "platform_scu.h" #include "power.h" -#include "rf_path.h" #ifdef IS_PRALINE #include "clock_io.h" #include "ice40_spi.h" @@ -204,7 +203,6 @@ void pins_shutdown(void) p2_ctrl_set(P2_SIGNAL_CLK3); p1_ctrl_set(P1_SIGNAL_CLKIN); - narrowband_filter_set(0); clkin_ctrl_set(CLKIN_SIGNAL_P1); gpio_output(gpio->p2_ctrl0); @@ -214,7 +212,6 @@ void pins_shutdown(void) gpio_output(gpio->p1_ctrl2); gpio_output(gpio->clkin_ctrl); gpio_output(gpio->pps_out); - gpio_output(gpio->aa_en); gpio_input(gpio->trigger_in); gpio_input(gpio->trigger_out); gpio_clear(gpio->fpga_cfg_spi_cs); @@ -223,8 +220,6 @@ void pins_shutdown(void) gpio_output(gpio->fpga_cfg_creset); gpio_input(gpio->fpga_cfg_cdone); gpio_input(gpio->max5864_select); - - rf_path_pin_shutdown(); } #endif @@ -288,75 +283,6 @@ void pins_setup(void) mixer_bus_setup(&mixer); - // initialize rf_path struct and assign gpio's -#ifdef IS_HACKRF_ONE - if (IS_HACKRF_ONE) { - rf_path = (rf_path_t){ - .switchctrl = 0, - .gpio_hp = gpio->hp, - .gpio_lp = gpio->lp, - .gpio_tx_mix_bp = gpio->tx_mix_bp, - .gpio_no_mix_bypass = gpio->no_mix_bypass, - .gpio_rx_mix_bp = gpio->rx_mix_bp, - .gpio_tx_amp = gpio->tx_amp, - .gpio_tx = gpio->tx, - .gpio_mix_bypass = gpio->mix_bypass, - .gpio_rx = gpio->rx, - .gpio_no_tx_amp_pwr = gpio->no_tx_amp_pwr, - .gpio_amp_bypass = gpio->amp_bypass, - .gpio_rx_amp = gpio->rx_amp, - .gpio_no_rx_amp_pwr = gpio->no_rx_amp_pwr, - }; - #ifdef IS_H1_R9 - if (IS_H1_R9) { - rf_path.gpio_rx = gpio->h1r9_rx; - rf_path.gpio_h1r9_no_ant_pwr = gpio->h1r9_no_ant_pwr; - } - #endif - } -#endif - -#ifdef IS_RAD1O - if (IS_RAD1O) { - rf_path = (rf_path_t){ - .switchctrl = 0, - .gpio_tx_rx_n = gpio->tx_rx_n, - .gpio_tx_rx = gpio->tx_rx, - .gpio_by_mix = gpio->by_mix, - .gpio_by_mix_n = gpio->by_mix_n, - .gpio_by_amp = gpio->by_amp, - .gpio_by_amp_n = gpio->by_amp_n, - .gpio_mixer_en = gpio->mixer_en, - .gpio_low_high_filt = gpio->low_high_filt, - .gpio_low_high_filt_n = gpio->low_high_filt_n, - .gpio_tx_amp = gpio->tx_amp, - .gpio_rx_lna = gpio->rx_lna, - }; - } -#endif - -#ifdef IS_PRALINE - if (IS_PRALINE) { - rf_path = (rf_path_t){ - .switchctrl = 0, - .gpio_tx_en = gpio->tx_en, - .gpio_mix_en_n = gpio->mix_en_n, - .gpio_lpf_en = gpio->lpf_en, - .gpio_rf_amp_en = gpio->rf_amp_en, - .gpio_ant_bias_en_n = gpio->ant_bias_en_n, - }; - if ((detected_revision() == BOARD_REV_PRALINE_R1_0) || - (detected_revision() == BOARD_REV_GSG_PRALINE_R1_0)) { - rf_path.gpio_mix_en_n = gpio->mix_en_n_r1_0; - } - scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PUP | SCU_CONF_FUNCTION4); - scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - } -#endif - - rf_path_pin_setup(&rf_path); - /* Configure external clock in */ scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1); } diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c index 27dd548f..51488c16 100644 --- a/firmware/common/rf_path.c +++ b/firmware/common/rf_path.c @@ -30,8 +30,6 @@ #ifdef IS_NOT_JAWBREAKER #include #include "platform_scu.h" -#endif -#ifdef IS_PRALINE #include "platform_gpio.h" #endif @@ -344,6 +342,10 @@ void rf_path_pin_shutdown(void) /* Configure RF power supply (VAA) switch */ scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + /* Disable narrowband filter. */ + narrowband_filter_set(0); + gpio_output(platform_gpio()->aa_en); } #endif } @@ -353,9 +355,77 @@ void rf_path_pin_setup(rf_path_t* const rf_path) #ifdef IS_JAWBREAKER (void) rf_path; #else + const platform_gpio_t* gpio = platform_gpio(); const platform_scu_t* scu = platform_scu(); #endif + // initialize rf_path struct and assign gpio's +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { + *rf_path = (rf_path_t){ + .switchctrl = 0, + .gpio_hp = gpio->hp, + .gpio_lp = gpio->lp, + .gpio_tx_mix_bp = gpio->tx_mix_bp, + .gpio_no_mix_bypass = gpio->no_mix_bypass, + .gpio_rx_mix_bp = gpio->rx_mix_bp, + .gpio_tx_amp = gpio->tx_amp, + .gpio_tx = gpio->tx, + .gpio_mix_bypass = gpio->mix_bypass, + .gpio_rx = gpio->rx, + .gpio_no_tx_amp_pwr = gpio->no_tx_amp_pwr, + .gpio_amp_bypass = gpio->amp_bypass, + .gpio_rx_amp = gpio->rx_amp, + .gpio_no_rx_amp_pwr = gpio->no_rx_amp_pwr, + }; + #ifdef IS_H1_R9 + if (IS_H1_R9) { + rf_path->gpio_rx = gpio->h1r9_rx; + rf_path->gpio_h1r9_no_ant_pwr = gpio->h1r9_no_ant_pwr; + } + #endif + } +#endif + +#ifdef IS_RAD1O + if (IS_RAD1O) { + *rf_path = (rf_path_t){ + .switchctrl = 0, + .gpio_tx_rx_n = gpio->tx_rx_n, + .gpio_tx_rx = gpio->tx_rx, + .gpio_by_mix = gpio->by_mix, + .gpio_by_mix_n = gpio->by_mix_n, + .gpio_by_amp = gpio->by_amp, + .gpio_by_amp_n = gpio->by_amp_n, + .gpio_mixer_en = gpio->mixer_en, + .gpio_low_high_filt = gpio->low_high_filt, + .gpio_low_high_filt_n = gpio->low_high_filt_n, + .gpio_tx_amp = gpio->tx_amp, + .gpio_rx_lna = gpio->rx_lna, + }; + } +#endif + +#ifdef IS_PRALINE + if (IS_PRALINE) { + *rf_path = (rf_path_t){ + .switchctrl = 0, + .gpio_tx_en = gpio->tx_en, + .gpio_mix_en_n = gpio->mix_en_n, + .gpio_lpf_en = gpio->lpf_en, + .gpio_rf_amp_en = gpio->rf_amp_en, + .gpio_ant_bias_en_n = gpio->ant_bias_en_n, + }; + if ((detected_revision() == BOARD_REV_PRALINE_R1_0) || + (detected_revision() == BOARD_REV_GSG_PRALINE_R1_0)) { + rf_path->gpio_mix_en_n = gpio->mix_en_n_r1_0; + } + scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PUP | SCU_CONF_FUNCTION4); + scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + } +#endif + #ifdef IS_HACKRF_ONE if (IS_HACKRF_ONE) { /* Configure RF switch control signals */ diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 944b9556..c4d32fae 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -430,12 +430,14 @@ int main(void) pins_shutdown(); sgpio_pin_shutdown(&sgpio_config); + rf_path_pin_shutdown(); if (board_id != BOARD_ID_RAD1O) { clock_gen_shutdown(); } delay_us_at_mhz(10000, 96); pins_setup(); sgpio_configure_pin_functions(&sgpio_config); + rf_path_pin_setup(&rf_path); #ifdef IS_PRALINE if (IS_PRALINE) { enable_3v3aux_power(); diff --git a/firmware/hackrf_usb/usb_api_board_info.c b/firmware/hackrf_usb/usb_api_board_info.c index 671cb862..a5bad4d8 100644 --- a/firmware/hackrf_usb/usb_api_board_info.c +++ b/firmware/hackrf_usb/usb_api_board_info.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -132,6 +133,7 @@ usb_request_status_t usb_vendor_request_reset( if (stage == USB_TRANSFER_STAGE_SETUP) { pins_shutdown(); sgpio_pin_shutdown(&sgpio_config); + rf_path_pin_shutdown(); clock_gen_shutdown(); #ifdef IS_H1_R9 if (IS_H1_R9) { From d534e1d019c6841d76a7d8a9c4f6f66c828ea719 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 18 May 2026 16:56:31 +0100 Subject: [PATCH 157/173] Move mixer-related bits out of pins_setup. --- firmware/common/pins.c | 3 --- firmware/hackrf_usb/hackrf_usb.c | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 97f3f413..b2d89b73 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -28,7 +28,6 @@ #include "cpld_jtag.h" #include "gpio.h" #include "leds.h" -#include "mixer.h" #include "platform_detect.h" #include "platform_gpio.h" #include "platform_scu.h" @@ -281,8 +280,6 @@ void pins_setup(void) } #endif - mixer_bus_setup(&mixer); - /* Configure external clock in */ scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1); } diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index c4d32fae..b6161cc8 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,6 @@ #include #endif #ifdef IS_NOT_RAD1O - #include #include #endif #ifdef IS_PRALINE @@ -436,6 +436,7 @@ int main(void) } delay_us_at_mhz(10000, 96); pins_setup(); + mixer_bus_setup(&mixer); sgpio_configure_pin_functions(&sgpio_config); rf_path_pin_setup(&rf_path); #ifdef IS_PRALINE From 0585339269c9ae4a2ae760e68c35543d4ae2acb7 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 18 May 2026 18:30:59 +0100 Subject: [PATCH 158/173] Move cpld_jtag-related bits out of pins_setup. --- firmware/common/cpld_jtag.c | 23 +++++++++++++++++++++++ firmware/common/cpld_jtag.h | 1 + firmware/common/pins.c | 18 ------------------ firmware/hackrf_usb/hackrf_usb.c | 5 ++--- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/firmware/common/cpld_jtag.c b/firmware/common/cpld_jtag.c index e6413ba4..ab998808 100644 --- a/firmware/common/cpld_jtag.c +++ b/firmware/common/cpld_jtag.c @@ -22,6 +22,7 @@ #include "cpld_jtag.h" #include "platform_detect.h" +#include "platform_gpio.h" #ifdef IS_NOT_PRALINE #include #include @@ -42,6 +43,28 @@ jtag_t jtag_cpld = { .gpio = &jtag_gpio_cpld, }; +void cpld_jtag_pin_setup(void) +{ + const platform_gpio_t* gpio = platform_gpio(); + + jtag_gpio_cpld.gpio_tck = gpio->cpld_tck; + +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + jtag_gpio_cpld.gpio_tms = gpio->cpld_tms; + jtag_gpio_cpld.gpio_tdi = gpio->cpld_tdi; + jtag_gpio_cpld.gpio_tdo = gpio->cpld_tdo; + } +#endif + +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { + jtag_gpio_cpld.gpio_pp_tms = gpio->cpld_pp_tms; + jtag_gpio_cpld.gpio_pp_tdo = gpio->cpld_pp_tdo; + } +#endif +} + void cpld_jtag_take(jtag_t* const jtag) { const jtag_gpio_t* const gpio = jtag->gpio; diff --git a/firmware/common/cpld_jtag.h b/firmware/common/cpld_jtag.h index c1f707d4..39cfc1e7 100644 --- a/firmware/common/cpld_jtag.h +++ b/firmware/common/cpld_jtag.h @@ -45,6 +45,7 @@ typedef struct { typedef void (*refill_buffer_cb)(void); +void cpld_jtag_pin_setup(void); void cpld_jtag_take(jtag_t* const jtag); void cpld_jtag_release(jtag_t* const jtag); diff --git a/firmware/common/pins.c b/firmware/common/pins.c index b2d89b73..016b4520 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -25,7 +25,6 @@ #include -#include "cpld_jtag.h" #include "gpio.h" #include "leds.h" #include "platform_detect.h" @@ -263,23 +262,6 @@ void pins_setup(void) } #endif - jtag_gpio_cpld.gpio_tck = gpio->cpld_tck; - -#ifdef IS_NOT_PRALINE - if (IS_NOT_PRALINE) { - jtag_gpio_cpld.gpio_tms = gpio->cpld_tms; - jtag_gpio_cpld.gpio_tdi = gpio->cpld_tdi; - jtag_gpio_cpld.gpio_tdo = gpio->cpld_tdo; - } -#endif - -#ifdef IS_EXPANSION_COMPATIBLE - if (IS_EXPANSION_COMPATIBLE) { - jtag_gpio_cpld.gpio_pp_tms = gpio->cpld_pp_tms; - jtag_gpio_cpld.gpio_pp_tdo = gpio->cpld_pp_tdo; - } -#endif - /* Configure external clock in */ scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1); } diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index b6161cc8..7135332e 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -67,9 +68,6 @@ #include #endif #endif -#ifdef IS_NOT_PRALINE - #include -#endif #include "usb_api_adc.h" #include "usb_api_board_info.h" @@ -436,6 +434,7 @@ int main(void) } delay_us_at_mhz(10000, 96); pins_setup(); + cpld_jtag_pin_setup(); mixer_bus_setup(&mixer); sgpio_configure_pin_functions(&sgpio_config); rf_path_pin_setup(&rf_path); From 977a9d24a625fb17709606c4772eaaa801e05c75 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 14 May 2026 16:56:23 +0100 Subject: [PATCH 159/173] Move ice40-related bits out of pins_setup. --- firmware/common/ice40_spi.c | 8 ++++++++ firmware/common/pins.c | 11 ----------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/firmware/common/ice40_spi.c b/firmware/common/ice40_spi.c index ce646fd6..c2777745 100644 --- a/firmware/common/ice40_spi.c +++ b/firmware/common/ice40_spi.c @@ -26,6 +26,7 @@ #include #include "delay.h" +#include "platform_gpio.h" #include "platform_scu.h" /* Driver instance. */ @@ -42,8 +43,15 @@ ice40_spi_driver_t ice40 = { void ice40_spi_target_init(ice40_spi_driver_t* const drv) { + const platform_gpio_t* gpio = platform_gpio(); const platform_scu_t* scu = platform_scu(); + /* Configure drivers and driver pins */ + ssp_config_ice40_fpga.gpio_select = gpio->fpga_cfg_spi_cs; + ice40.gpio_select = gpio->fpga_cfg_spi_cs; + ice40.gpio_creset = gpio->fpga_cfg_creset; + ice40.gpio_cdone = gpio->fpga_cfg_cdone; + /* Configure SSP1 Peripheral and relevant FPGA pins. */ scu_pinmux(scu->SSP1_CIPO, (SCU_SSP_IO | SCU_CONF_FUNCTION5)); scu_pinmux(scu->SSP1_COPI, (SCU_SSP_IO | SCU_CONF_FUNCTION5)); diff --git a/firmware/common/pins.c b/firmware/common/pins.c index 016b4520..51cfec0f 100644 --- a/firmware/common/pins.c +++ b/firmware/common/pins.c @@ -33,7 +33,6 @@ #include "power.h" #ifdef IS_PRALINE #include "clock_io.h" - #include "ice40_spi.h" #endif void pins_shutdown(void) @@ -252,16 +251,6 @@ void pins_setup(void) } #endif - /* Configure drivers and driver pins */ -#ifdef IS_PRALINE - if (IS_PRALINE) { - ssp_config_ice40_fpga.gpio_select = gpio->fpga_cfg_spi_cs; - ice40.gpio_select = gpio->fpga_cfg_spi_cs; - ice40.gpio_creset = gpio->fpga_cfg_creset; - ice40.gpio_cdone = gpio->fpga_cfg_cdone; - } -#endif - /* Configure external clock in */ scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1); } From 9153f3d55e2530c8e6a7fbf9ec7386ecc7e9949f Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 11 Jun 2026 20:23:35 +0100 Subject: [PATCH 160/173] Use PLL B for MCU_CLKIN on Praline. --- firmware/common/si5351c.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 136f63ea..259c062a 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -604,6 +604,9 @@ void si5351c_init(si5351c_driver_t* const drv) drv->clk[2] = mcu_clkin; drv->mcu_clkin_id = 2; } + + // Use PLL B for MCU, since we reset PLL A during sample_rate_set. + drv->clk[drv->mcu_clkin_id].pll = SI5351C_PLL_B; } #endif } From 42b7f7703dba64b34f921ec58448a06652c22fec Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 12 Jun 2026 16:00:48 +0100 Subject: [PATCH 161/173] Fix management of CLKOUT/MCU_CLK synth sharing on H1R9. --- firmware/common/si5351c.c | 47 +++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 259c062a..a455a23e 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -274,17 +274,6 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) SI5351C_OUTPUT_DISABLE); } si5351c_regs_commit(drv); - -#ifdef IS_H1_R9 - if (IS_H1_R9) { - const platform_gpio_t* gpio = platform_gpio(); - if (drv->clk[drv->clkout_id].output_enable) { - gpio_set(gpio->h1r9_clkout_en); - } else { - gpio_clear(gpio->h1r9_clkout_en); - } - } -#endif } void si5351c_set_int_mode( @@ -339,7 +328,7 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) } } -void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable) +static void si5351c_clkout_ms_enable(si5351c_driver_t* const drv, bool enable) { drv->clk[drv->clkout_id].output_enable = enable; drv->clk[drv->clkout_id].power_down = !enable; @@ -351,12 +340,42 @@ void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable) si5351c_enable_clock_outputs(drv); } +void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable) +{ +#ifdef IS_H1_R9 + if (IS_H1_R9) { + const platform_gpio_t* gpio = platform_gpio(); + + /* CLKOUT is shared with MCU_CLK, enable MS when either on. */ + bool mcu_clkin_enabled = gpio_read(gpio->h1r9_mcu_clk_en); + bool ms_needed = enable | mcu_clkin_enabled; + si5351c_clkout_ms_enable(drv, ms_needed); + + /* Set GPIO to gate CLKOUT output downstream of MS. */ + gpio_write(gpio->h1r9_clkout_en, enable); + } +#endif +#ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { + /* We have a dedicated CLKOUT multisynth. */ + si5351c_clkout_ms_enable(drv, enable); + } +#endif +} + void si5351c_mcu_clkin_enable(si5351c_driver_t* const drv, bool enable) { #ifdef IS_H1_R9 if (IS_H1_R9) { - /* MCU clock is shared with CLKOUT. */ - si5351c_clkout_enable(drv, enable); + const platform_gpio_t* gpio = platform_gpio(); + + /* MCU_CLK is shared with CLKOUT, enable MS when either on. */ + bool clkout_enabled = gpio_read(gpio->h1r9_clkout_en); + bool ms_needed = enable | clkout_enabled; + si5351c_clkout_ms_enable(drv, ms_needed); + + /* Set GPIO to gate MCU_CLK output downstream of MS. */ + gpio_write(gpio->h1r9_mcu_clk_en, enable); } #endif #ifdef IS_NOT_H1_R9 From 092d034d805b030de7b885bcc4b8d7ad151e723f Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 21 May 2026 18:55:25 +0100 Subject: [PATCH 162/173] Simplify PortaPack detection and init API. --- firmware/common/clock_gen.c | 4 ++-- firmware/common/portapack.c | 16 ++++++++-------- firmware/common/portapack.h | 8 +++----- firmware/common/ui_portapack.c | 2 +- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index 8a75074f..0cf21ab2 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -131,7 +131,7 @@ clock_source_t activate_best_clock_source(void) #ifdef IS_EXPANSION_COMPATIBLE if (IS_EXPANSION_COMPATIBLE) { /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ - if (portapack_reference_oscillator && portapack()) { + if (portapack_present()) { portapack_reference_oscillator(false); } } @@ -146,7 +146,7 @@ clock_source_t activate_best_clock_source(void) #ifdef IS_EXPANSION_COMPATIBLE if (IS_EXPANSION_COMPATIBLE) { /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ - if (portapack_reference_oscillator && portapack()) { + if (portapack_present()) { portapack_reference_oscillator(true); delay_ms(18); // for oscillator to enable. if (si5351c_clkin_signal_valid(&si5351c)) { diff --git a/firmware/common/portapack.c b/firmware/common/portapack.c index 0c3cbce2..b84bf03c 100644 --- a/firmware/common/portapack.c +++ b/firmware/common/portapack.c @@ -610,23 +610,23 @@ static bool portapack_detect(void) return idcode == 0x020A50DD || idcode == 0x00025610; } -static const portapack_t portapack_instance = {}; +static bool portapack_detected = false; -static const portapack_t* portapack_pointer = NULL; - -const portapack_t* portapack(void) +bool portapack_present(void) { - return portapack_pointer; + return portapack_detected; } -void portapack_init(void) +bool portapack_init(void) { if (portapack_detect()) { portapack_if_init(); portapack_lcd_reset(); portapack_lcd_init(); - portapack_pointer = &portapack_instance; + portapack_detected = true; } else { - portapack_pointer = NULL; + portapack_detected = false; } + + return portapack_detected; } diff --git a/firmware/common/portapack.h b/firmware/common/portapack.h index fdc369b8..3926bc3f 100644 --- a/firmware/common/portapack.h +++ b/firmware/common/portapack.h @@ -63,15 +63,13 @@ typedef struct { typedef struct { } portapack_t; -void portapack_init(void); +bool portapack_init(void); -/* If the "portapack" symbol is defined, PortaPack support is compiled in */ -/* If the portapack() call returns non-NULL, a PortaPack was detected and is initialized. */ -const portapack_t* portapack(void) __attribute__((weak)); +bool portapack_present(void); void portapack_backlight(const bool on); -void portapack_reference_oscillator(const bool on) __attribute__((weak)); +void portapack_reference_oscillator(const bool on); void portapack_fill_rectangle(const ui_rect_t rect, const ui_color_t color); diff --git a/firmware/common/ui_portapack.c b/firmware/common/ui_portapack.c index 85ffa705..3387b5eb 100644 --- a/firmware/common/ui_portapack.c +++ b/firmware/common/ui_portapack.c @@ -760,7 +760,7 @@ const hackrf_ui_t portapack_hackrf_ui = { const hackrf_ui_t* portapack_hackrf_ui_init(void) { - if (portapack()) { + if (portapack_present()) { return &portapack_hackrf_ui; } else { return NULL; From 3afd5d2acd75ba030389208b3a773c95fe9a9a48 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 15 Jun 2026 11:56:10 +0100 Subject: [PATCH 163/173] Move i2c_bus_start calls out of clock_gen_init and clock_gen_shutdown. --- firmware/common/clock_gen.c | 5 ----- firmware/hackrf_usb/hackrf_usb.c | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index 8a75074f..b3d8fd4c 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -24,8 +24,6 @@ #include #include "hackrf_ui.h" -#include "i2c_bus.h" -#include "i2c_lpc.h" #include "platform_detect.h" #include "sgpio.h" #include "si5351c.h" @@ -36,8 +34,6 @@ void clock_gen_init(void) { - i2c_bus_start(si5351c.bus, &i2c_config_fast_clock); - si5351c_init(&si5351c); si5351c_disable_all_outputs(&si5351c); si5351c_disable_oeb_pin_control(&si5351c); @@ -120,7 +116,6 @@ void clock_gen_init(void) void clock_gen_shutdown(void) { - i2c_bus_start(si5351c.bus, &i2c_config_fast_clock); si5351c_disable_all_outputs(&si5351c); si5351c_disable_oeb_pin_control(&si5351c); si5351c_power_down_all_clocks(&si5351c); diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index ce24d879..6bc00417 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include #include @@ -426,6 +428,8 @@ int main(void) detect_hardware_platform(); board_id_t board_id = detected_platform(); + i2c_bus_start(&i2c0, &i2c_config_fast_clock); + pins_shutdown(); sgpio_pin_shutdown(&sgpio_config); rf_path_pin_shutdown(); From d6913807a658f361456f13aab10f366808f5ccd6 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 15 Jun 2026 11:56:40 +0100 Subject: [PATCH 164/173] Specify I2C clock in kHz, and adjust for CPU clock changes. --- firmware/common/i2c_lpc.c | 8 ++++++-- firmware/common/i2c_lpc.h | 2 +- firmware/hackrf_usb/hackrf_usb.c | 3 +++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/firmware/common/i2c_lpc.c b/firmware/common/i2c_lpc.c index 9d6499fa..2c0fc5e2 100644 --- a/firmware/common/i2c_lpc.c +++ b/firmware/common/i2c_lpc.c @@ -25,6 +25,8 @@ #include #include +#include "cpu_clock.h" + /* Driver instances. */ i2c_bus_t i2c0 = { .obj = (void*) I2C0_BASE, @@ -41,7 +43,7 @@ i2c_bus_t i2c1 = { }; const i2c_lpc_config_t i2c_config_fast_clock = { - .duty_cycle_count = 255, + .clock_khz = 400, }; /* FIXME return i2c0 status from each function */ @@ -51,7 +53,9 @@ void i2c_lpc_start(i2c_bus_t* const bus, const void* const _config) const i2c_lpc_config_t* const config = _config; const uint32_t port = (uint32_t) bus->obj; - i2c_init(port, config->duty_cycle_count); + const uint16_t duty_cycle_count = + (cpu_clock_mhz * (2000 / config->clock_khz)) / 4; + i2c_init(port, duty_cycle_count); } void i2c_lpc_stop(i2c_bus_t* const bus) diff --git a/firmware/common/i2c_lpc.h b/firmware/common/i2c_lpc.h index e71cb362..2a2562ee 100644 --- a/firmware/common/i2c_lpc.h +++ b/firmware/common/i2c_lpc.h @@ -29,7 +29,7 @@ #include "i2c_bus.h" typedef struct { - const uint16_t duty_cycle_count; + const uint16_t clock_khz; } i2c_lpc_config_t; void i2c_lpc_start(i2c_bus_t* const bus, const void* const config); diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 6bc00417..813a9453 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -491,6 +491,9 @@ int main(void) #endif cpu_clock_init(); + /* Clock speed has changed, adjust I2C clock */ + i2c_bus_start(&i2c0, &i2c_config_fast_clock); + /* Wake the M0 */ ipc_halt_m0(); ipc_start_m0((uint32_t) &__ram_m0_start__); From aa76109b6ac96d7b2d2ad45cc5406eefbc435c35 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 15 Jun 2026 13:23:11 +0100 Subject: [PATCH 165/173] CI: Bump actions/checkout to v6. --- .github/workflows/build.yml | 4 ++-- .github/workflows/clang-format-check.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02239f82..6db34ce5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,7 +57,7 @@ jobs: shell: '${{ matrix.sys.shell }} {0}' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup cmake uses: lukka/get-cmake@latest @@ -179,7 +179,7 @@ jobs: runs-on: ${{ matrix.os }}-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 0bf33429..d498d609 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -16,7 +16,7 @@ jobs: - check: 'firmware/hackrf_usb' exclude: '' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Run clang-format-action uses: jidicula/clang-format-action@v4.6.2 with: From 73562ded31e91db216a99d2dc5383a9a6ee388a4 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 15 Jun 2026 13:59:17 +0100 Subject: [PATCH 166/173] CI: Simplify host build matrix and specify CMake generators. --- .github/workflows/build.yml | 95 ++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 38 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6db34ce5..e869824d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,44 +17,63 @@ jobs: host: strategy: matrix: - sys: - - {os: 'macos', shell: 'bash'} - - {os: 'ubuntu', shell: 'bash'} - - {os: 'windows', shell: 'pwsh'} - - {os: 'windows', shell: 'msys2'} - cmake: ['3.10.0', '3.16.0', '3.21.0', '4.0.0', 'latest'] + os: ['macos', 'ubuntu', 'windows'] + cmake: ['3.10.0', 'latest'] + compiler: ['gcc', 'msvc'] exclude: - # GitHub runners use Visual Studio 2022. Support added in CMake 3.21. - - sys: {os: 'windows', shell: 'pwsh'} + # MSVC only runs on Windows. + - os: 'macos' + compiler: 'msvc' + - os: 'ubuntu' + compiler: 'msvc' + # Only use the latest CMake on Windows. For MSVC, we can't use + # older CMake versions since the GitHub runners only have the + # latest Visual Studio release installed, and older CMake VS + # generators won't work. For GCC, we get curent CMake from the + # MSYS2 packages. + - os: 'windows' cmake: '3.10.0' - - sys: {os: 'windows', shell: 'pwsh'} - cmake: '3.16.0' - # MSYS2 always supplies the latest cmake. - - sys: {os: 'windows', shell: 'msys2'} - cmake: '3.10.0' - - sys: {os: 'windows', shell: 'msys2'} - cmake: '3.16.0' - - sys: {os: 'windows', shell: 'msys2'} - cmake: '3.21.0' - - sys: {os: 'windows', shell: 'msys2'} - cmake: '4.0.0' include: - - sys: {os: 'windows', shell: 'pwsh'} + # Shell selection + - os: 'macos' + shell: 'bash' + - os: 'ubuntu' + shell: 'bash' + - os: 'windows' + compiler: 'msvc' + shell: 'pwsh' + - os: 'windows' + compiler: 'gcc' + shell: 'msys2' + # CMake generator selection + - os: 'macos' + generator: 'Unix Makefiles' + - os: 'ubuntu' + generator: 'Unix Makefiles' + - os: 'windows' + compiler: 'msvc' + generator: 'Visual Studio 18 2026' + - os: 'windows' + compiler: 'gcc' + generator: 'MSYS Makefiles' + - os: 'windows' + compiler: 'msvc' cmake_args: >- -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake --install-prefix=$env:GITHUB_WORKSPACE/install - - sys: {os: 'windows', shell: 'msys2'} + - os: 'windows' + compiler: 'gcc' cmake_args: >- --install-prefix=/usr/local # Don't cancel all builds when one fails fail-fast: false - runs-on: ${{ matrix.sys.os }}-latest + runs-on: ${{ matrix.os }}-latest defaults: run: - shell: '${{ matrix.sys.shell }} {0}' + shell: '${{ matrix.shell }} {0}' steps: - uses: actions/checkout@v6 @@ -63,24 +82,24 @@ jobs: uses: lukka/get-cmake@latest with: cmakeVersion: ${{ matrix.cmake }} - if: matrix.sys.shell != 'msys2' + if: matrix.shell != 'msys2' - name: Install dependencies (macOS) run: brew install fftw - if: matrix.sys.os == 'macos' + if: matrix.os == 'macos' - name: Install dependencies (Ubuntu) run: | sudo apt update sudo apt install libfftw3-dev libusb-1.0-0-dev - if: matrix.sys.os == 'ubuntu' + if: matrix.os == 'ubuntu' - name: Install dependencies (Windows) run: vcpkg install --triplet=x64-windows libusb fftw3 pthreads pkgconf - if: matrix.sys.os == 'windows' && matrix.sys.shell != 'msys2' + if: matrix.os == 'windows' && matrix.shell != 'msys2' - name: Setup MSYS (Windows) - if: matrix.sys.os == 'windows' && matrix.sys.shell == 'msys2' + if: matrix.os == 'windows' && matrix.shell == 'msys2' uses: msys2/setup-msys2@v2 with: msystem: UCRT64 @@ -98,7 +117,7 @@ jobs: run: | cmake -E make_directory host/build cd host/build - cmake .. -DCMAKE_BUILD_TYPE=Release ${{matrix.cmake_args}} + cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{matrix.cmake_args}} cmake --build . --config Release # Build libhackrf ONLY @@ -107,22 +126,22 @@ jobs: run: | cmake -E make_directory host/libhackrf/build cd host/libhackrf/build - cmake .. -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }} + cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }} cmake --build . --config Release - name: Install (libhackrf) run: | sudo cmake --install host/libhackrf/build --config Release - if: matrix.sys.os != 'windows' && matrix.cmake != '3.10.0' + if: matrix.os != 'windows' && matrix.cmake != '3.10.0' - name: Install (libhackrf, CMake 3.10) run: | sudo cmake --build host/libhackrf/build --target install --config Release - if: matrix.sys.os != 'windows' && matrix.cmake == '3.10.0' + if: matrix.os != 'windows' && matrix.cmake == '3.10.0' - name: Install (libhackrf, Windows) run: cmake --install host/libhackrf/build --config Release - if: matrix.sys.os == 'windows' + if: matrix.os == 'windows' # Build hackrf-tools ONLY @@ -130,22 +149,22 @@ jobs: run: | cmake -E make_directory host/hackrf-tools/build cd host/hackrf-tools/build - cmake .. -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }} + cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }} cmake --build . --config Release - name: Install (hackrf-tools) run: | sudo cmake --install host/hackrf-tools/build --config Release - if: matrix.sys.os != 'windows' && matrix.cmake != '3.10.0' + if: matrix.os != 'windows' && matrix.cmake != '3.10.0' - name: Install (hackrf-tools, CMake 3.10) run: | sudo cmake --build host/hackrf-tools/build --target install --config Release - if: matrix.sys.os != 'windows' && matrix.cmake == '3.10.0' + if: matrix.os != 'windows' && matrix.cmake == '3.10.0' - name: Install (hackrf-tools, Windows) run: cmake --install host/hackrf-tools/build --config Release - if: matrix.sys.os == 'windows' + if: matrix.os == 'windows' # Publish the contents of install/bin (which should be the combination libhackrf and host-tools) for Windows - name: Publish Artifacts (Windows) @@ -153,7 +172,7 @@ jobs: with: name: hackrf-tools-windows path: ${{github.workspace}}/install/bin - if: matrix.sys.os == 'windows' && matrix.cmake == 'latest' && matrix.sys.shell == 'pwsh' + if: matrix.os == 'windows' && matrix.cmake == 'latest' && matrix.shell == 'pwsh' firmware: strategy: From 3e274a881d51504189bafebe0f2ef5bf38c79de1 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 15 Jun 2026 15:07:06 +0100 Subject: [PATCH 167/173] CI: Simplify install steps. --- .github/workflows/build.yml | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e869824d..64aa1bee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,6 +66,19 @@ jobs: compiler: 'gcc' cmake_args: >- --install-prefix=/usr/local + # CMake 3.10 uses --build with --target install + - cmake: '3.10.0' + install_cmd: --build + install_args: --target install + # Later versions just use --install + - cmake: 'latest' + install_cmd: --install + # We need sudo on MacOS and Ubuntu only + - os: macos + sudo: sudo + - os: ubuntu + sudo: sudo + # Don't cancel all builds when one fails fail-fast: false @@ -131,17 +144,7 @@ jobs: - name: Install (libhackrf) run: | - sudo cmake --install host/libhackrf/build --config Release - if: matrix.os != 'windows' && matrix.cmake != '3.10.0' - - - name: Install (libhackrf, CMake 3.10) - run: | - sudo cmake --build host/libhackrf/build --target install --config Release - if: matrix.os != 'windows' && matrix.cmake == '3.10.0' - - - name: Install (libhackrf, Windows) - run: cmake --install host/libhackrf/build --config Release - if: matrix.os == 'windows' + ${{ matrix.sudo }} cmake ${{ matrix.install_cmd }} host/libhackrf/build ${{ matrix.install_args }} --config Release # Build hackrf-tools ONLY @@ -154,17 +157,7 @@ jobs: - name: Install (hackrf-tools) run: | - sudo cmake --install host/hackrf-tools/build --config Release - if: matrix.os != 'windows' && matrix.cmake != '3.10.0' - - - name: Install (hackrf-tools, CMake 3.10) - run: | - sudo cmake --build host/hackrf-tools/build --target install --config Release - if: matrix.os != 'windows' && matrix.cmake == '3.10.0' - - - name: Install (hackrf-tools, Windows) - run: cmake --install host/hackrf-tools/build --config Release - if: matrix.os == 'windows' + ${{ matrix.sudo }} cmake ${{ matrix.install_cmd }} host/hackrf-tools/build ${{ matrix.install_args }} # Publish the contents of install/bin (which should be the combination libhackrf and host-tools) for Windows - name: Publish Artifacts (Windows) From eb8bc546a84b2774f1131bfb9b8d21b742b8d1e0 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 15 Jun 2026 15:32:28 +0100 Subject: [PATCH 168/173] CI: Build with more CMake and Visual Studio versions. --- .github/workflows/build.yml | 41 +++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64aa1bee..21593d8a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: os: ['macos', 'ubuntu', 'windows'] - cmake: ['3.10.0', 'latest'] + cmake: ['3.10.0', '3.21.7', '4.2.6', 'latest'] compiler: ['gcc', 'msvc'] exclude: # MSVC only runs on Windows. @@ -26,13 +26,14 @@ jobs: compiler: 'msvc' - os: 'ubuntu' compiler: 'msvc' - # Only use the latest CMake on Windows. For MSVC, we can't use - # older CMake versions since the GitHub runners only have the - # latest Visual Studio release installed, and older CMake VS - # generators won't work. For GCC, we get curent CMake from the - # MSYS2 packages. + # We can't use the oldest CMake versions on Windows. - os: 'windows' cmake: '3.10.0' + # The in-between CMake versions aren't so intersting with GCC. + - compiler: 'gcc' + cmake: '3.21.7' + - compiler: 'gcc' + cmake: '4.2.6' include: # Shell selection - os: 'macos' @@ -45,17 +46,31 @@ jobs: - os: 'windows' compiler: 'gcc' shell: 'msys2' + # MSVC version selection + - cmake: '3.21.7' + msvc: '17.14' + - cmake: '4.2.6' + msvc: 'latest' + - cmake: 'latest' + msvc: 'latest' # CMake generator selection - os: 'macos' generator: 'Unix Makefiles' - os: 'ubuntu' generator: 'Unix Makefiles' - - os: 'windows' - compiler: 'msvc' + - compiler: 'msvc' + cmake: '3.21.7' + generator: 'Visual Studio 17 2022' + - compiler: 'msvc' + cmake: '4.2.6' + generator: 'Visual Studio 18 2026' + - compiler: 'msvc' + cmake: 'latest' generator: 'Visual Studio 18 2026' - os: 'windows' compiler: 'gcc' generator: 'MSYS Makefiles' + # Additional CMake arguments - os: 'windows' compiler: 'msvc' cmake_args: >- @@ -71,6 +86,10 @@ jobs: install_cmd: --build install_args: --target install # Later versions just use --install + - cmake: '3.21.7' + install_cmd: --install + - cmake: '4.2.6' + install_cmd: --install - cmake: 'latest' install_cmd: --install # We need sudo on MacOS and Ubuntu only @@ -97,6 +116,12 @@ jobs: cmakeVersion: ${{ matrix.cmake }} if: matrix.shell != 'msys2' + - name: Install MSVC (Windows) + uses: k3DW/setup-msvc@v1 + with: + vs-version: ${{ matrix.msvc }} + if: matrix.compiler == 'msvc' && matrix.msvc != 'latest' + - name: Install dependencies (macOS) run: brew install fftw if: matrix.os == 'macos' From 74c3b730fa95163bcd4a3dbff9f3b2d60e174d22 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 29 Jun 2026 19:28:50 +0100 Subject: [PATCH 169/173] Add 1.63 fixed point type. --- firmware/common/fixed_point.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/firmware/common/fixed_point.h b/firmware/common/fixed_point.h index b6908797..974e34d2 100644 --- a/firmware/common/fixed_point.h +++ b/firmware/common/fixed_point.h @@ -54,3 +54,20 @@ typedef uint64_t fp_28_36_t; #define SR_FP_MHZ(mhz) (mhz##ULL * SR_FP_ONE_MHZ) #define SR_FP_KHZ(khz) (khz##ULL * SR_FP_ONE_KHZ) #define SR_FP_HZ(hz) (hz##ULL * SR_FP_ONE_HZ) + +/* 1.63 fixed point */ +typedef uint64_t fp_1_63_t; + +#define FRAC_ONE (1ULL << 63) + +/* one hundredth in 1.63 fixed point */ +#define FRAC_ONE_PERCENT (FRAC_ONE / (100ULL)) + +/* one thousandth in 1.63 fixed point */ +#define FRAC_ONE_PERMILLE (FRAC_ONE / (1000ULL)) + +/* one millionth in 1.63 fixed point */ +#define FRAC_ONE_PPM (FRAC_ONE / (1000000ULL)) + +/* one billionth in 1.63 fixed point */ +#define FRAC_ONE_PPB (FRAC_ONE / (1000000000ULL)) From dda161cb1f588bc7bc6a1888958b2d6f014f4f49 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 30 Jun 2026 01:56:12 +0100 Subject: [PATCH 170/173] Add multiply & divide implementations for 128-bit unsigned integers. --- firmware/common/u128.c | 88 ++++++++++++++++++++++++++++++++++++ firmware/common/u128.h | 32 +++++++++++++ firmware/hackrf-common.cmake | 1 + 3 files changed, 121 insertions(+) create mode 100644 firmware/common/u128.c create mode 100644 firmware/common/u128.h diff --git a/firmware/common/u128.c b/firmware/common/u128.c new file mode 100644 index 00000000..3543f332 --- /dev/null +++ b/firmware/common/u128.c @@ -0,0 +1,88 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "u128.h" + +/* From https://stackoverflow.com/a/58381061 */ + +u128 u128_multiply(uint64_t lhs, uint64_t rhs) +{ + uint64_t lo_lo = (lhs & 0xFFFFFFFF) * (rhs & 0xFFFFFFFF); + uint64_t hi_lo = (lhs >> 32) * (rhs & 0xFFFFFFFF); + uint64_t lo_hi = (lhs & 0xFFFFFFFF) * (rhs >> 32); + uint64_t hi_hi = (lhs >> 32) * (rhs >> 32); + uint64_t cross = ((lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi); + return (u128){ + .hi = ((hi_lo >> 32) + (cross >> 32) + hi_hi), + .lo = ((cross << 32) | (lo_lo & 0xFFFFFFFF)), + }; +} + +/* From https://stackoverflow.com/a/70052545 */ + +/* clang-format off */ + +#define SUBCcc(a,b,cy,t0,t1,t2) \ + (t0=(b)+cy, t1=(a), cy=t0= divisor + rem.lo = tmp.lo; + rem.hi = tmp.hi; + quot.lo |= 1; + } + } + + return quot; +} diff --git a/firmware/common/u128.h b/firmware/common/u128.h new file mode 100644 index 00000000..c51dc5f5 --- /dev/null +++ b/firmware/common/u128.h @@ -0,0 +1,32 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include + +typedef struct { + uint64_t hi; + uint64_t lo; +} u128; + +u128 u128_multiply(uint64_t a, uint64_t b); +u128 u128_divide(u128 dvnd, u128 dvsr); diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index dfc67d9e..8f6698da 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -203,6 +203,7 @@ macro(DeclareTargets) ${PATH_HACKRF_FIRMWARE_COMMON}/adc.c ${PATH_HACKRF_FIRMWARE_COMMON}/da7219.c ${PATH_HACKRF_FIRMWARE_COMMON}/max283x.c + ${PATH_HACKRF_FIRMWARE_COMMON}/u128.c ) if(BOARD STREQUAL "RAD1O") From 8bfbd716da361e49bcaefb960b12ea837a2dffb8 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 30 Jun 2026 02:17:27 +0100 Subject: [PATCH 171/173] Add RADIO_CLOCK_CORRECTION register, bump USB API to 1.13. --- firmware/common/radio.c | 96 ++++++++++++++++++++++++++-- firmware/common/radio.h | 18 ++++-- firmware/hackrf_usb/usb_descriptor.c | 2 +- host/hackrf-tools/src/hackrf_debug.c | 2 +- 4 files changed, 103 insertions(+), 15 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index f8b2e514..6bf89784 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -34,6 +34,7 @@ #include "rf_path.h" #include "transceiver_mode.h" #include "tuning.h" +#include "u128.h" #ifdef IS_PRALINE #include "fpga.h" #include "tune_config.h" @@ -132,6 +133,75 @@ static uint32_t radio_update_direction(radio_t* const radio, uint64_t* bank) return (1 << RADIO_OPMODE); } +/* Apply correction to a true frequency to get an adjusted frequency. */ +static uint64_t adjust(uint64_t freq, uint64_t correction) +{ + /* skip if we can */ + if (correction == FRAC_ONE) { + return freq; + } + + u128 product = u128_multiply(freq, correction); + + /* product >> 63 */ + return (product.hi << 1) | (product.lo >> 63); +} + +/* Given an adjusted frequency and correction, return the true frequency. */ +static uint64_t unadjust(uint64_t freq, uint64_t correction) +{ + /* skip if we can */ + if (correction == FRAC_ONE) { + return freq; + } + + /* (1 << (63 + 63)) */ + u128 numerator = (u128){ + .hi = 1ULL << 62, + .lo = 0, + }; + u128 denominator = (u128){ + .hi = 0, + .lo = correction, + }; + u128 reciprocal = u128_divide(numerator, denominator); + return adjust(freq, reciprocal.lo); +} + +static uint64_t restrict_correction(uint64_t requested_correction) +{ + /* Correction should be in the range 0.99 to 1.01 */ + const uint64_t min_correction = FRAC_ONE - FRAC_ONE_PERCENT; + const uint64_t max_correction = FRAC_ONE + FRAC_ONE_PERCENT; + if (requested_correction >= min_correction && + requested_correction <= max_correction) { + return requested_correction; + } else { + return FRAC_ONE; + } +} + +static uint64_t set_lo(uint64_t freq_lo, uint64_t correction, bool apply) +{ + fp_40_24_t corrected_lo = adjust(freq_lo, correction); + fp_40_24_t achieved_lo = mixer_set_frequency(&mixer, corrected_lo, apply); + return unadjust(achieved_lo, correction); +} + +static uint64_t set_if(uint64_t freq_if, uint64_t correction, bool apply) +{ + fp_40_24_t corrected_if = adjust(freq_if, correction); + fp_40_24_t achieved_if = max283x_set_frequency(&max283x, corrected_if, apply); + return unadjust(achieved_if, correction); +} + +static uint64_t set_afe_rate(uint64_t afe_rate, uint64_t correction, bool apply) +{ + fp_28_36_t corrected_rate = adjust(afe_rate, correction); + fp_28_36_t achieved_rate = sample_rate_set(corrected_rate, apply); + return unadjust(achieved_rate, correction); +} + #define ABSOLUTE_MIN_AFE_RATE SR_FP_KHZ(200) #define ABSOLUTE_MAX_AFE_RATE SR_FP_KHZ(43600) #define MAX_SUPPORTED_AFE_RATE SR_FP_KHZ(40000) @@ -183,6 +253,9 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) bool new_n = false; const uint64_t requested_rate = bank[RADIO_SAMPLE_RATE]; + const uint64_t requested_correction = bank[RADIO_CLOCK_CORRECTION]; + + uint64_t correction = restrict_correction(requested_correction); if (requested_rate != RADIO_UNSET) { rate = MIN(requested_rate, MAX_MCU_RATE); @@ -236,7 +309,7 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) new_n = (n != previous_n); afe_rate = rate << n; - afe_rate = sample_rate_set(afe_rate, false); + afe_rate = set_afe_rate(afe_rate, correction, false); previous_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]; if ((previous_n == RADIO_UNSET) || previous_rate == RADIO_UNSET) { previous_afe_rate = RADIO_UNSET; @@ -245,7 +318,7 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank) } new_afe_rate = (afe_rate != previous_afe_rate); if (new_afe_rate) { - afe_rate = sample_rate_set(afe_rate, true); + afe_rate = set_afe_rate(afe_rate, correction, true); } rate = afe_rate >> n; @@ -373,6 +446,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) #ifdef IS_PRALINE const uint64_t requested_rotation = bank[RADIO_ROTATION]; #endif + const uint64_t requested_correction = bank[RADIO_CLOCK_CORRECTION]; const uint64_t applied_rf = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF]; const uint64_t applied_if = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF]; @@ -381,6 +455,8 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT]; const uint64_t applied_rotation = radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION]; + const uint64_t applied_correction = + radio->config[RADIO_BANK_APPLIED][RADIO_CLOCK_CORRECTION]; uint64_t freq_rf = applied_rf; uint64_t analog_rf = applied_rf; @@ -388,6 +464,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) uint64_t freq_lo = applied_lo; uint64_t img_reject = applied_img_reject; uint64_t rotation = applied_rotation; + uint64_t correction = applied_correction; uint64_t opmode = bank[RADIO_OPMODE]; if (opmode == RADIO_UNSET) { @@ -405,11 +482,12 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) img_reject = RF_PATH_FILTER_BYPASS; } } + correction = restrict_correction(requested_correction); if (requested_if != RADIO_UNSET) { - freq_if = max283x_set_frequency(&max283x, requested_if, false); + freq_if = set_if(requested_if, correction, false); } if (requested_lo != RADIO_UNSET) { - freq_lo = mixer_set_frequency(&mixer, freq_lo, false); + freq_lo = set_lo(requested_lo, correction, false); } #ifdef IS_PRALINE if (IS_PRALINE) { @@ -491,7 +569,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) freq_lo = RADIO_UNSET; } if (freq_lo != RADIO_UNSET) { - freq_lo = mixer_set_frequency(&mixer, freq_lo, false); + freq_lo = set_lo(freq_lo, correction, false); } } @@ -513,12 +591,12 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) /* Apply settings. */ if ((freq_if != applied_if) && (freq_if != RADIO_UNSET)) { - freq_if = max283x_set_frequency(&max283x, freq_if, true); + freq_if = set_if(freq_if, correction, true); radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = freq_if; changed |= (1 << RADIO_FREQUENCY_IF); } if ((freq_lo != applied_lo) && (freq_lo != RADIO_UNSET)) { - freq_lo = mixer_set_frequency(&mixer, freq_lo, true); + freq_lo = set_lo(freq_lo, correction, true); radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = freq_lo; changed |= (1 << RADIO_FREQUENCY_LO); } @@ -561,6 +639,10 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank) radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] = freq_rf; changed |= (1 << RADIO_FREQUENCY_RF); } + if ((correction != applied_correction) && (correction != RADIO_UNSET)) { + radio->config[RADIO_BANK_APPLIED][RADIO_CLOCK_CORRECTION] = correction; + changed |= (1 << RADIO_CLOCK_CORRECTION); + } return changed; } diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 821c7fea..84b18c6e 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -165,17 +165,23 @@ typedef enum { * DC block enable of type bool. */ RADIO_DC_BLOCK = 22, + /** + * Correction factor for radio reference clock, of type fp_1_63_t. + */ + RADIO_CLOCK_CORRECTION = 23, } radio_register_t; -#define RADIO_NUM_REGS (23) +#define RADIO_NUM_REGS (24) #define RADIO_UNSET (0xffffffffffffffff) /* register groups for bitfield convenience */ -#define RADIO_REG_GROUP_RATE \ - ((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_RESAMPLE_TX) | (1 << RADIO_RESAMPLE_RX)) -#define RADIO_REG_GROUP_FREQ \ - ((1 << RADIO_FREQUENCY_RF) | (1 << RADIO_FREQUENCY_IF) | \ - (1 << RADIO_FREQUENCY_LO) | (1 << RADIO_IMAGE_REJECT) | (1 << RADIO_ROTATION)) +#define RADIO_REG_GROUP_RATE \ + ((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_RESAMPLE_TX) | \ + (1 << RADIO_RESAMPLE_RX) | (1 << RADIO_CLOCK_CORRECTION)) +#define RADIO_REG_GROUP_FREQ \ + ((1 << RADIO_FREQUENCY_RF) | (1 << RADIO_FREQUENCY_IF) | \ + (1 << RADIO_FREQUENCY_LO) | (1 << RADIO_IMAGE_REJECT) | (1 << RADIO_ROTATION) | \ + (1 << RADIO_CLOCK_CORRECTION)) #define RADIO_REG_GROUP_BW \ ((1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_BB_BANDWIDTH_RX) | \ (1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF) | \ diff --git a/firmware/hackrf_usb/usb_descriptor.c b/firmware/hackrf_usb/usb_descriptor.c index 005babb3..dde22e6e 100644 --- a/firmware/hackrf_usb/usb_descriptor.c +++ b/firmware/hackrf_usb/usb_descriptor.c @@ -28,7 +28,7 @@ #define USB_VENDOR_ID (0x1D50) -#define USB_API_VERSION (0x0112) +#define USB_API_VERSION (0x0113) #define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF) diff --git a/host/hackrf-tools/src/hackrf_debug.c b/host/hackrf-tools/src/hackrf_debug.c index 52ede9cb..5f8785b5 100644 --- a/host/hackrf-tools/src/hackrf_debug.c +++ b/host/hackrf-tools/src/hackrf_debug.c @@ -501,7 +501,7 @@ int radio_read_register( return result; } -#define RADIO_NUM_REGS (23) +#define RADIO_NUM_REGS (24) int radio_read_registers(hackrf_device* device, const uint8_t bank) { From 2b7ba8d7ab5d19b1df0eb31ab57cbc238875861c Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 16 Jul 2026 12:31:31 +0200 Subject: [PATCH 172/173] radio: rename RADIO_BANK_ACTIVE to RADIO_BANK_REQUESTED --- firmware/common/radio.c | 12 ++++---- firmware/common/radio.h | 8 +++--- firmware/hackrf_usb/usb_api_sweep.c | 4 +-- firmware/hackrf_usb/usb_api_transceiver.c | 34 +++++++++++------------ 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 6bf89784..36f65cad 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -54,7 +54,7 @@ void radio_init(radio_t* const radio) } } radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF; - radio->config[RADIO_BANK_ACTIVE][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF; + radio->config[RADIO_BANK_REQUESTED][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF; radio->config[RADIO_BANK_IDLE][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF; radio->config[RADIO_BANK_RX][RADIO_OPMODE] = TRANSCEIVER_MODE_RX; radio->config[RADIO_BANK_TX][RADIO_OPMODE] = TRANSCEIVER_MODE_TX; @@ -78,7 +78,7 @@ radio_error_t radio_reg_write( } switch (bank) { - case RADIO_BANK_ACTIVE: + case RADIO_BANK_REQUESTED: mark_dirty(radio, reg); /* fall through */ case RADIO_BANK_IDLE: @@ -937,7 +937,7 @@ bool radio_update(radio_t* const radio) return false; } radio->regs_dirty = 0; - memcpy(&tmp_bank[0], &(radio->config[RADIO_BANK_ACTIVE][0]), sizeof(tmp_bank)); + memcpy(&tmp_bank[0], &(radio->config[RADIO_BANK_REQUESTED][0]), sizeof(tmp_bank)); nvic_enable_irq(NVIC_USB0_IRQ); if ((dirty & RADIO_REG_GROUP_RATE) || @@ -998,14 +998,14 @@ void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode) nvic_disable_irq(NVIC_USB0_IRQ); for (uint8_t reg = 0; reg < RADIO_NUM_REGS; reg++) { value = radio->config[source_bank][reg]; - previous = radio->config[RADIO_BANK_ACTIVE][reg]; + previous = radio->config[RADIO_BANK_REQUESTED][reg]; if ((value != RADIO_UNSET) && (value != previous)) { - radio->config[RADIO_BANK_ACTIVE][reg] = value; + radio->config[RADIO_BANK_REQUESTED][reg] = value; mark_dirty(radio, reg); } } - radio->config[RADIO_BANK_ACTIVE][RADIO_OPMODE] = mode; + radio->config[RADIO_BANK_REQUESTED][RADIO_OPMODE] = mode; mark_dirty(radio, RADIO_OPMODE); nvic_enable_irq(NVIC_USB0_IRQ); radio_update(radio); diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 84b18c6e..8e12471a 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -191,7 +191,7 @@ typedef enum { (1 << RADIO_GAIN_RX_IF) | (1 << RADIO_GAIN_RX_BB)) /** - * Register bank RADIO_BANK_ACTIVE stores the active configuration. Active + * Register bank RADIO_BANK_REQUESTED stores the active configuration. Active * register settings are copied to the applied register when applied. * * The other three banks store settings that will be applied when switching to @@ -202,7 +202,7 @@ typedef enum { */ typedef enum { RADIO_BANK_APPLIED = 0, - RADIO_BANK_ACTIVE = 1, + RADIO_BANK_REQUESTED = 1, RADIO_BANK_IDLE = 2, RADIO_BANK_RX = 3, RADIO_BANK_TX = 4, @@ -237,7 +237,7 @@ typedef struct { void radio_init(radio_t* const radio); /** - * Write to one or more registers. Writes to RADIO_BANK_ACTIVE are applied at + * Write to one or more registers. Writes to RADIO_BANK_REQUESTED are applied at * the next radio_update(). Writes to RADIO_BANK_APPLIED are not supported. */ radio_error_t radio_reg_write( @@ -255,7 +255,7 @@ uint64_t radio_reg_read( const radio_register_t reg); /** - * Apply changes requested in RADIO_BANK_ACTIVE. + * Apply changes requested in RADIO_BANK_REQUESTED. * Return true if any changes were applied. */ bool radio_update(radio_t* const radio); diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index 0da2cf6e..1bea5dd2 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -132,7 +132,7 @@ usb_request_status_t usb_vendor_request_init_sweep( radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_RF, (sweep_freq + offset) * FP_ONE_HZ); usb_transfer_schedule_ack(endpoint->in); @@ -263,7 +263,7 @@ void sweep_mode(uint32_t seq) nvic_disable_irq(NVIC_USB0_IRQ); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_RF, (sweep_freq + offset) * FP_ONE_HZ); nvic_enable_irq(NVIC_USB0_IRQ); diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index dbbbf661..e72a408b 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -94,12 +94,12 @@ usb_request_status_t usb_vendor_request_set_baseband_filter_bandwidth( (endpoint->setup.index << 16) | endpoint->setup.value; radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_BB_BANDWIDTH_TX, bandwidth); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_BB_BANDWIDTH_RX, bandwidth); usb_transfer_schedule_ack(endpoint->in); @@ -123,22 +123,22 @@ usb_request_status_t usb_vendor_request_set_freq( set_freq_params.freq_mhz * 1000000ULL + set_freq_params.freq_hz; radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_RF, freq * FP_ONE_HZ); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_IF, RADIO_UNSET); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_LO, RADIO_UNSET); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_IMAGE_REJECT, RADIO_UNSET); usb_transfer_schedule_ack(endpoint->in); @@ -160,17 +160,17 @@ usb_request_status_t usb_vendor_request_set_freq_explicit( } else if (stage == USB_TRANSFER_STAGE_DATA) { radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_IF, explicit_params.if_freq_hz * FP_ONE_HZ); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_LO, explicit_params.lo_freq_hz * FP_ONE_HZ); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_IMAGE_REJECT, explicit_params.path); usb_transfer_schedule_ack(endpoint->in); @@ -215,7 +215,7 @@ usb_request_status_t usb_vendor_request_set_sample_rate_frac( uint32_t numerator = set_sample_r_params.freq_hz; uint32_t denominator = set_sample_r_params.divider; uint64_t value = round_sample_rate(numerator, denominator); - radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_SAMPLE_RATE, value); + radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_SAMPLE_RATE, value); usb_transfer_schedule_ack(endpoint->in); } return USB_REQUEST_STATUS_OK; @@ -228,12 +228,12 @@ usb_request_status_t usb_vendor_request_set_amp_enable( if (stage == USB_TRANSFER_STAGE_SETUP) { radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_GAIN_TX_RF, endpoint->setup.value); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_GAIN_RX_RF, endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); @@ -247,7 +247,7 @@ usb_request_status_t usb_vendor_request_set_lna_gain( { if (stage == USB_TRANSFER_STAGE_SETUP) { uint8_t gain = endpoint->setup.index; - radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_GAIN_RX_IF, gain); + radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_RX_IF, gain); endpoint->buffer[0] = RADIO_OK; usb_transfer_schedule_block( endpoint->in, @@ -266,7 +266,7 @@ usb_request_status_t usb_vendor_request_set_vga_gain( { if (stage == USB_TRANSFER_STAGE_SETUP) { uint8_t gain = endpoint->setup.index; - radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_GAIN_RX_BB, gain); + radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_RX_BB, gain); endpoint->buffer[0] = RADIO_OK; usb_transfer_schedule_block( endpoint->in, @@ -285,7 +285,7 @@ usb_request_status_t usb_vendor_request_set_txvga_gain( { if (stage == USB_TRANSFER_STAGE_SETUP) { uint8_t gain = endpoint->setup.index; - radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_GAIN_TX_IF, gain); + radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_TX_IF, gain); endpoint->buffer[0] = RADIO_OK; usb_transfer_schedule_block( endpoint->in, @@ -315,7 +315,7 @@ usb_request_status_t usb_vendor_request_set_antenna_enable( if (stage == USB_TRANSFER_STAGE_SETUP) { radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_BIAS_TEE, endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); @@ -427,7 +427,7 @@ usb_request_status_t usb_vendor_request_set_hw_sync_mode( if (stage == USB_TRANSFER_STAGE_SETUP) { radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_TRIGGER, endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); From 6259086c9625ebef760f51305aae9a364a465a9a Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Tue, 21 Jul 2026 11:44:26 +0200 Subject: [PATCH 173/173] libhackrf: set a 500ms timeout for changing fpga bitstream --- host/libhackrf/src/hackrf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 7bafd5f3..4018ac63 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -57,6 +57,7 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI #define DEFAULT_REQUEST_TIMEOUT 100 #define CPLD_WRITE_TIMEOUT 10000 #define SPIFLASH_WRITE_TIMEOUT 50000 // W25Q32JV max chip erase time +#define FPGA_BITSTREAM_TIMEOUT 500 // TODO: Factor this into a shared #include so that firmware can use // the same values. @@ -3496,7 +3497,7 @@ int ADDCALL hackrf_set_fpga_bitstream(hackrf_device* device, const uint8_t index 0, NULL, 0, - DEFAULT_REQUEST_TIMEOUT); + FPGA_BITSTREAM_TIMEOUT); if (result != 0) { last_libusb_error = result;

    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/rf901EkpCLQNPWtrLnbDGTmUxmnue9PPPy36tabSu4XHUwMDFiW1tva1vWbc907L5vzrZeh9dvLH9iey40kejviTf1e9GdwyBcdTAwMThP3r55XHUwMDEz9zB63uihl+VYI8tcciZw3z/wd632X/R/aLH7YV98Izv9XHRvKlx1MDAxM1x1MDAwZqzGYa/eXHJmR1HX6KbHyfhWLzDdgWPFTbdwnVFqaMFcdTAwMThHUkmBqVq03kGrYNRQhFwiTTFnQiZaZ3Y/XHUwMDE4hndIbUiMKfTXRGNNXHUwMDE3d1xmLXswXGbgXHUwMDE2TLhBJEaUaso4hS+Le1x1MDAxZSb0toZcdTAwMTZXJoHvXVnbnuP54az/wFb4Xzznrtm7XHUwMDFh+N7U7cf3mLyvLi7iey5sx+lcdTAwMDR30ciwyLCgW0vjn81nT5auXHUwMDE39YJcdTAwMDdcdTAwMGWGrjWZpObqjc2eXHUwMDFkhOuEUXw1nN14v1x1MDAxZu3Xv/GcfHNk7YdcdTAwMWLmTlx1MDAxZGdx2Xb7VrhcclufblJPc/vzp6XunlhWOFx1MDAwMtNIcS3jZYyBRSlZvnrouVx1MDAxMchgd6RAXFzi+OGTXHUwMDFkXHUwMDAwV1x1MDAxMI15YTpcdTAwMTMrXuVwXHUwMDA2u1x04MUvMlx1MDAxZPfNhy5YSq1gX6XERC7aXHUwMDFk271anrfj9a7ip0RXv7zOw3Ljvd9cdTAwMTX9v+uHXHUwMDFlac66XHUwMDFmqX1w0GxmsVx1MDAxY1i3QSGMiaZcXHKZxjGShqJcdTAwMWNjJKjkXGLJXGaOMWFcdTAwMDZcZkIpXHUwMDAzKHNYXpZcdTAwMDUy4dVcdTAwMDI38E13MjZ9WOSfXHUwMDFivF3krI5ejFx1MDAxNZhcdFx1MDAwMluRg1+BWVx1MDAxMX45w5grTemPxm9cZsdcdTAwMTCG8Pon57WdznGtNzRtN7GRnlx1MDAxYnTs+4h6KHW1ZY5sJ4JlaqiGY1x1MDAwZsI12HKsi1x1MDAwNCBgXHUwMDE5XHUwMDAyXHUwMDFi3MKiOfDGcWtcdTAwMGbGg8dafnZ7PN9cdTAwMWXYrumclk7TnFx1MDAwNt6JNXmYaOBPreRKWHtcdTAwMGIjblx1MDAxMF5CXFx9dDZcdTAwMWIzb3zFJi25Z3kmXHUwMDFhje+yxDV935ulmUuVNlx1MDAxMGaKYqaFVIn9XHUwMDBmV0hcImEwXHUwMDAx5CSwR1IpnSWuklx1MDAwNngmRmFcYspJnlx1MDAwM0LwXHUwMDAwXHUwMDBlvkdppbgkQsbPeOFxmse3+Tx+3O14K8n8ypdlesOOMcKVTOJ3Tm7JMy7rkdywP4TB1rBcbtmtXHUwMDEwXHUwMDEzWnCRfOxcdTAwMWHsXHUwMDFle3Yy6lxuP/G3WrxcdTAwMDPRXHUwMDFmi+//vs69u1x1MDAxMKVR71xmPuPhXi1cclx1MDAwYlx1MDAxODH9oFx0W2a7g+U3sdx+QUvUq1x1MDAxMbJvaJn9XHUwMDA30NpL4SD0Tt6RXHUwMDFl2el6s5xFXHUwMDFheTfWe7t/XHUwMDFjrdWZXHUwMDFkXGbn27KCr1x1MDAxZnnmZNS4PT1jl3V60fXsk8O/XHUwMDBlVzJcdTAwMTlaXHRcdTAwMDNgRpWgXHUwMDE0QTxcdTAwMTlcdTAwMWKF2GQoXHUwMDAyK6oohKcx2Vx1MDAxNyZDXHUwMDEyXHUwMDAzXHUwMDFjXHLmhHKtOM1x9bAlXGJcdTAwMTFKhFawXHUwMDFmOlxmXHRwXHUwMDFj/L5YjbTVuPtqq1x1MDAwMelcdTAwMDXjXHUwMDEwn4m8oFx1MDAxNsK2wqhcdTAwMTbCXGItJUL6VzVcdTAwMWNFWFxyP/VcXJj+XHUwMDBlxuOUWT7vTNrTvf3xh1Pnw8zaudxdP+nFkEhRgF7KgEBcdTAwMWVrYIo1XHUwMDE3WHCw1XHC9JyS3ouLnu7pn9xy4MZcdTAwMWF5g9RCXHUwMDAxMXVeaMFIJptYWFxioSFhRlx1MDAwMv3wxKFcdTAwMDTPzYtcdTAwMDG/camsX/ddvq9cdTAwMGaDz2ft7sp4XHUwMDBl8VxiXGLjSlxiolx1MDAxMMdcIo1nIVxywCFcdTAwMThcdTAwMDdcYj5cdTAwMTBl2Vx1MDAxOJogXHUwMDFlXHUwMDFhkvijs4Bm2mA68anYXHUwMDFiXHUwMDAynsPPz47n1lx1MDAxYXhmXGKMO8nHMyaqMFx1MDAxMYZOglwihDdcdTAwMTJyXHUwMDE2ziF2zlx1MDAwYlx1MDAxMKrdk5133fessz3anur71vm+3IkzxVx1MDAxNFx1MDAxMKPUd9Hw5XXZsMO9k0DtXHUwMDFmd1x1MDAwZrZb3ufhO10/O3x3lj/sQ7C34riXx7rdXHUwMDFje+ez66Nue9Twj/X79t5q42ZcdTAwMWNjPrlVNeQuX9dcdTAwMTJVXHUwMDBiQlx1MDAxYkMqoVx00vBcdTAwMGaVS7xcdTAwMDbWXHUwMDEzVcZrXGbMXHUwMDE3oaiFXHSkXHUwMDE2RGGS5fUvIGrhb0TmP9cgs0BcdTAwMWNpcFG5ZMZcdTAwMWNcdTAwMTWRXHUwMDE5Ylx1MDAwNM1cdE7ky1x1MDAxNTgncJRgoelm4eujqrV7XHUwMDFiQFBXXHUwMDBivJqqd+2gtv+mXYW41YPJW36JvDWy+/2kg0srXFxPecpl0av0LarRvu7IyD09+njDr9Fw3G+cXHUwMDEwdnh0upZoXHL5jURcdTAwMTDCp+itNDUwo1pSXHUwMDAx/OZcIm6NNWtq0NBdU/D5kktcdTAwMTVcdTAwMTPhV6L3t/LVf61Bb1wimFZAK57E7aOshTKkX7BcdTAwMWKB6WWYVZmdViRan/5cdTAwMWOidf40qyFueWBSpkBcdEnLXHUwMDAybo2NUMnGYNshXHUwMDFmZ1lcdTAwMDWKSGFcYkRcdTAwMDBQXG5pqlTcP6FAUcJcdTAwMThkPDCO4Fx1MDAwMlVcdTAwMWZv/1xu+ePp6lx1MDAxY4aUXHUwMDA1XCIlyXBcdTAwMTKki2PTwmibKYS5XHUwMDE0skJcdTAwMDdcdTAwMWTpS5xrMFxymzC4Un2pXlxixag1g8J4vExcZr0kLiXUv3lcdMMqXHUwMDBlvFx1MDAxNspC/VxiTp5cdTAwMGZcdTAwMGU72XBh31pcdTAwMGZqUerll1/Z4JBcdTAwMWNl5lx1MDAxOUuOP5XQVZ7klNkoiDo55Fx1MDAwN0TyXHUwMDEw9VwiUS5cdTAwMTDbKMX4Q/JcdTAwMTD7tYWJXHUwMDEy3IB4XHUwMDE2XHUwMDAzLlx1MDAxOFi5vNQhpu+LSYpN0ofVTVx1MDAxMlx09VpBwKXn2CRCMlfjsIJJyFxyXHSvsJbjeVx1MDAxOaVcIvAtdX/KXHUwMDA2XHUwMDE1XHUwMDBi3N/ROuG0toZQ+m2eMlfP0Ci977mSbX/g7+5uXHUwMDBmXHUwMDA2nfp9bzjw0MpqJYSvhpI8VGSFojhdcVx1MDAwNjBcdTAwMDSjJSlcIlx1MDAxMD2Bw8lmPVxuXHUwMDFiXHUwMDAwXHTBMURf4LZ0TuxEw3NcdTAwMTGmOVxutX3KwcatYamUUJZcdTAwMTa/g1j596rHdjRcdTAwMGLKXHUwMDA3XHUwMDAzxjXDmlKVZ78kL5TkXHUwMDE5h1CMk81cdTAwMTT5xexyJMFb11He5Hra4uxcdTAwMTS3XHUwMDBlxp/a15PGVpJum0mNXHUwMDFme1x1MDAwN/fjY7IzpXrUu79u3e01XHUwMDBld1ZSRjNcdTAwMTYqe9CIXHUwMDA0IH1ccptbQszyeZZIXHUwMDExSoVcdTAwMDVyXHUwMDFhXHUwMDEyXHUwMDE2KqRglC6RMlRcdTAwMWG1lJIyJWW2XG6UaUOGOiNkRNBbMJVXPbdOuPD7kPBs9XBcdTAwMDFcdTAwMDDDXHSjXHUwMDA051x1MDAxZJJcdTAwMGJSqDFiTFxiwbCpelx1MDAxM75VXHTdXHUwMDE4iY/ynGt2k/4hoT8kkurvoSs+4dMyuuLSxKtRJE7Oj3a8XfdMclNMPu9eXHUwMDBlu62RXt2xMjavi4FMLqymSpFYgONkVDCiXHSkXHUwMDA2XCLvuIByI3nEl1x1MDAxMLOLj1x1MDAwMeOHvJxcdTAwMDPGrD5fh9WcSFxmVjNPmMBSXHUwMDE2ioswT1x1MDAwZTGy2qxmrszd/dncbrJcdTAwMDa5PJ/dXFw1d4/Odi/b2F/J3ZV60W/lncfKufx8fHCpj0xnXHUwMDFiXHUwMDA391x1MDAxZph/VcVB4LxcdTAwMGVIaVbRKX/5upa5Z1ximUuYrakhVFx1MDAxObPVy8HfOuz9uIaqKFC4MTj33C95aLNM3uhcXJclq4uei0/e2a51w1uew2nfXHUwMDEzXHUwMDBlcdkrZ6dejV+2W5/2fLTf9vbZSdup351cXOJcdTAwMWSxsl/milx1MDAwMD9cdTAwMTVcdTAwMTFz9qbrVZ/2yy/lOVVcdTAwMTH701x1MDAxYW5cdTAwMTlrzlx1MDAwNaIo1y1rpZYvL5itgYdMbFx1MDAxOG2X+bmLsXvsdO6mreOPnVtZ327ttVx1MDAwZiuoz/m27lNgjdepQyhhYfn7l9XRIGpwXG5cdTAwMDRkYVGvTpd7Pu09sVg6Z39xpmtwzlxcg3NcdTAwMWFBXHUwMDBlI7HK5Vx1MDAxYyS/hZwjklx1MDAxMsE5qzzDpWC0N1x1MDAxMsRcdTAwMTc/XHUwMDBlXHUwMDBin1x1MDAxNtafsOdURfOEQ8v8dKzkJarxsOW5QemvQTAxJGZR0Vx1MDAxM0VcIlFcdTAwMTZcdTAwMTn9XHUwMDFhRKsn9ORIK+ZcdTAwMDA7sNiSc1xuVj9HvVx1MDAxMtigXHUwMDFjKXDeXHUwMDFjvCtH66S9P0jM6puTofWdXHUwMDFkbHdcdTAwMWSyQ0BEwa7m1dRgJIpcdTAwMWSsXHUwMDEwSnJK5fORsyo9/SpcdTAwMDNk+KlnsFx1MDAxOI+YccgrXHUwMDFlyperX7WNXHUwMDBl5aODrYI3n38rOJQvmmV5NlDRLLPHdSXTzlx1MDAxY84tnzg+dXBXcDhcdTAwMTfNt2Ncclx1MDAxZWGbLVPYn3TGVs82nZyHXHUwMDE2tn3FmV95lFhmoFx1MDAxNVUl4kX4c70wRJSF4Vx1MDAxN4PsXHUwMDA3Qlx1MDAwMjzPbEROleNLJUKeMe6tU+DIXHSTXGbMTZ4x5sXVUVx1MDAxMlx1MDAxYyvs6iYqRmpcdTAwMWU5iVx1MDAwM8csXoNcdTAwMWZWilCIvqXum1reb2HTUlNcXKtcdTAwMWOqaJbl8eJGs6y6LOLrLO+D5Xs138AtczzuXHUwMDA0XHUwMDAwyMWSbN3Y1qxZXHUwMDFjuL2azy00XHUwMDE1VnSK/uXVl/9cdTAwMDEsMploIn0=RX DSP chainExtend to 8-bit I/QTX DSP chainEnableDC blockRound to 4-bit I/Q \ No newline at end of file diff --git a/docs/images/gateware-rx-only-basic-block-diagram.png b/docs/images/gateware-rx-only-basic-block-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..421ae2c76396799d808b7bbd725a406bbd760b2d GIT binary patch literal 138600 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 z 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 -TuOxSK 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 literal 0 HcmV?d00001 diff --git a/docs/images/gateware-rx-only-basic-block-diagram.svg b/docs/images/gateware-rx-only-basic-block-diagram.svg new file mode 100644 index 00000000..bf470a17 --- /dev/null +++ b/docs/images/gateware-rx-only-basic-block-diagram.svg @@ -0,0 +1,4 @@ + + +eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1d2XLiyFx1MDAxMn3vr3D4vjaaytpr3mjjfaPbu+/ccFxiXHUwMDEwoDabXHUwMDAxL3hi/v1mYVx1MDAxYklIXGKE5W6Pw3SMx9ZSVKky85zMyiz9/WVlZXU46nmrf66selx1MDAwZlW35df67v3qV3v8zutcdTAwMGb8blx1MDAwN0/R8d+D7m2/Or6yOVx1MDAxY/ZcdTAwMDZ//vFHcIdT7baf7vJaXtvrXGZcdTAwMDd43X/x75WVv8c/8Yxfs/deNM/o7ujuql4oVbav6i2v2aSn41vHXHUwMDE3vXSm71WHbqfR8oJTXHUwMDBmeFxcMO1wKTVcdTAwMDFuqKJCTs6O8GyBa4dcYmXwnz3HXHUwMDE5nZy+92vDJl7CXHJzJDFcdTAwMDZcZlx1MDAwM8KUXHUwMDExkyuant9oXHUwMDBl7Wg1OFx1MDAwMlxiMDBaaS745JKnXHUwMDBl/blCJkdcdTAwMDbDfvfaW+u2un3b6/+AZ/9cdTAwMDV9rrjV60a/e9upXHUwMDA119THn+Caut9qXHUwMDFkXHJH45bxIeNcdTAwMDNdnWr/7LnzdOr4rLvwXHUwMDBiXHUwMDFizY43XHUwMDE4RPra7blVf2ifXHUwMDEzkOCo7V1vuzaer/9cdTAwMDV96rttb9tOWOe21ZpcdTAwMWP2OzXPTsPq5TDybZ3a87dFrlx1MDAxZXiebYFcdTAwMGIhqVx1MDAwNFx1MDAxNjzHQLKA89jhg25nLGaUaU6lYkFf/UFcdMVrOG617rZcdTAwMDZe8JxtXHUwMDFm1kOiXHUwMDE3XGbltldzn25cdTAwMDGlXHSRnEtNZfCtLb9zPd3zVrd6XHUwMDFkfMv46D9fk6S53+tcbulcdTAwMTd/XGbd9qj/sOOLm59bN1x1MDAwYkszSrCD4zNcdTAwMDL7pFhMmlx1MDAwNXEkaFBCK0GlXHUwMDExIGLiTIE4nFx1MDAxYk3wZsVcdFxinSTP3CGKMKqe2/mU57g83y8uzyBcdFx1MDAxYSCGZihBoLVms+RcdTAwMTm4UspIQmneXHUwMDAyrVCNIFx1MDAxZoG+OamU5F79bHR0f7R+8qM8gDatLyzQhmhHXHUwMDEzNL7mSaBFVKCBoSAqYTjHh0BQ+nVMoIFSR4OQhlx1MDAxMTS9ilx1MDAwM8RcdTAwMDVaS8dcdTAwMDDFdsBcdTAwMWFyLfM20K6o6X+9QD9sLi7RRlx1MDAxYsJcciiTINCGxsR8XCLQRCptOFx1MDAxNUtZ6OdcdTAwMTOBXHUwMDA0hqSwtv1QqT6sXHUwMDE1Ovvt01x1MDAxMpR33fJN20zGXHUwMDEzkUS33+/er07O/PM1rV3eO14vnVx1MDAxZmx8q+5tsmH52uyvlcvJ7Vx1MDAwZb2H4aLNbq3vXHUwMDE2XHUwMDFh3X3wXG7175detaaborBgd59/S9Fuxlx1MDAwNf6Wj3anjz869qhiM+IoTSlcdTAwMDcpXGZBVI4otnBcdTAwMThii0aAQq3EXHUwMDBi4rRLoV2Q+NGKXHUwMDAwKNBcdDBcdTAwMTWSolxc1HjYdzuDnttHefu3q/JpXHUwMDA2cEJWq/E7eVxmhqzhXHUwMDE0M3XZIKRpXCJcYiyjysnSq4yRaFe01lnIViCMVlxicfjF0tpfXHUwMDFkvzP0+nW3XHUwMDFhmvJ6tzM88lx1MDAxZsdMkUSObrhtv2WfvYw0Vmz5XHL7XHUwMDE0VquebW01/CiGPnoyk1x1MDAwYtp+rVx1MDAxNoa2Kjbq+lx1MDAxZK+/vVxiRnb7fsPvuK3jYFx1MDAwMCtcdP13b4fdXHUwMDFm3uBpXHUwMDA0w/6tXHUwMDE3fkje1otagENFikZcdTAwMWa6ZLChL3Yv5NlGcXR1eDHY+Xa/MF5cdTAwMDPRyuGKXHRBXHUwMDAx/5M6QIAoXHUwMDAzVZKhY0V48ERjXGaUSVwimFx1MDAwMpLoUb0w0Jd2PiloXFzNR4truVx1MDAxNJSjtuokXHUwMDA2XG7hrkxpOUP6r1xmzlbuXGaUh1x1MDAxYn1cdTAwMTVGeeXSjfDvKqVBa3/jiJOu2iolXHUwMDA0XGKegDRcIs3SWDFjXHUwMDE0u8KQzfAo+0Rwclx1MDAxMJ7wySkplSBExWR5XHUwMDFjXHUwMDFi4MBcYt4umTJcdNxcdTAwMTM9NmSvTFx1MDAxOM1cclx1MDAwMzRwXHUwMDE5JFlL7Vx1MDAxOflbQKvmXHUwMDBlmt6vXHUwMDE158fFxVx1MDAxOW2I0FpRkVx1MDAxOFwioFxuZsmzvVFcbkVV/jFcdTAwMDJcYmtXXHUwMDA22Op1/WmCXHUwMDFi/LZcdTAwMTI87PFcdTAwMWaT3//3NfHqmVx1MDAwMvn6u1x1MDAwYjFJXHUwMDBlmouR0sHQ7Vx1MDAwZr/h3PqdXHUwMDA2XHUwMDBlLaDAL+G+J2AkN2c/XHUwMDA2XHUwMDE3Pc/b2W9cdTAwMWPv3m6sb1xmT9ZDPHi13a2NxbHbr/jD8Im6/+DVyvbRRZ7cXG7CXHUwMDFm9l5cdTAwMWJglKEzSIiOPFx1MDAwMVx1MDAwN1VcdTAwMWRpJ/7QNlgnaWxcZlx1MDAxM+K+6nVq80dwdXn0uKtkoXRdO9+/XHUwMDE5kn3dPi2+blx1MDAwNLaThjGBPjrOXHUwMDA2XHUwMDAzqdTX6Fx1MDAwMCV2XHUwMDFmXSnkX2izXHUwMDE4T1x1MDAxOcN4XHUwMDFlitbyNT03pns4wvA5NCb+XHUwMDE05nutSvd+LLRcdTAwMTHCMe77kdd40Yyojtrv3Fx1MDAxZVx1MDAxY/W8qu+2XHUwMDEyvnP6XFyKbb91XHUwMDA37v366UW/w/3e2fre4SWqzsJsXHUwMDA1XHUwMDE5hsOkXHUwMDE4442hYFjUXHUwMDBiseFcdTAwMDWjJUVHXHUwMDAzT6E7YuLxsiC8XHUwMDAw3KpFiPBcdTAwMDThXHUwMDA15Whcbuh1PTVcdTAwMTNYgXy4SoXWaaXy7+Yq7uHixl0xdFx1MDAxOI2BXHUwMDE4KbGPWurpo5PgXHUwMDAy+lwiaNp1KGycU3Dhe/Ho8q7VumxteuKAbO6VOlx1MDAwN98uX1x1MDAxZlx1MDAwNPjWk7DfuNhsr3Xq/Ye19vHPs6uLXHUwMDFjYlx1MDAxNm9cdTAwMWJcXGBcIrxcdTAwMDbyKuKW/lxcU4JcdTAwMGKoh1xiVJJynHItXHUwMDE4TPE29FAo53hcdTAwMTGeXHUwMDAyXHUwMDEydlx1MDAxZV602tpcdTAwMDX7oUSCQm6XXHUwMDE0XHUwMDA0zznq/WGiXHUwMDBibnlxVcbZXHUwMDExmiGVSOJpis6kacCUpnhnyH3Mh6ZlXHUwMDE230BcdTAwMWGfnfOjzfJ2yJj9vqDCXHUwMDFjaJxcdTAwMGUqTPU7n2BCOvuZXHUwMDA3z1xuqCOU9e9cdTAwMDE00s6pWFx1MDAwMqIzVZSg30WQbUpcdTAwMWVXYzT5XHUwMDBlJ1pySdFJMyoplIDgjDZcdTAwMDCRQVwi/lx1MDAxYqlcdTAwMDNF+oz9T1T6e1x1MDAwNnRWklx1MDAwMeGhNalAo5mauZZFiUZcdTAwMDfbXHUwMDE4vtRaVlx1MDAxYdzRb3fNXHUwMDFk3z/c5o/HJ/euvzV42D57PTq/1YpCaX9fN0/uz9tUXHUwMDBlvvPvpHk56vdyaDc9XG6S2u5cdTAwMDKoL7nSKmxcIpZH/fT5SkF9dPdcdTAwMWQhUZfRYlxi9OFiqK+QqCNcdTAwMTEnSqF7XHUwMDFhXHUwMDBm1lx1MDAwMOVcdTAwMGW3qM+ZIFx1MDAwMtl+YFA+UX++iciwpoAsXVCi4lx1MDAwYoHWRsRcdTAwMTdcdTAwMWEmqK9xXlxm1yE37b2sKfw4XylcdTAwMWSVV6pNXHUwMDA04PdcdTAwMDD+c4B3XHUwMDFh/JO7n9OCQrV9bVxu+nrjorB3RnrNi3p95C6zoGCYYCTkvb2QXHUwMDAwzVHruVx1MDAwNqltJkBCXHUwMDA2wFwiLOAzXHUwMDAzYL6KV1x1MDAxNtdwY2zQXFyoJFx1MDAxZl2JlPArJ0ZIXHUwMDAzS2l4XHUwMDFh/NXP21e8tLtdVK39+ulVr/ZjIDtcdTAwMGKxgFT0W858pGhLej9T0Fx1MDAwZoCDXHUwMDAzNiVLakBIXHUwMDA2XHUwMDE2UVx1MDAxNIQ/6+sqVCVNNGNx+OPMQdk3RnPFXHUwMDAxUfJcdTAwMTP+suhGNYPTy1x1MDAwNTNM0UT4k7PXJlx1MDAwMKdNKibpu1xcUn9cdTAwMGaoN1x1MDAwN2pcdTAwMTLW0fNcdTAwMDa75maxt/F4XHRcdTAwMGZcdTAwMTfkYueuoTvFarO0iPpcdTAwMDaZm0xLXGJcdMFcdTAwMTjlrJMqhFx1MDAwNoriYVxipfE4NFx1MDAxN1x1MDAwZbXaayhTRFx1MDAxMpG0aP6pvDOU18+gvIJIVEEqk/zbcNJkLGJlXHUwMDE0slx1MDAwZmreRHlZWFizKu/+2klcdTAwMWXK2/Lqw1x1MDAxNNVcdTAwMWR2e7P0NtLZaSWN9C5cdTAwMWYlLVx1MDAxNvp7m+6t7vXgdHercvyjPLh8yL5ohD84ejFcdTAwMDHhXHUwMDFjSyNlXHUwMDBlaEI1Z6isQqmEqFx1MDAxNFx1MDAwNUdcbirQU+VEUJ1UMiCMg+4sXHUwMDEwyiUg7YXcc6w/xKLRz8X1lkslhVaJi0ZcdTAwMTCOXHUwMDA2TOuttCutXG5nNG9GurW5dbKzUXvY6V+Qy+bjkfx5R4dcdTAwMGIx0q9pzfJz2lx1MDAxMKdra71cdTAwMGLvhFx1MDAxY69cdTAwMWaOXHUwMDFlR416crPZ4zzcXG5sTkw3ffwpUMmJdKxiXGJCXHUwMDE5OmuhiOFYrDhxUC81eoxCccZcdTAwMTmLKSB7Xt1haD5cdTAwMDHCfOpcdTAwMTMr5+rc9eI6J1x1MDAwMFx1MDAxNFMwQ+dYXGZBX3ROWD+f8eWywNOlXHUwMDE3zfNroPKovP1cdTAwMWV47lx1MDAxY1x1MDAwMIst7YR7nVx1MDAwZoSmJ+bML7pTjpRcXFx1MDAxMiaBszBGPlx0qbb5XHUwMDEyhiPqKVwiQMazv9/Dws6HQNBOXHUwMDA25iupYFooXHUwMDE2y7CwXHUwMDBmW8zUZkAuZG9drkopXHLqTs9cdTAwMWJFfjeCjXqxcHDrN+RVkd6+XHUwMDFlQV+xUvJWyJzarto+6P9sjjr1q7K7Y6rtbqmyX32PiJ8+XymILyRzOFx1MDAwMrp1blGbp1xit3JYOFx1MDAwYjeO9+azVmR5XHUwMDBi0c2QRE5cZoI250musYbZrrGg6Fx1MDAxYmtcdLknkWeV3SS8X7HY2++2/ur0vYY/QNxcdTAwMWW8XHUwMDA3XG4wXHUwMDA3gFx1MDAxMyjAy0BWXHUwMDEyxpFcdTAwMGYpOLi4Wz9Qt5U1MyhcdTAwMWU23IJcdTAwMTi11k1cdTAwMDZSoFx1MDAxZGSFKECCaCFIfKFnnO1cdTAwMDFUXHUwMDEyKVx1MDAxOUmIgS2YizlmXHUwMDA18rmZvFx1MDAxN3o+XHUwMDAyK6hcdTAwMTCeYaVHaGRYWiWxgrTKXHUwMDExQFx1MDAwN04ztN65p2M+XHUwMDE2y63SNavf3Fx1MDAxY1xct+/IIS9ublx1MDAxZbzfdMy3Svh4U1rAcMJlTlx0XHUwMDFm6fOVRlx1MDAwYjh3XGbTXHUwMDA0XGZcdTAwMDFpwlx0KE81pNTme4CWXHUwMDA2oZ+bOC9cdTAwMDBiXHUwMDFjsMSAo1soXHUwMDE4elx1MDAxYZ/EIIORXHUwMDEwWZa8gKKNICwpXHUwMDE0YMzMXHUwMDFkO2z0zVYwmKWMRGqeJ4HQ4vSyeZ4rbqf2V6fe6t6/oOt74Fx1MDAwNnNwODHz045kJXkg+ZCDdPOZVoXHtbEhdVszXG5GXHUwMDAy0OiuJtQ4WoNcdTAwMDRcIlx0UYxcdTAwMTBcdTAwMWVcdTAwMGZcdTAwMTdwinZcdTAwMDKRxtbFXHUwMDE4JFx1MDAwNlx0XHUwMDAxP+IwobhcdTAwMDFcIpTgzKA3kTMz+ECKLzMovlHomymZWIjH9MxiXHJcdTAwMGVgluVcdTAwMDYpai9cdTAwMThcbsFSap9rXHUwMDE13kx5XHUwMDFj31x1MDAxZJfEoL1cdTAwMTgyL1hIl55cZr6yZCFcdTAwMWTXWjBpQFJjgp1cdTAwMDCeXHUwMDA2wfFcdTAwMTDCKqVcdTAwMWNcdTAwMDfKiVx1MDAxNLFBZKykS7dqS1xyoUCmxlx1MDAxMNquIPsoYrV0SfVy0Yq6aOXbpJYuKnr5XHUwMDE2003Otbt33r7/9FxcXHUwMDA2Z/6w+axDXHUwMDBiMLb0SFa6LddcdTAwMGWx5f9EXHUwMDE5rpicWjxcdTAwMTWoXHUwMDE4SuH/KCBGk3guXHUwMDFmZ47ktvBfI1x1MDAxNKB7XHUwMDFmUL6QJVx1MDAxN9Q6iXpM/YCGss0+XHJ51JBcdTAwMTczXHUwMDE4cvSmldaUJ+bswsycXck1RVx1MDAwNVx1MDAxM3lb8uXXcvK15LPk0X5cbnFRfL0lT1/7WcpcZlx1MDAxMsdMfaJGUFx1MDAxMFx1MDAxMu94Ruv9XHUwMDA2ldwkUy+XsM6p9c5RoXqFMd0+Lz9enFx1MDAxNlx1MDAwZb3G962T81x1MDAxZF070qejhYwpoGCNo1lEXHUwMDFiSamK+r/Ii1FJOPJcZlx1MDAwNDe7RWHcmlxu4qBcdTAwMTckuEL+zG0tXlwiL5bIRuzea5pcdTAwMTOtaPAln9Y0ak03Z1RAhFTieTJpXFw6n60sui/4XHUwMDAxSIymJWSvTPiysvlEMlQ78buzy3I1s4WZkjo+XHUwMDFiXHUwMDE30sXt7DRfmnEmv41cdTAwMTJyM1x1MDAxY+lcdTAwMTVoaYbDXHUwMDEwXHUwMDFh2VUxXHUwMDFharfpMYorhC6NpJdcdTAwMThcdTAwMWRPYWPM0YhrVFx1MDAwMkVh5Twh0I7UWoFiXHUwMDAy/XVBXHQ2l3eo/Vx1MDAwM1mOrVxma2xUMatcdTAwMDNJa2ygU1x1MDAwYiuIYeEo6McyXHUwMDEwMyVyfDZBXHUwMDE4X8/E0ndt+1x1MDAwNZTmd21AXHUwMDAzmXr5XHUwMDBlLWf6kkua5VREhkvSY4aT2q1JkHFJ677Ga2yYcezaiUC7i+4v6IRcdTAwMDVcdTAwMDewcmyUVFx1MDAxNFxiXHUwMDE4vNArfFKuWYZzd3HDSYkhQsjE7CUmZvqvjKLhVOGZzilcdTAwMTJpXHUwMDE3s4NH+fvs5iyJXHUwMDFjn01cdTAwMTLG11x1MDAxYs632Fx1MDAxMyt/w/lcdTAwMDbxxn+94TwtnVxuenj6rXZ17N81qvdnP1x1MDAwZk9cdTAwMWbjhnN2LS9cdTAwMGVaXHUwMDExxjklgk5vT09cdTAwMDV6rFx1MDAwNvk7JYjjUsb9VWp3XHUwMDBmXHUwMDFkr9xcdG3TN1x1MDAxMvxVUI6x8Vx1MDAxOGycXHUwMDE5YFmCf4l28aNcdTAwMTbzVsjF4raTXHUwMDFiidaTmcRE7jDOTS/fXHUwMDAyMjJcdTAwMDTD3HdTXHUwMDE0oJXJKSuxLLfafKu8R1x1MDAxZUeHpdvd0vd+736hOlx1MDAwNFx1MDAwMMJcdTAwMWRFXHUwMDE5XHUwMDEzxu5eYqbjL1x1MDAwMqlcdTAwMDJnUlBcclx1MDAxY59dQiGQYVx1MDAwZdhwXHUwMDFiml5Nw1umfKZcdTAwMWbMl99GluC11DZ4nSC+JlRZXHUwMDE1Sz7gyjAlVN55iXYjukzvXHUwMDBiXHSEMbRcdTAwMDN01V6zUuu2Xb/zV2fbplxmdNxW5HBcdTAwMWXJXGJvVNaXeVx1MDAwNPlkIVx1MDAxOOJvb19ccn72z+q33aPznUdf3NGFco2YduyylFx1MDAxMVJzYqZerPJSn4u20Fx1MDAxOEpcdTAwMTNeXHUwMDEzJOhTXHUwMDBlMrrLKFZcXH7WXHUwMDFjZdH1ZpaMXHUwMDAzZpBcdTAwMWSwxOp6XHUwMDFlh7BcdTAwMDCrqFwiaMjfYYHuRnmz+I5VOdq9fPR0r9pyq4Otys559fL+plHQ1cPL5kKgXHUwMDFjrImAofZNUcmaSqXdbEyx+P7zQKhD7d6PZlx1MDAxYyA1SbD8qaozVTVLLb3lPUaHX/dcdTAwMTLaXGKDzlZVXHUwMDAxdjNtSoOw83tR1WM7kVXPv1x1MDAwYufvvTuNTexlPoqbvrlsamqILYu3XHUwMDFiJCCtNlx1MDAxY1x1MDAwNI1uYKOkXHUwMDAzTElcIkBcdTAwMTD0XHUwMDFlTVxccVx1MDAwNddcdTAwMGV6loix6K4gXHUwMDFkT4JY5lx1MDAxMI183Vx1MDAxOPt2XG5mZJaiwFx1MDAwNXbbf4v3Rvz6nfYrkME3tPVcdTAwMWFKm+Sd9iFcdTAwMWVumyixwilAzyn3XHUwMDFjP3RcdTAwMGXDW4X+rsha5Oq43M27faYwv0FzzJFG4VxcXGKhXHUwMDA0sagog+a+TDX7XHUwMDFiXHUwMDEzXHUwMDBlx9l4oIVSoIlEXHUwMDE3Klx1MDAxMFx1MDAxZPtcdTAwMDGHUEnBVpIxlEdcdTAwMTNcdTAwMGZUZlxmXHUwMDAwvsX6TnhcYnanYTE1XHUwMDAyxCCG7lx1MDAwMVx1MDAwM1x1MDAwNlxcMpYyhF+0c/+sSptnW0HD/X/q09SQpzQhVXgj4vs0ilhrqZI8r/XJ7/FcdTAwMTKXWFwiZdQ2TGVSzq1p0Y+lbu+0ecf3dsnQVzdH9e+Lv8YzXGKUMkVQlqdee2jffiRtXHUwMDFlhNKCo0+aUFxu905cIqVcdTAwMWaiXHUwMDFhXHUwMDBl3MXRXHUwMDEwXHUwMDFkXHUwMDA2qoxiSS8+RI2OXHUwMDFknqRJMmmE3W4+ZzBcdTAwMTTAc1x1MDAwYpSmXHUwMDE3lGV5j9LUflx1MDAwZtPvUUqIlDLpSFx1MDAwMdq+koVwXHUwMDFiUI3L8+T1M8+t5E3t3kCWf1x1MDAwN7XLsIkngJGCiMQlUzq7slOg1Fx1MDAxMSXpMrJcdTAwMWPpRmJBN1sqcJpz+cYrLi9cYkQowe1cdTAwMGXUXHUwMDA00JOBudytgNqTLPxv0l5Mj15PXHUwMDA2f8FrnFR0Tqbe44Qg+Fo2+Fx1MDAwNsvB04Q2OobQe5xcdTAwMTghdufPd89cdTAwMDZZuP8z2GBUXHUwMDFiUuV3XHUwMDExOpguzPOaf1x1MDAwYj745bmVVbfXO1x1MDAxYaJcdTAwMDGbXGLV6p3v3X+bXHUwMDFkNfjyPKNcdTAwMTZavHHm8D9f/vk/eadZeyJ9ADCinterfaceSGPIORX DSP chainADCMCUSPISPI controlregistersSGPIO andflow controlADC clock domainInternal clock domainFPGATransceiver \ No newline at end of file diff --git a/docs/images/gateware-standard-dsp-chain.png b/docs/images/gateware-standard-dsp-chain.png new file mode 100644 index 0000000000000000000000000000000000000000..69973a94db572f97d8fcc83fb1b9001dd682c6e5 GIT binary patch literal 130043 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μqJBWb83Z3Q+%c7x5_t^?V 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