skywalker-1/docs/skywalker1-master-reference.md
Ryan Malloy 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

73 KiB

Genpix SkyWalker-1 Master Hardware and Firmware Reference

Consolidated technical reference for the Genpix SkyWalker-1 DVB-S USB 2.0 satellite receiver. Derived from Linux kernel driver analysis (dvb_usb_gp8psk), Ghidra firmware reverse engineering (v2.06, v2.10 Rev.2, v2.13 FW1/FW2/FW3), Windows BDA driver source review, and custom firmware development (v3.01.0, SDCC + fx2lib).


Table of Contents

  1. Hardware Overview
  2. USB Interface
  3. Vendor Command Reference
  4. Configuration Status Byte
  5. Boot Sequence
  6. BCM4500 Demodulator Interface
  7. Tuning Protocol
  8. GPIF Streaming Path
  9. LNB and DiSEqC Control
  10. GPIO Pin Map
  11. Firmware Versions
  12. I2C Bus Architecture
  13. Custom Firmware v3.01.0
  14. DVB-S2 Incompatibility
  15. Kernel Driver Notes
  16. Firmware Storage Formats
  17. Debugging Reference
  18. Sources

1. Hardware Overview

The Genpix SkyWalker-1 is a standalone USB 2.0 DVB-S satellite receiver built around two ICs:

Component Part Role
MCU Cypress CY7C68013A (FX2LP) USB 2.0 Hi-Speed controller, 8051 core at 48 MHz
Demodulator Broadcom BCM4500 DVB-S / Turbo / DCII / DSS demodulator, 128-pin MQFP
EEPROM 24Cxx-family (I2C address 0x51) FX2 firmware storage, serial number, calibration
Tuner/LNB Unknown IC (I2C address 0x10) Tuner or LNB controller on shared I2C bus

The FX2 handles USB communication, LNB control, DiSEqC signaling, and orchestrates tuning via I2C commands to the BCM4500. The BCM4500 performs RF demodulation, forward error correction, and outputs an MPEG-2 transport stream on an 8-bit parallel bus. The FX2's GPIF engine transfers the transport stream directly into a USB bulk endpoint with zero firmware intervention in the data path.

1.1 Supported Modulations

Index Modulation Constant FEC Family
0 DVB-S QPSK ADV_MOD_DVB_QPSK Viterbi + Reed-Solomon
1 Turbo-coded QPSK ADV_MOD_TURBO_QPSK Turbo
2 Turbo-coded 8PSK ADV_MOD_TURBO_8PSK Turbo
3 Turbo-coded 16QAM ADV_MOD_TURBO_16QAM Turbo
4 Digicipher II Combo ADV_MOD_DCII_C_QPSK DCII
5 Digicipher II I-stream (split) ADV_MOD_DCII_I_QPSK DCII
6 Digicipher II Q-stream (split) ADV_MOD_DCII_Q_QPSK DCII
7 Digicipher II Offset QPSK ADV_MOD_DCII_C_OQPSK DCII
8 DSS QPSK ADV_MOD_DSS_QPSK Viterbi + Reed-Solomon
9 DVB-S BPSK ADV_MOD_DVB_BPSK Viterbi + Reed-Solomon

DVB-S2 is not supported. See Section 14.

1.2 RF Specifications

Parameter Value
IF frequency range 950 -- 2150 MHz
Symbol rate 256 Ksps -- 30 Msps
Input connector IEC F-type female
LNB voltage 13V / 18V (or 14V / 19V with USE_EXTRA_VOLT)
LNB current 450 mA continuous, 750 mA burst
Switch control 22 kHz, Tone Burst, DiSEqC 1.0/1.2, Legacy Dish Network

1.3 Board Block Diagram

                          +--[ I2C EEPROM 0x51 ]
                          |
  USB 2.0 HS             |    I2C Bus (400 kHz)
  Host PC  <----> [ CY7C68013A FX2LP ] <--I2C--> [ BCM3440 Tuner 0x10 ] <--gateway--> [ BCM4500 Demod ]
                                       <--I2C--> [ BCM4500 Direct 0x08 (status only) ]
                    |  8051 @ 48 MHz  |            |
                    |  GPIF Engine    |<-----------+  8-bit parallel TS
                    |  EP2 Bulk IN    |
                    |  GPIO (P0/P3)   |---> [ 22 kHz Osc ] ---> LNB/Coax
                    |                 |---> [ LNB Voltage Ctrl ]
                    +-----------------+
                          |
                          +--[ Tuner/LNB IC 0x10 ]

2. USB Interface

2.1 VID/PID Table

All Genpix products share VID 0x09C0:

PID Product cold_ids warm_ids Notes
0x0200 8PSK-to-USB2 Rev.1 Cold Yes No Requires FW01 upload to RAM
0x0201 8PSK-to-USB2 Rev.1 Warm No Yes Requires FW02 (BCM4500 firmware)
0x0202 8PSK-to-USB2 Rev.2 No Yes Boots from EEPROM
0x0203 SkyWalker-1 No Yes Boots from EEPROM
0x0204 SkyWalker-1 (alternate) No Yes Boots from EEPROM
0x0205 SkyWalker-2 -- -- Not in kernel 6.16.5
0x0206 SkyWalker CW3K No Yes Requires CW3K_INIT (0x9D)

PID 0x0203 was added to the kernel device table after v6.6.1.

2.2 USB Endpoints and Streaming Properties

Property Value
Control endpoint EP0 (default, vendor requests)
Bulk IN endpoint EP2 (0x82) -- MPEG-2 transport stream
Generic bulk CTRL endpoint 0x01 (BCM4500 FW02 upload, Rev.1 only)
URB count 7
URB buffer size 8192 bytes each
Stream type USB_BULK
FX2 controller type CYPRESS_FX2

2.3 Warm Boot Behavior

The SkyWalker-1 (PID 0x0203) enumerates directly as a "warm" device. The DVB-USB framework skips firmware download when cold_ids is NULL. No host-side firmware files are required.

Device PID Needs FW01? Needs FW02? Boot Source
Rev.1 Cold 0x0200 Yes -- RAM (empty)
Rev.1 Warm 0x0201 No Yes RAM (FW01 loaded)
Rev.2 0x0202 No No EEPROM
SkyWalker-1 0x0203 No No EEPROM
SkyWalker CW3K 0x0206 No No EEPROM

The firmware files dvb-usb-gp8psk-01.fw and dvb-usb-gp8psk-02.fw were never open-sourced or included in linux-firmware.


3. Vendor Command Reference

All vendor commands use USB control transfers:

  • USB Type: USB_TYPE_VENDOR
  • Timeout: 2000 ms (kernel driver)
  • Retry: Up to 3 attempts for IN operations if partial data received
  • Data buffer maximum: 80 bytes (kernel driver state structure)

3.1 Stock Command Table (0x80--0x9D)

The vendor command dispatcher at CODE:0056 validates bRequest in the range 0x80--0x9D (30 entries) and dispatches via an indexed jump table at CODE:0076. Rev.2 supports only 0x80--0x9A (27 entries).

Cmd Name Dir wValue wIndex wLength Purpose v2.06 Rev.2 v2.13
0x80 GET_8PSK_CONFIG IN 0 0 1 Read configuration status byte OK OK OK
0x81 SET_8PSK_CONFIG OUT varies 0 0 Set config (reserved) STALL STALL STALL
0x82 (reserved) -- -- -- -- Reserved STALL STALL STALL
0x83 I2C_WRITE OUT dev_addr reg_addr N Write to I2C device OK OK OK
0x84 I2C_READ IN dev_addr reg_addr N Read from I2C device OK OK OK
0x85 ARM_TRANSFER OUT 0/1 0 0 Start (1) / stop (0) MPEG-2 stream OK OK OK
0x86 TUNE_8PSK OUT 0 0 10 Set tuning parameters (Section 7) OK OK OK
0x87 GET_SIGNAL_STRENGTH IN 0 0 6 Read SNR and diagnostics OK OK Changed
0x88 LOAD_BCM4500 OUT 1 0 0 Initiate BCM4500 FW download STALL STALL STALL
0x89 BOOT_8PSK IN 0/1 0 1 Power on (1) / off (0) demodulator OK OK OK
0x8A START_INTERSIL IN 0/1 0 1 Enable (1) / disable (0) LNB supply OK OK OK
0x8B SET_LNB_VOLTAGE OUT 0/1 0 0 13V (0) or 18V (1) OK OK OK
0x8C SET_22KHZ_TONE OUT 0/1 0 0 Tone off (0) or on (1) OK OK OK
0x8D SEND_DISEQC_COMMAND OUT msg[0] 0 len DiSEqC message or tone burst OK OK OK
0x8E SET_DVB_MODE OUT 1 0 0 Enable DVB-S mode STALL STALL STALL
0x8F SET_DN_SWITCH OUT cmd7bit 0 0 Legacy Dish Network switch protocol OK OK OK
0x90 GET_SIGNAL_LOCK IN 0 0 1 Read signal lock status OK OK OK
0x91 I2C_ADDR_ADJUST IN 0/1 0 1 Inc/dec internal counter (debug) OK OK OK
0x92 GET_FW_VERS IN 0 0 6 Read firmware version + build date OK OK OK
0x93 GET_SERIAL_NUMBER IN 0 0 4 Read 4-byte serial from EEPROM OK OK OK
0x94 USE_EXTRA_VOLT OUT 0/1 0 0 Enable +1V LNB boost (14V/19V) OK OK OK
0x95 GET_FPGA_VERS IN 0 0 1 Read EEPROM hardware/platform ID OK OK OK
0x96 SET_LNB_GPIO_MODE OUT 0/1 0 0 Configure LNB GPIO output enables OK OK OK
0x97 SET_GPIO_PINS OUT bitmap 0 0 Direct write to LNB GPIO pins OK OK OK
0x98 GET_GPIO_STATUS IN 0 0 1 Read LNB feedback GPIO pin OK OK OK
0x99 GET_DEMOD_STATUS IN 0 0 1 Read BCM4500 register 0xF9 STALL Proto OK
0x9A INIT_DEMOD OUT 0 0 0 Trigger demod re-init (3 attempts) STALL Proto OK
0x9B (reserved) -- -- -- -- Reserved STALL N/A STALL
0x9C DELAY_COMMAND OUT delay 0 0 Host-controlled tuning delay + poll STALL N/A OK
0x9D CW3K_INIT / SET_MODE_FLAG OUT 0/1 0 0 CW3K init or conditional demod reset OK N/A Changed

