Simulation of AVC Vectors

Hint

In order to use the simulation features documented in this section you have to install dumpling with the optional SIM extra:

pip install "./dumpling[SIM]"

Dumpling provides CocoTB integration to simulate generated vectors using CocoTB, a python based testbench framework that interacts with an RTL simulator of your choice. CocoTB performs a cosimulation of python coroutine based testbench code with the simulator through the VPI interface. Being an interpreted language, CocoTB is unarguably slower in simulation execution speed than native SystemVerilog testbenches but using Python as the testbench language has the key advantage of faster test development and a huge ecosystem of existing libraries that support the creation of more complex testpatterns.

While some knowledge in using CocoTB is certainly helpful, this section of dumpling’s documentation only tries to showcase how to simulate vectors generated by the dumpling library or vectors parsed from an existing AVC file.

The Setup

Executing a CocoTB testbench requires two key ingredients:

  1. A Makefile that specifies RTL source files to compile, selects the simulator to use and provides additional parameters to the simulation environment.

2. A pythonmodule containing testbenches (python functions anotated with the cocotb.test decorator) for CocoTB to execute once the Simulator (e.g. Questasim, Xcelium, Verilator and many more) has started.

A sample Makefiles for simple RTL projects

#Simulator Selection and Arguments
TOPLEVEL_LANG=verilog
SIM=questa
SIM_ARGS= -suppress vsim-3009

#Source File Specification
VERILOG_SOURCES = ../../rtl/fancy_module.sv \
             ../../rtl/submodule/some_submodule.sv

TOPLEVEL = fancy_module

#Testbench Module
MODULE=my_cocotb_testbenches # The python module (*.py file) that
                             # contains the various testbenches

#With the Testcase variable we can instruct CocoTB to only execute
#one specific test instead of executing all testbenches found within
#the module
TESTCASE=simulate_avc

#This is the magic line that includes CocoTBs make targets
#It obviously requires CocoTB to be installed
include $(shell cocotb-config --makefiles)/Makefile.sim

CocoTB provides a number of additional Makefile variables that might be usefull for your particular setup. For these additional options we refer to the documentation of CocoTB.

A Sample Makefile for PULP based Projects using Questasim

For larger projects, listing each and every source file in the Makefile might be infeasible. Especially PULP based projects that use the IPApprox IP Dependency management tool would be very cumbersome to use otherwise. However, IPApprox already provides a simulation platform build flow for Questasim. With some additional makefile target and shell magic we can make leverage the existing Questasim libs without the need to manually specify all the required source files:

#Point to the directory of a pre-built RTL platform
#this is the directory were the questasim libs for all
#sub-ips are installed
PLATFORM_INSTALL = pulpissimo/install/modelsim_libs

