Add wolfBoot target for NXP i.MX95 Cortex-M7

pull/871/head
David Garske 2026-08-18 11:27:39 -07:00 committed by Daniele Lacamera
parent 8015a1fa4f
commit 724ca34677
9 changed files with 772 additions and 0 deletions

View File

@ -284,6 +284,9 @@ ifeq ($(ARCH),ARM)
ifeq ($(TARGET),imx_rt)
CORTEX_M7=1
endif
ifeq ($(TARGET),imx95_m7)
CORTEX_M7=1
endif
ifeq ($(TARGET),stm32l0)
CORTEX_M0=1
SPI_TARGET=stm32

View File

@ -0,0 +1,35 @@
ARCH?=ARM
TARGET?=imx95_m7
SIGN?=ECC256
HASH?=SHA256
DEBUG?=0
VTOR?=1
CORTEX_M0?=0
NO_ASM?=0
NO_MPU?=1
EXT_FLASH?=0
SPI_FLASH?=0
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=0
WOLFBOOT_VERSION?=0
V?=0
SPMATH?=1
RAM_CODE?=1
DUALBANK_SWAP?=0
PKA?=0
# wolfBoot runs from ITCM; the partitions live in the M7's reserved DDR window
# (memory@80000000, 16 MiB) where Linux stages images before restarting the
# core. See hal/imx95_m7.c for why "flash" here is ordinary DDR.
WOLFBOOT_PARTITION_SIZE?=0x400000
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x80100000
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x80500000
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x80900000
# Sized for ML-DSA-87 from the start, even though bring-up signs with ECC256,
# so switching to SIGN=ML_DSA is a config change rather than a re-layout.
# ML-DSA-87: 4627-byte signature, 2592-byte public key.
WOLFBOOT_SECTOR_SIZE?=0x4000
IMAGE_HEADER_SIZE?=12288
WOLFBOOT_SMALL_STACK?=0

290
hal/imx95_m7.c 100644
View File

