Skip to content

LOKI JTAG

Callum Lee edited this page Aug 21, 2025 · 3 revisions

LOKI JTAG

This is a python library that facilitates the control of JTAG devices.

Table of Contents:

Creating a JTAG Device

To create an instance of a JTAG device to interact with, simply instantiate an instance of the GenericDevice class. This class' constructor takes a path to the corresponding configuration file (more on this below).

This can also be used as a base class to expand the functionality for more device-specific actions.

Device Configuration File

A device configuration file is a JSON file that stores information about a device such as:

  • The device's instruction register length
  • Device instructions
  • Device registers
  • Register addresses
  • Fields inside the device's registers
  • Subfields in the registers' fields
  • The device's boundary scan register length

Creating a Device Configuration File

Top Level

Key Value Type Description Optional
ir_length Integer The length of the device's instruction register No
instructions JSON JSON containing all the instructions for a device (see below) Yes
registers List of JSON A list of JSON containing the device's register information (see Register Configuration) Yes
bsr_length Integer The length of the boundary scan register Yes

The instructions should be structured as "instruction_name": "instruction_code". Where instruction_code is a binary string (see below for an example).

Note

Please ensure each instruction has an associated register (that has the same name) with at least the basic information, e.g name, address and length.

Note

If a register has no associated instruction, the address will be used as the register's instruction

Register Configuration

Key Value Type Description Optional
name String The name of the register No
address Integer The memory address of the register No
reg_length Integer The total length of the register in bits No
fields List of JSON JSON containing the register's field information (see Field Configuration) No

Field Configuration

Key Value Type Description Optional
name String The name of the field No
bit_length Integer The length of the field in bits No
reversed Boolean Defines if the field should be reversed, i.e. 110001 becomes 100011 Yes
subfields List of JSON Defines fields inside fields Yes

Note

If you do not know any of this information, please do not include the key in the configuration file (if the key is optional). Some features may not be available until this information is provided

Example Device Configuration File

{
    "ir_length": 5,
    "instructions": {
        "IDCODE": "110110"
    },
    "registers": [
        {
            "name": "IDCODE",
            "address": 0,
            "reg_length": 0,
            "fields": [
                {
                    "name": "field_name",
                    "bit_length": 0,
                    "reversed": false
                },
                {
                    "name": "field_name",
                    "bit_length": 5,
                    "subfields": [
                        {
                            "name": "field_name",
                            "bit_length": 1,
                            "reversed": false
                        },
                        {
                            "name": "field_name",
                            "bit_length": 10,
                            "reversed": false
                        }
                    ]
                }
            ]
        }
    ],
    "bsr_length": 0
}

Creating a JTAG Chain

To create a JTAG chain, firstly ensure that all of your devices have been instantiated. Instantiate a new instance of the JTAGChain class by passing a list containing your devices to the constructor, and the path to the UIO device you wish to use.

Note

Ensure that the devices are in the same position in the list as they are in the physical JTAG chain

Operating on a Device

How it Works

Calling the shift_ir() function on a specific device will shift the given instruction into the device's instruction register, while putting all other devices in the chain into bypass.

Shifting Instructions

To shift an instruction into a device's instruction register, there are two options.

The first is to have the instructions defined in the device's configuration file (see Creating a Device Configuration File). For example: device.shift_ir("IDCODE")

The second is to pass in a string of bits. This is useful if it is an instruction that is being tested quickly. For example: device.shift_ir("110001")

Data Register Interaction

To read the data in a register, simply call the read_reg() function, or call the read_modify_update() function if the register is destructive, (once the instruction for that register has been loaded into the instruction register) with the name of the register you would like to read as an argument, this will automatically shift 0s through the data register to shift out all of the data. The data returned will also be trimmed to account for the other devices being in bypass (if applicable).

Manually Shifting Bits

Sometimes, you may just want to manually shift in bits to both the data register and the instruction register (see here for information on shifting instructions into the instruction register)

You can manually shift bits into the data register by calling the shift_dr() function on the device class. This works in a similar way to the shift_ir() function, where you shift in a string of bits.

