13 Commits

Author SHA1 Message Date
0259950dfb Stop GPIF streaming before boot/shutdown to prevent bus contention
Stock firmware (0x1D62-0x1D6A) calls GPIF abort (0x1919) while the
BCM4500 is held in reset, before programming init blocks.  If BOOT_8PSK
is called while streaming is active, GPIF and I2C contend for the
BCM4500's bus, corrupting init block writes.

Add gpif_stop() guard at the top of bcm4500_boot() and
bcm4500_shutdown() — matches the stock firmware's safety sequence.

Code: 15,171 / 15,360 bytes (+16 bytes, 189 spare)
2026-02-20 12:03:17 -07:00
dffef75b06 Add bcm_wait_ready() comprehensive pre-write readiness check
Stock firmware wait_for_ready() at 0x2000 checks three conditions before
each init block write — all must be simultaneously true:

  1. A2.bit3 = 1  (BCM4500 DSP processing complete)
  2. A8.bit0 = 0, A8.bit1 = 1  (command register ready)
  3. A4.bit7 = 1  (init pipeline flushed)

Our bcm_poll_ready() only checked A8.bit0, and was used for both
pre-write and post-write checks. Now:

  - bcm_wait_ready(): strict 3-condition check for PRE-write
    (with fallback to simple A8 check for gateway quirks)
  - bcm_poll_ready(): simple A8 check for POST-write
    (matches stock firmware's poll_ready() at 0x20C5)

Code size: 15,155 / 15,360 bytes (205 bytes spare)
2026-02-20 11:48:23 -07:00
e9e5ab859a Add init block readback verification to match stock firmware
Stock firmware at 0x0DDD reads back each init block from register A7
after writing and verifies the data matches (with bit 7 XOR'd on the
block address byte). Without this readback, the BCM4500 may not
finalize the write — our init blocks were "silently failing" (I2C
succeeds, status reports booted, but SNR reads all zeros).

Changes:
- bcm_write_init_block: add pre-write bcm_poll_ready(), post-write
  A7 readback with XOR(0x80) on byte[0] and full data comparison
- i2c_rd buffer: expand from 8 to 24 bytes for 16-byte block readback

This is the most likely root cause of the BCM4500 "boots but doesn't
work" issue (Task #5). Needs hardware test to confirm.
2026-02-20 11:08:40 -07:00
3d2cd477b2 Add EEPROM boot firmware (exp 0xDB) and supporting tools
Firmware: Rewrite skywalker1.c for EEPROM boot experiment — tests
whether I2C hardware controller works after FX2 boot ROM completes
EEPROM load (bypassing the CPUCS restart that triggers BERR).

Tools:
- fw_load.py: Add I2C cleanup stub, pre-halt register flush, improved
  error handling and segment loading
- eeprom_write.py: Add IHX→C2 EEPROM image converter (16KB format
  with length-prefixed segments, checksum)
- eeprom_dump.py: Refactor for cleaner output, better hex display
- skywalker_lib.py: Minor I2C register constant updates

Docs:
- EEPROM-RECOVERY.md: Four recovery options for soft-bricked device
  (SOIC clip, SDA pull-up, desolder, wait-for-timeout)
- Master reference: Updated with EEPROM boot findings

Status: EEPROM flash blocked — stock firmware I2C proxy returns pipe
errors, host-side 0xA0 writes proven unable to drive peripheral bus.
Device boot ROM intermittently hangs on EEPROM I2C read (~3-6% success).
2026-02-20 10:56:21 -07:00
29df688f28 Fix BCM4500 full boot: strip init block length prefixes, handle gateway poll
Two root causes prevented BCM4500 init block writes from completing:

1. Init block data arrays included length prefix bytes from the stock
   firmware's XDATA format (17-byte blocks at code:0x0B4F). The stock
   firmware reads byte 0 as length and writes bytes 1..N to A7.
   Blocks 0 and 1 had the length prefix (0x06, 0x07) as the first
   data byte, corrupting the DSP's indirect register FIFO.

2. The BCM3440 gateway's A8 register does not clear bit 0 after
   indirect write commands (0x03), even though the BCM4500 processes
   them successfully (confirmed via direct address 0x08 where A8
   transitions from 0x03 → 0x02). bcm_poll_ready() now treats
   gateway timeout as success with a settling delay.

Boot now completes reliably in ~0.96s through all stages:
GPIO → power → reset → PLL/DSP load → init blocks 0,1,2 → 0xFF.
2026-02-19 22:08:44 -07:00
aecad367a0 Fix remaining safety review findings: DiSEqC timeout, hotplug, watchdog status
Addresses all remaining Apollo review items:

- C-3: DiSEqC Timer2 spin loops now have timeout protection via
  diseqc_wait_ticks() with per-tick I2C_TIMEOUT countdown. Tone burst
  refactored to use the shared helper instead of bare while(!TF2) loops.
  New ERR_DISEQC_TIMER (0x0C) error code on Timer2 failure.

- I-3: Hotplug scan hp_prev copy moved from start to end of scan.
  Aborted scans no longer corrupt the previous-good baseline, preventing
  false "removed" events on consecutive scan failures.

- S-1: Watchdog-fired detection in main loop — when wdt_armed==2 (ISR
  fired), sets ERR_WDT_FIRED (0x0D) readable via 0xBC and clears
  BM_STARTED|BM_FW_LOADED|BM_ARMED so host sees system is down.

- I-2: Comment explaining (WORD)sd_poll_count cast as SDCC optimization.

- I-1: CKCON bit ownership comment in diseqc_tone_burst() Timer2 setup.

Code: 13,079 / 15,360 bytes (85%). XRAM: 218 / 512. Stack: 132 bytes.
2026-02-16 05:48:43 -07:00
834c2bd9ee Add software watchdog and timeout protection for all I2C/USB paths (firmware v3.05.0)
Safety review identified infinite-hang paths in do_tune(), GPIF streaming,
EP0/EP2 FIFO waits, and the 0xB4 I2C bus scan. The firmware controls an LNB
power supply (750mA at 18V) on a Cypress FX2LP with no hardware watchdog.

Key changes:
- Software watchdog via Timer0 ISR (~2s timeout, cuts LNB power on stall)
- Replace fx2lib i2c_write() in do_tune() with timeout-protected helper
- ep0_wait_data() helper replaces 7 bare EP0 BUSY spin loops
- GPIF idle wait and EP2 FIFO full wait now have timeouts
- 0xB4 I2C bus scan uses i2c_wait_done()/i2c_wait_stop() instead of bare spins
- Return-value checks on all I2C writes in sweep/scan functions
- EP0 payload length validation on all vendor commands with data phase
- Zero-fill EP0BUF before indirect reads (0x87, 0xB7) for deterministic output
- i2c_wait_stop() now sets last_error on timeout
- New error codes: ERR_TUNE_FAIL through ERR_DISEQC_LEN
- BCM_LOCK_BIT constant replaces hardcoded 0x20 in lock checks
- DiSEqC Tone Burst B rejected with ERR_NOT_SUPPORTED
- DiSEqC message length error sets ERR_DISEQC_LEN
- hp_changes counter saturates instead of wrapping
- stream_diag_poll() only updates status on successful I2C reads
- do_tune() forward declaration eliminates implicit-function warning
- do_tune() sets ERR_BCM_NOT_READY when demod not booted
- wdt_kick() in all long-running sweep/scan loops

Code: 13,057 / 15,360 bytes (85%). XRAM: 218 / 512 bytes (43%).
Stack: 132 bytes free. Zero new SDCC warnings.
2026-02-16 03:41:08 -07:00
6e353c351f Add I2C hot-plug detection and streaming diagnostics (firmware v3.04.0)
Phase D firmware hardening: vendor commands 0xBD (streaming diagnostics)
and 0xBE (I2C hot-plug detection) with Python library, bridge, and demo
support. All I2C operations use timeout-protected helpers, BCM4500 reads
are rate-limited during streaming, and frame counter reads use atomic
read-verify-reread pattern. Counters saturate instead of wrapping.
2026-02-15 18:24:45 -07:00
cc3a0707a1 Add DiSEqC motor control, QO-100 DATV reception, and carrier survey
Firmware v3.03.0: DiSEqC Manchester encoder (cmd 0x8D extended),
parameterized spectrum sweep (0xBA), adaptive blind scan (0xBB),
error code reporting (0xBC). All new function locals moved to XDATA
to fit within FX2LP 256-byte internal RAM constraint.

Motor control: DiSEqC 1.2 positioner with USALS GotoX, stored
positions, interactive keyboard jog, 30-second safety auto-halt.

QO-100 DATV: Es'hail-2 wideband transponder tools — LNB IF
calculator, narrowband scan, tune, and TS-to-video pipe (ffplay/mpv).

Carrier survey: six-stage pipeline (coarse sweep → peak detection →
fine sweep → blind scan → TS sample → catalog). JSON catalog with
differential analysis, QO-100 optimized mode, CSV/text export.

TUI: F9 Motor screen (3-column layout with signal gauge), F10 Survey
screen (Full Band + QO-100 tabs). Bridge, demo, and theme updated.

Docs: motor.mdx, survey.mdx, qo100-datv.mdx guide, tui.mdx updated
for 10 screens. Site builds 41 pages, all links valid.
2026-02-15 17:01:11 -07:00
23055f34ab Add alternative operating modes: spectrum, scan, monitor, lband, track
Firmware v3.02.0 adds three new vendor commands:
- 0xB7 SIGNAL_MONITOR: fast 8-byte combined signal read
- 0xB8 TUNE_MONITOR: tune + dwell + read in one round-trip
- 0xB9 MULTI_REG_READ: batch read up to 64 indirect registers

New tools/skywalker.py provides five modes that use the BCM4500's
AGC registers as a crude power detector across 950-2150 MHz IF,
even without demodulator lock:
- spectrum: sweep analyzer with ASCII/waterfall/matplotlib display
- scan: automated transponder scanner (sweep + peak detect + blind scan)
- monitor: real-time signal strength for dish alignment
- lband: direct input analyzer with L-band allocation annotations
- track: carrier/beacon tracker with CSV/JSON logging and drift detection

Extracts shared SkyWalker1 class and constants into skywalker_lib.py;
tune.py now imports from the shared library.
2026-02-12 17:29:00 -07:00
d9f51548e0 Fix BCM4500 boot: spurious I2C STOP corrupted FX2 controller
Removed I2CS bmSTOP "bus reset" from bcm4500_boot() and debug modes.
Sending STOP with no active transaction puts the FX2 I2C controller
into an inconsistent state where subsequent START+ACK detection fails.

Root cause identified through incremental debug modes (wValue 0x80-0x85)
on live hardware: mode 0x82 (with bmSTOP) fails, mode 0x85 (identical
but without bmSTOP) succeeds. Raw I2C reads confirm BCM4500 is alive
the entire time -- only the controller state is corrupted.

BCM4500 now boots successfully in ~90ms. Three I2C devices found on
bus: 0x08 (BCM4500), 0x10 (tuner/LNB), 0x51 (EEPROM).

Also in this commit:
- Timeout-protected I2C functions replacing fx2lib bare while loops
- I2C bus scan and debug mode infrastructure
- Kernel driver blacklist for dvb_usb_gp8psk
- Test tools for incremental boot debugging
- Technical findings documented in docs/boot-debug-findings.md
2026-02-12 10:34:15 -07:00
890a38bfa0 Fix BCM4500 I2C address and add hardware diagnostic commands
Correct BCM4500 I2C address from 0x10 (8-bit wire) to 0x08 (7-bit)
since fx2lib shifts internally. Add i2c_combined_read() with repeated
START for proper BCM4500 register access. Add I2C bus scan (0xB4),
raw read (0xB5), and indirect protocol diagnostic (0xB6) commands.
Single-transaction indirect reads/writes for BCM4500 register protocol.

Verified on hardware: BCM4500 ACKs at 0x08, BOOT_8PSK returns config
0x03. Register reads still return zeros — BCM4500 needs DSP firmware
loaded via LOAD_BCM4500 (0x88) before registers become functional.
2026-02-12 07:17:47 -07:00
5710584267 Add custom FX2 firmware and RAM loader for open-source development
Custom firmware (SDCC + fx2lib) implements all stock vendor commands
(0x80-0x94) plus new commands for spectrum sweep (0xB0), raw BCM4500
register access (0xB1/0xB2), and blind scan (0xB3). Compiles to 6.3KB
of code with healthy RAM margins.

RAM loader (fw_load.py) uses the FX2 0xA0 vendor request to load
firmware into RAM without touching EEPROM -- power cycle restores
factory firmware. Supports Intel HEX and raw binary formats.
2026-02-11 19:46:50 -07:00