Status key: OK = implemented. STALL = routes to stall handler. Proto = partial/prototype. N/A = out of range (Rev.2 supports 0x80--0x9A only). Changed = implementation differs between versions.

Driver usage notes:

  • The Linux driver only sends LOAD_BCM4500 (0x88) for Rev.1 Warm (PID 0x0201). On SkyWalker-1, bm8pskFW_Loaded is already set and 0x88 STALLs.
  • The Linux driver only sends CW3K_INIT (0x9D) for SkyWalker CW3K (PID 0x0206).

3.2 Vendor Command Dispatch Mechanism

The vendor command dispatcher at CODE:0056 (identical code address across v2.06, v2.13, and Rev.2) follows this logic:

1. Check bmRequestType bit 6: if not set, not a vendor request -> handle standard
2. Read bRequest from SETUPDAT[1]
3. Subtract 0x80 (command base offset)
4. Compare against maximum: < 0x1E (v2.06/v2.13) or < 0x1B (Rev.2)
5. If in range: double the index (2 bytes per entry) and JMP @A+DPTR to jump table
6. If out of range: route to STALL handler

The jump table at CODE:0076 contains 2-byte AJMP targets. Each entry points to the handler for commands 0x80 through 0x9D (or 0x9A for Rev.2).

Jump table layout (first 6 entries shown, Rev.2):

CODE:0076: 01C1  ; 0x80 GET_8PSK_CONFIG -> 0x01C1
CODE:0078: 034B  ; 0x81 SET_8PSK_CONFIG -> 0x034B (STALL)
CODE:007A: 034B  ; 0x82 (reserved) -> 0x034B (STALL)
CODE:007C: 0103  ; 0x83 I2C_WRITE -> 0x0103
CODE:007E: 00D9  ; 0x84 I2C_READ -> 0x00D9
CODE:0080: 00C2  ; 0x85 ARM_TRANSFER -> 0x00C2
...

3.3 Custom Firmware Commands (0xB0--0xB6)

Commands added in custom firmware v3.01.0:

Cmd Name Dir wValue wIndex wLength Purpose
0xB0 SPECTRUM_SWEEP OUT 0 0 10 Step through freq range, read SNR at each step
0xB1 RAW_DEMOD_READ IN reg 0 1 Read BCM4500 indirect register
0xB2 RAW_DEMOD_WRITE OUT reg data 0 Write BCM4500 indirect register
0xB3 BLIND_SCAN OUT 0 0 16 Try symbol rates at given freq, report lock
0xB4 I2C_BUS_SCAN IN 0 0 16 Probe all 7-bit addresses, return 16-byte bitmap
0xB5 I2C_RAW_READ IN addr7 reg N Combined write-read from any I2C device
0xB6 I2C_DIAG IN page 0 8 Step-by-step indirect register diagnostic

3.4 Detailed Parameter Formats

0x87 GET_SIGNAL_STRENGTH: Returns 6 bytes. Bytes 0--1 are a 16-bit SNR value (little-endian, dBu * 256 units). Bytes 2--5 are reserved/diagnostic. SNR scaling from Windows BDA driver: if snr_raw <= 0x0F00: strength = snr_raw * 17; else strength = 0xFFFF. Version differences: v2.06 polls 3 registers (0xA2, 0xA8, 0xA4) up to 6 times; v2.13 consolidates to 1 register.

0x8D SEND_DISEQC_COMMAND: When wLength > 0, the payload is a standard DiSEqC message (3--6 bytes) with wValue = msg[0] (framing byte, typically 0xE0 or 0xE1). When wLength == 0: wValue == 0 sends tone burst A; wValue != 0 sends tone burst B. See Section 9.

0x8F SET_DN_SWITCH: wValue carries a 7-bit Dish Network switch command, bit-banged LSB-first on GPIO P0.4. The 8th bit (0x80) of the original switch command controls LNB voltage and is sent separately via SET_LNB_VOLTAGE (0x8B).

0x92 GET_FW_VERS: Returns 6 bytes of hardcoded constants:

Byte 0: version minor_minor  (e.g., 0x04)
Byte 1: version minor        (e.g., 0x06)
Byte 2: version major        (e.g., 0x02)
Byte 3: build day            (e.g., 0x0D = 13)
Byte 4: build month          (e.g., 0x07 = July)
Byte 5: build year - 2000    (e.g., 0x07 = 2007)

Full version = byte[2] << 16 | byte[1] << 8 | byte[0]. Build date = (2000 + byte[5]) / byte[4] / byte[3].

0x93 GET_SERIAL_NUMBER: Returns 4 bytes read from I2C EEPROM at device address 0x51 (7-bit), extracted at 8-bit intervals using a shift/rotate routine.

0x94 USE_EXTRA_VOLT: wValue=1 writes 0x6A to XRAM 0xE0B6; wValue=0 writes 0x62. The difference is bit 3 (0x08), which controls the voltage boost on the LNB power regulator.

0x95 GET_FPGA_VERS: Reads from I2C EEPROM at 0x51. Despite the name, there is no FPGA on the SkyWalker-1 -- this returns a hardware platform ID. v2.06 reads EEPROM offset 0x31 (2 bytes); v2.13/Rev.2 read offset 0x00 (1 byte).

0xB0 SPECTRUM_SWEEP: 10-byte EP0 payload: [start_freq(u32 LE kHz), stop_freq(u32 LE kHz), step_khz(u16 LE)]. Programs BCM4500 at each frequency step, reads SNR, packs u16 LE results into EP2 bulk FIFO.

0xB3 BLIND_SCAN: 16-byte EP0 payload: [freq_khz(u32 LE), sr_min(u32 LE sps), sr_max(u32 LE sps), sr_step(u32 LE sps)]. Returns 8 bytes on lock [freq_khz(4) + sr_locked(4)] or 1 byte 0x00 if no lock found.

0xB4 I2C_BUS_SCAN: Returns a 16-byte bitmap (128 bits for addresses 0x00--0x77). Each bit position corresponds to a 7-bit address; bit set = ACK received. Known devices on the SkyWalker-1 bus:

Address Identity
0x08 BCM4500 demodulator (7-bit; wire addresses 0x10 write / 0x11 read)
0x10 Tuner or LNB controller
0x51 Configuration EEPROM (24Cxx-family)

4. Configuration Status Byte

Returned by GET_8PSK_CONFIG (0x80). Stored in IRAM at a version-dependent address.

Bit 7 (0x80): bmArmed         - MPEG-2 stream transfer armed / GPIF active
Bit 6 (0x40): bmDCtuned       - DC offset tuning complete (set for DCII modes)
Bit 5 (0x20): bmSEL18V        - 18V LNB voltage selected (else 13V)
Bit 4 (0x10): bm22kHz         - 22 kHz tone active
Bit 3 (0x08): bmDVBmode       - DVB mode enabled
Bit 2 (0x04): bmIntersilOn    - LNB power supply enabled
Bit 1 (0x02): bm8pskFW_Loaded - BCM4500 firmware loaded (always set on SkyWalker-1)
Bit 0 (0x01): bm8pskStarted   - Device booted and running
Firmware IRAM Address
v2.06 0x6D
Rev.2 v2.10.4 0x4E
v2.13 0x4F

The kernel driver checks these bits to decide which initialization steps to perform. On the SkyWalker-1 after a successful BOOT_8PSK, config_status = 0x03 (STARTED + FW_LOADED).


5. Boot Sequence

5.1 Kernel Driver Boot Flow

1. GET_8PSK_CONFIG (0x80) -- read config status byte
   |-- Check bit 0: bm8pskStarted?

2. If not started:
   |-- BOOT_8PSK (0x89, wValue=1)
   |-- GET_FW_VERS (0x92) -- read firmware version

3. If bit 1 clear (bm8pskFW_Loaded):
   |-- LOAD_BCM4500 (0x88) -- Rev.1 Warm only; STALLs on SkyWalker-1

4. If bit 2 clear (bmIntersilOn):
   |-- START_INTERSIL (0x8A, wValue=1) -- enable LNB power supply

5. SET_DVB_MODE (0x8E, wValue=1) -- STALLs on all SkyWalker-1 FW versions

6. ARM_TRANSFER (0x85, wValue=0) -- abort any pending MPEG transfer

7. Device ready for tuning

5.2 BCM4500 Boot Sequence (BOOT_8PSK, 0x89)

As implemented in bcm4500_boot() in custom firmware v3.01.0, reverse-engineered from stock v2.06 FUN_CODE_1D4F + FUN_CODE_0ddd:

Step  Action                              GPIO/I2C            Duration
----  ------------------------------------  -----------------   --------
1     Assert BCM4500 RESET                 P0.5 = LOW          --
2     Power on                             P0.1 = HIGH         --
                                           P0.2 = LOW
3     Wait for power settle                --                   30 ms
4     Release RESET                        P0.5 = HIGH          --
5     Wait for BCM4500 POR + ROM boot      --                   50 ms
6     I2C probe (read register 0xA2)       I2C read 0x08:0xA2  ~0.1 ms
7     Write init block 0 to page 0         I2C write 0xA6/A7/A8 ~2 ms
8     Write init block 1 to page 0         I2C write 0xA6/A7/A8 ~2 ms
9     Write init block 2 to page 0         I2C write 0xA6/A7/A8 ~1 ms
10    Set config_status = 0x03             --                   --

Total boot time: approximately 90 ms (30 ms power + 50 ms POR + ~10 ms I2C).

5.3 BCM4500 Initialization Data

Three register initialization blocks are written to BCM4500 indirect registers (page 0x00) via the 0xA6/0xA7/0xA8 protocol. Data extracted from stock v2.06 firmware FUN_CODE_0ddd:

Block Start Register Length Data (hex)
0 0x06 7 bytes 06 0b 17 38 9f d9 80
1 0x07 8 bytes 07 09 39 4f 00 65 b7 10
2 0x0F 3 bytes 0f 0c 09

Each block is written as: page select (0xA6 = 0x00), data bytes to 0xA7, trailing zero to 0xA7, then commit (0xA8 = 0x03). The firmware polls 0xA8 until the command completes before proceeding to the next block.

5.4 FX2 CPUCS Recovery

