# Architecture Documentation

## Overview

`pydownloadfwviasrio` implementa un sistema di programmazione flash remota che usa **SRIO-over-TFTP** per comunicare con un target hardware. L'implementazione replica fedelmente il protocollo del software C++ di riferimento (`dwl_fw.cpp`) con workflow identico per garantire compatibilità hardware.

### Componenti principali

1. **Sender (client)**: Applicazione Python con GUI Tkinter che invia comandi flash via TFTP
2. **Receiver (target/emulator)**: Server TFTP che decodifica comandi SRIO, gestisce registri flash controller e scrive su flash memory
3. **SRIO Flash Controller**: Layer che gestisce sequenze registro-flash (MODE_REG, CMD_REG, CTRL_REG, STATUS_REG, FIFO)
4. **Profiles System**: Configurazione multi-target con supporto GlobalConfig → FlashModel → FlashTarget hierarchy

## Protocollo SRIO-over-TFTP

### Architettura Registri Flash Controller

Il sistema emula un flash controller SRIO con registri mappati in memoria (da `dwl_fw.h`):

```c
// Register addresses (256-byte aligned)
#define MODE_REG      0x4700002C  // Flash mode: 0x00=primary, 0x08=secondary
#define CMD_REG       0x47000030  // Flash command byte
#define ADDR_REG      0x47000034  // Flash memory address (32-bit)
#define NUM_BYTE_REG  0x47000038  // Number of bytes to transfer
#define CTRL_REG      0x47000060  // Control: 0x01=START, 0x00=END, 0x03=WRITE/READ
#define TX_FIFO_REG   0x47000400  // TX FIFO (256 bytes for write data)
#define STATUS_REG    0x47000864  // Status: bit[11]=busy/ack, bit[12]=tx_empty
#define RX_FIFO_REG   0x47000C00  // RX FIFO (256 bytes for read data/status)
```

### Formato comando TFTP

I comandi SRIO vengono codificati nel **filename TFTP** secondo questo schema:

```
$SRIO:<slot>/<address>+<length>
```

**Esempi:**
- `$SRIO:0x13/0x4700002C+256` → Write 256 byte a MODE_REG (registro)
- `$SRIO:0x13/0x47000400+256` → Write 256 byte a TX_FIFO_REG (dati flash)
- `$SRIO:0x13/0x01000000+256` → Write/Read 256 byte flash address 0x01000000

### Operazioni Base

#### Write Register (TFTP PUT)
```python
# Scrive valore 0x06 a MODE_REG
data = struct.pack('<I', 0x06)  # 4 byte little-endian
padded = data + b'\x00' * 252   # Pad a 256 byte
client.srio_write(slot="0x13", address=MODE_REG, data=padded)
```

#### Read Register (TFTP GET)
```python
# Legge STATUS_REG
block = client.srio_read(slot="0x13", address=STATUS_REG, length=256)
status = struct.unpack('<I', block[STATUS_REG & 0xFF : (STATUS_REG & 0xFF)+4])[0]
```

## Workflow Flash Operations (da dwl_fw.cpp)

### 1. Inizializzazione QSPI

Eseguita automaticamente al primo erase/write/verify (chiamata 2 volte come da `main()` line 713-714):

```python
def op_init_qspi(endpoint):
    """dwl_fw.cpp line 299-323"""
    # Write Enable
    srio_write_reg(endpoint, MODE_REG, 0x00 + flashport)
    srio_write_reg(endpoint, CMD_REG, 0x06)
    send_request(endpoint)
    
    # Write Volatile Config Register (0x61, data=0x6F)
    srio_write_reg(endpoint, MODE_REG, 0x00 + flashport)
    srio_write_reg(endpoint, CMD_REG, 0x61)
    start_request(endpoint)
    srio_write_reg(endpoint, TX_FIFO_REG, 0x6F)
    write_data(endpoint)
    
    # Write Enable again
    srio_write_reg(endpoint, MODE_REG, 0x02 + flashport)
    srio_write_reg(endpoint, CMD_REG, 0x06)
    send_request(endpoint)
    
    # Enter 4-byte address mode (0xB7)
    srio_write_reg(endpoint, MODE_REG, 0x02 + flashport)
    srio_write_reg(endpoint, CMD_REG, 0xB7)
    send_request(endpoint)
```

