Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
637 changes: 637 additions & 0 deletions src/port/stm32f4/stm32f4_eth.c

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions src/port/stm32f4/stm32f4_eth.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* stm32f4_eth.h
*
* Shared Ethernet driver for STM32F4xx (DWC GMAC legacy v1 descriptors).
* Used by STM32F407/F417/F427/F437/F439/F469/F479 wolfIP ports.
*
* 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_STM32F4_ETH_H
#define WOLFIP_STM32F4_ETH_H

#include <stdint.h>
#include "wolfip.h"

/* Millisecond tick counter, maintained by SysTick_Handler in the port's
* main.c. Reads on Cortex-M4 are non-atomic (LDRD is interruptible), so
* always read it through stm32f4_hal_time_ms() below to avoid tearing. */
extern volatile uint64_t HAL_time_ms;

/* Tear-free read of HAL_time_ms. Lamport double-read: in the worst case a
* SysTick fires between the two halves and a/b disagree, so re-read. */
static inline uint64_t stm32f4_hal_time_ms(void)
{
uint64_t a, b;
do {
a = HAL_time_ms;
b = HAL_time_ms;
} while (a != b);
return b;
}

/* Initialize the STM32F4 Ethernet MAC + DMA + PHY and hook the driver into
* the wolfIP link-layer device. PHY auto-negotiation is run synchronously
* with a 5-second timeout. Returns 0 on success, -2 if the PHY is reachable
* but link did not come up (MAC/DMA still left running so a late link comes
* up naturally), or -1 on a fatal init error.
*
* The caller must already have configured the RCC (ETHMAC/ETHMACTX/ETHMACRX
* clocks), SYSCFG_PMC.MII_RMII_SEL, and the RMII GPIO pinmux before calling.
*/
int stm32f4_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac);

void stm32f4_eth_get_stats(uint32_t *polls, uint32_t *pkts, uint32_t *tx_pkts,
uint32_t *tx_errs);
uint32_t stm32f4_eth_get_dma_status(void);
void stm32f4_eth_get_mac_diag(uint32_t *mac_cfg, uint32_t *mac_dbg);

#endif /* WOLFIP_STM32F4_ETH_H */
111 changes: 111 additions & 0 deletions src/port/stm32f439/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
CC ?= arm-none-eabi-gcc
OBJCOPY ?= arm-none-eabi-objcopy
SIZE ?= arm-none-eabi-size

ROOT := ../../..

# Cortex-M4F with FPU (hardware float, single-precision). Both NUCLEO-F439ZI
# and STM32439I-EVAL ship STM32F437/F439 silicon with the same FPU.
CFLAGS := -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
CFLAGS += -Os -ffreestanding -fdata-sections -ffunction-sections
CFLAGS += -g -ggdb -Wall -Wextra -Werror

# Include paths
CFLAGS += -I. -I$(ROOT) -I$(ROOT)/src -I$(ROOT)/src/port/stm32f4

# Board selection. Both supported boards use STM32F437/F439 silicon (same
# DWC GMAC + same register layout) but differ in PHY addr, UART pinmux,
# HSE source, and RMII TXD1 pin. Set BOARD on the make command line:
# BOARD=nucleo_f439zi NUCLEO-F439ZI dev board (LAN8742A, USART3, HSE 8MHz bypass)
# BOARD=stm32439i_eval STM32439I-EVAL eval board (DP83848, UART4, HSE 25MHz crystal)
BOARD ?= nucleo_f439zi

ifeq ($(BOARD),nucleo_f439zi)
CFLAGS += -DBOARD_NUCLEO_F439ZI -DSTM32F4_ETH_PHY_ADDR=0
else ifeq ($(BOARD),stm32439i_eval)
CFLAGS += -DBOARD_STM32439I_EVAL -DSTM32F4_ETH_PHY_ADDR=1
else
$(error Unknown BOARD '$(BOARD)' - use nucleo_f439zi or stm32439i_eval)
endif

EXTRA_CFLAGS ?=
CFLAGS += $(EXTRA_CFLAGS)

# Relaxed warnings for wolfIP core (some unused-param noise on small builds)
CFLAGS_EXT := $(filter-out -Werror,$(CFLAGS))
CFLAGS_EXT += -Wno-unused-variable -Wno-unused-function -Wno-unused-parameter

LDFLAGS := -nostdlib -T target.ld -Wl,-gc-sections

