MCU模擬JTAG接口對LATTICE CPLD FPGA 進行在線編程加載

完整版請點擊 https://hifpga.com/問題/719

索取源碼,向博主本人提問FPGA相關問題

作者:Rock.Ding(萊迪思半導體公司)
關鍵字:MCU, JTAG, 在線編程, CPLD。

前言

CPLD(Complex Programmable Logic Device)複雜可編程邏輯器件,是從PAL和GAL器件發展出來的器件,相對而言規模大,結構複雜,屬於大規模集成電路範圍。是一種用戶根據各自需要而自行構造邏輯功能的數字集成電路。其基本設計方法是藉助集成開發軟件平臺,用原理圖、硬件描述語言等方法,通過編譯,映射,佈局佈線,最後生成相應的JED文件,然後通過CPLD廠家提供的下載電纜和下載軟件把JED文件下載器件裏面,實現用戶的功能。在實際應用中,根據設計需要或修復之前設計中的Bug,常常需要在線升級CPLD的內容。目前各廠家都有提供相應的方案,可以在嵌入式系統中通過CPU來更新CPLD。廠家考慮到方案的兼容性,往往實現編程的代碼都比較複雜,而且佔用內存資源較大。不適合MCU這樣的小系統。本文以Lattice XO2系列的CPLD爲例,詳細介紹JTAG的編程原理及如何用MCU來模擬JTAG編程Lattice XO2 CPLD。理解了JTAG編程原理後,也可以很容易實現模擬JTAG編程其它的CPLD和FPGA。

一,JTAG介紹

JTAG最初是用來對芯片進行測試的,JTAG的基本原理是在器件內部定義一個TAP(Test Access Port;測試訪問口)通過專用的JTAG測試工具對內部節點進行測試。JTAG測試允許多個器件通過JTAG接口串聯在一起,形成一個JTAG鏈,能實現對各個器件分別測試。如今,JTAG接口還常用於實現ISP(In-System Programmer,在系統編程),對CPLD,FPGA等器件進行編程。
JTAG編程方式是在線編程,傳統生產流程中先對芯片進行預編程然後再裝到板上,簡化的流程爲先固定器件到電路板上,再用JTAG編程,從而大大加快工程進度及編程的靈活性。
JTAG引腳定義
具有JTAG口的芯片都有如下JTAG引腳定義:

  • TCK——測試時鐘/編程時鐘輸入;
  • TDI——測試數據/編程數據輸入,數據通過TDI輸入JTAG口;
  • TDO——測試數據/編程數據輸出,數據通過TDO從JTAG口輸出;
  • TMS——測試模式選擇/改變TAP內部的狀態機的狀態。
  • TDI, TMS上的數據輸入及TDO上的數據輸出都是在TCK時鐘的作用下完成的。

二,JED文件介紹

JED文件是一個包含了器件的編程內容的文本文件。本文以Lattice XO2系列的CPLD的JED文件爲例,簡約介紹JED文件的格式,下面是Lattice的開發工具生成的JED文件中截取的一部分:

NOTE Diamond_1.2_Production (92) JEDEC Compatible Fuse File.*
NOTE Copyright (C), 1992-2010, Lattice Semiconductor Corporation.*
QP132*
QF343936*
G0*
F0*
L000000
1111111111111111101111011011001111111111111111110011101100000000000000000
NOTE EBR_INIT DATA*
L137984
1111111111111111111111111111111111110110000000000000000000000000000000000
NOTE END CONFIG DATA*
L184832
0000000000000000000000000000000000000000000000000000000000000000000000000
NOTE TAG DATA*
L343808
0100010001000100000010001000000000000000000100010000000000000000000000000
C5CC8*
NOTE FEATURE_ROW*
E0000000000000000000000000000000000100001000010000000000000000000
NOTE User Electronic Signature Data*
UHCAFEBABE*
6243

...

三,模擬JTAG編程。

1,怎樣獲得JTAG的編程細節?

如果手上沒有某個CPLD的具體JTAG編程指導文檔,可以用廠家的編程工具生成SVF(Serial Vector Format)文件,該文件包含JTAG編程的詳細流程和編程類容。以Lattice最新的編程工具Diamond Programmer爲例,它附帶了一個小工具,叫做Deployment Tool(圖1),就可以生成SVF文件。
MCU模擬JTAG接口對LATTICE CPLD進行在線編程加載圖1

SVF文件是文本文件,裏面包含定義好的各種命令,詳細描述了JTAG的編程行爲。具體可參考Serial Vector Format Specification標準。SVF文件裏面的命令很容易理解,下面從SVF文件中摘取一段擦除器件的SVF代碼,做簡單介紹:

