; rp2040-psram ; ; Copyright © 2023 Ian Scott ; ; Permission is hereby granted, free of charge, to any person obtaining a copy of ; this software and associated documentation files (the “Software”), to deal in ; the Software without restriction, including without limitation the rights to ; use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies ; of the Software, and to permit persons to whom the Software is furnished to do ; so, subject to the following conditions: ; ; The above copyright notice and this permission notice shall be included in all ; copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ; SOFTWARE. ; SPI, customized as the PSRAM chips like to implement it: ; - Data is always written first, then optionally read ; Depending on PCB layout, introduce fudge factor: ; - Reads in high speed mode need an extra clock cycle to synchronize ; - Reads are done on the falling edge of SCK when > 83MHz .program spi_psram_fudge .side_set 2 ; sideset bit 1 is SCK, bit 0 is CS begin: out x, 8 side 0b01 ; x = number of bits to output. CS deasserted out y, 8 side 0b01 ; y = number of bits to input jmp x--, writeloop side 0b01 ; Pre-decement x by 1 so loop has correct number of iterations writeloop: ; Put the data and rise the clock!!!! out pins, 1 side 0b00 ; Write value on pin on lower clock. CS asserted jmp x--, writeloop side 0b10 ; Raise clock: this is when PSRAM reads the value. Loop if we have more to write jmp !y, begin side 0b00 ; If this is a write-only operation, jump back to beginning nop side 0b10 ; Fudge factor of extra clock cycle; the PSRAM needs 1 extra for output to start appearing jmp readloop_mid side 0b00 ; Jump to middle of readloop to decrement y and get right clock phase readloop: in pins, 1 side 0b00 ; Read value on pin, lower clock. Datasheet says to read on falling edge > 83MHz readloop_mid: jmp y--, readloop side 0b10 ; Raise clock. Loop if we have more to read % c-sdk { #include "hardware/gpio.h" static inline void pio_spi_psram_cs_init(bool init_done, PIO pio, uint sm, uint prog_offs, uint n_bits, float clkdiv, bool fudge, uint pin_cs, uint pin_mosi, uint pin_miso) { pio_sm_set_enabled(pio, sm, false); pio_sm_clear_fifos(pio, sm); pio_sm_restart(pio, sm); pio_sm_config c; c = spi_psram_fudge_program_get_default_config(prog_offs); sm_config_set_out_pins(&c, pin_mosi, 1); sm_config_set_in_pins(&c, pin_miso); sm_config_set_sideset_pins(&c, pin_cs); sm_config_set_out_shift(&c, false, true, n_bits); sm_config_set_in_shift(&c, false, true, n_bits); sm_config_set_clkdiv(&c, clkdiv); pio_sm_set_consecutive_pindirs(pio, sm, pin_cs, 2, true); pio_sm_set_consecutive_pindirs(pio, sm, pin_mosi, 1, true); pio_sm_set_consecutive_pindirs(pio, sm, pin_miso, 1, false); pio_gpio_init(pio, pin_mosi); pio_gpio_init(pio, pin_miso); pio_gpio_init(pio, pin_cs); pio_gpio_init(pio, pin_cs + 1); hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); if (!init_done) { pio_sm_init(pio, sm, prog_offs, &c); } pio_sm_set_enabled(pio, sm, true); } %} .program qspi_psram .side_set 2 begin: out x, 8 side 0b01 ; x = number of nibbles to output. CS deasserted out y, 8 side 0b01 ; y = number of nibbles to input jmp x--, writeloop side 0b01 ; Pre-decement x by 1 so loop has correct number of iterations writeloop: out pins, 4 side 0b00 ; Write value on pins on lower clock. CS asserted jmp x--, writeloop side 0b10 ; Raise clock: this is when PSRAM reads the value. Loop if we have more to write jmp !y, begin side 0b00 ; If this is a write-only operation, jump back to beginning nop side 0b10 ; 1 nop side 0b00 nop side 0b10 ; 2 nop side 0b00 nop side 0b10 ; 3 nop side 0b00 nop side 0b10 ; 4 nop side 0b00 ; data to read is put now by PSRAM nop side 0b10 set pindirs 0 side 0b00 ; Fudge factor of extra clock cycle; the PSRAM needs 1 extra for output to start appearing jmp readloop_mid side 0b10 ; Jump to middle of readloop to decrement y and get right clock phase readloop: in pins, 4 side 0b10 ; Read value on s, lower clock. Datasheet says to read on falling edge > 83MHz readloop_mid: jmp y--, readloop side 0b00 ; Raise clock. Loop if we have more to read set pindirs 0xF side 0b01 ; set pindirs 0 side 0b00 ; data to read is put now by PSRAM ; nop side 0b10 ; Fudge factor of extra clock cycle; the PSRAM needs 1 extra for output to start appearing ; jmp readloop_mid side 0b00 ; Jump to middle of readloop to decrement y and get right clock phase ;readloop: ; in pins, 4 side 0b00 ; Read value on s, lower clock. Datasheet says to read on falling edge > 83MHz ;readloop_mid: ; jmp y--, readloop side 0b10 ; Raise clock. Loop if we have more to read ; set pindirs 0xF side 0b00 % c-sdk { #include "hardware/gpio.h" static inline void pio_qspi_psram_cs_init(bool init_done, PIO pio, uint sm, uint prog_offs, uint n_bits, float clkdiv, uint pin_cs, uint pin_sio0) { pio_sm_set_enabled(pio, sm, false); pio_sm_clear_fifos(pio, sm); pio_sm_restart(pio, sm); pio_sm_config c; c = qspi_psram_program_get_default_config(prog_offs); sm_config_set_out_pins(&c, pin_sio0, 4); sm_config_set_in_pins(&c, pin_sio0); sm_config_set_set_pins(&c, pin_sio0, 4); sm_config_set_sideset_pins(&c, pin_cs); sm_config_set_out_shift(&c, false, true, n_bits); sm_config_set_in_shift(&c, false, true, n_bits); sm_config_set_clkdiv(&c, clkdiv); pio_sm_set_consecutive_pindirs(pio, sm, pin_cs, 2, true); pio_sm_set_consecutive_pindirs(pio, sm, pin_sio0, 4, true); pio_gpio_init(pio, pin_sio0); pio_gpio_init(pio, pin_sio0 + 1); pio_gpio_init(pio, pin_sio0 + 2); pio_gpio_init(pio, pin_sio0 + 3); pio_gpio_init(pio, pin_cs); pio_gpio_init(pio, pin_cs + 1); hw_set_bits(&pio->input_sync_bypass, 0xfu << pin_sio0); if (!init_done) { pio_sm_init(pio, sm, prog_offs, &c); } pio_sm_set_enabled(pio, sm, true); } %}