The FX2's CPUCS register at 0xE600 controls the 8051 run/halt state. The standard vendor request bRequest=0xA0 (RAM read/write) is handled by the FX2 boot ROM in silicon, not by user firmware. This means fw_load.py can reload firmware over a completely hung device:

sudo python3 tools/fw_load.py load firmware/build/skywalker1.ihx --wait 3

Writing 0x01 to CPUCS halts the CPU. New code is written to RAM. Writing 0x00 restarts it. The device re-enumerates with the new firmware.


6. BCM4500 Demodulator Interface

6.1 I2C Addressing — BCM3440 Tuner Gateway

CRITICAL (2025-02-19): The BCM4500's registers are accessed THROUGH the BCM3440 tuner's I2C gateway at address 0x10, NOT directly at 0x08. Stock firmware v2.06 disassembly of FUN_CODE_0DDD, FUN_CODE_10F2, and all internal register access functions confirms device address 0x10 is used for every register read/write.

Parameter Value
BCM3440 tuner gateway (7-bit) 0x10 — all BCM4500 register access
BCM3440 wire write / read 0x20 / 0x21
BCM4500 direct (7-bit) 0x08 — status byte only, no register addressing
BCM4500 wire write / read 0x10 / 0x11
Bus speed 400 kHz
FX2 I2C controller SFRs I2CS, I2DAT, I2CTL
Alternate probe addresses (v2.13) 0x3F, 0x7F

The BCM3440 tuner acts as an I2C bridge/gateway: register accesses in the 0xA0+ range sent to the tuner's address (0x10) are transparently forwarded to the BCM4500 demodulator. The BCM4500's own I2C address (0x08) only exposes a single status byte via simple reads — it does NOT support register-addressed reads at that address.

Stock firmware evidence (functions at wire address 0x20/0x21):

  • FUN_CODE_0DDD (init blocks): writes A6/A7/A8 via LCALL 0x1A81 with R7=0x10
  • FUN_CODE_10F2 (PLL/firmware download): writes A9/AA/AB via LCALL 0x1A81 with R7=0x10
  • FUN_CODE_15E9 (config mode): writes A0 via device 0x10
  • FUN_CODE_1556 (generic read): combined read [S][0x20][reg][Sr][0x21][data][P]

Stock I2C_READ (0x84) vendor command for BCM4500 (address 0x08): Simple read only — [S][0x11][data][P] — no register address sent. Returns whatever the BCM4500's I2C slave has ready (global status byte). Register address from wIndex is completely ignored for device 0x08 (confirmed by disassembly of FUN_CODE_2036).

The v2.13 firmware probes addresses 0x7F and 0x3F at startup (INT0 handler) to detect which demodulator variant is present.

6.2 Direct Registers

Accessed via I2C write/read through the BCM3440 gateway (address 0x10):

Register Function
0xA2 Status register (polled for readiness during boot)
0xA4 Lock/ready register; bit 5 (0x20) = signal locked
0xA6 Indirect page/address select
0xA7 Indirect data register (read/write)
0xA8 Indirect command register
0xF9 Demod status (read by v2.13 GET_DEMOD_STATUS / INT0 polling)

6.3 Indirect Register Protocol

The BCM4500 uses an indirect register access scheme through three directly-addressable registers:

Indirect Write Sequence:

1. I2C WRITE to 0x08, register 0xA6 <- page_number (typically 0x00)
2. I2C WRITE to 0x08, register 0xA7 <- data bytes (N bytes, auto-increment)
3. I2C WRITE to 0x08, register 0xA8 <- 0x03 (execute indirect write)
4. Poll register 0xA8 until bit 0 clear (command complete)
5. Optionally read back register 0xA7 to verify

Indirect Read Sequence:

1. I2C WRITE to 0x08, register 0xA6 <- target_register
2. I2C WRITE to 0x08, register 0xA7 <- 0x00 (placeholder)
3. I2C WRITE to 0x08, register 0xA8 <- 0x01 (execute indirect read)
4. Short delay (~1 ms)
5. I2C READ from 0x08, register 0xA7 <- result byte

6.4 Indirect Protocol Auto-Increment

The BCM4500's data register (0xA7) supports auto-increment for multi-byte writes within a single I2C transaction. When writing N data bytes to 0xA7 in one I2C WRITE operation (without issuing STOP between bytes), the BCM4500 internally advances its data buffer pointer after each byte. This allows writing an entire initialization block in a single I2C transaction:

I2C transaction:
  START -> 0x10 (write) -> 0xA7 (reg) -> data[0] -> data[1] -> ... -> data[N-1] -> STOP

The firmware exploits this for initialization blocks and tuning data, reducing I2C overhead compared to byte-by-byte writes.

Stock firmware init block write sequence (from FUN_CODE_0ddd):

1. I2C WRITE: [0x10] [0xA6] [0x00]           -- Page select = 0
2. I2C WRITE: [0x10] [0xA7] [data0..dataN]   -- Multi-byte data (auto-increment)
3. I2C WRITE: [0x10] [0xA7] [0x00]           -- Trailing zero (stock firmware quirk)
4. I2C WRITE: [0x10] [0xA8] [0x03]           -- Commit indirect write
5. Poll: I2C READ [0xA8] until bit 0 clear   -- Wait for completion

The trailing zero write (step 3) appears in all stock firmware versions. Its purpose is unclear -- it may zero-pad the data buffer or serve as an end-of-data marker within the BCM4500's indirect register engine.

6.5 Demodulator Scan

The tune function (stock firmware) tries up to 3 different I2C address configurations per attempt, with 3 outer retries (up to 9 total I2C programming attempts). This supports hardware variants where the BCM4500 may appear at different bus addresses.

v2.13 adds a boot-time probe: INT0 polls addresses 0x7F and 0x3F up to 40 times (0x28), setting flag _1_4 if neither responds. This prevents tuning attempts on boards with absent demodulators.

6.6 BCM4500 FEC Architecture

The BCM4500 contains two FEC decoder paths:

  1. Advanced Modulation Turbo FEC Decoder: Iterative turbo code decoder supporting QPSK (rates 1/4, 1/2, 3/4), 8PSK (rates 2/3, 3/4, 5/6, 8/9), 16QAM (rate 3/4), with Reed-Solomon (t=10) outer code.

  2. Legacy DVB/DIRECTV/DCII-Compliant FEC Decoder: Concatenated Viterbi inner decoder (convolutional code, rates 1/2 through 7/8) + Reed-Solomon outer decoder.

There is no LDPC or BCH decoder hardware. See Section 14.


7. Tuning Protocol

7.1 TUNE_8PSK Command Format (0x86)

The host sends a 10-byte OUT payload via USB control transfer:

USB SETUP:  bmRequestType=0x40, bRequest=0x86, wValue=0, wIndex=0, wLength=10

EP0BUF Layout:
  Byte  Content             Encoding
  ----  ------------------  ----------------
  [0]   Symbol Rate byte 0  Little-endian LSB
  [1]   Symbol Rate byte 1
  [2]   Symbol Rate byte 2
  [3]   Symbol Rate byte 3  Little-endian MSB
  [4]   Frequency byte 0    Little-endian LSB
  [5]   Frequency byte 1
  [6]   Frequency byte 2
  [7]   Frequency byte 3    Little-endian MSB
  [8]   Modulation Type      0--9 (see Section 1.1)
  [9]   Inner FEC Rate       Index into modulation-specific table

Symbol Rate is in samples per second (sps). The Windows driver multiplies ksps by 1000.

Frequency is the IF frequency in kHz (950000--2150000), computed by the host as (RF_freq - LO_freq) * multiplier.

7.2 Firmware EP0BUF Parsing

The firmware reads the 10-byte payload from EP0BUF (XRAM 0xE740--0xE749) and stores:

Source Destination Notes
EP0BUF[8] (modulation) IRAM 0x4D Direct copy
EP0BUF[9] (FEC) IRAM 0x4F Direct copy
EP0BUF[4--7] (frequency) XRAM 0xE0DB--0xE0DE Byte-reversed (LE to BE)
EP0BUF[0--3] (symbol rate) XRAM 0xE0CB--0xE0CE Byte-reversed (LE to BE)

The byte reversal converts host little-endian to BCM4500 big-endian so values can be written directly to the demodulator via I2C.

7.3 Modulation Dispatch

After parsing, the firmware validates the modulation type (bounds check < 10) and dispatches via a 20-byte jump table (10 entries x 2 bytes) at CODE:0873. Each handler:

  1. Validates the FEC index against the maximum for that modulation
  2. Looks up a preconfigured byte from an XRAM FEC rate table
  3. Writes configuration to four XRAM registers (0xE0EB, 0xE0EC, 0xE0F5, 0xE0F6)

Modulation jump table (from Rev.2 at CODE:0873):

Entry AJMP Target Modulation
0 0x08B7 DVB-S QPSK
1 0x08DF Turbo QPSK
2 0x08FA Turbo 8PSK
3 0x0915 Turbo 16QAM
4 0x0947 DCII Combo
5 0x094F DCII I-stream
6 0x0957 DCII Q-stream
7 0x095F DCII Offset QPSK
8 0x0887 DSS QPSK
9 0x0887 DVB BPSK (shares DSS handler)

DSS and DVB BPSK share the same handler. Their FEC lookup uses the same table (0xE0F9) but ORs the result with 0x80 to distinguish them from DVB-S QPSK.

7.4 FEC Rate Lookup Tables

Populated from the CODE-space init table at boot:

XRAM Base Modulation Max FEC Index Code Rates
0xE0F9 DVB-S QPSK, DSS, BPSK 7 1/2, 2/3, 3/4, 5/6, 7/8, auto, none
0xE0B7 Turbo QPSK 5 Turbo-specific rates
0xE0B1 Turbo 8PSK 5 Turbo-specific rates
0xE0BC Turbo 16QAM 1 Single code rate
0xE0BD DCII (all variants) 9 Combined code + modulation

7.5 BCM4500 XRAM Configuration After Dispatch

XRAM Addr Register DVB-S QPSK Turbo (Q/8/16) DCII DSS/BPSK
0xE0EB FEC Code Rate Table lookup Table lookup 0xFC (fixed) Table lookup OR 0x80
0xE0EC Modulation Type 0x09 0x09 From DCII table 0x09
0xE0F5 Demod Mode 0x10 0x10 0x10/0x11/0x12/0x16 0x10
0xE0F6 Turbo Flag 0x00 0x01 0x00 0x00

