ArthurHeymans/dedipico

Implement dediprog SF600 USB protocol on raspberry pi pico

★ 3Forks 1RustGitHub ↗Compare

README

DediPico

Dediprog SF600 protocol emulation on a Raspberry Pi Pico (RP2040), written in Rust with Embassy. Lets you use flashprog (or flashrom) with a $4 Pico as a USB-to-SPI flash programmer. The flash bus is implemented entirely with RP2040 PIO so the same engine can handle single, dual, and quad SPI phases.

[Host PC: flashprog] --USB--> [Raspberry Pi Pico] --SPI--> [SPI Flash Chip]

Pinout

SPI (directly to flash chip)

Function Pico Pin GPIO
SPI SCK 4 GP2
SPI IO0 / MOSI 5 GP3
SPI IO1 / MISO 6 GP4
SPI IO2 / WP# 7 GP5
SPI IO3 / HOLD# 9 GP6
SPI CS# 10 GP7

The SPI bus uses PIO0. Keep IO0 through IO3 on consecutive GPIOs (GP3-GP6); the PIO programs switch those pins between output and input for 1-1-1, 1-1-2, 1-2-2, 1-1-4, and 1-4-4 transactions.

UART bridge

Function Pico Pin GPIO
UART TX 1 GP0
UART RX 2 GP1

Board control GPIO

Function Pico Pin GPIO Direction
RESET# 11 GP8 open-drain output
POWER_SW# 12 GP9 open-drain output
Board power state 14 GP10 input
Auxiliary state 15 GP11 input

RESET# and POWER_SW# only pull low or release; they never drive high. Hold POWER_SW# low for about 5 s to request power-off on ATX-style boards.

LEDs (active-high)

Function Pico Pin GPIO
LED Pass 25 (onboard) GP25
LED Busy 19 GP14
LED Error 20 GP15

Wiring diagram

            Raspberry Pi Pico
           ┌─────────────────┐
           │               GP2├──── SCK
           │               GP3├──── IO0 / MOSI
           │               GP4├──── IO1 / MISO
           │               GP5├──── IO2 / WP#
           │               GP6├──── IO3 / HOLD#
           │               GP7├──── CS#
           │               GP8├──── RESET# (open-drain)
           │               GP9├──── POWER_SW# (open-drain)
           │              GP10├──── Board power state
           │              GP11├──── Auxiliary state
           │                  │
           │              GP25├──── Pass LED (onboard)
           │              GP14├──── Busy LED
           │              GP15├──── Error LED
           │                  │
           │              3V3 ├──── Flash VCC
           │              GND ├──── Flash GND
           │   USB            │
           └────┘  └──────────┘
                ▲
                │
           to host PC

The wiring above powers a loose 3.3 V flash chip directly from the Pico. For in-system programming, do not connect the Pico's 3V3 pin when the target board already powers the flash: tying two supplies together can damage either board. Always share ground, verify the target I/O voltage, and ensure the target CPU is not driving the SPI bus while DediPico is active. A 1.8 V part requires an external level shifter and a suitable 1.8 V supply.

When idle, the firmware releases SCK, IO0-IO3, and CS# to input/Hi-Z so an in-system mainboard SPI controller can own the bus. This is a convenience for carefully controlled setups, not electrical isolation or contention protection.

Building

rustup target add thumbv6m-none-eabi
cargo build --release

The workspace defaults to the embedded target. Host-only workspace members therefore need an explicit host target; plain cargo test --workspace or cargo clippy --workspace --all-targets will try to build them for RP2040.

cargo test -p dedipico-protocol --target x86_64-unknown-linux-gnu
cargo clippy -p dedipicoctl --target x86_64-unknown-linux-gnu -- -D warnings

Flashing

Install a prebuilt UF2

  1. Open the latest release on GitHub.
  2. Download dedipico.uf2 from the release assets.
  3. Hold BOOTSEL while plugging in the Pico.
  4. Copy dedipico.uf2 to the mounted RPI-RP2 drive.

Build and flash locally

With a debug probe (another Pico running debugprobe, a CMSIS-DAP adapter, etc.):

cargo run --release

Or build the UF2 yourself and copy it to the Pico's mass-storage bootloader:

cargo install elf2uf2-rs --no-default-features
elf2uf2-rs target/thumbv6m-none-eabi/release/dedipico dedipico.uf2
# hold BOOTSEL, plug in Pico, copy dedipico.uf2 to the RPI-RP2 drive

Usage

# detect the device
flashprog -p dediprog

# identify the flash chip
flashprog -p dediprog --flash-name

# read
flashprog -p dediprog -r dump.bin

# write (erases first)
flashprog -p dediprog -w firmware.bin

# quad/dual read path
flashprog -p dediprog:iomode=quad -r dump.bin
flashprog -p dediprog:iomode=dual -r dump.bin

# with explicit parameters
flashprog -p dediprog:spispeed=12M,voltage=3.5V -r dump.bin

# maximum debug output
flashprog -p dediprog -VVV

What it emulates

The firmware presents itself as a Dediprog SF600 running firmware v7.2.22, which selects Protocol V3 in flashprog. It exposes USB VID:PID 0483:DADA as a composite device with only vendor-specific interfaces, so Linux kernel drivers do not claim it and stock flashprog can still use it:

  • Interface 0: DediProg SF600-compatible vendor interface
    • EP0 control — all CMD_* vendor requests
    • EP1 OUT (0x01) — bulk write data from host
    • EP2 IN (0x82) — bulk read data to host
    • EP15 IN (0x8f, interrupt) — DPRAM reservation for EP2's double buffer; its non-bulk type prevents flashprog from selecting it as the read endpoint
  • Interface 1: DediPico auxiliary vendor interface
    • EP3 OUT (0x03) — host to GPIO/UART
    • EP4 IN (0x84) — GPIO/UART to host

