Skip to content

Instantly share code, notes, and snippets.

@fanoush
Last active September 3, 2019 21:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fanoush/9227640a869d78d69a799276dff0fb71 to your computer and use it in GitHub Desktop.
Save fanoush/9227640a869d78d69a799276dff0fb71 to your computer and use it in GitHub Desktop.
native nrf52 FPU floating point math in Espruino inline C
// https://github.com/gfwilliams/EspruinoCompiler/issues/10
// https://www.espruino.com/service/compiler => http://127.0.0.1:32766
// old compile.js: var cflags = "-mlittle-endian -mthumb -mcpu=cortex-m3 -mfix-cortex-m3-ldrd -mthumb-interwork -mfloat-abi=soft ";
// new: var cflags = "-mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=softfp -mfpu=fpv4-sp-d16 -fsingle-precision-constant -Wdouble-promotion -Wfloat-conversion ";
//
// power drain fix
// https://devzone.nordicsemi.com/f/nordic-q-a/15243/high-power-consumption-when-using-fpu
// https://devzone.nordicsemi.com/f/nordic-q-a/12433/fpu-divide-by-0-and-high-current-consumption
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52832_EngC%2FERR%2FnRF52832%2FEngineeringC%2Flatest%2Fanomaly_832_87.html&anchor=anomaly_832_87
/* test mandelbrodt
for (y=0;y<64;y++) {
line="";
for (x=0;x<64;x++) line += " *"[c.mandel(x,y)&1];
print(line);
}
*/
/* test float parameter passing
use float and int arrays sharing same storage to sent 32bit floats as 32bit ints
this works becasue we use softfp calling convention so float parameters are passed to method in same way as integers
fa=new Float32Array(3)
ia=new Int32Array(fa.buffer)
fa[0]=0.5;fa[1]=4;
ia[2]=c.fadd(ia[0],ia[1]);fa[2]
=4.5
>ia[2]=c.fmul(ia[0],ia[1]);fa[2]
=2
*/
var c = E.compiledC(`
// int mandel(int, int)
// int fadd(int,int)
// int fmul(int,int)
// void clearFPU()
// int getFPSCR()
// int getFPUPendingIRQ()
float fadd(float f1,float f2){
return f1+f2;
}
float fmul(float f1,float f2){
return f1*f2;
}
int mandel(int x,int y){
float Xr = 0.0;
float Xi = 0.0;
int i = 0;
float Cr=(4*x/64.0)-2;
float Ci=(4*y/64.0)-2;
while ((i<32) & ((Xr*Xr+Xi*Xi)<4.0)) {
float t=Xr*Xr - Xi*Xi + Cr;
Xi=2.0*Xr*Xi+Ci;
Xr=t;
i=i+1;
}
return i;
}
typedef unsigned int uint32_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
/*
https://github.com/espruino/Espruino/blob/master/targetlibs/nrf5x_12/components/toolchain/cmsis/include/cmsis_gcc.h
*/
__attribute__( ( always_inline ) ) static inline uint32_t __get_FPSCR(void)
{
uint32_t result;
/* Empty asm statement works as a scheduling barrier */
__asm volatile ("");
__asm volatile ("VMRS %0, fpscr" : "=r" (result) );
__asm volatile ("");
return(result);
}
__attribute__( ( always_inline ) ) static inline void __set_FPSCR(uint32_t fpscr)
{
/* Empty asm statement works as a scheduling barrier */
__asm volatile ("");
__asm volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc");
__asm volatile ("");
}
/* https://github.com/espruino/Espruino/blob/master/targetlibs/nrf5x_12/components/toolchain/cmsis/include/core_cm4.h
*/
typedef struct
{
volatile uint32_t ISER[8]; /*!< Offset: 0x000 Interrupt Set Enable Register */
uint32_t RESERVED0[24];
volatile uint32_t ICER[8]; /*!< Offset: 0x080 Interrupt Clear Enable Register */
uint32_t RSERVED1[24];
volatile uint32_t ISPR[8]; /*!< Offset: 0x100 Interrupt Set Pending Register */
uint32_t RESERVED2[24];
volatile uint32_t ICPR[8]; /*!< Offset: 0x180 Interrupt Clear Pending Register */
uint32_t RESERVED3[24];
volatile uint32_t IABR[8]; /*!< Offset: 0x200 Interrupt Active bit Register */
uint32_t RESERVED4[56];
volatile uint8_t IP[240]; /*!< Offset: 0x300 Interrupt Priority Register (8Bit wide) */
uint32_t RESERVED5[644];
volatile uint32_t STIR; /*!< Offset: 0xE00 Software Trigger Interrupt Register */
} NVIC_Type;
typedef enum {
/* ------------------- Cortex-M4 Processor Exceptions Numbers ------------------- */
Reset_IRQn = -15, /*!< 1 Reset Vector, invoked on Power up and warm reset */
NonMaskableInt_IRQn = -14, /*!< 2 Non maskable Interrupt, cannot be stopped or preempted */
HardFault_IRQn = -13, /*!< 3 Hard Fault, all classes of Fault */
MemoryManagement_IRQn = -12, /*!< 4 Memory Management, MPU mismatch, including Access Violation
and No Match */
BusFault_IRQn = -11, /*!< 5 Bus Fault, Pre-Fetch-, Memory Access Fault, other address/memory
related Fault */
UsageFault_IRQn = -10, /*!< 6 Usage Fault, i.e. Undef Instruction, Illegal State Transition */
SVCall_IRQn = -5, /*!< 11 System Service Call via SVC instruction */
DebugMonitor_IRQn = -4, /*!< 12 Debug Monitor */
PendSV_IRQn = -2, /*!< 14 Pendable request for system service */
SysTick_IRQn = -1, /*!< 15 System Tick Timer */
/* ---------------------- nrf52 Specific Interrupt Numbers ---------------------- */
POWER_CLOCK_IRQn = 0, /*!< 0 POWER_CLOCK */
RADIO_IRQn = 1, /*!< 1 RADIO */
UARTE0_UART0_IRQn = 2, /*!< 2 UARTE0_UART0 */
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn= 3, /*!< 3 SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 */
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn= 4, /*!< 4 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 */
NFCT_IRQn = 5, /*!< 5 NFCT */
GPIOTE_IRQn = 6, /*!< 6 GPIOTE */
SAADC_IRQn = 7, /*!< 7 SAADC */
TIMER0_IRQn = 8, /*!< 8 TIMER0 */
TIMER1_IRQn = 9, /*!< 9 TIMER1 */
TIMER2_IRQn = 10, /*!< 10 TIMER2 */
RTC0_IRQn = 11, /*!< 11 RTC0 */
TEMP_IRQn = 12, /*!< 12 TEMP */
RNG_IRQn = 13, /*!< 13 RNG */
ECB_IRQn = 14, /*!< 14 ECB */
CCM_AAR_IRQn = 15, /*!< 15 CCM_AAR */
WDT_IRQn = 16, /*!< 16 WDT */
RTC1_IRQn = 17, /*!< 17 RTC1 */
QDEC_IRQn = 18, /*!< 18 QDEC */
COMP_LPCOMP_IRQn = 19, /*!< 19 COMP_LPCOMP */
SWI0_EGU0_IRQn = 20, /*!< 20 SWI0_EGU0 */
SWI1_EGU1_IRQn = 21, /*!< 21 SWI1_EGU1 */
SWI2_EGU2_IRQn = 22, /*!< 22 SWI2_EGU2 */
SWI3_EGU3_IRQn = 23, /*!< 23 SWI3_EGU3 */
SWI4_EGU4_IRQn = 24, /*!< 24 SWI4_EGU4 */
SWI5_EGU5_IRQn = 25, /*!< 25 SWI5_EGU5 */
TIMER3_IRQn = 26, /*!< 26 TIMER3 */
TIMER4_IRQn = 27, /*!< 27 TIMER4 */
PWM0_IRQn = 28, /*!< 28 PWM0 */
PDM_IRQn = 29, /*!< 29 PDM */
MWU_IRQn = 32, /*!< 32 MWU */
PWM1_IRQn = 33, /*!< 33 PWM1 */
PWM2_IRQn = 34, /*!< 34 PWM2 */
SPIM2_SPIS2_SPI2_IRQn = 35, /*!< 35 SPIM2_SPIS2_SPI2 */
RTC2_IRQn = 36, /*!< 36 RTC2 */
I2S_IRQn = 37, /*!< 37 I2S */
FPU_IRQn = 38 /*!< 38 FPU */
} IRQn_Type;
/*
#define SCS_BASE (0xE000E000UL)
#define NVIC_BASE (SCS_BASE + 0x0100UL)
#define NVIC ((NVIC_Type *) NVIC_BASE)
Espruino inline compiler disables macros so define NVIC() method below
*/
__attribute__( ( always_inline ) ) static inline NVIC_Type *NVIC(){return ((NVIC_Type *)(0xE000E000UL+0x0100UL));}
static inline void NVIC_EnableIRQ(IRQn_Type IRQn)
{
NVIC()->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}
static inline void NVIC_DisableIRQ(IRQn_Type IRQn)
{
NVIC()->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}
static inline uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)
{
return((uint32_t)(((NVIC()->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
}
static inline void NVIC_SetPendingIRQ(IRQn_Type IRQn)
{
NVIC()->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}
static inline void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
{
NVIC()->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}
void volatile clearFPU(){
__set_FPSCR(__get_FPSCR() & ~(0x9FUL));
(void) __get_FPSCR();
NVIC_ClearPendingIRQ(FPU_IRQn);
}
int getFPUPendingIRQ(){
return NVIC_GetPendingIRQ(FPU_IRQn);
}
int getFPSCR(){
return __get_FPSCR();
}
`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment