@@ -28,7 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
29
29
*/
30
30
#include "hardware/address_mapped.h"
31
- #include "hardware/flash .h"
31
+ #include "hardware/clocks .h"
32
32
#include "hardware/gpio.h"
33
33
#include "hardware/regs/addressmap.h"
34
34
#include "hardware/spi.h"
@@ -37,6 +37,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37
37
#include "hardware/sync.h"
38
38
#include "pico/binary_info.h"
39
39
#include "pico/flash.h"
40
+ #include <math.h>
41
+ #include <stdio.h>
42
+ #include <stdlib.h>
43
+ #include <string.h>
40
44
41
45
// DETAILS/
42
46
//
@@ -49,21 +53,38 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49
53
// https://github.com/raspberrypi/pico-sdk-rp2350/issues/12#issuecomment-2055274428
50
54
//
51
55
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
53
81
///
54
- /// @param psram_cs_pin The pin that the PSRAM is connected to
55
82
/// @return size_t The size of the PSRAM
56
83
///
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 )
58
86
{
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
64
87
size_t psram_size = 0 ;
65
-
66
- // disable interrupts while we do this.
67
88
uint32_t intr_stash = save_and_disable_interrupts ();
68
89
69
90
// 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)
79
100
// Exit out of QMI in case we've inited already
80
101
qmi_hw -> direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS ;
81
102
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
+
84
107
while ((qmi_hw -> direct_csr & QMI_DIRECT_CSR_BUSY_BITS ) != 0 )
85
108
{
86
109
}
@@ -94,37 +117,91 @@ static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
94
117
uint8_t eid = 0 ;
95
118
for (size_t i = 0 ; i < 7 ; i ++ )
96
119
{
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 );
101
121
102
122
while ((qmi_hw -> direct_csr & QMI_DIRECT_CSR_TXEMPTY_BITS ) == 0 )
103
123
{
104
124
}
105
-
106
125
while ((qmi_hw -> direct_csr & QMI_DIRECT_CSR_BUSY_BITS ) != 0 )
107
126
{
108
127
}
109
-
110
128
if (i == 5 )
111
129
kgd = qmi_hw -> direct_rx ;
112
130
else if (i == 6 )
113
131
eid = qmi_hw -> direct_rx ;
114
132
else
115
- (void )qmi_hw -> direct_rx ;
133
+ (void )qmi_hw -> direct_rx ; // just read and discard
116
134
}
117
135
118
136
// Disable direct csr.
119
137
qmi_hw -> direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS );
120
138
121
- // valid ?
122
- if (kgd != 0x5D )
139
+ // is this the PSRAM we're looking for obi-wan ?
140
+ if (kgd == PSRAM_ID )
123
141
{
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 ;
126
151
}
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 );
127
172
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 ();
128
205
// Enable quad mode.
129
206
qmi_hw -> direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | QMI_DIRECT_CSR_EN_BITS ;
130
207
@@ -140,17 +217,16 @@ static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
140
217
{
141
218
qmi_hw -> direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS ;
142
219
if (i == 0 )
143
- qmi_hw -> direct_tx = 0x66 ;
220
+ qmi_hw -> direct_tx = PSRAM_CMD_RSTEN ;
144
221
else if (i == 1 )
145
- qmi_hw -> direct_tx = 0x99 ;
222
+ qmi_hw -> direct_tx = PSRAM_CMD_RST ;
146
223
else
147
- qmi_hw -> direct_tx = 0x35 ;
224
+ qmi_hw -> direct_tx = PSRAM_CMD_QUAD_ENABLE ;
225
+
148
226
while ((qmi_hw -> direct_csr & QMI_DIRECT_CSR_BUSY_BITS ) != 0 )
149
227
{
150
228
}
151
-
152
229
qmi_hw -> direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS );
153
-
154
230
for (size_t j = 0 ; j < 20 ; j ++ )
155
231
asm("nop" );
156
232
@@ -160,14 +236,12 @@ static size_t __no_inline_not_in_flash_func(setup_psram)(uint32_t psram_cs_pin)
160
236
// Disable direct csr.
161
237
qmi_hw -> direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS );
162
238
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 ();
171
245
172
246
qmi_hw -> m [1 ].rfmt = (QMI_M1_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_RFMT_PREFIX_WIDTH_LSB |
173
247
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)
178
252
QMI_M1_RFMT_PREFIX_LEN_VALUE_8 << QMI_M1_RFMT_PREFIX_LEN_LSB |
179
253
QMI_M1_RFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_RFMT_SUFFIX_LEN_LSB );
180
254
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 ;
182
256
183
257
qmi_hw -> m [1 ].wfmt = (QMI_M1_WFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_WFMT_PREFIX_WIDTH_LSB |
184
258
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)
189
263
QMI_M1_WFMT_PREFIX_LEN_VALUE_8 << QMI_M1_WFMT_PREFIX_LEN_LSB |
190
264
QMI_M1_WFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_WFMT_SUFFIX_LEN_LSB );
191
265
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 ;
203
267
204
268
// Mark that we can write to PSRAM.
205
269
xip_ctrl_hw -> ctrl |= XIP_CTRL_WRITABLE_M1_BITS ;
206
270
207
271
restore_interrupts (intr_stash );
272
+
208
273
return psram_size ;
209
274
}
275
+
210
276
// public interface
211
277
278
+ // setup call
212
279
size_t sfe_setup_psram (uint32_t psram_cs_pin )
213
280
{
214
281
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 ();
215
288
}
0 commit comments