DCII Demod Mode values:

Modulation Index XRAM 0xE0F5
DCII Combo 4 0x10
DCII I-stream 5 0x12
DCII Q-stream 6 0x16
DCII Offset QPSK 7 0x11

DSS (8) and DVB BPSK (9) share the DVB-S QPSK handler; they use the same FEC table but OR the lookup value with 0x80.

7.6 Complete Tuning Sequence (Host to Satellite)

=== Phase 1: LNB Configuration (separate vendor commands) ===
1. SET_LNB_VOLTAGE (0x8B) -- GPIO P0.4 (no I2C)
   H / Circular-L -> wValue=1 (18V)
   V / Circular-R -> wValue=0 (13V)
2. SET_22KHZ_TONE (0x8C)  -- GPIO P0.3 (no I2C)
   High band -> wValue=1 (tone on)
   Low band  -> wValue=0 (tone off)
3. SEND_DISEQC_COMMAND (0x8D) -- if multi-switch needed

=== Phase 2: Tune Command ===
4. TUNE_8PSK (0x86) -- 10-byte payload

=== Phase 3: Firmware Internal Processing ===
5. EP0BUF parsing: mod/FEC to IRAM, freq/SR byte-reversed to XRAM
6. Modulation dispatch: FEC lookup, XRAM config registers set
7. GPIO P3.6: DVB mode select

=== Phase 4: BCM4500 I2C Programming (3 outer retries x 3 I2C addresses) ===
8.  Poll BCM4500 ready: I2C READ regs 0xA2, 0xA8, 0xA4
9.  Write page: I2C WRITE reg 0xA6 <- 0x00
10. Write config: I2C WRITE reg 0xA7 <- [freq, SR, FEC, mod, demod params]
11. Execute: I2C WRITE reg 0xA8 <- 0x03 (indirect write command)
12. Poll completion: I2C READ regs 0xA8, 0xA2
13. Verify: I2C READ reg 0xA7 (read-back compare)

=== Phase 5: Signal Acquisition (host polling) ===
14. GET_SIGNAL_LOCK (0x90) -- poll until non-zero
15. GET_SIGNAL_STRENGTH (0x87) -- read SNR

7.7 Signal Lock and Strength

GET_SIGNAL_LOCK (0x90): Returns 1 byte from BCM4500 register 0xA4. Bit 5 (0x20) indicates signal lock. The kernel driver interprets any non-zero value as locked and reports FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER.

GET_SIGNAL_STRENGTH (0x87): Returns 6 bytes. Bytes 0--1 = 16-bit SNR (LE, dBu * 256). SNR scaling: snr_raw * 17 maps 0x0000--0x0F00 to 0--65535 (100% at SNR >= 0x0F00).


8. GPIF Streaming Path

8.1 Data Flow

BCM4500              Cypress FX2 (CY7C68013A)              USB Host
Demodulator    P3.5  GPIF Engine    EP2 FIFO     EP2 (0x82)
  (I2C:0x08) <-----> (Master Read)  (AUTOIN)   ------------> Bulk IN
             8-bit   0xE4xx wfm     4x buffer                7 URBs
             parallel               8-bit                     x 8KB

The path is fully hardware-managed. The GPIF engine reads data from the BCM4500's 8-bit parallel transport stream output directly into the EP2 FIFO. The AUTOIN bit causes automatic USB commit when the FIFO buffer is full. The FLOWSTATE engine re-triggers GPIF transactions when buffer space becomes available. No firmware intervention occurs in the data path after initial setup.

8.2 Key Register Configuration

All values are identical across the three stock firmware versions:

Register Address Value Function
IFCONFIG 0xE601 0xEE Internal 48 MHz clock, GPIF master, async, debug
EP2FIFOCFG 0xE618 0x0C AUTOIN=1, ZEROLENIN=1, 8-bit data path
REVCTL 0xE60B 0x03 NOAUTOARM + SKIPCOMMIT
CPUCS 0xE600 bits [4:3]=10 48 MHz CPU clock
FLOWSTATEA 0xE668 OR 0x09 FSEN (flow state enable) + FS[3]
GPIFIE 0xE65C OR 0x3D Waveform, TC, DONE, FIFO flag, WF2 interrupts

IFCONFIG decode (0xEE = 1110_1110):

Bit Name Value Meaning
7 IFCLKSRC 1 Internal clock source
6 3048MHZ 1 48 MHz IFCLK frequency
5 IFCLKOE 1 IFCLK pin drives output (clock to BCM4500)
4 IFCLKPOL 0 Non-inverted clock polarity
3 ASYNC 1 Asynchronous GPIF (RDY pin handshaking)
2 GSTATE 1 Debug state output on PORTE
1:0 IFCFG 10 GPIF internal master mode

8.3 ARM_TRANSFER Sequences

Start streaming (wValue=1):

  1. Set config_byte bit 7 (streaming active)
  2. Load GPIF transaction count: GPIFTCB3:2 = 0x8000 (effectively infinite)
  3. Reset GPIF address and EP2 FIFO byte count
  4. Assert P3.5 LOW (BCM4500 transport stream enable)
  5. Wait for initial GPIF transaction (poll GPIFTRIG bit 7)
  6. De-assert P3.5 HIGH
  7. Trigger continuous GPIF read: GPIFTRIG = 0x04 (read into EP2)
  8. Set P0.7 LOW (streaming indicator)

Stop streaming (wValue=0):

  1. Set P0.7 HIGH (streaming stopped)
  2. Write EP2FIFOBCH = 0xFF (force-flush current buffer)
  3. Wait for GPIF idle (poll GPIFTRIG bit 7)
  4. Write OUTPKTEND = 0x82 (skip/discard partial EP2 packet)
  5. Clear config_byte bit 7 (streaming inactive)
  6. Set P3 bits 7:5 = 1 (de-assert all BCM4500 control lines)

8.4 Throughput Analysis

Metric Value
USB 2.0 HS bulk theoretical 480 Mbps
USB 2.0 HS bulk practical ~280 Mbps (~35 MB/s)
GPIF engine theoretical 48 MHz x 8 bits = 384 Mbps
Typical DVB-S TS rate 1--5 MB/s
Maximum DVB-S2 rate (hypothetical) ~7.25 MB/s (58 Mbps)

The USB/GPIF path has approximately 5x headroom even at maximum theoretical DVB-S2 data rates. The bottleneck for supported modes is the satellite link, not the USB data path.

8.5 FIFO Reset Sequence

All endpoint FIFOs are reset during initialization using the Cypress-prescribed procedure:

FIFORESET = 0x80    ; NAKALL: NAK all host transfers during reset
FIFORESET = 0x02    ; Reset EP2 FIFO
FIFORESET = 0x04    ; Reset EP4 FIFO
FIFORESET = 0x06    ; Reset EP6 FIFO
FIFORESET = 0x08    ; Reset EP8 FIFO
FIFORESET = 0x00    ; Release NAKALL

Three NOP instructions (mandatory SYNCDELAY) are inserted between each write per Cypress TRM requirements.

8.6 EP2 Endpoint Configuration

EP2CFG = 0xE2;  // valid=1, dir=IN, type=BULK, size=512, buf=DOUBLE
Bit Value Meaning
7 (VALID) 1 Endpoint enabled
6 (DIR) 1 IN (device to host)
5:4 (TYPE) 10 Bulk transfer
3 (SIZE) 0 512-byte packets
1:0 (BUF) 10 Double-buffered

EP4, EP6, EP8 are disabled (&= ~bmVALID).

8.7 Interrupt Handling

INT4 and INT6 (GPIF/FIFO events) share a common handler that sets a software flag (_0_1) and clears EXIF.4. The main loop polls this flag, enters CPU idle mode (PCON.0) between events, and checks EP2CS for buffer availability before re-arming the GPIF.

Main loop structure (from v2.06 FUN_CODE_2297):

void main_loop_poll(void) {
    if (_0_1) {                     // GPIF/FIFO event pending
        _0_1 = 0;                  // Clear flag
        if (EP2CS & bmEPFULL) {    // EP2 buffer full?
            // Wait for host to read EP2
        }
    } else {
        PCON |= 0x01;             // CPU idle until next interrupt
    }
}

8.8 Prior IFCONFIG Value

During early initialization, IFCONFIG is temporarily set to 0xCA before the final 0xEE:

Value Decode Difference from 0xEE
0xCA 1100_1010 GSTATE=0, ASYNC=0
0xEE 1110_1110 GSTATE=1, ASYNC=1 (final)

The temporary value disables async mode and debug state output during FIFO setup.


9. LNB and DiSEqC Control

9.1 LNB Voltage

LNB voltage is controlled via GPIO P0.4. No I2C is involved.

wValue Voltage GPIO P0.4 Polarization
0 13V LOW Vertical / Circular-Right
1 18V HIGH Horizontal / Circular-Left

USE_EXTRA_VOLT (0x94) enables a +1V boost (13V->14V, 18V->19V) for long cable runs by writing to XRAM 0xE0B6 (0x62=normal, 0x6A=boosted; bit 3 is the difference).

9.2 22 kHz Tone

Controlled via GPIO P0.3. P0.3 gates an external 22 kHz oscillator on the PCB. The firmware does not generate the 22 kHz carrier directly.

wValue State GPIO P0.3 Band
0 OFF LOW Low band (9.75 GHz LO on universal LNB)
1 ON HIGH High band (10.6 GHz LO on universal LNB)

9.3 DiSEqC Protocol Implementation

All firmware versions implement DiSEqC via Timer2-based GPIO bit-bang. The algorithm is identical across versions; only the data pin differs per PCB revision.

Timer2 configuration (identical across all versions):

Parameter Value
T2CON 0x04 (auto-reload, running)
RCAP2H:RCAP2L 0xF82F (reload = 63535)
CKCON.T2M 0 (Timer2 clock = 48 MHz / 12 = 4 MHz)
Tick period (65536 - 63535) / 4 MHz = 500.25 us

DiSEqC timing parameters:

