DSP

最佳的FSM学习对象-JTAG标准的状态机实现

2019-07-13 20:50发布

JTAG标准的状态机实现 - 基于Verilog HDL
本文参考:《通信IC设计》 大家比较感兴趣的话,可以自行购买相应书籍进行研读。
JTAG协议是目前应用最广泛的下载和仿真协议,对协议的最初缔造者来说,这也许有点无心插柳柳成荫的感觉——最初的JTAG协议只是用来辅助专门的硬件质检部门对印刷电路进行检测的。这些老账现不再提了
我们现在看到的各种JTAG下载和仿真协议都是各生产厂商在原有JTAG协议的思想上进行扩充的,并不具有通用性,甚至连最基本的电路检测部分功能也被加以改造——比如,一个JTAG指令可能在大部分的版本中是4 位长度的,而对于AVR32来说,一个JTAG指令却是5位。总之,现在大行其道的JTAG协议,不变的部分就只有其核心TAP状态机了。ARM有ARM的JTAG,AVR有AVR的JTAG,51有51的JTAG……
这里写图片描述
这里写图片描述 首先需要了解以下含义:
  • A、JTAG协议的本质与SPI协议并没有什么不同,它等于一个复杂的SS状态机+变长的MOSI和MISO数据移位操作。不过所谓的变长,都是事先约定好的。
  • B、JTAG协议是一个同步通讯协议,它是全双工的。它的通讯原则是“以物易物”——即你如果想得到某些东西,你必须先给与相同长度的内容;你如果只是想发送一些数据,也会自动获取相同长度的内容,至于交换的内容是否有意义,这是另外一回事了。
  • C、JTAG协议无论多么复杂,实际上只有4根线起作用(有时候还有两根鸡肋的nSRST和TRST),他们分别是TMS、TCK、TDI和TDO,他们分别对应SPI协议里面的SS、SCK、MOSI和MISO。在本质上,他们并没有什么不同。即便是ARM的JTAG那么多的引脚,实际上起作用JTAG的也就这4根线而已。
  • D、JTAG的数据操作都是基于移位寄存器的。
  • E、如果JTAG协议在某个下载仿真协议中只是用来发送控制信息和少量的数据,而大量的数据传输是通过额外的其它引脚进行的,即便这个协议被称为JTAG仿真其本质也早已超过JTAG了,严格来说,不应该称之为JTAG。因为JTAG协议中就只有4根线(有时候也算上nSRST和TRST)而已。
1.JTAG标准介绍 JTAG的基本原理是在器件内部定义一个TAP(Test Access Port )(测试访问口) 通过庄勇的JTAG测试工具对内部节点进行测试。JTAG测试允许多个器件通过JTAG接口串联在一起,形成JTAG链,能实现对哥哥器件分别测试。JTAG引脚的定义如表1-6所示。
这里写图片描述
JTAG标准的信号时序图如图1-37所示。
这里写图片描述 通过JTAG连接,可以完成如下的功能:
  1. 1)对所有串接在一起的IC进行引脚连接性测试,确认PCB是否焊接正常;
  2. 2)对CPU、DSP、FPGA等进行调试;
  3. 3)通过JTAG对FPGA进行编程。
进行引脚连接测试的JTAG用法如图1-38所示。对各个芯片引脚的连通状态可以依次串接通信到PC的TDO引脚中。
这里写图片描述
按照菊花链方式串接调试的JTAG用法如图1-39所示,多个串接在一起的CPU和FPGA都能够在一起进行调试和测试。
这里写图片描述 图1-39已经给出了JTAG的调试原理:
  1. 将所有调试芯片的IR寄存器串接在一起,然后进行串行移位,最后,所有数据都进入到JTAG口的TDO中;PC通过对TDO数据的串并转换,获得每个CPU/DSP或FPGA的内部寄存器状态。
  2. PC将需要写入CPU/DSP或FPGA的数据通过并串转换放置到TDI总线上,最后通过状态移位到规定的CPU寄存器上,最后通过TMS指定生效时刻。
  3. TCK就是TDI和TDS的移位时钟,而TMS则是控制指令。
