On the internet for a dealing with FPGA programming, I often read the advice to write One-Process state machines only.
And I usually beg to differ. (You shouldn't be surprised ...)
The principal arguments given are that a One-Process state machine description is 'free from latches' and at the same time requires less typing. Now being lazy is a hallmark of a good engineer, but ...
In a lot of cases a One-Process description fits exactly. However in my line of work this is rarely so.
To understand the deeper differences between the One- and Two-Process description we have to go back to Moore and Mealy.
The outputs of a Moore machine depend solely on the current state whereas in a Mealy state machine they depend on both the current state and the curent inputs. As such a Moore machine can be described by a single pure synchronous process. The Mealy machine always needs a second combinatorial process to describe some of the outputs.
A lot of peoaple claim they write One-Process State Machines, but almost always you will see the decoding the present state, sometimes even the next state, in a second (sometimes multiple) combinatorial process and possibly also in a third ((sometimes multiple) third synchronous process. Thus creating Two- and Three- Process State Machines in disguise. Sometimes these additional processes create conditions to be acted ipon by the original One-Process State Machine!
If the design task at hand is complex, things get dispersed and dificult to read / understand / maintain by others. Unfortunately for any code written more than 3 months ago, oneself belongs to the others :)
@hgomersall drew my attention back to a blog from Olof Kindgren: https://olofkindgren.blogspot.com/2017/11/resetting-reset-handling.html where the author documents a clever way to mix signals that need a reset and signals that don't need a reset in a single always @(posedge clk)
process. You won't be surprised if you hear me saying, that I am not a fan. I don't do much Verilog (I progressed from AHDL to VHDL and then on to MyHDL), but sometimes have to read other's work and I am always surprised that Verilog coders tend to create a lot of always @(*)
handling combinatorial code and somewhat less of always @(posedge clk)
processes. So you could easily divide the have-resets and the have-no-resets over two always @(posedge clk)
processes; it would be just one or a few processes more ...
This text is targeted at MyHDL developers.
In the beginning MyHDL mimiced the Verilog method of declaring synchronous processes, and required you to handle the resetting of signals very much as in (both) VHDL and Verilog. Unfortunately the two dominant FPGA vendors differred on their reset approach (amomng other things ...) One preferred asynchronous resets, the other synchronous. Our BDFL sees MyHDL as a tool to develop vendor-independent code and came up with a (I was going to say clever ...) nice solution: @always_seq(clk.posedge, reset)
. Together with a new signal type: ResetSignal(val, active, isasync)
we can now write vendor-agnostic code, deferring the reset instantiation until the simulation and conversion phase. It has one drawback; in such an @always_seq(clk.posedge, reset)
all signals are reset, even the signals that actually wouldn't need a reset. The (obvious?) way to handle is to refactor the signals that don't need a reset to another @always_seq(clk.posedge, reset=None)
process. As we declare reset
as None
no resetting code will be generated nor simulated. Mission accomplished. The explicit reset=None
documents our intention to any future reader.
The Python file 2-Register.py describes what others on the net call a skidbuffer.
In my case it is a block (pun intended) to break the long combinatorial Ready-Valid loop of a long pipelined algorithm. It also helps avoiding deadlock if you are a bit sloppy and not obeing the Ready-Valid rules (as also described in the e.g. AXI-S specification).
I describe it as a Three-Process Stae Machine:
- the first: a combinatorial process starts at line 37. This process combines the present state with the inputs and generates the next state to be registered along, and this is key in muy design work, with other combinatorial outputs.
- the second: a synchronous process starts at line 77. This process does have a reset to properly initialize the present state. It main task is to register the next state in order to become the present state.
- the third: a synchronous process starts at line 85. Note that this could be optional, as shown here, in case there would be no actual data to be transported. Also note that the actual clock enables, to register any data, are actually outputs of the first (combinatorial) process.
You are welcome to peruse the generated VHDL and Verilog code.
In some designs, every LUT input counts. How would this look like with registered outputs for D_Ready and Q_Valid?