Parameter Value
Bit period 1.5 ms (3 Timer2 ticks)
Byte period 13.5 ms (9 bits: 8 data + 1 parity)
Tone burst A/B 12.5 ms (25 ticks)
Pre-TX settling delay 7.5 ms (15 ticks)
Data '0' 1.0 ms tone + 0.5 ms silence (2/3 duty cycle)
Data '1' 0.5 ms tone + 1.0 ms silence (1/3 duty cycle)
Carrier frequency 22 kHz (external oscillator, gated by P0.3)

Manchester encoding (decompiled from Rev.2 FUN_CODE_213c):

Each DiSEqC bit = 3 Timer2 ticks:
  Tick 1: inter-bit gap (carrier OFF via P0.3 = 0)
  Tick 2: carrier ON (P0.3 = 1)
  Tick 3: if data_pin='1', carrier OFF early; if '0', carrier stays ON
  End: carrier always OFF

DiSEqC bit waveforms:

Data '0' (2/3 tone, 1/3 silence):
        Tick 1       Tick 2       Tick 3
      (500 us)     (500 us)     (500 us)
P0.3: _____|========|========|________|
           ^tone ON          ^tone OFF
     (setup gap)   (1.0 ms carrier)  (0.5 ms silence)

Data '1' (1/3 tone, 2/3 silence):
        Tick 1       Tick 2       Tick 3
      (500 us)     (500 us)     (500 us)
P0.3: _____|========|________|________|
           ^tone ON ^tone OFF early
     (setup gap)   (0.5 ms carrier)  (1.0 ms silence)

Decompiled bit symbol function (from Rev.2 FUN_CODE_213c):

void diseqc_bit_symbol(void) {
    wait_TF2();                // Tick 1: inter-bit gap (500 us)
    P0 |= 0x08;               // P0.3 = 1 -> 22 kHz carrier ON
    wait_TF2();                // Tick 2: carrier period (500 us)
    if (data_pin != 0) {       // If data = '1':
        P0 &= 0xF7;           //   P0.3 = 0 -> carrier OFF (short pulse)
    }
    wait_TF2();                // Tick 3: final period (500 us)
    P0 &= 0xF7;               // P0.3 = 0 -> carrier always OFF at end
}

Decompiled byte transmission (from Rev.2 FUN_CODE_07d1):

void diseqc_send_byte(char first_byte, byte data) {
    byte ones_count = 0;
    if (first_byte == 0) TF2 = 0;     // Sync timer on first byte

    for (char i = 8; i > 0; i--) {     // 8 bits, MSB first
        if (data & 0x80) {
            data_pin = 1;               // Set data = '1'
            diseqc_bit_symbol();
            ones_count++;
        } else {
            data_pin = 0;               // Set data = '0'
            diseqc_bit_symbol();
        }
        data <<= 1;                     // Next bit
    }
    data_pin = ~ones_count & 1;         // Odd parity
    diseqc_bit_symbol();                // Transmit parity bit
}

Timing per byte: 9 bits x 1.5 ms = 13.5 ms

Tone burst (mini DiSEqC): 25 consecutive Timer2 ticks of carrier (12.5 ms). Tone burst A: wValue==0 and wLength==0. Tone burst B: wValue!=0 and wLength==0.

Timer tick wait (TF2 polling, identical across all versions):

void wait_TF2(void) {
    while (TF2 == 0) {}    // Poll Timer2 overflow flag
    TF2 = 0;               // Clear flag for next tick
}

9.4 DiSEqC Signal Architecture

FX2 Firmware              External Hardware             Coax Cable
+------------------+     +--------------------+     +------------------+
| P0.3 (carrier)   |---->| 22 kHz oscillator  |---->| LNB power line   |
| (enable/disable)  |     | (gated by P0.3)    |     | (13V/18V + tone) |
|                   |     |                    |     |                  |
| P0.x (data bit)   |     | (internal firmware |     |                  |
| (firmware only)   |     |  logic only)       |     |                  |
+------------------+     +--------------------+     +------------------+

The data pin (P0.7 / P0.4 / P0.0 depending on firmware version) is used only internally by the firmware's Manchester encoding logic. It controls whether the carrier gate signal is cut short or held for the full bit period.

9.5 Windows BDA Driver DiSEqC Interface

The Windows driver exposes DiSEqC through a BDA extended property:

// GUID: {0B5221EB-F4C4-4976-B959-EF74427464D9}
typedef struct __DISEQC_COMMAND {
    UCHAR ucMessage[6];     // Framing, Address, Command, Data[0..2]
    UCHAR ucMessageLength;  // 3-6 for DiSEqC; 1 for tone burst
} DISEQC_COMMAND;

For tone burst: ucMessageLength=1, ucMessage[0]=SEC_MINI_A (0x00) or SEC_MINI_B (0x01).

9.6 SET_DN_SWITCH (0x8F) -- Legacy Dish Network

A 7-bit serial command bit-banged on GPIO P0.4:

  1. Assert P0.4 HIGH (start pulse), delay ~32 cycles
  2. De-assert P0.4, delay ~8 cycles
  3. Shift out 7 bits LSB-first via P0.4, ~8 cycle delays between bits

The kernel calls this via dishnetwork_send_legacy_command. Bit 7 (0x80) of the original switch command selects LNB voltage and is sent separately via SET_LNB_VOLTAGE.


10. GPIO Pin Map

10.1 Port 0 / Port A (SFR 0x80, IOA)

Pin v2.06 Rev.2 v2.10 v2.13 Custom v3.01.0
P0.0 -- LNB control (0x97) DiSEqC data --
P0.1 Power enable Power enable Power enable Power enable
P0.2 Power disable Power disable (init=0x84) Power disable Power disable
P0.3 22 kHz tone 22 kHz tone 22 kHz tone 22 kHz tone
P0.4 LNB 13V/18V LNB 13V/18V + DiSEqC data LNB 13V/18V LNB 13V/18V
P0.5 BCM4500 RESET GPIO status input (0x98) BCM4500 RESET BCM4500 RESET
P0.6 -- GPIO control (0x97) -- --
P0.7 DiSEqC data Streaming indicator Streaming indicator DiSEqC data + streaming

10.2 Port 3 / Port D (SFR 0xB0, IOD)

Pin Function Notes
P3.0 Init HIGH
P3.4 GPIO control Used by Rev.2 FUN_CODE_1fcf
P3.5 TS_EN Transport stream enable: LOW=active, HIGH=idle
P3.6 DVB mode BCM4500 mode select; DiSEqC direction (Rev.2)
P3.7 BCM4500 control De-asserted (HIGH) when streaming stops

10.3 Port B (XRAM-mapped IOB)

Used by internal debug commands 0x96--0x98:

Pin v2.06/v2.13 Rev.2
IOB.0 GPIO status input (0x98) --
IOB.1 LNB control (0x97) --
IOB.2 LNB control (0x97) --
IOB.3 LNB GPIO mode (0x96) --
IOB.4 -- LNB GPIO mode (0x96) + control (0x97)

10.4 DiSEqC Data Pin Summary

Firmware Version Data Pin Carrier Pin
v2.06 P0.7 P0.3
Rev.2 v2.10 P0.4 P0.3
v2.13 P0.0 P0.3
Custom v3.01.0 P0.7 P0.3

The carrier pin (P0.3) is the same across all versions.

10.5 Initial GPIO State

Register Value Decode
IOA (P0) 0x84 P0.7=1 (idle), P0.2=1 (power disable active)
IOD (P3) 0xE1 P3.7:5=1 (controls idle), P3.0=1
OEA 0xBE P0.1-5,7 as outputs

11. Firmware Versions

11.1 Version Table

Firmware Version ID Build Date PID Functions Binary Size SP
v2.06.04 0x020604 2007-07-13 0x0203 61 9,472 bytes 0x72
Rev.2 v2.10.04 0x020A04 2010-03-12 0x0202 107 8,843 bytes 0x4F
v2.13.01 (FW1) 0x020D01 2010-03-12 0x0203 82-88 9,322 bytes 0x50
v2.13.02 (FW2) 0x020D01 2010-03-12 0x0203 83 9,377 bytes 0x50
v2.13.03 (FW3) 0x020D01 2010-03-12 0x0203 83 9,369 bytes 0x52
Custom v3.01.0 0x030100 2026-02-12 0x0203 N/A ~3 KB (RAM) N/A

Rev.2 v2.10 targets PID 0x0202 (different product). The v2.13 sub-variants target different SkyWalker-1 hardware sub-revisions. Custom v3.01.0 is compiled with SDCC + fx2lib and loaded into FX2 RAM (not flashed to EEPROM).

11.2 Kernel Version Constants

From gp8psk-fe.h:

GP8PSK_FW_REV1 = 0x020604    (v2.06.4)
GP8PSK_FW_REV2 = 0x020704    (v2.07.4)

If fw_vers >= GP8PSK_FW_REV2, the kernel enables Rev.2-specific code paths. The v2.10 and v2.13 firmwares are newer than either kernel constant.

11.3 Key Architectural Differences

Feature v2.06 Rev.2 v2.10 v2.13
Vendor commands 30 (0x80--0x9D) 27 (0x80--0x9A) 30 (0x80--0x9D)
INT0 handler USB re-enumeration USB re-enumeration Demod availability polling
Demod probe at boot No No Yes (40 attempts at 0x7F + 0x3F)
Retry loops No No Yes (20-attempt with checksum verify)
HW revision detection No Yes (descriptor walker) Yes (flag _1_3)
DiSEqC data pin P0.7 P0.4 P0.0
Config byte IRAM 0x6D 0x4E 0x4F
Descriptor base 0x1200 0x0E00 0x0E00
Init table address CODE:0B46 CODE:0B48 CODE:0B88
BCM4500 status poll 3 registers 3 registers 1 register (consolidated)
Anti-tampering string No No Yes (at firmware offset 0x1880)
New commands -- 0x99/0x9A proto 0x99, 0x9A, 0x9C
0x9D behavior HW revision mode N/A (out of range) Conditional demod reset

11.4 v2.13 Sub-Variant Differences

The three v2.13 sub-variants target fundamentally different hardware interfaces:

Aspect FW1 (v2.13.1) FW2 (v2.13.2) FW3 (v2.13.3)
Demod interface I2C bus Parallel bus (P0/P1) Parallel bus (enhanced)
Bus protocol I2C START/STOP/ACK Single-phase P1 read Dual-phase P1 read + OR accumulate
Stack pointer 0x50 0x50 0x52
P0 init 0xa4 0xa4 0xa0
Status register INTMEM 0x4F INTMEM 0x4F INTMEM 0x51
Config source Hardcoded External (0xE080-0xE08E) External (0xE080-0xE08E)
Binary distance from FW1 -- 3,993 bytes 3,789 bytes
Binary distance from FW2 3,993 bytes -- 1,525 bytes

