diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index e3a2910a6e..d173cdec91 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -216,6 +216,30 @@ jobs: arch: arm config-file: ./config/examples/mcxw-tz.config + nxp_s32k142_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/nxp-s32k142.config + + nxp_s32k144_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/nxp-s32k144.config + + nxp_s32k146_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/nxp-s32k146.config + + nxp_s32k148_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/nxp-s32k148.config + microchip_mpfs250_test: uses: ./.github/workflows/test-build-riscv.yml with: diff --git a/.gitignore b/.gitignore index 6e4b4dc3b3..1ab8df3b41 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ *.rom *.bin *.efi +*.srec # Debug files *.dSYM/ diff --git a/Makefile b/Makefile index 67067ba40a..9172788d17 100644 --- a/Makefile +++ b/Makefile @@ -458,6 +458,10 @@ endif $(WOLFBOOT_ORIGIN) wolfboot.bin \ $(WOLFBOOT_PARTITION_BOOT_ADDRESS) test-app/image_v1_signed.bin +factory.srec: factory.bin + @echo "\t[BIN2SREC] $@" + $(Q)$(OBJCOPY) -I binary -O srec --change-addresses=$(WOLFBOOT_ORIGIN) $< $@ + factory_wstage1.bin: $(BINASSEMBLE) stage1/loader_stage1.bin wolfboot.bin $(BOOT_IMG) $(PRIVATE_KEY) test-app/image_v1_signed.bin @echo "\t[MERGE] $@" $(Q)$(BINASSEMBLE) $@ \ @@ -509,6 +513,7 @@ $(LSCRIPT): $(LSCRIPT_IN) FORCE hex: wolfboot.hex srec: wolfboot.srec +factory-srec: factory.srec %.hex:%.elf @echo "\t[ELF2HEX] $@" @@ -541,7 +546,7 @@ clean: $(Q)rm -f src/wc_secure_calls.o $(Q)rm -f $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/*.o $(WOLFBOOT_LIB_WOLFTPM)/src/*.o $(WOLFBOOT_LIB_WOLFTPM)/hal/*.o $(WOLFBOOT_LIB_WOLFTPM)/examples/pcr/*.o $(Q)rm -f $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/Renesas/*.o - $(Q)rm -f wolfboot.bin wolfboot.elf wolfboot.map test-update.rom wolfboot.hex + $(Q)rm -f wolfboot.bin wolfboot.elf wolfboot.map test-update.rom wolfboot.hex wolfboot.srec factory.srec $(Q)rm -f $(MACHINE_OBJ) $(MAIN_TARGET) $(LSCRIPT) $(Q)rm -f $(OBJS) $(Q)rm -f tools/keytools/otp/otp-keystore-gen diff --git a/config/examples/nxp-s32k142.config b/config/examples/nxp-s32k142.config new file mode 100644 index 0000000000..edb4808b73 --- /dev/null +++ b/config/examples/nxp-s32k142.config @@ -0,0 +1,54 @@ +# wolfBoot configuration for NXP S32K142 +# +# S32K142: Cortex-M4F, 256KB Flash, 32KB SRAM +# Flash sector size: 2KB +# Default: RUN mode at 48 MHz (FIRC - internal RC oscillator) +# +# Build: cp config/examples/nxp-s32k142.config .config && make + +ARCH?=ARM +CORTEX_M4?=1 +TARGET?=s32k1xx +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +VTOR?=1 +NO_ASM?=0 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=0 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 + +# Disable MPU (S32K1xx MPU configuration needs customization) +WOLFBOOT_NO_MPU?=1 + +# Enable hardfault debugging +DEBUG_HARDFAULT?=0 + +# Debug UART on LPUART1 (PTC6=RX, PTC7=TX) +DEBUG_UART?=0 + +# 2KB sectors (S32K142 only - larger variants use 4KB sectors) +WOLFBOOT_SECTOR_SIZE?=0x800 + +# Memory layout for S32K142 (256KB Flash): +# Bootloader: 0x00000000 - 0x0000BFFF (48 KB) +# Boot Partition: 0x0000C000 - 0x00024FFF (100 KB) +# Update Partition: 0x00025000 - 0x0003DFFF (100 KB) +# Swap Sector: 0x0003E000 - 0x0003E7FF (2 KB) +WOLFBOOT_PARTITION_SIZE?=0x19000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xC000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x25000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x3E000 + +# Clock mode: Default is RUN mode with FIRC (48 MHz internal RC) +# To enable HSRUN mode (112 MHz), uncomment below (requires SOSC + SPLL, not fully implemented): +#CFLAGS_EXTRA+=-DS32K1XX_CLOCK_HSRUN + +# Optionally enable watchdog +#CFLAGS_EXTRA+=-DWATCHDOG \ No newline at end of file diff --git a/config/examples/nxp-s32k144.config b/config/examples/nxp-s32k144.config new file mode 100644 index 0000000000..1655a28331 --- /dev/null +++ b/config/examples/nxp-s32k144.config @@ -0,0 +1,57 @@ +# wolfBoot configuration for NXP S32K144 +# +# S32K144: Cortex-M4F, 512KB Flash, 64KB SRAM +# Flash sector size: 4KB (larger flash variants use 4KB sectors) +# Default: RUN mode at 48 MHz (FIRC - internal RC oscillator) +# +# Build: cp config/examples/nxp-s32k144.config .config && make + +ARCH?=ARM +CORTEX_M4?=1 +TARGET?=s32k1xx +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +VTOR?=1 +NO_ASM?=0 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=0 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 + +# Select S32K144 variant for correct flash size and sector size +CFLAGS_EXTRA+=-DS32K144 + +# Disable MPU (S32K1xx MPU configuration needs customization) +WOLFBOOT_NO_MPU?=1 + +# Enable hardfault debugging +#CFLAGS_EXTRA+=-DDEBUG_HARDFAULT + +# Debug UART on LPUART1 (PTC6=RX, PTC7=TX) +DEBUG_UART?=0 + +# 4KB sectors (S32K144/146/148 with 512KB+ flash) +WOLFBOOT_SECTOR_SIZE?=0x1000 + +# Memory layout for S32K144 (512KB Flash): +# Bootloader: 0x00000000 - 0x0000BFFF (48 KB) +# Boot Partition: 0x0000C000 - 0x00043FFF (224 KB) +# Update Partition: 0x00044000 - 0x0007BFFF (224 KB) +# Swap Sector: 0x0007C000 - 0x0007CFFF (4 KB) +WOLFBOOT_PARTITION_SIZE?=0x38000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xC000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x44000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x7C000 + +# Clock mode: Default is RUN mode with FIRC (48 MHz internal RC) +# To enable HSRUN mode (112 MHz), uncomment below (requires SOSC + SPLL, not fully implemented): +#CFLAGS_EXTRA+=-DS32K1XX_CLOCK_HSRUN + +# Optionally enable watchdog +#CFLAGS_EXTRA+=-DWATCHDOG diff --git a/config/examples/nxp-s32k146.config b/config/examples/nxp-s32k146.config new file mode 100644 index 0000000000..78f048688f --- /dev/null +++ b/config/examples/nxp-s32k146.config @@ -0,0 +1,57 @@ +# wolfBoot configuration for NXP S32K146 +# +# S32K146: Cortex-M4F, 1MB Flash, 128KB SRAM +# Flash sector size: 4KB (larger flash variants use 4KB sectors) +# Default: RUN mode at 48 MHz (FIRC - internal RC oscillator) +# +# Build: cp config/examples/nxp-s32k146.config .config && make + +ARCH?=ARM +CORTEX_M4?=1 +TARGET?=s32k1xx +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +VTOR?=1 +NO_ASM?=0 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=0 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 + +# Select S32K146 variant for correct flash size and sector size +CFLAGS_EXTRA+=-DS32K146 + +# Disable MPU (S32K1xx MPU configuration needs customization) +WOLFBOOT_NO_MPU?=1 + +# Enable hardfault debugging +#CFLAGS_EXTRA+=-DDEBUG_HARDFAULT + +# Debug UART on LPUART1 (PTC6=RX, PTC7=TX) +DEBUG_UART?=0 + +# 4KB sectors (S32K144/146/148 with 512KB+ flash) +WOLFBOOT_SECTOR_SIZE?=0x1000 + +# Memory layout for S32K146 (1MB Flash): +# Bootloader: 0x00000000 - 0x0000BFFF (48 KB) +# Boot Partition: 0x0000C000 - 0x0007FFFF (464 KB) +# Update Partition: 0x00080000 - 0x000F3FFF (464 KB) +# Swap Sector: 0x000F4000 - 0x000F4FFF (4 KB) +WOLFBOOT_PARTITION_SIZE?=0x74000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xC000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x80000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0xF4000 + +# Clock mode: Default is RUN mode with FIRC (48 MHz internal RC) +# To enable HSRUN mode (112 MHz), uncomment below (requires SOSC + SPLL, not fully implemented): +#CFLAGS_EXTRA+=-DS32K1XX_CLOCK_HSRUN + +# Optionally enable watchdog +#CFLAGS_EXTRA+=-DWATCHDOG diff --git a/config/examples/nxp-s32k148.config b/config/examples/nxp-s32k148.config new file mode 100644 index 0000000000..b44a3b1e0e --- /dev/null +++ b/config/examples/nxp-s32k148.config @@ -0,0 +1,57 @@ +# wolfBoot configuration for NXP S32K148 +# +# S32K148: Cortex-M4F, 2MB Flash, 256KB SRAM +# Flash sector size: 4KB (larger flash variants use 4KB sectors) +# Default: RUN mode at 48 MHz (FIRC - internal RC oscillator) +# +# Build: cp config/examples/nxp-s32k148.config .config && make + +ARCH?=ARM +CORTEX_M4?=1 +TARGET?=s32k1xx +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +VTOR?=1 +NO_ASM?=0 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=0 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 + +# Select S32K148 variant for correct flash size and sector size +CFLAGS_EXTRA+=-DS32K148 + +# Disable MPU (S32K1xx MPU configuration needs customization) +WOLFBOOT_NO_MPU?=1 + +# Enable hardfault debugging +#CFLAGS_EXTRA+=-DDEBUG_HARDFAULT + +# Debug UART on LPUART1 (PTC6=RX, PTC7=TX) +DEBUG_UART?=0 + +# 4KB sectors (S32K144/146/148 with 512KB+ flash) +WOLFBOOT_SECTOR_SIZE?=0x1000 + +# Memory layout for S32K148 (2MB Flash): +# Bootloader: 0x00000000 - 0x0000BFFF (48 KB) +# Boot Partition: 0x0000C000 - 0x000FBFFF (960 KB) +# Update Partition: 0x000FC000 - 0x001EBFFF (960 KB) +# Swap Sector: 0x001EC000 - 0x001ECFFF (4 KB) +WOLFBOOT_PARTITION_SIZE?=0xF0000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xC000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0xFC000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x1EC000 + +# Clock mode: Default is RUN mode with FIRC (48 MHz internal RC) +# To enable HSRUN mode (112 MHz), uncomment below (requires SOSC + SPLL, not fully implemented): +#CFLAGS_EXTRA+=-DS32K1XX_CLOCK_HSRUN + +# Optionally enable watchdog +#CFLAGS_EXTRA+=-DWATCHDOG diff --git a/docs/Targets.md b/docs/Targets.md index 87dbd47215..c8c90d45be 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -23,6 +23,7 @@ This README describes configuration of supported targets. * [NXP LS1028A](#nxp-ls1028a) * [NXP MCXA153](#nxp-mcxa153) * [NXP MCXW716](#nxp-mcxw716) +* [NXP S32K1XX](#nxp-s32k1xx) * [NXP P1021 PPC](#nxp-qoriq-p1021-ppc) * [NXP T1024 PPC](#nxp-qoriq-t1024-ppc) * [NXP T2080 PPC](#nxp-qoriq-t2080-ppc) @@ -2829,6 +2830,225 @@ c ``` +## NXP S32K1XX + +The NXP S32K1xx family (S32K142, S32K144, S32K146, S32K148) are automotive-grade +Cortex-M4F microcontrollers. wolfBoot support has been tested on the S32K142 with +256KB Flash and 32KB SRAM. + +**Key Features:** +- ARM Cortex-M4F core at up to 112 MHz (HSRUN mode) or 80 MHz (RUN mode) +- Flash sector size: 2KB (4KB when flash is over 256KB) +- 8-byte (phrase) flash programming unit +- Bare-metal implementation (no SDK required) +- LPUART debug output support + +### NXP S32K1XX: Memory Layout + +The default memory layout for S32K142 (256KB Flash): + +| Region | Address Range | Size | +|--------|---------------|------| +| Bootloader | 0x00000000 - 0x0000BFFF | 48 KB | +| Boot Partition | 0x0000C000 - 0x00024FFF | 100 KB | +| Update Partition | 0x00025000 - 0x0003DFFF | 100 KB | +| Swap Sector | 0x0003E000 - 0x0003E7FF | 2 KB | + +### NXP S32K1XX: Configuration + +Example configuration files: +- [/config/examples/nxp-s32k142.config](/config/examples/nxp-s32k142.config) - S32K142 (256KB Flash, 32KB SRAM) +- [/config/examples/nxp-s32k144.config](/config/examples/nxp-s32k144.config) - S32K144 (512KB Flash, 64KB SRAM) +- [/config/examples/nxp-s32k146.config](/config/examples/nxp-s32k146.config) - S32K146 (1MB Flash, 128KB SRAM) +- [/config/examples/nxp-s32k148.config](/config/examples/nxp-s32k148.config) - S32K148 (2MB Flash, 256KB SRAM) + +```sh +# Copy configuration (example for S32K142) +cp config/examples/nxp-s32k142.config .config + +# Build wolfBoot +make clean +make + +# Build test application +make test-app/image.bin +``` + +### NXP S32K1XX: Configuration Options + +The following build options are available for the S32K1xx HAL: + +| Option | Description | +|--------|-------------| +| `WOLFBOOT_RESTORE_CLOCK` | Restore clock to SIRC (8 MHz) before booting application. Recommended for applications that configure their own clocks. | +| `WOLFBOOT_DISABLE_WATCHDOG_ON_BOOT` | Keep watchdog disabled when jumping to application. By default, the watchdog is re-enabled before boot since it is enabled out of reset. | +| `WATCHDOG` | Enable watchdog during wolfBoot operation. Recommended for production. | +| `WATCHDOG_TIMEOUT_MS` | Watchdog timeout in milliseconds when `WATCHDOG` is enabled (default: 1000ms). | +| `S32K1XX_CLOCK_HSRUN` | Enable HSRUN mode (112 MHz). Requires external crystal and SPLL (not fully implemented). | +| `DEBUG_UART` | Enable LPUART1 debug output. | +| `S32K144`, `S32K146`, `S32K148` | Select variant (default is S32K142). Affects flash/SRAM size definitions. | + +**IMPORTANT:** Flash sector size depends on the S32K variant: +- **S32K142** (256KB Flash): 2KB sectors (`WOLFBOOT_SECTOR_SIZE=0x800`) +- **S32K144/S32K146/S32K148** (512KB+ Flash): 4KB sectors (`WOLFBOOT_SECTOR_SIZE=0x1000`) + +### NXP S32K1XX: Debug UART + +For UART debug output, connect a USB-to-serial adapter to LPUART1 pins (PTC6=RX, PTC7=TX) and open a terminal at 115200 baud. + +Debug output uses LPUART1 on pins: +- **TX**: PTC7 +- **RX**: PTC6 + +Baud rate: 115200, 8N1 + +Enable with `DEBUG_UART=1` in your configuration. + +### NXP S32K1XX: Programming and Debugging + +The S32K1xx can be programmed and debugged using various tools. The recommended approach uses PEMicro debug probes (commonly found on S32K EVB boards). + +**Using PEMicro (recommended for S32K EVB boards):** + +1. Install PEMicro GDB Server from [pemicro.com](https://www.pemicro.com/products/product_viewDetails.cfm?product_id=15320167) + +2. Start PEMicro GDB Server: +```sh +pegdbserver_console -device=NXP_S32K1xx_S32K142 -startserver -serverport=7224 +``` + +3. In another terminal, connect with GDB and flash: +```sh +arm-none-eabi-gdb --nx wolfboot.elf +target remote :7224 +monitor reset halt +load +monitor reset run +``` + +### NXP S32K1XX: USB Mass Storage Programming + +The S32K EVB boards include an OpenSDA debugger that exposes a USB mass storage interface for easy programming. Simply copy the `.srec` file to the mounted USB drive. + +**Steps:** + +1. Connect the S32K EVB board via USB (OpenSDA port) +2. The board will mount as a USB drive (e.g., `S32K142EVB`) +3. Build the factory image: + +```sh +make factory.srec +``` + +4. Copy the `.srec` file to the mounted drive: + +```sh +cp factory.srec /media//S32K142EVB/ +``` + +The board will automatically program the flash and reset. + +### NXP S32K1XX: Test Application + +The S32K1xx test application (`test-app/app_s32k1xx.c`) provides a feature-rich demo application for testing wolfBoot functionality. + +**Features:** +- **LED Indicators**: Green LED for firmware v1, Blue LED for firmware v2+ +- **Interactive Console**: UART-based command interface +- **XMODEM Firmware Update**: Upload new firmware images via XMODEM protocol +- **Partition Information**: Display boot/update partition status and versions +- **Keystore Display**: Show public key information from the bootloader + +**Console Commands:** + +| Command | Description | +|---------|-------------| +| `help` or `?` | Show available commands | +| `info` | Display system and partition information | +| `success` | Mark current firmware as successful (wolfBoot_success) | +| `update` | Trigger firmware update (wolfBoot_update_trigger) | +| `reset` | Perform software reset | +| `xmodem` | Receive firmware image via XMODEM protocol | + +**UART Configuration:** +- LPUART1: PTC7 (TX), PTC6 (RX) +- Baud rate: 115200, 8N1 + +**Example Output:** + +``` +======================================== +S32K1xx wolfBoot Test Application +Copyright 2025 wolfSSL Inc. +======================================== +Firmware Version: 1 + +=== Partition Information === +Boot Partition: + Address: 0x0000C000 + Version: 1 + State: SUCCESS +Update Partition: + Address: 0x00025000 + Version: 0 + State: SUCCESS +Swap Partition: + Address: 0x0003E000 + Size: 2048 bytes + +Type 'help' for available commands. + +cmd> +``` + +### NXP S32K1XX: Flash Configuration Field (FCF) + +The bootloader includes the Flash Configuration Field (FCF) at address 0x400-0x40F with the following settings: +- Flash security: Unsecured +- Flash protection: All regions unprotected +- Backdoor key access: Enabled + +**CRITICAL WARNING:** The FCF region at 0x400-0x40F controls device security settings. Writing incorrect values can **permanently lock the device**, making it irrecoverable. The wolfBoot HAL includes protection to prevent accidental writes to this region. + +### NXP S32K1XX: Recovering a Locked/Unresponsive Device + +If your S32K device becomes locked or unresponsive (e.g., stuck in reset with D1 LED illuminated on S32K-EVB boards), try these recovery procedures: + +**Symptoms of a locked device:** +- Debugger cannot connect ("Soft reset failed", "Failed to enter debug mode") +- Device stuck in reset (D1 LED constantly on for S32K-EVB) +- J-Link reports "Readout protection is set" at address 0x400-0x40F + +**Recovery Option 1: PEMicro Force Mass Erase** + +```sh +pegdbserver_console -device=NXP_S32K1xx_S32K142 -interface=OPENSDA -port=USB1 -forcemasserase -singlesession +``` + +After mass erase completes, power cycle the board before attempting to reconnect. + +**Recovery Option 2: J-Link Unlock** + +```sh +JLinkExe -if swd -Device S32K142 +unlock Kinetis +erase +r +q +``` + +Then power cycle the board. + +### NXP S32K1XX: TODO / Future Enhancements + +The following features are planned or available for contribution: + +- [ ] **SPLL + SOSC support**: Add external crystal oscillator and SPLL configuration for true 112 MHz operation in HSRUN mode +- [ ] **Hardware crypto acceleration**: Integrate CSEc (Cryptographic Services Engine) for hardware-accelerated crypto operations +- [ ] **FlexNVM/EEPROM support**: Add support for FlexNVM partitioning and EEPROM emulation +- [ ] **CAN/LIN bootloader**: Add firmware update over CAN or LIN bus for automotive applications + + ## TI Hercules TMS570LC435 See [/config/examples/ti-tms570lc435.config](/config/examples/ti-tms570lc435.config) for example configuration. diff --git a/hal/hal.c b/hal/hal.c index c89e42f3d7..3b79fcccd1 100644 --- a/hal/hal.c +++ b/hal/hal.c @@ -41,7 +41,6 @@ int hal_flash_test(void) { int ret = 0; uint32_t i; - uint8_t* pagePtr = (uint8_t*)TEST_ADDRESS; static uint8_t pageData[TEST_SZ]; wolfBoot_printf("Internal flash test at 0x%x\n", TEST_ADDRESS); diff --git a/hal/s32k1xx.c b/hal/s32k1xx.c new file mode 100644 index 0000000000..aab1ca33e8 --- /dev/null +++ b/hal/s32k1xx.c @@ -0,0 +1,637 @@ +/* nxp_s32k1xx.c + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * HAL for NXP S32K1xx (S32K142, S32K144, S32K146, S32K148) + * Tested on S32K142: Cortex-M4F, 256KB Flash, 32KB SRAM + */ + +#include +#include +#include "image.h" +#include "hal.h" +#include "printf.h" + +/* Override RAMFUNCTION for test-app: when RAM_CODE is set but not __WOLFBOOT, + * we still need flash functions to run from RAM for self-programming. */ +#if defined(RAM_CODE) && !defined(__WOLFBOOT) + #undef RAMFUNCTION + #define RAMFUNCTION __attribute__((used,section(".ramcode"),long_call)) +#endif + +/* Assembly helpers */ +#define DMB() __asm__ volatile ("dmb") +#define DSB() __asm__ volatile ("dsb") +#define ISB() __asm__ volatile ("isb") + +#include "s32k1xx.h" + +/* ============== Flash Configuration Field (FCF) ============== */ +/* Located at 0x400-0x40F in flash */ + +#ifdef __WOLFBOOT +#define FCF_LEN (16) +const uint8_t __attribute__((section(".flash_config"))) flash_config[FCF_LEN] = { + /* Backdoor comparison key (8 bytes) */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /* Program Flash Protection (4 bytes) - all unprotected */ + 0xFF, 0xFF, 0xFF, 0xFF, + /* Flash Security Byte */ + 0xFE, /* SEC=10 (unsecured), FSLACC=11, MEEN=11, KEYEN=11 */ + /* Flash Option Byte */ + 0xFF, + /* EEPROM Protection Byte */ + 0xFF, + /* Data Flash Protection Byte */ + 0xFF +}; +#endif + +/* ============== Watchdog Functions ============== */ + +/* Disable watchdog - must be called within 128 bus clock cycles after reset + * or after unlocking. The watchdog is enabled by default after reset. + */ +static void watchdog_disable(void) +{ + /* Unlock watchdog by writing unlock key to CNT register */ + WDOG_CNT = WDOG_CNT_UNLOCK; + + /* Wait for unlock to complete (ULK bit set) */ + while (!(WDOG_CS & WDOG_CS_ULK)) {} + + /* Set timeout to max and disable watchdog */ + WDOG_TOVAL = WDOG_TOVAL_DEFAULT; + WDOG_CS = WDOG_CS_DISABLE_CFG; + + /* Wait for reconfiguration to complete (RCS bit set) */ + while (!(WDOG_CS & WDOG_CS_RCS)) {} +} + +#ifdef WATCHDOG +/* Enable watchdog with specified timeout value + * timeout_ms: timeout in milliseconds (max ~512ms with LPO clock) + * LPO clock is 128kHz, so each tick is ~7.8125us + * For longer timeouts, use PRES bit for 256x prescaler + */ +static void watchdog_enable(uint32_t timeout_ms) +{ + uint32_t toval; + uint32_t cs_cfg = WDOG_CS_ENABLE_CFG; + + /* Calculate TOVAL from timeout_ms + * LPO = 128kHz = 128 ticks/ms + * With PRES=0: max timeout = 65535/128 = 512ms + * With PRES=1: max timeout = 65535*256/128 = 131 seconds + */ + if (timeout_ms > 512) { + /* Use prescaler for longer timeouts */ + cs_cfg |= WDOG_CS_PRES; + toval = (timeout_ms * 128) / 256; + } else { + toval = timeout_ms * 128; + } + + /* Clamp to max value */ + if (toval > 0xFFFF) { + toval = 0xFFFF; + } + + /* Unlock watchdog */ + WDOG_CNT = WDOG_CNT_UNLOCK; + while (!(WDOG_CS & WDOG_CS_ULK)) {} + + /* Configure and enable */ + WDOG_TOVAL = toval; + WDOG_CS = cs_cfg; + + /* Wait for reconfiguration to complete */ + while (!(WDOG_CS & WDOG_CS_RCS)) {} +} + +/* Refresh (kick) the watchdog to prevent reset + * Must be called periodically before timeout expires + */ +static void watchdog_refresh(void) +{ + /* For CMD32EN mode, write refresh key as 32-bit value */ + WDOG_CNT = WDOG_CNT_REFRESH; +} +#endif /* WATCHDOG */ + +/* ============== Clock Configuration ============== */ + +/* SIRC - Slow Internal RC (8 MHz) register fields */ +#define SCG_SIRCCSR_SIRCEN (1UL << 0) +#define SCG_SIRCCSR_SIRCVLD (1UL << 24) +#define SCG_xCCR_SCS_SIRC (2UL << SCG_xCCR_SCS_SHIFT) +#define SCG_CSR_SCS_SIRC (2UL << SCG_CSR_SCS_SHIFT) + +#ifdef WOLFBOOT_RESTORE_CLOCK +/* Restore clock to safe default (SIRC 8 MHz) before booting application. + * This allows the application to configure clocks from a known state. + */ +static void clock_restore_sirc(void) +{ + /* Enable SIRC (8 MHz) if not already enabled */ + SCG_SIRCDIV = (1UL << 8) | (1UL << 0); /* SIRCDIV1=/1, SIRCDIV2=/1 */ + SCG_SIRCCFG = 0; /* Range 0: 2 MHz (default) - actually S32K uses 8MHz SIRC */ + SCG_SIRCCSR = SCG_SIRCCSR_SIRCEN; + + /* Wait for SIRC valid */ + while (!(SCG_SIRCCSR & SCG_SIRCCSR_SIRCVLD)) {} + + /* Switch to SIRC as system clock + * SCS = SIRC (2) + * DIVCORE = /1 (8 MHz) + * DIVBUS = /1 (8 MHz) + * DIVSLOW = /1 (8 MHz) + */ + SCG_RCCR = SCG_xCCR_SCS_SIRC | + (0UL << SCG_xCCR_DIVCORE_SHIFT) | + (0UL << SCG_xCCR_DIVBUS_SHIFT) | + (0UL << SCG_xCCR_DIVSLOW_SHIFT); + + /* Wait for clock switch */ + while ((SCG_CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS_SIRC) {} + + /* Disable FIRC to save power (application can re-enable if needed) */ + SCG_FIRCCSR &= ~SCG_FIRCCSR_FIRCEN; +} +#endif /* WOLFBOOT_RESTORE_CLOCK */ + +static void clock_init_firc(void) +{ + /* Enable FIRC (48 MHz) */ + SCG_FIRCDIV = (1UL << 8) | (1UL << 0); /* FIRCDIV1=/1, FIRCDIV2=/1 */ + SCG_FIRCCFG = 0; /* Range 0: 48 MHz */ + SCG_FIRCCSR = SCG_FIRCCSR_FIRCEN; + + /* Wait for FIRC valid */ + while (!(SCG_FIRCCSR & SCG_FIRCCSR_FIRCVLD)) {} +} + +static void clock_init_spll(void) +{ + /* S32K1xx SPLL requires SOSC as source (FIRC cannot be used directly). + * For 112 MHz with 8 MHz SOSC: PREDIV=0, MULT=28 -> VCO=224 MHz, SPLL=112 MHz + * VCO range: 180-320 MHz, SPLL_CLK = VCO / 2 + * + * Currently using FIRC 48 MHz directly. TODO: Add SOSC + SPLL for 112 MHz. + */ + SCG_SPLLCSR &= ~SCG_SPLLCSR_SPLLEN; + SCG_SPLLDIV = (2UL << 8) | (4UL << 0); /* SPLLDIV1=/2, SPLLDIV2=/4 */ +} + +static void clock_init(void) +{ + /* Initialize FIRC to 48 MHz */ + clock_init_firc(); + + /* Configure Run mode clock control: + * SCS = FIRC (3) + * DIVCORE = /1 (48 MHz) + * DIVBUS = /1 (48 MHz) + * DIVSLOW = /2 (24 MHz for flash) + */ + SCG_RCCR = SCG_xCCR_SCS_FIRC | + (0UL << SCG_xCCR_DIVCORE_SHIFT) | + (0UL << SCG_xCCR_DIVBUS_SHIFT) | + (1UL << SCG_xCCR_DIVSLOW_SHIFT); + + /* Wait for clock switch */ + while ((SCG_CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS_FIRC) {} + +#ifdef S32K1XX_CLOCK_HSRUN + /* HSRUN mode (112 MHz) - requires SOSC + SPLL (not fully implemented yet) + * TODO: Add SOSC initialization and SPLL configuration for true 112 MHz + * Currently this enters HSRUN mode but still uses FIRC at 48 MHz + */ + + /* Enable HSRUN mode */ + SMC_PMPROT = SMC_PMPROT_AHSRUN; + + /* Configure HSRUN clock control (same as RUN for now with FIRC) */ + SCG_HCCR = SCG_xCCR_SCS_FIRC | + (0UL << SCG_xCCR_DIVCORE_SHIFT) | + (0UL << SCG_xCCR_DIVBUS_SHIFT) | + (1UL << SCG_xCCR_DIVSLOW_SHIFT); + + /* Enter HSRUN mode */ + SMC_PMCTRL = (SMC_PMCTRL & ~(3UL << SMC_PMCTRL_RUNM_SHIFT)) | SMC_PMCTRL_RUNM_HSRUN; + + /* Wait for HSRUN */ + while ((SMC_PMSTAT & 0xFF) != SMC_PMSTAT_HSRUN) {} +#endif +} + +/* ============== UART Functions ============== */ + +#ifdef DEBUG_UART + +#ifndef UART_BAUDRATE +#define UART_BAUDRATE 115200 +#endif + +void uart_init(void) +{ + uint32_t sbr; + uint32_t osr = 16; /* Oversampling ratio */ + uint32_t uart_clock = 48000000UL; /* FIRC 48 MHz */ + + /* Enable clock to TX and RX port(s) + * Note: If TX and RX use different ports, both need clock enabled + */ + DEBUG_UART_TX_PCC_PORT |= PCC_CGC; +#if !DEBUG_UART_SAME_PORT + DEBUG_UART_RX_PCC_PORT |= PCC_CGC; +#endif + + /* Configure pins for selected LPUART */ + DEBUG_UART_RX_PCR = DEBUG_UART_RX_MUX; + DEBUG_UART_TX_PCR = DEBUG_UART_TX_MUX; + + /* Enable clock to selected LPUART, source = FIRC (48 MHz) */ + PCC_LPUART = 0; /* Disable before changing source */ + PCC_LPUART = PCC_PCS_FIRC | PCC_CGC; + + /* Calculate baud rate: + * SBR = UART_CLK / (BAUD * OSR) + */ + sbr = uart_clock / (UART_BAUDRATE * osr); + + /* Disable TX/RX before configuration */ + LPUART_CTRL = 0; + + /* Configure baud rate */ + LPUART_BAUD = ((osr - 1) << LPUART_BAUD_OSR_SHIFT) | + (sbr << LPUART_BAUD_SBR_SHIFT); + + /* Enable transmitter and receiver */ + LPUART_CTRL = LPUART_CTRL_TE | LPUART_CTRL_RE; +} + +/* Transmit a single byte (raw, no conversion) - RAMFUNCTION for use during flash ops */ +void RAMFUNCTION uart_tx(uint8_t byte) +{ + while (!(LPUART1_STAT & LPUART_STAT_TDRE)) {} + LPUART1_DATA = byte; + while (!(LPUART1_STAT & LPUART_STAT_TC)) {} +} + +/* Used for sending ASCII and CRLF conversions */ +void uart_write(const char* buf, unsigned int sz) +{ + unsigned int i; + for (i = 0; i < sz; i++) { + /* Handle newline -> CRLF conversion */ + if (buf[i] == '\n') { + uart_tx('\r'); + } + uart_tx(buf[i]); + } + + /* Wait for transmission complete */ + while (!(LPUART_STAT & LPUART_STAT_TC)) {} +} + + + +/* Read a single character from UART (non-blocking) - RAMFUNCTION for use during flash ops + * Returns: 1 if character read, 0 if no data available + */ +int RAMFUNCTION uart_read(char* c) +{ + uint32_t stat = LPUART1_STAT; + + /* Clear any error flags first */ + if (stat & (LPUART_STAT_OR | LPUART_STAT_NF | LPUART_STAT_FE | LPUART_STAT_PF)) { + LPUART1_STAT = stat; /* Write 1 to clear flags */ + } + + /* Check if data available - read even if there was an error */ + if (stat & LPUART_STAT_RDRF) { + *c = (char)(LPUART1_DATA & 0xFF); + return 1; + } + + return 0; /* No data available */ +} + +#endif /* DEBUG_UART */ + +/* ============== Flash Functions ============== */ + +static void RAMFUNCTION flash_wait_complete(void) +{ + /* Wait for command complete */ + while (!(FTFC_FSTAT & FTFC_FSTAT_CCIF)) {} +} + +static void RAMFUNCTION flash_clear_errors(void) +{ + /* Clear error flags by writing 1 */ + if (FTFC_FSTAT & (FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL)) { + FTFC_FSTAT = FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL; + } +} + +static int RAMFUNCTION flash_program_phrase(uint32_t address, const uint8_t *data) +{ + /* Wait for previous command to complete */ + flash_wait_complete(); + flash_clear_errors(); + + /* Set up Program Phrase command (0x07) + * Programs 8 bytes at the specified address + */ + FTFC_FCCOB0 = FTFC_CMD_PROGRAM_PHRASE; + FTFC_FCCOB1 = (uint8_t)(address >> 16); + FTFC_FCCOB2 = (uint8_t)(address >> 8); + FTFC_FCCOB3 = (uint8_t)(address); + + /* Data bytes (big-endian order in FCCOB registers) */ + FTFC_FCCOB4 = data[3]; + FTFC_FCCOB5 = data[2]; + FTFC_FCCOB6 = data[1]; + FTFC_FCCOB7 = data[0]; + FTFC_FCCOB8 = data[7]; + FTFC_FCCOB9 = data[6]; + FTFC_FCCOBA = data[5]; + FTFC_FCCOBB = data[4]; + + /* Launch command */ + DSB(); + ISB(); + FTFC_FSTAT = FTFC_FSTAT_CCIF; + + /* Wait for completion */ + flash_wait_complete(); + +#ifdef WATCHDOG + /* Refresh watchdog after flash operation */ + watchdog_refresh(); +#endif + + /* Check for errors */ + if (FTFC_FSTAT & (FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL | FTFC_FSTAT_MGSTAT0)) { + return -1; + } + + return 0; +} + +static int RAMFUNCTION flash_erase_sector_internal(uint32_t address) +{ + uint32_t primask; + + /* Wait for previous command to complete */ + flash_wait_complete(); + flash_clear_errors(); + + /* Set up Erase Sector command (0x09) */ + FTFC_FCCOB0 = FTFC_CMD_ERASE_SECTOR; + FTFC_FCCOB1 = (uint8_t)(address >> 16); + FTFC_FCCOB2 = (uint8_t)(address >> 8); + FTFC_FCCOB3 = (uint8_t)(address); + + /* Launch command */ + DSB(); + ISB(); + + /* Disable interrupts during flash operation to prevent code fetch from flash */ + __asm__ volatile ("mrs %0, primask\n\t" + "cpsid i" : "=r" (primask) :: "memory"); + + FTFC_FSTAT = FTFC_FSTAT_CCIF; + + /* Wait for completion */ + flash_wait_complete(); + + /* Re-enable interrupts */ + __asm__ volatile ("msr primask, %0" :: "r" (primask) : "memory"); + +#ifdef WATCHDOG + /* Refresh watchdog after potentially long flash operation */ + watchdog_refresh(); +#endif + + /* Check for errors */ + if (FTFC_FSTAT & (FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL | FTFC_FSTAT_MGSTAT0)) { + return -1; + } + + return 0; +} + + +/* ============== HAL Interface Functions ============== */ + +void hal_init(void) +{ + /* Disable watchdog first - must be done early after reset */ + watchdog_disable(); + + /* Initialize clocks */ + clock_init(); + + /* Enable clock to flash controller */ + PCC_FTFC |= PCC_CGC; + +#ifdef DEBUG_UART + uart_init(); + +#ifdef __WOLFBOOT + wolfBoot_printf("wolfBoot Version: %s (%s %s)\n", + LIBWOLFBOOT_VERSION_STRING, __DATE__, __TIME__); +#endif +#endif + +#ifdef WATCHDOG + watchdog_enable(WATCHDOG_TIMEOUT_MS); +#endif +} + +void hal_prepare_boot(void) +{ +#ifdef DEBUG_UART + /* Wait for any pending UART transmission to complete */ + while (!(LPUART_STAT & LPUART_STAT_TC)) {} +#endif + +#ifdef WOLFBOOT_RESTORE_CLOCK + /* Restore clock to SIRC (8 MHz) before booting application. + * This gives the application a known clock state to start from. + */ + clock_restore_sirc(); +#endif + + /* Re-enable watchdog before booting application. + * The watchdog is enabled by default after reset, so the application + * may expect it to be running. Use a generous timeout to give the + * application time to reconfigure or disable the watchdog. + */ +#ifndef WOLFBOOT_DISABLE_WATCHDOG_ON_BOOT + { + /* Unlock watchdog */ + WDOG_CNT = WDOG_CNT_UNLOCK; + while (!(WDOG_CS & WDOG_CS_ULK)) {} + + /* Enable watchdog with ~2 second timeout (256k ticks at 128kHz LPO) + * Application should either service or reconfigure the watchdog + */ + WDOG_TOVAL = 0xFFFF; /* Max timeout ~512ms without prescaler */ + WDOG_CS = WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_CMD32EN | + WDOG_CS_CLK_LPO | WDOG_CS_PRES; /* With prescaler: ~131 sec */ + + /* Wait for reconfiguration to complete */ + while (!(WDOG_CS & WDOG_CS_RCS)) {} + } +#endif +} + +int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + int ret = 0; + int i = 0; + uint8_t phrase_buf[FLASH_PHRASE_SIZE]; + const uint8_t empty_phrase[FLASH_PHRASE_SIZE] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + /* CRITICAL: Protect the Flash Configuration Field (FCF) region. + * Writing incorrect values to 0x400-0x40F can permanently lock the device! + * The FCF is programmed once in the flash_config section and should never + * be modified at runtime. + */ + if ((address < FCF_END_ADDR) && ((address + len) > FCF_START_ADDR)) { + /* Requested write overlaps with FCF region - this is dangerous! + * Skip the FCF portion to prevent device locking. + */ + if (address < FCF_START_ADDR) { + /* Write portion before FCF */ + int pre_fcf_len = FCF_START_ADDR - address; + ret = hal_flash_write(address, data, pre_fcf_len); + if (ret != 0) return ret; + address = FCF_END_ADDR; + data += pre_fcf_len + (FCF_END_ADDR - FCF_START_ADDR); + len -= pre_fcf_len + (FCF_END_ADDR - FCF_START_ADDR); + } else if (address >= FCF_START_ADDR && address < FCF_END_ADDR) { + /* Skip entirely within FCF region */ + int skip = FCF_END_ADDR - address; + if (skip >= len) { + return 0; /* Entire write is within FCF - skip it */ + } + address = FCF_END_ADDR; + data += skip; + len -= skip; + } + if (len <= 0) return 0; + } + + while (len > 0) { + /* Handle unaligned start or partial phrase */ + if ((len < FLASH_PHRASE_SIZE) || (address & (FLASH_PHRASE_SIZE - 1))) { + uint32_t aligned_addr = address & ~(FLASH_PHRASE_SIZE - 1); + uint32_t offset = address - aligned_addr; + int bytes_to_copy; + + /* Read current phrase data */ + memcpy(phrase_buf, (void*)aligned_addr, FLASH_PHRASE_SIZE); + + /* Calculate bytes to copy */ + bytes_to_copy = FLASH_PHRASE_SIZE - offset; + if (bytes_to_copy > len) { + bytes_to_copy = len; + } + + /* Merge new data */ + memcpy(phrase_buf + offset, data + i, bytes_to_copy); + + /* Only program if not all 0xFF */ + if (memcmp(phrase_buf, empty_phrase, FLASH_PHRASE_SIZE) != 0) { + ret = flash_program_phrase(aligned_addr, phrase_buf); + if (ret != 0) { + return ret; + } + } + + address += bytes_to_copy; + i += bytes_to_copy; + len -= bytes_to_copy; + } + else { + /* Program full phrases */ + while (len >= FLASH_PHRASE_SIZE) { + /* Only program if not all 0xFF */ + if (memcmp(data + i, empty_phrase, FLASH_PHRASE_SIZE) != 0) { + ret = flash_program_phrase(address, data + i); + if (ret != 0) { + return ret; + } + } + + address += FLASH_PHRASE_SIZE; + i += FLASH_PHRASE_SIZE; + len -= FLASH_PHRASE_SIZE; + } + } + } + + return 0; +} + +int RAMFUNCTION hal_flash_erase(uint32_t address, int len) +{ + int ret; + + /* Align address to sector boundary */ + if (address % FLASH_SECTOR_SIZE) { + address -= (address % FLASH_SECTOR_SIZE); + } + + while (len > 0) { + ret = flash_erase_sector_internal(address); + if (ret != 0) { + return ret; + } + + address += FLASH_SECTOR_SIZE; + len -= FLASH_SECTOR_SIZE; + } + + return 0; +} + +void RAMFUNCTION hal_flash_unlock(void) +{ + /* Ensure flash controller clock is enabled */ + PCC_FTFC |= PCC_CGC; + + /* Clear any pending errors */ + if (FTFC_FSTAT & (FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL)) { + FTFC_FSTAT = FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL; + } +} + +void RAMFUNCTION hal_flash_lock(void) +{ + /* No explicit lock needed */ +} + diff --git a/hal/s32k1xx.h b/hal/s32k1xx.h new file mode 100644 index 0000000000..f105b3a3ba --- /dev/null +++ b/hal/s32k1xx.h @@ -0,0 +1,633 @@ +/* s32k1xx.h + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * Hardware register definitions for NXP S32K1xx (S32K142, S32K144, S32K146, S32K148) + */ + +#ifndef S32K1XX_H +#define S32K1XX_H + +#include + +/* + * Clock configuration + * S32K142 supports: + * - FIRC: 48 MHz Fast Internal RC + * - SIRC: 8 MHz Slow Internal RC + * - SOSC: 8-40 MHz System Oscillator (external crystal) + * - SPLL: System PLL (up to 160 MHz VCO, /2 for SPLL_CLK) + * + * Run modes: + * - RUN: Up to 80 MHz core clock (requires SPLL, currently using FIRC at 48 MHz) + * - HSRUN: Up to 112 MHz core clock (requires SOSC + SPLL, not implemented yet) + * + * Default: RUN mode with FIRC at 48 MHz (no external crystal required) + * To enable HSRUN mode (112 MHz), define S32K1XX_CLOCK_HSRUN (requires SOSC + SPLL) + */ + +/* ============== ARM Cortex-M4 System Registers ============== */ + +/* System Control Block (SCB) */ +#define SCB_BASE (0xE000ED00UL) +#define SCB_CPUID (*(volatile uint32_t *)(SCB_BASE + 0x00UL)) +#define SCB_ICSR (*(volatile uint32_t *)(SCB_BASE + 0x04UL)) +#define SCB_VTOR (*(volatile uint32_t *)(SCB_BASE + 0x08UL)) +#define SCB_AIRCR (*(volatile uint32_t *)(SCB_BASE + 0x0CUL)) +#define SCB_SCR (*(volatile uint32_t *)(SCB_BASE + 0x10UL)) +#define SCB_CCR (*(volatile uint32_t *)(SCB_BASE + 0x14UL)) + +/* AIRCR - Application Interrupt and Reset Control Register */ +#define AIRCR_VECTKEY (0x05FAUL << 16) +#define AIRCR_SYSRESETREQ (1UL << 2) + +/* SysTick Timer */ +#define SYST_BASE (0xE000E010UL) +#define SYST_CSR (*(volatile uint32_t *)(SYST_BASE + 0x00UL)) +#define SYST_RVR (*(volatile uint32_t *)(SYST_BASE + 0x04UL)) +#define SYST_CVR (*(volatile uint32_t *)(SYST_BASE + 0x08UL)) +#define SYST_CALIB (*(volatile uint32_t *)(SYST_BASE + 0x0CUL)) + +#define SYST_CSR_ENABLE (1UL << 0) +#define SYST_CSR_TICKINT (1UL << 1) +#define SYST_CSR_CLKSOURCE (1UL << 2) +#define SYST_CSR_COUNTFLAG (1UL << 16) + +/* Clock speed (FIRC = 48 MHz) */ +#ifndef CLOCK_SPEED +#define CLOCK_SPEED 48000000UL +#endif + +/* ============== NVIC - Nested Vectored Interrupt Controller ============== */ +#define NVIC_BASE (0xE000E100UL) +#define NVIC_ISER(n) (*(volatile uint32_t *)(NVIC_BASE + 0x000UL + 4*(n))) /* Interrupt Set Enable */ +#define NVIC_ICER(n) (*(volatile uint32_t *)(NVIC_BASE + 0x080UL + 4*(n))) /* Interrupt Clear Enable */ +#define NVIC_ISPR(n) (*(volatile uint32_t *)(NVIC_BASE + 0x100UL + 4*(n))) /* Interrupt Set Pending */ +#define NVIC_ICPR(n) (*(volatile uint32_t *)(NVIC_BASE + 0x180UL + 4*(n))) /* Interrupt Clear Pending */ +#define NVIC_IPR(n) (*(volatile uint32_t *)(NVIC_BASE + 0x300UL + 4*(n))) /* Interrupt Priority */ + +/* S32K142 LPUART IRQ numbers */ +#define LPUART0_IRQn 31 +#define LPUART1_IRQn 33 +#define LPUART2_IRQn 35 + +/* NVIC helper macros */ +#define NVIC_EnableIRQ(irq) NVIC_ISER((irq) >> 5) = (1UL << ((irq) & 0x1F)) +#define NVIC_DisableIRQ(irq) NVIC_ICER((irq) >> 5) = (1UL << ((irq) & 0x1F)) +#define NVIC_SetPriority(irq, prio) \ + do { \ + uint32_t _idx = (irq) >> 2; \ + uint32_t _shift = (((irq) & 0x3) << 3) + 4; \ + NVIC_IPR(_idx) = (NVIC_IPR(_idx) & ~(0xFUL << _shift)) | (((prio) & 0xF) << _shift); \ + } while(0) + +/* ============== System Control Registers ============== */ + +/* SCG - System Clock Generator */ +#define SCG_BASE (0x40064000UL) +#define SCG_CSR (*(volatile uint32_t *)(SCG_BASE + 0x010UL)) /* Clock Status Register */ +#define SCG_RCCR (*(volatile uint32_t *)(SCG_BASE + 0x014UL)) /* Run Clock Control Register */ +#define SCG_VCCR (*(volatile uint32_t *)(SCG_BASE + 0x018UL)) /* VLPR Clock Control Register */ +#define SCG_HCCR (*(volatile uint32_t *)(SCG_BASE + 0x01CUL)) /* HSRUN Clock Control Register */ +#define SCG_CLKOUTCNFG (*(volatile uint32_t *)(SCG_BASE + 0x020UL)) /* Clock Out Configuration */ + +/* SOSC - System OSC */ +#define SCG_SOSCCSR (*(volatile uint32_t *)(SCG_BASE + 0x100UL)) +#define SCG_SOSCDIV (*(volatile uint32_t *)(SCG_BASE + 0x104UL)) +#define SCG_SOSCCFG (*(volatile uint32_t *)(SCG_BASE + 0x108UL)) + +/* SIRC - Slow IRC */ +#define SCG_SIRCCSR (*(volatile uint32_t *)(SCG_BASE + 0x200UL)) +#define SCG_SIRCDIV (*(volatile uint32_t *)(SCG_BASE + 0x204UL)) +#define SCG_SIRCCFG (*(volatile uint32_t *)(SCG_BASE + 0x208UL)) + +/* FIRC - Fast IRC */ +#define SCG_FIRCCSR (*(volatile uint32_t *)(SCG_BASE + 0x300UL)) +#define SCG_FIRCDIV (*(volatile uint32_t *)(SCG_BASE + 0x304UL)) +#define SCG_FIRCCFG (*(volatile uint32_t *)(SCG_BASE + 0x308UL)) + +/* SPLL - System PLL */ +#define SCG_SPLLCSR (*(volatile uint32_t *)(SCG_BASE + 0x600UL)) +#define SCG_SPLLDIV (*(volatile uint32_t *)(SCG_BASE + 0x604UL)) +#define SCG_SPLLCFG (*(volatile uint32_t *)(SCG_BASE + 0x608UL)) + +/* SCG CSR fields */ +#define SCG_CSR_SCS_SHIFT 24 +#define SCG_CSR_SCS_MASK (0xFUL << SCG_CSR_SCS_SHIFT) +#define SCG_CSR_SCS_FIRC (3UL << SCG_CSR_SCS_SHIFT) +#define SCG_CSR_SCS_SPLL (6UL << SCG_CSR_SCS_SHIFT) + +/* SCG xCCR fields */ +#define SCG_xCCR_SCS_SHIFT 24 +#define SCG_xCCR_SCS_FIRC (3UL << SCG_xCCR_SCS_SHIFT) +#define SCG_xCCR_SCS_SPLL (6UL << SCG_xCCR_SCS_SHIFT) +#define SCG_xCCR_DIVCORE_SHIFT 16 +#define SCG_xCCR_DIVBUS_SHIFT 4 +#define SCG_xCCR_DIVSLOW_SHIFT 0 + +/* FIRC CSR fields */ +#define SCG_FIRCCSR_FIRCEN (1UL << 0) +#define SCG_FIRCCSR_FIRCVLD (1UL << 24) + +/* SPLL CSR fields */ +#define SCG_SPLLCSR_SPLLEN (1UL << 0) +#define SCG_SPLLCSR_SPLLVLD (1UL << 24) + +/* SPLL CFG fields */ +#define SCG_SPLLCFG_MULT_SHIFT 16 +#define SCG_SPLLCFG_PREDIV_SHIFT 8 + +/* SMC - System Mode Controller */ +#define SMC_BASE (0x4007E000UL) +#define SMC_PMPROT (*(volatile uint32_t *)(SMC_BASE + 0x000UL)) +#define SMC_PMCTRL (*(volatile uint32_t *)(SMC_BASE + 0x004UL)) +#define SMC_PMSTAT (*(volatile uint32_t *)(SMC_BASE + 0x008UL)) + +#define SMC_PMPROT_AHSRUN (1UL << 7) /* Allow HSRUN */ +#define SMC_PMCTRL_RUNM_SHIFT 5 +#define SMC_PMCTRL_RUNM_RUN (0UL << SMC_PMCTRL_RUNM_SHIFT) +#define SMC_PMCTRL_RUNM_HSRUN (3UL << SMC_PMCTRL_RUNM_SHIFT) +#define SMC_PMSTAT_HSRUN (0x80UL) +#define SMC_PMSTAT_RUN (0x01UL) + +/* PCC - Peripheral Clock Controller */ +#define PCC_BASE (0x40065000UL) +#define PCC_PORTA (*(volatile uint32_t *)(PCC_BASE + 0x124UL)) +#define PCC_PORTB (*(volatile uint32_t *)(PCC_BASE + 0x128UL)) +#define PCC_PORTC (*(volatile uint32_t *)(PCC_BASE + 0x12CUL)) +#define PCC_LPUART0 (*(volatile uint32_t *)(PCC_BASE + 0x1A8UL)) +#define PCC_LPUART1 (*(volatile uint32_t *)(PCC_BASE + 0x1ACUL)) +#define PCC_LPUART2 (*(volatile uint32_t *)(PCC_BASE + 0x1B0UL)) +#define PCC_FTFC (*(volatile uint32_t *)(PCC_BASE + 0x0B0UL)) + +#define PCC_CGC (1UL << 30) /* Clock Gate Control */ +#define PCC_PCS_SHIFT 24 +#define PCC_PCS_FIRC (3UL << PCC_PCS_SHIFT) /* FIRC 48MHz */ +#define PCC_PCS_SPLLDIV2 (6UL << PCC_PCS_SHIFT) /* SPLL DIV2 */ + +/* ============== GPIO / Port Registers ============== */ + +/* PCC for GPIO ports */ +#define PCC_PORTD (*(volatile uint32_t *)(PCC_BASE + 0x130UL)) +#define PCC_PORTE (*(volatile uint32_t *)(PCC_BASE + 0x134UL)) + +/* Port A - LPUART0/LPUART2 alternate pins */ +#define PORTA_BASE (0x40049000UL) +#define PORTA_PCR(n) (*(volatile uint32_t *)(PORTA_BASE + ((n) * 4))) +#define PORTA_PCR2 (*(volatile uint32_t *)(PORTA_BASE + 0x008UL)) /* LPUART0_RX (ALT6) */ +#define PORTA_PCR3 (*(volatile uint32_t *)(PORTA_BASE + 0x00CUL)) /* LPUART0_TX (ALT6) */ +#define PORTA_PCR8 (*(volatile uint32_t *)(PORTA_BASE + 0x020UL)) /* LPUART2_RX (ALT6) */ +#define PORTA_PCR9 (*(volatile uint32_t *)(PORTA_BASE + 0x024UL)) /* LPUART2_TX (ALT6) */ + +/* Port B - LPUART0/LPUART1 alternate pins */ +#define PORTB_BASE (0x4004A000UL) +#define PORTB_PCR(n) (*(volatile uint32_t *)(PORTB_BASE + ((n) * 4))) +#define PORTB_PCR0 (*(volatile uint32_t *)(PORTB_BASE + 0x000UL)) /* LPUART0_RX (ALT2) */ +#define PORTB_PCR1 (*(volatile uint32_t *)(PORTB_BASE + 0x004UL)) /* LPUART0_TX (ALT2) */ + +/* Port C - LPUART1/LPUART2 pins */ +#define PORTC_BASE (0x4004B000UL) +#define PORTC_PCR(n) (*(volatile uint32_t *)(PORTC_BASE + ((n) * 4))) +#define PORTC_PCR6 (*(volatile uint32_t *)(PORTC_BASE + 0x018UL)) /* LPUART1_RX (ALT2) */ +#define PORTC_PCR7 (*(volatile uint32_t *)(PORTC_BASE + 0x01CUL)) /* LPUART1_TX (ALT2) */ +#define PORTC_PCR8 (*(volatile uint32_t *)(PORTC_BASE + 0x020UL)) /* LPUART1_RX (ALT2) alt */ +#define PORTC_PCR9 (*(volatile uint32_t *)(PORTC_BASE + 0x024UL)) /* LPUART1_TX (ALT2) alt */ + +/* Port D - LED pins on S32K142EVB, LPUART2 alternate pins */ +#define PORTD_BASE (0x4004C000UL) +#define PORTD_PCR(n) (*(volatile uint32_t *)(PORTD_BASE + ((n) * 4))) +#define PORTD_PCR0 (*(volatile uint32_t *)(PORTD_BASE + 0x000UL)) /* Blue LED */ +#define PORTD_PCR6 (*(volatile uint32_t *)(PORTD_BASE + 0x018UL)) /* LPUART2_RX (ALT2) */ +#define PORTD_PCR7 (*(volatile uint32_t *)(PORTD_BASE + 0x01CUL)) /* LPUART2_TX (ALT2) */ +#define PORTD_PCR15 (*(volatile uint32_t *)(PORTD_BASE + 0x03CUL)) /* Red LED */ +#define PORTD_PCR16 (*(volatile uint32_t *)(PORTD_BASE + 0x040UL)) /* Green LED */ + +/* Port E - additional peripheral pins */ +#define PORTE_BASE (0x4004D000UL) +#define PORTE_PCR(n) (*(volatile uint32_t *)(PORTE_BASE + ((n) * 4))) + +/* GPIO D registers */ +#define GPIOD_BASE (0x400FF0C0UL) +#define GPIOD_PDOR (*(volatile uint32_t *)(GPIOD_BASE + 0x00UL)) /* Data Output */ +#define GPIOD_PSOR (*(volatile uint32_t *)(GPIOD_BASE + 0x04UL)) /* Set Output */ +#define GPIOD_PCOR (*(volatile uint32_t *)(GPIOD_BASE + 0x08UL)) /* Clear Output */ +#define GPIOD_PTOR (*(volatile uint32_t *)(GPIOD_BASE + 0x0CUL)) /* Toggle Output */ +#define GPIOD_PDIR (*(volatile uint32_t *)(GPIOD_BASE + 0x10UL)) /* Data Input */ +#define GPIOD_PDDR (*(volatile uint32_t *)(GPIOD_BASE + 0x14UL)) /* Data Direction */ + +/* Port Control Register fields */ +#define PORT_PCR_MUX_SHIFT 8 +#define PORT_PCR_MUX_MASK (7UL << PORT_PCR_MUX_SHIFT) +#define PORT_PCR_MUX_GPIO (1UL << PORT_PCR_MUX_SHIFT) /* GPIO mode */ +#define PORT_PCR_MUX_ALT2 (2UL << PORT_PCR_MUX_SHIFT) /* Alternate function 2 */ +#define PORT_PCR_MUX_ALT3 (3UL << PORT_PCR_MUX_SHIFT) /* Alternate function 3 */ +#define PORT_PCR_MUX_ALT4 (4UL << PORT_PCR_MUX_SHIFT) /* Alternate function 4 */ +#define PORT_PCR_MUX_ALT5 (5UL << PORT_PCR_MUX_SHIFT) /* Alternate function 5 */ +#define PORT_PCR_MUX_ALT6 (6UL << PORT_PCR_MUX_SHIFT) /* Alternate function 6 */ +#define PORT_PCR_MUX_ALT7 (7UL << PORT_PCR_MUX_SHIFT) /* Alternate function 7 */ + +/* S32K142EVB LED pins (accent RGB LED accent accent accent accent LED accent accent accent-low accent LED) */ +#define LED_PIN_BLUE 0 /* PTD0 - Blue LED (active low) */ +#define LED_PIN_RED 15 /* PTD15 - Red LED (active low) */ +#define LED_PIN_GREEN 16 /* PTD16 - Green LED (active low) */ + +/* ============== LPUART Registers ============== */ + +/* LPUART base addresses */ +#define LPUART0_BASE (0x4006A000UL) +#define LPUART1_BASE (0x4006B000UL) +#define LPUART2_BASE (0x4006C000UL) + +/* LPUART register offsets */ +#define LPUART_VERID_OFF 0x000UL +#define LPUART_PARAM_OFF 0x004UL +#define LPUART_GLOBAL_OFF 0x008UL +#define LPUART_BAUD_OFF 0x010UL +#define LPUART_STAT_OFF 0x014UL +#define LPUART_CTRL_OFF 0x018UL +#define LPUART_DATA_OFF 0x01CUL + +/* LPUART0 registers */ +#define LPUART0_VERID (*(volatile uint32_t *)(LPUART0_BASE + LPUART_VERID_OFF)) +#define LPUART0_PARAM (*(volatile uint32_t *)(LPUART0_BASE + LPUART_PARAM_OFF)) +#define LPUART0_GLOBAL (*(volatile uint32_t *)(LPUART0_BASE + LPUART_GLOBAL_OFF)) +#define LPUART0_BAUD (*(volatile uint32_t *)(LPUART0_BASE + LPUART_BAUD_OFF)) +#define LPUART0_STAT (*(volatile uint32_t *)(LPUART0_BASE + LPUART_STAT_OFF)) +#define LPUART0_CTRL (*(volatile uint32_t *)(LPUART0_BASE + LPUART_CTRL_OFF)) +#define LPUART0_DATA (*(volatile uint32_t *)(LPUART0_BASE + LPUART_DATA_OFF)) + +/* LPUART1 registers */ +#define LPUART1_VERID (*(volatile uint32_t *)(LPUART1_BASE + LPUART_VERID_OFF)) +#define LPUART1_PARAM (*(volatile uint32_t *)(LPUART1_BASE + LPUART_PARAM_OFF)) +#define LPUART1_GLOBAL (*(volatile uint32_t *)(LPUART1_BASE + LPUART_GLOBAL_OFF)) +#define LPUART1_BAUD (*(volatile uint32_t *)(LPUART1_BASE + LPUART_BAUD_OFF)) +#define LPUART1_STAT (*(volatile uint32_t *)(LPUART1_BASE + LPUART_STAT_OFF)) +#define LPUART1_CTRL (*(volatile uint32_t *)(LPUART1_BASE + LPUART_CTRL_OFF)) +#define LPUART1_DATA (*(volatile uint32_t *)(LPUART1_BASE + LPUART_DATA_OFF)) + +/* LPUART2 registers */ +#define LPUART2_VERID (*(volatile uint32_t *)(LPUART2_BASE + LPUART_VERID_OFF)) +#define LPUART2_PARAM (*(volatile uint32_t *)(LPUART2_BASE + LPUART_PARAM_OFF)) +#define LPUART2_GLOBAL (*(volatile uint32_t *)(LPUART2_BASE + LPUART_GLOBAL_OFF)) +#define LPUART2_BAUD (*(volatile uint32_t *)(LPUART2_BASE + LPUART_BAUD_OFF)) +#define LPUART2_STAT (*(volatile uint32_t *)(LPUART2_BASE + LPUART_STAT_OFF)) +#define LPUART2_CTRL (*(volatile uint32_t *)(LPUART2_BASE + LPUART_CTRL_OFF)) +#define LPUART2_DATA (*(volatile uint32_t *)(LPUART2_BASE + LPUART_DATA_OFF)) + +/* LPUART register field definitions */ +#define LPUART_BAUD_OSR_SHIFT 24 +#define LPUART_BAUD_SBR_SHIFT 0 +#define LPUART_CTRL_TE (1UL << 19) /* Transmitter Enable */ +#define LPUART_CTRL_RE (1UL << 18) /* Receiver Enable */ +#define LPUART_CTRL_RIE (1UL << 21) /* Receiver Interrupt Enable */ +#define LPUART_STAT_TDRE (1UL << 23) /* Transmit Data Register Empty */ +#define LPUART_STAT_TC (1UL << 22) /* Transmission Complete */ +#define LPUART_STAT_RDRF (1UL << 21) /* Receive Data Register Full */ +#define LPUART_STAT_OR (1UL << 19) /* Overrun Flag */ +#define LPUART_STAT_NF (1UL << 18) /* Noise Flag */ +#define LPUART_STAT_FE (1UL << 17) /* Framing Error */ +#define LPUART_STAT_PF (1UL << 16) /* Parity Error */ + +/* ============== LPUART Build-Time Configuration ============== */ +/* + * Select LPUART instance and pins at build time using these defines: + * + * DEBUG_UART_NUM: LPUART instance (0, 1, or 2). Default: 1 + * + * DEBUG_UART_TX_PORT: Port for TX pin (S32K_PORT_A/B/C/D/E). Default depends on LPUART + * DEBUG_UART_TX_PIN: Pin number for TX. Default depends on LPUART + * DEBUG_UART_TX_MUX: Pin mux function. Default: PORT_PCR_MUX_ALT2 + * + * DEBUG_UART_RX_PORT: Port for RX pin (S32K_PORT_A/B/C/D/E). Default depends on LPUART + * DEBUG_UART_RX_PIN: Pin number for RX. Default depends on LPUART + * DEBUG_UART_RX_MUX: Pin mux function. Default: PORT_PCR_MUX_ALT2 + * + * Example pin mappings for S32K1xx: + * + * LPUART0: + * - PTB0 (RX, ALT2), PTB1 (TX, ALT2) - Default + * - PTA2 (RX, ALT6), PTA3 (TX, ALT6) + * + * LPUART1: + * - PTC6 (RX, ALT2), PTC7 (TX, ALT2) - Default (S32K142EVB OpenSDA) + * - PTC8 (RX, ALT2), PTC9 (TX, ALT2) + * + * LPUART2: + * - PTA8 (RX, ALT6), PTA9 (TX, ALT6) + * - PTD6 (RX, ALT2), PTD7 (TX, ALT2) - Default + * + * Usage in .config file: + * CFLAGS_EXTRA+=-DDEBUG_UART_NUM=0 + * CFLAGS_EXTRA+=-DDEBUG_UART_TX_PORT=S32K_PORT_B -DDEBUG_UART_TX_PIN=1 + * CFLAGS_EXTRA+=-DDEBUG_UART_RX_PORT=S32K_PORT_B -DDEBUG_UART_RX_PIN=0 + */ + +/* Default LPUART instance */ +#ifndef DEBUG_UART_NUM +#define DEBUG_UART_NUM 1 +#endif + +/* Map to selected LPUART registers based on DEBUG_UART_NUM */ +#if DEBUG_UART_NUM == 0 + #define LPUART_BAUD LPUART0_BAUD + #define LPUART_STAT LPUART0_STAT + #define LPUART_CTRL LPUART0_CTRL + #define LPUART_DATA LPUART0_DATA + #define PCC_LPUART PCC_LPUART0 +#elif DEBUG_UART_NUM == 2 + #define LPUART_BAUD LPUART2_BAUD + #define LPUART_STAT LPUART2_STAT + #define LPUART_CTRL LPUART2_CTRL + #define LPUART_DATA LPUART2_DATA + #define PCC_LPUART PCC_LPUART2 +#else /* DEBUG_UART_NUM == 1 (default) */ + #define LPUART_BAUD LPUART1_BAUD + #define LPUART_STAT LPUART1_STAT + #define LPUART_CTRL LPUART1_CTRL + #define LPUART_DATA LPUART1_DATA + #define PCC_LPUART PCC_LPUART1 +#endif + +/* Port identifier values for preprocessor comparisons */ +#define S32K_PORT_A 0 +#define S32K_PORT_B 1 +#define S32K_PORT_C 2 +#define S32K_PORT_D 3 +#define S32K_PORT_E 4 + +/* Default pin configuration based on selected LPUART */ +#if DEBUG_UART_NUM == 0 + /* LPUART0 defaults: PTB0 (RX), PTB1 (TX) */ + #ifndef DEBUG_UART_TX_PORT + #define DEBUG_UART_TX_PORT S32K_PORT_B + #endif + #ifndef DEBUG_UART_TX_PIN + #define DEBUG_UART_TX_PIN 1 + #endif + #ifndef DEBUG_UART_RX_PORT + #define DEBUG_UART_RX_PORT S32K_PORT_B + #endif + #ifndef DEBUG_UART_RX_PIN + #define DEBUG_UART_RX_PIN 0 + #endif +#elif DEBUG_UART_NUM == 2 + /* LPUART2 defaults: PTD6 (RX), PTD7 (TX) */ + #ifndef DEBUG_UART_TX_PORT + #define DEBUG_UART_TX_PORT S32K_PORT_D + #endif + #ifndef DEBUG_UART_TX_PIN + #define DEBUG_UART_TX_PIN 7 + #endif + #ifndef DEBUG_UART_RX_PORT + #define DEBUG_UART_RX_PORT S32K_PORT_D + #endif + #ifndef DEBUG_UART_RX_PIN + #define DEBUG_UART_RX_PIN 6 + #endif +#else /* DEBUG_UART_NUM == 1 (default) */ + /* LPUART1 defaults: PTC6 (RX), PTC7 (TX) - S32K142EVB OpenSDA */ + #ifndef DEBUG_UART_TX_PORT + #define DEBUG_UART_TX_PORT S32K_PORT_C + #endif + #ifndef DEBUG_UART_TX_PIN + #define DEBUG_UART_TX_PIN 7 + #endif + #ifndef DEBUG_UART_RX_PORT + #define DEBUG_UART_RX_PORT S32K_PORT_C + #endif + #ifndef DEBUG_UART_RX_PIN + #define DEBUG_UART_RX_PIN 6 + #endif +#endif + +/* Default pin mux - ALT2 for most LPUART pins */ +#ifndef DEBUG_UART_TX_MUX +#define DEBUG_UART_TX_MUX PORT_PCR_MUX_ALT2 +#endif +#ifndef DEBUG_UART_RX_MUX +#define DEBUG_UART_RX_MUX PORT_PCR_MUX_ALT2 +#endif + +/* Map TX port/pin to PCR register and PCC */ +#if DEBUG_UART_TX_PORT == S32K_PORT_A + #define DEBUG_UART_TX_PCC_PORT PCC_PORTA + #define DEBUG_UART_TX_PCR PORTA_PCR(DEBUG_UART_TX_PIN) +#elif DEBUG_UART_TX_PORT == S32K_PORT_B + #define DEBUG_UART_TX_PCC_PORT PCC_PORTB + #define DEBUG_UART_TX_PCR PORTB_PCR(DEBUG_UART_TX_PIN) +#elif DEBUG_UART_TX_PORT == S32K_PORT_D + #define DEBUG_UART_TX_PCC_PORT PCC_PORTD + #define DEBUG_UART_TX_PCR PORTD_PCR(DEBUG_UART_TX_PIN) +#elif DEBUG_UART_TX_PORT == S32K_PORT_E + #define DEBUG_UART_TX_PCC_PORT PCC_PORTE + #define DEBUG_UART_TX_PCR PORTE_PCR(DEBUG_UART_TX_PIN) +#else /* S32K_PORT_C (default) */ + #define DEBUG_UART_TX_PCC_PORT PCC_PORTC + #define DEBUG_UART_TX_PCR PORTC_PCR(DEBUG_UART_TX_PIN) +#endif + +/* Map RX port/pin to PCR register and PCC */ +#if DEBUG_UART_RX_PORT == S32K_PORT_A + #define DEBUG_UART_RX_PCC_PORT PCC_PORTA + #define DEBUG_UART_RX_PCR PORTA_PCR(DEBUG_UART_RX_PIN) +#elif DEBUG_UART_RX_PORT == S32K_PORT_B + #define DEBUG_UART_RX_PCC_PORT PCC_PORTB + #define DEBUG_UART_RX_PCR PORTB_PCR(DEBUG_UART_RX_PIN) +#elif DEBUG_UART_RX_PORT == S32K_PORT_D + #define DEBUG_UART_RX_PCC_PORT PCC_PORTD + #define DEBUG_UART_RX_PCR PORTD_PCR(DEBUG_UART_RX_PIN) +#elif DEBUG_UART_RX_PORT == S32K_PORT_E + #define DEBUG_UART_RX_PCC_PORT PCC_PORTE + #define DEBUG_UART_RX_PCR PORTE_PCR(DEBUG_UART_RX_PIN) +#else /* S32K_PORT_C (default) */ + #define DEBUG_UART_RX_PCC_PORT PCC_PORTC + #define DEBUG_UART_RX_PCR PORTC_PCR(DEBUG_UART_RX_PIN) +#endif + +/* Check if TX and RX use the same port (for clock enable optimization) */ +#if DEBUG_UART_TX_PORT == DEBUG_UART_RX_PORT + #define DEBUG_UART_SAME_PORT 1 +#else + #define DEBUG_UART_SAME_PORT 0 +#endif + +/* ============== Flash (FTFC) Registers ============== */ + +#define FTFC_BASE (0x40020000UL) +#define FTFC_FSTAT (*(volatile uint8_t *)(FTFC_BASE + 0x000UL)) +#define FTFC_FCNFG (*(volatile uint8_t *)(FTFC_BASE + 0x001UL)) +#define FTFC_FSEC (*(volatile uint8_t *)(FTFC_BASE + 0x002UL)) +#define FTFC_FOPT (*(volatile uint8_t *)(FTFC_BASE + 0x003UL)) +#define FTFC_FCCOB3 (*(volatile uint8_t *)(FTFC_BASE + 0x004UL)) +#define FTFC_FCCOB2 (*(volatile uint8_t *)(FTFC_BASE + 0x005UL)) +#define FTFC_FCCOB1 (*(volatile uint8_t *)(FTFC_BASE + 0x006UL)) +#define FTFC_FCCOB0 (*(volatile uint8_t *)(FTFC_BASE + 0x007UL)) +#define FTFC_FCCOB7 (*(volatile uint8_t *)(FTFC_BASE + 0x008UL)) +#define FTFC_FCCOB6 (*(volatile uint8_t *)(FTFC_BASE + 0x009UL)) +#define FTFC_FCCOB5 (*(volatile uint8_t *)(FTFC_BASE + 0x00AUL)) +#define FTFC_FCCOB4 (*(volatile uint8_t *)(FTFC_BASE + 0x00BUL)) +#define FTFC_FCCOBB (*(volatile uint8_t *)(FTFC_BASE + 0x00CUL)) +#define FTFC_FCCOBA (*(volatile uint8_t *)(FTFC_BASE + 0x00DUL)) +#define FTFC_FCCOB9 (*(volatile uint8_t *)(FTFC_BASE + 0x00EUL)) +#define FTFC_FCCOB8 (*(volatile uint8_t *)(FTFC_BASE + 0x00FUL)) + +/* FTFC Commands */ +#define FTFC_CMD_PROGRAM_PHRASE 0x07 /* Program 8 bytes (phrase) */ +#define FTFC_CMD_ERASE_SECTOR 0x09 /* Erase flash sector (2KB) */ +#define FTFC_CMD_READ_RESOURCE 0x03 /* Read resource */ + +/* FTFC_FSTAT bits */ +#define FTFC_FSTAT_CCIF (1U << 7) /* Command Complete */ +#define FTFC_FSTAT_RDCOLERR (1U << 6) /* Read Collision Error */ +#define FTFC_FSTAT_ACCERR (1U << 5) /* Access Error */ +#define FTFC_FSTAT_FPVIOL (1U << 4) /* Protection Violation */ +#define FTFC_FSTAT_MGSTAT0 (1U << 0) /* Command Failure */ + +/* Flash programming unit: 8 bytes (double-word / phrase) */ +#define FLASH_PHRASE_SIZE 8 + +/* ============== S32K1xx Variant Flash Sizes ============== */ +/* Define the appropriate variant or use automatic detection based on WOLFBOOT config + * + * S32K142: 256KB Flash (0x00000 - 0x3FFFF), 32KB SRAM, 2KB sectors + * S32K144: 512KB Flash (0x00000 - 0x7FFFF), 64KB SRAM, 4KB sectors + * S32K146: 1MB Flash (0x00000 - 0xFFFFF), 128KB SRAM, 4KB sectors + * S32K148: 2MB Flash (0x00000 - 0x1FFFFF), 256KB SRAM, 4KB sectors + * + * IMPORTANT: Flash sector size depends on total flash size: + * - 256KB Flash (S32K142): 2KB sectors + * - 512KB+ Flash (S32K144/146/148): 4KB sectors + * + * All variants use 8-byte phrase programming. + */ +#if defined(S32K148) + #define FLASH_SIZE (2048 * 1024) /* 2MB */ + #define SRAM_SIZE (256 * 1024) /* 256KB */ + #define FLASH_SECTOR_SIZE 4096 /* 4KB sectors */ +#elif defined(S32K146) + #define FLASH_SIZE (1024 * 1024) /* 1MB */ + #define SRAM_SIZE (128 * 1024) /* 128KB */ + #define FLASH_SECTOR_SIZE 4096 /* 4KB sectors */ +#elif defined(S32K144) + #define FLASH_SIZE (512 * 1024) /* 512KB */ + #define SRAM_SIZE (64 * 1024) /* 64KB */ + #define FLASH_SECTOR_SIZE 4096 /* 4KB sectors */ +#else /* S32K142 (default) */ + #define FLASH_SIZE (256 * 1024) /* 256KB */ + #define SRAM_SIZE (32 * 1024) /* 32KB */ + #define FLASH_SECTOR_SIZE 2048 /* 2KB sectors */ +#endif + +/* Flash base address */ +#define FLASH_BASE_ADDR 0x00000000 + +/* Flash Configuration Field (FCF) region - MUST NOT be modified at runtime! + * Writing incorrect values here can permanently lock the device. + * Address range: 0x400 - 0x40F (16 bytes) + */ +#define FCF_START_ADDR 0x400 +#define FCF_END_ADDR 0x410 + +/* SRAM base address (all S32K1xx variants) */ +#define SRAM_BASE_ADDR 0x1FFF8000 /* Lower SRAM */ +#define SRAM_UPPER_ADDR 0x20000000 /* Upper SRAM */ + +/* ============== Watchdog (WDOG) Registers ============== */ +/* S32K1xx has a software-controlled watchdog timer */ + +#define WDOG_BASE (0x40052000UL) +#define WDOG_CS (*(volatile uint32_t *)(WDOG_BASE + 0x00UL)) /* Control and Status */ +#define WDOG_CNT (*(volatile uint32_t *)(WDOG_BASE + 0x04UL)) /* Counter */ +#define WDOG_TOVAL (*(volatile uint32_t *)(WDOG_BASE + 0x08UL)) /* Timeout Value */ +#define WDOG_WIN (*(volatile uint32_t *)(WDOG_BASE + 0x0CUL)) /* Window */ + +/* WDOG CS Register Bits */ +#define WDOG_CS_STOP (1UL << 0) /* Stop enable */ +#define WDOG_CS_WAIT (1UL << 1) /* Wait enable */ +#define WDOG_CS_DBG (1UL << 2) /* Debug enable */ +#define WDOG_CS_TST_SHIFT 3 +#define WDOG_CS_TST_MASK (3UL << WDOG_CS_TST_SHIFT) /* Test mode */ +#define WDOG_CS_UPDATE (1UL << 5) /* Allow updates */ +#define WDOG_CS_INT (1UL << 6) /* Interrupt enable */ +#define WDOG_CS_EN (1UL << 7) /* Watchdog enable */ +#define WDOG_CS_CLK_SHIFT 8 +#define WDOG_CS_CLK_MASK (3UL << WDOG_CS_CLK_SHIFT) /* Clock source */ +#define WDOG_CS_CLK_BUS (0UL << WDOG_CS_CLK_SHIFT) /* Bus clock */ +#define WDOG_CS_CLK_LPO (1UL << WDOG_CS_CLK_SHIFT) /* LPO clock (128kHz) */ +#define WDOG_CS_CLK_SOSC (2UL << WDOG_CS_CLK_SHIFT) /* SOSC clock */ +#define WDOG_CS_CLK_SIRC (3UL << WDOG_CS_CLK_SHIFT) /* SIRC clock */ +#define WDOG_CS_RCS (1UL << 10) /* Reconfiguration success */ +#define WDOG_CS_ULK (1UL << 11) /* Unlock status */ +#define WDOG_CS_PRES (1UL << 12) /* Prescaler (256 divider) */ +#define WDOG_CS_CMD32EN (1UL << 13) /* 32-bit command support */ +#define WDOG_CS_FLG (1UL << 14) /* Interrupt flag */ +#define WDOG_CS_WIN (1UL << 15) /* Window mode enable */ + +/* WDOG Unlock Key - write to CNT register to unlock */ +#define WDOG_CNT_UNLOCK (0xD928C520UL) + +/* WDOG Refresh Keys - write in sequence to CNT register to refresh */ +#define WDOG_CNT_REFRESH_HI (0xB480UL) +#define WDOG_CNT_REFRESH_LO (0xA602UL) +#define WDOG_CNT_REFRESH (0xB480A602UL) /* For CMD32EN mode */ + +/* Default WDOG timeout value (max = 0xFFFF) */ +#define WDOG_TOVAL_DEFAULT (0xFFFFUL) + +/* Watchdog disable configuration: + * - EN=0 (disabled), UPDATE=1, CMD32EN=1, CLK=LPO + */ +#define WDOG_CS_DISABLE_CFG (WDOG_CS_UPDATE | WDOG_CS_CMD32EN | WDOG_CS_CLK_LPO) + +/* Watchdog enable configuration: + * - EN=1, UPDATE=1, CMD32EN=1, CLK=LPO + * LPO is 128kHz, with PRES=0 (no 256 divider) + * Timeout = TOVAL / 128kHz = TOVAL * 7.8125us + * For ~1 second timeout: TOVAL = 128000 + */ +#define WDOG_CS_ENABLE_CFG (WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_CMD32EN | \ + WDOG_CS_CLK_LPO) + + +/* Default 1 second timeout */ +#ifndef WATCHDOG_TIMEOUT_MS +#define WATCHDOG_TIMEOUT_MS 1000 +#endif + +/* ============== UART Function Declarations ============== */ +/* These functions are implemented in hal/s32k1xx.c when DEBUG_UART is defined. + * They use the LPUART instance and pins configured by the DEBUG_UART_* macros. + */ + +#ifdef DEBUG_UART +/* Initialize the UART with configured LPUART and pins */ +void uart_init(void); + +/* Write data to UART (blocking, with automatic LF -> CRLF conversion) */ +void uart_write(const char* buf, unsigned int sz); + +/* Read a single character from UART (non-blocking) + * Returns: 1 if character read, 0 if no data available, -1 on error + */ +int uart_read(char* c); + +#endif /* DEBUG_UART */ + +#endif /* S32K1XX_H */ + diff --git a/hal/s32k1xx.ld b/hal/s32k1xx.ld new file mode 100644 index 0000000000..e28275919b --- /dev/null +++ b/hal/s32k1xx.ld @@ -0,0 +1,86 @@ +/* + * Linker script for wolfBoot on NXP S32K1xx + * + * S32K142: 256KB Flash (0x00000000 - 0x0003FFFF), 32KB SRAM (split) + * + * SRAM Layout (S32K142 - 32KB total, split across two regions): + * SRAM_L: 0x1FFFC000 - 0x1FFFFFFF (16 KB) - Lower SRAM + * SRAM_U: 0x20000000 - 0x20002FFF (12 KB) - Upper SRAM + * + * Flash Memory Layout: + * 0x00000000 - 0x0000BFFF: Bootloader (48 KB) + * 0x0000C000 - 0x00024FFF: Boot Partition (100 KB) + * 0x00025000 - 0x0003DFFF: Update Partition (100 KB) + * 0x0003E000 - 0x0003E7FF: Swap Sector (2 KB) + * + * Note: Using SRAM_L for data/bss and SRAM_U for stack + * Stack grows down from 0x20003000 + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = @BOOTLOADER_PARTITION_SIZE@ + SRAM_L (rwx) : ORIGIN = 0x1FFFC000, LENGTH = 0x4000 /* 16KB lower SRAM */ + SRAM_U (rwx) : ORIGIN = 0x20000000, LENGTH = 0x3000 /* 12KB upper SRAM */ +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + + /* Flash Configuration Field (FCF) at 0x400 */ + . = 0x400; + KEEP(*(.flash_config)) + . = ALIGN(4); + + *(.text*) + *(.rodata*) + *(.init*) + *(.fini*) + . = ALIGN(4); + _end_text = .; + } > FLASH + + .edidx : + { + . = ALIGN(4); + *(.ARM.exidx*) + } > FLASH + + _stored_data = .; + + .data : AT (_stored_data) + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > SRAM_L + + .bss (NOLOAD) : + { + _start_bss = .; + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + __bss_end__ = .; + _end = .; + } > SRAM_L + . = ALIGN(4); +} + +/* Partition addresses for wolfBoot */ +_wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@; +_wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@; +_wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@; +_wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@; + +/* Stack at end of SRAM_U (grows down from 0x20003000) */ +END_STACK = ORIGIN(SRAM_U) + LENGTH(SRAM_U); diff --git a/src/boot_arm.c b/src/boot_arm.c index 43a65babae..50b72cae6e 100644 --- a/src/boot_arm.c +++ b/src/boot_arm.c @@ -472,6 +472,9 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset) asm volatile("cpsid i"); /* Update IV */ VTOR = ((uint32_t)app_offset); + /* Memory barriers to ensure VTOR is updated before continuing */ + asm volatile("dsb"); + asm volatile("isb"); # endif /* Get stack pointer, entry point */ app_end_stack = (*((uint32_t *)(app_offset))); @@ -479,6 +482,8 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset) /* Update stack pointer */ asm volatile("msr msp, %0" ::"r"(app_end_stack)); + /* Barrier to ensure MSP update is complete */ + asm volatile("isb"); # ifndef NO_VTOR asm volatile("cpsie i"); # endif diff --git a/src/update_flash.c b/src/update_flash.c index 6f2c733174..9f6353130c 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -1278,6 +1278,7 @@ void RAMFUNCTION wolfBoot_start(void) #elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) (void)hal_hsm_server_cleanup(); #endif + hal_prepare_boot(); do_boot((void *)boot.fw_base); diff --git a/test-app/ARM-s32k1xx.ld b/test-app/ARM-s32k1xx.ld new file mode 100644 index 0000000000..4ee8d4014c --- /dev/null +++ b/test-app/ARM-s32k1xx.ld @@ -0,0 +1,76 @@ +/* + * Linker script for wolfBoot test application on NXP S32K1xx + * + * Application runs from boot partition after wolfBoot header + * + * S32K142 SRAM Layout (32KB total, split across two regions): + * SRAM_L: 0x1FFFC000 - 0x1FFFFFFF (16 KB) - Lower SRAM + * SRAM_U: 0x20000000 - 0x20002FFF (12 KB) - Upper SRAM + * + * Using upper SRAM for application data/bss/stack + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x3000 /* 12KB upper SRAM */ +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + *(.text*) + *(.rodata*) + *(.init*) + *(.fini*) + . = ALIGN(4); + _end_text = .; + } > FLASH + + .edidx : + { + . = ALIGN(4); + *(.ARM.exidx*) + } > FLASH + + _stored_data = .; + + .data : AT (_stored_data) + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > RAM + + .bss (NOLOAD) : + { + _start_bss = .; + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + __bss_end__ = .; + _end = .; + } > RAM + . = ALIGN(4); +} + + + +/* Partition info for libwolfboot */ +_wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@; +_wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@; +_wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@; +_wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@; + +/* Stack and heap indicators */ +_start_heap = _end; +_end_stack = ORIGIN(RAM) + LENGTH(RAM); + diff --git a/test-app/Makefile b/test-app/Makefile index 135dbfd5ac..d2e39d218b 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -73,6 +73,7 @@ LDFLAGS+=-T $(LSCRIPT) -Wl,-gc-sections -Wl,-Map=image.map OBJCOPY_FLAGS+=--gap-fill $(FILL_BYTE) ifeq ($(DEBUG_UART),1) + CFLAGS+=-DDEBUG_UART APP_OBJS+=../src/string.o endif @@ -348,6 +349,13 @@ ifeq ($(TARGET),mcxa) LDFLAGS+=--specs=nosys.specs endif +ifeq ($(TARGET),s32k1xx) + LSCRIPT_TEMPLATE=ARM-s32k1xx.ld + APP_OBJS+=../src/keystore.o + CFLAGS+=-DAPP_HAS_SYSTICK + CFLAGS+=-DRAM_CODE -DDEBUG_UART +endif + ifeq ($(TARGET),mcxw) ifeq ($(TZEN),1) LSCRIPT_TEMPLATE=ARM-mcxw-ns.ld diff --git a/test-app/app_s32k1xx.c b/test-app/app_s32k1xx.c new file mode 100644 index 0000000000..815747051f --- /dev/null +++ b/test-app/app_s32k1xx.c @@ -0,0 +1,1008 @@ +/* app_s32k1xx.c + * + * Test bare-metal application for NXP S32K1xx + * Features: + * - LED indicator based on firmware version (Green=v1, Blue=v>1) + * - Interactive console with commands + * - XMODEM firmware update support + * - Partition and keystore information display + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include +#include +#include "hal.h" +#include "../hal/s32k1xx.h" +#include "wolfboot/wolfboot.h" +#include "keystore.h" +#include "target.h" +#include "image.h" + +#ifdef TARGET_s32k1xx + +/* RAMFUNCTION for test-app: code that runs during flash operations must be in RAM */ +#ifdef RAM_CODE + #define APP_RAMFUNCTION __attribute__((used,section(".ramcode"),long_call)) +#else + #define APP_RAMFUNCTION +#endif + +/* ============== SysTick Timer ============== */ + +static volatile uint32_t jiffies = 0; + +/* SysTick interrupt handler - called isr_systick to match startup_arm.c */ +void isr_systick(void) +{ + jiffies++; +} + +static uint32_t get_time_ms(void) +{ + return jiffies; +} + +static void delay_ms(uint32_t ms) +{ + uint32_t start = jiffies; + while ((jiffies - start) < ms) { + __asm__ volatile ("wfi"); + } +} + +static void systick_init(void) +{ + /* Configure SysTick for 1ms tick */ + SYST_RVR = (CLOCK_SPEED / 1000) - 1; + SYST_CVR = 0; + SYST_CSR = SYST_CSR_ENABLE | SYST_CSR_TICKINT | SYST_CSR_CLKSOURCE; +} + +/* ============== LED Functions ============== */ + +static void led_init(void) +{ + /* Enable clock to PORTD */ + PCC_PORTD |= PCC_CGC; + + /* Configure LED pins as GPIO */ + PORTD_PCR0 = PORT_PCR_MUX_GPIO; /* Blue LED */ + PORTD_PCR15 = PORT_PCR_MUX_GPIO; /* Red LED */ + PORTD_PCR16 = PORT_PCR_MUX_GPIO; /* Green LED */ + + /* Set as outputs */ + GPIOD_PDDR |= (1UL << LED_PIN_BLUE) | (1UL << LED_PIN_RED) | (1UL << LED_PIN_GREEN); + + /* All LEDs off initially (active low) */ + GPIOD_PSOR = (1UL << LED_PIN_BLUE) | (1UL << LED_PIN_RED) | (1UL << LED_PIN_GREEN); +} + +static void led_green_on(void) +{ + GPIOD_PCOR = (1UL << LED_PIN_GREEN); /* Active low */ +} + +static void led_green_off(void) +{ + GPIOD_PSOR = (1UL << LED_PIN_GREEN); +} + +static void led_blue_on(void) +{ + GPIOD_PCOR = (1UL << LED_PIN_BLUE); /* Active low */ +} + +static void led_blue_off(void) +{ + GPIOD_PSOR = (1UL << LED_PIN_BLUE); +} + +static void APP_RAMFUNCTION led_red_on(void) +{ + GPIOD_PCOR = (1UL << LED_PIN_RED); /* Active low */ +} + +static void APP_RAMFUNCTION led_red_off(void) +{ + GPIOD_PSOR = (1UL << LED_PIN_RED); +} + +static void led_toggle_version(uint32_t version) +{ + if (version == 1) { + GPIOD_PTOR = (1UL << LED_PIN_GREEN); + } else { + GPIOD_PTOR = (1UL << LED_PIN_BLUE); + } +} + +/* Set LED based on version: Green for v1, Blue for v>1 */ +static void led_set_version(uint32_t version) +{ + /* Turn off both first */ + led_green_off(); + led_blue_off(); + + if (version == 1) { + led_green_on(); + } else if (version > 1) { + led_blue_on(); + } +} + +/* ============== System Control ============== */ + +void arch_reboot(void) +{ + SCB_AIRCR = AIRCR_VECTKEY | AIRCR_SYSRESETREQ; + while (1) { + __asm__ volatile ("wfi"); + } +} + +/* ============== UART / Printf Support ============== */ + +#ifdef DEBUG_UART +/* UART functions are declared in s32k1xx.h and implemented in hal/s32k1xx.c */ + +/* ============== UART RX Interrupt Buffering ============== */ +#define UART_RX_BUF_SIZE 512 +static volatile uint8_t uart_rx_buf[UART_RX_BUF_SIZE]; +static volatile uint32_t uart_rx_head = 0; /* Write index (ISR writes here) */ +static volatile uint32_t uart_rx_tail = 0; /* Read index (app reads from here) */ + +/* LPUART1 RX Interrupt Handler */ +void isr_lpuart1(void) +{ + uint32_t stat = LPUART1_STAT; + + /* Clear any error flags */ + if (stat & (LPUART_STAT_OR | LPUART_STAT_NF | LPUART_STAT_FE | LPUART_STAT_PF)) { + LPUART1_STAT = stat; /* Write 1 to clear flags */ + } + + /* Read all available bytes from FIFO */ + while (LPUART1_STAT & LPUART_STAT_RDRF) { + uint8_t c = (uint8_t)(LPUART1_DATA & 0xFF); + uint32_t next_head = (uart_rx_head + 1) % UART_RX_BUF_SIZE; + + /* Store byte if buffer not full */ + if (next_head != uart_rx_tail) { + uart_rx_buf[uart_rx_head] = c; + uart_rx_head = next_head; + } + /* else: buffer full, discard byte */ + } +} + +/* Read from RX buffer (for XMODEM) - returns number of bytes read + * Must be RAMFUNCTION since it's called during flash operations */ +static int APP_RAMFUNCTION uart_rx_isr(uint8_t *buf, int max_len) +{ + int count = 0; + + while (count < max_len && uart_rx_tail != uart_rx_head) { + buf[count++] = uart_rx_buf[uart_rx_tail]; + uart_rx_tail = (uart_rx_tail + 1) % UART_RX_BUF_SIZE; + } + + return count; +} + +/* Check if RX data available */ +static int uart_rx_available(void) +{ + return (uart_rx_head != uart_rx_tail) ? 1 : 0; +} + +/* Read single character from RX buffer (for console) - returns 1 if char read, 0 if none */ +static int uart_getc(char *c) +{ + if (uart_rx_tail != uart_rx_head) { + *c = uart_rx_buf[uart_rx_tail]; + uart_rx_tail = (uart_rx_tail + 1) % UART_RX_BUF_SIZE; + return 1; + } + return 0; +} + +/* Enable LPUART RX interrupt */ +static void uart_rx_irq_enable(void) +{ + /* Set interrupt priority lower than SysTick (higher number = lower priority) + * SysTick defaults to priority 0, so set LPUART to 2 to ensure + * jiffies keeps incrementing even during heavy UART traffic. */ + NVIC_SetPriority(LPUART1_IRQn, 2); + + /* Enable LPUART1 interrupt in NVIC */ + NVIC_EnableIRQ(LPUART1_IRQn); + + /* Enable Receiver Interrupt in LPUART */ + LPUART1_CTRL |= LPUART_CTRL_RIE; +} + +/* Print hex buffer (similar to stm32h5 style) */ +#define LINE_LEN 16 +static void print_hex(const uint8_t* buffer, uint32_t length, int dumpChars) +{ + uint32_t i, sz; + + if (!buffer) { + printf("\tNULL\r\n"); + return; + } + + while (length > 0) { + sz = length; + if (sz > LINE_LEN) + sz = LINE_LEN; + + printf("\t"); + for (i = 0; i < LINE_LEN; i++) { + if (i < length) + printf("%02x ", buffer[i]); + else + printf(" "); + } + if (dumpChars) { + printf("| "); + for (i = 0; i < sz; i++) { + if (buffer[i] > 31 && buffer[i] < 127) + printf("%c", buffer[i]); + else + printf("."); + } + } + printf("\r\n"); + + buffer += sz; + length -= sz; + } +} +#endif /* DEBUG_UART */ + +/* ============== Partition State Names ============== */ + +static const char* part_state_name(uint8_t state, int state_retval) +{ + if (state_retval == 0) { + return "(no trailer)"; + } + switch (state) { + case IMG_STATE_NEW: return "NEW"; + case IMG_STATE_UPDATING: return "UPDATING"; + case IMG_STATE_TESTING: return "TESTING"; + case IMG_STATE_SUCCESS: return "SUCCESS"; + default: return "UNKNOWN"; + } +} + +/* ============== Key Type Names ============== */ + +static const char* key_type_name(uint32_t type) +{ + switch (type) { + case AUTH_KEY_ECC256: return "ECDSA P-256 (secp256r1)"; + case AUTH_KEY_ECC384: return "ECDSA P-384 (secp384r1)"; + case AUTH_KEY_ECC521: return "ECDSA P-521 (secp521r1)"; + case AUTH_KEY_RSA2048: return "RSA-2048"; + case AUTH_KEY_RSA3072: return "RSA-3072"; + case AUTH_KEY_RSA4096: return "RSA-4096"; + case AUTH_KEY_ED25519: return "Ed25519"; + case AUTH_KEY_ED448: return "Ed448"; + case AUTH_KEY_LMS: return "LMS"; + case AUTH_KEY_XMSS: return "XMSS"; + case AUTH_KEY_ML_DSA: return "ML-DSA"; + default: return "Unknown"; + } +} + +static const char* hash_type_name(void) +{ +#ifdef WOLFBOOT_HASH_SHA256 + return "SHA-256"; +#elif defined(WOLFBOOT_HASH_SHA384) + return "SHA-384"; +#elif defined(WOLFBOOT_HASH_SHA512) + return "SHA-512"; +#elif defined(WOLFBOOT_HASH_SHA3_384) + return "SHA3-384"; +#endif + return "Unknown"; +} + +/* ============== Information Display ============== */ + +#ifdef DEBUG_UART +static void print_partition_info(void) +{ + uint32_t boot_ver, update_ver; + uint8_t boot_state = 0, update_state = 0; + int boot_state_valid, update_state_valid; + + boot_ver = wolfBoot_current_firmware_version(); + update_ver = wolfBoot_update_firmware_version(); + + boot_state_valid = wolfBoot_get_partition_state(PART_BOOT, &boot_state); + update_state_valid = wolfBoot_get_partition_state(PART_UPDATE, &update_state); + + printf("\r\n=== Partition Information ===\r\n"); + + printf("Boot Partition:\r\n"); + printf(" Address: 0x%08lX\r\n", (unsigned long)WOLFBOOT_PARTITION_BOOT_ADDRESS); + printf(" Version: %lu\r\n", (unsigned long)boot_ver); + printf(" State: %s\r\n", part_state_name(boot_state, boot_state_valid)); + + printf("Update Partition:\r\n"); + printf(" Address: 0x%08lX\r\n", (unsigned long)WOLFBOOT_PARTITION_UPDATE_ADDRESS); + printf(" Version: %lu\r\n", (unsigned long)update_ver); + printf(" State: %s\r\n", part_state_name(update_state, update_state_valid)); + + printf("Swap Partition:\r\n"); + printf(" Address: 0x%08lX\r\n", (unsigned long)WOLFBOOT_PARTITION_SWAP_ADDRESS); + printf(" Size: %lu bytes\r\n", (unsigned long)WOLFBOOT_SECTOR_SIZE); +} + +static void print_keystore_info(void) +{ + uint32_t n_keys; + int i; + + printf("\r\n=== Keystore Information ===\r\n"); + + + n_keys = keystore_num_pubkeys(); + printf("Number of public keys: %lu\r\n", (unsigned long)n_keys); + printf("Hash: %s\r\n", hash_type_name()); + + for (i = 0; i < (int)n_keys; i++) { + uint32_t size = keystore_get_size(i); + uint32_t type = keystore_get_key_type(i); + uint8_t* keybuf = keystore_get_buffer(i); + + printf("\r\nKey #%d:\r\n", i); + printf(" Algorithm: %s\r\n", key_type_name(type)); + printf(" Size: %lu bytes\r\n", (unsigned long)size); + printf(" Data:\r\n"); + print_hex(keybuf, size, 0); + } +} + +/* ============== XMODEM Transfer ============== */ + +#define XSOH 0x01 +#define XEOT 0x04 +#define XACK 0x06 +#define XNAK 0x15 +#define XCAN 0x18 +#define XCRC 'C' /* Request CRC mode */ + +#define XMODEM_PAYLOAD_SIZE 128 +#define XMODEM_PACKET_SIZE_CRC (3 + XMODEM_PAYLOAD_SIZE + 2) /* SOH + blk + ~blk + data + CRC16 */ +#define XMODEM_TIMEOUT_MS 1000 + +/* CRC-16-CCITT for XMODEM-CRC mode */ +static uint16_t APP_RAMFUNCTION crc16_ccitt(const uint8_t* data, int len) +{ + uint16_t crc = 0; + int i, j; + for (i = 0; i < len; i++) { + crc ^= ((uint16_t)data[i] << 8); + for (j = 0; j < 8; j++) { + if (crc & 0x8000) { + crc = (crc << 1) ^ 0x1021; + } else { + crc <<= 1; + } + } + } + return crc; +} + +/* Raw byte transmit for XMODEM (declared in hal, runs from RAM) */ +extern void uart_tx(uint8_t byte); + +/* RAM-based memory copy for use during flash operations */ +static void APP_RAMFUNCTION ram_memcpy(void *dst, const void *src, uint32_t len) +{ + uint8_t *d = (uint8_t *)dst; + const uint8_t *s = (const uint8_t *)src; + while (len--) { + *d++ = *s++; + } +} + +static void APP_RAMFUNCTION xmodem_cancel(void) +{ + int i; + for (i = 0; i < 10; i++) { + uart_tx(XCAN); + } +} + +/* XMODEM receive state - passed to RAM function */ +typedef struct { + uint32_t dst_offset; + int result; + /* Debug counters */ + uint32_t pkts_received; + uint32_t pkts_crc_fail; + uint32_t pkts_num_fail; + uint32_t pkts_soh_fail; + uint32_t timeouts; +} xmodem_state_t; + +/* Core XMODEM-CRC receive loop - runs entirely from RAM during flash operations + * Uses XMODEM-CRC mode (133-byte packets with 16-bit CRC) + * Returns: 0 on success, -1 on error + */ +static int APP_RAMFUNCTION xmodem_receive_ram(xmodem_state_t *state) +{ + uint8_t xpkt[XMODEM_PACKET_SIZE_CRC]; + uint8_t payload[XMODEM_PAYLOAD_SIZE]; + uint8_t pkt_num = 0, pkt_num_expected = 0xFF; + uint32_t t_size = 0; + uint32_t now; + uint32_t i = 0; + int transfer_started = 0; + int eot_expected = 0; + int ret = -1; + + state->dst_offset = 0; + state->result = -1; + state->pkts_received = 0; + state->pkts_crc_fail = 0; + state->pkts_num_fail = 0; + state->pkts_soh_fail = 0; + state->timeouts = 0; + + /* Send 'C' to request CRC mode (XMODEM-CRC) */ + uart_tx(XCRC); + + while (1) { + now = jiffies; /* Direct access to volatile - faster than function call */ + i = 0; + + /* Receive packet - uses interrupt-buffered RX to avoid FIFO overflow */ + while (i < XMODEM_PACKET_SIZE_CRC) { + int r = uart_rx_isr(&xpkt[i], XMODEM_PACKET_SIZE_CRC - i); + if (r > 0) { + i += r; + now = jiffies; + if (i >= 1 && xpkt[0] == XEOT) { + break; /* End of transmission */ + } + } else if (jiffies > (now + XMODEM_TIMEOUT_MS)) { + now = jiffies; + state->timeouts++; + if (i == 0) { + uart_tx(XCRC); /* Request CRC mode again */ + } + i = 0; + } + } + + /* Check for EOT */ + if (xpkt[0] == XEOT) { + uart_tx(XACK); + led_red_on(); /* Indicate transfer complete */ + ret = 0; + break; + } else if (eot_expected) { + uart_tx(XNAK); + ret = -1; + break; + } + + /* Validate SOH */ + if (xpkt[0] != XSOH) { + state->pkts_soh_fail++; + continue; + } + state->pkts_received++; + + /* Validate packet number */ + pkt_num = xpkt[1]; + if ((uint8_t)(~xpkt[2]) == pkt_num) { + uint16_t recv_crc, calc_crc; + + if (!transfer_started) { + pkt_num_expected = pkt_num; + transfer_started = 1; + } else if (pkt_num_expected != pkt_num) { + uart_tx(XNAK); + continue; + } + + /* Toggle LED to show activity */ + if ((pkt_num & 0x0F) == 0) { + led_red_on(); + } else if ((pkt_num & 0x0F) == 8) { + led_red_off(); + } + + /* Validate CRC-16 - XMODEM-CRC uses CRC over DATA bytes only */ + recv_crc = ((uint16_t)xpkt[XMODEM_PACKET_SIZE_CRC - 2] << 8) | + xpkt[XMODEM_PACKET_SIZE_CRC - 1]; + calc_crc = crc16_ccitt(xpkt + 3, XMODEM_PAYLOAD_SIZE); + + if (recv_crc == calc_crc) { + /* Copy payload using RAM-based memcpy */ + ram_memcpy(payload, xpkt + 3, XMODEM_PAYLOAD_SIZE); + + /* Send ACK first, then write to flash. + * This allows sender to prepare next packet while we write. + * Risk: if write fails, we've already ACKed - but that's rare. + */ + uart_tx(XACK); + + /* Write to flash */ + ret = hal_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + state->dst_offset, + payload, XMODEM_PAYLOAD_SIZE); + if (ret != 0) { + xmodem_cancel(); + /* No printf here - we're in RAM */ + break; + } + pkt_num_expected++; + state->dst_offset += XMODEM_PAYLOAD_SIZE; + + /* Get expected size from header (offset 4 = image size) */ + if (t_size == 0 && state->dst_offset >= 8) { + t_size = *(uint32_t*)(payload + 4) + IMAGE_HEADER_SIZE; + } + + if (t_size > 0 && state->dst_offset >= t_size) { + eot_expected = 1; + } + } else { + state->pkts_crc_fail++; + uart_tx(XNAK); + } + } else { + state->pkts_num_fail++; + uart_tx(XNAK); + } + } + + state->result = ret; + return ret; +} + +static int cmd_update_xmodem(void) +{ + xmodem_state_t state; + int ret; + uint32_t erase_addr; + int erase_ret; + + printf("Erasing update partition...\r\n"); +#ifdef DEBUG_FLASH + printf(" Address: 0x%08lX\r\n", (unsigned long)WOLFBOOT_PARTITION_UPDATE_ADDRESS); + printf(" Size: 0x%08lX (%lu bytes)\r\n", + (unsigned long)WOLFBOOT_PARTITION_SIZE, + (unsigned long)WOLFBOOT_PARTITION_SIZE); +#endif + + hal_flash_unlock(); + +#ifdef DEBUG_FLASH + /* Erase sector by sector with debug output */ + erase_addr = WOLFBOOT_PARTITION_UPDATE_ADDRESS; + while (erase_addr < WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE) { + printf(" Erasing sector at 0x%08lX...", (unsigned long)erase_addr); + fflush(stdout); + + erase_ret = hal_flash_erase(erase_addr, WOLFBOOT_SECTOR_SIZE); + if (erase_ret != 0) { + printf(" FAILED (%d)\r\n", erase_ret); + hal_flash_lock(); + return -1; + } + printf(" OK\r\n"); + erase_addr += WOLFBOOT_SECTOR_SIZE; + } +#else + (void)erase_addr; + (void)erase_ret; + hal_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_PARTITION_SIZE); +#endif + + printf("Done.\r\n"); + printf("Waiting for XMODEM transfer...\r\n"); + printf("(Send file now using XMODEM-CRC protocol)\r\n"); + + /* Flush all printf output before starting XMODEM */ + fflush(stdout); + /* Wait for UART TX to complete */ + while (!(LPUART1_STAT & LPUART_STAT_TC)) {} + + /* Drain any pending RX data before starting XMODEM */ + { + char c; + while (uart_getc(&c) > 0) {} /* Use ISR buffer, not hardware */ + } + + /* Small delay to ensure clean start */ + delay_ms(100); + + /* Run the receive loop from RAM */ + ret = xmodem_receive_ram(&state); + + hal_flash_lock(); + + /* Wait for sender to finish and drain any pending RX data. + * This prevents printf output from mixing with XMODEM retransmits. */ + { + char c; + delay_ms(3000); /* Wait for sender to give up */ + while (uart_read(&c) > 0) {} /* Drain RX buffer */ + } + + printf("\r\nTransfer %s\r\n", (ret == 0) ? "complete!" : "failed."); + printf("XMODEM stats: recv=%lu, crc_fail=%lu, num_fail=%lu, soh_fail=%lu, timeouts=%lu\r\n", + (unsigned long)state.pkts_received, (unsigned long)state.pkts_crc_fail, + (unsigned long)state.pkts_num_fail, (unsigned long)state.pkts_soh_fail, + (unsigned long)state.timeouts); + + if (ret == 0) { + uint32_t update_ver = wolfBoot_update_firmware_version(); + if (update_ver != 0) { + printf("New firmware version: %lu\r\n", (unsigned long)update_ver); + printf("Triggering update...\r\n"); + wolfBoot_update_trigger(); + printf("Reboot to apply update.\r\n"); + } else { + printf("Warning: No valid image detected\r\n"); + } + } + + led_red_off(); + return ret; +} + +/* ============== Console Commands ============== */ + +static int cmd_help(const char *args); +static int cmd_info(const char *args); +static int cmd_success(const char *args); +static int cmd_reboot(const char *args); +static int cmd_update(const char *args); +static int cmd_timestamp(const char *args); + +typedef struct { + int (*fn)(const char *args); + const char* name; + const char* help; +} console_cmd_t; + +static const console_cmd_t commands[] = { + {cmd_help, "help", "Show this help message"}, + {cmd_info, "info", "Display partition and key info"}, + {cmd_success, "success", "Mark firmware as successful"}, + {cmd_update, "update", "Update firmware via XMODEM"}, + {cmd_timestamp, "timestamp", "Show current system time"}, + {cmd_reboot, "reboot", "Reboot the system"}, + {NULL, NULL, NULL} +}; + +static int cmd_help(const char *args) +{ + int i; + (void)args; + printf("\r\nAvailable commands:\r\n"); + for (i = 0; commands[i].name != NULL; i++) { + printf(" %s - %s\r\n", commands[i].name, commands[i].help); + } + return 0; +} + +static int cmd_info(const char *args) +{ + (void)args; + print_partition_info(); + print_keystore_info(); + return 0; +} + +static int cmd_success(const char *args) +{ + (void)args; + wolfBoot_success(); + printf("Firmware marked as successful.\r\n"); + return 0; +} + +static int cmd_timestamp(const char *args) +{ + (void)args; + printf("Current systick: %lu ms\r\n", (unsigned long)jiffies); + return 0; +} + +static int cmd_reboot(const char *args) +{ + (void)args; + printf("Rebooting...\r\n"); + fflush(stdout); + delay_ms(100); /* Allow UART to flush */ + arch_reboot(); + return 0; +} + +static int cmd_update(const char *args) +{ + (void)args; + return cmd_update_xmodem(); +} + +static int parse_command(const char* cmd) +{ + int i; + for (i = 0; commands[i].name != NULL; i++) { + if (strcmp(cmd, commands[i].name) == 0) { + return commands[i].fn(NULL); + } + } + printf("Unknown command: %s\r\n", cmd); + printf("Type 'help' for available commands.\r\n"); + return -1; +} + +#define CMD_BUF_SIZE 64 + +static void console_loop(void) +{ + char cmd[CMD_BUF_SIZE]; + int idx; + char c; + + while (1) { + printf("\r\ncmd> "); + fflush(stdout); + idx = 0; + + while (idx < CMD_BUF_SIZE - 1) { + int ret = uart_getc(&c); + if (ret > 0) { + if (c == '\r' || c == '\n') { + printf("\r\n"); + break; + } else if (c == 0x08 || c == 0x7F) { /* Backspace */ + if (idx > 0) { + printf("\b \b"); + fflush(stdout); + idx--; + } + } else if (c >= 32 && c < 127) { + printf("%c", c); + fflush(stdout); + cmd[idx++] = c; + } + } + /* No delay - tight polling loop for responsive input */ + } + + cmd[idx] = '\0'; + if (idx > 0) { + parse_command(cmd); + } + } +} +#endif /* DEBUG_UART */ + +/* ============== Clock Functions ============== */ + +/* Ensure FIRC (48 MHz) is enabled for UART */ +static void clock_ensure_firc(void) +{ + /* Check if FIRC is valid */ + if (!(SCG_FIRCCSR & SCG_FIRCCSR_FIRCVLD)) { + /* Enable FIRC if not already enabled */ + SCG_FIRCDIV = (1UL << 8) | (1UL << 0); /* FIRCDIV1=/1, FIRCDIV2=/1 */ + SCG_FIRCCFG = 0; /* Range 0: 48 MHz */ + SCG_FIRCCSR = SCG_FIRCCSR_FIRCEN; + + /* Wait for FIRC valid */ + while (!(SCG_FIRCCSR & SCG_FIRCCSR_FIRCVLD)) {} + } + + /* Ensure system is running from FIRC */ + if ((SCG_CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS_FIRC) { + SCG_RCCR = SCG_xCCR_SCS_FIRC | + (0UL << SCG_xCCR_DIVCORE_SHIFT) | + (0UL << SCG_xCCR_DIVBUS_SHIFT) | + (1UL << SCG_xCCR_DIVSLOW_SHIFT); + + /* Wait for clock switch */ + while ((SCG_CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS_FIRC) {} + } +} + +/* ============== Main Entry Point ============== */ + +void main(void) +{ + uint32_t version; + + /* Disable watchdog - bootloader may have enabled it */ + WDOG_CNT = WDOG_CNT_UNLOCK; + while (!(WDOG_CS & WDOG_CS_ULK)) {} + WDOG_TOVAL = 0xFFFF; + WDOG_CS = WDOG_CS_UPDATE | WDOG_CS_CMD32EN | WDOG_CS_CLK_LPO; /* Disabled, but updatable */ + while (!(WDOG_CS & WDOG_CS_RCS)) {} + + /* Ensure FIRC clock is running at 48 MHz for UART */ + clock_ensure_firc(); + +#ifdef DEBUG_UART + /* Reinitialize UART - bootloader may have changed settings in hal_prepare_boot */ + uart_init(); + /* Enable interrupt-based RX buffering for reliable XMODEM transfers */ + uart_rx_irq_enable(); + /* Disable stdout buffering to prevent delayed output during XMODEM */ + setvbuf(stdout, NULL, _IONBF, 0); +#endif + + /* Initialize test-app hardware */ + systick_init(); + led_init(); + + /* Enable interrupts */ + __asm__ volatile ("cpsie i"); + + /* Get current firmware version */ + version = wolfBoot_current_firmware_version(); + + /* Set LED based on version: Green for v1, Blue for v>1 */ + led_set_version(version); + +#ifdef DEBUG_UART + printf("\r\n"); + printf("========================================\r\n"); + printf("S32K1xx wolfBoot Test Application\r\n"); + printf("Copyright 2025 wolfSSL Inc.\r\n"); + printf("========================================\r\n"); + printf("Firmware Version: %lu\r\n", (unsigned long)version); + + /* Auto-mark success for testing if version > 1 */ + if (version > 1) { + uint8_t state = 0; + wolfBoot_get_partition_state(PART_BOOT, &state); + if (state == IMG_STATE_TESTING) { + printf("Testing state detected, marking success...\r\n"); + wolfBoot_success(); + } + } + + /* Show initial info */ + print_partition_info(); + + printf("\r\nType 'help' for available commands.\r\n"); + + /* Enter interactive console */ + console_loop(); +#else + /* No UART - just blink LED */ + while (1) { + led_toggle_version(version); + delay_ms(500); + } +#endif +} + +/* ============== Syscalls for printf support ============== */ + +int _getpid(void) +{ + return 1; +} + +int _kill(int pid, int sig) +{ + (void)pid; + (void)sig; + return -1; +} + +void _exit(int status) +{ + _kill(status, -1); + while (1) {} +} + +int _read(int file, char *ptr, int len) +{ + (void)file; + (void)ptr; + (void)len; + return -1; +} + +int _write(int file, char *ptr, int len) +{ + (void)file; +#ifdef DEBUG_UART + uart_write(ptr, len); +#else + (void)ptr; +#endif + return len; +} + +int _close(int file) +{ + (void)file; + return -1; +} + +int _isatty(int file) +{ + (void)file; + return 1; +} + +int _lseek(int file, int ptr, int dir) +{ + (void)file; + (void)ptr; + (void)dir; + return 0; +} + +int _fstat(int file, struct stat *st) +{ + (void)file; + st->st_mode = S_IFCHR; + return 0; +} + +/* Back-end for malloc, used for printf */ +extern unsigned int _end; /* From linker script: end of BSS */ +extern unsigned int _end_stack; /* From linker script: end of RAM */ + +void *_sbrk(int incr) +{ + static unsigned char *heap = NULL; + unsigned char *prev_heap; + + if (heap == NULL) { + heap = (unsigned char *)&_end; + } + + prev_heap = heap; + + /* Align increment to 4 bytes */ + if (((incr >> 2) << 2) != incr) + incr = ((incr >> 2) + 1) << 2; + + /* Check we don't overflow into the stack */ + if ((heap + incr) > (unsigned char *)&_end_stack) { + return (void *)-1; + } + + heap += incr; + return prev_heap; +} + +#endif /* TARGET_s32k1xx */ diff --git a/test-app/startup_arm.c b/test-app/startup_arm.c index d6a76c6a1b..68e6c37465 100644 --- a/test-app/startup_arm.c +++ b/test-app/startup_arm.c @@ -39,11 +39,16 @@ extern void isr_tim2(void); extern void isr_usart3(void); #endif +#ifdef TARGET_s32k1xx +extern void isr_lpuart1(void); +#endif + #ifdef TARGET_va416x0 -extern void SysTick_Handler(void); -#elif defined(APP_HAS_SYSTICK) -extern void isr_systick(void); +#define isr_systick SysTick_Handler +#elif !defined(APP_HAS_SYSTICK) +#define isr_systick isr_empty #endif +extern void isr_systick(void); #ifndef STACK_PAINTING #define STACK_PAINTING 0 @@ -91,26 +96,26 @@ void isr_reset(void) { void isr_fault(void) { /* Panic. */ - while(1) ;; + while(1) ; } void isr_memfault(void) { /* Panic. */ - while(1) ;; + while(1) ; } void isr_busfault(void) { /* Panic. */ - while(1) ;; + while(1) ; } void isr_usagefault(void) { /* Panic. */ - while(1) ;; + while(1) ; } @@ -119,8 +124,6 @@ void isr_empty(void) } - - __attribute__ ((section(".isr_vector"))) void (* const IV[])(void) = { @@ -139,14 +142,7 @@ void (* const IV[])(void) = isr_empty, // DebugMonitor 0, // reserved isr_empty, // PendSV - -#ifdef TARGET_va416x0 - SysTick_Handler, // SysTick -#elif defined(APP_HAS_SYSTICK) isr_systick, // SysTick -#else - isr_empty, // SysTick -#endif /* Device specific IRQs for LM3S */ @@ -438,6 +434,97 @@ void (* const IV[])(void) = isr_empty, // LPTIM5_IRQHandler isr_empty, // LPTIM6_IRQHandler +#elif defined(TARGET_s32k1xx) /* For NXP S32K1xx */ + isr_empty, // DMA0 0 + isr_empty, // DMA1 1 + isr_empty, // DMA2 2 + isr_empty, // DMA3 3 + isr_empty, // DMA4 4 + isr_empty, // DMA5 5 + isr_empty, // DMA6 6 + isr_empty, // DMA7 7 + isr_empty, // DMA8 8 + isr_empty, // DMA9 9 + isr_empty, // DMA10 10 + isr_empty, // DMA11 11 + isr_empty, // DMA12 12 + isr_empty, // DMA13 13 + isr_empty, // DMA14 14 + isr_empty, // DMA15 15 + isr_empty, // DMA_Error 16 + isr_empty, // MCM 17 + isr_empty, // FTFC 18 + isr_empty, // Read_Collision 19 + isr_empty, // LVD_LVW 20 + isr_empty, // FTFC_Fault 21 + isr_empty, // WDOG_EWM 22 + isr_empty, // RCM 23 + isr_empty, // LPI2C0_Master 24 + isr_empty, // LPI2C0_Slave 25 + isr_empty, // LPSPI0 26 + isr_empty, // LPSPI1 27 + isr_empty, // LPSPI2 28 + isr_empty, // Reserved29 29 + isr_empty, // Reserved30 30 + isr_empty, // LPUART0_RxTx 31 + isr_empty, // Reserved32 32 + isr_lpuart1, // LPUART1_RxTx 33 + isr_empty, // Reserved34 34 + isr_empty, // LPUART2_RxTx 35 + isr_empty, // Reserved36 36 + isr_empty, // Reserved37 37 + isr_empty, // ADC0 38 + isr_empty, // ADC1 39 + isr_empty, // CMP0 40 + isr_empty, // Reserved41 41 + isr_empty, // Reserved42 42 + isr_empty, // ERM_single 43 + isr_empty, // ERM_double 44 + isr_empty, // RTC 45 + isr_empty, // RTC_Seconds 46 + isr_empty, // LPIT0_Ch0 47 + isr_empty, // LPIT0_Ch1 48 + isr_empty, // LPIT0_Ch2 49 + isr_empty, // LPIT0_Ch3 50 + isr_empty, // PDB0 51 + isr_empty, // Reserved52 52 + isr_empty, // Reserved53 53 + isr_empty, // Reserved54 54 + isr_empty, // Reserved55 55 + isr_empty, // SCG 56 + isr_empty, // LPTMR0 57 + isr_empty, // PORTA 58 + isr_empty, // PORTB 59 + isr_empty, // PORTC 60 + isr_empty, // PORTD 61 + isr_empty, // PORTE 62 + isr_empty, // Reserved63 63 + isr_empty, // PDB1 64 + isr_empty, // FLEXIO 65 + isr_empty, // CAN0_ORed 66 + isr_empty, // CAN0_Error 67 + isr_empty, // CAN0_Wake_Up 68 + isr_empty, // CAN0_MB0_15 69 + isr_empty, // CAN0_MB16_31 70 + isr_empty, // FTM0_Ch0_Ch1 71 + isr_empty, // FTM0_Ch2_Ch3 72 + isr_empty, // FTM0_Ch4_Ch5 73 + isr_empty, // FTM0_Ch6_Ch7 74 + isr_empty, // FTM0_Fault 75 + isr_empty, // FTM0_Ovf_Reload 76 + isr_empty, // FTM1_Ch0_Ch1 77 + isr_empty, // FTM1_Ch2_Ch3 78 + isr_empty, // FTM1_Ch4_Ch5 79 + isr_empty, // FTM1_Ch6_Ch7 80 + isr_empty, // FTM1_Fault 81 + isr_empty, // FTM1_Ovf_Reload 82 + isr_empty, // FTM2_Ch0_Ch1 83 + isr_empty, // FTM2_Ch2_Ch3 84 + isr_empty, // FTM2_Ch4_Ch5 85 + isr_empty, // FTM2_Ch6_Ch7 86 + isr_empty, // FTM2_Fault 87 + isr_empty, // FTM2_Ovf_Reload 88 + #elif defined(STM32) /* For STM32 */ isr_empty, // NVIC_WWDG_IRQ 0 isr_empty, // PVD_IRQ 1 diff --git a/tools/scripts/nxp-s32k142-flash.sh b/tools/scripts/nxp-s32k142-flash.sh new file mode 100755 index 0000000000..f903d06782 --- /dev/null +++ b/tools/scripts/nxp-s32k142-flash.sh @@ -0,0 +1,225 @@ +#!/bin/bash +# +# NXP S32K142 Flash and UART Monitor Script +# +# This script automates: +# 1. Configure for NXP S32K142 +# 2. Build factory.srec +# 3. Start UART capture (before flash to catch boot messages) +# 4. Flash via USB mass storage +# 5. Capture UART output for specified duration +# + +set -e + +# Configuration +CONFIG_FILE="./config/examples/nxp-s32k142.config" +MOUNT_PATH="/media/davidgarske/S32K142EVB" +UART_DEV="/dev/ttyACM1" +UART_BAUD=115200 +SREC_FILE="factory.srec" +UART_TIMEOUT=5 # Default 5 seconds capture + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# Parse command line arguments +SKIP_BUILD=0 +SKIP_FLASH=0 +SKIP_UART=0 +UART_ONLY=0 +INTERACTIVE=0 + +usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --skip-build Skip the build step (use existing factory.srec)" + echo " --skip-flash Skip flashing (just monitor UART)" + echo " --skip-uart Skip UART monitoring (just build and flash)" + echo " --uart-only Only monitor UART (same as --skip-build --skip-flash)" + echo " --interactive Keep UART open until Ctrl+C (default: auto-exit after timeout)" + echo " --timeout SECS UART capture duration in seconds (default: $UART_TIMEOUT)" + echo " --mount PATH Override mount path (default: $MOUNT_PATH)" + echo " --uart DEV Override UART device (default: $UART_DEV)" + echo " --baud RATE Override baud rate (default: $UART_BAUD)" + echo " -h, --help Show this help message" + exit 0 +} + +while [[ $# -gt 0 ]]; do + case $1 in + --skip-build) + SKIP_BUILD=1 + shift + ;; + --skip-flash) + SKIP_FLASH=1 + shift + ;; + --skip-uart) + SKIP_UART=1 + shift + ;; + --uart-only) + SKIP_BUILD=1 + SKIP_FLASH=1 + shift + ;; + --interactive) + INTERACTIVE=1 + shift + ;; + --timeout) + UART_TIMEOUT="$2" + shift 2 + ;; + --mount) + MOUNT_PATH="$2" + shift 2 + ;; + --uart) + UART_DEV="$2" + shift 2 + ;; + --baud) + UART_BAUD="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + echo -e "${RED}Unknown option: $1${NC}" + usage + ;; + esac +done + +# Cleanup function to kill background UART capture +UART_PID="" +cleanup() { + if [ -n "$UART_PID" ] && kill -0 "$UART_PID" 2>/dev/null; then + kill "$UART_PID" 2>/dev/null || true + wait "$UART_PID" 2>/dev/null || true + fi +} +trap cleanup EXIT + +echo -e "${GREEN}=== NXP S32K142 Flash and Monitor Script ===${NC}" + +# Change to wolfboot root directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WOLFBOOT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +cd "${WOLFBOOT_ROOT}" +echo -e "${YELLOW}Working directory: ${WOLFBOOT_ROOT}${NC}" + +# Step 1: Configure +if [ $SKIP_BUILD -eq 0 ]; then + echo "" + echo -e "${GREEN}[1/4] Configuring for NXP S32K142...${NC}" + if [ ! -f "${CONFIG_FILE}" ]; then + echo -e "${RED}Error: Config file not found: ${CONFIG_FILE}${NC}" + exit 1 + fi + cp "${CONFIG_FILE}" .config + echo "Copied ${CONFIG_FILE} to .config" + + # Step 2: Build + echo "" + echo -e "${GREEN}[2/4] Building factory.srec...${NC}" + make clean + make factory.srec + + if [ ! -f "${SREC_FILE}" ]; then + echo -e "${RED}Error: Build failed - ${SREC_FILE} not found${NC}" + exit 1 + fi + echo -e "${GREEN}Build successful: ${SREC_FILE}${NC}" +else + echo "" + echo -e "${YELLOW}[1/4] Skipping configure (--skip-build)${NC}" + echo -e "${YELLOW}[2/4] Skipping build (--skip-build)${NC}" +fi + +# Step 3: Start UART capture BEFORE flashing +if [ $SKIP_UART -eq 0 ]; then + echo "" + echo -e "${GREEN}[3/4] Starting UART capture on ${UART_DEV} at ${UART_BAUD} baud...${NC}" + + # Check if UART device exists + if [ ! -c "${UART_DEV}" ]; then + echo -e "${RED}Error: UART device ${UART_DEV} not found${NC}" + echo "Available ttyACM devices:" + ls -la /dev/ttyACM* 2>/dev/null || echo " (none found)" + exit 1 + fi + + # Configure UART + stty -F "${UART_DEV}" "${UART_BAUD}" raw -echo -hupcl + + # Start background UART capture that outputs to terminal + cat "${UART_DEV}" & + UART_PID=$! + echo -e "${CYAN}UART capture started (PID: ${UART_PID})${NC}" +else + echo "" + echo -e "${YELLOW}[3/4] Skipping UART capture (--skip-uart)${NC}" +fi + +# Step 4: Flash +if [ $SKIP_FLASH -eq 0 ]; then + echo "" + echo -e "${GREEN}[4/4] Flashing to S32K142EVB...${NC}" + + if [ ! -f "${SREC_FILE}" ]; then + echo -e "${RED}Error: ${SREC_FILE} not found. Run without --skip-build first.${NC}" + exit 1 + fi + + # Wait for mount point if not available + WAIT_COUNT=0 + while [ ! -d "${MOUNT_PATH}" ]; do + if [ $WAIT_COUNT -eq 0 ]; then + echo -e "${YELLOW}Waiting for ${MOUNT_PATH} to be mounted...${NC}" + echo "(Connect the S32K142EVB board via USB)" + fi + sleep 1 + WAIT_COUNT=$((WAIT_COUNT + 1)) + if [ $WAIT_COUNT -gt 30 ]; then + echo -e "${RED}Error: Timeout waiting for ${MOUNT_PATH}${NC}" + exit 1 + fi + done + + echo "Copying ${SREC_FILE} to ${MOUNT_PATH}..." + cp "${SREC_FILE}" "${MOUNT_PATH}/" + sync + echo -e "${GREEN}Flash complete! Waiting for boot output...${NC}" +else + echo "" + echo -e "${YELLOW}[4/4] Skipping flash (--skip-flash)${NC}" +fi + +# UART monitoring +if [ $SKIP_UART -eq 0 ]; then + echo "" + if [ $INTERACTIVE -eq 1 ]; then + echo -e "${YELLOW}=== UART Output (Press Ctrl+C to exit) ===${NC}" + wait "$UART_PID" 2>/dev/null || true + else + echo -e "${YELLOW}=== UART Output (capturing for ${UART_TIMEOUT} seconds) ===${NC}" + sleep "$UART_TIMEOUT" + + # Kill the background cat process + cleanup + UART_PID="" + fi +fi + +echo "" +echo -e "${GREEN}=== Complete ===${NC}"