Skip to content

Commit c2f1365

Browse files
authored
Fixes exceptions resulting from using SPI0Command (#9140)
* Resolves exceptions occuring when using SPI0Command for flash write operations such as: Write Status Register-1, Sector Eraser, etc. Moved PRECACHE_END to ensure `Wait_SPI_Idlep` and `xt_wsr_ps` are included in the iCache. Added SPIUCSSETUP to give more settling time for #CS. * There was a risk of flash reads inserted between an "enable opcode" and the "target opcode". They are now tightly coupled. Update flash quirks. * When sending instruction Write Enable 0x06, use BootROM API SPI_write_enable for the special handling of the WEL bit. Corrected zero mask for fractional byte returns where the partial byte bits are positioned at the most significant bit position in the byte.
1 parent e4887b7 commit c2f1365

4 files changed

+95
-15
lines changed

cores/esp8266/core_esp8266_flash_quirks.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,13 @@ void initFlashQuirks() {
6666
newSR3=SR3;
6767
if (get_flash_mhz()>26) { // >26Mhz?
6868
// Set the output drive to 100%
69+
// These definitions are for the XM25QH32B part. On a XM25QH32C
70+
// part, the XM25QH32B's 100% is C's 25% driver strength.
6971
newSR3 &= ~(SPI_FLASH_SR3_XMC_DRV_MASK << SPI_FLASH_SR3_XMC_DRV_S);
7072
newSR3 |= (SPI_FLASH_SR3_XMC_DRV_100 << SPI_FLASH_SR3_XMC_DRV_S);
7173
}
7274
if (newSR3 != SR3) { // only write if changed
73-
if (SPI0Command(SPI_FLASH_CMD_WEVSR,NULL,0,0)==SPI_RESULT_OK) // write enable volatile SR
74-
SPI0Command(SPI_FLASH_CMD_WSR3,&newSR3,8,0); // write to SR3
75+
SPI0Command(SPI_FLASH_CMD_WSR3,&newSR3,8,0,SPI_FLASH_CMD_WEVSR); // write to SR3, use write enable volatile prefix
7576
SPI0Command(SPI_FLASH_CMD_WRDI,NULL,0,0); // write disable - probably not needed
7677
}
7778
}

cores/esp8266/core_esp8266_spi_utils.cpp

+80-12
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "core_esp8266_features.h"
3333

3434
#include "spi_utils.h"
35+
#include "spi_flash_defs.h"
3536

3637
extern "C" uint32_t Wait_SPI_Idle(SpiFlashChip *fc);
3738

@@ -51,12 +52,12 @@ namespace experimental {
5152
static SpiOpResult PRECACHE_ATTR
5253
_SPICommand(volatile uint32_t spiIfNum,
5354
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)
5556
{
5657
if (spiIfNum>1)
5758
return SPI_RESULT_ERR;
5859

59-
// force SPI register access via base+offset.
60+
// force SPI register access via base+offset.
6061
// Prevents loading individual address constants from flash.
6162
uint32_t *spibase = (uint32_t*)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD));
6263
#define SPIREG(reg) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD)))))
@@ -65,6 +66,7 @@ _SPICommand(volatile uint32_t spiIfNum,
6566
// Everything defined here must be volatile or the optimizer can
6667
// treat them as constants, resulting in the flash reads we're
6768
// trying to avoid
69+
SpiFlashOpResult (* volatile SPI_write_enablep)(SpiFlashChip *) = SPI_write_enable;
6870
uint32_t (* volatile Wait_SPI_Idlep)(SpiFlashChip *) = Wait_SPI_Idle;
6971
volatile SpiFlashChip *fchip=flashchip;
7072
volatile uint32_t spicmdusr=SPICMDUSR;
@@ -77,15 +79,30 @@ _SPICommand(volatile uint32_t spiIfNum,
7779
PRECACHE_START();
7880
Wait_SPI_Idlep((SpiFlashChip *)fchip);
7981
}
80-
82+
8183
// preserve essential controller state such as incoming/outgoing
8284
// data lengths and IO mode.
8385
uint32_t oldSPI0U = SPIREG(SPI0U);
8486
uint32_t oldSPI0U2= SPIREG(SPI0U2);
8587
uint32_t oldSPI0C = SPIREG(SPI0C);
8688

87-
//SPI0S &= ~(SPISE|SPISBE|SPISSE|SPISCD);
8889
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);
89106
SPIREG(SPI0U) = spiu;
90107
SPIREG(SPI0U1)= spiu1;
91108
SPIREG(SPI0U2)= spiu2;
@@ -117,11 +134,22 @@ _SPICommand(volatile uint32_t spiIfNum,
117134
SPIREG(SPI0U) = oldSPI0U;
118135
SPIREG(SPI0U2)= oldSPI0U2;
119136
SPIREG(SPI0C) = oldSPI0C;
120-
121-
PRECACHE_END();
137+
122138
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);
123150
xt_wsr_ps(saved_ps);
124151
}
152+
PRECACHE_END();
125153
return (timeout>0 ? SPI_RESULT_OK : SPI_RESULT_TIMEOUT);
126154
}
127155

