2021-04-03 18:36:40 +02:00
|
|
|
/**
|
2021-08-07 01:04:08 +02:00
|
|
|
* Copyright (C) 2021, Mate Kukri <km@mkukri.xyz>
|
2024-01-15 18:28:00 +01:00
|
|
|
* Copyright (C) 2023, 2024, Riku Viitanen <riku.viitanen@protonmail.com>
|
2021-08-07 01:04:08 +02:00
|
|
|
* Based on "pico-serprog" by Thomas Roth <code@stacksmashing.net>
|
2021-04-03 18:36:40 +02:00
|
|
|
*
|
|
|
|
* Licensed under GPLv3
|
2021-08-07 01:04:08 +02:00
|
|
|
*
|
|
|
|
* Also based on stm32-vserprog:
|
2021-04-03 18:36:40 +02:00
|
|
|
* https://github.com/dword1511/stm32-vserprog
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2024-01-27 23:05:10 +01:00
|
|
|
#define DESCRIPTION "SPI flash chip programmer using Flashprog's serprog protocol"
|
2023-11-28 20:26:21 +01:00
|
|
|
#define WEBSITE "https://codeberg.org/Riku_V/pico-serprog/"
|
|
|
|
|
2021-04-03 18:36:40 +02:00
|
|
|
#include "pico/stdlib.h"
|
2023-11-28 20:26:21 +01:00
|
|
|
#include "pico/binary_info.h"
|
2021-08-07 02:34:05 +02:00
|
|
|
#include "hardware/spi.h"
|
2021-08-07 03:42:16 +02:00
|
|
|
#include "tusb.h"
|
2021-08-07 02:34:05 +02:00
|
|
|
#include "serprog.h"
|
2021-04-03 18:36:40 +02:00
|
|
|
|
2021-08-07 03:42:16 +02:00
|
|
|
#define CDC_ITF 0 // USB CDC interface no
|
|
|
|
|
2021-08-07 02:34:05 +02:00
|
|
|
#define SPI_IF spi0 // Which PL022 to use
|
|
|
|
#define SPI_BAUD 12000000 // Default baudrate (12 MHz)
|
2024-01-15 18:28:00 +01:00
|
|
|
#define SPI_CS_0 5 // The default CS pin
|
2021-08-07 02:34:05 +02:00
|
|
|
#define SPI_MISO 4
|
|
|
|
#define SPI_MOSI 3
|
|
|
|
#define SPI_SCK 2
|
|
|
|
|
2024-01-15 18:28:00 +01:00
|
|
|
uint8_t spi_enabled = 0;
|
|
|
|
uint cs_pin = SPI_CS_0;
|
|
|
|
#define NUM_CS_AVAILABLE 4 // Number of usable chip selects
|
|
|
|
|
2023-11-29 16:11:55 +01:00
|
|
|
static const char progname[16] = "pico-serprog";
|
|
|
|
|
|
|
|
/* Map of supported serprog commands */
|
|
|
|
static const uint32_t cmdmap[8] = {
|
2024-01-15 18:28:00 +01:00
|
|
|
(1 << S_CMD_NOP) |
|
|
|
|
(1 << S_CMD_Q_IFACE) |
|
|
|
|
(1 << S_CMD_Q_CMDMAP) |
|
|
|
|
(1 << S_CMD_Q_PGMNAME) |
|
|
|
|
(1 << S_CMD_Q_SERBUF) |
|
|
|
|
(1 << S_CMD_Q_BUSTYPE) |
|
|
|
|
(1 << S_CMD_SYNCNOP) |
|
|
|
|
(1 << S_CMD_O_SPIOP) |
|
|
|
|
(1 << S_CMD_S_BUSTYPE) |
|
|
|
|
(1 << S_CMD_S_SPI_FREQ) |
|
|
|
|
(1 << S_CMD_S_PIN_STATE) |
|
|
|
|
(1 << S_CMD_S_SPI_CS)
|
2023-11-29 16:11:55 +01:00
|
|
|
};
|
|
|
|
|
2023-11-28 19:32:42 +01:00
|
|
|
|
2024-01-27 22:55:30 +01:00
|
|
|
static void use_cs(uint pin)
|
2024-01-15 18:28:00 +01:00
|
|
|
{
|
|
|
|
gpio_put(pin, 1);
|
|
|
|
gpio_set_dir(pin, GPIO_OUT);
|
|
|
|
gpio_set_drive_strength(pin, GPIO_DRIVE_STRENGTH_12MA);
|
|
|
|
}
|
|
|
|
|
2024-01-27 22:55:30 +01:00
|
|
|
static void pullup_cs(uint pin)
|
|
|
|
{
|
|
|
|
gpio_set_dir(pin, GPIO_IN);
|
|
|
|
gpio_pull_up(pin);
|
|
|
|
}
|
|
|
|
|
2021-08-07 02:34:05 +02:00
|
|
|
static void enable_spi(uint baud)
|
|
|
|
{
|
2023-12-23 13:58:12 +01:00
|
|
|
#ifdef PICO_DEFAULT_LED_PIN
|
2023-11-28 19:32:42 +01:00
|
|
|
// Setup status LED
|
2023-12-23 13:58:12 +01:00
|
|
|
gpio_init(PICO_DEFAULT_LED_PIN);
|
|
|
|
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
|
|
|
#endif
|
2023-11-28 19:32:42 +01:00
|
|
|
|
2024-01-27 22:55:30 +01:00
|
|
|
/* Setup default CS as output, others as inputs with pull-ups */
|
|
|
|
for (uint8_t i = SPI_CS_0+1; i<SPI_CS_0+NUM_CS_AVAILABLE; i++) {
|
|
|
|
gpio_init(i);
|
|
|
|
pullup_cs(i);
|
|
|
|
}
|
|
|
|
gpio_init(cs_pin);
|
|
|
|
use_cs(cs_pin);
|
2021-08-07 02:34:05 +02:00
|
|
|
|
|
|
|
// Setup PL022
|
|
|
|
spi_init(SPI_IF, baud);
|
|
|
|
gpio_set_function(SPI_MISO, GPIO_FUNC_SPI);
|
|
|
|
gpio_set_function(SPI_MOSI, GPIO_FUNC_SPI);
|
|
|
|
gpio_set_function(SPI_SCK, GPIO_FUNC_SPI);
|
2023-11-29 22:39:58 +01:00
|
|
|
|
|
|
|
gpio_set_drive_strength(SPI_MISO, GPIO_DRIVE_STRENGTH_12MA);
|
|
|
|
gpio_set_drive_strength(SPI_MOSI, GPIO_DRIVE_STRENGTH_12MA);
|
|
|
|
gpio_set_drive_strength(SPI_SCK, GPIO_DRIVE_STRENGTH_12MA);
|
2024-01-15 18:28:00 +01:00
|
|
|
|
|
|
|
spi_enabled = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void disable_pin(uint pin)
|
|
|
|
{
|
|
|
|
gpio_init(pin); // Set pin to SIO input
|
|
|
|
gpio_set_pulls(pin, 0, 0); // Disable all pulls
|
2021-08-07 02:34:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void disable_spi()
|
|
|
|
{
|
2024-01-27 22:55:30 +01:00
|
|
|
for (uint8_t i=0; i<NUM_CS_AVAILABLE; i++)
|
|
|
|
disable_pin(SPI_CS_0 + i);
|
2024-01-15 18:28:00 +01:00
|
|
|
disable_pin(SPI_MISO);
|
|
|
|
disable_pin(SPI_MOSI);
|
|
|
|
disable_pin(SPI_SCK);
|
2021-08-07 02:34:05 +02:00
|
|
|
|
|
|
|
// Disable SPI peripheral
|
|
|
|
spi_deinit(SPI_IF);
|
2024-01-15 18:28:00 +01:00
|
|
|
|
|
|
|
spi_enabled = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_cs_pin(uint8_t cs)
|
|
|
|
{
|
|
|
|
cs += SPI_CS_0;
|
|
|
|
if (spi_enabled) {
|
|
|
|
if (cs_pin != cs) {
|
2024-01-27 22:55:30 +01:00
|
|
|
pullup_cs(cs_pin);
|
|
|
|
use_cs(cs);
|
2024-01-15 18:28:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
cs_pin = cs;
|
2021-08-07 02:34:05 +02:00
|
|
|
}
|
2021-04-03 18:36:40 +02:00
|
|
|
|
2021-08-07 01:04:08 +02:00
|
|
|
static inline void cs_select(uint cs_pin)
|
|
|
|
{
|
2021-04-03 18:36:40 +02:00
|
|
|
asm volatile("nop \n nop \n nop"); // FIXME
|
|
|
|
gpio_put(cs_pin, 0);
|
|
|
|
asm volatile("nop \n nop \n nop"); // FIXME
|
|
|
|
}
|
|
|
|
|
2021-08-07 01:04:08 +02:00
|
|
|
static inline void cs_deselect(uint cs_pin)
|
|
|
|
{
|
2021-04-03 18:36:40 +02:00
|
|
|
asm volatile("nop \n nop \n nop"); // FIXME
|
|
|
|
gpio_put(cs_pin, 1);
|
|
|
|
asm volatile("nop \n nop \n nop"); // FIXME
|
|
|
|
}
|
|
|
|
|
2021-08-07 03:42:16 +02:00
|
|
|
static void wait_for_read(void)
|
2021-08-07 01:04:08 +02:00
|
|
|
{
|
2021-08-07 03:42:16 +02:00
|
|
|
do
|
|
|
|
tud_task();
|
|
|
|
while (!tud_cdc_n_available(CDC_ITF));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void readbytes_blocking(void *b, uint32_t len)
|
|
|
|
{
|
|
|
|
while (len) {
|
|
|
|
wait_for_read();
|
|
|
|
uint32_t r = tud_cdc_n_read(CDC_ITF, b, len);
|
|
|
|
b += r;
|
|
|
|
len -= r;
|
|
|
|
}
|
2021-08-07 01:04:08 +02:00
|
|
|
}
|
|
|
|
|
2021-08-07 03:42:16 +02:00
|
|
|
static inline uint8_t readbyte_blocking(void)
|
2021-08-07 01:04:08 +02:00
|
|
|
{
|
2021-08-07 03:42:16 +02:00
|
|
|
wait_for_read();
|
|
|
|
uint8_t b;
|
|
|
|
tud_cdc_n_read(CDC_ITF, &b, 1);
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wait_for_write(void)
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
tud_task();
|
|
|
|
} while (!tud_cdc_n_write_available(CDC_ITF));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void sendbytes_blocking(const void *b, uint32_t len)
|
|
|
|
{
|
|
|
|
while (len) {
|
|
|
|
wait_for_write();
|
|
|
|
uint32_t w = tud_cdc_n_write(CDC_ITF, b, len);
|
|
|
|
b += w;
|
|
|
|
len -= w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void sendbyte_blocking(uint8_t b)
|
|
|
|
{
|
|
|
|
wait_for_write();
|
|
|
|
tud_cdc_n_write(CDC_ITF, &b, 1);
|
2021-04-03 18:36:40 +02:00
|
|
|
}
|
|
|
|
|
2023-11-28 19:32:42 +01:00
|
|
|
static void process_command(uint8_t cmd, uint *baud) {
|
|
|
|
switch (cmd) {
|
|
|
|
case S_CMD_NOP:
|
|
|
|
sendbyte_blocking(S_ACK);
|
|
|
|
break;
|
|
|
|
case S_CMD_Q_IFACE:
|
|
|
|
sendbyte_blocking(S_ACK);
|
|
|
|
sendbyte_blocking(0x01);
|
|
|
|
sendbyte_blocking(0x00);
|
|
|
|
break;
|
|
|
|
case S_CMD_Q_CMDMAP:
|
2023-11-29 16:11:55 +01:00
|
|
|
sendbyte_blocking(S_ACK);
|
|
|
|
sendbytes_blocking((uint8_t *) cmdmap, sizeof cmdmap);
|
|
|
|
break;
|
2023-11-28 19:32:42 +01:00
|
|
|
case S_CMD_Q_PGMNAME:
|
2023-11-29 16:11:55 +01:00
|
|
|
sendbyte_blocking(S_ACK);
|
|
|
|
sendbytes_blocking(progname, sizeof progname);
|
|
|
|
break;
|
2023-11-28 19:32:42 +01:00
|
|
|
case S_CMD_Q_SERBUF:
|
|
|
|
sendbyte_blocking(S_ACK);
|
|
|
|
sendbyte_blocking(0xFF);
|
|
|
|
sendbyte_blocking(0xFF);
|
|
|
|
break;
|
|
|
|
case S_CMD_Q_BUSTYPE:
|
|
|
|
sendbyte_blocking(S_ACK);
|
|
|
|
sendbyte_blocking((1 << 3)); // BUS_SPI
|
|
|
|
break;
|
|
|
|
case S_CMD_SYNCNOP:
|
|
|
|
sendbyte_blocking(S_NAK);
|
|
|
|
sendbyte_blocking(S_ACK);
|
|
|
|
break;
|
|
|
|
case S_CMD_S_BUSTYPE:
|
|
|
|
// If SPI is among the requsted bus types we succeed, fail otherwise
|
|
|
|
if((uint8_t) readbyte_blocking() & (1 << 3))
|
2021-08-07 03:42:16 +02:00
|
|
|
sendbyte_blocking(S_ACK);
|
2023-11-28 19:32:42 +01:00
|
|
|
else
|
2021-08-07 03:42:16 +02:00
|
|
|
sendbyte_blocking(S_NAK);
|
2023-11-28 19:32:42 +01:00
|
|
|
break;
|
|
|
|
case S_CMD_O_SPIOP:
|
|
|
|
{
|
|
|
|
static uint8_t buf[4096];
|
2021-08-07 02:34:05 +02:00
|
|
|
|
2023-11-28 19:32:42 +01:00
|
|
|
uint32_t wlen = 0;
|
|
|
|
readbytes_blocking(&wlen, 3);
|
|
|
|
uint32_t rlen = 0;
|
|
|
|
readbytes_blocking(&rlen, 3);
|
2021-08-07 01:04:08 +02:00
|
|
|
|
2024-01-15 18:28:00 +01:00
|
|
|
cs_select(cs_pin);
|
2021-04-03 18:36:40 +02:00
|
|
|
|
2023-11-28 19:32:42 +01:00
|
|
|
while (wlen) {
|
|
|
|
uint32_t cur = MIN(wlen, sizeof buf);
|
|
|
|
readbytes_blocking(buf, cur);
|
|
|
|
spi_write_blocking(SPI_IF, buf, cur);
|
|
|
|
wlen -= cur;
|
|
|
|
}
|
2021-08-07 01:04:08 +02:00
|
|
|
|
2023-11-28 19:32:42 +01:00
|
|
|
sendbyte_blocking(S_ACK);
|
2021-04-03 18:36:40 +02:00
|
|
|
|
2023-11-28 19:32:42 +01:00
|
|
|
while (rlen) {
|
|
|
|
uint32_t cur = MIN(rlen, sizeof buf);
|
|
|
|
spi_read_blocking(SPI_IF, 0, buf, cur);
|
|
|
|
sendbytes_blocking(buf, cur);
|
|
|
|
rlen -= cur;
|
2021-04-03 18:36:40 +02:00
|
|
|
}
|
2023-11-28 19:32:42 +01:00
|
|
|
|
2024-01-15 18:28:00 +01:00
|
|
|
cs_deselect(cs_pin);
|
2023-11-28 19:32:42 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case S_CMD_S_SPI_FREQ:
|
|
|
|
{
|
|
|
|
uint32_t want_baud;
|
|
|
|
readbytes_blocking(&want_baud, 4);
|
|
|
|
if (want_baud) {
|
|
|
|
// Set frequence
|
|
|
|
*baud = spi_set_baudrate(SPI_IF, want_baud);
|
|
|
|
// Send back actual value
|
|
|
|
sendbyte_blocking(S_ACK);
|
|
|
|
sendbytes_blocking(baud, 4);
|
|
|
|
} else {
|
|
|
|
// 0 Hz is reserved
|
|
|
|
sendbyte_blocking(S_NAK);
|
2021-08-07 02:34:05 +02:00
|
|
|
}
|
|
|
|
break;
|
2021-08-07 01:04:08 +02:00
|
|
|
}
|
2023-11-28 19:32:42 +01:00
|
|
|
case S_CMD_S_PIN_STATE:
|
|
|
|
if (readbyte_blocking())
|
|
|
|
enable_spi(*baud);
|
|
|
|
else
|
|
|
|
disable_spi();
|
|
|
|
sendbyte_blocking(S_ACK);
|
|
|
|
break;
|
2024-01-15 18:28:00 +01:00
|
|
|
case S_CMD_S_SPI_CS:
|
|
|
|
uint8_t cs_index = readbyte_blocking();
|
|
|
|
if (cs_index < NUM_CS_AVAILABLE) {
|
|
|
|
set_cs_pin(cs_index);
|
|
|
|
sendbyte_blocking(S_ACK);
|
|
|
|
} else {
|
|
|
|
sendbyte_blocking(S_NAK);
|
|
|
|
}
|
|
|
|
break;
|
2023-11-28 19:32:42 +01:00
|
|
|
default:
|
|
|
|
sendbyte_blocking(S_NAK);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tud_cdc_n_write_flush(CDC_ITF);
|
2023-12-23 13:58:12 +01:00
|
|
|
#ifdef PICO_DEFAULT_LED_PIN
|
|
|
|
gpio_put(PICO_DEFAULT_LED_PIN, 0);
|
|
|
|
#endif
|
2023-11-28 19:32:42 +01:00
|
|
|
}
|
2021-08-07 03:42:16 +02:00
|
|
|
|
2023-11-28 19:32:42 +01:00
|
|
|
static void command_loop(void)
|
|
|
|
{
|
|
|
|
uint baud = spi_get_baudrate(SPI_IF);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
uint8_t cmd = readbyte_blocking();
|
2023-12-23 13:58:12 +01:00
|
|
|
#ifdef PICO_DEFAULT_LED_PIN
|
|
|
|
gpio_put(PICO_DEFAULT_LED_PIN, 1);
|
|
|
|
#endif
|
2023-11-28 19:32:42 +01:00
|
|
|
process_command(cmd, &baud);
|
2023-12-23 13:58:12 +01:00
|
|
|
#ifdef PICO_DEFAULT_LED_PIN
|
|
|
|
gpio_put(PICO_DEFAULT_LED_PIN, 0);
|
|
|
|
#endif
|
2021-04-03 18:36:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-07 02:34:05 +02:00
|
|
|
int main()
|
|
|
|
{
|
2023-11-28 20:26:21 +01:00
|
|
|
// Metadata for picotool
|
|
|
|
bi_decl(bi_program_description(DESCRIPTION));
|
|
|
|
bi_decl(bi_program_url(WEBSITE));
|
2023-12-23 13:58:12 +01:00
|
|
|
#ifdef PICO_DEFAULT_LED_PIN
|
|
|
|
bi_decl(bi_1pin_with_name(PICO_DEFAULT_LED_PIN, "Activity LED"));
|
|
|
|
#endif
|
2023-11-28 20:26:21 +01:00
|
|
|
bi_decl(bi_1pin_with_name(SPI_MISO, "MISO"));
|
|
|
|
bi_decl(bi_1pin_with_name(SPI_MOSI, "MOSI"));
|
|
|
|
bi_decl(bi_1pin_with_name(SPI_SCK, "SCK"));
|
2024-01-15 18:28:00 +01:00
|
|
|
bi_decl(bi_1pin_with_name(SPI_CS_0, "CS_0 (default)"));
|
|
|
|
bi_decl(bi_1pin_with_name(SPI_CS_0+1, "CS_1"));
|
|
|
|
bi_decl(bi_1pin_with_name(SPI_CS_0+2, "CS_2"));
|
|
|
|
bi_decl(bi_1pin_with_name(SPI_CS_0+3, "CS_3"));
|
2023-11-28 20:26:21 +01:00
|
|
|
|
2021-08-07 03:42:16 +02:00
|
|
|
// Setup USB
|
|
|
|
tusb_init();
|
|
|
|
// Setup PL022 SPI
|
2021-08-07 02:34:05 +02:00
|
|
|
enable_spi(SPI_BAUD);
|
2021-08-07 03:42:16 +02:00
|
|
|
|
2021-08-07 02:34:05 +02:00
|
|
|
command_loop();
|
2021-04-03 18:36:40 +02:00
|
|
|
}
|