ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

VCS +define+ 用法详解:语法、Verdi 联调与编译避坑

VCS +define+ 用法详解:语法、Verdi 联调与编译避坑 做数字前端或者验证的同学几乎绕不开 VCS 这套编译仿真流程而define这个选项属于那种看一眼就懂、用起来天天踩坑的东西。它的作用很朴素在编译命令行上给源码塞一个宏定义让同一份 RTL 或验证代码在不同编译参数下呈现出不同形态。今天就把 VCSdefine的简单用法从头到尾捋一遍——基本语法、带值宏和字符串宏怎么写、多个宏怎么批量传、跟 Verdi 联合仿真时怎么保证两边宏状态一致以及我在实际工程里踩过的那些坑。不管你是刚上手数字 IC 验证的新人还是已经写了几年 testbench 的老手这篇都可以当成随手查的手册命令行直接抄过去改改就能用。先说清楚定位define是编译期选项只能喂给vcs这个编译命令不是仿真命令simv的参数。这一点听起来像废话但我见过不止一个新人把它拼在./simv后面然后对着宏没生效的日志怀疑人生。理解了这个前提后面所有内容都会顺很多。1. 先把 define 这件事说透1.1 它解决的核心问题一份代码多种形态做芯片验证最现实的问题是同一份代码要在很多个场景下跑模块级跑一遍、子系统跑一遍、全芯片跑一遍有的场景要打开断言、有的场景要关掉断言换性能有的场景需要把数据位宽配成 32有的场景要配成 64。如果每个场景都去复制一份源码改几行那维护成本会爆炸改一个 bug 要改十几个文件。define就是用来消灭这种复制的。它的思路很简单源码里用ifdef、ifndef、else把差异部分包起来具体走哪个分支由编译命令行决定。这样代码只有一份形态有无数种改逻辑改一处换配置改一行脚本。我在实际项目里一个中等规模的验证环境编译脚本里常驻的宏大概有七八个覆盖仿真开关、位宽配置、检查使能、日志等级、随机种子策略这些维度全靠这一套撑起来。它跟文件里的define是同一件事的两个入口都是往编译器的宏表里塞条目只不过一个写在代码里一个写在命令行上。写代码里的好处是跟着文件走、有版本管理写命令行的好处是不用改代码就能换配置特别适合 CI 流水线批量跑回归。1.2 define 和代码里的 define 到底谁赢这是最常见的疑问之一。结论是谁后进宏表谁生效同名会触发宏重定义警告。VCS 在编译时先处理命令行上的define再逐文件解析源码如果源码里又对同一个宏名做了一次define你会在编译日志里看到类似Warning-[MDCV]之类的重定义提示然后以你源码里的那次为准。这个行为本身不算坑坑在于它只给 Warning 不给 Error日志刷屏的时候很容易被忽略最后表现成我明明在命令行定义了 32为什么综合出来还是 16。我的习惯是源码里所有可能被命令行覆盖的宏一律加保护ifndef DATA_WIDTH define DATA_WIDTH 16 endif这样命令行给了就用命令行的没给就用默认值永远不会重定义日志也干净。这条规范我在带新人的时候是强制要求的因为它能省掉大量宏到底从哪来的排查时间。1.3 什么时候该用它什么时候别用define好用但不是万能。我的判断标准有三条第一这个差异如果是功能逻辑层面的比如某个算法有两种实现那更适合用 SystemVerilog 的配置类、工厂重载或者 parameter而不是宏第二如果这个差异需要在仿真运行时动态切换宏做不到宏在编译那一刻就固化了运行时改不了第三如果差异只影响一个模块用 parameter 传进去比全局宏更干净全局宏是污染性的任何文件都能看到它。反过来说下面这几类场景用宏就非常合适仿真与综合的代码隔离比如ifdef SYNTHESIS包掉$display和initial块、验证组件的调试打印开关、覆盖率收集开关、DPI 调用里的 C 代码开关、以及版本号或编译时间戳这类元信息注入。这些场景的共同特点是差异是构建期决定的而且往往是全局性质的。2. 语法细节与命令行参数全拆解2.1 最常用的三种写法define的语法非常朴素但有几个变体必须记住因为写错了不会报错只会静默地变成你想要之外的东西。第一种是不带值的裸定义vcs -full64 -sverilog defineSIM defineCHK_EN -f filelist.f -o simv这种写法等价于把宏定义成1。源码里用ifdef SIM判断存在性或者直接用SIM当成数值 1 参与运算都是合法的。裸定义是最常用的形式适合做开关。第二种是带值定义vcs -full64 -sverilog defineDATA_WIDTH32 defineDEPTH1024 -f filelist.f -o simv等号右边的内容会原样成为宏的展开文本。注意是原样——你写32它就是32你写32d10它就是32d10你写一个表达式它也是原样展开。这个特性给了很大灵活度但也意味着 VCS 不会帮你做任何类型检查宏值不合理只会在编译报错时才发现。第三种是批量定义一次给多个宏vcs -full64 -sverilog defineSIMCHK_ENCOV_ON -f filelist.f -o simv用加号连着写每个都是裸定义为 1。这个写法的好处是脚本短坏处是可读性差、加错一个字符很难发现。我一般只在宏数量少三个以内、且含义相近的时候用批量写法超过三个就老老实实一行一个配合反斜杠换行让脚本能一眼看懂。注意define和宏名之间、宏名和等号之间都不要有空格。defineDATA_WIDTH 32会被拆成三个参数VCS 只会认第一个剩下两个要么被当成文件路径报错要么被静默忽略。2.2 带值宏、字符串宏与带参数宏带值宏在实际工程里最典型的用法是参数注入。比如上面那个DATA_WIDTH代码里可以这么写module datapath #( ifdef DATA_WIDTH parameter int W DATA_WIDTH, else parameter int W 16, endif parameter int DEPTH 512 ) ( input logic clk, input logic [W-1:0] din, output logic [W-1:0] dout ); endmodule这种写法让位宽从脚本一层控制到底层模块配合不同的回归用例改一个数字就行不用碰任何 RTL。字符串宏要稍微绕一下因为涉及 shell、VCS、SystemVerilog 三层转义。目标是在代码里得到一个真正的字符串字面量vcs -full64 -sverilog defineTC_NAMEsmoke_test -f filelist.f -o simv关键是最外层用单引号把整个参数包起来。这样 shell 不会去解释双引号VCS 收到的宏体是smoke_test展开到代码里就是合法的 SV 字符串。代码里这样用initial begin $display([INFO] running testcase: %s, TC_NAME); end如果字符串里带空格比如smoke test v2不加外层引号会被 shell 按空格切成两段第二段 VCS 认不出来就报错所以带空格的字符串宏必须整体加引号这一点没有商量余地。带参数的宏也能从命令行定义虽然用得少但偶尔很省事vcs -full64 -sverilog defineMAX(a,b)((a)(b)?(a):(b)) -f filelist.f -o simv注意这里逗号和括号都在单引号保护范围内不会被误切。这种用法我一般只在临时调试时用正式工程里更倾向于把这类宏写进公共头文件因为命令行里堆复杂宏会导致脚本难读、也容易在不同 shell 下出现转义差异。还有一个经常被忽略的点define只影响 Verilog/SystemVerilog 代码不影响 C 代码。如果你的工程里有 DPI-C 或者 PLI 的 C 源文件想让它们也吃到宏得走另一条路vcs -full64 -sverilog defineSIM -CFLAGS -DDPI_DEBUG -O2 -f filelist.f -o simv-CFLAGS后面跟的是标准 C 编译器的参数-D才是 C 侧的定义方式。两边名字最好保持一致省得以后看着SIM和DPI_DEBUG两个名字犯迷糊。2.3 批量定义、取消定义与文件列表配合宏多了以后把它们集中管理比散落在命令行里靠谱得多。最朴素的做法是在 Makefile 里维护一个变量DEFINES defineSIM \ defineDATA_WIDTH32 \ defineDEPTH1024 \ defineCHK_EN \ defineCOV_ON然后用的时候直接展开compile: vcs -full64 -sverilog $(DEFINES) \ -debug_accessall -kdb -lca \ -f filelist.f -o simv这样切换配置只需要改DEFINES的定义或者做成两个变量用命令行传参选择ifeq ($(MODE),debug) DEFINES defineDEBUG_PRINT defineASSERT_ON endif至于-f文件列表define是可以写进去的VCS 解析-f文件时会把里面的define当参数处理。但这里有三个注意事项一是-f文件里一行一个参数不要用逗号分隔二是不要在里面写续行符VCS 的-f解析规则和 Makefile 不一样三是宏定义放在-f里会让这个宏从哪来变得难找我个人的偏好是宏只放 Makefile-f里只放文件路径和-incdir。还有个冷门但好用的选项是undefine用来在编译时取消一个宏vcs -full64 -sverilog defineSIMDEBUG_PRINT undefineDEBUG_PRINT -f filelist.f -o simv典型用途是在继承了一套公共编译选项的基础上针对某个特殊编译把某个开关摘掉而不用去改公共选项的定义。3. 工程里真正跑得通的四种用法3.1 阶段开关让同一套环境区分仿真与综合这是宏最古老也最经典的用法。RTL 里那些只对仿真有意义、综合工具不认的语句——initial块、$display、$fsdbDumpfile、延时语句、$random调用——全部用ifdef包起来ifdef SIM initial begin $display([%0t] datapath init, W%0d, $time, W); end always (posedge clk) begin if (din x) $error(din is X at %0t, $time); end endif编译仿真时给defineSIM综合时不给或者给defineSYNTHESIS。这样做的价值不只是综合工具不报错更重要的是避免仿真专用逻辑被意外综合进去——比如某个initial块给寄存器赋了初值在某些综合流程里真的会被推导成上电复位逻辑那就麻烦了。实操上我建议把这个开关的命名固定下来团队统一叫SIM或者SYNTHESIS不要一个人写SIMULATION、另一个人写SIM_EN。这类命名混乱造成的后果是某个文件用了ifdef SIMULATION而你命令行给的是SIM那段代码永远不生效而且不报任何错。这种情况排查起来非常耗时间。3.2 调试开关按需挂载打印与波形验证环境里打印信息最容易失控。全打开的时候一次长回归产生几百 MB 日志真正有用的信息被淹没全关掉的时候出了问题又没线索。用宏做分层控制就很合适ifdef DBG_HIGH define DBG_LOG(msg) $display([DBG][%0t] %s, $time, msg) elsif DBG_LOW define DBG_LOG(msg) $display([DBG][%0t] %s, $time, msg) else define DBG_LOG(msg) endif编译时按需给defineDBG_HIGH或defineDBG_LOW不给就是完全静默。这种宏包宏的写法在大型环境里很常见好处是调用点统一写DBG_LOG(...)具体行为由编译开关决定。波形也是同理。FSDB 的 dump 控制可以用层级宏来切ifdef DUMP_TOP initial begin $fsdbDumpfile(top.fsdb); $fsdbDumpvars(0, tb_top); end elsif DUMP_DUT initial begin $fsdbDumpfile(dut.fsdb); $fsdbDumpvars(0, tb_top.u_dut); end endif模块级调试只 dump DUT、系统级调试 dump 全层次靠一个宏切换波形文件大小能差一个数量级跑回归的时间差别很实在。3.3 参数注入把配置从代码里搬到脚本宏注入参数最舒服的地方在于它让配置这件事从 RTL 工程师手里转移到了验证脚本里。举个我实际项目里的例子一个数据通路模块支持 8/16/32 位三种位宽早期是在 top 里例化三次分别写死参数。后来改成宏控制回归脚本里不同用例传不同值# 用例 A vcs -full64 -sverilog defineDATA_WIDTH8 -f filelist.f -o simv_a # 用例 B vcs -full64 -sverilog defineDATA_WIDTH32 -f filelist.f -o simv_b编译出两个 simv跑不同的激励。这里要提醒的是宏注入的参数不具备运行时可变性每个编译出来的 simv 是固化的所以如果需要在一个仿真里跑遍所有位宽宏就不是好选择应该用 SystemVerilog 的 parameter 加循环例化或者配置类在 build 阶段决定。判断标准很简单这个值在一次仿真生命周期内会变吗会变就别用宏。3.4 版本与用例裁剪宏做条件编译的取舍还有一类用法是往代码里塞元信息比如编译版本号、编译时间、当前用例名vcs -full64 -sverilog \ defineBUILD_VERv1.2.3 \ defineBUILD_TIME2024-05-20 \ -f filelist.f -o simv代码里把它打进日志头部出了问题一看日志就知道跑的是哪个版本省得跟人扯你跑的是最新代码吗。这个成本极低、收益极高我基本每个项目都会加。但这里有个取舍必须说清楚宏不能滥用。我见过环境里用宏做了十几个维度的裁剪最后一份 RTL 在不同宏组合下有几十种形态任何一次改动都要在脑子里跑一遍组合维护成本远超收益。我的经验是一个模块的宏数量控制在三个以内超过就说明这段差异应该用 parameter、配置类或者干脆拆文件来解决了。4. 一套可复现的实操流程4.1 目录与文件准备为了把流程说清楚我搭一个最小可跑的工程。目录结构如下proj/ ├── rtl/ │ ── datapath.sv ├── tb/ │ └── tb_top.sv ├── filelist.f └── Makefilertl/datapath.sv就是上面那个受DATA_WIDTH控制的模块tb/tb_top.sv里例化它并打印当前配置。filelist.f内容简单到不能再简单./rtl/datapath.sv ./tb/tb_top.svtb_top.sv里我加一段用来验证宏是否真的生效的打印这是排查问题最有效的手段module tb_top; logic clk 0; logic [31:0] din, dout; ifdef DATA_WIDTH localparam int W DATA_WIDTH; else localparam int W 16; endif always #5 clk ~clk; datapath #(.W(W)) u_dut (.clk(clk), .din(din[W-1:0]), .dout(dout[W-1:0])); initial begin $display([TB] W%0d, W); ifdef SIM $display([TB] SIM macro is ON); else $display([TB] SIM macro is OFF); endif #100; $finish; end endmodule4.2 Makefile 与编译命令Makefile长这样注意制表符缩进这是最经典的翻车点VCS vcs -full64 -sverilog -timescale1ns/1ps DEFINES defineSIM defineDATA_WIDTH32 SIM_OPT -debug_accessall -kdb -lca compile: $(VCS) $(DEFINES) $(SIM_OPT) -f filelist.f -o simv run: ./simv -l run.log clean: rm -rf simv simv.daidir csrc ucli.key novas.* *.fsdb *.log编译命令展开后是vcs -full64 -sverilog -timescale1ns/1ps \ defineSIM defineDATA_WIDTH32 \ -debug_accessall -kdb -lca \ -f filelist.f -o simv几个参数的选择理由-full64走 64 位编译现在基本是默认选择能支持更大的内存-sverilog打开 SystemVerilog 支持不加的话.sv文件里的logic、always_ff这些会报错-timescale给全局时间精度省得每个文件都写一遍-debug_accessall和-kdb -lca是为了后面用 Verdi 看波形和源码-kdb会生成知识数据库Verdi 直接读这个库不用自己重新解析一遍源码——这一步对宏场景特别关键后面会讲。4.3 与 Verdi 联合仿真的衔接宏在 Verdi 这一侧有个容易被忽视的坑Verdi 自己也会解析源码如果你让它独立解析文件列表它并不知道你编译时给了哪些宏于是它看到的ifdef分支和实际编译的完全不是一回事。表现就是波形里信号名对得上但双击进源码看到的是被剪掉的代码或者行号错位。正确做法是走kdb 流程编译时加-kdb -lcaVerdi 用-dbdir读编译产物而不是自己重新解析源码。# 编译生成 simv 和 simv.daidir/kdb vcs -full64 -sverilog defineSIM defineDATA_WIDTH32 \ -debug_accessall -kdb -lca -f filelist.f -o simv # 跑仿真dump 波形 ./simv -l run.log # 用 Verdi 打开直接复用编译库 verdi -dbdir simv.daidir/kdb -ssf dump.fsdb 这个组合我实测是最稳的宏状态、源码行号、信号层级三边完全一致不会出现波形里的信号在源码里找不到这种情况。如果你的环境还在用老式的 PLI 方式挂 Verdi原理上也行但依然建议用 kdb因为老流程在多版本工具共存时更容易出兼容问题。4.4 实验记录同一份代码编出两个版本跑一次做个对照能直观看到宏的作用。第一次编译给defineDATA_WIDTH32$ make clean make compile make run [TB] W32 [TB] SIM macro is ON $finish at simulation time 100第二次换掉宏值只改DEFINES$ sed -i s/DATA_WIDTH32/DATA_WIDTH8/ Makefile $ make clean make compile make run [TB] W8 [TB] SIM macro is ON源码一个字没改位宽从 32 变成 8。如果这个时候去看编译产物两次的simv是不同的可执行文件simv.daidir也是不同的所以切换宏之后必须重新编译这一点没有捷径。顺手验证一下undefine$ vcs -full64 -sverilog defineSIM undefineSIM -f filelist.f -o simv_undef $ ./simv_undef [TB] W16 [TB] SIM macro is OFF可以看到SIM被取消后DATA_WIDTH也没给代码回落到默认的 16。这就是前面说的默认值保护写法在起作用的证明。5. 编译工具横向对照与选型参考5.1 VCS 与 Xcelium 的选项对照很多同学是先用 Xcelium 上手后来转到 VCS两边选项符号体系完全不同最容易搞混的就是宏定义。VCS 用的是加号开头的defineXcelium 的xrun用的是减号开头的-define功能一样但写法完全不通用。下面这张表是我自己整理的对照贴在工位上备查很实用。功能VCSXceliumxrun定义裸宏defineSIM-define SIM定义带值宏defineW32-define W32取消宏undefineSIM-undefine SIM头文件路径incdir./inc-incdir ./inc文件列表-f filelist.f-f filelist.f调试访问-debug_accessall-access rwc生成 Verdi 库-kdb -lca由 Verdi 侧-ssf/-dbdir配合从表里能看出来除了宏和头文件路径这两项其他很多是共通的所以脚本迁移的主要工作量就在把define批量替换成-define。如果你维护的是双工具环境我建议在 Makefile 里做一层抽象ifeq ($(SIMULATOR),vcs) SIM_DEF $(addprefix define,$(MACROS)) else SIM_DEF $(addprefix -define ,$(MACROS)) endifMACROS里只写SIM W32这样的裸内容前缀由工具决定。这样一套宏配置能喂给两套工具回归脚本不用维护两份维护成本直接砍半。5.2 数字 IC 验证里的常见组合说说数字 IC 领域里大家实际在用什么。目前的普遍情况是前端仿真和验证以 VCS Verdi 为主这是长期积累下来的流程惯性脚本、IP 库、覆盖率流程都围绕这套建的Xcelium 在三大家的混用环境里也很常见尤其是在综合前仿真和某些形式验证流程里还有些团队为了省编译时间会把回归任务分到两套工具上跑用同一份宏配置保证结果可比。这就带来一个现实要求宏定义必须做到工具无关、脚本一处定义多处使用。我踩过的坑就是某次回归里VCS 侧给了defineCOV_ONXcelium 侧漏了对应的-define结果两边覆盖率数字对不上排查了大半天才定位到是脚本问题不是设计问题。从那之后我就坚持所有宏只在 Makefile 顶部的MACROS变量里定义工具特有的前缀全部由模板生成人只碰一处。至于环境本身VCS 这类工具是 EDA 环境的统一部署内容个人层面要关心的就两件事一是工具是否能正常启动、版本是否符合项目要求二是授权配置是否就绪这一点通常由环境负责人统一处理自己不要乱动相关配置。编译前先跑一次vcs -ID之类的版本查询确认工具可用比编译到一半报错要省时间得多。6. 常见问题与排查实录6.1 宏没生效的五种典型原因这是最高频的问题我把遇到过的原因按出现概率排了个序基本能覆盖九成情况。第一种宏拼给了./simv而不是vcs。前面强调过define是编译期选项simv不认它要么报未知参数要么被静默忽略反正宏肯定不生效。判断方法很简单看你的命令行第一条命令是vcs还是./simv。第二种宏名大小写不一致。Verilog 的宏名是大小写敏感的命令行给defineSIM代码里写ifdef Sim永远不成立而且不会有任何提示。我的做法是全部大写团队统一不接受例外。第三种define写在了-f文件里的奇怪位置。比如某个-f文件末尾缺了换行最后一行的宏和后面的文件路径粘在一起或者中间的宏被注释符号挡住了。这种问题的特征是部分宏生效、部分不生效很容易误导排查方向。建议-f里只放文件路径宏全部外置。第四种源码里有同名define覆盖了命令行。这就是 1.2 节说的情况日志里有 Warning但没人看。加了ifndef保护就能根治。第五种增量编译没重新构建。改了宏但没清simv.daidirVCS 复用了上一次的编译中间结果。这个坑最隐蔽因为它有时候能正确重建、有时候不能表现非常灵异。后面单独说。6.2 转义、引号与特殊字符的坑字符串宏是踩坑重灾区我把几个典型情况整理成表可以当速查用。需求命令行写法代码里得到的东西简单字符串defineNAMEsmokesmoke带空格字符串defineMSGhello worldhello world数值defineW3232带位宽的数值defineINIT8\h5A8h5A表达式defineMAXV(18)-1(18)-1带参宏defineM(a,b)((a)(b)?(a):(b))函数式宏带单引号的数值那一行要特别注意SV 里的8h5A本身含单引号如果你外层也用单引号包shell 会提前结束引用。稳妥的写法是用双引号包外层、内部单引号照写或者用\这种转义写法。实测下来我在脚本里更倾向于避免在命令行写带位宽的数值宏改成只传一个纯数字让代码里做位宽转换省得跟 shell 较劲。还有一个隐蔽问题是shell 与 Makefile 的转义层级不同。同一条命令你在终端里跑没问题写进 Makefile 就出问题因为 make 会先展开一次变量再交给 shell 执行$、、的语义在这两层里不一样。排查方法是最直接的用make -n把展开后的命令行打出来复制到终端里手动跑一遍能复现就说明是 Makefile 转义问题不能复现就说明是 shell 环境差异。6.3 增量编译与缓存引起的灵异现象VCS 为了加快二次编译会保留simv.daidir和csrc目录只重新处理变化的部分。这个机制正常情况下很有价值但和define组合起来偶尔会出问题宏发生了变化但 VCS 的依赖判断没能完全识别于是复用了一部分旧的编译结果。表现是你改了DATA_WIDTH32改成16日志里define也确实是 16但仿真跑出来的行为还是 32 的。第一次遇到会以为是设计有问题其实只是缓存。处理办法很朴素但很有效——换宏就清一次中间产物rm -rf simv simv.daidir csrc ucli.key我在 CI 脚本里干脆每次回归都彻底清理牺牲一点编译时间换确定性非常值。本机开发时可以保守一点只在切换宏配置时清但一定要形成改宏先清缓存的肌肉记忆。6.4 常见问题速查表最后把排查路径整理成一张表出问题时从上往下逐条排除基本能定位到原因。现象可能原因排查动作宏完全不生效拼给了 simv大小写不一致确认命令行grep代码里的宏名拼写部分宏生效-f文件拼接问题shell 切分用make -n看展开后的完整命令宏值不对源码同名define覆盖查编译日志里的宏重定义 Warning字符串宏报错引号转义层级不对加外层单引号或用make -n确认改了宏行为没变增量编译缓存清simv.daidir和csrc重编Verdi 源码对不上Verdi 独立解析源码改用-kdb -lca-dbdir流程覆盖率两边对不上双工具宏配置不同步宏集中定义前缀由脚本生成这张表我自己用了好几年每次环境出问题先对着扫一遍大部分情况三五分钟就能定位。7. 几个能省时间的实操习惯关于命名我建议所有开关类宏用XXX_ON或者干脆全大写单词的形式值类宏用XXX_WIDTH、XXX_DEPTH这种带单位含义的名字这样在几万行的代码里 grep 的时候一眼能分辨出是开关还是参数。团队里如果有新人加入先在公共头文件里列一份宏清单并加上注释说明每个宏的用途、默认值和生效范围这份文档的价值会在半年后体现得非常明显。关于默认值我的原则是默认配置必须是能跑通的最简配置。也就是说一个新人拉下代码不加任何define直接编译应该能编译通过并且跑出一个基本正确的结果。这要求所有ifdef都配else分支而不是只写ifdef就完事。很多环境的必须带一堆宏才能编译就是这么来的维护成本高得离谱。关于日志我强烈建议在每个define影响的代码分支里埋一条打印哪怕只是一行$display(macro X is on)。上面tb_top.sv里那段就是干这个的。当你有一个几十个宏的大型环境时这些打印能让你在几秒钟内确认宏状态而不是靠读代码猜。这一点在排查 CI 环境与本地环境差异时尤其管用——两边日志一比哪个宏不一样立刻现形。关于清理前面说过换宏必清缓存。我还会在clean目标里把所有中间产物和波形一起删掉包括*.fsdb、*.vpd、ucli.key、novas.conf。这些东西留着不但占空间还会在下次启动时被工具悄悄读取制造一些难以复现的小问题。宁多删一点别留隐患。关于工具链对照如果团队同时维护 VCS 和 Xcelium 两套流程务必花半天时间把宏定义抽象成工具无关的一层就是前面 5.1 节那个MACROS变量的写法。这半天投入会在后面每一次回归、每一次新用例加入、每一次覆盖率比对里省回来而且能避免两边结果对不上这类最难查的问题。我自己吃过这个亏后来每次新项目第一件事就是把这层抽象搭起来再没在这上面浪费过时间。
返回列表