@ -0,0 +1,290 @@
/* imx95_m7.c
*
* HAL for the Cortex-M7 on the NXP i.MX95.
*
* Copyright (C) 2026 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
*/
/* The M7 on this part has no dedicated flash. wolfBoot is loaded into TCM by
* the Linux remoteproc driver on the A55 cluster, and the images it verifies
* live in the DDR region reserved for the M7 (memory@80000000, 16 MiB). The
* "flash" the partitions sit in is therefore ordinary DDR: writes are copies
* and erases are fills. Updates are staged by Linux writing the update
* partition before restarting the core.
*
* Addresses come from the i.MX 95 Reference Manual rev 5 ch. 123 and from the
* board's own device tree; see BOARD-FINDINGS.md in the demo tree.
*
* ITCM core view 0x00000000 system view 0x203C0000 256 KiB
* DTCM core view 0x20000000 system view 0x20400000 256 KiB
* DDR 0x80000000 16 MiB
*
* The core/system distinction matters: wolfBoot is linked for the core view,
* while remoteproc loads through the system view and the imx_rproc driver
* translates between them.
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <target.h>
#include "image.h"
/* ---------------------------------------------------------------------------
* remoteproc resource table
*
* Linux finds the resource table by looking for a .resource_table section in
* the ELF *it* loads. On this target that ELF is wolfBoot, not the payload, so
* the vdev/vring declaration has to live here even though the RPMsg endpoint is
* serviced later by whatever wolfBoot boots. Without it the kernel logs
* "No resource table in elf" and never creates the virtio device.
*
* The addresses are the ones the board's device tree already reserves, so the
* two sides agree by construction rather than by convention:
*
* vdev0vring0 0x88000000 32 KiB
* vdev0vring1 0x88008000 32 KiB
* vdevbuffer 0x88020000 1 MiB
*
* A vring of 256 buffers at 4 KiB alignment needs about 10 KiB, so each fits
* its 32 KiB reservation with room to spare.
* ------------------------------------------------------------------------- */
#define RSC_VDEV 3U
#define VIRTIO_ID_RPMSG 7U
#define VIRTIO_RPMSG_F_NS (1U << 0) /* name service announcements */
#define RSC_VRING0_DA 0x88000000U
#define RSC_VRING1_DA 0x88008000U
#define RSC_VRING_ALIGN 0x1000U
#define RSC_VRING_NUM 256U
struct fw_rsc_vdev_vring {
uint32_t da;
uint32_t align;
uint32_t num;
uint32_t notifyid;
uint32_t reserved;
};
struct fw_rsc_vdev {
uint32_t type;
uint32_t id;
uint32_t notifyid;
uint32_t dfeatures;
uint32_t gfeatures;
uint32_t config_len;
uint8_t status;
uint8_t num_of_vrings;
uint8_t reserved[2];
struct fw_rsc_vdev_vring vring[2];
};
struct imx95_rsc_table {
uint32_t ver;
uint32_t num;
uint32_t reserved[2];
uint32_t offset[1];
struct fw_rsc_vdev vdev;
};
const struct imx95_rsc_table resource_table
__attribute__((section(".resource_table"), used)) = {
1, /* ver */
1, /* num: one entry */
{ 0, 0 }, /* reserved */
{ offsetof(struct imx95_rsc_table, vdev) },
{
RSC_VDEV,
VIRTIO_ID_RPMSG,
0, /* notifyid, assigned by the host */
VIRTIO_RPMSG_F_NS, /* dfeatures */
0, /* gfeatures, filled by the host */
0, /* config_len */
0, /* status, driven by the host */
2, /* num_of_vrings */
{ 0, 0 },
{
{ RSC_VRING0_DA, RSC_VRING_ALIGN, RSC_VRING_NUM, 0, 0 },
{ RSC_VRING1_DA, RSC_VRING_ALIGN, RSC_VRING_NUM, 1, 0 }
}
}
};
/* Erased state of the DDR-backed pseudo-flash. */
#define FLASH_ERASED_BYTE 0xFFU
/* Bounds of the M7's reserved DDR window. Anything outside it is rejected
* rather than silently corrupting memory that belongs to Linux. */
#define M7_DDR_BASE 0x80000000UL
#define M7_DDR_SIZE 0x01000000UL
/* Liveness marker. The M7 has no confirmed UART route on this carrier, so
* early bring-up reports progress through shared memory that Linux can read
* with devmem instead of over a serial port.
*
* This lives inside the M7's own DDR window, immediately above the console
* ring at 0x80F00000, and deliberately NOT in the 0x88000000 block: the device
* tree reserves that for RPMsg (vdev0/vdev1 vrings, and a 1 MiB vdevbuffer at
* 0x88020000), so a status word there would be scribbling on buffers Linux
* owns as soon as RPMsg is brought up. */
#define WOLFBOOT_STATUS_ADDR 0x80F10000UL
#define WOLFBOOT_STATUS_MAGIC 0x57424F54UL /* "WBOT" */
/* Cortex-M7 DWT cycle counter. With no usable console on this carrier the
* cycle counter is how the signature-verification cost gets measured: it is
* exact, free to read, and needs no peripheral. Timestamps are published
* alongside the status word for the A55 to read. */
#define CORE_DEMCR (*(volatile uint32_t *)0xE000EDFCUL)
#define DEMCR_TRCENA (1UL << 24)
#define DWT_CTRL (*(volatile uint32_t *)0xE0001000UL)
#define DWT_CYCCNTENA (1UL << 0)
#define DWT_CYCCNT (*(volatile uint32_t *)0xE0001004UL)
/* Cortex-M7 cache maintenance. Both caches are OFF out of reset, and this
* image runs its text from DDR, so leaving them off means every instruction
* fetch and data access goes to external memory - measured at ~664 cycles per
* byte for SHA-256, roughly fifty times slower than the core is capable of.
*
* The ARMv7-M default memory map already marks 0x80000000-0x9FFFFFFF as normal
* cacheable memory, so no MPU region is needed to make this take effect. */
#define SCB_CCR (*(volatile uint32_t *)0xE000ED14UL)
#define SCB_CCSIDR (*(volatile uint32_t *)0xE000ED80UL)
#define SCB_CSSELR (*(volatile uint32_t *)0xE000ED84UL)
#define SCB_ICIALLU (*(volatile uint32_t *)0xE000EF50UL)
#define SCB_DCISW (*(volatile uint32_t *)0xE000EF60UL)
#define CCR_IC (1UL << 17)
#define CCR_DC (1UL << 16)
#define DSB() __asm__ volatile ("dsb 0xF" ::: "memory")
#define ISB() __asm__ volatile ("isb 0xF" ::: "memory")
static void hal_cache_enable(void)
{
uint32_t ccsidr, sets, ways, s, w;
/* I-cache: invalidate, then enable. */
DSB(); ISB();
SCB_ICIALLU = 0UL;
DSB(); ISB();
SCB_CCR |= CCR_IC;
DSB(); ISB();
/* D-cache: invalidate every set/way before enabling, otherwise stale
* lines from reset can be written back over live DDR. */
SCB_CSSELR = 0UL; /* select L1 data cache */
DSB();
ccsidr = SCB_CCSIDR;
sets = (ccsidr >> 13) & 0x7FFFUL;
ways = (ccsidr >> 3) & 0x3FFUL;
for (s = 0; s <= sets; s++) {
for (w = 0; w <= ways; w++) {
/* DCISW: way in [31:30], set in [13:5] for a 32-byte line. */
SCB_DCISW = ((w << 30) | (s << 5));
}
}
DSB();
SCB_CCR |= CCR_DC;
DSB(); ISB();
}
static void hal_cycle_counter_start(void)
{
CORE_DEMCR |= DEMCR_TRCENA;
DWT_CYCCNT = 0;
DWT_CTRL |= DWT_CYCCNTENA;
}
static int hal_addr_is_valid(uint32_t address, int len)
{
uint64_t start = (uint64_t)address;
uint64_t end;
if (len < 0)
return 0;
end = start + (uint64_t)len;
if (start < (uint64_t)M7_DDR_BASE)
return 0;
if (end > (uint64_t)M7_DDR_BASE + (uint64_t)M7_DDR_SIZE)
return 0;
return 1;
}
#ifdef __WOLFBOOT
/* Record a progress code where the A55 side can see it. Kept deliberately
* trivial: no locking, no ordering guarantees beyond the write itself. */
void hal_status(uint32_t code)
{
volatile uint32_t *status = (volatile uint32_t *)WOLFBOOT_STATUS_ADDR;
status[0] = WOLFBOOT_STATUS_MAGIC;
status[1] = code;
/* status[2] is stamped at hal_init, status[3] at hal_prepare_boot; the
* difference is the cost of everything wolfBoot does in between, which is
* dominated by the signature verification. */
status[code + 1] = DWT_CYCCNT;
}
void hal_init(void)
{
/* Clocks, DDR and the TCM split are configured before this core is
* released: DDR by the OEI/SPL stage, the TCM split by the System Manager
* on the M33 via M7_CFG[TCM_SIZE] in BLK_CTRL_Secure_AON. There is
* nothing for wolfBoot to bring up here except the caches, which are off
* out of reset and which this image needs because it runs from DDR. */
hal_cache_enable();
hal_cycle_counter_start();
hal_status(1);
}
void hal_prepare_boot(void)
{
hal_status(2);
}
#endif /* __WOLFBOOT */
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
{
if (data == NULL)
return -1;
if (!hal_addr_is_valid(address, len))
return -1;
memcpy((void *)(uintptr_t)address, data, (size_t)len);
return 0;
}
void RAMFUNCTION hal_flash_unlock(void)
{
}
void RAMFUNCTION hal_flash_lock(void)
{
}
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
{
if (!hal_addr_is_valid(address, len))
return -1;
memset((void *)(uintptr_t)address, FLASH_ERASED_BYTE, (size_t)len);
return 0;
}