#Simulator Selection and Arguments
TOPLEVEL_LANG=verilog
SIM=questa
#Add the precompiled libraries as dependencies
LIBS=$(shell find ${PLATFORM_INSTALL}/* -maxdepth 0 -type d -printf "-L %f ")
SIM_ARGS= ${LIBS} #Add your additional args here

#Source File Specification
#Only add the toplevel system verilog source file here
VERILOG_SOURCES = ../../rtl/pulp_chip.sv
TOPLEVEL = pulp_chip

#Register a custom simulation dependency target
CUSTOM_SIM_DEPS=vmap_libs

#Testbench Module
MODULE=my_cocotb_testbenches # The python module (*.py file) that
      # contains the various testbenches

#This is the magic line that includes CocoTBs make targets
#It obviously requires CocoTB to be installed
include $(shell cocotb-config --makefiles)/Makefile.sim

#With the Testcase variable we can instruct CocoTB to only execute
#one specific test instead of executing all testbenches found within
#the module
TESTCASE=simulate_avc

#A custom target that maps all pre-compiled libs to the working directory
vmap_libs:
     @for lib in $(shell find ${PLATFORM_INSTALL}/* -maxdepth 0 -type d -printf "%f "); do vmap $${lib} ${PLATFORM_INSTALL}/$${lib}; done

.. note::

We only specified the SystemVerilog source for the toplevel module. All other
submodules are defined in the libraries which are automatically mapped by the
``vmap_libs`` target and loaded with the ``LIBS`` variable that we supplied
to the ``SIM_ARGS`` variable.

The Testbench Module

The second of the aforementioned ingredients is a python module that contains CocoTB testbenches. Without going further into the details of how to write CocoTB testbenches, after all that is fairly well documented on their project website, we will show a minimal python module with a single testbench that assigns a number of static values to signals not present in our vector file (e.g. bootmode or chip reset) and provides a clock for clock pins and finally applies each vector of an AVC file to the DUT:

# File <my_cocotb_testbenches.py>

import cocotb
from cocotb.triggers import Timer, FallingEdge
from cocotb.result import TestFailure
from cocotb.clock import Clock
from pathlib import Path
from dumpling.Common.Simulation import CocotbVectorDriver

@cocotb.test()
async def test_avc_vectors(dut):
  T_JTAG_PS = int(100e3)
  T_APPL_DELAY = T_JTAG_PS * 0.5  # apply with falling edge T/2
  T_ACQ_DELAY = T_JTAG_PS * 0.05  # sample T*0.05 before rising edge
  clock_wavefun = CocotbVectorDriver.simple_clock_gen_wavefun(T_JTAG_PS, start_high=True, idle_low=True)
  apl_wavefun = CocotbVectorDriver.simple_stimuli_appl_wavefun(appl_delay_ps=T_APPL_DELAY, wave_period_ps=T_JTAG_PS)
  acq_wavefun = CocotbVectorDriver.simple_response_acq_wavefun(acq_delay_ps=T_ACQ_DELAY, wave_period_ps=T_JTAG_PS)
  pins = {
      'chip_reset': {'name': 'pad_reset_n', 'default': '0', 'wavefun': apl_wavefun},
      'trst': {'name': 'pad_jtag_trst', 'default': '1', 'wavefun': apl_wavefun},
      'tms': {'name': 'pad_jtag_tms', 'default': '0', 'wavefun': apl_wavefun},
      'tck': {'name': 'pad_jtag_tck', 'default': '0', 'wavefun': clock_wavefun},
      'tdi': {'name': 'pad_jtag_tdi', 'default': '0', 'wavefun': apl_wavefun},
      'tdo': {'name': 'pad_jtag_tdo', 'default': 'X', 'wavefun': acq_wavefun}
  }
  driver = CocotbVectorDriver(pins, dut)


  # Apply 32kHz reference clock
  ref_clock = Clock(dut.pad_xtal_in, 31250, units='ns')
  cocotb.fork(ref_clock.start())

  # Assign value to signals
  dut.pad_bootsel <= 1
  dut.pad_fll_bypass <= 1
  dut.pad_test_enable <= 0
  dut.pad_scan_enable <= 0

  # Assert reset
  dut.pad_reset_n <= 0
  await Timer(1, units='us') #Advance simulation time by 1us
  dut.pad_reset_n <= 1

  # Apply stimuli parsed from AVC vector file to device under test
  passed = await driver.simulate_avc(Path("execute_hello_world.avc"))

  if not passed:
      raise TestFailure("Missmatch during application of avc file. Check error log above.")

As you can see from the example, applying vectors from an AVC file is fairly easy. We just await on the completion of the simulate_avc() coroutine which will return the value True if there were zero missmatches between simulated response and expected respone (from your AVC vectors) or False otherwise.

Note

The CocotbVectorDriver also contains functions to directly apply vectors generated by the dumpling library witouth going through the roundabout of writing them to AVC. Just await on the apply_vectors() coroutine supplying it with a list of vectors as argument.

Wavefunctions

If you went throught the above example carefully you noticed that the pin declaration dictionary contained some the additional wavefun key. When using dumpling for CocoTB Vector simulation this key is required. The value is a coroutine function that is supposed to mimic the behavior of the ASIC Testers Wavetable (thus the name “wavefunction”). Have a look at the docstring of CocotbVectorDriver for additional information on the nature of these functions. For now it suffices to say that these function contain the logic how a pins state character is supposed to map to a physical waveform or a sequence of signal sampling events. The CocotbVectorDriver provides a selection of reasonable default functions (e.g. simple_clock_gen_wavefun() ) that can be used for most setups. If you are using a more complex wavetable i.e. you are using X-mode you need to provide your own wavefunction so CocotbVectorDriver knows how to translate the vectors to actual waveforms. If you choose the right wavefunction with the right parameters for stimuli application skew and response acquisition skew (the little time delays you introduce in your timing ecquations to account for setup and hold time) it is possible to have the time scale of the RTL simulation perfectly match the ASIC testers timescale and every stimuli application and response acquisition event is simulated excatly at the same time like on the ASIC tester.

Postlayout Simulations

Certain bugs in the hardware might require debugging that goes beyond RTL simulations. Switching to a Postlayout simulation with CocoTB is fairly easy: just supply the postlayout netlist as a source file and add the postlayout sim specific arguments (e.g. loading the standard cell libraries and performing SDF annotation) to the SIM_ARGS Make Variable.