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:
(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:
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:
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** |
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.
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
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.
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 |
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
**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.
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.
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:
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.
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 |
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):**
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.
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:
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 |
- **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