72
hal/imx95_m7.ld 100644
View File

@ -0,0 +1,72 @@
/* Linker script for wolfBoot on the i.MX95 Cortex-M7.
*
* Linked for the *core* view of TCM. The Linux remoteproc driver loads through
* the system view (ITCM 0x203C0000, DTCM 0x20400000 at the default 256K/256K
* TCM split) and imx_rproc translates between the two. Linking for the system
* view produces an image that loads cleanly and faults on the first fetch.
*
* Sizes assume M7_CFG[TCM_SIZE] = 000b (256 KiB ITCM + 256 KiB DTCM), which is
* the reset default. The System Manager on the M33 owns that field.
*/
MEMORY
{
ITCM (rx) : ORIGIN = 0x00000000, LENGTH = 256K
DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
}
SECTIONS
{
.text :
{
_start_text = .;
KEEP(*(.isr_vector))
. = ALIGN(8);
*(.text*)
*(.rodata*)
*(.init*)
*(.fini*)
. = ALIGN(4);
_end_text = .;
} > ITCM
/* Consumed by the remoteproc ELF loader. Not required to start the core,
* but RPMsg needs it, so keep it even when empty. */
.resource_table :
{
. = ALIGN(8);
KEEP(*(.resource_table*))
. = ALIGN(8);
} > ITCM
.edidx :
{
. = ALIGN(4);
*(.ARM.exidx*)
} > ITCM
_stored_data = .;
.data : AT (_stored_data)
{
_start_data = .;
KEEP(*(.data*))
. = ALIGN(4);
_end_data = .;
} > DTCM
.bss (NOLOAD) :
{
_start_bss = .;
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_end_bss = .;
__bss_end__ = .;
_end = .;
} > DTCM
. = ALIGN(4);
}
END_STACK = ORIGIN(DTCM) + LENGTH(DTCM);

