1919 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2020*/
2121
22- #include < stdint.h>
23- #include < string.h>
22+ #include < cstdint>
23+ #include < cstring>
24+ #include < memory>
2425
2526// register names
2627#include " esp8266_peri.h"
@@ -43,35 +44,80 @@ namespace experimental {
4344 * Kept in a separate function to aid with precaching
4445 * PRECACHE_* saves having to make the function IRAM_ATTR.
4546 *
46- * spiIfNum needs to be volatile to keep the optimiser from
47- * deciding it can be treated as a constant (due to this being a
48- * static function only called with spiIfNum set to 0 )
47+ * PRELOAD_* allows access to consts and external values
48+ * through a different name, while also forcing immediate load.
49+ * (but, note that compiler only knows about DST and SRC as dependencies )
4950 *
5051 * Note: if porting to ESP32 mosi/miso bits are set in 2 registers, not 1.
5152 */
53+
54+ #define PRELOAD_DST_SRC (DST,SRC )\
55+ __asm__ __volatile__ (\
56+ " mov %0, %1\n\t " \
57+ : " =a" (DST)\
58+ : "r"(SRC)\
59+ : "memory")
60+
61+ #define PRELOAD_IMMEDIATE (DST,SRC )\
62+ uint32_t DST;\
63+ __asm__ __volatile__ (\
64+ " movi %0, %1\n\t " \
65+ : " =a" (DST)\
66+ : "i"(SRC)\
67+ : "memory")
68+
69+ #define PRELOAD_VAL (DST,SRC )\
70+ decltype (SRC) DST;\
71+ PRELOAD_DST_SRC (DST,SRC)
72+
73+ #define PRELOAD_PTR (DST,SRC )\
74+ decltype (std::addressof(SRC)) DST;\
75+ PRELOAD_DST_SRC (DST,SRC)
76+
5277static SpiOpResult PRECACHE_ATTR
53- _SPICommand (volatile uint32_t spiIfNum,
54- uint32_t spic,uint32_t spiu,uint32_t spiu1,uint32_t spiu2,
55- uint32_t *data,uint32_t writeWords,uint32_t readWords, uint32_t pre_cmd )
78+ _SPICommand (uint32_t spiIfNum,
79+ uint32_t spic, uint32_t spiu, uint32_t spiu1, uint32_t spiu2,
80+ uint32_t *data, uint32_t writeWords, uint32_t readWords, uint32_t _pre_cmd )
5681{
5782 if (spiIfNum>1 )
5883 return SPI_RESULT_ERR;
5984
60- // force SPI register access via base+offset.
61- // Prevents loading individual address constants from flash.
62- uint32_t *spibase = (uint32_t *)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD));
63- #define SPIREG (reg ) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD)))))
85+ // force SPI register access via base+offset by deconstructing SPI# access macros
86+ // note that the function below only ever calls this one w/ spiIfNum==0
87+ // in case it is *really* necessary, preload spiIfNum as well
88+ #define VOLATILE_PTR (X ) reinterpret_cast <volatile uint32_t *>(X)
89+ #define SPIADDR (X ) const_cast <uint32_t *>(std::addressof(X))
90+
91+ // preload all required constants and functions into variables.
92+ // when modifying code below, always double-check the asm output
93+
94+ PRELOAD_IMMEDIATE (spi0cmd_addr, SPIADDR (SPI0CMD));
95+ PRELOAD_IMMEDIATE (spi1cmd_addr, SPIADDR (SPI1CMD));
96+ uint32_t *spibase = spiIfNum
97+ ? reinterpret_cast <uint32_t *>(spi1cmd_addr)
98+ : reinterpret_cast <uint32_t *>(spi0cmd_addr);
99+ #define SPIREG (reg ) \
100+ (*VOLATILE_PTR (spibase + (SPIADDR (reg) - SPIADDR (SPI0CMD))))
101+
102+ PRELOAD_PTR (SPI_write_enablep, SPI_write_enable);
103+ PRELOAD_PTR (Wait_SPI_Idlep, Wait_SPI_Idle);
104+
105+ PRELOAD_VAL (fchip, flashchip);
106+
107+ PRELOAD_IMMEDIATE (spicmdusr, SPICMDUSR);
108+ PRELOAD_IMMEDIATE (saved_ps, 0 );
109+
110+ // also force 'pre_cmd' & 'spiu' mask constant to be loaded right now
111+ // (TODO write all of the preamble in asm directly?)
112+ PRELOAD_VAL (pre_cmd, _pre_cmd);
64113
65- // preload any constants and functions we need into variables
66- // Everything defined here must be volatile or the optimizer can
67- // treat them as constants, resulting in the flash reads we're
68- // trying to avoid
69- SpiFlashOpResult (* volatile SPI_write_enablep)(SpiFlashChip *) = SPI_write_enable;
70- uint32_t (* volatile Wait_SPI_Idlep)(SpiFlashChip *) = Wait_SPI_Idle;
71- volatile SpiFlashChip *fchip=flashchip;
72- volatile uint32_t spicmdusr=SPICMDUSR;
114+ PRELOAD_IMMEDIATE (pre_cmd_spiu_mask, ~(SPIUMOSI | SPIUMISO));
115+ uint32_t _pre_cmd_spiu = spiu & pre_cmd_spiu_mask;
116+ PRELOAD_VAL (pre_cmd_spiu, _pre_cmd_spiu);
73117
74- uint32_t saved_ps=0 ;
118+ PRELOAD_IMMEDIATE (pre_cmd_spiu2_mask, ~0xFFFFu );
119+ uint32_t _pre_cmd_spiu2 = (spiu2 & pre_cmd_spiu2_mask) | pre_cmd;
120+ PRELOAD_VAL (pre_cmd_spiu2, _pre_cmd_spiu2);
75121
76122 if (!spiIfNum) {
77123 // Only need to disable interrupts and precache when using SPI0
@@ -91,12 +137,11 @@ _SPICommand(volatile uint32_t spiIfNum,
91137 if (SPI_FLASH_CMD_WREN == pre_cmd) {
92138 // See SPI_write_enable comments in esp8266_undocumented.h
93139 SPI_write_enablep ((SpiFlashChip *)fchip);
94- } else
95- if (pre_cmd) {
140+ } else if (pre_cmd) {
96141 // Send prefix cmd w/o data - sends 8 bits. eg. Volatile SR Write Enable, 0x50
97- SPIREG (SPI0U) = (spiu & ~(SPIUMOSI|SPIUMISO)) ;
142+ SPIREG (SPI0U) = pre_cmd_spiu ;
98143 SPIREG (SPI0U1) = 0 ;
99- SPIREG (SPI0U2) = (spiu2 & ~ 0xFFFFu ) | pre_cmd ;
144+ SPIREG (SPI0U2) = pre_cmd_spiu2 ;
100145
101146 SPIREG (SPI0CMD) = spicmdusr; // Send cmd
102147 while ((SPIREG (SPI0CMD) & spicmdusr));
0 commit comments