"""Run the bounded TCP sender against a register-level W5100S simulator.""" from pathlib import Path import subprocess import tempfile import unittest ROOT = Path(__file__).resolve().parents[1] DRIVER = (ROOT / ".pio/libdeps/RAK_4631_repeater_unified_lora_ota" / "RAK13800-W5100S/src/w5100.h") ARDUINO = r''' #pragma once #include constexpr unsigned OUTPUT = 1, LOW = 0, HIGH = 1; inline void pinMode(unsigned, unsigned) {} inline void digitalWrite(unsigned, unsigned) {} ''' SPI = r''' #pragma once #include constexpr unsigned MSBFIRST = 1, SPI_MODE0 = 0; struct SPISettings { SPISettings(unsigned, unsigned, unsigned) {} }; struct SPIClass { bool active = false; unsigned begins = 0, ends = 0; void beginTransaction(SPISettings) { assert(!active); active = true; ++begins; } void endTransaction() { assert(active); active = false; ++ends; } }; ''' RAK_HEADER = r''' #pragma once #define _RAK13800_W5100S_H_ #define MAX_SOCK_NUM 8 #include #include ''' # Keep the production register interface available in native CI without # downloading board libraries. A second test uses the actual installed vendor # header when available, and the firmware build always checks that interface. PORTABLE_DRIVER = r''' #pragma once #include #include #define SPI_ETHERNET_SETTINGS SPISettings(14000000, MSBFIRST, SPI_MODE0) struct SnSR { static constexpr uint8_t CLOSED=0, ESTABLISHED=0x17, CLOSE_WAIT=0x1c; }; struct SnIR { static constexpr uint8_t SEND_OK=0x10, TIMEOUT=0x08; }; enum SockCMD { Sock_CLOSE=0x10, Sock_SEND=0x20, Sock_RECV=0x40 }; class W5100Class { static uint8_t chip, CH_BASE_MSB, ss_pin; static SPIClass* spi; public: static constexpr uint16_t SSIZE=2048, SMASK=2047; static uint8_t init(); static uint8_t getChip() { return chip; } static SPIClass* getSPI() { return spi; } static void setSPI(SPIClass& value) { spi=&value; } static uint16_t SBASE(uint8_t socket) { return 0x4000 + socket*SSIZE; } static uint16_t RBASE(uint8_t socket) { return 0x6000 + socket*SSIZE; } static uint16_t read(uint16_t, uint8_t*, uint16_t); static uint16_t write(uint16_t, const uint8_t*, uint16_t); static uint8_t read(uint16_t address) { uint8_t value; read(address,&value,1); return value; } static uint8_t write(uint16_t address,uint8_t value) { return write(address,&value,1); } static uint16_t reg(uint8_t socket,uint16_t offset) { return (CH_BASE_MSB<<8)+socket*256+offset; } static uint8_t readSnSR(uint8_t socket) { return read(reg(socket,3)); } static uint8_t readSnIR(uint8_t socket) { return read(reg(socket,2)); } static uint8_t readSnCR(uint8_t socket) { return read(reg(socket,1)); } static void writeSnIR(uint8_t socket,uint8_t value) { write(reg(socket,2),value); } static void writeSnCR(uint8_t socket,uint8_t value) { write(reg(socket,1),value); } static uint16_t readSnTX_WR(uint8_t socket) { uint8_t bytes[2]; read(reg(socket,0x24),bytes,2); return (bytes[0]<<8)|bytes[1]; } static uint16_t readSnTX_FSR(uint8_t socket) { uint8_t bytes[2]; read(reg(socket,0x20),bytes,2); return (bytes[0]<<8)|bytes[1]; } static uint16_t readSnRX_RSR(uint8_t socket) { uint8_t bytes[2]; read(reg(socket,0x26),bytes,2); return (bytes[0]<<8)|bytes[1]; } static uint16_t readSnRX_RD(uint8_t socket) { uint8_t bytes[2]; read(reg(socket,0x28),bytes,2); return (bytes[0]<<8)|bytes[1]; } static void writeSnRX_RD(uint8_t socket,uint16_t value) { uint8_t bytes[2]={uint8_t(value>>8),uint8_t(value)}; write(reg(socket,0x28),bytes,2); } static void writeSnTX_WR(uint8_t socket,uint16_t value) { uint8_t bytes[2]={uint8_t(value>>8),uint8_t(value)}; write(reg(socket,0x24),bytes,2); } static void execCmdSn(uint8_t, SockCMD); // deliberately undefined: a blocking call fails linking }; extern W5100Class W5100; ''' RUNNER = r''' #include #include #include #include "src/helpers/nrf52/EthernetCliTransmit.h" using mesh::nrf52::EthernetCliTransmit; static_assert(Sock_SEND == 0x20 && Sock_CLOSE == 0x10 && Sock_RECV == 0x40); static_assert(SnIR::SEND_OK == 0x10 && SnIR::TIMEOUT == 0x08); static_assert(SnSR::ESTABLISHED == 0x17 && SnSR::CLOSE_WAIT == 0x1c); static SPIClass spi; uint8_t W5100Class::chip = 51, W5100Class::CH_BASE_MSB = 4, W5100Class::ss_pin = 26; SPIClass* W5100Class::spi = &::spi; W5100Class W5100; static uint8_t memory[65536] = {}; static uint8_t next_chip = 51; static unsigned reads = 0, writes = 0, free_reads = 0, data_writes = 0, commands = 0; static bool short_write = false; static std::vector changing_free; static std::vector changing_receive; uint8_t W5100Class::init() { chip = next_chip; spi = &::spi; return chip; } uint16_t W5100Class::read(uint16_t address, uint8_t* buffer, uint16_t length) { assert(::spi.active); ++reads; if (address >= 0x400 && address < 0x800 && (address&255) == 0x20 && length == 2) { ++free_reads; if (!changing_free.empty()) { const uint16_t value = changing_free.front(); changing_free.erase(changing_free.begin()); buffer[0] = value>>8; buffer[1] = value; return length; } } if (address >= 0x400 && address < 0x800 && (address&255) == 0x26 && length == 2 && !changing_receive.empty()) { const uint16_t value=changing_receive.front(); changing_receive.erase(changing_receive.begin()); buffer[0]=value>>8; buffer[1]=value; return length; } memcpy(buffer,memory+address,length); return length; } uint16_t W5100Class::write(uint16_t address,const uint8_t* buffer,uint16_t length) { assert(::spi.active); ++writes; if (address >= 0x4000) { ++data_writes; if (short_write) return 0; } if (address >= 0x400 && address < 0x800 && length == 1 && (address&255) == 2) { memory[address] &= ~buffer[0]; } else { if (address >= 0x400 && address < 0x800 && length == 1 && (address&255) == 1) ++commands; memcpy(memory+address,buffer,length); } return length; } static uint16_t reg(unsigned offset) { return 0x400+offset; } static void put16(unsigned offset,uint16_t value) { memory[reg(offset)] = value>>8; memory[reg(offset)+1] = value; } static uint16_t get16(unsigned offset) { return (memory[reg(offset)]<<8)|memory[reg(offset)+1]; } static void reset() { memset(memory,0,sizeof(memory)); memory[reg(3)] = SnSR::ESTABLISHED; put16(0x20,2048); reads = writes = free_reads = data_writes = commands = 0; changing_free.clear(); changing_receive.clear(); short_write = false; next_chip = 51; W5100.init(); assert(!::spi.active && ::spi.begins == ::spi.ends); } int main() { uint8_t payload[512]; for (unsigned i=0;i