Files
mongoose/tutorials/silabs/xiao-mg24/minimal/hal.c
T
2026-11-05 22:23:06 +00:00

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) {
}