! Erase the Flash
! Shift in ISC ERASE(0x0E) instruction
SIR 8 TDI (0E); SDR 8 TDI (0E);
RUNTEST IDLE 2 TCK;
! Shift in LSC_CHECK_BUSY(0xF0) instruction
SIR 8 TDI (F0);
LOOP 350 ;
RUNTEST IDLE 2 TCK 1.00E-002 SEC;
SDR 1 TDI (0) TDO (0);
ENDLOOP ;

感嘆號後面的類容都是註釋,只是爲了更好的理解SVF命令。
...

2,解析SVF命令。

如果用廠家的編程工具來編程,是由廠家的軟件來控制JTAG口上的輸入/輸出數據的時序。現在我們通過SVF文件瞭解JTAG編程具體流程後,也可以解析SVF 命令來模擬這個時序。仔細觀察SVF文件,裏面主要包括三類指令,SIR,SDR,控制狀態機的指令,SDR常用的有三種方式:

  • 1,只在TDI上送入數據,例如:

    SDR 128 TDI (0000B280071D000000623FFC0DB01216);

  • 2,送入數據同時,在TDO上讀取數據,並判斷讀取的數據是否是期望的值,例如:

    SDR 128 TDI (00000000000000000000000000000000)
    TDO (414900000040000000DCFFFFCDBDFFFF);

  • 3,是在TDO上讀取數據時,只判斷MASK相應Bit位是1的TDO的值是否是期望的值,例如:

    SDR 32 TDI (00000000) TDO (012B2043) MASK (FFFFFFFF);

下面是解析SVF命令的C51函數,可以直接在Keil C51環境下編譯。

1,JTAG狀態機轉換。

通過在TMS管腳上輸入不同的01序列來實現JTAG狀態機的控制,下面控制狀態機的代碼是在Lattice編程工具提供的參考代碼修改而來。

typedef struct
{     
     BYTE CurState;/*** From this state ***/
      BYTE NextState;/*** Step to this state ***/
      BYTE Pattern;/*** The pattern of TMS ***/
      BYTE Pulses;/*** The number of steps ***/
}JTAGState;
JTAGState code JTAGStateTable[25]=
{
        {DRPAUSE,  SHIFTDR,  0x80,  2},
        {IRPAUSE,  SHIFTIR,  0x80,  2},一共25個狀態,篇幅影響,這裏只列了2個狀態。
};
void Set_JTAG_State_Machine(BYTE nextstate)
{
    BYTE data i,j,temp;
    if((CurState == nextstate)&&(CurState != RESET))return;
    for(i = 0;i < 25;i++)
    {
        if((CurState == JTAGStateTable[i].CurState)&&(nextstate == 
        JTAGStateTable[i].NextState))break;
    }
    CurState = nextstate;
    temp = JTAGStateTable[i].Pattern;
    for(j = 0;j < JTAGStateTable[i].Pulses;j++)
    {
        if((temp & 0x80)== 0x80)TMSPin = 0x01; else TMSPin = 0x00;
        Send_1Clk(); temp = temp << 1;
    }
    TDIPin = 0x00; TMSPin = 0x00;
}

首先定義一個數據結構,....

2,解析SIR(Scan Instruction Register)命令。

SIR是往指令寄存器中送入數據,縱觀整個SVF文件,SIR命令送入的數據都是8Bit,實現SIR命令的C51函數如下:

BYTE Excute_SIR(BYTE instruction)
{
    BYTE data i;
    ....
    for(i = 0;i < 8;i++)
    {
        ...
    }
    Set_JTAG_State_Machine(IRPAUSE); return(SUCCESS);
}

該函數只需輸入一個參數instruction,函數開始會把JTAG狀態機轉換到IRPAUSE狀態,然後再轉換到SHIFTIR狀態,再把8Bit 的instruction從TDI管腳送入JTAG,先送低Bit。最後再把JTAG狀態機轉換到IRPAUSE狀態。注意在for循環中,在TCK上只需送入7個時鐘週期,也就是說這裏送入的時鐘週期比數據少一個。

3,解析SDR(Scan Data Register)命令

SDR命令往數據寄存器中送入和讀出數據。編程時的數據輸入和輸出都是通過這個命令來實現。實現SDR的C51函數如下:

BYTE Excute_SDR(BYTE bit_len,BYTE TDI_mark,BYTE TDO_mark,BYTE mask_mark)
{
    BYTE data I, TDI_data, TDO_data, mask_data, ptr, result;   
    ptr = 0;  result = SUCCESS;
    ...
    for(i = 0;i < bit_len;i++)
    {
    ...
    }
    Set_JTAG_State_Machine(DRPAUSE);
    return(result);
}