因此,JTAG则是通过一个标准状态机就能将CPU/DSP/FPGA的内部状态查明,也能改变内部寄存器内容,这也是一种状态控制原理。在1990年前,JTAG的状态机基本是由各个厂商自行定义的,后来出现的IEEE1149.1标准对状态转移过程中进行了标准化。某种意义上讲,JTAG的状态机实现过程是最佳的FSM学习对象。 2.JTAG状态机的设计 JTAG内部的状态转移图如图1-40所示,其中的状态值可以由自己定义,但是推荐采用独热码或者格雷码进行编码。
这里写图片描述 下面的代码是实现JTAG功能的FSM部分,该代码已经应用到多款ASIC芯片中,具有较高的研究价值。 //TAP FSM implementation module tap_FSM #(parameter sync_mode = 1)( input tck, input trst_n, input tms, input tdi, output byp_out, output updateIR,reset_n, output reg clockDR, updateDR, clockIR, tdo_en, shiftDR,shiftIR, output selectIR, sync_capture_en, sync_update_dr, flag, output [15:0] tap_state ); //Inter signal declaration reg [15:0] state; reg [15:0] next_s; reg scan_out_a, scan_out_s, updateIR_a; localparam TEST_LOGIC_RESET = 16'h0001, RUN_TEST_IDLE = 16'h0002, SELECT_DR_SCAN = 16'H0004, CAPTURE_DR= 16'h0008, SHIFT_DR = 16'h0010, EXIT1_DR = 16'h0020,PAUSE_DR = 16'h0040, EXIT2_DR = 16'h0080, UPDATE_DR= 16'h0100, SELECT_IR_SCAN = 16'h0200, CAPTURE_IR= 16'h0400, SHIFT_IR = 16'h0800, EXIT1_IR = 16'h1000, PAUSE_IR = 16'h2000, EXIT2_IR = 16'h4000, UPDATE_IR= 16'h8000; assign flag = state[10] || state[11]; wire updateIR_s = state == UPDATE_IR; assign updateIR = sync_mode ? updateIR_s : updateIR_a; assign tap_state= state; always @(posedge tck or negedge trst_n) if ( !trst_n ) state<=TEST_LOGIC_RESET; else state<=next_s; always @(*) case(state) TEST_LOGIC_RESET: if(tms) next_s=TEST_LOGIC_RESET; else next_s=RUN_TEST_IDLE; RUN_TEST_IDLE: if( tms ) next_s=SELECT_DR_SCAN; else next_s=RUN_TEST_IDLE; SELECT_DR_SCAN: if(tms) next_s=SELECT_IR_SCAN; else next_s=CAPTURE_DR; CAPTURE_DR: if(tms) next_s=EXIT1_DR; else next_s=SHIFT_DR; SHIFT_DR: if(tms) next_s=EXIT1_DR; else next_s=SHIFT_DR; EXIT1_DR: if(tms) next_s=UPDATE_DR; else next_s=PAUSE_DR; PAUSE_DR: if(tms) next_s=EXIT2_DR; else next_s=PAUSE_DR; EXIT2_DR: if(tms) next_s=UPDATE_DR; else next_s=SHIFT_DR; UPDATE_DR: if(tms) next_s=SELECT_DR_SCAN; else next_s=RUN_TEST_IDLE; SELECT_IR_SCAN:if(tms) next_s=TEST_LOGIC_RESET; else next_s=CAPTURE_IR; CAPTURE_IR: if(tms) next_s=EXIT1_IR; else next_s=SHIFT_IR; SHIFT_IR: if(tms) next_s=EXIT1_IR; else next_s=SHIFT_IR; EXIT1_IR: if(tms) next_s=UPDATE_IR; else next_s=PAUSE_IR; PAUSE_IR: if(tms) next_s=EXIT2_IR; else next_s=PAUSE_IR; EXIT2_IR: if(tms) next_s=UPDATE_IR; else next_s=SHIFT_IR; UPDATE_IR: if(tms) next_s=SELECT_DR_SCAN; else next_s=RUN_TEST_IDLE; endcase //FSM outputs reg rst_n; //reg clockDR, updateDR, clockIR, tdo_en, rst_n, shiftDR, shiftIR; //ClockDR/ClockIR - posedge occurs at the posedge of tck //updateDR/updateIR - posedge occurs at the negedge of tck always @( tck or state )begin if ( !tck && ( state == CAPTURE_DR || state == SHIFT_DR )) clockDR = 0; else clockDR = 1; if ( !tck && ( state == UPDATE_DR )) updateDR = 1; else updateDR = 0; if ( !tck && ( state == CAPTURE_IR || state == SHIFT_IR )) clockIR = 0; else clockIR = 1; if ( !tck && ( state == UPDATE_IR )) updateIR_a = 1; else updateIR_a = 0; end always @( negedge tck ) if ( state == SHIFT_IR || state == SHIFT_DR ) tdo_en <= 1; else tdo_en <= 0; always @( negedge tck ) if ( state == TEST_LOGIC_RESET ) rst_n <= 0; else rst_n <= 1; always @(negedge tck or negedge trst_n) if ( !trst_n ) shiftDR <= 0; else if ( state == SHIFT_DR ) shiftDR <= 1; else shiftDR <= 0; always @(negedge tck or negedge trst_n) if ( !trst_n ) shiftIR <= 0; else if ( state == SHIFT_IR ) shiftIR <= 1; else shiftIR <= 0; assign reset_n = rst_n & trst_n; assign selectIR = state == SHIFT_IR; assign sync_capture_en = ~(shiftDR | (state == CAPTURE_DR) | (state == SHIFT_DR)); assign sync_update_dr = state == UPDATE_DR; always @( posedge clockDR ) scan_out_a <= shiftDR & tdi & ~(state == CAPTURE_DR); wire nxt_st_3 = (state == SELECT_DR_SCAN) & ~tms; wire nxt_st_4 = ((state == CAPTURE_DR) & ~tms) || ( state == SHIFT_DR & ~tms); reg sel; always @(posedge tck or negedge trst_n) if(!trst_n ) sel <= 0; else sel <= ~(nxt_st_3 | nxt_st_4); wire scan_out = sel ? scan_out_s : shiftDR & tdi; always @(posedge tck ) scan_out_s <= scan_out & ~(state == CAPTURE_DR); assign byp_out = sync_mode ? scan_out_s : scan_out_a; endmodule 对于上述代码,还有一种非常简洁的描述,同样采用三段式描述风格,但是最重要的组合电路部分可通过手工推导,直接将always描述的组合电路化简为最小逻辑实现代码。 module vjtag ( input clk, // Internal clock input tdo_mux,// TDO before the negative edge flop input bypass, // JTAG instruction=BYPASS input tck, // clock input input trst_n, // optional async reset active low input tms, // Test Mode Select input tdi, // Test Data In output reg tdo, // Test Data Out output reg tdo_enb,//Test Data Out tristate enable output tdi_r1, // TDI flopped on TCK. output tck_rise, // tck rate clock enable output captureDR,// JTAG state=CAPTURE_DR output shiftDR, // JTAG state=SHIFT_DR output updateDR, // JTAG state=UPDATE_DR output captureIR,// JTAG state=CAPTURE_IR output shiftIR, // JTAG state=SHIFT_IR output updateIR ); reg tck_r1,tck_r2,tck_r3; reg tdi_f_local; // local version wire tdo_enb_nxt; // D input to TDO_ENB flop wire tdo_nxt; // D input to TDO flop wire itck_rise; wire tck_fall; reg [3:0] state; // current state wire a,b,c,d,a_nxt,b_nxt,c_nxt,d_nxt; assign a = state[0]; assign b = state[1]; assign c = state[2]; assign d = state[3]; assign a_nxt=(~tms & ~c & a) |(tms & ~b)|(tms & ~a)|(tms & d & c); assign b_nxt=(~tms & b & ~a) |(~tms & ~c)|(~tms & ~d & b)|(~tms & ~d & ~a)|(tms & c & ~b)|(tms & d & c & a); assign c_nxt=(c & ~b)|(c & a)|(tms & ~b); assign d_nxt=(d & ~c)|(d & b)|(~tms & c & ~b)|(~d & c & ~b & ~a); assign tdo_enb_nxt = state == 4'b0010 | state == 4'b1010 ? 1'b1 : 1'b0; assign captureIR = state == 4'b1110 ? 1'b1 : 1'b0; assign shiftIR = state == 4'b1010 ? 1'b1 : 1'b0; assign updateIR = state == 4'b1101 ? 1'b1 : 1'b0; assign captureDR = state == 4'b0110 ? 1'b1 : 1'b0; assign shiftDR = state == 4'b0010 ? 1'b1 : 1'b0; assign updateDR = state == 4'b0101 ? 1'b1 : 1'b0; assign tdo_nxt = bypass == 1'b1 & state == 4'b0010 ? tdi_f_local : tdo_mux; assign tdi_r1 = tdi_f_local; always @(posedge clk) begin : rtck_proc tck_r3 <= tck_r2; tck_r2 <= tck_r1; //synchronizers for edge detection tck_r1 <= tck; end assign tck_rise = itck_rise; assign itck_rise = tck_r2 & ~tck_r3; assign tck_fall = ~tck_r2 & tck_r3; always @(posedge clk) if (trst_n == 1'b0) state <= 4'b1111; else if (itck_rise == 1'b1)begin state <= {d_nxt, c_nxt, b_nxt, a_nxt}; end always @(posedge clk) if (trst_n == 1'b0) tdi_f_local <= 1'b0; else if (itck_rise == 1'b1 ) begin tdi_f_local <= tdi; end always @(posedge clk) if (trst_n == 1'b0)begin tdo <= 1'b0; tdo_enb <= 1'b0; end else if (tck_fall == 1'b1 ) begin tdo <= tdo_nxt; tdo_enb <= tdo_enb_nxt; end endmodule // module vjtag JTAG接口除了标准的4信号引脚外,TI还定义了一种叫做SBW-JTAG的接口,仅用两根引脚(TCK、DIO)即可实现JTAG的功能,通常用于引脚受限的芯片上。ARM的Cortex-M系列CPU均包括SW-JTAG定义标准。