SOLVED — root cause: NVEM V5 (at least this clone/revision) has only 2MB of flash, but Remora-RT1052-cpp stores the config at an address beyond that
For anyone hitting the same wall I did — board pings fine with the old firmware but never responds at all (no ping, no ARP, nothing) with any of the newer Remora-RT1052-cpp builds (2.1.1, 3.1.x) — here's what was actually going on, at least on my board.
Board: NVEM V5 clone, silkscreen "1NKR_NVEM_V6", MIMXRT1052CVL5B
The flash chip on my board is a Winbond W25Q16JVSIQ. "16" = 16 Mbit = 2MB. The firmware, however, defines JSON_STORAGE_ADDRESS in source/configuration.h as:
#define JSON_STORAGE_ADDRESS 0x00200000 // beginning of Block 32
0x00200000 is exactly 2MB — i.e. one byte past the end of a 2MB chip. On boot, once the firmware reaches jsonFromFlash() and tries to read the length word at that address, it hard-faults silently (the hard fault handler in this firmware only catches semihosting breakpoints, so a genuine fault just hangs with zero console output). This happens before the main loop ever gets around to servicing the Ethernet/lwIP stack, which is why nothing ever answers on the wire — no ARP, no ICMP, nothing. It looks exactly like a dead ENET/PHY, but it isn't.
How I confirmed it:
- Got a debug UART connection going (LPUART4, 115200 8N1, pins GPIO_B1_00/B1_01 — on my board these come out on connector P6, which is also wired to a MAX3232 for RS232 levels, pins 2/3, pin 1 GND)
- Boot log stopped dead right after "1. Loading JSON configuration file from Flash memory" — no "Flash storage location is empty" message, no crash message, just silence
- Older firmware (0.2.1, no TFTP, config hardcoded) never touches this address at all and boots/pings fine every time — same hardware, same cable, same everything else
- Confirmed the JSON_UPLOAD_ADDRESS (0x001F0000, "Block 31") is already the last valid block on a 2MB chip — Block 32 simply doesn't exist on this flash
Fix: moved JSON_STORAGE_ADDRESS to an address safely within the 2MB range and clear of both the app code and the upload staging area:
#define JSON_STORAGE_ADDRESS 0x001E0000 // Block 30
Rebuilt with MCUXpresso (had to set up the toolchain from scratch, straightforward once the SDK pulls in), reflashed, and the board now boots all the way through — PHY init, JSON load (correctly falls back to default config when flash is empty), full module setup, IDLE state — and pings/TFTP-uploads immediately. Config upload via upload_config.py now works exactly as expected.
Worth checking your own flash chip's part number before assuming it's a hardware/ENET/PHY problem if you're seeing total silence on the network with the C++ firmware — a 2MB chip on these clone boards looks to be common enough that others may be hitting exactly this.
Happy to share the exact board.c / configuration.h diffs if useful.