There are no tutorials explaining the use of BSCAN_SPARTAN3 primitive on Xilinx FPGAs. It's a very useful feature, which allows to literally establish a PC-to-FPGA link over a JTAG adapter. This primitive is already used in projects such as xc3sprog and Papilio-Loader to program SPI flash memory over JTAG.
BSCAN_SPARTAN3 is actually not a good name for this primitive. According to the JTAG standard, the Boundary Scan register must exist and control the input/output pins of the circuit. BSCAN_SPARTAN3 should have been named as "USER1" or "USER2" register since the corresponding instructions select this register.
Anyways, below is the VHDL code which allows accessing BSCAN_SPARTAN3 primitive. The primitive is invoked as a "component" in VHDL. Its outputs are mapped to the top architecture. USER1 register is called SHIFT_REGISTER in this design. The length of this register can be arbitrarily large, but in our code it is 8, so that register can hold only one byte of information. TDI is mapped to the most significant bit of the register, and TDO is mapped to the least significant bit of the register. So when one byte of information is shifted in through TDI, the old content of the register is shifted out.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
------------------------------------------------------------------
-- Project Name: BScan primitive of Spartan 3 FPGA family
-- Description: The circuit receives 8 bits (LSB first) from the
-- TDI input of the built-in JTAG Test Access Point
-- and displays them on LEDs.
-- Target Devices: xc3s50a (or any Spartan 3 device)
-- Engineer: Altynbek Isabekov
-- Create Date: 17:43:53 2017/08/08
------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
library UNISIM;
use UNISIM.VComponents.all;
entity BScan_Primitive is
generic (N: INTEGER:=8);
port ( LEDS: out STD_LOGIC_VECTOR (N-1 downto 0));
end BScan_Primitive;
architecture Behavioral of BScan_Primitive is
component BSCAN_SPARTAN3
port (CAPTURE : out STD_LOGIC;
DRCK1 : out STD_LOGIC;
DRCK2 : out STD_LOGIC;
RESET : out STD_LOGIC;
SEL1 : out STD_LOGIC;
SEL2 : out STD_LOGIC;
SHIFT : out STD_LOGIC;
TDI : out STD_LOGIC;
UPDATE : out STD_LOGIC;
TDO1 : in STD_LOGIC;
TDO2 : in STD_LOGIC);
end component;
signal user_CAPTURE : STD_LOGIC;
signal user_DRCK1 : STD_LOGIC;
signal user_DRCK2 : STD_LOGIC;
signal user_RESET : STD_LOGIC;
signal user_SEL1 : STD_LOGIC;
signal user_SEL2 : STD_LOGIC;
signal user_SHIFT : STD_LOGIC;
signal user_TDI : STD_LOGIC;
signal user_UPDATE : STD_LOGIC;
signal user_TDO1 : STD_LOGIC;
signal user_TDO2 : STD_LOGIC;
signal SHIFT_REGISTER : STD_LOGIC_VECTOR(N-1 downto 0);
begin
BS : BSCAN_SPARTAN3
port map (
CAPTURE => user_CAPTURE,
DRCK1 => user_DRCK1,
DRCK2 => user_DRCK2,
RESET => user_RESET,
SEL1 => user_SEL1,
SEL2 => user_SEL2,
SHIFT => user_SHIFT,
TDI => user_TDI,
UPDATE => user_UPDATE,
TDO1 => user_TDO1,
TDO2 => user_TDO2);
process(user_DRCK1)
begin
if(user_RESET ='1') then
SHIFT_REGISTER <= (others => '0');
elsif (rising_edge(user_DRCK1)) then
-- Chain: TDI => SHIFT_REGISTER(MSB -> LSB) => TDO
SHIFT_REGISTER <= user_TDI & SHIFT_REGISTER(SHIFT_REGISTER'HIGH downto 1);
user_TDO1 <= SHIFT_REGISTER(0);
end if;
end process;
LEDS <= SHIFT_REGISTER;
end Behavioral;
|
Every bit of the shift register is mapped to a LED. Here is the user constraints file for the Prometheus board:
|
1
2
3
4
5
6
7
8
|
NET "LEDS<7>" LOC = "P84" ;
NET "LEDS<6>" LOC = "P86" ;
NET "LEDS<5>" LOC = "P89" ;
NET "LEDS<4>" LOC = "P93" ;
NET "LEDS<3>" LOC = "P3" ;
NET "LEDS<2>" LOC = "P6" ;
NET "LEDS<1>" LOC = "P13" ;
NET "LEDS<0>" LOC = "P16" ;
|
Writing data using UrJTAG
Writing data into SHIFT_REGISTER:
- Shift opcode 0x02 into the instruction register. Execute it. USER1 register ("SHIFT_REGISTER" in VHDL) will be selected as data register.
- Shift a sequence of bits into the data register. We defined it to be 8-bit long.
Firstly, we need to select the cable driver. The Promethus FPGA board has a FT232H chip which is used as a JTAG adapter. For this purpose, we use "ft2232" driver which uses MPSSE capabilities of the FTDI chip. Then we detect the device in the chain in order to load corresponding description file. It contains instruction length information which is needed to define new instructions. UrJTAG's description file is incomplete so we have to refer to $XILINX_ISE/14.7/ISE_DS/ISE/spartan3a/data/xc3s50a.bsd to look USER1 instruction up. Its opcode is 0b000010 or 0x02.
After executing USER1 instruction, TDI will be connected to USER1 register which we've defined in the VHDL file above as "SHIFT_REGISTER". UrJTAG has to know its length before shifting anything throughout the TDI line. Our shift register is 8-bit long. Specifying bit-stream, which will be written to the selected data register, is done by using the "dr" command.
Another end of the USER1 register is connected to TDO, so shifting the bit-stream by using "shift dr" command will return another bit-stream, which is the old content of the USER1 register:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
[johndoe@ArchLinux]% jtag
UrJTAG 0.10 #2052
Copyright (C) 2002, 2003 ETC s.r.o.
Copyright (C) 2007, 2008, 2009 Kolja Waschk and the respective authors
UrJTAG is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
There is absolutely no warranty for UrJTAG.
warning: UrJTAG may damage your hardware!
Type "quit" to exit, "help" for help.
jtag> cable ft2232 vid=0x0403 pid=0x6014
Connected to libftd2xx driver.
jtag> detect
IR length: 6
Chain length: 1
Device Id: 00000010001000010000000010010011 (0x02210093)
Manufacturer: Xilinx (0x093)
Part(0): xc3s50a (0x2210)
Stepping: 0
Filename: /usr/share/urjtag/xilinx/xc3s50a/xc3s50a
jtag> register USER1_REG 8
jtag> instruction USER1 000010 USER1_REG
jtag> instruction USER1
jtag> shift ir
jtag> dr 0x93
10010011 (0x93)
jtag> shift dr
jtag> dr
00000000 (0x00)
jtag> dr 0xA1
10100001 (0xA1)
jtag> shift dr
jtag> dr
10010011 (0x93)
|
Writing data using OpenOCD
OpenOCD has low-level JTAG commands as well. Exactly same operations done with UrJTAG can be rewritten as a procedure in a configuration script. Here is the content of the Prometheus_BScan.cfg file with all necessary definitions:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# Test Access Port: Spartan3A XC3S50A FPGA
jtag newtap spartan3a tap -irlen 6 -expected-id 0x02210093
# USER1 instruction is defined in file xc3s50a.bsd
set BSCAN_USER1 0x02
# Our shift register is of length 8
set USER1_REG_LEN 8
proc send_byte {BYTE} {
global BSCAN_USER1 USER1_REG_LEN
# Send instruction over TDI
irscan spartan3a.tap $BSCAN_USER1
# Send $BYTE to data register
drscan spartan3a.tap $USER1_REG_LEN $BYTE
}
|
Content of the FT232H.cfg file, which describes JTAG adapter:
|
1
2
3
4
5
6
7
|
interface ftdi
ftdi_vid_pid 0x0403 0x6014
ftdi_layout_init 0x0088 0x008b
adapter_khz 6000
transport select jtag
ftdi_layout_signal nTRST -data 0x0100 -noe 0x0400
ftdi_layout_signal nSRST -data 0x0200 -noe 0x0800
|
Start OpenOCD server in terminal:
|
1
2
3
4
5
6
7
8
9
10
|
[johndoe@ArchLinux]% openocd -f FT232H.cfg -f Prometheus_BScan.cfg
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
adapter speed: 6000 kHz
xc3s_get_dna
Info : clock speed 6000 kHz
Info : JTAG tap: spartan3a.tap tap/device found: 0x02210093 (mfg: 0x049 (Xilinx), part: 0x2210, ver: 0x0)
Warn : gdb services need one or more targets defined
|
In another terminal connect to the running OpenOCD server and run send_byte script:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
[johndoe@ArchLinux]% telnet localhost 4444
Trying ::1...
Connection failed: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> scan_chain
TapName Enabled IdCode Expected IrLen IrCap IrMask
-- ------------------- -------- ---------- ---------- ----- ----- ------
0 spartan3a.tap Y 0x02210093 0x02210093 6 0x01 0x03
> send_byte 0x93
00
> send_byte 0xA1
93
|
Here is the state of the Prometheus board after writing 0x93 to the USER1 register:

Here is the state of the Prometheus board after writing 0xA1 to the USER1 register:
