JTAG interface modules for Cocotb
GitHub repository: https://github.com/daxzio/cocotbext-jtag
Introduction
JTAG simulation models for cocotb.
Installation
Installation from pip (release version, stable):
$ pip install cocotbext-jtag
Installation from git (latest development version, potentially unstable):
$ pip install https://github.com/daxzio/cocotbext-jtag/archive/main.zip
Installation for active development:
$ git clone https://github.com/daxzio/cocotbext-jtag
$ pip install -e cocotbext-jtag
Documentation and usage examples
See the tests directory for complete testbenches using these modules.
OpenOCD socket test (local)
To run the test where OpenOCD connects to the cocotb JTAG socket (remote bitbang):
-
Prerequisites: Icarus Verilog, OpenOCD, Python deps:
pip install -r requirements.txt pip install -e . # Optional: sudo apt-get install openocd # for full automated test
-
Full test (sim in background, then OpenOCD):
./run_openocd_socket_test.sh
-
Sim only (you run OpenOCD in another terminal):
./run_openocd_socket_test.sh --sim-only # In another terminal: cd tests/test_socket && openocd -f openocd_remote_bitbang.cfg -c "init; exit"
JTAG Bus
The JTAGBus is used to map to a JTAG interface on the dut. These hold instances of bus objects for the individual channels, which are currently extensions of cocotb_bus.bus.Bus. Class methods from_entity and from_prefix are provided to facilitate signal default name matching.
Required:
- tck
- tms
- tdi
- tdo
Optional:
- trst
JTAG Driver
The JTAGDriver class implements a JTAG driver and is capable of generating read and write operations against JTAG devices, either singularly or in a chain.
To use these modules, import the one you need and connect it to the DUT:
from cocotbext.jtag import JTAGDriver, JTAGBus, JTAGDevice
bus = JTAGBus(dut)
jtag_driver = JTAGDriver(bus)
jtag_driver.add_device(JTAGDevice())
The first argument to the constructor accepts an JTAGBus object. These objects are containers for the interface signals and include class methods to automate connections.
Once the module is instantiated, read and write operations can be initiated in a couple of different ways.
Additional optional arguments for JTAGDriver
- period: Clock frequency period of
tck, default100 - units: Clock units, default
ns - logging_enabled: Logging enable, default
True
Methods
add_device(device): Add device to jtag chain, must be of typeJTAGDeviceset_reset(num): Reset for num if trst is present inJTAGBus, raise warning if trst is not presentreset_finished(): Async wait until reset is finishedreset_fsm(): Send 5 tck pulses while tms is held high inJTAGBus, this resets the finite state machine inside a JTAG TAPsend_val(addr, val, device, write): Send addr to device (default:0). The val is used to write if write is True or verify against if write is Falsewrite(addr, val, device=0): Write val to addr of device (default:0).read(addr, val=None, device=0): Read from addr of device (default:0). If val present verify against returned value.read_idcode(device=0, retry=0): Read the IDCODE register for device and check it against the value configured on that device’sJTAGDevice. Withretry=0(default), a single read is performed. With a positiveretry, the driver may repeat an IDCODE shift and TAP reset until the captured value matches or the attempt limit is reached (see below).capture_ir(): Return the Instruction Register value captured during the most recent IR shift operationcapture_dr(): Return the Data Register value captured during the most recent DR shift operation
IDCODE retries (read_idcode(..., retry=N))
Use a positive retry when the first IDCODE read after reset or bring-up is unreliable. That often happens when the TAP or chain needs a few cycles to settle: power-on, asynchronous reset release, clock-domain crossing into the JTAG logic, or simulator startup ordering can produce a wrong or unknown value on the first DR capture even though the DUT is otherwise correct.
With a positive retry, the driver repeatedly loads the IDCODE instruction, captures the DR, compares the result to the expected idcode on the active JTAGDevice, and runs a short TAP reset between attempts. If a match is found before the limit, it continues with a normal verified read; if not, it raises after N failed attempts. When the TAP is already stable, keep the default retry=0 to avoid extra cycles.
JTAG Device
JTAGDriver needs to be told what devices are in the JTAG chain it is communicating with inside the dut. A JTAGDevice needs to be defined for this purpose or a device that inherits from this base class.
A JTAGDevice needs a name, idcode and ir_len. It also needs to have the IR Register map defined and the width of the register. 'BYPASS' is prepopulated in the base class at last address in the IRLEN address map.
First inherit from the base class, and then add all the other IR registers using the add_jtag_reg method.
from cocotbext.jtag import JTAGDevice
class J1JTAGDevice(JTAGDevice):
def __init__(self, name='jtaglet1', idcode=0x53817905, ir_len=5):
super().__init__(name, idcode, ir_len)
self.add_jtag_reg("IDCODE", 32, 0x1e)
self.add_jtag_reg('USERDATA', 32, 0x8)
self.add_jtag_reg('USEROP', 8, 0x9)
self.idle_delay = 6
add_jtag_reg(name, width, address): Add an IR register toJTAGDevice. name is a string, width is the DR shift width of the register and the address, which should be within the range of the ir_len ofJTAGDevice
This results in a JTAGDevice that has 4 IR register defined:
0x08: USERDATA[31:0]
0x09: USEROP[7:0]
0x1e: IDCODE[31:0]
0x1f: BYPASS[0:0]
Multiple devices inside the JTAG chain:
from cocotbext.jtag import JTAGDriver
from cocotbext.jtag import JTAGBus
class testbench:
def __init__(self, dut):
bus = JTAGBus(dut)
self.jtag = JTAGDriver(bus)
self.jtag.add_device(J1JTAGDevice())
self.jtag.add_device(J2JTAGDevice())
Metadata
Release files for cocotbext-jtag 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| cocotbext_jtag-0.4.0.tar.gz | 19.8 kB | With provenance | Details for cocotbext_jtag-0.4.0.tar.gz |
Release files / cocotbext_jtag-0.4.0.tar.gz
| Download URL | cocotbext_jtag-0.4.0.tar.gz |
|---|---|
| Size | 19.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3c83a0353c79c8fc360af553d8540da0dd3375bac8a2cd8803e132da84d5b09b
|
|
BLAKE2b-256 checksum How to use checksums |
5402eee07b7e0762c15f7090dd4acc7653320e995eaf9c6fd0e754c92566885d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 10, 2026.
Transparency logSecurity for cocotbext-jtag 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 1 file in this release has been uploaded with provenance metadata.
Attestation for cocotbext_jtag-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.