SANBlaze I3C Controller Platform
V1535 Release Notes
Release Date: October 2026
Overview
V1535 carries forward V1534’s headline capability - surviving a live NVMe firmware update - and adds the fix that makes I3C 12.5 MHz reliable on the level-shifter riser. Surviving a live firmware update works as follows. When a drive is sent a Firmware Activate command it reports More Processing Required, drops off the bus to apply the image, and rejoins some seconds later - often in a different state than it left. Earlier releases lost the drive across that window: the link went quiet and the host could not recover it without operator intervention. V1535 keeps watch across the reboot and brings the drive back automatically, so a firmware update completes end to end without a manual re-enumeration or a power cycle.
The recovery is built on a host-armed device-presence poller and a set of wake-and-re-enumerate steps that handle the several ways a drive can come back: still in I2C-passive mode, back in I3C but with its dynamic address dropped, or back believing it still owns its old address and ignoring normal traffic. Each of these is detected and handled without host involvement beyond arming the poller once.
V1535 also completes I3C 12.5 MHz support across both iRiser6i signal-path variants: V1534 fixed enumeration on the level-shifter-bypassed riser, and V1535 fixes sustained data transfer on the level-shifter riser (short transfers passed but 4K transfers failed intermittently). It further hardens I2C receive timing across the full 100 kHz / 400 kHz / 1 MHz range and restores live on-the-wire GETPID reads while keeping the status command silent on the bus.
V1535 also delivers a set of enumeration, CLI and bring-up script changes driven
by customer reset and bring-up testing. The hot_join switch now turns off all
automatic enumeration, so a test can drive address assignment entirely by hand.
ENTDAA now completes when the device raises a Hot-Join request at the moment
ENTDAA is issued, instead of reporting 0 targets. GETSTATUS, GETMWL and GETMRL
now exit non-zero when the device returns no data. sb_i3c_init no longer
power-cycles the drive when switching to I3C, with a new -P option to force a
power cycle. The force_hjn command, which no longer reached the bus, is removed.
V1535 preserves the V1531/V1532 read contract and ring buffer frame format. The
command surface is unchanged except that force_hjn is removed and three CCC
read commands now report failure through their exit code. Existing host code
runs unchanged, the default bus speed and default behavior are unchanged, and
I3C data-phase behavior is unchanged.
This release supersedes V1534. It is V1534 plus the level-shifter 12.5 MHz data fix and the enumeration, CLI and bring-up script changes described below.
What’s New
Live firmware update (LFU) survival
A drive that is sent an NVMe Firmware Activate goes offline to apply the image and rejoins the bus afterward. V1535 keeps the controller-to-drive link alive across that reboot and re-establishes the drive automatically.
The mechanism is a device-presence poller the host arms once, before starting the update:
(
: ${SLOT:?Set your slot number with SLOT=N first}
# Arm the poller on the drive's dynamic address, then start the update.
sb_i3c -d $SLOT poll_on 0x1d
# ... issue the NVMe Firmware Activate as usual ...
)
Once armed, the poller is silent while the drive is present and answering. When the drive departs (the response ring goes empty across the reboot window), the poller begins waking it, brings it back, and returns to its idle armed state - ready for the next update. It stays armed until explicitly turned off:
sb_i3c -d $SLOT poll_off
The poller retains the drive’s dynamic address independently of the enumeration table, so it can direct a wake to the correct address even after the drive has dropped off the bus. No host polling loop, no manual re-enumeration, and no power cycle is required to complete the update.
Automatic recovery across all drive come-back modes
A drive returning from a firmware reboot can rejoin in more than one state. V1535 detects and handles each:
I2C-passive. The drive reverts to behaving as an I2C target and will not rejoin the I3C bus until it observes an I3C frame. The poller issues a directed GETSTATUS to the drive’s retained address; that frame re-arms the drive’s Hot-Join, the drive rejoins, and the controller re-assigns its address automatically.
Rejoined without an address. The drive Hot-Joins on its own; the controller’s existing autonomous Hot-Join handling assigns the dynamic address.
Rejoined but unresponsive (address retained). The drive comes back believing it still owns its old address and ignores the directed GETSTATUS. After several unanswered attempts the poller escalates to a bus reset (RSTDAA) followed by a fresh enumeration (ENTDAA), which forces the drive to surrender the stale address and take it again. The reset and the enumeration are issued as separate bus operations so the drive settles between them.
Throughout recovery, if the drive delivers any data to the ring, the poller treats it as alive and stops escalating - so a normal, self-recovering update is never disturbed by a redundant reset.
Automatic enumeration control (hot_join)
The hot_join switch, added in V1534 to gate autonomous Hot-Join handling, now
controls all automatic enumeration:
sb_i3c -d $SLOT hot_join 1 # enable (default) - automatic enumeration on
sb_i3c -d $SLOT hot_join 0 # disable - the controller never runs ENTDAA on its own
With automatic enumeration enabled (the default), the controller assigns an address in three situations without being asked: when a device raises a Hot-Join request; before a GET-type CCC (GETPID, GETBCR, GETDCR, GETSTATUS, GETMWL, GETMRL) sent to an address it has no record of; and after such a CCC fails, to re-enumerate a device that was reset behind its back. This keeps routine traffic working across resets, but it also hides those resets from a test that is trying to observe them. For example, after RSTDAA a GETSTATUS to the old address succeeded because the controller quietly re-enumerated the device first.
With hot_join 0, none of these happen. A CCC sent to an address the device has
given up goes to the wire as-is and its NAK is reported as a failure, and ENTDAA
runs only when you issue it. Hot-Join requests are still recorded in the error
log (hjn_request, then hjn_ignored_disabled), so a test can confirm whether
the device raised one:
(
: ${SLOT:?Set your slot number with SLOT=N first}
sb_i3c -d $SLOT hot_join 0
sb_i3c -d $SLOT i3c_err_clear
# ... apply the reset under test ...
sb_i3c -d $SLOT i3c_err_get # no hjn_request: no Hot-Join raised
sb_i3c -d $SLOT targetreset # broadcast 0x7E (RSTDAA)
sb_i3c -d $SLOT i3c_err_get # hjn_request, hjn_ignored_disabled
sb_i3c -d $SLOT entdaa 0x1d # assign the address manually
sb_i3c -d $SLOT hot_join 1 # restore the default
)
The device-presence poller (poll_on) is an explicit opt-in and is not affected
by this setting. The status command reports the setting as
Hot Join: enabled|disabled (auto-enum: hot-join + pre-enum).
ENTDAA completes when a Hot-Join request is pending
A device that has just been woken (for example by the broadcast RSTDAA that
sb_i3c_init sends) raises a Hot-Join request a few milliseconds later. If an
ENTDAA was issued at that moment, the controller saw the bus busy and gave up,
and entdaa reported 0 targets even though the device was ready to join. With
automatic enumeration enabled this was usually masked, because the next command
re-enumerated the device in the background; with it disabled, the device could
not be enumerated at all.
V1535 accepts the pending Hot-Join request and completes the ENTDAA, so the
entdaa command and the ENTDAA step in sb_i3c_init report the device that was
actually assigned. A request from an already-addressed device (a normal IBI) is
not consumed this way; ENTDAA still declines to start in that case.
Recovery after an NVMe Subsystem Reset
After an NVMe Subsystem Reset, the drive needs two broadcast 0x7E frames before it raises its Hot-Join request, which arrives about 10 ms after the second one. The recommended sequences are:
(
: ${SLOT:?Set your slot number with SLOT=N first}
# Default (automatic Hot-Join handling): wake twice, then let the controller enumerate
sb_i3c -d $SLOT entdaa 0x1d # first 0x7E (wake)
sb_i3c -d $SLOT entdaa 0x1d # second 0x7E; the drive raises Hot-Join after this
sleep 0.1
sb_i3c -d $SLOT getpid 0x1d # the drive answers at 0x1D
# Manual (hot_join 0): a third ENTDAA accepts the pending Hot-Join
sb_i3c -d $SLOT hot_join 0
sb_i3c -d $SLOT entdaa 0x1d
sb_i3c -d $SLOT entdaa 0x1d
sb_i3c -d $SLOT entdaa 0x1d # 1 target discovered
sb_i3c -d $SLOT hot_join 1
)
In the default sequence, keep the short wait: other commands issued while the drive is holding the bus for its Hot-Join request fail.
sb_i3c_init: no power cycle when switching to I3C
sb_i3c_init previously power-cycled the drive on every run. That is not
required to bring a drive into I3C mode, and it adds a device reset that a test
of the mode transition does not want. V1535 changes the default:
| Current host mode | sb_i3c_init -p |
Power cycle |
|---|---|---|
| I2C | i3c |
No |
| I3C | i3c (re-initialize or change clock) |
No |
| I3C or I2C | i2c |
Yes |
| Cannot be read | either | Yes |
When the drive still holds a dynamic address, the broadcast RSTDAA step clears it
before ENTDAA, so enumeration is unaffected. A new -P option forces the power
cycle in any case, for a known starting state or to recover a drive in an
unknown state:
sb_i3c_init -d $SLOT -p i3c -P
mi -T i3c runs sb_i3c_init when the slot is not already in I3C mode, so it
inherits the new behavior.
Exit codes for short CCC replies
getstatus, getmwl and getmrl now exit with status 1 when the device returns
no data or fewer bytes than expected. Previously they printed
got 0 bytes (expected N) and exited 0, so a script could not detect the
failure.
force_hjn removed; RSTACT through the pass-through command
The force_hjn command was documented as sending RSTACT plus the Target Reset
Pattern. Since the device-presence poller was added, its control code has been
handled as a poller request instead, so it placed nothing on the bus while still
reporting success. It is removed. RSTACT can be sent with the broadcast pass-through command (defining
byte 0x00 = no reset, 0x01 = reset the I3C peripheral, 0x02 = reset the whole
target):
sb_i3c -d $SLOT i3c_sdr_ccc_bc_write 0x2A <defining_byte>
Named RSTACT and Target Reset Pattern commands are planned for a future release.
I3C 12.5 MHz enumeration on level-shifter-bypassed risers (from V1534)
On risers whose signal path bypasses the level shifters (the iRiser6i on the SBExpress-6SA), I3C enumeration failed at 12.5 MHz: ENTDAA found no targets and the drive never came up, although the same firmware enumerated cleanly at 12.5 MHz on the iRiser6SE. Data transfer to an already-addressed drive was not affected; only the address-assignment phase failed.
The address-less setup phases of I3C - the wake broadcast, the Hot-Join address read, ENTDAA, and target reset - run in the bus’s slower open-drain mode, which is inherently more sensitive to edge rate than the push-pull data phase. On the direct-signal-path riser those open-drain edges could not be read reliably at 12.5 MHz. V1534 runs those setup phases at a reduced clock while leaving the data phase at the configured rate, matching how I3C separates open-drain arbitration from push-pull data. Enumeration and sustained 12.5 MHz data both now hold on the 6i; the 6SE is unaffected.
Verified on the iRiser6i / 6SA with an NVMe drive: 10 of 10 enumerations succeeded at 12.5 MHz with correct BCR and PID each time, followed by a full 4.25 MB firmware image download at 12.5 MHz.
I3C 12.5 MHz sustained data on the level-shifter riser (new in V1535)
On the iRiser6i variant that has level shifters in the signal path, 12.5 MHz behaved differently from the bypassed-shifter variant above: the drive enumerated and short transfers passed, but sustained 4K transfers (a 4K Identify is about 57 packets) failed intermittently - roughly 15-25% success. A single packet made it through; a long transfer accumulated a marginal bit and dropped. Lower clocks were solid (11 MHz always passed, 12 MHz marginal), which pinned the cause to timing margin at the top clock rather than a logic fault.
At 12.5 MHz the controller’s bit clock runs at one system-clock per bit - the maximum rate, with no oversampling headroom - so the input sampling instant is maximally sensitive to any added delay. The level shifter contributes a fixed propagation delay each direction; the SDA pin’s input Schmitt trigger added a further small input delay. Together these pushed the data valid window past the sample point often enough to corrupt long transfers. V1535 turns off the SDA input Schmitt trigger (applied once at initialization), which returns enough of the sampling margin to make 12.5 MHz reliable through the shifter path.
Verified across the full qualification matrix on BOTH riser variants (level shifters in circuit, and bypassed), all I2C and I3C speeds, short and long, with extended soak runs: every case passes with no failures and no warnings. The change is global (it is not gated to a bus mode) because it was confirmed not to regress the slower open-drain phases or I2C on either riser.
Pad drive strength (already at the 12 mA maximum), slew rate (already fast), and bus termination (all settings swept) were evaluated and did not resolve it; the Schmitt-trigger change is the fix.
I2C receive timing hardened across all speeds
The multi-packet MCTP receive path introduced in V1532 carried timing constants tuned for 100 kHz. Under systematic qualification, the extremes of the supported I2C range showed failures that the middle rate did not - a signature of fixed time constants rather than signal integrity. V1535 scales the receive and end-of-transaction timing to the configured bus speed and gates the receive engine’s recovery on the detected bus boundary rather than a fixed schedule.
Qualification now passes clean across the full matrix:
| Mode | Speed | MCTP short | MCTP long (4K Identify) |
|---|---|---|---|
| I2C | 100 kHz | Pass | Pass |
| I2C | 400 kHz | Pass | Pass |
| I2C | 1 MHz | Pass | Pass |
| I3C | 4 MHz | Pass | Pass |
| I3C | 8 MHz | Pass | Pass |
| I3C | 12.5 MHz | Pass | Pass |
GETPID returns to the wire by default
GETPID had been changed to read from a cached value so that the frequently polled status command would not place a GETPID transaction on the bus each time. That silenced status but also stopped GETPID from ever performing a live read.
V1535 restores the original behavior for direct callers: getpid (and every
caller other than status) performs a live GETPID on the wire and returns the
drive’s current PID. The status command alone reads from the cache and remains
silent on the bus. A live read refreshes the cache, so status continues to
report the current value.
What’s Fixed
Drive lost across a firmware update
Sending a Firmware Activate left the drive unreachable after it rebooted: the link went quiet and only a manual re-enumeration or power cycle recovered it. See “Live firmware update (LFU) survival” and “Automatic recovery across all drive come-back modes” above. This is the primary fix carried into V1535 from V1534.
I3C enumeration failed at 12.5 MHz on bypassed-shifter risers
ENTDAA found no targets at 12.5 MHz on the iRiser6i / 6SA. Fixed by running the open-drain setup phases at a reduced clock. See “I3C 12.5 MHz enumeration on level-shifter-bypassed risers” above.
I3C 12.5 MHz sustained 4K transfers failed on the level-shifter riser
On the level-shifter iRiser6i variant, short transfers passed at 12.5 MHz but 4K transfers failed intermittently (~15-25% success). Fixed by turning off the SDA input Schmitt trigger to recover sampling margin at the top clock rate. See “I3C 12.5 MHz sustained data on the level-shifter riser” above. This is the primary new fix in V1535.
I2C failures at the speed extremes
I2C traffic failed at 100 kHz (long transfers) and 1 MHz (short and long) while 400 kHz passed. Fixed by scaling the receive timing to the bus speed. See “I2C receive timing hardened across all speeds” above.
GETPID never reached the wire
A caller needing a live PID read got the cached value instead. Fixed with a cache-versus-wire selection; status uses the cache, all other callers go to the wire. See “GETPID returns to the wire by default” above.
ENTDAA reported 0 targets while the device was joining
An ENTDAA issued while the device was raising a Hot-Join request gave up and
reported 0 targets. This was most visible right after sb_i3c_init’s wake-up
reset and after an NVMe Subsystem Reset, where the trace showed a successful
assignment (made by the controller’s background handling) while the command
output said 0 targets. ENTDAA now accepts the pending request and completes.
Manual enumeration not possible with automatic enumeration off
With hot_join 0, a CCC sent after RSTDAA still succeeded because the
controller re-enumerated the device in the background, and a manual ENTDAA could
not enumerate a device that was raising Hot-Join. Both are fixed; see
“Automatic enumeration control” above.
CCC read commands exited 0 on failure
getstatus, getmwl and getmrl returned exit status 0 when the device
returned no data. They now return 1.
force_hjn reported success without sending anything
Removed; see “force_hjn removed; RSTACT through the pass-through command”
above.
sb_i3c_init power-cycled the drive when switching to I3C
Removed for switches to I3C; see “sb_i3c_init: no power cycle when switching
to I3C” above.
Non-ASCII characters in tool output
Status and informational strings contained Unicode punctuation (em dashes and similar) that rendered as garbage once the output left a UTF-8 terminal - in the HTML qualification report, in logs viewed under other encodings, and in scraped command output. All firmware and host CLI strings are now plain ASCII.
Known Limitations
After I3C enumeration, a drive’s I2C side goes quiet until a power cycle
Once a drive has been enumerated on the I3C bus (via ENTDAA or SETDASA), it operates as an I3C device and its I2C-side functions - including an on-board EEPROM at its I2C address - stop answering. RSTDAA does not restore the I2C side; only a power cycle does. A test or workflow that switches to I3C and then expects plain I2C access to the same drive must power-cycle the drive back to I2C first. This is drive behavior, exposed by mode switching, not a controller defect.
First ENTDAA after an NVMe Subsystem Reset ends after the 0x7E header
Immediately after an NVMe Subsystem Reset, the drive acknowledges the first
broadcast 0x7E with SMBus timing, after the point where the controller samples
the acknowledge. The controller reads it as a NAK and ends the transfer with a
STOP instead of continuing with the ENTDAA code, so the first entdaa reports
0 targets. The drive still sees the 0x7E, so the wake-up works; follow the
recovery sequence above. Correct handling of this acknowledge is planned for a
future release.
Host device information must be refreshed after a firmware update
Updating firmware re-enumerates the iRiser on USB, and its I2C bus number
(/dev/i2c-N) can change. Host tools read the bus number from the cache that
sb_i3c_find_all writes. If discovery is not re-run after an update, status
shows the old bus number and tools that use it (including i2cdetect,
i2ctransfer and the test suite) address the wrong bus. Always run
sb_i3c_find_all after sb_i3c_update; see Upgrade Path.
I3C 12.5 MHz is validated but remains setup-sensitive
12.5 MHz enumeration and sustained data are now validated on the iRiser6SE and on both iRiser6i / 6SA variants (level shifters in circuit, and bypassed), short and long, across extended soak runs. That said, 12.5 MHz is the top supported clock and runs with the least timing margin, so it stays sensitive to the physical setup - cabling, bus loading, added instrumentation, and drive can all affect it. Marginal signal integrity can produce a hard hang rather than a clean error. Validate at a candidate clock on the actual setup before relying on it; if a given setup proves marginal at 12.5 MHz, a moderate clock (for example 11 MHz) trades a little bandwidth for margin.
SETDASA to the controller’s own target NAKs at 12.5 MHz
A SETDASA directed at the controller’s own local target address is NAK’d when the I3C clock is set to 12.5 MHz; it succeeds at the default 8 MHz. This is a pre-existing condition, present in prior firmware, and affects only the local target-mode self-test path, not drive enumeration or data transfer.
1 MHz support is drive-dependent
Not every drive that accepts a 1 MHz Configuration Set operates correctly at that rate. Confirm with a short traffic run after setting the speed, not with the Configuration Get read-back alone. All drives tested operate correctly at 100 kHz and 400 kHz. Use 400 kHz for data transfer; 1 MHz is suitable for speed validation and for drives confirmed to support it.
Bus speed is not preserved across a power cycle
Both the controller and the drive return to their default speed after a power cycle. Re-apply both settings as part of bring-up; setting only one leaves the two mismatched, which presents as a bus that enumerates but cannot carry traffic.
FW download throughput and I3C clock
A full 4.25 MB image downloads in roughly 80 seconds with per-chunk validation enabled. The per-packet cost is dominated by host/USB transfer overhead rather than the I3C wire, so the I3C clock rate has little effect on total download time. Select the I3C clock for reliability, not speed. See User Guide section 7 for the procedure to choose a clock on a given setup.
HDR-DDR mode not supported
V1535 operates in SDR mode only. HDR-DDR is scheduled for a future release.
Compatibility
Firmware
Runs on all iRiser hardware supported by V1531/V1532 - no hardware requirement changes.
Validated on the iRiser6SE and the iRiser6i (SBExpress-6SA), both level-shifter and bypassed-shifter variants.
No new memory or resource requirements.
Host CLI (sb_i3c)
Adds three commands:
poll_on,poll_off,hot_join.hot_join 0now also disables the controller’s background ENTDAA (see “Automatic enumeration control”).Removes
force_hjn, which no longer reached the bus. Usei3c_sdr_ccc_bc_write 0x2A <defining_byte>for RSTACT.getstatus,getmwlandgetmrlexit 1 when the device returns no data.The status
Hot Join:line carries a(auto-enum: hot-join + pre-enum)suffix; the label itself is unchanged for scripts that match on it.sb_i3c_initno longer power-cycles when switching to I3C and adds-Pto force a power cycle.getpidbehavior is restored to a live wire read (its pre-cache behavior);statusis unchanged and remains silent on the bus.
Ring buffer and host read path
Frame format is unchanged: one framed entry per transaction, one entry per read, in arrival order.
A stale or late response carrying a prior request’s tag is now dropped rather than delivered against a new request; this is internal and requires no host change.
The USB command set is compatible; new control requests were added for the poller and Hot-Join control and do not affect existing requests.
No host or driver change is required to receive multi-packet messages or to benefit from LFU survival beyond arming the poller.
Test suite (sb_i3c_test)
Update
EXPECTED_VERSIONto1535and include1535inACCEPTED_VERSIONS.The suite’s mode-switch helper passes
-Ptosb_i3c_init, keeping the power cycle on every mode switch that the tests were written for.Run
sb_i3c_find_allafter flashing and before running the suite, so it uses the correct I2C bus.All prior tests run unchanged. The EEPROM read test now power-cycles a drive back to I2C mode if a preceding I3C test left it enumerated (see the I2C-side limitation above).
Existing customer workflows
V1531/V1532 host code runs without modification.
I3C data-phase behavior is unchanged.
Default bus speed is unchanged at 100 kHz. Higher speeds are opt-in.
The device-presence poller is off unless armed; a workflow that does not arm it sees prior behavior.
Upgrade Path
Standard firmware update:
sb_i3c_update -d $SLOT /etc/iRiser/sanblaze_i3c_fw_v1535.uf2
Then re-run device discovery:
sb_i3c_find_all
Host-side device information - including reported firmware version - is discovered at boot and cached. After updating firmware in place, refresh the cache so host software selects V1535 behavior:
Discovered devices:
slot=2 usb=1-5.2.7.4 tty=/dev/ttyACM3 ver=1535 i2c=/dev/i2c-2
Confirm ver= matches the firmware just installed, and that i2c= shows the
bus the iRiser is on now, before running traffic against the device. The bus
number can change across a firmware update; without this step, host tools may
address the wrong /dev/i2c-N.
No configuration migration is required. Persistent settings (clock frequencies in
/tmp/NVMe/iRiser/<slot>/) are format-compatible.
Support
For questions, defect reports, or feature requests, contact SANBlaze support with the output of:
sb_i3c -d $SLOT status
sb_i3c -d $SLOT i3c_err_get
cat /tmp/NVMe/iRiser/$SLOT/i3c_*
For LFU/recovery issues, also capture the poller state and a debug log across the update window:
sb_i3c -d $SLOT status | grep -i poll
The V1535 firmware version string is V1535. Confirm with:
sb_i3c -d $SLOT status | grep Version