The following is the simplified design flow for digital circuits.
- Design Entry
- Behavioral Simulation
- Functional Simulation
- Place and Route
- Timing Simulation
Synthesis is the process of turning the HDL code into circuitry. A software analogy for this is the compilation process that turns software code into machine binary. It is one part of the overall hardware design flow.
Behavioral simulation is mostly concerned with how the hardware is supposed to work. At this stage, there isn’t any circuit yet as the entire design merely exists as HDL code. The purpose of this simulation is to ensure that our design is capable of performing the work it needs to do. The synthesis process is used to reduce the HDL code into circuitry.
Functional simulation can be used to simulate the functionality of the circuits. The purpose of this simulation is to ensure that the circuitry produces the correct output to the correct inputs at the right time. But any timing information obtained at this point is fictional as things are simulated under ideal conditions. Place and route will actually figure out how to physically realize the circuitry.
Timing simulation will then be able to give us a much more accurate picture of the performance of the design under various best-case and worst-case scenarios. The purpose of this simulation is to determine whether the circuit is capable of performing within the speed requirements. If things pass, then the design can be implemented in real-life.
The most common problem with most novice HDL designers is the use of non-synthesis-able HDL. While the language may be large, there is only a small sub-set of the entire language that can be used for synthesis. The rest of the language may be useful for other purposes e.g. simulation, or interfacing with third-party libraries.
During simulation, a number of language features are available to make life easier e.g. by allowing the designer to programmatically craft and feed complex inputs into the design; and by allowing the designer to write outputs to external files, which can often be inspected with a waveform viewer or post-processed with custom code.
So, we often end up writing synthesisable HDL in one module and then wrapping it with another module that contains the non-synthesisable code, which is mainly used for behavioral simulation. This allows us to turn the actual HDL code into circuits while taking out the code used only for simulation. Another way would be to wrap the non-synthesisable code inside some conditional define that is only set during simulation but this is messier.
This is similar with writing the unit-test code in software – where the unit-test framework is often provided as a wrapper around the run-time code. This allows the testing of the module to be separated out from the run-time code.
Just like in software compilation, the synthesis process can take the same input HDL code and reduce it into different circuits e.g. when the input code has some sort of combinatorial logic, the synthesis tool can turn it into either the minterm or maxterm circuits, or something else in between; or when the input code has some addition arithmetic logic, the synthesis tool has several adder architectures to choose from.
Which circuit ends up being chosen in the end, depends on constraints. Synthesis is often referred to as a constraint driven process as the timing and area constraints have a great influence on the resultant output. We will be looking into this in more detail in subsequent posts.