From 4d4cc6a9ed7403a70198f183d90bc7d744a7e5a3 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 19 Mar 2026 10:25:24 -0700 Subject: [PATCH 1/5] =?UTF-8?q?Add=20STM32N6=20(NUCLEO-N657X0-Q)=20port=20?= =?UTF-8?q?=E2=80=94=20UART,=20PHY,=20DMA=20working,=20RMII=20data=20path?= =?UTF-8?q?=20WIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New bare-metal port for STM32N657X0 Cortex-M55 on NUCLEO-N657X0-Q board. Working: - UART at 115200 via USART1 PE5/PE6 (VCP confirmed) - PLL clock config: 600MHz CPU, PLL1 VCO 1200MHz - LAN8742A PHY detected at addr 0, link UP 100Mbps FD - ETH DMA configured with no bus errors (buffers in AXISRAM2) - wolfIP stack init, TCP echo socket, main loop running - Flash via OpenOCD flash.sh to AXISRAM1 Not yet working: - RMII data path: MAC debug register shows idle (0x00), zero frames received by DMA despite PHY link UP and RJ45 LEDs active. Likely needs full HAL ETH_MACDMAConfig register set replicated. Key N6 differences from H5/H7 (discovered during bring-up): - ETH1 on AHB5 at 0x58036000 (not 0x50028000) - ETH clocks in RCC_AHB5ENR bits 22-24, use SET register (+0x800) - RMII mode via RCC_CCIPR2 bit 18 (offset 0x148) - RMII pins all on GPIOF AF11 (PF4/7/10-15) + PG11 MDC - VDDIO3 is 1.8V — needs HSLV via BSEC shadow FVR124 bit 15 - AXISRAM2+ powered down by default, needs RAMCFG SRAMSD clear - RIMC master 6 (ETH1) needs CID=1, SEC, PRIV config - RCC peripheral enables use SET registers (ENSR at +0x800) - USART clock is 32MHz at HSI (BRR=278 for 115200) - HardFault handler must not access unclocked peripherals (LOCKUP) --- src/port/stm32/stm32_eth.c | 96 +++- src/port/stm32/stm32_eth.h | 2 +- src/port/stm32n6/Makefile | 61 +++ src/port/stm32n6/config.h | 66 +++ src/port/stm32n6/flash.sh | 42 ++ src/port/stm32n6/ivt.c | 62 +++ src/port/stm32n6/main.c | 940 +++++++++++++++++++++++++++++++++++ src/port/stm32n6/openocd.cfg | 5 + src/port/stm32n6/startup.c | 47 ++ src/port/stm32n6/syscalls.c | 141 ++++++ src/port/stm32n6/target.ld | 104 ++++ 11 files changed, 1539 insertions(+), 27 deletions(-) create mode 100644 src/port/stm32n6/Makefile create mode 100644 src/port/stm32n6/config.h create mode 100755 src/port/stm32n6/flash.sh create mode 100644 src/port/stm32n6/ivt.c create mode 100644 src/port/stm32n6/main.c create mode 100644 src/port/stm32n6/openocd.cfg create mode 100644 src/port/stm32n6/startup.c create mode 100644 src/port/stm32n6/syscalls.c create mode 100644 src/port/stm32n6/target.ld diff --git a/src/port/stm32/stm32_eth.c b/src/port/stm32/stm32_eth.c index c57c3825..c89d8a26 100644 --- a/src/port/stm32/stm32_eth.c +++ b/src/port/stm32/stm32_eth.c @@ -1,6 +1,6 @@ /* stm32_eth.c * - * Common Ethernet MAC/PHY driver for STM32H5 and STM32H7. + * Common Ethernet MAC/PHY driver for STM32H5, STM32H7 and STM32N6. * * Copyright (C) 2024-2026 wolfSSL Inc. * @@ -11,8 +11,8 @@ #include "config.h" #include "stm32_eth.h" -#if !defined(STM32H5) && !defined(STM32H7) -#error "Define STM32H5 or STM32H7 for stm32_eth.c" +#if !defined(STM32H5) && !defined(STM32H7) && !defined(STM32N6) +#error "Define STM32H5, STM32H7 or STM32N6 for stm32_eth.c" #endif #if defined(STM32H5) @@ -25,6 +25,21 @@ #elif defined(STM32H7) #define ETH_BASE 0x40028000UL #define STM32_ETH_NEEDS_MDIO_DELAY 1 +#elif defined(STM32N6) +#define ETH_BASE 0x58036000UL /* ETH1 on AHB5, secure alias */ +#define STM32_ETH_NEEDS_MDIO_DELAY 1 /* M55 at 600MHz needs stabilization */ +#endif + +/* MDIO clock divider: CR field in ETH_MACMDIOAR. + * CR=0: 20-35MHz, CR=1: 35-60MHz, CR=2: 60-100MHz, + * CR=3: 100-150MHz, CR=4: 150-250MHz, CR=5: 250-300MHz + * STM32N6 at HSI: HCLK ~32MHz → CR=0 + * STM32N6 at PLL: HCLK 250-300MHz → CR=5 + * STM32H5/H7: HCLK 150-250MHz → CR=4 */ +#if defined(STM32N6) +#define MDIO_CR_VALUE 4U /* PLL: AHB5 ~200MHz → CR=4 (150-250MHz range) */ +#else +#define MDIO_CR_VALUE 4U #endif #define ETH_REG(offset) (*(volatile uint32_t *)(ETH_BASE + (offset))) @@ -33,9 +48,10 @@ /* MAC registers */ #define ETH_MACCR ETH_REG(0x0000U) #define ETH_MACPFR ETH_REG(0x0004U) +#define ETH_MAC1USTCR ETH_REG(0x00DCU) #define ETH_MACA0HR ETH_REG(0x0300U) #define ETH_MACA0LR ETH_REG(0x0304U) -#if defined(STM32H7) +#if defined(STM32H7) || defined(STM32N6) #define ETH_MACRXQC0R ETH_REG(0x00A0U) #endif @@ -64,6 +80,7 @@ #define ETH_MACCR_TE (1U << 1) #define ETH_MACCR_DM (1U << 13) #define ETH_MACCR_FES (1U << 14) +#define ETH_MACCR_PS (1U << 15) /* Port Select: 1=MII/RMII, 0=GMII */ /* DMA bits */ #define ETH_DMAMR_SWR (1U << 0) @@ -154,7 +171,7 @@ struct eth_desc { #define DMA_TPBL 32U #define DMA_RPBL 32U -#if defined(STM32H7) +#if defined(STM32H7) || defined(STM32N6) #define ETH_SECTION __attribute__((section(".eth_buffers"))) #elif defined(STM32H5) #if TZEN_ENABLED @@ -164,6 +181,11 @@ struct eth_desc { #endif #endif +/* On STM32N6, the ETH DMA (RIMC master 6) is configured as non-secure. + * On STM32N6, the ETH DMA is configured as a secure master (CID=1, SEC+PRIV). + * Use secure addresses (0x34xxxxxx) — matches CubeN6 HAL example. */ +#define ETH_DMA_ADDR(ptr) ((uint32_t)(ptr)) + static struct eth_desc rx_ring[RX_DESC_COUNT] __attribute__((aligned(32))) ETH_SECTION; static struct eth_desc tx_ring[TX_DESC_COUNT] __attribute__((aligned(32))) ETH_SECTION; static uint8_t rx_buffers[RX_DESC_COUNT][RX_BUF_SIZE] __attribute__((aligned(32))) ETH_SECTION; @@ -195,14 +217,23 @@ static int eth_hw_reset(void) while ((ETH_DMAMR & ETH_DMAMR_SWR) && (timeout > 0U)) { timeout--; } +#if defined(STM32N6) + /* On STM32N6, the DMA SWR may not self-clear without the PHY REF_CLK. + * After RCC reset, the MAC is already in reset state. Continue anyway + * and clear SWR manually — the MAC/DMA registers are writable. */ + if (timeout == 0U) { + ETH_DMAMR &= ~ETH_DMAMR_SWR; + } +#else if (timeout == 0U) return -1; +#endif #if STM32_ETH_NEEDS_MDIO_DELAY /* Wait for MAC internal state to stabilize after reset. */ eth_delay(400000); /* ~1ms at 400MHz SYSCLK */ /* Pre-configure MDIO clock divider before PHY access. */ - ETH_MACMDIOAR = (4U << ETH_MACMDIOAR_CR_SHIFT); + ETH_MACMDIOAR = (MDIO_CR_VALUE << ETH_MACMDIOAR_CR_SHIFT); #endif return 0; @@ -217,11 +248,18 @@ static void eth_trigger_tx(void) static void eth_config_mac(const uint8_t mac[6]) { uint32_t maccr = ETH_MACCR; - maccr &= ~(ETH_MACCR_DM | ETH_MACCR_FES); + maccr &= ~(ETH_MACCR_DM | ETH_MACCR_FES | ETH_MACCR_PS); maccr |= ETH_MACCR_DM | ETH_MACCR_FES; +#if defined(STM32N6) + /* PS=1 selects MII/RMII port (vs GMII). */ + maccr |= ETH_MACCR_PS; + /* Configure 1µs tick counter — required for MAC internal timing. + * Value = (HCLK_Hz / 1000000) - 1. With PLL: ~300MHz → 299. */ + ETH_MAC1USTCR = 299U; +#endif ETH_MACCR = maccr; -#if defined(STM32H7) +#if defined(STM32H7) || defined(STM32N6) /* Enable RX Queue 0 for DCB/Generic traffic. */ ETH_MACRXQC0R = (2U << 0); #endif @@ -291,22 +329,22 @@ static void eth_init_desc(void) rx_idx = 0; tx_idx = 0; - /* Step 2: Configure DMA registers. */ + /* Step 2: Configure DMA registers (use DMA-visible addresses). */ __asm volatile ("dsb sy" ::: "memory"); - ETH_DMACTXDLAR = (uint32_t)&tx_ring[0]; - ETH_DMACRXDLAR = (uint32_t)&rx_ring[0]; + ETH_DMACTXDLAR = ETH_DMA_ADDR(&tx_ring[0]); + ETH_DMACRXDLAR = ETH_DMA_ADDR(&rx_ring[0]); ETH_DMACTXRLR = TX_DESC_COUNT - 1U; ETH_DMACRXRLR = RX_DESC_COUNT - 1U; - ETH_DMACTXDTPR = (uint32_t)&tx_ring[0]; - ETH_DMACRXDTPR = (uint32_t)&rx_ring[RX_DESC_COUNT - 1U]; + ETH_DMACTXDTPR = ETH_DMA_ADDR(&tx_ring[0]); + ETH_DMACRXDTPR = ETH_DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1U]); __asm volatile ("dsb sy" ::: "memory"); /* Step 3: Now set buffer addresses and OWN bit. */ for (i = 0; i < TX_DESC_COUNT; i++) { - *(volatile uint32_t *)&tx_ring[i].des0 = (uint32_t)tx_buffers[i]; + *(volatile uint32_t *)&tx_ring[i].des0 = ETH_DMA_ADDR(tx_buffers[i]); } for (i = 0; i < RX_DESC_COUNT; i++) { - *(volatile uint32_t *)&rx_ring[i].des0 = (uint32_t)rx_buffers[i]; + *(volatile uint32_t *)&rx_ring[i].des0 = ETH_DMA_ADDR(rx_buffers[i]); *(volatile uint32_t *)&rx_ring[i].des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; } @@ -314,7 +352,7 @@ static void eth_init_desc(void) __asm volatile ("dsb sy" ::: "memory"); __asm volatile ("isb sy" ::: "memory"); /* Step 4: Update tail pointer to signal DMA that descriptors are ready. */ - ETH_DMACRXDTPR = (uint32_t)&rx_ring[RX_DESC_COUNT - 1U]; + ETH_DMACRXDTPR = ETH_DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1U]); /* Final barrier. */ __asm volatile ("dsb sy" ::: "memory"); } @@ -370,8 +408,7 @@ static uint16_t eth_mdio_read(uint32_t phy, uint32_t reg) { uint32_t cfg; eth_mdio_wait_ready(); - /* CR = 4 for HCLK 150-250MHz range. */ - cfg = (4U << ETH_MACMDIOAR_CR_SHIFT) | + cfg = (MDIO_CR_VALUE << ETH_MACMDIOAR_CR_SHIFT) | (reg << ETH_MACMDIOAR_RDA_SHIFT) | (phy << ETH_MACMDIOAR_PA_SHIFT) | (ETH_MACMDIOAR_GOC_READ << ETH_MACMDIOAR_GOC_SHIFT); @@ -385,8 +422,7 @@ static void eth_mdio_write(uint32_t phy, uint32_t reg, uint16_t value) uint32_t cfg; eth_mdio_wait_ready(); ETH_MACMDIODR = (uint32_t)value; - /* CR = 4 for HCLK 150-250MHz range. */ - cfg = (4U << ETH_MACMDIOAR_CR_SHIFT) | + cfg = (MDIO_CR_VALUE << ETH_MACMDIOAR_CR_SHIFT) | (reg << ETH_MACMDIOAR_RDA_SHIFT) | (phy << ETH_MACMDIOAR_PA_SHIFT) | (ETH_MACMDIOAR_GOC_WRITE << ETH_MACMDIOAR_GOC_SHIFT); @@ -414,7 +450,10 @@ static void eth_phy_init(void) if (phy_addr < 0) { phy_addr = eth_detect_phy(); - if (phy_addr < 0) phy_addr = 0; + if (phy_addr < 0) { + phy_addr = 0; + return; /* No PHY found — skip init to avoid long timeouts */ + } } /* Reset PHY. */ @@ -471,7 +510,7 @@ static int eth_poll(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) desc->des1 = 0; desc->des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; __asm volatile ("dsb sy" ::: "memory"); - ETH_DMACRXDTPR = (uint32_t)desc; + ETH_DMACRXDTPR = ETH_DMA_ADDR(desc); rx_idx = (rx_idx + 1U) % RX_DESC_COUNT; return (int)frame_len; @@ -497,7 +536,7 @@ static int eth_send(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) if (dma_len > len) memset(tx_buffers[tx_idx] + len, 0, dma_len - len); /* Setup descriptor. */ - desc->des0 = (uint32_t)tx_buffers[tx_idx]; + desc->des0 = ETH_DMA_ADDR(tx_buffers[tx_idx]); desc->des1 = 0; desc->des2 = (dma_len & ETH_TDES2_B1L_MASK); __asm volatile ("dsb sy" ::: "memory"); @@ -511,7 +550,7 @@ static int eth_send(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) if (tx_idx == 0U) eth_trigger_tx(); next_idx = (tx_idx + 1U) % TX_DESC_COUNT; - ETH_DMACTXDTPR = (uint32_t)&tx_ring[next_idx]; + ETH_DMACTXDTPR = ETH_DMA_ADDR(&tx_ring[next_idx]); tx_idx = next_idx; return (int)len; @@ -527,6 +566,11 @@ static void stm32_eth_generate_mac(uint8_t mac[6]) mac[3] = 0x33; mac[4] = 0x44; mac[5] = 0x55; +#elif defined(STM32N6) + mac[2] = 0xCC; + mac[3] = 0xDD; + mac[4] = 0x55; + mac[5] = 0x66; #else mac[2] = 0xAA; mac[3] = 0xBB; @@ -576,14 +620,14 @@ void stm32_eth_kick_rx(void) uint32_t i; /* Reinitialize all RX descriptors and kick DMA. */ for (i = 0; i < RX_DESC_COUNT; i++) { - *(volatile uint32_t *)&rx_ring[i].des0 = (uint32_t)rx_buffers[i]; + *(volatile uint32_t *)&rx_ring[i].des0 = ETH_DMA_ADDR(rx_buffers[i]); *(volatile uint32_t *)&rx_ring[i].des1 = 0; *(volatile uint32_t *)&rx_ring[i].des2 = 0; *(volatile uint32_t *)&rx_ring[i].des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; } __asm volatile ("dsb sy" ::: "memory"); __asm volatile ("isb sy" ::: "memory"); - ETH_DMACRXDTPR = (uint32_t)&rx_ring[RX_DESC_COUNT - 1U]; + ETH_DMACRXDTPR = ETH_DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1U]); } int stm32_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac) diff --git a/src/port/stm32/stm32_eth.h b/src/port/stm32/stm32_eth.h index fb702eff..6aa06521 100644 --- a/src/port/stm32/stm32_eth.h +++ b/src/port/stm32/stm32_eth.h @@ -1,6 +1,6 @@ /* stm32_eth.h * - * Common STM32H5/STM32H7 Ethernet driver interface. + * Common STM32H5/STM32H7/STM32N6 Ethernet driver interface. * * Copyright (C) 2024-2026 wolfSSL Inc. * diff --git a/src/port/stm32n6/Makefile b/src/port/stm32n6/Makefile new file mode 100644 index 00000000..a5973fec --- /dev/null +++ b/src/port/stm32n6/Makefile @@ -0,0 +1,61 @@ +CC ?= arm-none-eabi-gcc +OBJCOPY ?= arm-none-eabi-objcopy + +ROOT := ../../.. + +# Base compiler flags — Cortex-M55 soft-float +CFLAGS := -mcpu=cortex-m55 -mthumb -mfloat-abi=soft -Os -ffreestanding -fdata-sections -ffunction-sections +CFLAGS += -g -ggdb -Wall -Wextra -Werror +CFLAGS += -I. -I$(ROOT) -I$(ROOT)/src -I$(ROOT)/src/port -I$(ROOT)/src/port/stm32 +CFLAGS += -DSTM32N6 -DTZEN_ENABLED=0 +EXTRA_CFLAGS ?= +CFLAGS += $(EXTRA_CFLAGS) + +LDSCRIPT := target.ld +LDFLAGS := -nostdlib -T $(LDSCRIPT) -Wl,-gc-sections + +# Source files +SRCS := startup.c ivt.c syscalls.c +SRCS += main.c $(ROOT)/src/port/stm32/stm32_eth.c $(ROOT)/src/wolfip.c + +OBJS := $(patsubst %.c,%.o,$(SRCS)) + +all: app.bin + @echo "Built STM32N6 wolfIP echo server" + +app.elf: $(OBJS) $(LDSCRIPT) + $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -Wl,--start-group -lc -lm -lgcc -lnosys -Wl,--end-group -o $@ + +app.bin: app.elf + $(OBJCOPY) -O binary $< $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f *.o app.elf app.bin + rm -f $(ROOT)/src/*.o + rm -f $(ROOT)/src/port/*.o + rm -f $(ROOT)/src/port/stm32/*.o + +size: app.elf + @echo "=== Memory Usage ===" + @arm-none-eabi-size app.elf + +flash: app.bin + bash flash.sh + +monitor: + picocom -b 115200 /dev/ttyACM0 + +debug: app.elf + openocd -f openocd.cfg & + arm-none-eabi-gdb app.elf \ + -ex "target remote :3333" \ + -ex "monitor reset init" \ + -ex "load" \ + -ex "monitor mww 0xE000ED08 0x34000000" \ + -ex "break main" \ + -ex "continue" + +.PHONY: all clean size flash monitor debug diff --git a/src/port/stm32n6/config.h b/src/port/stm32n6/config.h new file mode 100644 index 00000000..a703e556 --- /dev/null +++ b/src/port/stm32n6/config.h @@ -0,0 +1,66 @@ +/* config.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP 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. + * + * wolfIP 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 + */ +#ifndef WOLF_CONFIG_H +#define WOLF_CONFIG_H + +#ifndef CONFIG_IPFILTER +#define CONFIG_IPFILTER 0 +#endif + +#define ETHERNET +#define LINK_MTU 1536 + +#define MAX_TCPSOCKETS 4 +#define MAX_UDPSOCKETS 2 +#define MAX_ICMPSOCKETS 1 +#define RXBUF_SIZE (LINK_MTU * 2) +#define TXBUF_SIZE (LINK_MTU * 2) + +#define MAX_NEIGHBORS 16 + +#ifndef WOLFIP_MAX_INTERFACES +#define WOLFIP_MAX_INTERFACES 1 +#endif + +#ifndef WOLFIP_ENABLE_FORWARDING +#define WOLFIP_ENABLE_FORWARDING 0 +#endif + +#ifndef WOLFIP_ENABLE_LOOPBACK +#define WOLFIP_ENABLE_LOOPBACK 0 +#endif + +#ifndef WOLFIP_ENABLE_DHCP +#define WOLFIP_ENABLE_DHCP 0 +#endif + +/* Static IP configuration */ +#define WOLFIP_IP "192.168.12.11" +#define WOLFIP_NETMASK "255.255.255.0" +#define WOLFIP_GW "192.168.12.1" + +#if WOLFIP_ENABLE_DHCP +#define DHCP +#define DHCP_DISCOVER_RETRIES 1 +#define DHCP_REQUEST_RETRIES 1 +#endif + +#endif /* WOLF_CONFIG_H */ diff --git a/src/port/stm32n6/flash.sh b/src/port/stm32n6/flash.sh new file mode 100755 index 00000000..2a8d445f --- /dev/null +++ b/src/port/stm32n6/flash.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Flash wolfIP to STM32N6 AXISRAM via OpenOCD +set -e +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +BIN="${SCRIPT_DIR}/app.bin" +CFG="${SCRIPT_DIR}/openocd.cfg" + +[ -f "$BIN" ] || { echo "app.bin not found. Run 'make' first."; exit 1; } + +# Extract initial SP (word 0) and entry point (word 1) from vector table +INIT_SP=$(od -A n -t x4 -N 4 "$BIN" | awk '{print "0x"$1}') +ENTRY=$(od -A n -j 4 -t x4 -N 4 "$BIN" | awk '{print "0x"$1}') +ENTRY_THUMB=$(printf "0x%08x" $(( ENTRY | 1 ))) + +echo "Loading app.bin to AXISRAM1 (0x34000000)" +echo " SP: ${INIT_SP}, Entry: ${ENTRY_THUMB}" + +# Try normal reset init first. If it fails (CPU in LOCKUP), try recovery +# via AP0 AIRCR write, then retry. +openocd -f "$CFG" -c " + reset init; + load_image ${BIN} 0x34000000 bin; + reg msplim_s 0x00000000; + reg psplim_s 0x00000000; + reg msp ${INIT_SP}; + mww 0xE000ED08 0x34000000; + mww 0xE000ED28 0xFFFFFFFF; + resume ${ENTRY_THUMB}; + shutdown +" 2>&1 +STATUS=$? + +if [ $STATUS -ne 0 ]; then + echo "" + echo "*** Normal flash failed (CPU may be in LOCKUP state) ***" + echo "Please press the RESET button on the NUCLEO board, then re-run this script." + echo "" + echo "If the board is unrecoverable, unplug and replug the USB cable." + exit 1 +fi + +echo "Done. Monitor UART: picocom -b 115200 /dev/ttyACM0" diff --git a/src/port/stm32n6/ivt.c b/src/port/stm32n6/ivt.c new file mode 100644 index 00000000..cb70f113 --- /dev/null +++ b/src/port/stm32n6/ivt.c @@ -0,0 +1,62 @@ +/* ivt.c + * + * Cortex-M55 interrupt vector table for STM32N6. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP 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. + * + * wolfIP 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 + +extern void Reset_Handler(void); +extern unsigned long _estack; + +static void default_handler(void) +{ + while (1) { } +} + +void NMI_Handler(void) __attribute__((weak, alias("default_handler"))); +void HardFault_Handler(void) __attribute__((weak, alias("default_handler"))); +void MemManage_Handler(void) __attribute__((weak, alias("default_handler"))); +void BusFault_Handler(void) __attribute__((weak, alias("default_handler"))); +void UsageFault_Handler(void) __attribute__((weak, alias("default_handler"))); +void SecureFault_Handler(void)__attribute__((weak, alias("default_handler"))); +void SVC_Handler(void) __attribute__((weak, alias("default_handler"))); +void DebugMon_Handler(void) __attribute__((weak, alias("default_handler"))); +void PendSV_Handler(void) __attribute__((weak, alias("default_handler"))); +void SysTick_Handler(void) __attribute__((weak, alias("default_handler"))); + +/* Cortex-M55 vector table: 16 system + 200 IRQs (ETH1_IRQn = 179) */ +__attribute__((section(".isr_vector"))) +const uint32_t vector_table[16 + 200] = { + [0] = (uint32_t)&_estack, + [1] = (uint32_t)&Reset_Handler, + [2] = (uint32_t)&NMI_Handler, + [3] = (uint32_t)&HardFault_Handler, + [4] = (uint32_t)&MemManage_Handler, + [5] = (uint32_t)&BusFault_Handler, + [6] = (uint32_t)&UsageFault_Handler, + [7] = (uint32_t)&SecureFault_Handler, + [8] = 0, [9] = 0, [10] = 0, + [11] = (uint32_t)&SVC_Handler, + [12] = (uint32_t)&DebugMon_Handler, + [13] = 0, + [14] = (uint32_t)&PendSV_Handler, + [15] = (uint32_t)&SysTick_Handler, + [16 ... 215] = (uint32_t)&default_handler +}; diff --git a/src/port/stm32n6/main.c b/src/port/stm32n6/main.c new file mode 100644 index 00000000..75a9b07c --- /dev/null +++ b/src/port/stm32n6/main.c @@ -0,0 +1,940 @@ +/* main.c + * + * STM32N6 (NUCLEO-N657X0-Q, Cortex-M55) wolfIP echo server. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP 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. + * + * wolfIP 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 "config.h" +#include "wolfip.h" +#include "stm32_eth.h" + +/* Forward declarations */ +static void uart_puts(const char *s); +static void delay(uint32_t count); + +/* ========================================================================= + * RCC — Reset and Clock Control (base 0x56028000, secure bus) + * ========================================================================= */ +#define RCC_BASE 0x56028000UL + +/* Control: direct read / set (+0x800) / clear (+0x1000) */ +#define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x000u)) +#define RCC_CSR (*(volatile uint32_t *)(RCC_BASE + 0x800u)) +#define RCC_CCR (*(volatile uint32_t *)(RCC_BASE + 0x1000u)) +#define RCC_CR_HSION (1u << 3) +#define RCC_CR_PLL1ON (1u << 8) + +/* Status — ready flags */ +#define RCC_SR (*(volatile uint32_t *)(RCC_BASE + 0x04u)) +#define RCC_SR_HSIRDY (1u << 3) +#define RCC_SR_PLL1RDY (1u << 8) + +/* Clock switching */ +#define RCC_CFGR1 (*(volatile uint32_t *)(RCC_BASE + 0x20u)) +#define RCC_CFGR1_CPUSW_SHIFT 16u +#define RCC_CFGR1_CPUSW_MASK (0x3u << 16u) +#define RCC_CFGR1_CPUSWS_SHIFT 20u +#define RCC_CFGR1_CPUSWS_MASK (0x3u << 20u) +#define RCC_CFGR1_SYSSW_SHIFT 24u +#define RCC_CFGR1_SYSSW_MASK (0x3u << 24u) +#define RCC_CFGR1_SYSSWS_SHIFT 28u +#define RCC_CFGR1_SYSSWS_MASK (0x3u << 28u) + +/* AHB/APB prescalers */ +#define RCC_CFGR2 (*(volatile uint32_t *)(RCC_BASE + 0x24u)) +#define RCC_CFGR2_HPRE_SHIFT 20u +#define RCC_CFGR2_HPRE_MASK (0x7u << 20u) + +/* PLL1 configuration */ +#define RCC_PLL1CFGR1 (*(volatile uint32_t *)(RCC_BASE + 0x80u)) +#define RCC_PLL1CFGR1_SEL_MASK (0x7u << 28u) +#define RCC_PLL1CFGR1_DIVM_MASK (0x3Fu << 20u) +#define RCC_PLL1CFGR1_DIVN_MASK (0xFFFu << 8u) +#define RCC_PLL1CFGR1_BYP (1u << 27u) +#define RCC_PLL1CFGR1_SEL_HSI (0x0u << 28u) +#define RCC_PLL1CFGR1_DIVM_SHIFT 20u +#define RCC_PLL1CFGR1_DIVN_SHIFT 8u + +#define RCC_PLL1CFGR2 (*(volatile uint32_t *)(RCC_BASE + 0x84u)) + +#define RCC_PLL1CFGR3 (*(volatile uint32_t *)(RCC_BASE + 0x88u)) +#define RCC_PLL1CFGR3_PDIV1_SHIFT 27u +#define RCC_PLL1CFGR3_PDIV2_SHIFT 24u +#define RCC_PLL1CFGR3_MODSSDIS (1u << 2u) +#define RCC_PLL1CFGR3_MODSSRST (1u << 0u) +#define RCC_PLL1CFGR3_PDIVEN (1u << 30u) + +/* IC dividers */ +#define RCC_IC1CFGR (*(volatile uint32_t *)(RCC_BASE + 0xC4u)) +#define RCC_IC2CFGR (*(volatile uint32_t *)(RCC_BASE + 0xC8u)) +#define RCC_IC6CFGR (*(volatile uint32_t *)(RCC_BASE + 0xD8u)) +#define RCC_IC11CFGR (*(volatile uint32_t *)(RCC_BASE + 0xECu)) +#define RCC_ICCFGR_INT_SHIFT 16u +#define RCC_ICCFGR_SEL_PLL1 (0x0u << 28u) + +/* Divider enable: direct / set (+0x800) / clear (+0x1000) */ +#define RCC_DIVENR (*(volatile uint32_t *)(RCC_BASE + 0x240u)) +#define RCC_DIVENSR (*(volatile uint32_t *)(RCC_BASE + 0xA40u)) +#define RCC_DIVENCR (*(volatile uint32_t *)(RCC_BASE + 0x1240u)) +#define RCC_DIVENR_IC1EN (1u << 0u) +#define RCC_DIVENR_IC2EN (1u << 1u) +#define RCC_DIVENR_IC6EN (1u << 5u) +#define RCC_DIVENR_IC11EN (1u << 10u) + +/* Peripheral clock enable — use SET registers (write 1s to set bits). + * Direct ENR registers are read-only status; ENSR is the set register. */ +#define RCC_AHB1ENR (*(volatile uint32_t *)(RCC_BASE + 0x250u)) /* read */ +#define RCC_AHB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x254u)) /* read */ +#define RCC_AHB3ENR (*(volatile uint32_t *)(RCC_BASE + 0x258u)) /* read */ +#define RCC_AHB4ENR (*(volatile uint32_t *)(RCC_BASE + 0x25Cu)) /* read */ +#define RCC_AHB5ENR (*(volatile uint32_t *)(RCC_BASE + 0x260u)) /* read */ +#define RCC_APB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x26Cu)) /* read */ +#define RCC_MEMENR (*(volatile uint32_t *)(RCC_BASE + 0x24Cu)) /* read */ + +/* SET registers — write-only, bits written as 1 get set in ENR */ +#define RCC_AHB2ENSR (*(volatile uint32_t *)(RCC_BASE + 0xA54u)) +#define RCC_AHB3ENSR (*(volatile uint32_t *)(RCC_BASE + 0xA58u)) +#define RCC_AHB4ENSR (*(volatile uint32_t *)(RCC_BASE + 0xA5Cu)) +#define RCC_AHB5ENSR (*(volatile uint32_t *)(RCC_BASE + 0xA60u)) +#define RCC_APB2ENSR (*(volatile uint32_t *)(RCC_BASE + 0xA6Cu)) +#define RCC_MEMENSR (*(volatile uint32_t *)(RCC_BASE + 0xA4Cu)) + +/* Clock configuration — ETH PHY interface selection */ +#define RCC_CCIPR2 (*(volatile uint32_t *)(RCC_BASE + 0x148u)) +#define RCC_CCIPR2_ETH1SEL_RMII (0x4u << 16u) /* bit 18 = RMII mode */ + +/* Peripheral reset */ +#define RCC_AHB1RSTR (*(volatile uint32_t *)(RCC_BASE + 0x210u)) +#define RCC_AHB5RSTR (*(volatile uint32_t *)(RCC_BASE + 0x220u)) + +/* ========================================================================= + * GPIO — secure bus addresses + * ========================================================================= */ +#define GPIOA_BASE 0x56020000UL +#define GPIOB_BASE 0x56020400UL +#define GPIOC_BASE 0x56020800UL +#define GPIOE_BASE 0x56021000UL +#define GPIOF_BASE 0x56021400UL +#define GPIOG_BASE 0x56021800UL +#define GPIOO_BASE 0x56023800UL + +#define GPIO_MODER(base) (*(volatile uint32_t *)((base) + 0x00u)) +#define GPIO_OSPEEDR(base) (*(volatile uint32_t *)((base) + 0x08u)) +#define GPIO_PUPDR(base) (*(volatile uint32_t *)((base) + 0x0Cu)) +#define GPIO_ODR(base) (*(volatile uint32_t *)((base) + 0x14u)) +#define GPIO_BSRR(base) (*(volatile uint32_t *)((base) + 0x18u)) +#define GPIO_AFRL(base) (*(volatile uint32_t *)((base) + 0x20u)) +#define GPIO_AFRH(base) (*(volatile uint32_t *)((base) + 0x24u)) + +/* ========================================================================= + * PWR — Power Control (base 0x56024800) + * ========================================================================= */ +#define PWR_BASE 0x56024800UL +#define PWR_SVMCR1 (*(volatile uint32_t *)(PWR_BASE + 0x34u)) +#define PWR_SVMCR2 (*(volatile uint32_t *)(PWR_BASE + 0x38u)) +#define PWR_SVMCR3 (*(volatile uint32_t *)(PWR_BASE + 0x3Cu)) +#define PWR_SVMCR1_VDDIO4SV (1u << 8u) +#define PWR_SVMCR2_VDDIO5SV (1u << 8u) +#define PWR_SVMCR3_VDDIO2SV (1u << 8u) +#define PWR_SVMCR3_VDDIO3SV (1u << 9u) + +/* ========================================================================= + * USART1 — PE5 (TX) / PE6 (RX), AF7 (base 0x52001000) + * ========================================================================= */ +#define USART1_BASE 0x52001000UL +#define USART1_CR1 (*(volatile uint32_t *)(USART1_BASE + 0x00u)) +#define USART1_CR2 (*(volatile uint32_t *)(USART1_BASE + 0x04u)) +#define USART1_CR3 (*(volatile uint32_t *)(USART1_BASE + 0x08u)) +#define USART1_BRR (*(volatile uint32_t *)(USART1_BASE + 0x0Cu)) +#define USART1_ISR (*(volatile uint32_t *)(USART1_BASE + 0x1Cu)) +#define USART1_TDR (*(volatile uint32_t *)(USART1_BASE + 0x28u)) + +/* ========================================================================= + * SCB — Cortex-M55 cache control + * ========================================================================= */ +#define SCB_CCR (*(volatile uint32_t *)(0xE000ED14UL)) +#define SCB_CCR_IC (1u << 17u) +#define SCB_CCR_DC (1u << 16u) +#define SCB_ICIALLU (*(volatile uint32_t *)(0xE000EF50UL)) + +/* ========================================================================= + * MPU — Cortex-M55 ARMv8.1-M Memory Protection Unit + * ========================================================================= */ +#define MPU_TYPE (*(volatile uint32_t *)(0xE000ED90UL)) +#define MPU_CTRL (*(volatile uint32_t *)(0xE000ED94UL)) +#define MPU_RNR (*(volatile uint32_t *)(0xE000ED98UL)) +#define MPU_RBAR (*(volatile uint32_t *)(0xE000ED9CUL)) +#define MPU_RLAR (*(volatile uint32_t *)(0xE000EDA0UL)) +#define MPU_MAIR0 (*(volatile uint32_t *)(0xE000EDC0UL)) +#define MPU_MAIR1 (*(volatile uint32_t *)(0xE000EDC4UL)) + +/* ========================================================================= + * Fault registers + * ========================================================================= */ +#define SCB_HFSR (*(volatile uint32_t *)0xE000ED2CUL) +#define SCB_CFSR (*(volatile uint32_t *)0xE000ED28UL) +#define SCB_BFAR (*(volatile uint32_t *)0xE000ED38UL) +#define SCB_MMFAR (*(volatile uint32_t *)0xE000ED34UL) + +/* ========================================================================= + * Barrier macros + * ========================================================================= */ +#define DSB() __asm volatile ("dsb sy" ::: "memory") +#define ISB() __asm volatile ("isb sy" ::: "memory") +#define DMB() __asm volatile ("dmb sy" ::: "memory") + +/* ========================================================================= + * LED — LD1 green on PO1 (GPIOO), LD2 red on PG10 (GPIOG) + * Note: These are from the N6570-DK DTS. NUCLEO board may differ. + * ========================================================================= */ +#define LED1_PORT GPIOO_BASE +#define LED1_PIN 1u +#define LED1_RCC_BIT 14u /* RCC_AHB4ENR bit for GPIOO */ + +#define ECHO_PORT 7 +#define RX_BUF_SIZE 1024 + +static struct wolfIP *IPStack; +static int listen_fd = -1; +static int client_fd = -1; +static uint8_t rx_buf[RX_BUF_SIZE]; + +/* ========================================================================= + * HardFault Handler — safe version that won't cause LOCKUP + * + * IMPORTANT: Do NOT access USART or other peripherals here unless we know + * their clocks are enabled. Accessing unclocked peripherals from the fault + * handler causes a double-fault → CPU LOCKUP (unrecoverable without NRST). + * ========================================================================= */ +static volatile int uart_ready = 0; + +#define FAULT_USART1_ISR (*(volatile uint32_t *)(USART1_BASE + 0x1Cu)) +#define FAULT_USART1_TDR (*(volatile uint32_t *)(USART1_BASE + 0x28u)) + +static void fault_uart_putc(char c) +{ + while ((FAULT_USART1_ISR & (1u << 7)) == 0) { } + FAULT_USART1_TDR = (uint32_t)c; +} + +static void fault_uart_puts(const char *s) +{ + while (*s) { + if (*s == '\n') fault_uart_putc('\r'); + fault_uart_putc(*s++); + } +} + +static void fault_uart_puthex(uint32_t val) +{ + const char hex[] = "0123456789ABCDEF"; + fault_uart_puts("0x"); + for (int i = 28; i >= 0; i -= 4) + fault_uart_putc(hex[(val >> i) & 0xF]); +} + +void hard_fault_handler_c(uint32_t *frame) +{ + if (uart_ready) { + fault_uart_puts("\n\n*** HARD FAULT ***\n"); + fault_uart_puts(" PC: "); fault_uart_puthex(frame[6]); fault_uart_puts("\n"); + fault_uart_puts(" LR: "); fault_uart_puthex(frame[5]); fault_uart_puts("\n"); + fault_uart_puts(" R0: "); fault_uart_puthex(frame[0]); fault_uart_puts("\n"); + fault_uart_puts(" R1: "); fault_uart_puthex(frame[1]); fault_uart_puts("\n"); + fault_uart_puts(" R2: "); fault_uart_puthex(frame[2]); fault_uart_puts("\n"); + fault_uart_puts(" R3: "); fault_uart_puthex(frame[3]); fault_uart_puts("\n"); + fault_uart_puts(" R12: "); fault_uart_puthex(frame[4]); fault_uart_puts("\n"); + fault_uart_puts(" xPSR: "); fault_uart_puthex(frame[7]); fault_uart_puts("\n"); + fault_uart_puts(" HFSR: "); fault_uart_puthex(SCB_HFSR); fault_uart_puts("\n"); + fault_uart_puts(" CFSR: "); fault_uart_puthex(SCB_CFSR); fault_uart_puts("\n"); + if (SCB_CFSR & 0x00008200u) { + fault_uart_puts(" BFAR: "); fault_uart_puthex(SCB_BFAR); fault_uart_puts("\n"); + } + if (SCB_CFSR & 0x00000082u) { + fault_uart_puts(" MMFAR:"); fault_uart_puthex(SCB_MMFAR); fault_uart_puts("\n"); + } + } + /* Bare spin — do NOT access any peripherals (GPIO, UART) here. + * If the fault happens before peripheral clocks are enabled, + * any peripheral access would cause a double-fault → LOCKUP. + * Use GDB to inspect fault registers instead. */ + while (1) { } +} + +void HardFault_Handler(void) __attribute__((naked)); +void HardFault_Handler(void) +{ + __asm volatile( + "tst lr, #4 \n" + "ite eq \n" + "mrseq r0, msp \n" + "mrsne r0, psp \n" + "b hard_fault_handler_c \n" + ); +} + +/* ========================================================================= + * Clock Configuration — PLL1 → 600 MHz CPU + * + * HSI 64 MHz → PLL1 (M=4, N=75) → VCO 1200 MHz → PDIV1=1 → 1200 MHz + * IC1 /2 = 600 MHz → CPU + * IC2 /3 = 400 MHz → AXI bus + * IC6 /4 = 300 MHz → system bus C + * IC11/3 = 400 MHz → system bus D + * AHB prescaler /2 → HCLK = 300 MHz + * ========================================================================= */ +static void clock_config(void) +{ + uint32_t reg; + + /* Enable HSI 64 MHz */ + RCC_CSR = RCC_CR_HSION; + while (!(RCC_SR & RCC_SR_HSIRDY)) + ; + + /* Disable PLL1 before reconfiguring */ + RCC_CCR = RCC_CR_PLL1ON; + while (RCC_SR & RCC_SR_PLL1RDY) + ; + + /* PLL1: HSI / 4 * 75 = 1200 MHz VCO. + * Clear BYP — Boot ROM leaves it set. */ + reg = RCC_PLL1CFGR1; + reg &= ~(RCC_PLL1CFGR1_SEL_MASK | RCC_PLL1CFGR1_DIVM_MASK | + RCC_PLL1CFGR1_DIVN_MASK | RCC_PLL1CFGR1_BYP); + reg |= RCC_PLL1CFGR1_SEL_HSI | + (4u << RCC_PLL1CFGR1_DIVM_SHIFT) | + (75u << RCC_PLL1CFGR1_DIVN_SHIFT); + RCC_PLL1CFGR1 = reg; + + RCC_PLL1CFGR2 = 0; /* no fractional */ + + /* PDIV1=1, PDIV2=1 → PLL output = VCO = 1200 MHz */ + RCC_PLL1CFGR3 = (1u << RCC_PLL1CFGR3_PDIV1_SHIFT) | + (1u << RCC_PLL1CFGR3_PDIV2_SHIFT) | + RCC_PLL1CFGR3_MODSSDIS | + RCC_PLL1CFGR3_MODSSRST | + RCC_PLL1CFGR3_PDIVEN; + + /* Enable PLL1, wait for lock */ + RCC_CSR = RCC_CR_PLL1ON; + while (!(RCC_SR & RCC_SR_PLL1RDY)) + ; + + /* Configure IC dividers: disable → configure → re-enable */ + RCC_DIVENCR = RCC_DIVENR_IC1EN; + RCC_IC1CFGR = RCC_ICCFGR_SEL_PLL1 | ((2u - 1u) << RCC_ICCFGR_INT_SHIFT); + RCC_DIVENSR = RCC_DIVENR_IC1EN; + + RCC_DIVENCR = RCC_DIVENR_IC2EN; + RCC_IC2CFGR = RCC_ICCFGR_SEL_PLL1 | ((3u - 1u) << RCC_ICCFGR_INT_SHIFT); + RCC_DIVENSR = RCC_DIVENR_IC2EN; + + RCC_DIVENCR = RCC_DIVENR_IC6EN; + RCC_IC6CFGR = RCC_ICCFGR_SEL_PLL1 | ((4u - 1u) << RCC_ICCFGR_INT_SHIFT); + RCC_DIVENSR = RCC_DIVENR_IC6EN; + + RCC_DIVENCR = RCC_DIVENR_IC11EN; + RCC_IC11CFGR = RCC_ICCFGR_SEL_PLL1 | ((3u - 1u) << RCC_ICCFGR_INT_SHIFT); + RCC_DIVENSR = RCC_DIVENR_IC11EN; + + /* AHB prescaler /2 → HCLK = 300 MHz */ + reg = RCC_CFGR2; + reg &= ~RCC_CFGR2_HPRE_MASK; + reg |= (1u << RCC_CFGR2_HPRE_SHIFT); + RCC_CFGR2 = reg; + + /* Switch CPU to IC1, system bus to IC2/IC6/IC11 */ + reg = RCC_CFGR1; + reg &= ~(RCC_CFGR1_CPUSW_MASK | RCC_CFGR1_SYSSW_MASK); + reg |= (0x3u << RCC_CFGR1_CPUSW_SHIFT) | + (0x3u << RCC_CFGR1_SYSSW_SHIFT); + RCC_CFGR1 = reg; + while ((RCC_CFGR1 & RCC_CFGR1_CPUSWS_MASK) != + (0x3u << RCC_CFGR1_CPUSWS_SHIFT)) + ; + while ((RCC_CFGR1 & RCC_CFGR1_SYSSWS_MASK) != + (0x3u << RCC_CFGR1_SYSSWS_SHIFT)) + ; +} + +/* ========================================================================= + * Power — mark VDDIO supplies valid + * ========================================================================= */ +static void pwr_enable_io_supply(void) +{ + /* Enable PWR peripheral clock */ + RCC_AHB4ENSR = (1u << 18u); /* PWREN */ + DMB(); + PWR_SVMCR1 |= PWR_SVMCR1_VDDIO4SV; + PWR_SVMCR2 |= PWR_SVMCR2_VDDIO5SV; + PWR_SVMCR3 |= PWR_SVMCR3_VDDIO2SV | PWR_SVMCR3_VDDIO3SV; + DMB(); +} + +/* ========================================================================= + * Cache + * ========================================================================= */ +static void icache_enable(void) +{ + DSB(); ISB(); + SCB_ICIALLU = 0; + DSB(); ISB(); + SCB_CCR |= SCB_CCR_IC; + DSB(); ISB(); +} + +static void dcache_enable(void) +{ + DSB(); + SCB_CCR |= SCB_CCR_DC; + DSB(); ISB(); +} + +/* ========================================================================= + * MPU — Configure ETH DMA buffer region as non-cacheable + * + * Cortex-M55 ARMv8.1-M MPU: + * RBAR: base address | AP[2:1] | XN[0] + * RLAR: limit address | AttrIdx[3:1] | EN[0] + * MAIR: attribute encoding per index + * + * ETH buffers (.eth_buffers) are in AXISRAM2 at 0x34100000. + * ========================================================================= */ +static void mpu_configure_eth_nocache(void) +{ + /* Disable MPU */ + MPU_CTRL = 0; + DSB(); + + /* Region 0: AXISRAM2 (ETH DMA buffers), non-cacheable Normal memory */ + MPU_RNR = 0; + MPU_RBAR = 0x34100000u | (1u << 1u) | (1u << 0u); + MPU_RLAR = (0x3410FFFFu & ~0x1Fu) | (2u << 1u) | 1u; + + /* MAIR0: Attr2 (bits [23:16]) = 0x44 → Normal, Non-cacheable */ + MPU_MAIR0 = (0x44u << 16u); + + /* Enable MPU + PRIVDEFENA (default map for other regions) */ + MPU_CTRL = 5u; + DSB(); ISB(); +} + +/* ========================================================================= + * Simple delay + * ========================================================================= */ +static void delay(uint32_t count) +{ + for (volatile uint32_t i = 0; i < count; i++) { } +} + +/* ========================================================================= + * LED — LD1 green on PO1 + * ========================================================================= */ +static void led_init(void) +{ + uint32_t moder; + + /* Enable GPIOO clock */ + RCC_AHB4ENSR = (1u << LED1_RCC_BIT); + delay(100); + + /* Set PO1 as output */ + moder = GPIO_MODER(LED1_PORT); + moder &= ~(3u << (LED1_PIN * 2u)); + moder |= (1u << (LED1_PIN * 2u)); + GPIO_MODER(LED1_PORT) = moder; +} + +static void led_on(void) +{ + GPIO_BSRR(LED1_PORT) = (1u << LED1_PIN); +} + +static void led_off(void) +{ + GPIO_BSRR(LED1_PORT) = (1u << (LED1_PIN + 16u)); +} + +static void led_toggle(void) +{ + GPIO_ODR(LED1_PORT) ^= (1u << LED1_PIN); +} + +/* ========================================================================= + * UART — USART1 on PE5 (TX) / PE6 (RX), AF7 + * + * HCLK (APB2 clock after PLL) = 300 MHz + * BRR = 300000000 / 115200 = 2604 + * ========================================================================= */ +static void uart_init(void) +{ + uint32_t moder, afr; + + /* Enable GPIOE + USART1 clocks */ + RCC_AHB4ENSR = (1u << 4u); /* GPIOEEN */ + RCC_APB2ENSR = (1u << 4u); /* USART1EN */ + delay(100); + + /* PE5 + PE6 → AF mode */ + moder = GPIO_MODER(GPIOE_BASE); + moder &= ~((3u << (5u * 2u)) | (3u << (6u * 2u))); + moder |= (2u << (5u * 2u)) | (2u << (6u * 2u)); + GPIO_MODER(GPIOE_BASE) = moder; + + /* High speed */ + GPIO_OSPEEDR(GPIOE_BASE) |= (3u << (5u * 2u)) | (3u << (6u * 2u)); + + /* AF7 for PE5 and PE6 (both in AFRL since pins < 8) */ + afr = GPIO_AFRL(GPIOE_BASE); + afr &= ~((0xFu << (5u * 4u)) | (0xFu << (6u * 4u))); + afr |= (7u << (5u * 4u)) | (7u << (6u * 4u)); + GPIO_AFRL(GPIOE_BASE) = afr; + + /* Configure USART1: 115200 baud + * After reset the CPU runs at HSI 64 MHz, but the APB2 clock feeding + * USART1 is 32 MHz (IC2 default divider = 2). + * BRR is re-configured after clock_config() switches to PLL. */ + USART1_CR1 = 0; + USART1_CR2 = 0; + USART1_CR3 = 0; + USART1_BRR = 32000000u / 115200u; /* 278 */ + + /* Enable transmitter + receiver + USART */ + USART1_CR1 = (1u << 3u) | (1u << 2u); /* TE + RE */ + delay(10); + USART1_CR1 |= (1u << 0u); /* UE */ + delay(100); +} + +static void uart_putc(char c) +{ + while ((USART1_ISR & (1u << 7u)) == 0) { } /* Wait for TXE */ + USART1_TDR = (uint32_t)c; +} + +static void uart_puts(const char *s) +{ + while (*s) { + if (*s == '\n') uart_putc('\r'); + uart_putc(*s++); + } +} + +static void uart_puthex(uint32_t val) +{ + const char hex[] = "0123456789ABCDEF"; + uart_puts("0x"); + for (int i = 28; i >= 0; i -= 4) { + uart_putc(hex[(val >> i) & 0xF]); + } +} + +static void uart_puthex8(uint8_t val) +{ + const char hex[] = "0123456789ABCDEF"; + uart_putc(hex[(val >> 4) & 0xF]); + uart_putc(hex[val & 0xF]); +} + +static void uart_putdec(uint32_t val) +{ + char buf[12]; + int i = 0; + if (val == 0) { + uart_putc('0'); + return; + } + while (val > 0 && i < 11) { + buf[i++] = '0' + (val % 10); + val /= 10; + } + while (i > 0) { + uart_putc(buf[--i]); + } +} + +static void uart_putip4(ip4 ip) +{ + uart_putdec((ip >> 24) & 0xFF); + uart_putc('.'); + uart_putdec((ip >> 16) & 0xFF); + uart_putc('.'); + uart_putdec((ip >> 8) & 0xFF); + uart_putc('.'); + uart_putdec(ip & 0xFF); +} + +/* wolfIP requires this symbol */ +uint32_t wolfIP_getrandom(void) +{ + /* Simple LFSR-based PRNG — no HW RNG on initial bring-up. + * Replace with TRNG when available. */ + static uint32_t state = 0xDEADBEEF; + state ^= state << 13; + state ^= state >> 17; + state ^= state << 5; + return state; +} + +/* ========================================================================= + * SYSCFG — I/O compensation for VDDIO3 (Ethernet GPIOF pins) + * + * SYSCFG base = 0x56008000 (secure, APB4 + 0x8000). + * VDDIO3CCCR (offset 0x5C): compensation cell control for GPIOF. + * bit 8 = EN (enable compensation cell) + * bit 9 = CS (code selection: 0=auto, 1=manual) + * VDDIO3CCSR (offset 0x60): compensation cell status (RDY bit). + * ========================================================================= */ +#define SYSCFG_BASE 0x56008000UL +#define SYSCFG_VDDIO3CCCR (*(volatile uint32_t *)(SYSCFG_BASE + 0x5Cu)) +#define SYSCFG_VDDIO3CCSR (*(volatile uint32_t *)(SYSCFG_BASE + 0x60u)) + +/* RCC clock for SYSCFG — APB4ENR2 at offset 0x278, bit 0 = SYSCFGEN */ +#define RCC_APB4ENR2 (*(volatile uint32_t *)(RCC_BASE + 0x278u)) +#define RCC_APB4ENSR2 (*(volatile uint32_t *)(RCC_BASE + 0xA78u)) + +/* ========================================================================= + * Ethernet GPIO — RMII pin configuration (NUCLEO-N657X0-Q) + * + * Pin mapping (AF11) — all on GPIOF except MDC on PG11: + * PF4 — ETH_MDIO PG11 — ETH_MDC + * PF7 — ETH_REF_CLK PF10 — ETH_CRS_DV + * PF11 — ETH_TX_EN PF12 — ETH_TXD0 PF13 — ETH_TXD1 + * PF14 — ETH_RXD0 PF15 — ETH_RXD1 + * ========================================================================= */ +static void gpio_eth_pin(uint32_t base, uint32_t pin) +{ + uint32_t moder, ospeedr, afr; + uint32_t pos2 = pin * 2u; + + /* Alternate function mode (0b10) */ + moder = GPIO_MODER(base); + moder &= ~(3u << pos2); + moder |= (2u << pos2); + GPIO_MODER(base) = moder; + + /* High speed */ + ospeedr = GPIO_OSPEEDR(base); + ospeedr |= (3u << pos2); + GPIO_OSPEEDR(base) = ospeedr; + + /* AF11 for Ethernet */ + if (pin < 8u) { + afr = GPIO_AFRL(base); + afr &= ~(0xFu << (pin * 4u)); + afr |= (11u << (pin * 4u)); + GPIO_AFRL(base) = afr; + } else { + afr = GPIO_AFRH(base); + afr &= ~(0xFu << ((pin - 8u) * 4u)); + afr |= (11u << ((pin - 8u) * 4u)); + GPIO_AFRH(base) = afr; + } +} + +static void eth_gpio_init(void) +{ + /* Enable GPIO clocks: F and G */ + RCC_AHB4ENSR = (1u << 5u) | (1u << 6u); /* GPIOFEN + GPIOGEN */ + delay(1000); + + /* Power on AXISRAM2 for ETH DMA buffers. + * AXISRAM2-6 are powered down by default after boot ROM. */ + { + volatile uint32_t *ramcfg_sram2_cr = (volatile uint32_t *)0x52023080u; + RCC_AHB2ENSR = (1u << 12u); /* RAMCFGEN */ + RCC_MEMENSR = (1u << 1u); /* AXISRAM2EN */ + delay(100); + *ramcfg_sram2_cr &= ~(1u << 20u); /* Clear SRAMSD → power on */ + DSB(); + delay(10000); + } + + /* Configure RIFSC: grant ETH1 DMA (RIMC master 6) memory access. + * RIMC_ATTR6: DSEL=1 (use default CID), DSEC=0 (non-secure), DPRIV=1 (privileged) + * RIFSC_BASE = 0x54024000, RIMC_ATTR[6] at offset 0xC10 + 6*4 = 0xC28 */ + { + volatile uint32_t *rimc_attr6 = (volatile uint32_t *)(0x54024000u + 0xC28u); + RCC_AHB3ENSR = (1u << 9u); /* RIFSCEN */ + delay(100); + /* CID=1, DSEL=1, DSEC=1 (secure), DPRIV=1 — matches CubeN6 example */ + *rimc_attr6 = (1u << 0u) | (1u << 4u) | (1u << 8u) | (1u << 9u); + DSB(); + uart_puts(" RIMC_ATTR6 (ETH1): "); + uart_puthex(*rimc_attr6); + uart_puts("\n"); + } + + /* Enable SYSCFG clock and configure VDDIO3 I/O compensation. + * GPIOF pins are on VDDIO3 — compensation cell must be enabled + * for the RMII data signals to work at 50 MHz. */ + RCC_APB4ENSR2 = (1u << 0u); /* SYSCFGEN */ + delay(100); + SYSCFG_VDDIO3CCCR |= (1u << 8u); /* EN = 1 (enable compensation) */ + DSB(); + + /* Enable HSLV (1.8V) mode for VDDIO3 (GPIOF Ethernet pins). + * The VDDIO3VRSEL bit in PWR_SVMCR3 is protected by OTP124 bit 15. + * Write the BSEC shadow register first to unlock it (non-permanent). */ + { + volatile uint32_t *bsec_fvr124 = (volatile uint32_t *)0x560091F0u; + *bsec_fvr124 |= (1u << 15u); /* HSLV_VDDIO3 shadow */ + DSB(); + PWR_SVMCR3 |= (1u << 26u); /* VDDIO3VRSEL = 1.8V */ + DMB(); + } + + /* Configure RMII pins (AF11) — NUCLEO-N657X0-Q pinout */ + gpio_eth_pin(GPIOF_BASE, 4); /* MDIO */ + gpio_eth_pin(GPIOG_BASE, 11); /* MDC */ + gpio_eth_pin(GPIOF_BASE, 7); /* REF_CLK */ + gpio_eth_pin(GPIOF_BASE, 10); /* CRS_DV */ + gpio_eth_pin(GPIOF_BASE, 11); /* TX_EN */ + gpio_eth_pin(GPIOF_BASE, 12); /* TXD0 */ + gpio_eth_pin(GPIOF_BASE, 13); /* TXD1 */ + gpio_eth_pin(GPIOF_BASE, 14); /* RXD0 */ + gpio_eth_pin(GPIOF_BASE, 15); /* RXD1 */ +} + +/* ========================================================================= + * TCP echo callback — stack-agnostic, mirrors H563 implementation + * ========================================================================= */ +static void echo_cb(int fd, uint16_t event, void *arg) +{ + struct wolfIP *s = (struct wolfIP *)arg; + int ret; + + if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { + client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd > 0) { + wolfIP_register_callback(s, client_fd, echo_cb, s); + } + return; + } + + if ((fd == client_fd) && (event & CB_EVENT_READABLE)) { + ret = wolfIP_sock_recvfrom(s, client_fd, rx_buf, sizeof(rx_buf), 0, NULL, NULL); + if (ret > 0) { + (void)wolfIP_sock_sendto(s, client_fd, rx_buf, (uint32_t)ret, 0, NULL, 0); + } else if (ret == 0) { + wolfIP_sock_close(s, client_fd); + client_fd = -1; + } + } + + if ((fd == client_fd) && (event & CB_EVENT_CLOSED)) { + wolfIP_sock_close(s, client_fd); + client_fd = -1; + } +} + +/* ========================================================================= + * main + * ========================================================================= */ +int main(void) +{ + struct wolfIP_ll_dev *ll; + struct wolfIP_sockaddr_in addr; + uint64_t tick = 0; + int ret; + + /* LED first — absolute minimum, just GPIO clock + pin mode. + * This ensures the HardFault handler can blink the LED. */ + pwr_enable_io_supply(); + + /* Enable HSLV mode for VDDIO3 — AFTER pwr_enable_io_supply to avoid + * the PWR_SVMCR3 read-modify-write from clearing VRSEL. + * VDDIO3 is 1.8V on NUCLEO-N657X0-Q. Without HSLV, I/O pads can't + * switch fast enough for 50MHz RMII Ethernet. + * BSEC shadow register unlocks the PWR VRSEL write (non-permanent). */ + { + volatile uint32_t *bsec_fvr124 = (volatile uint32_t *)0x560091F0u; + *bsec_fvr124 |= (1u << 15u); /* HSLV_VDDIO3 shadow */ + __asm volatile ("dsb sy" ::: "memory"); + for (volatile uint32_t d = 0; d < 10000; d++) {} /* Wait for BSEC */ + PWR_SVMCR3 |= (1u << 26u); /* VDDIO3VRSEL = 1.8V */ + __asm volatile ("dmb sy" ::: "memory"); + } + led_init(); + led_on(); + + /* UART init — after this, HardFault handler can print diagnostics */ + uart_init(); /* Uses HSI 64 MHz for now — BRR adjusted below */ + uart_ready = 1; + + uart_puts("\n[BOOT] LED+UART OK at HSI\n"); + uart_puts("[BOOT] Enabling PLL...\n"); + clock_config(); + /* UART baud is now wrong (clock changed) — just keep going. + * Ping test doesn't need UART output. */ + icache_enable(); + mpu_configure_eth_nocache(); + dcache_enable(); + + /* Blink to confirm UART init */ + led_off(); delay(200000); + led_on(); delay(200000); + led_off(); delay(200000); + led_on(); + + uart_puts("\n\n=== wolfIP STM32N6 Echo Server ===\n"); + + /* Initialize wolfIP stack */ + uart_puts("Initializing wolfIP stack...\n"); + wolfIP_init_static(&IPStack); + + /* ETH init sequence: + * 1. Configure GPIO for RMII (pins + VDDIO3 compensation + RIMC) + * 2. Select RMII mode in CCIPR2 BEFORE enabling ETH clocks + * 3. Enable ETH clocks + * 4. Reset ETH MAC */ + + /* Step 1: GPIO and peripheral setup */ + eth_gpio_init(); + + /* Step 2: RMII mode select — must be before ETH clocks on some STM32 */ + /* ETH1SEL = RMII (bit 18) */ + RCC_CCIPR2 |= RCC_CCIPR2_ETH1SEL_RMII; + DSB(); + delay(1000); + + /* Step 3: Enable Ethernet clocks (AHB5) */ + /* Only enable MAC, TX, RX clocks (NOT ETH1EN at bit 25 — per CubeN6 example) */ + RCC_AHB5ENSR = (1u << 22u) | (1u << 23u) | (1u << 24u); + DSB(); + delay(10000); + + /* Step 4: Reset Ethernet MAC */ + RCC_AHB5RSTR |= (1u << 22u); + DSB(); + delay(1000); + RCC_AHB5RSTR &= ~(1u << 22u); + DSB(); + delay(100000); /* Longer delay after reset */ + + /* Verify ETH peripheral is accessible */ + { + volatile uint32_t *eth_maccr = (volatile uint32_t *)0x58036000u; + volatile uint32_t *eth_dmamr = (volatile uint32_t *)0x58037000u; + volatile uint32_t *eth_dmadsr = (volatile uint32_t *)0x58037008u; + uart_puts(" ETH1_MACCR @0x58036000: "); + uart_puthex(*eth_maccr); + uart_puts("\n ETH1_DMAMR @0x58037000: "); + uart_puthex(*eth_dmamr); + uart_puts("\n ETH1_DMADSR @0x58037008: "); + uart_puthex(*eth_dmadsr); + uart_puts("\n"); + /* Try DMA SWR manually */ + uart_puts(" Writing SWR...\n"); + *eth_dmamr |= 1u; + delay(100000); + uart_puts(" ETH1_DMAMR after SWR: "); + uart_puthex(*eth_dmamr); + uart_puts("\n"); + } + + /* Test MDIO directly before full init */ + { + volatile uint32_t *macmdioar = (volatile uint32_t *)0x58036200u; + volatile uint32_t *macmdiodr = (volatile uint32_t *)0x58036204u; + uint32_t cfg, to; + uart_puts(" MDIO test: reading PHY0 reg2...\n"); + /* CR=0, RDA=2 (ID1), PA=0, GOC=3 (read), MB=1 */ + cfg = (0u << 8) | (2u << 16) | (0u << 21) | (3u << 2) | 1u; + *macmdioar = cfg; + to = 100000; + while ((*macmdioar & 1u) && --to) {} + uart_puts(" MACMDIOAR: "); + uart_puthex(*macmdioar); + uart_puts("\n MACMDIODR: "); + uart_puthex(*macmdiodr); + uart_puts(" (timeout left: "); + uart_putdec(to); + uart_puts(")\n"); + } + + /* Initialize Ethernet MAC + PHY */ + uart_puts("Initializing Ethernet MAC...\n"); + ll = wolfIP_getdev(IPStack); + ret = stm32_eth_init(ll, NULL); + if (ret < 0) { + uart_puts(" ERROR: stm32_eth_init failed ("); + uart_puthex((uint32_t)ret); + uart_puts(")\n"); + } else { + uart_puts(" PHY link: "); + uart_puts((ret & 0x100) ? "UP" : "DOWN"); + uart_puts(", PHY addr: "); + uart_puthex(ret & 0xFF); + uart_puts("\n MAC: "); + { + int mi; + for (mi = 0; mi < 6; mi++) { + if (mi > 0) uart_puts(":"); + uart_puthex8(ll->mac[mi]); + } + } + uart_puts("\n"); + } + + /* Static IP configuration */ + { + ip4 ip = atoip4(WOLFIP_IP); + ip4 nm = atoip4(WOLFIP_NETMASK); + ip4 gw = atoip4(WOLFIP_GW); + uart_puts("Setting IP configuration:\n"); + uart_puts(" IP: "); + uart_putip4(ip); + uart_puts("\n Mask: "); + uart_putip4(nm); + uart_puts("\n GW: "); + uart_putip4(gw); + uart_puts("\n"); + wolfIP_ipconfig_set(IPStack, ip, nm, gw); + } + + /* TCP echo server on port 7 */ + uart_puts("Creating TCP socket on port 7...\n"); + listen_fd = wolfIP_sock_socket(IPStack, AF_INET, IPSTACK_SOCK_STREAM, 0); + wolfIP_register_callback(IPStack, listen_fd, echo_cb, IPStack); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(ECHO_PORT); + addr.sin_addr.s_addr = 0; + (void)wolfIP_sock_bind(IPStack, listen_fd, (struct wolfIP_sockaddr *)&addr, + sizeof(addr)); + (void)wolfIP_sock_listen(IPStack, listen_fd, 1); + + uart_puts("Entering main loop. Ready for connections!\n"); + uart_puts(" TCP Echo: port 7\n"); + + for (;;) { + (void)wolfIP_poll(IPStack, tick++); + + /* Toggle LED every ~256K iterations as heartbeat */ + if ((tick & 0x3FFFFu) == 0) { + led_toggle(); + } + } + return 0; +} diff --git a/src/port/stm32n6/openocd.cfg b/src/port/stm32n6/openocd.cfg new file mode 100644 index 00000000..c68117da --- /dev/null +++ b/src/port/stm32n6/openocd.cfg @@ -0,0 +1,5 @@ +source [find interface/stlink.cfg] +transport select swd +set CHIPNAME stm32n6x +source [find target/stm32n6x.cfg] +init diff --git a/src/port/stm32n6/startup.c b/src/port/stm32n6/startup.c new file mode 100644 index 00000000..53cecea1 --- /dev/null +++ b/src/port/stm32n6/startup.c @@ -0,0 +1,47 @@ +/* startup.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP 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. + * + * wolfIP 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 + +extern uint32_t _sidata; +extern uint32_t _sdata; +extern uint32_t _edata; +extern uint32_t _sbss; +extern uint32_t _ebss; +extern void __libc_init_array(void); + +int main(void); + +void Reset_Handler(void) +{ + uint32_t *src; + uint32_t *dst; + + src = &_sidata; + for (dst = &_sdata; dst < &_edata; ) { + *dst++ = *src++; + } + for (dst = &_sbss; dst < &_ebss; ) { + *dst++ = 0u; + } + __libc_init_array(); + (void)main(); + while (1) { } +} diff --git a/src/port/stm32n6/syscalls.c b/src/port/stm32n6/syscalls.c new file mode 100644 index 00000000..5d0f1e29 --- /dev/null +++ b/src/port/stm32n6/syscalls.c @@ -0,0 +1,141 @@ +/* syscalls.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP 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. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#include +#include +#include +#include +#include +#include + +extern uint32_t _ebss; +extern uint32_t _heap_limit; + +static char *heap_end; + +int _write(int file, const char *ptr, int len) +{ + (void)file; + (void)ptr; + return len; +} + +int _close(int file) +{ + (void)file; + return -1; +} + +int _fstat(int file, struct stat *st) +{ + (void)file; + if (st == 0) { + errno = EINVAL; + return -1; + } + st->st_mode = S_IFCHR; + return 0; +} + +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 _read(int file, char *ptr, int len) +{ + (void)file; + (void)ptr; + (void)len; + return 0; +} + +void *_sbrk(ptrdiff_t incr) +{ + char *prev; + if (heap_end == 0) { + heap_end = (char *)&_ebss; + } + prev = heap_end; + if ((heap_end + incr) >= (char *)&_heap_limit) { + errno = ENOMEM; + return (void *)-1; + } + heap_end += incr; + return prev; +} + +int _gettimeofday(struct timeval *tv, void *tzvp) +{ + (void)tzvp; + if (tv == 0) { + errno = EINVAL; + return -1; + } + tv->tv_sec = 0; + tv->tv_usec = 0; + return 0; +} + +time_t time(time_t *t) +{ + if (t != 0) { + *t = 0; + } + return 0; +} + +void _exit(int status) +{ + (void)status; + while (1) { + __asm volatile("wfi"); + } +} + +int _kill(int pid, int sig) +{ + (void)pid; + (void)sig; + errno = EINVAL; + return -1; +} + +int _getpid(void) +{ + return 1; +} + +void _init(void) +{ +} + +void _fini(void) +{ +} diff --git a/src/port/stm32n6/target.ld b/src/port/stm32n6/target.ld new file mode 100644 index 00000000..ec1afd77 --- /dev/null +++ b/src/port/stm32n6/target.ld @@ -0,0 +1,104 @@ +/* STM32N6 Linker Script — AXISRAM execution via JTAG + * + * Memory Map (all execution from SRAM, no flash): + * AXISRAM1: 512K @ 0x34000000 — code + rodata + data + bss + heap + stack + * + * Note: Only AXISRAM1 is guaranteed accessible after boot ROM. + * Other regions (AXISRAM2-6) may need explicit RCC enable. + * ETH DMA buffers are placed at the end of AXISRAM1 for now, + * with MPU marking them non-cacheable. + */ + +MEMORY +{ + SRAM (rwx) : ORIGIN = 0x34000000, LENGTH = 512K + AXISRAM2 (rwx) : ORIGIN = 0x34100000, LENGTH = 1024K +} + +_estack = ORIGIN(SRAM) + LENGTH(SRAM); +_sidata = LOADADDR(.data); + +/* Heap and stack sizes */ +_Min_Heap_Size = 0x4000; /* 16KB heap */ +_Min_Stack_Size = 0x4000; /* 16KB stack */ +_heap_limit = _estack - _Min_Stack_Size; + +SECTIONS +{ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) + . = ALIGN(4); + } > SRAM + + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + *(.ARM.extab* .gnu.linkonce.armextab.*) + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + *(.glue_7) + *(.glue_7t) + *(.eh_frame) + . = ALIGN(4); + } > SRAM + + .preinit_array : + { + __preinit_array_start = .; + KEEP(*(.preinit_array*)) + __preinit_array_end = .; + } > SRAM + + .init_array : + { + __init_array_start = .; + KEEP(*(.init_array*)) + __init_array_end = .; + } > SRAM + + .fini_array : + { + __fini_array_start = .; + KEEP(*(.fini_array*)) + __fini_array_end = .; + } > SRAM + + .data : + { + . = ALIGN(4); + _sdata = .; + *(.data*) + . = ALIGN(4); + _edata = .; + } > SRAM + + .bss (NOLOAD) : + { + . = ALIGN(4); + _sbss = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + } > SRAM + + .eth_buffers (NOLOAD) : + { + . = ALIGN(32); + *(.eth_buffers*) + . = ALIGN(32); + } > AXISRAM2 + + ._user_heap_stack (NOLOAD) : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } > SRAM +} From 83dd652a7d79c22a426b2ecc7fea33fd1fc2318d Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Mon, 23 Mar 2026 19:32:07 -0700 Subject: [PATCH 2/5] =?UTF-8?q?Fix=20STM32N6=20ETH=20RX/TX=20=E2=80=94=20p?= =?UTF-8?q?ing=20and=20TCP=20echo=20working=20=20=201.=20RISAF3=20(AXISRAM?= =?UTF-8?q?2=20firewall)=20=E2=80=94=20default=20base=20region=20ENDR=3D0x?= =?UTF-8?q?FFF=20=20=20=20=20=20covered=20only=204KB.=20ETH=20DMA=20buffer?= =?UTF-8?q?s=20at=20offset=200xF8000=20were=20=20=20=20=20=20blocked.=20Ex?= =?UTF-8?q?tended=20region=20to=20cover=20full=201MB=20with=20all=20CIDs?= =?UTF-8?q?=20=20=20=20=20=20read+write,=20SEC=3D1=20for=20secure-alias=20?= =?UTF-8?q?access.=20=20=202.=20RIMC=20CID=20mismatch=20=E2=80=94=20ETH=20?= =?UTF-8?q?DMA=20was=20configured=20as=20CID=3D0=20(default)=20=20=20=20?= =?UTF-8?q?=20=20but=20RISAF=20grants=20access=20by=20CID.=20Changed=20to?= =?UTF-8?q?=20CID=3D1=20matching=20=20=20=20=20=20the=20CPU=20and=20CubeN6?= =?UTF-8?q?=20HAL=20(RISAF=5FConfig).=20Also=20added=20RISC=20=20=20=20=20?= =?UTF-8?q?=20slave=20security=20attributes=20for=20ETH1=20peripheral.=20?= =?UTF-8?q?=20=203.=20MACCR=20SARC=3D3=20was=20inserting=206=20extra=20byt?= =?UTF-8?q?es=20(source=20address)=20=20=20=20=20=20into=20TX=20frames=20i?= =?UTF-8?q?nstead=20of=20replacing,=20corrupting=20the=20Ethernet=20=20=20?= =?UTF-8?q?=20=20=20header.=20Removed=20SARC=20=E2=80=94=20wolfIP=20constr?= =?UTF-8?q?ucts=20complete=20frames.=20=20=20-=20SAU:=20clear=20all=20regi?= =?UTF-8?q?ons,=20set=20ALLNS=3D1=20(matching=20CubeN6=20SystemInit)=20=20?= =?UTF-8?q?=20-=20RCC=20ETH=20peripheral=20reset=20before=20init=20for=20c?= =?UTF-8?q?lean=20state=20=20=20-=20eth=5Fstart:=20fix=20raw=20cast=20to?= =?UTF-8?q?=20use=20ETH=5FDMA=5FADDR=20for=20tail=20pointer=20=20=20-=20et?= =?UTF-8?q?h=5Fpoll:=20restore=20des0=20(buffer=20address)=20on=20descript?= =?UTF-8?q?or=20re-arm,=20=20=20=20=20since=20GMAC=20v5=20writeback=20over?= =?UTF-8?q?writes=20des0=20with=20timestamp=20=20=20-=20eth=5Fstart:=20arm?= =?UTF-8?q?=20RX=20descriptors=20after=20DMA=20start=20(HAL=20flow)=20=20?= =?UTF-8?q?=20-=20Enable=20DMA=20interrupt=20flags=20(DMACIER)=20for=20pro?= =?UTF-8?q?per=20operation=20=20=20-=20ETH=20buffers=20placed=20at=200x341?= =?UTF-8?q?F8000=20(end=20of=20AXISRAM2,=20matching=20=20=20=20=20CubeN6?= =?UTF-8?q?=20HAL=20descriptor=20placement)=20=20=20-=20D-cache=20re-enabl?= =?UTF-8?q?ed=20with=20MPU=20non-cacheable=20region=20for=20ETH=20buffers?= =?UTF-8?q?=20=20=20-=20AHBSRAM1/2=20clocks=20enabled=20for=20potential=20?= =?UTF-8?q?future=20use=20=20=20Tested:=20ping=20(5/5,=20<0.2ms=20RTT),=20?= =?UTF-8?q?TCP=20echo=20on=20port=207.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/port/stm32/stm32_eth.c | 342 ++++++++++++++++++++++++++++++------- src/port/stm32n6/main.c | 264 ++++++++++++++-------------- src/port/stm32n6/target.ld | 5 +- 3 files changed, 415 insertions(+), 196 deletions(-) diff --git a/src/port/stm32/stm32_eth.c b/src/port/stm32/stm32_eth.c index c89d8a26..c79cb13c 100644 --- a/src/port/stm32/stm32_eth.c +++ b/src/port/stm32/stm32_eth.c @@ -47,7 +47,11 @@ /* MAC registers */ #define ETH_MACCR ETH_REG(0x0000U) -#define ETH_MACPFR ETH_REG(0x0004U) +#define ETH_MACECR ETH_REG(0x0004U) /* Extended Configuration */ +#define ETH_MACPFR ETH_REG(0x0008U) /* Packet Filter */ +#define ETH_MACWTR ETH_REG(0x000CU) /* Watchdog Timeout */ +#define ETH_MACQ0TXFCR ETH_REG(0x0070U) /* TX Flow Control Q0 */ +#define ETH_MACRXFCR ETH_REG(0x0090U) /* RX Flow Control */ #define ETH_MAC1USTCR ETH_REG(0x00DCU) #define ETH_MACA0HR ETH_REG(0x0300U) #define ETH_MACA0LR ETH_REG(0x0304U) @@ -56,8 +60,10 @@ #endif /* MTL registers */ -#define ETH_MTLTXQOMR ETH_REG(0x0D00U) -#define ETH_MTLRXQOMR ETH_REG(0x0D30U) +#define ETH_MTLOMR ETH_REG(0x0C00U) /* MTL Operation Mode */ +#define ETH_MTLTXQOMR ETH_REG(0x0D00U) /* MTL TX Q0 Operation Mode */ +#define ETH_MTLRXQOMR ETH_REG(0x0D30U) /* MTL RX Q0 Operation Mode */ +#define ETH_MTLRXQDMAMR ETH_REG(0x0C30U) /* RX Queue to DMA Mapping */ /* DMA registers */ #define ETH_DMAMR ETH_REG(0x1000U) @@ -75,6 +81,21 @@ #define ETH_MACMDIOAR ETH_REG(0x0200U) #define ETH_MACMDIODR ETH_REG(0x0204U) +/* N6 DMA Channel 1 registers (offset 0x80 from CH0) */ +#if defined(STM32N6) +#define ETH_DMAC1CR ETH_REG(0x1180U) +#define ETH_DMAC1TXCR ETH_REG(0x1184U) +#define ETH_DMAC1RXCR ETH_REG(0x1188U) +#define ETH_DMAC1TXDLAR ETH_REG(0x1194U) +#define ETH_DMAC1RXDLAR ETH_REG(0x119CU) +#define ETH_DMAC1TXDTPR ETH_REG(0x11A0U) +#define ETH_DMAC1RXDTPR ETH_REG(0x11A8U) +#define ETH_DMAC1TXRLR ETH_REG(0x11ACU) +#define ETH_DMAC1RXRLR ETH_REG(0x11B0U) +#define ETH_DMAC1SR ETH_REG(0x11E0U) +#define ETH_MTLTXQ1OMR ETH_REG(0x0D40U) +#endif + /* MAC control bits */ #define ETH_MACCR_RE (1U << 0) #define ETH_MACCR_TE (1U << 1) @@ -102,9 +123,14 @@ #define ETH_MTLTXQOMR_TSF (1U << 1) #define ETH_MTLTXQOMR_TXQEN_SHIFT 2 #define ETH_MTLTXQOMR_TXQEN_ENABLE (2U << ETH_MTLTXQOMR_TXQEN_SHIFT) -#define ETH_MTLTXQOMR_MASK 0x00000072U +#define ETH_MTLTXQOMR_TQS_SHIFT 16 /* TX Queue Size */ +#define ETH_MTLTXQOMR_TQS_2048 (0x7U << ETH_MTLTXQOMR_TQS_SHIFT) /* 2KB */ +#define ETH_MTLTXQOMR_MASK 0x00070072U #define ETH_MTLRXQOMR_RSF (1U << 5) -#define ETH_MTLRXQOMR_MASK 0x0000007BU +#define ETH_MTLRXQOMR_RQS_SHIFT 20 /* RX Queue Size */ +#define ETH_MTLRXQOMR_RQS_4096 (0xFU << ETH_MTLRXQOMR_RQS_SHIFT) /* 4KB */ +#define ETH_MTLRXQOMR_DEHF (1U << 6) /* Drop Error Half Frame */ +#define ETH_MTLRXQOMR_MASK 0x00F0007FU /* MDIO bits */ #define ETH_MACMDIOAR_MB (1U << 0) @@ -140,12 +166,18 @@ #define PHY_ANAR_DEFAULT 0x01E1U -/* DMA descriptor structure */ +/* DMA descriptor structure. + * On N6 GMAC v5.20, DSL=1 skips 1 doubleword (8 bytes) between + * 16-byte descriptors → stride = 24 bytes. HAL uses 6-field struct + * (DESC0-3 + BackupAddr0/1). */ struct eth_desc { volatile uint32_t des0; volatile uint32_t des1; volatile uint32_t des2; volatile uint32_t des3; +#if defined(STM32N6) + volatile uint32_t _pad[2]; /* DSL=1: 8-byte skip (1 doubleword) */ +#endif }; /* Descriptor bits */ @@ -181,16 +213,20 @@ struct eth_desc { #endif #endif -/* On STM32N6, the ETH DMA (RIMC master 6) is configured as non-secure. - * On STM32N6, the ETH DMA is configured as a secure master (CID=1, SEC+PRIV). - * Use secure addresses (0x34xxxxxx) — matches CubeN6 HAL example. */ +/* DMA buffer address — use secure alias (0x34), matching RISAF SEC=1. */ #define ETH_DMA_ADDR(ptr) ((uint32_t)(ptr)) static struct eth_desc rx_ring[RX_DESC_COUNT] __attribute__((aligned(32))) ETH_SECTION; static struct eth_desc tx_ring[TX_DESC_COUNT] __attribute__((aligned(32))) ETH_SECTION; static uint8_t rx_buffers[RX_DESC_COUNT][RX_BUF_SIZE] __attribute__((aligned(32))) ETH_SECTION; static uint8_t tx_buffers[TX_DESC_COUNT][TX_BUF_SIZE] __attribute__((aligned(32))) ETH_SECTION; -static uint8_t rx_staging_buffer[RX_BUF_SIZE] __attribute__((aligned(32))); + +#if defined(STM32N6) +/* DMA Channel 1 descriptor rings and buffers */ +static struct eth_desc rx_ring1[RX_DESC_COUNT] __attribute__((aligned(32))) ETH_SECTION; +static struct eth_desc tx_ring1[TX_DESC_COUNT] __attribute__((aligned(32))) ETH_SECTION; +static uint8_t rx_buffers1[RX_DESC_COUNT][RX_BUF_SIZE] __attribute__((aligned(32))) ETH_SECTION; +#endif static uint32_t rx_idx; static uint32_t tx_idx; @@ -211,22 +247,21 @@ static void eth_delay(uint32_t count) static int eth_hw_reset(void) { - uint32_t timeout = 1000000U; + uint32_t timeout; + +#if defined(STM32N6) + /* On N6, SWR requires REF_CLK from PHY. Use a very long timeout + * (HAL uses 1 second). Do NOT manually clear SWR — it corrupts DMA. */ + timeout = 50000000U; /* ~80ms at 600MHz */ +#else + timeout = 1000000U; +#endif ETH_DMAMR |= ETH_DMAMR_SWR; while ((ETH_DMAMR & ETH_DMAMR_SWR) && (timeout > 0U)) { timeout--; } -#if defined(STM32N6) - /* On STM32N6, the DMA SWR may not self-clear without the PHY REF_CLK. - * After RCC reset, the MAC is already in reset state. Continue anyway - * and clear SWR manually — the MAC/DMA registers are writable. */ - if (timeout == 0U) { - ETH_DMAMR &= ~ETH_DMAMR_SWR; - } -#else if (timeout == 0U) return -1; -#endif #if STM32_ETH_NEEDS_MDIO_DELAY /* Wait for MAC internal state to stabilize after reset. */ @@ -247,21 +282,59 @@ static void eth_trigger_tx(void) static void eth_config_mac(const uint8_t mac[6]) { - uint32_t maccr = ETH_MACCR; - maccr &= ~(ETH_MACCR_DM | ETH_MACCR_FES | ETH_MACCR_PS); - maccr |= ETH_MACCR_DM | ETH_MACCR_FES; + uint32_t maccr; + + /* Build MACCR value to match HAL defaults: + * - AutomaticPadCRCStrip = ENABLE (bit 20) + * - CRCStripTypePacket = ENABLE (bit 21) + * - ChecksumOffload = ENABLE (bit 27) + * - Watchdog = ENABLE (bit 19 = 0) + * - Jabber = ENABLE (bit 17 = 0) + * - InterPacketGap = 96bit (bits 26:24 = 0) + * - DuplexMode = Full (bit 13) + * - Speed = 100Mbps (bit 14) + * - PortSelect = MII/RMII (bit 15) for N6 + */ + maccr = (1U << 20) | /* ACS - Auto Pad/CRC Strip */ + (1U << 21) | /* CST - CRC Strip Type */ + (1U << 27) | /* IPC - Checksum Offload */ + ETH_MACCR_DM | ETH_MACCR_FES; + #if defined(STM32N6) - /* PS=1 selects MII/RMII port (vs GMII). */ + /* PS=1 selects MII/RMII port (vs GMII). + * SARC=0: don't modify source address (wolfIP constructs full frame). */ maccr |= ETH_MACCR_PS; /* Configure 1µs tick counter — required for MAC internal timing. - * Value = (HCLK_Hz / 1000000) - 1. With PLL: ~300MHz → 299. */ - ETH_MAC1USTCR = 299U; + * Value = (HCLK_Hz / 1000000) - 1. With PLL: HCLK ~200MHz → 199. */ + ETH_MAC1USTCR = 199U; #endif ETH_MACCR = maccr; -#if defined(STM32H7) || defined(STM32N6) - /* Enable RX Queue 0 for DCB/Generic traffic. */ - ETH_MACRXQC0R = (2U << 0); + /* Default packet filter: unicast (our MAC) + broadcast. + * Promiscuous mode disabled to avoid flooding wolfIP with irrelevant traffic. */ + ETH_MACPFR = 0; + + /* Configure MACECR (Extended Config) - HAL defaults: + * - GiantPacketSizeLimit = 0x618 (1560 bytes) + * - CRCCheckingRxPackets = ENABLE (bit 16 = 0) + */ + ETH_MACECR = 0x618U; /* Giant packet size limit */ + + /* Configure MACWTR (Watchdog Timeout) - HAL defaults: + * - WatchdogTimeout = 2KB (bits 3:0 = 0) + * - ProgrammableWatchdog = DISABLE (bit 8 = 0) + */ + ETH_MACWTR = 0x0U; + + /* Configure flow control - HAL defaults: disabled */ + ETH_MACQ0TXFCR = (1U << 7); /* Zero Quanta Pause disabled (DZPQ=1) */ + ETH_MACRXFCR = 0x0U; + +#if defined(STM32N6) + /* Enable both RX queues — N6 requires both DMA channels active */ + ETH_MACRXQC0R = 0x0Au; +#elif defined(STM32H7) + ETH_MACRXQC0R = 0x02u; #endif ETH_MACA0HR = ((uint32_t)mac[5] << 8) | (uint32_t)mac[4]; @@ -297,15 +370,40 @@ static void eth_config_speed_duplex(void) static void eth_config_mtl(void) { - uint32_t txqomr = ETH_MTLTXQOMR; - uint32_t rxqomr = ETH_MTLRXQOMR; + uint32_t txqomr; + uint32_t rxqomr; + /* Configure MTL Operation Mode (MTLOMR) - HAL defaults: + * - TxSchedulingAlgorithm = Strict Priority (bits 6:5 = 0) + * - ReceiveArbitrationAlgorithm = Strict Priority (bit 8 = 0) + * - TransmitStatus = ENABLE (bit 1 = 0, DTXSTS=0) + */ + /* CubeN6 sets 0x60: bits 6:5 = Strict Priority arbitration */ + ETH_MTLOMR = 0x00000060u; + +#if defined(STM32N6) + /* Map RX Queue 0 to DMA Channel 0 (MTLRXQDMAMR) */ + ETH_MTLRXQDMAMR = 0x0U; /* Q0 -> CH0, Q1 -> CH1 */ +#endif + + /* Configure TX Queue 0 Operation Mode: + * - TSF = Transmit Store and Forward (bit 1) + * - TXQEN = Enabled (bits 3:2 = 2) + * - TQS = 2KB (bits 22:16 = 0x7) + */ + txqomr = ETH_MTLTXQOMR; txqomr &= ~ETH_MTLTXQOMR_MASK; - txqomr |= (ETH_MTLTXQOMR_TSF | ETH_MTLTXQOMR_TXQEN_ENABLE); + txqomr |= ETH_MTLTXQOMR_TSF | ETH_MTLTXQOMR_TXQEN_ENABLE | ETH_MTLTXQOMR_TQS_2048; ETH_MTLTXQOMR = txqomr; + /* Configure RX Queue 0 Operation Mode: + * - RSF = Receive Store and Forward (bit 5) + * - RQS = 4KB (bits 23:20 = 0xF) + * - DEHF = Drop Error Half Frames (bit 6 = 0, disabled by HAL) + */ + rxqomr = ETH_MTLRXQOMR; rxqomr &= ~ETH_MTLRXQOMR_MASK; - rxqomr |= ETH_MTLRXQOMR_RSF; + rxqomr |= ETH_MTLRXQOMR_RSF | ETH_MTLRXQOMR_RQS_4096; ETH_MTLRXQOMR = rxqomr; } @@ -313,23 +411,19 @@ static void eth_init_desc(void) { uint32_t i; - /* Step 1: Clear all descriptors (like HAL does). */ + /* Clear all descriptors (HAL flow: init empty, arm later via eth_start). */ for (i = 0; i < TX_DESC_COUNT; i++) { - tx_ring[i].des0 = 0; - tx_ring[i].des1 = 0; - tx_ring[i].des2 = 0; - tx_ring[i].des3 = 0; + tx_ring[i].des0 = 0; tx_ring[i].des1 = 0; + tx_ring[i].des2 = 0; tx_ring[i].des3 = 0; } for (i = 0; i < RX_DESC_COUNT; i++) { - rx_ring[i].des0 = 0; - rx_ring[i].des1 = 0; - rx_ring[i].des2 = 0; - rx_ring[i].des3 = 0; + rx_ring[i].des0 = 0; rx_ring[i].des1 = 0; + rx_ring[i].des2 = 0; rx_ring[i].des3 = 0; } rx_idx = 0; tx_idx = 0; - /* Step 2: Configure DMA registers (use DMA-visible addresses). */ + /* Configure DMA descriptor registers with EMPTY descriptors. */ __asm volatile ("dsb sy" ::: "memory"); ETH_DMACTXDLAR = ETH_DMA_ADDR(&tx_ring[0]); ETH_DMACRXDLAR = ETH_DMA_ADDR(&rx_ring[0]); @@ -339,39 +433,115 @@ static void eth_init_desc(void) ETH_DMACRXDTPR = ETH_DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1U]); __asm volatile ("dsb sy" ::: "memory"); - /* Step 3: Now set buffer addresses and OWN bit. */ + /* TX descriptors: set buffer addresses (no OWN, host owns them). */ for (i = 0; i < TX_DESC_COUNT; i++) { *(volatile uint32_t *)&tx_ring[i].des0 = ETH_DMA_ADDR(tx_buffers[i]); } + /* RX descriptors: do NOT set OWN yet — will be armed by eth_start + * via ETH_UpdateDescriptor-style flow (matching CubeN6 HAL). */ + +#if defined(STM32N6) + /* Initialize DMA Channel 1 descriptors (N6 has 2 channels) */ + for (i = 0; i < TX_DESC_COUNT; i++) { + tx_ring1[i].des0 = 0; tx_ring1[i].des1 = 0; + tx_ring1[i].des2 = 0; tx_ring1[i].des3 = 0; + } for (i = 0; i < RX_DESC_COUNT; i++) { - *(volatile uint32_t *)&rx_ring[i].des0 = ETH_DMA_ADDR(rx_buffers[i]); - *(volatile uint32_t *)&rx_ring[i].des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; + rx_ring1[i].des0 = ETH_DMA_ADDR(rx_buffers1[i]); + rx_ring1[i].des1 = 0; rx_ring1[i].des2 = 0; + rx_ring1[i].des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; } - - /* Data synchronization barrier before updating tail pointer. */ __asm volatile ("dsb sy" ::: "memory"); - __asm volatile ("isb sy" ::: "memory"); - /* Step 4: Update tail pointer to signal DMA that descriptors are ready. */ - ETH_DMACRXDTPR = ETH_DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1U]); + ETH_DMAC1TXDLAR = ETH_DMA_ADDR(&tx_ring1[0]); + ETH_DMAC1RXDLAR = ETH_DMA_ADDR(&rx_ring1[0]); + ETH_DMAC1TXRLR = TX_DESC_COUNT - 1U; + ETH_DMAC1RXRLR = RX_DESC_COUNT - 1U; + ETH_DMAC1TXDTPR = ETH_DMA_ADDR(&tx_ring1[0]); + ETH_DMAC1RXDTPR = ETH_DMA_ADDR(&rx_ring1[RX_DESC_COUNT - 1U]); +#endif + /* Final barrier. */ __asm volatile ("dsb sy" ::: "memory"); } #define ETH_DMACCR_DSL_0BIT (0x00000000u) +/* DMA System Bus Mode Register bits */ +#define ETH_DMASBMR_RX_OSR_LIMIT_3 (0x3U << 16) /* RX Outstanding requests limit */ +#define ETH_DMASBMR_TX_OSR_LIMIT_3 (0x3U << 24) /* TX Outstanding requests limit */ +#define ETH_DMASBMR_BLEN4 (1U << 1) /* AXI Burst Length 4 */ + static void eth_config_dma(void) { - ETH_DMASBMR = ETH_DMASBMR_FB | ETH_DMASBMR_AAL; - /* Set DSL=0 for 16-byte descriptors (no skip). */ + /* Configure DMA System Bus Mode (DMASBMR) - HAL defaults: + * - AddressAlignedBeats = ENABLE (bit 12) + * - BurstMode = Fixed (bit 0) + * - RxOSRLimit = 3 (bits 17:16) + * - TxOSRLimit = 3 (bits 25:24) + * - AXIBLENMaxSize = 4 (bit 1) + */ + /* DMA System Bus Mode (matching CubeN6) */ + ETH_DMASBMR = 0x03031003u; + + /* DMACCR: DSL and PBLX8 mode. CubeN6 uses 0x00040218 (MSS=536, PBLX8=1, DSL=1). + * DSL=0 for 16-byte contiguous descriptors (our struct is 16 bytes). */ +#if defined(STM32N6) + /* DSL=1 (32-bit/4-byte skip → 20-byte stride), MSS=536 (matches HAL) */ + ETH_DMACCR = 0x40218u; +#else ETH_DMACCR = ETH_DMACCR_DSL_0BIT; +#endif + + /* Configure RX DMA: + * - RBSZ = buffer size + * - RPBL = 32 beat burst + */ ETH_DMACRXCR = ((RX_BUF_SIZE & ETH_RDES3_PL_MASK) << ETH_DMACRXCR_RBSZ_SHIFT) | ETH_DMACRXCR_RPBL(DMA_RPBL); + + /* Configure TX DMA: + * - TPBL = 32 beat burst + * - OSF = Operate on Second Frame (bit 4) - N6 does NOT use OSF (per CubeN6/Oryx) + */ +#if defined(STM32N6) + ETH_DMACTXCR = ETH_DMACTXCR_TPBL(DMA_TPBL); + + /* Configure DMA Channel 1 (N6 has 2 TX queues / DMA channels) */ + ETH_DMAC1CR = 0x40218u; /* DSL=1, MSS=536 — same as CH0 */ + ETH_DMAC1RXCR = ((RX_BUF_SIZE & ETH_RDES3_PL_MASK) << ETH_DMACRXCR_RBSZ_SHIFT) | + ETH_DMACRXCR_RPBL(DMA_RPBL); + ETH_DMAC1TXCR = ETH_DMACTXCR_TPBL(DMA_TPBL); + + /* MTL TX Queue 1: same as Q0 */ + ETH_MTLTXQ1OMR = ETH_MTLTXQOMR_TSF | ETH_MTLTXQOMR_TXQEN_ENABLE | + ETH_MTLTXQOMR_TQS_2048; + + /* Map RX Q1 → DMA CH1 */ + ETH_MTLRXQDMAMR = (1u << 8); /* Q1MDMACH = 1 */ +#else ETH_DMACTXCR = ETH_DMACTXCR_OSF | ETH_DMACTXCR_TPBL(DMA_TPBL); +#endif } #define ETH_DMACSR_TPS (1U << 1) #define ETH_DMACSR_RPS (1U << 8) +static void eth_arm_rx_descriptors(void) +{ + uint32_t i; + /* Arm RX descriptors: set buffer address + OWN bit (HAL ETH_UpdateDescriptor flow). + * Must be done AFTER DMA is started. */ + for (i = 0; i < RX_DESC_COUNT; i++) { + rx_ring[i].des0 = ETH_DMA_ADDR(rx_buffers[i]); + rx_ring[i].des1 = 0; + rx_ring[i].des2 = 0; + __asm volatile ("dsb sy" ::: "memory"); + rx_ring[i].des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; + } + __asm volatile ("dmb sy" ::: "memory"); + ETH_DMACRXDTPR = ETH_DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1U]); +} + static void eth_start(void) { ETH_MACCR |= ETH_MACCR_TE | ETH_MACCR_RE; @@ -379,12 +549,46 @@ static void eth_start(void) ETH_DMACTXCR |= ETH_DMACTXCR_ST; ETH_DMACRXCR |= ETH_DMACRXCR_SR; - /* Clear TX and RX process stopped flags. */ - ETH_DMACSR = ETH_DMACSR_TPS | ETH_DMACSR_RPS; +#if defined(STM32N6) + /* N6 has 2 DMA channels. Both must be started for MAC RX to work. + * CubeN6 configures both channels identically. */ + ETH_MTLTXQ1OMR |= ETH_MTLTXQOMR_FTQ; + ETH_DMAC1TXCR |= ETH_DMACTXCR_ST; + ETH_DMAC1RXCR |= ETH_DMACRXCR_SR; + ETH_DMAC1SR = 0xFFFF; /* Clear all status */ + __asm volatile ("dsb sy" ::: "memory"); + ETH_DMAC1RXDTPR = ETH_DMA_ADDR(&rx_ring1[RX_DESC_COUNT - 1U]); +#endif + + /* Enable DMA interrupt flags (matching CubeN6 HAL_ETH_Start_IT). + * Even in polled mode, some GMAC implementations need these enabled + * for the DMA to process descriptors. */ + ETH_REG(0x1134U) = (1U << 15) | (1U << 14) | (1U << 7) | (1U << 6) | (1U << 0); +#if defined(STM32N6) + ETH_REG(0x11B4U) = (1U << 15) | (1U << 14) | (1U << 7) | (1U << 6) | (1U << 0); +#endif + + /* Clear TX and RX process stopped flags + RBU (bit 7). */ + ETH_DMACSR = ETH_DMACSR_TPS | ETH_DMACSR_RPS | (1U << 7); __asm volatile ("dsb sy" ::: "memory"); - /* Write tail pointer to start RX DMA. */ - ETH_DMACRXDTPR = (uint32_t)&rx_ring[RX_DESC_COUNT - 1U]; + + /* Arm RX descriptors AFTER DMA is started (HAL_ETH_Start_IT flow). + * This tells the DMA about available descriptors via tail pointer. */ + eth_arm_rx_descriptors(); +#if defined(STM32N6) + { + uint32_t j; + for (j = 0; j < RX_DESC_COUNT; j++) { + rx_ring1[j].des0 = ETH_DMA_ADDR(rx_buffers1[j]); + rx_ring1[j].des1 = 0; rx_ring1[j].des2 = 0; + __asm volatile ("dsb sy" ::: "memory"); + rx_ring1[j].des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; + } + __asm volatile ("dmb sy" ::: "memory"); + ETH_DMAC1RXDTPR = ETH_DMA_ADDR(&rx_ring1[RX_DESC_COUNT - 1U]); + } +#endif } static void eth_stop(void) @@ -484,6 +688,8 @@ static void eth_phy_init(void) } while ((bsr & PHY_BSR_LINK_STATUS) == 0U && --timeout != 0U); } +#define ETH_DMACSR_RBU (1U << 7) + static int eth_poll(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) { struct eth_desc *desc; @@ -493,6 +699,14 @@ static int eth_poll(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) (void)dev; rx_poll_count++; + /* Recover from RBU (Receive Buffer Unavailable) — DMA stops after + * exhausting all descriptors. Clear the status and kick the DMA. */ + if (ETH_DMACSR & ETH_DMACSR_RBU) { + ETH_DMACSR = ETH_DMACSR_RBU; /* W1C */ + __asm volatile ("dsb sy" ::: "memory"); + ETH_DMACRXDTPR = ETH_DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1U]); + } + desc = &rx_ring[rx_idx]; if (desc->des3 & ETH_RDES3_OWN) return 0; @@ -502,12 +716,17 @@ static int eth_poll(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) if ((status & (ETH_RDES3_FS | ETH_RDES3_LS)) == (ETH_RDES3_FS | ETH_RDES3_LS)) { frame_len = status & ETH_RDES3_PL_MASK; if (frame_len > len) frame_len = len; - memcpy(rx_staging_buffer, rx_buffers[rx_idx], frame_len); - memcpy(frame, rx_staging_buffer, frame_len); + if (frame_len > 0) { + memcpy(frame, rx_buffers[rx_idx], frame_len); + } } - /* Reinitialize descriptor. */ + /* Reinitialize descriptor — must restore des0 (buffer address) since + * DMA writeback overwrites it with timestamp data. */ + desc->des0 = ETH_DMA_ADDR(rx_buffers[rx_idx]); desc->des1 = 0; + desc->des2 = 0; + __asm volatile ("dsb sy" ::: "memory"); desc->des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; __asm volatile ("dsb sy" ::: "memory"); ETH_DMACRXDTPR = ETH_DMA_ADDR(desc); @@ -585,6 +804,7 @@ void stm32_eth_get_stats(uint32_t *polls, uint32_t *pkts) if (pkts) *pkts = rx_pkt_count; } + uint32_t stm32_eth_get_rx_des3(void) { return rx_ring[0].des3; @@ -654,8 +874,8 @@ int stm32_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac) } eth_config_mac(mac); eth_config_mtl(); + eth_config_dma(); /* DMA mode config BEFORE descriptor setup (per CubeN6 HAL) */ eth_init_desc(); - eth_config_dma(); eth_phy_init(); eth_config_speed_duplex(); eth_start(); diff --git a/src/port/stm32n6/main.c b/src/port/stm32n6/main.c index 75a9b07c..2bee99de 100644 --- a/src/port/stm32n6/main.c +++ b/src/port/stm32n6/main.c @@ -386,7 +386,8 @@ static void pwr_enable_io_supply(void) DMB(); PWR_SVMCR1 |= PWR_SVMCR1_VDDIO4SV; PWR_SVMCR2 |= PWR_SVMCR2_VDDIO5SV; - PWR_SVMCR3 |= PWR_SVMCR3_VDDIO2SV | PWR_SVMCR3_VDDIO3SV; + PWR_SVMCR3 |= (1u << 4u) | PWR_SVMCR3_VDDIO2SV | PWR_SVMCR3_VDDIO3SV | + (1u << 12u) | (1u << 20u); /* Match CubeN6: 0x00101310 */ DMB(); } @@ -419,16 +420,22 @@ static void dcache_enable(void) * * ETH buffers (.eth_buffers) are in AXISRAM2 at 0x34100000. * ========================================================================= */ +extern uint32_t _eth_start; +extern uint32_t _eth_end; + static void mpu_configure_eth_nocache(void) { + uint32_t base = (uint32_t)&_eth_start & ~0x1Fu; /* Align down to 32 */ + uint32_t limit = ((uint32_t)&_eth_end + 0x1Fu) & ~0x1Fu; /* Align up */ + /* Disable MPU */ MPU_CTRL = 0; DSB(); - /* Region 0: AXISRAM2 (ETH DMA buffers), non-cacheable Normal memory */ + /* Region 0: ETH DMA descriptors + buffers, Normal Non-cacheable. */ MPU_RNR = 0; - MPU_RBAR = 0x34100000u | (1u << 1u) | (1u << 0u); - MPU_RLAR = (0x3410FFFFu & ~0x1Fu) | (2u << 1u) | 1u; + MPU_RBAR = base | (1u << 1u) | (1u << 0u); /* AP=RW, XN=1 */ + MPU_RLAR = ((limit - 1u) & ~0x1Fu) | (2u << 1u) | 1u; /* AttrIdx=2, EN=1 */ /* MAIR0: Attr2 (bits [23:16]) = 0x44 → Normal, Non-cacheable */ MPU_MAIR0 = (0x44u << 16u); @@ -469,11 +476,6 @@ static void led_on(void) GPIO_BSRR(LED1_PORT) = (1u << LED1_PIN); } -static void led_off(void) -{ - GPIO_BSRR(LED1_PORT) = (1u << (LED1_PIN + 16u)); -} - static void led_toggle(void) { GPIO_ODR(LED1_PORT) ^= (1u << LED1_PIN); @@ -509,25 +511,23 @@ static void uart_init(void) afr |= (7u << (5u * 4u)) | (7u << (6u * 4u)); GPIO_AFRL(GPIOE_BASE) = afr; - /* Configure USART1: 115200 baud - * After reset the CPU runs at HSI 64 MHz, but the APB2 clock feeding - * USART1 is 32 MHz (IC2 default divider = 2). - * BRR is re-configured after clock_config() switches to PLL. */ + /* Configure USART1: 115200 baud using IC9 kernel clock (64 MHz). + * BRR = 64000000 / 115200 = 556 (0x22C), matching CubeN6. */ USART1_CR1 = 0; USART1_CR2 = 0; USART1_CR3 = 0; - USART1_BRR = 32000000u / 115200u; /* 278 */ - - /* Enable transmitter + receiver + USART */ - USART1_CR1 = (1u << 3u) | (1u << 2u); /* TE + RE */ - delay(10); - USART1_CR1 |= (1u << 0u); /* UE */ - delay(100); + USART1_BRR = 64000000u / 115200u; /* 556 */ + DSB(); + delay(1000); + USART1_CR1 = (1u << 0u) | (1u << 2u) | (1u << 3u); /* UE + RE + TE */ + delay(1000); } static void uart_putc(char c) { - while ((USART1_ISR & (1u << 7u)) == 0) { } /* Wait for TXE */ + volatile uint32_t timeout = 100000u; + while ((USART1_ISR & (1u << 7u)) == 0 && --timeout) { } + if (timeout == 0) return; /* USART kernel clock not running */ USART1_TDR = (uint32_t)c; } @@ -632,9 +632,10 @@ static void gpio_eth_pin(uint32_t base, uint32_t pin) moder |= (2u << pos2); GPIO_MODER(base) = moder; - /* High speed */ + /* High speed (0b10) — match CubeN6. Very High (0b11) causes issues on N6. */ ospeedr = GPIO_OSPEEDR(base); - ospeedr |= (3u << pos2); + ospeedr &= ~(3u << pos2); + ospeedr |= (2u << pos2); GPIO_OSPEEDR(base) = ospeedr; /* AF11 for Ethernet */ @@ -662,47 +663,59 @@ static void eth_gpio_init(void) { volatile uint32_t *ramcfg_sram2_cr = (volatile uint32_t *)0x52023080u; RCC_AHB2ENSR = (1u << 12u); /* RAMCFGEN */ - RCC_MEMENSR = (1u << 1u); /* AXISRAM2EN */ + RCC_MEMENSR = (1u << 1u) | /* AXISRAM2EN */ + (1u << 4u) | /* AHBSRAM1EN */ + (1u << 5u); /* AHBSRAM2EN */ delay(100); *ramcfg_sram2_cr &= ~(1u << 20u); /* Clear SRAMSD → power on */ DSB(); delay(10000); } + /* Open RISAF3 (AXISRAM2) to allow ETH DMA R+W access. + * Default ENDR=0xFFF covers only 4KB — ETH buffers at offset 0xF8000 are blocked! + * Extend to cover full 1MB. Leave RISAF2 (AXISRAM1) untouched (code runs there). + * RISAF3_BASE = 0x54028000 */ + { + /* RISAF3 REG0: cover full AXISRAM2 (1MB), all CIDs R+W, Secure. + * CFGR: BREN=1 (bit 0), SEC=1 (bit 8) — must match secure bus alias. */ + *(volatile uint32_t *)0x54028044u = 0x00000000u; /* STARTR = 0 */ + *(volatile uint32_t *)0x54028048u = 0x000FFFFFu; /* ENDR = 1MB-1 */ + *(volatile uint32_t *)0x5402804Cu = 0x000F000Fu; /* CIDCFGR: CID0-3 R+W */ + *(volatile uint32_t *)0x54028040u = 0x00000101u; /* CFGR: BREN=1, SEC=1 */ + DSB(); + } + /* Configure RIFSC: grant ETH1 DMA (RIMC master 6) memory access. - * RIMC_ATTR6: DSEL=1 (use default CID), DSEC=0 (non-secure), DPRIV=1 (privileged) - * RIFSC_BASE = 0x54024000, RIMC_ATTR[6] at offset 0xC10 + 6*4 = 0xC28 */ + * Matches CubeN6 RISAF_Config exactly: + * 1. RIMC_ATTR6: CID=1, SEC, PRIV + * 2. RISC slave: ETH1 peripheral marked SEC+PRIV + * RIFSC_BASE = 0x54024000 */ { volatile uint32_t *rimc_attr6 = (volatile uint32_t *)(0x54024000u + 0xC28u); RCC_AHB3ENSR = (1u << 9u); /* RIFSCEN */ delay(100); - /* CID=1, DSEL=1, DSEC=1 (secure), DPRIV=1 — matches CubeN6 example */ - *rimc_attr6 = (1u << 0u) | (1u << 4u) | (1u << 8u) | (1u << 9u); + /* RIMC: CID=1, DSEL=0, DSEC=1 (secure), DPRIV=1 — 0x301 */ + *rimc_attr6 = 0x1u | (1u << 8u) | (1u << 9u); + /* RISC: Set ETH1 peripheral as Secure + Privileged. + * ETH1 is bit 28 of SECCFGRx[1] and PRIVCFGRx[1]. */ + *(volatile uint32_t *)(0x54024000u + 0x14u) |= (1u << 28u); /* SEC */ + *(volatile uint32_t *)(0x54024000u + 0x34u) |= (1u << 28u); /* PRIV */ DSB(); uart_puts(" RIMC_ATTR6 (ETH1): "); uart_puthex(*rimc_attr6); uart_puts("\n"); } - /* Enable SYSCFG clock and configure VDDIO3 I/O compensation. - * GPIOF pins are on VDDIO3 — compensation cell must be enabled - * for the RMII data signals to work at 50 MHz. */ + /* Compensation cells per Errata Sheet ES0620 (from CubeN6 SystemInit). + * ALL VDDIO domains get the same errata workaround value 0x287: + * CS=1 (manual code selection) + specific NMOS/PMOS compensation codes. */ RCC_APB4ENSR2 = (1u << 0u); /* SYSCFGEN */ delay(100); - SYSCFG_VDDIO3CCCR |= (1u << 8u); /* EN = 1 (enable compensation) */ + /* VDDIO3 compensation per Errata ES0620 (CubeN6 SystemInit value) */ + SYSCFG_VDDIO3CCCR = 0x00000287u; DSB(); - /* Enable HSLV (1.8V) mode for VDDIO3 (GPIOF Ethernet pins). - * The VDDIO3VRSEL bit in PWR_SVMCR3 is protected by OTP124 bit 15. - * Write the BSEC shadow register first to unlock it (non-permanent). */ - { - volatile uint32_t *bsec_fvr124 = (volatile uint32_t *)0x560091F0u; - *bsec_fvr124 |= (1u << 15u); /* HSLV_VDDIO3 shadow */ - DSB(); - PWR_SVMCR3 |= (1u << 26u); /* VDDIO3VRSEL = 1.8V */ - DMB(); - } - /* Configure RMII pins (AF11) — NUCLEO-N657X0-Q pinout */ gpio_eth_pin(GPIOF_BASE, 4); /* MDIO */ gpio_eth_pin(GPIOG_BASE, 11); /* MDC */ @@ -757,120 +770,103 @@ int main(void) uint64_t tick = 0; int ret; - /* LED first — absolute minimum, just GPIO clock + pin mode. - * This ensures the HardFault handler can blink the LED. */ - pwr_enable_io_supply(); - - /* Enable HSLV mode for VDDIO3 — AFTER pwr_enable_io_supply to avoid - * the PWR_SVMCR3 read-modify-write from clearing VRSEL. - * VDDIO3 is 1.8V on NUCLEO-N657X0-Q. Without HSLV, I/O pads can't - * switch fast enough for 50MHz RMII Ethernet. - * BSEC shadow register unlocks the PWR VRSEL write (non-permanent). */ + /* Set SAU ALLNS=1: all memory treated as Non-Secure. + * This allows Non-Secure DMA masters to write to any memory region. */ { - volatile uint32_t *bsec_fvr124 = (volatile uint32_t *)0x560091F0u; - *bsec_fvr124 |= (1u << 15u); /* HSLV_VDDIO3 shadow */ - __asm volatile ("dsb sy" ::: "memory"); - for (volatile uint32_t d = 0; d < 10000; d++) {} /* Wait for BSEC */ - PWR_SVMCR3 |= (1u << 26u); /* VDDIO3VRSEL = 1.8V */ - __asm volatile ("dmb sy" ::: "memory"); + volatile uint32_t *sau_ctrl = (volatile uint32_t *)0xE000EDD0u; + volatile uint32_t *sau_rnr = (volatile uint32_t *)0xE000EDD8u; + volatile uint32_t *sau_rbar = (volatile uint32_t *)0xE000EDDCu; + volatile uint32_t *sau_rlar = (volatile uint32_t *)0xE000EDE0u; + int i; + *sau_ctrl = 0; + DSB(); + for (i = 0; i < 8; i++) { + *sau_rnr = (uint32_t)i; + *sau_rbar = 0; + *sau_rlar = 0; + } + *sau_ctrl = 2u; /* ALLNS=1, ENABLE=0 → all memory Non-Secure */ + DSB(); ISB(); } + + pwr_enable_io_supply(); + led_init(); led_on(); - /* UART init — after this, HardFault handler can print diagnostics */ - uart_init(); /* Uses HSI 64 MHz for now — BRR adjusted below */ + /* PLL + IC dividers must be up before UART — boot ROM leaves IC dividers + * disabled, so APB2 has no clock source and USART1 kernel clock is dead. */ + clock_config(); + + /* Blink LED 3x fast to indicate PLL locked */ + { + int blink; + for (blink = 0; blink < 3; blink++) { + led_toggle(); + delay(500000); + led_toggle(); + delay(500000); + } + } + + /* Enable IC9 divider (USART1 kernel clock source on N6). + * CubeN6 uses IC9CFGR=0x30000000 (SEL=3=HSI, INT=0=div1) → 64 MHz. + * Then CCIPR13 USART1SEL=2 selects ic9_ck as USART1 kernel clock. */ + { + volatile uint32_t *ic9cfgr = (volatile uint32_t *)(RCC_BASE + 0xE4u); + volatile uint32_t *ccipr13 = (volatile uint32_t *)(RCC_BASE + 0x174u); + /* Disable IC9, configure, re-enable */ + RCC_DIVENCR = (1u << 8u); /* IC9 disable */ + *ic9cfgr = 0x30000000u; /* SEL=3 (HSI), INT=0 (div 1) → 64 MHz */ + RCC_DIVENSR = (1u << 8u); /* IC9 enable */ + DSB(); + *ccipr13 = (*ccipr13 & ~0x7u) | 0x2u; /* USART1SEL = ic9_ck */ + DSB(); + } + + uart_init(); uart_ready = 1; - uart_puts("\n[BOOT] LED+UART OK at HSI\n"); - uart_puts("[BOOT] Enabling PLL...\n"); - clock_config(); - /* UART baud is now wrong (clock changed) — just keep going. - * Ping test doesn't need UART output. */ icache_enable(); mpu_configure_eth_nocache(); dcache_enable(); - /* Blink to confirm UART init */ - led_off(); delay(200000); - led_on(); delay(200000); - led_off(); delay(200000); - led_on(); - - uart_puts("\n\n=== wolfIP STM32N6 Echo Server ===\n"); - /* Initialize wolfIP stack */ - uart_puts("Initializing wolfIP stack...\n"); wolfIP_init_static(&IPStack); - /* ETH init sequence: - * 1. Configure GPIO for RMII (pins + VDDIO3 compensation + RIMC) - * 2. Select RMII mode in CCIPR2 BEFORE enabling ETH clocks - * 3. Enable ETH clocks - * 4. Reset ETH MAC */ - - /* Step 1: GPIO and peripheral setup */ - eth_gpio_init(); + /* ETH init sequence — matching CubeN6 HAL MspInit order: + * 1. Enable ETH clocks (AHB5) + * 2. Set RMII mode (CCIPR2) + * 3. Configure GPIO + RIMC + compensation + * CubeN6 HAL: MspInit enables clocks → HAL_ETH_Init sets CCIPR2 → SWR → config */ - /* Step 2: RMII mode select — must be before ETH clocks on some STM32 */ - /* ETH1SEL = RMII (bit 18) */ - RCC_CCIPR2 |= RCC_CCIPR2_ETH1SEL_RMII; - DSB(); - delay(1000); - - /* Step 3: Enable Ethernet clocks (AHB5) */ - /* Only enable MAC, TX, RX clocks (NOT ETH1EN at bit 25 — per CubeN6 example) */ + /* Step 1: Enable Ethernet clocks */ RCC_AHB5ENSR = (1u << 22u) | (1u << 23u) | (1u << 24u); DSB(); delay(10000); - /* Step 4: Reset Ethernet MAC */ - RCC_AHB5RSTR |= (1u << 22u); + /* Step 1b: RCC peripheral reset of ETH1 — deeper than SWR */ + RCC_AHB5RSTR = (1u << 22u); /* Assert ETH1MAC reset */ DSB(); - delay(1000); - RCC_AHB5RSTR &= ~(1u << 22u); + delay(10000); + RCC_AHB5RSTR = 0; /* Release reset */ DSB(); - delay(100000); /* Longer delay after reset */ + delay(10000); - /* Verify ETH peripheral is accessible */ - { - volatile uint32_t *eth_maccr = (volatile uint32_t *)0x58036000u; - volatile uint32_t *eth_dmamr = (volatile uint32_t *)0x58037000u; - volatile uint32_t *eth_dmadsr = (volatile uint32_t *)0x58037008u; - uart_puts(" ETH1_MACCR @0x58036000: "); - uart_puthex(*eth_maccr); - uart_puts("\n ETH1_DMAMR @0x58037000: "); - uart_puthex(*eth_dmamr); - uart_puts("\n ETH1_DMADSR @0x58037008: "); - uart_puthex(*eth_dmadsr); - uart_puts("\n"); - /* Try DMA SWR manually */ - uart_puts(" Writing SWR...\n"); - *eth_dmamr |= 1u; - delay(100000); - uart_puts(" ETH1_DMAMR after SWR: "); - uart_puthex(*eth_dmamr); - uart_puts("\n"); - } + /* Step 2: Select RMII mode (external REF_CLK from PHY). + * RCC_CCIPR2 fields (from Zephyr stm32n6_clock.h): + * ETH1PTP_SEL [1:0] = 0 (default) + * ETH1CLK_SEL [13:12]= 0 (default, bus clock) + * ETH1_SEL [18:16]= 4 (RMII mode) + * ETH1REFCLK_SEL [20] = 0 (external REF_CLK from PHY) + * ETH1GTXCLK_SEL [24] = 0 (default) */ + RCC_CCIPR2 |= RCC_CCIPR2_ETH1SEL_RMII; + DSB(); + delay(10000); - /* Test MDIO directly before full init */ - { - volatile uint32_t *macmdioar = (volatile uint32_t *)0x58036200u; - volatile uint32_t *macmdiodr = (volatile uint32_t *)0x58036204u; - uint32_t cfg, to; - uart_puts(" MDIO test: reading PHY0 reg2...\n"); - /* CR=0, RDA=2 (ID1), PA=0, GOC=3 (read), MB=1 */ - cfg = (0u << 8) | (2u << 16) | (0u << 21) | (3u << 2) | 1u; - *macmdioar = cfg; - to = 100000; - while ((*macmdioar & 1u) && --to) {} - uart_puts(" MACMDIOAR: "); - uart_puthex(*macmdioar); - uart_puts("\n MACMDIODR: "); - uart_puthex(*macmdiodr); - uart_puts(" (timeout left: "); - uart_putdec(to); - uart_puts(")\n"); - } + /* Step 3: GPIO and peripheral setup (AFTER clocks + RMII selection) */ + eth_gpio_init(); + delay(100000); /* Wait for PHY REF_CLK */ /* Initialize Ethernet MAC + PHY */ uart_puts("Initializing Ethernet MAC...\n"); diff --git a/src/port/stm32n6/target.ld b/src/port/stm32n6/target.ld index ec1afd77..98a42e0d 100644 --- a/src/port/stm32n6/target.ld +++ b/src/port/stm32n6/target.ld @@ -12,7 +12,8 @@ MEMORY { SRAM (rwx) : ORIGIN = 0x34000000, LENGTH = 512K - AXISRAM2 (rwx) : ORIGIN = 0x34100000, LENGTH = 1024K + AXISRAM2 (rwx) : ORIGIN = 0x341F8000, LENGTH = 32K + AHBSRAM (rw) : ORIGIN = 0x38000000, LENGTH = 32K } _estack = ORIGIN(SRAM) + LENGTH(SRAM); @@ -88,8 +89,10 @@ SECTIONS .eth_buffers (NOLOAD) : { . = ALIGN(32); + _eth_start = .; *(.eth_buffers*) . = ALIGN(32); + _eth_end = .; } > AXISRAM2 ._user_heap_stack (NOLOAD) : From d1d4eb9e5734ed91d735ad96aaef5cfa152887b9 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 24 Mar 2026 09:34:06 -0700 Subject: [PATCH 3/5] STM32N6: cleanup, add CI build, add README offset 0x1180 is DMAC1CR on N6 (clobbers CH1 DMA config) - Add named DMACIER/DMAC1IER defines, replace magic numbers - Remove unused debug functions (get_rx_des0, get_rx_ring_addr, get_rx_tail, kick_rx) from driver and header - Fix stale DMACCR comment (DSL=1 doubleword skip, not DSL=0) - Make CH1 RX descriptor init consistent with CH0 (arm in eth_start) - Remove unused defines (GPIOA/B/C, read-only ENR regs, AHB1RSTR) - Remove AHBSRAM linker region and clock enables (unused) - Guard boot LED blink behind DEBUG_BLINK - Fix comments (linker script, LED pin) - Add CI workflow: compile-check with arm-none-eabi-gcc - Add README.md for N6 port --- .github/workflows/stm32n6-build.yml | 32 ++++ src/port/stm32/stm32_eth.c | 68 +++----- src/port/stm32/stm32_eth.h | 4 - src/port/stm32n6/README.md | 256 ++++++++++++++++++++++++++++ src/port/stm32n6/main.c | 213 ++++++----------------- src/port/stm32n6/target.ld | 3 +- 6 files changed, 363 insertions(+), 213 deletions(-) create mode 100644 .github/workflows/stm32n6-build.yml create mode 100644 src/port/stm32n6/README.md diff --git a/.github/workflows/stm32n6-build.yml b/.github/workflows/stm32n6-build.yml new file mode 100644 index 00000000..828afa59 --- /dev/null +++ b/.github/workflows/stm32n6-build.yml @@ -0,0 +1,32 @@ +name: STM32N6 Build + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +jobs: + stm32n6_build: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + - name: Install ARM toolchain + run: | + set -euo pipefail + sudo apt-get update + sudo apt-get install -y gcc-arm-none-eabi + + - name: Build STM32N6 echo firmware + run: | + set -euo pipefail + make -C src/port/stm32n6 CC=arm-none-eabi-gcc OBJCOPY=arm-none-eabi-objcopy + + - name: Verify binary + run: | + set -euo pipefail + test -f src/port/stm32n6/app.bin + arm-none-eabi-size src/port/stm32n6/app.elf diff --git a/src/port/stm32/stm32_eth.c b/src/port/stm32/stm32_eth.c index c79cb13c..0a8620f8 100644 --- a/src/port/stm32/stm32_eth.c +++ b/src/port/stm32/stm32_eth.c @@ -78,6 +78,12 @@ #define ETH_DMACTXRLR ETH_REG(0x112CU) #define ETH_DMACRXRLR ETH_REG(0x1130U) #define ETH_DMACSR ETH_REG(0x1160U) +#define ETH_DMACIER ETH_REG(0x1134U) /* DMA CH0 Interrupt Enable */ +#define ETH_DMACIER_NIE (1U << 15) /* Normal Interrupt Summary Enable */ +#define ETH_DMACIER_AIE (1U << 14) /* Abnormal Interrupt Summary Enable */ +#define ETH_DMACIER_RBUE (1U << 7) /* RX Buffer Unavailable Enable */ +#define ETH_DMACIER_RIE (1U << 6) /* Receive Interrupt Enable */ +#define ETH_DMACIER_TIE (1U << 0) /* Transmit Interrupt Enable */ #define ETH_MACMDIOAR ETH_REG(0x0200U) #define ETH_MACMDIODR ETH_REG(0x0204U) @@ -93,6 +99,7 @@ #define ETH_DMAC1TXRLR ETH_REG(0x11ACU) #define ETH_DMAC1RXRLR ETH_REG(0x11B0U) #define ETH_DMAC1SR ETH_REG(0x11E0U) +#define ETH_DMAC1IER ETH_REG(0x11B4U) /* DMA CH1 Interrupt Enable */ #define ETH_MTLTXQ1OMR ETH_REG(0x0D40U) #endif @@ -274,11 +281,13 @@ static int eth_hw_reset(void) return 0; } +#if !defined(STM32N6) static void eth_trigger_tx(void) { ETH_TPDR = 0U; __asm volatile ("dsb sy" ::: "memory"); } +#endif static void eth_config_mac(const uint8_t mac[6]) { @@ -447,9 +456,8 @@ static void eth_init_desc(void) tx_ring1[i].des2 = 0; tx_ring1[i].des3 = 0; } for (i = 0; i < RX_DESC_COUNT; i++) { - rx_ring1[i].des0 = ETH_DMA_ADDR(rx_buffers1[i]); - rx_ring1[i].des1 = 0; rx_ring1[i].des2 = 0; - rx_ring1[i].des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; + rx_ring1[i].des0 = 0; rx_ring1[i].des1 = 0; + rx_ring1[i].des2 = 0; rx_ring1[i].des3 = 0; } __asm volatile ("dsb sy" ::: "memory"); ETH_DMAC1TXDLAR = ETH_DMA_ADDR(&tx_ring1[0]); @@ -483,10 +491,9 @@ static void eth_config_dma(void) /* DMA System Bus Mode (matching CubeN6) */ ETH_DMASBMR = 0x03031003u; - /* DMACCR: DSL and PBLX8 mode. CubeN6 uses 0x00040218 (MSS=536, PBLX8=1, DSL=1). - * DSL=0 for 16-byte contiguous descriptors (our struct is 16 bytes). */ + /* DMACCR: DSL=1 (skip 1 doubleword = 8 bytes between 16-byte descriptors, + * stride = 24 bytes matching HAL ETH_DMADescTypeDef). MSS=536. */ #if defined(STM32N6) - /* DSL=1 (32-bit/4-byte skip → 20-byte stride), MSS=536 (matches HAL) */ ETH_DMACCR = 0x40218u; #else ETH_DMACCR = ETH_DMACCR_DSL_0BIT; @@ -563,9 +570,11 @@ static void eth_start(void) /* Enable DMA interrupt flags (matching CubeN6 HAL_ETH_Start_IT). * Even in polled mode, some GMAC implementations need these enabled * for the DMA to process descriptors. */ - ETH_REG(0x1134U) = (1U << 15) | (1U << 14) | (1U << 7) | (1U << 6) | (1U << 0); + ETH_DMACIER = ETH_DMACIER_NIE | ETH_DMACIER_AIE | ETH_DMACIER_RBUE | + ETH_DMACIER_RIE | ETH_DMACIER_TIE; #if defined(STM32N6) - ETH_REG(0x11B4U) = (1U << 15) | (1U << 14) | (1U << 7) | (1U << 6) | (1U << 0); + ETH_DMAC1IER = ETH_DMACIER_NIE | ETH_DMACIER_AIE | ETH_DMACIER_RBUE | + ETH_DMACIER_RIE | ETH_DMACIER_TIE; #endif /* Clear TX and RX process stopped flags + RBU (bit 7). */ @@ -766,7 +775,11 @@ static int eth_send(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) __asm volatile ("dsb sy" ::: "memory"); ETH_DMACSR = ETH_DMACSR_TBU; +#if !defined(STM32N6) + /* TPDR (0x1180) is a separate TX poll register on H5/H7. + * On N6, offset 0x1180 is DMAC1CR — writing 0 would clobber CH1 config. */ if (tx_idx == 0U) eth_trigger_tx(); +#endif next_idx = (tx_idx + 1U) % TX_DESC_COUNT; ETH_DMACTXDTPR = ETH_DMA_ADDR(&tx_ring[next_idx]); @@ -810,45 +823,6 @@ uint32_t stm32_eth_get_rx_des3(void) return rx_ring[0].des3; } -uint32_t stm32_eth_get_rx_des0(void) -{ - return rx_ring[0].des0; -} - -uint32_t stm32_eth_get_rx_ring_addr(void) -{ - return (uint32_t)&rx_ring[0]; -} - -uint32_t stm32_eth_get_dmacsr(void) -{ - /* Clear RBU by writing 1 to bit 7. */ - uint32_t val = ETH_DMACSR; - if (val & 0x80) { - ETH_DMACSR = 0x80; - } - return val; -} - -uint32_t stm32_eth_get_rx_tail(void) -{ - return ETH_DMACRXDTPR; -} - -void stm32_eth_kick_rx(void) -{ - uint32_t i; - /* Reinitialize all RX descriptors and kick DMA. */ - for (i = 0; i < RX_DESC_COUNT; i++) { - *(volatile uint32_t *)&rx_ring[i].des0 = ETH_DMA_ADDR(rx_buffers[i]); - *(volatile uint32_t *)&rx_ring[i].des1 = 0; - *(volatile uint32_t *)&rx_ring[i].des2 = 0; - *(volatile uint32_t *)&rx_ring[i].des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; - } - __asm volatile ("dsb sy" ::: "memory"); - __asm volatile ("isb sy" ::: "memory"); - ETH_DMACRXDTPR = ETH_DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1U]); -} int stm32_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac) { diff --git a/src/port/stm32/stm32_eth.h b/src/port/stm32/stm32_eth.h index 6aa06521..989d39e8 100644 --- a/src/port/stm32/stm32_eth.h +++ b/src/port/stm32/stm32_eth.h @@ -15,10 +15,6 @@ int stm32_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac); void stm32_eth_get_stats(uint32_t *polls, uint32_t *pkts); uint32_t stm32_eth_get_rx_des3(void); -uint32_t stm32_eth_get_rx_des0(void); -uint32_t stm32_eth_get_rx_ring_addr(void); uint32_t stm32_eth_get_dmacsr(void); -uint32_t stm32_eth_get_rx_tail(void); -void stm32_eth_kick_rx(void); #endif /* WOLFIP_STM32_ETH_H */ diff --git a/src/port/stm32n6/README.md b/src/port/stm32n6/README.md new file mode 100644 index 00000000..685e03aa --- /dev/null +++ b/src/port/stm32n6/README.md @@ -0,0 +1,256 @@ +# wolfIP STM32N6 Port + +This port provides a bare-metal wolfIP TCP/IP stack implementation for the +STM32N6 microcontroller (NUCLEO-N657X0-Q board). The STM32N6 is ST's first +Cortex-M55 microcontroller, running at 600 MHz (up to 800 MHz) with Helium +vector extensions, targeting high-performance edge AI workloads. + +## Features + +- **TCP/IP Stack**: Full wolfIP stack with TCP, UDP, ICMP, ARP +- **600 MHz Cortex-M55**: PLL1 configured for 600 MHz CPU clock +- **Ethernet**: RMII interface with LAN8742 PHY, dual DMA channels +- **Bare Metal**: No HAL, no CMSIS, no RTOS — fully self-contained +- **JTAG-to-SRAM**: Loads directly into AXISRAM via OpenOCD (no flash required) + +## Hardware Requirements + +- NUCLEO-N657X0-Q development board (STM32N657X0H, MB1940) +- Ethernet cable connected to a switch or directly to host PC +- USB cable for ST-Link programming and debug UART + +## About the STM32N6 + +The STM32N6 is built around the Arm Cortex-M55 core running at 600 MHz, +with support for up to 800 MHz at Voltage Scale 0. Unlike most STM32 parts, +the N6 has no internal flash — all firmware resides on external NOR flash +(XSPI2) or is loaded directly into on-chip SRAM. The device provides over +4.2 MB of on-chip SRAM across multiple regions (AXISRAM1-6, AHBSRAM, DTCM). + +Key differences from STM32H5/H7: +- **GMAC v5.20**: Dual-channel DMA with 24-byte descriptor stride (DSL=1) +- **RISAF firewall**: Memory access requires explicit RISAF region configuration +- **RIMC/RISC**: ETH DMA bus master identity (CID) must match RISAF grants +- **SAU/IDAU**: Security attribution affects DMA memory access paths +- **No internal flash**: Code runs from SRAM (JTAG load) or external XSPI flash + +## Clock Configuration + +``` +HSI 64 MHz -> PLL1 (M=4, N=75) -> VCO 1200 MHz -> PDIV1=1 -> 1200 MHz + IC1 /2 = 600 MHz -> CPU + IC2 /3 = 400 MHz -> AXI bus + IC6 /4 = 300 MHz -> System bus C + IC11 /3 = 400 MHz -> System bus D +AHB prescaler /2 -> HCLK = 300 MHz +USART1 kernel clock: IC9 -> HSI 64 MHz +``` + +## Pin Configuration (NUCLEO-N657X0-Q RMII) + +| Pin | Function | Description | +|------|--------------|--------------------| +| PF4 | ETH_MDIO | PHY management | +| PG11 | ETH_MDC | PHY clock | +| PF7 | ETH_REF_CLK | 50 MHz reference | +| PF10 | ETH_CRS_DV | Carrier sense | +| PF11 | ETH_TX_EN | Transmit enable | +| PF12 | ETH_TXD0 | Transmit data 0 | +| PF13 | ETH_TXD1 | Transmit data 1 | +| PF14 | ETH_RXD0 | Receive data 0 | +| PF15 | ETH_RXD1 | Receive data 1 | +| PE5 | USART1_TX | Debug output | +| PE6 | USART1_RX | Debug input | +| PO1 | LED LD1 | Green heartbeat | + +All Ethernet pins use AF11. USART1 uses AF7. + +## Building + +### Prerequisites + +- ARM GCC toolchain (`arm-none-eabi-gcc`) +- OpenOCD (STMicroelectronics fork with STM32N6 support) + +```bash +# Ubuntu/Debian +sudo apt install gcc-arm-none-eabi +``` + +### Build + +```bash +cd src/port/stm32n6 +CC=arm-none-eabi-gcc make +``` + +This produces `app.elf` and `app.bin` for loading into AXISRAM via JTAG. + +### Memory Usage + +```bash +make size +``` + +| Configuration | Code + Data | BSS (static RAM) | +|---------------|-------------|------------------| +| TCP Echo only | ~25 KB | ~146 KB | + +## Flashing + +The N6 port loads firmware directly into AXISRAM1 (0x34000000) via JTAG. +No flash programming is needed. + +```bash +make flash +# or +bash flash.sh +``` + +The `flash.sh` script: +1. Resets the CPU via OpenOCD +2. Loads `app.bin` to AXISRAM1 at 0x34000000 +3. Sets VTOR, stack pointer, and entry point +4. Resumes execution + +## Serial Console + +Connect to the ST-Link VCP at 115200 baud: + +```bash +picocom -b 115200 /dev/ttyACM0 +# or +make monitor +``` + +### Expected Boot Output + +``` + RIMC_ATTR6 (ETH1): 0x00000301 +Initializing Ethernet MAC... + PHY link: UP, PHY addr: 0x00000000 + MAC: 02:11:CC:DD:55:66 +Setting IP configuration: + IP: 192.168.12.11 + Mask: 255.255.255.0 + GW: 192.168.12.1 +Creating TCP socket on port 7... +Entering main loop. Ready for connections! + TCP Echo: port 7 +``` + +## Network Configuration + +The port uses static IP by default (DHCP disabled). + +| Setting | Value | +|-------------|-----------------| +| IP Address | 192.168.12.11 | +| Subnet Mask | 255.255.255.0 | +| Gateway | 192.168.12.1 | + +Configure your host PC to be on the same subnet: + +```bash +sudo ip addr add 192.168.12.1/24 dev eth0 +sudo ip link set eth0 up +``` + +Replace `eth0` with your Ethernet interface name. + +## Testing + +### Ping + +```bash +ping 192.168.12.11 +``` + +Expected: sub-millisecond replies (~0.15 ms RTT). + +### TCP Echo (Port 7) + +```bash +echo "Hello STM32N6!" | nc -q1 -w2 192.168.12.11 7 +``` + +Expected: `Hello STM32N6!` echoed back. + +## Memory Map + +| Region | Address | Size | Usage | +|-----------|--------------|--------|---------------------------------------| +| AXISRAM1 | 0x34000000 | 512 KB | Code, data, BSS, heap, stack | +| AXISRAM2 | 0x341F8000 | 32 KB | ETH DMA descriptors + buffers | + +ETH DMA buffers are placed at the end of AXISRAM2 (matching the CubeN6 HAL +descriptor placement). The MPU marks this region as Normal Non-cacheable for +DMA coherency. + +## N6-Specific Initialization + +The STM32N6 requires several security and bus fabric configurations that +other STM32 ports do not need: + +### SAU (Security Attribution Unit) +All SAU regions are cleared and `ALLNS=1` is set, making all memory +non-secure. This matches the CubeN6 SystemInit behavior. + +### RISAF3 (AXISRAM2 Firewall) +The default RISAF3 base region covers only 4 KB. The ETH DMA buffers at +offset 0xF8000 are outside this range and would be blocked. The port extends +RISAF3 REG0 to cover the full 1 MB of AXISRAM2 with all CIDs granted +read+write access (`SEC=1` for secure-alias compatibility). + +### RIMC (Resource Isolation Master Control) +The ETH DMA bus master is configured with `CID=1` (matching the CPU), +`SEC=1`, `PRIV=1`. This matches the CubeN6 HAL `RISAF_Config()`. + +### RISC (Resource Isolation Slave Control) +The ETH1 peripheral is marked as Secure + Privileged in the RIFSC slave +security registers. + +### RCC ETH Reset +A full RCC peripheral reset of ETH1 is performed before MAC initialization +to ensure a clean state. + +## Debugging + +### GDB + +```bash +make debug +``` + +This starts OpenOCD and connects GDB with the firmware loaded. + +### HardFault Handler + +The port includes a detailed HardFault handler that prints register state +via UART (PC, LR, R0-R3, R12, xPSR, HFSR, CFSR, BFAR, MMFAR). The handler +only accesses UART if `uart_ready` is set, preventing double-faults during +early boot. + +## File Structure + +``` +stm32n6/ + Makefile Build system + README.md This file + config.h wolfIP configuration (static IP, socket counts) + target.ld Linker script (AXISRAM1 code + AXISRAM2 ETH buffers) + openocd.cfg OpenOCD configuration for NUCLEO-N657X0-Q + flash.sh JTAG load script (SRAM execution) + startup.c Cortex-M55 startup code + ivt.c Interrupt vector table + syscalls.c Newlib syscall stubs + main.c Application: clocks, GPIO, UART, SAU, RISAF, RIMC, ETH + ../stm32/stm32_eth.c Shared Ethernet MAC/PHY driver (H5, H7, N6) + ../stm32/stm32_eth.h Shared Ethernet driver header +``` + +## License + +Copyright (C) 2026 wolfSSL Inc. + +This project is licensed under GPLv3. See the wolfIP LICENSE file for details. diff --git a/src/port/stm32n6/main.c b/src/port/stm32n6/main.c index 2bee99de..ab197caf 100644 --- a/src/port/stm32n6/main.c +++ b/src/port/stm32n6/main.c @@ -30,9 +30,7 @@ static void uart_puts(const char *s); static void delay(uint32_t count); -/* ========================================================================= - * RCC — Reset and Clock Control (base 0x56028000, secure bus) - * ========================================================================= */ +/* RCC Reset and Clock Control (secure bus) */ #define RCC_BASE 0x56028000UL /* Control: direct read / set (+0x800) / clear (+0x1000) */ @@ -42,7 +40,7 @@ static void delay(uint32_t count); #define RCC_CR_HSION (1u << 3) #define RCC_CR_PLL1ON (1u << 8) -/* Status — ready flags */ +/* Status ready flags */ #define RCC_SR (*(volatile uint32_t *)(RCC_BASE + 0x04u)) #define RCC_SR_HSIRDY (1u << 3) #define RCC_SR_PLL1RDY (1u << 8) @@ -90,7 +88,7 @@ static void delay(uint32_t count); #define RCC_ICCFGR_INT_SHIFT 16u #define RCC_ICCFGR_SEL_PLL1 (0x0u << 28u) -/* Divider enable: direct / set (+0x800) / clear (+0x1000) */ +/* Divider enable direct / set (+0x800) / clear (+0x1000) */ #define RCC_DIVENR (*(volatile uint32_t *)(RCC_BASE + 0x240u)) #define RCC_DIVENSR (*(volatile uint32_t *)(RCC_BASE + 0xA40u)) #define RCC_DIVENCR (*(volatile uint32_t *)(RCC_BASE + 0x1240u)) @@ -99,17 +97,7 @@ static void delay(uint32_t count); #define RCC_DIVENR_IC6EN (1u << 5u) #define RCC_DIVENR_IC11EN (1u << 10u) -/* Peripheral clock enable — use SET registers (write 1s to set bits). - * Direct ENR registers are read-only status; ENSR is the set register. */ -#define RCC_AHB1ENR (*(volatile uint32_t *)(RCC_BASE + 0x250u)) /* read */ -#define RCC_AHB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x254u)) /* read */ -#define RCC_AHB3ENR (*(volatile uint32_t *)(RCC_BASE + 0x258u)) /* read */ -#define RCC_AHB4ENR (*(volatile uint32_t *)(RCC_BASE + 0x25Cu)) /* read */ -#define RCC_AHB5ENR (*(volatile uint32_t *)(RCC_BASE + 0x260u)) /* read */ -#define RCC_APB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x26Cu)) /* read */ -#define RCC_MEMENR (*(volatile uint32_t *)(RCC_BASE + 0x24Cu)) /* read */ - -/* SET registers — write-only, bits written as 1 get set in ENR */ +/* Peripheral clock enable SET registers (write 1s to set bits) */ #define RCC_AHB2ENSR (*(volatile uint32_t *)(RCC_BASE + 0xA54u)) #define RCC_AHB3ENSR (*(volatile uint32_t *)(RCC_BASE + 0xA58u)) #define RCC_AHB4ENSR (*(volatile uint32_t *)(RCC_BASE + 0xA5Cu)) @@ -117,20 +105,14 @@ static void delay(uint32_t count); #define RCC_APB2ENSR (*(volatile uint32_t *)(RCC_BASE + 0xA6Cu)) #define RCC_MEMENSR (*(volatile uint32_t *)(RCC_BASE + 0xA4Cu)) -/* Clock configuration — ETH PHY interface selection */ +/* Clock configuration for ETH PHY interface selection */ #define RCC_CCIPR2 (*(volatile uint32_t *)(RCC_BASE + 0x148u)) #define RCC_CCIPR2_ETH1SEL_RMII (0x4u << 16u) /* bit 18 = RMII mode */ /* Peripheral reset */ -#define RCC_AHB1RSTR (*(volatile uint32_t *)(RCC_BASE + 0x210u)) #define RCC_AHB5RSTR (*(volatile uint32_t *)(RCC_BASE + 0x220u)) -/* ========================================================================= - * GPIO — secure bus addresses - * ========================================================================= */ -#define GPIOA_BASE 0x56020000UL -#define GPIOB_BASE 0x56020400UL -#define GPIOC_BASE 0x56020800UL +/* GPIO secure bus addresses */ #define GPIOE_BASE 0x56021000UL #define GPIOF_BASE 0x56021400UL #define GPIOG_BASE 0x56021800UL @@ -144,9 +126,7 @@ static void delay(uint32_t count); #define GPIO_AFRL(base) (*(volatile uint32_t *)((base) + 0x20u)) #define GPIO_AFRH(base) (*(volatile uint32_t *)((base) + 0x24u)) -/* ========================================================================= - * PWR — Power Control (base 0x56024800) - * ========================================================================= */ +/* PWR Power Control */ #define PWR_BASE 0x56024800UL #define PWR_SVMCR1 (*(volatile uint32_t *)(PWR_BASE + 0x34u)) #define PWR_SVMCR2 (*(volatile uint32_t *)(PWR_BASE + 0x38u)) @@ -156,9 +136,7 @@ static void delay(uint32_t count); #define PWR_SVMCR3_VDDIO2SV (1u << 8u) #define PWR_SVMCR3_VDDIO3SV (1u << 9u) -/* ========================================================================= - * USART1 — PE5 (TX) / PE6 (RX), AF7 (base 0x52001000) - * ========================================================================= */ +/* USART1 PE5 (TX) / PE6 (RX), AF7 */ #define USART1_BASE 0x52001000UL #define USART1_CR1 (*(volatile uint32_t *)(USART1_BASE + 0x00u)) #define USART1_CR2 (*(volatile uint32_t *)(USART1_BASE + 0x04u)) @@ -167,17 +145,13 @@ static void delay(uint32_t count); #define USART1_ISR (*(volatile uint32_t *)(USART1_BASE + 0x1Cu)) #define USART1_TDR (*(volatile uint32_t *)(USART1_BASE + 0x28u)) -/* ========================================================================= - * SCB — Cortex-M55 cache control - * ========================================================================= */ +/* SCB Cortex-M55 cache control */ #define SCB_CCR (*(volatile uint32_t *)(0xE000ED14UL)) #define SCB_CCR_IC (1u << 17u) #define SCB_CCR_DC (1u << 16u) #define SCB_ICIALLU (*(volatile uint32_t *)(0xE000EF50UL)) -/* ========================================================================= - * MPU — Cortex-M55 ARMv8.1-M Memory Protection Unit - * ========================================================================= */ +/* MPU ARMv8.1-M Memory Protection Unit */ #define MPU_TYPE (*(volatile uint32_t *)(0xE000ED90UL)) #define MPU_CTRL (*(volatile uint32_t *)(0xE000ED94UL)) #define MPU_RNR (*(volatile uint32_t *)(0xE000ED98UL)) @@ -186,25 +160,18 @@ static void delay(uint32_t count); #define MPU_MAIR0 (*(volatile uint32_t *)(0xE000EDC0UL)) #define MPU_MAIR1 (*(volatile uint32_t *)(0xE000EDC4UL)) -/* ========================================================================= - * Fault registers - * ========================================================================= */ +/* Fault registers */ #define SCB_HFSR (*(volatile uint32_t *)0xE000ED2CUL) #define SCB_CFSR (*(volatile uint32_t *)0xE000ED28UL) #define SCB_BFAR (*(volatile uint32_t *)0xE000ED38UL) #define SCB_MMFAR (*(volatile uint32_t *)0xE000ED34UL) -/* ========================================================================= - * Barrier macros - * ========================================================================= */ +/* Barrier macros */ #define DSB() __asm volatile ("dsb sy" ::: "memory") #define ISB() __asm volatile ("isb sy" ::: "memory") #define DMB() __asm volatile ("dmb sy" ::: "memory") -/* ========================================================================= - * LED — LD1 green on PO1 (GPIOO), LD2 red on PG10 (GPIOG) - * Note: These are from the N6570-DK DTS. NUCLEO board may differ. - * ========================================================================= */ +/* LED — LD1 green on PO1 */ #define LED1_PORT GPIOO_BASE #define LED1_PIN 1u #define LED1_RCC_BIT 14u /* RCC_AHB4ENR bit for GPIOO */ @@ -217,13 +184,8 @@ static int listen_fd = -1; static int client_fd = -1; static uint8_t rx_buf[RX_BUF_SIZE]; -/* ========================================================================= - * HardFault Handler — safe version that won't cause LOCKUP - * - * IMPORTANT: Do NOT access USART or other peripherals here unless we know - * their clocks are enabled. Accessing unclocked peripherals from the fault - * handler causes a double-fault → CPU LOCKUP (unrecoverable without NRST). - * ========================================================================= */ +/* HardFault Handler use only prints if uart_ready (avoids double-fault on + * unclocked peripherals which would cause CPU LOCKUP). */ static volatile int uart_ready = 0; #define FAULT_USART1_ISR (*(volatile uint32_t *)(USART1_BASE + 0x1Cu)) @@ -272,10 +234,6 @@ void hard_fault_handler_c(uint32_t *frame) fault_uart_puts(" MMFAR:"); fault_uart_puthex(SCB_MMFAR); fault_uart_puts("\n"); } } - /* Bare spin — do NOT access any peripherals (GPIO, UART) here. - * If the fault happens before peripheral clocks are enabled, - * any peripheral access would cause a double-fault → LOCKUP. - * Use GDB to inspect fault registers instead. */ while (1) { } } @@ -291,16 +249,9 @@ void HardFault_Handler(void) ); } -/* ========================================================================= - * Clock Configuration — PLL1 → 600 MHz CPU - * - * HSI 64 MHz → PLL1 (M=4, N=75) → VCO 1200 MHz → PDIV1=1 → 1200 MHz - * IC1 /2 = 600 MHz → CPU - * IC2 /3 = 400 MHz → AXI bus - * IC6 /4 = 300 MHz → system bus C - * IC11/3 = 400 MHz → system bus D - * AHB prescaler /2 → HCLK = 300 MHz - * ========================================================================= */ +/* Clock: HSI 64MHz -> PLL1 (M=4,N=75) -> 1200MHz VCO + * IC1/2=600MHz CPU, IC2/3=400MHz AXI, IC6/4=300MHz, IC11/3=400MHz + * AHB/2 -> HCLK 300MHz */ static void clock_config(void) { uint32_t reg; @@ -316,7 +267,7 @@ static void clock_config(void) ; /* PLL1: HSI / 4 * 75 = 1200 MHz VCO. - * Clear BYP — Boot ROM leaves it set. */ + * Clear BYP for Boot ROM leaves it set. */ reg = RCC_PLL1CFGR1; reg &= ~(RCC_PLL1CFGR1_SEL_MASK | RCC_PLL1CFGR1_DIVM_MASK | RCC_PLL1CFGR1_DIVN_MASK | RCC_PLL1CFGR1_BYP); @@ -327,7 +278,7 @@ static void clock_config(void) RCC_PLL1CFGR2 = 0; /* no fractional */ - /* PDIV1=1, PDIV2=1 → PLL output = VCO = 1200 MHz */ + /* PDIV1=1, PDIV2=1 -> PLL output = VCO = 1200 MHz */ RCC_PLL1CFGR3 = (1u << RCC_PLL1CFGR3_PDIV1_SHIFT) | (1u << RCC_PLL1CFGR3_PDIV2_SHIFT) | RCC_PLL1CFGR3_MODSSDIS | @@ -339,7 +290,7 @@ static void clock_config(void) while (!(RCC_SR & RCC_SR_PLL1RDY)) ; - /* Configure IC dividers: disable → configure → re-enable */ + /* Configure IC dividers: disable -> configure -> re-enable */ RCC_DIVENCR = RCC_DIVENR_IC1EN; RCC_IC1CFGR = RCC_ICCFGR_SEL_PLL1 | ((2u - 1u) << RCC_ICCFGR_INT_SHIFT); RCC_DIVENSR = RCC_DIVENR_IC1EN; @@ -356,13 +307,13 @@ static void clock_config(void) RCC_IC11CFGR = RCC_ICCFGR_SEL_PLL1 | ((3u - 1u) << RCC_ICCFGR_INT_SHIFT); RCC_DIVENSR = RCC_DIVENR_IC11EN; - /* AHB prescaler /2 → HCLK = 300 MHz */ + /* AHB prescaler /2 -> HCLK = 300 MHz */ reg = RCC_CFGR2; reg &= ~RCC_CFGR2_HPRE_MASK; reg |= (1u << RCC_CFGR2_HPRE_SHIFT); RCC_CFGR2 = reg; - /* Switch CPU to IC1, system bus to IC2/IC6/IC11 */ + /* Switch CPU to IC1 system bus to IC2/IC6/IC11 */ reg = RCC_CFGR1; reg &= ~(RCC_CFGR1_CPUSW_MASK | RCC_CFGR1_SYSSW_MASK); reg |= (0x3u << RCC_CFGR1_CPUSW_SHIFT) | @@ -376,9 +327,7 @@ static void clock_config(void) ; } -/* ========================================================================= - * Power — mark VDDIO supplies valid - * ========================================================================= */ +/* For power mark VDDIO supplies valid */ static void pwr_enable_io_supply(void) { /* Enable PWR peripheral clock */ @@ -391,9 +340,7 @@ static void pwr_enable_io_supply(void) DMB(); } -/* ========================================================================= - * Cache - * ========================================================================= */ +/* Cache */ static void icache_enable(void) { DSB(); ISB(); @@ -410,16 +357,7 @@ static void dcache_enable(void) DSB(); ISB(); } -/* ========================================================================= - * MPU — Configure ETH DMA buffer region as non-cacheable - * - * Cortex-M55 ARMv8.1-M MPU: - * RBAR: base address | AP[2:1] | XN[0] - * RLAR: limit address | AttrIdx[3:1] | EN[0] - * MAIR: attribute encoding per index - * - * ETH buffers (.eth_buffers) are in AXISRAM2 at 0x34100000. - * ========================================================================= */ +/* MPU for mark ETH DMA buffers (AXISRAM2) as non-cacheable */ extern uint32_t _eth_start; extern uint32_t _eth_end; @@ -437,7 +375,7 @@ static void mpu_configure_eth_nocache(void) MPU_RBAR = base | (1u << 1u) | (1u << 0u); /* AP=RW, XN=1 */ MPU_RLAR = ((limit - 1u) & ~0x1Fu) | (2u << 1u) | 1u; /* AttrIdx=2, EN=1 */ - /* MAIR0: Attr2 (bits [23:16]) = 0x44 → Normal, Non-cacheable */ + /* MAIR0: Attr2 (bits [23:16]) = 0x44 -> Normal, Non-cacheable */ MPU_MAIR0 = (0x44u << 16u); /* Enable MPU + PRIVDEFENA (default map for other regions) */ @@ -445,17 +383,11 @@ static void mpu_configure_eth_nocache(void) DSB(); ISB(); } -/* ========================================================================= - * Simple delay - * ========================================================================= */ static void delay(uint32_t count) { for (volatile uint32_t i = 0; i < count; i++) { } } -/* ========================================================================= - * LED — LD1 green on PO1 - * ========================================================================= */ static void led_init(void) { uint32_t moder; @@ -481,12 +413,7 @@ static void led_toggle(void) GPIO_ODR(LED1_PORT) ^= (1u << LED1_PIN); } -/* ========================================================================= - * UART — USART1 on PE5 (TX) / PE6 (RX), AF7 - * - * HCLK (APB2 clock after PLL) = 300 MHz - * BRR = 300000000 / 115200 = 2604 - * ========================================================================= */ +/* UART USART1 on PE5/PE6 (AF7), 115200 baud via IC9 64MHz kernel clock */ static void uart_init(void) { uint32_t moder, afr; @@ -496,7 +423,7 @@ static void uart_init(void) RCC_APB2ENSR = (1u << 4u); /* USART1EN */ delay(100); - /* PE5 + PE6 → AF mode */ + /* PE5 + PE6 -> AF mode */ moder = GPIO_MODER(GPIOE_BASE); moder &= ~((3u << (5u * 2u)) | (3u << (6u * 2u))); moder |= (2u << (5u * 2u)) | (2u << (6u * 2u)); @@ -586,7 +513,7 @@ static void uart_putip4(ip4 ip) /* wolfIP requires this symbol */ uint32_t wolfIP_getrandom(void) { - /* Simple LFSR-based PRNG — no HW RNG on initial bring-up. + /* Simple LFSR-based PRNG, no HW RNG on initial bring-up. * Replace with TRNG when available. */ static uint32_t state = 0xDEADBEEF; state ^= state << 13; @@ -595,32 +522,16 @@ uint32_t wolfIP_getrandom(void) return state; } -/* ========================================================================= - * SYSCFG — I/O compensation for VDDIO3 (Ethernet GPIOF pins) - * - * SYSCFG base = 0x56008000 (secure, APB4 + 0x8000). - * VDDIO3CCCR (offset 0x5C): compensation cell control for GPIOF. - * bit 8 = EN (enable compensation cell) - * bit 9 = CS (code selection: 0=auto, 1=manual) - * VDDIO3CCSR (offset 0x60): compensation cell status (RDY bit). - * ========================================================================= */ +/* SYSCFG I/O compensation for VDDIO3 (Ethernet GPIOF pins) */ #define SYSCFG_BASE 0x56008000UL #define SYSCFG_VDDIO3CCCR (*(volatile uint32_t *)(SYSCFG_BASE + 0x5Cu)) #define SYSCFG_VDDIO3CCSR (*(volatile uint32_t *)(SYSCFG_BASE + 0x60u)) -/* RCC clock for SYSCFG — APB4ENR2 at offset 0x278, bit 0 = SYSCFGEN */ +/* RCC clock for SYSCFG APB4ENR2 at offset 0x278, bit 0 = SYSCFGEN */ #define RCC_APB4ENR2 (*(volatile uint32_t *)(RCC_BASE + 0x278u)) #define RCC_APB4ENSR2 (*(volatile uint32_t *)(RCC_BASE + 0xA78u)) -/* ========================================================================= - * Ethernet GPIO — RMII pin configuration (NUCLEO-N657X0-Q) - * - * Pin mapping (AF11) — all on GPIOF except MDC on PG11: - * PF4 — ETH_MDIO PG11 — ETH_MDC - * PF7 — ETH_REF_CLK PF10 — ETH_CRS_DV - * PF11 — ETH_TX_EN PF12 — ETH_TXD0 PF13 — ETH_TXD1 - * PF14 — ETH_RXD0 PF15 — ETH_RXD1 - * ========================================================================= */ +/* Ethernet GPIO RMII pins (AF11) on GPIOF, MDC on PG11 */ static void gpio_eth_pin(uint32_t base, uint32_t pin) { uint32_t moder, ospeedr, afr; @@ -632,7 +543,8 @@ static void gpio_eth_pin(uint32_t base, uint32_t pin) moder |= (2u << pos2); GPIO_MODER(base) = moder; - /* High speed (0b10) — match CubeN6. Very High (0b11) causes issues on N6. */ + /* High speed (0b10) match CubeN6. Very High (0b11) + * causes issues on N6. */ ospeedr = GPIO_OSPEEDR(base); ospeedr &= ~(3u << pos2); ospeedr |= (2u << pos2); @@ -663,22 +575,15 @@ static void eth_gpio_init(void) { volatile uint32_t *ramcfg_sram2_cr = (volatile uint32_t *)0x52023080u; RCC_AHB2ENSR = (1u << 12u); /* RAMCFGEN */ - RCC_MEMENSR = (1u << 1u) | /* AXISRAM2EN */ - (1u << 4u) | /* AHBSRAM1EN */ - (1u << 5u); /* AHBSRAM2EN */ + RCC_MEMENSR = (1u << 1u); /* AXISRAM2EN */ delay(100); *ramcfg_sram2_cr &= ~(1u << 20u); /* Clear SRAMSD → power on */ DSB(); delay(10000); } - /* Open RISAF3 (AXISRAM2) to allow ETH DMA R+W access. - * Default ENDR=0xFFF covers only 4KB — ETH buffers at offset 0xF8000 are blocked! - * Extend to cover full 1MB. Leave RISAF2 (AXISRAM1) untouched (code runs there). - * RISAF3_BASE = 0x54028000 */ + /* RISAF3 (AXISRAM2): extend base region to full 1MB, all CIDs R+W, SEC=1 */ { - /* RISAF3 REG0: cover full AXISRAM2 (1MB), all CIDs R+W, Secure. - * CFGR: BREN=1 (bit 0), SEC=1 (bit 8) — must match secure bus alias. */ *(volatile uint32_t *)0x54028044u = 0x00000000u; /* STARTR = 0 */ *(volatile uint32_t *)0x54028048u = 0x000FFFFFu; /* ENDR = 1MB-1 */ *(volatile uint32_t *)0x5402804Cu = 0x000F000Fu; /* CIDCFGR: CID0-3 R+W */ @@ -686,37 +591,27 @@ static void eth_gpio_init(void) DSB(); } - /* Configure RIFSC: grant ETH1 DMA (RIMC master 6) memory access. - * Matches CubeN6 RISAF_Config exactly: - * 1. RIMC_ATTR6: CID=1, SEC, PRIV - * 2. RISC slave: ETH1 peripheral marked SEC+PRIV - * RIFSC_BASE = 0x54024000 */ + /* RIFSC: ETH1 DMA as CID=1/SEC/PRIV, ETH1 peripheral as SEC+PRIV */ { volatile uint32_t *rimc_attr6 = (volatile uint32_t *)(0x54024000u + 0xC28u); RCC_AHB3ENSR = (1u << 9u); /* RIFSCEN */ delay(100); - /* RIMC: CID=1, DSEL=0, DSEC=1 (secure), DPRIV=1 — 0x301 */ - *rimc_attr6 = 0x1u | (1u << 8u) | (1u << 9u); - /* RISC: Set ETH1 peripheral as Secure + Privileged. - * ETH1 is bit 28 of SECCFGRx[1] and PRIVCFGRx[1]. */ - *(volatile uint32_t *)(0x54024000u + 0x14u) |= (1u << 28u); /* SEC */ - *(volatile uint32_t *)(0x54024000u + 0x34u) |= (1u << 28u); /* PRIV */ + *rimc_attr6 = 0x1u | (1u << 8u) | (1u << 9u); /* CID=1, SEC, PRIV */ + *(volatile uint32_t *)(0x54024000u + 0x14u) |= (1u << 28u); /* ETH1 SEC */ + *(volatile uint32_t *)(0x54024000u + 0x34u) |= (1u << 28u); /* ETH1 PRIV */ DSB(); uart_puts(" RIMC_ATTR6 (ETH1): "); uart_puthex(*rimc_attr6); uart_puts("\n"); } - /* Compensation cells per Errata Sheet ES0620 (from CubeN6 SystemInit). - * ALL VDDIO domains get the same errata workaround value 0x287: - * CS=1 (manual code selection) + specific NMOS/PMOS compensation codes. */ + /* VDDIO3 compensation cells per Errata ES0620 */ RCC_APB4ENSR2 = (1u << 0u); /* SYSCFGEN */ delay(100); - /* VDDIO3 compensation per Errata ES0620 (CubeN6 SystemInit value) */ SYSCFG_VDDIO3CCCR = 0x00000287u; DSB(); - /* Configure RMII pins (AF11) — NUCLEO-N657X0-Q pinout */ + /* Configure RMII pins (AF11) NUCLEO-N657X0-Q pinout */ gpio_eth_pin(GPIOF_BASE, 4); /* MDIO */ gpio_eth_pin(GPIOG_BASE, 11); /* MDC */ gpio_eth_pin(GPIOF_BASE, 7); /* REF_CLK */ @@ -728,9 +623,7 @@ static void eth_gpio_init(void) gpio_eth_pin(GPIOF_BASE, 15); /* RXD1 */ } -/* ========================================================================= - * TCP echo callback — stack-agnostic, mirrors H563 implementation - * ========================================================================= */ +/* TCP echo callback */ static void echo_cb(int fd, uint16_t event, void *arg) { struct wolfIP *s = (struct wolfIP *)arg; @@ -760,9 +653,6 @@ static void echo_cb(int fd, uint16_t event, void *arg) } } -/* ========================================================================= - * main - * ========================================================================= */ int main(void) { struct wolfIP_ll_dev *ll; @@ -785,7 +675,7 @@ int main(void) *sau_rbar = 0; *sau_rlar = 0; } - *sau_ctrl = 2u; /* ALLNS=1, ENABLE=0 → all memory Non-Secure */ + *sau_ctrl = 2u; /* ALLNS=1, ENABLE=0 -> all memory Non-Secure */ DSB(); ISB(); } @@ -794,10 +684,11 @@ int main(void) led_init(); led_on(); - /* PLL + IC dividers must be up before UART — boot ROM leaves IC dividers + /* PLL + IC dividers must be up before UART boot ROM leaves IC dividers * disabled, so APB2 has no clock source and USART1 kernel clock is dead. */ clock_config(); +#ifdef DEBUG_BLINK /* Blink LED 3x fast to indicate PLL locked */ { int blink; @@ -808,16 +699,17 @@ int main(void) delay(500000); } } +#endif /* Enable IC9 divider (USART1 kernel clock source on N6). - * CubeN6 uses IC9CFGR=0x30000000 (SEL=3=HSI, INT=0=div1) → 64 MHz. + * CubeN6 uses IC9CFGR=0x30000000 (SEL=3=HSI, INT=0=div1) -> 64 MHz. * Then CCIPR13 USART1SEL=2 selects ic9_ck as USART1 kernel clock. */ { volatile uint32_t *ic9cfgr = (volatile uint32_t *)(RCC_BASE + 0xE4u); volatile uint32_t *ccipr13 = (volatile uint32_t *)(RCC_BASE + 0x174u); /* Disable IC9, configure, re-enable */ RCC_DIVENCR = (1u << 8u); /* IC9 disable */ - *ic9cfgr = 0x30000000u; /* SEL=3 (HSI), INT=0 (div 1) → 64 MHz */ + *ic9cfgr = 0x30000000u; /* SEL=3 (HSI), INT=0 (div 1) -> 64 MHz */ RCC_DIVENSR = (1u << 8u); /* IC9 enable */ DSB(); *ccipr13 = (*ccipr13 & ~0x7u) | 0x2u; /* USART1SEL = ic9_ck */ @@ -834,11 +726,12 @@ int main(void) /* Initialize wolfIP stack */ wolfIP_init_static(&IPStack); - /* ETH init sequence — matching CubeN6 HAL MspInit order: + /* ETH init sequence matching CubeN6 HAL MspInit order: * 1. Enable ETH clocks (AHB5) * 2. Set RMII mode (CCIPR2) * 3. Configure GPIO + RIMC + compensation - * CubeN6 HAL: MspInit enables clocks → HAL_ETH_Init sets CCIPR2 → SWR → config */ + * CubeN6 HAL: MspInit enables clocks -> HAL_ETH_Ini + * sets CCIPR2 -> SWR -> config */ /* Step 1: Enable Ethernet clocks */ RCC_AHB5ENSR = (1u << 22u) | (1u << 23u) | (1u << 24u); diff --git a/src/port/stm32n6/target.ld b/src/port/stm32n6/target.ld index 98a42e0d..53531ad9 100644 --- a/src/port/stm32n6/target.ld +++ b/src/port/stm32n6/target.ld @@ -5,7 +5,7 @@ * * Note: Only AXISRAM1 is guaranteed accessible after boot ROM. * Other regions (AXISRAM2-6) may need explicit RCC enable. - * ETH DMA buffers are placed at the end of AXISRAM1 for now, + * ETH DMA buffers are placed in AXISRAM2 at 0x341F8000, * with MPU marking them non-cacheable. */ @@ -13,7 +13,6 @@ MEMORY { SRAM (rwx) : ORIGIN = 0x34000000, LENGTH = 512K AXISRAM2 (rwx) : ORIGIN = 0x341F8000, LENGTH = 32K - AHBSRAM (rw) : ORIGIN = 0x38000000, LENGTH = 32K } _estack = ORIGIN(SRAM) + LENGTH(SRAM); From 79c53f4b2a6aac053063bda7fbf74654dd44963a Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 24 Mar 2026 11:31:44 -0700 Subject: [PATCH 4/5] Use the internal TRNG, address feedback - Restored stm32_eth_get_dmacsr() - Hardware TRNG - SAU comment - cppcheck suppressions --- Makefile | 2 ++ src/port/stm32/stm32_eth.c | 8 ++++++++ src/port/stm32n6/main.c | 37 +++++++++++++++++++++++++++---------- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 7bdbb111..5cd157ba 100644 --- a/Makefile +++ b/Makefile @@ -119,6 +119,8 @@ CPPCHECK_FLAGS=--enable=warning,performance,portability,missingInclude \ --suppress=comparePointers:src/port/stm32h563/syscalls.c \ --suppress=comparePointers:src/port/stm32h753/startup.c \ --suppress=comparePointers:src/port/stm32h753/syscalls.c \ + --suppress=comparePointers:src/port/stm32n6/startup.c \ + --suppress=comparePointers:src/port/stm32n6/syscalls.c \ --suppress=comparePointers:src/port/va416xx/startup.c \ --suppress=comparePointers:src/port/va416xx/syscalls.c \ --disable=style \ diff --git a/src/port/stm32/stm32_eth.c b/src/port/stm32/stm32_eth.c index 0a8620f8..97a29e62 100644 --- a/src/port/stm32/stm32_eth.c +++ b/src/port/stm32/stm32_eth.c @@ -823,6 +823,14 @@ uint32_t stm32_eth_get_rx_des3(void) return rx_ring[0].des3; } +uint32_t stm32_eth_get_dmacsr(void) +{ + uint32_t val = ETH_DMACSR; + if (val & 0x80) { + ETH_DMACSR = 0x80; /* Clear RBU (W1C) */ + } + return val; +} int stm32_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac) { diff --git a/src/port/stm32n6/main.c b/src/port/stm32n6/main.c index ab197caf..62eacdbb 100644 --- a/src/port/stm32n6/main.c +++ b/src/port/stm32n6/main.c @@ -510,16 +510,32 @@ static void uart_putip4(ip4 ip) uart_putdec(ip & 0xFF); } -/* wolfIP requires this symbol */ +/* STM32N6 hardware RNG (TRNG) */ +#define RNG_BASE_ADDR 0x54020000UL /* AHB3 offset 0x0000 (secure) */ +#define RNG_CR (*(volatile uint32_t *)(RNG_BASE_ADDR + 0x00u)) +#define RNG_SR (*(volatile uint32_t *)(RNG_BASE_ADDR + 0x04u)) +#define RNG_DR (*(volatile uint32_t *)(RNG_BASE_ADDR + 0x08u)) + +static int rng_initialized = 0; + +static void rng_init(void) +{ + RCC_AHB3ENSR = (1u << 0u); /* RNGEN clock */ + DSB(); + delay(100); + RNG_CR = (1u << 2u); /* RNGEN=1 */ + rng_initialized = 1; +} + uint32_t wolfIP_getrandom(void) { - /* Simple LFSR-based PRNG, no HW RNG on initial bring-up. - * Replace with TRNG when available. */ - static uint32_t state = 0xDEADBEEF; - state ^= state << 13; - state ^= state >> 17; - state ^= state << 5; - return state; + uint32_t timeout = 100000u; + if (!rng_initialized) + rng_init(); + while (!(RNG_SR & 1u) && --timeout) { } /* Wait for DRDY */ + if (timeout == 0) + return 0; + return RNG_DR; } /* SYSCFG I/O compensation for VDDIO3 (Ethernet GPIOF pins) */ @@ -660,8 +676,9 @@ int main(void) uint64_t tick = 0; int ret; - /* Set SAU ALLNS=1: all memory treated as Non-Secure. - * This allows Non-Secure DMA masters to write to any memory region. */ + /* SAU ALLNS=1: all memory Non-Secure (required for ETH DMA access). + * Note: disables TrustZone isolation. Production builds with TZEN=1 + * should configure SAU regions to restrict DMA to ETH buffer memory only. */ { volatile uint32_t *sau_ctrl = (volatile uint32_t *)0xE000EDD0u; volatile uint32_t *sau_rnr = (volatile uint32_t *)0xE000EDD8u; From 8ff185da64afc1f2afc95e2c4a3edd71a980d5d9 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 24 Mar 2026 14:44:53 -0700 Subject: [PATCH 5/5] Address davids comments --- src/port/stm32/stm32_eth.c | 37 ++++----- src/port/stm32n6/Makefile | 5 ++ src/port/stm32n6/ivt.c | 37 +++++---- src/port/stm32n6/main.c | 154 ++++++++++++++++++------------------- 4 files changed, 118 insertions(+), 115 deletions(-) diff --git a/src/port/stm32/stm32_eth.c b/src/port/stm32/stm32_eth.c index 97a29e62..ae49c05b 100644 --- a/src/port/stm32/stm32_eth.c +++ b/src/port/stm32/stm32_eth.c @@ -33,14 +33,8 @@ /* MDIO clock divider: CR field in ETH_MACMDIOAR. * CR=0: 20-35MHz, CR=1: 35-60MHz, CR=2: 60-100MHz, * CR=3: 100-150MHz, CR=4: 150-250MHz, CR=5: 250-300MHz - * STM32N6 at HSI: HCLK ~32MHz → CR=0 - * STM32N6 at PLL: HCLK 250-300MHz → CR=5 - * STM32H5/H7: HCLK 150-250MHz → CR=4 */ -#if defined(STM32N6) -#define MDIO_CR_VALUE 4U /* PLL: AHB5 ~200MHz → CR=4 (150-250MHz range) */ -#else + * HCLK 150-250MHz -> CR=4 */ #define MDIO_CR_VALUE 4U -#endif #define ETH_REG(offset) (*(volatile uint32_t *)(ETH_BASE + (offset))) #define ETH_TPDR ETH_REG(0x1180U) @@ -175,7 +169,7 @@ /* DMA descriptor structure. * On N6 GMAC v5.20, DSL=1 skips 1 doubleword (8 bytes) between - * 16-byte descriptors → stride = 24 bytes. HAL uses 6-field struct + * 16-byte descriptors -> stride = 24 bytes. HAL uses 6-field struct * (DESC0-3 + BackupAddr0/1). */ struct eth_desc { volatile uint32_t des0; @@ -248,7 +242,8 @@ static void eth_mdio_write(uint32_t phy, uint32_t reg, uint16_t value); #if STM32_ETH_NEEDS_MDIO_DELAY static void eth_delay(uint32_t count) { - for (volatile uint32_t i = 0; i < count; i++) { } + volatile uint32_t i; + for (i = 0; i < count; i++) { } } #endif @@ -314,7 +309,7 @@ static void eth_config_mac(const uint8_t mac[6]) * SARC=0: don't modify source address (wolfIP constructs full frame). */ maccr |= ETH_MACCR_PS; /* Configure 1µs tick counter — required for MAC internal timing. - * Value = (HCLK_Hz / 1000000) - 1. With PLL: HCLK ~200MHz → 199. */ + * Value = (HCLK_Hz / 1000000) - 1. With PLL: HCLK ~200MHz -> 199. */ ETH_MAC1USTCR = 199U; #endif ETH_MACCR = maccr; @@ -523,7 +518,7 @@ static void eth_config_dma(void) ETH_MTLTXQ1OMR = ETH_MTLTXQOMR_TSF | ETH_MTLTXQOMR_TXQEN_ENABLE | ETH_MTLTXQOMR_TQS_2048; - /* Map RX Q1 → DMA CH1 */ + /* Map RX Q1 -> DMA CH1 */ ETH_MTLRXQDMAMR = (1u << 8); /* Q1MDMACH = 1 */ #else ETH_DMACTXCR = ETH_DMACTXCR_OSF | ETH_DMACTXCR_TPBL(DMA_TPBL); @@ -551,6 +546,9 @@ static void eth_arm_rx_descriptors(void) static void eth_start(void) { +#if defined(STM32N6) + uint32_t j; +#endif ETH_MACCR |= ETH_MACCR_TE | ETH_MACCR_RE; ETH_MTLTXQOMR |= ETH_MTLTXQOMR_FTQ; ETH_DMACTXCR |= ETH_DMACTXCR_ST; @@ -586,17 +584,14 @@ static void eth_start(void) * This tells the DMA about available descriptors via tail pointer. */ eth_arm_rx_descriptors(); #if defined(STM32N6) - { - uint32_t j; - for (j = 0; j < RX_DESC_COUNT; j++) { - rx_ring1[j].des0 = ETH_DMA_ADDR(rx_buffers1[j]); - rx_ring1[j].des1 = 0; rx_ring1[j].des2 = 0; - __asm volatile ("dsb sy" ::: "memory"); - rx_ring1[j].des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; - } - __asm volatile ("dmb sy" ::: "memory"); - ETH_DMAC1RXDTPR = ETH_DMA_ADDR(&rx_ring1[RX_DESC_COUNT - 1U]); + for (j = 0; j < RX_DESC_COUNT; j++) { + rx_ring1[j].des0 = ETH_DMA_ADDR(rx_buffers1[j]); + rx_ring1[j].des1 = 0; rx_ring1[j].des2 = 0; + __asm volatile ("dsb sy" ::: "memory"); + rx_ring1[j].des3 = ETH_RDES3_OWN | ETH_RDES3_IOC | ETH_RDES3_BUF1V; } + __asm volatile ("dmb sy" ::: "memory"); + ETH_DMAC1RXDTPR = ETH_DMA_ADDR(&rx_ring1[RX_DESC_COUNT - 1U]); #endif } diff --git a/src/port/stm32n6/Makefile b/src/port/stm32n6/Makefile index a5973fec..ed0f0ef9 100644 --- a/src/port/stm32n6/Makefile +++ b/src/port/stm32n6/Makefile @@ -11,6 +11,11 @@ CFLAGS += -DSTM32N6 -DTZEN_ENABLED=0 EXTRA_CFLAGS ?= CFLAGS += $(EXTRA_CFLAGS) +# Optional: DEBUG_FAULT=1 enables verbose HardFault handler with register dump +ifdef DEBUG_FAULT +CFLAGS += -DDEBUG_FAULT +endif + LDSCRIPT := target.ld LDFLAGS := -nostdlib -T $(LDSCRIPT) -Wl,-gc-sections diff --git a/src/port/stm32n6/ivt.c b/src/port/stm32n6/ivt.c index cb70f113..a8d0b793 100644 --- a/src/port/stm32n6/ivt.c +++ b/src/port/stm32n6/ivt.c @@ -41,22 +41,29 @@ void DebugMon_Handler(void) __attribute__((weak, alias("default_handler"))); void PendSV_Handler(void) __attribute__((weak, alias("default_handler"))); void SysTick_Handler(void) __attribute__((weak, alias("default_handler"))); +/* Repeat macros for filling IRQ entries portably */ +#define DH (uint32_t)&default_handler +#define DH5 DH, DH, DH, DH, DH +#define DH10 DH5, DH5 +#define DH50 DH10, DH10, DH10, DH10, DH10 +#define DH200 DH50, DH50, DH50, DH50 + /* Cortex-M55 vector table: 16 system + 200 IRQs (ETH1_IRQn = 179) */ __attribute__((section(".isr_vector"))) const uint32_t vector_table[16 + 200] = { - [0] = (uint32_t)&_estack, - [1] = (uint32_t)&Reset_Handler, - [2] = (uint32_t)&NMI_Handler, - [3] = (uint32_t)&HardFault_Handler, - [4] = (uint32_t)&MemManage_Handler, - [5] = (uint32_t)&BusFault_Handler, - [6] = (uint32_t)&UsageFault_Handler, - [7] = (uint32_t)&SecureFault_Handler, - [8] = 0, [9] = 0, [10] = 0, - [11] = (uint32_t)&SVC_Handler, - [12] = (uint32_t)&DebugMon_Handler, - [13] = 0, - [14] = (uint32_t)&PendSV_Handler, - [15] = (uint32_t)&SysTick_Handler, - [16 ... 215] = (uint32_t)&default_handler + (uint32_t)&_estack, + (uint32_t)&Reset_Handler, + (uint32_t)&NMI_Handler, + (uint32_t)&HardFault_Handler, + (uint32_t)&MemManage_Handler, + (uint32_t)&BusFault_Handler, + (uint32_t)&UsageFault_Handler, + (uint32_t)&SecureFault_Handler, + 0, 0, 0, + (uint32_t)&SVC_Handler, + (uint32_t)&DebugMon_Handler, + 0, + (uint32_t)&PendSV_Handler, + (uint32_t)&SysTick_Handler, + DH200 }; diff --git a/src/port/stm32n6/main.c b/src/port/stm32n6/main.c index 62eacdbb..54ac0511 100644 --- a/src/port/stm32n6/main.c +++ b/src/port/stm32n6/main.c @@ -184,6 +184,7 @@ static int listen_fd = -1; static int client_fd = -1; static uint8_t rx_buf[RX_BUF_SIZE]; +#ifdef DEBUG_FAULT /* HardFault Handler use only prints if uart_ready (avoids double-fault on * unclocked peripherals which would cause CPU LOCKUP). */ static volatile int uart_ready = 0; @@ -208,8 +209,9 @@ static void fault_uart_puts(const char *s) static void fault_uart_puthex(uint32_t val) { const char hex[] = "0123456789ABCDEF"; + int i; fault_uart_puts("0x"); - for (int i = 28; i >= 0; i -= 4) + for (i = 28; i >= 0; i -= 4) fault_uart_putc(hex[(val >> i) & 0xF]); } @@ -248,6 +250,7 @@ void HardFault_Handler(void) "b hard_fault_handler_c \n" ); } +#endif /* DEBUG_FAULT */ /* Clock: HSI 64MHz -> PLL1 (M=4,N=75) -> 1200MHz VCO * IC1/2=600MHz CPU, IC2/3=400MHz AXI, IC6/4=300MHz, IC11/3=400MHz @@ -385,7 +388,8 @@ static void mpu_configure_eth_nocache(void) static void delay(uint32_t count) { - for (volatile uint32_t i = 0; i < count; i++) { } + volatile uint32_t i; + for (i = 0; i < count; i++) { } } static void led_init(void) @@ -469,8 +473,9 @@ static void uart_puts(const char *s) static void uart_puthex(uint32_t val) { const char hex[] = "0123456789ABCDEF"; + int i; uart_puts("0x"); - for (int i = 28; i >= 0; i -= 4) { + for (i = 28; i >= 0; i -= 4) { uart_putc(hex[(val >> i) & 0xF]); } } @@ -582,44 +587,39 @@ static void gpio_eth_pin(uint32_t base, uint32_t pin) static void eth_gpio_init(void) { + volatile uint32_t *ramcfg_sram2_cr = (volatile uint32_t *)0x52023080u; + volatile uint32_t *rimc_attr6 = (volatile uint32_t *)(0x54024000u + 0xC28u); + /* Enable GPIO clocks: F and G */ RCC_AHB4ENSR = (1u << 5u) | (1u << 6u); /* GPIOFEN + GPIOGEN */ delay(1000); /* Power on AXISRAM2 for ETH DMA buffers. * AXISRAM2-6 are powered down by default after boot ROM. */ - { - volatile uint32_t *ramcfg_sram2_cr = (volatile uint32_t *)0x52023080u; - RCC_AHB2ENSR = (1u << 12u); /* RAMCFGEN */ - RCC_MEMENSR = (1u << 1u); /* AXISRAM2EN */ - delay(100); - *ramcfg_sram2_cr &= ~(1u << 20u); /* Clear SRAMSD → power on */ - DSB(); - delay(10000); - } + RCC_AHB2ENSR = (1u << 12u); /* RAMCFGEN */ + RCC_MEMENSR = (1u << 1u); /* AXISRAM2EN */ + delay(100); + *ramcfg_sram2_cr &= ~(1u << 20u); /* Clear SRAMSD -> power on */ + DSB(); + delay(10000); /* RISAF3 (AXISRAM2): extend base region to full 1MB, all CIDs R+W, SEC=1 */ - { - *(volatile uint32_t *)0x54028044u = 0x00000000u; /* STARTR = 0 */ - *(volatile uint32_t *)0x54028048u = 0x000FFFFFu; /* ENDR = 1MB-1 */ - *(volatile uint32_t *)0x5402804Cu = 0x000F000Fu; /* CIDCFGR: CID0-3 R+W */ - *(volatile uint32_t *)0x54028040u = 0x00000101u; /* CFGR: BREN=1, SEC=1 */ - DSB(); - } + *(volatile uint32_t *)0x54028044u = 0x00000000u; /* STARTR = 0 */ + *(volatile uint32_t *)0x54028048u = 0x000FFFFFu; /* ENDR = 1MB-1 */ + *(volatile uint32_t *)0x5402804Cu = 0x000F000Fu; /* CIDCFGR: CID0-3 R+W */ + *(volatile uint32_t *)0x54028040u = 0x00000101u; /* CFGR: BREN=1, SEC=1 */ + DSB(); /* RIFSC: ETH1 DMA as CID=1/SEC/PRIV, ETH1 peripheral as SEC+PRIV */ - { - volatile uint32_t *rimc_attr6 = (volatile uint32_t *)(0x54024000u + 0xC28u); - RCC_AHB3ENSR = (1u << 9u); /* RIFSCEN */ - delay(100); - *rimc_attr6 = 0x1u | (1u << 8u) | (1u << 9u); /* CID=1, SEC, PRIV */ - *(volatile uint32_t *)(0x54024000u + 0x14u) |= (1u << 28u); /* ETH1 SEC */ - *(volatile uint32_t *)(0x54024000u + 0x34u) |= (1u << 28u); /* ETH1 PRIV */ - DSB(); - uart_puts(" RIMC_ATTR6 (ETH1): "); - uart_puthex(*rimc_attr6); - uart_puts("\n"); - } + RCC_AHB3ENSR = (1u << 9u); /* RIFSCEN */ + delay(100); + *rimc_attr6 = 0x1u | (1u << 8u) | (1u << 9u); /* CID=1, SEC, PRIV */ + *(volatile uint32_t *)(0x54024000u + 0x14u) |= (1u << 28u); /* ETH1 SEC */ + *(volatile uint32_t *)(0x54024000u + 0x34u) |= (1u << 28u); /* ETH1 PRIV */ + DSB(); + uart_puts(" RIMC_ATTR6 (ETH1): "); + uart_puthex(*rimc_attr6); + uart_puts("\n"); /* VDDIO3 compensation cells per Errata ES0620 */ RCC_APB4ENSR2 = (1u << 0u); /* SYSCFGEN */ @@ -675,26 +675,30 @@ int main(void) struct wolfIP_sockaddr_in addr; uint64_t tick = 0; int ret; + volatile uint32_t *sau_ctrl = (volatile uint32_t *)0xE000EDD0u; + volatile uint32_t *sau_rnr = (volatile uint32_t *)0xE000EDD8u; + volatile uint32_t *sau_rbar = (volatile uint32_t *)0xE000EDDCu; + volatile uint32_t *sau_rlar = (volatile uint32_t *)0xE000EDE0u; + volatile uint32_t *ic9cfgr = (volatile uint32_t *)(RCC_BASE + 0xE4u); + volatile uint32_t *ccipr13 = (volatile uint32_t *)(RCC_BASE + 0x174u); + ip4 ip, nm, gw; + int i; +#ifdef DEBUG_BLINK + int blink; +#endif /* SAU ALLNS=1: all memory Non-Secure (required for ETH DMA access). * Note: disables TrustZone isolation. Production builds with TZEN=1 * should configure SAU regions to restrict DMA to ETH buffer memory only. */ - { - volatile uint32_t *sau_ctrl = (volatile uint32_t *)0xE000EDD0u; - volatile uint32_t *sau_rnr = (volatile uint32_t *)0xE000EDD8u; - volatile uint32_t *sau_rbar = (volatile uint32_t *)0xE000EDDCu; - volatile uint32_t *sau_rlar = (volatile uint32_t *)0xE000EDE0u; - int i; - *sau_ctrl = 0; - DSB(); - for (i = 0; i < 8; i++) { - *sau_rnr = (uint32_t)i; - *sau_rbar = 0; - *sau_rlar = 0; - } - *sau_ctrl = 2u; /* ALLNS=1, ENABLE=0 -> all memory Non-Secure */ - DSB(); ISB(); + *sau_ctrl = 0; + DSB(); + for (i = 0; i < 8; i++) { + *sau_rnr = (uint32_t)i; + *sau_rbar = 0; + *sau_rlar = 0; } + *sau_ctrl = 2u; /* ALLNS=1, ENABLE=0 -> all memory Non-Secure */ + DSB(); ISB(); pwr_enable_io_supply(); @@ -707,34 +711,28 @@ int main(void) #ifdef DEBUG_BLINK /* Blink LED 3x fast to indicate PLL locked */ - { - int blink; - for (blink = 0; blink < 3; blink++) { - led_toggle(); - delay(500000); - led_toggle(); - delay(500000); - } + for (blink = 0; blink < 3; blink++) { + led_toggle(); + delay(500000); + led_toggle(); + delay(500000); } #endif /* Enable IC9 divider (USART1 kernel clock source on N6). * CubeN6 uses IC9CFGR=0x30000000 (SEL=3=HSI, INT=0=div1) -> 64 MHz. * Then CCIPR13 USART1SEL=2 selects ic9_ck as USART1 kernel clock. */ - { - volatile uint32_t *ic9cfgr = (volatile uint32_t *)(RCC_BASE + 0xE4u); - volatile uint32_t *ccipr13 = (volatile uint32_t *)(RCC_BASE + 0x174u); - /* Disable IC9, configure, re-enable */ - RCC_DIVENCR = (1u << 8u); /* IC9 disable */ - *ic9cfgr = 0x30000000u; /* SEL=3 (HSI), INT=0 (div 1) -> 64 MHz */ - RCC_DIVENSR = (1u << 8u); /* IC9 enable */ - DSB(); - *ccipr13 = (*ccipr13 & ~0x7u) | 0x2u; /* USART1SEL = ic9_ck */ - DSB(); - } + RCC_DIVENCR = (1u << 8u); /* IC9 disable */ + *ic9cfgr = 0x30000000u; /* SEL=3 (HSI), INT=0 (div 1) -> 64 MHz */ + RCC_DIVENSR = (1u << 8u); /* IC9 enable */ + DSB(); + *ccipr13 = (*ccipr13 & ~0x7u) | 0x2u; /* USART1SEL = ic9_ck */ + DSB(); uart_init(); +#ifdef DEBUG_FAULT uart_ready = 1; +#endif icache_enable(); mpu_configure_eth_nocache(); @@ -803,20 +801,18 @@ int main(void) } /* Static IP configuration */ - { - ip4 ip = atoip4(WOLFIP_IP); - ip4 nm = atoip4(WOLFIP_NETMASK); - ip4 gw = atoip4(WOLFIP_GW); - uart_puts("Setting IP configuration:\n"); - uart_puts(" IP: "); - uart_putip4(ip); - uart_puts("\n Mask: "); - uart_putip4(nm); - uart_puts("\n GW: "); - uart_putip4(gw); - uart_puts("\n"); - wolfIP_ipconfig_set(IPStack, ip, nm, gw); - } + ip = atoip4(WOLFIP_IP); + nm = atoip4(WOLFIP_NETMASK); + gw = atoip4(WOLFIP_GW); + uart_puts("Setting IP configuration:\n"); + uart_puts(" IP: "); + uart_putip4(ip); + uart_puts("\n Mask: "); + uart_putip4(nm); + uart_puts("\n GW: "); + uart_putip4(gw); + uart_puts("\n"); + wolfIP_ipconfig_set(IPStack, ip, nm, gw); /* TCP echo server on port 7 */ uart_puts("Creating TCP socket on port 7...\n");