上小節說到SDR命令可能有3種模式,不同的模式可能需要用到TDI,TDO,MASK數據,在調用這個函數前,根據需要先把數據存入TDI_Buffer,TDO_Buffer,MASK_Buffer裏面,然後調用函數時,把需要送入JTAG的 Bit數據長度,是否有TDI數據,TDO數據及MASK數據四個參數傳送給函數。在TDI上送入數據前,先把JTAG狀態機轉換到DRPAUSE,再轉換到SHIFTDR,然後開始送入數據,同樣需要注意在送數據時,在TCK上時鐘週期數是送入數據Bit數少一個,例如送入128Bit數據,只送127個時鐘週期。送完數據後,再把狀態機轉換到DRPAUSE,這樣整個操作完成。函數執行成功,返回1,否則返回0。

四, MCU模擬JTAG編程CPLD完整例子。

上一節已經講了實現JTAG編程的3個主要函數,本節主要是怎樣調用這三個函數實現對CPLD的編程。本例子用到MCU是STC89LE516RD+,CPLD是Lattice LCMXO1200ZE。
Lattice LCMXO1200ZE大致編程流程是:

  • 1,檢查器件ID,2,編程BSCAN 寄存器,3,使能編程,4,擦除Flash,5,編程Flash,一次編程1行128Bit,一種2175行,6, 編程user code,7,校驗Flash, 8,校驗User code,9,編程Done Bit,10, 退出編程模式, 11,Refresh。每一個操作可以用一個函數來實現,這裏只講解兩個典型的操作:
  • 2, 編程Flash ,編程Flash前首先要復位Flash的地址,這個操作只執行一次,接着每編程一行,Flash的地址會自動加一,函數如下:

    BYTE Init_Address(void)
    {
       BYTE  result;
       Excute_SIR(0x46); TDI_Buffer[0] = 0x04;
       result = Excute_SDR(8,1,0,0);
       Set_JTAG_State_Machine(IDLE);
       Send_Num_Clk(2);Wait_ms(10);
       return(result);
    }
    

    然後以行爲單位,編程Flash。調用函數前,需要把編程數據放入TDO_Buffer中。函數執行過程,先送PROG_INCR_NV指令,然後送入編程數據,最後檢測編程狀態,直到Busy Bit爲0後才表示編程成功。成功函數返回1,否則返回0。下面是編程一行的函數,根據器件大小,Flash的行數不同,反覆調用這個函數來編程Flash。

    BYTE Program_Row(void)
    {
      BYTE data loop,result;
       Excute_SIR(0x70); Excute_SDR(128,1,0,0);
       Set_JTAG_State_Machine(IDLE);
       Send_Num_Clk(2); Excute_SIR(0xf0);
       TDI_Buffer[0] = 0x00; TDO_Buffer[0] = 0x00;
       for(loop = 0;loop < 10;loop++)
       {
          Set_JTAG_State_Machine(IDLE); Send_Num_Clk(2);
           Wait_ms(10); result = Excute_SDR(1,1,1,0);
           if(result == SUCCESS)break;
       }
       return(result);
    }
    
  • 3, 校驗 Flash ,校驗Flash時,也需要先調用復位Flash 地址的函數Init_Address;然後輸入LSC_READ_INCR_NV指令,表示開始讀取Flash的數據,最後循環調用Verify_Row函數,來讀取數據並校驗是否正確,在每次調用Verify_Row函數前,需把校驗數據放入TDO_Buffer中。相關函數如下:

    BYTE Verify_Row(void)
    {
       BYTE data result;
       result = Excute_SDR(128,0,1,0);
       Set_JTAG_State_Machine(IDLE);
       Send_Num_Clk(2);  Wait_ms(1);
       return(result);
    }
    
  • 4, 本例子中所有的命令和數據來自串口,每接收到一個命令執行一次操作。串口函數根據約定好的協議,從上位機接收命令和數據,接收到成功後,把相關命令傳送給主函數,主函數調用相關函數執行相應的操作,執行完成後,又回到等待狀態,等待下一個命令。上位機界面如下,只需要選擇好串口,選好下載文件,點“Start”按鈕就可以了。這裏下載的文件是從JED文件轉換而來,因爲JED文件是文本文件,而且包含了很多註釋信息,這裏只提取了Flash行的類容,並轉化爲BIN文件。
    MCU模擬JTAG接口對LATTICE CPLD進行在線編程加載圖2

總結

本例子模擬JTAG編程有關的代碼大約只有300行,只要理解JTAG編程原理,就可以化繁爲簡。根據自己的需求,定製編程流程,不需完全照搬廠家提供的代碼,這樣在調試或功能改進中都更加容易和靈活。有需要源代碼的可以先在下面回覆然後發郵件給我,發郵件時記得寫上論壇用戶名,我的郵箱是:
[email protected]

參考文獻:
1, MachXO2 Family Handbook,HB1010.pdf。
2, Serial Vector Format Specification,SVF_Spec_RevE.pdf。
3, Lattice VME source code。

 

完整版請點擊 https://hifpga.com/問題/719

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章