skywalker-1/gpif-streaming-analysis.md
Ryan Malloy e4117421a1 Add GPIF streaming path and kernel firmware format analyses
GPIF streaming analysis (772 lines):
- IFCONFIG=0xEE: GPIF master mode, 48MHz async, BCM4500 data bus
- Fully hardware-managed path: BCM4500 -> GPIF -> EP2 FIFO -> USB bulk
- EP2FIFOCFG=0x0C (AUTOIN, 8-bit), FLOWSTATEA |= 0x09 (auto re-trigger)
- Byte-for-byte identical register config across all firmware versions
- ARM_TRANSFER (cmd 0x85) start/stop flow documented with disassembly

Kernel FW01 analysis (425 lines):
- FW01/FW02 files never existed in linux-firmware or any distribution
- SkyWalker-1 boots from onboard EEPROM, never needs host firmware
- DVB-USB binary hexline format documented (compact Intel HEX variant)
- C2 EEPROM format vs kernel hexline format comparison
- Binary comparison matrix for all 5 firmware dumps
- Anti-tampering string found in v2.13 at offset 0x1880
2026-02-11 12:18:09 -07:00

773 lines
32 KiB
Markdown

# Genpix SkyWalker-1: GPIF MPEG-2 Streaming Path Analysis
## Overview
This document traces the MPEG-2 transport stream data path from the Broadcom BCM4500 satellite demodulator through the Cypress FX2's General Programmable Interface (GPIF) engine to USB bulk endpoint EP2 (0x82). Analysis covers all three firmware versions:
| Instance | Port | Firmware | Functions | Config Byte | GPIF Ctrl Fn |
|----------|------|----------|-----------|-------------|-------------|
| v2.06 | 8193 | Original | 61 | IRAM 0x6D | 0x1919 |
| v2.13 FW1 | 8194 | Revised | 82 | IRAM 0x4F | 0x1800 |
| Rev.2 v2.10.4 | 8197 | HW Rev.2 | 107 | IRAM 0x4E | 0x0D7C |
---
## Data Flow Architecture
```
Cypress FX2 (CY7C68013A)
+-----------------------------+
| |
BCM4500 P3.5 TS_EN | GPIF Engine EP2 FIFO | USB 2.0 HS
Demodulator <-----------------+ (Master Read) (AUTOIN) +------------> Host
(I2C:0x3F) GPIF Data Bus | 0xE4xx wfm 4x buf | EP2 (0x82)
-------------------> CTL/RDY pins 8-bit | Bulk IN
8-bit parallel TS | | 7 URBs x 8KB
+-----------------------------+
```
**The path is: BCM4500 -> GPIF master read -> EP2 FIFO (auto-commit) -> USB bulk IN.**
There is no software intermediary in the data path. The GPIF engine reads data directly into the EP2 FIFO buffer. The EP2FIFOCFG AUTOIN bit causes the hardware to automatically commit full packets to the USB controller for transfer to the host. The firmware only needs to start/stop the GPIF engine and handle completion interrupts.
---
## 1. IFCONFIG: Interface Configuration
**Register: IFCONFIG (0xE601) = 0xEE**
Written in FUN_CODE_1000 (Rev.2 at CODE:1000), called during hardware initialization:
```asm
; Rev.2 FUN_CODE_1000 at CODE:1000
CODE:1000: 90e601 MOV DPTR,#0xe601 ; IFCONFIG register
CODE:1003: 74ee MOV A,#0xee ; 0xEE
CODE:1005: f0 MOVX @DPTR,A
```
**Bit 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 (available to BCM4500) |
| 4 | IFCLKPOL | 0 | Non-inverted clock polarity |
| 3 | ASYNC | 1 | Asynchronous GPIF (no IFCLK-synchronized handshake) |
| 2 | GSTATE | 1 | GPIF state machine outputs visible on PORTE (debug) |
| 1:0 | IFCFG | 10 | **GPIF internal master mode** |
The FX2 operates as a GPIF master, reading data from the BCM4500's parallel transport stream output. Asynchronous mode means the GPIF uses RDY pin handshaking rather than clock-edge sampling. The 48 MHz IFCLK output provides a reference clock to the BCM4500, though actual data capture is handshake-controlled.
This value is identical across all three firmware versions.
### Prior IFCONFIG Value
During early init (FUN_CODE_10d9), IFCONFIG is temporarily set to 0xCA before FUN_CODE_1000 overwrites it:
```asm
; Early init - temporary IFCONFIG
CODE:10f0: 90e601 MOV DPTR,#0xe601
CODE:10f3: 74ca MOV A,#0xca ; 0xCA = internal 48MHz, IFCLKOE, no GSTATE, GPIF
CODE:10f5: f0 MOVX @DPTR,A
```
0xCA (1100_1010): Same as 0xEE but GSTATE=0 and ASYNC=0. The final value (0xEE) enables async mode and debug state output.
---
## 2. FIFO Reset Sequence
All endpoint FIFOs are reset during initialization using the Cypress-prescribed procedure.
**Register: FIFORESET (0xE604)**
```asm
; Rev.2 FUN_CODE_10d9 at CODE:1130-1152
CODE:1130: 90e604 MOV DPTR,#0xe604 ; FIFORESET
CODE:1133: 7480 MOV A,#0x80 ; Bit 7 = NAKALL: NAK all host transfers
CODE:1135: f0 MOVX @DPTR,A
CODE:1136: 00 00 00 ; mandatory 3-NOP sync delay
CODE:1139: 7402 MOV A,#0x02 ; Reset EP2 FIFO
CODE:113b: f0 MOVX @DPTR,A
CODE:113c: 00 00 00
CODE:113f: 7404 MOV A,#0x04 ; Reset EP4 FIFO
CODE:1141: f0 MOVX @DPTR,A
CODE:1142: 00 00 00
CODE:1145: 7406 MOV A,#0x06 ; Reset EP6 FIFO
CODE:1147: f0 MOVX @DPTR,A
CODE:1148: 00 00 00
CODE:114b: 7408 MOV A,#0x08 ; Reset EP8 FIFO
CODE:114d: f0 MOVX @DPTR,A
CODE:114e: 00 00 00
CODE:1151: e4 CLR A ; NAKALL = 0 (resume)
CODE:1152: f0 MOVX @DPTR,A
```
The sequence: NAKALL on -> reset EP2 -> EP4 -> EP6 -> EP8 -> NAKALL off. Triple-NOP delays between writes are required by the FX2 architecture: XRAM register writes take 2 cycles to propagate, and back-to-back writes to the same register need at least 3 instruction cycles between them.
---
## 3. Endpoint FIFO Configuration
### EP2FIFOCFG (0xE618) = 0x0C
```asm
; Rev.2 FUN_CODE_10d9 at CODE:1156
CODE:1156: 90e618 MOV DPTR,#0xe618 ; EP2FIFOCFG
CODE:1159: 740c MOV A,#0x0c ; 0x0C
CODE:115b: f0 MOVX @DPTR,A
```
**Bit decode (0x0C = 0000_1100):**
| Bit | Name | Value | Meaning |
|-----|------|-------|---------|
| 4 | INFM1 | 0 | IN endpoint: packet count not decremented |
| 3 | AUTOIN | 1 | **Auto-commit IN packets when FIFO buffer full** |
| 2 | ZEROLENIN | 1 | Allow zero-length IN packets |
| 1 | (reserved) | 0 | -- |
| 0 | WORDWIDE | 0 | **8-bit data path** (not 16-bit) |
The AUTOIN bit is critical: when the GPIF engine fills an EP2 FIFO buffer to the configured packet size, the FX2 hardware automatically arms the buffer for USB transfer. No firmware intervention is needed in the data path. The 8-bit WORDWIDE setting matches the BCM4500's 8-bit parallel transport stream output.
### Other Endpoint FIFOs (All Disabled)
```asm
CODE:115f: e4 CLR A ; A = 0
CODE:1160: 90e619 MOV DPTR,#0xe619 ; EP4FIFOCFG = 0x00
CODE:1163: f0 MOVX @DPTR,A
CODE:1167: 90e61a MOV DPTR,#0xe61a ; EP6FIFOCFG = 0x00
CODE:116a: f0 MOVX @DPTR,A
CODE:116e: 90e61b MOV DPTR,#0xe61b ; EP8FIFOCFG = 0x00
CODE:1171: f0 MOVX @DPTR,A
```
Only EP2 is configured for streaming. EP4/EP6/EP8 FIFOs are left in default (manual/disabled) state.
---
## 4. GPIF Waveform and Control Configuration
### Init Table-Driven Configuration
The GPIF waveform descriptors (0xE400-0xE47F), GPIFIDLECTL (0xE660), GPIFCTLCFG (0xE661), GPIFWFSELECT (0xE662), and related registers are programmed via a compressed init table processed during the main() entry point, before the main loop begins.
| Version | Init Table Address | Parser Function |
|---------|-------------------|-----------------|
| v2.06 | CODE:0B46 | main (0x188D) |
| v2.13 FW1 | CODE:0B88 | main_entry (0x170D) |
| Rev.2 | CODE:0B48 | main_init (0x15A6) |
The init table uses a custom encoding:
- **Control byte** bits [7:6] select the write mode (IRAM, XRAM 2-byte addr, bit manipulation, XRAM 1-byte addr)
- **Control byte** bits [5:0] encode the byte count (extended to 2 bytes if bit 5 is set)
- Data bytes follow, written sequentially to the target address range
The table writes configuration data to the 0xE0xx scratch RAM area (device descriptors, I2C addresses, modulation parameters) and to the 0xE4xx/0xE6xx GPIF/USB register space. The GPIF waveform descriptors occupy 128 bytes at 0xE400-0xE47F and define the state machine transitions for reading data from the BCM4500.
### GPIF Waveform Structure
The GPIF waveform data embedded in the init table contains 4 waveform slots (32 bytes each):
The waveform data visible in the init table (from 0x0BAD region in Rev.2) includes the pattern:
```
01 01 01 01 01 01 07 00 01 00 00 00 00 00 00
F0 F0 F0 F0 F0 F0 F0 F0 00 3F 00 00 00 00 3F
```
This is characteristic of a simple single-state GPIF read waveform:
- **States 0-5**: CTL outputs = 0x01 (single control line asserted), length = 1 IFCLK
- **State 6**: CTL = 0x07, length = 0 (idle/terminate state)
- **Opcode/branch**: 0x00 (SDP = sample data point, branch idle)
- **Output/logic**: 0xF0 pattern (FIFO write flags)
The waveform programs a straightforward "assert read strobe, capture data, de-assert" cycle that reads one byte per GPIF transaction from the BCM4500's parallel port into the EP2 FIFO.
### REVCTL (0xE60B) = 0x03
```asm
CODE:1127: 90e60b MOV DPTR,#0xe60b ; REVCTL
CODE:112a: 7403 MOV A,#0x3
CODE:112c: f0 MOVX @DPTR,A
```
REVCTL = 0x03: Both NOAUTOARM and SKIPCOMMIT bits set. This disables the automatic arming of endpoint buffers on access and skips the auto-commit behavior. Combined with EP2FIFOCFG.AUTOIN=1, this means the GPIF engine explicitly controls when data is committed -- the AUTOIN triggers on FIFO fullness, not on CPU access.
### GPIFSGLDATH (SFR 0xBB) - GPIF Trigger Register
The SFR at address 0xBB is used as the GPIF trigger/status register:
- **Read**: Bit 7 (DONE) = 1 when GPIF is idle
- **Write**: Bits [1:0] = endpoint select (00=EP2), Bit 2 = read direction
```asm
; Poll for GPIF completion
CODE:0dca: e5bb MOV A,0xbb ; Read GPIFTRIG
CODE:0dcc: 30e7fb JNB 0xe7,0x0dca ; Loop until bit 7 (DONE) = 1
; Trigger next GPIF read into EP2
CODE:0dd2: 75bb04 MOV 0xbb,#0x4 ; GPIFTRIG = 0x04 (read, EP2)
```
---
## 5. ARM_TRANSFER (Vendor Command 0x85)
### Dispatch
The host issues USB vendor command 0x85 to start or stop the MPEG-2 transport stream. The command dispatches via the vendor command jump table at CODE:0x0076:
| Version | Jump Table Entry | Handler Address | GPIF Control Fn |
|---------|-----------------|-----------------|-----------------|
| v2.06 | `21 10` (AJMP) | CODE:0110 | LCALL 0x1919 |
| v2.13 FW1 | `21 10` (AJMP) | CODE:0110 | LCALL 0x1800 |
| Rev.2 | `01 fa` (AJMP) | CODE:00FA | LCALL 0x0D7C |
The handler logic is identical across versions despite the address differences:
```asm
; Rev.2 ARM_TRANSFER handler at CODE:00FA (v2.06/v2.13 at CODE:0110)
CODE:00fa: e54e MOV A,0x4e ; Read config byte
CODE:00fc: 30e00d JNB 0xe0,0x010c ; Test bit 0: demodulator active?
CODE:00ff: 90e6ba MOV DPTR,#0xe6ba ; Read USB SETUP wValue (low byte)
CODE:0102: e0 MOVX A,@DPTR
CODE:0103: 24ff ADD A,#0xff ; Set CY if wValue != 0
CODE:0105: 9206 MOV 0x06,CY ; arm_flag = (wValue != 0)
CODE:0107: 120d7c LCALL 0x0d7c ; Call GPIF control function
CODE:010a: 8005 SJMP 0x0111 ; Skip to done
CODE:010c: c206 CLR 0x06 ; arm_flag = 0 (force stop if no demod)
CODE:010e: 120d7c LCALL 0x0d7c ; Call GPIF control function
CODE:0111: e4 CLR A
CODE:0112: 90e68b MOV DPTR,#0xe68b ; EP0BCL = 0 (acknowledge control transfer)
CODE:0115: f0 MOVX @DPTR,A
```
The handler checks bit 0 of the configuration byte (demodulator present/active). If active, it reads wValue from the USB SETUP packet: wValue=1 means start streaming, wValue=0 means stop. If the demodulator is not active, it forces a stop.
### GPIF Control Function - START Path
When arm_flag=1 (start streaming), the GPIF control function at 0x0D7C (Rev.2) / 0x1800 (v2.13) / 0x1919 (v2.06) executes:
```c
// Decompiled from Rev.2 FUN_CODE_0d7c -- START STREAMING
if (arm_flag != 0 && config_byte.7 == 0) {
// Not already streaming -- initialize GPIF transfer
config_byte |= 0x80; // Mark streaming active
// 1. Set GPIF transaction count (0xE630:0xE631)
GPIFTCB3 = 0x80; // 0xE630 = 0x80
GPIFTCB2 = 0x00; // 0xE631 = 0x00
// Transaction count = 0x8000_0000 (2 GB) -- effectively infinite
// 2. Clear GPIF address registers
*(0xE6D2) = 0x00; // Address high = 0
*(0xE6D3) = 0x01; // Address low = 1 (initial)
EP2FIFOBCL = 0x00; // 0xE6F1: Reset byte count
*(0xE6D3) = 0x00; // Address low = 0
// 3. Initial GPIF trigger via XRAM registers
*(0xE6D0) = 0x02; // Trigger/config = EP2 read
*(0xE6D1) = 0x00; // Trigger latch
// 4. Assert BCM4500 transport stream enable
P3 &= ~0x20; // P3.5 LOW = TS_EN asserted
// 5. Wait for GPIF completion
while (!(GPIFTRIG & 0x80)) {} // Poll SFR 0xBB bit 7 (DONE)
// 6. Re-enable P3.5
P3 |= 0x20; // P3.5 HIGH
// 7. Trigger continuous GPIF read into EP2
GPIFTRIG = 0x04; // SFR 0xBB = read EP2
// 8. Signal data path active
P0 &= ~0x80; // P0.7 LOW = streaming active indicator
}
```
**Disassembly (Rev.2 at CODE:0D7C):**
```asm
; START path (arm_flag = 1, config.7 = 0)
CODE:0d84: 434e80 ORL 0x4e,#0x80 ; config_byte |= 0x80 (streaming flag)
CODE:0d87: 90e630 MOV DPTR,#0xe630 ; GPIFTCB3
CODE:0d8a: 7480 MOV A,#0x80
CODE:0d8c: f0 MOVX @DPTR,A ; GPIFTCB3 = 0x80
CODE:0d90: e4 CLR A
CODE:0d91: 90e631 MOV DPTR,#0xe631 ; GPIFTCB2
CODE:0d94: f0 MOVX @DPTR,A ; GPIFTCB2 = 0x00
CODE:0d98: 90e6d2 MOV DPTR,#0xe6d2
CODE:0d9b: f0 MOVX @DPTR,A ; [0xE6D2] = 0x00
CODE:0d9f: 90e6d3 MOV DPTR,#0xe6d3
CODE:0da2: 04 INC A ; A = 1
CODE:0da3: f0 MOVX @DPTR,A ; [0xE6D3] = 0x01
CODE:0da7: e4 CLR A
CODE:0da8: 90e6f1 MOV DPTR,#0xe6f1 ; EP2FIFOBCL (byte count low)
CODE:0dab: f0 MOVX @DPTR,A ; EP2FIFOBCL = 0x00
CODE:0daf: 90e6d3 MOV DPTR,#0xe6d3
CODE:0db2: f0 MOVX @DPTR,A ; [0xE6D3] = 0x00
CODE:0db6: 90e6d0 MOV DPTR,#0xe6d0
CODE:0db9: 7402 MOV A,#0x2
CODE:0dbb: f0 MOVX @DPTR,A ; [0xE6D0] = 0x02
CODE:0dbf: e4 CLR A
CODE:0dc0: 90e6d1 MOV DPTR,#0xe6d1
CODE:0dc3: f0 MOVX @DPTR,A ; [0xE6D1] = 0x00
CODE:0dc7: 53b0df ANL 0xb0,#0xdf ; P3 &= 0xDF -> P3.5 = 0 (TS_EN assert)
CODE:0dca: e5bb MOV A,0xbb ; Read GPIFTRIG status
CODE:0dcc: 30e7fb JNB 0xe7,0x0dca ; Wait for DONE bit
CODE:0dcf: 43b020 ORL 0xb0,#0x20 ; P3 |= 0x20 -> P3.5 = 1
CODE:0dd2: 75bb04 MOV 0xbb,#0x4 ; GPIFTRIG = 0x04 (read EP2)
CODE:0dd8: 53807f ANL 0x80,#0x7f ; P0 &= 0x7F -> P0.7 = 0
CODE:0ddb: 22 RET
```
### GPIF Control Function - STOP Path
When arm_flag=0 (stop streaming) and config_byte.7=1 (currently streaming):
```c
// Decompiled from Rev.2 FUN_CODE_0d7c -- STOP STREAMING
if (arm_flag == 0 && config_byte.7 == 1) {
// Currently streaming -- abort GPIF and flush
P0 |= 0x80; // P0.7 HIGH = streaming stopped
EP2FIFOBCH = 0xFF; // 0xE6F5: Force byte count high = 0xFF
// (skip/flush current FIFO packet)
while (!(GPIFTRIG & 0x80)) {} // Wait for GPIF to go idle
OUTPKTEND = 0x82; // 0xE648 = 0x82: Force-commit EP2 packet
// Bit 7 = skip, bits[3:0] = EP2
config_byte &= ~0x80; // Clear streaming flag
P3 |= 0xE0; // P3.7:5 = 1 (de-assert all control lines)
}
```
**Disassembly (Rev.2 at CODE:0DDC):**
```asm
; STOP path (arm_flag = 0, config.7 = 1)
CODE:0de1: 438080 ORL 0x80,#0x80 ; P0 |= 0x80 -> P0.7 = 1 (stopped)
CODE:0de4: 90e6f5 MOV DPTR,#0xe6f5 ; EP2FIFOBCH (byte count high)
CODE:0de7: 74ff MOV A,#0xff
CODE:0de9: f0 MOVX @DPTR,A ; = 0xFF (force flush)
CODE:0dea: e5bb MOV A,0xbb ; Read GPIFTRIG
CODE:0dec: 30e7fb JNB 0xe7,0x0dea ; Wait for DONE
CODE:0def: 90e648 MOV DPTR,#0xe648 ; OUTPKTEND
CODE:0df2: 7482 MOV A,#0x82 ; Skip=1, EP2
CODE:0df4: f0 MOVX @DPTR,A ; Force-commit/skip EP2 packet
CODE:0df5: 534e7f ANL 0x4e,#0x7f ; config_byte &= 0x7F (clear streaming)
CODE:0df8: 43b0e0 ORL 0xb0,#0xe0 ; P3 |= 0xE0 (de-assert controls)
CODE:0dfb: 22 RET
```
**OUTPKTEND (0xE648) = 0x82** is notable: bit 7 set means "skip" (discard partially filled packet), bits [3:0] = 2 = EP2. This ensures any partial FIFO buffer is flushed when stopping the stream.
---
## 6. INT4/INT6 Interrupt Handlers (GPIF/FIFO Events)
### Rev.2 v2.10.4: Shared Handler via Trampoline
Both INT4 and INT6 vectors jump to the same handler:
```asm
CODE:0043: 021200 LJMP 0x1200 ; INT4_FX2_vector -> trampoline
CODE:0053: 021200 LJMP 0x1200 ; INT6_FX2_vector -> trampoline
; Trampoline at 0x1200
CODE:1200: 022084 LJMP 0x2084 ; -> GPIF_INT4_INT6_handler
```
**GPIF_INT4_INT6_handler (CODE:2084):**
```asm
CODE:2084: c0e0 PUSH A ; Save accumulator
CODE:2086: c083 PUSH DPH ; Save DPTR high
CODE:2088: c082 PUSH DPL ; Save DPTR low
CODE:208a: d201 SETB 0x01 ; Set bit flag _0_1 (GPIF event pending)
CODE:208c: 5391ef ANL 0x91,#0xef ; Clear EXIF.4 (INT4/INT6 IRQ flag)
CODE:208f: 90e65d MOV DPTR,#0xe65d ; GPIFIRQ (GPIF Interrupt Request)
CODE:2092: 7401 MOV A,#0x1
CODE:2094: f0 MOVX @DPTR,A ; Clear GPIFIRQ bit 0
CODE:2095: d082 POP DPL ; Restore context
CODE:2097: d083 POP DPH
CODE:2099: d0e0 POP A
CODE:209b: 32 RETI ; Return from interrupt
```
The handler is minimal: it sets a software flag (_0_1) and clears the hardware interrupt. The actual GPIF processing happens in the main loop when this flag is polled.
### v2.13 FW1: Same Pattern
```asm
CODE:0043: 021500 LJMP 0x1500 ; INT4 -> thunk_FUN_CODE_2252
CODE:1500: 022252 LJMP 0x2252 ; -> FUN_CODE_2252
```
**FUN_CODE_2252 (CODE:2252):** identical logic, sets _0_6 flag, clears EXIF.4 and GPIFIRQ.0.
### v2.06: Different Vector Dispatch
In v2.06, INT4 and INT6 both jump to 0x1600 which is a jump table indexed by the interrupt source register. The GPIF-related entry jumps to the same pattern (set flag, clear IRQ, return).
### INT2 (USB/GPIF Combined Vector)
The INT2 vector at CODE:0033 handles USB/GPIF combined interrupts:
```asm
; Rev.2
CODE:0033: 02223f LJMP 0x223f ; USB_GPIF_handler
; USB_GPIF_handler at CODE:223F
CODE:223f: c0e0 PUSH A
CODE:2241: 53d8ef ANL 0xd8,#0xef ; Clear CCON.4 (PCA counter flag)
CODE:2244: d0e0 POP A
CODE:2246: 32 RETI
```
This handler simply clears the PCA (Programmable Counter Array) interrupt flag. The actual USB processing is handled by polling in the main loop.
---
## 7. FLOWSTATE Configuration
During initialization (FUN_CODE_09a9 at CODE:0AEF), the GPIF flow state machine is configured:
```asm
CODE:0aef: 90e668 MOV DPTR,#0xe668 ; FLOWSTATEA
CODE:0af2: e0 MOVX A,@DPTR
CODE:0af3: 4409 ORL A,#0x09 ; Set bits 0 and 3
CODE:0af5: f0 MOVX @DPTR,A
```
**FLOWSTATEA (0xE668) |= 0x09:**
| Bit | Value | Meaning |
|-----|-------|---------|
| 0 | 1 | FSEN: Flow State enable -- GPIF uses flow state logic |
| 3 | 1 | FS[3]: Flow state flag -- specific to waveform design |
The flow state machine controls automated GPIF-to-FIFO data transfers. With FSEN=1, the GPIF engine can automatically re-trigger transactions when FIFO space is available, creating a continuous streaming pipeline without firmware intervention after initial setup.
### GPIF Interrupt Enable
```asm
CODE:0af6: 90e65c MOV DPTR,#0xe65c ; GPIFIE
CODE:0af9: e0 MOVX A,@DPTR
CODE:0afa: 443d ORL A,#0x3d ; Enable bits 0,2,3,4,5
CODE:0afc: f0 MOVX @DPTR,A
```
**GPIFIE (0xE65C) |= 0x3D = 0011_1101:**
| Bit | Name | Enabled | Purpose |
|-----|------|---------|---------|
| 0 | GPIFWF | Yes | Waveform completion interrupt |
| 1 | (reserved) | No | -- |
| 2 | GPIFTCEXP | Yes | Transaction count expired |
| 3 | GPIFGPIFDONE | Yes | GPIF operation done |
| 4 | GPIFFF | Yes | FIFO flag interrupt |
| 5 | GPIFWF2 | Yes | Waveform 2 completion |
These interrupts drive the INT4/INT6 handlers described in section 6. The combination of TC expire, DONE, and FIFO flag interrupts allows the main loop to manage the streaming pipeline: restart GPIF when buffers become available, handle end-of-transfer, and detect error conditions.
---
## 8. GPIF Pin Assignments
### Control Outputs (CTL[5:0])
Based on the init table and the P3 register manipulation in the GPIF control function:
| Pin | Direction | Function | Start State | Active State |
|-----|-----------|----------|-------------|--------------|
| P3.5 | Output | TS_EN (Transport Stream Enable) | HIGH (inactive) | LOW (active) |
| P3.6 | Output | BCM4500 control line | HIGH | LOW |
| P3.7 | Output | BCM4500 control line | HIGH | HIGH |
| P0.7 | Output | Host streaming indicator | HIGH (idle) | LOW (streaming) |
### Data Bus
The GPIF uses the FD[7:0] data bus (Port B/D depending on FX2 variant) in 8-bit mode (WORDWIDE=0).
### Ready Signals (RDY[5:0])
The GPIF waveform references RDY pins for handshaking with the BCM4500. The specific RDY pin assignment is encoded in the waveform descriptors loaded from the init table.
---
## 9. Main Loop Integration
After initialization, the main loop (FUN_CODE_09a9 at CODE:0B0E) polls for GPIF events:
```c
// Simplified main loop (Rev.2)
while (true) {
FUN_CODE_201e(); // Poll I2C / USB status
if (gpif_event_flag) { // _0_1 set by INT4/INT6 handler
FUN_CODE_0319(); // Process vendor commands
gpif_event_flag = 0;
}
if (gpif_done_flag) { // _0_3 set when GPIF transfer completes
FUN_CODE_2265(); // (stub -- no-op in Rev.2)
if (carry_set) {
gpif_done_flag = 0;
// Re-arm GPIF or handle completion
while (true) {
FUN_CODE_1faa(); // Enter idle (PCON.0 = 1)
if (remote_wakeup) break;
// Check EP2CS for buffer availability
ep2cs = *(0xE682); // EP2CS register
if ((ep2cs & 0x80) && // EP2 busy
(ep2cs & 0x02)) // EP2 stall
continue; // Keep waiting
if ((ep2cs & 0x40) && // EP2 full
(ep2cs & 0x01)) // EP2 empty
continue; // Keep waiting
break; // Buffer available
}
FUN_CODE_1eda(); // Handle USB re-enumerate if needed
FUN_CODE_2267(); // (stub -- no-op in Rev.2)
}
}
}
```
**Disassembly of the main loop polling (Rev.2 at CODE:0B0E):**
```asm
CODE:0b0e: 12201e LCALL 0x201e ; Poll I2C/USB
CODE:0b11: 300105 JNB 0x01,0x0b19 ; Check _0_1 (GPIF event flag)
CODE:0b14: 120319 LCALL 0x0319 ; Process vendor commands
CODE:0b17: c201 CLR 0x01 ; Clear flag
CODE:0b19: 3003f2 JNB 0x03,0x0b0e ; Check _0_3 (GPIF done flag)
CODE:0b1c: 122265 LCALL 0x2265 ; (no-op)
CODE:0b1f: 50ed JNC 0x0b0e ; If no carry, loop back
CODE:0b21: c203 CLR 0x03 ; Clear GPIF done flag
CODE:0b23: 121faa LCALL 0x1faa ; Enter idle (PCON.0 = sleep)
CODE:0b26: 200016 JB 0x00,0x0b3f ; Remote wakeup -> skip
; Poll EP2CS for buffer availability
CODE:0b29: 90e682 MOV DPTR,#0xe682 ; EP2CS register
CODE:0b2c: e0 MOVX A,@DPTR
CODE:0b2d: 30e704 JNB 0xe7,0x0b34 ; bit 7 = busy?
CODE:0b30: e0 MOVX A,@DPTR
CODE:0b31: 20e1ef JB 0xe1,0x0b23 ; bit 1 = stall? -> keep waiting
CODE:0b34: 90e682 MOV DPTR,#0xe682
CODE:0b37: e0 MOVX A,@DPTR
CODE:0b38: 30e604 JNB 0xe6,0x0b3f ; bit 6 = full?
CODE:0b3b: e0 MOVX A,@DPTR
CODE:0b3c: 20e0e4 JB 0xe0,0x0b23 ; bit 0 = empty? -> keep waiting
CODE:0b3f: 121eda LCALL 0x1eda ; USB re-enumerate check
CODE:0b42: 122267 LCALL 0x2267 ; (no-op)
CODE:0b45: 80c7 SJMP 0x0b0e ; Loop forever
```
The main loop uses CPU idle mode (PCON.0) between GPIF events, waking on interrupts. The EP2CS polling checks that EP2 buffers are available before re-arming the GPIF, preventing FIFO overrun.
---
## 10. CPUCS and Clock Configuration
**Register: CPUCS (0xE600)**
Set during init (FUN_CODE_10d9):
```asm
CODE:10e4: 90e600 MOV DPTR,#0xe600 ; CPUCS
CODE:10e7: e0 MOVX A,@DPTR ; Read current value
CODE:10e8: 54e5 ANL A,#0xe5 ; Clear bits 4,3,1 (CLKSPD, CLKINV)
CODE:10ea: 4410 ORL A,#0x10 ; Set bit 4
CODE:10ec: f0 MOVX @DPTR,A ; Write back
```
CPUCS bits:
- Bits [4:3] CLKSPD = 10 = **48 MHz** CPU clock
- Other bits preserved from power-on defaults
---
## 11. GPIO Pin Summary for Streaming
| Pin | SFR | Direction | Streaming Active | Streaming Stopped | Function |
|-----|-----|-----------|-----------------|-------------------|----------|
| P0.2 | 0x80 | Out | Set during init | -- | BCM4500 config |
| P0.7 | 0x80 | Out | LOW | HIGH | Streaming status indicator |
| P3.5 | 0xB0 | Out | Pulsed LOW | HIGH | BCM4500 TS_EN (Transport Stream Enable) |
| P3.6 | 0xB0 | Out | Controlled | HIGH | BCM4500 control |
| P3.7 | 0xB0 | Out | Controlled | HIGH | BCM4500 control |
During init (FUN_CODE_10d9):
```asm
CODE:111b: 758084 MOV 0x80,#0x84 ; P0 = 0x84 (bits 7,2 set)
CODE:1121: 75b0e1 MOV 0xb0,#0xe1 ; P3 = 0xE1 (bits 7,6,5,0 set)
```
P0 init = 0x84: P0.7=1 (streaming off), P0.2=1 (BCM4500 control)
P3 init = 0xE1: P3.7:5=1 (all control lines inactive), P3.0=1
---
## 12. Complete Streaming Sequence
### Start (Host sends ARM_TRANSFER wValue=1):
```
1. Host -> USB vendor cmd 0x85, wValue=1
2. FX2 checks demod active (config_byte bit 0)
3. FX2 sets arm_flag = 1
4. gpif_ctrl():
a. Set config_byte.7 = 1 (streaming active)
b. Load GPIF transaction count: GPIFTCB3:2 = 0x8000 (huge count)
c. Reset GPIF address and EP2 FIFO byte count
d. Initial GPIF setup via XRAM 0xE6D0-0xE6D3
e. Assert P3.5 LOW -> BCM4500 transport stream output enabled
f. Wait for initial GPIF transaction to complete (poll GPIFTRIG.7)
g. De-assert P3.5 HIGH
h. Trigger continuous GPIF read: GPIFTRIG = 0x04 (read into EP2)
i. Set P0.7 LOW (streaming indicator)
5. GPIF engine now auto-reads BCM4500 data into EP2 FIFO
6. EP2FIFOCFG.AUTOIN commits full packets to USB automatically
7. Host reads EP2 (0x82) bulk IN pipe continuously
```
### Steady State:
```
BCM4500 data bus -> GPIF state machine read -> EP2 FIFO buffer
| |
| (8-bit async handshake via RDY/CTL) | AUTOIN = 1
| |
v v
Continuous GPIF transactions Auto-commit when full
(FLOWSTATE re-triggers) (hardware, no CPU)
|
v
USB bulk IN transfer
(host polls EP2 0x82)
```
The FLOWSTATE engine (FLOWSTATEA bit 0 = FSEN) automatically re-triggers GPIF transactions when EP2 FIFO buffers become available after USB transfers complete. This creates a fully hardware-managed pipeline where the CPU only needs to handle exceptional conditions.
### Stop (Host sends ARM_TRANSFER wValue=0):
```
1. Host -> USB vendor cmd 0x85, wValue=0
2. gpif_ctrl():
a. Set P0.7 HIGH (streaming stopped)
b. Write EP2FIFOBCH = 0xFF (force-flush current buffer)
c. Wait for GPIF idle (poll GPIFTRIG.7)
d. Write OUTPKTEND = 0x82 (skip/discard partial EP2 packet)
e. Clear config_byte.7 (streaming inactive)
f. Set P3.7:5 = 1 (de-assert all BCM4500 control lines)
```
---
## 13. Cross-Version Comparison
The GPIF streaming path is functionally identical across all three firmware versions. The only differences are:
| Aspect | v2.06 (port 8193) | v2.13 FW1 (port 8194) | Rev.2 v2.10.4 (port 8197) |
|--------|-------|-----------|---------------|
| ARM_TRANSFER handler | CODE:0110 | CODE:0110 | CODE:00FA |
| GPIF control function | CODE:1919 | CODE:1800 | CODE:0D7C |
| Config byte location | IRAM 0x6D | IRAM 0x4F | IRAM 0x4E |
| Arm bit flag | bit 0x09 | bit 0x02 | bit 0x06 |
| INT4/INT6 vector | 0x1600 (dispatch table) | 0x1500 (thunk to 0x2252) | 0x1200 (thunk to 0x2084) |
| GPIF ISR action | Set flag, clear EXIF/GPIFIRQ | Set flag, clear EXIF/GPIFIRQ | Set flag, clear EXIF/GPIFIRQ |
| EP2FIFOCFG value | 0x0C (AUTOIN, ZEROLENIN, 8-bit) | 0x0C | 0x0C |
| IFCONFIG value | 0xEE (GPIF master, async, 48MHz) | 0xEE | 0xEE |
| FLOWSTATEA setting | \|= 0x09 (FSEN + FS3) | \|= 0x09 | \|= 0x09 |
| GPIFIE setting | \|= 0x3D | \|= 0x3D | \|= 0x3D |
The register sequences within the GPIF control function are byte-for-byte identical (the same XRAM addresses, same values, same NOP delays). Differences are limited to which IRAM locations store state variables and which bit-addressable flags are used.
---
## 14. Register Reference
### XRAM Registers Written During Streaming
| Address | Name | Start Value | Stop Value | Notes |
|---------|------|-------------|------------|-------|
| 0xE600 | CPUCS | (val & 0xE5) \| 0x10 | -- | 48 MHz CPU clock |
| 0xE601 | IFCONFIG | 0xEE | -- | GPIF master, async, 48 MHz |
| 0xE604 | FIFORESET | 0x80/0x02/0x04/0x06/0x08/0x00 | -- | Init only |
| 0xE60B | REVCTL | 0x03 | -- | NOAUTOARM + SKIPCOMMIT |
| 0xE618 | EP2FIFOCFG | 0x0C | -- | AUTOIN, ZEROLENIN, 8-bit |
| 0xE630 | GPIFTCB3 | 0x80 | -- | TC = 0x8000xxxx |
| 0xE631 | GPIFTCB2 | 0x00 | -- | TC continued |
| 0xE648 | OUTPKTEND | -- | 0x82 | Skip EP2 (stop only) |
| 0xE65C | GPIFIE | \|= 0x3D | -- | WF, TC, DONE, FF, WF2 |
| 0xE65D | GPIFIRQ | 0x01 | -- | Clear in ISR |
| 0xE668 | FLOWSTATEA | \|= 0x09 | -- | FSEN + FS[3] |
| 0xE670 | GPIFHOLDAMOUNT | 0x00 | -- | No hold time |
| 0xE6D0 | (GPIF trigger/config) | 0x02 | -- | EP2 read setup |
| 0xE6D1 | (GPIF trigger latch) | 0x00 | -- | Trigger latch |
| 0xE6D2 | (GPIF addr high) | 0x00 | -- | Address = 0 |
| 0xE6D3 | (GPIF addr low) | 0x01 then 0x00 | -- | Address setup |
| 0xE6F1 | EP2FIFOBCL | 0x00 | -- | Reset byte count |
| 0xE6F5 | EP2FIFOBCH | -- | 0xFF | Force flush (stop only) |
### SFR Registers
| Address | Name | Start Value | Stop Value | Notes |
|---------|------|-------------|------------|-------|
| 0x80 | P0 | 0x84 | \|= 0x80 | Bit 7 = streaming indicator |
| 0x91 | EXIF | &= 0xEF | -- | Clear bit 4 (INT4/6 flag) |
| 0xB0 | P3 | 0xE1 | \|= 0xE0 | Bits 7:5 = BCM4500 control |
| 0xBB | GPIFTRIG | 0x04 (read EP2) | (poll) | Trigger / status |
---
## 15. Timing Considerations
- **GPIF clock**: 48 MHz internal (IFCONFIG IFCLKSRC=1, 3048MHZ=1)
- **CPU clock**: 48 MHz (CPUCS CLKSPD=10)
- **GPIF async mode**: Data capture not synchronized to IFCLK edges; uses RDY pin handshaking
- **NOP delays**: 3 NOPs between consecutive XRAM writes to same register (~62.5 ns at 48 MHz)
- **EP2 buffer commit**: Automatic via AUTOIN when FIFO reaches packet size
- **GPIF re-trigger**: Automatic via FLOWSTATE when EP2 buffer space available
- **Transport stream rate**: BCM4500 outputs at the satellite symbol rate (up to 30 Msps), but the effective byte rate depends on modulation and coding. USB 2.0 High Speed bulk bandwidth (480 Mbps theoretical, ~35 MB/s practical) is more than sufficient for DVB-S transport streams (typically 1-5 MB/s).
---
## Sources
- Ghidra decompilation/disassembly of firmware images on ports 8193, 8194, 8197
- Cypress CY7C68013A (FX2LP) Technical Reference Manual, register map
- Linux kernel `gp8psk` driver (`drivers/media/usb/dvb-usb/gp8psk.c`)
- Prior analysis: `gp8psk-driver-analysis.md`, `firmware-analysis-v206-vs-v213.md`