32
32
#include " core_esp8266_features.h"
33
33
34
34
#include " spi_utils.h"
35
+ #include " spi_flash_defs.h"
35
36
36
37
extern " C" uint32_t Wait_SPI_Idle (SpiFlashChip *fc);
37
38
@@ -51,12 +52,12 @@ namespace experimental {
51
52
static SpiOpResult PRECACHE_ATTR
52
53
_SPICommand (volatile uint32_t spiIfNum,
53
54
uint32_t spic,uint32_t spiu,uint32_t spiu1,uint32_t spiu2,
54
- uint32_t *data,uint32_t writeWords,uint32_t readWords)
55
+ uint32_t *data,uint32_t writeWords,uint32_t readWords, uint32_t pre_cmd )
55
56
{
56
57
if (spiIfNum>1 )
57
58
return SPI_RESULT_ERR;
58
59
59
- // force SPI register access via base+offset.
60
+ // force SPI register access via base+offset.
60
61
// Prevents loading individual address constants from flash.
61
62
uint32_t *spibase = (uint32_t *)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD));
62
63
#define SPIREG (reg ) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD)))))
@@ -65,6 +66,7 @@ _SPICommand(volatile uint32_t spiIfNum,
65
66
// Everything defined here must be volatile or the optimizer can
66
67
// treat them as constants, resulting in the flash reads we're
67
68
// trying to avoid
69
+ SpiFlashOpResult (* volatile SPI_write_enablep)(SpiFlashChip *) = SPI_write_enable;
68
70
uint32_t (* volatile Wait_SPI_Idlep)(SpiFlashChip *) = Wait_SPI_Idle;
69
71
volatile SpiFlashChip *fchip=flashchip;
70
72
volatile uint32_t spicmdusr=SPICMDUSR;
@@ -77,15 +79,30 @@ _SPICommand(volatile uint32_t spiIfNum,
77
79
PRECACHE_START ();
78
80
Wait_SPI_Idlep ((SpiFlashChip *)fchip);
79
81
}
80
-
82
+
81
83
// preserve essential controller state such as incoming/outgoing
82
84
// data lengths and IO mode.
83
85
uint32_t oldSPI0U = SPIREG (SPI0U);
84
86
uint32_t oldSPI0U2= SPIREG (SPI0U2);
85
87
uint32_t oldSPI0C = SPIREG (SPI0C);
86
88
87
- // SPI0S &= ~(SPISE|SPISBE|SPISSE|SPISCD);
88
89
SPIREG (SPI0C) = spic;
90
+
91
+ if (SPI_FLASH_CMD_WREN == pre_cmd) {
92
+ // See SPI_write_enable comments in esp8266_undocumented.h
93
+ SPI_write_enablep ((SpiFlashChip *)fchip);
94
+ } else
95
+ if (pre_cmd) {
96
+ // Send prefix cmd w/o data - sends 8 bits. eg. Volatile SR Write Enable, 0x50
97
+ SPIREG (SPI0U) = (spiu & ~(SPIUMOSI|SPIUMISO));
98
+ SPIREG (SPI0U1) = 0 ;
99
+ SPIREG (SPI0U2) = (spiu2 & ~0xFFFFu ) | pre_cmd;
100
+
101
+ SPIREG (SPI0CMD) = spicmdusr; // Send cmd
102
+ while ((SPIREG (SPI0CMD) & spicmdusr));
103
+ }
104
+
105
+ // SPI0S &= ~(SPISE|SPISBE|SPISSE|SPISCD);
89
106
SPIREG (SPI0U) = spiu;
90
107
SPIREG (SPI0U1)= spiu1;
91
108
SPIREG (SPI0U2)= spiu2;
@@ -117,11 +134,22 @@ _SPICommand(volatile uint32_t spiIfNum,
117
134
SPIREG (SPI0U) = oldSPI0U;
118
135
SPIREG (SPI0U2)= oldSPI0U2;
119
136
SPIREG (SPI0C) = oldSPI0C;
120
-
121
- PRECACHE_END ();
137
+
122
138
if (!spiIfNum) {
139
+ // w/o a call to Wait_SPI_Idlep, 'Exception 0' or other exceptions (saw
140
+ // 28) may occur later after returning to iCache code. This issue was
141
+ // observed with non-volatile status register writes.
142
+ //
143
+ // My guess is: Returning too soon to uncached iCache executable space. An
144
+ // iCache read may not complete properly because the Flash or SPI
145
+ // interface is still busy with the last write operation. In such a case,
146
+ // I expect new reads from iROM to result in zeros. This would explain
147
+ // the Exception 0 for code, and Exception 20, 28, and 29 where a literal
148
+ // was misread as 0 and then used as a pointer.
149
+ Wait_SPI_Idlep ((SpiFlashChip *)fchip);
123
150
xt_wsr_ps (saved_ps);
124
151
}
152
+ PRECACHE_END ();
125
153
return (timeout>0 ? SPI_RESULT_OK : SPI_RESULT_TIMEOUT);
126
154
}
127
155
@@ -139,12 +167,37 @@ _SPICommand(volatile uint32_t spiIfNum,
139
167
* miso_bits
140
168
* Number of bits to read from the SPI bus after the outgoing
141
169
* data has been sent.
170
+ * pre_cmd
171
+ * A few SPI Flash commands require enable commands to immediately preceed
172
+ * them. Since two calls to SPI0Command from ICACHE memory most likely would
173
+ * be separated by SPI Flash read request for iCache, use this option to
174
+ * supply a prefix command, 8-bits w/o read or write data.
175
+ *
176
+ * Case in point from the GD25Q32E datasheet: "The Write Enable for Volatile
177
+ * Status Register command must be issued prior to a Write Status Register
178
+ * command and any other commands can’t be inserted between them."
142
179
*
143
180
* Note: This code has only been tested with SPI bus 0, but should work
144
181
* equally well with other buses. The ESP8266 has bus 0 and 1,
145
182
* newer chips may have more one day.
183
+ *
184
+ * Supplemental Notes:
185
+ *
186
+ * SPI Bus wire view: Think of *data as an array of bytes, byte[0] goes out
187
+ * first with the most significant bit shifted out first and so on. When
188
+ * thinking of the data as an array of 32bit-words, the least significant byte
189
+ * of the first 32bit-word goes out first on the SPI bus with the most
190
+ * significant bit of that byte shifted out first onto the wire.
191
+ *
192
+ * When presenting a 3 or 4-byte address, the byte order will need to be
193
+ * reversed. Don't overthink it. For a 3-byte address, view *data as a byte
194
+ * array and set the first 3-bytes to the address. eg. byteData[0] MSB,
195
+ * byteData[1] middle, and byteData[2] LSB.
196
+ *
197
+ * When sending a fractional byte, fill in the most significant bit positions
198
+ * of the byte first.
146
199
*/
147
- SpiOpResult SPI0Command (uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits) {
200
+ SpiOpResult SPI0Command (uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits, uint32_t pre_cmd ) {
148
201
if (mosi_bits>(64 *8 ))
149
202
return SPI_RESULT_ERR;
150
203
if (miso_bits>(64 *8 ))
@@ -159,8 +212,16 @@ SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_
159
212
if (miso_bits % 32 != 0 )
160
213
miso_words++;
161
214
215
+ // Use SPI_CS_SETUP to add time for #CS to settle (ringing) before SPI CLK
216
+ // begins. The BootROM does not do this; however, RTOS SDK and NONOS SDK do
217
+ // as part of flash init/configuration.
218
+ //
219
+ // One SPI bus clock cycle time inserted between #CS active and the 1st SPI
220
+ // bus clock cycle. The number of clock cycles is in SPI_CNTRL2
221
+ // SPI_SETUP_TIME, which defaults to 1.
222
+ //
162
223
// Select user defined command mode in the controller
163
- uint32_t spiu=SPIUCOMMAND; // SPI_USR_COMMAND
224
+ uint32_t spiu=SPIUCOMMAND | SPIUCSSETUP ; // SPI_USR_COMMAND | SPI_CS_SETUP
164
225
165
226
// Set the command byte to send
166
227
uint32_t spiu2 = ((7 & SPIMCOMMAND)<<SPILCOMMAND) | cmd;
@@ -183,12 +244,19 @@ SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_
183
244
spic &= ~(SPICQIO | SPICDIO | SPICQOUT | SPICDOUT | SPICAHB | SPICFASTRD);
184
245
spic |= (SPICRESANDRES | SPICSHARE | SPICWPR | SPIC2BSE);
185
246
186
- SpiOpResult rc =_SPICommand (0 ,spic,spiu,spiu1,spiu2,data,mosi_words,miso_words);
247
+ SpiOpResult rc =_SPICommand (0 ,spic,spiu,spiu1,spiu2,data,mosi_words,miso_words,pre_cmd );
187
248
188
249
if (rc==SPI_RESULT_OK) {
189
- // clear any bits we did not read in the last word.
190
- if (miso_bits % 32 ) {
191
- data[miso_bits/32 ] &= ~(0xFFFFFFFF << (miso_bits % 32 ));
250
+ // Clear any bits we did not read in the last word. Bits in a fractional
251
+ // bytes will be stored in the most significant part of the byte first.
252
+ if (miso_bits % 32u ) {
253
+ uint32_t whole_byte_bits = (miso_bits % 32u ) & ~7u ;
254
+ uint32_t mask = ~(0xFFFFFFFFu << whole_byte_bits);
255
+ if (miso_bits % 8u ) {
256
+ // Select fractional byte bits.
257
+ mask |= (~(0xFFu >> (miso_bits % 8u )) & 0xFFu ) << whole_byte_bits;
258
+ }
259
+ data[miso_bits/32u ] &= mask;
192
260
}
193
261
}
194
262
return rc;
0 commit comments