Add frdm-mcxw71 support

This commit is contained in:
cpq committed 2026-10-20 21:48:34 +01:00
1 parent a6b6a0d779
commit 1ca4fd1fef
27 files changed
+4018 -19

No files matched your search

+4 -2
View File
@@ -2,7 +2,7 @@
MGDIR = ../../../..
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow
CFLAGS += -Wformat-truncation -fno-common -Wno-conversion -Wno-sign-conversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include
@@ -10,9 +10,11 @@ CFLAGS += -Icmsis_mcxw71/MCXW/MCXW716C -Icmsis_mcxw71/MCXW/periph2
CFLAGS += -DCPU_MCXW716CMFTA
CFLAGS += -D__START=main -D__STARTUP_CLEAR_BSS # For startup_MCXW716C.S
CFLAGS += -mcpu=cortex-m33 -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 $(CFLAGS_EXTRA)
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs=nano.specs -lc -lgcc -Wl,--gc-sections
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs=nano.specs -lc -lm -lgcc -Wl,--gc-sections
SOURCES = main.c hal.c $(MGDIR)/mongoose.c
SOURCES += $(MGDIR)/tutorials/mqtt/mqtt-client/mongoose_mqtt.c
SOURCES += cmsis_mcxw71/MCXW/MCXW716C/gcc/startup_MCXW716C.S
all build: firmware.bin
+55
View File
@@ -2,6 +2,61 @@
// All rights reserved
#include "hal.h"
#include "mongoose.h"
// Initialise the transceiver the way NXP XCVR_Init() does it: start the RF
// oscillator, wait for the radio to wake up, enable its clocks and load the
// 802.15.4 register setup. Then select the legacy 802.15.4 demodulator and
// CCA from RX digital. See PhyHwInit() in NXP ieee_802_15_4 PHY sources,
// PhyPacketProcessor.c
bool hal_radio_init(void) {
uint32_t stat = RFMC_RF2P4GHZ_STAT_RST_STAT_MASK |
RFMC_RF2P4GHZ_STAT_LP_ACK_STAT_MASK;
size_t i, n = 1000000;
RFMC->CTRL &= ~RFMC_CTRL_RFMC_RST_MASK; // RFMC out of reset
RFMC->RF2P4GHZ_CTRL |= RFMC_RF2P4GHZ_CTRL_XO_EN_MASK; // Start RF oscillator
RFMC->RF2P4GHZ_CTRL &=
~(RFMC_RF2P4GHZ_CTRL_LP_ENTER_MASK | RFMC_RF2P4GHZ_CTRL_RF_POR_MASK |
RFMC_RF2P4GHZ_CTRL_RST_MASK); // Release radio reset, no low power
while ((RFMC->RF2P4GHZ_STAT & stat) != 0 ||
(RFMC->XO_STAT & RFMC_XO_STAT_XTAL_RDY_MASK) == 0) {
if (--n == 0) return false; // Radio did not wake up
}
RADIO_CTRL->LL_CTRL = RADIO_CTRL_LL_CTRL_ACTIVE_LL_MASK;
RFMC->RF2P4GHZ_CTRL |=
RFMC_RF2P4GHZ_CTRL_BLE_LP_EN_MASK | RFMC_RF2P4GHZ_CTRL_BLE_WKUP_MASK;
for (i = 0; i < sizeof(s_radio_regs) / sizeof(s_radio_regs[0]); i++) {
*s_radio_regs[i].reg = s_radio_regs[i].val;
}
XCVR_MISC->XCVR_CTRL &= ~XCVR_MISC_XCVR_CTRL_DEMOD_SEL_MASK;
XCVR_MISC->XCVR_CTRL |= XCVR_MISC_XCVR_CTRL_DEMOD_SEL(2);
XCVR_ZBDEMOD->CCA_LQI_SRC |= XCVR_ZBDEMOD_CCA_LQI_SRC_CCA1_FROM_RX_DIG(1);
return true;
}
// Random bytes for Mongoose, MG_ENABLE_CUSTOM_RANDOM. Entropy is radio noise:
// receiver RSSI, sampled while the radio receives, mixed with SysTick by
// SHA-256. Good enough for random addresses and nonces, not a certified TRNG
bool mg_random(void *buf, size_t len) {
static uint32_t counter;
uint8_t digest[32], *p = (uint8_t *) buf;
size_t i, n;
while (len > 0) {
mg_sha256_ctx ctx;
mg_sha256_init(&ctx);
counter++;
mg_sha256_update(&ctx, (uint8_t *) &counter, sizeof(counter));
for (i = 0; i < 64; i++) {
uint32_t sample[2] = {XCVR_RX_DIG->NB_RSSI_RES0, SysTick->VAL};
mg_sha256_update(&ctx, (uint8_t *) sample, sizeof(sample));
for (n = 0; n < 50; n++) __NOP(); // Let the RSSI update
}
mg_sha256_final(digest, &ctx);
n = len < sizeof(digest) ? len : sizeof(digest);
memcpy(p, digest, n), p += n, len -= n;
}
return true;
}
// 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) {
+314 -10
View File
@@ -13,13 +13,18 @@
#include <stdint.h>
#include <string.h>
uint64_t hal_get_tick(void);
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now);
size_t hal_ram_used(void);
size_t hal_ram_free(void);
bool hal_radio_init(void); // Set up the 2.4 GHz transceiver for 802.15.4
extern uint32_t SystemCoreClock; // CPU clock, Hz. Set by hal_clock_init()
#define BIT(x) (1UL << (x))
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define SIRC_FREQUENCY 6000000UL // Slow IRC (FRO6M), Hz
extern uint32_t SystemCoreClock; // CPU clock, Hz. Set by hal_clock_init()
// MRCC peripheral clock source, MRCC_xx[MUX], see fsl_clock.h
enum { MRCC_SRC_FRO6M = 2, MRCC_SRC_FRO192M = 3 };
@@ -30,10 +35,43 @@ static inline uint32_t hal_firc_freq(void) {
return freqs[SCG0->FIRCCFG & SCG_FIRCCFG_RANGE_MASK];
}
// Work out the CPU clock from the SCG clock source and core divider. The
// clock is left as configured at reset
// Switch the system clock: source (2 SIRC, 3 FIRC), core and slow dividers
// as divider - 1. Wait until the switch is done. See CLOCK_SetRunModeSysClkConfig
static inline void hal_set_sysclk(uint32_t src, uint32_t divcore,
uint32_t divslow) {
SCG0->RCCR = SCG_RCCR_SCS(src) | SCG_RCCR_DIVCORE(divcore) |
SCG_RCCR_DIVSLOW(divslow);
while (((SCG0->CSR & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT) != src) (void) 0;
}
// Run the core at 96 MHz from FIRC, slow clock 24 MHz. Order as in NXP
// BOARD_BootClockRUN(), clock_config.c: raise the core voltage and memory
// timing first, then the clock. FIRC can only be changed when not in use
static inline void hal_clock_96mhz(void) {
while (SPC0->SC & SPC_SC_BUSY_MASK) (void) 0;
CLRSET(SPC0->ACTIVE_CFG, SPC_ACTIVE_CFG_CORELDO_VDD_LVL_MASK,
SPC_ACTIVE_CFG_CORELDO_VDD_LVL(2)); // Normal voltage
while (SPC0->SC & SPC_SC_BUSY_MASK) (void) 0;
CLRSET(FMU0->FCTRL, FMU_FCTRL_RWSC_MASK, FMU_FCTRL_RWSC(2)); // Wait states
CLRSET(SPC0->SRAMCTL, SPC_SRAMCTL_VSM_MASK, SPC_SRAMCTL_VSM(2)); // 1.1 V
SPC0->SRAMCTL |= SPC_SRAMCTL_REQ_MASK;
while ((SPC0->SRAMCTL & SPC_SRAMCTL_ACK_MASK) == 0) (void) 0;
SPC0->SRAMCTL &= ~SPC_SRAMCTL_REQ_MASK;
hal_set_sysclk(2, 0, 3); // Run from SIRC meanwhile
SCG0->FIRCCSR &= ~SCG_FIRCCSR_LK_MASK; // Unlock
SCG0->FIRCCSR = SCG_FIRCCSR_FIRCERR_MASK; // Disable, clear error
SCG0->FIRCCFG = SCG_FIRCCFG_RANGE(2); // 96 MHz
SCG0->FIRCCSR |= SCG_FIRCCSR_FIRCEN_MASK;
while ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRCVLD_MASK) == 0) (void) 0;
hal_set_sysclk(3, 0, 3); // FIRC, core / 1, slow / 4
}
// Set the CPU clock to 96 MHz, then work out SystemCoreClock from the SCG
// clock source and core divider
static inline void hal_clock_init(void) {
uint32_t csr = SCG0->CSR, freq = 0;
uint32_t csr, freq = 0;
hal_clock_96mhz();
csr = SCG0->CSR;
uint32_t src = (csr & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT;
uint32_t div = ((csr & SCG_CSR_DIVCORE_MASK) >> SCG_CSR_DIVCORE_SHIFT) + 1;
if (src == 2) freq = SIRC_FREQUENCY; // Slow IRC
@@ -108,7 +146,273 @@ static inline void hal_sys_init(void) {
hal_wdog_disable();
}
uint64_t hal_get_tick(void);
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now);
size_t hal_ram_used(void);
size_t hal_ram_free(void);
// 802.15.4 (O-QPSK 250 kbps) radio register setup for the MCXW71 transceiver.
// Values are taken from the NXP XCVR driver, release/25.06.00, configs/gen45:
// XCVR_RadioGenRegSetup() and XCVR_RadioGenRBMESetup() with
// xcvr_oqpsk_802p15p4_250kbps_full_config and xcvr_ble_uncoded_config.
// https://github.com/nxp-mcuxpresso/mcuxsdk-middleware-xcvr
// Copyright 2016 Freescale Semiconductor, Inc.
// Copyright 2018-2023 NXP
// SPDX-License-Identifier: BSD-3-Clause
// One radio register write
struct w71_radio_reg {
volatile uint32_t *reg; // Register address
uint32_t val; // Value to write
};
// Register writes, in the order the NXP driver performs them
static const struct w71_radio_reg s_radio_regs[] = {
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[0].DMD_WAVE_REG0, 0x0420b3d3},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[0].DMD_WAVE_REG1, 0x31d78f00},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[0].DMD_WAVE_REG2, 0x00025a6d},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[1].DMD_WAVE_REG0, 0x0420b3d3},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[1].DMD_WAVE_REG1, 0x38d78f00},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[1].DMD_WAVE_REG2, 0x0000403c},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[2].DMD_WAVE_REG0, 0x3c00420b},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[2].DMD_WAVE_REG1, 0x082c8100},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[2].DMD_WAVE_REG2, 0x0003c004},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[3].DMD_WAVE_REG0, 0x3c00420b},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[3].DMD_WAVE_REG1, 0x0f2c8100},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[3].DMD_WAVE_REG2, 0x0001b5d3},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[4].DMD_WAVE_REG0, 0x0403ce35},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[4].DMD_WAVE_REG1, 0x31d78f00},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[4].DMD_WAVE_REG2, 0x00025a6d},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[5].DMD_WAVE_REG0, 0x0403ce35},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[5].DMD_WAVE_REG1, 0x38d78f00},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[5].DMD_WAVE_REG2, 0x0000403c},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[6].DMD_WAVE_REG0, 0x3ce35c6d},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[6].DMD_WAVE_REG1, 0x082c8100},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[6].DMD_WAVE_REG2, 0x0003c004},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[7].DMD_WAVE_REG0, 0x3ce35c6d},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[7].DMD_WAVE_REG1, 0x0f2c8100},
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[7].DMD_WAVE_REG2, 0x0001b5d3},
{&XCVR_2P4GHZ_PHY->DMD_CTRL0, 0x6d940101},
{&XCVR_2P4GHZ_PHY->DMD_CTRL1, 0xa8049008},
{&XCVR_2P4GHZ_PHY->FSK_CFG0, 0x15001502},
{&XCVR_2P4GHZ_PHY->FSK_CFG1, 0x0f080020},
{&XCVR_2P4GHZ_PHY->FSK_CFG2, 0x40000000},
{&XCVR_2P4GHZ_PHY->FSK_FAD_CFG, 0x00000000},
{&XCVR_2P4GHZ_PHY->FSK_FAD_CTRL, 0x00000000},
{&XCVR_2P4GHZ_PHY->FSK_PD_CFG0, 0x0000ff0c},
{&XCVR_2P4GHZ_PHY->FSK_PD_CFG1, 0x00000055},
{&XCVR_2P4GHZ_PHY->FSK_PD_CFG2, 0x00f600f6},
{&XCVR_2P4GHZ_PHY->FSK_PD_PH[0], 0x00030503},
{&XCVR_2P4GHZ_PHY->FSK_PD_PH[1], 0x003d3b3d},
{&XCVR_2P4GHZ_PHY->FSK_PT, 0x00000048},
{&XCVR_2P4GHZ_PHY->LR_AA_CFG, 0x06020696},
{&XCVR_2P4GHZ_PHY->LR_PD_CFG, 0x0001019a},
{&XCVR_2P4GHZ_PHY->LR_PD_PH[0], 0x3d383028},
{&XCVR_2P4GHZ_PHY->LR_PD_PH[1], 0x20283038},
{&XCVR_2P4GHZ_PHY->LR_PD_PH[2], 0x03081018},
{&XCVR_2P4GHZ_PHY->LR_PD_PH[3], 0x20181008},
{&XCVR_2P4GHZ_PHY->MISC, 0x03ff00a0},
{&XCVR_2P4GHZ_PHY->PREPHY_MISC, 0x00000c10},
{&XCVR_2P4GHZ_PHY->RTT_CTRL, 0x00004166},
{&XCVR_2P4GHZ_PHY->RTT_REF, 0x00806348},
{&XCVR_2P4GHZ_PHY->SM_CFG, 0x00a02432},
{&RADIO_CTRL->LL_CTRL, 0x00000001},
{&XCVR_ANALOG->LDO_0, 0x04440400},
{&XCVR_ANALOG->LDO_1, 0x00000005},
{&XCVR_ANALOG->PLL, 0x00000040},
{&XCVR_ANALOG->RX_0, 0x01000002},
{&XCVR_ANALOG->RX_1, 0x00022008},
{&XCVR_ANALOG->TX_DAC_PA, 0x01000000},
{&XCVR_ANALOG->XO_DIST, 0x00000000},
{&XCVR_MISC->IPS_FO_ADDR[0], 0x00000370},
{&XCVR_MISC->IPS_FO_ADDR[1], 0x0000002c},
{&XCVR_MISC->IPS_FO_DRS0_DATA[0], 0x00000000},
{&XCVR_MISC->IPS_FO_DRS0_DATA[1], 0x00000000},
{&XCVR_MISC->IPS_FO_DRS1_DATA[0], 0x00000000},
{&XCVR_MISC->IPS_FO_DRS1_DATA[1], 0x00000000},
{&XCVR_MISC->LDO_TRIM_0, 0x00020000},
{&XCVR_MISC->LDO_TRIM_1, 0x00000000},
{&XCVR_MISC->XCVR_CTRL, 0x00130180},
{&XCVR_PLL_DIG->CHAN_MAP, 0x00040000},
{&XCVR_PLL_DIG->CHAN_MAP_EXT, 0x00100000},
{&XCVR_PLL_DIG->DATA_RATE_OVRD_CTRL1, 0x00000089},
{&XCVR_PLL_DIG->DATA_RATE_OVRD_CTRL2, 0x00180000},
{&XCVR_PLL_DIG->DELAY_MATCH, 0x00050102},
{&XCVR_PLL_DIG->HPMCAL_CTRL, 0x00002000},
{&XCVR_PLL_DIG->HPM_BUMP, 0x00440144},
{&XCVR_PLL_DIG->HPM_CAL_TIMING, 0x00000000},
{&XCVR_PLL_DIG->HPM_CTRL, 0x00840000},
{&XCVR_PLL_DIG->HPM_SDM_RES, 0x00ff0000},
{&XCVR_PLL_DIG->LOCK_DETECT, 0x0e1a1800},
{&XCVR_PLL_DIG->LPM_CTRL, 0x88880a00},
{&XCVR_PLL_DIG->LPM_SDM_CTRL1, 0x00260000},
{&XCVR_PLL_DIG->MOD_CTRL, 0x80000000},
{&XCVR_PLL_DIG->PLL_DATARATE_CTRL, 0x04126644},
{&XCVR_PLL_DIG->TUNING_CAP_RX_CTRL, 0x00000000},
{&XCVR_PLL_DIG->TUNING_CAP_TX_CTRL, 0x00ffffa3},
{&XCVR_RX_DIG->ACQ_FILT_0_3, 0x737f0502},
{&XCVR_RX_DIG->ACQ_FILT_0_3_DRS, 0x0f7f3a01},
{&XCVR_RX_DIG->ACQ_FILT_10_11, 0x00ef0061},
{&XCVR_RX_DIG->ACQ_FILT_10_11_DRS, 0x012b0021},
{&XCVR_RX_DIG->ACQ_FILT_4_7, 0xf8221478},
{&XCVR_RX_DIG->ACQ_FILT_4_7_DRS, 0x3c06617f},
{&XCVR_RX_DIG->ACQ_FILT_8_9, 0x01d501bc},
{&XCVR_RX_DIG->ACQ_FILT_8_9_DRS, 0x019401f3},
{&XCVR_RX_DIG->AGC_CTRL, 0x587c8d19},
{&XCVR_RX_DIG->AGC_CTRL_STAT, 0x0000872c},
{&XCVR_RX_DIG->AGC_IDX0_GAIN_CFG, 0x000000f2},
{&XCVR_RX_DIG->AGC_IDX0_GAIN_VAL, 0x000003fd},
{&XCVR_RX_DIG->AGC_IDX0_THR, 0x0000001e},
{&XCVR_RX_DIG->AGC_IDX10_GAIN_CFG, 0x00004a03},
{&XCVR_RX_DIG->AGC_IDX10_GAIN_VAL, 0x000000eb},
{&XCVR_RX_DIG->AGC_IDX10_THR, 0x0096001e},
{&XCVR_RX_DIG->AGC_IDX11_GAIN_CFG, 0x00007e03},
{&XCVR_RX_DIG->AGC_IDX11_GAIN_VAL, 0x000000fa},
{&XCVR_RX_DIG->AGC_IDX11_THR, 0x00780000},
{&XCVR_RX_DIG->AGC_IDX1_GAIN_CFG, 0x000001b2},
{&XCVR_RX_DIG->AGC_IDX1_GAIN_VAL, 0x0000000b},
{&XCVR_RX_DIG->AGC_IDX1_THR, 0x015e001e},
{&XCVR_RX_DIG->AGC_IDX2_GAIN_CFG, 0x00000123},
{&XCVR_RX_DIG->AGC_IDX2_GAIN_VAL, 0x00000026},
{&XCVR_RX_DIG->AGC_IDX2_THR, 0x00c8001e},
{&XCVR_RX_DIG->AGC_IDX3_GAIN_CFG, 0x00000113},
{&XCVR_RX_DIG->AGC_IDX3_GAIN_VAL, 0x0000003c},
{&XCVR_RX_DIG->AGC_IDX3_THR, 0x00c8001e},
{&XCVR_RX_DIG->AGC_IDX4_GAIN_CFG, 0x00000083},
{&XCVR_RX_DIG->AGC_IDX4_GAIN_VAL, 0x00000057},
{&XCVR_RX_DIG->AGC_IDX4_THR, 0x00c8001e},
{&XCVR_RX_DIG->AGC_IDX5_GAIN_CFG, 0x00000183},
{&XCVR_RX_DIG->AGC_IDX5_GAIN_VAL, 0x0000006d},
{&XCVR_RX_DIG->AGC_IDX5_THR, 0x00fa001e},
{&XCVR_RX_DIG->AGC_IDX6_GAIN_CFG, 0x00000203},
{&XCVR_RX_DIG->AGC_IDX6_GAIN_VAL, 0x00000087},
{&XCVR_RX_DIG->AGC_IDX6_THR, 0x012c001e},
{&XCVR_RX_DIG->AGC_IDX7_GAIN_CFG, 0x00000603},
{&XCVR_RX_DIG->AGC_IDX7_GAIN_VAL, 0x000000a0},
{&XCVR_RX_DIG->AGC_IDX7_THR, 0x012c001e},
{&XCVR_RX_DIG->AGC_IDX8_GAIN_CFG, 0x00000e03},
{&XCVR_RX_DIG->AGC_IDX8_GAIN_VAL, 0x000000b7},
{&XCVR_RX_DIG->AGC_IDX8_THR, 0x012c001e},
{&XCVR_RX_DIG->AGC_IDX9_GAIN_CFG, 0x00002203},
{&XCVR_RX_DIG->AGC_IDX9_GAIN_VAL, 0x000000d2},
{&XCVR_RX_DIG->AGC_IDX9_THR, 0x0096001e},
{&XCVR_RX_DIG->AGC_MIS_GAIN_CFG, 0x00000001},
{&XCVR_RX_DIG->AGC_THR_FAST, 0x0190000f},
{&XCVR_RX_DIG->AGC_THR_FAST_DRS, 0x0190001e},
{&XCVR_RX_DIG->AGC_THR_MIS, 0x00960028},
{&XCVR_RX_DIG->AGC_TIMING0, 0x04102398},
{&XCVR_RX_DIG->AGC_TIMING0_DRS, 0x40002000},
{&XCVR_RX_DIG->AGC_TIMING1, 0x190242a7},
{&XCVR_RX_DIG->AGC_TIMING1_DRS, 0x000002a7},
{&XCVR_RX_DIG->AGC_TIMING2, 0xe000007f},
{&XCVR_RX_DIG->AGC_TIMING2_DRS, 0x0000007f},
{&XCVR_RX_DIG->CTRL0, 0x68063083},
{&XCVR_RX_DIG->CTRL0_DRS, 0x000030c0},
{&XCVR_RX_DIG->CTRL1, 0x00000180},
{&XCVR_RX_DIG->DCOC_CTRL0, 0x04332d28},
{&XCVR_RX_DIG->DCOC_CTRL0_DRS, 0x00000114},
{&XCVR_RX_DIG->DCOC_CTRL1, 0x00000000},
{&XCVR_RX_DIG->DEMOD_FILT_0_1, 0x01fe01fd},
{&XCVR_RX_DIG->DEMOD_FILT_0_1_DRS, 0x00ff0003},
{&XCVR_RX_DIG->DEMOD_FILT_2_4, 0x0ba20824},
{&XCVR_RX_DIG->DEMOD_FILT_2_4_DRS, 0x124221ff},
{&XCVR_RX_DIG->NB_RSSI_CTRL0, 0xed472234},
{&XCVR_RX_DIG->NB_RSSI_CTRL1, 0x00000000},
{&XCVR_RX_DIG->RCCAL_CTRL0, 0x0000003a},
{&XCVR_RX_DIG->RSSI_GLOBAL_CTRL, 0x80008330},
{&XCVR_RX_DIG->WB_RSSI_CTRL, 0x00223342},
{&XCVR_TSM->CTRL, 0xff000000},
{&XCVR_TSM->END_OF_SEQ, 0x5c5a7270},
{&XCVR_TSM->FAST_CTRL1, 0x24006f00},
{&XCVR_TSM->FAST_CTRL2, 0x66284208},
{&XCVR_TSM->FAST_CTRL3, 0x18001800},
{&XCVR_TSM->RECYCLE_COUNT, 0x00001258},
{&XCVR_TSM->TIMING00, 0xffffffff},
{&XCVR_TSM->TIMING01, 0xffffffff},
{&XCVR_TSM->TIMING02, 0xffffffff},
{&XCVR_TSM->TIMING03, 0xffffffff},
{&XCVR_TSM->TIMING04, 0xffffffff},
{&XCVR_TSM->TIMING05, 0xffffffff},
{&XCVR_TSM->TIMING06, 0xffffffff},
{&XCVR_TSM->TIMING07, 0xffffffff},
{&XCVR_TSM->TIMING08, 0xffffffff},
{&XCVR_TSM->TIMING09, 0x5700ffff},
{&XCVR_TSM->TIMING10, 0x11081108},
{&XCVR_TSM->TIMING11, 0x5b137113},
{&XCVR_TSM->TIMING12, 0x5b19714f},
{&XCVR_TSM->TIMING13, 0x572dffff},
{&XCVR_TSM->TIMING14, 0x0000716f},
{&XCVR_TSM->TIMING15, 0x5b58716f},
{&XCVR_TSM->TIMING16, 0x59580000},
{&XCVR_TSM->TIMING17, 0x5b580000},
{&XCVR_TSM->TIMING18, 0x5b580000},
{&XCVR_TSM->TIMING19, 0x11061106},
{&XCVR_TSM->TIMING20, 0x11001100},
{&XCVR_TSM->TIMING21, 0x19001100},
{&XCVR_TSM->TIMING22, 0x58006f00},
{&XCVR_TSM->TIMING23, 0x58006f00},
{&XCVR_TSM->TIMING24, 0x5b007100},
{&XCVR_TSM->TIMING25, 0x5c007200},
{&XCVR_TSM->TIMING26, 0x5c007200},
{&XCVR_TSM->TIMING27, 0x5c007200},
{&XCVR_TSM->TIMING28, 0x5c007200},
{&XCVR_TSM->TIMING29, 0xffff7200},
{&XCVR_TSM->TIMING30, 0x5c00ffff},
{&XCVR_TSM->TIMING31, 0x5b087108},
{&XCVR_TSM->TIMING32, 0x1911ffff},
{&XCVR_TSM->TIMING33, 0x23115a11},
{&XCVR_TSM->TIMING34, 0x5b117111},
{&XCVR_TSM->TIMING35, 0x5b117111},
{&XCVR_TSM->TIMING36, 0x5b117111},
{&XCVR_TSM->TIMING37, 0x5b117111},
{&XCVR_TSM->TIMING38, 0x5b117111},
{&XCVR_TSM->TIMING39, 0x5b2b7111},
{&XCVR_TSM->TIMING40, 0xffff7111},
{&XCVR_TSM->TIMING41, 0xffff7111},
{&XCVR_TSM->TIMING42, 0x5b127112},
{&XCVR_TSM->TIMING43, 0x5b12ffff},
{&XCVR_TSM->TIMING44, 0x5b12ffff},
{&XCVR_TSM->TIMING45, 0xffff7112},
{&XCVR_TSM->TIMING46, 0xffff716f},
{&XCVR_TSM->TIMING47, 0x5b137157},
{&XCVR_TSM->TIMING48, 0x5b23715a},
{&XCVR_TSM->TIMING49, 0x5b23715a},
{&XCVR_TSM->TIMING50, 0x5b2bffff},
{&XCVR_TSM->TIMING51, 0x5b2bffff},
{&XCVR_TSM->TIMING52, 0xffffffff},
{&XCVR_TSM->WU_LATENCY, 0x00060006},
{&XCVR_TX_DIG->DATARATE_CONFIG_FILTER_CTRL, 0x00010000},
{&XCVR_TX_DIG->DATARATE_CONFIG_FSK_CTRL, 0x08001800},
{&XCVR_TX_DIG->DATARATE_CONFIG_GFSK_CTRL, 0x00000804},
{&XCVR_TX_DIG->DATA_PADDING_CTRL, 0x00001012},
{&XCVR_TX_DIG->DATA_PADDING_CTRL_1, 0x00001600},
{&XCVR_TX_DIG->DATA_PADDING_CTRL_2, 0x08001800},
{&XCVR_TX_DIG->FSK_CTRL, 0x04001c00},
{&XCVR_TX_DIG->GFSK_COEFF_0_1, 0x00040001},
{&XCVR_TX_DIG->GFSK_COEFF_2_3, 0x0028000d},
{&XCVR_TX_DIG->GFSK_COEFF_4_5, 0x00c00063},
{&XCVR_TX_DIG->GFSK_COEFF_6_7, 0x0176012c},
{&XCVR_TX_DIG->GFSK_CTRL, 0x000003fd},
{&XCVR_TX_DIG->IMAGE_FILTER_CTRL, 0x00000000},
{&XCVR_TX_DIG->PA_CTRL, 0x00020126},
{&XCVR_TX_DIG->PA_RAMP_TBL0, 0x06040201},
{&XCVR_TX_DIG->PA_RAMP_TBL1, 0x14100c09},
{&XCVR_TX_DIG->PA_RAMP_TBL2, 0x26211c18},
{&XCVR_TX_DIG->PA_RAMP_TBL3, 0x3c38322c},
{&XCVR_TX_DIG->SWITCH_TX_CTRL, 0x00000000},
{&XCVR_TX_DIG->TXDIG_CTRL, 0x00000007},
{&XCVR_2P4GHZ_PHY->DMD_CTRL1, 0x8c049008},
{&RBME->CRCW_CFG, 0x00000001},
{&RBME->CRCW_CFG2, 0x00000000},
{&RBME->CRCW_CFG3, 0x00000403},
{&RBME->CRC_INIT, 0x55555500},
{&RBME->CRC_PHR_SZ, 0x00000002},
{&RBME->CRC_POLY, 0x00065b00},
{&RBME->CRC_XOR_OUT, 0x00000000},
{&RBME->FCP_CFG, 0x00000000},
{&RBME->FEC_BSZ_OV_B4SP, 0x00000000},
{&RBME->FEC_CFG1, 0x00000000},
{&RBME->FEC_CFG2, 0x00000000},
{&RBME->FRAME_OVER_SZ, 0x00000000},
{&RBME->NPAYL_OVER_SZ, 0x00000000},
{&RBME->PKT_SZ, 0x00244000},
{&RBME->SPREAD_CFG, 0x00000000},
{&RBME->WHITEN_CFG, 0x00410705},
{&RBME->WHITEN_POLY, 0x00000004},
{&RBME->WHITEN_SZ_THR, 0x00000000},
};
+2 -1
View File
@@ -1,7 +1,7 @@
/* MCXW716C memory map, see MCUXpresso SDK gcc/mcxw716_flash.ld */
ENTRY(Reset_Handler);
MEMORY {
flash(rx) : ORIGIN = 0x00000000, LENGTH = 1024k
flash(rx) : ORIGIN = 0x00000000, LENGTH = 512k /* Upper half: OTA staging */
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 112k
}
__StackTop = ORIGIN(sram) + LENGTH(sram); /* Stack points here */
@@ -16,6 +16,7 @@ SECTIONS {
__data_start__ = .;
_sdata = .;
*(.first_data)
*(.iram .iram.*) /* OTA flash operations, copied to RAM by startup */
*(.data SORT(.data.*))
. = ALIGN(4);
__data_end__ = .;
+61 -4
View File
@@ -1,5 +1,10 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
//
// IPv4 over IEEE 802.15.4 on FRDM-MCXW71, Mongoose built-in TCP/IP stack.
// The radio carries Ethernet frames, channel 15, PAN ID 0xabcd. With the
// FRDM-RW612 eth_radio_bridge tutorial, gets an address from the LAN's DHCP
// server and is reachable from the LAN. Serves pages on ports 80 and 443
#include "hal.h"
#include "mongoose.h"
@@ -14,19 +19,71 @@ uint64_t mg_millis(void) {
return hal_get_tick();
}
// Self-signed P-256 certificate and key for the HTTPS listener. Created by:
// openssl ecparam -genkey -name prime256v1 -noout -out key.pem
// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg
#define TLS_CERT \
"-----BEGIN CERTIFICATE-----\n" \
"MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \
"FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \
"MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \
"SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \
"uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \
"MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \
"BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \
"lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \
"BCjX++LI\n" \
"-----END CERTIFICATE-----\n"
#define TLS_KEY \
"-----BEGIN EC PRIVATE KEY-----\n" \
"MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \
"AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \
"LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \
"-----END EC PRIVATE KEY-----\n"
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
} else if (mg_match(hm->uri, mg_str("/api/crash"), NULL)) {
__asm volatile("udf #0"); // Trigger HardFault
} else {
mg_http_reply(c, 200, "",
"Hi from Mongoose!\n"
"tick: %llu\n"
"ram used: %zu\n"
"ram free: %zu\n",
hal_get_tick(), hal_ram_used(), hal_ram_free());
}
}
}
int main(void) {
hal_clock_init();
hal_uart_init(UART_DEBUG, 115200);
mg_log_set_fn(log_fn, UART_DEBUG);
if (!hal_radio_init()) MG_ERROR(("Radio init failed"));
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0:80", http_ev_handler, NULL);
mg_http_listen(&mgr, "https://0.0.0.0:443", http_ev_handler, NULL);
mg_mqtt_init(&mgr);
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free()));
MG_INFO(("Crash record: %M", mg_print_crash_record));
for (;;) {
static uint64_t timer;
if (hal_timer_expired(&timer, 1000, hal_get_tick())) {
MG_INFO(("Hi from Mongoose, tick %llu", hal_get_tick()));
}
mg_mgr_poll(&mgr, 0);
mg_mqtt_poll(&mgr);
}
return 0;
@@ -1,7 +1,23 @@
#pragma once
#include "MCXW716C.h"
// See https://mongoose.ws/documentation/#build-options
// See https://mongoose.ws/docs/getting-started/build-environment/
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_TLS MG_TLS_BUILTIN
#define MG_OTA MG_OTA_MCXW71
#define MG_ENABLE_TCPIP 1 // Built-in TCP/IP stack. No network driver yet
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_DRIVER_MCXW71 1
#define MG_ENABLE_CUSTOM_MILLIS 1
#define MG_ENABLE_CUSTOM_RANDOM 1
#define MG_ENABLE_TCPIP_TCPRTX 1 // Link can be lossy, enable retransmit
// Stable MAC, so the DHCP address stays the same across reboots: a hash of
// the 128-bit chip unique ID, MSCM UID, locally administered
#define MG_SET_MAC_ADDRESS(mac) \
do { \
uint8_t b_[32]; \
mg_sha256(b_, (uint8_t *) MSCM->UID, sizeof(MSCM->UID)); \
(mac)[0] = 2, (mac)[1] = b_[1], (mac)[2] = b_[2], (mac)[3] = b_[3]; \
(mac)[4] = b_[4], (mac)[5] = b_[5]; \
} while (0)
@@ -0,0 +1,63 @@
# Environment setup: https://mongoose.ws/documentation/tutorials/tools/
MGDIR = ../../../..
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wno-conversion -Wno-sign-conversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -Wno-cast-function-type -Wno-old-style-declaration
CFLAGS += -Wno-unused-parameter
CFLAGS += -I. -I$(MGDIR) -Imongoose -Icmsis_core/CMSIS/Core/Include/
CFLAGS += $(addprefix -Icmsis_rw612/devices/RW612/drivers/romapi/,bootloader nboot otp iap flexspi)
CFLAGS += -Icmsis_rw612/devices/RW612/ -Icmsis_rw612/devices/RW612/periph/ -Icmsis_rw612/devices/RW612/drivers/
CFLAGS += -DCPU_RW612ETA2I -DCPU_RW612ETA2I_cm33_no_dsp
CFLAGS += -DBOOT_HEADER_ENABLE=1
CFLAGS += -DRADIO_FW='"$(RADIO_FW)"'
CFLAGS += -mcpu=cortex-m33+nodsp -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 $(CFLAGS_EXTRA)
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs=nano.specs -lc -lgcc -Wl,--gc-sections
SOURCES = main.c hal.c radio.c startup.c flexspi.c flash_config.c $(MGDIR)/mongoose.c
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_clock.c
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_trng.c
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_reset.c
SOURCES += $(addprefix cmsis_rw612/devices/RW612/drivers/,fsl_power.c fsl_ocotp.c \
romapi/bootloader/fsl_romapi.c romapi/nboot/fsl_romapi_nboot.c romapi/otp/fsl_romapi_otp.c)
# NXP BLE/802.15.4 combo firmware for CPU2, linked into our image. NXP license:
# https://github.com/NXP/wifi_nb_fw/blob/nxp-v4.4.3/LICENSE.txt
RADIO_FW = rw61x_sb_ble_15d4_combo_a2.bin
RADIO_FW_URL = https://github.com/NXP/wifi_nb_fw/raw/nxp-v4.4.3/rw61x/$(RADIO_FW)
RADIO_FW_SHA256 = 2efac0d771e371377d57159c40f0909c7ecc6f6b950ce69ed2a3e02ac7eb686a
all build: firmware.bin
firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
arm-none-eabi-size --format=berkeley $<
firmware.elf: cmsis_core cmsis_rw612 $(RADIO_FW) $(SOURCES) $(wildcard *.h) link.ld Makefile mongoose_config.h
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
cmsis_core:
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
$(RADIO_FW):
curl -sL $(RADIO_FW_URL) -o $@.tmp
echo "$(RADIO_FW_SHA256) $@.tmp" | shasum -a 256 -c - >/dev/null
mv $@.tmp $@
cmsis_rw612:
curl -sL https://mcuxpresso.nxp.com/cmsis_pack/repo/NXP.RW612_DFP.25.12.00.pack -o NXP.RW612_DFP.25.12.00.pack
unzip -q NXP.RW612_DFP.25.12.00.pack -d $@
rm NXP.RW612_DFP.25.12.00.pack
# Show all symbols sorted by size
show_symbols_sorted_by_size: firmware.bin
arm-none-eabi-readelf -sW firmware.elf | sed 1,3d | sed 's,^.*: ,,' | tr -s ' ' | cut -d ' ' -f 2,7 | sort -nk1 | grep -v '^0 '
flash: firmware.bin
(echo 'loadfile firmware.elf'; echo 'reset'; echo 'exit';) | JLinkExe -device RW612 -if SWD -speed 4000 -autoconnect 1 -NoGui 1 -ExitOnError 1
# JRun --nogui --device RW612 --if SWD firmware.elf
clean:
rm -rf firmware.* cmsis_* mongoose/mongoose.* $(RADIO_FW)
@@ -0,0 +1,86 @@
/*
* Copyright 2021-2024 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "flash_config.h"
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.flash_config"
#endif
/*******************************************************************************
* Code
******************************************************************************/
#if defined(BOOT_HEADER_ENABLE) && (BOOT_HEADER_ENABLE == 1)
#if defined(__ARMCC_VERSION) || defined(__GNUC__)
__attribute__((section(".flash_conf"), used))
#elif defined(__ICCARM__)
#pragma location = ".flash_conf"
#endif
const fc_flexspi_nor_config_t flexspi_config = {
.memConfig =
{
.tag = FC_BLOCK_TAG,
.version = FC_BLOCK_VERSION,
.readSampleClkSrc = 1,
.csHoldTime = 3,
.csSetupTime = 3,
.deviceModeCfgEnable = 1,
.deviceModeSeq = {.seqNum = 1, .seqId = 2},
.deviceModeArg = 0x02,
.configCmdEnable = 0,
.deviceType = 0x1,
.sflashPadType = kSerialFlash_4Pads,
.serialClkFreq = 5,
.sflashA1Size = 0x4000000U,
.sflashA2Size = 0,
.sflashB1Size = 0,
.sflashB2Size = 0,
.lookupTable =
{
/* Read */
[0] = FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0xEC, FC_RADDR_SDR, FC_FLEXSPI_4PAD, 0x20),
[1] = FC_FLEXSPI_LUT_SEQ(FC_MODE8_SDR, FC_FLEXSPI_4PAD, 0xF0, FC_DUMMY_SDR, FC_FLEXSPI_4PAD, 0x04),
[2] = FC_FLEXSPI_LUT_SEQ(FC_READ_SDR, FC_FLEXSPI_4PAD, 0x04, FC_STOP_EXE, FC_FLEXSPI_1PAD, 0x00),
/* Read Status */
[4 * 1 + 0] =
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x05, FC_READ_SDR, FC_FLEXSPI_1PAD, 0x04),
/* Write Status */
[4 * 2 + 0] =
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x31, FC_WRITE_SDR, FC_FLEXSPI_1PAD, 0x01),
/* Write Enable */
[4 * 3 + 0] =
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x06, FC_STOP_EXE, FC_FLEXSPI_1PAD, 0x00),
/* Sector erase */
[4 * 5 + 0] =
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x21, FC_RADDR_SDR, FC_FLEXSPI_1PAD, 0x20),
/* Block erase */
[4 * 8 + 0] =
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0xDC, FC_RADDR_SDR, FC_FLEXSPI_1PAD, 0x20),
/* Page program */
[4 * 9 + 0] =
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x34, FC_RADDR_SDR, FC_FLEXSPI_1PAD, 0x20),
[4 * 9 + 1] =
FC_FLEXSPI_LUT_SEQ(FC_WRITE_SDR, FC_FLEXSPI_4PAD, 0x00, FC_STOP_EXE, FC_FLEXSPI_1PAD, 0x00),
/* chip erase */
[4 * 11 + 0] =
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0xC7, FC_STOP_EXE, FC_FLEXSPI_1PAD, 0x00),
},
},
.pageSize = 0x100,
.sectorSize = 0x1000,
.ipcmdSerialClkFreq = 0,
.blockSize = 0x10000,
.fcb_fill[0] = 0xFFFFFFFF,
};
#endif /* BOOT_HEADER_ENABLE */
@@ -0,0 +1,221 @@
/*
* Copyright 2021-2023 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __FLASH_CONFIG__
#define __FLASH_CONFIG__
#include <stdint.h>
#include <stdbool.h>
#include "fsl_common.h"
/*! @name Driver version */
/*@{*/
/*! @brief FLASH_CONFIG driver version 2.0.0. */
#define FSL_FLASH_CONFIG_DRIVER_VERSION (MAKE_VERSION(2, 0, 0))
/*@}*/
/*******************************************************************************
* Definition
******************************************************************************/
/* FLEXSPI memory config block related defintions */
#define FC_BLOCK_TAG (0x42464346)
#define FC_BLOCK_VERSION (0x00000000)
#define FC_CMD_SDR 0x01
#define FC_CMD_DDR 0x21
#define FC_RADDR_SDR 0x02
#define FC_RADDR_DDR 0x22
#define FC_CADDR_SDR 0x03
#define FC_CADDR_DDR 0x23
#define FC_MODE1_SDR 0x04
#define FC_MODE1_DDR 0x24
#define FC_MODE2_SDR 0x05
#define FC_MODE2_DDR 0x25
#define FC_MODE4_SDR 0x06
#define FC_MODE4_DDR 0x26
#define FC_MODE8_SDR 0x07
#define FC_MODE8_DDR 0x27
#define FC_WRITE_SDR 0x08
#define FC_WRITE_DDR 0x28
#define FC_READ_SDR 0x09
#define FC_READ_DDR 0x29
#define FC_LEARN_SDR 0x0A
#define FC_LEARN_DDR 0x2A
#define FC_DATSZ_SDR 0x0B
#define FC_DATSZ_DDR 0x2B
#define FC_DUMMY_SDR 0x0C
#define FC_DUMMY_DDR 0x2C
#define FC_DUMMY_RWDS_SDR 0x0D
#define FC_DUMMY_RWDS_DDR 0x2D
#define FC_JMP_ON_CS 0x1F
#define FC_STOP_EXE 0
#define FC_FLEXSPI_1PAD 0
#define FC_FLEXSPI_2PAD 1
#define FC_FLEXSPI_4PAD 2
#define FC_FLEXSPI_8PAD 3
#define FC_FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \
(FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \
FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1))
/* !@brief Data pad used in Read command */
enum
{
kSerialFlash_1Pads = 1,
kSerialFlash_2Pads = 2,
kSerialFlash_4Pads = 4,
kSerialFlash_8Pads = 8,
};
/* !@brief FLEXSPI clock configuration - In High speed boot mode mode */
enum
{
kFlexSpiSerialClk_30MHz = 1,
kFlexSpiSerialClk_50MHz = 2,
kFlexSpiSerialClk_60MHz = 3,
kFlexSpiSerialClk_80MHz = 4,
kFlexSpiSerialClk_100MHz = 5,
kFlexSpiSerialClk_120MHz = 6,
kFlexSpiSerialClk_133MHz = 7,
kFlexSpiSerialClk_166MHz = 8,
kFlexSpiSerialClk_200MHz = 9,
};
/* !@brief FLEXSPI clock configuration - In Normal boot SDR mode */
enum
{
kFlexSpiSerialClk_SDR_24MHz = 1,
kFlexSpiSerialClk_SDR_48MHz = 2,
};
/* !@brief FLEXSPI clock configuration - In Normal boot DDR mode */
enum
{
kFlexSpiSerialClk_DDR_48MHz = 1,
};
/* !@brief Misc feature bit definitions */
enum
{
kFlexSpiMiscOffset_DiffClkEnable = 0, /* !< Bit for Differential clock enable */
kFlexSpiMiscOffset_WordAddressableEnable = 3, /* !< Bit for Word Addressable enable */
kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, /* !< Bit for Safe Configuration Frequency enable */
kFlexSpiMiscOffset_DdrModeEnable = 6, /* !< Bit for DDR clock confiuration indication. */
};
/* !@brief Flash Configuration Command Type */
enum
{
kDeviceConfigCmdType_Generic, /* !< Generic command, for example: configure dummy cycles, drive strength, etc */
kDeviceConfigCmdType_QuadEnable, /* !< Quad Enable command */
kDeviceConfigCmdType_Spi2Xpi, /* !< Switch from SPI to DPI/QPI/OPI mode */
kDeviceConfigCmdType_Xpi2Spi, /* !< Switch from DPI/QPI/OPI to SPI mode */
kDeviceConfigCmdType_Spi2NoCmd, /* !< Switch to 0-4-4/0-8-8 mode */
kDeviceConfigCmdType_Reset, /* !< Reset device command */
};
typedef struct _fc_flexspi_dll_time
{
uint8_t time_100ps; /* !< Data valid time, in terms of 100ps */
uint8_t delay_cells; /* !< Data valid time, in terms of delay cells */
} fc_flexspi_dll_time_t;
/* !@brief FlexSPI LUT Sequence structure */
typedef struct _fc_flexspi_lut_seq
{
uint8_t seqNum; /* !< Sequence Number, valid number: 1-16 */
uint8_t seqId; /* !< Sequence Index, valid number: 0-15 */
uint16_t reserved;
} fc_flexspi_lut_seq_t;
/* !@brief FlexSPI Memory Configuration Block */
typedef struct _fc_flexspi_mem_config
{
uint32_t tag; /* !< [0x000-0x003] Tag, fixed value 0x42464346UL */
uint32_t version; /* !< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix */
uint32_t reserved0; /* !< [0x008-0x00b] Reserved for future use */
uint8_t readSampleClkSrc; /* !< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 */
uint8_t csHoldTime; /* !< [0x00d-0x00d] CS hold time, default value: 3 */
uint8_t csSetupTime; /* !< [0x00e-0x00e] CS setup time, default value: 3 */
uint8_t columnAddressWidth; /* !< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For
Serial NAND, need to refer to datasheet */
uint8_t deviceModeCfgEnable; /* !< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable */
uint8_t deviceModeType; /* !< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch,
Generic configuration, etc. */
uint16_t waitTimeCfgCommands; /* !< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for
DPI/QPI/OPI switch or reset command */
fc_flexspi_lut_seq_t deviceModeSeq; /* !< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] -
LUt sequence number, [31:16] Reserved */
uint32_t deviceModeArg; /* !< [0x018-0x01b] Argument/Parameter for device configuration */
uint8_t configCmdEnable; /* !< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable */
uint8_t configModeType[3]; /* !< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe */
fc_flexspi_lut_seq_t configCmdSeqs[3]; /* !< [0x020-0x02b] Sequence info for Device Configuration command, similar
as deviceModeSeq */
uint32_t reserved1; /* !< [0x02c-0x02f] Reserved for future use */
uint32_t configCmdArgs[3]; /* !< [0x030-0x03b] Arguments/Parameters for device Configuration commands */
uint32_t reserved2; /* !< [0x03c-0x03f] Reserved for future use */
uint32_t controllerMiscOption; /* !< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for
more details */
uint8_t deviceType; /* !< [0x044-0x044] Device Type: See Flash Type Definition for more details */
uint8_t sflashPadType; /* !< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal */
uint8_t serialClkFreq; /* !< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot
Chapter for more details */
uint8_t lutCustomSeqEnable; /* !< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot
be done using 1 LUT sequence, currently, only applicable to HyperFLASH */
uint32_t reserved3[2]; /* !< [0x048-0x04f] Reserved for future use */
uint32_t sflashA1Size; /* !< [0x050-0x053] Size of Flash connected to A1 */
uint32_t sflashA2Size; /* !< [0x054-0x057] Size of Flash connected to A2 */
uint32_t sflashB1Size; /* !< [0x058-0x05b] Size of Flash connected to B1 */
uint32_t sflashB2Size; /* !< [0x05c-0x05f] Size of Flash connected to B2 */
uint32_t csPadSettingOverride; /* !< [0x060-0x063] CS pad setting override value */
uint32_t sclkPadSettingOverride; /* !< [0x064-0x067] SCK pad setting override value */
uint32_t dataPadSettingOverride; /* !< [0x068-0x06b] data pad setting override value */
uint32_t dqsPadSettingOverride; /* !< [0x06c-0x06f] DQS pad setting override value */
uint32_t timeoutInMs; /* !< [0x070-0x073] Timeout threshold for read status command */
uint32_t commandInterval; /* !< [0x074-0x077] CS deselect interval between two commands */
fc_flexspi_dll_time_t dataValidTime[2]; /* !< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B */
uint16_t busyOffset; /* !< [0x07c-0x07d] Busy offset, valid value: 0-31 */
uint16_t busyBitPolarity; /* !< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 -
busy flag is 0 when flash device is busy */
uint32_t lookupTable[64]; /* !< [0x080-0x17f] Lookup table holds Flash command sequences */
fc_flexspi_lut_seq_t lutCustomSeq[12]; /* !< [0x180-0x1af] Customizable LUT Sequences */
uint32_t reserved4[4]; /* !< [0x1b0-0x1bf] Reserved for future use */
} fc_flexspi_mem_config_t;
/*
* Serial NOR configuration block
*/
typedef struct _fc_flexspi_nor_config
{
#if defined(__ARMCC_VERSION) || defined(__ICCARM__)
uint8_t padding[0x400]; /* !< Padding for MDK and IAR */
#endif
fc_flexspi_mem_config_t memConfig; /* !< Common memory configuration info via FlexSPI */
uint32_t pageSize; /* !< Page size of Serial NOR */
uint32_t sectorSize; /* !< Sector size of Serial NOR */
uint8_t ipcmdSerialClkFreq; /* !< Clock frequency for IP command */
uint8_t isUniformBlockSize; /* !< Sector/Block size is the same */
uint8_t isDataOrderSwapped; /* !< Data order (D0, D1, D2, D3) is swapped (D1,D0, D3, D2) */
uint8_t reserved0[1]; /* !< Reserved for future use */
uint8_t serialNorType; /* !< Serial NOR Flash type: 0/1/2/3 */
uint8_t needExitNoCmdMode; /* !< Need to exit NoCmd mode before other IP command */
uint8_t halfClkForNonReadCmd; /* !< Half the Serial Clock for non-read command: true/false */
uint8_t needRestoreNoCmdMode; /* !< Need to Restore NoCmd mode after IP commmand execution */
uint32_t blockSize; /* !< Block size */
uint32_t flashStateCtx; /* !< Flash State Context */
uint32_t reserve2[10]; /* !< Reserved for future use */
uint32_t fcb_fill[0x280]; /* !< Fill */
} fc_flexspi_nor_config_t;
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* __FLASH_CONFIG__ */
@@ -0,0 +1,82 @@
#include "RW612.h"
#include "fsl_flexspi.h" // peripheral structures
__attribute__((section(".iram"))) static void deinit_flash(FLEXSPI_Type *base) {
CLKCTL0->PSCCTL0_SET =
CLKCTL0_PSCCTL0_SET_FLEXSPI0_MASK; // Enable FLEXSPI clock again
base->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK; // Enable FLEXSPI module
// Wait until FLEXSPI is not busy
while (!((base->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) &&
(base->STS0 & FLEXSPI_STS0_SEQIDLE_MASK)))
(void)0;
base->MCR0 |=
FLEXSPI_MCR0_MDIS_MASK; // Disable module during the reset procedure
}
__attribute__((section(".iram"))) static void init_flash(FLEXSPI_Type *base) {
uint32_t status;
uint32_t lastStatus;
uint32_t retry;
// Loopback from DQS pad can maximize RD board flash speed.
if ((base->MCR0 & FLEXSPI_MCR0_RXCLKSRC_MASK) != FLEXSPI_MCR0_RXCLKSRC(1)) {
base->MCR0 =
(base->MCR0 & ~FLEXSPI_MCR0_RXCLKSRC_MASK) | FLEXSPI_MCR0_RXCLKSRC(1);
}
base->DLLCR[0] = 0x1U;
base->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK; // Enable FLEXSPI module
base->MCR0 |= FLEXSPI_MCR0_SWRESET_MASK;
while (base->MCR0 & FLEXSPI_MCR0_SWRESET_MASK)
(void)0;
// Need to wait DLL locked if DLL enabled
if (0U != (base->DLLCR[0] & FLEXSPI_DLLCR_DLLEN_MASK)) {
lastStatus = base->STS2;
retry = 10;
// Wait slave delay line locked and slave reference delay line locked.
do {
status = base->STS2;
if ((status &
(FLEXSPI_STS2_AREFLOCK_MASK | FLEXSPI_STS2_ASLVLOCK_MASK)) ==
(FLEXSPI_STS2_AREFLOCK_MASK | FLEXSPI_STS2_ASLVLOCK_MASK)) {
// Locked
retry = 100;
break;
} else if (status == lastStatus) {
retry--;
} else {
retry = 10;
lastStatus = status;
}
} while (retry > 0);
// According to ERR011377, need to delay at least 100 NOPs to ensure the DLL
// is locked
for (; retry > 0U; retry--)
(void)0;
}
}
__attribute__((section(".iram"))) void
set_flexspi_clock(FLEXSPI_Type *base, uint32_t src, uint32_t divider) {
if ((CLKCTL0->FLEXSPIFCLKSEL != CLKCTL0_FLEXSPIFCLKSEL_SEL(src)) ||
((CLKCTL0->FLEXSPIFCLKDIV & CLKCTL0_FLEXSPIFCLKDIV_DIV_MASK) !=
(divider - 1))) {
// Always deinit FLEXSPI and init FLEXSPI for the flash to make sure the
// flash works correctly after the FLEXSPI root clock changed as the default
// FLEXSPI configuration may does not work for the new root clock frequency.
deinit_flash(base);
// Disable clock before changing clock source
CLKCTL0->PSCCTL0_CLR = CLKCTL0_PSCCTL0_CLR_FLEXSPI0_MASK;
// Update flexspi clock.
CLKCTL0->FLEXSPIFCLKSEL = CLKCTL0_FLEXSPIFCLKSEL_SEL(src);
CLKCTL0->FLEXSPIFCLKDIV |=
CLKCTL0_FLEXSPIFCLKDIV_RESET_MASK; // Reset the divider counter
CLKCTL0->FLEXSPIFCLKDIV = CLKCTL0_FLEXSPIFCLKDIV_DIV(divider - 1);
while ((CLKCTL0->FLEXSPIFCLKDIV) & CLKCTL0_FLEXSPIFCLKDIV_REQFLAG_MASK)
(void)0;
// Enable FLEXSPI clock again
CLKCTL0->PSCCTL0_SET = CLKCTL0_PSCCTL0_SET_FLEXSPI0_MASK;
init_flash(base);
}
}
@@ -0,0 +1,133 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "hal.h"
// 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;
};
uint32_t SystemCoreClock = SYS_FREQUENCY;
int hal_radio_err; // Result of hal_radio_init()
void SystemInit(void) { // Called automatically by startup code
hal_system_init(); // Enable FPU
hal_radio_err = hal_radio_init(); // At the boot clock, see radio.c
hal_clock_init();
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
}
void ExitRun0Mode(void) {
}
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)) int _link(const char *a, const char *b) {
(void) a, (void) b;
return -1;
}
__attribute__((weak)) int _unlink(const char *a) {
(void) a;
return -1;
}
__attribute__((weak)) int _stat(const char *path, struct stat *st) {
(void) path, (void) st;
return -1;
}
__attribute__((weak)) int mkdir(const char *path, int mode) {
(void) path, (void) mode;
return -1;
}
__attribute__((weak)) void _init(void) {
}
@@ -0,0 +1,366 @@
// Copyright (c) 2023 Cesanta Software Limited
// All rights reserved
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "RW612.h"
#include "fsl_clock.h"
#include "fsl_ocotp.h"
#include "fsl_power.h"
#include "fsl_io_mux.h"
#include "fsl_reset.h"
#include "fsl_trng.h"
int hal_radio_init(void); // Start the 802.15.4 radio firmware. 0: success
extern int hal_radio_err; // hal_radio_init() result, called by SystemInit()
#define BIT(x) (1UL << (x))
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define PIN(bank, num) ((bank << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)
void hal_init(void);
size_t hal_ram_free(void);
size_t hal_ram_used(void);
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now);
uint64_t hal_get_tick(void);
static inline void spin(volatile uint32_t count) {
while (count--)
(void)0;
}
#define SYS_FREQUENCY 260000000UL
#define FRG_PLL_CLKDIV 13
enum { HAL_GPIO_MODE_INPUT, HAL_GPIO_MODE_OUTPUT };
enum {
HAL_GPIO_TYPE_NOPULL_WEAKEST = 0x0,
HAL_GPIO_TYPE_NOPULL_WEAK = 0x4,
HAL_GPIO_TYPE_NOPULL_STRONG = 0x8,
HAL_GPIO_TYPE_NOPULL_STRONGEST = 0xC,
HAL_GPIO_TYPE_PULLUP_WEAKEST = 0x1,
HAL_GPIO_TYPE_PULLUP_WEAK = 0x5,
HAL_GPIO_TYPE_PULLUP_STRONG = 0x9,
HAL_GPIO_TYPE_PULLUP_STRONGEST = 0xD,
HAL_GPIO_TYPE_PULLDOWN_WEAKEST = 0x2,
HAL_GPIO_TYPE_PULLDOWN_WEAK = 0x6,
HAL_GPIO_TYPE_PULLDOWN_STRONG = 0xA,
HAL_GPIO_TYPE_PULLDOWN_STRONGEST = 0xE,
HAL_GPIO_TYPE_NOPULL = HAL_GPIO_TYPE_NOPULL_STRONGEST,
HAL_GPIO_TYPE_PULLUP = HAL_GPIO_TYPE_PULLUP_STRONGEST,
HAL_GPIO_TYPE_PULLDOWN = HAL_GPIO_TYPE_PULLDOWN_STRONGEST,
};
// Wrapper structure over the IO_MUX_* macros in fsl_io_mux.h
// To use those macros with our gpio functions,
// wrap them inside this structure as shown in the example from the
// ethernet_init function.
typedef struct
{
uint32_t pinLowMask, pinHighMask, gpioFcSetMask,
gpioFcClrMask, fselSetMask, fselClrMask, ctimerSetMask,
ctimerClrMask, sctimerSetMask, sctimerClrMask;
} io_mux_config_t;
static inline void hal_gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
io_mux_config_t *mux_config) {
uint8_t bank = PINBANK(pin);
uint8_t pin_no = PINNO(pin);
if (mode == HAL_GPIO_MODE_INPUT) {
GPIO->DIR[bank] &= ~(1 << pin_no);
} else {
GPIO->DIR[bank] |= (1 << pin_no);
}
// configure pull
pin_no += bank * 32;
uint32_t shift = (pin_no % 16) * 2;
uint32_t pull_val = (uint32_t)type & 3;
uint32_t drive_val = ((uint32_t)type >> 2) & 3;
volatile uint32_t *pull_reg, *drive_reg;
if (pin_no < 22 || pin_no > 27) {
pull_reg = &SOCCTRL->PAD_PU_PD_EN0 + pin_no / 16;
drive_reg = &SOCCTRL->SR_CONFIG0 + pin_no / 16;
} else {
pull_reg = &AON_SOC_CIU->PAD_PU_PD_EN1;
drive_reg = &AON_SOC_CIU->SR_CONFIG1;
}
*pull_reg = (*pull_reg & ~(3 << shift)) | (pull_val << shift);
*drive_reg = (*drive_reg & ~(3 << shift)) | (drive_val << shift);
if (mux_config) {
IO_MUX_SetPinMux(mux_config->pinLowMask, mux_config->pinHighMask,
mux_config->gpioFcSetMask, mux_config->gpioFcClrMask,
mux_config->fselSetMask, mux_config->fselClrMask,
mux_config->ctimerSetMask, mux_config->ctimerClrMask,
mux_config->sctimerSetMask, mux_config->sctimerClrMask
);
} else {
// if no mux config is specified, assume default gpio settings and
// no special function and just enable the IO
IO_MUX_SetPinMux(bank ? 0 : BIT(PINNO(pin)), !bank ? 0 : BIT(PINNO(pin)),
1 << 16, 0, 0, 0, 0, 0, 0, 0);
}
}
static inline void hal_gpio_input(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_INPUT, HAL_GPIO_TYPE_PULLUP_STRONGEST, NULL);
}
static inline void hal_gpio_output(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_TYPE_NOPULL_STRONGEST, NULL);
}
static inline bool hal_gpio_read(uint16_t pin) {
return GPIO->PIN[PINBANK(pin)] & (1 << PINNO(pin));
}
static inline void hal_gpio_write(uint16_t pin, bool value) {
if (value) {
GPIO->SET[PINBANK(pin)] |= (1 << PINNO(pin));
} else {
GPIO->CLR[PINBANK(pin)] |= (1 << PINNO(pin));
}
}
static inline void hal_gpio_toggle(uint16_t pin) {
hal_gpio_read(pin) ? hal_gpio_write(pin, false) : hal_gpio_write(pin, true);
}
#if defined(__ZEPHYR__)
#undef MG_HAL_DISABLE_SYSTEM_INIT
#define MG_HAL_DISABLE_SYSTEM_INIT 1
#undef MG_HAL_DISABLE_NEWLIB
#define MG_HAL_DISABLE_NEWLIB 1
#undef MG_HAL_DISABLE_CLOCK
#define MG_HAL_DISABLE_CLOCK 1
#undef MG_HAL_DISABLE_RANDOM
#define MG_HAL_DISABLE_RANDOM 1
#undef MG_HAL_DISABLE_MILLIS
#define MG_HAL_DISABLE_MILLIS 1
#undef MG_HAL_DISABLE_UART
#define MG_HAL_DISABLE_UART 1
#undef MG_HAL_DISABLE_ETHERNET
#define MG_HAL_DISABLE_ETHERNET 1
#undef MG_HAL_SYSTICK
#define MG_HAL_SYSTICK MG_HAL_SYSTICK_NONE
#endif
static inline void hal_uart_init(USART_Type *uart, unsigned long baud) {
if (uart == USART0) {
IO_MUX_SetPinMux(IO_MUX_FC0_USART_DATA);
} else if (uart == USART1) {
IO_MUX_SetPinMux(IO_MUX_FC1_USART_DATA);
} else if (uart == USART2) {
IO_MUX_SetPinMux(IO_MUX_FC2_USART_DATA);
} else if (uart == USART3) {
IO_MUX_SetPinMux(IO_MUX_FC3_USART_DATA);
} else if (uart == USART14) {
IO_MUX_SetPinMux(IO_MUX_FC14_USART_DATA);
} else {
return;
}
uint32_t uart_index = ((uint32_t)uart - USART0_BASE) / 0x1000;
// set FRG clk
CLKCTL1->FLEXCOMM[uart_index].FRGCLKSEL = kCLOCK_FrgPllDiv;
CLKCTL1->FLEXCOMM[uart_index].FRGCTL =
(CLKCTL1_FRGCTL_MULT(0) | CLKCTL1_FRGCTL_DIV(255));
CLOCK_AttachClk(CLKCTL1_TUPLE_MUXA(0x508 + uart_index * 0x20, 4));
// set FLEXCOMM to USART mode
CLOCK_EnableClock(CLK_GATE_DEFINE(CLK_CTL1_PSCCTL0, uart_index + 8));
uint32_t mask = BIT(uart_index + 8);
RSTCTL1->PRSTCTL0_SET = mask;
while ((RSTCTL1->PRSTCTL0 & mask) == 0)
spin(1);
RSTCTL1->PRSTCTL0_CLR = mask;
while ((RSTCTL1->PRSTCTL0 & mask) == mask)
spin(1);
((FLEXCOMM_Type *)uart)->PSELID = 1;
uart->FIFOCFG |= USART_FIFOCFG_EMPTYTX_MASK |
USART_FIFOCFG_ENABLETX_MASK; // empty and enable txFIFO
uart->CFG = BIT(2) | BIT(0); // 8 bit data length and enable
// compute baud
uint32_t uart_freq = SYS_FREQUENCY / FRG_PLL_CLKDIV;
uint32_t best_diff = (uint32_t)-1, best_osr = 0xF, best_brg = (uint32_t)-1;
uint32_t osr, brg, diff, current_baud;
for (osr = 15; osr >= 4; osr--) {
brg = (((uart_freq * 10) / ((osr + 1) * baud)) - 5) / 10;
if (brg > 0xFFFF)
continue;
current_baud = uart_freq / ((osr + 1) * (brg + 1));
diff =
(baud < current_baud) ? (current_baud - baud) : (baud - current_baud);
if (diff < best_diff) {
best_diff = diff;
best_osr = osr;
best_brg = brg;
}
}
uart->OSR = best_osr;
uart->BRG = best_brg;
}
static inline void hal_uart_write_byte(USART_Type *uart, uint8_t byte) {
while ((uart->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK) == 0)
spin(1);
uart->FIFOWR = byte;
}
static inline void hal_uart_write_buf(USART_Type *uart, char *buf, size_t len) {
while (len-- > 0)
hal_uart_write_byte(uart, *(uint8_t *)buf++);
}
static inline void hal_rng_init(void) {
trng_config_t config;
TRNG_GetDefaultConfig(&config);
TRNG_Init(TRNG, &config);
}
static inline uint32_t hal_rng_read(void) {
uint32_t value;
TRNG_GetRandomData(TRNG, &value, sizeof(value));
return value;
}
static inline void hal_ethernet_init(void) {
CLOCK_EnableClock(kCLOCK_TddrMciEnetClk);
hal_gpio_init(PIN(0, 25), HAL_GPIO_MODE_OUTPUT, IO_MUX_PinConfigPullUpDriveWeak,
&(io_mux_config_t){ IO_MUX_ENET_CLK }); // ENET_CLK on GPIO_25
IO_MUX_SetPinMux(
IO_MUX_ENET_RX); // ENET_RX on pin GPIO_62, GPIO_63, GPIO_22, GPIO_23
IO_MUX_SetPinMux(IO_MUX_ENET_TX); // ENET_TX on pin GPIO_58, GPIO_59, GPIO_60
IO_MUX_SetPinMux(IO_MUX_ENET_MDIO); // ENET_MDIO on pin GPIO_56, GPIO_57
// reset ENET peripheral
RSTCTL0->PRSTCTL1_SET = BIT(25);
while ((RSTCTL0->PRSTCTL1 & BIT(25)) == 0)
spin(1);
RSTCTL0->PRSTCTL1_CLR = BIT(25);
while ((RSTCTL0->PRSTCTL1 & BIT(25)) == BIT(25))
spin(1);
RSTCTL0->PRSTCTL1_SET = BIT(26);
while ((RSTCTL0->PRSTCTL1 & BIT(26)) == 0)
spin(1);
RSTCTL0->PRSTCTL1_CLR = BIT(26);
while ((RSTCTL0->PRSTCTL1 & BIT(26)) == BIT(26))
spin(1);
// Enable clocks
CLOCK_EnableClock(kCLOCK_EnetIpg);
CLOCK_EnableClock(kCLOCK_EnetIpgS);
NVIC_EnableIRQ(ENET_IRQn);
}
extern void set_flexspi_clock(FLEXSPI_Type *base, uint32_t src,
uint32_t divider);
static inline void hal_clock_init(void) {
const clock_avpll_config_t avpllConfig_BOARD_BootClockRUN = {
.ch1Freq =
kCLOCK_AvPllChFreq12p288m, // AVPLL channel frequency 12.288 MHz
.ch2Freq = kCLOCK_AvPllChFreq64m, // AVPLL channel frequency 64 MHz
.enableCali = true, // AVPLL calibration is enabled
};
// As NXP BOARD_BootClockRUN(): the ROM leaves the ELS glitch detector and
// voltage sensors armed, trimmed for the boot clock. Disable them before
// changing the clock, or the ROM resets the chip when it next uses ELS
POWER_DisableGDetVSensors();
if ((PMU->CAU_SLP_CTRL & PMU_CAU_SLP_CTRL_SOC_SLP_RDY_MASK) == 0) {
CLOCK_EnableClock(
kCLOCK_RefClkCauSlp); // Enable the CAU sleep clock for PMU
}
if ((SYSCTL2->SOURCE_CLK_GATE & SYSCTL2_SOURCE_CLK_GATE_REFCLK_SYS_CG_MASK) !=
0) {
CLOCK_EnableClock(kCLOCK_RefClkSys); // Enable the REFCLK_SYS clock
}
CLOCK_InitT3RefClk(kCLOCK_T3MciIrc48m); // Initialize T3 PLL and enable
// outputs that are not clock gated
CLOCK_EnableClock(
kCLOCK_T3PllMciIrcClk); // Enable FFRO - T3 PLL 48/60 MHz IRC clock output
CLOCK_EnableClock(
kCLOCK_T3PllMci256mClk); // Enable T3 PLL 256 MHz clock output
// Move FLEXSPI clock source to T3 256M / 4 to avoid instruction/data fetch
// issue in XIP when updating PLL and main clock.
set_flexspi_clock(FLEXSPI, 6, 4);
CLOCK_AttachClk(kSYSOSC_to_MAIN_CLK); // Set core clock to safe system
// oscillator for initialization
CLOCK_SetClkDiv(kCLOCK_DivSysCpuAhbClk,
1); // Set system CPU AHB clock divider to 1
CLOCK_InitTcpuRefClk(
3120000000UL,
kCLOCK_TcpuFlexspiDiv10); // Initialize TCPU PLL and enable outputs
CLOCK_EnableClock(kCLOCK_TcpuMciClk); // Enable TCPU PLL MCI clock output
CLOCK_InitTddrRefClk(
kCLOCK_TddrFlexspiDiv10); // Initialize TDDR PLL and enable outputs
CLOCK_EnableClock(
kCLOCK_TddrMciFlexspiClk); // Enable TDDR PLL FlexSPI clock output
CLOCK_InitAvPll(&avpllConfig_BOARD_BootClockRUN); // Initialize AVPLL and
// enable both channels
CLOCK_AttachClk(kRC32K_to_CLK32K); // Switch CLK32K to RC32K
CLOCK_AttachClk(
kSYSTICK_DIV_to_SYSTICK_CLK); // Switch SYSTICK_CLK to SYSTICK_DIV
CLOCK_SetClkDiv(kCLOCK_DivAudioPllClk, 1U); // Set AUDIOPLLCLKDIV divider to 1
CLOCK_SetClkDiv(kCLOCK_DivPllFrgClk,
FRG_PLL_CLKDIV); // Set FRGPLLCLKDIV divider
CLOCK_SetClkDiv(kCLOCK_DivMainPllClk, 1U); // Set MAINPLLCLKDIV divider to 1
CLOCK_SetClkDiv(kCLOCK_DivAux0PllClk, 1U); // Set AUX0PLLCLKDIV divider to 1
CLOCK_SetClkDiv(kCLOCK_DivSystickClk, 1U); // Set SYSTICKFCLKDIV divider to 1
CLOCK_SetClkDiv(kCLOCK_DivPmuFclk, 5U); // Set PMUFCLKDIV divider to 5
CLOCK_AttachClk(
kMAIN_PLL_to_MAIN_CLK); // Select the main clock source for system clock
set_flexspi_clock(FLEXSPI, 2U, 2U); // set_flexspi_clock
CLOCK_EnableClock(kCLOCK_HsGpio0);
RSTCTL1->PRSTCTL1_SET = BIT(0); // Reset GPIO_0
while ((RSTCTL1->PRSTCTL1 & BIT(0)) == 0)
spin(1);
RSTCTL1->PRSTCTL1_CLR = BIT(0);
while ((RSTCTL1->PRSTCTL1 & BIT(0)) == BIT(0))
spin(1);
CLOCK_EnableClock(kCLOCK_HsGpio1);
RSTCTL1->PRSTCTL1_SET = BIT(1); // Reset GPIO_1
while ((RSTCTL1->PRSTCTL1 & BIT(1)) == 0)
spin(1);
RSTCTL1->PRSTCTL1_CLR = BIT(1);
while ((RSTCTL1->PRSTCTL1 & BIT(1)) == BIT(1))
spin(1);
}
#if defined(__MCUXPRESSO)
#undef MG_HAL_DISABLE_SYSTEM_INIT
#define MG_HAL_DISABLE_SYSTEM_INIT 1
#undef MG_HAL_SBRK_NONE
#define MG_HAL_SBRK_NONE 1
#endif
static inline void hal_system_init(void) {
SCB->CPACR |=
(3UL << 0 * 2) | (3UL << 1 * 2) | // Enable PowerQuad (CPO/CP1)
(3UL << 10 * 2) | (3UL << 11 * 2); // Enable FPU
SCB->NSACR |= ((3UL << 0) | (3UL << 10));
CACHE64_CTRL0->CCR = CACHE64_CTRL_CCR_INVW1_MASK | CACHE64_CTRL_CCR_INVW0_MASK;
CACHE64_CTRL0->CCR |= CACHE64_CTRL_CCR_GO_MASK;
while ((CACHE64_CTRL0->CCR & CACHE64_CTRL_CCR_GO_MASK) != 0) spin(1);
// Enable cache, enable write buffer
CACHE64_CTRL0->CCR = (CACHE64_CTRL_CCR_ENWRBUF_MASK | CACHE64_CTRL_CCR_ENCACHE_MASK);
// Set whole FlexSPI0 space to write through
CACHE64_POLSEL0->REG0_TOP = 0x07FFFC00U;
CACHE64_POLSEL0->REG1_TOP = 0x0U;
CACHE64_POLSEL0->POLSEL = 0x1U;
__DSB();
__ISB();
}
@@ -0,0 +1,38 @@
ENTRY(Reset_Handler);
MEMORY {
flash(rx) : ORIGIN = 0x08000000, LENGTH = 0x4000000
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 0x123000 /* Top is reserved, as in NXP wireless linker scripts */
}
__StackTop = ORIGIN(sram) + LENGTH(sram);
SECTIONS {
.boot_hdr : ALIGN(4) { FILL(0xFF); . = 0x400; __FLASH_BASE = .; KEEP(*(.flash_conf)) . = 0x1000; } > flash
.vectors : { FILL(256) KEEP(*(.isr_vector)) } > flash
.text : { *(.text.* *.text.*) } > flash
.rodata : { *(.rodata*) } > flash
.iram : {
_siram = .;
*(.iram)
*(CodeQuickAccess) /* NXP SDK drivers, e.g. fsl_power.c */
*(DataQuickAccess)
_eiram = .;
} > sram AT > flash
_siiram = LOADADDR(.iram);
.data : {
_sdata = .;
*(.first_data)
*(.data SORT(.data.*))
_edata = .;
} > sram AT > flash
_sidata = LOADADDR(.data);
.bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram
. = ALIGN(8);
_end = .;
_image_start = LOADADDR(.text);
_image_end = LOADADDR(.data) + SIZEOF(.data);
_image_size = _image_end - _image_start;
}
@@ -0,0 +1,101 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
//
// Ethernet <-> IEEE 802.15.4 radio bridge. Radio frames carry Ethernet frames
// split into pieces, see mg_l2_802154_tx(). Radio nodes, e.g. the
// FRDM-MCXW71 minimal tutorial, are on the Ethernet LAN: they get IPv4
// addresses from its DHCP server. The bridge itself has no IP address.
// Everything from the radio goes to Ethernet. From Ethernet, only broadcasts
// and frames to radio nodes go to the radio, it is much slower
#include "hal.h"
#include "mongoose.h"
#define UART_DEBUG USART3
static void log_fn(char ch, void *param) {
hal_uart_write_buf(param, &ch, 1);
}
uint64_t mg_millis(void) {
return hal_get_tick();
}
// MAC addresses of radio nodes, learned from frames they send
static uint8_t s_nodes[8][6];
static size_t s_next;
// Remember a radio node's MAC address, if new
static void learn(const uint8_t *mac) {
size_t i, n = sizeof(s_nodes) / sizeof(s_nodes[0]);
for (i = 0; i < n && memcmp(s_nodes[i], mac, 6) != 0; i++) (void) 0;
if (i < n) return;
memcpy(s_nodes[s_next], mac, 6);
s_next = (s_next + 1) % n;
MG_INFO(("Radio node %M", mg_print_mac, mac));
}
// Is an Ethernet frame for the radio: broadcast, or to a radio node
static bool for_radio(const uint8_t *dst) {
size_t i, n = sizeof(s_nodes) / sizeof(s_nodes[0]);
if (memcmp(dst, "\xff\xff\xff\xff\xff\xff", 6) == 0) return true;
for (i = 0; i < n && memcmp(s_nodes[i], dst, 6) != 0; i++) (void) 0;
return i < n;
}
int main(void) {
static struct mg_tcpip_driver_rw612_data eth_data = {
.mdc_cr = MG_DRIVER_MDC_CR, .phy_addr = MG_TCPIP_PHY_ADDR};
static struct mg_l2_802154_data radio_data = {
.channel = MG_L2_802154_CHANNEL, .pan_id = MG_L2_802154_PAN_ID};
static struct mg_tcpip_if eth = {.driver = &mg_tcpip_driver_rw612,
.driver_data = &eth_data};
static struct mg_tcpip_if radio = {.driver = &mg_tcpip_driver_rw612_154,
.driver_data = &radio_data};
static char eth_queue[8192]; // Filled by the Ethernet IRQ handler
static uint8_t frame[1540];
uint8_t uid[FSL_OCOTP_UID_LENGTH], digest[32];
uint32_t uid_len = sizeof(uid);
uint64_t timer = 0;
char *p;
size_t n;
hal_clock_init();
hal_uart_init(UART_DEBUG, 115200);
mg_log_set_fn(log_fn, UART_DEBUG);
if (hal_radio_err) {
MG_ERROR(("Radio firmware failed to start, step %d", hal_radio_err));
}
// MAC addresses made from the chip unique ID
memset(uid, 0, sizeof(uid));
OCOTP_ReadUniqueID(uid, &uid_len);
mg_sha256(digest, uid, sizeof(uid));
memcpy(eth.mac, digest, 6);
memcpy(radio.mac, digest + 6, 6);
eth.mac[0] = radio.mac[0] = 0x02; // Locally administered, unicast
hal_ethernet_init();
mg_queue_init(&eth.recv_queue, eth_queue, sizeof(eth_queue));
if (!eth.driver->init(&eth)) MG_ERROR(("Ethernet init failed"));
if (!radio.driver->init(&radio)) MG_ERROR(("Radio init failed"));
ENET->RCR |= ENET_RCR_PROM_MASK; // Receive frames for radio nodes, too
MG_INFO(("Bridge started, radio channel %d", radio_data.channel));
for (;;) {
if ((n = mg_queue_next(&eth.recv_queue, &p)) > 0) { // Ethernet -> radio
if (n >= 14 && for_radio((uint8_t *) p)) radio.driver->tx(p, n, &radio);
mg_queue_del(&eth.recv_queue, n);
}
if ((n = radio.driver->rx(frame, sizeof(frame), &radio)) >= 14) {
learn(frame + 6); // Radio -> Ethernet
eth.driver->tx(frame, n, &eth);
}
if (mg_timer_expired(&timer, 1000, mg_millis())) { // Ethernet link
bool up = eth.driver->poll(&eth, true);
eth.state = up ? MG_TCPIP_STATE_UP : MG_TCPIP_STATE_DOWN;
}
}
return 0;
}
@@ -0,0 +1,10 @@
#pragma once
// See https://mongoose.ws/docs/getting-started/build-environment/
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_DRIVER_RW612 1 // Ethernet
#define MG_ENABLE_DRIVER_RW612_154 1 // 802.15.4 radio
#define MG_ENABLE_TCPIP_DRIVER_INIT 0 // main.c drives both, no TCP/IP stack
#define MG_ENABLE_CUSTOM_MILLIS 1
@@ -0,0 +1,236 @@
// Load NXP BLE/802.15.4 combo firmware to CPU2 and start it.
// Port of NXP mcux-component conn_fwloader: fsl_loader_utils.c (SB3 loader,
// load_service_monolithic) and nboot_hal.c (manifest parameters).
// Copyright 2024 NXP
// SPDX-License-Identifier: BSD-3-Clause
#include "hal.h"
#include "fsl_ocotp.h"
#include "fsl_power.h"
#include "fsl_romapi_nboot.h"
#include "fsl_romapi_otp.h"
#include "fsl_kb_api.h"
#include "fsl_sbloader_v3.h"
// The firmware image, linked into our flash. Signed SB3 container, see
// https://github.com/NXP/wifi_nb_fw, rw61x/rw61x_sb_ble_15d4_combo_a2.bin
__asm__(
".section .rodata.radio_fw,\"a\"\n"
".balign 16\n"
"radio_fw:\n"
".incbin \"" RADIO_FW "\"\n"
".balign 4\n"
"radio_fw_end:\n"
".previous\n");
extern const uint8_t radio_fw[], radio_fw_end[];
#define CIU2_RST_SW3 (*(volatile uint32_t *) 0x4424011cU)
#define CIU2_RST_SW3_CPU2_RST BIT(4)
#define CPU2_MAGIC (*(volatile uint32_t *) 0x443c0000U) // Set by firmware
#define CPU2_MAGIC_VALUE 0xabcdef89U
#define OTP_LCS_FUSE_IDX 45U // Life cycle state, and its redundant copy
#define OTP_PCK_BLOB_FUSE_IDX 116U // Part common key blob, 48 bytes
// SB3 loader state. See fsl_ldr_Context_v3_t
struct sb3 {
nboot_context_t nboot; // ROM nboot context
uint8_t buf[MAX(NBOOT_SB3_MANIFEST_MAX_SIZE_IN_BYTES,
NBOOT_SB3_BLOCK_MAX_SIZE_IN_BYTES)]; // Block being read
uint32_t buf_size; // Bytes needed for the current block
uint32_t buf_pos; // Bytes in buf so far
bool header_done; // Manifest (block 0) processed
uint32_t data_offset; // Data offset within a block
uint32_t blocks; // Data blocks processed
uint8_t *data; // Data of the current block
uint32_t pos; // Position in data
bool in_section; // Inside a data section
uint32_t section_done; // Bytes of the section handled
sb3_section_header_t section;
bool in_range; // Inside a data range
uint32_t range_gap; // Padding after the range, 16 byte alignment
sb3_data_range_header_t range;
bool has_expansion; // Range expansion read
bool executed; // Execute command seen: firmware loaded
};
static struct sb3 s_sb3;
static uint32_t antipole(uint32_t v) {
return (v & 0xffffU) | (~(v & 0xffffU) << 16);
}
// Manifest parameters: root of trust for NXP signed images, rev A2 chips.
// See nboot_hal_get_sb3_manifest_params(), nboot_hal.c
static bool manifest_params(nboot_sb3_load_manifest_parms_t *p) {
static const uint32_t rkh_a2[8] = {0xE7C7E9BB, 0x12C8C535, 0x37E61148,
0x2BE7F18C, 0x8F0E3094, 0xB2BA7F32,
0xEC9B4ECB, 0xAD9FC941};
uint32_t i, lcs = OCOTP->OTP_SHADOW[OTP_LCS_FUSE_IDX];
memset(p, 0, sizeof(*p));
if ((lcs & 0xff) != ((lcs >> 8) & 0xff)) return false; // Corrupt LCS
for (i = 0; i < NBOOT_ROOT_CERT_COUNT; i++) {
p->soc_RoTNVM.soc_rootKeyRevocation[i] = kNBOOT_RootKey_Enabled;
}
memcpy(p->soc_RoTNVM.soc_rkh, rkh_a2, sizeof(rkh_a2));
p->soc_RoTNVM.soc_numberOfRootKeys = 1;
for (i = 1; i < NBOOT_ROOT_CERT_COUNT; i++) {
p->soc_RoTNVM.soc_rootKeyUsage[i] = kNBOOT_RootKeyUsage_Unused;
}
p->soc_RoTNVM.soc_rootKeyTypeAndLength = kNBOOT_RootKey_Ecdsa_P256;
p->soc_RoTNVM.soc_lifecycle = (nboot_soc_lifecycle_t) antipole(lcs & 0xff);
for (i = 0; i < sizeof(p->pckBlob) / 4; i++) {
if (OCOTP_OtpFuseRead(OTP_PCK_BLOB_FUSE_IDX + i,
(uint32_t *) &p->pckBlob[4 * i]) != kStatus_Success) {
return false;
}
}
return true;
}
// Manifest (block 0): verify, learn the block layout. See ldr_DoHeader_v3()
static bool sb3_header(struct sb3 *s) {
nboot_sb3_header_t *h = (nboot_sb3_header_t *) s->buf;
nboot_sb3_load_manifest_parms_t parms;
if (s->buf_size == sizeof(nboot_sb3_header_t)) { // Header only so far
s->buf_size = h->imageTotalLength;
s->buf_pos = sizeof(nboot_sb3_header_t);
return s->buf_size <= sizeof(s->buf);
}
if (!manifest_params(&parms)) return false;
// Protected status: the status code is in the low 32 bits
if ((nboot_status_t) nboot_sb3_load_manifest(&s->nboot, (uint32_t *) h,
&parms) != kStatus_NBOOT_Success) {
return false;
}
s->data_offset = 4 + h->certificateBlockOffset - sizeof(nboot_sb3_header_t);
s->buf_size = s->data_offset + NBOOT_SB3_CHUNK_SIZE_IN_BYTES;
s->header_done = true;
return true;
}
// Copy (part of) the current load range to its destination
static void sb3_load(struct sb3 *s) {
uint32_t avail = NBOOT_SB3_CHUNK_SIZE_IN_BYTES - s->pos;
uint32_t n = s->range.length < avail ? s->range.length : avail;
memcpy((void *) s->range.startAddress, &s->data[s->pos], n);
s->pos += n, s->section_done += n;
s->range.startAddress += n, s->range.length -= n;
if (s->range.length == 0) { // Range done, skip alignment padding
s->in_range = false;
s->pos += s->range_gap, s->section_done += s->range_gap;
}
}
// Process one decrypted data block. See ldr_DoBlock()
static bool sb3_block(struct sb3 *s) {
while (s->pos < NBOOT_SB3_CHUNK_SIZE_IN_BYTES) {
if (!s->in_section) { // New section
memcpy(&s->section, &s->data[s->pos], sizeof(s->section));
if (s->section.sectionType == kSectionNone) break; // Rest is unused
if (s->section.sectionType != kSectionDataRange) return false;
s->in_section = true, s->section_done = 0, s->in_range = false;
s->pos += sizeof(sb3_section_header_t);
}
if (!s->in_range) { // New range
memcpy(&s->range, &s->data[s->pos], sizeof(s->range));
if (s->range.tag != SB3_DATA_RANGE_HEADER_TAG) return false;
s->in_range = true, s->has_expansion = false, s->range_gap = 0;
s->pos += sizeof(sb3_data_range_header_t);
s->section_done += sizeof(sb3_data_range_header_t);
if (s->range.cmd == kSB3_CmdLoad) {
s->range_gap = (SB3_DATA_ALIGNMENT_SIZE_IN_BYTE -
(s->range.length & 15U)) & 15U;
}
}
if (s->range.cmd == kSB3_CmdLoad) {
if (!s->has_expansion) {
if (s->pos == NBOOT_SB3_CHUNK_SIZE_IN_BYTES) break; // Next block
s->has_expansion = true;
s->pos += sizeof(sb3_data_range_expansion_t);
s->section_done += sizeof(sb3_data_range_expansion_t);
}
if (s->pos < NBOOT_SB3_CHUNK_SIZE_IN_BYTES) sb3_load(s);
} else if (s->range.cmd == kSB3_CmdExecute) {
s->executed = true;
return true;
} else {
s->in_range = false; // Other commands: nothing to do
}
if (s->section_done == s->section.length) s->in_section = false;
if (s->section_done > s->section.length) return false;
}
s->pos = 0;
return true;
}
// Feed image bytes, process complete blocks. See fsl_sbloader_pump()
static bool sb3_pump(struct sb3 *s, const uint8_t *p, uint32_t len) {
while (len > 0 && !s->executed) {
uint32_t n = s->buf_size - s->buf_pos;
if (n > len) n = len;
memcpy(&s->buf[s->buf_pos], p, n);
s->buf_pos += n, p += n, len -= n;
if (s->buf_pos < s->buf_size) break;
s->buf_pos = 0;
if (!s->header_done) {
if (!sb3_header(s)) return false;
} else {
if (s->blocks >= s->nboot.totalBlocks) return false;
if ((nboot_status_t) nboot_sb3_load_block(
&s->nboot, (uint32_t *) s->buf) != kStatus_NBOOT_Success) {
return false;
}
s->blocks++;
s->data = &s->buf[s->data_offset];
if (!sb3_block(s)) return false;
}
}
return true;
}
// Authenticate the image with the ROM, copy it to CPU2 memory.
// See loader_process_sb_file()
static bool sb3_process(const uint8_t *img, size_t len) {
struct sb3 *s = &s_sb3;
bool ok = false;
memset(s, 0, sizeof(*s));
s->buf_size = sizeof(nboot_sb3_header_t);
// Start ELS only if it is off: the ROM keeps keys in it after boot
if ((CLKCTL0->PSCCTL0 & CLKCTL0_PSCCTL0_ELS_MASK) == 0 ||
(CLKCTL0->PSCCTL1 & CLKCTL0_PSCCTL1_ELS_APB_MASK) == 0 ||
(RSTCTL0->PRSTCTL0 & RSTCTL0_PRSTCTL0_ELS_MASK) != 0) {
CLOCK_EnableClock(kCLOCK_Els);
CLOCK_EnableClock(kCLOCK_ElsApb);
RESET_PeripheralReset(kELS_RST_SHIFT_RSTn);
}
if (nboot_context_init(&s->nboot) == kStatus_NBOOT_Success) {
ok = sb3_pump(s, img, (uint32_t) len) && s->executed;
(void) nboot_context_deinit(&s->nboot);
}
return ok;
}
// Load the radio firmware to CPU2 and start it. See sb3_fw_reset() and
// load_service_monolithic(), fsl_loader_utils.c. Call before changing the
// boot clock: the ROM trims the ELS glitch detector for it, and after the
// clock change ELS reports glitch events. Interrupts may be off.
// Return 0 on success, or the failed step: 1 chip revision, 2 OTP, 3 image
// load, 4 firmware start
int hal_radio_init(void) {
uint32_t n = 100000000; // Firmware start timeout, loop iterations
bool ok;
if ((SOCCTRL->CHIP_INFO & 15U) != 2U) return 1; // Image is for rev A2
POWER_PowerOffBle();
spin(100000);
if (otp_init(0) != kStatus_Success) return 2;
POWER_PowerOnBle();
spin(100000);
CPU2_MAGIC = 0;
ok = sb3_process(radio_fw, (size_t) (radio_fw_end - radio_fw));
(void) otp_deinit();
if (!ok) return 3;
CIU2_RST_SW3 |= CIU2_RST_SW3_CPU2_RST;
while (CPU2_MAGIC != CPU2_MAGIC_VALUE) {
if (--n == 0) return 4; // Firmware did not start
}
return 0;
}
@@ -0,0 +1,338 @@
#include "RW612.h"
void Reset_Handler(void); // Defined below
void Dummy_Handler(void); // Defined below
void SysTick_Handler(void); // Defined in main.c
void SystemInit(void); // Defined in main.c, called by reset handler
void __StackTop(void); // Defined in link.ld
void _image_size(void);
#define WEAK __attribute__((weak, alias("Default_Handler")))
void Default_Handler(void);
WEAK void NMI_Handler(void);
WEAK void HardFault_Handler(void);
WEAK void MemManage_Handler(void);
WEAK void BusFault_Handler(void);
WEAK void UsageFault_Handler(void);
WEAK void SecureFault_Handler(void);
WEAK void SVC_Handler(void);
WEAK void DebugMon_Handler(void);
WEAK void PendSV_Handler(void);
WEAK void SysTick_Handler(void);
WEAK void IntDefaultHandler(void);
WEAK void WDT0_IRQHandler(void);
WEAK void DMA0_IRQHandler(void);
WEAK void GPIO_INTA_IRQHandler(void);
WEAK void GPIO_INTB_IRQHandler(void);
WEAK void PIN_INT0_IRQHandler(void);
WEAK void PIN_INT1_IRQHandler(void);
WEAK void PIN_INT2_IRQHandler(void);
WEAK void PIN_INT3_IRQHandler(void);
WEAK void UTICK_IRQHandler(void);
WEAK void MRT_IRQHandler(void);
WEAK void CTIMER0_IRQHandler(void);
WEAK void CTIMER1_IRQHandler(void);
WEAK void SCT0_IRQHandler(void);
WEAK void CTIMER3_IRQHandler(void);
WEAK void FLEXCOMM0_IRQHandler(void);
WEAK void FLEXCOMM1_IRQHandler(void);
WEAK void FLEXCOMM2_IRQHandler(void);
WEAK void FLEXCOMM3_IRQHandler(void);
WEAK void Reserved34_IRQHandler(void);
WEAK void Reserved35_IRQHandler(void);
WEAK void FLEXCOMM14_IRQHandler(void);
WEAK void Reserved37_IRQHandler(void);
WEAK void Reserved38_IRQHandler(void);
WEAK void GFMRT_IRQHandler(void);
WEAK void Reserved40_IRQHandler(void);
WEAK void DMIC_IRQHandler(void);
WEAK void WKDEEPSLEEP_IRQHandler(void);
WEAK void HYPERVISOR_IRQHandler(void);
WEAK void SECUREVIOLATION_IRQHandler(void);
WEAK void HWVAD_IRQHandler(void);
WEAK void Reserved46_IRQHandler(void);
WEAK void Reserved47_IRQHandler(void);
WEAK void RTC_IRQHandler(void);
WEAK void Reserved49_IRQHandler(void);
WEAK void Reserved50_IRQHandler(void);
WEAK void PIN_INT4_IRQHandler(void);
WEAK void PIN_INT5_IRQHandler(void);
WEAK void PIN_INT6_IRQHandler(void);
WEAK void PIN_INT7_IRQHandler(void);
WEAK void CTIMER2_IRQHandler(void);
WEAK void CTIMER4_IRQHandler(void);
WEAK void OS_EVENT_TIMER_IRQHandler(void);
WEAK void FLEXSPI_IRQHandler(void);
WEAK void Reserved59_IRQHandler(void);
WEAK void Reserved60_IRQHandler(void);
WEAK void Reserved61_IRQHandler(void);
WEAK void SDIO_IRQHandler(void);
WEAK void SGPIO_INTA_IRQHandler(void);
WEAK void SGPIO_INTB_IRQHandler(void);
WEAK void Reserved65_IRQHandler(void);
WEAK void USB_IRQHandler(void);
WEAK void Reserved67_IRQHandler(void);
WEAK void Reserved68_IRQHandler(void);
WEAK void Reserved69_IRQHandler(void);
WEAK void DMA1_IRQHandler(void);
WEAK void PUF_IRQHandler(void);
WEAK void POWERQUAD_IRQHandler(void);
WEAK void Reserved73_IRQHandler(void);
WEAK void Reserved74_IRQHandler(void);
WEAK void Reserved75_IRQHandler(void);
WEAK void Reserved76_IRQHandler(void);
WEAK void LCD_IRQHandler(void);
WEAK void CAPTIMER_IRQHandler(void);
WEAK void Reserved79_IRQHandler(void);
WEAK void W2MWKUP_DONE0_IRQHandler(void);
WEAK void W2MWKUP_DONE1_IRQHandler(void);
WEAK void W2MWKUP_DONE2_IRQHandler(void);
WEAK void W2MWKUP_DONE3_IRQHandler(void);
WEAK void W2MWKUP_DONE4_IRQHandler(void);
WEAK void W2MWKUP_DONE5_IRQHandler(void);
WEAK void W2MWKUP_DONE6_IRQHandler(void);
WEAK void W2MWKUP_DONE7_IRQHandler(void);
WEAK void W2MWKUP0_IRQHandler(void);
WEAK void W2MWKUP1_IRQHandler(void);
WEAK void WL_MCI_INT0_IRQHandler(void);
WEAK void WL_MCI_INT1_IRQHandler(void);
WEAK void WL_MCI_INT2_IRQHandler(void);
WEAK void WL_MCI_INT3_IRQHandler(void);
WEAK void WL_MCI_INT4_IRQHandler(void);
WEAK void WL_MCI_INT5_IRQHandler(void);
WEAK void WL_MCI_INT6_IRQHandler(void);
WEAK void WL_MCI_INT7_IRQHandler(void);
WEAK void B2MWKUP_DONE0_IRQHandler(void);
WEAK void B2MWKUP_DONE1_IRQHandler(void);
WEAK void B2MWKUP_DONE2_IRQHandler(void);
WEAK void B2MWKUP_DONE3_IRQHandler(void);
WEAK void B2MWKUP_DONE4_IRQHandler(void);
WEAK void B2MWKUP_DONE5_IRQHandler(void);
WEAK void B2MWKUP_DONE6_IRQHandler(void);
WEAK void B2MWKUP_DONE7_IRQHandler(void);
WEAK void B2MWKUP0_IRQHandler(void);
WEAK void B2MWKUP1_IRQHandler(void);
WEAK void BLE_MCI_INT0_IRQHandler(void);
WEAK void BLE_MCI_INT1_IRQHandler(void);
WEAK void BLE_MCI_INT2_IRQHandler(void);
WEAK void BLE_MCI_INT3_IRQHandler(void);
WEAK void BLE_MCI_INT4_IRQHandler(void);
WEAK void BLE_MCI_INT5_IRQHandler(void);
WEAK void BLE_MCI_INT6_IRQHandler(void);
WEAK void BLE_MCI_INT7_IRQHandler(void);
WEAK void PIN0_INT_IRQHandler(void);
WEAK void PIN1_INT_IRQHandler(void);
WEAK void ELS_IRQHandler(void);
WEAK void ELS_GDET_IRQHandler(void);
WEAK void ELS_GDET_UM_IRQHandler(void);
WEAK void PKC_INT_IRQHandler(void);
WEAK void PKC_ERR_IRQHandler(void);
WEAK void CDOG_INT_IRQHandler(void);
WEAK void GAU_DAC_IRQHandler(void);
WEAK void GAU_ACOMP_WKUP_IRQHandler(void);
WEAK void GAU_ACOMP_IRQHandler(void);
WEAK void GAU_ADC1_IRQHandler(void);
WEAK void GAU_ADC0_IRQHandler(void);
WEAK void USIM_IRQHandler(void);
WEAK void OTP_IRQHandler(void);
WEAK void ENET_IRQHandler(void);
WEAK void ENET_TIMER_IRQHandler(void);
WEAK void PMIP_IRQHandler(void);
WEAK void PMIP_CHANGE_IRQHandler(void);
WEAK void ITRC_IRQHandler(void);
WEAK void Reserved136_IRQHandler(void);
WEAK void Reserved137_IRQHandler(void);
WEAK void Reserved138_IRQHandler(void);
WEAK void TRNG_IRQHandler(void);
WEAK void ACC_C_INT_IRQHandler(void);
WEAK void ACC_S_INT_IRQHandler(void);
WEAK void WACC_IRQHandler(void);
WEAK void BACC_IRQHandler(void);
WEAK void GDMA_IRQHandler(void);
#define WEAK_ALIAS __attribute__((weak, alias("Default_Handler")))
__attribute__((section(".vectors"))) void (*const tab[16 + 156])(void) = {
&__StackTop, // The initial stack pointer
Reset_Handler, // The reset handler
NMI_Handler, // NMI Handler
HardFault_Handler, // Hard Fault Handler
MemManage_Handler, // MPU Fault Handler
BusFault_Handler, // Bus Fault Handler
UsageFault_Handler, // Usage Fault Handler
SecureFault_Handler, // Secure Fault Handler
&_image_size, // Image size
0, // Image type
0, // Reserved
SVC_Handler, // SVCall Handler
DebugMon_Handler, // Debug Monitor Handler
(void (*)())tab, // Image load address
PendSV_Handler, // PendSV Handler
SysTick_Handler, // SysTick Handler
// Chip Level - RW612
WDT0_IRQHandler, // 16 : Windowed watchdog timer 0 (WDT0)(Cortex-M33 watchdog)
DMA0_IRQHandler, // 17 : Direct memory access (DMA) controller 0 (secure or Cortex-M33 DMA)
GPIO_INTA_IRQHandler, // 18 : GPIO interrupt A
GPIO_INTB_IRQHandler, // 19 : GPIO interrupt B
PIN_INT0_IRQHandler, // 20 : Pin interrupt 0 or pattern match engine slice 0
PIN_INT1_IRQHandler, // 21 : Pin interrupt 1 or pattern match engine slice 1
PIN_INT2_IRQHandler, // 22 : Pin interrupt 2 or pattern match engine slice 2
PIN_INT3_IRQHandler, // 23 : Pin interrupt 3 or pattern match engine slice 3
UTICK_IRQHandler, // 24 : Micro-tick Timer (UTICK)
MRT_IRQHandler, // 25 : Multi-Rate Timer (MRT). Global MRT interrupts
CTIMER0_IRQHandler, // 26 : Standard counter/timer CTIMER0
CTIMER1_IRQHandler, // 27 : Standard counter/timer CTIMER1
SCT0_IRQHandler, // 28 : SCTimer/PWM
CTIMER3_IRQHandler, // 29 : Standard counter/timer CTIMER3
FLEXCOMM0_IRQHandler, // 30 : Flexcomm Interface 0 (USART, SPI, I2C, I2S)
FLEXCOMM1_IRQHandler, // 31 : Flexcomm Interface 1 (USART, SPI, I2C, I2S)
FLEXCOMM2_IRQHandler, // 32 : Flexcomm Interface 2 (USART, SPI, I2C, I2S)
FLEXCOMM3_IRQHandler, // 33 : Flexcomm Interface 3 (USART, SPI, I2C, I2S)
Reserved34_IRQHandler, // 34 : Reserved interrupt
Reserved35_IRQHandler, // 35 : Reserved interrupt
FLEXCOMM14_IRQHandler, // 36 : Flexcomm Interface 14 (USART, SPI, I2C, I2S)
Reserved37_IRQHandler, // 37 : Reserved interrupt
Reserved38_IRQHandler, // 38 : Reserved interrupt
GFMRT_IRQHandler, // 39 : Free Multi-rate timer (GFMRT). Global MRT interrupts
Reserved40_IRQHandler, // 40 : Reserved interrupt
DMIC_IRQHandler, // 41 : Digital microphone (DMIC) and DMIC subsystem
WKDEEPSLEEP_IRQHandler, // 42 : Wake-up from deep sleep
HYPERVISOR_IRQHandler, // 43 : Hypervisor service software interrupt
SECUREVIOLATION_IRQHandler, // 44 : Secure violation
HWVAD_IRQHandler, // 45 : Hardware Voice Activity Detector
Reserved46_IRQHandler, // 46 : Reserved interrupt
Reserved47_IRQHandler, // 47 : Reserved interrupt
RTC_IRQHandler, // 48 : RTC alarm and wake-up
Reserved49_IRQHandler, // 49 : Reserved interrupt
Reserved50_IRQHandler, // 50 : Reserved interrupt
PIN_INT4_IRQHandler, // 51 : Pin interrupt 4 or pattern match engine slice 4
PIN_INT5_IRQHandler, // 52 : Pin interrupt 5 or pattern match engine slice 5
PIN_INT6_IRQHandler, // 53 : Pin interrupt 6 or pattern match engine slice 6
PIN_INT7_IRQHandler, // 54 : Pin interrupt 7 or pattern match engine slice 7
CTIMER2_IRQHandler, // 55 : Standard counter/timer CTIMER2
CTIMER4_IRQHandler, // 56 : Standard counter/timer CTIMER4
OS_EVENT_TIMER_IRQHandler, // 57 : OS event timer 0
FLEXSPI_IRQHandler, // 58 : FLEXSPI interface
Reserved59_IRQHandler, // 59 : Reserved interrupt
Reserved60_IRQHandler, // 60 : Reserved interrupt
Reserved61_IRQHandler, // 61 : Reserved interrupt
SDIO_IRQHandler, // 62 : The secure digital interface
SGPIO_INTA_IRQHandler, // 63 : Secure GPIO interrupt A
SGPIO_INTB_IRQHandler, // 64 : Secure GPIO interrupt B
Reserved65_IRQHandler, // 65 : Reserved interrupt
USB_IRQHandler, // 66 : High-speed USB device/host
Reserved67_IRQHandler, // 67 : Reserved interrupt
Reserved68_IRQHandler, // 68 : Reserved interrupt
Reserved69_IRQHandler, // 69 : Reserved interrupt
DMA1_IRQHandler, // 70 : DMA controller 1 (non-secure or HiFi 4 DMA)
PUF_IRQHandler, // 71 : Physical Unclonable Function
POWERQUAD_IRQHandler, // 72 : PowerQuad math coprocessor
Reserved73_IRQHandler, // 73 : Reserved interrupt
Reserved74_IRQHandler, // 74 : Reserved interrupt
Reserved75_IRQHandler, // 75 : Reserved interrupt
Reserved76_IRQHandler, // 76 : Reserved interrupt
LCD_IRQHandler, // 77 : LCDIC
CAPTIMER_IRQHandler, // 78 : Capture timer
Reserved79_IRQHandler, // 79 : Reserved interrupt
W2MWKUP_DONE0_IRQHandler, // 80 : Wi-Fi to MCU, wakeup done 0
W2MWKUP_DONE1_IRQHandler, // 81 : Wi-Fi to MCU, wakeup done 1
W2MWKUP_DONE2_IRQHandler, // 82 : Wi-Fi to MCU, wakeup done 2
W2MWKUP_DONE3_IRQHandler, // 83 : Wi-Fi to MCU, wakeup done 3
W2MWKUP_DONE4_IRQHandler, // 84 : Wi-Fi to MCU, wakeup done 4
W2MWKUP_DONE5_IRQHandler, // 85 : Wi-Fi to MCU, wakeup done 5
W2MWKUP_DONE6_IRQHandler, // 86 : Wi-Fi to MCU, wakeup done 6
W2MWKUP_DONE7_IRQHandler, // 87 : Wi-Fi to MCU, wakeup done 7
W2MWKUP0_IRQHandler, // 88 : Wi-Fi to MCU, wakeup signal 0
W2MWKUP1_IRQHandler, // 89 : Wi-Fi to MCU, wakueup signal 1
WL_MCI_INT0_IRQHandler, // 90 : Wi-Fi to MCU interrupt 0
WL_MCI_INT1_IRQHandler, // 91 : Reserved for Wi-Fi to MCU
WL_MCI_INT2_IRQHandler, // 92 : Reserved for Wi-Fi to MCU
WL_MCI_INT3_IRQHandler, // 93 : Reserved for Wi-Fi to MCU
WL_MCI_INT4_IRQHandler, // 94 : Reserved for Wi-Fi to MCU
WL_MCI_INT5_IRQHandler, // 95 : Reserved for Wi-Fi to MCU
WL_MCI_INT6_IRQHandler, // 96 : Reserved for Wi-Fi to MCU
WL_MCI_INT7_IRQHandler, // 97 : Reserved for Wi-Fi to MCU
B2MWKUP_DONE0_IRQHandler, // 98 : Bluetooth LE/802.15.4 radio to MCU, wakeup done 0
B2MWKUP_DONE1_IRQHandler, // 99 : Bluetooth LE/802.15.4 radio to MCU, wakeup done 1
B2MWKUP_DONE2_IRQHandler, // 100: Bluetooth LE/802.15.4 radio to MCU, wakeup done 2
B2MWKUP_DONE3_IRQHandler, // 101: Bluetooth LE/802.15.4 radio to MCU, wakeup done 3
B2MWKUP_DONE4_IRQHandler, // 102: Bluetooth LE/802.15.4 radio to MCU, wakeup done 4
B2MWKUP_DONE5_IRQHandler, // 103: Bluetooth LE/802.15.4 radio to MCU, wakeup done 5
B2MWKUP_DONE6_IRQHandler, // 104: Bluetooth LE/802.15.4 radio to MCU, wakeup done 6
B2MWKUP_DONE7_IRQHandler, // 105: Bluetooth LE/802.15.4 radio to MCU, wakeup done 7
B2MWKUP0_IRQHandler, // 106: Bluetooth LE/802.15.4 radio to MCU, wakeup signal 0
B2MWKUP1_IRQHandler, // 107: Bluetooth LE/802.15.4 radio to MCU, wakeup signal 1
BLE_MCI_INT0_IRQHandler, // 108: Bluetooth LE/802.15.4 radio to MCU interrupt 0
BLE_MCI_INT1_IRQHandler, // 109: Reserved for Bluetooth LE/802.15.4 radio to MCU
BLE_MCI_INT2_IRQHandler, // 110: Reserved for Bluetooth LE/802.15.4 radio to MCU
BLE_MCI_INT3_IRQHandler, // 111: Reserved for Bluetooth LE/802.15.4 radio to MCU
BLE_MCI_INT4_IRQHandler, // 112: Reserved for Bluetooth LE/802.15.4 radio to MCU
BLE_MCI_INT5_IRQHandler, // 113: Reserved for Bluetooth LE/802.15.4 radio to MCU
BLE_MCI_INT6_IRQHandler, // 114: Reserved for Bluetooth LE/802.15.4 radio to MCU
BLE_MCI_INT7_IRQHandler, // 115: Reserved for Bluetooth LE/802.15.4 radio to MCU
PIN0_INT_IRQHandler, // 116: From AON GPIO
PIN1_INT_IRQHandler, // 117: From AON GPIO
ELS_IRQHandler, // 118: EdgeLock subsystem (ELS)
ELS_GDET_IRQHandler, // 119: ELS IRQ line for GDET error
ELS_GDET_UM_IRQHandler, // 120: ELS un-gated latched error
PKC_INT_IRQHandler, // 121: Public key crypto-processor (PKC) interrupt
PKC_ERR_IRQHandler, // 122: PKC error
CDOG_INT_IRQHandler, // 123: Code watch dog timmer interrupt
GAU_DAC_IRQHandler, // 124: General analog unit (GAU) digital to analog converter (DAC)
GAU_ACOMP_WKUP_IRQHandler, // 125: GAU analog comparator (ACOMP) wake-up
GAU_ACOMP_IRQHandler, // 126: GAU analog comparator
GAU_ADC1_IRQHandler, // 127: GAU analog to digital converter 1 (ADC1)
GAU_ADC0_IRQHandler, // 128: GAU analog to digital converter 0 (ADC0)
USIM_IRQHandler, // 129: Universal subscriber identity module (USIM) interface
OTP_IRQHandler, // 130: One time programmable (OTP) memory interrupt
ENET_IRQHandler, // 131: Etheret interrupt
ENET_TIMER_IRQHandler, // 132: Ethernet timer interrupt
PMIP_IRQHandler, // 133: Power management IP (PMIP)
PMIP_CHANGE_IRQHandler, // 134: PMIP change from 1 to 0
ITRC_IRQHandler, // 135: Intrusion and tamper response controller (ITRC) interrupt request
Reserved136_IRQHandler, // 136: Reserved interrupt
Reserved137_IRQHandler, // 137: Reserved interrupt
Reserved138_IRQHandler, // 138: Reserved interrupt
TRNG_IRQHandler, // 139: TRNG interrupt request
ACC_C_INT_IRQHandler, // 140: AHB memory access checker - Cortex-M33 code bus
ACC_S_INT_IRQHandler, // 141: AHB memory access checker - Cortex-M33 sys bus
WACC_IRQHandler, // 142: Wi-Fi accessed during power off
BACC_IRQHandler, // 143: Bluetooth LE/802.15.4 radio accessed during power off
GDMA_IRQHandler, // 144: General purpose direct memory access (GDMA) interrupt
};
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
__attribute__((naked, noreturn)) void Reset_Handler(void) {
__asm("cpsid i"); // Disable interrupts
// Config VTOR & MSPLIM register
__asm("LDR R0, =0xE000ED08 \n"
"STR %0, [R0] \n"
"LDR R1, [%0] \n"
"MSR MSP, R1 \n"
"MSR MSPLIM, %1 \n"
:
: "r"(tab), "r"(_end)
: "r0", "r1");
extern long _sbss, _ebss, _sdata, _edata, _sidata, _siram, _siiram, _eiram;
for (long *dst = &_sbss; dst < &_ebss; dst++) *dst = 0;
for (long *dst = &_sdata, *src = &_sidata; dst < &_edata;) *dst++ = *src++;
for (long *dst = &_siram, *src = &_siiram; dst < &_eiram;) *dst++ = *src++;
SystemInit();
__asm("cpsie i"); // Reenable interrupts
// Call main()
extern void main(void);
main();
for (;;) (void) 0; // Infinite loop
}
void SystemCoreClockUpdate (void) {
}
void Default_Handler(void) {
for (;;) (void) 0;
}