JTAG to AXI bridge python I/F
Project description
JTAG to AXI master
Table of Contents
- Introduction
- Design details
- JTAG Data registers
- AXI write/read flow operation
- Test JTAG_AXI with urjtag
- License
Introduction
This design implements a JTAG (IEEE 1149.1-2013) interface that is capable of dispatching AXI Lite transactions into an AXI bus. As an AXI master, there are several applications that can be build with it, however, its primary intentions are:
- To debug SoC peripherals connected to an AXI bus - dispatch read / write single beat burst AXI transactions.
- Program memories in SoCs to act as a bootloader.
- Can be used to drive reset IOs through
IC_RESET
output with variable width. - User can also set/tie the
USERCODE
to any 32-bit internal design value and read through the JTAG Data Registers. - Through
USERDATA
it is possible drive values which could be used to implement some protocol/logic within its integration.
Note: For the 2) this requires a JTAG SW lib that can interface with the debug adapter through a fast handle, maybe a low level API in C/C++ that communicates with the debug adapter . The python package developed within this repository (discussed later) uses pyftdi to interface with the design and it is not capable of dispatching JTAG reads/write through a reasonable speed, which makes the read/writes process through JTAG slow and not so recommended to use to act as bootloader.
Design details
The design is divided into two clocks domains as indicated by the diagram above, the JTAG clock domain (tck) and the AXI clock domain. In order to exchange data between the two clock domains 3x AFIFOs are used along with some other CDC 2FF modules for discrete signals.
In the TAP wrapper all the JTAG-specific logic is implemented such as the instruction register, the data registers and the TAP fsm following all the standard 16-states. In order to optmise the ammount of logic involved for the data registers, a single shift register is defined where its length will depend on the widest data register to shift. Thus whenever data needs to be shifted in or out, it will use this register entirely or some bits starting from the LSB as the "exchange" register.
For the AXI logic segment, the dispatch module encompasses all the required CDC handshake along with the logic that is necessary to drive the AXI master signals. In the AXI IF, the design receives the requests through the AFIFO and drives the proper AXI channel with the values set by the user, the master ID is set through a parameter called AXI_MASTER_ID on the top of the design instantiation. Following the standard, each AXI channel is individually controlled its valid & ready handshake, with a causality effect on the respective response channel. For instance, the master will be available only for the response of the respective type of AXI transaction dispatched thus if a read is issued, the axi.rready == 1 only when handshake through address channel happens first, that simplifies the general handling logic. Same happens for writes with axi.bready.
By default all the AFIFO
s have 4x slots depth which means that up to 4x outstanding transactions can be issued in a row through the JTAG_AXI
design, however, the python package discussed below and the UML diagrams are suggesting single reads and writes to simplify the general understanding. This means that if the user understand the design well enough, it is possible to create an API that issues 4x reads / writes in-order in a row to speedup the general throughput of the design. The interface module also keeps track of the no of OT (outstanding txns) that are issued in order to flag whether the JTAG is running or command processing has been finished.
Each AXI channel has a valid / ready timeout monitor to indicate when the AXI bus / slave does not reply within a certain window. It is recommended that both resets are issued if a timeout happens in any of the channels to make sure the design is into a know-state. The timeout value is configurable through a parameter as well.
JTAG Data Registers
This design follows the JTAG (IEEE 1149.1-2013) standard by implementing a TAP (Test Access Port, instruction register and data registers. However, there are some exceptions in the design, for instance the boundary scan feature is not implemented. The instruction register is capable of decoding EXTEST
and SAMPLE_PRELOAD
but the BS features are not part of the original plan, there is a specific encoding for these but it is not an intention to implement at this point (maybe in future). Additionally, other features such as BYPASS
and IDCODE
are both implemented, the bypass works similar to any JTAG device where the device will delay the tdi>tdo shifting by a single clock cycle. In terms of encoding for the idcode, the design does not follow the manufacturer / product codes available, instead it is configured by the top parameter IDCODE_VAL
.
The list of JTAG Data Registers is available below with their respective encodings, acces control and bit length. As mentioned before, EXTEST
and SAMPLE_PRELOAD
are not implemented, they are both listed because its encoding is available in the IR (Instruction Register) but there is not a correspondent register to be shifted out. For the SAMPLE_PRELOAD
, if selected through the IR, it'll work as a bypass SR (Shift Register) with length equal to the width of the main SR in the design.
One of the JTAG Data Register that has external I/F is the IC_RESET
, this register has its size configurable through the parameter IC_RST_WIDTH
and can be used to drive the resets within the circuitry in a digital design. In order to change its value, the user needs to shift_ir == IC_RESET
encoding, then switch the TAP state to shift_dr where it can start shifting data in the register. Once it is done, the value will get reflected in the IC_RESET
register when the TAP fsm reaches the state update_dr. The same approach should be used to all registers (shift_ir > shift_dr) like any JTAG device, below details are considering the user assumes all other data registers are following the same procedure.
Both IDCODE
and BYPASS
as previously mentioned will follow the JTAG standard so nothing differs from the spec. The USERCODE
was added to allow the designer to add some specific information about its design by driving a 32-bit value on the jtag_axi.usercode_i, when IR == USERCODE
, and the TAP fsm switches to the state update_dr, the jtag_axi.usercode_update_o will switch high (1) till the fsm moves out of this state. That was designed in case the user wants to update the jtag_axi.usercode_i after its read.
The USERDATA
is a configurable width register that can be used to shift data inside the design where some logic can process through the design output jtag_axi.userdata_o. Similar to the USERCODE
, an output jtag_axi.userdata_update_o is available that is asserted when the USERDATA
register is updated.
Name | Encoding (4 bits) | Access (RW/RO) | Bit length | Description |
---|---|---|---|---|
EXTEST | 0000 | TBD | TBD | TBD |
SAMPLE_PRELOAD | 1010 | TBD | As long as the main SR | No fn - TBD |
IC_RESET | 1100 | RW | IC_RST_WIDTH - Def. 4 | Programmable register to be used to control external resets |
IDCODE | 1110 | RO | 32 | Returns the IDCODE of the device - 0xBADC0FFE |
BYPASS | 1111 | RW | 1 | Standard bypass JTAG register |
ADDR_AXI_REG | 0001 | RW | ADDR_AXI_WIDTH - Def. 32 | Indicates the AXI address of the transaction |
DATA_W_AXI_REG | 0010 | RW | DATA_AXI_WIDTH - Def. 32 | Indicates the AXI data to be written |
WSTRB_AXI_REG | 0011 | RW | AXI_DATA_WIDTH/8 - Def. 4 | AXI Write strobe mask |
CTRL_AXI_REG | 0100 | RW* | 8 | Controls AXI master I/F dispatch |
STATUS_AXI_REG | 0101 | RO | AXI_DATA_WIDTH + 4 - Def. 36 | Return the status of the AXI transaction |
USERCODE | 0111 | RO | 32 | User input for design values |
USERDATA | 0110 | RW | USERDATA_WIDTH - Def. 4 | User programmable register |
*Note: CTRL_AXI_REG not all registers are RW, more info below.
Encoding of AXI registers
Some AXI data registers such as CTRL_AXI_REG
and STATUS_AXI_REG
follow a specific format and the its encoding is detailed down below.
STATUS_AXI_REG
This register stores the data that is read on AXI read commands and the status of the last transaction issued in order.
STATUS_AXI_REG | AXI Data Read (32 bits) |
Status (4 bits) |
---|---|---|
Bit order | MSB | LSB |
Access (RW/RO) | RO | RO |
Description | When read transaction is performed, data read will be written here in order as dispatched |
Current status of last transaction |
Status encoding:
Status code | Status alias | Description |
---|---|---|
0 | JTAG_IDLE | That is the default state of the design when no transactions (txn) have been dispatched |
1 | JTAG_RUNNING | When a transaction (txn) is started, the design will change to this state. It should stay there until a response is received or it times out. |
2 | JTAG_TIMEOUT_AR | Indicates a transaction timeout in the AXI - Address Read channel |
3 | JTAG_TIMEOUT_R | Indicates a transaction timeout in the AXI - Read data channel |
4 | JTAG_TIMEOUT_AW | Indicates a transaction timeout in the AXI - Address Write channel |
5 | JTAG_TIMEOUT_W | Indicates a transaction timeout in the AXI - Write data channel |
6 | JTAG_TIMEOUT_B | Indicates a transaction timeout in the AXI - Write response channel |
7 | JTAG_AXI_OKAY | Indicates a transaction response: AXI OKAY |
8 | JTAG_AXI_EXOKAY | Indicates a transaction response: AXI EXOKAY |
9 | JTAG_AXI_SLVERR | Indicates a transaction response: AXI SLVERR |
10 | JTAG_AXI_DECER | Indicates a transaction response: AXI DECER |
CTRL_AXI_REG
This register controls the dispatch of AXI transactions, when CTRL_AXI_REG.start is shift with value 1, it will start a new AXI transaction of type CTRL_AXI_REG.txn_type. The field free_slots indicates how many slots are available in the asynchronous FIFO used to handshake between the two clock domains. Lastly, the size_axi_txn follows the same encoding as the AXI specification, i.e data to be transfered == 2^ASIZE bytes. It is recommended for the user to always check the free slots before shifting new instructions to ensure no data is lost in case the AFIFO is full (outstanding txns are being processed). Typically that should not be a problem because JTAG runs in a slower frequency (~10MHz) than the internal logic.
CTRL_AXI_REG | Start (1 bit) |
Txn type (1 bit) |
Free Slots (3 bits) |
Size AXI txn (3 bits) |
---|---|---|---|---|
Bit order | MSB | ... | ... | LSB |
Access (RW/RO) | RW | RW | RO | RW |
Description | Once written 1, dispatches an AXI transaction |
1 - Write, 0 - Read | Number of slots available in the Asynchronous FIFO |
Size of the AXI transaction following AXIv4 encoding |
AXI write/read flow operation
In order to perform AXI writes and reads, the user needs to follow a specific JTAG sequence provided below. This sequence will ensure all correct fields are properly set before issuing the AXI master transaction downstream in the AXI bus where the jtag_axi master is connected to.
For a write sequence:
sequenceDiagram
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-IR: IDCODE
JTAG I/F ->> JTAG Debug Adapter (PC): Shift-DR: Get 0xBADC0FFE
Note left of JTAG Debug Adapter (PC): OPTIONAL: Check whether the IDCODE <br/> matches 0xBADC0FFE
JTAG Debug Adapter (PC) --> JTAG I/F: Set txn addr
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-IR: ADDR_AXI_REG
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-DR: Shift the address of the txn
JTAG Debug Adapter (PC) ->> JTAG I/F: Update-DR
JTAG Debug Adapter (PC) --> JTAG I/F: Set txn data to be written
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-IR: DATA_W_AXI_REG
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-DR: Shift the data of the txn
JTAG Debug Adapter (PC) ->> JTAG I/F: Update-DR
JTAG Debug Adapter (PC) --> JTAG I/F: Set Write strobe mask
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-IR: WSTRB_AXI_REG
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-DR: Shift the write strobe mask of the txn
JTAG Debug Adapter (PC) ->> JTAG I/F: Update-DR
JTAG Debug Adapter (PC) --> JTAG I/F: Start txn
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-IR: CTRL_AXI_REG
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-DR: Shift the control values of the txn
JTAG Debug Adapter (PC) ->> JTAG I/F: Update-DR
JTAG I/F ->> AXI Master I/F: Txn written into Async. FIFO
AXI Master I/F ->> AXI Bus: Txn issued on the AXI Bus
JTAG Debug Adapter (PC) --> AXI Bus: ...
AXI Bus ->> AXI Master I/F: AXI Slave response from AXI Bus
AXI Master I/F ->> JTAG I/F: Txn response written into Async. FIFO
JTAG Debug Adapter (PC) --> JTAG I/F: Monitor txn status
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-IR: STATUS_AXI_REG
Note right of JTAG I/F: Whenever Capture-DR is selected <br/> with IR set to STATUS_AXI_REG <br/> the latest status will be updated <br/> into STATUS_AXI_REG shift register
JTAG I/F ->> JTAG Debug Adapter (PC): Shift-DR: Get STATUS_AXI_REG
Note left of JTAG Debug Adapter (PC): If txn_status == RUNNING <br/> move to Capture-DR and then back to Shift-DR <br/> this will update the STATUS_AXI_REG
Note left of JTAG Debug Adapter (PC): If txn_status != JTAG_RUNNING <br/> txn has finished
JTAG I/F ->> JTAG Debug Adapter (PC): Shift-DR: Get STATUS_AXI_REG
For a read sequence:
sequenceDiagram
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-IR: IDCODE
JTAG I/F ->> JTAG Debug Adapter (PC): Shift-DR: Get 0xBADC0FFE
Note left of JTAG Debug Adapter (PC): Check whether the IDCODE <br/> matches 0xBADC0FFE
JTAG Debug Adapter (PC) --> JTAG I/F: Set txn addr
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-IR: ADDR_AXI_REG
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-DR: Shift the address of the txn
JTAG Debug Adapter (PC) ->> JTAG I/F: Update-DR
JTAG Debug Adapter (PC) --> JTAG I/F: Start txn
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-IR: CTRL_AXI_REG
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-DR: Shift the control values of the txn
JTAG Debug Adapter (PC) ->> JTAG I/F: Update-DR
JTAG I/F ->> AXI Master I/F: Txn written into Async. FIFO
AXI Master I/F ->> AXI Bus: Txn issued on the AXI Bus
JTAG Debug Adapter (PC) --> AXI Bus: ...
AXI Bus ->> AXI Master I/F: AXI Slave response from AXI Bus
AXI Master I/F ->> JTAG I/F: Txn response written into Async. FIFO
JTAG Debug Adapter (PC) --> JTAG I/F: Monitor txn status
JTAG Debug Adapter (PC) ->> JTAG I/F: Shift-IR: STATUS_AXI_REG
Note right of JTAG I/F: Whenever Capture-DR is selected <br/> with IR set to STATUS_AXI_REG <br/> the latest status will be updated <br/> into STATUS_AXI_REG shift register
JTAG I/F ->> JTAG Debug Adapter (PC): Shift-DR: Get STATUS_AXI_REG
Note left of JTAG Debug Adapter (PC): If txn_status == RUNNING <br/> move to Capture-DR and then back to Shift-DR <br/> this will update the STATUS_AXI_REG
Note left of JTAG Debug Adapter (PC): If txn_status != JTAG_RUNNING <br/> txn has finished
JTAG I/F ->> JTAG Debug Adapter (PC): Shift-DR: Get STATUS_AXI_REG
Python package (jtag-axi)
In order to use this design with a real debug adapter, a simple python package was developed called jtag-axi which makes usage of the pyftdi to communicate through FTDI compatible JTAG debug adapters. Some example ICs are listed as:
- FT2232C/D (dual port, clock up to 6 MHz)
- FT232H (single port, clock up to 30 MHz)
- FT2232H (dual port, clock up to 30 MHz)
- FT4232H (quad port, clock up to 30 MHz)
- FT4232HA (quad port, clock up to 30 MHz)
JTAG-AXI Python Package jtag-axi
is a Python package designed for interfacing AXI-based systems using JTAG through FTDI devices. It allows for reading and writing to memory through the JTAG-to-AXI bridge, utilizing the PyFTDI library for low-level JTAG communication.
This package can be installed with:
pip install jtag-axi
Features
- Read and write operations to AXI memory space -
- Built on top of PyFTDI for handling JTAG communication
An example on how to use this package can be found in this repository on run_me.py
.
API
JtagToAXIFTDI
The core class used to interface with JTAG-to-AXI hardware via an FTDI device. When instantiating the object, few input parameters are required to define the design details:
from jtag_axi import JtagToAXIFTDI
...
jtag = JtagToAXIFTDI(
device="ftdi://ftdi:2232/1", # Pointer to the FTDI device
name: str = "JTAG to AXI IP", # Object name
freq: int = 1e6, # JTAG frequency
trst: bool = False, # trst available?
debug: bool = False, # Enable debug info
addr_width: int = 32, # AXI address witdh
data_width: int = 32, # AXI data width
async_fifo_depth: int = 4, # Number of AFIFO depth
ic_reset_width: int = 4, # IC_RESET width
userdata_width: int = 4, # USERDATA width
)
Methods
write_axi(address, data, size=None, wstrb=0xF)
: Writes data to a specified AXI address.read_axi(address, size=None)
: Reads data from a specified AXI address.reset()
: Resets the JTAG interface.write_ic_reset(value)
: Writes to the IC_RESET register.write_userdata(value)
: Writes to the USERDATA register.read_jdrs()
: Reads all JTAG data registers
Test JTAG_AXI with urjtag
Once design is synthesized and you want to run a quick test to check whether the design works, try the commands below. It should indicate whether the correct IDCODE
is read.
jtag
> cable jlink # or another debug adapter
> frequency 10000000
> detect
License
jtag_axi
is licensed under the permissive MIT license.Please refer to the LICENSE file for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.