FW1 uses standard I2C master-mode transactions. FW2/FW3 use a parallel data bus with P0 for control signals (chip select, read strobe) and P1 for 8-bit data. FW3 adds dual-phase reading with OR-accumulation, likely for a demodulator chip with different bus timing. The updater program selects the correct sub-variant based on hardware detection.

11.5 Binary Comparison Matrix

Byte-level similarity (percentage of matching bytes within shared length):

v2.06 v2.13.1 v2.13.2 v2.13.3 Rev.2
v2.06 -- 4.8% 4.3% 4.3% 6.0%
v2.13.1 -- 57.2% 59.4% 8.0%
v2.13.2 -- 83.5% 5.8%
v2.13.3 -- 5.8%
Rev.2 --

The very low similarity between major versions (4--8%) indicates complete recompilation with different linker configurations. Functions relocate even when logic is identical.

11.6 Anti-Tampering (v2.13 Only)

At firmware offset 0x1880, all v2.13 sub-variants contain:

"Tampering is detected. Attempt is logged. Warranty is voided ! \n"

Followed by I2C register write commands (01 10 aa 82 02 41 41 83). This string and mechanism are absent from v2.06 and Rev.2.

11.7 Rev.2 as Transitional Firmware

Rev.2 v2.10.4 sits architecturally between v2.06 and v2.13:

  • Adopted v2.13's descriptor base (0x0E00) and similar stack pointer
  • Retained v2.06's INT0 USB re-enumeration behavior
  • Has the most functions (107) but smallest binary (~8.8 KB) due to granular decomposition
  • Lacks v2.13's demodulator polling, retry loops, and additional vendor commands

11.8 Key Function Correspondence Across Versions

v2.06 Function Rev.2 Function v2.13 Function Role
main (0x188D) main (0x155F) main_entry (0x170D) RESET vector: clear IRAM, process init table
FUN_CODE_09a7 FUN_CODE_09a9 FUN_CODE_0800 Main init + main loop
FUN_CODE_13c3 FUN_CODE_10d9 FUN_CODE_11ab USB/peripheral descriptor setup
FUN_CODE_032a FUN_CODE_0319 FUN_CODE_034e Standard USB request handler
FUN_CODE_0056 vendor_cmd_dispatch FUN_CODE_0056 Vendor request dispatcher (identical code)
FUN_CODE_2297 -- FUN_CODE_21ec Main loop poll (USB IRQ processing)
FUN_CODE_1919 FUN_CODE_0d7c FUN_CODE_1800 GPIF/FIFO management
FUN_CODE_1d4f -- -- v2.06 demod init (GPIO-based)
-- -- FUN_CODE_1d4b v2.13 demod init (I2C write to 0x7F/0xF0)
FUN_CODE_0ddd FUN_CODE_0c64 FUN_CODE_0ca4 BCM4500 firmware loader
FUN_CODE_2000 -- FUN_CODE_208d BCM4500 status polling
FUN_CODE_1dfb FUN_CODE_1bda FUN_CODE_14b9 Delay loop (clock-speed-aware)
INT0_vec (0x0003) INT0_ISR (0x0003) INT0_vector (0x0003) INT0 handler (different purpose)
-- -- FUN_CODE_2239 v2.13 I2C single-byte read helper
-- -- FUN_CODE_2031 v2.13 USB reconnect function
-- -- FUN_CODE_1799 v2.13 demod signature verification
-- -- FUN_CODE_1ac6 v2.13 tuning acquisition sequence

11.9 INT0 Handler Evolution

The INT0 interrupt vector (CODE:0003) was repurposed between firmware versions:

v2.06 and Rev.2 -- USB Re-enumeration:

void INT0_vec(void) {
    if (flag == 0) CPUCS |= 0x08;      // CPUCS bit 3
    else           CPUCS |= 0x0A;      // CPUCS bits 3+1
    delay(5, 0xDC);                     // ~1500 cycles
    EPIRQ = 0xFF;                       // Clear endpoint IRQs
    USBIRQ = 0xFF;                      // Clear USB IRQs
    EXIF &= 0xEF;                       // Clear external interrupt flag
    CPUCS &= 0xF7;                      // Clear CPUCS bit 3
}

Pulses CPUCS.3 to trigger a controlled USB re-enumeration, then clears all pending interrupts.

v2.13 -- Demodulator Availability Polling:

void INT0_vector(void) {
    for (counter = 0x28; counter != 0; counter--) {     // 40 attempts
        byte result = I2C_read(0x7F);                   // Demod address A
        if (result != 0x01) {
            result = I2C_read(0x3F);                    // Demod address B
            if (result != 0x01) break;
        }
    }
    no_demod_flag = (counter == 0);     // Set if loop exhausted
}

Polls two I2C addresses (0x7F, 0x3F) to detect which demodulator variant is present. The no_demod_flag prevents tuning attempts on boards with absent or failed demodulators.

In v2.13, the USB re-enumeration code was moved to FUN_CODE_2031 and called as a normal function before the main loop starts, freeing INT0 for demodulator polling.

11.10 v2.13 Integrity Verification

v2.13 performs two integrity checks during initialization, absent from v2.06 and Rev.2:

Demodulator Signature Verification (FUN_CODE_1799):

  1. Writes 4 bytes to I2C device 0x7F, register 0xF0
  2. Reads 5 bytes from register 0x0A (stepping by 2), each character
  3. Subtracts 0x30 ('0') from each byte (ASCII to binary)
  4. Sums values and compares against expected parameter (0x021C)
  5. Up to 20 retry attempts

Descriptor Checksum Verification (FUN_CODE_1ca0):

  1. Iterates bytes 6 through 0x29 (36 bytes) of a descriptor block
  2. Computes running sum, compares against 0x0706
  3. Iterates bytes 0x2C through 0x4F (36 bytes) of same block
  4. Computes second sum, compares against 0x0686
  5. Up to 20 retry attempts

Both checks call FUN_CODE_1ac6(100) (tuning acquisition with 100 ms delay) as a recovery action if verification fails after all attempts.

11.11 XRAM Initialization Table

All firmware versions initialize XRAM peripheral registers from a table stored in CODE space. The table is processed at startup before entering the main loop.

Table format (all versions):

Each entry: [addr_hi] [addr_lo] [data_byte]
Terminator: [0x00] [0x00] (address 0x0000)

The parser reads 3 bytes at a time: a 16-bit XRAM address (big-endian) and a data byte. It writes the byte to the address until it encounters address 0x0000.

Key XRAM registers initialized from the table:

XRAM Address Register Typical Value Purpose
0xE604 FIFORESET 0x80 Start FIFO reset sequence
0xE601 IFCONFIG 0xCA Initial interface config (overwritten later)
0xE610 EP2CFG 0xE2 EP2 bulk IN, 512-byte, double-buffered
0xE612 EP4CFG 0x00 EP4 disabled
0xE618 EP2FIFOCFG 0x0C AUTOIN, ZEROLENIN, 8-bit
0xE620 REVCTL 0x03 NOAUTOARM + SKIPCOMMIT
0xE67A I2CTL 0x01 I2C 400 kHz
0xE68A EP0BCH 0x00 EP0 byte count high = 0

Init table addresses by version:

Firmware Table Address
v2.06 CODE:0B46
Rev.2 CODE:0B48
v2.13 CODE:0B88

11.12 Main Loop Architecture

All firmware versions use the same main loop structure: poll the SUDAV (setup data available) interrupt flag, process vendor commands, then idle the CPU until the next interrupt.

v2.06 (simplified decompilation):

void main_loop(void) {    // FUN_CODE_09a7
    // 1. Process init table from CODE:0B46
    // 2. Call FUN_CODE_13c3 (USB/peripheral setup)
    // 3. EA = 1 (global interrupts enable)

    while (1) {
        if (sudav_flag) {
            handle_setupdata();     // Process USB SETUP packet
            sudav_flag = 0;
        }
        if (gpif_flag) {
            handle_gpif_event();
            gpif_flag = 0;
        } else {
            PCON |= 0x01;          // CPU idle until next interrupt
        }
    }
}

The SUDAV ISR simply sets sudav_flag = 1 and clears the interrupt. All actual USB processing happens in the main loop context.


12. I2C Bus Architecture

12.1 FX2 I2C Controller

The FX2's I2C master controller is a hardware peripheral accessed through SFRs:

SFR Address Function
I2CS 0xE678 (XRAM) I2C control/status register
I2DAT 0xE679 (XRAM) I2C data register
I2CTL 0xE67A (XRAM) I2C control (speed selection)

Key I2CS bits: bmSTART (initiate START), bmSTOP (initiate STOP), bmLASTRD (signal last read byte), bmDONE (transaction byte complete), bmACK (ACK received), bmBERR (bus error).

12.2 Bus Speed

The I2C bus speed is 400 kHz, set via:

  • C2 EEPROM header config byte = 0x40 (at boot)
  • I2CTL = bm400KHZ (in custom firmware)

12.3 Known Bus Devices

7-bit Address Wire Write/Read Identity
0x08 0x10 / 0x11 BCM4500 demodulator
0x10 0x20 / 0x21 Tuner or LNB controller
0x51 0xA2 / 0xA3 Configuration EEPROM (24Cxx-family)

The EEPROM at 0x51 stores: device serial number (read by GET_SERIAL_NUMBER 0x93), hardware platform ID (read by GET_FPGA_VERS 0x95), and calibration data.

12.4 Combined Write-Read (Repeated START) Protocol

All BCM4500 register reads use the I2C combined write-read protocol with a repeated START condition. This is required because the BCM4500 uses a register-addressed protocol where the register number must be sent as a write phase before the read phase:

