W5500 Decoder in Logic Analyzer
A custom protocol plug-in that allows Saleae Logic Analyzer software to decode SPI communication from the WIZnet W5500 Ethernet chip.

Getting Started with the Python Extension Decoder in Logic 2
1. Launch Logic 2
2. click the Extensions tab
3. In the “Create Extension” window that opens, select “High-Level Analyzer”.
4. Enter a name of your choice, specify a save location
In your new High Level Analyzer (HLA) extension folder you will find 3 files:
README.md
- Documentation for your extension, shown within Logic 2 when you select an extension, and what users will see if you put your extension on the Marketplace.
extension.json
- Every extension must have this file in its root directory.
- Contains metadata about the extension, and the HLAs and Measurement scripts that are included with the extension.
- See Extension File Format for more information.
HighLevelAnalyzer.py
- Python source code for your HLA.
- For the purposes of this document, we will be focusing on
HighLevelAnalyzer.py
5. You can do this by modifying HighLevelAnalyzer.py as shown below.
HighLevelAnalyzer.py
from typing import Optional, Union
from saleae.analyzers import HighLevelAnalyzer, AnalyzerFrame
regs = {
0x0000: "MR",
0x0001: "GAR0", 0x0002: "GAR1", 0x0003: "GAR2", 0x0004: "GAR3",
0x0005: "SUBR0", 0x0006: "SUBR1", 0x0007: "SUBR2", 0x0008: "SUBR3",
0x0009: "SHAR0", 0x000A: "SHAR1", 0x000B: "SHAR2",
0x000C: "SHAR3", 0x000D: "SHAR4", 0x000E: "SHAR5",
0x000F: "SIPR0", 0x0010: "SIPR1", 0x0011: "SIPR2", 0x0012: "SIPR3",
0x0013: "INTLEVEL0", 0x0014: "INTLEVEL1",
0x0015: "IR", 0x0016: "IMR", 0x0017: "SIR", 0x0018: "SIMR",
0x0019: "RTR0", 0x001A: "RTR1", 0x001B: "RCR",
0x001C: "PTIMER", 0x001D: "PMAGIC",
0x001E: "PHAR0", 0x001F: "PHAR1", 0x0020: "PHAR2",
0x0021: "PHAR3", 0x0022: "PHAR4", 0x0023: "PHAR5",
0x0024: "PSID0", 0x0025: "PSID1", 0x0026: "PMRU0", 0x0027: "PMRU1",
0x0028: "UIPR0", 0x0029: "UIPR1", 0x002A: "UIPR2", 0x002B: "UIPR3",
0x002C: "UPORTR0", 0x002D: "UPORTR1", 0x002E: "PHYCFGR",
0x0039: "VERSIONR"
}
sn_regs = {
0x0000: "MR", 0x0001: "CR", 0x0002: "IR", 0x0003: "SR",
0x0004: "PORT0", 0x0005: "PORT1",
0x0006: "DHAR0", 0x0007: "DHAR1", 0x0008: "DHAR2",
0x0009: "DHAR3", 0x000A: "DHAR4", 0x000B: "DHAR5",
0x000C: "DIPR0", 0x000D: "DIPR1", 0x000E: "DIPR2", 0x000F: "DIPR3",
0x0010: "DPORT0", 0x0011: "DPORT1",
0x0012: "MSSR0", 0x0013: "MSSR1",
0x0015: "TOS", 0x0016: "TTL",
0x001E: "RXBUF_SIZE", 0x001F: "TXBUF_SIZE",
0x0020: "TX_FSR0", 0x0021: "TX_FSR1",
0x0022: "TX_RD0", 0x0023: "TX_RD1",
0x0024: "TX_WR0", 0x0025: "TX_WR1",
0x0026: "RX_RSR0", 0x0027: "RX_RSR1",
0x0028: "RX_RD0", 0x0029: "RX_RD1",
0x002A: "RX_WR0", 0x002B: "RX_WR1",
0x002C: "IMR", 0x002D: "FRAG0", 0x002E: "FRAG1",
0x002F: "KPALVTR"
}
def get_reg_name(address: int) -> str:
return regs.get(address, "INVALID")
def get_sn_reg_name(address: int) -> str:
return sn_regs.get(address, "INVALID")
class Hla(HighLevelAnalyzer):
result_types = {
"address": {"format": "Address {{data.address}}"},
"control": {"format": "{{data.block}} block {{data.rw}}"},
"read": {"format": "{{data.rw}} {{data.reg}} {{data.value}}"},
"write": {"format": "{{data.rw}} {{data.reg}} {{data.value}}"},
}
def __init__(self):
self._previous_type = ""
self._address = None
self._block = ""
self._rw = ""
self._byte_pos = 0
self._sn = None
self._start_of_address_frame = None
def decode(self, frame: AnalyzerFrame) -> Optional[AnalyzerFrame]:
is_first_byte = self._previous_type == "enable"
self._previous_type = frame.type
if is_first_byte:
self._byte_pos = 0
else:
self._byte_pos += 1
if frame.type != "result":
return None
mosi = frame.data["mosi"]
miso = frame.data["miso"]
if self._byte_pos == 0:
self._address = mosi[0] << 8
self._start_of_address_frame = frame.start_time
elif self._byte_pos == 1:
self._address |= mosi[0]
return AnalyzerFrame("address", self._start_of_address_frame, frame.end_time, {
"address": f"0x{self._address:04X}"
})
elif self._byte_pos == 2:
control = mosi[0]
self._rw = "write" if ((control >> 2) & 0b1) else "read"
bsb = control >> 3
self._sn = None
if bsb == 0:
self._block = "common"
else:
sn = int(bsb / 4)
if sn <= 7:
self._sn = sn
snblock = bsb - sn * 4 - 1
if snblock == 0:
self._block = f"Sn{sn} Socket"
elif snblock == 1:
self._block = f"Sn{sn} TX"
elif snblock == 2:
self._block = f"Sn{sn} RX"
else:
self._block = "INVALID"
else:
self._block = "INVALID"
return AnalyzerFrame("control", frame.start_time, frame.end_time, {
"block": self._block,
"rw": self._rw
})
elif self._byte_pos > 2:
byte = mosi[0] if self._rw == "write" else miso[0]
if self._sn is not None:
if self._block.endswith("TX"):
name = "TX"
elif self._block.endswith("RX"):
name = "RX"
else:
name = get_sn_reg_name(self._address)
else:
name = get_reg_name(self._address)
result = AnalyzerFrame(self._rw.lower(), frame.start_time, frame.end_time, {
"reg": name,
"rw": self._rw,
"value": f"0x{byte:02X}"
})
self._address = (self._address + 1) & 0xFFFF
return result
6. On the Analyzers tab, add the W5500, connect the SPI, and you're ready to decode.
1. read the chip version of the W5500
2. Get the MAC Address of the W5500
3. read the socket's status register