View File

@ -0,0 +1,106 @@
/* uart_drv_imx95_m7.c
*
* Console for the Cortex-M7 on the NXP i.MX95.
*
* Copyright (C) 2026 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
*/
/* This is a shared-memory console, not a peripheral driver.
*
* The M7's LPUART is not routed to an accessible header on the Toradex SMARC
* Development Board: markers written to /dev/ttyLP1..3 from Linux appear on no
* host serial channel, and Linux owns all four LPUART instances anyway, so an
* M7 writing to one would contend with it. Rather than depend on unproven pin
* routing, the console is a ring buffer in the M7's own reserved DDR window,
* which the A55 cluster reads with the memtool helper.
*
* The buffer sits at the top of the M7's 16 MiB carveout (memory@80000000),
* clear of the wolfBoot partitions lower down (BOOT 0x80100000, UPDATE
* 0x80500000, SWAP 0x80900000). It deliberately does NOT use the RPMsg
* vdevbuffer carveout, which belongs to the RPMsg transport.
*
* Reader contract: `wr` counts bytes ever written and never wraps within a run,
* so a reader tracks its own position and copies (wr - pos) bytes from
* data[pos % size]. Overrun is possible and expected for very chatty output;
* the reader detects it when (wr - pos) > size and reports the gap rather than
* silently printing corrupt text.
*/
#include <stdint.h>
#define CONSOLE_BASE 0x80F00000UL
#define CONSOLE_MAGIC 0x4E4F4357UL /* "WCON" little-endian */
#define CONSOLE_SIZE 0x10000UL /* 64 KiB of text */
struct console_hdr {
volatile uint32_t magic;
volatile uint32_t wr; /* total bytes written, monotonic */
volatile uint32_t size; /* == CONSOLE_SIZE */
volatile uint32_t rsvd;
};
#define CONSOLE_HDR ((struct console_hdr *)CONSOLE_BASE)
#define CONSOLE_DATA ((volatile uint8_t *)(CONSOLE_BASE + sizeof(struct console_hdr)))
int uart_init(uint32_t bitrate, uint8_t data, char parity, uint8_t stop)
{
struct console_hdr *h = CONSOLE_HDR;
(void)bitrate;
(void)data;
(void)parity;
(void)stop;
h->wr = 0;
h->size = CONSOLE_SIZE;
/* Magic last: a reader that samples mid-init sees no valid console rather
* than a valid magic with a stale size. */
h->magic = CONSOLE_MAGIC;
return 0;
}
int uart_tx(const uint8_t c)
{
struct console_hdr *h = CONSOLE_HDR;
uint32_t w;
if (h->magic != CONSOLE_MAGIC)
(void)uart_init(0, 0, 'N', 0);
w = h->wr;
CONSOLE_DATA[w % CONSOLE_SIZE] = c;
/* Publish the byte before advancing the counter, so a reader never sees a
* count covering a byte that has not been stored yet. */
h->wr = w + 1;
return 0;
}
int uart_rx(uint8_t *c)
{
(void)c;
return -1; /* console is write-only */
}
void uart_write(const char *buf, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++)
(void)uart_tx((const uint8_t)buf[i]);
}

View File