Complete I2C transaction for reading register 0xA2 from device 0x08:

  Phase 1 (Write):
    [S] [0x10] [ACK] [0xA2] [ACK]
     |    |      |      |      |
     |    |      |      |      +-- BCM4500 ACKs register address
     |    |      |      +--------- Register address
     |    |      +---------------- BCM4500 ACKs its address
     |    +----------------------- Device address (0x08 << 1) = 0x10 (write)
     +---------------------------- START condition

  Phase 2 (Read with Repeated START):
    [Sr] [0x11] [ACK] [DATA] [NACK] [P]
     |     |      |      |      |      |
     |     |      |      |      |      +-- STOP condition
     |     |      |      |      +--------- Master NACKs (last byte)
     |     |      |      +---------------- Register data
     |     |      +----------------------- BCM4500 ACKs its address
     |     +------------------------------ Device address (0x08 << 1 | 1) = 0x11 (read)
     +------------------------------------ REPEATED START (no STOP between phases)

The repeated START (Sr) is essential. A STOP between phases would release the bus, and the BCM4500 would lose the register address context.

FX2 I2C SFR sequence for combined read (from custom firmware):

I2CS |= bmSTART;        // Generate START
I2DAT = 0x10;           // Write: device addr + W
// wait bmDONE, check bmACK
I2DAT = 0xA2;           // Write: register address
// wait bmDONE, check bmACK
I2CS |= bmSTART;        // Generate REPEATED START (no STOP first!)
I2DAT = 0x11;           // Write: device addr + R
// wait bmDONE, check bmACK
I2CS |= bmLASTRD;       // Signal this is the last read byte
tmp = I2DAT;            // Dummy read (triggers first clock burst)
// wait bmDONE
I2CS |= bmSTOP;         // Generate STOP after reading
data = I2DAT;           // Read actual data byte
// wait bmSTOP to clear

12.5 I2C STOP Corruption Bug

Sending I2CS |= bmSTOP when no I2C transaction is active (no prior START issued, bus idle) corrupts the FX2 I2C controller's internal state machine. The bmSTOP bit may not self-clear, and subsequent START conditions fail to detect ACK from slaves.

This was the root cause of the firmware hang in custom v3.01.0 during boot. The stock firmware's "bus reset" step:

/* BROKEN: */
I2CS |= bmSTOP;
i2c_wait_stop();

was removed. The correct approach is to simply proceed with a new START condition. If the bus is idle (after power-on or after the previous transaction completed normally), the START succeeds and the controller enters its normal operating state. The Cypress TRM does not document STOP as a standalone bus-reset mechanism.

12.6 Timeout Protection

The fx2lib I2C functions poll bmDONE with no timeout:

while (!(I2CS & bmDONE) && !cancel_i2c_trans);

Since cancel_i2c_trans is never set during normal operation, these loops are effectively infinite. The custom firmware replaces all fx2lib I2C functions with timeout-protected wrappers:

#define I2C_TIMEOUT 6000

static BOOL i2c_wait_done(void) {
    WORD timeout = I2C_TIMEOUT;
    while (!(I2CS & bmDONE)) {
        if (--timeout == 0) return FALSE;
    }
    return TRUE;
}

A WORD counter of 6000 decremented in a tight SDCC-compiled loop at 48 MHz gives approximately 5--10 ms per wait. At 400 kHz I2C, a single byte transfer takes 22.5 us, so the timeout provides over 200x margin for normal operations.


13. Custom Firmware v3.01.0

13.1 Overview

Custom replacement firmware built with SDCC and fx2lib. Loaded into FX2 RAM for testing via fw_load.py (not flashed to EEPROM).

Property Value
Toolchain SDCC + fx2lib
Source firmware/skywalker1.c (1351 lines)
Version ID 0x030100
Build date 2026-02-12
Load method RAM upload via tools/fw_load.py

13.2 Stock-Compatible Commands

The custom firmware implements all commands needed for the kernel driver: GET_8PSK_CONFIG (0x80), ARM_TRANSFER (0x85), TUNE_8PSK (0x86), GET_SIGNAL_STRENGTH (0x87), BOOT_8PSK (0x89), START_INTERSIL (0x8A), SET_LNB_VOLTAGE (0x8B), SET_22KHZ_TONE (0x8C), SEND_DISEQC (0x8D), GET_SIGNAL_LOCK (0x90), GET_FW_VERS (0x92), USE_EXTRA_VOLT (0x94).

13.3 Custom Commands

Command Function
SPECTRUM_SWEEP (0xB0) Step through frequency range reading signal energy
RAW_DEMOD_READ (0xB1) Read any BCM4500 indirect register
RAW_DEMOD_WRITE (0xB2) Write any BCM4500 indirect register
BLIND_SCAN (0xB3) Try symbol rates at a frequency looking for lock
I2C_BUS_SCAN (0xB4) Probe all 7-bit I2C addresses
I2C_RAW_READ (0xB5) Read from any I2C device address
I2C_DIAG (0xB6) Step-by-step indirect register read diagnostic

13.4 Debug Boot Modes

The BOOT_8PSK (0x89) command supports incremental debug modes via wValue:

wValue Action Result
0x80 No-op: return config_status and boot_stage Works
0x81 GPIO + power + delays only (no I2C) Works
0x82 GPIO + power + I2C probe (bmSTOP removed) Works
0x83 GPIO + power + probe + init block 0 Works
0x84 I2C-only probe (chip already powered) Works
0x85 Same as 0x82 without bmSTOP Works
0x01 Full boot (production) Works
0x00 Shutdown Works

These modes were used to isolate the I2C STOP corruption bug (see Section 12.4).

13.5 Key Implementation Patterns

I2C Combined Read (repeated START):

static BOOL i2c_combined_read(BYTE addr, BYTE reg, BYTE len, BYTE *buf) {
    I2CS |= bmSTART;
    I2DAT = addr << 1;          // START + write address
    // ... wait for DONE, check ACK ...
    I2DAT = reg;                // Register address
    // ... wait for DONE, check ACK ...
    I2CS |= bmSTART;
    I2DAT = (addr << 1) | 1;   // REPEATED START + read address
    // ... read len bytes with LASTRD/STOP on final byte ...
}

BCM4500 Init Block Write:

static BOOL bcm_write_init_block(const __code BYTE *data, BYTE len) {
    bcm_direct_write(BCM_REG_PAGE, 0x00);           // Page select
    i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_DATA, len, data);  // Data
    bcm_direct_write(BCM_REG_DATA, 0x00);            // Trailing zero
    bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE);    // Commit (0x03)
    return bcm_poll_ready();                          // Wait for completion
}

14. DVB-S2 Incompatibility

14.1 Definitive Conclusion

The SkyWalker-1's inability to receive DVB-S2 is a fundamental hardware limitation of the BCM4500 demodulator silicon. The BCM4500 was designed before the DVB-S2 standard was ratified (March 2005) and contains no LDPC or BCH decoder hardware. No firmware update can add DVB-S2 support.

14.2 FEC Architecture Comparison

Feature BCM4500 (SkyWalker-1) DVB-S2 Requirement
Inner FEC Viterbi (DVB-S) or Turbo (proprietary) LDPC
Outer FEC Reed-Solomon (t=10) BCH
Block size Convolutional / short turbo blocks 64,800 or 16,200 bits
Decoder type Trellis (Viterbi) or iterative turbo Iterative belief propagation
Hardware IP Hardwired Viterbi + turbo silicon Requires dedicated LDPC engine

14.3 Evidence

From firmware analysis:

  1. The firmware modulation dispatch table has exactly 10 entries (0--9), with no DVB-S2-specific modes. The bounds check at CODE:0866 rejects values >= 10.
  2. No LDPC/BCH code rate values exist in any FEC lookup table. The XRAM tables at 0xE0B1, 0xE0B7, 0xE0BC, 0xE0BD, and 0xE0F9 contain only Viterbi rates (1/2 through 7/8), turbo rates, and DCII combined codes.
  3. No DVB-S2-specific register addresses appear in any I2C traffic. The BCM4500 is programmed exclusively through indirect registers 0xA6/0xA7/0xA8 with page 0x00.

From Windows BDA driver source:

  1. SkyWalker1TunerFilter.cpp (line 1070): else if(ulNewInnerFecType == BDA_FEC_VITERBI) -- only Viterbi FEC is accepted; any other type returns STATUS_INVALID_PARAMETER.
  2. SkyWalker1Control.cpp (line 292): ucCommand[8] = ADV_MOD_DVB_QPSK; -- the driver hardcodes modulation type 0 (DVB-S QPSK) regardless of application request.
  3. SkyWalker1Control.h (lines 64--74): modulation constants cap at ADV_MOD_DVB_BPSK (9). No value 10+ exists.

From datasheets:

  1. The BCM4500 datasheet describes exactly two FEC paths: "an advanced modulation turbo decoder" and "a DVB/DIRECTV/DCII-compliant FEC decoder." No third path for LDPC/BCH.
  2. BCM4500 specification: 128-pin MQFP, 3.3V I/O, 1.8V digital, symbol rate 256 Ksps to 30 Msps. No mention of LDPC, BCH, or DVB-S2.

14.4 Broadcom DVB-S2 Chip Timeline

Chip Year DVB-S2? Notes
BCM4500 ~2003 No Turbo FEC + legacy Viterbi/RS
BCM4501 2006 Yes First dual-tuner DVB-S2; LDPC/BCH
BCM4505 2007 Yes Single-channel, 65nm
BCM4506 2007 Yes Dual-channel, 65nm

Broadcom restricted BCM4501/4505/4506 sales to set-top box manufacturers, preventing Genpix from using them.

14.5 What Genpix Did

Released the SkyWalker-3, replacing the BCM4500 with a different demodulator (likely STMicroelectronics STV0903). The trade-off: gained DVB-S2 LDPC/BCH support, lost proprietary turbo-FEC support (turbo codes are Broadcom/EchoStar proprietary).

14.6 USB Data Path is Not the Bottleneck

The GPIF/USB 2.0 path has approximately 5x headroom for DVB-S2 rates (~58 Mbps max vs ~280 Mbps USB practical throughput). The 8-bit transport stream interface uses the same MPEG-TS format (188-byte packets). The bottleneck is the demodulator silicon.


15. Kernel Driver Notes

15.1 Module Names

  • dvb_usb_gp8psk -- USB transport and device management
  • gp8psk_fe -- DVB frontend (demodulation, tuning)

15.2 Kernel Driver Race Condition

The kernel module auto-loads via udev when VID:PID 09C0:0203 appears on the USB bus (every FX2 re-enumeration after firmware load). The driver races with test tools and sends its own BOOT_8PSK command.

