Skip to content

Commit 35d2271

Browse files
committed
updated to use system clock to determine parameters; broke up large function; made a stand alone timing function
1 parent 7b586d4 commit 35d2271

File tree

2 files changed

+127
-49
lines changed

2 files changed

+127
-49
lines changed

sparkfun_pico/sfe_psram.c

Lines changed: 122 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2828
2929
*/
3030
#include "hardware/address_mapped.h"
31-
#include "hardware/flash.h"
31+
#include "hardware/clocks.h"
3232
#include "hardware/gpio.h"
3333
#include "hardware/regs/addressmap.h"
3434
#include "hardware/spi.h"
@@ -37,6 +37,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3737
#include "hardware/sync.h"
3838
#include "pico/binary_info.h"
3939
#include "pico/flash.h"
40+
#include <math.h>
41+
#include <stdio.h>
42+
#include <stdlib.h>
43+
#include <string.h>
4044

4145
// DETAILS/
4246
//
@@ -49,21 +53,38 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4953
// https://github.com/raspberrypi/pico-sdk-rp2350/issues/12#issuecomment-2055274428
5054
//
5155

52-
/// @brief The setup_psram function - note that this is not in flash
56+
// Details on the PSRAM IC that are used during setup/configuration of PSRAM on SparkFun RP2350 boards.
57+
58+
// max select pulse width = 8us
59+
#define SFE_PSRAM_MAX_SELECT 0.000008f
60+
61+
// min deselect pulse width = 50ns
62+
#define SFE_PSRAM_MIN_DESELECT 0.000000050f
63+
64+
// from psram datasheet - max Freq at 3.3v
65+
#define SFE_PSRAM_MAX_SCK_HZ 109000000.f
66+
67+
// PSRAM SPI command codes
68+
#define PSRAM_CMD_QUAD_END 0xF5
69+
#define PSRAM_CMD_QUAD_ENABLE 0x35
70+
#define PSRAM_CMD_READ_ID 0x9F
71+
#define PSRAM_CMD_RSTEN 0x66
72+
#define PSRAM_CMD_RST 0x99
73+
#define PSRAM_CMD_QUAD_READ 0xEB
74+
#define PSRAM_CMD_QUAD_WRITE 0x38
75+
#define PSRAM_CMD_NOOP 0xFF
76+
77+
#define PSRAM_ID 0x5D
78+
79+
//-----------------------------------------------------------------------------
80+
/// @brief Communicate directly with the PSRAM IC - validate it is present and return the size
5381
///
54-
/// @param psram_cs_pin The pin that the PSRAM is connected to
5582
/// @return size_t The size of the PSRAM
5683
///
57-
static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
84+
/// @note This function expects the CS pin set
85+
static size_t __no_inline_not_in_flash_func(get_psram_size)(void)
5886
{
59-
60-
// Set the PSRAM CS pin in the SDK
61-
gpio_set_function(psram_cs_pin, GPIO_FUNC_XIP_CS1);
62-
63-
// start with zero size
6487
size_t psram_size = 0;
65-
66-
// disable interrupts while we do this.
6788
uint32_t intr_stash = save_and_disable_interrupts();
6889

6990
// Try and read the PSRAM ID via direct_csr.
@@ -79,8 +100,10 @@ static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
79100
// Exit out of QMI in case we've inited already
80101
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS;
81102

82-
// Transmit as quad.
83-
qmi_hw->direct_tx = QMI_DIRECT_TX_OE_BITS | QMI_DIRECT_TX_IWIDTH_VALUE_Q << QMI_DIRECT_TX_IWIDTH_LSB | 0xf5;
103+
// Transmit the command to exit QPI quad mode - read ID as standard SPI
104+
qmi_hw->direct_tx =
105+
QMI_DIRECT_TX_OE_BITS | QMI_DIRECT_TX_IWIDTH_VALUE_Q << QMI_DIRECT_TX_IWIDTH_LSB | PSRAM_CMD_QUAD_END;
106+
84107
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0)
85108
{
86109
}
@@ -94,37 +117,91 @@ static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
94117
uint8_t eid = 0;
95118
for (size_t i = 0; i < 7; i++)
96119
{
97-
if (i == 0)
98-
qmi_hw->direct_tx = 0x9f;
99-
else
100-
qmi_hw->direct_tx = 0xff;
120+
qmi_hw->direct_tx = (i == 0 ? PSRAM_CMD_READ_ID : PSRAM_CMD_NOOP);
101121

102122
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_TXEMPTY_BITS) == 0)
103123
{
104124
}
105-
106125
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0)
107126
{
108127
}
109-
110128
if (i == 5)
111129
kgd = qmi_hw->direct_rx;
112130
else if (i == 6)
113131
eid = qmi_hw->direct_rx;
114132
else
115-
(void)qmi_hw->direct_rx;
133+
(void)qmi_hw->direct_rx; // just read and discard
116134
}
117135

