Skip to content
Merged
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ had changed in them - which is the gap this file exists to close.

### Fixed

- **Native nodes see CRC errors and a busy channel.** The chip model,
virtual-sx1262 v1.5.0, answers the four-byte status reads RadioLib sends in
full. Native MeshCore builds made on it no longer repeat a frame that failed
its CRC as a good one, and listen-before-talk sees preamble and header
detection. Emulated boards pin the same version.
- **Renode's chip binding is safe with a newer chip library.** Its
`VsxState` and `VsxCounters` were laid out for virtual-sx1262 ABI 1.3 while
the version check accepted any later minor, so a 1.4+ library wrote past
the end of both. They now follow the 1.7 header, a library newer than that
is refused, and the sizes are checked against C's on load. Renode nodes also
report `irq_suppressed` from a chip that counts it.
- **A Linux tarball install knows it is one.** The updater recognised the
tarball by a `radioserver` beside the binary, which no tarball has shipped
since the chip became a library, so every tarball install reported itself
Expand Down
16 changes: 8 additions & 8 deletions internal/app/resource/toolchaincatalogue.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ package resource
const (
qemuBase = "https://github.com/MeshBench/qemu/releases/download/v9.2.2-meshbench-sx1262-15/"
renodeBase = "https://github.com/MeshBench/renode/releases/download/meshbench-20260904-e7196ef/"
chipBase = "https://github.com/MeshBench/virtual-sx1262/releases/download/v1.3.1/"
chipBase = "https://github.com/MeshBench/virtual-sx1262/releases/download/v1.5.0/"
)