Symptoms:

  • "resource busy" or "entity not found" errors from test scripts
  • BCM4500 enters unexpected state from partial kernel initialization
  • Kernel driver detaches mid-test

Fix: Blacklist the module:

# /etc/modprobe.d/blacklist-gp8psk.conf
blacklist dvb_usb_gp8psk
blacklist gp8psk_fe

Then unload: sudo modprobe -r dvb_usb_gp8psk gp8psk_fe

15.3 FPGA Version Failure

gp8psk: usb in 149 operation failed.
gp8psk: failed to get FPGA version

Command 0x95 (GET_FPGA_VERS, decimal 149) fails on some SkyWalker-1 units. The driver logs the failure but continues normally.

15.4 Commands Used by Kernel Driver

Command Usage Notes
0x80 GET_8PSK_CONFIG Boot check Always
0x83 I2C_WRITE BCM4500 reg writes Via frontend ops
0x84 I2C_READ BCM4500 reg reads Via frontend ops
0x85 ARM_TRANSFER Stream start/stop Always
0x86 TUNE_8PSK Frequency tuning Via frontend ops
0x87 GET_SIGNAL_STRENGTH SNR readback Via frontend ops
0x88 LOAD_BCM4500 BCM4500 FW load Rev.1 Warm only (STALLs on SW-1)
0x89 BOOT_8PSK Power on/off Always
0x8A START_INTERSIL LNB power Always
0x8B SET_LNB_VOLTAGE 13V/18V Via frontend ops
0x8C SET_22KHZ_TONE Tone control Via frontend ops
0x8D SEND_DISEQC DiSEqC messages Via frontend ops
0x8F SET_DN_SWITCH Legacy Dish switch Via send_legacy_dish_cmd callback
0x90 GET_SIGNAL_LOCK Lock status Via frontend ops
0x92 GET_FW_VERS Version check Boot only
0x94 USE_EXTRA_VOLT +1V boost Via enable_high_lnb_voltage callback
0x95 GET_FPGA_VERS Platform ID Boot only
0x9D CW3K_INIT CW3K init PID 0x0206 only

16. Firmware Storage Formats

16.1 Cypress C2 EEPROM Boot Format

The SkyWalker-1 firmware is stored in Cypress C2 IIC second-stage boot format, read by the FX2's internal boot ROM on power-up.

Header (8 bytes):

Offset Size Field SkyWalker-1 Value
0 1 Marker 0xC2 (external memory, large code model)
1 2 VID (LE) 0x09C0
3 2 PID (LE) 0x0203
5 2 DID (LE) 0x0000
7 1 Config 0x40 (400 kHz I2C)

Code segments: 2-byte length (BE) + 2-byte target address (BE) + data. Maximum segment size: 1023 bytes (FX2 I2C boot ROM buffer limit). All SkyWalker-1 variants use 10 segments.

Terminator: 0x80xx (high bit set) + 2-byte entry point address (0xE600 = CPUCS).

Segment layout (all SkyWalker-1 variants):

Segment  Address   Length
-------  -------   ------
1        0x0000    1023    Contains reset vector, interrupt handlers
2        0x03FF    1023
3        0x07FE    1023
4        0x0BFD    1023
5        0x0FFC    1023
6        0x13FB    1023
7        0x17FA    1023
8        0x1BF9    1023
9        0x1FF8    1023
10       0x23F7    varies  (115--265 bytes depending on version)

16.2 Decoded C2 Headers

File VID PID Segments Code Size Entry
skywalker1_eeprom.bin (v2.06) 0x09C0 0x0203 10 9,472 bytes 0xE600
sw1_v213_fw_1_c2.bin (v2.13.1) 0x09C0 0x0203 10 9,322 bytes 0xE600
sw1_v213_fw_2_c2.bin (v2.13.2) 0x09C0 0x0203 10 9,377 bytes 0xE600
sw1_v213_fw_3_c2.bin (v2.13.3) 0x09C0 0x0203 10 9,369 bytes 0xE600
rev2_v210_fw_1_c2.bin (Rev.2) 0x09C0 0x0202 9 8,843 bytes 0xE600

16.3 DVB-USB Binary Hexline Format (Kernel FW01)

The format the kernel expects for dvb-usb-gp8psk-01.fw (only needed for Rev.1 Cold, PID 0x0200):

Record structure:
  Offset  Size  Field
  0       1     len       - Number of data bytes
  1       1     addr_lo   - Target address low byte
  2       1     addr_hi   - Target address high byte
  3       1     type      - 0x00=data, 0x01=EOF, 0x04=extended addr
  4       len   data[]    - Payload bytes
  4+len   1     chk       - Checksum byte

16.4 FW02 Chunk Format (BCM4500 Firmware)

Only needed for Rev.1 Warm (PID 0x0201):

Chunk format:
  Byte 0:     payload_length (N)
  Bytes 1-3:  header/address bytes
  Bytes 4..N+3: payload data
  Terminator: single byte 0xFF
  Maximum chunk size: 64 bytes (USB control transfer limit)

Command 0x88 (LOAD_BCM4500) initiates the transfer. Each chunk is sent via bulk endpoint 0x01. On the SkyWalker-1, 0x88 routes to STALL (BCM4500 firmware is in ROM).

16.5 Format Incompatibility

C2 (EEPROM) and hexline (kernel FW01) are structurally different containers. They cannot be used interchangeably, but the payload data is identical. A C2 file can be converted to hexline by stripping the 8-byte header, splitting segments into 16-byte records, and appending an EOF record.


17. Debugging Reference

17.1 I2C STOP Corruption Root Cause

The root cause of the initial firmware hang was traced through incremental debug modes:

wValue Action Result Diagnosis
0x82 GPIO + power + bmSTOP + probe Fails bmSTOP corrupts controller
0x85 GPIO + power + probe (no bmSTOP) Works Confirms bmSTOP is the cause
0x84 I2C probe only (chip already powered) Works BCM4500 is alive; I2C function is correct

Key finding: mode 0x84 succeeds immediately after 0x82 fails, proving the BCM4500 was alive the whole time. The FX2 I2C controller was in a bad state, not the bus or slave.

17.2 Boot Results After Fix

Metric Value
Boot time ~90 ms total
config_status 0x03 (STARTED + FW_LOADED)
boot_stage 0xFF (COMPLETE)
Direct registers 0xA2-0xA8 All return 0x02 (powered, not locked)
Signal lock 0x00 (no lock -- dish not aimed)
USB responsiveness No hang; fully responsive throughout

17.3 Test Tools

Located in tools/ directory:

Script Purpose
test_boot_debug.py Sends debug modes 0x80--0x83 sequentially
test_i2c_debug.py Powers on via 0x81, runs bus scans, tests probe timing
test_i2c_isolate.py Tests re-reset and insufficient delay as failure causes
test_i2c_pinpoint.py Definitive test: compares 0x84, 0x85, and 0x82
fw_load.py RAM firmware loader (halt CPU, write, restart)

17.4 FX2 Register Quick Reference

Address Name Notes
0xE600 CPUCS CPU control/status; write 0x01 to halt, 0x00 to run
0xE601 IFCONFIG Interface configuration (GPIF mode, clock)
0xE60B REVCTL Revision control (NOAUTOARM, SKIPCOMMIT)
0xE618 EP2FIFOCFG EP2 FIFO configuration (AUTOIN, 8-bit)
0xE678 I2CS I2C control/status
0xE679 I2DAT I2C data
0xE67A I2CTL I2C speed control
0xE6B8 SETUPDAT[0] bmRequestType
0xE6B9 SETUPDAT[1] bRequest
0xE6BA SETUPDAT[2] wValueL
0xE6BB SETUPDAT[3] wValueH
0xE6BC SETUPDAT[4] wIndexL
0xE6BD SETUPDAT[5] wIndexH
0xE6BE SETUPDAT[6] wLengthL
0xE6BF SETUPDAT[7] wLengthH
0xE68A EP0BCH EP0 byte count high
0xE68B EP0BCL EP0 byte count low (write triggers transfer)
0xE740 EP0BUF EP0 data buffer start
0xE0B6 (custom) LNB voltage control register (XRAM)

18. Sources

Firmware Analysis

  • Ghidra decompilation/disassembly of five firmware images:
    • v2.06.04 (Ghidra port 8193) -- extracted from SkyWalker-1 EEPROM
    • Rev.2 v2.10.04 (Ghidra port 8197) -- extracted from Rev.2 hardware
    • v2.13.01 FW1 (Ghidra port 8194) -- extracted from Windows updater
    • v2.13.02 FW2 (Ghidra port 8195) -- extracted from Windows updater
    • v2.13.03 FW3 (Ghidra port 8196) -- extracted from Windows updater
  • Firmware dumps: firmware-dump/

Driver Source

  • Linux kernel 6.16.5: drivers/media/usb/dvb-usb/gp8psk.c, gp8psk.h, gp8psk-fe.c, gp8psk-fe.h
  • Linux kernel: drivers/media/usb/dvb-usb/dvb-usb-firmware.c
  • Windows BDA driver: SkyWalker1_Final_Release/Source/SkyWalker1Control.cpp
  • Windows BDA driver: SkyWalker1_Final_Release/Include/SkyWalker1Control.h, SkyWalker1CommonDef.h

Hardware Documentation

Analysis Reports (This Project)

  1. gp8psk-driver-analysis.md -- Linux kernel driver analysis
  2. firmware-analysis-v206-vs-v213.md -- v2.06 vs v2.13 firmware comparison
  3. rev2-deep-analysis.md -- Rev.2 deep function inventory (107 functions)
  4. gpif-streaming-analysis.md -- GPIF/MPEG-2 streaming path
  5. tuning-protocol-analysis.md -- TUNE_8PSK protocol deep dive
  6. vendor-commands-unknown.md -- Vendor command decode (0x8F, 0x91--0x98)
  7. kernel-fw01-analysis.md -- Kernel firmware format and EEPROM boot
  8. firmware-dump/fw_v213_comparison_report.md -- v2.13 sub-variant comparison
  9. dvb-s2-investigation.md -- DVB-S2 incompatibility investigation
  10. docs/boot-debug-findings.md -- Boot/I2C debugging findings
  11. docs/diseqc/diseqc-skywalker-1.md -- DiSEqC Windows BDA interface
  12. firmware/skywalker1.c -- Custom firmware v3.01.0 source

Community References