MicroZed Chronicles: Introduction to RTL Simulation and XSIM

No matter how captured (RTL, HLS, Model Driven), all programmable logic designs should start with agreed requirements that define the interfacing and functional performance. Depending upon the target application, the requirements may be significant in defining every aspect operation and behaviour under failure modes. Alternatively, the requirements maybe a cardinal point specification of key performance requirements.

Demonstrating that these requirements have been implemented correctly is often the role of simulation. Simulation enables us to stimulate a programmable logic design and observe its outputs.

So, what is involved in creating a simulation from the beginning? Designing a good simulation requires careful thought as to what is going to be tested and how -- even before we begin to write a line of code.

Test Plan – The test plan defines all the tests which are to be applied to the unit under test. This test plan will include all the tests which will be implemented. Such tests may include:

  • Ensuring clocks and resets are functioning correctly. Example tests may check that the outputs go to the correct reset state when reset is asserted. If the reset is asynchronous, do the outputs go into the correct reset state if no clock is present?

  • For algorithmic algorithms, we may want to enter values which we know will hit corner cases of the algorithms (for example using maximum value inputs to see if the result overflows).

  • For data transfer / buffer, the test plan may test the data when FIFO overflows or if underflows occur. It also may ensure correct behaviour if both the same location in dual-port RAM is written at the same time.

  • Constrained random tests are often used when testing if all the inputs permutations is impossible. Constrained random testing allows you to define coverage conditions to ensure possible conditions are covered by the stimulus.

Ideally the test plan would be written by a different developer other than the developer of the UUT to prevent specific interpretations of the specification from being used. If we are working on a complex algorithm, we may also define a behavioural model which is used to validate the outputs of the UUT on a cycle-by-cycle basis.

Test Bench – The test bench is the vehicle which is used to stimulate the UUT and will typically use a language such as Verilog / System Verilog or VHDL, although there are higher-level languages like E, SystemC and SystemVerilog. While looking at the waveform output is helpful to debug if we find errors. We want the test bench to be self-checking so that it reports pass / fails for tests and an overall status. This self-checking, if written to text files, can be used to demonstrate evidence of design maturity at design reviews. Depending upon what we are testing, we may want the test bench to be able to read in vectors and results from files to provide stimulus and result values. These values and results might be generated by a higher-level algorithm model using Python for example.

Framework – Creating a test bench can be very time consuming since you may need to implement functions to perform constrained random testing and monitor signals for changes or events. Of course, all this needs to be self-checking and documenting. To avoid having to start from scratch, there are several frameworks which can be used to create a test bench or even pull together complete systems.

  • OSVVM – Open Source VHDL Verification Methodology – Verification framework

  • UVVM – Universal VHDL Verification Methodology – Verification framework

  • UVM – Universal Verification Methodology – Verification framework

  • VUnit – Verification framework

  • FuseSOC – Package manager and build tool

We are going to examine all these frameworks over time, I promise.

Now that we understand some of the higher-level elements of verification, let’s get started looking at a simple example in XSIM which is provided free with Vivado.

This will introduce us to the simplest elements and concepts of simulation.

The free XSIM provides us with the ability to simulate VHDL, Verilog, and SystemVerilog and also supports UVM1.2.

To help us understand the system-level aspects, XSIM will also work with Standard Delay Format for timing simulation and with Switching Activity Interchange Format (SAIF) for power estimation.

To demonstrate the basics, I am going to create a simple rolling 8-bit average RTL file and text bench. The created test bench is self-checking, meaning that it reads in stimulus from a stimulus text file along with reading in the expected results also from a text file. It then applies the stimulus to the UUT and checks the outputs considering any latency. The files are available here.

Running a behavioural simulation will result in the output transcript showing the pass or fail of the tests.

If desired, this can be verified in the waveform window where you will see the inputs and corresponding outputs.

However, if our simulation does not function as desired, we can investigate it in either the test bench source or the RTL source using the break points in the sources.

These breakpoints work exactly as they do in the SW world. When we hit a breakpoint, the simulation halts and we can observe the values of signals by holding the cursor over the signal of interest.

Being able to breakpoint the application can aid our understanding of the issues if examining the waveforms and the source code does not clarify the issue. If we are using sub programs (for example functions etc.), then XSIM also provides call and frame call stacks so we can examine processes that are waiting.

Within Vivado Project Manager, we can also run post implementation timing and functional simulations. In this example, I have set the clock to 300 MHz which achieved timing closure in a ZU3-EG device. Selecting the post implementation timing simulation can be achieved once the implementation has completed from the simulation menu.

One of the more advanced things that XSIM enables is the ability to output SAIF files which work with power estimation.

To do this, simply select the SAIF filename under the simulation settings. Once this file has been generated from the behavioural simulation, this can be pulled into the power report to make a more accurate power estimation.

In this example, we have created a simple application and test bench to show how XSIM can be used to verify the performance of the RTL module.

I will be looking at simulation and test bench structures a little more next week!