**Chiamata nel main:**
```cpp
op_init_qspi(endpoint);  // Prima chiamata
op_init_qspi(endpoint);  // Seconda chiamata (dwl_fw.cpp line 713-714)
```

### 2. Erase Sector (64KB)

```python
def erase_section(endpoint, address):
    """dwl_fw.cpp line 580-594"""
    # Write Enable
    srio_write_reg(endpoint, MODE_REG, 0x02 + flashport)
    srio_write_reg(endpoint, CMD_REG, 0x06)
    send_request(endpoint)
    
    # Sector Erase command (0xD8)
    srio_write_reg(endpoint, MODE_REG, 0x06 + flashport)
    srio_write_reg(endpoint, ADDR_REG, address)
    srio_write_reg(endpoint, NUM_BYTE_REG, 0x00)
    srio_write_reg(endpoint, CMD_REG, 0xD8)
    send_request(endpoint)

def wait_flash(endpoint):
    """dwl_fw.cpp line 500-513"""
    # Read flash status register (0x05)
    srio_write_reg(endpoint, MODE_REG, 0x06 + flashport)
    srio_write_reg(endpoint, CMD_REG, 0x05)
    read_data(endpoint)
    srio_wait_reg(endpoint, RX_FIFO_REG, bit=0, value=0)  # Wait bit[0]=0 (ready)
    end_request(endpoint)

# Main erase loop (dwl_fw.cpp line 723-733)
for i in range(0, file_size, ERASE_BLOCK_SIZE):
    erase_section(endpoint, start_address + i)
    wait_flash_with_retry(endpoint, MAX_RETRY_READ=1000, timeout_us=1000)
```

### 3. Write Flash (256 byte chunks)

```python
def write_flash(endpoint, address, data_256bytes):
    """dwl_fw.cpp line 555-578"""
    # Write Enable
    srio_write_reg(endpoint, MODE_REG, 0x02 + flashport)
    srio_write_reg(endpoint, CMD_REG, 0x06)
    send_request(endpoint)
    
    # Page Program command (0x02)
    srio_write_reg(endpoint, MODE_REG, 0x06 + flashport)
    srio_write_reg(endpoint, ADDR_REG, address)
    srio_write_reg(endpoint, NUM_BYTE_REG, 256)
    srio_write_reg(endpoint, CMD_REG, 0x02)
    start_request(endpoint)
    srio_write_data(endpoint, TX_FIFO_REG, data_256bytes)
    write_data(endpoint)

# Main write loop (dwl_fw.cpp line 747-767)
address = start_address
for chunk in chunks_256bytes:
    write_flash(endpoint, address, chunk)
    usleep(WRITE_DELAY_US)           # 100ms delay (line 562)
    wait_flash_with_retry(endpoint, MAX_RETRY_READ=1000, timeout_us=1000)
    usleep(WAITFLASH_DELAY_US)       # 100ms delay (line 564)
    address += 256
```

### 4. Read/Verify Flash

```python
def read_flash(endpoint, address, num_bytes):
    """dwl_fw.cpp line 344-358"""
    # Fast Read command (0x0B)
    srio_write_reg(endpoint, MODE_REG, 0x06 + flashport)
    srio_write_reg(endpoint, ADDR_REG, address)
    srio_write_reg(endpoint, NUM_BYTE_REG, num_bytes)
    srio_write_reg(endpoint, CMD_REG, 0x0B)
    read_data(endpoint)
    data = srio_read_reg(endpoint, RX_FIFO_REG, num_bytes)
    end_request(endpoint)
    return data
```

### Macro SRIO (dwl_fw.cpp line 235-293)

