Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .github/workflows/codespell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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,FRO
ignore_words_list: inh,inout,keypair,nd,parm,rcv,ser,tha,HSI,TE,UE,Synopsys,synopsys,FRO,EMAC,emac
9 changes: 9 additions & 0 deletions src/port/pic32mz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Build artifacts
*.o
app.elf
app.hex
app.map
# MDB / IPE transient files
mdb_flash.cmd
MPLABXLog.*
log.*
108 changes: 108 additions & 0 deletions src/port/pic32mz/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# wolfIP PIC32MZ EF port - build with Microchip XC32
#
# Copyright (C) 2026 wolfSSL Inc. Part of the wolfIP TCP/IP stack (GPLv3).
#
# Phase 0: clocks + UART2 console + heartbeat.
# Phase 1: wolfCrypt PIC32MZ hardware TRNG self-test.
# Phase 2: MDIO + LAN8740 PHY link bring-up.
# (Full Ethernet RX/TX driver + wolfIP core are added in later phases.)
#
# Usage:
# make # build app.hex
# make flash # program over the on-board debugger (v6.20 IPE)
# make clean
# Overridable:
# XC32_BIN= XC32 bin dir (default /opt/microchip/xc32/v5.10/bin)
# DFP= device family pack version dir
# DEVICE= 32MZ2048EFM144
# WOLFSSL_ROOT= path to wolfssl checkout (used from Phase 1 on)
# MDB= MPLAB X MDB CLI (default v6.20; v6.30 dropped the EF SK debugger)

XC32_BIN ?= /opt/microchip/xc32/v5.10/bin
DFP ?= /opt/microchip/mplabx/v6.30/packs/Microchip/PIC32MZ-EF_DFP/1.5.173
DEVICE ?= 32MZ2048EFM144

CC := $(XC32_BIN)/xc32-gcc
BIN2HEX := $(XC32_BIN)/xc32-bin2hex
SIZE := $(XC32_BIN)/xc32-size

# Flashing via MDB (Microchip DeBugger CLI). The headless ipecmd cannot resolve
# device packs ("Unable to locate DFP"), but MDB launches the full platform and
# resolves them like the IDE/IPE GUI does. Default tool is an MPLAB ICD 5 on the
# ICSP header; the EF Starter Kit's flaky on-board debugger (MDB tool type "sk",
# only in v6.20) proved unreliable over USB, so an external debugger is preferred.
MDB ?= /opt/microchip/mplabx/v6.30/mplab_platform/bin/mdb.sh
MDB_DEVICE ?= PIC32MZ2048EFM144
MDB_TOOL ?= icd5

ROOT := ../../..
# Default to a wolfssl checkout sitting beside the wolfip repo.
WOLFSSL_ROOT ?= $(ROOT)/../wolfssl

# -O1 is available in the free XC32 edition (-O2/-Os/-O3 need a PRO license).
CFLAGS := -mprocessor=$(DEVICE) -mdfp="$(DFP)"
CFLAGS += -O1 -g -Wall -Wextra -ffunction-sections -fdata-sections
CFLAGS += -I. -I$(ROOT) -I$(ROOT)/src
CFLAGS += -DWOLFSSL_USER_SETTINGS -I$(WOLFSSL_ROOT)
CFLAGS += $(EXTRA_CFLAGS)

# Heap for the Hash-DRBG (wc_InitRng allocates the DRBG state).
LDFLAGS := -mprocessor=$(DEVICE) -mdfp="$(DFP)"
LDFLAGS += -Wl,--defsym,_min_heap_size=0x8000
LDFLAGS += -Wl,--gc-sections -Wl,-Map=app.map,--cref

# Port + application sources (strict warnings)
APP_SRCS := device_config.c clock_init.c uart_console.c timebase.c \
wolf_compat.c rng_selftest.c pic32mz_eth.c phy_lan8740.c main.c
APP_OBJS := $(patsubst %.c,%.o,$(APP_SRCS))

# wolfcrypt sources for the RNG self-test (compiled with relaxed warnings).
# Pulled directly from the sibling wolfssl checkout, no copy.
WC_SRC := $(WOLFSSL_ROOT)/wolfcrypt/src
WC_NAMES := random sha256 hash wc_port logging memory error
WC_OBJS := $(addsuffix .o,$(addprefix wc_,$(WC_NAMES)))
CFLAGS_WC := -mprocessor=$(DEVICE) -mdfp="$(DFP)" -O1 -g \
-ffunction-sections -fdata-sections \
-DWOLFSSL_USER_SETTINGS -I. -I$(WOLFSSL_ROOT)

# wolfIP core stack (relaxed warnings; does not need wolfssl).
CFLAGS_CORE := -mprocessor=$(DEVICE) -mdfp="$(DFP)" -O1 -g \
-ffunction-sections -fdata-sections -I. -I$(ROOT) -I$(ROOT)/src

ALL_OBJS := $(APP_OBJS) wolfip.o $(WC_OBJS)

all: app.hex
@echo "Built PIC32MZ wolfIP port"
@$(SIZE) app.elf

