Apply .gitattributes normalization to convert all CRLF line endings inherited from Windows-origin source files to Unix LF. 175 files, zero content changes.
572 lines
28 KiB
Markdown
572 lines
28 KiB
Markdown
# Genpix SkyWalker-1 FX2 Firmware Comparative Analysis: v2.06 vs v2.13 FW1
|
|
|
|
## Executive Summary
|
|
|
|
v2.13 is a significant evolution of the v2.06 firmware with 21 additional functions (82 vs 61). The key changes are:
|
|
|
|
1. **Three new vendor commands** (0x99, 0x9A, 0x9C) for LNB/tuner control
|
|
2. **Restructured INT0 handler** with active satellite front-end polling
|
|
3. **I2C-based initialization** with retry logic for the satellite demodulator
|
|
4. **Version-aware code paths** (hardware revision detection via descriptor byte)
|
|
5. **Refactored DiSEqC GPIO pin assignment** (data pin moved from P0.7 to P0.0, same bit-bang algorithm)
|
|
6. **Simplified BCM4500 status polling** (consolidated from 3 register reads to 1)
|
|
|
|
---
|
|
|
|
## 1. Vendor Command Dispatch Table Comparison
|
|
|
|
Both versions use the same two-stage USB control request dispatch:
|
|
|
|
- **Stage 1** (`FUN_CODE_032a` / `FUN_CODE_034e`): Handles standard USB requests (bRequest 0x00-0x0B) via a 12-entry jump table at CODE:033B / CODE:035F. These handle GET_STATUS, CLEAR_FEATURE, SET_FEATURE, SET_ADDRESS, GET_DESCRIPTOR, SET_DESCRIPTOR, GET_CONFIGURATION, SET_CONFIGURATION, GET_INTERFACE, SET_INTERFACE, SYNCH_FRAME, and FW_VERSION_READ (0x0B).
|
|
|
|
- **Stage 2** (`FUN_CODE_0056`, identical in both versions): Handles vendor requests (bmRequestType bit 6 set, bRequest 0x80-0x9D) via a 30-entry jump table at CODE:0076. Range check: `(bRequest + 0x80) < 0x1E`, dispatching to `JMP @A+DPTR` at `0x76 + (bRequest - 0x80) * 2`.
|
|
|
|
### Vendor Command Jump Table (0x80-0x9D)
|
|
|
|
| bRequest | Name | v2.06 Target | v2.13 Target | Status |
|
|
|----------|------|-------------|-------------|--------|
|
|
| 0x80 | GET_8PSK_CONFIG | 0x00B2 | 0x00B2 | **Both**: Read config byte to EP0BUF (v2.06: IRAM 0x6D, v2.13: IRAM 0x4F) |
|
|
| 0x81 | SET_8PSK_CONFIG | 0x0326 (STALL) | 0x034A (STALL) | **Both STALL** - not implemented in either |
|
|
| 0x82 | (reserved) | 0x0326 (STALL) | 0x034A (STALL) | **Both STALL** |
|
|
| 0x83 | I2C_WRITE | 0x00F1 | 0x00F1 | **Both**: Same I2C write handler |
|
|
| 0x84 | I2C_READ | 0x0102 | 0x0102 | **Both**: Same I2C read handler |
|
|
| 0x85 | ARM_TRANSFER | 0x0110 | 0x0110 | **Both**: Same ARM transfer handler |
|
|
| 0x86 | TUNE_8PSK | 0x012E | 0x012E | **Both**: Same tuning handler |
|
|
| 0x87 | GET_SIGNAL_STRENGTH | 0x0140 | 0x0162 | **CHANGED** - see section below |
|
|
| 0x88 | LOAD_BCM4500 | 0x0326 (STALL) | 0x034A (STALL) | **Both STALL** - BCM4500 loading via different mechanism |
|
|
| 0x89 | BOOT_8PSK | 0x00C4 | 0x00C4 | **Both**: Same boot handler |
|
|
| 0x8A | START_INTERSIL | 0x019C | 0x01BE | **Relocated** but functionally similar |
|
|
| 0x8B | SET_LNB_VOLTAGE | 0x01CB | 0x01ED | **Relocated** but functionally similar |
|
|
| 0x8C | SET_22KHZ_TONE | 0x01DD | 0x01FF | **Relocated** but functionally similar |
|
|
| 0x8D | SEND_DISEQC_COMMAND | 0x01EF | 0x0211 | **CHANGED** - different DiSEqC implementation |
|
|
| 0x8E | SET_DVB_MODE | 0x0326 (STALL) | 0x034A (STALL) | **Both STALL** |
|
|
| 0x8F | (unknown) | 0x01FC | 0x021E | Both: Similar read-back function |
|
|
| 0x90 | GET_SIGNAL_LOCK | 0x020B | 0x022D | **Relocated** but functionally similar |
|
|
| 0x91 | (unknown) | 0x022C | 0x024E | Both: I2C read-back |
|
|
| 0x92 | (unknown) | 0x024A | 0x026C | Both: I2C read-back |
|
|
| 0x93 | GET_SERIAL_NUMBER | 0x026F | 0x0293 | **Relocated** but functionally similar |
|
|
| 0x94 | (unknown) | 0x01B9 | 0x01DB | Both: LNB-related |
|
|
| 0x95 | (unknown) | 0x02DF | 0x0303 | Both: Read-back function |
|
|
| 0x96 | (unknown) | 0x02B4 | 0x02D8 | Both: Similar handler |
|
|
| 0x97 | (unknown) | 0x02C1 | 0x02E5 | Both: Similar handler |
|
|
| 0x98 | (unknown) | 0x02CB | 0x02EF | Both: Similar handler |
|
|
| 0x99 | **NEW: GET_DEMOD_STATUS** | 0x0326 (STALL) | 0x0317 | **ADDED in v2.13** |
|
|
| 0x9A | **NEW: INIT_DEMOD** | 0x0326 (STALL) | 0x0140 | **ADDED in v2.13** |
|
|
| 0x9B | (reserved) | 0x0326 (STALL) | 0x034A (STALL) | **Both STALL** |
|
|
| 0x9C | **NEW: DELAY_COMMAND** | 0x0326 (STALL) | 0x032B | **ADDED in v2.13** |
|
|
| 0x9D | SET_MODE_FLAG | 0x02FA | 0x033A | **CHANGED** - different implementation |
|
|
|
|
### Commands Added in v2.13
|
|
|
|
**0x99 - GET_DEMOD_STATUS (new read command)**
|
|
```
|
|
LCALL FUN_CODE_2421 ; calls FUN_CODE_2239(0x3F, 0xF9)
|
|
; -> I2C read from device 0x3F, register 0xF9
|
|
MOV EP0BUF[0], R7 ; return I2C read result
|
|
EP0BCL = 1 ; send 1 byte back
|
|
```
|
|
Reads demodulator register 0xF9 via I2C (device address 0x3F) and returns the value to the host. This is a status/diagnostics register read for the satellite demodulator IC.
|
|
|
|
**0x9A - INIT_DEMOD (new control command)**
|
|
```
|
|
LCALL 0x231E ; EP0 flush/prepare
|
|
if (config_flags.0 == 1) { ; check if demodulator is present
|
|
counter = 0;
|
|
while (counter < 3) {
|
|
LCALL FUN_CODE_1977 ; initialization step
|
|
if (success) break;
|
|
counter++;
|
|
}
|
|
}
|
|
EP0BCL = 0 ; no data returned
|
|
```
|
|
Performs up to 3 attempts to initialize the demodulator, but only if the demodulator-present flag (bit 0 of DAT_INTMEM_4F) is set. This provides host-triggered re-initialization capability.
|
|
|
|
**0x9C - DELAY_COMMAND (new control command)**
|
|
```
|
|
R7 = wValue ; delay parameter from USB SETUP packet
|
|
LCALL FUN_CODE_1ac6 ; tuning/acquisition delay function
|
|
EP0BCL = 0 ; no data returned
|
|
```
|
|
Calls FUN_CODE_1ac6 with the wValue parameter from the USB SETUP packet. FUN_CODE_1ac6 performs an I2C-based tuning acquisition sequence: it reads demod register 0xF9, writes control values to registers 0xF8 and 0xF9, then polls register 0xF9 up to 40 times (0x28) waiting for bit 0 to be set (lock acquired). If lock fails, it calls FUN_CODE_1e3c which performs a full demodulator reset sequence.
|
|
|
|
### Commands Removed from v2.13
|
|
None were removed -- all commands that were STALL in v2.06 remain STALL in v2.13.
|
|
|
|
### Changed Command Implementations
|
|
|
|
**0x87 - GET_SIGNAL_STRENGTH (modified)**
|
|
- v2.06 at 0x0140: Checks DAT_INTMEM_6D bit 0 (demod active), reads three I2C status registers (0xA2, 0xA8, 0xA4) to compute signal quality, loops up to 6 iterations polling for demod readiness
|
|
- v2.13 at 0x0162: Checks DAT_INTMEM_4F bit 0 (demod active), reads I2C but uses different function call chain (FUN_CODE_1278 vs FUN_CODE_0c97). Same overall logic with relocated internal variables.
|
|
|
|
**0x8D - SEND_DISEQC_COMMAND (GPIO pin reassignment)**
|
|
- v2.06: `LCALL 0x23e0; LCALL 0x1e41` -- GPIO bit-bang DiSEqC via FUN_CODE_2098, data on **P0.7**, carrier on P0.3
|
|
- v2.13: `LCALL 0x231e; LCALL FUN_CODE_0dbc` -- GPIO bit-bang DiSEqC via FUN_CODE_2060, data on **P0.0**, carrier on P0.3
|
|
|
|
Both versions use the identical algorithm:
|
|
1. Read wLength (0xE6BE) as message byte count
|
|
2. Clear P0.3 (disable 22kHz carrier)
|
|
3. Delay 15 ticks via delay function (7.5ms settling time)
|
|
4. If message bytes present: iterate through EP0BUF, sending each byte via Manchester-encoded bit-bang (8 data bits + odd parity, 3 Timer2 ticks per bit)
|
|
5. If wValue == 0 and no bytes: tone burst A (25 Timer2 ticks = 12.5ms)
|
|
6. If wValue != 0 and no bytes: tone burst B via byte transmit with 0xFF pattern
|
|
|
|
**CORRECTION**: Earlier analysis incorrectly identified v2.13 as using "I2C-based DiSEqC." Deep decompilation of the sub-functions (FUN_CODE_2060, FUN_CODE_22f3, FUN_CODE_22b0) reveals they are GPIO bit-bang implementations identical in algorithm to v2.06's FUN_CODE_2098 and FUN_CODE_2372. The only change is the data pin assignment (P0.7 -> P0.0), reflecting a different PCB layout.
|
|
|
|
**0x9D - SET_MODE_FLAG (different logic)**
|
|
- v2.06: Reads byte at descriptor_base + 10, checks if value is 4, 5, or 6, and conditionally sets bit flag 0x06 based on wValue - 1
|
|
- v2.13: Simply checks if wValue != 0, and if so calls FUN_CODE_21d1 which performs a conditional demodulator reset (calls FUN_CODE_1e3c if `_1_4` flag isn't already set, then writes I2C control registers 0xFC on both device 0x7F and 0x3F)
|
|
|
|
---
|
|
|
|
## 2. Key Function Correspondence
|
|
|
|
| v2.06 Function | v2.13 Function | Role |
|
|
|---------------|---------------|------|
|
|
| `main` (0x188D) | `main_entry` (0x170D) | RESET vector - clears IRAM, processes init table, jumps to init |
|
|
| `FUN_CODE_09a7` (0x09A7) | `FUN_CODE_0800` (0x0800) | Main init + main loop |
|
|
| `FUN_CODE_13c3` (0x13C3) | `FUN_CODE_11ab` (0x11AB) | USB/peripheral descriptor setup |
|
|
| `FUN_CODE_032a` (0x032A) | `FUN_CODE_034e` (0x034E) | Standard USB request handler |
|
|
| `FUN_CODE_0056` (0x0056) | `FUN_CODE_0056` (0x0056) | Vendor request dispatcher (identical code) |
|
|
| `FUN_CODE_2297` (0x2297) | `FUN_CODE_21ec` (0x21EC) | Main loop poll (USB IRQ processing) |
|
|
| `FUN_CODE_21ed` (0x21ED) | `FUN_CODE_2189` (0x2189) | EP2CS setup + PCON idle |
|
|
| `FUN_CODE_211d` (0x211D) | `FUN_CODE_20b9` (0x20B9) | CPUCS reset pulse (EP2 management) |
|
|
| `FUN_CODE_2174` (0x2174) | `FUN_CODE_2110` (0x2110) | USB descriptor type walker (identical code) |
|
|
| `FUN_CODE_1919` (0x1919) | `FUN_CODE_1800` (0x1800) | GPIF/FIFO management (identical logic) |
|
|
| `FUN_CODE_1d4f` (0x1D4F) | -- | v2.06 demod init (GPIO-based, complex) |
|
|
| -- | `FUN_CODE_1d4b` (0x1D4B) | v2.13 demod init (I2C write 4 bytes to 0x7F/0xF0) |
|
|
| `FUN_CODE_1da8` (0x1DA8) | -- | v2.06 I2C read with timeout (uses FUN_CODE_1556) |
|
|
| -- | `FUN_CODE_0eea` (0x0EEA) | v2.13 I2C read with retry (20 attempts) |
|
|
| `FUN_CODE_1dfb` (0x1DFB) | `FUN_CODE_14b9` (0x14B9) | Delay loop (clock-dependent timing) |
|
|
| `FUN_CODE_1cf3` (0x1CF3) | `FUN_CODE_1c44` (0x1C44) | Configuration update function |
|
|
| `FUN_CODE_12ea` (0x12EA) | `FUN_CODE_1000` (0x1000) | USB endpoint configuration |
|
|
| `FUN_CODE_0ddd` (0x0DDD) | `FUN_CODE_0ca4` (0x0CA4) | BCM4500 firmware loader |
|
|
| `FUN_CODE_2000` (0x2000) | `FUN_CODE_208d` (0x208D) | BCM4500 status polling |
|
|
| `FUN_CODE_1556` (0x1556) | `FUN_CODE_0eea` (0x0EEA) | I2C multi-byte transfer |
|
|
| `FUN_CODE_24d2` (0x24D2) | `FUN_CODE_243d` (0x243D) | SET_DVB_MODE config store |
|
|
| `FUN_CODE_2419` (0x2419) | `FUN_CODE_2357` (0x2357) | GET config byte to EP0BUF |
|
|
| `FUN_CODE_23cb` (0x23CB) | `FUN_CODE_2309` (0x2309) | Read descriptor byte to EP0BUF |
|
|
| `FUN_CODE_1a0e` (0x1A0E) | -- | v2.06 serial number reader (EEPROM) |
|
|
| `INT0_vec` (0x0003) | `INT0_vector` (0x0003) | INT0 interrupt handler (**significantly different**) |
|
|
| -- | `FUN_CODE_2239` (0x2239) | v2.13 I2C single-byte read helper |
|
|
| -- | `FUN_CODE_2031` (0x2031) | v2.13 USB reconnect function |
|
|
| -- | `FUN_CODE_1799` (0x1799) | v2.13 demod checksum/signature verify |
|
|
| -- | `FUN_CODE_1ca0` (0x1CA0) | v2.13 descriptor checksum verify |
|
|
| -- | `FUN_CODE_1ac6` (0x1AC6) | v2.13 tuning acquisition sequence |
|
|
| -- | `FUN_CODE_1e3c` (0x1E3C) | v2.13 demodulator full reset |
|
|
| -- | `FUN_CODE_10d9` (0x10D9) | v2.13 demod status polling/init |
|
|
| -- | `FUN_CODE_0dbc` (0x0DBC) | v2.13 DiSEqC GPIO bit-bang wrapper (data on P0.0) |
|
|
|
|
### USB Descriptor Setup (FUN_CODE_13c3 vs FUN_CODE_11ab)
|
|
|
|
Both functions are structurally identical:
|
|
1. Disable USB disconnect (0xE605 bit 1 clear)
|
|
2. Configure IFCONFIG (0xE600) for internal clock, 48MHz
|
|
3. Set REVCTL (0xE601) to 0xCA
|
|
4. Configure GPIFIDLECTL, PORTACFG
|
|
5. Set PORT registers (P0, P3, IPL1)
|
|
6. Configure GPIFCTLCFG, FIFORESET, FIFOPINPOLAR
|
|
7. Configure Timer2 (RCAP2H=0xF8, RCAP2L=0x2F -> ~2ms period at 48MHz/12)
|
|
8. Initialize subsystem modules
|
|
|
|
Key difference: v2.13 calls `INT0_vector()` (the INT0 handler) during initialization as a probing step. This runs the demodulator availability check during USB setup, before enabling interrupts. v2.06 does not do this.
|
|
|
|
Descriptor pointer offsets:
|
|
- v2.06: BANK1_R4:R5 = 0x1200 (descriptor base)
|
|
- v2.13: BANK1_R4:R5 = 0x0E00 (descriptor base, lower due to code restructuring)
|
|
|
|
---
|
|
|
|
## 3. Structural Differences
|
|
|
|
### 3.1 v2.13 Retry Loops (FUN_CODE_1799 and FUN_CODE_1ca0)
|
|
|
|
In the main init function `FUN_CODE_0800`, v2.13 has:
|
|
|
|
```c
|
|
// Retry loop 1: FUN_CODE_1799 - demodulator signature verification
|
|
DAT_INTMEM_36 = 0x14; // 20 attempts
|
|
while (DAT_INTMEM_36 != 0 && FUN_CODE_1799() fails) {
|
|
DAT_INTMEM_36--;
|
|
}
|
|
if (DAT_INTMEM_36 == 0) {
|
|
FUN_CODE_1ac6(100); // tuning acquisition with 100ms delay
|
|
}
|
|
|
|
// Retry loop 2: FUN_CODE_1ca0 - descriptor checksum verification
|
|
DAT_INTMEM_36 = 0x14; // 20 attempts
|
|
while (DAT_INTMEM_36 != 0 && FUN_CODE_1ca0() fails) {
|
|
DAT_INTMEM_36--;
|
|
}
|
|
if (DAT_INTMEM_36 == 0) {
|
|
FUN_CODE_1ac6(100); // tuning acquisition with 100ms delay
|
|
}
|
|
```
|
|
|
|
**FUN_CODE_1799 - Demodulator Signature Verification:**
|
|
1. Calls FUN_CODE_1d4b() which writes 4 bytes via I2C to device 0x7F, register 0xF0 (demodulator control)
|
|
2. Saves parameters DAT_INTMEM_39:3A
|
|
3. Checks if parameters match 0x021C (known good value) - returns early if match
|
|
4. Reads 5 I2C bytes via FUN_CODE_0718, each at register 0x0A offset (stepping by 2)
|
|
5. Subtracts 0x30 ('0') from each byte (ASCII to binary conversion)
|
|
6. Sums the values and compares sum to the saved parameters
|
|
7. Returns success (carry set) if checksum matches
|
|
|
|
This verifies the demodulator responds correctly with the expected identification pattern. The ASCII-to-binary conversion suggests the demod returns a readable version string at register 0x0A.
|
|
|
|
**FUN_CODE_1ca0 - Descriptor Checksum Verification:**
|
|
1. Iterates bytes 6 through 0x29 (36 bytes) of the BANK2_R6:R7 descriptor block
|
|
2. Computes running sum, compares against expected value 0x0706
|
|
3. If first block passes, iterates bytes 0x2C through 0x4F (36 bytes) of same block
|
|
4. Computes second running sum, compares against expected value 0x0686
|
|
5. Returns success only if both checksums match
|
|
|
|
This validates the integrity of a 2-block descriptor/configuration structure (possibly EEPROM-loaded calibration data).
|
|
|
|
**v2.06 equivalent:** v2.06 does NOT have these retry loops. It calls `FUN_CODE_1a0e` (serial number/EEPROM reader) directly without verification, then proceeds immediately. There is no signature check or checksum validation.
|
|
|
|
### 3.2 Version Byte Check (Hardware Revision Detection)
|
|
|
|
After the retry loops, v2.13 performs:
|
|
|
|
```c
|
|
// Read byte at descriptor_base + 10
|
|
byte version_byte = *(BANK1_R4:R5 + 10); // 0x0E0A
|
|
if (version_byte == 0x03) {
|
|
bVar4 = 0x80; // flag = set
|
|
} else {
|
|
bVar4 = 0; // flag = clear
|
|
}
|
|
_1_3 = bVar4 >> 7; // store as bit flag _1_3
|
|
```
|
|
|
|
This reads byte offset 10 from the USB descriptor base address. Offset 10 in a USB device descriptor is `bMaxPacketSize0` in standard USB, but since this is a custom descriptor area, it likely encodes a hardware revision. The value 0x03 sets bit flag `_1_3`, creating a hardware-revision-aware code path.
|
|
|
|
**Impact:** The `_1_3` flag is used elsewhere in v2.13 to conditionally execute different initialization sequences, supporting multiple hardware revisions of the SkyWalker-1 board.
|
|
|
|
### 3.3 FUN_CODE_2031 - USB Reconnect Before Main Loop
|
|
|
|
```c
|
|
void FUN_CODE_2031(void) {
|
|
if (_0_0 == 0) {
|
|
CPUCS |= 0x08; // Set CPUCS.3 (8051 reset bit? Or re-enumerate)
|
|
} else {
|
|
CPUCS |= 0x0A; // Set CPUCS.3 + CPUCS.1
|
|
}
|
|
FUN_CODE_14b9(5, 0xDC); // Delay ~1500 cycles
|
|
EPIRQ = 0xFF; // Clear all endpoint interrupts
|
|
USBIRQ = 0xFF; // Clear all USB interrupts
|
|
DAT_SFR_91 &= 0xEF; // Clear EXIF.4 (USB interrupt flag)
|
|
CPUCS &= 0xF7; // Clear CPUCS.3
|
|
}
|
|
```
|
|
|
|
This performs a controlled USB re-enumeration by pulsing CPUCS.3, then clearing all pending USB/endpoint interrupts. The conditional on `_0_0` adds CPUCS.1 when the flag is set (possibly switching between 12MHz and 48MHz operation).
|
|
|
|
**v2.06 equivalent:** In v2.06, this exact same logic exists as `INT0_vec` (the INT0 interrupt handler at 0x0003). The critical difference is that in v2.06 this code runs as an interrupt handler, while in v2.13 it's called as a normal function (`FUN_CODE_2031`) before the main loop starts, AND the INT0 vector is repurposed for demodulator polling (see section 4).
|
|
|
|
---
|
|
|
|
## 4. INT0 Handler Difference
|
|
|
|
### v2.06 INT0 (CODE:0003) - USB Re-enumeration
|
|
|
|
```c
|
|
void INT0_vec(void) {
|
|
if (_0_7 == 0) {
|
|
CPUCS |= 0x08; // CPUCS bit 3
|
|
} else {
|
|
CPUCS |= 0x0A; // CPUCS bits 3+1
|
|
}
|
|
FUN_CODE_1dfb(5, 0xDC); // Delay
|
|
EPIRQ = 0xFF; // Clear endpoint IRQs
|
|
USBIRQ = 0xFF; // Clear USB IRQs
|
|
DAT_SFR_91 &= 0xEF; // Clear external interrupt flag
|
|
CPUCS &= 0xF7; // Clear CPUCS bit 3
|
|
}
|
|
```
|
|
|
|
Simple USB reconnect/re-enumeration handler. Pulses CPUCS.3, clears all pending interrupts.
|
|
|
|
### v2.13 INT0 (CODE:0003) - Demodulator Availability Polling
|
|
|
|
```c
|
|
void INT0_vector(void) {
|
|
for (DAT_INTMEM_37 = 0x28; DAT_INTMEM_37 != 0; DAT_INTMEM_37--) {
|
|
// Read I2C device 0x7F (demod A), checking for response
|
|
byte result = FUN_CODE_2239(0x7F); // I2C read from 0x7F
|
|
if (result != 0x01) {
|
|
// Try I2C device 0x3F (demod B)
|
|
result = FUN_CODE_2239(0x3F); // I2C read from 0x3F
|
|
if (result != 0x01) break; // Neither responded normally
|
|
}
|
|
}
|
|
_1_4 = (DAT_INTMEM_37 == 0); // Set flag if loop exhausted (no demod found)
|
|
}
|
|
```
|
|
|
|
This is a complete replacement of INT0's purpose. Instead of USB re-enumeration, INT0 now polls two I2C devices (0x7F and 0x3F) up to 40 times (0x28). These are two possible addresses for the satellite demodulator IC.
|
|
|
|
**FUN_CODE_2239 decompiled:**
|
|
```c
|
|
undefined1 FUN_CODE_2239(byte device_addr) {
|
|
DAT_INTMEM_48 = 0xE1; // buffer address high
|
|
DAT_INTMEM_49 = 0; // buffer address low
|
|
FUN_CODE_0eea(1, device_addr, 0x51); // I2C read 1 byte from device
|
|
return DAT_EXTMEM_e100; // return the read byte
|
|
}
|
|
```
|
|
|
|
The function performs an I2C single-byte read from the specified device address, using address 0x51 as a parameter (likely selecting a specific I2C bus or mode via the FX2's auxiliary I2C controller at 0xE678). It stores the result at 0xE100 (XRAM buffer).
|
|
|
|
**Behavioral meaning:** The flag `_1_4` is set to 1 if neither demodulator responded after 40 attempts - indicating no demodulator hardware is present. This flag is later checked by:
|
|
- `FUN_CODE_21d1` (command 0x9D handler) - skips demodulator reset if `_1_4 != 1`
|
|
- Various initialization paths to avoid hanging on missing hardware
|
|
|
|
**Why this matters:** v2.06 assumes the demodulator is always present. v2.13 can detect and gracefully handle boards where the demodulator is absent or unresponsive, making it more robust for manufacturing QC and field failures.
|
|
|
|
---
|
|
|
|
## 5. What Can v2.13 Do That v2.06 Cannot?
|
|
|
|
### 5.1 Demodulator Hardware Detection
|
|
v2.13 probes two I2C addresses (0x7F, 0x3F) at startup to determine which demodulator variant is installed, or if none is present. v2.06 blindly assumes the hardware configuration.
|
|
|
|
### 5.2 Host-Initiated Demodulator Re-initialization (Command 0x9A)
|
|
The host can trigger a demodulator re-initialization via USB vendor command 0x9A, with up to 3 retry attempts. v2.06 has no mechanism for the host to request re-initialization.
|
|
|
|
### 5.3 Demodulator Status Read (Command 0x99)
|
|
Direct I2C register read of demodulator register 0xF9, returned to host. This enables diagnostic/monitoring software to check demodulator status without going through the full signal quality pipeline.
|
|
|
|
### 5.4 Host-Controlled Tuning Delay (Command 0x9C)
|
|
Allows the host to invoke the tuning acquisition sequence with a configurable delay parameter. In v2.06, the tuning timing is entirely firmware-controlled with no host influence.
|
|
|
|
### 5.5 DiSEqC GPIO Pin Reassignment
|
|
All firmware versions use the same GPIO bit-bang algorithm for DiSEqC signaling. The only change is the data pin assignment per PCB revision:
|
|
|
|
| Version | Data Pin | Carrier Pin | Byte Transmit | Bit Symbol | Timer Tick |
|
|
|---------|----------|-------------|--------------|------------|------------|
|
|
| v2.06 | **P0.7** | P0.3 | 0x2098 | 0x23B5 | 0x24C6 |
|
|
| Rev.2 v2.10 | **P0.4** | P0.3 | FUN_CODE_07d1 | FUN_CODE_213c | FUN_CODE_225f |
|
|
| v2.13 FW1 | **P0.0** | P0.3 | FUN_CODE_2060 | FUN_CODE_22f3 | func_0x2431 |
|
|
|
|
The algorithm is identical across all versions: Manchester-encoded bit-bang with Timer2-based timing, odd parity per byte, and 25-tick tone bursts for mini-commands.
|
|
|
|
### 5.6 Firmware/Descriptor Integrity Verification
|
|
v2.13 validates demodulator identification (ASCII version string checksum) and descriptor block integrity (two 36-byte checksums) before proceeding. If verification fails after 20 attempts, it falls back to a recovery sequence. v2.06 does no integrity checking.
|
|
|
|
### 5.7 Hardware Revision Awareness
|
|
The version byte check (descriptor offset 10, value 0x03) creates conditional code paths allowing a single firmware image to support multiple SkyWalker-1 hardware revisions. v2.06 has a single code path for one hardware revision.
|
|
|
|
### 5.8 Simplified BCM4500 Status Polling
|
|
v2.06's `FUN_CODE_2000` polls three separate BCM4500 registers (0xA2, 0xA8, 0xA4 via I2C) to determine demodulator readiness. v2.13's `FUN_CODE_208d` polls only one register (0xA4), suggesting either the demodulator firmware was updated to consolidate status, or the additional checks were found to be redundant.
|
|
|
|
### 5.9 Conditional Demodulator Reset (Command 0x9D)
|
|
v2.13's 0x9D handler can trigger a full demodulator reset sequence (FUN_CODE_1e3c -> register writes to 0x18 bus) controlled by the host via wValue. This is useful for error recovery without full device re-enumeration.
|
|
|
|
---
|
|
|
|
## 6. Architecture Summary
|
|
|
|
| Aspect | v2.06 | v2.13 |
|
|
|--------|-------|-------|
|
|
| Total functions | 61 | 82 (+21) |
|
|
| RESET vector | 0x188D | 0x170D |
|
|
| Stack pointer | 0x72 | 0x50 |
|
|
| Init data table | CODE:0B46 | CODE:0B88 |
|
|
| Descriptor base | 0x1200 | 0x0E00 |
|
|
| Config byte (IRAM) | 0x6D | 0x4F |
|
|
| INT0 purpose | USB re-enumerate | Demod probe |
|
|
| DiSEqC data pin | P0.7 (GPIO bit-bang) | P0.0 (GPIO bit-bang) |
|
|
| Demod init | Direct, no retry | 20-attempt retry |
|
|
| Integrity checks | None | Checksum verification |
|
|
| HW revision support | Single | Multi-revision (flag _1_3) |
|
|
| New vendor cmds | -- | 0x99, 0x9A, 0x9C |
|
|
|
|
---
|
|
|
|
## 7. DiSEqC Timing Chain Analysis
|
|
|
|
### 7.1 Timer2 Configuration (Identical Across All Versions)
|
|
|
|
All firmware versions configure Timer2 identically during USB descriptor setup:
|
|
|
|
```
|
|
T2CON = 0x04 ; Auto-reload mode, internal clock, TR2=1 (running)
|
|
RCAP2H = 0xF8 ; Reload high byte
|
|
RCAP2L = 0x2F ; Reload low byte -> RCAP2 = 0xF82F = 63535
|
|
CKCON = 0x00 ; Default (T2M=0 -> Timer2 clock = CLKOUT/12)
|
|
```
|
|
|
|
**Timer2 Clock Derivation:**
|
|
```
|
|
FX2 master clock = 48 MHz
|
|
CKCON.T2M = 0 -> Timer2 clock = 48 MHz / 12 = 4 MHz
|
|
Count per overflow = 65536 - 63535 = 2001
|
|
Tick period = 2001 / 4,000,000 = 500.25 us ~ 500 us
|
|
Tick frequency ~ 2.0 kHz
|
|
```
|
|
|
|
Timer2 runs continuously from power-on and is never stopped or reconfigured. It serves as a stable 500 us timebase for all DiSEqC operations.
|
|
|
|
### 7.2 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 to FX2 | | |
|
|
| (firmware only) | | firmware logic) | | |
|
|
+------------------+ +--------------------+ +------------------+
|
|
```
|
|
|
|
The firmware does NOT generate the 22 kHz carrier directly. P0.3 gates an external
|
|
22 kHz oscillator circuit on the PCB. The data pin (P0.7/P0.4/P0.0 depending on
|
|
version) is used only internally by the firmware to control the Manchester encoding
|
|
logic -- it tells the bit-symbol function whether to cut the carrier short or leave
|
|
it on for the full period.
|
|
|
|
### 7.3 Manchester Encoding (DiSEqC Bit Symbol)
|
|
|
|
Each DiSEqC bit consists of 3 Timer2 ticks (3 x 500 us = 1.5 ms):
|
|
|
|
**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)
|
|
```
|
|
|
|
**Implementation (decompiled from Rev.2 FUN_CODE_213c):**
|
|
```c
|
|
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
|
|
}
|
|
```
|
|
|
|
### 7.4 Byte Transmission (8 Data Bits + Odd Parity)
|
|
|
|
Each DiSEqC byte is 9 bits: 8 data (MSB first) + 1 parity (odd).
|
|
|
|
**Implementation (decompiled from Rev.2 FUN_CODE_07d1):**
|
|
```c
|
|
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) { // Test MSB
|
|
data_pin = 1; // Set data = '1'
|
|
diseqc_bit_symbol(); // Transmit '1' symbol
|
|
ones_count++;
|
|
} else {
|
|
data_pin = 0; // Set data = '0'
|
|
diseqc_bit_symbol(); // Transmit '0' symbol
|
|
}
|
|
data <<= 1; // Next bit
|
|
}
|
|
|
|
data_pin = ~ones_count & 1; // Odd parity: '1' if even count
|
|
diseqc_bit_symbol(); // Transmit parity bit
|
|
}
|
|
```
|
|
|
|
**Timing per byte:** 9 bits x 1.5 ms = 13.5 ms
|
|
|
|
### 7.5 Tone Burst (Mini DiSEqC Command)
|
|
|
|
For legacy 2-way satellite switches, a simple tone burst is used instead of a
|
|
full DiSEqC message. The burst is 25 Timer2 ticks of continuous carrier:
|
|
|
|
```c
|
|
void tone_burst_A(void) {
|
|
TF2 = 0; // Sync timer
|
|
wait_TF2(); // One tick gap
|
|
P0 |= 0x08; // P0.3 = 1 -> carrier ON
|
|
for (char i = 25; i > 0; i--) {
|
|
wait_TF2(); // 25 x 500 us = 12.5 ms
|
|
}
|
|
P0 &= 0xF7; // P0.3 = 0 -> carrier OFF
|
|
}
|
|
```
|
|
|
|
**Burst duration:** 25 x 500 us = 12.5 ms (matches DiSEqC spec)
|
|
|
|
### 7.6 Timer Tick Wait (TF2 Polling)
|
|
|
|
The lowest-level timing primitive is a busy-wait on Timer2 overflow:
|
|
|
|
```c
|
|
void wait_TF2(void) {
|
|
while (TF2 == 0) {} // Poll Timer2 overflow flag
|
|
TF2 = 0; // Clear flag for next tick
|
|
}
|
|
```
|
|
|
|
This is identical across all versions (v2.06: 0x24C6, Rev.2: FUN_CODE_225f,
|
|
v2.13: func_0x2431). Timer2 overflows every 500.25 us, providing the fundamental
|
|
DiSEqC timebase.
|
|
|
|
### 7.7 CPU Clock Compensation (Delay Function)
|
|
|
|
The delay function used before DiSEqC transmission adjusts for CPU clock speed:
|
|
|
|
```c
|
|
void delay(byte high, byte low) {
|
|
byte clkspd = CPUCS & 0x18; // CPUCS[4:3] = clock speed bits
|
|
if (clkspd == 0x00) { // 12 MHz: halve the count
|
|
// Adjust high:low /= 2
|
|
} else if (clkspd == 0x10) { // 48 MHz: double the count
|
|
// Adjust high:low *= 2
|
|
}
|
|
// 24 MHz (0x08): use count as-is
|
|
while (high:low > 0) {
|
|
wait_TF2();
|
|
high:low--;
|
|
}
|
|
}
|
|
```
|
|
|
|
The pre-DiSEqC delay call is `delay(0, 0x0F)` = 15 ticks x 500 us = 7.5 ms.
|
|
This allows the LNB voltage to stabilize before DiSEqC signaling begins.
|
|
|
|
### 7.8 Complete DiSEqC Timing Summary
|
|
|
|
| Parameter | Value | Source |
|
|
|-----------|-------|--------|
|
|
| Timer2 clock | 4 MHz (48 MHz / 12) | CKCON default, T2M=0 |
|
|
| Timer2 reload | 0xF82F | RCAP2H:RCAP2L |
|
|
| Tick period | 500.25 us | (65536 - 63535) / 4 MHz |
|
|
| Bit period | 1.5 ms (3 ticks) | DiSEqC Manchester encoding |
|
|
| Byte period | 13.5 ms (9 bits) | 8 data + 1 parity |
|
|
| Tone burst | 12.5 ms (25 ticks) | Mini-command A/B |
|
|
| Pre-TX delay | 7.5 ms (15 ticks) | Voltage settling |
|
|
| 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 |
|
|
| Carrier enable | P0.3 | All versions |
|
|
| Data pin (v2.06) | P0.7 | PCB revision A |
|
|
| Data pin (Rev.2) | P0.4 | PCB revision B |
|
|
| Data pin (v2.13) | P0.0 | PCB revision C |
|