19
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
20
*/
21
21
22
- #include < stdint.h>
23
- #include < string.h>
22
+ #include < cstdint>
23
+ #include < cstring>
24
+ #include < memory>
24
25
25
26
// register names
26
27
#include " esp8266_peri.h"
@@ -43,35 +44,80 @@ namespace experimental {
43
44
* Kept in a separate function to aid with precaching
44
45
* PRECACHE_* saves having to make the function IRAM_ATTR.
45
46
*
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 )
49
50
*
50
51
* Note: if porting to ESP32 mosi/miso bits are set in 2 registers, not 1.
51
52
*/
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
+
52
77
static 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 )
56
81
{
57
82
if (spiIfNum>1 )
58
83
return SPI_RESULT_ERR;
59
84
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);
64
113
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);
73
117
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);
75
121
76
122
if (!spiIfNum) {
77
123
// Only need to disable interrupts and precache when using SPI0
@@ -91,12 +137,11 @@ _SPICommand(volatile uint32_t spiIfNum,
91
137
if (SPI_FLASH_CMD_WREN == pre_cmd) {
92
138
// See SPI_write_enable comments in esp8266_undocumented.h
93
139
SPI_write_enablep ((SpiFlashChip *)fchip);
94
- } else
95
- if (pre_cmd) {
140
+ } else if (pre_cmd) {
96
141
// 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 ;
98
143
SPIREG (SPI0U1) = 0 ;
99
- SPIREG (SPI0U2) = (spiu2 & ~ 0xFFFFu ) | pre_cmd ;
144
+ SPIREG (SPI0U2) = pre_cmd_spiu2 ;
100
145
101
146
SPIREG (SPI0CMD) = spicmdusr; // Send cmd
102
147
while ((SPIREG (SPI0CMD) & spicmdusr));
0 commit comments