# Application sources (strict warnings)
APP_SRCS := startup.c ivt.c syscalls.c main.c \
$(ROOT)/src/port/stm32f4/stm32f4_eth.c
APP_OBJS := startup.o ivt.o syscalls.o main.o stm32f4_eth.o

# wolfIP core (relaxed warnings)
WOLFIP_OBJ := wolfip.o

ALL_OBJS := $(APP_OBJS) $(WOLFIP_OBJ)

all: app.bin
@echo "Built STM32F439 wolfIP port (BOARD=$(BOARD))"
@$(SIZE) app.elf

app.elf: $(ALL_OBJS) target.ld
$(CC) $(CFLAGS) $(ALL_OBJS) $(LDFLAGS) \
-Wl,--start-group -lc -lm -lgcc -lnosys -Wl,--end-group -o $@

app.bin: app.elf
$(OBJCOPY) -O binary $< $@

# Local sources -- strict warnings
startup.o: startup.c
$(CC) $(CFLAGS) -c $< -o $@
ivt.o: ivt.c
$(CC) $(CFLAGS) -c $< -o $@
syscalls.o: syscalls.c
$(CC) $(CFLAGS) -c $< -o $@
main.o: main.c
$(CC) $(CFLAGS) -c $< -o $@

# Shared F4 ETH driver (strict warnings)
stm32f4_eth.o: $(ROOT)/src/port/stm32f4/stm32f4_eth.c
$(CC) $(CFLAGS) -c $< -o $@

# wolfIP core (relaxed warnings)
$(WOLFIP_OBJ): $(ROOT)/src/wolfip.c
$(CC) $(CFLAGS_EXT) -c $< -o $@

clean:
rm -f *.o app.elf app.bin

# Show memory usage
size: app.elf
@echo "=== Memory Usage ==="
@$(SIZE) app.elf
@echo ""
@echo "Flash usage: $$($(SIZE) app.elf | awk 'NR==2{printf "%.2f%% (%d / %d bytes)", ($$1+$$2)*100/2097152, $$1+$$2, 2097152}')"
@echo "RAM usage (static): $$($(SIZE) app.elf | awk 'NR==2{printf "%.2f%% (%d / %d bytes)", ($$2+$$3)*100/196608, $$2+$$3, 196608}')"

.PHONY: all clean size help

help:
@echo "STM32F439 wolfIP Build System (supports F437/F439 silicon)"
@echo ""
@echo "Usage: make [target] [options]"
@echo ""
@echo "Targets:"
@echo " all Build app.bin (default)"
@echo " clean Remove build artifacts"
@echo " size Show memory usage statistics"
@echo " help Show this help"
@echo ""
@echo "Options:"
@echo " BOARD=nucleo_f439zi NUCLEO-F439ZI (default; LAN8742A, USART3, HSE 8MHz bypass)"
@echo " BOARD=stm32439i_eval STM32439I-EVAL (DP83848, UART4, HSE 25MHz crystal)"
@echo " EXTRA_CFLAGS=-DDEBUG_ETH Verbose ETH driver diagnostics"
@echo " CC= C compiler (default: arm-none-eabi-gcc)"
@echo ""
@echo "Testing:"
@echo " ping <ip> # ICMP ping"
@echo " echo 'hello' | nc <ip> 7 # TCP echo"
130 changes: 130 additions & 0 deletions src/port/stm32f439/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# wolfIP on STM32F437/F439

Bare-metal wolfIP port for STMicro STM32F437/F439 silicon. Supports two
boards from one source tree, selected at compile time via `BOARD=`:

| Board | MCU | PHY | VCP UART | HSE | RMII TXD1 |
|------------------|--------------|-----------|------------------------|----------------------------|-----------|
| `nucleo_f439zi` | STM32F439ZIT | LAN8742A | USART3 (PD8/PD9, AF7) | 8 MHz BYPASS (ST-LINK MCO) | PB13 |
| `stm32439i_eval` | STM32F437IIH | DP83848 | UART4 (PC10/PC11, AF8) | 25 MHz crystal | PG14 |

Brings up Ethernet (RMII), runs DHCP, and exposes a TCP echo server on
port 7 plus ICMP ping.

**Status:** `nucleo_f439zi` has been booted end-to-end on hardware (no
cable -- clocks/UART/MAC/MDIO/PHY-ID verified; link-up + DHCP path not
yet exercised). `stm32439i_eval` is compile-tested only; the build
artifact has not yet been validated on the STM32439I-EVAL hardware.

## Hardware

