-
Notifications
You must be signed in to change notification settings - Fork 0
LOKI JTAG
This is a python library that facilitates the control of JTAG devices.
Table of Contents:
- Creating a JTAG Device
- Device Configuration File
- Creating a JTAG Chain
- Operating on a Device
- Classes
- Example Usage
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.
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
| 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
| 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 |
| 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
{
"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
}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
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.
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")
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).
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
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.
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
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.
The list below is comprised of all functions that are intended to be used by users
read_all_id_codes() - Returns the ID code for all the devices on the chain
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
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
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
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()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)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()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()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")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")