```python
def start_request(endpoint):
    """Set CTRL=0x01, wait STATUS[11]=1"""
    srio_write_reg(endpoint, CTRL_REG, 0x01)
    srio_wait_reg(endpoint, STATUS_REG, bit=11, value=1)

def end_request(endpoint):
    """Check error bits, set CTRL=0x00, wait STATUS[11]=0"""
    for bit in [1, 4, 5, 6, 10]:
        srio_wait_reg(endpoint, STATUS_REG, bit=bit, value=0)
    srio_write_reg(endpoint, CTRL_REG, 0x00)
    srio_wait_reg(endpoint, STATUS_REG, bit=11, value=0)

def send_request(endpoint):
    """Full request: START → WRITE/READ → END"""
    start_request(endpoint)
    srio_write_reg(endpoint, CTRL_REG, 0x03)
    srio_wait_reg(endpoint, STATUS_REG, bit=12, value=1)
    srio_write_reg(endpoint, CTRL_REG, 0x01)
    srio_wait_reg(endpoint, STATUS_REG, bit=12, value=0)
    end_request(endpoint)

def write_data(endpoint):
    """Write TX_FIFO data to flash"""
    srio_write_reg(endpoint, CTRL_REG, 0x03)
    srio_wait_reg(endpoint, STATUS_REG, bit=12, value=1)
    srio_write_reg(endpoint, CTRL_REG, 0x01)
    srio_wait_reg(endpoint, STATUS_REG, bit=12, value=0)
    end_request(endpoint)

def read_data(endpoint):
    """Read flash data into RX_FIFO"""
    start_request(endpoint)
    srio_write_reg(endpoint, CTRL_REG, 0x03)
    srio_wait_reg(endpoint, STATUS_REG, bit=12, value=1)
    srio_write_reg(endpoint, CTRL_REG, 0x01)
    srio_wait_reg(endpoint, STATUS_REG, bit=12, value=0)
```

## Gestione Profili (Hierarchy System)

### Struttura a 3 livelli

```
GlobalConfig
    ├─ ip: "192.168.1.100"
    ├─ port: 69
    ├─ default_target: "TGT-DWL_BRD-1"
    │
    ├─ FlashModel (MODEL-1)
    │   ├─ model: "MT25QL128"
    │   ├─ flash_type: "NOR"
    │   ├─ is_4byte_addressing: false
    │   ├─ num_sectors: 256
    │   ├─ golden_start: 0x00000000
    │   ├─ golden_stop:  0x007FFFFF
    │   ├─ user_start:   0x00800000
    │   └─ user_stop:    0x00FFFFFF
    │
    └─ FlashTarget (TGT-1)
        ├─ id_target: "TGT-DWL_BRD-1"
        ├─ slot_address: 0x13
        ├─ architecture: "PowerPC"
        ├─ model: "MODEL-1" (→ riferimento a FlashModel)
        ├─ golden_binary_path: "C:/firmware/golden.bin"
        └─ user_binary_path: "C:/firmware/user.bin"
```

### Caricamento da targets.ini

```ini
[default]
ip = 192.168.1.100
port = 69
default_target = TGT-DWL_BRD-1

[MODEL-1]
model = MT25QL128
description = Micron 128Mbit NOR Flash
flash_type = NOR
is_4byte_addressing = false
num_sectors = 256
golden_start = 0x00000000
golden_stop = 0x007FFFFF
user_start = 0x00800000
user_stop = 0x00FFFFFF

[TGT-1]
id_target = TGT-DWL_BRD-1
description = Download Board #1
slot_address = 0x13
architecture = PowerPC
model = MODEL-1
golden_binary_path = 
user_binary_path =
```

### ProfileManager

```python
manager = ProfileManager()
manager.load_from_ini(Path("targets.ini"))  # Carica da INI
manager.export_to_ini(Path("targets.ini"))  # Salva su INI
manager.save()  # Persiste in flash_profiles.json
manager.load()  # Carica da flash_profiles.json

# Ottieni target con modello
target, model = manager.get_target_with_model("TGT-DWL_BRD-1")
```

## File Structure

