This project contains some JTAG boundary scan related tools that help me with reverse engineering PCB.
Important: these are hacked up scripts for personal usage. They may very well only work for my specific case.
Requires the following input:
-
BSDL file
This is file provided by a chip company that describes the JTAG contents of the chip.
If it contains IO port boundary scan registers, this information can be used to dump the state of the IO ports that have such a register.
-
File with hex values of the JTAG boundary scan register, as dumped by OpenOCD.
One line per hex value.
Output:
- A report that shows the values of all boundary scan register in chronological order
I have a PCB with an Intel Arria GX EP1AGX90EF115C6N chip.
-
Download its BDSL file from the Intel website.
This will give you a file called
EP1AGX90EF1152.BSD
. -
Install requirements by
python3 -m pip install -r requirements.txt
-
Create an OpenOCD file that describes the JTAG chain of the PCB.
The Comtech AHA363PCIE0301G board that I'm using has a relatively complex chain with 4 devices.
./example/aha363.tcl
:
jtag newtap AHA6310B_0 tap -irlen 5 -expected-id 0x10e1b291
jtag newtap AHA6310B_1 tap -irlen 5 -expected-id 0x10e1b291
jtag newtap EP1AGX90EF1152C6N tap -irlen 10 -expected-id 0x021230dd
jtag newtap EPM570 tap -irlen 10 -expected-id 0x020a20dd
The -expected-id
parameter specifies the JTAG IDCODE of the chips in the JTAG scan chain.
OpenOCD can figure out those values for you with an autoscan. For well documented chips (such
as Intel FPGAs and CPLDs), you can also find these values in their development documentation
or in their BDSL files.
- Create an OpenOCD file that prints the boundary scan register contents:
./example/bscan_dump.tcl
:
set BSCAN_SAMPLE 0x005
set BSCAN_LEN 2016
irscan EP1AGX90EF1152C6N.tap $BSCAN_SAMPLE
drscan EP1AGX90EF1152C6N.tap $BSCAN_LEN 0
- Using OpenOCD, connect to the chip:
tom@thinkcenter:~/projects/bscan_tools/example$ sudo /opt/openocd/bin/openocd -f /opt/openocd/share/openocd/scripts/interface/altera-usb-blaster.cfg -f ./aha363.tcl
Open On-Chip Debugger 0.10.0+dev-00930-g09eb941 (2019-09-16-21:01)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'jtag'
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : usb blaster interface using libftdi
Info : This adapter doesn't support configurable speed
Info : JTAG tap: AHA6310B.tap tap/device found: 0x10e1b291 (mfg: 0x148 (Agilent Technologies), part: 0x0e1b, ver: 0x1)
Info : JTAG tap: AHA6310B.tap tap/device found: 0x10e1b291 (mfg: 0x148 (Agilent Technologies), part: 0x0e1b, ver: 0x1)
Info : JTAG tap: EP1AGX90EF1152C6N.tap tap/device found: 0x021230dd (mfg: 0x06e (Altera), part: 0x2123, ver: 0x0)
Info : JTAG tap: EPM570.tap tap/device found: 0x020a20dd (mfg: 0x06e (Altera), part: 0x20a2, ver: 0x0)
Warn : gdb services need one or more targets defined
- Telnet to the OpenOCD session, and dump the boundary scan values:
tom@thinkcenter:~/projects/bscan_tools/example$ telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> source bscan_dump.tcl
5FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDBFDF7FFFDFEDB6DFFDF7DFFFF6DBFDF7FBFFFFFFFFFFFFFFFFFFFE9FED7FFFFFFDBFFBFDFEFFEDB6DFFFF6DFEDBFFBFDF7DB6DFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5FBE97FFFFBFFFFFFFDFFFFEFFFFFFFFFDF7FFFFFFFFFFFFFFFFF7DFFFF7FFFDFFDB6FFFFF7FFFFFFDBFFFFFFFFFFFFFFFFFFFFFFFFFDFFDFEFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6DC693FFFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF178FB7DB7FFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7DFFFBFDB6DFE082
> source bscan_dump.tcl
5FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDBFDF7FFFDFEDB6DFFDF7DFFFF6DBFDF7FBFFFFFFFFFFFFFFFFFFFE9FED7FFFFFFDBFFBFDFEFFEDB6DFFFF6DFEDBFFBFDF7DB6DFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5FBE97FFFFBFFFFFFFDFFFFEFFFFFFFFFDF7FFFFFFFFFFFFFFFFF7DFFFF7FFFDFFDB6FFFFF7FFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFDFFDFEFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6DC693FFFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF178FB7DB7FFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7DFFFBFDB6DFE082
> source bscan_dump.tcl
5FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDBFDF7FFFDFEDB6DFFDF7DFFFF6DBFDF7FBFFFFFFFFFFFFFFFFFFFE9FED7FFFFFFDBFFBFDFEFFEDB6DFFFF6DFEDBFFBFDF7DB6DFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5FBE97FFFFBFFFFFFFDFFFFEFFFFFFFFFDF7FFFFFFFFFFFFFFFFF7DFFFF7FFFDFFDB6FFFFF7FFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFDFFDFEFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6DC693FFFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF178FB7DB7FFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7DFFFBFDB6DFE082
- Store the values above in file
./example/bscan_values.txt
.
./bscan_proc.py EP1AGX90EF1152.bsdl bscan_values.txt > pin_report.txt
If the parsed EP1AGX90EF1152.bsdl
wasn't already in the local cache
(located in ~/.cache/bscan_proc
by default), bscan_proc.py
will also
create a EP1AGX90EF1152_dev.tcl
with a number of useful constants
that can be used by OpenOCD.
The pin_report.txt
file will look like this:
...
AJ16 (IOAJ16) : INPUT : 1 1 1 1 1 1 1 1 1 1
AJ16 (IOAJ16) : OUTPUT3 : 1 1 1 1 1 1 1 1 1 1
AJ16 (IOAJ16) : CONTROL : 1 1 1 1 1 1 1 1 1 1
AJ19 (IOAJ19) : INPUT : 0 0 0 1 1 0 1 1 1 0 !
AJ19 (IOAJ19) : OUTPUT3 : 1 1 1 1 1 1 1 1 1 1
AJ19 (IOAJ19) : CONTROL : 1 1 1 1 1 1 1 1 1 1
AJ20 (IOAJ20) : INPUT : 1 1 1 1 1 1 1 1 1 1
AJ20 (IOAJ20) : OUTPUT3 : 1 1 1 1 1 1 1 1 1 1
AJ20 (IOAJ20) : CONTROL : 1 1 1 1 1 1 1 1 1 1
AJ21 (IOAJ21) : INPUT : 0 0 0 0 0 0 0 0 0 0
AJ21 (IOAJ21) : OUTPUT3 : 1 1 1 1 1 1 1 1 1 1
AJ21 (IOAJ21) : CONTROL : 1 1 1 1 1 1 1 1 1 1
...
H14 (IOH14) : INPUT : 1 1 1 1 1 1 1 1 1 1
H14 (IOH14) : OUTPUT3 : 1 1 1 1 1 1 1 1 1 1
H14 (IOH14) : CONTROL : 1 1 1 1 1 1 1 1 1 1
H20 (IOH20) : INPUT : 0 0 0 0 0 0 0 0 0 0
H20 (IOH20) : OUTPUT3 : 0 0 0 0 0 0 0 0 0 0
H20 (IOH20) : CONTROL : 0 0 0 0 0 0 0 0 0 0
H22 (IOH22) : INPUT : 1 1 1 1 1 1 1 1 1 1
H22 (IOH22) : OUTPUT3 : 1 1 1 1 1 1 1 1 1 1
H22 (IOH22) : CONTROL : 0 0 0 0 0 0 0 0 0 0
...
K10 (MSEL0) : INPUT : 0 0 0 0 0 0 0 0 0 0
J9 (MSEL1) : INPUT : 0 0 0 0 0 0 0 0 0 0
J10 (MSEL2) : INPUT : 1 1 1 1 1 1 1 1 1 1
H11 (MSEL3) : INPUT : 1 1 1 1 1 1 1 1 1 1
AF8 (PLL_ENA) : INPUT : 1 1 1 1 1 1 1 1 1 1
AE8 (PORSEL) : INPUT : 1 1 1 1 1 1 1 1 1 1
AF24 (VCCSEL) : INPUT : 0 0 0 0 0 0 0 0 0 0
AE7 (nIO_PULLUP): INPUT : 1 1 1 1 1 1 1 1 1 1
-
MSEL[3:0] == 4'b1100
This means "Remote system upgrade FPP with decompression feature enabled". That's extremely useful information to configure the chip.
-
H20
andH22
are configured as output.These pins are used as PGM pins when the FPGA is configured in remote system upgrade mode.
-
AJ19
has a toggling pin (See the '!' at the end of the line)AJ19 is a clock input of this FPGA, so we've very likely found the main clock source.
As you discover new pin values, you can translate the original port name (e.g. IOH22
) to the real name (PGM[0]
) with
an pin renaming file.
./example/pin_renamings.txt
:
AJ19:CLK100
K10:MSEL[0]
J9:MSEL[1]
J10:MSEL[2]
H11:MSEL[3]
G20:PGM[2]
H20:PGM[1]
H22:PGM[0]
Now rerun with the pin renamings file as a parameter:
./bscan_proc.py -r pin_renamings.txt EP1AGX90EF1152.bsdl bscan_values.txt > pin_report.ann.txt
And you get somethingn like this:
./example/pin_report.ann.txt
:
...
AJ19 (CLK100) : INPUT : 0 0 0 1 1 0 1 1 1 0 !
AJ19 (CLK100) : OUTPUT3 : 1 1 1 1 1 1 1 1 1 1
AJ19 (CLK100) : CONTROL : 1 1 1 1 1 1 1 1 1 1
...
- Link the script directly with OpenOCD through its TCL interface for interactive debugging
- Create OpenOCD scripts to change the value of the boundary scan register
- ...