app.elf: $(ALL_OBJS)
$(CC) $(CFLAGS) $(ALL_OBJS) $(LDFLAGS) -o $@

app.hex: app.elf
$(BIN2HEX) $<

%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@

wc_%.o: $(WC_SRC)/%.c
$(CC) $(CFLAGS_WC) -c $< -o $@

wolfip.o: $(ROOT)/src/wolfip.c
$(CC) $(CFLAGS_CORE) -c $< -o $@

# Program the hex over an MPLAB ICD 5 (on the ICSP/debug header) via MDB. Close
# the MPLAB X IPE/IDE GUI first so it isn't holding the tool. Command script:
# set programoptions.eraseb4program true -> full chip erase before program
# hwtool icd5 -p -> connect the ICD 5 for programming
# program <app.hex> / Reset -> program, then release from reset
# The trailing "Reset" is required: -p alone leaves the target held in reset.
# See the mplab-icd5 skill for connection/wiring troubleshooting.
flash: app.hex
@printf 'device %s\nset programoptions.eraseb4program true\nhwtool %s -p\nprogram %s\nReset\nquit\n' \
"$(MDB_DEVICE)" "$(MDB_TOOL)" "$(CURDIR)/app.hex" > mdb_flash.cmd
cd $(dir $(MDB)) && sh ./mdb.sh $(CURDIR)/mdb_flash.cmd

clean:
rm -f *.o app.elf app.hex app.map mdb_flash.cmd MPLABXLog.* log.*

.PHONY: all clean flash
81 changes: 81 additions & 0 deletions src/port/pic32mz/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# wolfIP PIC32MZ EF Port

Bare-metal [wolfIP](../../../README.md) port for the **Microchip PIC32MZ EF**
(PIC32MZ2048EFM144, MIPS32 M-class) with a **LAN8740** PHY over RMII. Built
with the Microchip XC32 toolchain; no MPLAB X project or Harmony framework
required. Also exercises wolfCrypt's built-in hardware TRNG
(`WOLFSSL_PIC32MZ_RNG`).

## Hardware

- **MCU:** PIC32MZ2048EFM144 (200 MHz, 2 MB flash, 512 KB RAM), EF Starter Kit.
- **PHY:** LAN8740 daughter board over RMII (PHY drives the 50 MHz reference clock).
- **Console:** UART2, `U2TX` on RPB14 / `U2RX` on RPG6, 115200 8N1.
- **Programmer:** MPLAB ICD 5 on the ICSP/debug header (see the mplab-icd5 skill).

## Layout

| File | Purpose |
|------|---------|
| `device_config.c` | DEVCFG config words: 200 MHz PLL (POSC EC 24 MHz), RMII, watchdog off. |
| `clock_init.c/.h` | Flash wait-states + prefetch. Reusable early bring-up (wolfBoot). |
| `cache.h` | MIPS KSEG0/KSEG1 + virtual/physical helpers for DMA memory. |
| `uart_console.c/.h` | UART2 console + XC32 `_mon_putc` `printf` retarget. |
| `timebase.c/.h` | 64-bit `millis()` from the CP0 core timer. |
| `pic32mz_eth.c/.h` | EMAC + RMII + MDIO bring-up, DMA descriptor rings, `poll`/`send`. |
| `phy_lan8740.c/.h` | MAC-agnostic clause-22 LAN8740 driver (scan, autoneg, link). |
| `rng_selftest.c/.h` | wolfCrypt hardware-TRNG self-test. |
| `user_settings.h` | wolfCrypt configuration (`WOLFSSL_MICROCHIP_PIC32MZ`). |
| `config.h` | wolfIP profile (MTU, socket counts, DHCP). |
| `main.c` | TRNG self-test, DHCP, and a TCP echo / throughput server. |

## Build

Requires XC32 (>= v5.10) and the PIC32MZ-EF DFP, plus a wolfssl checkout beside
the wolfip repo (override with `WOLFSSL_ROOT=`).

```sh
make # -> app.hex
make SPEED_TEST=1 EXTRA_CFLAGS=-DSPEED_TEST # throughput server on port 9
make flash # program over MPLAB ICD 5 (MDB)
make clean
```

Overridable: `XC32_BIN`, `DFP`, `DEVICE`, `WOLFSSL_ROOT`, `MDB`, `MDB_TOOL`.

## Test

Console at 115200 8N1. On boot the firmware prints the banner, the RNG
self-test result, the resolved PHY link, and (once bound) the DHCP address:

```
=== wolfIP PIC32MZ EF port ===
[RNG] self-test: PASS
Ethernet init (LAN8740 over RMII)...
DHCP bound: 10.0.4.x
TCP service listening on port 7
```

Then, from a host on the same network:

```sh
ping <ip>
echo hello | nc <ip> 7 # echo (default build)
dd if=/dev/zero bs=1460 count=700 | nc <ip> 9 # RX throughput (SPEED_TEST build)
nc <ip> 9 </dev/null | pv >/dev/null # TX throughput (SPEED_TEST build)
```

## Notes

