MCCI Trusted Bootloader
Simple trusted bootloader and tools for small embedded systems
mccibootloaderboard_catena1sj_spi.c
Go to the documentation of this file.
1/*
2
3Module: mccibootloaderboard_catena1sj_spi.c
4
5Function:
6 SPI (flash) driver for Catena boards based on Murata 1SJ and STM32L0.
7
8Copyright and License:
9 This file copyright (C) 2026 by
10
11 MCCI Corporation
12 3520 Krums Corners Road
13 Ithaca, NY 14850
14
15 See accompanying LICENSE file for copyright and license information.
16
17Author:
18 Terry Moore, MCCI Corporation March 2026
19
20*/
21
23#include "mcci_stm32l0xx.h"
24#include <stdbool.h>
25#include <stdint.h>
26#include <stdlib.h>
27␌
28/****************************************************************************\
29|
30| Manifest constants & typedefs.
31|
32\****************************************************************************/
33
34#define MCCI_CATENA1SJ_PA8_MASK (UINT32_C(1) << 8) // mask for PB8 in the various registers
35
36/****************************************************************************\
37|
38| Read-only data.
39|
40\****************************************************************************/
41
42
43
44/****************************************************************************\
45|
46| Variables.
47|
48\****************************************************************************/
49
50␌
51/*
52
53Name: McciBootloaderBoard_Catena1sj_spiInit()
54
55Function:
56 Initialize SPI subsystem for bootloader for Catenas based on 1SJ module.
57
58Definition:
59 McciBootloaderPlatform_SpiInitFn_t McciBootloaderBoard_Catena1sj_spiInit;
60
61 void McciBootloaderBoard_Catena1sj_spiInit(
62 void
63 );
64
65Description:
66 SPI1 is initialized. We assume the following:
67
68 SPI connections:
69 PA8: nss OUT (manually driven)
70 PB3: sclk AF0 pulldown.
71 PB4: miso AF0
72 PB5: mosi AF0
73
74 Clock: 16 MHz (SYSCLK/2).
75 MSB first.
76 clock polarity 0, phase 0
77
78 The boot loader's architecture assumes that NSS is controlled by the "spi subsystem".
79 On older Catena boards, NSS could be controlled by the SPI hardware. On this
80 family of boards, NSS is wired to a GPIO, and must be manually controlled.
81 This module therefore sets up PA8 as a suitable GPIO, initializes it to 1, and
82 manually drives it active during SPI transfers.
83
84Returns:
85 No explicit result.
86
87Notes:
88
89
90*/
91
92
93void
95 {
96 // set up the storage connections
97 // PB8 is NSS so we need to set it up. Initialize the data value before making it an output.
101 );
102
103 // make PB8 an output.
108 );
109
110 // set up the SPI bits.
119 );
120
125 );
126
135 );
136
141 );
142
151 );
152
153 // enable SPI1 at APB2
157 );
158
159 // reset the SPI1 block
163 );
167 );
168
169 // setup SPI_CR1
170 // since we reset above, we don't have to worry about anything
171 // except non-default settings
174 (
175 MCCI_STM32L0_SPI_CR1_BR_2 | // fastest
176 MCCI_STM32L0_SPI_CR1_MSTR | // master mode
177 // NSS on this board is a plain GPIO (PA8), manually driven by
178 // spiTransfer() -- it is not connected to the SPI1 peripheral's
179 // own NSS/AF function. With SSM=0 (the reset default), the
180 // peripheral still watches its internal NSS signal and treats
181 // it going low, while master, as another device contending for
182 // the bus -- triggering MODF (mode fault), which immediately
183 // clears SPE and permanently disables the peripheral. SSM=1
184 // disables that hardware NSS monitoring; SSI=1 forces the
185 // internal NSS signal to a fixed high, matching "I am solely
186 // the master, chip-select is my own responsibility."
187 MCCI_STM32L0_SPI_CR1_SSM | // software (not hardware) NSS management
188 MCCI_STM32L0_SPI_CR1_SSI // force internal NSS high
189 )
190 );
191
192 // setup SPI_CR2
193 // since we reset above, we don't have to worry about anything
194 // except non-default settings. And since we are using a non-default
195 // NSS, manually managed, we don't want to write anything at all.
196 // McciArm_putRegOr(
197 // MCCI_STM32L0_REG_SPI1 + MCCI_STM32L0_SPI_CR2,
198 // 0 /* MCCI_STM32L0_SPI_CR2_SSOE would enable the nss output */
199 // );
200
201 // setup I2SCFGR_I2SMOD
202 // since we reset above, we don't need to do anything.
203 // McciArm_putRegClear(
204 // MCCI_STM32L0_REG_SPI1 + MCCI_STM32L0_SPI_I2SCFGR,
205 // MCCI_STM32L0_SPI_I2SCFGR_I2SMOD
206 // );
207 }
208␌
209/*
210
211Name: McciBootloaderBoard_Catena1sj_spiTransfer()
212
213Function:
214 Implementation of spi_transfer method for Catena1SJ family.
215
216Definition:
217 typedef McciBootloaderPlatform_SpiTransferFn_t
218 McciBootloaderBoard_Catena1sj_spiTransfer;
219
220 void McciBootloaderBoard_Catena1sj_spiTransfer(
221 uint8_t *pRx,
222 const uint8_t *pTx,
223 size_t nBytes,
224 bool fContinue
225 );
226
227Description:
228 This API writes and reads data from the SPI bus, optionally leaving the
229 target device selected.
230
231 nBytes bytes of data are transferred from the buffer at pTx to the SPI
232 bus, and received from the SPI bus to pRx.
233
234 The API defines pRx and pTx as optional; if NULL, bytes are discarded
235 or zeroes inserted, respectively.
236
237Returns:
238 No explicit result.
239
240Notes:
241
242
243*/
244
245// do a transfer
246void
248 uint8_t *pRx,
249 const uint8_t *pTx,
250 size_t nBytes,
251 bool fContinue
252 )
253 {
254 uint8_t txdata;
255
256 // drive NSS low.
260 );
261
265 );
266 txdata = 0;
267 for (; nBytes > 0; --nBytes)
268 {
270 ;
271
272 if (pTx)
273 txdata = *pTx++;
274
276
278 ;
279
281 if (pRx)
282 *pRx++ = (uint8_t)rxData;
283 }
284
285 if (! fContinue)
286 {
290 );
291
292 // drive NSS high.
296 );
297 }
298 }
299
300/**** end of mccibootloaderboard_catena1sj_spi.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
static uint32_t McciArm_putRegClear(uint32_t reg, uint32_t clearVal)
clear out 32-bit values to a cm0plus register
static uint32_t McciArm_putRegMasked(uint32_t reg, uint32_t maskVal, uint32_t modVal)
store to cm0plus register under mask
#define MCCI_BOOTLOADER_FIELD_SET_VALUE(fmask, val)
McciBootloaderPlatform_SpiTransferFn_t McciBootloaderBoard_Catena1sj_spiTransfer
#define MCCI_STM32L0_GPIO_BSRR
#define MCCI_STM32L0_GPIO_OSPEED_HIGH
#define MCCI_STM32L0_GPIO_PUPD_P(p)
compute the mask for the mode bits for port bits 0..15
#define MCCI_STM32L0_REG_GPIOB
Section 9.4.12: GPIO register map (1K)
#define MCCI_STM32L0_REG_SPI1
Section 30.7.10: SPI register map (1K)
#define MCCI_STM32L0_SPI_CR1_BR_2
PCLK/2.
#define MCCI_STM32L0_GPIO_PUPD_NONE
#define MCCI_STM32L0_SPI_CR1
offset to SPI control register 1
#define MCCI_STM32L0_GPIO_MODER
#define MCCI_STM32L0_REG_GPIOA
Section 9.4.12: GPIO register map (1K)
#define MCCI_STM32L0_SPI_SR
offset to SPI status register
#define MCCI_STM32L0_SPI_CR1_SSI
internal slave select
#define MCCI_STM32L0_SPI_SR_RXNE
RX buffer not-empty.
#define MCCI_STM32L0_SPI_CR1_SSM
software slave management
#define MCCI_STM32L0_GPIO_OSPEEDR
#define MCCI_STM32L0_GPIO_BRR
#define MCCI_STM32L0_GPIO_OSPEED_P(p)
compute the mask for the mode bits for port bits 0..15
#define MCCI_STM32L0_GPIO_MODE_AF
#define MCCI_STM32L0_REG_RCC_APB2RSTR
APB2 peripheral reset.
#define MCCI_STM32L0_SPI_CR1_MSTR
Master (not slave)
#define MCCI_STM32L0_SPI_DR
offset to SPI data register
#define MCCI_STM32L0_GPIO_PUPD_PULLDOWN
#define MCCI_STM32L0_SPI_SR_TXE
TX buffer empty.
#define MCCI_STM32L0_GPIO_PUPDR
#define MCCI_STM32L0_SPI_CR1_SPE
SPI enabled.
#define MCCI_STM32L0_GPIO_MODE_OUT
#define MCCI_STM32L0_REG_RCC_APB2RSTR_SPI1RST
SPI1 reset.
#define MCCI_STM32L0_GPIO_MODE_P(p)
compute the mask for the mode bits for port bits 0..15
#define MCCI_STM32L0_REG_RCC_APB2ENR
APB2 peripheral clock enable.
#define MCCI_STM32L0_REG_RCC_APB2ENR_SPI1EN
SPI1 enable.
#define MCCI_CATENA1SJ_PA8_MASK
void McciBootloaderBoard_Catena1sj_spiInit(void)