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.