- DMA descriptors/buffers use XC32 `__attribute__((coherent))` (uncached), so
no cache maintenance is needed; the EMAC is given physical addresses.
- The Ethernet module needs PBCLK5 enabled and `PMD6.ETHMD` cleared before any
EMAC register access — `pic32mz_emac_mii_init()` does this first.
- The PIC32 Ethernet DMA descriptor is 4 words (16 bytes): header, buffer
address, and two words the DMA writes back. The received frame length comes
from the status word (`status & 0xFFFF`), not the header.
- The echo server holds transfers in the per-socket buffers
(`RXBUF_SIZE`/`TXBUF_SIZE`), so a single burst larger than that will stall
the simple echo callback; raise the buffers or use the throughput server
(`-DSPEED_TEST`) for large streams.
- Build with `EXTRA_CFLAGS=-DPIC32_ETH_TRACE` to trace the EMAC bring-up.
38 changes: 38 additions & 0 deletions src/port/pic32mz/board.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* board.h
*
* Board constants for the PIC32MZ EF Starter Kit (DM320007) + LAN8740 PHY DB.
*
* 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 PIC32MZ_BOARD_H
#define PIC32MZ_BOARD_H

/* Clock tree (set by DEVCFG config words at reset; see device_config.c) */
#define SYS_CLK_FREQ 200000000ul /* SYSCLK from SPLL */
#define PBCLK2_FREQ 100000000ul /* peripheral bus 2 (UART) = SYSCLK/2 */
#define PBCLK5_FREQ 100000000ul /* peripheral bus 5 (EMAC) = SYSCLK/2 */

/* Console UART: U2TX on RPB14, U2RX on RPG6 (external MCP2221 USB-UART) */
#define CONSOLE_BAUD 115200u

/* On-board LEDs LED1/LED2/LED3 on RH0/RH1/RH2 (active high) */
#define LED_MASK 0x0007u
#define LED_HEARTBEAT 0x0001u /* LED1 = RH0 */

#endif /* PIC32MZ_BOARD_H */
51 changes: 51 additions & 0 deletions src/port/pic32mz/cache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* cache.h
*
* MIPS KSEG segment helpers for DMA-coherent access on PIC32MZ.
*
* 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 PIC32MZ_CACHE_H
#define PIC32MZ_CACHE_H

#include <stdint.h>

/* The PIC32MZ Ethernet controller (EMAC) is not cache-coherent and programs
* its descriptor base/buffer pointers with PHYSICAL addresses. The simplest
* coherent scheme on MIPS is to access all descriptor rings and DMA buffers
* through KSEG1 (uncached) virtual aliases and hand the EMAC the physical
* address. This avoids per-operation cache clean/invalidate entirely.
*
* Equivalent to XC32 <sys/kmem.h> KVA0_TO_KVA1 / KVA_TO_PA / PA_TO_KVA1, but
* kept self-contained so the early bare-metal layer can be lifted into
* wolfBoot without pulling in the XC32 system headers.
*/

/* Cached KSEG0 virtual address -> uncached KSEG1 virtual address */
#define PIC32_KVA0_TO_KVA1(v) (((uint32_t)(v)) | 0x20000000u)

/* Any KSEG0/KSEG1 virtual address -> physical address (for the EMAC) */
#define PIC32_KVA_TO_PA(v) (((uint32_t)(v)) & 0x1FFFFFFFu)

/* Physical address -> uncached KSEG1 virtual address */
#define PIC32_PA_TO_KVA1(pa) (((uint32_t)(pa)) | 0xA0000000u)

/* Pointer helper: uncached view of a normally-allocated object */
#define PIC32_UNCACHED(p) ((void *)PIC32_KVA0_TO_KVA1(p))

#endif /* PIC32MZ_CACHE_H */
37 changes: 37 additions & 0 deletions src/port/pic32mz/clock_init.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* clock_init.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 <xc.h>
#include "clock_init.h"

void clock_init(void)
{
/* Flash wait-states + prefetch for 200 MHz SYSCLK.
* PFMWS = 2 wait-states is required above ~134 MHz on PIC32MZ EF.
* PREFEN = 3 enables predictive prefetch for cacheable and
* non-cacheable regions. PRECON is not lock-protected. */
PRECONbits.PFMWS = 2;
PRECONbits.PREFEN = 3;

/* Peripheral buses PBCLK2 (UART) and PBCLK5 (EMAC) remain at their
* reset default of SYSCLK/2 = 100 MHz, which is what this port targets.
* The L1 cache and KSEG0 coherency are enabled by the XC32 reset
* startup code, so nothing is done here for v1. */
}
30 changes: 30 additions & 0 deletions src/port/pic32mz/clock_init.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* clock_init.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 PIC32MZ_CLOCK_INIT_H
#define PIC32MZ_CLOCK_INIT_H

/* Configure flash wait-states / prefetch for 200 MHz operation.
* The PLL itself is brought up at reset from the DEVCFG config words
* (FNOSC=SPLL), so by the time main() runs SYSCLK is already 200 MHz.
* Bare-metal reusable (intended to be lifted into a future wolfBoot port). */
void clock_init(void);

#endif /* PIC32MZ_CLOCK_INIT_H */
Loading
Loading