Skip to content

Simulated project deviates from real device behavior #668

Closed
@CoreRasurae

Description

@CoreRasurae

When I was implementing a slave Quad SPI controller with amaranth I found two cases, one where the simulation indicates that the device will work, but in reality it does not, and another where in simulation it indicates that device will not work, but in reality it works.
I have simplified the project and created a simplified serial controller with a FSM that is able to reproduce the issue. (Example can be found in the attachment along with examples of the two issues)

  • Issue 1: The simulation indicates that the device will work, but in reality it does not. This is the scenario in the attached example SimpleFSMClient1. In this case the simulation clock is set to be:
m.d.comb += clkSerial.eq(clkSerialExt & ~simpleFSMClient1.serialCSn)

that is, the serial clock signal is only generated during the data transmission, when the CSn (negated Chip Select) signal is active. In this case, in reality, the FSM misses one extra clock cycle to come out of the FSM Data state into the Idle state, thus failing to make it ready for receiving a second transfer. However in simulation despite the needed and missing extra clock cycle the simulation says it will come to Idle state at the end of transfer, when serialCSn signal goes high/inactive. This simulation result is generated when the signals between the modules are set like so:

simpleFSM.serialCSn = self.serialCSn

The result can be seen here:
sim1IdleStateOnCSnHigh

  • Issue 2: The simulation indicates that the device will not work, but in reality it does work. This is the scenario in the attached example SimpleFSMClient2. In this case the simulation clock is set to be:
m.d.comb += clkSerial.eq((clkSerialExt & ~simpleFSMClient2.serialCSn) | ((simpleFSMClient2.simpleFSM.fsmState != 0) & simpleFSMClient2.serialCSn))

that is, the serial clock is generated only when serialCSn when signal is low/active and an extra clock cycle is generated by the serialCSn signal itself. Here the simpleFSMClient2.simpleFSM.fsmState != 0 condition is not required in reality, but only for simulation, because serialCSn starts low by default, despite the simulation process setting it high as early as it can. The condition ensures that the extra cycle is only generated when the FSM machine has its current state set to a non Idle state.

This scenario was tested with a Xilinx XC7S15 (Spartan-7) and is working properly in the device. The simulation however indicates that the FSM machine will get stuck in the Datastate, thus failing the second transfer. This simulation result is caused when the following serialCSn signal assignment is used:

m.d.comb += simpleFSM.serialCSn.eq(self.serialCSn)

as depicted here:
sim2NonIdleStateOnCSnHigh

In summary:

  • The design always works in simulation when serialCSn is assigned with:
simpleFSM.serialCSn = self.serialCSn
  • The design never works in simulation when serialCSnis assigned with:
m.d.comb += simpleFSM.serialCSn.eq(self.serialCSn)
  • In Issue1 the simulation should get stuck in the Data state at the end of the first transfer
  • In Issue2 the simulation should reach the Idle state at the end of the first transfer

SimpleFSM.txt
SimpleFSMClient1.txt
SimpleFSMClient2.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions