From 92a8a7e9625aad0c35e929ae468529e27f980e75 Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 5 Jan 2026 09:06:46 -0800 Subject: [PATCH 1/4] Support for NXP S32K14XX --- .github/workflows/test-configs.yml | 24 ++ config/examples/nxp-s32k142.config | 52 +++ config/examples/nxp-s32k144.config | 57 +++ config/examples/nxp-s32k146.config | 57 +++ config/examples/nxp-s32k148.config | 57 +++ docs/Targets.md | 158 ++++++++ hal/s32k1xx.c | 632 +++++++++++++++++++++++++++++ hal/s32k1xx.h | 283 +++++++++++++ hal/s32k1xx.ld | 86 ++++ test-app/ARM-s32k1xx.ld | 70 ++++ test-app/Makefile | 4 + test-app/app_s32k1xx.c | 212 ++++++++++ 12 files changed, 1692 insertions(+) create mode 100644 config/examples/nxp-s32k142.config create mode 100644 config/examples/nxp-s32k144.config create mode 100644 config/examples/nxp-s32k146.config create mode 100644 config/examples/nxp-s32k148.config create mode 100644 hal/s32k1xx.c create mode 100644 hal/s32k1xx.h create mode 100644 hal/s32k1xx.ld create mode 100644 test-app/ARM-s32k1xx.ld create mode 100644 test-app/app_s32k1xx.c 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/config/examples/nxp-s32k142.config b/config/examples/nxp-s32k142.config new file mode 100644 index 0000000000..11867963d9 --- /dev/null +++ b/config/examples/nxp-s32k142.config @@ -0,0 +1,52 @@ +# 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?=0 +DUALBANK_SWAP?=0 + +# Disable MPU (S32K1xx MPU configuration needs customization) +WOLFBOOT_NO_MPU?=1 + +# Enable hardfault debugging +DEBUG_HARDFAULT?=1 + +# Debug UART on LPUART1 (PTC6=RX, PTC7=TX) +DEBUG_UART?=1 + +# 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 + diff --git a/config/examples/nxp-s32k144.config b/config/examples/nxp-s32k144.config new file mode 100644 index 0000000000..78f4bccc4a --- /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?=0 +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 +DEBUG_HARDFAULT?=1 + +# Debug UART on LPUART1 (PTC6=RX, PTC7=TX) +DEBUG_UART?=1 + +# 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 + + + diff --git a/config/examples/nxp-s32k146.config b/config/examples/nxp-s32k146.config new file mode 100644 index 0000000000..8f3e366d2b --- /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?=0 +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 +DEBUG_HARDFAULT?=1 + +# Debug UART on LPUART1 (PTC6=RX, PTC7=TX) +DEBUG_UART?=1 + +# 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 + + + diff --git a/config/examples/nxp-s32k148.config b/config/examples/nxp-s32k148.config new file mode 100644 index 0000000000..9af244b09e --- /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?=0 +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 +DEBUG_HARDFAULT?=1 + +# Debug UART on LPUART1 (PTC6=RX, PTC7=TX) +DEBUG_UART?=1 + +# 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 + + + diff --git a/docs/Targets.md b/docs/Targets.md index 87dbd47215..c0ec7e55cb 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,163 @@ 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 file: [/config/examples/nxp-s32k142.config](/config/examples/nxp-s32k142.config) + +```sh +# Copy configuration +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 + +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 wolfboot.elf +target remote :7224 +monitor reset halt +load +# Flash the signed application to boot partition +monitor programbin test-app/image_v1_signed.bin 0xc000 +monitor reset run +``` + +### NXP S32K1XX: Debugging Tips + +When debugging with GDB: +```sh +arm-none-eabi-gdb +target remote :7224 +add-symbol-file wolfboot.elf 0x0 +add-symbol-file test-app/image.elf 0xC100 +set mem inaccessible-by-default off +monitor reset halt +b main +c +``` + +For UART debug output, connect a USB-to-serial adapter to LPUART1 pins (PTC6=RX, PTC7=TX) and open a terminal at 115200 baud. + +### 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/s32k1xx.c b/hal/s32k1xx.c new file mode 100644 index 0000000000..e2dbaeff54 --- /dev/null +++ b/hal/s32k1xx.c @@ -0,0 +1,632 @@ +/* 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" + +/* 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) +{ + /* Disable SPLL before configuration */ + SCG_SPLLCSR &= ~SCG_SPLLCSR_SPLLEN; + + /* Configure SPLL: + * Using FIRC (48 MHz) as source + * For 112 MHz: PREDIV=0 (/1), MULT=12 (x28) + * VCO = 48 MHz * 28 = 1344 MHz (wait, that's too high) + * + * Actually S32K uses SOSC as SPLL source typically. + * With FIRC we need different approach. + * + * For HSRUN at 112 MHz from FIRC: + * Use FIRC directly for core at 48 MHz in RUN mode + * Then switch to SPLL for HSRUN + * + * SPLL: VCO range is 180-320 MHz + * SPLL_CLK = VCO / 2 + * + * If using 8 MHz SOSC: PREDIV=0, MULT=28 -> VCO=224 MHz, SPLL_CLK=112 MHz + * + * For this bare-metal impl, we'll use FIRC at 48 MHz as a safe default, + * and configure SPLL with SOSC if available. + * + * For simplicity: Use FIRC 48MHz with appropriate dividers. + * HSRUN mode allows higher frequencies but requires SPLL. + */ + + /* SPLL dividers */ + SCG_SPLLDIV = (2UL << 8) | (4UL << 0); /* SPLLDIV1=/2, SPLLDIV2=/4 */ + + /* SPLL configuration: MULT and PREDIV + * PREDIV: 0-7 -> divide by 1-8 + * MULT: 0-31 -> multiply by 16-47 + * VCO = (FIRC/PREDIV) * MULT, must be 180-320 MHz + * SPLL_CLK = VCO / 2 + * + * For 112 MHz with 48 MHz FIRC: + * Need VCO = 224 MHz + * 48 / 1 * 28 = 1344 MHz (too high, FIRC can't be SPLL source directly) + * + * S32K1xx SPLL source is SOSC only. For bare-metal without crystal, + * we run from FIRC at 48 MHz maximum. + * + * If SOSC 8 MHz is available: + * 8 / 1 * 28 = 224 MHz VCO, 112 MHz SPLL_CLK + */ + + /* For now, skip SPLL and use FIRC directly */ + /* TODO: Add SOSC + SPLL support for true 112 MHz operation */ +} + +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 PORTC */ + PCC_PORTC |= PCC_CGC; + + /* Configure pins for LPUART1: + * PTC6 = LPUART1_RX (ALT2) + * PTC7 = LPUART1_TX (ALT2) + */ + PORTC_PCR6 = PORT_PCR_MUX_ALT2; + PORTC_PCR7 = PORT_PCR_MUX_ALT2; + + /* Enable clock to LPUART1, source = FIRC (48 MHz) */ + PCC_LPUART1 = 0; /* Disable before changing source */ + PCC_LPUART1 = PCC_PCS_FIRC | PCC_CGC; + + /* Calculate baud rate: + * SBR = UART_CLK / (BAUD * OSR) + */ + sbr = uart_clock / (UART_BAUDRATE * osr); + + /* Disable TX/RX before configuration */ + LPUART1_CTRL = 0; + + /* Configure baud rate */ + LPUART1_BAUD = ((osr - 1) << LPUART_BAUD_OSR_SHIFT) | + (sbr << LPUART_BAUD_SBR_SHIFT); + + /* Enable transmitter and receiver */ + LPUART1_CTRL = LPUART_CTRL_TE | LPUART_CTRL_RE; +} + +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') { + /* Wait for transmit buffer empty */ + while (!(LPUART1_STAT & LPUART_STAT_TDRE)) {} + LPUART1_DATA = '\r'; + } + + /* Wait for transmit buffer empty */ + while (!(LPUART1_STAT & LPUART_STAT_TDRE)) {} + LPUART1_DATA = buf[i]; + } + + /* Wait for transmission complete */ + while (!(LPUART1_STAT & LPUART_STAT_TC)) {} +} + +#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) +{ + /* 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(); + FTFC_FSTAT = FTFC_FSTAT_CCIF; + + /* Wait for completion */ + flash_wait_complete(); + +#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(); + uart_write("wolfBoot HAL Init\n", 18); +#endif + +#ifdef WATCHDOG + /* Re-enable watchdog with configured timeout */ +#ifndef WATCHDOG_TIMEOUT_MS +#define WATCHDOG_TIMEOUT_MS 1000 /* Default 1 second timeout */ +#endif + watchdog_enable(WATCHDOG_TIMEOUT_MS); +#endif +} + +void hal_prepare_boot(void) +{ +#ifdef DEBUG_UART + /* Wait for any pending UART transmission to complete */ + while (!(LPUART1_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 +} + +/* 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 + +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) +{ + /* Flash is always accessible on S32K1xx after reset */ +} + +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..d3818ac01b --- /dev/null +++ b/hal/s32k1xx.h @@ -0,0 +1,283 @@ +/* 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) + */ + +/* ============== 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_PORTC (*(volatile uint32_t *)(PCC_BASE + 0x12CUL)) +#define PCC_LPUART1 (*(volatile uint32_t *)(PCC_BASE + 0x1ACUL)) +#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 ============== */ + +#define PORTC_BASE (0x4004B000UL) +#define PORTC_PCR6 (*(volatile uint32_t *)(PORTC_BASE + 0x018UL)) +#define PORTC_PCR7 (*(volatile uint32_t *)(PORTC_BASE + 0x01CUL)) + +#define PORT_PCR_MUX_SHIFT 8 +#define PORT_PCR_MUX_ALT2 (2UL << PORT_PCR_MUX_SHIFT) /* LPUART1 */ + +/* ============== LPUART Registers ============== */ + +#define LPUART1_BASE (0x4006B000UL) +#define LPUART1_VERID (*(volatile uint32_t *)(LPUART1_BASE + 0x000UL)) +#define LPUART1_PARAM (*(volatile uint32_t *)(LPUART1_BASE + 0x004UL)) +#define LPUART1_GLOBAL (*(volatile uint32_t *)(LPUART1_BASE + 0x008UL)) +#define LPUART1_BAUD (*(volatile uint32_t *)(LPUART1_BASE + 0x010UL)) +#define LPUART1_STAT (*(volatile uint32_t *)(LPUART1_BASE + 0x014UL)) +#define LPUART1_CTRL (*(volatile uint32_t *)(LPUART1_BASE + 0x018UL)) +#define LPUART1_DATA (*(volatile uint32_t *)(LPUART1_BASE + 0x01CUL)) + +#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_STAT_TDRE (1UL << 23) /* Transmit Data Register Empty */ +#define LPUART_STAT_TC (1UL << 22) /* Transmission Complete */ + +/* ============== 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 + +/* 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) + +#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/test-app/ARM-s32k1xx.ld b/test-app/ARM-s32k1xx.ld new file mode 100644 index 0000000000..4a0f404a5c --- /dev/null +++ b/test-app/ARM-s32k1xx.ld @@ -0,0 +1,70 @@ +/* + * Linker script for wolfBoot test application on NXP S32K1xx + * + * Application runs from boot partition after wolfBoot header + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x7000 /* 28KB 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..02c6397c4e 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -348,6 +348,10 @@ ifeq ($(TARGET),mcxa) LDFLAGS+=--specs=nosys.specs endif +ifeq ($(TARGET),s32k1xx) + LSCRIPT_TEMPLATE=ARM-s32k1xx.ld +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..da30b8764e --- /dev/null +++ b/test-app/app_s32k1xx.c @@ -0,0 +1,212 @@ +/* app_s32k1xx.c + * + * Test bare-metal application for NXP S32K1xx + * + * 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 "hal.h" +#include "wolfboot/wolfboot.h" +#include "target.h" + +#ifdef TARGET_s32k1xx + +/* System Control Block */ +#define AIRCR (*(volatile uint32_t *)(0xE000ED0C)) +#define AIRCR_VKEY (0x05FA << 16) +#define AIRCR_SYSRESETREQ (1 << 2) + +/* SysTick Timer */ +#define SYST_CSR (*(volatile uint32_t *)(0xE000E010)) +#define SYST_RVR (*(volatile uint32_t *)(0xE000E014)) +#define SYST_CVR (*(volatile uint32_t *)(0xE000E018)) +#define SYST_CALIB (*(volatile uint32_t *)(0xE000E01C)) + +#define SYST_CSR_ENABLE (1 << 0) +#define SYST_CSR_TICKINT (1 << 1) +#define SYST_CSR_CLKSOURCE (1 << 2) +#define SYST_CSR_COUNTFLAG (1 << 16) + +/* GPIO for LED (example: PTD15 on S32K142 EVB) */ +#define PCC_BASE (0x40065000UL) +#define PCC_PORTD (*(volatile uint32_t *)(PCC_BASE + 0x130UL)) +#define PCC_CGC (1UL << 30) + +#define PORTD_BASE (0x4004C000UL) +#define PORTD_PCR15 (*(volatile uint32_t *)(PORTD_BASE + 0x03CUL)) +#define PORT_PCR_MUX_GPIO (1UL << 8) + +#define GPIOD_BASE (0x400FF0C0UL) +#define GPIOD_PDOR (*(volatile uint32_t *)(GPIOD_BASE + 0x00UL)) +#define GPIOD_PSOR (*(volatile uint32_t *)(GPIOD_BASE + 0x04UL)) +#define GPIOD_PCOR (*(volatile uint32_t *)(GPIOD_BASE + 0x08UL)) +#define GPIOD_PTOR (*(volatile uint32_t *)(GPIOD_BASE + 0x0CUL)) +#define GPIOD_PDIR (*(volatile uint32_t *)(GPIOD_BASE + 0x10UL)) +#define GPIOD_PDDR (*(volatile uint32_t *)(GPIOD_BASE + 0x14UL)) + +#define LED_PIN 15 + +/* Clock speed (FIRC = 48 MHz) */ +#ifndef CLOCK_SPEED +#define CLOCK_SPEED 48000000UL +#endif + +/* Simple delay counter */ +static volatile uint32_t systick_count = 0; + +void SysTick_Handler(void) +{ + systick_count++; +} + +static void delay_ms(uint32_t ms) +{ + uint32_t start = systick_count; + while ((systick_count - 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; +} + +static void led_init(void) +{ + /* Enable clock to PORTD */ + PCC_PORTD |= PCC_CGC; + + /* Configure PTD15 as GPIO */ + PORTD_PCR15 = PORT_PCR_MUX_GPIO; + + /* Set PTD15 as output */ + GPIOD_PDDR |= (1UL << LED_PIN); + + /* LED off initially (active low on most boards) */ + GPIOD_PSOR = (1UL << LED_PIN); +} + +static void led_on(void) +{ + GPIOD_PCOR = (1UL << LED_PIN); /* Active low */ +} + +static void led_off(void) +{ + GPIOD_PSOR = (1UL << LED_PIN); +} + +static void led_toggle(void) +{ + GPIOD_PTOR = (1UL << LED_PIN); +} + +void arch_reboot(void) +{ + AIRCR = AIRCR_VKEY | AIRCR_SYSRESETREQ; + while (1) { + __asm__ volatile ("wfi"); + } +} + +#ifdef DEBUG_UART +extern void uart_init(void); +extern void uart_write(const char* buf, unsigned int sz); + +static void print_version(uint32_t version) +{ + char buf[16]; + int i = 0; + + uart_write("Version: ", 9); + + /* Simple number to string */ + if (version == 0) { + uart_write("0", 1); + } else { + char tmp[10]; + int j = 0; + while (version > 0) { + tmp[j++] = '0' + (version % 10); + version /= 10; + } + while (j > 0) { + buf[i++] = tmp[--j]; + } + uart_write(buf, i); + } + uart_write("\n", 1); +} +#endif + +void main(void) +{ + uint32_t version; + int i; + + /* Initialize hardware */ + hal_init(); + systick_init(); + led_init(); + + /* Enable interrupts */ + __asm__ volatile ("cpsie i"); + +#ifdef DEBUG_UART + uart_write("wolfBoot S32K1xx Test App\n", 26); +#endif + + /* Get current firmware version */ + version = wolfBoot_current_firmware_version(); + +#ifdef DEBUG_UART + print_version(version); +#endif + + /* Mark firmware as successful if version is even */ + if ((version & 0x01) == 0) { + wolfBoot_success(); +#ifdef DEBUG_UART + uart_write("Firmware marked successful\n", 27); +#endif + } + + /* Blink LED to show we're running */ + led_on(); + +#ifdef DEBUG_UART + uart_write("Entering main loop...\n", 22); +#endif + + /* Main loop - blink LED */ + while (1) { + led_toggle(); + delay_ms(500); /* Toggle every 500ms */ + } +} + +#endif /* TARGET_s32k1xx */ + From b7292291ee3dcf9076fbd70e5e47ca676f3bfc1d Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 8 Jan 2026 12:50:43 -0800 Subject: [PATCH 2/4] Testing fixes and improvements to test-app --- .gitignore | 1 + Makefile | 7 +- config/examples/nxp-s32k142.config | 5 +- docs/Targets.md | 2 +- hal/hal.c | 1 - hal/s32k1xx.c | 109 +++- hal/s32k1xx.h | 78 +++ src/boot_arm.c | 5 + src/update_flash.c | 6 + test-app/ARM-s32k1xx.ld | 8 +- test-app/Makefile | 5 + test-app/app_s32k1xx.c | 794 +++++++++++++++++++++++++---- test-app/startup_arm.c | 8 +- tools/scripts/nxp-s32k142-flash.sh | 225 ++++++++ 14 files changed, 1138 insertions(+), 116 deletions(-) create mode 100755 tools/scripts/nxp-s32k142-flash.sh 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 index 11867963d9..a297754744 100644 --- a/config/examples/nxp-s32k142.config +++ b/config/examples/nxp-s32k142.config @@ -11,7 +11,7 @@ CORTEX_M4?=1 TARGET?=s32k1xx SIGN?=ECC256 HASH?=SHA256 -DEBUG?=0 +DEBUG?=1 VTOR?=1 NO_ASM?=0 EXT_FLASH?=0 @@ -21,7 +21,7 @@ NVM_FLASH_WRITEONCE?=0 WOLFBOOT_VERSION?=0 V?=0 SPMATH?=1 -RAM_CODE?=0 +RAM_CODE?=1 DUALBANK_SWAP?=0 # Disable MPU (S32K1xx MPU configuration needs customization) @@ -49,4 +49,3 @@ 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 - diff --git a/docs/Targets.md b/docs/Targets.md index c0ec7e55cb..56bd4b4b09 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -2914,7 +2914,7 @@ pegdbserver_console -device=NXP_S32K1xx_S32K142 -startserver -serverport=7224 3. In another terminal, connect with GDB and flash: ```sh -arm-none-eabi-gdb wolfboot.elf +arm-none-eabi-gdb --nx wolfboot.elf target remote :7224 monitor reset halt load 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 index e2dbaeff54..9adca4a9ba 100644 --- a/hal/s32k1xx.c +++ b/hal/s32k1xx.c @@ -339,6 +339,55 @@ void uart_write(const char* buf, unsigned int sz) while (!(LPUART1_STAT & LPUART_STAT_TC)) {} } +/* 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) +{ + 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 */ + return -1; + } + + /* Check if data available */ + if (stat & LPUART_STAT_RDRF) { + *c = (char)(LPUART1_DATA & 0xFF); + return 1; + } + + return 0; /* No data available */ +} + +/* Blocking read with timeout (in ms) + * Returns: number of characters read, or -1 on error/timeout + */ +int uart_read_timeout(char* buf, int len, uint32_t timeout_ms) +{ + int count = 0; + int ret; + + /* Simple polling - timeout not implemented in HAL (no systick access) */ + (void)timeout_ms; + + while (count < len) { + ret = uart_read(&buf[count]); + if (ret > 0) { + count++; + } else if (ret < 0) { + return -1; /* Error */ + } + /* If no timeout support, just return what we have */ + if (ret == 0 && count > 0) { + break; + } + } + + return count; +} + #endif /* DEBUG_UART */ /* ============== Flash Functions ============== */ @@ -404,24 +453,65 @@ static int RAMFUNCTION flash_program_phrase(uint32_t address, const uint8_t *dat static int RAMFUNCTION flash_erase_sector_internal(uint32_t address) { + uint32_t primask; + +#ifdef DEBUG_FLASH + /* Debug: Print flash status before erase */ + uart_write(" FSTAT=", 8); + { + uint8_t fstat = FTFC_FSTAT; + char hex[3]; + hex[0] = "0123456789ABCDEF"[(fstat >> 4) & 0xF]; + hex[1] = "0123456789ABCDEF"[fstat & 0xF]; + hex[2] = ' '; + uart_write(hex, 3); + } +#endif + /* Wait for previous command to complete */ flash_wait_complete(); flash_clear_errors(); +#ifdef DEBUG_FLASH + uart_write("rdy ", 4); +#endif + /* 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); +#ifdef DEBUG_FLASH + uart_write("cmd ", 4); +#endif + /* Launch command */ DSB(); ISB(); + +#ifdef DEBUG_FLASH + uart_write("go\n", 3); + /* Wait for UART to transmit before flash operation */ + while (!(LPUART1_STAT & LPUART_STAT_TC)) {} +#endif + + /* 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 DEBUG_FLASH + uart_write("done\n", 5); +#endif + #ifdef WATCHDOG /* Refresh watchdog after potentially long flash operation */ watchdog_refresh(); @@ -454,10 +544,6 @@ void hal_init(void) #endif #ifdef WATCHDOG - /* Re-enable watchdog with configured timeout */ -#ifndef WATCHDOG_TIMEOUT_MS -#define WATCHDOG_TIMEOUT_MS 1000 /* Default 1 second timeout */ -#endif watchdog_enable(WATCHDOG_TIMEOUT_MS); #endif } @@ -500,13 +586,6 @@ void hal_prepare_boot(void) #endif } -/* 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 - int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) { int ret = 0; @@ -622,7 +701,13 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) void RAMFUNCTION hal_flash_unlock(void) { - /* Flash is always accessible on S32K1xx after reset */ + /* 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) diff --git a/hal/s32k1xx.h b/hal/s32k1xx.h index d3818ac01b..336d3c06d2 100644 --- a/hal/s32k1xx.h +++ b/hal/s32k1xx.h @@ -42,6 +42,38 @@ * 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 + /* ============== System Control Registers ============== */ /* SCG - System Clock Generator */ @@ -124,13 +156,40 @@ /* ============== GPIO / Port Registers ============== */ +/* PCC for GPIO ports */ +#define PCC_PORTD (*(volatile uint32_t *)(PCC_BASE + 0x130UL)) + +/* Port C - UART pins */ #define PORTC_BASE (0x4004B000UL) #define PORTC_PCR6 (*(volatile uint32_t *)(PORTC_BASE + 0x018UL)) #define PORTC_PCR7 (*(volatile uint32_t *)(PORTC_BASE + 0x01CUL)) +/* Port D - LED pins on S32K142EVB */ +#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_PCR15 (*(volatile uint32_t *)(PORTD_BASE + 0x03CUL)) /* Red LED */ +#define PORTD_PCR16 (*(volatile uint32_t *)(PORTD_BASE + 0x040UL)) /* Green LED */ + +/* 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_GPIO (1UL << PORT_PCR_MUX_SHIFT) /* GPIO mode */ #define PORT_PCR_MUX_ALT2 (2UL << PORT_PCR_MUX_SHIFT) /* LPUART1 */ +/* 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 ============== */ #define LPUART1_BASE (0x4006B000UL) @@ -146,8 +205,14 @@ #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 */ /* ============== Flash (FTFC) Registers ============== */ @@ -219,6 +284,13 @@ /* 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 */ @@ -279,5 +351,11 @@ #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 + #endif /* S32K1XX_H */ 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..a6999a556a 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -1278,6 +1278,12 @@ void RAMFUNCTION wolfBoot_start(void) #elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) (void)hal_hsm_server_cleanup(); #endif + + /* Debug: Print boot address and vector table info */ + wolfBoot_printf("do_boot: fw_base = 0x%lx\n", (uint32_t)boot.fw_base); + wolfBoot_printf("do_boot: stack = 0x%lx, reset = 0x%lx\n", + *((uint32_t*)boot.fw_base), *((uint32_t*)boot.fw_base + 1)); + hal_prepare_boot(); do_boot((void *)boot.fw_base); diff --git a/test-app/ARM-s32k1xx.ld b/test-app/ARM-s32k1xx.ld index 4a0f404a5c..4ee8d4014c 100644 --- a/test-app/ARM-s32k1xx.ld +++ b/test-app/ARM-s32k1xx.ld @@ -2,12 +2,18 @@ * 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 = 0x7000 /* 28KB upper SRAM */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x3000 /* 12KB upper SRAM */ } SECTIONS diff --git a/test-app/Makefile b/test-app/Makefile index 02c6397c4e..38a7e5ce6f 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 @@ -350,6 +351,10 @@ endif ifeq ($(TARGET),s32k1xx) LSCRIPT_TEMPLATE=ARM-s32k1xx.ld + APP_OBJS+=../src/keystore.o + CFLAGS+=-DAPP_HAS_SYSTICK + # To allow RAMCODE to be support in test-app + CFLAGS+=-D__WOLFBOOT endif ifeq ($(TARGET),mcxw) diff --git a/test-app/app_s32k1xx.c b/test-app/app_s32k1xx.c index da30b8764e..d1fde92ac6 100644 --- a/test-app/app_s32k1xx.c +++ b/test-app/app_s32k1xx.c @@ -1,6 +1,11 @@ /* 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. * @@ -25,59 +30,29 @@ #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 -/* System Control Block */ -#define AIRCR (*(volatile uint32_t *)(0xE000ED0C)) -#define AIRCR_VKEY (0x05FA << 16) -#define AIRCR_SYSRESETREQ (1 << 2) - -/* SysTick Timer */ -#define SYST_CSR (*(volatile uint32_t *)(0xE000E010)) -#define SYST_RVR (*(volatile uint32_t *)(0xE000E014)) -#define SYST_CVR (*(volatile uint32_t *)(0xE000E018)) -#define SYST_CALIB (*(volatile uint32_t *)(0xE000E01C)) - -#define SYST_CSR_ENABLE (1 << 0) -#define SYST_CSR_TICKINT (1 << 1) -#define SYST_CSR_CLKSOURCE (1 << 2) -#define SYST_CSR_COUNTFLAG (1 << 16) - -/* GPIO for LED (example: PTD15 on S32K142 EVB) */ -#define PCC_BASE (0x40065000UL) -#define PCC_PORTD (*(volatile uint32_t *)(PCC_BASE + 0x130UL)) -#define PCC_CGC (1UL << 30) - -#define PORTD_BASE (0x4004C000UL) -#define PORTD_PCR15 (*(volatile uint32_t *)(PORTD_BASE + 0x03CUL)) -#define PORT_PCR_MUX_GPIO (1UL << 8) - -#define GPIOD_BASE (0x400FF0C0UL) -#define GPIOD_PDOR (*(volatile uint32_t *)(GPIOD_BASE + 0x00UL)) -#define GPIOD_PSOR (*(volatile uint32_t *)(GPIOD_BASE + 0x04UL)) -#define GPIOD_PCOR (*(volatile uint32_t *)(GPIOD_BASE + 0x08UL)) -#define GPIOD_PTOR (*(volatile uint32_t *)(GPIOD_BASE + 0x0CUL)) -#define GPIOD_PDIR (*(volatile uint32_t *)(GPIOD_BASE + 0x10UL)) -#define GPIOD_PDDR (*(volatile uint32_t *)(GPIOD_BASE + 0x14UL)) - -#define LED_PIN 15 - -/* Clock speed (FIRC = 48 MHz) */ -#ifndef CLOCK_SPEED -#define CLOCK_SPEED 48000000UL -#endif +/* ============== SysTick Timer ============== */ -/* Simple delay counter */ static volatile uint32_t systick_count = 0; -void SysTick_Handler(void) +/* SysTick interrupt handler - called isr_systick to match startup_arm.c */ +void isr_systick(void) { systick_count++; } +static uint32_t get_time_ms(void) +{ + return systick_count; +} + static void delay_ms(uint32_t ms) { uint32_t start = systick_count; @@ -94,119 +69,752 @@ static void systick_init(void) 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 PTD15 as GPIO */ - PORTD_PCR15 = PORT_PCR_MUX_GPIO; + /* 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); +} - /* Set PTD15 as output */ - GPIOD_PDDR |= (1UL << LED_PIN); +static void led_blue_on(void) +{ + GPIOD_PCOR = (1UL << LED_PIN_BLUE); /* Active low */ +} - /* LED off initially (active low on most boards) */ - GPIOD_PSOR = (1UL << LED_PIN); +static void led_blue_off(void) +{ + GPIOD_PSOR = (1UL << LED_PIN_BLUE); } -static void led_on(void) +static void led_red_on(void) { - GPIOD_PCOR = (1UL << LED_PIN); /* Active low */ + GPIOD_PCOR = (1UL << LED_PIN_RED); /* Active low */ } -static void led_off(void) +static void led_red_off(void) { - GPIOD_PSOR = (1UL << LED_PIN); + 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); + } } -static void led_toggle(void) +/* Set LED based on version: Green for v1, Blue for v>1 */ +static void led_set_version(uint32_t version) { - GPIOD_PTOR = (1UL << LED_PIN); + /* 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) { - AIRCR = AIRCR_VKEY | AIRCR_SYSRESETREQ; + SCB_AIRCR = AIRCR_VECTKEY | AIRCR_SYSRESETREQ; while (1) { __asm__ volatile ("wfi"); } } +/* ============== UART / Printf Support ============== */ + #ifdef DEBUG_UART extern void uart_init(void); extern void uart_write(const char* buf, unsigned int sz); +extern int uart_read(char* c); + +/* Simple printf-like function using wolfBoot_printf format */ +#include + +static void uart_putc(char c) +{ + uart_write(&c, 1); +} + +static void uart_puts(const char* s) +{ + while (*s) { + uart_putc(*s++); + } +} -static void print_version(uint32_t version) +static void print_hex_byte(uint8_t b) { - char buf[16]; + const char hex[] = "0123456789ABCDEF"; + uart_putc(hex[(b >> 4) & 0x0F]); + uart_putc(hex[b & 0x0F]); +} + +static void print_hex32(uint32_t val) +{ + uart_puts("0x"); + print_hex_byte((val >> 24) & 0xFF); + print_hex_byte((val >> 16) & 0xFF); + print_hex_byte((val >> 8) & 0xFF); + print_hex_byte(val & 0xFF); +} + +static void print_dec(uint32_t val) +{ + char buf[12]; int i = 0; - uart_write("Version: ", 9); + if (val == 0) { + uart_putc('0'); + return; + } + + while (val > 0) { + buf[i++] = '0' + (val % 10); + val /= 10; + } + + while (i > 0) { + uart_putc(buf[--i]); + } +} + +/* Non-blocking character read */ +static int uart_getc(char* c) +{ + return uart_read(c); +} + +/* Blocking character read */ +static char uart_getc_blocking(void) +{ + char c; + while (uart_read(&c) <= 0) { + __asm__ volatile ("wfi"); + } + return c; +} +#endif /* DEBUG_UART */ + +/* ============== Partition State Names ============== */ + +static const char* part_state_name(uint8_t state, int valid) +{ + if (!valid) { + 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 ============== */ - /* Simple number to string */ - if (version == 0) { - uart_write("0", 1); +static const char* key_type_name(uint32_t type) +{ + switch (type) { + case AUTH_KEY_ED25519: return "Ed25519"; + case AUTH_KEY_ECC256: return "ECDSA P-256 (secp256r1)"; + case AUTH_KEY_RSA2048: return "RSA-2048"; + case AUTH_KEY_RSA4096: return "RSA-4096"; + case AUTH_KEY_ED448: return "Ed448"; + case AUTH_KEY_ECC384: return "ECDSA P-384 (secp384r1)"; + case AUTH_KEY_ECC521: return "ECDSA P-521 (secp521r1)"; + case AUTH_KEY_RSA3072: return "RSA-3072"; + case AUTH_KEY_LMS: return "LMS"; + case AUTH_KEY_XMSS: return "XMSS"; + default: 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) == 0); + update_state_valid = (wolfBoot_get_partition_state(PART_UPDATE, &update_state) == 0); + + uart_puts("\n=== Partition Information ===\n"); + + uart_puts("Boot Partition:\n"); + uart_puts(" Address: "); + print_hex32(WOLFBOOT_PARTITION_BOOT_ADDRESS); + uart_puts("\n"); + uart_puts(" Version: "); + print_dec(boot_ver); + uart_puts("\n"); + uart_puts(" State: "); + uart_puts(part_state_name(boot_state, boot_state_valid)); + uart_puts("\n"); + + uart_puts("Update Partition:\n"); + uart_puts(" Address: "); + print_hex32(WOLFBOOT_PARTITION_UPDATE_ADDRESS); + uart_puts("\n"); + uart_puts(" Version: "); + if (update_ver == 0) { + uart_puts("(empty)"); } else { - char tmp[10]; - int j = 0; - while (version > 0) { - tmp[j++] = '0' + (version % 10); - version /= 10; + print_dec(update_ver); + } + uart_puts("\n"); + uart_puts(" State: "); + uart_puts(part_state_name(update_state, update_state_valid)); + uart_puts("\n"); + + uart_puts("Swap Partition:\n"); + uart_puts(" Address: "); + print_hex32(WOLFBOOT_PARTITION_SWAP_ADDRESS); + uart_puts("\n"); + uart_puts(" Size: "); + print_dec(WOLFBOOT_SECTOR_SIZE); + uart_puts(" bytes\n"); +} + +static void print_keystore_info(void) +{ + uint32_t n_keys; + int i; + + uart_puts("\n=== Keystore Information ===\n"); + + /* Show configured signature and hash algorithms */ +#ifdef WOLFBOOT_SIGN_ECC256 + uart_puts("Signature: ECDSA P-256 (secp256r1)\n"); +#elif defined(WOLFBOOT_SIGN_ECC384) + uart_puts("Signature: ECDSA P-384 (secp384r1)\n"); +#elif defined(WOLFBOOT_SIGN_ECC521) + uart_puts("Signature: ECDSA P-521 (secp521r1)\n"); +#elif defined(WOLFBOOT_SIGN_ED25519) + uart_puts("Signature: Ed25519\n"); +#elif defined(WOLFBOOT_SIGN_ED448) + uart_puts("Signature: Ed448\n"); +#elif defined(WOLFBOOT_SIGN_RSA2048) + uart_puts("Signature: RSA-2048\n"); +#elif defined(WOLFBOOT_SIGN_RSA3072) + uart_puts("Signature: RSA-3072\n"); +#elif defined(WOLFBOOT_SIGN_RSA4096) + uart_puts("Signature: RSA-4096\n"); +#elif defined(WOLFBOOT_SIGN_LMS) + uart_puts("Signature: LMS\n"); +#elif defined(WOLFBOOT_SIGN_XMSS) + uart_puts("Signature: XMSS\n"); +#else + uart_puts("Signature: Unknown\n"); +#endif + +#ifdef WOLFBOOT_HASH_SHA256 + uart_puts("Hash: SHA-256\n"); +#elif defined(WOLFBOOT_HASH_SHA384) + uart_puts("Hash: SHA-384\n"); +#elif defined(WOLFBOOT_HASH_SHA512) + uart_puts("Hash: SHA-512\n"); +#elif defined(WOLFBOOT_HASH_SHA3_384) + uart_puts("Hash: SHA3-384\n"); +#else + uart_puts("Hash: Unknown\n"); +#endif + + n_keys = keystore_num_pubkeys(); + uart_puts("Number of public keys: "); + print_dec(n_keys); + uart_puts("\n"); + + 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); + int j; + + uart_puts("\nKey #"); + print_dec(i); + uart_puts(":\n"); + uart_puts(" Algorithm: "); + uart_puts(key_type_name(type)); + uart_puts("\n"); + uart_puts(" Size: "); + print_dec(size); + uart_puts(" bytes\n"); + uart_puts(" Data: "); + + /* Print first 16 bytes of key */ + for (j = 0; j < 16 && j < (int)size; j++) { + print_hex_byte(keybuf[j]); + uart_putc(' '); } - while (j > 0) { - buf[i++] = tmp[--j]; + if (size > 16) { + uart_puts("..."); } - uart_write(buf, i); + uart_puts("\n"); } - uart_write("\n", 1); } + +/* ============== XMODEM Transfer ============== */ + +#define XSOH 0x01 +#define XEOT 0x04 +#define XACK 0x06 +#define XNAK 0x15 +#define XCAN 0x18 + +#define XMODEM_PAYLOAD_SIZE 128 +#define XMODEM_PACKET_SIZE (3 + XMODEM_PAYLOAD_SIZE + 1) +#define XMODEM_TIMEOUT_MS 1000 + +static uint8_t crc8_checksum(uint8_t* data, int len) +{ + uint8_t sum = 0; + int i; + for (i = 0; i < len; i++) { + sum += data[i]; + } + return sum; +} + +static void xmodem_cancel(void) +{ + int i; + for (i = 0; i < 10; i++) { + uart_putc(XCAN); + } +} + +static int cmd_update_xmodem(void) +{ + int ret = -1; + uint8_t xpkt[XMODEM_PACKET_SIZE]; + uint8_t payload[XMODEM_PAYLOAD_SIZE]; + uint32_t dst_offset = 0; + 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; + uint32_t erase_addr; + int erase_ret; + + uart_puts("Erasing update partition...\n"); +#ifdef DEBUG_FLASH + uart_puts(" Address: "); + print_hex32(WOLFBOOT_PARTITION_UPDATE_ADDRESS); + uart_puts("\n Size: "); + print_hex32(WOLFBOOT_PARTITION_SIZE); + uart_puts(" ("); + print_dec(WOLFBOOT_PARTITION_SIZE); + uart_puts(" bytes)\n"); #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) { + uart_puts(" Erasing sector at "); + print_hex32(erase_addr); + uart_puts("..."); + + erase_ret = hal_flash_erase(erase_addr, WOLFBOOT_SECTOR_SIZE); + if (erase_ret != 0) { + uart_puts(" FAILED ("); + print_dec(erase_ret); + uart_puts(")\n"); + hal_flash_lock(); + return -1; + } + uart_puts(" OK\n"); + erase_addr += WOLFBOOT_SECTOR_SIZE; + } +#else + (void)erase_addr; + (void)erase_ret; + hal_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_PARTITION_SIZE); +#endif + + uart_puts("Done.\n"); + + uart_puts("Waiting for XMODEM transfer...\n"); + uart_puts("(Send file now using XMODEM protocol)\n"); + + while (1) { + now = get_time_ms(); + i = 0; + + /* Receive packet */ + while (i < XMODEM_PACKET_SIZE) { + char c; + int r = uart_getc(&c); + if (r > 0) { + xpkt[i++] = (uint8_t)c; + now = get_time_ms(); + if (i == 1 && xpkt[0] == XEOT) { + break; /* End of transmission */ + } + } else { + if (get_time_ms() > (now + XMODEM_TIMEOUT_MS)) { + now = get_time_ms(); + if (i == 0) { + uart_putc(XNAK); /* Request retransmit */ + } + i = 0; + } else { + __asm__ volatile ("wfi"); + } + } + } + + /* Check for EOT */ + if (xpkt[0] == XEOT) { + uart_putc(XACK); + led_red_on(); /* Indicate transfer complete */ + ret = 0; + break; + } else if (eot_expected) { + uart_putc(XNAK); + ret = -1; + break; + } + + /* Validate SOH */ + if (xpkt[0] != XSOH) { + continue; + } + + /* Validate packet number */ + pkt_num = xpkt[1]; + if ((uint8_t)(~xpkt[2]) == pkt_num) { + if (!transfer_started) { + pkt_num_expected = pkt_num; + transfer_started = 1; + } else if (pkt_num_expected != pkt_num) { + uart_putc(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 checksum */ + uint8_t crc = xpkt[XMODEM_PACKET_SIZE - 1]; + uint8_t calc_crc = crc8_checksum(xpkt, XMODEM_PACKET_SIZE - 1); + + if (crc == calc_crc) { + /* Write to flash */ + memcpy(payload, xpkt + 3, XMODEM_PAYLOAD_SIZE); + ret = hal_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + dst_offset, + payload, XMODEM_PAYLOAD_SIZE); + if (ret != 0) { + xmodem_cancel(); + uart_puts("Error: Flash write failed\n"); + break; + } + + uart_putc(XACK); + pkt_num_expected++; + dst_offset += XMODEM_PAYLOAD_SIZE; + + /* Get expected size from header */ + if (t_size == 0 && dst_offset >= 8) { + t_size = *(uint32_t*)(payload + 4) + IMAGE_HEADER_SIZE; + } + + if (t_size > 0 && dst_offset >= t_size) { + eot_expected = 1; + } + } else { + uart_putc(XNAK); + } + } else { + uart_putc(XNAK); + } + } + + hal_flash_lock(); + + uart_puts("\nTransfer "); + if (ret == 0) { + uart_puts("complete!\n"); + + uint32_t update_ver = wolfBoot_update_firmware_version(); + if (update_ver != 0) { + uart_puts("New firmware version: "); + print_dec(update_ver); + uart_puts("\n"); + uart_puts("Triggering update...\n"); + wolfBoot_update_trigger(); + uart_puts("Reboot to apply update.\n"); + } else { + uart_puts("Warning: No valid image detected\n"); + } + } else { + uart_puts("failed.\n"); + } + + led_red_off(); + return ret; +} + +/* ============== Console Commands ============== */ + +static void cmd_help(void); +static void cmd_info(void); +static void cmd_success(void); +static void cmd_reboot(void); +static void cmd_update(void); + +typedef struct { + const char* name; + const char* help; + void (*fn)(void); +} console_cmd_t; + +static const console_cmd_t commands[] = { + {"help", "Show this help message", cmd_help}, + {"info", "Display partition and key info", cmd_info}, + {"success", "Mark firmware as successful", cmd_success}, + {"update", "Update firmware via XMODEM", cmd_update}, + {"reboot", "Reboot the system", cmd_reboot}, + {NULL, NULL, NULL} +}; + +static void cmd_help(void) +{ + int i; + uart_puts("\nAvailable commands:\n"); + for (i = 0; commands[i].name != NULL; i++) { + uart_puts(" "); + uart_puts(commands[i].name); + uart_puts(" - "); + uart_puts(commands[i].help); + uart_puts("\n"); + } +} + +static void cmd_info(void) +{ + print_partition_info(); + print_keystore_info(); +} + +static void cmd_success(void) +{ + wolfBoot_success(); + uart_puts("Firmware marked as successful.\n"); +} + +static void cmd_reboot(void) +{ + uart_puts("Rebooting...\n"); + delay_ms(100); /* Allow UART to flush */ + arch_reboot(); +} + +static void cmd_update(void) +{ + cmd_update_xmodem(); +} + +static void parse_command(const char* cmd) +{ + int i; + for (i = 0; commands[i].name != NULL; i++) { + if (strcmp(cmd, commands[i].name) == 0) { + commands[i].fn(); + return; + } + } + uart_puts("Unknown command: "); + uart_puts(cmd); + uart_puts("\nType 'help' for available commands.\n"); +} + +#define CMD_BUF_SIZE 64 + +static void console_loop(uint32_t version) +{ + char cmd[CMD_BUF_SIZE]; + int idx; + char c; + + (void)version; /* LED is set once at startup, no toggling */ + + while (1) { + uart_puts("\ncmd> "); + idx = 0; + + while (idx < CMD_BUF_SIZE - 1) { + int ret = uart_getc(&c); + if (ret > 0) { + if (c == '\r' || c == '\n') { + uart_puts("\n"); + break; + } else if (c == 0x08 || c == 0x7F) { /* Backspace */ + if (idx > 0) { + uart_puts("\b \b"); + idx--; + } + } else if (c >= 32 && c < 127) { + uart_putc(c); + 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; - int i; - /* Initialize hardware */ - hal_init(); + /* 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(); + + /* Simple test message */ + uart_puts("Test App Started!\n"); +#endif + + /* Initialize test-app hardware */ systick_init(); led_init(); /* Enable interrupts */ __asm__ volatile ("cpsie i"); -#ifdef DEBUG_UART - uart_write("wolfBoot S32K1xx Test App\n", 26); -#endif - /* Get current firmware version */ version = wolfBoot_current_firmware_version(); -#ifdef DEBUG_UART - print_version(version); -#endif + /* Set LED based on version: Green for v1, Blue for v>1 */ + led_set_version(version); - /* Mark firmware as successful if version is even */ - if ((version & 0x01) == 0) { - wolfBoot_success(); #ifdef DEBUG_UART - uart_write("Firmware marked successful\n", 27); -#endif + uart_puts("\n"); + uart_puts("========================================\n"); + uart_puts("S32K1xx wolfBoot Test Application\n"); + uart_puts("Copyright 2025 wolfSSL Inc.\n"); + uart_puts("========================================\n"); + uart_puts("Firmware Version: "); + print_dec(version); + uart_puts("\n"); + + /* 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) { + uart_puts("Testing state detected, marking success...\n"); + wolfBoot_success(); + } } - /* Blink LED to show we're running */ - led_on(); + /* Show initial info */ + print_partition_info(); -#ifdef DEBUG_UART - uart_write("Entering main loop...\n", 22); -#endif + uart_puts("\nType 'help' for available commands.\n"); - /* Main loop - blink LED */ + /* Enter interactive console */ + console_loop(version); +#else + /* No UART - just blink LED */ while (1) { - led_toggle(); - delay_ms(500); /* Toggle every 500ms */ + led_toggle_version(version); + delay_ms(500); } +#endif } #endif /* TARGET_s32k1xx */ - diff --git a/test-app/startup_arm.c b/test-app/startup_arm.c index d6a76c6a1b..ac41d0d187 100644 --- a/test-app/startup_arm.c +++ b/test-app/startup_arm.c @@ -91,26 +91,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) ; } 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}" From ac071564b12e4b7184f101d1c4572dc17a8c1ff6 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 9 Jan 2026 11:58:57 -0800 Subject: [PATCH 3/4] Cleanups for test app and remove debugging. --- config/examples/nxp-s32k142.config | 9 +- config/examples/nxp-s32k144.config | 10 +- config/examples/nxp-s32k146.config | 10 +- config/examples/nxp-s32k148.config | 10 +- docs/Targets.md | 94 ++++- hal/s32k1xx.c | 167 ++------- hal/s32k1xx.h | 277 ++++++++++++++- src/update_flash.c | 5 - test-app/Makefile | 3 +- test-app/app_s32k1xx.c | 545 +++++++++++++++-------------- test-app/startup_arm.c | 16 +- 11 files changed, 682 insertions(+), 464 deletions(-) diff --git a/config/examples/nxp-s32k142.config b/config/examples/nxp-s32k142.config index a297754744..edb4808b73 100644 --- a/config/examples/nxp-s32k142.config +++ b/config/examples/nxp-s32k142.config @@ -11,7 +11,7 @@ CORTEX_M4?=1 TARGET?=s32k1xx SIGN?=ECC256 HASH?=SHA256 -DEBUG?=1 +DEBUG?=0 VTOR?=1 NO_ASM?=0 EXT_FLASH?=0 @@ -28,10 +28,10 @@ DUALBANK_SWAP?=0 WOLFBOOT_NO_MPU?=1 # Enable hardfault debugging -DEBUG_HARDFAULT?=1 +DEBUG_HARDFAULT?=0 # Debug UART on LPUART1 (PTC6=RX, PTC7=TX) -DEBUG_UART?=1 +DEBUG_UART?=0 # 2KB sectors (S32K142 only - larger variants use 4KB sectors) WOLFBOOT_SECTOR_SIZE?=0x800 @@ -49,3 +49,6 @@ 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 index 78f4bccc4a..1655a28331 100644 --- a/config/examples/nxp-s32k144.config +++ b/config/examples/nxp-s32k144.config @@ -21,7 +21,7 @@ NVM_FLASH_WRITEONCE?=0 WOLFBOOT_VERSION?=0 V?=0 SPMATH?=1 -RAM_CODE?=0 +RAM_CODE?=1 DUALBANK_SWAP?=0 # Select S32K144 variant for correct flash size and sector size @@ -31,10 +31,10 @@ CFLAGS_EXTRA+=-DS32K144 WOLFBOOT_NO_MPU?=1 # Enable hardfault debugging -DEBUG_HARDFAULT?=1 +#CFLAGS_EXTRA+=-DDEBUG_HARDFAULT # Debug UART on LPUART1 (PTC6=RX, PTC7=TX) -DEBUG_UART?=1 +DEBUG_UART?=0 # 4KB sectors (S32K144/146/148 with 512KB+ flash) WOLFBOOT_SECTOR_SIZE?=0x1000 @@ -53,5 +53,5 @@ WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x7C000 # 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 index 8f3e366d2b..78f048688f 100644 --- a/config/examples/nxp-s32k146.config +++ b/config/examples/nxp-s32k146.config @@ -21,7 +21,7 @@ NVM_FLASH_WRITEONCE?=0 WOLFBOOT_VERSION?=0 V?=0 SPMATH?=1 -RAM_CODE?=0 +RAM_CODE?=1 DUALBANK_SWAP?=0 # Select S32K146 variant for correct flash size and sector size @@ -31,10 +31,10 @@ CFLAGS_EXTRA+=-DS32K146 WOLFBOOT_NO_MPU?=1 # Enable hardfault debugging -DEBUG_HARDFAULT?=1 +#CFLAGS_EXTRA+=-DDEBUG_HARDFAULT # Debug UART on LPUART1 (PTC6=RX, PTC7=TX) -DEBUG_UART?=1 +DEBUG_UART?=0 # 4KB sectors (S32K144/146/148 with 512KB+ flash) WOLFBOOT_SECTOR_SIZE?=0x1000 @@ -53,5 +53,5 @@ WOLFBOOT_PARTITION_SWAP_ADDRESS?=0xF4000 # 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 index 9af244b09e..b44a3b1e0e 100644 --- a/config/examples/nxp-s32k148.config +++ b/config/examples/nxp-s32k148.config @@ -21,7 +21,7 @@ NVM_FLASH_WRITEONCE?=0 WOLFBOOT_VERSION?=0 V?=0 SPMATH?=1 -RAM_CODE?=0 +RAM_CODE?=1 DUALBANK_SWAP?=0 # Select S32K148 variant for correct flash size and sector size @@ -31,10 +31,10 @@ CFLAGS_EXTRA+=-DS32K148 WOLFBOOT_NO_MPU?=1 # Enable hardfault debugging -DEBUG_HARDFAULT?=1 +#CFLAGS_EXTRA+=-DDEBUG_HARDFAULT # Debug UART on LPUART1 (PTC6=RX, PTC7=TX) -DEBUG_UART?=1 +DEBUG_UART?=0 # 4KB sectors (S32K144/146/148 with 512KB+ flash) WOLFBOOT_SECTOR_SIZE?=0x1000 @@ -53,5 +53,5 @@ WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x1EC000 # 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 56bd4b4b09..c8c90d45be 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -2856,10 +2856,14 @@ The default memory layout for S32K142 (256KB Flash): ### NXP S32K1XX: Configuration -Example configuration file: [/config/examples/nxp-s32k142.config](/config/examples/nxp-s32k142.config) +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 +# Copy configuration (example for S32K142) cp config/examples/nxp-s32k142.config .config # Build wolfBoot @@ -2870,7 +2874,6 @@ make make test-app/image.bin ``` - ### NXP S32K1XX: Configuration Options The following build options are available for the S32K1xx HAL: @@ -2891,6 +2894,8 @@ The following build options are available for the S32K1xx HAL: ### 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 @@ -2918,26 +2923,83 @@ arm-none-eabi-gdb --nx wolfboot.elf target remote :7224 monitor reset halt load -# Flash the signed application to boot partition -monitor programbin test-app/image_v1_signed.bin 0xc000 monitor reset run ``` -### NXP S32K1XX: Debugging Tips +### 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: -When debugging with GDB: ```sh -arm-none-eabi-gdb -target remote :7224 -add-symbol-file wolfboot.elf 0x0 -add-symbol-file test-app/image.elf 0xC100 -set mem inaccessible-by-default off -monitor reset halt -b main -c +make factory.srec ``` -For UART debug output, connect a USB-to-serial adapter to LPUART1 pins (PTC6=RX, PTC7=TX) and open a terminal at 115200 baud. +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) diff --git a/hal/s32k1xx.c b/hal/s32k1xx.c index 9adca4a9ba..b688457d79 100644 --- a/hal/s32k1xx.c +++ b/hal/s32k1xx.c @@ -26,6 +26,7 @@ #include #include "image.h" #include "hal.h" +#include "printf.h" /* Assembly helpers */ #define DMB() __asm__ volatile ("dmb") @@ -181,55 +182,14 @@ static void clock_init_firc(void) static void clock_init_spll(void) { - /* Disable SPLL before configuration */ - SCG_SPLLCSR &= ~SCG_SPLLCSR_SPLLEN; - - /* Configure SPLL: - * Using FIRC (48 MHz) as source - * For 112 MHz: PREDIV=0 (/1), MULT=12 (x28) - * VCO = 48 MHz * 28 = 1344 MHz (wait, that's too high) - * - * Actually S32K uses SOSC as SPLL source typically. - * With FIRC we need different approach. - * - * For HSRUN at 112 MHz from FIRC: - * Use FIRC directly for core at 48 MHz in RUN mode - * Then switch to SPLL for HSRUN - * - * SPLL: VCO range is 180-320 MHz - * SPLL_CLK = VCO / 2 - * - * If using 8 MHz SOSC: PREDIV=0, MULT=28 -> VCO=224 MHz, SPLL_CLK=112 MHz + /* 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 * - * For this bare-metal impl, we'll use FIRC at 48 MHz as a safe default, - * and configure SPLL with SOSC if available. - * - * For simplicity: Use FIRC 48MHz with appropriate dividers. - * HSRUN mode allows higher frequencies but requires SPLL. + * Currently using FIRC 48 MHz directly. TODO: Add SOSC + SPLL for 112 MHz. */ - - /* SPLL dividers */ + SCG_SPLLCSR &= ~SCG_SPLLCSR_SPLLEN; SCG_SPLLDIV = (2UL << 8) | (4UL << 0); /* SPLLDIV1=/2, SPLLDIV2=/4 */ - - /* SPLL configuration: MULT and PREDIV - * PREDIV: 0-7 -> divide by 1-8 - * MULT: 0-31 -> multiply by 16-47 - * VCO = (FIRC/PREDIV) * MULT, must be 180-320 MHz - * SPLL_CLK = VCO / 2 - * - * For 112 MHz with 48 MHz FIRC: - * Need VCO = 224 MHz - * 48 / 1 * 28 = 1344 MHz (too high, FIRC can't be SPLL source directly) - * - * S32K1xx SPLL source is SOSC only. For bare-metal without crystal, - * we run from FIRC at 48 MHz maximum. - * - * If SOSC 8 MHz is available: - * 8 / 1 * 28 = 224 MHz VCO, 112 MHz SPLL_CLK - */ - - /* For now, skip SPLL and use FIRC directly */ - /* TODO: Add SOSC + SPLL support for true 112 MHz operation */ } static void clock_init(void) @@ -288,19 +248,21 @@ void uart_init(void) uint32_t osr = 16; /* Oversampling ratio */ uint32_t uart_clock = 48000000UL; /* FIRC 48 MHz */ - /* Enable clock to PORTC */ - PCC_PORTC |= PCC_CGC; - - /* Configure pins for LPUART1: - * PTC6 = LPUART1_RX (ALT2) - * PTC7 = LPUART1_TX (ALT2) + /* Enable clock to TX and RX port(s) + * Note: If TX and RX use different ports, both need clock enabled */ - PORTC_PCR6 = PORT_PCR_MUX_ALT2; - PORTC_PCR7 = PORT_PCR_MUX_ALT2; + 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 LPUART1, source = FIRC (48 MHz) */ - PCC_LPUART1 = 0; /* Disable before changing source */ - PCC_LPUART1 = PCC_PCS_FIRC | PCC_CGC; + /* 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) @@ -308,14 +270,14 @@ void uart_init(void) sbr = uart_clock / (UART_BAUDRATE * osr); /* Disable TX/RX before configuration */ - LPUART1_CTRL = 0; + LPUART_CTRL = 0; /* Configure baud rate */ - LPUART1_BAUD = ((osr - 1) << LPUART_BAUD_OSR_SHIFT) | - (sbr << LPUART_BAUD_SBR_SHIFT); + LPUART_BAUD = ((osr - 1) << LPUART_BAUD_OSR_SHIFT) | + (sbr << LPUART_BAUD_SBR_SHIFT); /* Enable transmitter and receiver */ - LPUART1_CTRL = LPUART_CTRL_TE | LPUART_CTRL_RE; + LPUART_CTRL = LPUART_CTRL_TE | LPUART_CTRL_RE; } void uart_write(const char* buf, unsigned int sz) @@ -326,17 +288,17 @@ void uart_write(const char* buf, unsigned int sz) /* Handle newline -> CRLF conversion */ if (buf[i] == '\n') { /* Wait for transmit buffer empty */ - while (!(LPUART1_STAT & LPUART_STAT_TDRE)) {} - LPUART1_DATA = '\r'; + while (!(LPUART_STAT & LPUART_STAT_TDRE)) {} + LPUART_DATA = '\r'; } /* Wait for transmit buffer empty */ - while (!(LPUART1_STAT & LPUART_STAT_TDRE)) {} - LPUART1_DATA = buf[i]; + while (!(LPUART_STAT & LPUART_STAT_TDRE)) {} + LPUART_DATA = buf[i]; } /* Wait for transmission complete */ - while (!(LPUART1_STAT & LPUART_STAT_TC)) {} + while (!(LPUART_STAT & LPUART_STAT_TC)) {} } /* Read a single character from UART (non-blocking) @@ -344,50 +306,23 @@ void uart_write(const char* buf, unsigned int sz) */ int uart_read(char* c) { - uint32_t stat = LPUART1_STAT; + uint32_t stat = LPUART_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 */ + LPUART_STAT = stat; /* Write 1 to clear flags */ return -1; } /* Check if data available */ if (stat & LPUART_STAT_RDRF) { - *c = (char)(LPUART1_DATA & 0xFF); + *c = (char)(LPUART_DATA & 0xFF); return 1; } return 0; /* No data available */ } -/* Blocking read with timeout (in ms) - * Returns: number of characters read, or -1 on error/timeout - */ -int uart_read_timeout(char* buf, int len, uint32_t timeout_ms) -{ - int count = 0; - int ret; - - /* Simple polling - timeout not implemented in HAL (no systick access) */ - (void)timeout_ms; - - while (count < len) { - ret = uart_read(&buf[count]); - if (ret > 0) { - count++; - } else if (ret < 0) { - return -1; /* Error */ - } - /* If no timeout support, just return what we have */ - if (ret == 0 && count > 0) { - break; - } - } - - return count; -} - #endif /* DEBUG_UART */ /* ============== Flash Functions ============== */ @@ -455,47 +390,20 @@ static int RAMFUNCTION flash_erase_sector_internal(uint32_t address) { uint32_t primask; -#ifdef DEBUG_FLASH - /* Debug: Print flash status before erase */ - uart_write(" FSTAT=", 8); - { - uint8_t fstat = FTFC_FSTAT; - char hex[3]; - hex[0] = "0123456789ABCDEF"[(fstat >> 4) & 0xF]; - hex[1] = "0123456789ABCDEF"[fstat & 0xF]; - hex[2] = ' '; - uart_write(hex, 3); - } -#endif - /* Wait for previous command to complete */ flash_wait_complete(); flash_clear_errors(); -#ifdef DEBUG_FLASH - uart_write("rdy ", 4); -#endif - /* 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); -#ifdef DEBUG_FLASH - uart_write("cmd ", 4); -#endif - /* Launch command */ DSB(); ISB(); -#ifdef DEBUG_FLASH - uart_write("go\n", 3); - /* Wait for UART to transmit before flash operation */ - while (!(LPUART1_STAT & LPUART_STAT_TC)) {} -#endif - /* Disable interrupts during flash operation to prevent code fetch from flash */ __asm__ volatile ("mrs %0, primask\n\t" "cpsid i" : "=r" (primask) :: "memory"); @@ -508,10 +416,6 @@ static int RAMFUNCTION flash_erase_sector_internal(uint32_t address) /* Re-enable interrupts */ __asm__ volatile ("msr primask, %0" :: "r" (primask) : "memory"); -#ifdef DEBUG_FLASH - uart_write("done\n", 5); -#endif - #ifdef WATCHDOG /* Refresh watchdog after potentially long flash operation */ watchdog_refresh(); @@ -525,6 +429,7 @@ static int RAMFUNCTION flash_erase_sector_internal(uint32_t address) return 0; } + /* ============== HAL Interface Functions ============== */ void hal_init(void) @@ -540,7 +445,11 @@ void hal_init(void) #ifdef DEBUG_UART uart_init(); - uart_write("wolfBoot HAL Init\n", 18); + +#ifdef __WOLFBOOT + wolfBoot_printf("wolfBoot Version: %s (%s %s)\n", + LIBWOLFBOOT_VERSION_STRING, __DATE__, __TIME__); +#endif #endif #ifdef WATCHDOG @@ -552,7 +461,7 @@ void hal_prepare_boot(void) { #ifdef DEBUG_UART /* Wait for any pending UART transmission to complete */ - while (!(LPUART1_STAT & LPUART_STAT_TC)) {} + while (!(LPUART_STAT & LPUART_STAT_TC)) {} #endif #ifdef WOLFBOOT_RESTORE_CLOCK diff --git a/hal/s32k1xx.h b/hal/s32k1xx.h index 336d3c06d2..87d5b5923e 100644 --- a/hal/s32k1xx.h +++ b/hal/s32k1xx.h @@ -145,8 +145,12 @@ /* 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 */ @@ -158,19 +162,43 @@ /* PCC for GPIO ports */ #define PCC_PORTD (*(volatile uint32_t *)(PCC_BASE + 0x130UL)) - -/* Port C - UART pins */ +#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_PCR6 (*(volatile uint32_t *)(PORTC_BASE + 0x018UL)) -#define PORTC_PCR7 (*(volatile uint32_t *)(PORTC_BASE + 0x01CUL)) +#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 */ +/* 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 */ @@ -182,8 +210,14 @@ /* 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) /* LPUART1 */ +#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) */ @@ -192,15 +226,48 @@ /* ============== LPUART Registers ============== */ +/* LPUART base addresses */ +#define LPUART0_BASE (0x4006A000UL) #define LPUART1_BASE (0x4006B000UL) -#define LPUART1_VERID (*(volatile uint32_t *)(LPUART1_BASE + 0x000UL)) -#define LPUART1_PARAM (*(volatile uint32_t *)(LPUART1_BASE + 0x004UL)) -#define LPUART1_GLOBAL (*(volatile uint32_t *)(LPUART1_BASE + 0x008UL)) -#define LPUART1_BAUD (*(volatile uint32_t *)(LPUART1_BASE + 0x010UL)) -#define LPUART1_STAT (*(volatile uint32_t *)(LPUART1_BASE + 0x014UL)) -#define LPUART1_CTRL (*(volatile uint32_t *)(LPUART1_BASE + 0x018UL)) -#define LPUART1_DATA (*(volatile uint32_t *)(LPUART1_BASE + 0x01CUL)) - +#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 */ @@ -214,6 +281,169 @@ #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) @@ -357,5 +587,24 @@ #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/src/update_flash.c b/src/update_flash.c index a6999a556a..9f6353130c 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -1279,11 +1279,6 @@ void RAMFUNCTION wolfBoot_start(void) (void)hal_hsm_server_cleanup(); #endif - /* Debug: Print boot address and vector table info */ - wolfBoot_printf("do_boot: fw_base = 0x%lx\n", (uint32_t)boot.fw_base); - wolfBoot_printf("do_boot: stack = 0x%lx, reset = 0x%lx\n", - *((uint32_t*)boot.fw_base), *((uint32_t*)boot.fw_base + 1)); - hal_prepare_boot(); do_boot((void *)boot.fw_base); diff --git a/test-app/Makefile b/test-app/Makefile index 38a7e5ce6f..d2e39d218b 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -353,8 +353,7 @@ ifeq ($(TARGET),s32k1xx) LSCRIPT_TEMPLATE=ARM-s32k1xx.ld APP_OBJS+=../src/keystore.o CFLAGS+=-DAPP_HAS_SYSTICK - # To allow RAMCODE to be support in test-app - CFLAGS+=-D__WOLFBOOT + CFLAGS+=-DRAM_CODE -DDEBUG_UART endif ifeq ($(TARGET),mcxw) diff --git a/test-app/app_s32k1xx.c b/test-app/app_s32k1xx.c index d1fde92ac6..d6ed7cbdfb 100644 --- a/test-app/app_s32k1xx.c +++ b/test-app/app_s32k1xx.c @@ -26,9 +26,11 @@ * 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" @@ -40,23 +42,23 @@ /* ============== SysTick Timer ============== */ -static volatile uint32_t systick_count = 0; +static volatile uint32_t jiffies = 0; /* SysTick interrupt handler - called isr_systick to match startup_arm.c */ void isr_systick(void) { - systick_count++; + jiffies++; } static uint32_t get_time_ms(void) { - return systick_count; + return jiffies; } static void delay_ms(uint32_t ms) { - uint32_t start = systick_count; - while ((systick_count - start) < ms) { + uint32_t start = jiffies; + while ((jiffies - start) < ms) { __asm__ volatile ("wfi"); } } @@ -154,91 +156,61 @@ void arch_reboot(void) /* ============== UART / Printf Support ============== */ #ifdef DEBUG_UART -extern void uart_init(void); -extern void uart_write(const char* buf, unsigned int sz); -extern int uart_read(char* c); +/* UART functions are declared in s32k1xx.h and implemented in hal/s32k1xx.c */ -/* Simple printf-like function using wolfBoot_printf format */ -#include - -static void uart_putc(char c) -{ - uart_write(&c, 1); -} - -static void uart_puts(const char* s) -{ - while (*s) { - uart_putc(*s++); - } -} - -static void print_hex_byte(uint8_t b) -{ - const char hex[] = "0123456789ABCDEF"; - uart_putc(hex[(b >> 4) & 0x0F]); - uart_putc(hex[b & 0x0F]); -} - -static void print_hex32(uint32_t val) -{ - uart_puts("0x"); - print_hex_byte((val >> 24) & 0xFF); - print_hex_byte((val >> 16) & 0xFF); - print_hex_byte((val >> 8) & 0xFF); - print_hex_byte(val & 0xFF); -} - -static void print_dec(uint32_t val) +/* Print hex buffer (similar to stm32h5 style) */ +#define LINE_LEN 16 +static void print_hex(const uint8_t* buffer, uint32_t length, int dumpChars) { - char buf[12]; - int i = 0; + uint32_t i, sz; - if (val == 0) { - uart_putc('0'); + if (!buffer) { + printf("\tNULL\r\n"); return; } - while (val > 0) { - buf[i++] = '0' + (val % 10); - val /= 10; - } - - while (i > 0) { - uart_putc(buf[--i]); - } -} - -/* Non-blocking character read */ -static int uart_getc(char* c) -{ - return uart_read(c); -} + 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"); -/* Blocking character read */ -static char uart_getc_blocking(void) -{ - char c; - while (uart_read(&c) <= 0) { - __asm__ volatile ("wfi"); + buffer += sz; + length -= sz; } - return c; } #endif /* DEBUG_UART */ /* ============== Partition State Names ============== */ -static const char* part_state_name(uint8_t state, int valid) +static const char* part_state_name(uint8_t state, int state_retval) { - if (!valid) { + 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"; + 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"; } } @@ -247,20 +219,35 @@ static const char* part_state_name(uint8_t state, int valid) static const char* key_type_name(uint32_t type) { switch (type) { - case AUTH_KEY_ED25519: return "Ed25519"; case AUTH_KEY_ECC256: return "ECDSA P-256 (secp256r1)"; - case AUTH_KEY_RSA2048: return "RSA-2048"; - case AUTH_KEY_RSA4096: return "RSA-4096"; - case AUTH_KEY_ED448: return "Ed448"; 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 @@ -273,44 +260,24 @@ static void print_partition_info(void) boot_ver = wolfBoot_current_firmware_version(); update_ver = wolfBoot_update_firmware_version(); - boot_state_valid = (wolfBoot_get_partition_state(PART_BOOT, &boot_state) == 0); - update_state_valid = (wolfBoot_get_partition_state(PART_UPDATE, &update_state) == 0); - - uart_puts("\n=== Partition Information ===\n"); - - uart_puts("Boot Partition:\n"); - uart_puts(" Address: "); - print_hex32(WOLFBOOT_PARTITION_BOOT_ADDRESS); - uart_puts("\n"); - uart_puts(" Version: "); - print_dec(boot_ver); - uart_puts("\n"); - uart_puts(" State: "); - uart_puts(part_state_name(boot_state, boot_state_valid)); - uart_puts("\n"); - - uart_puts("Update Partition:\n"); - uart_puts(" Address: "); - print_hex32(WOLFBOOT_PARTITION_UPDATE_ADDRESS); - uart_puts("\n"); - uart_puts(" Version: "); - if (update_ver == 0) { - uart_puts("(empty)"); - } else { - print_dec(update_ver); - } - uart_puts("\n"); - uart_puts(" State: "); - uart_puts(part_state_name(update_state, update_state_valid)); - uart_puts("\n"); + 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"); - uart_puts("Swap Partition:\n"); - uart_puts(" Address: "); - print_hex32(WOLFBOOT_PARTITION_SWAP_ADDRESS); - uart_puts("\n"); - uart_puts(" Size: "); - print_dec(WOLFBOOT_SECTOR_SIZE); - uart_puts(" bytes\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) @@ -318,76 +285,23 @@ static void print_keystore_info(void) uint32_t n_keys; int i; - uart_puts("\n=== Keystore Information ===\n"); - - /* Show configured signature and hash algorithms */ -#ifdef WOLFBOOT_SIGN_ECC256 - uart_puts("Signature: ECDSA P-256 (secp256r1)\n"); -#elif defined(WOLFBOOT_SIGN_ECC384) - uart_puts("Signature: ECDSA P-384 (secp384r1)\n"); -#elif defined(WOLFBOOT_SIGN_ECC521) - uart_puts("Signature: ECDSA P-521 (secp521r1)\n"); -#elif defined(WOLFBOOT_SIGN_ED25519) - uart_puts("Signature: Ed25519\n"); -#elif defined(WOLFBOOT_SIGN_ED448) - uart_puts("Signature: Ed448\n"); -#elif defined(WOLFBOOT_SIGN_RSA2048) - uart_puts("Signature: RSA-2048\n"); -#elif defined(WOLFBOOT_SIGN_RSA3072) - uart_puts("Signature: RSA-3072\n"); -#elif defined(WOLFBOOT_SIGN_RSA4096) - uart_puts("Signature: RSA-4096\n"); -#elif defined(WOLFBOOT_SIGN_LMS) - uart_puts("Signature: LMS\n"); -#elif defined(WOLFBOOT_SIGN_XMSS) - uart_puts("Signature: XMSS\n"); -#else - uart_puts("Signature: Unknown\n"); -#endif + printf("\r\n=== Keystore Information ===\r\n"); -#ifdef WOLFBOOT_HASH_SHA256 - uart_puts("Hash: SHA-256\n"); -#elif defined(WOLFBOOT_HASH_SHA384) - uart_puts("Hash: SHA-384\n"); -#elif defined(WOLFBOOT_HASH_SHA512) - uart_puts("Hash: SHA-512\n"); -#elif defined(WOLFBOOT_HASH_SHA3_384) - uart_puts("Hash: SHA3-384\n"); -#else - uart_puts("Hash: Unknown\n"); -#endif n_keys = keystore_num_pubkeys(); - uart_puts("Number of public keys: "); - print_dec(n_keys); - uart_puts("\n"); + 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); - int j; - - uart_puts("\nKey #"); - print_dec(i); - uart_puts(":\n"); - uart_puts(" Algorithm: "); - uart_puts(key_type_name(type)); - uart_puts("\n"); - uart_puts(" Size: "); - print_dec(size); - uart_puts(" bytes\n"); - uart_puts(" Data: "); - - /* Print first 16 bytes of key */ - for (j = 0; j < 16 && j < (int)size; j++) { - print_hex_byte(keybuf[j]); - uart_putc(' '); - } - if (size > 16) { - uart_puts("..."); - } - uart_puts("\n"); + + 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); } } @@ -416,8 +330,9 @@ static uint8_t crc8_checksum(uint8_t* data, int len) static void xmodem_cancel(void) { int i; + char can = XCAN; for (i = 0; i < 10; i++) { - uart_putc(XCAN); + uart_write(&can, 1); } } @@ -435,16 +350,14 @@ static int cmd_update_xmodem(void) int eot_expected = 0; uint32_t erase_addr; int erase_ret; + char ack = XACK, nak = XNAK; - uart_puts("Erasing update partition...\n"); + printf("Erasing update partition...\r\n"); #ifdef DEBUG_FLASH - uart_puts(" Address: "); - print_hex32(WOLFBOOT_PARTITION_UPDATE_ADDRESS); - uart_puts("\n Size: "); - print_hex32(WOLFBOOT_PARTITION_SIZE); - uart_puts(" ("); - print_dec(WOLFBOOT_PARTITION_SIZE); - uart_puts(" bytes)\n"); + 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(); @@ -453,19 +366,16 @@ static int cmd_update_xmodem(void) /* Erase sector by sector with debug output */ erase_addr = WOLFBOOT_PARTITION_UPDATE_ADDRESS; while (erase_addr < WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE) { - uart_puts(" Erasing sector at "); - print_hex32(erase_addr); - uart_puts("..."); + 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) { - uart_puts(" FAILED ("); - print_dec(erase_ret); - uart_puts(")\n"); + printf(" FAILED (%d)\r\n", erase_ret); hal_flash_lock(); return -1; } - uart_puts(" OK\n"); + printf(" OK\r\n"); erase_addr += WOLFBOOT_SECTOR_SIZE; } #else @@ -474,10 +384,10 @@ static int cmd_update_xmodem(void) hal_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_PARTITION_SIZE); #endif - uart_puts("Done.\n"); + printf("Done.\r\n"); - uart_puts("Waiting for XMODEM transfer...\n"); - uart_puts("(Send file now using XMODEM protocol)\n"); + printf("Waiting for XMODEM transfer...\r\n"); + printf("(Send file now using XMODEM protocol)\r\n"); while (1) { now = get_time_ms(); @@ -486,7 +396,7 @@ static int cmd_update_xmodem(void) /* Receive packet */ while (i < XMODEM_PACKET_SIZE) { char c; - int r = uart_getc(&c); + int r = uart_read(&c); if (r > 0) { xpkt[i++] = (uint8_t)c; now = get_time_ms(); @@ -497,7 +407,7 @@ static int cmd_update_xmodem(void) if (get_time_ms() > (now + XMODEM_TIMEOUT_MS)) { now = get_time_ms(); if (i == 0) { - uart_putc(XNAK); /* Request retransmit */ + uart_write(&nak, 1); /* Request retransmit */ } i = 0; } else { @@ -508,12 +418,12 @@ static int cmd_update_xmodem(void) /* Check for EOT */ if (xpkt[0] == XEOT) { - uart_putc(XACK); + uart_write(&ack, 1); led_red_on(); /* Indicate transfer complete */ ret = 0; break; } else if (eot_expected) { - uart_putc(XNAK); + uart_write(&nak, 1); ret = -1; break; } @@ -530,7 +440,7 @@ static int cmd_update_xmodem(void) pkt_num_expected = pkt_num; transfer_started = 1; } else if (pkt_num_expected != pkt_num) { - uart_putc(XNAK); + uart_write(&nak, 1); continue; } @@ -552,11 +462,11 @@ static int cmd_update_xmodem(void) payload, XMODEM_PAYLOAD_SIZE); if (ret != 0) { xmodem_cancel(); - uart_puts("Error: Flash write failed\n"); + printf("Error: Flash write failed\r\n"); break; } - uart_putc(XACK); + uart_write(&ack, 1); pkt_num_expected++; dst_offset += XMODEM_PAYLOAD_SIZE; @@ -569,32 +479,27 @@ static int cmd_update_xmodem(void) eot_expected = 1; } } else { - uart_putc(XNAK); + uart_write(&nak, 1); } } else { - uart_putc(XNAK); + uart_write(&nak, 1); } } hal_flash_lock(); - uart_puts("\nTransfer "); - if (ret == 0) { - uart_puts("complete!\n"); + printf("\r\nTransfer %s\r\n", (ret == 0) ? "complete!" : "failed."); + if (ret == 0) { uint32_t update_ver = wolfBoot_update_firmware_version(); if (update_ver != 0) { - uart_puts("New firmware version: "); - print_dec(update_ver); - uart_puts("\n"); - uart_puts("Triggering update...\n"); + printf("New firmware version: %lu\r\n", (unsigned long)update_ver); + printf("Triggering update...\r\n"); wolfBoot_update_trigger(); - uart_puts("Reboot to apply update.\n"); + printf("Reboot to apply update.\r\n"); } else { - uart_puts("Warning: No valid image detected\n"); + printf("Warning: No valid image detected\r\n"); } - } else { - uart_puts("failed.\n"); } led_red_off(); @@ -603,105 +508,120 @@ static int cmd_update_xmodem(void) /* ============== Console Commands ============== */ -static void cmd_help(void); -static void cmd_info(void); -static void cmd_success(void); -static void cmd_reboot(void); -static void cmd_update(void); +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; - void (*fn)(void); } console_cmd_t; static const console_cmd_t commands[] = { - {"help", "Show this help message", cmd_help}, - {"info", "Display partition and key info", cmd_info}, - {"success", "Mark firmware as successful", cmd_success}, - {"update", "Update firmware via XMODEM", cmd_update}, - {"reboot", "Reboot the system", cmd_reboot}, + {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 void cmd_help(void) +static int cmd_help(const char *args) { int i; - uart_puts("\nAvailable commands:\n"); + (void)args; + printf("\r\nAvailable commands:\r\n"); for (i = 0; commands[i].name != NULL; i++) { - uart_puts(" "); - uart_puts(commands[i].name); - uart_puts(" - "); - uart_puts(commands[i].help); - uart_puts("\n"); + printf(" %s - %s\r\n", commands[i].name, commands[i].help); } + return 0; } -static void cmd_info(void) +static int cmd_info(const char *args) { + (void)args; print_partition_info(); print_keystore_info(); + return 0; } -static void cmd_success(void) +static int cmd_success(const char *args) { + (void)args; wolfBoot_success(); - uart_puts("Firmware marked as successful.\n"); + printf("Firmware marked as successful.\r\n"); + return 0; } -static void cmd_reboot(void) +static int cmd_timestamp(const char *args) { - uart_puts("Rebooting...\n"); + (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 void cmd_update(void) +static int cmd_update(const char *args) { - cmd_update_xmodem(); + (void)args; + return cmd_update_xmodem(); } -static void parse_command(const char* cmd) +static int parse_command(const char* cmd) { int i; for (i = 0; commands[i].name != NULL; i++) { if (strcmp(cmd, commands[i].name) == 0) { - commands[i].fn(); - return; + return commands[i].fn(NULL); } } - uart_puts("Unknown command: "); - uart_puts(cmd); - uart_puts("\nType 'help' for available commands.\n"); + 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(uint32_t version) +static void console_loop(void) { char cmd[CMD_BUF_SIZE]; int idx; char c; - (void)version; /* LED is set once at startup, no toggling */ - while (1) { - uart_puts("\ncmd> "); + printf("\r\ncmd> "); + fflush(stdout); idx = 0; while (idx < CMD_BUF_SIZE - 1) { - int ret = uart_getc(&c); + int ret = uart_read(&c); if (ret > 0) { if (c == '\r' || c == '\n') { - uart_puts("\n"); + printf("\r\n"); break; } else if (c == 0x08 || c == 0x7F) { /* Backspace */ if (idx > 0) { - uart_puts("\b \b"); + printf("\b \b"); + fflush(stdout); idx--; } } else if (c >= 32 && c < 127) { - uart_putc(c); + printf("%c", c); + fflush(stdout); cmd[idx++] = c; } } @@ -763,9 +683,6 @@ void main(void) #ifdef DEBUG_UART /* Reinitialize UART - bootloader may have changed settings in hal_prepare_boot */ uart_init(); - - /* Simple test message */ - uart_puts("Test App Started!\n"); #endif /* Initialize test-app hardware */ @@ -782,21 +699,19 @@ void main(void) led_set_version(version); #ifdef DEBUG_UART - uart_puts("\n"); - uart_puts("========================================\n"); - uart_puts("S32K1xx wolfBoot Test Application\n"); - uart_puts("Copyright 2025 wolfSSL Inc.\n"); - uart_puts("========================================\n"); - uart_puts("Firmware Version: "); - print_dec(version); - uart_puts("\n"); + 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) { - uart_puts("Testing state detected, marking success...\n"); + printf("Testing state detected, marking success...\r\n"); wolfBoot_success(); } } @@ -804,10 +719,10 @@ void main(void) /* Show initial info */ print_partition_info(); - uart_puts("\nType 'help' for available commands.\n"); + printf("\r\nType 'help' for available commands.\r\n"); /* Enter interactive console */ - console_loop(version); + console_loop(); #else /* No UART - just blink LED */ while (1) { @@ -817,4 +732,98 @@ void main(void) #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 ac41d0d187..48c641aec1 100644 --- a/test-app/startup_arm.c +++ b/test-app/startup_arm.c @@ -40,10 +40,11 @@ extern void isr_usart3(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 @@ -119,8 +120,6 @@ void isr_empty(void) } - - __attribute__ ((section(".isr_vector"))) void (* const IV[])(void) = { @@ -139,14 +138,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 */ From 1977a3ef0a4784e32d9adfbe74d3712d58925700 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 9 Jan 2026 16:39:52 -0800 Subject: [PATCH 4/4] Progress with getting XMODEM updates working. --- hal/s32k1xx.c | 45 ++++-- hal/s32k1xx.h | 23 +++ test-app/app_s32k1xx.c | 357 +++++++++++++++++++++++++++++++---------- test-app/startup_arm.c | 95 +++++++++++ 4 files changed, 414 insertions(+), 106 deletions(-) diff --git a/hal/s32k1xx.c b/hal/s32k1xx.c index b688457d79..aab1ca33e8 100644 --- a/hal/s32k1xx.c +++ b/hal/s32k1xx.c @@ -28,6 +28,13 @@ #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") @@ -280,43 +287,47 @@ void uart_init(void) 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') { - /* Wait for transmit buffer empty */ - while (!(LPUART_STAT & LPUART_STAT_TDRE)) {} - LPUART_DATA = '\r'; + uart_tx('\r'); } - - /* Wait for transmit buffer empty */ - while (!(LPUART_STAT & LPUART_STAT_TDRE)) {} - LPUART_DATA = buf[i]; + uart_tx(buf[i]); } /* Wait for transmission complete */ while (!(LPUART_STAT & LPUART_STAT_TC)) {} } -/* Read a single character from UART (non-blocking) - * Returns: 1 if character read, 0 if no data available, -1 on error + + +/* 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 uart_read(char* c) +int RAMFUNCTION uart_read(char* c) { - uint32_t stat = LPUART_STAT; + uint32_t stat = LPUART1_STAT; - /* Clear any error flags */ + /* Clear any error flags first */ if (stat & (LPUART_STAT_OR | LPUART_STAT_NF | LPUART_STAT_FE | LPUART_STAT_PF)) { - LPUART_STAT = stat; /* Write 1 to clear flags */ - return -1; + LPUART1_STAT = stat; /* Write 1 to clear flags */ } - /* Check if data available */ + /* Check if data available - read even if there was an error */ if (stat & LPUART_STAT_RDRF) { - *c = (char)(LPUART_DATA & 0xFF); + *c = (char)(LPUART1_DATA & 0xFF); return 1; } diff --git a/hal/s32k1xx.h b/hal/s32k1xx.h index 87d5b5923e..f105b3a3ba 100644 --- a/hal/s32k1xx.h +++ b/hal/s32k1xx.h @@ -74,6 +74,29 @@ #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 */ diff --git a/test-app/app_s32k1xx.c b/test-app/app_s32k1xx.c index d6ed7cbdfb..815747051f 100644 --- a/test-app/app_s32k1xx.c +++ b/test-app/app_s32k1xx.c @@ -40,6 +40,13 @@ #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; @@ -110,12 +117,12 @@ static void led_blue_off(void) GPIOD_PSOR = (1UL << LED_PIN_BLUE); } -static void led_red_on(void) +static void APP_RAMFUNCTION led_red_on(void) { GPIOD_PCOR = (1UL << LED_PIN_RED); /* Active low */ } -static void led_red_off(void) +static void APP_RAMFUNCTION led_red_off(void) { GPIOD_PSOR = (1UL << LED_PIN_RED); } @@ -158,6 +165,82 @@ void arch_reboot(void) #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) @@ -312,135 +395,142 @@ static void print_keystore_info(void) #define XACK 0x06 #define XNAK 0x15 #define XCAN 0x18 +#define XCRC 'C' /* Request CRC mode */ #define XMODEM_PAYLOAD_SIZE 128 -#define XMODEM_PACKET_SIZE (3 + XMODEM_PAYLOAD_SIZE + 1) +#define XMODEM_PACKET_SIZE_CRC (3 + XMODEM_PAYLOAD_SIZE + 2) /* SOH + blk + ~blk + data + CRC16 */ #define XMODEM_TIMEOUT_MS 1000 -static uint8_t crc8_checksum(uint8_t* data, int len) +/* CRC-16-CCITT for XMODEM-CRC mode */ +static uint16_t APP_RAMFUNCTION crc16_ccitt(const uint8_t* data, int len) { - uint8_t sum = 0; - int i; + uint16_t crc = 0; + int i, j; for (i = 0; i < len; i++) { - sum += data[i]; + crc ^= ((uint16_t)data[i] << 8); + for (j = 0; j < 8; j++) { + if (crc & 0x8000) { + crc = (crc << 1) ^ 0x1021; + } else { + crc <<= 1; + } + } } - return sum; + return crc; } -static void xmodem_cancel(void) +/* 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; - char can = XCAN; for (i = 0; i < 10; i++) { - uart_write(&can, 1); + uart_tx(XCAN); } } -static int cmd_update_xmodem(void) +/* 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) { - int ret = -1; - uint8_t xpkt[XMODEM_PACKET_SIZE]; + uint8_t xpkt[XMODEM_PACKET_SIZE_CRC]; uint8_t payload[XMODEM_PAYLOAD_SIZE]; - uint32_t dst_offset = 0; 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; - uint32_t erase_addr; - int erase_ret; - char ack = XACK, nak = XNAK; - - 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 + int ret = -1; - printf("Done.\r\n"); + 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; - printf("Waiting for XMODEM transfer...\r\n"); - printf("(Send file now using XMODEM protocol)\r\n"); + /* Send 'C' to request CRC mode (XMODEM-CRC) */ + uart_tx(XCRC); while (1) { - now = get_time_ms(); + now = jiffies; /* Direct access to volatile - faster than function call */ i = 0; - /* Receive packet */ - while (i < XMODEM_PACKET_SIZE) { - char c; - int r = uart_read(&c); + /* 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) { - xpkt[i++] = (uint8_t)c; - now = get_time_ms(); - if (i == 1 && xpkt[0] == XEOT) { + i += r; + now = jiffies; + if (i >= 1 && xpkt[0] == XEOT) { break; /* End of transmission */ } - } else { - if (get_time_ms() > (now + XMODEM_TIMEOUT_MS)) { - now = get_time_ms(); - if (i == 0) { - uart_write(&nak, 1); /* Request retransmit */ - } - i = 0; - } else { - __asm__ volatile ("wfi"); + } 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_write(&ack, 1); + uart_tx(XACK); led_red_on(); /* Indicate transfer complete */ ret = 0; break; } else if (eot_expected) { - uart_write(&nak, 1); + 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_write(&nak, 1); + uart_tx(XNAK); continue; } @@ -451,44 +541,129 @@ static int cmd_update_xmodem(void) led_red_off(); } - /* Validate checksum */ - uint8_t crc = xpkt[XMODEM_PACKET_SIZE - 1]; - uint8_t calc_crc = crc8_checksum(xpkt, XMODEM_PACKET_SIZE - 1); + /* 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); - if (crc == calc_crc) { /* Write to flash */ - memcpy(payload, xpkt + 3, XMODEM_PAYLOAD_SIZE); - ret = hal_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + dst_offset, + ret = hal_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + state->dst_offset, payload, XMODEM_PAYLOAD_SIZE); if (ret != 0) { xmodem_cancel(); - printf("Error: Flash write failed\r\n"); + /* No printf here - we're in RAM */ break; } - - uart_write(&ack, 1); pkt_num_expected++; - dst_offset += XMODEM_PAYLOAD_SIZE; + state->dst_offset += XMODEM_PAYLOAD_SIZE; - /* Get expected size from header */ - if (t_size == 0 && dst_offset >= 8) { + /* 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 && dst_offset >= t_size) { + if (t_size > 0 && state->dst_offset >= t_size) { eot_expected = 1; } } else { - uart_write(&nak, 1); + state->pkts_crc_fail++; + uart_tx(XNAK); } } else { - uart_write(&nak, 1); + 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(); @@ -608,7 +783,7 @@ static void console_loop(void) idx = 0; while (idx < CMD_BUF_SIZE - 1) { - int ret = uart_read(&c); + int ret = uart_getc(&c); if (ret > 0) { if (c == '\r' || c == '\n') { printf("\r\n"); @@ -683,6 +858,10 @@ void main(void) #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 */ diff --git a/test-app/startup_arm.c b/test-app/startup_arm.c index 48c641aec1..68e6c37465 100644 --- a/test-app/startup_arm.c +++ b/test-app/startup_arm.c @@ -39,6 +39,10 @@ extern void isr_tim2(void); extern void isr_usart3(void); #endif +#ifdef TARGET_s32k1xx +extern void isr_lpuart1(void); +#endif + #ifdef TARGET_va416x0 #define isr_systick SysTick_Handler #elif !defined(APP_HAS_SYSTICK) @@ -430,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