1、JTAG是什么?
JTAG是20世紀(jì)80年代開發(fā)的IEEE標(biāo)準(zhǔn)(1149.1),用來解決電路板的生產(chǎn)制造檢修問題。
現(xiàn)在JTAG還可以用來燒程序、調(diào)試以及檢測端口狀態(tài)。本文主要介紹JTAG的基本功能,邊界掃描。
JTAG是20世紀(jì)80年代開發(fā)的IEEE標(biāo)準(zhǔn)(1149.1),用來解決電路板的生產(chǎn)制造檢修問題。
現(xiàn)在JTAG還可以用來燒程序、調(diào)試以及檢測端口狀態(tài)。本文主要介紹JTAG的基本功能,邊界掃描。
1.1邊界掃描
如圖1所示,在一個電路板上有兩個芯片元件,一個CPU和FPGA。
圖1
每個芯片都會有很多引腳,那么芯片之間的互聯(lián)就會有很多連線,圖2示意圖僅僅畫了4條連接線。
圖2
正常情況下,對于芯片廠商,一次制作成千上萬個PCB板子,每個班子上都有許許多多連接線,廠家需要如何保證每根芯片連接線都是正常的呢?
這么大的工作量也不可能通過手工來每一根線進(jìn)行檢測。因此JTAG就應(yīng)運而生了。
圖3
JTAG可以控制芯片的每個引腳,圖3中,我們可以通過JTAG使得所有的CPU引腳發(fā)送數(shù)據(jù),而所有的FPGA引腳接收數(shù)據(jù),然后根據(jù)FPGA中是否收到準(zhǔn)確的數(shù)據(jù)來判斷所有的芯片連接是否正常。
實際上JTAG的連接包括4根信號線,分別是TDI、TDO、TMS和TCK。從電腦主機的角度來看,TDI、TMS、TCK為輸出,TDO為輸入,如果從待測試的芯片角度來看則相反。
圖4
JTAG的四根信號線有特定的連接方式,如圖5所示,TMS和TCK是并聯(lián)在所有待測芯片上的。
圖5
TDI和TDO信號線則是串聯(lián)在一起形成一個閉環(huán)鏈條。在JTAG的技術(shù)手冊中,這種方式也叫JTAG鏈。
圖6
因此,每個JTAG鏈上的芯片都會有四根線連接,其中三個輸入,一個輸出。在技術(shù)手冊中,還會有一個可選的信號線TRST作為第五根信號線。一般而言,JTAG的四個引腳都是專用引腳。
現(xiàn)在所有的JTAG應(yīng)用越來越普遍,基本上所有多引腳的芯片都會包含JTAG邊界掃描功能。此外正如我們開頭所說,CPU和FPGA廠商還用JTAG接口進(jìn)行調(diào)試,對于可編程硬件FPGA和CPLD,還可以用JTAG接口繼續(xù)配置和燒錄程序。
2、JTAG如何起作用?
上一章我們知道了JTAG是如何連接芯片,現(xiàn)在學(xué)習(xí)具體工作原理以及如何通過PC端來控制器運行。
2.1 PC控制JTAG
一般我們用JTAG連接線來連接PC和JTAG端口,電腦端口有并行端口(也叫打印機端口db25)、USB端口以及網(wǎng)線端口。對于數(shù)據(jù)量不大的情況下推薦并行端口,操作簡單。對于大數(shù)據(jù)量推薦USB端口和網(wǎng)口,其速度快但是操作復(fù)雜一些。
2.2 并行端口
電腦主機的并行端口12根線為輸出,5根線為輸入。對于JTAG而言,只用到了3個輸出和一個輸入(從PC角度來看輸入輸出)。因此,中間需要用到一些緩存器,如賽靈思的parallel-III cable。
從軟件代碼的角度來看,并行端口由于簡單是最理想的JTAG端口。例如,阿爾特拉的ByteBlaster JTAG接口用C語言改變TCK信號代碼如下:
#define lpt_addr 0x378
#define TCK 0x01
void toggle_TCK()
{
outport(lpt_addr, 0);
outport(lpt_addr, TCK);
outport(lpt_addr, 0);
}
2.3 JTAG TAP控制器
PC和芯片之間的JTAG連接方式如圖6,下面介紹這四根信號線分別代表什么意思。
TCKTCK是JTAG的時鐘信號,另外三個信號TDI、TDO、TMS都是跟該時鐘信號同步的。一般其他三根信號都是在TCK時鐘的上升沿發(fā)生改變或者狀態(tài)的切換。
TMS在每個芯片的內(nèi)部都有JTAG TAP控制器,圖6中有兩個CPU和FPGA兩個芯片,那么就有兩個TAP控制器。
一般我們在數(shù)據(jù)手冊上看到的狀態(tài)控制器就是這個,它有16個狀態(tài),如圖8所示。TMS就是個控制TAP控制器的信號,根據(jù)TMS的高低電平變化,TAP控制器進(jìn)入這16個狀態(tài)中的一種,又因為同一個PCB板子上TMS是并聯(lián)所有芯片 ,因此所有芯片都會處于同一狀態(tài)。
圖8
上圖中每個狀態(tài)旁邊的0和1代表的是TMS的低、高電平。
比如如果TAP狀態(tài)控制器處于Select DR-Scan狀態(tài),且TMS為0,那么當(dāng)TCK時鐘信號切換時,TAP的狀態(tài)就會變化下面的Capture-DR。
這里再強調(diào)一遍,要想JTAG正常工作,所有的鏈上的TAP控制器必須處于同一狀態(tài)。
PCB板上電后,是如何保證所有芯片的TAP處于同一狀態(tài)呢?
仔細(xì)觀察圖8,不管TAP在哪個狀態(tài),如果TMS在5個時鐘周期內(nèi)都保持1,那么TAP都會變成Test-Logic-Reset狀態(tài),這便是用來同步TAP狀態(tài)的方法。
來看下面的代碼,如何將TAP控制器切換到Shift-IR狀態(tài)。
// first sync everybody to the test-logic-reset state
for(i=0; i<5; i++) JTAG_clock(TMS);
// now that everybody is in a known and identical state, we can move together to another state
// let's go to Shift-IR
JTAG_clock(0);
JTAG_clock(TMS);
JTAG_clock(TMS);
JTAG_clock(0);
JTAG_clock(0);
TDI和TDO現(xiàn)在我們已經(jīng)知道了如何切換TAP狀態(tài)了,下面介紹JTAG最重要的兩個狀態(tài)Shift-DR和Shift-IR。
圖9
Shift-DR和Shift-IR必須結(jié)合TDI和TDO信號線才能起作用,首先介紹Shift-DR。
每個芯片的TAP控制器中都有一個IR寄存器,也叫做指令寄存器。你可以把相關(guān)指令寫入這個寄存器,然后TAP控制器會根據(jù)IR寄存器的指令進(jìn)行相關(guān)操作。
每個IR寄存器都有一定的長度,我們假設(shè)CPU的IR寄存器是5位,F(xiàn)PGA的寄存器是10位,那么通過TDI和TDO的信號線連接方式,CPU和FPGA的IR寄存器其實是串聯(lián)的,如圖10所示。
圖10
我們從PC主機的角度來看,整個鏈的IR寄存器是15位的,5位CPU和10位FPGA。
要想將IR寄存器寫入數(shù)據(jù),我們需要將TAP控制器的狀態(tài)切換成Shift-IR,然后PC通過TDI信號線寫入15位數(shù)據(jù)。前10位數(shù)據(jù)寫入的是FPGA的IR寄存器,后5位數(shù)據(jù)寫入的是CPU的IR寄存器。
如果PC寫入的數(shù)據(jù)多于15位,那么溢出的數(shù)據(jù)就會通過TDO信號線再被PC端給接收,只不過延時了15個時鐘周期。
例如,我們想吧數(shù)值00100寫入CPU的IR寄存器,而0000000010寫入FPGA的IR寄存器,C語言代碼如下:
// Because the bits are shifted through in a chain, we must start sending the data for the device that is at the end of the chain
// so we send the 10 FPGA IR bits first
JTAG_clock(0);
JTAG_clock(1);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0);
// then send the 5 CPU IR bits
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(1);
JTAG_clock(0);
JTAG_clock(0 | TMS); // last bit needs to have TMS active (to exit shift-IR)
在我們的假設(shè)中,CPU的IR是5位(可以表示數(shù)值0~31)。
那么CPU的IR寄存器可以支持32條JTAG指令。實際上,一個CPU可能只會有5~10條指令,剩下的IR寄存器數(shù)值都沒有用。
同樣的對于FPGA,它的IR寄存器是10位,那么它可以支持1024條JTAG指令(大部分也是沒用的)。
但是JTAG有幾條強制的指令必須都有:
BYPASSEXTESTSAMPLE/PRELOADIDCODE(這個不是強制的,但是非常常見)每個芯片的都有IR數(shù)值的指令集,從芯片手冊上都可以查到。
每個芯片的TAP控制器都只有一個IR寄存器,但是會有很多DR寄存器。我們知道IR寄存器數(shù)據(jù)切換是通過TAP的Shift-IR狀態(tài),類似的,DR寄存器的數(shù)據(jù)切換也是這樣,只不過狀態(tài)是TAP的Shift-DR狀態(tài)。
每一個IR寄存器的數(shù)值都會對應(yīng)一個不同的DR寄存器,在我們的假設(shè)中IR寄存器為5位,那么就有32個IR數(shù)值,因此就有32個DR寄存器(如果32個IR數(shù)值都被當(dāng)做指令的話)。
2.4 計算JTAG鏈中元件個數(shù)
IR寄存器的指令不同芯片有所區(qū)別,但是有一個指令是一樣的,那就是BYPASS指令。
它的IR寄存器所有位都是1。對于CPU是11111,對于FPGA的IR寄存器,其數(shù)值是1111111111。
在BYPASS指令模式下,TAP控制器對應(yīng)的DR寄存器是個單觸發(fā)器,只是將TDI的輸入數(shù)據(jù)延時一個時鐘周期然后通過TDO輸出。
根據(jù)這個特性,我們可以用BYPASS指令來計算JTAG鏈上有多少個芯片。
在此指令下,每個芯片的DR寄存器會延時一個時鐘周期,那么我們發(fā)送一個數(shù)據(jù)后,檢查延時多少周期收到數(shù)據(jù),即可知道JTAG鏈上芯片的數(shù)量。
具體實現(xiàn)的C語言代碼如下:
// go to reset state
for(i=0; i<5; i++) JTAG_clock(TMS);
// go to Shift-IR
JTAG_clock(0);
JTAG_clock(TMS);
JTAG_clock(TMS);
JTAG_clock(0);
JTAG_clock(0);
// Send plenty of ones into the IR registers
// That makes sure all devices are in BYPASS!
for(i=0; i<999; i++) JTAG_clock(1);
JTAG_clock(1 | TMS); // last bit needs to have TMS active, to exit shift-IR
// we are in Exit1-IR, go to Shift-DR
JTAG_clock(TMS);
JTAG_clock(TMS);
JTAG_clock(0);
JTAG_clock(0);
// Send plenty of zeros into the DR registers to flush them
for(i=0; i<1000; i++) JTAG_clock(0);
// now send ones until we receive one back
for(i=0; i<1000; i++) if(JTAG_clock(1)) break;
nbDevices = i;
printf("There are %d device(s) in the JTAG chain\n", nbDevices);
// 將JTAG鏈置于復(fù)位狀態(tài)
// 根據(jù)JTAG標(biāo)準(zhǔn),通過一系列TMS信號(通常是特定的序列)將JTAG接口置于復(fù)位狀態(tài)
// 這里簡單地假設(shè)連續(xù)5個TMS時鐘信號足以實現(xiàn)復(fù)位
for(i=0; i<5; i++) JTAG_clock(TMS);
// 從復(fù)位狀態(tài)轉(zhuǎn)移到Shift-IR狀態(tài)
// Shift-IR狀態(tài)允許數(shù)據(jù)被移入到JTAG鏈上每個設(shè)備的指令寄存器(IR)中
// 這里的序列是JTAG標(biāo)準(zhǔn)定義的一部分,用于進(jìn)入Shift-IR狀態(tài)
JTAG_clock(0);
JTAG_clock(TMS);
JTAG_clock(TMS);
JTAG_clock(0);
JTAG_clock(0);
// 向IR寄存器發(fā)送多個1
// 因為BYPASS指令在大多數(shù)JTAG設(shè)備中是通過在IR寄存器中設(shè)置全1來實現(xiàn)的
// 所以這里通過發(fā)送999個1(實際上只需要足夠多的1以確保所有設(shè)備都接收到BYPASS指令)
// 來確保所有設(shè)備都進(jìn)入BYPASS模式
for(i=0; i<999; i++) JTAG_clock(1);
JTAG_clock(1 | TMS); // 發(fā)送最后一個1時,同時激活TMS以退出Shift-IR狀態(tài)
// 從Exit1-IR狀態(tài)轉(zhuǎn)移到Shift-DR狀態(tài)
// Shift-DR狀態(tài)允許數(shù)據(jù)被移入到JTAG鏈上每個設(shè)備的數(shù)據(jù)寄存器(DR)中
// 同樣,這里的序列是JTAG標(biāo)準(zhǔn)定義的一部分
JTAG_clock(TMS);
JTAG_clock(TMS);
JTAG_clock(0);
JTAG_clock(0);
// 發(fā)送多個0到DR寄存器以清空它們
// 在進(jìn)入測量模式前,先通過發(fā)送一系列0來清空DR寄存器中的任何殘留數(shù)據(jù)
for(i=0; i<1000; i++) JTAG_clock(0);
// 現(xiàn)在發(fā)送1,直到接收到返回的1
// 在BYPASS模式下,DR寄存器被簡化為一個單觸發(fā)器,它會將TDI的輸入延時一個時鐘周期后輸出到TDO
// 通過發(fā)送一系列1并檢查TDO輸出,我們可以測量從發(fā)送到接收的延時,從而計算出JTAG鏈上的設(shè)備數(shù)量
// 當(dāng)TDO返回1時,循環(huán)結(jié)束
for(i=0; i<1000; i++) if(JTAG_clock(1)) break;
// i的值現(xiàn)在等于JTAG鏈上設(shè)備的數(shù)量(加1,因為包括發(fā)送的那個時鐘周期)
// 但由于我們是在尋找TDO返回1的時鐘周期,所以實際的設(shè)備數(shù)量應(yīng)該是i
nbDevices = i;
// 打印JTAG鏈上的設(shè)備數(shù)量
printf("There are %d device(s) in the JTAG chain\n", nbDevices);
啰嗦解釋一下:
由于BYPASS模式下,每個設(shè)備都會將輸入數(shù)據(jù)延時一個時鐘周期后輸出,因此,我們可以通過發(fā)送一個數(shù)據(jù)位,并測量從發(fā)送到接收到該數(shù)據(jù)位的時間(即時鐘周期數(shù)),來推斷JTAG鏈上設(shè)備的數(shù)量。具體做法如下:
初始化JTAG鏈:首先,通過一系列TMS信號將JTAG鏈置于復(fù)位狀態(tài),然后切換到Shift-IR狀態(tài),向每個設(shè)備的IR寄存器發(fā)送全1(即BYPASS指令),最后將JTAG鏈切換到Shift-DR狀態(tài)。清空DR寄存器:在發(fā)送BYPASS指令后,通過向DR寄存器發(fā)送一系列0來清空可能存在的舊數(shù)據(jù)。發(fā)送并接收數(shù)據(jù):向TDI發(fā)送一個1,然后逐個時鐘周期地檢查TDO輸出。由于每個設(shè)備都會將輸入延時一個時鐘周期,所以TDO將在(設(shè)備數(shù)量+1)個時鐘周期后輸出1(加1是因為包括發(fā)送數(shù)據(jù)的那個時鐘周期)。計算設(shè)備數(shù)量:通過記錄從發(fā)送數(shù)據(jù)到接收到數(shù)據(jù)所經(jīng)過的時鐘周期數(shù),減去1(發(fā)送數(shù)據(jù)的那個周期),即可得到JTAG鏈上的設(shè)備數(shù)量。C程序它首先通過一系列TMS信號將JTAG鏈置于正確的狀態(tài),然后發(fā)送BYPASS指令到每個設(shè)備的IR寄存器,并清空DR寄存器。最后,通過發(fā)送一系列1到TDI并檢查TDO的輸出來計算設(shè)備數(shù)量。當(dāng)TDO輸出1時,循環(huán)結(jié)束,此時循環(huán)的迭代次數(shù)(減去1)即為JTAG鏈上的設(shè)備數(shù)量。
2.5 獲得JTAG鏈上芯片的ID
大部分的芯片JTAG模塊都支持IDCODE指令**,這個指令對應(yīng)的DR寄存器是32位,具體數(shù)值代表者不同芯片的ID。**
不同于BYPASS指令,INCODE指令的IR寄存器數(shù)值不是標(biāo)準(zhǔn)的,我們可以通過器件手冊來查詢。
還有一種方法,當(dāng)TAP控制器的狀態(tài)處于Test-Logic-Reset時,它都會將INCODE數(shù)據(jù)寫入DR寄存器中,我們可以據(jù)此讀出DR寄存器的內(nèi)容,C語言代碼如下:
// go to reset state (that loads IDCODE into IR of all the devices)
for(i=0; i<5; i++) JTAG_clock(TMS);
// go to Shift-DR
JTAG_clock(0);
JTAG_clock(TMS);
JTAG_clock(0);
JTAG_clock(0);
// and read the IDCODES
for(i=0; i < nbDevices; i++)
{
printf("IDCODE for device %d is %08X\n", i+1, JTAG_read(32));
}
3、邊界掃描
本章節(jié)講解JTAG的邊界掃描。
當(dāng)TAP控制器進(jìn)入“boundary-scan”的狀態(tài),其實也就是IR寄存器存入SAMPLE指令、EXTEST指令等,此時對應(yīng)的DR寄存器就是邊界掃描寄存器,這個寄存器將每個I/O單元連接在一起并且可以控制每個引腳。
圖11
當(dāng)芯片正常過程當(dāng)中也是可以進(jìn)行邊界掃描的,例如對正常運行中的FPGA進(jìn)行邊界掃描,它可以將每個管腳的狀態(tài)顯示出來。
3.1、SAMPLE
現(xiàn)在我們嘗試讀取管腳的值,對應(yīng)的IR寄存器的指令是SAMPLE。每個芯片的具體指令數(shù)值不同,查找數(shù)據(jù)手冊或者芯片的BSDL文件來獲取具體的指令。
BSDL全稱是boundary scan description language,它是硬件描述語言(VHDL)的一個子集。
一個BSDL文件其實就是一個描述邊界鏈的VHDL文件。下面是阿爾特拉的BSDL文件(Cyclone EP1C3 in TQFP 100 pins package):
attribute INSTRUCTION_LENGTH of EP1C3T100 : entity is 10;
attribute INSTRUCTION_OPCODE of EP1C3T100 : entity is
"BYPASS (1111111111), "&
"EXTEST (0000000000), "&
"SAMPLE (0000000101), "&
"IDCODE (0000000110), "&
"USERCODE (0000000111), "&
"CLAMP (0000001010), "&
"HIGHZ (0000001011), "&
"CONFIG_IO (0000001101)";
attribute INSTRUCTION_CAPTURE of EP1C3T100 : entity is "0101010101";
attribute IDCODE_REGISTER of EP1C3T100 : entity is
"0000"& --4-bit Version
"0010000010000001"& --16-bit Part Number (hex 2081)
"00001101110"& --11-bit Manufacturer's Identity
"1"; --Mandatory LSB
attribute BOUNDARY_LENGTH of EP1C3T100 : entity is 339;
從上面這個文件我們可以知道:
IR寄存器的長度是10位;IR指令寄存器的指令清單,比如SAMPLE的是0000000101,也就是0x005;該器件的IDCODE,00001101110是廠商的代號(阿爾特拉);邊界掃描鏈的長度是339位。邊界掃描寄存器有339位,并不意味著有339個管腳。每一個管腳都有一個IO pad(芯片管腳處理模塊),IO pad用1~3位寄存器(取決于該管腳是輸入、三態(tài)輸出或是輸入輸出均可)。
當(dāng)然一些IO pad包含的寄存器不一定包含在邊界掃描鏈中。這就解釋了為什么這個100管腳的芯片有339位的邊界掃描寄存器。
接著看BSDL文件:
attribute BOUNDARY_REGISTER of EP1C3T100 : entity is
--BSC group 0 for I/O pin 100
"0 (BC_1, IO100, input, X)," &
"1 (BC_1, *, control, 1)," &
"2 (BC_1, IO100, output3, X, 1, 1, Z)," &
--BSC group 1 for I/O pin 99
"3 (BC_1, IO99, input, X)," &
"4 (BC_1, *, control, 1)," &
"5 (BC_1, IO99, output3, X, 4, 1, Z)," &
...
...
...
--BSC group 112 for I/O pin 1
"336 (BC_1, IO1, input, X)," &
"337 (BC_1, *, control, 1)," &
"338 (BC_1, IO1, output3, X, 337, 1, Z)" ;
這一段羅列了邊界掃描寄存器的339位的用途。
例如,處于第4位(其實是位3,從0開始計算的)保存的是管腳99的值。
現(xiàn)在讀取邊界掃描寄存器,并且將管腳99的值打印出來:
// go to reset state
for(i=0; i<5; i++) JTAG_clock(TMS);
// go to Shift-IR
JTAG_clock(0);
JTAG_clock(TMS);
JTAG_clock(TMS);
JTAG_clock(0);
JTAG_clock(0);
// Assuming that IR is 10 bits long,
// that there is only one device in the chain,
// and that SAMPLE code = 0000000101b
JTAG_clock(1);
JTAG_clock(0);
JTAG_clock(1);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0);
JTAG_clock(0 or TMS); // last bit needs to have TMS active, to exit shift-IR
// we are in Exit1-IR, go to Shift-DR
JTAG_clock(TMS);
JTAG_clock(TMS);
JTAG_clock(0);
JTAG_clock(0);
// read the boundary-scan chain bits in an array called BSB
JTAG_read(BSB, 339);
printf("Status of pin 99 = %d\n, BSB[3]);
3.2、邊界掃描寄存器
下圖是阿爾特拉數(shù)據(jù)手冊中TAP狀態(tài)寄存器、IR寄存器、DR寄存器結(jié)構(gòu)圖,各層級關(guān)系比較一目了然。
我們可以得出以下信息:
IR指令寄存器(Instruction Register)的值決定采用什么指令以及選擇對應(yīng)的DR寄存器(Data Register);Bypass Register只有一位,其指令BYPASS我們上文已經(jīng)講過,其提供從TDI到TDO最短路徑;邊界掃描寄存器是個移位寄存器,由芯片上所有的管腳BSC組成。阿爾特拉的BSC全稱是Booundary scan cell,它可以將信號施加到管腳,或者獲得管腳上的數(shù)據(jù)和內(nèi)部邏輯信號。我們JTAG測試的數(shù)據(jù)也是串行輸入到BSC單元中,捕獲到的數(shù)據(jù)也是串行從BSC輸出,進(jìn)而判斷測試結(jié)果。根據(jù)我的理解,此處的BSC就是我們上文提到的IO pad芯片管腳處理模塊。3.3、JTAG還可以做什么?
控制芯片的引腳狀態(tài),對應(yīng)的IR指令為EXTEST,表示外部測試,可以讓輸出管腳輸出高低邏輯電平,根據(jù)輸入接受到的電平信號檢測JTAG鏈中任何設(shè)備管腳處的開路和短路情況;用于FPGA和CPLD的配置;JTAG接口可以作為調(diào)試端口。怎么樣,通過本文的講解是不是對JTAG有了基本的認(rèn)識。