Skip to content

Zero-delay combinatorial loop between backend and cache (Iteration limit reached in Vivado XSIM) #4

Description

@filmil

Describe the bug

When simulating the Muntjac core using an event-driven simulator like Vivado XSIM, the simulation fails immediately at $t=0$ with a fatal iteration limit error: Fatal Error: Iteration limit reached. Possible zero delay oscillation detected where simulation time can not advance.

This is caused by a circular combinatorial dependency violating standard valid/ready handshake rules: the backend's req_valid signal combinationally depends on the cache's req_ready signal, but the cache's req_ready signal combinationally depends on req_valid through its internal arbiter.

While cycle-based simulators like Verilator may mask this zero-delay loop, event-driven simulators (like XSIM) get stuck in an infinite evaluation loop within a single delta cycle.

Steps to Reproduce

  1. Instantiate muntjac_core in a testbench.
  2. Simulate using Vivado XSIM.
  3. The simulator immediately aborts at time 0 ps with an iteration limit error.

Root Cause / Trace

The combinatorial loop can be traced as follows:

1. muntjac_backend.sv (Cache Ready blocks Request Valid):

  • Line 1074: assign mem_ready = dcache_d2h_i.req_ready;
  • Line 335: In the OP_MEM case of always_comb, if !mem_ready, struct_hazard is asserted: if (!mem_ready) struct_hazard = 1'b1;
  • Line 407: ex_issue depends on !struct_hazard.
  • Line 1058: dcache_h2d_o.req_valid is gated by ex_issue: assign dcache_h2d_o.req_valid = ex_issue && de_ex_decoded.op_type == OP_MEM;
    (Result: The backend waits for the cache to be ready before asserting valid).

2. muntjac_dcache.sv / muntjac_icache.sv (Request Valid drives Cache Ready):

  • Line 330: wire req_valid = cache_h2d_i.req_valid;
  • Line 2024: (and others) req_valid feeds into mem_a_valid_mult.
  • Line 389-393: mem_a_valid_mult feeds into openip_round_robin_arbiter's request and enable ports.
  • Line 407: The arbiter's grant output (mem_a_arb_grant) drives mem_a_selected (and ultimately mem_a_select).
  • Line 419: mem_a_ready_mult[i] = mem_a_select[i] && mem_a_ready;
  • Line 1248/1802+: mem_a_ready_mult combinationally drives cache_d2h_o.req_ready.
    (Result: The cache waits for a valid request to arbitrate before asserting ready).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions