JTAG Driver tutorial

The jtag_driver entity provides facilities for generating JTAG test logic input excitations for rtl simulations.

Since JTAG test logic can be used for various control and test functions, this entity aims to be as general as possible by providing only the primitive operations required by all JTAG use cases. Concretely, jtag_driver defines operations for controlling a JTAG state machine and leaves the implementation of more complex control sequences to the user of this entity. The circuit designer can define further functions that utilize this minimal driver for generating application specific control sequences for their use case. The benefit of this approach is that multiple different helper objects can share a reference to a single instance of the jtag_driver, which enbles the control of multiple different devices over a single JTAG Test Access Port.

Since JTAG implementations can vary, this entity supports:

  • configurable Instruction Register (IR) width

  • arbitrary Test Data Register (TDR) widths

The jtag_driver class provides helpers for:

  • resetting all JTAG test logic through the dedicated tapRST input

  • primitive functions for moving around the JTAG state machine

  • shifting values to

    • the Instruction Register, and

    • the corresponding Test Data Registers

  • capturing and shifting out parallel inputs

Example

This example shows how to use the jtag_driver to do primitive operations on the JTAG state machine as well as how to define a custom helpers for automating JTAG test operations to control test logic.

When jtag_driver is instantiated, it creates a TheSyDeKick iofile with the name 'jtag_tap' and adds it to self.iofile_bundle. This iofile contains the simulation input excitation that will be fed to the JTAG TAP inputs. The helper functions can then use this shared reference to control the JTAG state machine to accomplish the desired sequence of actions.

To demonstrate how to use the jtag_driver to build a custom simulation controller consider the following design:

  • JTAG configuration

    • IR width = 4

  • Test Data Registers

    • "0000"EXTEST

    • "0001"enable

    • "1111"BYPASS

EXTEST and BYPASS are standard instructions required by the JTAG specification. The instruction "0001" is user-defined for controlling user-defined logic. The parallel output of the TDR selected by IR=0001 is drives an enable signal that is used to control some logic inside the design.

In order to abstract away the JTAG configuration from the simulation controls. We can define a jtag_controller helper class for controlling these signals. This lets us reason about the control in terms of the signal names instead of having to think about them in terms of the associated JTAG instruction numbers.

from JTAG.driver import jtag_driver

class jtag_controller:
    def __init__(self, driver):
        assert isinstance(driver, jtag_driver)
        self.driver = driver

    def enable(self, value):
        self.driver.shift_to_chain(icode_str="0001", tdi_str=value)

The jtag_controller stores a local reference to the jtag_driver. The helper functions use this stored reference to call the driver’s primitive methods for controlling the JTAG state machine to achieve the desired functionality. Here the helper enable simply shifts in the bit given by its value argument to a TDR selected by IR=0001. With this definition, we can use the more descriptively named helpers to control the simulation in the main simulation test bench.

from JTAG.driver import jtag_driver

# instantiate design under test
dut = ...

driver = jtag_driver()
ctrl = jtag_controller(driver=driver)

# control sequence
driver.reset_tap()
driver.reset_to_idle()
ctrl.enable("1")

...

# driver contains the whole JTAG input waveform
dut.IOS.Members['jtag_tap'] = driver.IOS.Members['jtag_tap']

While in this example it is trivial to see that ctrl.enable("1") corresponds to the single action driver.shift_to_chain(icode_str="0001", tdi_str="1"), in larger desigs it becomes tedious to manually track which signals correspond to which instructions. By defining helpers based on jtag_driver, we can reason about the signals in terms of their usage rather than the associated JTAG implementation. Furthermore, it’s possible to define longer control sequences. For example, one could define a helper for uploading data into device memory over JTAG. Once these helpers have been written, they can also be reused in different projects that use similar test logic.