尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Tessent Shell设计内省与编辑:DFT网表操作实战指南

Tessent Shell设计内省与编辑:DFT网表操作实战指南 Tessent Shell用户手册翻到第三章的时候我停下来多看了几眼。前面两章基本是环境、启动、基础命令这些热身内容到了Design Introspection and Editing这里才真正开始碰核心的东西。这章讲的是两件大事怎么把一个设计对象里的信息看透以及怎么在工具内部直接改设计。说大白话就是Tessent Shell不只是一个跑命令的壳它更像是一个DFT流程里的“总控台”比netlist编辑器灵活又比文本脚本直观搞定它的内省和编辑能力整个Tessent流程才算真正掌握。这篇笔记我会从设计数据模型讲起把命名、查询、属性访问、连接修改这些内容拆开揉碎再补一些在真实项目里才会遇到的坑。不管你是刚接触Tessent Shell的新手还是已经跑过几条scan chain的老手这篇内容都能帮你在翻手册的时候少走点弯路。1. 花一整章讲看和改Tessent Shell的底气在哪1.1 它不是Linux shell的兄弟而是DFT工作台很多人第一次听到Tessent Shell这个名字会下意识拿它和bash、csh做类比。确实Tessent Shell底层是基于Tcl的交互式命令行环境支持变量、循环、条件判断、proc定义看起来像个脚本壳。但它的核心不是通用计算而是面向Tessent工具链的统一设计操作平台。在DFT流程里你经常要做的事情是读入综合后的门级网表确认顶层端口和时钟手动插一条scan chain检查某个EDT通道的连接关系或者把某个测试点信号从A点挪到B点。这些操作如果用文本编辑器改网表既容易出错又不可追溯如果用专门的结构化设计工具代价又太大。Tessent Shell正好填了这个空档——它在工具内部直接建立设计与命令的映射关系让工程师可以用Tcl语句操作设计对象。Tessent Shell的操作风格沿袭了门级EDA工具的一贯思路先读设计再把设计拆解成可寻址的对象集合通过命令对对象做查询、遍历和修改。第三章之所以把Design Introspection和Editing放在一起是因为这两个能力在实操中本来就是咬合在一起的——你得先看清楚网表长什么样才知道要改哪里改完了之后又得靠introspection去验证改的结果是否符合预期。1.2 Introspection与Editing为什么必须放在同一章我最初翻手册的时候也想过为什么不干脆把设计查询和设计修改分成两个独立章节多跑两个项目之后我才意识到这两者在Tessent Shell里的边界几乎是模糊的。很多查询命令本身就带有状态改变的副作用比如某些get_*命令会触发内部缓存更新反过来几乎所有编辑命令在执行完后都会自动做一次一致性检查并报告内部数据库状态。这种看和改深度耦合理念本质上是为DFT特有的增量修改场景设计的。在功能仿真里你可以把一个完整RTL代码从头跑一遍但DFT流程往往是在一个已经基本冻结的门级网上做局部改动scan chain重建、observability cell插入、时钟mux替换每一样都是精细手术。Tessent Shell通过统一的introspection和editing机制保证了这类改动在同一个建模空间内完成而不是外部脚本改文本网表这种粗放方式。这一点是读第三章之前必须先建立的观念Tessent Shell里的设计不是一个个孤立的状态列表而是一个带类型定义、属性绑定、连接关系索引的数据库。Introspection是对这个数据库的读取操作Editing是对这个数据库的事务性写入操作。两者配合才能在不离开工具的前提下完成验证闭环。2. 先把家底摸清楚设计对象模型与命名规范2.1 设计读入之后工具眼里的网表是什么手册第三章开头部分会介绍Tessent Shell中设计数据的组织方式。如果你在模拟器或者综合工具里接触过网表理解起来会很快设计里大致有cell器件单元、port顶层端口、pin端口引脚、net连线这几类基础对象。值得注意的是Tessent Shell还会额外维护一些测试领域特有的对象类型比如scan_cell、scan_group、edt_channel、test_point等。这些对象不是网表里原生存在的而是经过Tessent工具识别或插入后产生的逻辑抽象。比如一条scan chain在网表层面看来只是若干个DFF首尾相连、加上对应的scan_en/scan_in/scan_out信号。但在Tessent Shell的对象模型里它会抽象成一个scan_group或scan_path对象你可以直接对这个对象做整体报告、属性修改、甚至重排顺序不需要一个个去摸cell。这种高层抽象是Tessent Shell比直接操作门级网表高效的关键也是Introspection能力的主要价值所在。要看到设计里都有哪些对象最基本的命令是set all_cells [get_cells -hier -filter is_scan_cell true] puts Total scan cells: [sizeof_collection $all_cells]这里-hier表示遍历层次-filter按照对象的属性做筛选。执行完之后all_cells是一个collection对象不是普通的Tcl list所以要用sizeof_collection来取数量。这是一个新手几乎必踩的坑后面我会专门讲。2.2 层次化路径与命名解析的底层规则看门级网表最头疼的就是层次化路径。Tessent Shell延续了EDA工具的经典命名风格用/作为层间分隔符。比如一个top/block1/u_fft/fft_core/gen_fft[0].u_fft_reg这样的名字代表从顶层一路trace到某个寄存器。在Tessent Shell内部对象的名字本质上是一串路径信息但它在显示和匹配的时候有自己的一套规则。第一个规则是相对路径与绝对路径。如果你在current_instance是顶层时用get_cells u_fft_reg它只会找当前层次下的直接子单元如果要用绝对路径必须在前面加/例如get_cells /top/block1/u_fft/fft_core/gen_fft[0]/u_fft_reg。注意Tessent Shell对/的处理很严格——多一个少一个都可能导致匹配失败。第二个规则是名字里的特殊字符。门级网表里经常会出现[0]、\这类字符。如果你直接用get_cells gen_fft[0].u_fft_reg工具可能会把[0]解释成数组下标或者通配符语法。规避的办法有两个要么给名字加花括号做精确匹配要么用-regexp时做合理的转义。我个人习惯是遇到总线单元名字时优先用get_cells -filter full_name ~ *gen_fft*这种模糊匹配避免跟特殊字符较劲。第三个规则是集合的上下文绑定。Tessent Shell里很多命令的操作对象集合是有当前范围概念的比如all_inputs、all_outputs、all_registers这类内置集合会随current_instance变化而不同。做层次化分析时务必确认当前的instance指向不然你查到的端口列表可能根本不是你脑子里想的那个模块。current_instance /top/block1 set pins_in_block1 [get_pins -hier -filter direction in] current_instance / ;# 用完之后记得切回来这个习惯不养成脚本跑到一半报出一堆莫名其妙的空集合排除问题会花掉大量时间。3. Design Introspection命令体系从查名字到查约束3.1 get_* 系列命令与过滤条件Tessent Shell的Introspection命令按对象类型可以列出一长串get_cells、get_pins、get_ports、get_nets、get_clocks、get_properties等等。光记住命令不够还得掌握它们的统一选项风格。几乎所有get_*命令都支持以下常用选项选项作用适用场景-hier递归遍历所有子层次全设计范围查找对象-filter按属性条件过滤精准定位特定属性对象-regexp使用正则表达式匹配名字对复杂名字做模糊查询-quiet匹配不到时报错但不中断独立脚本容错-of_objects从某个已有集合反查关联对象依据pin找net等反向追踪举个综合一点的例子我想查一下设计中所有带scan_en功能的时钟引脚并且只关心在测试模式下被启用的。set scan_en_pins [get_pins -hier -filter full_name ~ *scan_en*] set test_mode_pins [get_pins -hier -filter name test_mode] puts Scan enable pins: [sizeof_collection $scan_en_pins] puts Test mode pins: [sizeof_collection $test_mode_pins]-filter支持的操作符类似Tcl表达式的扩展常见的、!、~、、都能用属性名和值之间用空格隔开。有一点要特别注意属性名到底是name还是full_name在不同对象类型上结果完全不一样。name通常只取叶子节点名字full_name则包含完整层次路径。你要是用name去匹配一个路径字段大概率什么都查不到。3.2 属性读取与Tcl变量的联动Introspection不仅仅是查对象名单更重要的是查对象绑定的属性。Tessent Shell里几乎所有对象都带有一组属性比如cell有ref_name、is_scan_cell、is_black_boxpin有direction、is_clock、is_scan_enablenet有is_scan_net、is_clock_net等等。读取属性用得最多的命令是get_propertyset test_cells [get_cells -hier -filter is_scan_cell true] foreach cell $test_cells { set library_cell [get_property $cell ref_name] if {[string match *EDT* $library_cell]} { puts EDT-related scan cell: $cell ($library_cell) } }注意get_property返回的结果可能是字符串、整型、布尔值也可能是一个collection。比如查某个pin连接的netget_property $pin net返回的经常是一个net对象集合需要再用get_property去取这个net的full_name。如果直接用puts打印你只会看到像net_42这种内部ID而不是可读的网表名字。这时候要手动追一层set my_net [get_property $my_pin net] set net_name [get_property $my_net full_name] puts Pin $my_pin is driven by net: $net_name这个属性套属性的写法是第三章里非常高频的玩法本质上对应着对象数据库里的关系索引——pin与net的关系、cell与pin的隶属关系、instance与hierarchy的包含关系都是靠层层取属性实现的。3.3 设计健康度检查与报告生成Introspection不光是给脚本服务也承担设计质量检查的功能。Tessent Shell里很多命令本身就对设计做了静态检查比如读取时钟树时会检查时钟定义是否完整读取scan chain时会把链断掉的地方打出来。这些检查结果通常会在命令执行时直接输出到终端或log文件里。想要系统性地报告设计状态可以用report_*系列命令。例如report_clocks -verbose report_scan_cells report_scan_groups -verbose report_design_rules这类命令在第三章节里可能不会花太多篇幅但你真正做项目时离不开它们。我的经验是每次跑完一轮大的编辑操作之后至少做一次report_design_rules它会发现断链、悬空引脚、多个驱动源等基本问题。DFT流程和功能流程不一样功能综合后的网表只要是闭合的一般不会有大问题但DFT编辑是推荐扫描链重连、插入观察点这类动作任何一个环节失败都会直接打断后续的ATPG准备。这里还要提一句report_*输出到屏幕上内容很多跑脚本时最好用Tcl的redirect命令把输出保存到文件方便后续比对redirect -file ./report_before_edit.txt { report_scan_groups -verbose report_design_rules }不要直接让几万行report刷在你的终端里滚动窗口既刷屏又难回溯。规范化保存log是所有可回归脚本的第一步。4. Design Editing上手改连接、改约束、改属性的正确姿势4.1 为什么DFT流程需要改设计有人会觉得综合工具都帮我把scan chain stitch好了还需要手工改吗真做过几个项目你会发现需要手工干预的地方非常多。典型的场景包括apb/axi测试接口复用时需要把测试相关的控制信号从原来的功能通路切到Tessent定义的测试通路上。时钟不可控问题某个子模块的时钟在测试模式下仍由PLL输出但DFT约束要求测试时钟必须完全可控这时候需要在测试模式下用mux把时钟源切换掉。异步复位信号的测试隔离有些异步复位网络在上电时会毛刺测试时要切断它改接到固定的test_mode ? 1b1 : 功能值上。扫描链顺序优化为了布局布线拥塞经常要调整链上cell的排列顺序。在网表层面改连接比重新综合再插入扫描便宜得多。这些操作如果做在纯文本网表上改起来又慢又容易漏。Tessent Shell的Editing能力让你在对设计做了充分Introspection之后直接对具体对象发出修改指令。4.2 连接修改从点操作到网操作连接修改是Editing最核心的内容。手册里常见的命令是change_connection、disconnect_net、connect_net、create_net、remove_net这类。它们做的事很简单把某个pin从旧net上摘下来然后挂到另一个net上。以最常见的需求为例——把一个叫func_scan_en的信号改接到一个测试模式的mux输出上current_instance /top set mux_out_pin [get_pins example_mux/Y] set target_pin [get_pins dft_top_func_scan_en_reg/D] disconnect_net -pin $target_pin connect_net -pin $mux_out_pin -pin $target_pin这里有三点值得注意。第一disconnect_net执行之后目标pin会变成floating状态如果在这中间工具报warning不要慌这是正常的中间态。关键是尽快完成下一句connect_net不要让设计长时间处于不完整状态。第二connect_net的语义在不同Tessent版本上有细微差异。有的版本要求你直接指定net对象有的版本允许传一个pin对象让它自动创建或复用net。用pin对象的写法在多数版本都能工作也最直观。第三绑定的方向性。如果你真的是在做网表编辑而不是纯粹改扫描链顺序务必确认source pin和sink pin的方向。把一个驱动pin接到另一个驱动pin上工具会报multiple driver错误把sink pin接到sink pin则会得到floating源。这类问题靠report_design_rules能抓到但在编辑的时候养成先查方向再连接的习惯能省很多事。方向检查的小技巧set pin_dir [get_property $target_pin direction] if {$pin_dir ! in} { error Expected target pin directionin, got $pin_dir }4.3 属性与约束的修改边界连接修改是物理层面的属性修改则是逻辑层面的。Tessent Shell里可以用set_property修改对象属性比如把一个cell标记成dont_touch、把某个pin标记为is_clock、设置某个net的测试属性。这类改动不像连接修改那样改变网表拓扑但它们会影响后续Tessent工具对设计的理解和处理。典型场景是手动指定扫描链类型。Tessent在自动识别scan cell时偶尔会因为跨时钟域或特殊cell类型判断错误。你可以这样修正set scan_cells [get_cells -hier -filter is_scan_cell true] foreach cell $scan_cells { set ref [get_property $cell ref_name] if {[string match *SDFF* $ref]} { set_property $cell scan_type muxed_dff } }注意不要随意修改工具内部维护而你没有明确理解的对象属性。比如is_scan_cell这个属性通常是由analyze_scan_cells这种命令根据库信息推断出来的如果你手工覆盖后面的ATPG可能会做出错误的假设。修改属性前先确认这个属性是用户可写还是工具自动维护否则你改完后面跑出的结果会完全对不上。手册里一般会标注属性的读写属性归属别跳过那些属性表格。否则出了问题排查方向往往错得离谱。5. 把看和改串成流程一个可回归的DFT准备脚本5.1 脚本骨架与执行策略Introspection和Editing单独用都简单难的是把它们串成一个流程。项目一复杂你可能要经历读入设计 - 多层实例多次切换 - 内部属性检查 - 连接修改 - 同步修改时钟约束 - 重新报告这样一环扣一环的流程。这时候脚本的组织方式决定了调试效率。我习惯把脚本拆成三个阶段。阶段一是环境与设计加载只做read_design和必要的基本配置阶段二是结构化内省把设计关键信息写到外部文件或Tcl变量里阶段三才是编辑验证。这样设计的最大好处是当第三阶段报错时你可以随时重新source前两阶段不需要重新读一遍设计。# Stage 1: 环境与设计 set_workspace ./tmp_run read_design -netlist ../data/top_netlist.v -top top \ -library ../lib/techlib.tessent # Stage 2: 内省与检查 source ./procs/inspect_design.tcl inspect_design -output ./report/design_snapshot.txt # Stage 3: 编辑和验证 set_property [get_cells top/dft_wrapper] is_black_box false change_connection ... source ./procs/validate_edits.tcl注意在长时间运行的批处理脚本里每一段关键操作后面都应该加一句puts Completed: ... 这样的日志输出。Tessent Shell不是调试器你没有逐行断点可踩唯一的定位依据就是log。日志不打清楚实际项目里会很难受。5.2 与乒乓操作和断点保存的配合Tessent Shell对设计修改的支持虽然灵活但毕竟不是增量式版本管理工具。一个常见的失误是脚本一口气做了几十处修改做到第35处时发现前面的第12处修改引入了一个后续无法忽略的问题。这时候你回滚的代价就很大了除非你提前做了保存。所以我在做长期编辑任务时会刻意采取分段保存策略。比如每完成一个逻辑功能块的修改就执行write_design -format ddc -output ./checkpoints/design_after_mux_edits.ddc这样如果后续出错你不需要从头开始读原始网表而是直接从最近一个checkpoint继续。配合write_design的还有对应的读回命令read_design。Tessent Shell的read/write体系支持ddc、verilog、def等格式但checkpoint最好选工具原生格式因为只有原生格式才能无失真地保留所有Tessent扩展属性。如果你导出Verilog再读回来那些scan_group、edt_channel的抽象信息很可能会丢。5.3 从脚本报错倒推设计问题的经验脚本报错是家常便饭但很多报错其实非常典型。比如Cannot find object这种往往不是命令写错了而是current_instance指向不对。又比如Collection is empty不一定是设计本身的问题很可能你在-filter里写了一个不存在的属性名工具直接返回空集合连warning都不打。我在排查这类问题时有一套固定的三查套路。先查上下文。看报错那一行前面最近一次current_instance是什么大部分失败都能在这个层面解决。再查对象类型。确定你要操作的是cell、pin还是net用对应类型的get_*命令去确认对象是否存在顺便看一下它的full_name是不是和你预期的一致。最后查属性绑定。如果你用了-filter把filter里的属性名单独拿出来跑一遍比如get_property [get_cells xxx] 你的属性名看能否正确返回值。大多数脚本问题都能在第三步解决。如果你走到第三步还是查不出来那就要怀疑设计数据的完整性了比如是否漏读库文件、是否有module mismatch。这种问题一般会在log早期阶段出现往回翻log往往能找到线索。6. 内省与编辑的常见坑位排查实录6.1 名字解析失败大小写、逃逸与路径三连坑名字解析问题是introspection中最频繁的败点。第一个坑是大小写。门级网表里的命名通常保留大小写但Tessent Shell在默认情况下对名字是敏感还是不敏感实际上和具体版本、设置有关系。我遇到过在某个版本里get_cells TOP能查到顶层但get_cells top查不到的情况。所以写脚本时名字大小写一律以get_property $obj full_name输出为准不要凭记忆敲。第二个坑是总线名的方括号。综合工具生成的名字经常是data_reg[0]、data_reg[31]这种形式。如果直接用get_cells data_reg[0]Tessent Shell有可能会把[0]当作Tcl列表下标或者正则表达式的字符组来解析结果完全不是你想要的。稳妥的写法是用花括号做精确匹配set cell [get_cells {top/data_reg[0]}]第三个坑是路径前缀。Tessent Shell里有些命令在解析路径时会自动在对象名前加current_instance前缀有些则不会。如果你的脚本切换过current_instance同样的对象名在不同阶段可能解析到不同对象。要彻底规避这个问题最可靠的做法是在关键操作前显式、完整地写出绝对路径或者先在脚本里把对象抓成collection变量后续操作都用变量引用不要反复用名字去解析。6.2 改动没生效数据库一致性问题是真凶比找不到对象更隐蔽的是命令跑完了但后面的工具不认这些改动。常见情况是你用connect_net连了一条线report_design_rules也检查通过但跑到insert_scan或者build_atpg时工具仍然按旧连接去分析。这时候问题往往不在连接本身而在于属性缓存和层次上下文的关联。Tessent Shell内部对设计是有缓存机制的某些大范围报告类命令会在内存里缓存netlist的拓扑信息。如果你在报告命令之后做了编辑但没有触发对应的缓存失效后续分析就可能读到旧数据。这类问题的演示方式简单粗暴执行完一系列编辑之后调用一次update_design -full或者reset_design让工具强制刷新内部数据库。不同的Tessent版本对这个命令的命名方式会有差异但基本思想一样。如果找不到就跑一条无关紧要的重量级report命令通常也会触发刷新。经验法则不要在一条只读报告和另一条只读报告之间直接做修改否则报告的间隔里很容易混入过期缓存。也不要在循环体内频繁做全量刷新那会大幅拖慢大型设计的运行效率。批量编辑 - 一次刷新 - 集中验证这是最优节奏。6.3 改坏了怎么办备份策略与增量编辑最后一个坑也是最致命的是改了设计之后发现改不回去。Tessent Shell的undo支持非常有限不像文本编辑器那样能不断CtrlZ。所以永远不要在一个不可恢复的原始设计上直接做大量修改。我个人的做法是三层保险。第一层是文件层面备份。启动脚本时第一件事就是write_design -format ddc -output ./bk/original.ddc保留一个纯原始状态。第二层是checkpoint备份前面提到过每完成一个功能性修改就保存一个checkpoint。第三层是snapshot式的属性记录——用introspection命令把关键属性导出成文本文件set fp [open ./bk/original_attributes.txt w] foreach cell [get_cells -hier -filter is_scan_cell true] { puts $fp $cell [get_property $cell scan_type] } close $fp这样的文本snapshot即使工具崩溃打不开ddc你也能通过重新读入网表、再执行一段属性恢复脚本把关键信息复原。某种程度上这份文本snapshot比ddc更有价值因为它可读、可diff、可审计。6.4 编辑后验证不只看report还要看时间序最后想额外提醒一点。Tessent Shell的report_design_rules和report_scan_groups能抓结构性问题但抓不了时序相关性。DFT编辑尤其是替换时钟mux、插入测试点这类操作往往会引入新的组合路径或新的约束冲突。在做完结构验证之后务必再跑一遍完整的时序约束检查比如report_constraints -verbose或工具自带的DRC确保改动没有破坏原有的时序边界。我见过有人把一条scan_en直接从功能引脚改接到内部测试寄存器输出结构上完全正确report_design_rules干干净净但那条路径的驱动强度不够到布局布线阶段变成了一个时序违约点。这类问题在逻辑编辑阶段看不见但它确实是编辑后验证需要考虑的一环。Tessent Shell本身提供的内省能力已经很强了但跨工具的数据流验证永远不能省。回到第三章标题里的Design Introspection and Editing它其实是在传达一个理念真正高效的DFT设计维护不是你抓着网表文件一遍遍翻而是建立起结构可以查询、对象可以操作、结果可以验证的闭环。Tessent Shell把这个闭环做进了命令层工程师要做的就是学会用它的语法去描述脑子里那张设计图。以我自己的使用体会收个尾刚上手时别急着追求掌握所有get_*命令先把get_cells、get_pins、get_nets、get_property和current_instance这一套组合玩熟再慢慢往外扩。第三章作为未完待续的手册章节我猜后面还会有更深入的场景化案例比如和insert_scan、edt_*命令体系的联动。真到那一步前面这些内省和编辑的基础就会成为你能往前走的底气。
返回列表