Skip to content

Commit 8f407ce

Browse files
dgarskeaidangarske
authored andcommitted
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. Co-authored-by: Aidan Garske <aidan@wolfssl.com>
1 parent 680a6c5 commit 8f407ce

19 files changed

Lines changed: 2116 additions & 1 deletion

.github/workflows/codespell.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ jobs:
2323
uses: codespell-project/actions-codespell@v2
2424
with:
2525
skip: .git,./IDE,*.der,*.pem
26-
ignore_words_list: inh,inout,keypair,nd,parm,rcv,ser,tha,HSI,TE,UE,Synopsys,synopsys
26+
ignore_words_list: inh,inout,keypair,nd,parm,rcv,ser,tha,HSI,TE,UE,Synopsys,synopsys,FRO

.github/workflows/lpc54s018.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: LPC54S018 Build
2+
3+
on:
4+
push:
5+
branches: [ 'master', 'main', 'release/**' ]
6+
pull_request:
7+
branches: [ '*' ]
8+
9+
jobs:
10+
lpc54s018_build:
11+
runs-on: ubuntu-latest
12+
timeout-minutes: 10
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Install ARM toolchain
18+
run: |
19+
set -euo pipefail
20+
sudo apt-get update
21+
sudo apt-get install -y gcc-arm-none-eabi
22+
23+
- name: Build LPC54S018 firmware
24+
run: |
25+
set -euo pipefail
26+
make -C src/port/lpc54s018
27+
28+
- name: Verify binary
29+
run: |
30+
set -euo pipefail
31+
test -f src/port/lpc54s018/app.bin
32+
arm-none-eabi-size src/port/lpc54s018/app.elf

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ CPPCHECK_FLAGS=--enable=warning,performance,portability,missingInclude \
124124
--suppress=comparePointers:src/port/stm32n6/syscalls.c \
125125
--suppress=comparePointers:src/port/va416xx/startup.c \
126126
--suppress=comparePointers:src/port/va416xx/syscalls.c \
127+
--suppress=comparePointers:src/port/lpc54s018/startup.c \
128+
--suppress=comparePointers:src/port/lpc54s018/syscalls.c \
127129
--disable=style \
128130
--std=c99 --language=c \
129131
--platform=unix64 \

src/port/lpc54s018/Makefile

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
CC := arm-none-eabi-gcc
2+
OBJCOPY := arm-none-eabi-objcopy
3+
SIZE := arm-none-eabi-size
4+
5+
ROOT := ../../..
6+
7+
CFLAGS := -mcpu=cortex-m4 -mthumb -mfloat-abi=soft
8+
CFLAGS += -Os -ffreestanding -fdata-sections -ffunction-sections
9+
CFLAGS += -g -ggdb -Wall -Wextra -Werror
10+
CFLAGS += -I. -I$(ROOT) -I$(ROOT)/src -I$(ROOT)/src/port/lpc_enet
11+
12+
CFLAGS_EXT := $(filter-out -Werror,$(CFLAGS))
13+
CFLAGS_EXT += -Wno-unused-variable -Wno-unused-function -Wno-unused-parameter
14+
CFLAGS_EXT += -Wno-sign-compare -Wno-missing-field-initializers
15+
16+
# Default to flash boot (target.ld). Use `make ram` for SRAM-loaded build.
17+
LDSCRIPT ?= target.ld
18+
LDFLAGS := -nostdlib -T $(LDSCRIPT) -Wl,-gc-sections
19+
20+
APP_SRCS := startup.c ivt.c syscalls.c main.c
21+
APP_OBJS := $(patsubst %.c,%.o,$(APP_SRCS))
22+
23+
# Shared LPC ENET driver (src/port/lpc_enet/)
24+
LPC_ENET_SRC := $(ROOT)/src/port/lpc_enet/lpc_enet.c
25+
LPC_ENET_OBJ := lpc_enet.o
26+
27+
# wolfIP core
28+
WOLFIP_SRC := $(ROOT)/src/wolfip.c
29+
WOLFIP_OBJ := wolfip.o
30+
31+
ALL_OBJS := $(APP_OBJS) $(LPC_ENET_OBJ) $(WOLFIP_OBJ)
32+
33+
all: app.bin
34+
@echo "Built LPC54S018M-EVK wolfIP port"
35+
@$(SIZE) app.elf
36+
37+
app.elf: $(ALL_OBJS) $(LDSCRIPT)
38+
$(CC) $(CFLAGS) $(ALL_OBJS) $(LDFLAGS) \
39+
-Wl,--start-group -lc -lm -lgcc -lnosys -Wl,--end-group -o $@
40+
41+
app.bin: app.elf
42+
$(OBJCOPY) -O binary $< $@
43+
python3 fix_checksum.py $@
44+
45+
%.o: %.c
46+
$(CC) $(CFLAGS) -c $< -o $@
47+
48+
# Shared LPC ENET driver (include board header for LPC_ENET_* defines)
49+
$(LPC_ENET_OBJ): $(LPC_ENET_SRC)
50+
$(CC) $(CFLAGS) -include lpc54s018_eth.h -c $< -o $@
51+
52+
$(WOLFIP_OBJ): $(WOLFIP_SRC)
53+
$(CC) $(CFLAGS_EXT) -c $< -o $@
54+
55+
# Build the SRAM-loaded variant for fast iteration (no UART - see README).
56+
ram:
57+
$(MAKE) clean
58+
$(MAKE) LDSCRIPT=target_ram.ld
59+
60+
clean:
61+
rm -f *.o app.elf app.bin
62+
63+
size: app.elf
64+
@$(SIZE) app.elf
65+
66+
.PHONY: all ram clean size help
67+
68+
help:
69+
@echo "LPC54S018M-EVK wolfIP Build System"
70+
@echo ""
71+
@echo " make Build app.bin for SPIFI flash boot (default)"
72+
@echo " make ram Build app.bin for SRAM load (no UART, dev only)"
73+
@echo " make clean Remove build artifacts"
74+
@echo " make size Show memory usage"
75+
@echo ""
76+
@echo "Flashing (on-board Link2 CMSIS-DAP):"
77+
@echo " bash flash.sh Program SPIFI flash and reset (recommended)"
78+
@echo " bash flash_ram.sh Load to SRAM and run (no UART)"
79+
@echo ""
80+
@echo "Testing:"
81+
@echo " ping <device-ip>"
82+
@echo " echo test | nc <device-ip> 7"