118136
// Disable direct csr.
119137
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS);
120138

121-
// valid?
122-
if (kgd != 0x5D)
139+
// is this the PSRAM we're looking for obi-wan?
140+
if (kgd == PSRAM_ID)
123141
{
124-
restore_interrupts(intr_stash);
125-
return psram_size; // returns 0 size
142+
// PSRAM size
143+
psram_size = 1024 * 1024; // 1 MiB
144+
uint8_t size_id = eid >> 5;
145+
if (eid == 0x26 || size_id == 2)
146+
psram_size *= 8;
147+
else if (size_id == 0)
148+
psram_size *= 2;
149+
else if (size_id == 1)
150+
psram_size *= 4;
126151
}
152+
restore_interrupts(intr_stash);
153+
return psram_size;
154+
}
155+
//-----------------------------------------------------------------------------
156+
/// @brief Update the PSRAM timing configuration based on system clock
157+
///
158+
/// @note This function expects interrupts to be enabled on entry
159+
160+
static void __no_inline_not_in_flash_func(set_psram_timing)(void)
161+
{
162+
// Get secs / cycle for the system clock - get before disabling interrupts
163+
float sysHz = clock_get_hz(clk_sys);
164+
float secsPerCycle = 1.0f / sysHz;
165+
166+
volatile uint8_t clockDivider = (uint8_t)ceil(sysHz / SFE_PSRAM_MAX_SCK_HZ);
167+
168+
uint32_t intr_stash = save_and_disable_interrupts();
169+
170+
// the maxSelect value is defined in units of 64 clock cycles. = PSRAM MAX Select time/secsPerCycle / 64
171+
volatile uint8_t maxSelect = (uint8_t)round(SFE_PSRAM_MAX_SELECT / secsPerCycle / 64.0f);
127172

173+
// minDeselect time - in system clock cycle
174+
volatile uint8_t minDeselect = (uint8_t)round(SFE_PSRAM_MIN_DESELECT / secsPerCycle);
175+
176+
// printf("Max Select: %d, Min Deselect: %d, clock divider: %d\n", maxSelect, minDeselect, clockDivider);
177+
178+
qmi_hw->m[1].timing = QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB | // Break between pages.
179+
3 << QMI_M1_TIMING_SELECT_HOLD_LSB | // Delay releasing CS for 3 extra system cycles.
180+
1 << QMI_M1_TIMING_COOLDOWN_LSB | 1 << QMI_M1_TIMING_RXDELAY_LSB |
181+
maxSelect << QMI_M1_TIMING_MAX_SELECT_LSB | minDeselect << QMI_M1_TIMING_MIN_DESELECT_LSB |
182+
clockDivider << QMI_M1_TIMING_CLKDIV_LSB;
183+
184+
restore_interrupts(intr_stash);
185+
}
186+
//-----------------------------------------------------------------------------
187+
/// @brief The setup_psram function - note that this is not in flash
188+
///
189+
/// @param psram_cs_pin The pin that the PSRAM is connected to
190+
/// @return size_t The size of the PSRAM
191+
///
192+
static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
193+
{
194+
// Set the PSRAM CS pin in the SDK
195+
gpio_set_function(psram_cs_pin, GPIO_FUNC_XIP_CS1);
196+
197+
// start with zero size
198+
size_t psram_size = get_psram_size();
199+
200+
// No PSRAM - no dice
201+
if (psram_size == 0)
202+
return 0;
203+
204+
uint32_t intr_stash = save_and_disable_interrupts();
128205
// Enable quad mode.
129206
qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | QMI_DIRECT_CSR_EN_BITS;
130207

@@ -140,17 +217,16 @@ static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
140217
{
141218
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS;
142219
if (i == 0)
143-
qmi_hw->direct_tx = 0x66;
220+
qmi_hw->direct_tx = PSRAM_CMD_RSTEN;
144221
else if (i == 1)
145-
qmi_hw->direct_tx = 0x99;
222+
qmi_hw->direct_tx = PSRAM_CMD_RST;
146223
else
147-
qmi_hw->direct_tx = 0x35;
224+
qmi_hw->direct_tx = PSRAM_CMD_QUAD_ENABLE;
225+
148226
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0)
149227
{
150228
}
151-
152229
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS);
153-
154230
for (size_t j = 0; j < 20; j++)
155231
asm("nop");
156232

