在本系列的第一部分中,我们研究了通用的单片机/FPGA/ASIC接口标准JTAG。但是,尽管我们讨论了很多关于指令和寄存器的内容,我们仍然需要了解如何操作JTAG测试访问端口(TAP)。
正如在前一篇文章中提到的,TAP是通过状态机来控制的,它有两条路径,这取决于我们是在加载指令,还是在读/写数据寄存器。在这一部分中,我们将详细研究状态机,甚至还将看到一个简单JTAG接口的一些伪代码。
TAP状态机
如下面的图1所示,IEEE 1149.1-2013标准中显示了状态机。
状态机很简单,包括两条路径:
- 数据寄存器(DR)路径(以绿色显示),用于加载指令
- 指令寄存器(IR)路径(以蓝色表示),用于从数据寄存器读写数据,包括边界扫描寄存器(BSR)
图1所示。TAP状态机,详见IEEE 1149.1-2013标准。点击在这里为了一个更大的版本。
状态机在测试时钟(TCK)边缘上进行,测试模式选择(TMS)引脚的值控制行为。
假设状态机从Test-Logic-Reset开始,我们首先对一个TMS = 0进行计时以进入Run-Test/Idle状态,然后对一个TMS = 1进行计时以开始选择路径。
图2简要总结了不同状态的角色。
图2。带有状态描述的TAP状态机。点击在这里为了一个更大的版本。
为了帮助理解这些状态,请再次查看前一篇文章(图3)中的JTAG系统。
图3。JTAG架构
TAP控制器管理状态机,根据选择的状态,输出MUX被切换。
这两条路径分别是:
- 的指令capture-shift路径
- 的数据capture-shift路径
请注意边界扫描寄存器(包含IO引脚周围的边界扫描单元)是如何成为数据寄存器之一的。数据寄存器是移位寄存器,可以是任意长度的。
捕获、更新和移位状态
最“活跃”的状态是捕获,转变,更新州。
捕获状态可能是最神秘的,与指令路径相比,数据路径执行不同的操作。在这里,捕获指将数据并行装入移位寄存器,而不是将数据串行地装入寄存器。移位意味着将数据移到移位寄存器中。然后,更新阶段锁存寄存器,状态机可以复位。
具体来说,Capture-DR是一种状态,在这种状态下,如果需要,可以将测试数据并行加载到当前数据寄存器的shift-capture路径中。(当前数据寄存器由之前设置的当前指令设置。)这意味着数据被并行加载到当前指令选择的数据寄存器中,而不是移位。
Capture-IR用于JTAG系统中的故障隔离,尽管该标准对其用途并不明确。一个固定的逻辑值(必须以{…01}结尾)被并行加载到指令寄存器的移位捕获路径中。这就是说,指令寄存器是用固定的逻辑值并行加载(而不是移位)的。
Shift-DR和Shift-IR状态是将数据串行加载到数据寄存器或指令寄存器的主要状态。当状态机处于这些状态之一时,TMS保持LOW状态,直到转移操作完成。Update-DR和Update-IR状态将数据锁存到寄存器中,将指令寄存器中的数据设置为当前指令(这样做时,将为下一个周期设置当前数据寄存器)。
操作TAP状态机的示例通常以时序图的形式给出,但此类图传递信息的能力有限,因此感兴趣的读者可以参考JTAG标准本身以获得更多信息,包括各种逻辑块的实现建议。
JTAG接口的伪代码
为了充实上述思想,在本节中,我们将组装一些可能控制JTAG接口(可以像微控制器开发板一样简单)的伪代码。代码实现了最基本的功能,没有任何错误检查或指令的特殊处理。包括一些延迟来管理时间,包括一个短延迟来适应不能保证时间的多任务系统。
/ /定义针
JTAG_TMS = PA01
JTAG_TCK = PA02
JTAG_TDI = PA03
JTAG_TDO = PA04
//创建一个由5个1组成的字符串,用于强制重置
tms_reset_str = {1, 1, 1, 1, 1}
/ / JTAG功能
//发送一个常量字符串到TAP,不设置TDI或TDO
transmit_tms_str (tms_str)
{
For I = 0: len(tms_str)
{
set_pin (JTAG_TMS tms_str[我])
jtag_short_delay ()
set_pin (JTAG_TCK、高)
jtag_clock_delay ()
set_pin (JTAG_TCK,低)
jtag_clock_delay ()
}
}
shift_tdi_str (tdi_str)
{
set_pin(JTAG_TMS, LOW) //保持TMS LOW,平移
For I = 0: len(tdi_str)
{
set_pin (JTAG_TDI tdi_str[我])
jtag_short_delay ()
set_pin (JTAG_TCK、高)
jtag_clock_delay ()
set_pin (JTAG_TCK,低)
jtag_clock_delay ()
}
}
shift_tdo_str(长度)
{
//这个函数返回从TDO移出的字符串
set_pin(JTAG_TMS, LOW) //移动时保持TMS LOW
Output_str = {}
对于I = 0: length
{
set_pin (JTAG_TCK、高)
jtag_short_delay ()
output_str + = read_pin (JTAG_TDO)
jtag_clock_delay ()
set_pin (JTAG_TCK,低)
jtag_clock_delay ()
}
返回output_str
}
reset_jtag ()
{
transmit_tms_str (tms_reset_str)
}
load_jtag_instruction (instr)
{
//假设我们在运行测试/空闲
//注意:没有错误检查,早期退出,或暂停
/ /在这里实现
transmit_tms_str({1,1,0,0}) //使我们处于Shift-IR状态
shift_tdi_str(instr) //转移指令数据
transmit_tms_str({1,0,1,1,0}) //返回Run-Test/Idle状态
}
read_jtag_register (reg_length)
{
//这个函数读取当前数据寄存器(由大多数设置)
/ /最近的指令)
//假设我们在运行测试/空闲
//注意:没有错误检查,早期退出,或暂停
/ /在这里实现
transmit_tms_str({1,0,0}) //使我们处于Shift-DR状态
reg_str = shift_tdo_str(reg_length) //移出寄存器数据
transmit_tms_str({1,0,1,1,0}) //返回Run-Test/Idle状态
返回reg_str
}
如果浏览Black Magic Probe源代码,您可以看到JTAG接口编程的一个实际示例,可以在Github上找到。(特别是src/platforms/目录和src/include/目录)。
结论
我们现在已经看到了JTAG TAP最重要的部分,即它的状态机。在IEEE 1149.1-2013标准中可以找到本系列前两部分所涉及的材料,以及许多有用的实现提示和细节。
从这里开始,我们将变得更加实际,看看各种可用的JTAG接口,讨论常用的pinouts和连接器,最后仔细看看Arm调试接口(ADI),作为实践中的JTAG示例。