@@ -139,12 +167,37 @@ _SPICommand(volatile uint32_t spiIfNum,
139167
* miso_bits
140168
* Number of bits to read from the SPI bus after the outgoing
141169
* 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."
142179
*
143180
* Note: This code has only been tested with SPI bus 0, but should work
144181
* equally well with other buses. The ESP8266 has bus 0 and 1,
145182
* 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.
146199
*/
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) {
148201
if (mosi_bits>(64*8))
149202
return SPI_RESULT_ERR;
150203
if (miso_bits>(64*8))
@@ -159,8 +212,16 @@ SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_
159212
if (miso_bits % 32 != 0)
160213
miso_words++;
161214

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+
//
162223
// 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
164225

165226
// Set the command byte to send
166227
uint32_t spiu2 = ((7 & SPIMCOMMAND)<<SPILCOMMAND) | cmd;
@@ -183,12 +244,19 @@ SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_
183244
spic &= ~(SPICQIO | SPICDIO | SPICQOUT | SPICDOUT | SPICAHB | SPICFASTRD);
184245
spic |= (SPICRESANDRES | SPICSHARE | SPICWPR | SPIC2BSE);
185246

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);
187248

188249
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;
192260
}
193261
}
194262
return rc;

cores/esp8266/esp8266_undocumented.h

+11
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,17 @@ extern fn_c_exception_handler_t _xtos_c_handler_table[XCHAL_EXCCAUSE_NUM];
241241
extern fn_c_exception_handler_t _xtos_set_exception_handler(int cause, fn_c_exception_handler_t fn);
242242
#endif
243243

244+
/*
245+
BootROM function that sends the SPI Flash "Write Enable" command, 0x06.
246+
The function internally calls Wait_SPI_Idle before enabling.
247+
Polls status register forever waiting for WEL bit to set.
248+
This function always returns 0; however, most examples test for 0.
249+
250+
Every function I find that needs WEL set, call this function. I suspect the
251+
waiting for the WEL bit to set is a Flash chip anomaly workaround.
252+
*/
253+
extern SpiFlashOpResult SPI_write_enable(SpiFlashChip *fc);
254+
244255
extern uint32_t Wait_SPI_Idle(SpiFlashChip *fc);
245256
extern void Cache_Read_Disable();
246257
extern int32_t system_func1(uint32_t);

cores/esp8266/spi_utils.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ typedef enum {
3535
SPI_RESULT_TIMEOUT
3636
} SpiOpResult;
3737

38-
SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits);
38+
SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits, uint32_t pre_cmd=0);
3939
}
4040

4141
#ifdef __cplusplus

0 commit comments

Comments
 (0)