From 7335f906d44c2a9ec31d1f3b1d38d4f0740f2718 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 2 Apr 2026 11:49:09 -0700 Subject: [PATCH 1/3] Add NXP LPC54S018M-EVK bare-metal port with shared LPC ENET driver First NXP platform support for wolfIP. Adds a bare-metal port for the LPCXpresso54S018M development board (Cortex-M4F, 96 MHz) with DHCP, ICMP ping, and TCP echo server over 100 Mbps Ethernet (LAN8720A PHY). The Ethernet driver is split into a shared lpc_enet/ component (Synopsys DesignWare Ethernet QoS with enhanced descriptors) and board-specific code in lpc54s018/, matching the existing stm32/stm32h563 structure for easy reuse across other NXP LPC boards. Tested: DHCP lease acquisition, ICMP ping (<1 ms), TCP echo on port 7. --- src/port/lpc54s018/Makefile | 73 +++++ src/port/lpc54s018/README.md | 175 ++++++++++ src/port/lpc54s018/config.h | 68 ++++ src/port/lpc54s018/fix_checksum.py | 50 +++ src/port/lpc54s018/ivt.c | 66 ++++ src/port/lpc54s018/lpc54s018_eth.h | 38 +++ src/port/lpc54s018/main.c | 337 +++++++++++++++++++ src/port/lpc54s018/startup.c | 50 +++ src/port/lpc54s018/syscalls.c | 158 +++++++++ src/port/lpc54s018/target.ld | 90 ++++++ src/port/lpc54s018/target_ram.ld | 69 ++++ src/port/lpc_enet/lpc_enet.c | 502 +++++++++++++++++++++++++++++ src/port/lpc_enet/lpc_enet.h | 51 +++ 13 files changed, 1727 insertions(+) create mode 100644 src/port/lpc54s018/Makefile create mode 100644 src/port/lpc54s018/README.md create mode 100644 src/port/lpc54s018/config.h create mode 100644 src/port/lpc54s018/fix_checksum.py create mode 100644 src/port/lpc54s018/ivt.c create mode 100644 src/port/lpc54s018/lpc54s018_eth.h create mode 100644 src/port/lpc54s018/main.c create mode 100644 src/port/lpc54s018/startup.c create mode 100644 src/port/lpc54s018/syscalls.c create mode 100644 src/port/lpc54s018/target.ld create mode 100644 src/port/lpc54s018/target_ram.ld create mode 100644 src/port/lpc_enet/lpc_enet.c create mode 100644 src/port/lpc_enet/lpc_enet.h diff --git a/src/port/lpc54s018/Makefile b/src/port/lpc54s018/Makefile new file mode 100644 index 00000000..76e162b1 --- /dev/null +++ b/src/port/lpc54s018/Makefile @@ -0,0 +1,73 @@ +CC := arm-none-eabi-gcc +OBJCOPY := arm-none-eabi-objcopy +SIZE := arm-none-eabi-size + +ROOT := ../../.. + +CFLAGS := -mcpu=cortex-m4 -mthumb -mfloat-abi=soft +CFLAGS += -Os -ffreestanding -fdata-sections -ffunction-sections +CFLAGS += -g -ggdb -Wall -Wextra -Werror +CFLAGS += -I. -I$(ROOT) -I$(ROOT)/src -I$(ROOT)/src/port/lpc_enet + +CFLAGS_EXT := $(filter-out -Werror,$(CFLAGS)) +CFLAGS_EXT += -Wno-unused-variable -Wno-unused-function -Wno-unused-parameter +CFLAGS_EXT += -Wno-sign-compare -Wno-missing-field-initializers + +LDSCRIPT ?= target_ram.ld +LDFLAGS := -nostdlib -T $(LDSCRIPT) -Wl,-gc-sections + +APP_SRCS := startup.c ivt.c syscalls.c main.c +APP_OBJS := $(patsubst %.c,%.o,$(APP_SRCS)) + +# Shared LPC ENET driver (src/port/lpc_enet/) +LPC_ENET_SRC := $(ROOT)/src/port/lpc_enet/lpc_enet.c +LPC_ENET_OBJ := lpc_enet.o + +# wolfIP core +WOLFIP_SRC := $(ROOT)/src/wolfip.c +WOLFIP_OBJ := wolfip.o + +ALL_OBJS := $(APP_OBJS) $(LPC_ENET_OBJ) $(WOLFIP_OBJ) + +all: app.bin + @echo "Built LPC54S018M-EVK wolfIP port" + @$(SIZE) app.elf + +app.elf: $(ALL_OBJS) $(LDSCRIPT) + $(CC) $(CFLAGS) $(ALL_OBJS) $(LDFLAGS) \ + -Wl,--start-group -lc -lm -lgcc -lnosys -Wl,--end-group -o $@ + +app.bin: app.elf + $(OBJCOPY) -O binary $< $@ + python3 fix_checksum.py $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# Shared LPC ENET driver (include board header for LPC_ENET_* defines) +$(LPC_ENET_OBJ): $(LPC_ENET_SRC) + $(CC) $(CFLAGS) -include lpc54s018_eth.h -c $< -o $@ + +$(WOLFIP_OBJ): $(WOLFIP_SRC) + $(CC) $(CFLAGS_EXT) -c $< -o $@ + +clean: + rm -f *.o app.elf app.bin + +size: app.elf + @$(SIZE) app.elf + +.PHONY: all clean size help + +help: + @echo "LPC54S018M-EVK wolfIP Build System" + @echo "" + @echo " make Build app.bin" + @echo " make clean Remove build artifacts" + @echo " make size Show memory usage" + @echo "" + @echo "Flashing (on-board Link2 CMSIS-DAP):" + @echo " pyocd flash -t lpc54608 app.elf" + @echo "" + @echo "Testing:" + @echo " ping 192.168.0.200" diff --git a/src/port/lpc54s018/README.md b/src/port/lpc54s018/README.md new file mode 100644 index 00000000..cb8a2079 --- /dev/null +++ b/src/port/lpc54s018/README.md @@ -0,0 +1,175 @@ +# wolfIP LPC54S018M-EVK Port + +Bare-metal port of wolfIP for the NXP LPCXpresso54S018M development board, featuring an Ethernet driver and TCP/IP echo server example. + +## Quick Start + +1. **Build:** + ```bash + cd src/port/lpc54s018 + make + ``` + +2. **Flash to board:** + ```bash + pyocd flash -t lpc54608 app.elf + ``` + +3. **Monitor UART output** (115200 baud on /dev/ttyACM0): + ```bash + screen /dev/ttyACM0 115200 + ``` + Get the device IP address from the DHCP output. + +4. **Test** (replace `` with IP from step 3): + ```bash + # TCP Echo + echo "Hello" | nc 7 + + # Ping + ping + ``` + +## Hardware Requirements + +- LPCXpresso54S018M development board (NXP OM40003) +- Ethernet connection (RMII, RJ45 at J4) +- USB cable for debug (J8, on-board Link2 CMSIS-DAP) + +## Software Requirements + +- ARM GCC toolchain (`arm-none-eabi-gcc`) +- pyocd (`pip install pyocd`) +- Serial terminal (screen, minicom, picocom) + +### Installing Dependencies (Ubuntu/Debian) + +```bash +sudo apt install gcc-arm-none-eabi +pip install pyocd +``` + +## Building + +```bash +cd src/port/lpc54s018 +make +``` + +This produces `app.elf` and `app.bin`. + +### Checking Memory Usage + +```bash +make size +``` + +``` + text data bss dec hex filename + 52000 1732 53076 106808 1a138 app.elf +``` + +## Flashing + +```bash +pyocd flash -t lpc54608 app.elf +``` + +Note: use `-t lpc54608` as the target type since `lpc54s018` is not in pyocd's built-in list. + +## Serial Console + +Connect to the USB serial port at 115200 baud: + +```bash +screen /dev/ttyACM0 115200 +``` + +## Example Output + +``` +=== wolfIP LPC54S018M-EVK === +PHY addr=0 link=UP +Starting DHCP... +Ready! ping / echo test | nc 7 +DHCP bound: 192.168.0.138 +``` + +## Network Configuration + +### DHCP (Default) + +DHCP is enabled by default. The board obtains its IP automatically and prints it to UART. Falls back to static IP after 30 seconds if no DHCP server responds. + +### Static IP + +Set `WOLFIP_ENABLE_DHCP` to `0` in `config.h`: + +```c +#define WOLFIP_ENABLE_DHCP 0 +#define WOLFIP_IP "192.168.1.10" +#define WOLFIP_NETMASK "255.255.255.0" +#define WOLFIP_GW "192.168.1.1" +``` + +## Testing TCP Echo Server + +```bash +# Test ICMP +ping + +# Test TCP echo (port 7) +echo "Hello wolfIP!" | nc 7 + +# Interactive +nc 7 +``` + +## Jumper Settings + +| Jumper | Position | Function | +|--------|----------|----------| +| JP11 | 1-2 (default) | Ethernet TXD/RXD | +| JP12 | 1-2 (default) | Ethernet TXD/RXD | +| JP14 | 1-2 (EN) | ENET MDC | +| JP15 | 1-2 (EN) | ENET MDIO | +| JP5 | Open (default) | Link2 normal boot | + +## Files + +| File | Description | +|------|-------------| +| `main.c` | Application entry, wolfIP init, echo server | +| `../lpc_enet/lpc_enet.c` | Ethernet MAC/DMA driver (shared) | +| `../lpc_enet/lpc_enet.h` | Ethernet driver header (shared) | +| `lpc54s018_eth.h` | Board-specific ENET parameters | +| `startup.c` | Startup code and data initialization | +| `ivt.c` | Interrupt vector table + SPIFI config | +| `syscalls.c` | Newlib syscall stubs | +| `target_ram.ld` | Linker script (RAM execution) | +| `target.ld` | Linker script (SPIFI flash boot) | +| `config.h` | wolfIP stack configuration | +| `fix_checksum.py` | LPC boot ROM vector checksum tool | +| `Makefile` | Build system | + +## Troubleshooting + +### No Serial Output +- Check J8 (USB Debug-Link) is connected, not J1 +- Check JP5 is open (not shunted) +- Verify baud rate is 115200 + +### Ethernet Not Responding +- Verify cable is in J4, check RJ45 link LEDs are lit +- Check JP14 and JP15 are both in EN position +- If using static IP, ensure host is on the same subnet + +### pyocd Cannot Find Target +- Use `-t lpc54608` as target type +- Add udev rule: `SUBSYSTEM=="usb", ATTR{idVendor}=="1fc9", MODE="0666"` + +## License + +This code is part of wolfIP and is licensed under GPLv3. See the LICENSE file in the repository root for details. + +Copyright (C) 2026 wolfSSL Inc. diff --git a/src/port/lpc54s018/config.h b/src/port/lpc54s018/config.h new file mode 100644 index 00000000..3ed725a1 --- /dev/null +++ b/src/port/lpc54s018/config.h @@ -0,0 +1,68 @@ +/* config.h + * + * wolfIP configuration for LPC54S018M-EVK + * + * 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 2 +#define MAX_UDPSOCKETS 1 +#define MAX_ICMPSOCKETS 1 +#define RXBUF_SIZE LINK_MTU +#define TXBUF_SIZE LINK_MTU + +#define MAX_NEIGHBORS 4 +#define WOLFIP_ARP_PENDING_MAX 2 + +#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 1 +#endif + +/* Static IP fallback (used when DHCP is disabled or times out) */ +#define WOLFIP_IP "192.168.1.10" +#define WOLFIP_NETMASK "255.255.255.0" +#define WOLFIP_GW "192.168.1.1" +#define WOLFIP_STATIC_DNS_IP "8.8.8.8" + +#if WOLFIP_ENABLE_DHCP +#define DHCP +#endif + +#endif /* WOLF_CONFIG_H */ diff --git a/src/port/lpc54s018/fix_checksum.py b/src/port/lpc54s018/fix_checksum.py new file mode 100644 index 00000000..ef82ac29 --- /dev/null +++ b/src/port/lpc54s018/fix_checksum.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# fix_checksum.py - compute LPC vector table checksum +# +# The LPC54S018 boot ROM requires that vector table entries 0-7 sum to zero. +# The vector table starts at offset 0x200 in the binary (after the 512-byte +# SPIFI configuration block). +# +# This script patches entry[7] so that sum(entries[0:8]) == 0 (mod 2^32). + +import struct +import sys + +SPIFI_CONFIG_SIZE = 0x200 # 512-byte SPIFI config block before vector table + +def main(): + if len(sys.argv) != 2: + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + + fname = sys.argv[1] + with open(fname, 'r+b') as f: + f.seek(SPIFI_CONFIG_SIZE) + data = f.read(32) + if len(data) < 32: + print(f"Error: file too small (need at least {SPIFI_CONFIG_SIZE + 32} bytes)") + sys.exit(1) + + vecs = list(struct.unpack('<8I', data)) + # Compute checksum: entry[7] = -(sum of entries 0-6) mod 2^32 + partial_sum = sum(vecs[:7]) & 0xFFFFFFFF + cksum = (0x100000000 - partial_sum) & 0xFFFFFFFF + vecs[7] = cksum + + # Write back + f.seek(SPIFI_CONFIG_SIZE + 7 * 4) + f.write(struct.pack(' + +/* SPIFI config block (512 bytes at flash offset 0). + * All-zeros is safe for debugger-loaded images. For standalone boot, + * populate with Winbond W25Q128JV flash config from MCUXpresso SDK. */ +__attribute__((section(".spifi_config"), used)) +const uint32_t spifi_config[4] = { 0, 0, 0, 0 }; + +/* Vector table (at flash offset 0x200). + * Entry[7] is patched by fix_checksum.py (entries 0-7 must sum to 0). + * LPC54S018 has 73 external interrupts (IRQ 0-72). */ +extern void Reset_Handler(void); +extern void SysTick_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 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"))); + +__attribute__((section(".isr_vector"), used)) +const uint32_t vector_table[16 + 73] = { + [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] = 0, /* Checksum (patched by fix_checksum.py) */ + [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 ... 88] = (uint32_t)&default_handler +}; diff --git a/src/port/lpc54s018/lpc54s018_eth.h b/src/port/lpc54s018/lpc54s018_eth.h new file mode 100644 index 00000000..5a589a1a --- /dev/null +++ b/src/port/lpc54s018/lpc54s018_eth.h @@ -0,0 +1,38 @@ +/* lpc54s018_eth.h + * + * LPC54S018 board-specific Ethernet configuration. + * Wraps the shared lpc_enet driver with board parameters. + * + * 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 WOLFIP_LPC54S018_ETH_H +#define WOLFIP_LPC54S018_ETH_H + +/* Board-specific ENET parameters for LPC54S018M-EVK */ +#define LPC_ENET_BASE 0x40092000UL /* ENET peripheral base */ +#define LPC_ENET_MDIO_CR 0U /* CR=0: 60-100MHz (AHB=96MHz) */ +#define LPC_ENET_1US_TIC 95U /* (96MHz / 1MHz) - 1 */ + +#include "lpc_enet.h" + +/* Convenience aliases matching board-specific naming */ +#define lpc54s018_eth_init lpc_enet_init +#define lpc54s018_eth_get_stats lpc_enet_get_stats + +#endif /* WOLFIP_LPC54S018_ETH_H */ diff --git a/src/port/lpc54s018/main.c b/src/port/lpc54s018/main.c new file mode 100644 index 00000000..6723fd6d --- /dev/null +++ b/src/port/lpc54s018/main.c @@ -0,0 +1,337 @@ +/* main.c + * + * LPC54S018M-EVK wolfIP Bare Metal Port + * + * 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 "config.h" +#include "wolfip.h" +#include "lpc54s018_eth.h" + +/* SYSCON (0x40000000) */ +#define SYSCON_BASE 0x40000000UL +#define SYSCON_REG(off) (*(volatile uint32_t *)(SYSCON_BASE + (off))) +#define AHBCLKCTRL2 SYSCON_REG(0x208U) +#define PRESETCTRL2 SYSCON_REG(0x108U) +#define AHBCLKCTRLSET0 SYSCON_REG(0x220U) +#define AHBCLKCTRLSET1 SYSCON_REG(0x224U) +#define AHBCLKCTRLSET2 SYSCON_REG(0x228U) +#define PRESETCTRLSET1 SYSCON_REG(0x124U) +#define PRESETCTRLCLR1 SYSCON_REG(0x144U) +#define ETHPHYSEL SYSCON_REG(0x450U) +#define FCLKSEL0 SYSCON_REG(0x508U) +#define FROCTRL SYSCON_REG(0x550U) +#define MAINCLKSELA SYSCON_REG(0x280U) +#define MAINCLKSELB SYSCON_REG(0x284U) +#define AHBCLKDIV SYSCON_REG(0x380U) +#define FLASHCFG SYSCON_REG(0x400U) + +/* IOCON (0x40001000) */ +#define IOCON_BASE 0x40001000UL +#define IOCON_PIN(port, pin) (*(volatile uint32_t *)(IOCON_BASE + ((port) * 32U + (pin)) * 4U)) +#define IOCON_DIGITAL_EN (1U << 8) +#define IOCON_INPFILT_OFF (1U << 9) +#define IOCON_MODE_PULLUP (2U << 4) + +/* GPIO (0x4008C000) */ +#define GPIO_BASE 0x4008C000UL +#define GPIO_DIR(port) (*(volatile uint32_t *)(GPIO_BASE + 0x2000U + (port) * 4U)) +#define GPIO_SET(port) (*(volatile uint32_t *)(GPIO_BASE + 0x2200U + (port) * 4U)) +#define GPIO_CLR(port) (*(volatile uint32_t *)(GPIO_BASE + 0x2280U + (port) * 4U)) +#define GPIO_NOT(port) (*(volatile uint32_t *)(GPIO_BASE + 0x2300U + (port) * 4U)) + +/* Flexcomm0 / USART0 (0x40086000) */ +#define FC0_BASE 0x40086000UL +#define FC0_REG(off) (*(volatile uint32_t *)(FC0_BASE + (off))) +#define FC0_PSELID FC0_REG(0xFF8U) +#define USART0_CFG FC0_REG(0x000U) +#define USART0_BRG FC0_REG(0x020U) +#define USART0_FIFOCFG FC0_REG(0xE00U) + +/* SysTick */ +#define SYST_CSR (*(volatile uint32_t *)0xE000E010UL) +#define SYST_RVR (*(volatile uint32_t *)0xE000E014UL) +#define SYST_CVR (*(volatile uint32_t *)0xE000E018UL) + +/* System clock: 96 MHz (FRO HF) */ +#define SYSTEM_CLOCK_HZ 96000000U + +#define DHCP_TIMEOUT_MS 30000U +#define DHCP_REINIT_MS 10000U + +static volatile uint64_t tick_ms; + +void SysTick_Handler(void) +{ + tick_ms++; +} + +uint32_t wolfIP_getrandom(void) +{ + static uint32_t lfsr; + static int seeded = 0; + if (!seeded) { + lfsr = (uint32_t)tick_ms; + if (lfsr == 0U) lfsr = 0x1A2B3C4DU; + seeded = 1; + } + lfsr ^= lfsr << 13; + lfsr ^= lfsr >> 17; + lfsr ^= lfsr << 5; + return lfsr; +} + +static void delay_ms(uint32_t ms) +{ + uint64_t target = tick_ms + ms; + while (tick_ms < target) { } +} + +static void clock_init(void) +{ + AHBCLKCTRLSET0 = (1U << 3) | (1U << 4) | (1U << 5) | (1U << 6); + AHBCLKCTRLSET0 = (1U << 13) | (1U << 14) | (1U << 15) | + (1U << 16) | (1U << 17); + AHBCLKCTRLSET2 = (1U << 2) | (1U << 3); + + FROCTRL |= (1U << 30); /* FRO HF 96 MHz */ + FLASHCFG = (FLASHCFG & ~0xFU) | 0x4U; /* 4 wait states for 96 MHz */ + MAINCLKSELA = 3; /* FRO HF */ + MAINCLKSELB = 0; /* MAINCLKSELA output */ + AHBCLKDIV = 0; +} + +static void usart0_init(void) +{ + AHBCLKCTRLSET1 = (1U << 11); + PRESETCTRLSET1 = (1U << 11); + for (volatile uint32_t i = 0; i < 100; i++) { } + PRESETCTRLCLR1 = (1U << 11); + + FCLKSEL0 = 4U; /* FRO_HF 96 MHz */ + IOCON_PIN(0, 30) = 1U | IOCON_DIGITAL_EN; + IOCON_PIN(0, 29) = 1U | IOCON_DIGITAL_EN; + FC0_PSELID = 1U; + USART0_CFG = 0; + USART0_BRG = 51U; /* 96M / (16*52) = 115384 baud */ + USART0_FIFOCFG = (1U << 0) | (1U << 1); + USART0_CFG = (1U << 0) | (1U << 2); +} + +static void systick_init(void) +{ + SYST_RVR = (SYSTEM_CLOCK_HZ / 1000U) - 1U; + SYST_CVR = 0; + SYST_CSR = 0x07U; +} + +static void eth_gpio_init(void) +{ + IOCON_PIN(4, 8) = 1U | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF; /* TXD0 */ + IOCON_PIN(0, 17) = 7U | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF; /* TXD1 */ + IOCON_PIN(4, 13) = 1U | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF; /* TX_EN */ + IOCON_PIN(4, 11) = 1U | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF; /* RXD0 */ + IOCON_PIN(4, 12) = 1U | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF; /* RXD1 */ + IOCON_PIN(4, 10) = 1U | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF; /* RX_DV */ + IOCON_PIN(4, 14) = 1U | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF; /* REF_CLK */ + IOCON_PIN(1, 16) = 1U | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF; /* MDC */ + IOCON_PIN(1, 23) = 4U | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF; /* MDIO */ + + IOCON_PIN(2, 26) = 0U | IOCON_DIGITAL_EN | IOCON_MODE_PULLUP; + GPIO_DIR(2) |= (1U << 26); + GPIO_SET(2) = (1U << 26); +} + +static void phy_reset(void) +{ + GPIO_CLR(2) = (1U << 26); + delay_ms(1); + GPIO_SET(2) = (1U << 26); + delay_ms(50); +} + +static void eth_clk_init(void) +{ + AHBCLKCTRLSET2 = (1U << 8); + PRESETCTRL2 = (1U << 8); + PRESETCTRL2 &= ~(1U << 8); + ETHPHYSEL |= (1U << 2); /* RMII before DMA reset */ +} + +static void led_init(void) +{ + IOCON_PIN(3, 14) = 0U | IOCON_DIGITAL_EN; + GPIO_DIR(3) |= (1U << 14); + GPIO_CLR(3) = (1U << 14); +} + +static void led_toggle(void) +{ + GPIO_NOT(3) = (1U << 14); +} + +static struct wolfIP *IPStack; + +/* TCP Echo Server (port 7) */ +#define ECHO_PORT 7 +static int listen_fd = -1; +static int client_fd = -1; +static uint8_t rx_buf[512]; + +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; + } +} + +int main(void) +{ + struct wolfIP_ll_dev *ll; + int ret; + + clock_init(); + usart0_init(); + systick_init(); + led_init(); + + printf("\r\n=== wolfIP LPC54S018M-EVK ===\r\n"); + + eth_gpio_init(); + phy_reset(); + eth_clk_init(); + + wolfIP_init_static(&IPStack); + + ll = wolfIP_getdev(IPStack); + ret = lpc54s018_eth_init(ll, NULL); + if (ret < 0) { + printf("ETH init failed (%d)\r\n", ret); + } else { + printf("PHY addr=%d link=%s\r\n", + ret & 0xFF, (ret & 0x100) ? "UP" : "DOWN"); + } + + /* IP configuration: DHCP or static fallback */ +#ifdef DHCP + printf("Starting DHCP...\r\n"); + (void)wolfIP_poll(IPStack, tick_ms); /* Prime last_tick */ + (void)dhcp_client_init(IPStack); +#else + { + ip4 ip = atoip4(WOLFIP_IP); + ip4 nm = atoip4(WOLFIP_NETMASK); + ip4 gw = atoip4(WOLFIP_GW); + wolfIP_ipconfig_set(IPStack, ip, nm, gw); + printf("Static IP: %s\r\n", WOLFIP_IP); + } +#endif + + /* TCP echo server on port 7 */ + { + struct wolfIP_sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(ECHO_PORT); + addr.sin_addr.s_addr = 0; + listen_fd = wolfIP_sock_socket(IPStack, AF_INET, IPSTACK_SOCK_STREAM, 0); + wolfIP_register_callback(IPStack, listen_fd, echo_cb, IPStack); + (void)wolfIP_sock_bind(IPStack, listen_fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + (void)wolfIP_sock_listen(IPStack, listen_fd, 1); + } + + printf("Ready! ping / echo test | nc 7\r\n"); + + /* Main loop */ + { + uint64_t last_led_ms = 0; +#ifdef DHCP + uint64_t dhcp_start_ms = tick_ms; + uint64_t dhcp_reinit_ms = tick_ms; + int dhcp_done = 0; +#endif + + for (;;) { + uint64_t now = tick_ms; + (void)wolfIP_poll(IPStack, now); + +#ifdef DHCP + if (!dhcp_done) { + if (dhcp_bound(IPStack)) { + ip4 ip = 0, nm = 0, gw = 0; + wolfIP_ipconfig_get(IPStack, &ip, &nm, &gw); + printf("DHCP bound: %u.%u.%u.%u\r\n", + (unsigned)((ip >> 24) & 0xFF), + (unsigned)((ip >> 16) & 0xFF), + (unsigned)((ip >> 8) & 0xFF), + (unsigned)(ip & 0xFF)); + dhcp_done = 1; + } else if ((now - dhcp_start_ms) > DHCP_TIMEOUT_MS) { + ip4 ip = 0, nm = 0, gw = 0; + wolfIP_ipconfig_get(IPStack, &ip, &nm, &gw); + if (ip == 0) { + printf("DHCP timeout, using static IP\r\n"); + wolfIP_ipconfig_set(IPStack, + atoip4(WOLFIP_IP), atoip4(WOLFIP_NETMASK), + atoip4(WOLFIP_GW)); + } + dhcp_done = 1; + } else if ((now - dhcp_reinit_ms) > DHCP_REINIT_MS) { + (void)dhcp_client_init(IPStack); + dhcp_reinit_ms = now; + } + } +#endif + + if ((now - last_led_ms) >= 2000U) { + led_toggle(); + last_led_ms = now; + } + } + } + + return 0; +} diff --git a/src/port/lpc54s018/startup.c b/src/port/lpc54s018/startup.c new file mode 100644 index 00000000..e4277490 --- /dev/null +++ b/src/port/lpc54s018/startup.c @@ -0,0 +1,50 @@ +/* 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; + +int main(void); + +/* Enable SRAM1/2 clocks before touching BSS or stack in those regions */ +#define AHBCLKCTRLSET0 (*(volatile uint32_t *)0x40000220UL) + +void Reset_Handler(void) +{ + uint32_t *src, *dst; + + /* SRAM1/2/3/X clocks (bits 3-6) must be on before BSS zeroing */ + AHBCLKCTRLSET0 = (1U << 3) | (1U << 4) | (1U << 5) | (1U << 6); + + src = &_sidata; + for (dst = &_sdata; dst < &_edata; ) + *dst++ = *src++; + + for (dst = &_sbss; dst < &_ebss; ) + *dst++ = 0u; + + (void)main(); + while (1) { } +} diff --git a/src/port/lpc54s018/syscalls.c b/src/port/lpc54s018/syscalls.c new file mode 100644 index 00000000..247bfa60 --- /dev/null +++ b/src/port/lpc54s018/syscalls.c @@ -0,0 +1,158 @@ +/* syscalls.c + * + * Minimal newlib stubs for LPC54S018 bare-metal. + * _write routes to Flexcomm0/USART0 FIFO for debug output. + * + * 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 + +/* Flexcomm0 / USART0 registers */ +#define USART0_BASE 0x40086000UL +#define USART0_FIFOSTAT (*(volatile uint32_t *)(USART0_BASE + 0xE04U)) +#define USART0_FIFOWR (*(volatile uint32_t *)(USART0_BASE + 0xE20U)) +/* FIFOSTAT bits 12-19 = TXLVL (number of bytes in TX FIFO). + * FIFO depth is 8. TX is possible when TXLVL < 8. + * Note: bit 5 is TXEMPTY (not TXNOTFULL as on some other chips). */ +#define USART0_FIFOSTAT_TXLVL_MASK (0xFFU << 12) +#define USART0_FIFOSTAT_TXLVL_MAX (8U << 12) + +extern uint32_t _ebss; +extern uint32_t _estack; + +static char *heap_end; + +int _write(int file, const char *ptr, int len) +{ + int i; + (void)file; + for (i = 0; i < len; i++) { + while ((USART0_FIFOSTAT & USART0_FIFOSTAT_TXLVL_MASK) >= USART0_FIFOSTAT_TXLVL_MAX) { } + USART0_FIFOWR = (uint32_t)ptr[i]; + } + 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 *)&_estack) { + 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/lpc54s018/target.ld b/src/port/lpc54s018/target.ld new file mode 100644 index 00000000..21f33ba2 --- /dev/null +++ b/src/port/lpc54s018/target.ld @@ -0,0 +1,90 @@ +/* LPC54S018 Linker Script + * SPIFI Flash: 16MB at 0x10000000 (Winbond W25Q128JV, XIP) + * SRAM: 192KB total across three AHB-accessible banks: + * SRAM0: 64KB @ 0x20000000 (CPU + ENET DMA) + * SRAM1: 64KB @ 0x20010000 (CPU + ENET DMA) + * SRAM2: 64KB @ 0x20020000 (CPU + ENET DMA) + * SRAMX: 32KB @ 0x04000000 (code bus only — NOT DMA accessible) + * + * SPIFI flash is NOT DMA-accessible. All DMA buffers (Ethernet descriptors + * and frame buffers) must be in SRAM at 0x20000000+. + * + * Image layout: + * 0x10000000: .spifi_config (512 bytes) — SPIFI boot ROM configuration + * 0x10000200: .isr_vector — Cortex-M4 vector table + * 0x100002xx: .text, .rodata + * 0x20000000: .data (copied from flash), .bss + */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x10000000, LENGTH = 16M + SRAM0 (rwx) : ORIGIN = 0x20000000, LENGTH = 64K + SRAM1 (rwx) : ORIGIN = 0x20010000, LENGTH = 64K + SRAM2 (rwx) : ORIGIN = 0x20020000, LENGTH = 64K +} + +_estack = ORIGIN(SRAM0) + LENGTH(SRAM0); +_sidata = LOADADDR(.data); + +SECTIONS +{ + /* SPIFI configuration block — must be at very start of flash. + * The boot ROM reads this to configure the SPIFI controller. */ + .spifi_config : + { + KEEP(*(.spifi_config)) + . = 0x200; /* Pad to 512 bytes */ + } > FLASH + + .isr_vector : + { + KEEP(*(.isr_vector)) + } > FLASH + + .text : + { + *(.text*) + *(.rodata*) + *(.ARM.extab* .gnu.linkonce.armextab.*) + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + *(.glue_7) + *(.glue_7t) + *(.eh_frame) + } > FLASH + + .preinit_array : + { + __preinit_array_start = .; + KEEP(*(.preinit_array*)) + __preinit_array_end = .; + } > FLASH + + .init_array : + { + __init_array_start = .; + KEEP(*(.init_array*)) + __init_array_end = .; + } > FLASH + + .fini_array : + { + __fini_array_start = .; + KEEP(*(.fini_array*)) + __fini_array_end = .; + } > FLASH + + .data : + { + _sdata = .; + *(.data*) + _edata = .; + } > SRAM0 AT > FLASH + + .bss (NOLOAD) : + { + _sbss = .; + *(.bss*) + *(COMMON) + _ebss = .; + } > SRAM0 +} diff --git a/src/port/lpc54s018/target_ram.ld b/src/port/lpc54s018/target_ram.ld new file mode 100644 index 00000000..a5c4498a --- /dev/null +++ b/src/port/lpc54s018/target_ram.ld @@ -0,0 +1,69 @@ +/* LPC54S018 RAM-only Linker Script (debugger-loaded) + * + * SRAM0: 64KB @ 0x20000000 - code, data, BSS + * SRAM1: 64KB @ 0x20010000 - BSS overflow + * SRAM2: 32KB @ 0x20020000 - stack (separate to avoid BSS/stack overlap) + */ +MEMORY +{ + CODE (rwx) : ORIGIN = 0x20000000, LENGTH = 128K + STACK (rw) : ORIGIN = 0x20020000, LENGTH = 32K +} + +_estack = ORIGIN(STACK) + LENGTH(STACK); + +SECTIONS +{ + .isr_vector : { KEEP(*(.isr_vector)) } > CODE + + .text : + { + *(.text*) + *(.rodata*) + *(.ARM.extab* .gnu.linkonce.armextab.*) + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + *(.glue_7) + *(.glue_7t) + *(.eh_frame) + } > CODE + + .preinit_array : + { + __preinit_array_start = .; + KEEP(*(.preinit_array*)) + __preinit_array_end = .; + } > CODE + + .init_array : + { + __init_array_start = .; + KEEP(*(.init_array*)) + __init_array_end = .; + } > CODE + + .fini_array : + { + __fini_array_start = .; + KEEP(*(.fini_array*)) + __fini_array_end = .; + } > CODE + + .data : + { + _sidata = .; + _sdata = .; + *(.data*) + _edata = .; + } > CODE + + .bss (NOLOAD) : + { + _sbss = .; + *(.bss*) + *(COMMON) + _ebss = .; + } > CODE + + ASSERT(_ebss <= ORIGIN(STACK), + "ERROR: code+data+BSS exceeds 128KB, collides with stack") +} diff --git a/src/port/lpc_enet/lpc_enet.c b/src/port/lpc_enet/lpc_enet.c new file mode 100644 index 00000000..67753d06 --- /dev/null +++ b/src/port/lpc_enet/lpc_enet.c @@ -0,0 +1,502 @@ +/* lpc_enet.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 + * + * Common NXP LPC Ethernet MAC/PHY driver for wolfIP. + * Synopsys DesignWare Ethernet QoS, enhanced descriptor format. + * Shared across LPC54018, LPC54S018, LPC546xx, and similar NXP MCUs. + * + * Board must define via CFLAGS or board header before compiling: + * LPC_ENET_BASE - ENET peripheral base address + * LPC_ENET_MDIO_CR - MDIO clock divider value + * LPC_ENET_1US_TIC - MAC 1us tick counter (AHB_MHz - 1) + */ + +#include +#include +#include "config.h" +#include "lpc_enet.h" + +/* Register access */ +#define ETH_REG(offset) (*(volatile uint32_t *)(LPC_ENET_BASE + (offset))) + +/* MAC registers */ +#define ETH_MACCR ETH_REG(0x0000U) +#define ETH_MACECR ETH_REG(0x0004U) +#define ETH_MACPFR ETH_REG(0x0008U) +#define ETH_MACWTR ETH_REG(0x000CU) +#define ETH_MACQ0TXFCR ETH_REG(0x0070U) +#define ETH_MACRXFCR ETH_REG(0x0090U) +#define ETH_MACRXQC0R ETH_REG(0x00A0U) +#define ETH_MAC1USTCR ETH_REG(0x00DCU) +#define ETH_MACMDIOAR ETH_REG(0x0200U) +#define ETH_MACMDIODR ETH_REG(0x0204U) +#define ETH_MACA0HR ETH_REG(0x0300U) +#define ETH_MACA0LR ETH_REG(0x0304U) + +/* MTL registers */ +#define ETH_MTLOMR ETH_REG(0x0C00U) +#define ETH_MTLTXQOMR ETH_REG(0x0D00U) +#define ETH_MTLRXQOMR ETH_REG(0x0D30U) + +/* DMA registers */ +#define ETH_DMAMR ETH_REG(0x1000U) +#define ETH_DMASBMR ETH_REG(0x1004U) +#define ETH_DMACCR ETH_REG(0x1100U) +#define ETH_DMACTXCR ETH_REG(0x1104U) +#define ETH_DMACRXCR ETH_REG(0x1108U) +#define ETH_DMACTXDLAR ETH_REG(0x1114U) +#define ETH_DMACRXDLAR ETH_REG(0x111CU) +#define ETH_DMACTXDTPR ETH_REG(0x1120U) +#define ETH_DMACRXDTPR ETH_REG(0x1128U) +#define ETH_DMACTXRLR ETH_REG(0x112CU) +#define ETH_DMACRXRLR ETH_REG(0x1130U) +#define ETH_DMACIER ETH_REG(0x1134U) +#define ETH_DMACSR ETH_REG(0x1160U) + +/* MAC bits */ +#define MACCR_RE (1U << 0) +#define MACCR_TE (1U << 1) +#define MACCR_DM (1U << 13) +#define MACCR_FES (1U << 14) +#define MACCR_PS (1U << 15) + +/* DMA bits */ +#define DMAMR_SWR (1U << 0) +#define DMASBMR_FB (1U << 0) +#define DMASBMR_AAL (1U << 12) +#define DMACTXCR_ST (1U << 0) +#define DMACTXCR_OSF (1U << 4) +#define DMACRXCR_SR (1U << 0) +#define DMACRXCR_RBSZ_SHIFT 1 +#define DMACSR_TBU (1U << 2) +#define DMACSR_TPS (1U << 1) +#define DMACSR_RPS (1U << 8) +#define DMACSR_RBU (1U << 7) +#define DMACTXCR_TPBL(v) (((uint32_t)(v) & 0x3FU) << 16) +#define DMACRXCR_RPBL(v) (((uint32_t)(v) & 0x3FU) << 16) +#define DMACIER_NIE (1U << 15) +#define DMACIER_AIE (1U << 14) +#define DMACIER_RBUE (1U << 7) +#define DMACIER_RIE (1U << 6) +#define DMACIER_TIE (1U << 0) + +/* MTL bits */ +#define TXQOMR_FTQ (1U << 0) +#define TXQOMR_TSF (1U << 1) +#define TXQOMR_TXQEN_ENABLE (2U << 2) +#define TXQOMR_TQS_2048 (0x7U << 16) +#define TXQOMR_MASK 0x00070072U +#define RXQOMR_RSF (1U << 5) +#define RXQOMR_RQS_4096 (0xFU << 20) +#define RXQOMR_MASK 0x00F0007FU + +/* MDIO bits */ +#define MDIOAR_MB (1U << 0) +#define MDIOAR_GOC_SHIFT 2 +#define MDIOAR_GOC_WRITE 0x1U +#define MDIOAR_GOC_READ 0x3U +#define MDIOAR_CR_SHIFT 8 +#define MDIOAR_CR_MASK (0xFU << 8) +#define MDIOAR_RDA_SHIFT 16 +#define MDIOAR_PA_SHIFT 21 + +/* PHY registers */ +#define PHY_BCR 0x00U +#define PHY_BSR 0x01U +#define PHY_ID1 0x02U +#define PHY_ANAR 0x04U + +#define BCR_RESET (1U << 15) +#define BCR_SPEED_100 (1U << 13) +#define BCR_AUTONEG_ENABLE (1U << 12) +#define BCR_POWER_DOWN (1U << 11) +#define BCR_ISOLATE (1U << 10) +#define BCR_RESTART_AUTONEG (1U << 9) +#define BCR_FULL_DUPLEX (1U << 8) +#define BSR_LINK_STATUS (1U << 2) +#define BSR_AUTONEG_COMPLETE (1U << 5) +#define BSR_10_HALF (1U << 11) +#define BSR_10_FULL (1U << 12) +#define BSR_100_HALF (1U << 13) +#define BSR_100_FULL (1U << 14) +#define ANAR_DEFAULT 0x01E1U + +/* DMA descriptor (enhanced format, 4 words) */ +struct eth_desc { + volatile uint32_t des0; + volatile uint32_t des1; + volatile uint32_t des2; + volatile uint32_t des3; +}; + +#define TDES3_OWN (1U << 31) +#define TDES3_FD (1U << 29) +#define TDES3_LD (1U << 28) +#define TDES2_B1L_MASK (0x3FFFU) +#define TDES3_FL_MASK (0x7FFFU) +#define RDES3_OWN (1U << 31) +#define RDES3_IOC (1U << 30) +#define RDES3_BUF1V (1U << 24) +#define RDES3_FS (1U << 29) +#define RDES3_LS (1U << 28) +#define RDES3_PL_MASK (0x3FFFU) + +#define RX_DESC_COUNT 4U +#define TX_DESC_COUNT 3U +#define RX_BUF_SIZE LINK_MTU +#define TX_BUF_SIZE LINK_MTU +#define FRAME_MIN_LEN 60U +#define DMA_PBL 32U + +#define DMA_ADDR(ptr) ((uint32_t)(ptr)) + +static struct eth_desc rx_ring[RX_DESC_COUNT] __attribute__((aligned(32))); +static struct eth_desc tx_ring[TX_DESC_COUNT] __attribute__((aligned(32))); +static uint8_t rx_buffers[RX_DESC_COUNT][RX_BUF_SIZE] __attribute__((aligned(32))); +static uint8_t tx_buffers[TX_DESC_COUNT][TX_BUF_SIZE] __attribute__((aligned(32))); + +static uint32_t rx_idx; +static uint32_t tx_idx; +static int32_t phy_addr = -1; +static uint32_t rx_poll_count; +static uint32_t rx_pkt_count; + +/* MDIO */ + +static void mdio_wait(void) +{ + uint32_t t = 100000U; + while ((ETH_MACMDIOAR & MDIOAR_MB) && --t) { } +} + +static void mdio_init(void) +{ + ETH_MACMDIOAR = (LPC_ENET_MDIO_CR << MDIOAR_CR_SHIFT); +} + +static uint16_t mdio_read(uint32_t phy, uint32_t reg) +{ + uint32_t cr; + mdio_wait(); + cr = ETH_MACMDIOAR & MDIOAR_CR_MASK; + ETH_MACMDIOAR = cr | (MDIOAR_GOC_READ << MDIOAR_GOC_SHIFT) | + (phy << MDIOAR_PA_SHIFT) | (reg << MDIOAR_RDA_SHIFT); + ETH_MACMDIOAR |= MDIOAR_MB; + mdio_wait(); + return (uint16_t)(ETH_MACMDIODR & 0xFFFFU); +} + +static void mdio_write(uint32_t phy, uint32_t reg, uint16_t value) +{ + uint32_t cr; + mdio_wait(); + cr = ETH_MACMDIOAR & MDIOAR_CR_MASK; + ETH_MACMDIOAR = cr | (MDIOAR_GOC_WRITE << MDIOAR_GOC_SHIFT) | + (phy << MDIOAR_PA_SHIFT) | (reg << MDIOAR_RDA_SHIFT); + ETH_MACMDIODR = (uint32_t)value; + ETH_MACMDIOAR |= MDIOAR_MB; + mdio_wait(); +} + +/* PHY */ + +static int32_t phy_detect(void) +{ + uint32_t addr; + for (addr = 0; addr < 32; addr++) { + uint16_t id = mdio_read(addr, PHY_ID1); + if (id != 0xFFFFU && id != 0x0000U) + return (int32_t)addr; + } + return -1; +} + +static void phy_init(void) +{ + uint32_t timeout; + uint16_t ctrl, bsr; + + if (phy_addr < 0) { + phy_addr = phy_detect(); + if (phy_addr < 0) { phy_addr = 0; return; } + } + + mdio_write((uint32_t)phy_addr, PHY_BCR, BCR_RESET); + timeout = 100000U; + do { ctrl = mdio_read((uint32_t)phy_addr, PHY_BCR); + } while ((ctrl & BCR_RESET) && --timeout); + + ctrl &= ~(BCR_POWER_DOWN | BCR_ISOLATE | BCR_SPEED_100 | BCR_FULL_DUPLEX); + mdio_write((uint32_t)phy_addr, PHY_ANAR, ANAR_DEFAULT); + ctrl |= BCR_AUTONEG_ENABLE | BCR_RESTART_AUTONEG; + mdio_write((uint32_t)phy_addr, PHY_BCR, ctrl); + + timeout = 100000U; + do { bsr = mdio_read((uint32_t)phy_addr, PHY_BSR); + bsr |= mdio_read((uint32_t)phy_addr, PHY_BSR); + } while (!(bsr & BSR_AUTONEG_COMPLETE) && --timeout); + + timeout = 100000U; + do { bsr = mdio_read((uint32_t)phy_addr, PHY_BSR); + bsr |= mdio_read((uint32_t)phy_addr, PHY_BSR); + } while (!(bsr & BSR_LINK_STATUS) && --timeout); +} + +/* MAC/DMA */ + +static int hw_reset(void) +{ + uint32_t t = 1000000U; + ETH_DMAMR |= DMAMR_SWR; + while ((ETH_DMAMR & DMAMR_SWR) && --t) { } + return t ? 0 : -1; +} + +static void config_mac(const uint8_t mac[6]) +{ + ETH_MAC1USTCR = LPC_ENET_1US_TIC; + ETH_MACCR = (1U << 20) | (1U << 21) | (1U << 27) | + MACCR_DM | MACCR_FES | MACCR_PS; + ETH_MACRXQC0R = 0x02U; /* Enable RX Queue 0 (required on LPC) */ + ETH_MACPFR = 0; + ETH_MACECR = 0x618U; + ETH_MACWTR = 0; + ETH_MACQ0TXFCR = (1U << 7); + ETH_MACRXFCR = 0; + ETH_MACA0HR = ((uint32_t)mac[5] << 8) | (uint32_t)mac[4]; + ETH_MACA0LR = ((uint32_t)mac[3] << 24) | ((uint32_t)mac[2] << 16) | + ((uint32_t)mac[1] << 8) | (uint32_t)mac[0]; +} + +static void config_speed_duplex(void) +{ + uint32_t maccr; + uint16_t bsr; + if (phy_addr < 0) return; + maccr = ETH_MACCR & ~(MACCR_FES | MACCR_DM); + bsr = mdio_read((uint32_t)phy_addr, PHY_BSR); + bsr |= mdio_read((uint32_t)phy_addr, PHY_BSR); + if (bsr & BSR_100_FULL) maccr |= MACCR_FES | MACCR_DM; + else if (bsr & BSR_100_HALF) maccr |= MACCR_FES; + else if (bsr & BSR_10_FULL) maccr |= MACCR_DM; + ETH_MACCR = maccr; +} + +static void config_mtl(void) +{ + uint32_t txq, rxq; + ETH_MTLOMR = 0x60U; + txq = (ETH_MTLTXQOMR & ~TXQOMR_MASK) | + TXQOMR_TSF | TXQOMR_TXQEN_ENABLE | TXQOMR_TQS_2048; + ETH_MTLTXQOMR = txq; + rxq = (ETH_MTLRXQOMR & ~RXQOMR_MASK) | RXQOMR_RSF | RXQOMR_RQS_4096; + ETH_MTLRXQOMR = rxq; +} + +static void config_dma(void) +{ + ETH_DMASBMR = DMASBMR_AAL | DMASBMR_FB; + ETH_DMACCR = 0; + ETH_DMACRXCR = ((RX_BUF_SIZE & RDES3_PL_MASK) << DMACRXCR_RBSZ_SHIFT) | + DMACRXCR_RPBL(DMA_PBL); + ETH_DMACTXCR = DMACTXCR_OSF | DMACTXCR_TPBL(DMA_PBL); +} + +static void init_desc(void) +{ + uint32_t i; + 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; + } + 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_idx = tx_idx = 0; + + __asm volatile ("dsb sy" ::: "memory"); + ETH_DMACTXDLAR = DMA_ADDR(&tx_ring[0]); + ETH_DMACRXDLAR = DMA_ADDR(&rx_ring[0]); + ETH_DMACTXRLR = TX_DESC_COUNT - 1; + ETH_DMACRXRLR = RX_DESC_COUNT - 1; + ETH_DMACTXDTPR = DMA_ADDR(&tx_ring[0]); + ETH_DMACRXDTPR = DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1]); + __asm volatile ("dsb sy" ::: "memory"); + + for (i = 0; i < TX_DESC_COUNT; i++) + *(volatile uint32_t *)&tx_ring[i].des0 = DMA_ADDR(tx_buffers[i]); + __asm volatile ("dsb sy" ::: "memory"); +} + +static void arm_rx(void) +{ + uint32_t i; + for (i = 0; i < RX_DESC_COUNT; i++) { + rx_ring[i].des0 = DMA_ADDR(rx_buffers[i]); + rx_ring[i].des1 = 0; rx_ring[i].des2 = 0; + __asm volatile ("dsb sy" ::: "memory"); + rx_ring[i].des3 = RDES3_OWN | RDES3_IOC | RDES3_BUF1V; + } + __asm volatile ("dmb sy" ::: "memory"); + ETH_DMACRXDTPR = DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1]); +} + +static void mac_start(void) +{ + ETH_MACCR |= MACCR_TE | MACCR_RE; + ETH_MTLTXQOMR |= TXQOMR_FTQ; + ETH_DMACTXCR |= DMACTXCR_ST; + ETH_DMACRXCR |= DMACRXCR_SR; + ETH_DMACIER = DMACIER_NIE | DMACIER_AIE | DMACIER_RBUE | + DMACIER_RIE | DMACIER_TIE; + ETH_DMACSR = DMACSR_TPS | DMACSR_RPS | DMACSR_RBU; + __asm volatile ("dsb sy" ::: "memory"); + arm_rx(); +} + +static void mac_stop(void) +{ + ETH_DMACTXCR &= ~DMACTXCR_ST; + ETH_DMACRXCR &= ~DMACRXCR_SR; + ETH_MACCR &= ~MACCR_RE; + ETH_MTLTXQOMR |= TXQOMR_FTQ; + ETH_MACCR &= ~MACCR_TE; +} + +/* wolfIP poll/send callbacks */ + +static int eth_poll(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) +{ + struct eth_desc *desc; + uint32_t status, frame_len = 0; + (void)dev; + + rx_poll_count++; + + if (ETH_DMACSR & DMACSR_RBU) { + ETH_DMACSR = DMACSR_RBU; + __asm volatile ("dsb sy" ::: "memory"); + ETH_DMACRXDTPR = DMA_ADDR(&rx_ring[RX_DESC_COUNT - 1]); + } + + desc = &rx_ring[rx_idx]; + if (desc->des3 & RDES3_OWN) return 0; + + rx_pkt_count++; + status = desc->des3; + if ((status & (RDES3_FS | RDES3_LS)) == (RDES3_FS | RDES3_LS)) { + frame_len = status & RDES3_PL_MASK; + if (frame_len > len) frame_len = len; + if (frame_len > 0) + memcpy(frame, rx_buffers[rx_idx], frame_len); + } + + desc->des0 = DMA_ADDR(rx_buffers[rx_idx]); + desc->des1 = 0; desc->des2 = 0; + __asm volatile ("dsb sy" ::: "memory"); + desc->des3 = RDES3_OWN | RDES3_IOC | RDES3_BUF1V; + __asm volatile ("dsb sy" ::: "memory"); + ETH_DMACRXDTPR = DMA_ADDR(desc); + rx_idx = (rx_idx + 1) % RX_DESC_COUNT; + + return (int)frame_len; +} + +static int eth_send(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) +{ + struct eth_desc *desc; + uint32_t dma_len, next; + (void)dev; + + if (len == 0 || len > TX_BUF_SIZE) return -1; + desc = &tx_ring[tx_idx]; + if (desc->des3 & TDES3_OWN) return -2; + + memcpy(tx_buffers[tx_idx], frame, len); + dma_len = (len < FRAME_MIN_LEN) ? FRAME_MIN_LEN : len; + if (dma_len > len) memset(tx_buffers[tx_idx] + len, 0, dma_len - len); + + desc->des0 = DMA_ADDR(tx_buffers[tx_idx]); + desc->des1 = 0; + desc->des2 = (dma_len & TDES2_B1L_MASK); + __asm volatile ("dsb sy" ::: "memory"); + desc->des3 = (dma_len & TDES3_FL_MASK) | TDES3_FD | TDES3_LD | TDES3_OWN; + __asm volatile ("dsb sy" ::: "memory"); + + ETH_DMACSR = DMACSR_TBU; + next = (tx_idx + 1) % TX_DESC_COUNT; + ETH_DMACTXDTPR = DMA_ADDR(&tx_ring[next]); + tx_idx = next; + return (int)len; +} + +/* Public API */ + +void lpc_enet_get_stats(uint32_t *polls, uint32_t *pkts) +{ + if (polls) *polls = rx_poll_count; + if (pkts) *pkts = rx_pkt_count; +} + +uint32_t lpc_enet_get_dmacsr(void) +{ + return ETH_DMACSR; +} + +int lpc_enet_init(struct wolfIP_ll_dev *ll, const uint8_t *mac) +{ + uint8_t local_mac[6]; + uint16_t id1, bsr; + + if (!ll) return -1; + + if (!mac) { + local_mac[0] = 0x02; local_mac[1] = 0x11; + local_mac[2] = 0x54; local_mac[3] = 0x18; + local_mac[4] = 0x00; local_mac[5] = 0x01; + mac = local_mac; + } + + memcpy(ll->mac, mac, 6); + strncpy(ll->ifname, "eth0", sizeof(ll->ifname) - 1); + ll->ifname[sizeof(ll->ifname) - 1] = '\0'; + ll->poll = eth_poll; + ll->send = eth_send; + + mac_stop(); + if (hw_reset() != 0) return -2; + + mdio_init(); + config_mac(mac); + config_mtl(); + config_dma(); + init_desc(); + phy_init(); + config_speed_duplex(); + mac_start(); + + id1 = mdio_read((uint32_t)phy_addr, PHY_ID1); + bsr = mdio_read((uint32_t)phy_addr, PHY_BSR); + + return ((id1 & 0xFF00U) << 8) | + ((bsr & 0x04U) ? 0x100 : 0) | + (phy_addr & 0xFF); +} diff --git a/src/port/lpc_enet/lpc_enet.h b/src/port/lpc_enet/lpc_enet.h new file mode 100644 index 00000000..992917fa --- /dev/null +++ b/src/port/lpc_enet/lpc_enet.h @@ -0,0 +1,51 @@ +/* lpc_enet.h + * + * Common NXP LPC Ethernet driver for wolfIP. + * Synopsys DesignWare Ethernet QoS, enhanced descriptor format. + * Shared across LPC54018, LPC54S018, LPC546xx, and similar NXP MCUs. + * + * Board-specific code must define LPC_ENET_BASE, LPC_ENET_MDIO_CR, + * and LPC_ENET_1US_TIC before compiling lpc_enet.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 + */ +#ifndef WOLFIP_LPC_ENET_H +#define WOLFIP_LPC_ENET_H + +#include +#include "wolfip.h" + +/* + * Board must define these via CFLAGS or a board header: + * + * LPC_ENET_BASE ENET peripheral base address + * (0x40092000 for LPC54S018/LPC54608) + * + * LPC_ENET_MDIO_CR MDIO clock divider (NXP LPC mapping): + * CR=2: <35MHz, CR=3: 35-60MHz, CR=0: 60-100MHz, + * CR=1: 100-150MHz, CR=4: 150-250MHz + * + * LPC_ENET_1US_TIC MAC 1us tick counter value: (AHB_clock_MHz - 1) + */ + +int lpc_enet_init(struct wolfIP_ll_dev *ll, const uint8_t *mac); +void lpc_enet_get_stats(uint32_t *polls, uint32_t *pkts); +uint32_t lpc_enet_get_dmacsr(void); + +#endif /* WOLFIP_LPC_ENET_H */ From 96a246f6293c19111878c4b273b34c9c77050e35 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 2 Apr 2026 11:55:12 -0700 Subject: [PATCH 2/3] Add build workflows --- .github/workflows/lpc54s018.yml | 32 +++++++++++++++++++ .../{stm32n6-build.yml => stm32n6.yml} | 0 2 files changed, 32 insertions(+) create mode 100644 .github/workflows/lpc54s018.yml rename .github/workflows/{stm32n6-build.yml => stm32n6.yml} (100%) diff --git a/.github/workflows/lpc54s018.yml b/.github/workflows/lpc54s018.yml new file mode 100644 index 00000000..94594a48 --- /dev/null +++ b/.github/workflows/lpc54s018.yml @@ -0,0 +1,32 @@ +name: LPC54S018 Build + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +jobs: + lpc54s018_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 LPC54S018 firmware + run: | + set -euo pipefail + make -C src/port/lpc54s018 + + - name: Verify binary + run: | + set -euo pipefail + test -f src/port/lpc54s018/app.bin + arm-none-eabi-size src/port/lpc54s018/app.elf diff --git a/.github/workflows/stm32n6-build.yml b/.github/workflows/stm32n6.yml similarity index 100% rename from .github/workflows/stm32n6-build.yml rename to .github/workflows/stm32n6.yml From 0a91639cb8eafdb258363e5550679f777b61722f Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 2 Apr 2026 13:39:30 -0700 Subject: [PATCH 3/3] Address fenrir, copilot, and internal review - Makefile: Add cppcheck comparePointers suppressions for lpc54s018 startup.c/syscalls.c - main.c: Fix non-atomic 64-bit tick_ms reads with get_tick_ms() (disable/enable interrupts) - startup.c: Restore __libc_init_array() call (matches other ports) - lpc_enet.c: Add RX_BUF_SIZE bounds check in eth_poll to prevent buffer over-read - fix_checksum.py: Auto-detect vector table offset (0x0 for RAM build, 0x200 for flash) - target_ram.ld: Clarify SRAM2 is 32KB on J4M package - flash.sh: Add flash helper script (matches N6 port's flash.sh) --- .github/workflows/codespell.yml | 2 +- Makefile | 2 ++ src/port/lpc54s018/README.md | 8 +++--- src/port/lpc54s018/fix_checksum.py | 41 ++++++++++++++++-------------- src/port/lpc54s018/flash.sh | 29 +++++++++++++++++++++ src/port/lpc54s018/main.c | 25 +++++++++++++----- src/port/lpc54s018/startup.c | 2 ++ src/port/lpc54s018/target.ld | 12 ++++----- src/port/lpc54s018/target_ram.ld | 2 +- src/port/lpc_enet/lpc_enet.c | 1 + 10 files changed, 86 insertions(+), 38 deletions(-) create mode 100755 src/port/lpc54s018/flash.sh diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 352b7b67..224a9f1d 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -23,4 +23,4 @@ jobs: uses: codespell-project/actions-codespell@v2 with: skip: .git,./IDE,*.der,*.pem - ignore_words_list: inh,inout,keypair,nd,parm,rcv,ser,tha,HSI,TE,UE,Synopsys,synopsys + ignore_words_list: inh,inout,keypair,nd,parm,rcv,ser,tha,HSI,TE,UE,Synopsys,synopsys,FRO diff --git a/Makefile b/Makefile index 31ed261b..fbfd2cf7 100644 --- a/Makefile +++ b/Makefile @@ -123,6 +123,8 @@ CPPCHECK_FLAGS=--enable=warning,performance,portability,missingInclude \ --suppress=comparePointers:src/port/stm32n6/syscalls.c \ --suppress=comparePointers:src/port/va416xx/startup.c \ --suppress=comparePointers:src/port/va416xx/syscalls.c \ + --suppress=comparePointers:src/port/lpc54s018/startup.c \ + --suppress=comparePointers:src/port/lpc54s018/syscalls.c \ --disable=style \ --std=c99 --language=c \ --platform=unix64 \ diff --git a/src/port/lpc54s018/README.md b/src/port/lpc54s018/README.md index cb8a2079..145cf638 100644 --- a/src/port/lpc54s018/README.md +++ b/src/port/lpc54s018/README.md @@ -10,9 +10,9 @@ Bare-metal port of wolfIP for the NXP LPCXpresso54S018M development board, featu make ``` -2. **Flash to board:** +2. **Flash to board (RAM-loaded via on-board Link2):** ```bash - pyocd flash -t lpc54608 app.elf + bash flash.sh ``` 3. **Monitor UART output** (115200 baud on /dev/ttyACM0): @@ -72,10 +72,10 @@ make size ## Flashing ```bash -pyocd flash -t lpc54608 app.elf +bash flash.sh ``` -Note: use `-t lpc54608` as the target type since `lpc54s018` is not in pyocd's built-in list. +The flash script loads `app.elf` into SRAM via pyocd and starts execution. Uses `-t lpc54608` as target type since `lpc54s018` is not in pyocd's built-in list. ## Serial Console diff --git a/src/port/lpc54s018/fix_checksum.py b/src/port/lpc54s018/fix_checksum.py index ef82ac29..5d2d4ff3 100644 --- a/src/port/lpc54s018/fix_checksum.py +++ b/src/port/lpc54s018/fix_checksum.py @@ -2,49 +2,52 @@ # fix_checksum.py - compute LPC vector table checksum # # The LPC54S018 boot ROM requires that vector table entries 0-7 sum to zero. -# The vector table starts at offset 0x200 in the binary (after the 512-byte -# SPIFI configuration block). -# -# This script patches entry[7] so that sum(entries[0:8]) == 0 (mod 2^32). +# Auto-detects whether vector table is at offset 0 (RAM build) or 0x200 +# (SPIFI flash build with 512-byte config block). import struct import sys -SPIFI_CONFIG_SIZE = 0x200 # 512-byte SPIFI config block before vector table +def find_vector_offset(data): + """Detect vector table offset by checking for valid SP.""" + for off in (0, 0x200): + if off + 32 > len(data): + continue + sp = struct.unpack_from('") + print("Usage: %s " % sys.argv[0]) sys.exit(1) fname = sys.argv[1] with open(fname, 'r+b') as f: - f.seek(SPIFI_CONFIG_SIZE) - data = f.read(32) - if len(data) < 32: - print(f"Error: file too small (need at least {SPIFI_CONFIG_SIZE + 32} bytes)") + data = f.read() + off = find_vector_offset(data) + + if off + 32 > len(data): + print("Error: file too small") sys.exit(1) - vecs = list(struct.unpack('<8I', data)) - # Compute checksum: entry[7] = -(sum of entries 0-6) mod 2^32 + vecs = list(struct.unpack_from('<8I', data, off)) partial_sum = sum(vecs[:7]) & 0xFFFFFFFF cksum = (0x100000000 - partial_sum) & 0xFFFFFFFF - vecs[7] = cksum - # Write back - f.seek(SPIFI_CONFIG_SIZE + 7 * 4) + f.seek(off + 7 * 4) f.write(struct.pack('des3; if ((status & (RDES3_FS | RDES3_LS)) == (RDES3_FS | RDES3_LS)) { frame_len = status & RDES3_PL_MASK; + if (frame_len > RX_BUF_SIZE) frame_len = RX_BUF_SIZE; if (frame_len > len) frame_len = len; if (frame_len > 0) memcpy(frame, rx_buffers[rx_idx], frame_len);