// qemuArm64LinuxIsUntried is the one platform the fork builds and this does not
Expand Down Expand Up @@ -52,30 +52,30 @@ const qemuArm64LinuxIsUntried = "the fork publishes an aarch64 Linux build and "
// before it clocks the next.
var toolReleases = []toolRelease{{
Name: "virtual-sx1262",
Version: "v1.3.1",
Version: "v1.5.0",
MCU: "",
Why: "the SX1262 itself, which both emulators load and a native node links; " +
"every emulated node needs it, ESP32 or nRF52",
Terms: chipModelTerms,
Assets: map[string]toolAsset{
"linux/amd64": {
URL: chipBase + "virtual-sx1262-linux-amd64.tar.gz",
SHA256: "7e04041afd4f0b2e063566042c136b6255e7adf6da27e19c02a8ac6737ee0fa0",
Bytes: 26335, Kind: tarGzip, Magic: elfAMD64,
SHA256: "bfa992792c080d992a49396e221a6038ee085d23a386289c293b87c7943f34ad",
Bytes: 44958, Kind: tarGzip, Magic: elfAMD64,
Root: "virtual-sx1262-linux-amd64",
Binary: "virtual-sx1262-linux-amd64/lib/libvirtualsx1262.so",
},
"darwin/arm64": {
URL: chipBase + "virtual-sx1262-macos-arm64.tar.gz",
SHA256: "167e471ba64416cace0296e5b2c40dabbe1ed80f523e200cc96890f5f96c0fbc",
Bytes: 19119, Kind: tarGzip, Magic: machARM64,
SHA256: "9503d1cb1dd28069ccdecbb2a1b6ac59d7e0d86f89ae2dd646d348dfe8d8afc1",
Bytes: 29339, Kind: tarGzip, Magic: machARM64,
Root: "virtual-sx1262-macos-arm64",
Binary: "virtual-sx1262-macos-arm64/lib/libvirtualsx1262.dylib",
},
"windows/amd64": {
URL: chipBase + "virtual-sx1262-windows-amd64.tar.gz",
SHA256: "90621299f0e2e85720900a7b75ebcdfd8fd7fe2db89432cdb9ff4505ff9ac67d",
Bytes: 146549, Kind: tarGzip, Magic: peAMD64,
SHA256: "ea5296f8f2607f76a82502b1f85f21fb3501038b2ed930033aafe18135ed83b4",
Bytes: 188504, Kind: tarGzip, Magic: peAMD64,
Root: "virtual-sx1262-windows-amd64",
Binary: "virtual-sx1262-windows-amd64/lib/libvirtualsx1262.dll",
},
Expand Down
6 changes: 5 additions & 1 deletion packaging/emulator-pins.env
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ RENODE_ASSET_WINDOWS_AMD64=meshbench-renode-1.16.1.windows-portable.zip
# any ordinary Windows machine and every emulated board there died as a radio
# that never answered. It is statically linked now and imports only kernel32
# and msvcrt. MeshBench#632, fixed by MeshBench/virtual-sx1262#1.
CHIPMODEL_RELEASE=v1.3.1
#
# v1.5.0 is ABI 1.8: four-byte status reads answered in full, the received
# header's CRC flag, and receive failures 4 to 6. Nothing was appended to the
# structs a host reads, so a 1.3 host still works with it.
CHIPMODEL_RELEASE=v1.5.0
CHIPMODEL_ASSET_LINUX_AMD64=virtual-sx1262-linux-amd64.tar.gz
CHIPMODEL_ASSET_DARWIN_ARM64=virtual-sx1262-macos-arm64.tar.gz
CHIPMODEL_ASSET_WINDOWS_AMD64=virtual-sx1262-windows-amd64.tar.gz
25 changes: 22 additions & 3 deletions tools/renode/peripherals/VirtualSX1262Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,25 @@ private void SendStats()
// carrier, the pin was still high when RxDone arrived, and a driver
// that attaches on the rising edge never learned the packet existed.
Put16(sb, 37, state.Dio1Mask);
// The console's line rate. Zero, which the engine reads as "not
// on a UART": the chip cannot see the console, and the board's
// UART is not this peripheral's to ask.
Put32(sb, 39, 0);
// What IrqMask kept out, only from a chip new enough to count it.
// An older one leaves the field zero, and zero there would read as
// "nothing suppressed" rather than "not counted", so the record
// stops short of it instead.
var length = StatsLength;
if(lib.Minor >= VirtualSX1262Lib.AbiIrqSuppressed)
{
Put32(sb, 43, counters.IrqSuppressed);
}
else
{
length = StatsLength - 4;
}

Send(RadioStats, sb, sb.Length);
Send(RadioStats, sb, length);
}

private static bool ReadAll(NetworkStream wire, byte[] buffer, int count)
Expand Down Expand Up @@ -327,8 +344,10 @@ public void Dispose()
private const byte ChannelBusy = 0x08;
private const byte RadioStats = 0x09;

// The stats record, whose layout the engine reads on length.
private const int StatsLength = 39;
// The stats record, whose layout the engine reads on length: up to
// irq_suppressed, which ends at byte 47. Byte 47 itself, the
// capabilities, is left off - this host sends no failed frames.
private const int StatsLength = 47;

private readonly VirtualSX1262Lib lib;
private readonly IntPtr chip;
Expand Down
91 changes: 84 additions & 7 deletions tools/renode/peripherals/VirtualSX1262Lib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,28 @@
//
// The ABI is a contract, and the other side of it is
// MeshBench/virtual-sx1262's include/virtual_sx1262.h. It is append-only:
// never reorder a struct, never change what an entry point means. This
// declares ABI 1.3, the version that added the byte-at-a-time SPI path, which
// is the only one an emulator can use - Renode's ISPIPeripheral.Transmit is
// called once per clocked byte and must answer that byte before the next.
// never reorder a struct, never change what an entry point means. The floor is
// ABI 1.3, the version that added the byte-at-a-time SPI path, which is the
// only one an emulator can use - Renode's ISPIPeripheral.Transmit is called
// once per clocked byte and must answer that byte before the next.
//
// The two structs below are laid out to the newest ABI this file knows, 1.7.
// Append-only cuts one way: an older library writes a prefix of them and the
// rest stays zero, which is safe, but a newer one may have appended fields
// and would write past the end, which corrupts whatever is beyond rather than
// failing. So a minor above the newest known is refused, and declaring it
// means bringing both structs up to its header first (#747).
//
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace Antmicro.Renode.Peripherals.Radio
{
// Laid out to match vsx_state exactly. Sequential, and no field may be
// reordered or resized: a mismatch here does not fail, it reads one
// setting as another.
// Laid out to match vsx_state exactly, as of ABI 1.7. Sequential, and no
// field may be reordered or resized: a mismatch here does not fail, it
// reads one setting as another. The comment on each group is the minor
// that appended it; a library older than that leaves it zero.
[StructLayout(LayoutKind.Sequential)]
public struct VsxState
{
Expand All @@ -47,8 +55,35 @@ public struct VsxState
public byte RxGainReg;
public byte FemAtTx; // 0 never transmitted, 1 module out, 2 module in
public ushort Dio1Mask;
// 1.4
public byte CadSymbolNum;
public byte CadDetPeak;
public byte CadDetMin;
public byte CadExitMode;
// 1.5
public ushort SyncWord;
public byte LowDataRateOptimize;
// 1.6
public byte PacketType;
public byte FallbackMode;
public byte RegulatorMode;
public byte Dio2AsRfSwitch;
public byte Dio3AsTcxo;
public byte PaDutyCycle;
public byte PaHpMax;
public byte PaDeviceSel;
public byte SymbNumTimeout;
public byte StopTimerOnPreamble;
public byte TxContinuousWave;
public byte TxInfinitePreamble;
public byte HeaderImplicit;
public byte CrcOn;
public byte InvertIq;
public uint RxDutyRxPeriod;
public uint RxDutySleepPeriod;
}

// vsx_counters, as of ABI 1.7.
[StructLayout(LayoutKind.Sequential)]
public struct VsxCounters
{
Expand All @@ -58,6 +93,17 @@ public struct VsxCounters
public uint SpuriousRaises;
public uint PreambleRaises;
public uint FramesDropped;
// 1.4
public uint IrqSuppressed;
public uint CadRuns;
public uint CadDetections;
// 1.5
public uint SyncMismatches;
public uint ParamsRejected;
// 1.6
public ushort StatRxPackets;
public ushort StatCrcErrors;
public ushort StatHeaderErrors;
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
Expand All @@ -67,6 +113,17 @@ public class VirtualSX1262Lib
{
public const int AbiMajor = 1;
public const int AbiMinor = 3;
// The newest minor the structs above are laid out for.
// 1.8 added receive failures 4 to 6 and header registers, and nothing
// to either struct, so its layouts are 1.7's.
public const int AbiNewestMinor = 8;
// The minor that appended irq_suppressed to vsx_counters.
public const int AbiIrqSuppressed = 4;
// sizeof(vsx_state) and sizeof(vsx_counters) at AbiNewestMinor, as a C
// compiler lays them out. Checked on load, so a field mistyped here
// fails loudly rather than shifting every field after it.
public const int StateSize = 52;
public const int CountersSize = 52;

public static VirtualSX1262Lib Open(string path)
{
Expand All @@ -90,11 +147,31 @@ public static VirtualSX1262Lib Open(string path)
"{0} is ABI {1}.{2}, and this peripheral needs {3}.{4} or a later minor",
path, major, minor, AbiMajor, AbiMinor));
}
// A later minor may have appended to vsx_state or vsx_counters,
// and the library would write the longer struct into the shorter
// one declared here.
if(minor > AbiNewestMinor)
{
throw new InvalidOperationException(string.Format(
"{0} is ABI {1}.{2}, newer than the {1}.{3} this peripheral's structs are laid out for: " +
"bring VsxState and VsxCounters up to its header first",
path, major, minor, AbiNewestMinor));
}
if(Marshal.SizeOf(typeof(VsxState)) != StateSize || Marshal.SizeOf(typeof(VsxCounters)) != CountersSize)
{
throw new InvalidOperationException(string.Format(
"VsxState is {0} bytes and VsxCounters {1}, where the C header lays them out at {2} and {3}",
Marshal.SizeOf(typeof(VsxState)), Marshal.SizeOf(typeof(VsxCounters)), StateSize, CountersSize));
}
lib.Minor = minor;
return lib;
}

public string Path { get; private set; }

// The library's ABI minor, which says how much of each struct it fills.
public int Minor { get; private set; }

public AbiVersionFn AbiVersion;
public CreateFn Create;
public DestroyFn Destroy;
Expand Down