- MCU: Cortex-M4F @ 168 MHz, 2 MB flash, 256 KB RAM (192 KB main SRAM +
64 KB CCM; this port keeps everything in main SRAM since the GMAC DMA
cannot reach CCM).
- Clock: HSE -> PLL -> SYSCLK 168 MHz, HCLK 168 MHz, PCLK1 42 MHz,
PCLK2 84 MHz. PLL pre-divider differs per board (PLLM=8 for NUCLEO
HSE_BYPASS 8 MHz, PLLM=25 for EVAL 25 MHz crystal); the post-divider
chain and SYSCLK target are identical.
- Ethernet: Synopsys DWC GMAC (legacy 16-byte descriptors, ATDS=0) in
RMII mode. Driver in `src/port/stm32f4/`.

## RMII pin map (shared, with the per-board TXD1 noted)

| Signal | Pin | AF |
|------------------|--------------------------------|-----|
| ETH_RMII_REF_CLK | PA1 | 11 |
| ETH_MDIO | PA2 | 11 |
| ETH_RMII_CRS_DV | PA7 | 11 |
| ETH_MDC | PC1 | 11 |
| ETH_RMII_RXD0 | PC4 | 11 |
| ETH_RMII_RXD1 | PC5 | 11 |
| ETH_RMII_TX_EN | PG11 | 11 |
| ETH_RMII_TXD0 | PG13 | 11 |
| ETH_RMII_TXD1 | PB13 (NUCLEO) / PG14 (EVAL) | 11 |

## Build

```
make clean
make # default: BOARD=nucleo_f439zi
make BOARD=stm32439i_eval # for the STM32439I-EVAL
```

Outputs `app.bin` (raw binary for ST-LINK) and `app.elf` (with debug
info for GDB / addr2line).

Verbose ETH bring-up diagnostics:

```
make clean
make EXTRA_CFLAGS=-DDEBUG_ETH
```

Memory usage report:

```
make size
```

## Flash

Via ST-LINK CLI:

```
st-flash write app.bin 0x08000000
```

If multiple ST-LINKs are connected, target a specific probe by serial:

```
st-flash --serial <ST-LINK-serial> --reset write app.bin 0x08000000
```

Or via STM32CubeProgrammer with the onboard ST-LINK/V2-1.

## Expected UART output

```
=== wolfIP STM32F437/F439 (NUCLEO-F439ZI) ===
Build: <date> <time>
SYSCLK = 168000000 Hz, HCLK = 168000000 Hz, ...
Initializing Ethernet (RMII + LAN8742A)...
PHY ID at addr 0: 0x0007 / 0xC131
PHY link: UP, AN: complete
Starting DHCP...
DHCP bound:
IP: <ip>
Mask: <mask>
GW: <gw>
TCP echo server on port 7
Ready! Test with:
ping <ip>
echo 'hello' | nc <ip> 7
```

Every 10 s the main loop prints a diagnostic line with packet counters
and MAC/DMA register snapshots (useful when the link isn't coming up):

```
[30] rx=0 tx=3/343030 maccr=0x0000848C macdbg=0x01120000 dmasr=0x00260400
```

For the EVAL build, the banner shows `STM32439I-EVAL` and `DP83848` /
PHY addr 1 instead.

## Test

From a host on the same subnet as the device:

```
ping <ip>
echo 'hello' | nc <ip> 7
```

## Out of scope (milestone 1)

- TLS / HTTPS / SSH / MQTT (the H563 port shows the wolfSSL hook-in
pattern; can be ported here once basic eth works).
- wolfBoot / TFTP partition update.
- FreeRTOS.
- IRQ-driven RX (this port uses poll mode like the VA416xx milestone-1).
71 changes: 71 additions & 0 deletions src/port/stm32f439/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* config.h
*
* wolfIP build-time configuration for the STM32F437 port (STM32439I-EVAL).
*
* 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

/* STM32F437IIHx has 256KB RAM (192KB main SRAM + 64KB CCM). We have
* plenty of room compared to the VA416xx port. Keep the socket count
* modest for milestone-1 bring-up. */
#define MAX_TCPSOCKETS 2 /* listen + 1 client */
#define MAX_UDPSOCKETS 1 /* DHCP */
#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.12.37"
#define WOLFIP_NETMASK "255.255.255.0"
#define WOLFIP_GW "192.168.12.1"
#define WOLFIP_STATIC_DNS_IP "8.8.8.8"

#if WOLFIP_ENABLE_DHCP
#define DHCP
#endif

#endif /* WOLF_CONFIG_H */
Loading
Loading