When shifting out data from the data register after shifting in an instruction as a string of bits, you must provide the number of bits you wish to shift out. This is because the length of bits to shift out is usually determined by the register that is associated with the instruction (defined in the device configuration file).

Note

The argument passed into the shift_dr() function must be the same length as the data register of the device you are interacting with

Boundary Scan

In order for the boundary scan operation to work, ensure:

  • A boundary scan instruction is defined in the device+ configuration file (e.g "SAMPLE", "PRELOAD", "EXTEST")
  • The boundary scan register length is defined in the device configuration file

To do a boundary scan, call the boundary_scan function on the device class and pass in the bits to shift through the boundary scan register, as well as the instruction name from the configuration file as an argument.

Interacting With Registers

The update_reg() function allows you to change the value stored in the specified register by passing in the name of the register and the

The read_reg() function returns the current value of the register specified.

Note

The name of the register must match the corresponding instruction to read it

Reading Register Fields

The get_reg_field_value() functions allows you to read a specific field from a register that has been defined in the device configuration file.

Classes

The list below is comprised of all functions that are intended to be used by users

JTAGChain

read_all_id_codes() - Returns the ID code for all the devices on the chain

GenericDevice

shift_ir(instruction: str) - Shifts instruction into device's instruction register, puts all other devices in bypass

shift_dr(value: Optional[str]) - Shifts bits into data register to either read out data or write data

get_all_registers() - Returns all registers defined on the device

get_register(name: str) - Returns the register with the name specified in the argument

get_reg_field_value(reg_name: str, field_name: str) - Returns the current value in field specified, inside the register specified

read_reg(reg_name: str) - Returns the current value of the register specified

update_reg(reg_name: str, bits: str) - Updates the register specified with the bits specified

get_bsr_length() - Returns the boundary scan register length in bits

boundary_scan(tdi: str, read_instruction_name: str) - Completes a boundary scan on the device, shifts tdi through the boundary scan register

JTAGReg

get_total_bit_length() - Returns the total length of the register in bits

get_name() - Returns the name of the register

get_address() - Returns the memory address of the register

JTAGField

get_name() - Returns the name of the field

get_bit_length() - Returns the length of the field in bits

get_reversed() - Returns a boolean for if a field is reversed

Example Usage

Reading All ID Codes

device1 = GenericDevice("device1_config.json")
device2 = GenericDevice("device2_config.json")
device3 = GenericDevice("device3_config.json")

chain = JTAGChain([device1, device2, device3])

chain.read_all_id_codes()

Running a Boundary Scan (Read)

device1 = GenericDevice("device1_config.json")
device2 = GenericDevice("device2_config.json")
device3 = GenericDevice("device3_config.json")

chain = JTAGChain([device1, device2, device3])

bits_in = "0" * device2.get_bsr_length()

device2.boundary_scan(bits_in)

Shifting an Instruction From Configuration File

device1 = GenericDevice("device1_config.json")
device2 = GenericDevice("device2_config.json")
device3 = GenericDevice("device3_config.json")

chain = JTAGChain([device1, device2, device3])

device2.shift_ir("IDCODE")
device2.shift_dr()

Shifting an Instruction Not From Configuration File

device1 = GenericDevice("device1_config.json")
device2 = GenericDevice("device2_config.json")
device3 = GenericDevice("device3_config.json")

chain = JTAGChain([device1, device2, device3])

device2.shift_ir("10001110")
device2.shift_dr()

Reading a Register

device1 = GenericDevice("device1_config.json")
device2 = GenericDevice("device2_config.json")
device3 = GenericDevice("device3_config.json")

chain = JTAGChain([device1, device2, device3])

device2.shift_ir("IDCODE")
device2.read_reg("IDCODE")

Reading a Destructive Register

device1 = GenericDevice("device1_config.json")
device2 = GenericDevice("device2_config.json")
device3 = GenericDevice("device3_config.json")

chain = JTAGChain([device1, device2, device3])

device2.shift_ir("IDCODE")
device2.read_modify_write_reg("IDCODE")

Clone this wiki locally