Description
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
- 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 Data
state, 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)
In summary:
- The design always works in simulation when
serialCSn
is assigned with:
simpleFSM.serialCSn = self.serialCSn
- The design never works in simulation when
serialCSn
is 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