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

32 KiB

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:

; 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:

; 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)

; 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

; 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)

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

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
; 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:

; 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:

// 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):

; 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):

// 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):

; 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:

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):

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

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:

; 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:

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

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:

// 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):

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):

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):

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