PR WITH FIX: #443
Issue
With TerminateTransaction = 1'b1, axi_isolate instantiates a demux that routes the slave port either to axi_isolate_inner (port 0, normal operation) or to the axi_err_slv (port 1, while isolated). The demux ports slv_aw_select_i and slv_ar_select_i are driven by isolated_o.
The demux requires its selects to be stable while a request is pending: the ports are documented as "has to be stable, when aw_valid", and axi_demux_simple has slv_aw_select_stable / slv_ar_select_stable assertions for this. However, axi_isolate doesn't guard against violating that requirement, and therefore isolated_o as the select creates the bug: it rises at the end of a drain, which is a window in which the inner module is holding a request unaccepted. So any request that arrives during a drain and isn't accepted before it finishes draining produces the violation, firing the assertion.
For a write arriving while the inner module is draining:
- While draining,
axi_isolate_inner holds aw_ready low, so an AW presented during this window sits at the demux unaccepted for as long as the drain takes. When the drain finishes, isolated_o rises, and the select changes while the request is still pending.
- The demux does not use the select the same way for the AW and W. The AW is routed by the live select, but the W beats follow
w_select_q, which is latched when the AW is first presented (w_cnt_up increments regardless of aw_ready). Normally these agree because the select is stable for the whole handshake; here it changes mid-handshake.
- So the AW completes on port 1 (
axi_err_slv), while the W beats are sent to port 0 (axi_isolate_inner), which is now isolated with w_ready low. The error slave waits for write data that never arrives, the inner module never accepts it, no B response is ever returned, and the master hangs with w_valid stuck high. De-isolating does not recover this: the error slave is still waiting on W beats that are routed to port 0.
- The AR path violates the same stability assumption (an AR pending across the
isolated_o edge trips slv_ar_select_stable), though it happens to complete since reads don't have split routing.
Waveform
- One write is in flight (
pending_aw_q = 1) when isolation is requested; the AW FSM enters Drain.
- The master presents a new AW during the drain.
isolated_o is still 0, so the demux sends it to port 0, locks the decision (lock_aw_valid_q), and latches w_select_q = 0. The inner module holds aw_ready low so the handshake cannot be completed.
- The drain completes and
isolated_o rises with the AW still pending. (The slv_aw_select_stable assertion would fire here)
- The AW handshake then completes on port 1 (
mst_reqs_o[1].aw_valid/aw_ready).
- The error slave raises
w_ready on port 1 and waits, but the W beats are offered on port 0 (mst_reqs_o[0].w_valid high, w_ready never comes). No B is returned and the master's w_valid (and subsequent aw_valid) hang for the rest of the run.
Proposed fix
Drive the demux selects from a registered version of isolate_i that only updates on cycles where the respective channel has no pending unaccepted request, so the select is stable for the full duration of any handshake.
I have this implemented and am validating it locally, and can follow up with a PR once it's verified.
PR WITH FIX: #443
Issue
With
TerminateTransaction = 1'b1,axi_isolateinstantiates a demux that routes the slave port either toaxi_isolate_inner(port 0, normal operation) or to theaxi_err_slv(port 1, while isolated). The demux portsslv_aw_select_iandslv_ar_select_iare driven byisolated_o.The demux requires its selects to be stable while a request is pending: the ports are documented as "has to be stable, when aw_valid", and
axi_demux_simplehasslv_aw_select_stable/slv_ar_select_stableassertions for this. However,axi_isolatedoesn't guard against violating that requirement, and thereforeisolated_oas the select creates the bug: it rises at the end of a drain, which is a window in which the inner module is holding a request unaccepted. So any request that arrives during a drain and isn't accepted before it finishes draining produces the violation, firing the assertion.For a write arriving while the inner module is draining:
axi_isolate_innerholdsaw_readylow, so an AW presented during this window sits at the demux unaccepted for as long as the drain takes. When the drain finishes,isolated_orises, and the select changes while the request is still pending.w_select_q, which is latched when the AW is first presented (w_cnt_upincrements regardless ofaw_ready). Normally these agree because the select is stable for the whole handshake; here it changes mid-handshake.axi_err_slv), while the W beats are sent to port 0 (axi_isolate_inner), which is now isolated withw_readylow. The error slave waits for write data that never arrives, the inner module never accepts it, no B response is ever returned, and the master hangs withw_validstuck high. De-isolating does not recover this: the error slave is still waiting on W beats that are routed to port 0.isolated_oedge tripsslv_ar_select_stable), though it happens to complete since reads don't have split routing.Waveform
pending_aw_q = 1) when isolation is requested; the AW FSM entersDrain.isolated_ois still 0, so the demux sends it to port 0, locks the decision (lock_aw_valid_q), and latchesw_select_q = 0. The inner module holdsaw_readylow so the handshake cannot be completed.isolated_orises with the AW still pending. (Theslv_aw_select_stableassertion would fire here)mst_reqs_o[1].aw_valid/aw_ready).w_readyon port 1 and waits, but the W beats are offered on port 0 (mst_reqs_o[0].w_validhigh,w_readynever comes). No B is returned and the master'sw_valid(and subsequentaw_valid) hang for the rest of the run.Proposed fix
Drive the demux selects from a registered version of
isolate_ithat only updates on cycles where the respective channel has no pending unaccepted request, so the select is stable for the full duration of any handshake.I have this implemented and am validating it locally, and can follow up with a PR once it's verified.