```
pydownloadfwviasrio/
├── core/
│   ├── core.py              # FirmwareFlasher (high-level erase/write/verify)
│   └── srio_flash.py        # SRIOFlashController (low-level register protocol)
├── gui/
│   └── gui.py               # FlasherGUI (Tkinter UI con dual logs)
├── gui/
│   └── gui.py               # FlasherGUI (Tkinter UI con dual logs)
├── profiles.py              # GlobalConfig, FlashModel, FlashTarget, ProfileManager
├── tftp_client.py           # SimpleTFTPClient + SRIOTFTPClient (TFTP sender)
└── __main__.py              # Entry point

tools/
└── tftp_receiver.py         # TFTP server emulator con simulazione flash controller

_OLD/
├── linux_app/
│   ├── dwl_fw.cpp           # ⭐ REFERENCE implementation (native SRIO Linux)
│   └── dwl_fw.h             # Register definitions
└── Vecchia_app/
    └── FpgaBeamMeUp/        # Vecchia Qt app (SRIO-over-TFTP sender)
        ├── fpgaflashengine.cpp
        ├── fpgaflashinterface.h
        └── fgpaprogrammer.cpp
```

## GUI Features

### Main Window

- **Target Selector**: ComboBox con lista target da `flash_profiles.json`
- **Target Info Panel**: Visualizza IP, porta, slot SRIO, modello flash, aree memoria, path binari golden/user
- **Operation Buttons**: Erase, Write, Verify, Erase+Write+Verify
- **Progress Bar**: Barra progresso con percentuale
- **Dual Log Panels**:
  - **General Log** (sinistra): Messaggi applicazione, errori, progress
  - **TFTP Commands** (destra, monospace): Trace protocollo TFTP (PUT/GET con indirizzi)

### Configuration Manager (3 Tabs)

1. **Global Tab**: IP, porta TFTP, target di default
2. **Flash Models Tab**: Lista modelli flash con editor inline (tipo, addressing, settori, aree golden/user)
3. **Targets Tab**: Lista target con editor inline (slot SRIO, modello, architettura, path binari)

### Memory Area Selection Dialog

Quando si preme Erase/Write/Verify, appare dialog per scegliere:
- **Golden Area**: Mostra indirizzo start-stop da modello flash
- **User Area**: Mostra indirizzo start-stop da modello flash
- Usa il path binario corrispondente configurato nel target

## Testing Locale

### 1. Avvia il server TFTP emulator
```powershell
python tools/tftp_receiver.py --port 6969 --workdir ./tftp_root
```

Il server:
- Simula registri SRIO (MODE_REG, CMD_REG, CTRL_REG, STATUS_REG, FIFO)
- Gestisce state machine flash controller (START/END request, bit polling)
- Esegue operazioni flash (erase sector → 0xFF, write → salva in file, read → legge da file)
- Logga tutte le operazioni:
```
SRIO WRITE: slot=0x13, addr=0x4700002C, len=256
  MODE_REG = 0x06
SRIO WRITE: slot=0x13, addr=0x47000030, len=256
  CMD_REG = 0xD8
SRIO WRITE: slot=0x13, addr=0x47000060, len=256
  CTRL_REG = 0x03 → WRITE/READ_DATA (STATUS[12]=1)
    → Flash: ERASE sector at 0x01000000
```

### 2. Avvia la GUI
```powershell
python -m pydownloadfwviasrio
```

### 3. Configura target di test

In Configuration Manager → Global Tab:
- IP: `127.0.0.1`
- Port: `6969`

Oppure crea target in `targets.ini`:
```ini
[TGT-TEST]
id_target = TGT-TEST-LOCAL
slot_address = 0x13
model = MODEL-1
golden_binary_path = C:/test/golden_test.bin
user_binary_path = C:/test/user_test.bin
```

### 4. Testa operazioni

1. Seleziona target test
2. Premi "Write" → Scegli "User Area" → Seleziona file binario
3. Verifica log TFTP panel (destra) per vedere comandi SRIO
4. Verifica server console per vedere operazioni flash

## Compatibilità con Vecchia_app

