mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-06 15:51:25 +00:00
263 lines
8.3 KiB
C
263 lines
8.3 KiB
C
// Copyright (c) 2026 Cesanta Software Limited
|
|
// All rights reserved
|
|
|
|
#include "hal.h"
|
|
|
|
// Secure Engine data descriptor, sli_se_datatransfer_t in Simplicity SDK
|
|
struct se_dt {
|
|
volatile void *volatile data; // Data buffer
|
|
volatile void *volatile next; // Next descriptor, or 1: last one
|
|
volatile uint32_t length; // Length in bytes, plus flags
|
|
};
|
|
|
|
#define SE_CMD_TRNG_GET_RANDOM 0x07000000UL // SE mailbox commands
|
|
#define SE_DT_STOP 1UL // Descriptor flags
|
|
#define SE_DT_REALIGN 0x20000000UL
|
|
#define SE_RESPONSE_MASK 0x000F0000UL // RX_HEADER response code, 0: OK
|
|
|
|
// Fill buf with n random bytes, n a multiple of 4, from the Secure Engine
|
|
// TRNG (RM section 11.2.5), through the SE mailbox, as sl_se_get_random()
|
|
// and sli_se_mailbox_execute_command() in Simplicity SDK. buf and the
|
|
// descriptor are static: on the stack, at the top of RAM, the SE reported a
|
|
// bus error. Return false on SE error
|
|
static bool se_random(uint32_t *buf, uint32_t n) {
|
|
static struct se_dt out;
|
|
out.data = buf, out.next = (void *) SE_DT_STOP;
|
|
out.length = n | SE_DT_REALIGN;
|
|
while ((SEMAILBOX_HOST->TX_STATUS & SEMAILBOX_TX_STATUS_TXINT) == 0) (void) 0;
|
|
SEMAILBOX_HOST->TX_HEADER = sizeof(uint32_t) * 5; // 4 words + 1 parameter
|
|
SEMAILBOX_HOST->FIFO = SE_CMD_TRNG_GET_RANDOM;
|
|
SEMAILBOX_HOST->FIFO = 0; // No input data
|
|
SEMAILBOX_HOST->FIFO = (uint32_t) &out; // Output data
|
|
SEMAILBOX_HOST->FIFO = n; // Parameter: number of bytes
|
|
while ((SEMAILBOX_HOST->RX_STATUS & SEMAILBOX_RX_STATUS_RXINT) == 0) (void) 0;
|
|
return (SEMAILBOX_HOST->RX_HEADER & SE_RESPONSE_MASK) == 0;
|
|
}
|
|
|
|
// Random 32 bits, for mg_random(). One SE request fills a pool of 16 words.
|
|
// If the SE fails 3 times in a row, the pool is zeros: see se_random()
|
|
uint32_t hal_rng_read(void) {
|
|
static uint32_t pool[16];
|
|
static size_t avail;
|
|
if (avail == 0) {
|
|
int i;
|
|
for (i = 0; i < 3 && !se_random(pool, sizeof(pool)); i++) (void) 0;
|
|
if (i == 3) memset(pool, 0, sizeof(pool));
|
|
avail = sizeof(pool) / sizeof(pool[0]);
|
|
}
|
|
return pool[--avail];
|
|
}
|
|
|
|
// RAIL library needs these Simplicity SDK functions. Implemented as in the
|
|
// SDK: sl_core_cortexm.c, em_cmu.c, em_emu.c, em_system.c, sl_gpio.c,
|
|
// pa_conversions_efr32.c. Atomic sections mask IRQs with priority number 3
|
|
// and above, see hal_radio_init() for the IRQ priorities
|
|
#define CORE_ATOMIC_BASE_PRIORITY_LEVEL 3
|
|
|
|
uint32_t CORE_EnterCritical(void) {
|
|
uint32_t state = __get_PRIMASK();
|
|
__disable_irq();
|
|
return state;
|
|
}
|
|
|
|
void CORE_ExitCritical(uint32_t state) {
|
|
if (state == 0) __enable_irq(), __ISB();
|
|
}
|
|
|
|
uint32_t CORE_EnterAtomic(void) {
|
|
uint32_t state = __get_BASEPRI();
|
|
__set_BASEPRI(CORE_ATOMIC_BASE_PRIORITY_LEVEL << (8U - __NVIC_PRIO_BITS));
|
|
return state;
|
|
}
|
|
|
|
void CORE_ExitAtomic(uint32_t state) {
|
|
__set_BASEPRI(state);
|
|
__ISB();
|
|
}
|
|
|
|
// Clock IDs: bits 0..4 bit number, bits 5..6 register, 1 CLKEN0, 2 CLKEN1
|
|
void CMU_ClockEnable(uint32_t clock, bool enable) {
|
|
volatile uint32_t *reg = (clock >> 5) == 1 ? &CMU->CLKEN0
|
|
: (clock >> 5) == 2 ? &CMU->CLKEN1
|
|
: NULL;
|
|
if (reg == NULL) return;
|
|
if (enable) {
|
|
*reg |= BIT(clock & 31);
|
|
} else {
|
|
*reg &= ~BIT(clock & 31);
|
|
}
|
|
}
|
|
|
|
void EMU_DCDCSetPFMXTimeoutMaxCtrl(uint32_t value) {
|
|
bool clk_was_on = (CMU->CLKEN0 & CMU_CLKEN0_DCDC) != 0;
|
|
bool was_locked;
|
|
CMU->CLKEN0_SET = CMU_CLKEN0_DCDC;
|
|
was_locked = (DCDC->LOCKSTATUS & DCDC_LOCKSTATUS_LOCK) != 0;
|
|
DCDC->LOCK = DCDC_LOCK_LOCKKEY_UNLOCKKEY;
|
|
while (DCDC->SYNCBUSY & DCDC_SYNCBUSY_PFMXCTRL) (void) 0;
|
|
DCDC->PFMXCTRL = (DCDC->PFMXCTRL & ~_DCDC_PFMXCTRL_IPKTMAXCTRL_MASK) |
|
|
(value << _DCDC_PFMXCTRL_IPKTMAXCTRL_SHIFT);
|
|
if (was_locked) DCDC->LOCK = ~DCDC_LOCK_LOCKKEY_UNLOCKKEY;
|
|
if (!clk_was_on) CMU->CLKEN0_CLR = CMU_CLKEN0_DCDC;
|
|
}
|
|
|
|
// SYSTEM_ChipRevision_TypeDef, em_system.h
|
|
struct chip_rev {
|
|
uint8_t minor; // Minor revision number
|
|
uint8_t major; // Major revision number
|
|
uint8_t family; // Device family number
|
|
};
|
|
|
|
void SYSTEM_ChipRevisionGet(struct chip_rev *rev) {
|
|
uint32_t chiprev;
|
|
CMU->CLKEN0_SET = CMU_CLKEN0_SYSCFG;
|
|
chiprev = SYSCFG->CHIPREV;
|
|
rev->family = (chiprev & _SYSCFG_CHIPREV_FAMILY_MASK) >>
|
|
_SYSCFG_CHIPREV_FAMILY_SHIFT;
|
|
rev->major = (chiprev & _SYSCFG_CHIPREV_MAJOR_MASK) >>
|
|
_SYSCFG_CHIPREV_MAJOR_SHIFT;
|
|
rev->minor = (chiprev & _SYSCFG_CHIPREV_MINOR_MASK) >>
|
|
_SYSCFG_CHIPREV_MINOR_SHIFT;
|
|
}
|
|
|
|
// sl_gpio_t, sl_device_gpio.h
|
|
struct sl_gpio {
|
|
uint8_t port; // GPIO port number
|
|
uint8_t pin; // GPIO pin number
|
|
};
|
|
|
|
uint32_t sl_gpio_set_pin(const struct sl_gpio *gpio) {
|
|
GPIO->P_SET[gpio->port].DOUT = BIT(gpio->pin);
|
|
return 0;
|
|
}
|
|
|
|
uint32_t sl_gpio_clear_pin(const struct sl_gpio *gpio) {
|
|
GPIO->P_CLR[gpio->port].DOUT = BIT(gpio->pin);
|
|
return 0;
|
|
}
|
|
|
|
// We set TX power as a raw PA level and load no PA curves. RAIL calls this to
|
|
// check per-channel dBm limits: RAIL_TX_POWER_MAX means no limit, 255. Without
|
|
// curves the SDK returns 0, see RAIL_ConvertDbmToRaw(), pa_conversions_efr32.c
|
|
uint8_t RAIL_ConvertDbmToRaw(void *rail, int mode, int16_t power) {
|
|
(void) rail, (void) mode;
|
|
return power >= 0x7fff ? 255 : 0; // 0x7fff: RAIL_TX_POWER_MAX
|
|
}
|
|
|
|
// Added to minimise the bin size: avoid pulling printf and friends
|
|
void __assert_func(const char *s, int line, const char *fn, const char *expr) {
|
|
(void) s, (void) line, (void) fn, (void) expr;
|
|
for (;;) (void) 0; // or trigger the fault handler
|
|
}
|
|
|
|
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) {
|
|
uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry
|
|
if (period == 0) return false; // Avoid division by zero
|
|
if (diff < period) return false; // Period has not elapsed yet
|
|
*t += (diff / period) * period; // Preserve cadence, skip missed periods
|
|
return true;
|
|
}
|
|
|
|
static volatile uint64_t s_ticks; // Milliseconds since boot
|
|
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
|
|
s_ticks++;
|
|
}
|
|
|
|
uint64_t hal_get_tick(void) {
|
|
return s_ticks;
|
|
}
|
|
|
|
uint64_t mg_micros(void);
|
|
// SysTick runs at the core clock and reloads every millisecond. Sample
|
|
// the tick counter and the sub-millisecond counter in one critical section.
|
|
uint64_t mg_micros(void) {
|
|
uint32_t primask = __get_PRIMASK(), val, pending;
|
|
uint64_t ms;
|
|
__disable_irq();
|
|
ms = hal_get_tick();
|
|
pending = SCB->ICSR & SCB_ICSR_PENDSTSET_Msk;
|
|
val = SysTick->VAL;
|
|
if (pending != (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk)) {
|
|
pending = SCB_ICSR_PENDSTSET_Msk;
|
|
val = SysTick->VAL;
|
|
}
|
|
__set_PRIMASK(primask);
|
|
if (pending != 0 && val != 0) ms++;
|
|
return ms * 1000 + ((uint64_t) (SysTick->LOAD - val) * 1000) /
|
|
(SysTick->LOAD + 1);
|
|
}
|
|
|
|
struct stat;
|
|
__attribute__((weak)) int _fstat(int fd, struct stat *st) {
|
|
(void) fd, (void) st;
|
|
return -1;
|
|
}
|
|
|
|
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
|
|
static unsigned char *s_current_heap_end = _end;
|
|
|
|
size_t hal_ram_used(void) {
|
|
return (size_t) (s_current_heap_end - _end);
|
|
}
|
|
|
|
size_t hal_ram_free(void) {
|
|
unsigned char endofstack;
|
|
return (size_t) (&endofstack - s_current_heap_end);
|
|
}
|
|
|
|
void *_sbrk(int incr) {
|
|
unsigned char *prev_heap;
|
|
unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256);
|
|
prev_heap = s_current_heap_end;
|
|
// Check how much space we got from the heap end to the stack end
|
|
if (s_current_heap_end + incr > heap_end) return (void *) -1;
|
|
s_current_heap_end += incr;
|
|
return prev_heap;
|
|
}
|
|
|
|
__attribute__((weak)) int _open(const char *path) {
|
|
(void) path;
|
|
return -1;
|
|
}
|
|
|
|
__attribute__((weak)) int _close(int fd) {
|
|
(void) fd;
|
|
return -1;
|
|
}
|
|
|
|
__attribute__((weak)) int _isatty(int fd) {
|
|
(void) fd;
|
|
return 1;
|
|
}
|
|
|
|
__attribute__((weak)) int _lseek(int fd, int ptr, int dir) {
|
|
(void) fd, (void) ptr, (void) dir;
|
|
return 0;
|
|
}
|
|
|
|
__attribute__((weak)) void _exit(int status) {
|
|
(void) status;
|
|
for (;;) asm volatile("BKPT #0");
|
|
}
|
|
|
|
__attribute__((weak)) void _kill(int pid, int sig) {
|
|
(void) pid, (void) sig;
|
|
}
|
|
|
|
__attribute__((weak)) int _getpid(void) {
|
|
return -1;
|
|
}
|
|
|
|
__attribute__((weak)) int _write(int fd, char *ptr, int len) {
|
|
(void) fd, (void) ptr, (void) len;
|
|
return -1;
|
|
}
|
|
|
|
__attribute__((weak)) int _read(int fd, char *ptr, int len) {
|
|
(void) fd, (void) ptr, (void) len;
|
|
return -1;
|
|
}
|
|
|
|
__attribute__((weak)) void _init(void) {
|
|
}
|