From bd68aba756b5d0cf9b52daea6ecdf0b512234524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Tue, 9 Jan 2024 02:39:43 +0100 Subject: [PATCH 1/2] Implement PIN_STATE command: Only set pins to output when requested This has the advantage that the pico-serprog can stay attached to the board even while a CPU etc. runs on the board and uses the flash. --- README.md | 5 +++++ main.c | 14 +++++++++++--- pio/spi.pio | 10 ++++++++-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 62c227a..5ede88e 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ Dump a flashchip: flashrom -p serprog:dev=/dev/ttyACM0:115200,spispeed=12M -r foo.bin ``` +pico-serprog only switches the pins to output when requested by flashrom. This +means that you can leave your pico-serprog programmer attached to the flash; +you don't have to detach it before booting the board that you're programming. + + ## License The project is based on the spi_flash example by Raspberry Pi (Trading) Ltd. which is licensed under BSD-3-Clause. diff --git a/main.c b/main.c index f6b907c..13ddf07 100644 --- a/main.c +++ b/main.c @@ -79,7 +79,14 @@ void putu32(uint32_t d) { unsigned char write_buffer[4096]; +void apply_pin_state(const pio_spi_inst_t *spi, bool state) { + pio_spi_enable_outputs(spi->pio, spi->sm, state, PIN_SCK, PIN_MOSI, PIN_MISO); + gpio_set_dir(PIN_CS, state? GPIO_OUT : GPIO_IN); +} + void process(const pio_spi_inst_t *spi, int command) { + static bool pin_state = false; + switch(command) { case S_CMD_NOP: putchar(S_ACK); @@ -155,14 +162,15 @@ void process(const pio_spi_inst_t *spi, int command) { if (freq >= 1) { putchar(S_ACK); putu32(serprog_spi_init(freq)); + apply_pin_state(spi, pin_state); } else { putchar(S_NAK); } } break; case S_CMD_S_PIN_STATE: - //TODO: - getchar(); + pin_state = !!getchar(); + apply_pin_state(spi, pin_state); putchar(S_ACK); break; default: @@ -227,7 +235,7 @@ int main() { // Initialize CS gpio_init(PIN_CS); gpio_put(PIN_CS, 1); - gpio_set_dir(PIN_CS, GPIO_OUT); + gpio_set_dir(PIN_CS, GPIO_IN); // switch to output on S_CMD_S_PIN_STATE spi_offset = pio_add_program(spi.pio, &spi_cpha0_program); serprog_spi_init(1000000); // 1 MHz diff --git a/pio/spi.pio b/pio/spi.pio index 132c661..7dfc940 100644 --- a/pio/spi.pio +++ b/pio/spi.pio @@ -54,9 +54,9 @@ static inline void pio_spi_init(PIO pio, uint sm, uint prog_offs, uint n_bits, sm_config_set_in_shift(&c, false, true, n_bits); sm_config_set_clkdiv(&c, clkdiv); - // MOSI, SCK output are low, MISO is input + // MOSI, SCK output are low, MISO is input -- but we start in input mode pio_sm_set_pins_with_mask(pio, sm, 0, (1u << pin_sck) | (1u << pin_mosi)); - pio_sm_set_pindirs_with_mask(pio, sm, (1u << pin_sck) | (1u << pin_mosi), (1u << pin_sck) | (1u << pin_mosi) | (1u << pin_miso)); + pio_sm_set_pindirs_with_mask(pio, sm, 0, (1u << pin_sck) | (1u << pin_mosi) | (1u << pin_miso)); pio_gpio_init(pio, pin_mosi); pio_gpio_init(pio, pin_miso); pio_gpio_init(pio, pin_sck); @@ -70,6 +70,12 @@ static inline void pio_spi_init(PIO pio, uint sm, uint prog_offs, uint n_bits, pio_sm_init(pio, sm, prog_offs, &c); pio_sm_set_enabled(pio, sm, true); } + +static inline void pio_spi_enable_outputs(PIO pio, uint sm, bool output, uint pin_sck, uint pin_mosi, uint pin_miso) { + uint mask = output? (1u << pin_sck) | (1u << pin_mosi) : 0; + pio_sm_set_pindirs_with_mask(pio, sm, mask, (1u << pin_sck) | (1u << pin_mosi) | (1u << pin_miso)); +} + %} ; SPI with Chip Select From e2867c7e9ca015fae8501d36141ca3ec7dd09ffb Mon Sep 17 00:00:00 2001 From: Funkeleinhorn Date: Tue, 13 Feb 2024 23:54:53 +0100 Subject: [PATCH 2/2] Added support for multiple chip selects Flashprog recently added support for multiple chip selects to serprog: https://review.sourcearcade.org/plugins/gitiles/flashprog/+/ddb6d926783d4f9cbee04c7392718ed8f89daa0e This commit adds support for this feature to pico-serprog. This is useful if you want to drive multiple SPI devices on one bus with pico-serprog as it eliminates the need for reflashing the pico or changing cables. --- README.md | 15 +++++++----- main.c | 69 +++++++++++++++++++++++++++++++++++++------------------ spi.h | 1 + 3 files changed, 57 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 5ede88e..850ba23 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,15 @@ It does not require a custom version of flashrom, just drag the compiled uf2 ont The default pin-out is: -| GPIO | Pico Pin | Function | -|------|----------|----------| -| 1 | 2 | CS | -| 2 | 4 | SCK | -| 3 | 5 | MOSI | -| 4 | 6 | MISO | +| GPIO | Pico Pin | Function | +|------|----------|----------------| +| 1 | 2 | CS_0 (default) | +| 2 | 4 | SCK | +| 3 | 5 | MOSI | +| 4 | 6 | MISO | +| 5 | 7 | CS_1 | +| 6 | 9 | CS_2 | +| 7 | 10 | CS_3 | ## Usage diff --git a/main.c b/main.c index 13ddf07..c40eb7d 100644 --- a/main.c +++ b/main.c @@ -22,34 +22,42 @@ #define PIN_MISO 4 #define PIN_MOSI 3 #define PIN_SCK 2 -#define PIN_CS 1 +#define PIN_CS_0 1 +#define PIN_CS_1 5 +#define PIN_CS_2 6 +#define PIN_CS_3 7 #define BUS_SPI (1 << 3) #define S_SUPPORTED_BUS BUS_SPI #define S_CMD_MAP ( \ - (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_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) \ ) +uint active_cs_pin = 0; +#define NUM_CS_AVAILABLE 4 // Number of usable chip selects +uint8_t cs_pins[NUM_CS_AVAILABLE] = { PIN_CS_0, PIN_CS_1, PIN_CS_2, PIN_CS_3 }; + static uint32_t serprog_spi_init(uint32_t freq); static inline void cs_select(uint cs_pin) { asm volatile("nop \n nop \n nop"); // FIXME - gpio_put(cs_pin, 0); + gpio_put(cs_pins[cs_pin], 0); asm volatile("nop \n nop \n nop"); // FIXME } static inline void cs_deselect(uint cs_pin) { asm volatile("nop \n nop \n nop"); // FIXME - gpio_put(cs_pin, 1); + gpio_put(cs_pins[cs_pin], 1); asm volatile("nop \n nop \n nop"); // FIXME } @@ -81,7 +89,9 @@ unsigned char write_buffer[4096]; void apply_pin_state(const pio_spi_inst_t *spi, bool state) { pio_spi_enable_outputs(spi->pio, spi->sm, state, PIN_SCK, PIN_MOSI, PIN_MISO); - gpio_set_dir(PIN_CS, state? GPIO_OUT : GPIO_IN); + for (int i = 0; i < NUM_CS_AVAILABLE; i++) { + gpio_set_dir(cs_pins[i], state? GPIO_OUT : GPIO_IN); + } } void process(const pio_spi_inst_t *spi, int command) { @@ -138,7 +148,7 @@ void process(const pio_spi_inst_t *spi, int command) { uint32_t wlen = getu24(); uint32_t rlen = getu24(); - cs_select(PIN_CS); + cs_select(active_cs_pin); fread(write_buffer, 1, wlen, stdin); pio_spi_write8_blocking(spi, write_buffer, wlen); @@ -153,7 +163,7 @@ void process(const pio_spi_inst_t *spi, int command) { fflush(stdout); } - cs_deselect(PIN_CS); + cs_deselect(active_cs_pin); } break; case S_CMD_S_SPI_FREQ: @@ -173,6 +183,17 @@ void process(const pio_spi_inst_t *spi, int command) { apply_pin_state(spi, pin_state); putchar(S_ACK); break; + case S_CMD_S_SPI_CS: + { + uint8_t new_cs = getchar(); + if (new_cs < NUM_CS_AVAILABLE) { + active_cs_pin = new_cs; + putchar(S_ACK); + } else { + putchar(S_NAK); + } + break; + } default: putchar(S_NAK); } @@ -182,7 +203,6 @@ void process(const pio_spi_inst_t *spi, int command) { static const pio_spi_inst_t spi = { .pio = pio1, .sm = 0, - .cs_pin = PIN_CS }; static uint spi_offset; @@ -225,7 +245,10 @@ int main() { bi_decl(bi_1pin_with_name(PIN_MISO, "MISO")); bi_decl(bi_1pin_with_name(PIN_MOSI, "MOSI")); bi_decl(bi_1pin_with_name(PIN_SCK, "SCK")); - bi_decl(bi_1pin_with_name(PIN_CS, "CS#")); + bi_decl(bi_1pin_with_name(PIN_CS_0, "CS_0 (default)")); + bi_decl(bi_1pin_with_name(PIN_CS_1, "CS_1")); + bi_decl(bi_1pin_with_name(PIN_CS_2, "CS_2")); + bi_decl(bi_1pin_with_name(PIN_CS_3, "CS_3")); stdio_init_all(); @@ -233,9 +256,11 @@ int main() { // Initialize CS - gpio_init(PIN_CS); - gpio_put(PIN_CS, 1); - gpio_set_dir(PIN_CS, GPIO_IN); // switch to output on S_CMD_S_PIN_STATE + for (int i = 0; i < NUM_CS_AVAILABLE; i++) { + gpio_init(cs_pins[i]); + gpio_put(cs_pins[i], 1); + gpio_set_dir(cs_pins[i], GPIO_IN); // switch to output on S_CMD_S_PIN_STATE + } spi_offset = pio_add_program(spi.pio, &spi_cpha0_program); serprog_spi_init(1000000); // 1 MHz diff --git a/spi.h b/spi.h index b54aaea..49a1a5b 100644 --- a/spi.h +++ b/spi.h @@ -23,3 +23,4 @@ #define S_CMD_O_SPIOP 0x13 /* Perform SPI operation. */ #define S_CMD_S_SPI_FREQ 0x14 /* Set SPI clock frequency */ #define S_CMD_S_PIN_STATE 0x15 /* Enable/disable output drivers */ +#define S_CMD_S_SPI_CS 0x16 /* Set SPI chip select to use */