ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Tessent ICL实战:详解SIB、TDR与TAP的语法和工程应用

Tessent ICL实战:详解SIB、TDR与TAP的语法和工程应用 做DFT的同行应该都有体会IEEE 1687IJTAG推出这么多年真正落地时最绕不开的就是ICLInstrument Connectivity Language这套描述语言。Tessent作为西门子EDA家的DFT旗舰工具链在ICL支持上做得相当完整但问题也恰恰出在这——语法灵活、层次复杂新手拿到一堆.icl文件时经常一头雾水不知道SIB、TDR、TAP之间到底怎么组织更不知道每条语句背后的语义约束。这篇文章我就从实际项目角度出发把Tessent ICL里定义SIB、TDR和TAP模块的核心语法完整拆一遍。不是简单抄手册而是结合我在真实芯片项目中写ICL、调ICL、被ICL折磨过的经验把关键语法点、踩坑点、工具链配合方式都梳理清楚。无论你是刚开始接触1687还是已经在用Tessent做可测试性设计这篇文章都能给你一份能直接上手的参考。1. ICL到底在描述什么先搞懂1687网络的分层逻辑1.1 从TAP到仪器一条扫描链上的三级路由在展开语法之前必须先把ICL描述的对象模型讲清楚。IEEE 1687标准定义了一种可重构的扫描访问网络它的核心思想是不再像传统JTAG那样把所有测试数据寄存器TDR一股脑并联在TDI和TDO之间而是通过一组可配置的开关节点SIB把扫描链动态地切成一段一段需要访问哪个仪器就接通哪条支路。这套网络天然是分层的。最顶层是TAPTest Access Port控制器它负责解析指令、产生时钟和移位控制信号中间层是SIBSegment Insertion Bit相当于扫描链上的“路由开关”最底层是TDRTest Data Register也就是真正挂在仪器上的数据寄存器。ICL这门语言就是用来描述这三层结构以及它们之间连接关系的。你可以在ICL里看到频率很高的几个关键词Module、TapModule、Register、ScanMux、ScanRegister、ScanInterface。这些词对应到实际硬件上分别描述的是一个功能模块、TAP控制器模块、寄存器实例、扫描多路选择器、扫描寄存器、扫描接口信号组。提示ICL并不是一种可综合的硬件描述语言它不产生门级网表只提供“连接关系访问协议”的描述。换句话说ICL描述的是“测试网络长什么样”而不是“测试网络怎么实现”。1.2 ICL文件和Tessent工具链的配合方式在Tessent流程里ICL文件通常由两个来源产生对于标准仪器比如Tessent自家生成的BSCAN、SSN等工具会自动生成对应的ICL。对于自定义仪器比如你内部设计的一个调试寄存器就需要手写ICL或者通过Tessent的write_icl命令从网表/TCDBTest Coverage Database里抽取。ICL文件在流程中被消费的场景主要有三个Tessent Shell里的dft_inserter做扫描插入和网络优化时需要读取已有仪器的ICL来维持层次Tessent ICL Compilericlc负责语法检查和语义检查Tessent SSNStreaming Scan Network工具则会把ICL描述的网络转换成物理实现。我个人的建议是不管你是从哪个入口拿到ICL一定要先过一遍iclc做语法和语义检查再往下一级工具里送。很多隐蔽的连接错误在综合前发现不了等跑到布局布线再回头改ICL代价就大了。2. 定义TAP模块整个访问网络的根节点2.1 TapModule的语法骨架与端口声明TAP模块是ICL层次结构的根。它对应芯片上的JTAG TAP控制器ICL里用TapModule关键字来声明而不是普通的Module。这两者的区别在于TapModule内部必须有TAP状态机相关的描述而且只能出现在顶层。一段最简TAP模块的ICL长这样TapModule top_tap { // TAP接口定义 TapInterface { Tdi tdi; Tdo tdo; Tms tms; Tck tck; Trst trst; } // 指令寄存器定义 TapInstruction { Length 8; Opcode SAMPLE 8b10000000; Opcode PRELOAD 8b10000001; Opcode EXTEST 8b10000010; Opcode BYPASS 8b11111111; Opcode ACCESS_TDR1 8b10010000; } // 顶层TDR定义 Register TDR_TDR1; RegisterData TDR_TDR1_data; TapRegister TDR_TDR1_data; TapRegister BypassRegister bypass_reg; // 扫描网络连接 ScanNetwork { Tdi - BypassRegister - Tdo; Tdi - TDR_TDR1 - Tdo; } }看这段代码TapInterface声明了标准的五线JTAG接口TRST可选。TapInstruction定义了指令集每条Opcode就是一条JTAG指令用于选中对应的数据寄存器。TapRegister关键字说明这个寄存器直接挂在TAP的DR扫描链上是可以被指令直接选中的。2.2 指令译码与数据寄存器选择的底层逻辑理解TAP模块的关键在于搞清楚TapInstruction和TapRegister之间的对应关系。在真实硬件里TAP控制器有一个指令寄存器IR当外部通过TMS状态机把一条指令移入IR后TAP控制逻辑会把这条指令译码成一条“选择信号”这个信号决定TDI到TDO之间走哪条DR路径。ICL里并没有一个显式的Select语句把指令和寄存器绑在一起而是在ScanNetwork里通过连接关系来表达。Tessent工具会自动分析Opcode和TapRegister的映射关系然后生成对应的译码逻辑。注意TapRegister和RegisterData必须配对出现。Register定义的是寄存器的抽象实例RegisterData描述的是它的数据字段。如果你漏了RegisterDataiclc会直接报错而且错误信息往往不太直观。实际操作中我遇到过一种很容易犯的错在TapInstruction里定义了指令也在TapRegister里挂了寄存器但ScanNetwork里忘了把TDI到TDO的路径写全。这种情况下iclc不一定报错但生成的网表里这条指令对应的DR路径就是空的Tessent在后续dft_inserter阶段才会报出“no scan path for instruction”的诡异错误。所以定义TAP模块时ScanNetwork里的路径必须和指令集一一核对。2.3 TapModule中ScanNetwork的路径组织细则ScanNetwork是ICL里最容易写错的地方它描述的是TDI到TDO之间所有可能的数据通路。在一个TAP模块内部通常有三条必有的路径旁路路径Tdi - BypassRegister - Tdo指令寄存器路径Tdi - InstructionRegister - Tdo这条路径由TapModule隐式定义不需要显式写出数据寄存器路径每条指令对应一条DR路径如果TAP下挂了多个TDR你可能会想当然地这样写ScanNetwork { Tdi - TDR1 - Tdo; Tdi - TDR2 - Tdo; Tdi - TDR3 - Tdo; }这种写法语法上没错但语义上是有问题的。它表达了三条路径同时存在却没有说明什么时候选哪条。正确的做法是每个TDR路径前面必须有关联的TapInstruction来控制或者让Tessent根据TapRegister的连线自动推断。我个人的经验是在ScanNetwork里把路径写得尽量“笨”一点路径越直接越好不要在网络里嵌套多级ScanMux去搞复用逻辑。TAP层级的ScanMux一旦多了工具在生成译码逻辑时容易产生额外的面积开销而且后仿真时时序收敛也更麻烦。TAP层就老老实实做“指令选通DR”这件事层次化的复用交给下层的SIB去解决。3. 定义TDR仪器访问的数据入口3.1 Register与RegisterData的配对关系TDRTest Data Register在ICL体系里是最基础的存储单元描述。从硬件角度看它就是一个移位寄存器在Shift-DR状态下把数据从TDI移入在Update-DR状态下把数据锁存到并行输出。ICL定义一个TDR的典型结构是Module tdr_debug_reg { // 端口定义 Input data_out[32]; Input shift_enable; Input capture_enable; Input update_enable; Output data_in[32]; // 寄存器实例 Register debug_reg; RegisterData debug_reg_data { Field data_field[32]; Clock clock tck; CaptureClock capture capture_enable; UpdateClock update update_enable; ShiftEnable shift shift_enable; } }注意这里的关键点Register本身不包含任何数据格式的信息它只是声明“存在一个叫做debug_reg的寄存器”。真正描述寄存器内部结构的是RegisterData里面的Field定义了位宽和字段划分Clock、CaptureClock、UpdateClock、ShiftEnable则定义了寄存器的时序行为。3.2 Field定义与位域映射的常见玩法Field是TDR里最有文章可做的地方。一个Field可以映射到模块的某个输出端口也可以只是寄存器内部的一个存储位。当Field需要驱动芯片内部信号时必须在Field级别做端口连接。回到上面的例子Field data_field[32]通过Output data_in[32]把寄存器值输出到外部。如果你想实现“寄存器某一位控制芯片的复位信号”就应该这样写Field reset_control[1]; Output reset_n; // 通过Field和端口的映射关系来关联 Field reset_control data_field[0];在ICL语法中Field的端口映射有一种简写方式直接在Field声明时指定连接的端口Field data_field[32] - data_in;这种写法把“Field定义”和“端口连接”合并在一起代码更紧凑。但对新手来说这种简写容易让人误以为端口是Field的一部分从而在模块端口列表里漏掉Output data_in[32]的声明。工具报错的时候指向的问题行在Field上很多人会在这里卡很久。3.3 多TDR的编址与选择机制当芯片上TDR数量多起来之后就需要考虑寻址问题了。传统JTAG的TDR寻址完全靠指令一条指令对应一个TDR。如果TDR数量多指令位宽就要拉长指令译码逻辑也会膨胀。这时候可以借用层次化SIB来缓解指令压力——这也是IEEE 1687相较传统JTAG的核心优势之一。在ICL中你可以这样做TAP层只保留少数几条“访问某一组SIB”的指令真正的TDR选择交给SIB的移位配置来完成。举个例子TapModule top_tap { TapInstruction { Length 8; Opcode ACCESS_SIB_CHAIN 8b10000000; } TapRegister SIB_chain_reg; // 将SIB链寄存器挂到TAP的DR路径上 ScanNetwork { Tdi - SIB_chain_reg - Tdo; } } Module sib_group { // 内部定义多个SIB SIB sib_1; SIB sib_2; // 每个SIB后面挂一个TDR Register tdr_1; Register tdr_2; }这种设计的直接好处是指令集长度不会随着仪器数量增长而膨胀新增仪器只需要在SIB链上多加一位控制位就行。实际项目中这种模式被广泛应用于多核芯片的调试网络——每个CPU核心挂一组SIBTDR核心内部再细分层次。4. 定义SIB层次化扫描网络的路由开关4.1 SIB的语法本质与硬件行为SIB在物理上是一个非常简单的器件一个移位寄存器位加上两个二选一MUX。但当它被层次化组织起来后就能构成一颗复杂的扫描路由树。ICL中SIB的语法骨架Module sib_example { Input sel_in; // 来自上层SIB链或TAP的移位输入 Output sel_out; // 继续传递到下一位的移位输出 Input tdi; // 数据输入 Output tdo; // 数据输出 Input tck; Input shift_en; Input capture_en; Input update_en; ScanRegister sib_ctl; RegisterData sib_ctl_data { Field ctl_bit[1]; Clock clock tck; CaptureClock capture capture_en; UpdateClock update update_en; ShiftEnable shift shift_en; } ScanMux { // 旁路链路SIB关闭时数据从TDI直接到TDO Bypass tdi - tdo; // 接入链路SIB打开时数据经过内部段Segment Segment tdi - internal_tdi - tdo; } // 控制位为1时选择Segment路径 ScanMux select ctl_bit; }这里的ScanRegister表示在一个扫描移位寄存器链上SIB的控制位本身也是链上的一环。ScanMux描述了两条路径旁路路径SIB关闭和段路径SIB打开。4.2 Segment端口与内部网络的衔接方式SIB最核心的连接点是Segment端口。所谓Segment端口就是SIB内部接入的那条子扫描链的入口和出口。在ICL中这个端口通常被命名为Segment它是一个端口组内部包含tdi和tdo两个方向相反的单向信号。继续上面的例子internal_tdi和internal_tdo就是Segment接口的内部信号。当ctl_bit为1时SIB将TDI链路切换到Segment侧这样测试数据就可以流入挂在Segment后面的子网络。当ctl_bit为0时SIB旁路掉Segment侧的所有仪器数据直接流向下一级。这里有一个非常关键的设计约束SIB的Segment侧连接的网络必须是一个完整的、闭合的扫描链。也就是说从Segment.tdi进去的数据最终必须能从Segment.tdo出来。如果中间某个模块的端口连接断掉了或者某个仪器的扫描链没有闭合iclc会报出“open scan path”的错误。提示排查这种“open scan path”错误时别急着看SIB本身。先顺着Segment往下一层一层追检查每个TDR的TDI/TDO是否首尾相接最后再回到SIB的出口。我至少有三次遇到这个报错最后定位到的都是底下某个TDR的ScanInterface端口方向写反了。4.3 多级SIB级联的层次结构设计SIB的价值在级联时才真正体现。多级SIB级联的ICL描述方式本质上是通过“在SIB的Segment后面再接SIB”来实现的。每一级SIB负责一段扫描链的通断从而把整个访问网络组织成一棵树。下面是一个二级SIB级联的例子Module top_network { Input tdi; Output tdo; Input tck; Input shift_en; Input capture_en; Input update_en; SIB sib_top; RegisterData sib_top_ctl { Field ctl_bit[1]; Clock clock tck; } Module inner_group { Input tdi; Output tdo; SIB sib_inner; // 内层SIB控制字 RegisterData sib_inner_ctl { Field ctl_bit[1]; Clock clock tck; } // 内层SIB后挂TDR Register inner_tdr; } // 顶层SIB的Segment连接到内层子网络 ScanMux { Bypass tdi - tdo; Segment tdi - inner_group.tdi - inner_group.tdo - tdo; } }这种结构的访问流程是先通过一条长的移位操作配置顶层SIB的控制位将链路切换到inner_group然后再次执行移位操作数据就会穿过顶层SIB进入内层继续配置内层SIB以此类推逐层展开最终访问到目标TDR。配置SIB链的过程在IEEE 1687标准中有一个专门术语叫“Active Scan Chain”。每次切换SIB状态后当前链路会发生变化工具必须感知到这个变化并重新生成访问路径。Tessent的dft_inserter能够处理这种动态重配置网络但在定义网络层次时还是要注意深度和宽度的平衡——过于深层的SIB链会显著增加测试时间每层都要单独配置一次而过于宽层的SIB链则会增加扫描移位长度。我在一个项目里做过一次对比把8个TDR全部并联在TAP层传统方式和用两级SIB树每级4个分支组织。前者指令位宽需要4位后者只需要2位指令加2位SIB配置位整体测试时间节省了接近30%。这个数字在Tessent的时序报告里看得清清楚楚。5. 从ICL到Tessent流程语法外的实战衔接5.1 iclc编译检查与常见错误解读不管你写ICL写得多么顺手第一步永远是过编译检查。Tessent带了一个专门的ICL编译器命令行工具叫iclc用法很简单iclc -f top.icl -f sub_modules.icl -o output_dir-f参数可以多次使用把多个ICL文件一起喂进去。工具会做完整的语法和语义检查并生成编译后的中间表示。如果编译通过输出目录里会有一些.icldb之类的文件这些是给后续Tessent工具用的库文件。实际使用中iclc报错信息大致分三类语法错误关键字拼错、大括号不配对、分号缺失。这种最好修看错误行号就行。语义错误比如引用的模块没有定义、端口方向不匹配、寄存器实例没有对应的RegisterData。这种大部分是因为模块间的端口连接没对齐。警告不阻断编译比如某些信号悬空、某些寄存器没有被任何扫描路径引用。这类警告建议全部消除因为它们往往是时序约束或者覆盖率收集阶段的隐藏雷。我特别想强调一点iclc对大小写是敏感的。ICL语法里Input和input的容忍度要看工具版本但Register、Module这类关键字一定严格区分。有些从老版本工程拷过来的ICL在Tessent 2021以后版本编译时频繁报错多数原因是新版本对关键字大小写做了收紧。5.2 TDR访问时序与Tessent Pattern的对应关系ICL里对TDR时序的描述会直接影响到Tessent生成测试向量Pattern的方式。CaptureClock和UpdateClock这两个属性决定了Tessent在产生capture和update脉冲时应该参考哪个信号。有一种常见配置值得注意如果你的TDR使用的是独立时钟域非TCK域ICL里必须通过Clock属性显式声明否则Tessent默认所有TDR都和TCK同步。跨时钟域的TDR在生成Pattern时Tessent会插入一些同步逻辑保证捕获数据的稳定性。另外ShiftEnable属性的写法也会影响Pattern的移位时序。如果你定义的TDR在外部用了一个高电平有效的移位使能信号而ICL里写成了低电平有效Tessent生成的Pattern在仿真时就会表现为“移不进去数据”。这种问题用逻辑分析仪查波形时非常隐蔽因为信号本身都在动只是采样沿和使能沿错开了。5.3 SIB配置流程在仿真中的验证方法写完ICL、做完编译检查别急着往后续流程推。强烈建议先用Tessent的仿真环境把SIB的配置流程跑一遍确认网络切换行为符合预期。具体做法是在Tessent Shell里加载ICL编译产物然后手动驱动一组移位向量先配置顶层SIB再配置内层SIB最后访问目标TDR。观察TDO输出是否符合预期。这种仿真不需要完整的网表ICL本身的描述就足够支撑行为级仿真了。Tessent里可以用类似下面的命令做行为级验证read_icl compiled_network.icldb open_network top_network set_sib sib_top 1 update shift_dr -length 32 -tdi 0xA5A5A5A5 close_network命令执行后Tessent会返回TDO数据你可以通过比对期望值来判断网络行为是否正确。我在实际项目中严格遵循“仿真通过再进网表”的流程这让我至少避开了三次因为ICL连接错误导致的ECO工程变更单。6. 常见问题与排查技巧实录6.1 端口方向不匹配导致iclc编译失败的典型场景iclc最常报的错就是端口方向不匹配。ICL对方向的约束很强两个模块连接时驱动端必须是Output接收端必须是Input。在实际项目中最容易出问题的地方是SIB的Segment端口连接。比如你有一个SIB它的Segment.tdi是Output方向数据流出SIB到子网络而内层子网络的端口你声明成了Output本意是接收数据但从SIB视角看方向不对两者连在一起就会报错。排查方法其实很简单画一张接口方向表把每个模块的输入输出列出来再对照SIB的Segment方向逐项核查。6.2 多TDR共享数据端口时的ICL描述冲突有时候多个TDR会共享同一个数据总线比如两个调试寄存器都挂在同一组32位调试总线上。这种情况下ICL描述里如果直接把两个TDR的Output都连到同一组信号上就会产生多驱动冲突。解决办法是在两个TDR之间增加一个ScanMux做输出选择或者让其中一个TDR的Output保持高阻态。但从ICL语义角度讲标准做法还是用ScanMux做显式选择。在实际项目中我更推荐另一种方案把共享总线的TDR合并成一个大的TDR通过Field来划分不同功能区域。合并后既避免了多驱动冲突又能减少扫描链的切换次数——代价是访问某个子功能时必须整体移位一个大寄存器但如果这个总线不是被频繁访问的代价完全可以接受。6.3 与Tessent SSN集成时的层次命名注意事项最后提一下与Tessent SSN集成的情况。SSNStreaming Scan Network是Tessent用来处理大规模层次化扫描网络的引擎它要求ICL中的层次命名具有全局一致性。换句话说同一个模块在同一份ICL描述里只能有一个名字不能出现“顶层叫A模块底层引用时叫B模块”的情况。在实际操作中我发现一个高发错误在层级化ICL文件中两个不同文件里定义了相同名字的Module但端口不同。iclc编译时会报“module redefinition”错误。修这个错很简单——改名就行但改名后要确保所有引用同步更新否则就是“undefined module”的报错。SSN集成时的另一个建议是所有SIB的控制位尽量放在连续的Field里这样SSN在合成一条长的配置链时能够更高效地合并移位顺序减少测试时间。我在项目中对比过分散定义SIB控制位和连续定义SIB控制位两种情况前者在SSN生成的Pattern长度上要比后者多出大约15%。6.4 波形时序不对先查ScanInterface别急着改ICL有一次我在做后仿真时发现某个TDR的捕获数据总是全零波形看起来一切正常——时钟有了、移位使能有了、TDI数据也有了但就是抓不到值。排查了很久最后定位到问题根本不在ICL而在ScanInterface端口组定义。ICL里对TDR的CaptureClock指派了一个内部信号但这个内部信号在网表综合阶段被优化掉了实际连到TDR上的捕获时钟根本没有翻转。这个经历让我养成了一个习惯ICL里的ScanInterface端口组尽量用模块顶层可见的端口信号进行描述不要用内部中间信号。如果必须用内部信号一定要在综合时加dont_touch约束防止它被工具优化掉。7. 关于工具版本与ICL兼容性的经验补充Tessent工具版本升级对ICL的影响比很多人想象的大。尤其在2020到2023这几年Tessent对IEEE 1687标准的支持从“部分实现”逐步走向“完整支持”一些老版本里的宽松写法在新版本里开始被严格限制。最典型的变化有几点早期版本允许Module里直接写RegisterData而不声明Register新版本要求两者必须成对出现。ScanMux的选择信号早期允许使用任意表达式新版本要求必须是Field或RegisterData中的位。TapInstruction的Opcode长度必须与Length一致早期版本允许缺失前导零新版本严格要求完全对齐。如果你手上的工程是从旧版本Tessent迁移过来的我建议先跑一遍iclc然后把所有警告逐条过一遍不要跳过。旧版本里积累的ICL代码质量参差不齐很多“能编译但语义不严谨”的写法在新版本里都会被揪出来。这个时候系统性修正比等到流程中段再排错要划算得多。兼容性方面还有一个实用技巧使用iclc的-compat选项可以指定兼容某个特定旧版本的语法规则。但这个选项只建议用来过渡不建议长期依赖毕竟工具升级后新特性新优化都不会在兼容模式下生效。我在一个长期维护的项目里专门维护了一份“ICL编写规范”里面记录了当前工具版本下允许的写法、禁止的写法以及容易引起歧义的结构模式。每次工具版本升级第一件事就是更新这份规范然后全量编译一遍ICL库。这份文档看起来不起眼但确确实实替我省了无数排错时间。回到SIB、TDR、TAP三者的定义上我的最终建议是TAP层做薄一点只做指令译码和顶层DR选择不要把业务逻辑堆在TAP层。SIB层级做平衡深度不要超过五级宽度优先这样可以兼顾测试时间和布线面积。TDR的Field定义尽量和芯片内部寄存器位域对齐减少仿真和验证时的认知负担。每写完一个ICL模块立刻跑iclc检查发现警告当场处理不要攒到最后一口气修。如果你按这个思路走一遍流程再回头看那些堆成山的.icl文件你会发现架构其实非常清晰——TAP是入口SIB是路由TDR是终点ICL只是在把这个路由关系白纸黑字写下来而已。弄清楚这一点语法就只是表达工具而不再是你和DTF工具之间的拦路虎。
返回列表