Branch under test: https://github.com/waynieack/proxmark3/tree/srt512-emulation
Describe the bug
I am writing a software SRT512/SRI512 ISO 14443B tag emulator (hf 14b simsrx). The anticollision state machine (INITIATE -> SELECT -> GET UID -> READ_BLK x 17) is fully implemented and produces correct responses. The data-read phase completes reliably once reached. However, after the emulator sends its reply to SELECT, it frequently fails to receive the subsequent GET UID command -- the emulator receives a new INITIATE instead, as if the reader restarted the entire anticollision sequence.
The device I am writing this for polls continuously: anticollision -> read all 17 blocks (0-15 + block 255) -> field drop -> repeat. If more than 3 consecutive full-read cycles fail, the host device reports an error. At stock and with the bitCnt > 8 variant, failures happen frequently enough to regularly trigger that threshold.
The root failure -- INITIATE received instead of GET UID after SELECT:
From the stock Handle14443bSampleFromReader console log, the emulator correctly handles INITIATE and SELECT, but then receives another INITIATE (causing a state reset) instead of the expected GET UID:
[#] SRT512: [READY] INITIATE chip_id=90 slot=0 -> INVENTORY (no-field-loss)
[#] SRT512: [INVENTORY] SELECT chip_id=90 ok -> SELECTED
[#] SRT512: [SELECTED] INITIATE -> reset to READY #2 consecutive (no-field-loss)
The corresponding trace shows SELECT was answered correctly, but GET UID never arrives -- instead, the reader's next INITIATE comes ~930 ms later:
38618112 | 38626560 | Rdr |06 00 97 5B | ok | INITIATE
38626560 | 38634496 | Tag |90 F1 64 | ok |
39272192 | 39280640 | Rdr |0E 90 DE 01 | ok | SELECT(144)
39280640 | 39288576 | Tag |90 F1 64 | ok |
<- GET UID never received
51878144 | 51886592 | Rdr |06 00 97 5B | ok | INITIATE (~930ms later)
The ~930 ms gap suggests the reader did send GET UID, but the emulator's UART was in a bad state and parsed it as something else or discarded it entirely. The emulator then received the next INITIATE (after the reader gave up, dropped the field, and restarted), which the state machine correctly treated as a reset.
To Reproduce
- Flash firmware from branch
srt512-emulation.
- Run the emulator against a reader that polls continuously:
hf 14b simsrx -f <dumpfile.json> -n -0
- Let the reader run a polling loop for several minutes.
- Observe
hf 14b list for the INITIATE -> SELECT -> INITIATE pattern (no GET UID between SELECT and the next INITIATE).
Expected behavior
After the emulator replies to SELECT, it should correctly receive and respond to GET UID, then proceed to the READ_BLK sequence. The INITIATE -> SELECT -> INITIATE restart loop should not occur.
Screenshots / Traces
Three traces were captured with different STATE_14B_GOT_FALLING_EDGE_OF_SOF thresholds in Handle14443bSampleFromReader:
Test 1 -- Stock Handle14443bSampleFromReader (Uart.bitCnt > 9)
Frequent INITIATE -> SELECT -> INITIATE failures. The reader eventually gets stuck in a tight loop with no data reads completing. The trace below shows a successful full-read cycle followed by a cycle where GET UID is never received:
38618112 | 38626560 | Rdr |06 00 97 5B | ok | INITIATE
38626560 | 38634496 | Tag |90 F1 64 | ok |
39272192 | 39280640 | Rdr |0E 90 DE 01 | ok | SELECT(144)
39280640 | 39288576 | Tag |90 F1 64 | ok |
51878144 | 51886592 | Rdr |06 00 97 5B | ok | INITIATE <- GET UID never received; ~930ms gap
Console (stock -- from the restart loop at end of trace):
[#] SRT512: [SELECTED] INITIATE -> reset to READY #2 consecutive (no-field-loss)
[#] SRT512: [READY] INITIATE chip_id=40 slot=0 -> INVENTORY (no-field-loss)
[#] SRT512: [INVENTORY] SELECT chip_id=40 ok -> SELECTED
[#] SRT512: [SELECTED] INITIATE -> reset to READY #3 consecutive (no-field-loss)
[#] SRT512: [READY] INITIATE chip_id=C0 slot=0 -> INVENTORY (no-field-loss)
[#] SRT512: [INVENTORY] SELECT chip_id=C0 ok -> SELECTED
Full trace: trace-stock-Handle14443bSampleFromReader.txt (attached)
Test 2 -- Uart.bitCnt > 8 (one bit lower threshold)
Improved over stock -- more successful full-read cycles before a failure. However, the INITIATE -> SELECT -> INITIATE error still occurs multiple times and eventually reaches 3-4 consecutive failures, triggering the device error threshold.
Console (bitcnt8 -- worst failure sequence captured):
[#] SRT512: [READY] INITIATE chip_id=80 slot=0 -> INVENTORY (no-field-loss)
[#] SRT512: [INVENTORY] SELECT chip_id=80 ok -> SELECTED
[#] SRT512: [SELECTED] INITIATE -> reset to READY #2 consecutive (no-field-loss)
[#] SRT512: [READY] INITIATE chip_id=D0 slot=0 -> INVENTORY (no-field-loss)
[#] SRT512: [INVENTORY] SELECT chip_id=D0 ok -> SELECTED
[#] SRT512: [SELECTED] INITIATE -> reset to READY #3 consecutive (no-field-loss)
[#] SRT512: [READY] INITIATE chip_id=A0 slot=0 -> INVENTORY (no-field-loss)
[#] SRT512: [INVENTORY] SELECT chip_id=A0 ok -> SELECTED
[#] SRT512: [SELECTED] INITIATE -> reset to READY #4 consecutive (no-field-loss)
Full trace: trace-bitcnt8-Handle14443bSampleFromReader.txt (attached)
Test 3 -- Direct sample counting, Uart.bitCnt >= 31
No INITIATE -> SELECT -> INITIATE failures observed in the entire run. The emulator consistently reaches GET UID and completes all 17 block reads each cycle.
However, this change introduces a SOF phase-slip side effect (described below). Despite the phase-slip, this variant is functionally the most stable.
Console (bitcnt31 -- representative successful run):
[#] SRT512: [READY] INITIATE chip_id=C0 slot=0 -> INVENTORY (no-field-loss)
[#] SRT512: [INVENTORY] SELECT chip_id=C0 ok -> SELECTED
[#] SRT512: [SELECTED] GET_UID
[#] SRT512: [SELECTED] READ_BLOCK blk=00 (idx=0)
...
[#] SRT512: [SELECTED] READ_BLOCK blk=FF (idx=16)
[#] SRT512: [READY] INITIATE chip_id=90 slot=0 -> INVENTORY (no-field-loss)
[#] SRT512: [INVENTORY] SELECT chip_id=90 ok -> SELECTED
[#] SRT512: [SELECTED] GET_UID
...
Full trace: trace-bitcnt31-Handle14443bSampleFromReader.txt (attached)
SOF phase-slip side effect (bitcnt31 workaround)
Raising the SOF bit count threshold filters the false SOF events that were causing the SELECT -> GET UID drop, but it also raises the bar high enough that on some INITIATE frames the UART locks onto the SOF late -- missing the first two data bytes (06 00) and only receiving the CRC bytes (97 5B).
The raw SSC byte buffer captured at the moment of a phase-slip (256 bytes, oldest first) shows the carrier mostly idle (0xFF), then the SOF low-pulse, then the subcarrier-modulated data beginning partway through:
[#] [ 0] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [ 16] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [ 32] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [ 48] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [ 64] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [ 80] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [ 96] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [112] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [128] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [144] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [160] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [176] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [192] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[#] [208] FF FF FF FF FF FF FF FF FF FF FF FF FF FE 00 FF
[#] [224] FF FF FF FF C0 1F C0 00 01 FC 00 00 00 00 1F C1
[#] [240] FF E1 C0 1F FE 1F C1 FC 1C 1F C0 00 00 00 00 07
This phase-slip is a side effect of the bitcnt threshold workaround, not the root cause. It is recoverable: the INITIATE command for mask=00 always has a known fixed CRC (97 5B), so when only 2 bytes are received and they match that CRC, the full 06 00 97 5B frame can be reconstructed. This workaround is already implemented in the branch.
Desktop
- OS: Ubuntu 24.04 Linux (x86_64)
- hw version *[ Proxmark3 ]
[ Client ]
Iceman/srt512-emulation/v4.21611-191-g6f45b287...-suspect 2026-05-25 20:00:16 06dfb9e17
Compiler.................. GCC 13.3.0
Platform.................. Linux / x86_64
Readline support.......... present
QT GUI support............ present
Native BT support......... present
Python script support..... present ( 3.12.3 )
Python SWIG support....... present
Lua script support........ present ( 5.4.7 )
Lua SWIG support.......... present
[ Model ]
Firmware.................. PM3 GENERIC
[ ARM ]
Bootrom.... Iceman/srt512-emulation/v4.21611-191-g6f45b287...-suspect 2026-05-25 20:00:18 06dfb9e17
OS......... Iceman/srt512-emulation/v4.21611-191-g6f45b287...-suspect 2026-05-25 20:00:16 06dfb9e17
Compiler... GCC 13.2.1 20231009
[ FPGA ]
fpga_pm3_hf.ncd image 2s30vq100 12-05-2026 14:54:54
fpga_pm3_lf.ncd image 2s30vq100 12-05-2026 14:54:54
fpga_pm3_felica.ncd image 2s30vq100 12-05-2026 14:54:54
fpga_pm3_hf_15.ncd image 2s30vq100 12-05-2026 14:54:54
[ Hardware ]
--= uC: AT91SAM7S512 Rev A
--= Embedded Processor: ARM7TDMI
--= Internal SRAM size: 64K bytes
--= Architecture identifier: AT91SAM7Sxx Series
--= Embedded flash memory 512K bytes ( 74% used )
*
- hw status *[#] Memory
[#] BigBuf_size............. 38520
[#] Available memory........ 35756
[#] Tracing
[#] tracing ................ 0
[#] traceLen ............... 35744
[#] Sending buffers
[#] dma8 memory............. 0
[#] dma16 memory............ 0
[#] toSend memory........... 36212
[#] Current FPGA image
[#] mode.................... fpga_pm3_hf.ncd image 2s30vq100 12-05-2026 14:54:54
[#] LF Sampling config
[#] [q] divisor............. 95 ( 125.00 kHz )
[#] [b] bits per sample..... 8
[#] [d] decimation.......... 1
[#] [a] averaging........... yes
[#] [t] trigger threshold... 0
[#] [s] samples to skip..... 0
[#]
[#] LF T55XX config
[#] [r] [a] [b] [c] [d] [e] [f] [g]
[#] mode |start|write|write|write| read|write|write
[#] | gap | gap | 0 | 1 | gap | 2 | 3
[#] ---------------------------+-----+-----+-----+-----+-----+-----+------
[#] fixed bit length (default) | 31 | 20 | 18 | 50 | 15 | n/a | n/a |
[#] long leading reference | 31 | 20 | 18 | 50 | 15 | n/a | n/a |
[#] leading zero | 31 | 20 | 18 | 40 | 15 | n/a | n/a |
[#] 1 of 4 coding reference | 31 | 20 | 18 | 34 | 15 | 50 | 66 |
[#]
[#] HF 14a config
[#] [a] Anticol override........... std ( follow standard )
[#] [b] BCC override............... std ( follow standard )
[#] [2] CL2 override............... std ( follow standard )
[#] [3] CL3 override............... std ( follow standard )
[#] [r] RATS override.............. std ( follow standard )
[#] [m] Magsafe polling............ disabled
[#] [p] Polling loop annotation.... disabled 00000000000000000000000000000000
[#] HF 14b config
[#] [p] Polling loop annotation.... disabled 00000000000000000000000000000000
[#] Transfer Speed
[#] Sending packets to client...
[#] Time elapsed................... 500ms
[#] Bytes transferred.............. 346624
[#] Transfer Speed PM3 -> Client... 693248 bytes/s
[#] Various
[#] Max stack usage..... 3616 / 8480 bytes
[#] Debug log level..... 4 ( extended )
[#] ToSendMax........... 46
[#] ToSend BUFFERSIZE... 2308
[#] Slow clock.......... 30527 Hz
[#] Installed StandAlone Mode
[#] LF HID26 standalone - aka SamyRun (Samy Kamkar)
[#]
- hw tune *[=] -------- Reminder ----------------------------
[=] hw tune doesn't actively tune your antennas.
[=] It's only informative.
[=] Measuring antenna characteristics...
🕐 20[#] Buffer cleared (38520 bytes)
🕛 9[#] Buffer cleared (38520 bytes)
[=] -------- LF Antenna ----------
[!] ⚠️ LF antenna............ unusable
[=] -------- HF Antenna ----------
[+] 13.56 MHz............. 13.18 V
[+]
[+] Approx. Q factor measurement
[+] Peak voltage.......... 3.8
[+] HF antenna ( ok )
[-] ⛔ All values are zero. Not showing LF tuning graph
[=] Q factor must be measured without tag on the antenna
*
- hf tune *[=] Measuring HF antenna
[=] Press pm3 button or to exit
[=] 13060 mV / 13 V / 13 Vmax
Additional context
- The reader polls on an ~8-10 second cycle: anticollision -> 17 block reads -> field drop -> repeat.
- Inter-command spacing at the reader is ~49 ms (~662,000 carrier periods) between each command.
- Raising the threshold (bitcnt31 variant) suppresses the false SOF, which is why it eliminates the SELECT -> GET UID failure -- but the exact threshold at which the UART correctly frames both the SOF and the first data byte has not yet been found.
- When any failures happen or its done reading, the reader drops the field which makes the problem worse because it causes more delays, so I added an option to disable field detection and I just use the INIT cmd to flip the tag back to the ready state. All of the tests were done with the field detection disabled.
- I attempted to follow the SRT512 datasheet as close as possible to fully implement all tag features.
trace-stock-Handle14443bSampleFromReader.txt
trace-bitcnt31-Handle14443bSampleFromReader.txt
trace-bitcnt8-Handle14443bSampleFromReader.txt
Branch under test: https://github.com/waynieack/proxmark3/tree/srt512-emulation
Describe the bug
I am writing a software SRT512/SRI512 ISO 14443B tag emulator (
hf 14b simsrx). The anticollision state machine (INITIATE -> SELECT -> GET UID -> READ_BLK x 17) is fully implemented and produces correct responses. The data-read phase completes reliably once reached. However, after the emulator sends its reply to SELECT, it frequently fails to receive the subsequent GET UID command -- the emulator receives a new INITIATE instead, as if the reader restarted the entire anticollision sequence.The device I am writing this for polls continuously: anticollision -> read all 17 blocks (0-15 + block 255) -> field drop -> repeat. If more than 3 consecutive full-read cycles fail, the host device reports an error. At stock and with the
bitCnt > 8variant, failures happen frequently enough to regularly trigger that threshold.The root failure -- INITIATE received instead of GET UID after SELECT:
From the stock
Handle14443bSampleFromReaderconsole log, the emulator correctly handles INITIATE and SELECT, but then receives another INITIATE (causing a state reset) instead of the expected GET UID:The corresponding trace shows SELECT was answered correctly, but GET UID never arrives -- instead, the reader's next INITIATE comes ~930 ms later:
The ~930 ms gap suggests the reader did send GET UID, but the emulator's UART was in a bad state and parsed it as something else or discarded it entirely. The emulator then received the next INITIATE (after the reader gave up, dropped the field, and restarted), which the state machine correctly treated as a reset.
To Reproduce
srt512-emulation.hf 14b listfor theINITIATE -> SELECT -> INITIATEpattern (no GET UID between SELECT and the next INITIATE).Expected behavior
After the emulator replies to SELECT, it should correctly receive and respond to GET UID, then proceed to the READ_BLK sequence. The
INITIATE -> SELECT -> INITIATErestart loop should not occur.Screenshots / Traces
Three traces were captured with different
STATE_14B_GOT_FALLING_EDGE_OF_SOFthresholds inHandle14443bSampleFromReader:Test 1 -- Stock
Handle14443bSampleFromReader(Uart.bitCnt > 9)Frequent INITIATE -> SELECT -> INITIATE failures. The reader eventually gets stuck in a tight loop with no data reads completing. The trace below shows a successful full-read cycle followed by a cycle where GET UID is never received:
Console (stock -- from the restart loop at end of trace):
Full trace:
trace-stock-Handle14443bSampleFromReader.txt(attached)Test 2 --
Uart.bitCnt > 8(one bit lower threshold)Improved over stock -- more successful full-read cycles before a failure. However, the INITIATE -> SELECT -> INITIATE error still occurs multiple times and eventually reaches 3-4 consecutive failures, triggering the device error threshold.
Console (bitcnt8 -- worst failure sequence captured):
Full trace:
trace-bitcnt8-Handle14443bSampleFromReader.txt(attached)Test 3 -- Direct sample counting,
Uart.bitCnt >= 31No INITIATE -> SELECT -> INITIATE failures observed in the entire run. The emulator consistently reaches GET UID and completes all 17 block reads each cycle.
However, this change introduces a SOF phase-slip side effect (described below). Despite the phase-slip, this variant is functionally the most stable.
Console (bitcnt31 -- representative successful run):
Full trace:
trace-bitcnt31-Handle14443bSampleFromReader.txt(attached)SOF phase-slip side effect (bitcnt31 workaround)
Raising the SOF bit count threshold filters the false SOF events that were causing the SELECT -> GET UID drop, but it also raises the bar high enough that on some INITIATE frames the UART locks onto the SOF late -- missing the first two data bytes (
06 00) and only receiving the CRC bytes (97 5B).The raw SSC byte buffer captured at the moment of a phase-slip (256 bytes, oldest first) shows the carrier mostly idle (
0xFF), then the SOF low-pulse, then the subcarrier-modulated data beginning partway through:This phase-slip is a side effect of the bitcnt threshold workaround, not the root cause. It is recoverable: the INITIATE command for mask=00 always has a known fixed CRC (
97 5B), so when only 2 bytes are received and they match that CRC, the full06 00 97 5Bframe can be reconstructed. This workaround is already implemented in the branch.Desktop
[ Client ]
Iceman/srt512-emulation/v4.21611-191-g6f45b287...-suspect 2026-05-25 20:00:16 06dfb9e17
Compiler.................. GCC 13.3.0
Platform.................. Linux / x86_64
Readline support.......... present
QT GUI support............ present
Native BT support......... present
Python script support..... present ( 3.12.3 )
Python SWIG support....... present
Lua script support........ present ( 5.4.7 )
Lua SWIG support.......... present
[ Model ]
Firmware.................. PM3 GENERIC
[ ARM ]
Bootrom.... Iceman/srt512-emulation/v4.21611-191-g6f45b287...-suspect 2026-05-25 20:00:18 06dfb9e17
OS......... Iceman/srt512-emulation/v4.21611-191-g6f45b287...-suspect 2026-05-25 20:00:16 06dfb9e17
Compiler... GCC 13.2.1 20231009
[ FPGA ]
fpga_pm3_hf.ncd image 2s30vq100 12-05-2026 14:54:54
fpga_pm3_lf.ncd image 2s30vq100 12-05-2026 14:54:54
fpga_pm3_felica.ncd image 2s30vq100 12-05-2026 14:54:54
fpga_pm3_hf_15.ncd image 2s30vq100 12-05-2026 14:54:54
[ Hardware ]
--= uC: AT91SAM7S512 Rev A
--= Embedded Processor: ARM7TDMI
--= Internal SRAM size: 64K bytes
--= Architecture identifier: AT91SAM7Sxx Series
--= Embedded flash memory 512K bytes ( 74% used )
*
[#] BigBuf_size............. 38520
[#] Available memory........ 35756
[#] Tracing
[#] tracing ................ 0
[#] traceLen ............... 35744
[#] Sending buffers
[#] dma8 memory............. 0
[#] dma16 memory............ 0
[#] toSend memory........... 36212
[#] Current FPGA image
[#] mode.................... fpga_pm3_hf.ncd image 2s30vq100 12-05-2026 14:54:54
[#] LF Sampling config
[#] [q] divisor............. 95 ( 125.00 kHz )
[#] [b] bits per sample..... 8
[#] [d] decimation.......... 1
[#] [a] averaging........... yes
[#] [t] trigger threshold... 0
[#] [s] samples to skip..... 0
[#]
[#] LF T55XX config
[#] [r] [a] [b] [c] [d] [e] [f] [g]
[#] mode |start|write|write|write| read|write|write
[#] | gap | gap | 0 | 1 | gap | 2 | 3
[#] ---------------------------+-----+-----+-----+-----+-----+-----+------
[#] fixed bit length (default) | 31 | 20 | 18 | 50 | 15 | n/a | n/a |
[#] long leading reference | 31 | 20 | 18 | 50 | 15 | n/a | n/a |
[#] leading zero | 31 | 20 | 18 | 40 | 15 | n/a | n/a |
[#] 1 of 4 coding reference | 31 | 20 | 18 | 34 | 15 | 50 | 66 |
[#]
[#] HF 14a config
[#] [a] Anticol override........... std ( follow standard )
[#] [b] BCC override............... std ( follow standard )
[#] [2] CL2 override............... std ( follow standard )
[#] [3] CL3 override............... std ( follow standard )
[#] [r] RATS override.............. std ( follow standard )
[#] [m] Magsafe polling............ disabled
[#] [p] Polling loop annotation.... disabled 00000000000000000000000000000000
[#] HF 14b config
[#] [p] Polling loop annotation.... disabled 00000000000000000000000000000000
[#] Transfer Speed
[#] Sending packets to client...
[#] Time elapsed................... 500ms
[#] Bytes transferred.............. 346624
[#] Transfer Speed PM3 -> Client... 693248 bytes/s
[#] Various
[#] Max stack usage..... 3616 / 8480 bytes
[#] Debug log level..... 4 ( extended )
[#] ToSendMax........... 46
[#] ToSend BUFFERSIZE... 2308
[#] Slow clock.......... 30527 Hz
[#] Installed StandAlone Mode
[#] LF HID26 standalone - aka SamyRun (Samy Kamkar)
[#]
[=]
hw tunedoesn't actively tune your antennas.[=] It's only informative.
[=] Measuring antenna characteristics...
🕐 20[#] Buffer cleared (38520 bytes)
🕛 9[#] Buffer cleared (38520 bytes)
[=] -------- LF Antenna ----------⚠️ LF antenna............ unusable
[!]
[=] -------- HF Antenna ----------
[+] 13.56 MHz............. 13.18 V
[+]
[+] Approx. Q factor measurement
[+] Peak voltage.......... 3.8
[+] HF antenna ( ok )
[-] ⛔ All values are zero. Not showing LF tuning graph
[=] Q factor must be measured without tag on the antenna
*
[=] Press pm3 button or to exit
[=] 13060 mV / 13 V / 13 Vmax
Additional context
trace-stock-Handle14443bSampleFromReader.txt
trace-bitcnt31-Handle14443bSampleFromReader.txt
trace-bitcnt8-Handle14443bSampleFromReader.txt