src/port/lpc54s018/README.md

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# wolfIP LPC54S018M-EVK Port
2+
3+
Bare-metal port of wolfIP for the NXP LPCXpresso54S018M development board, featuring an Ethernet driver and TCP/IP echo server example.
4+
5+
## Quick Start
6+
7+
1. **Install pyocd target pack** (one-time):
8+
```bash
9+
pyocd pack install lpc54s018j4met180
10+
```
11+
12+
2. **Build for SPIFI flash boot:**
13+
```bash
14+
cd src/port/lpc54s018
15+
make
16+
```
17+
18+
3. **Program SPIFI flash and reset:**
19+
```bash
20+
bash flash.sh
21+
```
22+
23+
4. **Monitor UART output** (115200 baud on /dev/ttyACM0):
24+
```bash
25+
screen /dev/ttyACM0 115200
26+
```
27+
Get the device IP address from the DHCP output.
28+
29+
5. **Test** (replace `<device-ip>` with IP from step 4):
30+
```bash
31+
# TCP Echo
32+
echo "Hello" | nc <device-ip> 7
33+
34+
# Ping
35+
ping <device-ip>
36+
```
37+
38+
## Hardware Requirements
39+
40+
- LPCXpresso54S018M development board (NXP OM40003)
41+
- Ethernet connection (RMII, RJ45 at J4)
42+
- USB cable for debug (J8, on-board Link2 CMSIS-DAP)
43+
44+
## Software Requirements
45+
46+
- ARM GCC toolchain (`arm-none-eabi-gcc`)
47+
- pyocd (`pip install pyocd`)
48+
- Serial terminal (screen, minicom, picocom)
49+
50+
### Installing Dependencies (Ubuntu/Debian)
51+
52+
```bash
53+
sudo apt install gcc-arm-none-eabi
54+
pip install pyocd
55+
```
56+
57+
## Building
58+
59+
```bash
60+
cd src/port/lpc54s018
61+
make
62+
```
63+
64+
This produces `app.elf` and `app.bin`.
65+
66+
### Checking Memory Usage
67+
68+
```bash
69+
make size
70+
```
71+
72+
```
73+
text data bss dec hex filename
74+
52000 1732 53076 106808 1a138 app.elf
75+
```
76+
77+
## Flashing
78+
79+
### SPIFI Flash Boot (Recommended)
80+
81+
```bash
82+
bash flash.sh
83+
```
84+
85+
The flash script programs `app.bin` to SPIFI flash at `0x10000000` via pyocd
86+
using the `lpc54s018j4met180` target pack, then resets the board so the
87+
boot ROM runs and jumps into our firmware. The boot ROM validates the
88+
enhanced boot header at offset `0x160` and the vector checksum at `0x1C`
89+
(both written by `fix_checksum.py`).
90+
91+
### SRAM-Loaded Build (Development)
92+
93+
For fast iteration without re-programming SPIFI flash:
94+
95+
```bash
96+
make ram # Build with target_ram.ld
97+
bash flash_ram.sh
98+
```
99+
100+
This loads the firmware into SRAM via pyocd and starts execution from
101+
`0x20000181`. UART may be unreliable in this mode because the boot ROM
102+
clock setup is bypassed; use SPIFI flash boot for verified UART operation.
103+
104+
## Notes
105+
106+
### LPC54S018 PRESETCTRL register polarity
107+
108+
The LPC54018/LPC54S018 PRESETCTRL registers use this convention (matches
109+
the NXP MCUXpresso SDK `fsl_reset.c`):
110+
111+
- Bit = **1** means peripheral is **in reset** (asserted)
112+
- Bit = **0** means peripheral is **out of reset** (released)
113+
114+
Therefore: `PRESETCTRLSET` (bit -> 1) **asserts** reset, and
115+
`PRESETCTRLCLR` (bit -> 0) **deasserts** reset.
116+
117+
### DHCP / Link Behavior
118+
119+
The PHY (LAN8742A on the LPCXpresso54S018M-EVK) is held in reset via GPIO
120+
P2.26 and needs ~167ms after release before its REF_CLK stabilizes and MDIO
121+
is reliable. The port waits 200ms then runs PHY auto-negotiation.
122+
123+
After ETH init the firmware prints PHY diagnostics:
124+
```
125+
PHY: addr=0 ID=0007:c130 BSR=786d autoneg=OK link=UP
126+
```
127+
- `ID` = `PHY_ID1:PHY_ID2`. LAN8742A reports `0007:c130`.
128+
- `BSR` = full Basic Status Register value (raw).
129+
- `autoneg`/`link` are decoded from BSR bit 5 / bit 2.
130+
131+
Before starting DHCP the firmware waits up to 5s for link UP. If the cable
132+
is plugged in later, DHCP DISCOVER is re-issued every 10s only when the PHY
133+
reports link UP — this avoids flooding the network with discovers when the
134+
cable is unplugged. After 30s total, it falls back to the static IP defined
135+
in `config.h`.
136+
137+
## Serial Console
138+
139+
Connect to the USB serial port at 115200 baud:
140+
141+
```bash
142+
screen /dev/ttyACM0 115200
143+
```
144+
145+
## Example Output
146+
147+
```
148+
=== wolfIP LPC54S018M-EVK ===
149+
PHY addr=0 link=UP
150+
Starting DHCP...
151+
Ready! ping <ip> / echo test | nc <ip> 7
152+
DHCP bound: 192.168.0.138
153+
```
154+
155+
## Network Configuration
156+
157+
### DHCP (Default)
158+
159+
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.
160+
161+
### Static IP
162+
163+
Set `WOLFIP_ENABLE_DHCP` to `0` in `config.h`:
164+
165+
```c
166+
#define WOLFIP_ENABLE_DHCP 0
167+
#define WOLFIP_IP "192.168.1.10"
168+
#define WOLFIP_NETMASK "255.255.255.0"
169+
#define WOLFIP_GW "192.168.1.1"
170+
```
171+
172+
## Testing TCP Echo Server
173+
174+
```bash
175+
# Test ICMP
176+
ping <device-ip>
177+
178+
# Test TCP echo (port 7)
179+
echo "Hello wolfIP!" | nc <device-ip> 7
180+
181+
# Interactive
182+
nc <device-ip> 7
183+
```
184+
185+
## Jumper Settings
186+
187+
| Jumper | Position | Function |
188+
|--------|----------|----------|
189+
| JP11 | 1-2 (default) | Ethernet TXD/RXD |
190+
| JP12 | 1-2 (default) | Ethernet TXD/RXD |
191+
| JP14 | 1-2 (EN) | ENET MDC |
192+
| JP15 | 1-2 (EN) | ENET MDIO |
193+
| JP5 | Open (default) | Link2 normal boot |
194+
195+
## Files
196+
197+
| File | Description |
198+
|------|-------------|
199+
| `main.c` | Application entry, wolfIP init, echo server |
200+
| `../lpc_enet/lpc_enet.c` | Ethernet MAC/DMA driver (shared) |
201+
| `../lpc_enet/lpc_enet.h` | Ethernet driver header (shared) |
202+
| `lpc54s018_eth.h` | Board-specific ENET parameters |
203+
| `startup.c` | Startup code and data initialization |
204+
| `ivt.c` | Interrupt vector table + SPIFI config |
205+
| `syscalls.c` | Newlib syscall stubs |
206+
| `target.ld` | Linker script (SPIFI flash boot - default) |
207+
| `target_ram.ld` | Linker script (RAM execution - dev only) |
208+
| `flash.sh` | Program SPIFI flash and reset (recommended) |
209+
| `flash_ram.sh` | Load firmware to SRAM and run (dev only, no UART) |
210+
| `config.h` | wolfIP stack configuration |
211+
| `fix_checksum.py` | LPC boot ROM vector checksum tool |
212+
| `Makefile` | Build system |
213+
214+
## Troubleshooting
215+
216+
### No Serial Output
217+
- Check J8 (USB Debug-Link) is connected, not J1
218+
- Check JP5 is open (not shunted)
219+
- Verify baud rate is 115200
220+
221+
### Ethernet Not Responding
222+
- Verify cable is in J4, check RJ45 link LEDs are lit
223+
- Check JP14 and JP15 are both in EN position
224+
- If using static IP, ensure host is on the same subnet
225+
226+
### pyocd Cannot Find Target
227+
- Use `-t lpc54608` as target type
228+
- Add udev rule: `SUBSYSTEM=="usb", ATTR{idVendor}=="1fc9", MODE="0666"`
229+
230+
## License
231+
232+
This code is part of wolfIP and is licensed under GPLv3. See the LICENSE file in the repository root for details.
233+
234+
Copyright (C) 2026 wolfSSL Inc.

0 commit comments

Comments
 (0)