Il protocollo è **100% compatibile** con il formato usato da `FpgaBeamMeUp`:
- ✅ Stesso schema filename: `$SRIO:<slot>/<address>+<len>`
- ✅ Stesso chunk size: 256 byte (dwl_fw.cpp DWL_CHUNK_SIZE)
- ✅ Stesso comportamento TFTP read/write
- ✅ Stessa sequenza registri flash (da dwl_fw.cpp)

**Differenze (miglioramenti):**
- ✅ **Semplificato**: nessun Qt event loop, nessun layer FpgaFlashEngine/FlashOperation
- ✅ **Workflow identico a dwl_fw.cpp**: stesso ordine operazioni, stessi delay (100ms)
- ✅ **Dual log**: separazione log applicazione vs protocollo TFTP
- ✅ **Testabile**: emulator locale con simulazione completa registri
- ✅ **Type-safe**: type hints Python, no casting void*
- ✅ **Cross-platform**: nessuna dipendenza Qt, Windows/Linux ready

## Riferimenti Codice Legacy

### dwl_fw.cpp (Linux SRIO nativo) ⭐ REFERENCE

- **main()**: Sequenza completa erase→write (line 600-792)
  - `op_init_qspi()` x2 (line 713-714)
  - Erase loop con `wait_flash_with_retry()` (line 723-733)
  - Write loop con delay 100ms + wait + delay 100ms (line 747-767)
- **Macro SRIO**: `start_request()`, `end_request()`, `send_request()`, `write_data()`, `read_data()` (line 235-293)
- **Flash ops**: `erase_section()`, `write_flash()`, `read_flash()`, `wait_flash()` (line 344-594)
- **Constants**: `WRITE_DELAY_US=100000`, `WAITFLASH_DELAY_US=100000`, `MAX_RETRY_READ=1000` (line 18-20)

### fgpaprogrammer.cpp (Qt SRIO-over-TFTP sender)

- **rtgWrite()**: TFTP PUT con filename `$SRIO:<slot>/0x<address>+<len>` (line 194-260)
- **rtgRead()**: TFTP GET (line 338-410)
- **Retry logic**: `NUM_REPEAT_WRITE`, `NUM_REPEAT_READ`, exponential backoff (line 225-240)

### qgtftptargetsim.cpp (Qt TFTP server emulator)

- **bsk_tftpd_receive_delegate()**: Decodifica filename SRIO e gestisce operazioni (line 178-279)
- **bsk_tftp_mfs_decode_filename()**: Parser filename `$SRIO:` format (line 235)

### fpgaflashengine.cpp / fpgaflashinterface.h (Qt flash abstraction)

- Layer troppo complesso per le nostre esigenze
- Usavamo solo per riferimento architetturale (simulator pattern)

## Decisioni Architetturali

### Perché TFTP e non TCP/UDP custom?
- ✅ TFTP è standard RFC 1350, testabile con tool esterni (`tftp`, Wireshark)
- ✅ Target hardware espone già server TFTP funzionante
- ✅ Compatibilità con infrastruttura esistente
- ✅ Debugging semplice: filename in chiaro nei log

### Perché filename-as-command?
- ✅ Evita di implementare protocollo binario custom
- ✅ Parsing trivial (regex su stringa)
- ✅ Self-documenting (log leggibili)
- ✅ Compatibile con vecchia app Qt

### Perché Python e non C++?
- ✅ Rapid prototyping e facilità test
- ✅ Cross-platform senza ricompilare
- ✅ Type hints moderni (meglio di Qt C++ pre-C++11)
- ✅ GUI Tkinter nativa (no dipendenze Qt)
- ✅ TFTP client custom minimale (no `tftpy` issues con `fcntl` Windows)

### Perché SRIOFlashController separato da FirmwareFlasher?
- ✅ **Separation of Concerns**: 
  - `SRIOFlashController`: Low-level register protocol (dwl_fw.cpp lines 100-594)
  - `FirmwareFlasher`: High-level flash operations (dwl_fw.cpp main loop)
- ✅ **Testability**: Posso testare controller SRIO senza file binari
- ✅ **Reusability**: Controller riutilizzabile per altre operazioni SRIO

