diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 224a9f1d..f848a0f2 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,FRO + ignore_words_list: inh,inout,keypair,nd,parm,rcv,ser,tha,HSI,TE,UE,Synopsys,synopsys,FRO,EMAC,emac diff --git a/src/port/pic32mz/.gitignore b/src/port/pic32mz/.gitignore new file mode 100644 index 00000000..155f9d51 --- /dev/null +++ b/src/port/pic32mz/.gitignore @@ -0,0 +1,9 @@ +# Build artifacts +*.o +app.elf +app.hex +app.map +# MDB / IPE transient files +mdb_flash.cmd +MPLABXLog.* +log.* diff --git a/src/port/pic32mz/Makefile b/src/port/pic32mz/Makefile new file mode 100644 index 00000000..83b356cc --- /dev/null +++ b/src/port/pic32mz/Makefile @@ -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 / 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 diff --git a/src/port/pic32mz/README.md b/src/port/pic32mz/README.md new file mode 100644 index 00000000..822c2409 --- /dev/null +++ b/src/port/pic32mz/README.md @@ -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 +echo hello | nc 7 # echo (default build) +dd if=/dev/zero bs=1460 count=700 | nc 9 # RX throughput (SPEED_TEST build) +nc 9 /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. diff --git a/src/port/pic32mz/board.h b/src/port/pic32mz/board.h new file mode 100644 index 00000000..a0c56d49 --- /dev/null +++ b/src/port/pic32mz/board.h @@ -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 */ diff --git a/src/port/pic32mz/cache.h b/src/port/pic32mz/cache.h new file mode 100644 index 00000000..586d19ab --- /dev/null +++ b/src/port/pic32mz/cache.h @@ -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 + +/* 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 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 */ diff --git a/src/port/pic32mz/clock_init.c b/src/port/pic32mz/clock_init.c new file mode 100644 index 00000000..4a5e32c8 --- /dev/null +++ b/src/port/pic32mz/clock_init.c @@ -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 +#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. */ +} diff --git a/src/port/pic32mz/clock_init.h b/src/port/pic32mz/clock_init.h new file mode 100644 index 00000000..3e26aa61 --- /dev/null +++ b/src/port/pic32mz/clock_init.h @@ -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 */ diff --git a/src/port/pic32mz/config.h b/src/port/pic32mz/config.h new file mode 100644 index 00000000..169c51ff --- /dev/null +++ b/src/port/pic32mz/config.h @@ -0,0 +1,67 @@ +/* config.h + * + * wolfIP configuration for the PIC32MZ EF 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 + */ +#ifndef WOLF_CONFIG_H +#define WOLF_CONFIG_H + +#ifndef CONFIG_IPFILTER +#define CONFIG_IPFILTER 0 +#endif + +#define ETHERNET +#define LINK_MTU 1536 + +/* PIC32MZ EF has 512 KB SRAM, so the profile is roomier than the tiny ports. */ +#define MAX_TCPSOCKETS 4 +#define MAX_UDPSOCKETS 2 /* DHCP + 1 app */ +#define MAX_ICMPSOCKETS 1 +#define RXBUF_SIZE (4 * LINK_MTU) +#define TXBUF_SIZE (4 * LINK_MTU) + +#define MAX_NEIGHBORS 8 +#define WOLFIP_ARP_PENDING_MAX 4 + +#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.11" +#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 */ diff --git a/src/port/pic32mz/device_config.c b/src/port/pic32mz/device_config.c new file mode 100644 index 00000000..97e6e882 --- /dev/null +++ b/src/port/pic32mz/device_config.c @@ -0,0 +1,92 @@ +/* device_config.c + * + * PIC32MZ2048EFM144 device configuration words (DEVCFG0-3). + * + * 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 + */ + +/* Config-word settings for the PIC32MZ EF Starter Kit (DM320007). + * + * Clock: POSC = 24 MHz external clock (EC). SPLL multiplies to 200 MHz SYSCLK: + * 24 MHz / FPLLIDIV(3) = 8 MHz -> FPLLMULT(50) = 400 MHz -> FPLLODIV(2) = 200 MHz. + * Peripheral buses default to SYSCLK/2 = 100 MHz (PBCLK2 UART, PBCLK5 EMAC). + * + * Values mirror the known-good Harmony-derived settings in + * wolfssl/mplabx/PIC32MZ-serial.h, EXCEPT FMIIEN is OFF here: this port drives + * the LAN8740 PHY daughter board over RMII, not MII. + */ + +/*** DEVCFG0 ***/ +#pragma config DEBUG = OFF +#pragma config JTAGEN = OFF +#pragma config ICESEL = ICS_PGx2 +#pragma config TRCEN = OFF +#pragma config BOOTISA = MIPS32 +#pragma config FECCCON = OFF_UNLOCKED +#pragma config FSLEEP = OFF +#pragma config DBGPER = PG_ALL +#pragma config SMCLR = MCLR_NORM +#pragma config SOSCGAIN = GAIN_2X +#pragma config SOSCBOOST = ON +#pragma config POSCGAIN = GAIN_2X +#pragma config POSCBOOST = ON +#pragma config EJTAGBEN = NORMAL +#pragma config CP = OFF + +/*** DEVCFG1 ***/ +#pragma config FNOSC = SPLL +#pragma config DMTINTV = WIN_127_128 +#pragma config FSOSCEN = OFF +#pragma config IESO = OFF +#pragma config POSCMOD = EC +#pragma config OSCIOFNC = OFF +#pragma config FCKSM = CSECME +#pragma config WDTPS = PS1048576 +#pragma config WDTSPGM = STOP +#pragma config FWDTEN = OFF +#pragma config WINDIS = NORMAL +#pragma config FWDTWINSZ = WINSZ_25 +#pragma config DMTCNT = DMT31 +#pragma config FDMTEN = OFF + +/*** DEVCFG2 ***/ +#pragma config FPLLIDIV = DIV_3 +#pragma config FPLLRNG = RANGE_5_10_MHZ +#pragma config FPLLICLK = PLL_POSC +#pragma config FPLLMULT = MUL_50 +#pragma config FPLLODIV = DIV_2 +#pragma config UPLLFSEL = FREQ_24MHZ + +/*** DEVCFG3 ***/ +#pragma config USERID = 0xffff +#pragma config FMIIEN = OFF /* RMII (LAN8740 PHY daughter board) */ +/* FETHIO selects the default (ON) vs alternate (OFF) Ethernet I/O pins; it + * also moves the RMII reference-clock input (EREFCLK: RB4 default / RJ11 + * alternate). Set to the board's routing for the 50 MHz PHY REFCLK. */ +#pragma config FETHIO = ON /* default Ethernet I/O pin set */ +#pragma config PGL1WAY = ON +#pragma config PMDL1WAY = ON +#pragma config IOL1WAY = ON +#pragma config FUSBIDIO = ON + +/*** BF1SEQ0 ***/ +#pragma config TSEQ = 0x0000 +#pragma config CSEQ = 0xffff + +#include diff --git a/src/port/pic32mz/main.c b/src/port/pic32mz/main.c new file mode 100644 index 00000000..0c82d827 --- /dev/null +++ b/src/port/pic32mz/main.c @@ -0,0 +1,260 @@ +/* main.c + * + * wolfIP PIC32MZ EF port: hardware TRNG self-test, DHCP, and a TCP echo / + * throughput server over the LAN8740. + * + * 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 "config.h" +#include "wolfip.h" +#include "board.h" +#include "clock_init.h" +#include "uart_console.h" +#include "timebase.h" +#include "rng_selftest.h" +#include "pic32mz_eth.h" + +#define DHCP_TIMEOUT_MS 30000U /* wait this long before static IP */ + +static struct wolfIP *IPStack; +static uint8_t app_buf[1024]; + +/* wolfIP entropy: xorshift seeded from the CP0 cycle counter and remixed with + * it each call. Adequate for ISN/port/xid selection; the crypto-grade TRNG is + * exercised separately by rng_selftest(). */ +uint32_t wolfIP_getrandom(void) +{ + static uint32_t s; + + if (s == 0) + s = _CP0_GET_COUNT() | 1u; + s ^= _CP0_GET_COUNT(); + s ^= s << 13; + s ^= s >> 17; + s ^= s << 5; + return s; +} + +static void print_ip(const char *label, ip4 ip) +{ + printf("%s%u.%u.%u.%u\r\n", label, + (unsigned)((ip >> 24) & 0xFF), (unsigned)((ip >> 16) & 0xFF), + (unsigned)((ip >> 8) & 0xFF), (unsigned)(ip & 0xFF)); +} + +#ifdef SPEED_TEST + +/* Throughput service on port 9: discard incoming (RX test) and stream data + * (TX test), measured together. + * RX: dd if=/dev/zero bs=1460 count=700 | nc 9 + * TX: nc 9 /dev/null */ +#define APP_PORT 9 +static int listen_fd = -1; +static int client_fd = -1; +static uint32_t rx_bytes, tx_bytes; +static uint64_t start_ms; + +static void app_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, app_cb, s); + rx_bytes = 0; + tx_bytes = 0; + start_ms = millis(); + } + return; + } + if (fd != client_fd) + return; + + if (event & CB_EVENT_READABLE) { + ret = wolfIP_sock_recvfrom(s, client_fd, app_buf, sizeof(app_buf), + 0, NULL, NULL); + if (ret > 0) + rx_bytes += (uint32_t)ret; + } + if (event & CB_EVENT_WRITABLE) { + ret = wolfIP_sock_send(s, client_fd, app_buf, sizeof(app_buf), 0); + if (ret > 0) + tx_bytes += (uint32_t)ret; + } + if (event & CB_EVENT_CLOSED) { + uint32_t ms = (uint32_t)(millis() - start_ms); + if (ms == 0) + ms = 1; + printf("Speed: %lu ms RX %lu B (~%lu B/s) TX %lu B (~%lu B/s)\r\n", + (unsigned long)ms, (unsigned long)rx_bytes, + (unsigned long)((uint64_t)rx_bytes * 1000U / ms), + (unsigned long)tx_bytes, + (unsigned long)((uint64_t)tx_bytes * 1000U / ms)); + wolfIP_sock_close(s, client_fd); + client_fd = -1; + } +} + +#else /* echo server on port 7 */ + +#define APP_PORT 7 +static int listen_fd = -1; +static int client_fd = -1; + +static void app_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, app_cb, s); + return; + } + if (fd != client_fd) + return; + + if (event & CB_EVENT_READABLE) { + ret = wolfIP_sock_recvfrom(s, client_fd, app_buf, sizeof(app_buf), + 0, NULL, NULL); + if (ret > 0) + (void)wolfIP_sock_sendto(s, client_fd, app_buf, (uint32_t)ret, + 0, NULL, 0); + else if (ret == 0) + event |= CB_EVENT_CLOSED; + } + if (event & CB_EVENT_CLOSED) { + wolfIP_sock_close(s, client_fd); + client_fd = -1; + } +} + +#endif /* SPEED_TEST */ + +int main(void) +{ + struct wolfIP_ll_dev *ll; + struct wolfIP_sockaddr_in addr; + uint64_t now, next_beat = 0; + uint32_t tick = 0; + int eth_ret; + int dhcp_done = 0; + uint64_t dhcp_start = 0; + + clock_init(); + uart_init(); + ANSELHCLR = LED_MASK; + TRISHCLR = LED_MASK; + LATHCLR = LED_MASK; + + printf("\r\n=== wolfIP PIC32MZ EF port ===\r\n"); + printf("Device : PIC32MZ2048EFM144 SYSCLK %lu Hz\r\n", + (unsigned long)SYS_CLK_FREQ); + printf("Build : %s %s\r\n", __DATE__, __TIME__); + + (void)rng_selftest(); + + wolfIP_init_static(&IPStack); + ll = wolfIP_getdev(IPStack); + printf("\r\nEthernet init (LAN8740 over RMII)...\r\n"); + eth_ret = pic32mz_eth_init(ll, NULL); + if (eth_ret == -2) + printf(" PHY link down at startup (check cable) - continuing\r\n"); + else if (eth_ret < 0) + printf(" ERROR: eth init failed (%d)\r\n", eth_ret); + printf(" MAC %02X:%02X:%02X:%02X:%02X:%02X\r\n", + ll->mac[0], ll->mac[1], ll->mac[2], + ll->mac[3], ll->mac[4], ll->mac[5]); +#ifdef PIC32_ETH_TRACE + pic32mz_eth_diag(); +#endif + + (void)wolfIP_poll(IPStack, millis()); /* prime last_tick */ +#ifdef DHCP + printf("Starting DHCP...\r\n"); + (void)dhcp_client_init(IPStack); + dhcp_start = millis(); +#else + wolfIP_ipconfig_set(IPStack, atoip4(WOLFIP_IP), atoip4(WOLFIP_NETMASK), + atoip4(WOLFIP_GW)); + print_ip("Static IP: ", atoip4(WOLFIP_IP)); +#endif + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = 0; + addr.sin_port = ee16(APP_PORT); + listen_fd = wolfIP_sock_socket(IPStack, AF_INET, IPSTACK_SOCK_STREAM, 0); + wolfIP_register_callback(IPStack, listen_fd, app_cb, IPStack); + (void)wolfIP_sock_bind(IPStack, listen_fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + (void)wolfIP_sock_listen(IPStack, listen_fd, 1); + printf("TCP service listening on port %d\r\n", APP_PORT); + + for (;;) { + now = millis(); + (void)wolfIP_poll(IPStack, now); + +#ifdef DHCP + if (!dhcp_done) { + ip4 ip = 0, nm = 0, gw = 0; + if (dhcp_bound(IPStack)) { + wolfIP_ipconfig_get(IPStack, &ip, &nm, &gw); + print_ip("DHCP bound: ", ip); + dhcp_done = 1; + } + else if ((now - dhcp_start) > DHCP_TIMEOUT_MS) { + wolfIP_ipconfig_set(IPStack, atoip4(WOLFIP_IP), + atoip4(WOLFIP_NETMASK), atoip4(WOLFIP_GW)); + print_ip("DHCP timeout, static IP: ", atoip4(WOLFIP_IP)); + dhcp_done = 1; + } + } +#endif + + if (now >= next_beat) { + next_beat = now + 1000; + tick++; + LATHINV = LED_HEARTBEAT; + if ((tick % 10u) == 0u) { + uint32_t rx = 0, tx = 0; + ip4 ip = 0, nm = 0, gw = 0; + pic32mz_eth_stats(&rx, &tx); + wolfIP_ipconfig_get(IPStack, &ip, &nm, &gw); + printf("[t=%lus] rx=%lu tx=%lu ip=%u.%u.%u.%u\r\n", + (unsigned long)(now / 1000U), + (unsigned long)rx, (unsigned long)tx, + (unsigned)((ip >> 24) & 0xFF), + (unsigned)((ip >> 16) & 0xFF), + (unsigned)((ip >> 8) & 0xFF), (unsigned)(ip & 0xFF)); +#ifdef PIC32_ETH_TRACE + pic32mz_eth_diag(); +#endif + } + } + } + /* not reached */ +} diff --git a/src/port/pic32mz/phy_lan8740.c b/src/port/pic32mz/phy_lan8740.c new file mode 100644 index 00000000..4ab88e67 --- /dev/null +++ b/src/port/pic32mz/phy_lan8740.c @@ -0,0 +1,181 @@ +/* phy_lan8740.c + * + * Microchip LAN8740 (clause-22 MDIO) PHY driver for the PIC32MZ wolfIP 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 "phy_lan8740.h" +#include "timebase.h" + +#ifdef PIC32_ETH_TRACE +#include +#define PHY_TRACE(s) do { printf("[PHY] " s "\r\n"); } while (0) +#else +#define PHY_TRACE(s) do { } while (0) +#endif + +/* Standard clause-22 register addresses. */ +#define REG_BMCR 0x00 /* Basic control */ +#define REG_BMSR 0x01 /* Basic status */ +#define REG_PHYID1 0x02 +#define REG_PHYID2 0x03 +#define REG_ANAR 0x04 /* Auto-neg advertisement */ +#define REG_SCSR 0x1F /* LAN8740 Special Control/Status */ + +#define BMCR_RESET 0x8000 +#define BMCR_ANEN 0x1000 +#define BMCR_ANRESTART 0x0200 + +#define BMSR_ANCOMPLETE 0x0020 +#define BMSR_LINK 0x0004 + +/* Advertise 100/10 full and half duplex, IEEE 802.3 selector. */ +#define ANAR_DEFAULT 0x01E1 + +/* LAN8740 OUI high word. */ +#define LAN8740_ID1 0x0007 + +/* SCSR autonegotiation-done and resolved speed/duplex (HCDSPEED, bits [4:2]). */ +#define SCSR_AUTODONE 0x1000 +#define SCSR_SPD_MASK 0x001C +#define SCSR_SPD_10HD 0x0004 +#define SCSR_SPD_100HD 0x0008 +#define SCSR_SPD_10FD 0x0014 +#define SCSR_SPD_100FD 0x0018 + +static int phy_scan(mdio_read_fn rd, struct phy_link *out) +{ + uint16_t id1; + uint16_t id2; + int addr; + int candidate; + + candidate = -1; + for (addr = 0; addr < 32; addr++) { + if (rd((uint8_t)addr, REG_PHYID1, &id1) != 0) + continue; + if (id1 == 0xFFFF || id1 == 0x0000) + continue; + if (rd((uint8_t)addr, REG_PHYID2, &id2) != 0) + continue; + /* Prefer a LAN8740, but remember any responding PHY as a fallback. */ + if (id1 == LAN8740_ID1) { + out->found = 1; + out->addr = (uint8_t)addr; + out->id1 = id1; + out->id2 = id2; + return 0; + } + if (candidate < 0) { + candidate = addr; + out->id1 = id1; + out->id2 = id2; + } + } + + if (candidate >= 0) { + out->found = 1; + out->addr = (uint8_t)candidate; + return 0; + } + return -1; +} + +int phy_lan8740_bringup(mdio_read_fn rd, mdio_write_fn wr, + uint32_t link_up_timeout_ms, struct phy_link *out) +{ + uint16_t reg; + uint16_t spd; + uint32_t deadline; + + if (rd == NULL || wr == NULL || out == NULL) + return -1; + + out->found = 0; + out->addr = 0; + out->id1 = 0; + out->id2 = 0; + out->link_up = 0; + out->speed_100 = 0; + out->full_duplex = 0; + + PHY_TRACE("scan: reading PHY IDs over MDIO"); + if (phy_scan(rd, out) != 0) { + PHY_TRACE("scan: no PHY responded"); + return -1; + } + PHY_TRACE("scan: PHY found, resetting"); + + /* Software reset and wait for it to self-clear. */ + if (wr(out->addr, REG_BMCR, BMCR_RESET) != 0) + return -1; + deadline = (uint32_t)millis() + 1000u; + do { + if (rd(out->addr, REG_BMCR, ®) != 0) + return -1; + } while ((reg & BMCR_RESET) && ((uint32_t)millis() < deadline)); + if (reg & BMCR_RESET) + return -1; + + /* Advertise all speeds/duplex and restart auto-negotiation. */ + if (wr(out->addr, REG_ANAR, ANAR_DEFAULT) != 0) + return -1; + if (wr(out->addr, REG_BMCR, BMCR_ANEN | BMCR_ANRESTART) != 0) + return -1; + + /* Wait for link. BMSR latches link-low, so read it twice. */ + deadline = (uint32_t)millis() + link_up_timeout_ms; + for (;;) { + if (rd(out->addr, REG_BMSR, ®) != 0) + return -1; + if (rd(out->addr, REG_BMSR, ®) != 0) + return -1; + if (reg & BMSR_LINK) + break; + if ((uint32_t)millis() >= deadline) + return -1; + } + out->link_up = 1; + + /* Resolved speed/duplex from the LAN8740 Special Control/Status reg. */ + if (rd(out->addr, REG_SCSR, &spd) != 0) + return -1; + switch (spd & SCSR_SPD_MASK) { + case SCSR_SPD_100FD: + out->speed_100 = 1; + out->full_duplex = 1; + break; + case SCSR_SPD_100HD: + out->speed_100 = 1; + out->full_duplex = 0; + break; + case SCSR_SPD_10FD: + out->speed_100 = 0; + out->full_duplex = 1; + break; + case SCSR_SPD_10HD: + default: + out->speed_100 = 0; + out->full_duplex = 0; + break; + } + + return 0; +} diff --git a/src/port/pic32mz/phy_lan8740.h b/src/port/pic32mz/phy_lan8740.h new file mode 100644 index 00000000..83292bfe --- /dev/null +++ b/src/port/pic32mz/phy_lan8740.h @@ -0,0 +1,48 @@ +/* phy_lan8740.h + * + * Microchip LAN8740 (clause-22 MDIO) PHY driver for the PIC32MZ wolfIP 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 + */ +#ifndef PHY_LAN8740_H +#define PHY_LAN8740_H + +#include + +/* MDIO accessors supplied by the MAC driver (see pic32mz_eth.h). */ +typedef int (*mdio_read_fn)(uint8_t phy_addr, uint8_t reg, uint16_t *val); +typedef int (*mdio_write_fn)(uint8_t phy_addr, uint8_t reg, uint16_t val); + +struct phy_link { + uint8_t found; /* 1 if a PHY responded on the bus */ + uint8_t addr; /* MDIO address of the PHY */ + uint16_t id1; /* PHYID1 (LAN8740 OUI high = 0x0007) */ + uint16_t id2; /* PHYID2 */ + uint8_t link_up; /* 1 if link is up */ + uint8_t speed_100; /* 1 = 100 Mbps, 0 = 10 Mbps */ + uint8_t full_duplex; /* 1 = full duplex, 0 = half */ +}; + +/* Scan for the PHY, reset it, advertise all modes, run auto-negotiation and + * wait for link. Fills *out. Returns 0 on link up, negative on error/timeout. + * link_up_timeout_ms bounds the link wait. */ +int phy_lan8740_bringup(mdio_read_fn rd, mdio_write_fn wr, + uint32_t link_up_timeout_ms, struct phy_link *out); + +#endif /* PHY_LAN8740_H */ diff --git a/src/port/pic32mz/pic32mz_eth.c b/src/port/pic32mz/pic32mz_eth.c new file mode 100644 index 00000000..5b75f9e9 --- /dev/null +++ b/src/port/pic32mz/pic32mz_eth.c @@ -0,0 +1,408 @@ +/* pic32mz_eth.c + * + * PIC32MZ Ethernet controller (EMAC) driver for wolfIP. + * + * 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 "pic32mz_eth.h" +#include "phy_lan8740.h" +#include "cache.h" + +/* Bring-up tracing: build with EXTRA_CFLAGS=-DPIC32_ETH_TRACE to emit a marker + * before each EMAC register-access group. If the console stops after a given + * marker, the following register access is stalling the CPU bus (typically a + * disabled/unclocked module). */ +#ifdef PIC32_ETH_TRACE +#include +#define ETH_TRACE(s) do { printf("[EMAC] " s "\r\n"); } while (0) +#else +#define ETH_TRACE(s) do { } while (0) +#endif + +/* MII management clock divisor (EMAC1MCFG.CLKSEL). + * 0b1010 = host clock / 50. At SYSCLK = 200 MHz this gives MDC ~= 4 MHz, the + * lowest the divider offers; LAN8740 tolerates it. */ +#define MII_CLKSEL_DIV50 0x0Au + +/* Bounded spin used to wait for MII management to finish a transfer. */ +#define MII_TIMEOUT 1000000u + +static void short_delay(void) +{ + volatile int i; + + /* The MIIMBUSY flag takes a few host-clock cycles to assert after a + * command is issued; give it time before polling. */ + for (i = 0; i < 64; i++) { + } +} + +int pic32mz_emac_mii_init(void) +{ + volatile int i; + uint32_t timeout; + + /* The Ethernet module is clocked by PBCLK5 and gated by PMD6.ETHMD. If + * PBCLK5 is off or the module is PMD-disabled, the first ETH/EMAC register + * access stalls the CPU bus forever (and wedges the ICSP debugger). Enable + * both before touching any EMAC register. These are behind the system + * unlock (SYSKEY); PMDLOCK is left unlocked afterwards to avoid the + * PMDL1WAY one-way trap. */ + ETH_TRACE("z0: enable PBCLK5"); + if (PB5DIVbits.ON == 0) { + SYSKEY = 0x00000000; + SYSKEY = 0xAA996655; + SYSKEY = 0x556699AA; + PB5DIVbits.ON = 1; + SYSKEY = 0x33333333; + } + ETH_TRACE("z1: enable ETH module (PMD6.ETHMD=0)"); + if (PMD6bits.ETHMD != 0) { + SYSKEY = 0x00000000; + SYSKEY = 0xAA996655; + SYSKEY = 0x556699AA; + CFGCONbits.PMDLOCK = 0; + PMD6bits.ETHMD = 0; + SYSKEY = 0x33333333; + } + + /* Disable the Ethernet controller and wait for it to go idle. + * If marker "a" prints but "b" never does, the ETHCON1 access below is + * still stalling (module clock/enable issue). */ + ETH_TRACE("a: ETHCON1 off"); + ETHCON1CLR = _ETHCON1_ON_MASK; + timeout = MII_TIMEOUT; + while ((ETHSTAT & _ETHSTAT_ETHBUSY_MASK) && (timeout != 0)) + timeout--; + if (timeout == 0) { + /* Controller never went idle; the EMAC state is unreliable, so bail + * rather than proceed into MDIO with a stuck module. */ + ETH_TRACE("a: ETHBUSY stuck - abort"); + return -1; + } + + /* Enable the module (required to access the EMAC1 register block). */ + ETH_TRACE("b: ETHCON1 on"); + ETHCON1CLR = _ETHCON1_TXRTS_MASK | _ETHCON1_RXEN_MASK; + ETHCON1SET = _ETHCON1_ON_MASK; + + /* Reset the MAC: assert all soft resets, then release. */ + ETH_TRACE("c: EMAC1CFG1 reset"); + EMAC1CFG1 = _EMAC1CFG1_SOFTRESET_MASK | _EMAC1CFG1_SIMRESET_MASK | + _EMAC1CFG1_RESETRMCS_MASK | _EMAC1CFG1_RESETRFUN_MASK | + _EMAC1CFG1_RESETTMCS_MASK | _EMAC1CFG1_RESETTFUN_MASK; + for (i = 0; i < 1000; i++) { + } + EMAC1CFG1 = 0; + + /* Reset the RMII logic. */ + ETH_TRACE("d: EMAC1SUPP RMII reset"); + EMAC1SUPPSET = _EMAC1SUPP_RESETRMII_MASK; + for (i = 0; i < 1000; i++) { + } + EMAC1SUPPCLR = _EMAC1SUPP_RESETRMII_MASK; + + /* Reset MII management, then set the MDC clock divisor. */ + ETH_TRACE("e: EMAC1MCFG"); + EMAC1MCFGSET = _EMAC1MCFG_RESETMGMT_MASK; + for (i = 0; i < 100; i++) { + } + EMAC1MCFGCLR = _EMAC1MCFG_RESETMGMT_MASK; + EMAC1MCFGbits.CLKSEL = MII_CLKSEL_DIV50; + ETH_TRACE("f: mii init done"); + return 0; +} + +int pic32mz_mdio_read(uint8_t phy_addr, uint8_t reg, uint16_t *val) +{ + uint32_t timeout; + + if (val == NULL) + return -1; + + EMAC1MADR = ((uint32_t)phy_addr << _EMAC1MADR_PHYADDR_POSITION) | + ((uint32_t)reg << _EMAC1MADR_REGADDR_POSITION); + + /* Issue a single read command. */ + EMAC1MCMDSET = _EMAC1MCMD_READ_MASK; + short_delay(); + + timeout = MII_TIMEOUT; + while ((EMAC1MIND & _EMAC1MIND_MIIMBUSY_MASK) && (timeout != 0)) + timeout--; + + EMAC1MCMDCLR = _EMAC1MCMD_READ_MASK; + + if (timeout == 0) + return -1; + + *val = (uint16_t)(EMAC1MRDD & _EMAC1MRDD_MRDD_MASK); + return 0; +} + +int pic32mz_mdio_write(uint8_t phy_addr, uint8_t reg, uint16_t val) +{ + uint32_t timeout; + + EMAC1MADR = ((uint32_t)phy_addr << _EMAC1MADR_PHYADDR_POSITION) | + ((uint32_t)reg << _EMAC1MADR_REGADDR_POSITION); + + /* Writing the data register initiates the management write. */ + EMAC1MWTD = (uint32_t)val; + short_delay(); + + timeout = MII_TIMEOUT; + while ((EMAC1MIND & _EMAC1MIND_MIIMBUSY_MASK) && (timeout != 0)) + timeout--; + + if (timeout == 0) + return -1; + return 0; +} + +/* ==== Ethernet DMA: descriptor rings, poll/send, full MAC bring-up ======== */ + +/* PIC32 Ethernet descriptor header bits (FRM section 35). */ +#define ED_EOWN (1u << 7) /* 1 = owned by the Ethernet DMA */ +#define ED_NPV (1u << 8) /* next-descriptor pointer valid */ +#define ED_COUNT_POS 16 /* byte count field, bits 16..26 */ +#define ED_COUNT_MASK 0x07FF0000u +#define ED_EOP (1u << 30) +#define ED_SOP (1u << 31) + +/* Four-word (16-byte) hardware descriptor (PIC32 FRM / LiteBSD layout). The + * DMA writes ctl/status back into words 2-3 on completion, so those words MUST + * be part of the descriptor -- an 8-byte descriptor lets the write-back corrupt + * the next descriptor. With NPV=0 the DMA advances to the next CONTIGUOUS + * descriptor (linear); only the last data descriptor sets NPV=1 and its NEXT_ED + * (the hdr of the trailing "link" descriptor after it) points back to desc 0. */ +typedef struct { + volatile uint32_t hdr; /* EOWN/NPV/EOP/SOP + TX byte count */ + volatile uint32_t addr; /* physical buffer address */ + volatile uint32_t ctl; /* control (TX) / filter status (RX) */ + volatile uint32_t status; /* DMA status; RX size = status & 0xFFFF */ +} eth_dcpt_t; + +#define RX_DESC_COUNT 8 +#define TX_DESC_COUNT 8 + +/* DMA memory: XC32 'coherent' places it uncached, so no cache maintenance is + * needed; the EMAC gets physical addresses (KVA_TO_PA). One extra "link" + * descriptor per ring holds the wrap pointer (see above). */ +static eth_dcpt_t rx_desc[RX_DESC_COUNT + 1] __attribute__((coherent, aligned(8))); +static eth_dcpt_t tx_desc[TX_DESC_COUNT + 1] __attribute__((coherent, aligned(8))); +static uint8_t rx_buf[RX_DESC_COUNT][LINK_MTU] __attribute__((coherent, aligned(16))); +static uint8_t tx_buf[TX_DESC_COUNT][LINK_MTU] __attribute__((coherent, aligned(16))); + +static int rx_idx, tx_idx; +static uint32_t rx_count, tx_count; + +#ifdef PIC32_ETH_TRACE +/* First bytes of the most recent RX/TX frame, for the diagnostic dump. */ +static uint8_t dbg_rx[32], dbg_tx[32]; +static uint32_t dbg_rx_len, dbg_tx_len; +#endif + +static void eth_rings_init(void) +{ + int i; + + for (i = 0; i < RX_DESC_COUNT; i++) { + rx_desc[i].addr = PIC32_KVA_TO_PA(rx_buf[i]); + /* NPV=0 -> linear advance; last data descriptor wraps via the link. */ + rx_desc[i].hdr = ED_EOWN | + ((i == RX_DESC_COUNT - 1) ? ED_NPV : 0u); + } + rx_desc[RX_DESC_COUNT].hdr = PIC32_KVA_TO_PA(&rx_desc[0]); /* wrap link */ + rx_idx = 0; + + for (i = 0; i < TX_DESC_COUNT; i++) { + tx_desc[i].addr = PIC32_KVA_TO_PA(tx_buf[i]); + tx_desc[i].hdr = (i == TX_DESC_COUNT - 1) ? ED_NPV : 0u; /* CPU-owned */ + } + tx_desc[TX_DESC_COUNT].hdr = PIC32_KVA_TO_PA(&tx_desc[0]); /* wrap link */ + tx_idx = 0; +} + +/* wolfIP RX poll: one frame (>0), 0 if none. */ +static int eth_poll(struct wolfIP_ll_dev *ll, void *frame, uint32_t len) +{ + eth_dcpt_t *d = &rx_desc[rx_idx]; + uint32_t n; + + (void)ll; + if (d->hdr & ED_EOWN) + return 0; /* still owned by the DMA */ + + n = d->status & 0xFFFFu; /* RX frame size is in status */ + if (n > 4u) + n -= 4u; /* drop the 4-byte FCS */ + if (n > len) + n = len; + memcpy(frame, rx_buf[rx_idx], n); +#ifdef PIC32_ETH_TRACE + memcpy(dbg_rx, frame, (n > 32u) ? 32u : n); + dbg_rx_len = n; +#endif + + /* Re-arm: EOWN back to the DMA, keeping the wrap flag on the last one. */ + d->hdr = ED_EOWN | ((rx_idx == RX_DESC_COUNT - 1) ? ED_NPV : 0u); + ETHCON1SET = _ETHCON1_BUFCDEC_MASK; /* a buffer became free */ + rx_idx = (rx_idx + 1) % RX_DESC_COUNT; + rx_count++; + return (int)n; +} + +/* wolfIP TX send: bytes queued, or <0 if the TX ring is full. */ +static int eth_send(struct wolfIP_ll_dev *ll, void *frame, uint32_t len) +{ + eth_dcpt_t *d = &tx_desc[tx_idx]; + + (void)ll; + if ((d->hdr & ED_EOWN) || (len > LINK_MTU)) + return -1; /* ring slot still in flight */ + + memcpy(tx_buf[tx_idx], frame, len); +#ifdef PIC32_ETH_TRACE + memcpy(dbg_tx, frame, (len > 32u) ? 32u : len); + dbg_tx_len = len; +#endif + d->addr = PIC32_KVA_TO_PA(tx_buf[tx_idx]); + d->hdr = ED_SOP | ED_EOP | + ((tx_idx == TX_DESC_COUNT - 1) ? ED_NPV : 0u) | + ((len << ED_COUNT_POS) & ED_COUNT_MASK) | ED_EOWN; + tx_idx = (tx_idx + 1) % TX_DESC_COUNT; + ETHCON1SET = _ETHCON1_TXRTS_MASK; /* resume TX from the ring */ + tx_count++; + return (int)len; +} + +void pic32mz_eth_stats(uint32_t *rx, uint32_t *tx) +{ + if (rx != NULL) + *rx = rx_count; + if (tx != NULL) + *tx = tx_count; +} + +#ifdef PIC32_ETH_TRACE +#include +void pic32mz_eth_diag(void) +{ + printf("[ETH] CON1=%08lX STAT=%08lX IRQ=%08lX RXFC=%08lX CON2=%08lX\r\n", + (unsigned long)ETHCON1, (unsigned long)ETHSTAT, + (unsigned long)ETHIRQ, (unsigned long)ETHRXFC, + (unsigned long)ETHCON2); + printf("[ETH] CFG1=%08lX CFG2=%08lX SUPP=%08lX RXST=%08lX TXST=%08lX\r\n", + (unsigned long)EMAC1CFG1, (unsigned long)EMAC1CFG2, + (unsigned long)EMAC1SUPP, (unsigned long)ETHRXST, + (unsigned long)ETHTXST); + printf("[ETH] rx_idx=%d rxhdr: %08lX %08lX %08lX %08lX link=%08lX rx=%lu tx=%lu\r\n", + rx_idx, + (unsigned long)rx_desc[0].hdr, (unsigned long)rx_desc[1].hdr, + (unsigned long)rx_desc[2].hdr, (unsigned long)rx_desc[3].hdr, + (unsigned long)rx_desc[RX_DESC_COUNT].hdr, + (unsigned long)rx_count, (unsigned long)tx_count); + { + uint32_t i; + printf("[ETH] rx[%lu]:", (unsigned long)dbg_rx_len); + for (i = 0; (i < dbg_rx_len) && (i < 32u); i++) + printf(" %02X", dbg_rx[i]); + printf("\r\n[ETH] tx[%lu]:", (unsigned long)dbg_tx_len); + for (i = 0; (i < dbg_tx_len) && (i < 32u); i++) + printf(" %02X", dbg_tx[i]); + printf("\r\n"); + } +} +#endif + +static void eth_default_mac(uint8_t mac[6]) +{ + mac[0] = 0x02; /* locally administered */ + mac[1] = 0x11; + mac[2] = 0xAA; + mac[3] = 0xBB; + mac[4] = 0x32; /* "32" for PIC32 */ + mac[5] = 0x4D; +} + +int pic32mz_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac) +{ + struct phy_link link; + uint8_t addr[6]; + int ret; + + if (mac != NULL) + memcpy(addr, mac, 6); + else + eth_default_mac(addr); + + /* RMII pins that share an analog function must be forced digital + * (ANSEL=0). On the 144-pin default (FETHIO=ON) pinout the 50 MHz + * reference clock EREFCLK is on RJ11, which is also AN4/C1INB: left + * analog, the MAC data path gets no clock, so TX stalls mid-frame and RX + * never clocks in (MDIO/link still work on their own MDC clock). */ + ANSELJCLR = (1u << 11) | (1u << 8) | (1u << 9); /* RJ11 EREFCLK, RJ8/RJ9 ETXD0/1 */ + ANSELHCLR = (1u << 4) | (1u << 5); /* RH4/RH5 ERXERR/ERXD1 */ + + ret = pic32mz_emac_mii_init(); + if (ret != 0) + return ret; + + /* LAN8740 link (fills speed/duplex even if the link is currently down). */ + ret = phy_lan8740_bringup(pic32mz_mdio_read, pic32mz_mdio_write, 5000u, + &link); + + /* MAC: auto pad + CRC, duplex from the PHY. */ + EMAC1CFG2 = _EMAC1CFG2_PADENABLE_MASK | _EMAC1CFG2_CRCENABLE_MASK | + (link.full_duplex ? _EMAC1CFG2_FULLDPLX_MASK : 0u); + EMAC1IPGT = link.full_duplex ? 0x15 : 0x12; + EMAC1IPGR = 0x0C12; + EMAC1CLRT = 0x370F; + EMAC1MAXF = LINK_MTU; + EMAC1SUPPbits.SPEEDRMII = link.speed_100 ? 1 : 0; + + /* Station address: EMAC1SA2/1/0 hold MAC bytes 0..5, low byte first. */ + EMAC1SA2 = ((uint32_t)addr[1] << 8) | addr[0]; + EMAC1SA1 = ((uint32_t)addr[3] << 8) | addr[2]; + EMAC1SA0 = ((uint32_t)addr[5] << 8) | addr[4]; + + EMAC1CFG1SET = _EMAC1CFG1_RXENABLE_MASK; /* MAC receive on */ + + eth_rings_init(); + ETHCON2 = (LINK_MTU / 16) << 4; /* RX buffer size (16B units) */ + ETHRXST = PIC32_KVA_TO_PA(&rx_desc[0]); + ETHTXST = PIC32_KVA_TO_PA(&tx_desc[0]); + ETHRXFC = _ETHRXFC_UCEN_MASK | _ETHRXFC_BCEN_MASK | _ETHRXFC_CRCOKEN_MASK; + ETHCON1SET = _ETHCON1_RXEN_MASK; /* controller RX on */ + + memcpy(ll->mac, addr, 6); + strncpy(ll->ifname, "eth0", sizeof(ll->ifname) - 1); + ll->non_ethernet = 0; + ll->mtu = LINK_MTU; + ll->poll = eth_poll; + ll->send = eth_send; + ll->priv = NULL; + + return (ret == 0) ? 0 : -2; +} diff --git a/src/port/pic32mz/pic32mz_eth.h b/src/port/pic32mz/pic32mz_eth.h new file mode 100644 index 00000000..c13c9e91 --- /dev/null +++ b/src/port/pic32mz/pic32mz_eth.h @@ -0,0 +1,54 @@ +/* pic32mz_eth.h + * + * PIC32MZ Ethernet controller (EMAC) driver for wolfIP. + * + * 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_ETH_H +#define PIC32MZ_ETH_H + +#include +#include "wolfip.h" + +/* Full Ethernet init: EMAC/RMII/MDIO bring-up, LAN8740 link, MAC config, DMA + * descriptor rings, and wiring of ll->poll / ll->send / ll->mac. Pass a 6-byte + * MAC, or NULL for a locally-administered default. Returns 0 on link up, -2 if + * the MAC/DMA are up but the PHY link is down (traffic flows once link rises), + * negative on a fatal init error. */ +int pic32mz_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac); + +/* Frames received / transmitted since init (for diagnostics). */ +void pic32mz_eth_stats(uint32_t *rx, uint32_t *tx); + +#ifdef PIC32_ETH_TRACE +void pic32mz_eth_diag(void); /* dump EMAC/controller registers + descriptors */ +#endif + +/* Phase 2: bring the Ethernet module, RMII and MII-management block out of + * reset and program the MDC clock divisor. Must be called before any MDIO + * access. Returns 0 on success, negative if the controller never went idle + * (ETHBUSY stuck). */ +int pic32mz_emac_mii_init(void); + +/* Clause-22 MDIO primitives. Return 0 on success, negative on timeout. + * Signatures match the mdio_read_fn / mdio_write_fn types in phy_lan8740.h. */ +int pic32mz_mdio_read(uint8_t phy_addr, uint8_t reg, uint16_t *val); +int pic32mz_mdio_write(uint8_t phy_addr, uint8_t reg, uint16_t val); + +#endif /* PIC32MZ_ETH_H */ diff --git a/src/port/pic32mz/rng_selftest.c b/src/port/pic32mz/rng_selftest.c new file mode 100644 index 00000000..2c365e1a --- /dev/null +++ b/src/port/pic32mz/rng_selftest.c @@ -0,0 +1,103 @@ +/* rng_selftest.c + * + * Standalone wolfCrypt RNG self-test for the PIC32MZ EF hardware TRNG. + * + * 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 "rng_selftest.h" + +#define RNG_BLOCK_LEN 32 + +static void print_hex(const char *label, const unsigned char *p, int n) +{ + int i; + + printf("%s", label); + for (i = 0; i < n; i++) + printf("%02X", p[i]); + printf("\r\n"); +} + +int rng_selftest(void) +{ + WC_RNG rng; + unsigned char a[RNG_BLOCK_LEN]; + unsigned char b[RNG_BLOCK_LEN]; + int ret; + int i; + int all_zero; + int all_ff; + int identical; + + printf("\r\n[RNG] WOLFSSL_PIC32MZ_RNG hardware TRNG self-test\r\n"); + + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("[RNG] wc_InitRng failed: %d (%s)\r\n", + ret, wc_GetErrorString(ret)); + return -1; + } + + ret = wc_RNG_GenerateBlock(&rng, a, sizeof(a)); + if (ret != 0) { + printf("[RNG] GenerateBlock A failed: %d (%s)\r\n", + ret, wc_GetErrorString(ret)); + (void)wc_FreeRng(&rng); + return -1; + } + + ret = wc_RNG_GenerateBlock(&rng, b, sizeof(b)); + if (ret != 0) { + printf("[RNG] GenerateBlock B failed: %d (%s)\r\n", + ret, wc_GetErrorString(ret)); + (void)wc_FreeRng(&rng); + return -1; + } + + (void)wc_FreeRng(&rng); + + print_hex("[RNG] block A: ", a, RNG_BLOCK_LEN); + print_hex("[RNG] block B: ", b, RNG_BLOCK_LEN); + + /* Coarse sanity checks: blocks must not be trivial or identical. */ + all_zero = 1; + all_ff = 1; + identical = 1; + for (i = 0; i < RNG_BLOCK_LEN; i++) { + if (a[i] != 0x00) + all_zero = 0; + if (a[i] != 0xFF) + all_ff = 0; + if (a[i] != b[i]) + identical = 0; + } + + if (all_zero || all_ff || identical) { + printf("[RNG] self-test: FAIL (all_zero=%d all_ff=%d identical=%d)\r\n", + all_zero, all_ff, identical); + return -1; + } + + printf("[RNG] self-test: PASS\r\n"); + return 0; +} diff --git a/src/port/pic32mz/rng_selftest.h b/src/port/pic32mz/rng_selftest.h new file mode 100644 index 00000000..8ed3afb0 --- /dev/null +++ b/src/port/pic32mz/rng_selftest.h @@ -0,0 +1,29 @@ +/* rng_selftest.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_RNG_SELFTEST_H +#define PIC32MZ_RNG_SELFTEST_H + +/* Exercise wolfCrypt's WOLFSSL_PIC32MZ_RNG hardware TRNG path: seed a DRBG, + * generate two blocks, and sanity-check the output. Prints results over the + * console UART. Returns 0 on pass, negative on failure. */ +int rng_selftest(void); + +#endif /* PIC32MZ_RNG_SELFTEST_H */ diff --git a/src/port/pic32mz/timebase.c b/src/port/pic32mz/timebase.c new file mode 100644 index 00000000..07efab28 --- /dev/null +++ b/src/port/pic32mz/timebase.c @@ -0,0 +1,58 @@ +/* timebase.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#include +#include "timebase.h" +#include "board.h" + +/* The CP0 Count register increments at SYSCLK/2. It is 32-bit and wraps + * roughly every 42 s at 100 MHz, so it is extended to 64 bits by tracking + * wraps. millis() must be polled at least once per wrap period; the wolfIP + * main loop calls it continuously, so this is trivially satisfied. + * Single-threaded; not interrupt-safe. */ +#define COUNT_HZ (SYS_CLK_FREQ / 2u) +#define COUNT_PER_MS (COUNT_HZ / 1000u) + +static uint32_t s_last_count; +static uint64_t s_accum; + +static uint64_t cp0_count64(void) +{ + uint32_t c = _CP0_GET_COUNT(); + + if (c < s_last_count) + s_accum += 0x100000000ull; + s_last_count = c; + return s_accum + (uint64_t)c; +} + +uint64_t millis(void) +{ + return cp0_count64() / COUNT_PER_MS; +} + +void delay_ms(uint32_t ms) +{ + uint64_t target = millis() + (uint64_t)ms; + + while (millis() < target) { + /* busy wait */ + } +} diff --git a/src/port/pic32mz/timebase.h b/src/port/pic32mz/timebase.h new file mode 100644 index 00000000..e26a53f7 --- /dev/null +++ b/src/port/pic32mz/timebase.h @@ -0,0 +1,31 @@ +/* timebase.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_TIMEBASE_H +#define PIC32MZ_TIMEBASE_H + +#include + +/* Free-running millisecond counter derived from the CP0 Count register. + * This is the time source passed to wolfIP_poll(). */ +uint64_t millis(void); +void delay_ms(uint32_t ms); + +#endif /* PIC32MZ_TIMEBASE_H */ diff --git a/src/port/pic32mz/uart_console.c b/src/port/pic32mz/uart_console.c new file mode 100644 index 00000000..3dca0942 --- /dev/null +++ b/src/port/pic32mz/uart_console.c @@ -0,0 +1,84 @@ +/* uart_console.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#include +#include +#include "uart_console.h" +#include "board.h" + +/* BRGH = 0 -> 16x oversampling. BRG = PBCLK2 / (16 * baud) - 1. */ +#define UART_BRG ((PBCLK2_FREQ / (16u * CONSOLE_BAUD)) - 1u) + +void uart_init(void) +{ + /* Make the two pins digital and set direction. */ + ANSELBCLR = (1u << 14); /* RB14 digital */ + ANSELGCLR = (1u << 6); /* RG6 digital */ + TRISBCLR = (1u << 14); /* RB14 output (U2TX) */ + TRISGSET = (1u << 6); /* RG6 input (U2RX) */ + + /* Peripheral Pin Select: RPB14 -> U2TX, U2RX <- RPG6. */ + RPB14R = 0x02; /* output function 2 = U2TX */ + U2RXR = 0x01; /* input selection 1 = RPG6 */ + + U2BRG = (uint32_t)UART_BRG; + U2STA = 0; + U2MODE = (1u << 15); /* ON, 8N1, BRGH=0, no flow control */ + U2STAbits.URXEN = 1; + U2STAbits.UTXEN = 1; +} + +void uart_putc(char c) +{ + while (U2STAbits.UTXBF) { + /* wait for room in the TX FIFO */ + } + U2TXREG = (uint8_t)c; +} + +void uart_write(const char *buf, unsigned int len) +{ + unsigned int i; + + if (buf == NULL) + return; + for (i = 0; i < len; i++) + uart_putc(buf[i]); +} + +int uart_rx_ready(void) +{ + return (int)U2STAbits.URXDA; +} + +int uart_getc(void) +{ + if (U2STAbits.URXDA == 0) + return -1; + return (int)(U2RXREG & 0xFF); +} + +/* XC32 retargets stdout (printf) through _mon_putc(). Map LF -> CRLF. */ +void _mon_putc(char c) +{ + if (c == '\n') + uart_putc('\r'); + uart_putc(c); +} diff --git a/src/port/pic32mz/uart_console.h b/src/port/pic32mz/uart_console.h new file mode 100644 index 00000000..1f668b1f --- /dev/null +++ b/src/port/pic32mz/uart_console.h @@ -0,0 +1,33 @@ +/* uart_console.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_UART_CONSOLE_H +#define PIC32MZ_UART_CONSOLE_H + +/* UART2 on RPB14 (U2TX) / RPG6 (U2RX) at 115200 8N1, via the external + * MCP2221 USB-UART bridge. Also retargets XC32 stdout (printf) to this UART + * through _mon_putc(). */ +void uart_init(void); +void uart_putc(char c); +void uart_write(const char *buf, unsigned int len); +int uart_rx_ready(void); +int uart_getc(void); /* returns byte, or -1 if none available */ + +#endif /* PIC32MZ_UART_CONSOLE_H */ diff --git a/src/port/pic32mz/user_settings.h b/src/port/pic32mz/user_settings.h new file mode 100644 index 00000000..0c965583 --- /dev/null +++ b/src/port/pic32mz/user_settings.h @@ -0,0 +1,83 @@ +/* user_settings.h + * + * wolfCrypt configuration for the wolfIP PIC32MZ EF 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 + */ +#ifndef WOLFSSL_USER_SETTINGS_H +#define WOLFSSL_USER_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* for size_t */ + +/* ---- Platform -------------------------------------------------------- */ +#define WOLFSSL_GENERAL_ALIGNMENT 4 +#define SINGLE_THREADED +#define WOLFSSL_SMALL_STACK + +#define MICROCHIP_PIC32 +#define WOLFSSL_MICROCHIP_PIC32MZ /* auto-enables WOLFSSL_PIC32MZ_RNG */ + +/* This milestone exercises ONLY the hardware TRNG. Keep the PIC32MZ crypto + * engine (AES/hash acceleration) disabled so SHA-256 (used by the Hash-DRBG) + * runs in software and we don't need the pic32mz-crypt.c engine driver yet. */ +#define NO_PIC32MZ_CRYPT +#define NO_PIC32MZ_HASH + +/* ---- RNG ------------------------------------------------------------- */ +#define HAVE_HASHDRBG /* SHA-256 based DRBG seeded by the TRNG */ + +/* ---- Trim algorithms not needed for the RNG self-test ---------------- */ +#define NO_RSA +#define NO_DH +#define NO_DSA +#define NO_AES +#define NO_DES3 +#define NO_RC4 +#define NO_MD4 +#define NO_MD5 +#define NO_SHA /* SHA-1 not needed; SHA-256 stays on */ +#define NO_PWDBASED +#define NO_PSK +#define NO_OLD_TLS + +/* ---- Environment ----------------------------------------------------- */ +#define NO_FILESYSTEM +#define NO_WRITEV +#define NO_MAIN_DRIVER +#define NO_DEV_RANDOM /* no /dev/random on bare metal */ +#define NO_ASN_TIME /* no RTC */ +#define WOLFSSL_NO_SOCK + +/* WOLFSSL_MICROCHIP_PIC32MZ makes settings.h define WOLFSSL_HAVE_MIN/MAX, + * which tells wolfcrypt that min()/max() are supplied externally (normally by + * the Microchip framework). This bare-metal port supplies them in + * wolf_compat.c; declare prototypes here so the wolfcrypt sources see them + * (word32 == unsigned int on this 32-bit target). */ +extern unsigned int min(unsigned int a, unsigned int b); +extern unsigned int max(unsigned int a, unsigned int b); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFSSL_USER_SETTINGS_H */ diff --git a/src/port/pic32mz/wolf_compat.c b/src/port/pic32mz/wolf_compat.c new file mode 100644 index 00000000..727c481b --- /dev/null +++ b/src/port/pic32mz/wolf_compat.c @@ -0,0 +1,40 @@ +/* wolf_compat.c + * + * Small wolfCrypt compatibility shims for the bare-metal PIC32MZ 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 + */ + +/* The WOLFSSL_MICROCHIP_PIC32MZ settings block defines WOLFSSL_HAVE_MIN / + * WOLFSSL_HAVE_MAX, which tells wolfcrypt/src/misc.c NOT to define min()/max() + * because the Microchip Harmony / TCP-IP framework normally provides them. + * This bare-metal port does not use that framework, so provide them here. + * Signatures match the wolfcrypt/wolfcrypt/misc.h prototypes. */ +#include +#include + +word32 min(word32 a, word32 b) +{ + return (a < b) ? a : b; +} + +word32 max(word32 a, word32 b) +{ + return (a > b) ? a : b; +}