Supported commands: TRANSCEIVE, READ, WRITE, SET_VCC, SET_SPI_CLK, SET_TARGET, SET_IO_LED, SET_STANDALONE, IO_MODE, SET_CS, READ_PROG_INFO, READ_EEPROM, GET_UID, SET_VOLTAGE legacy reads, and various stubs (SET_VPP, SET_HOLD, GET_BUTTON, READ_FPGA_VERSION, CHECK_SOCKET, etc.). The USB serial number, programmer information string, and emulated EEPROM serial data are derived from the Pico's onboard flash unique ID. The programmer information suffix and flashprog's EEPROM selection ID are the same decimal number, so -p dediprog:id=SF... matches what the device reports.

Bulk setup requests have a capacity-one follow-up queue, allowing flashprog to submit its verify read while the final programmed pages are still settling. USB packet waits are bounded; an abandoned transfer is cancelled, the flash bus is returned to control requests, and the failed bulk endpoint is stalled rather than leaving the programmer wedged until it is unplugged.

The auxiliary interface uses variable-length packets of up to 64 bytes. Every packet starts with [type, request_id, payload_len]. A nonzero request ID asks the device to return a correlated response; request ID zero is reserved for unsolicited events.

Host-to-device commands and their payloads:

  • 0x01 GPIO_GET_STATE: []
  • 0x02 GPIO_SET_DIRECTION: [mask, directions]
  • 0x03 GPIO_SET_OUTPUT: [mask, values]
  • 0x04 GPIO_PULSE_LOW: [mask, ms_lo, ms_hi]
  • 0x10 UART_SET_BAUD: [baud_le32]
  • 0x11 UART_WRITE: UART bytes

Device-to-host packets:

  • 0x80 RESPONSE: [command, status, data...]
  • 0x81 GPIO_STATE event: [inputs, outputs, directions, caps]
  • 0x90 UART_DATA event: batched UART bytes

GPIO command responses include the four-byte GPIO state as their response data. GPIO bits are bit0 RESET#, bit1 POWER_SW#, bit2 board power state, and bit3 auxiliary state. For RESET#/POWER_SW#, direction=0 releases the pin; direction=1 with output=0 pulls it low.

UART input is accumulated until the 61-byte payload is full or a 1 ms deadline from the first buffered byte expires. GPIO responses are serviced before queued UART data. UART RX and TX use bounded queues so a stalled terminal cannot block board management. Power/reset pulses are acknowledged immediately and released by a timer without blocking other auxiliary work.

dedipicoctl daemon is the only process that claims the auxiliary USB interface. It prints a UART pseudo-terminal and listens on a per-user Unix socket for management commands:

# terminal 1: keep the daemon running
(cd tools/dedipicoctl && cargo run --release --target x86_64-unknown-linux-gnu -- daemon 115200)
# pty: /dev/pts/N
# socket: /run/user/1000/dedipico.sock

# terminal 2: connect a terminal program to the printed PTY
picocom /dev/pts/N

# management commands go through the daemon while UART remains active
(cd tools/dedipicoctl && cargo run --release --target x86_64-unknown-linux-gnu -- state)
(cd tools/dedipicoctl && cargo run --release --target x86_64-unknown-linux-gnu -- reset)
(cd tools/dedipicoctl && cargo run --release --target x86_64-unknown-linux-gnu -- power)
(cd tools/dedipicoctl && cargo run --release --target x86_64-unknown-linux-gnu -- poweroff)

Use --socket PATH on both daemon and client commands to override the socket location. The daemon owns interface 1 while flashprog uses interface 0, so they can remain active at the same time.

Limitations

  • Full Speed USB only (12 Mbit/s vs 480 Mbit/s on a real SF600). A 16 MiB flash read takes ~11 s instead of ~0.3 s. Functionally identical, just slower.
  • All SPI traffic uses the PIO flash engine. iomode=dual and iomode=quad exercise Dediprog/flashprog's 1-1-2, 1-2-2, 1-1-4, and 1-4-4 read paths. Classic 1-1-1 commands and page programs also use PIO rather than the RP2040 PL022 SPI block. Bulk reads use DMA for both the RX FIFO and the per-byte clock tokens.
  • SPI clock speed switching is supported by the PIO clock divider. The bus defaults to 24 MHz, caps requested speeds at 24 MHz, and is reconfigured at runtime when flashprog sends SET_SPI_CLK (e.g. spispeed=12M).
  • No voltage switching or isolation. SET_VCC is acknowledged for protocol compatibility but does not control the Pico's 3V3 pin. Use external switching or leave 3V3 disconnected when the target supplies its own flash rail.

Project structure

src/
├── main.rs                 Entry point and peripheral/USB construction
├── bulk.rs                 Bulk-operation state machine and transfer workers
├── aux_usb.rs              Auxiliary USB/UART worker
├── usb_handler.rs          DediProg control-transfer dispatch
├── protocol.rs             DediProg V2/V3 command parsing
├── spi_flash.rs            PIO + DMA flash driver
├── spi_flash_programs.rs   PIO instruction assembly
├── fast_bulk_in.rs         RP2040 double-buffered EP2 fast path
├── aux.rs                  Board GPIO state and pulse control
├── leds.rs                 GPIO LED driver
└── config.rs               Hardware and transfer constants

shared/dedipico-protocol/   Shared no-std auxiliary protocol and device identity
tools/dedipicoctl/          Host UART/control daemon and CLI

License

MIT

Contributors

ArthurHeymansCopilot

Issues