Skip to content

Commit 1d83599

Browse files
committed
SpiUtils - explicit asm instead of volatile
getting rid of volatile arg(s) is must-have for -std=c++20 and later provide helper macros to init & load vars at specific points in code modify existing macros to use c++-style casts & less volatile change locals to plain non-volatile variables, avoid extra memw also fixes unintentional const mask load in (pre_cmd) branch
1 parent d4dcda2 commit 1d83599

File tree

1 file changed

+66
-26
lines changed

1 file changed

+66
-26
lines changed

cores/esp8266/core_esp8266_spi_utils.cpp

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
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>
2424

2525
// register names
2626
#include "esp8266_peri.h"
@@ -43,35 +43,76 @@ namespace experimental {
4343
* Kept in a separate function to aid with precaching
4444
* PRECACHE_* saves having to make the function IRAM_ATTR.
4545
*
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)
49-
*
5046
* Note: if porting to ESP32 mosi/miso bits are set in 2 registers, not 1.
5147
*/
48+
49+
#define PRELOAD_DST(DST,SRC)\
50+
__asm__ __volatile__ (\
51+
"mov %0, %1\n\t"\
52+
: "=r"(DST)\
53+
: "r"(SRC)\
54+
: "memory")
55+
56+
#define PRELOAD_IMMEDIATE(DST,SRC)\
57+
uint32_t DST;\
58+
__asm__ __volatile__ (\
59+
"movi %0, %1\n\t"\
60+
: "=r"(DST)\
61+
: "i"(SRC)\
62+
: "memory")
63+
64+
#define PRELOAD_VAR(DST,SRC)\
65+
decltype(SRC) DST;\
66+
PRELOAD_DST(DST,SRC)
67+
68+
#define PRELOAD_FUNC(DST,SRC)\
69+
decltype(&SRC) DST;\
70+
PRELOAD_DST(DST,SRC)
71+
5272
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)
73+
_SPICommand(uint32_t spiIfNum,
74+
uint32_t spic, uint32_t spiu, uint32_t spiu1, uint32_t spiu2,
75+
uint32_t *data, uint32_t writeWords, uint32_t readWords, uint32_t _pre_cmd)
5676
{
5777
if (spiIfNum>1)
5878
return SPI_RESULT_ERR;
5979

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)))))
80+
// force SPI register access via base+offset by deconstructing SPI# access macros
81+
// note that the function below only ever calls this one w/ spiIfNum==0
82+
// in case it is *really* necessary, preload spiIfNum as well
83+
#define VOLATILE_PTR(X) reinterpret_cast<volatile uint32_t *>(X)
84+
#define SPIADDR(X) const_cast<uint32_t *>(&(X))
85+
86+
// preload all required constants and functions into variables.
87+
// when modifying code below, always double-check the asm output
88+
89+
PRELOAD_IMMEDIATE(spi0cmd_addr, SPIADDR(SPI0CMD));
90+
PRELOAD_IMMEDIATE(spi1cmd_addr, SPIADDR(SPI1CMD));
91+
uint32_t *spibase = spiIfNum
92+
? reinterpret_cast<uint32_t*>(spi1cmd_addr)
93+
: reinterpret_cast<uint32_t*>(spi0cmd_addr);
94+
#define SPIREG(reg) \
95+
(*VOLATILE_PTR(spibase + (SPIADDR(reg) - SPIADDR(SPI0CMD))))
96+
97+
PRELOAD_FUNC(SPI_write_enablep, SPI_write_enable);
98+
PRELOAD_FUNC(Wait_SPI_Idlep, Wait_SPI_Idle);
99+
100+
PRELOAD_VAR(fchip, flashchip);
101+
102+
PRELOAD_IMMEDIATE(spicmdusr, SPICMDUSR);
103+
PRELOAD_IMMEDIATE(saved_ps, 0);
104+
105+
// also force 'pre_cmd' & 'spiu' mask constant to be loaded right now
106+
// (TODO write all of the preamble in asm directly?)
107+
PRELOAD_VAR(pre_cmd, _pre_cmd);
64108

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;
109+
PRELOAD_IMMEDIATE(pre_cmd_spiu_mask, ~(SPIUMOSI | SPIUMISO));
110+
uint32_t _pre_cmd_spiu = spiu & pre_cmd_spiu_mask;
111+
PRELOAD_VAR(pre_cmd_spiu, _pre_cmd_spiu);
73112

74-
uint32_t saved_ps=0;
113+
PRELOAD_IMMEDIATE(pre_cmd_spiu2_mask, ~0xFFFFu);
114+
uint32_t _pre_cmd_spiu2 = (spiu2 & pre_cmd_spiu2_mask) | pre_cmd;
115+
PRELOAD_VAR(pre_cmd_spiu2, _pre_cmd_spiu2);
75116

76117
if (!spiIfNum) {
77118
// Only need to disable interrupts and precache when using SPI0
@@ -91,12 +132,11 @@ _SPICommand(volatile uint32_t spiIfNum,
91132
if (SPI_FLASH_CMD_WREN == pre_cmd) {
92133
// See SPI_write_enable comments in esp8266_undocumented.h
93134
SPI_write_enablep((SpiFlashChip *)fchip);
94-
} else
95-
if (pre_cmd) {
135+
} else if (pre_cmd) {
96136
// Send prefix cmd w/o data - sends 8 bits. eg. Volatile SR Write Enable, 0x50
97-
SPIREG(SPI0U) = (spiu & ~(SPIUMOSI|SPIUMISO));
137+
SPIREG(SPI0U) = pre_cmd_spiu;
98138
SPIREG(SPI0U1) = 0;
99-
SPIREG(SPI0U2) = (spiu2 & ~0xFFFFu) | pre_cmd;
139+
SPIREG(SPI0U2) = pre_cmd_spiu2;
100140

101141
SPIREG(SPI0CMD) = spicmdusr; //Send cmd
102142
while ((SPIREG(SPI0CMD) & spicmdusr));

0 commit comments

Comments
 (0)