MCCI Trusted Bootloader
Simple trusted bootloader and tools for small embedded systems
mccibootloaderboard_catena1sj_systeminit.c
Go to the documentation of this file.
1/*
2
3Module: mccibootloaderboard_catena1sj_systeminit.c
4
5Function:
6 McciBootloaderBoard_Catena1sj_systemInit() and system init logic for
7 MCCI Catenas based on Murata type-1SJ modules.
8
9Copyright and License:
10 This file copyright (C) 2026 by
11
12 MCCI Corporation
13 3520 Krums Corners Road
14 Ithaca, NY 14850
15
16 See accompanying LICENSE file for copyright and license information.
17
18Author:
19 Terry Moore, MCCI Corporation March 2026
20
21*/
22
24
26#include "mcci_bootloader.h"
29#include "mcci_stm32l0xx.h"
30#include "mcci_arm_cm0plus.h"
31␌
32/****************************************************************************\
33|
34| Manifest constants & typedefs.
35|
36\****************************************************************************/
37
39static void
42
43static void
44delayTick(void);
45
46/****************************************************************************\
47|
48| Read-only data.
49|
50\****************************************************************************/
51
52/****************************************************************************\
53|
54| Variables.
55|
56\****************************************************************************/
57
58/// the tick count for 1sj platforms
59static volatile McciBootloader_Milliseconds_t s_tickCount;
60␌
61/*
62
63Name: McciBootloaderBoard_Catena1sj_systemInit()
64
65Function:
66 Initialize MCCI Catena based on Murata 1SJ module for bootloader.
67
68Definition:
69 McciBootloaderPlatform_SystemInitFn_t
70 McciBootloaderBoard_Catena1sj_systemInit;
71
72 void McciBootloaderBoard_Catena1sj_systemInit(
73 void
74 );
75
76Description:
77 We set up the CPU for 32 MHz operation (using stm32l0 initialization).
78
79 We then enable GPIOs for the LED (pin PB2).
80
81Returns:
82 No explicit result.
83
84Notes:
85
86
87*/
88
89void
91 void
92 )
93 {
95
96 // enable GPIO port A & B
100 );
101
102 // set up GPIO port B2 as an output
109 )
110 );
111
112 // clear B2
115 UINT32_C(1) << 2
116 );
117
118 // set B2 and clear it, so we can see things on a scope
121 UINT32_C(1) << 2
122 );
125 UINT32_C(1) << 2
126 );
127 }
128
129void
137
138void
146
147void
149 {
150 uint32_t const start = McciBootloaderBoard_Catena1sj_getMilliseconds();
151
153 ;
154 }
155
156static void
161
162void
168
171 {
172 bool fSysTickCanPreempt = false;
173 uint32_t const systick_csr = McciArm_getReg(MCCI_CM0PLUS_SYSTICK_CSR);
174
175 // In case the clock isn't enabled at all, we can't provide this service.
176 if ((systick_csr & MCCI_CM0PLUS_SYSTICK_CSR_ENABLE) == 0)
177 {
178 // ticks can't happen! This is a fundamental service that everyone uses,
179 // so we can't actually crash because crash display will try to use the clock.
180 // Our solution: spin for a fixed number of loops, and then advance
181 // by "1 ms". 32000/12 is used so that we'll get 1ms at 32MHz (the loop takes
182 // 12 clocks), longer at slower clock rates.
183 for (volatile unsigned i = 0; i < 32000 / 12; ++i)
184 ;
185
186 ++s_tickCount;
187 return s_tickCount;
188 }
189
190 /* systick is an exception, not an interrupt. But it can be blocked by primask. */
191 /* if interrupts are enabled, clocks still might be blocked */
192 if (McciArm_getPRIMASK() == 0)
193 {
194 // determine the current CM0 urgency
195 uint32_t const ipsr = McciArm_getIPSR();
196
197 // in thread mode, we can be preempted.
199 fSysTickCanPreempt = true;
200
201 // if in an exception, systick can preempt if higher priority
202 else
203 {
204 int32_t const intpri = McciArmNvic_getPriority(McciArmIPSR_toIRQ(ipsr));
205 int32_t const systickpri = McciArm_getSysTickPriority();
206
207 if (systickpri < intpri)
208 fSysTickCanPreempt = true;
209 }
210
211 // otherwise: can't be preempted.
212 }
213
214 // SysTick could preempt us, but it might not be enabled:
215 if (fSysTickCanPreempt)
216 {
217 if ((systick_csr & MCCI_CM0PLUS_SYSTICK_CSR_TICKINT) == 0)
218 {
219 // ticks can't interrupt us
220 fSysTickCanPreempt = false;
221 }
222 }
223
224 // if systick can preempt us, use the count it maintains.
225 if (fSysTickCanPreempt)
226 {
227 // if we saw COUNTFLAG set, we assume that the CM0+ will still
228 // be interrupted. We depend on our systick handler not caring about
229 // the value of COUNTFLAG when it is entered.
230 return s_tickCount;
231 }
232
233 // if systick can't preempt us, count roll-overs.
234 else
235 {
236 if ((systick_csr & MCCI_CM0PLUS_SYSTICK_CSR_COUNTFLAG) != 0)
237 ++s_tickCount;
238
239 return s_tickCount;
240 }
241 }
242
243void
245 McciBootloaderError_t errorCode
246 )
247 {
249 // if the error is "corrupt boot loader", just do a fast blink.
251 else
252 {
253 // otherwise the bootloader is probably OK; display the error code for
254 // 60 seconds, then attempt another reboot in case the problem was
255 // transitory.
256 unsigned timeToReboot;
257
258 McciArm_disableInterrupts();
259
260 // wait 60 seconds flashing error code before rebooting
261 timeToReboot = 60 * 1000;
262
263 // We may get here before annunciator init (for example, if PMIC
264 // setup fails), so start the LED state machine here. Restarting
265 // it is harmless, because we set a new code right after.
267
268 // set the error code to be displayed
270
271 // busy loop, flashing the code.
272 for (; timeToReboot > 0; --timeToReboot)
273 {
274 delayTick();
276 }
277
278 // make sure everything has gone out
279 McciArm_DataSynchBarrier();
280
281 // force a reset
288 );
289
290 // make sure the reset gets to the register.
291 McciArm_DataSynchBarrier();
292
293 while (true)
294 /* spin till reset */;
295 }
296 }
297
298static void fastBlinkForever(void)
299 {
301
302 while (true)
303 {
304 // toggle the led
305 rOdr ^= UINT32_C(1) << 2;
308 }
309 }
310
311/**** end of mccibootloaderboard_catena1sj_systeminit.c ****/
static uint32_t McciArm_putRegOr(uint32_t reg, uint32_t orVal)
or 32-bit values to a cm0plus register
static uint32_t McciArm_getReg(uint32_t reg)
read a 32-bit value from a cm0plus register
static uint32_t McciArm_putReg(uint32_t reg, uint32_t val)
write a 32-bit value to a cm0plus register
#define MCCI_CM0PLUS_SCB_AIRCR_SYSRESETREQ
#define MCCI_CM0PLUS_SYSTICK_CSR
#define MCCI_CM0PLUS_SCB_AIRCR
#define MCCI_CM0PLUS_SYSTICK_CSR_ENABLE
enable counter
static bool McciArmIPSR_queryThreadMode(uint32_t ipsr)
test IPSR to see if Cortex-M0+ is in thread mode.
#define MCCI_CM0PLUS_SCB_AIRCR_VECTKEY_VALUE
Value to write to unlock regster.
#define MCCI_CM0PLUS_SYSTICK_CSR_TICKINT
enable tick exception
static int32_t McciArmNvic_getPriority(int32_t irq)
return priority of speciified IRQ
static uint32_t McciArm_putRegMasked(uint32_t reg, uint32_t maskVal, uint32_t modVal)
store to cm0plus register under mask
#define MCCI_CM0PLUS_SYSTICK_CSR_COUNTFLAG
timer has counted to zero
#define MCCI_CM0PLUS_SCB_AIRCR_VECTKEY
Vector key.
static uint32_t McciArm_getSysTickPriority(void)
return priority of SysTick exception.
static int32_t McciArmIPSR_toIRQ(uint32_t ipsr)
convert IPSR value to IRQ number, if in handler.
@ McciBootloaderError_BootloaderNotValid
bootloader image isn't valid
#define MCCI_BOOTLOADER_FIELD_SET_VALUE(fmask, val)
McciBootloaderPlatform_DelayMsFn_t McciBootloaderBoard_Catena1sj_delayMs
McciBootloaderPlatform_SystemInitFn_t McciBootloaderBoard_Catena1sj_systemInit
McciBootloaderPlatform_AnnunciatorIndicateStateFn_t McciBootloaderBoard_Catena1sj_annunciatorIndicateState
void McciBootloaderBoard_Catena1sj_annunciatorHandleSysTick(void)
MCCI_BOOTLOADER_BEGIN_DECLS void McciBootloader_Stm32L0_systemInit(void)
#define MCCI_BOOTLOADER_NORETURN_SFX
uint32_t McciBootloader_Milliseconds_t
Time (in milliseconds)
uint32_t McciBootloaderError_t
error codes for the bootloader
#define MCCI_BOOTLOADER_NORETURN_PFX
#define MCCI_STM32L0_GPIO_BSRR
#define MCCI_STM32L0_REG_RCC_IOPENR_IOPBEN
#define MCCI_STM32L0_REG_GPIOB
Section 9.4.12: GPIO register map (1K)
#define MCCI_STM32L0_GPIO_ODR
#define MCCI_STM32L0_GPIO_MODER
#define MCCI_STM32L0_REG_RCC_IOPENR
GPIO clock enable.
#define MCCI_STM32L0_GPIO_BRR
#define MCCI_STM32L0_REG_RCC_IOPENR_IOPAEN
#define MCCI_STM32L0_GPIO_MODE_OUT
#define MCCI_STM32L0_GPIO_MODE_P(p)
compute the mask for the mode bits for port bits 0..15
void McciBootloader_LED_annunciatorInit(void)
McciBootloader_Milliseconds_t McciBootloaderBoard_Catena1sj_getMilliseconds(void)
void McciBootloaderBoard_Catena1sj_setLed(void)
static MCCI_BOOTLOADER_NORETURN_PFX void fastBlinkForever(void)
void McciBootloaderBoard_Catena1sj_handleSysTick(void)
static void delayTick(void)
void McciBootloaderBoard_Catena1sj_clearLed(void)
void McciBootloaderBoard_Catena1sj_fail(McciBootloaderError_t errorCode)