### Perché dual log panels?
- ✅ **Debugging**: TFTP trace separato dai messaggi applicazione
- ✅ **User Experience**: Utente vede progress in General Log, developer vede protocollo in TFTP Log
- ✅ **Protocol Analysis**: Facile confrontare sequenza TFTP con dwl_fw.cpp

## Features Implementate

### Core
- ✅ **SRIO Flash Controller**: Gestione completa registri (MODE, CMD, CTRL, STATUS, FIFO)
- ✅ **QSPI Init**: `op_init_qspi()` chiamato 2x automaticamente (dwl_fw.cpp line 713-714)
- ✅ **Erase**: Sector erase 64KB con `wait_flash_with_retry(MAX_RETRY_READ=1000)`
- ✅ **Write**: 256-byte chunks con delay 100ms + wait + delay 100ms (dwl_fw.cpp line 561-564)
- ✅ **Verify**: Read back e confronto byte-by-byte
- ✅ **Retry Logic**: Exponential backoff su timeout TFTP (max 3 attempts)
- ✅ **Timing**: Delay esatti da dwl_fw.cpp (WRITE_DELAY_US, WAITFLASH_DELAY_US)

### GUI
- ✅ **Target Selection**: ComboBox con lista da flash_profiles.json
- ✅ **Info Panel**: IP, slot, modello, aree memoria, path binari golden/user
- ✅ **Dual Logs**: General + TFTP (monospace per trace protocollo)
- ✅ **Progress Bar**: Percentuale e barra visuale
- ✅ **Memory Area Dialog**: Selezione golden/user con indirizzi visibili
- ✅ **Config Manager**: 3 tabs (Global, Models, Targets) con edit inline

### Configuration
- ✅ **INI Support**: Caricamento/salvataggio `targets.ini` format
- ✅ **JSON Persistence**: `flash_profiles.json` per stato runtime
- ✅ **3-Level Hierarchy**: GlobalConfig → FlashModel → FlashTarget
- ✅ **Binary Paths**: golden_binary_path + user_binary_path per target
- ✅ **Migration**: Tool per convertire vecchi profili (tools/migrate_profiles_json.py)

### Testing
- ✅ **TFTP Server Emulator**: Simulazione completa flash controller
  - Registri SRIO (MODE_REG, CMD_REG, CTRL_REG, STATUS_REG, FIFO)
  - State machine (START/END request, bit polling)
  - Flash operations (erase → 0xFF, write → file, read → file)
  - Logging dettagliato operazioni
- ✅ **Local Testing**: Server + client su localhost per test senza hardware

## Prossimi Step Possibili

1. **Logging su File**: Salvare log operazioni in file per audit e debug
2. **Test Hardware Reale**: Validare con target fisico e confrontare timing con dwl_fw.cpp
3. **Checksum Verification**: Aggiungere CRC32/MD5 per verifica integrità file
4. **Batch Operations**: Supporto per programmare multipli target in parallelo
5. **Profile Import/Export**: Esportazione profili tra macchine diverse
6. **Wireshark Dissector**: Plugin per decode pacchetti TFTP con `$SRIO:` format

## Troubleshooting

### TFTP Timeout
- **Causa**: Server non raggiungibile o firewall blocca porta 69/6969
- **Soluzione**: Verificare con `ping <ip>`, disabilitare firewall per test, controllare porta con `netstat`

### Flash Write Failed
- **Causa**: QSPI non inizializzato, flash protetto write, bad sector
- **Soluzione**: Verificare log TFTP per sequenza `op_init_qspi()`, controllare STATUS_REG error bits

### Verify Mismatch
- **Causa**: Flash non scritta correttamente, read timeout, bit flip
- **Soluzione**: Ripetere erase+write, aumentare retry count, controllare checksum file sorgente

### Server Not Responding
- **Causa**: Server TFTP emulator non avviato, porta già in uso
- **Soluzione**: `python tools/tftp_receiver.py --port 6969`, controllare con `netstat -an | findstr 6969`