@ -0,0 +1,89 @@
/* Linker script for the wolfBoot test app on the i.MX95 Cortex-M7.
*
* The generic ARM.ld is not usable here: it gives RAM only 16 KiB and defines
* no heap symbols, and wolfCrypt's test/benchmark need both a real heap (RSA
* and ECC key material) and syscalls.c's _sbrk, which references
* _Min_Heap_Size.
*
* Code runs from the BOOT partition in the M7's reserved DDR window; data and
* heap live in DTCM (core view 0x20000000, 256 KiB at the reset-default
* M7_CFG[TCM_SIZE] split). Keeping the working set in TCM is the point of the
* exercise -- these numbers are meant to characterise the real-time core, not
* DDR latency.
*/
_Min_Heap_Size = 0x00018000; /* 96 KiB - RSA/ECC test vectors and key structs */
_Min_Stack_Size = 0x00008000; /* 32 KiB */
MEMORY
{
FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K /* DTCM, core view */
}
SECTIONS
{
.text :
{
_start_text = .;
KEEP(*(.isr_vector))
*(.init)
*(.fini)
*(.text*)
KEEP(*(.rodata*))
. = ALIGN(4);
_end_text = .;
} > FLASH
.ARM :
{
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > FLASH
_stored_data = .;
.data : AT (_stored_data)
{
_start_data = .;
KEEP(*(.data*))
. = ALIGN(4);
KEEP(*(.ramcode))
. = ALIGN(4);
_end_data = .;
} > RAM
.bss :
{
_start_bss = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_end_bss = .;
_end = .;
} > RAM
/* Reserve heap and stack explicitly so the link fails loudly if DTCM is
* over-committed, rather than overflowing at run time. */
.heap (NOLOAD) :
{
. = ALIGN(8);
PROVIDE ( _start_heap = . );
. = . + _Min_Heap_Size;
. = ALIGN(8);
} > RAM
.stack (NOLOAD) :
{
. = . + _Min_Stack_Size;
. = ALIGN(8);
PROVIDE ( END_STACK = . );
PROVIDE ( _end_stack = . );
} > RAM
}
_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@;

View File

@ -616,6 +616,16 @@ ifeq ($(TARGET),stm32l5)
APP_OBJS+=../hal/uart/uart_drv_$(UART_TARGET).o
endif
# i.MX95 Cortex-M7. The console is a shared-memory ring buffer rather than an
# LPUART: no M7 UART is routed to a host-accessible channel on the Toradex SMARC
# carrier, and Linux owns all four LPUART instances. -mcpu/-mfpu come from
# arch.mk via CORTEX_M7.
ifeq ($(TARGET),imx95_m7)
CFLAGS+=-I..
APP_OBJS+=../hal/uart/uart_drv_$(UART_TARGET).o
LSCRIPT_TEMPLATE=ARM-imx95_m7.ld
endif
ifeq ($(TARGET),stm32h5)
ifeq ($(TZEN),1)
LSCRIPT_TEMPLATE=ARM-stm32h5-ns.ld

View File

@ -0,0 +1,113 @@
/* app_imx95_m7.c
*
* Test application for the Cortex-M7 on the NXP i.MX95.
*
* Copyright (C) 2026 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
*/
/* Output goes to the shared-memory console in hal/uart/uart_drv_imx95_m7.c,
* because this part has no M7 UART routed to a host-accessible channel on the
* Toradex SMARC carrier. wolfCrypt's test and benchmark call printf(), which
* test-app/syscalls.c routes to uart_write(), so their output lands there too.
*
* Liveness markers stay in a shared status block so the demo's right pane can
* poll them cheaply without parsing console text:
*
* devmem 0x80F10010 -> 0x41505031 ("APP1") once the app is running
* devmem 0x80F10014 -> heartbeat, increments continuously
*
* wolfBoot writes its own progress codes at 0x80F10000, so a reader can tell
* "bootloader ran" from "payload ran" with no peripheral wired up. The block
* sits just above the console ring in the M7's DDR window, clear of the
* RPMsg reservations at 0x88000000.
*/
#include <stdint.h>
#include "wolfboot/wolfboot.h"
#if defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK)
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/wc_port.h>
#endif
#ifdef WOLFCRYPT_TEST
int wolfcrypt_test(void *args);
#endif
#ifdef WOLFCRYPT_BENCHMARK
int benchmark_test(void *args);
#endif
#define APP_STATUS_ADDR 0x80F10010UL
#define APP_STATUS_MAGIC 0x41505031UL /* "APP1" */
extern int uart_init(uint32_t bitrate, uint8_t data, char parity, uint8_t stop);
extern void uart_write(const char *buf, unsigned int len);
/* Local, so the banner works in builds that do not link ../src/string.o. */
static void say(const char *s)
{
unsigned int n = 0;
while (s[n] != '\0')
n++;
uart_write(s, n);
}
void main(void)
{
volatile uint32_t *status = (volatile uint32_t *)APP_STATUS_ADDR;
uint32_t heartbeat = 0;
/* remoteproc loads only PT_LOAD segments, and .bss is NOBITS - nothing
* zeroes it before entry, so do it here before any static is read. */
extern char _start_bss[], _end_bss[];
char *p;
for (p = _start_bss; p < _end_bss; p++)
*p = 0;
(void)uart_init(115200, 8, 'N', 1);
status[0] = APP_STATUS_MAGIC;
status[1] = heartbeat;
say("\r\nwolfBoot test app: i.MX95 Cortex-M7\r\n");
#if defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK)
wolfCrypt_Init();
#ifdef WOLFCRYPT_TEST
say("\r\nRunning wolfCrypt tests...\r\n");
wolfcrypt_test(NULL);
say("Tests complete.\r\n");
#endif
#ifdef WOLFCRYPT_BENCHMARK
say("\r\nRunning wolfCrypt benchmarks...\r\n");
benchmark_test(NULL);
say("Benchmarks complete.\r\n");
#endif
wolfCrypt_Cleanup();
#endif
say("Idle.\r\n");
/* Confirm this image booted so wolfBoot does not roll it back. */
wolfBoot_success();
while (1) {
status[1] = ++heartbeat;
}
}

View File

@ -44,6 +44,47 @@
extern volatile uint64_t HAL_time_ms;
#elif defined(TARGET_lpc55s69)
extern volatile uint64_t SysTick_time_ms;
#elif defined(TARGET_imx95_m7)
/* Cortex-M7 DWT cycle counter. Exact, free to read, and needs no
* peripheral - which matters here because the M7 has no console UART
* routed on this carrier. The core runs at 800 MHz (read from the board's
* clk_summary: "m7 800000000"), not assumed. CYCCNT is 32-bit and wraps
* every ~5.4 s at that rate, so accumulate into 64 bits on each read;
* benchmark intervals are shorter than one wrap, so sampling on every call
* is sufficient to catch it. */
#define IMX95_M7_HZ 800000000ULL
static uint64_t m7_cyc_hi = 0;
static uint32_t m7_cyc_last = 0;
static int m7_cyc_inited = 0;
static void m7_cyc_init(void);
static uint64_t m7_get_ticks(void)
{
uint32_t now;
if (!m7_cyc_inited) {
m7_cyc_init();
m7_cyc_inited = 1;
}
now = *(volatile uint32_t *)0xE0001004UL; /* DWT_CYCCNT */
if (now < m7_cyc_last)
m7_cyc_hi += 0x100000000ULL; /* wrapped since last read */
m7_cyc_last = now;
return m7_cyc_hi + now;
}
static void m7_cyc_init(void)
{
*(volatile uint32_t *)0xE000EDFCUL |= (1UL << 24); /* DEMCR.TRCENA */
*(volatile uint32_t *)0xE0001004UL = 0; /* DWT_CYCCNT = 0 */
*(volatile uint32_t *)0xE0001000UL |= 1UL; /* DWT_CTRL.CYCCNTENA */
m7_cyc_hi = 0;
m7_cyc_last = 0;
}
static uint64_t m7_start_ticks = 0;
#elif defined(TARGET_nxp_t2080) || defined(TARGET_nxp_t1024)
/* PPC time base register for accurate timing (e6500). */
static uint32_t ppc_tb_hz = 0;
@ -147,6 +188,12 @@ unsigned long my_time(unsigned long* timer)
unsigned long t = (unsigned long)(SysTick_time_ms / 1000);
if (timer) *timer = t;
return t;
#elif defined(TARGET_imx95_m7)
{
unsigned long t = (unsigned long)(m7_get_ticks() / IMX95_M7_HZ);
if (timer) *timer = t;
return t;
}
#elif defined(TARGET_nxp_t2080) || defined(TARGET_nxp_t1024)
if (ppc_tb_hz == 0)
ppc_tb_hz = ppc_get_timebase_hz();
@ -191,6 +238,13 @@ double current_time(int reset)
#elif defined(TARGET_lpc55s69)
(void)reset;
return (double)SysTick_time_ms / 1000.0;
#elif defined(TARGET_imx95_m7)
if (reset) {
m7_cyc_init();
m7_cyc_inited = 1;
m7_start_ticks = 0;
}
return (double)(m7_get_ticks() - m7_start_ticks) / (double)IMX95_M7_HZ;
#elif defined(TARGET_nxp_t2080) || defined(TARGET_nxp_t1024)
if (ppc_tb_hz == 0)
ppc_tb_hz = ppc_get_timebase_hz();