@@ -160,14 +236,12 @@ static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
160236
// Disable direct csr.
161237
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS);
162238

163-
qmi_hw->m[1].timing =
164-
QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB | // Break between pages.
165-
3 << QMI_M1_TIMING_SELECT_HOLD_LSB | // Delay releasing CS for 3 extra system cycles.
166-
1 << QMI_M1_TIMING_COOLDOWN_LSB | 1 << QMI_M1_TIMING_RXDELAY_LSB |
167-
16 << QMI_M1_TIMING_MAX_SELECT_LSB | // In units of 64 system clock cycles. PSRAM says 8us max. 8 / 0.00752 /64
168-
// = 16.62
169-
7 << QMI_M1_TIMING_MIN_DESELECT_LSB | // In units of system clock cycles. PSRAM says 50ns.50 / 7.52 = 6.64
170-
2 << QMI_M1_TIMING_CLKDIV_LSB;
239+
// check our interrupts and setup the timing
240+
restore_interrupts(intr_stash);
241+
set_psram_timing();
242+
243+
// and now stash interrupts again
244+
intr_stash = save_and_disable_interrupts();
171245

172246
qmi_hw->m[1].rfmt = (QMI_M1_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_RFMT_PREFIX_WIDTH_LSB |
173247
QMI_M1_RFMT_ADDR_WIDTH_VALUE_Q << QMI_M1_RFMT_ADDR_WIDTH_LSB |
@@ -178,7 +252,7 @@ static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
178252
QMI_M1_RFMT_PREFIX_LEN_VALUE_8 << QMI_M1_RFMT_PREFIX_LEN_LSB |
179253
QMI_M1_RFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_RFMT_SUFFIX_LEN_LSB);
180254

181-
qmi_hw->m[1].rcmd = 0xeb << QMI_M1_RCMD_PREFIX_LSB | 0 << QMI_M1_RCMD_SUFFIX_LSB;
255+
qmi_hw->m[1].rcmd = PSRAM_CMD_QUAD_READ << QMI_M1_RCMD_PREFIX_LSB | 0 << QMI_M1_RCMD_SUFFIX_LSB;
182256

183257
qmi_hw->m[1].wfmt = (QMI_M1_WFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_WFMT_PREFIX_WIDTH_LSB |
184258
QMI_M1_WFMT_ADDR_WIDTH_VALUE_Q << QMI_M1_WFMT_ADDR_WIDTH_LSB |
@@ -189,27 +263,26 @@ static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
189263
QMI_M1_WFMT_PREFIX_LEN_VALUE_8 << QMI_M1_WFMT_PREFIX_LEN_LSB |
190264
QMI_M1_WFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_WFMT_SUFFIX_LEN_LSB);
191265

192-
qmi_hw->m[1].wcmd = 0x38 << QMI_M1_WCMD_PREFIX_LSB | 0 << QMI_M1_WCMD_SUFFIX_LSB;
193-
194-
// Calculate the size of the PSRAM.
195-
psram_size = 1024 * 1024; // 1 MiB
196-
uint8_t size_id = eid >> 5;
197-
if (eid == 0x26 || size_id == 2)
198-
psram_size *= 8;
199-
else if (size_id == 0)
200-
psram_size *= 2;
201-
else if (size_id == 1)
202-
psram_size *= 4;
266+
qmi_hw->m[1].wcmd = PSRAM_CMD_QUAD_WRITE << QMI_M1_WCMD_PREFIX_LSB | 0 << QMI_M1_WCMD_SUFFIX_LSB;
203267

204268
// Mark that we can write to PSRAM.
205269
xip_ctrl_hw->ctrl |= XIP_CTRL_WRITABLE_M1_BITS;
206270

207271
restore_interrupts(intr_stash);
272+
208273
return psram_size;
209274
}
275+
210276
// public interface
211277

278+
// setup call
212279
size_t sfe_setup_psram(uint32_t psram_cs_pin)
213280
{
214281
return setup_psram(psram_cs_pin);
282+
}
283+
284+
// update timing -- used if the system clock/timing was changed.
285+
void sfe_psram_update_timing(void)
286+
{
287+
set_psram_timing();
215288
}

sparkfun_pico/sfe_psram.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3939
///
4040
size_t sfe_setup_psram(uint32_t psram_cs_pin);
4141

42+
/// @brief The sfe_psram_update_timing function - note that this is not in flash
43+
///
44+
/// @note - updates the PSRAM QSPI timing - call if the system clock is changed after PSRAM is initialized
45+
///
46+
void sfe_psram_update_timing(void);
4247
#endif

0 commit comments

Comments
 (0)