# rtl_note_script **Repository Path**: gjm9999/rtl_note_script ## Basic Information - **Project Name**: rtl_note_script - **Description**: 受verilog-mode的启发,越来越认同代码即注释,注释即代码的思想了。 因此将以注释生成代码的若干脚本汇总在一个工程下,供大家使用。 - **Primary Language**: Python - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 6 - **Forks**: 0 - **Created**: 2023-10-19 - **Last Updated**: 2025-12-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 介绍 受verilog-mode的启发,越来越认同代码即注释,注释即代码的思想了。 因此将以注释生成代码的若干脚本汇总在一个工程下,供大家使用。 # 脚本使用 将工程目录下载于linux工作站后,在\~/.vimrc中添加如下的内容: ``` "you need change the path command! L :execute '%! /home/ICer/gitee_path/rtl_note_script/src/gen_link.py -f %' command! LD :execute '%! /home/ICer/gitee_path/rtl_note_script/src -d -f %' command! U :execute '%! /home/ICer/gitee_path/rtl_note_script/src/auto_unfold.py -f %' command! UD :execute '%! /home/ICer/gitee_path/rtl_note_script/src/auto_unfold.py -d -f %' command! AS :execute '%! /home/ICer/gitee_path/rtl_note_script/src/auto_assert.py %' command! DF :execute '%! /home/ICer/gitee_path/rtl_note_script/src/auto_dff.py -f %' command! DFD :execute '%! /home/ICer/gitee_path/rtl_note_script/src/auto_dff.py -d -f %' ``` 将其中的路径替换为实际的工程路径。之后可以在vim中直接调用这些脚本,调用方式为在阅览模式下键入冒号+对应的大写字母,之后enter确认即可。 # auto_unfold.py 在src/auto_unfold_test目录下有auto_unfold的测试文件。脚本识别的典型注释为: ``` /* AUTO_UNFOLD #for i 1..4 wire [15:0] sig#i#; END */ ``` i为循环变量,1和4为上下界均为包含模式。在键入:U后会展开为: ``` /* AUTO_UNFOLD #for i 1..4 wire [15:0] sig#i#; END */ //AUTO_UNFOLD_START wire [15:0] sig1; wire [15:0] sig2; wire [15:0] sig3; wire [15:0] sig4; //AUTO_UNFOLD_END ``` 键入:UD后会删除所有//AUTO_UNFOLD_START \~ //AUTO_UNFOLD_END之间的生成内容,恢复原本的文本内容。 上下界可以从大到小,从小到大,也可以一样: ``` /* AUTO_UNFOLD #for i 1..1 wire [15:0] sig#i#; END */ //AUTO_UNFOLD_START wire [15:0] sig1; //AUTO_UNFOLD_END /* AUTO_UNFOLD #for i 5..1 wire [15:0] sig#i#; END */ //AUTO_UNFOLD_START wire [15:0] sig5; wire [15:0] sig4; wire [15:0] sig3; wire [15:0] sig2; wire [15:0] sig1; //AUTO_UNFOLD_END ``` 支持有多行的生成项: ``` /* AUTO_UNFOLD #for i 5..1 wire [15:0] sig#i#; wire [15:0] reg#i#; END */ //AUTO_UNFOLD_START wire [15:0] sig5; wire [15:0] reg5; wire [15:0] sig4; wire [15:0] reg4; wire [15:0] sig3; wire [15:0] reg3; wire [15:0] sig2; wire [15:0] reg2; wire [15:0] sig1; wire [15:0] reg1; //AUTO_UNFOLD_END ``` 支持多个循环变量嵌套,嵌套时先循环上方的变量: ``` /* AUTO_UNFOLD #for i 2..1 #for j 5..5 #for k 4..3 wire [15:0] k#k#_sig#j#_sub#i#; END */ //AUTO_UNFOLD_START wire [15:0] k4_sig5_sub2; wire [15:0] k4_sig5_sub1; wire [15:0] k3_sig5_sub2; wire [15:0] k3_sig5_sub1; //AUTO_UNFOLD_END ``` 支持localparam,但是要求localparam的格式很固定,只能是下面这两种。因为localparam是本地的参数,原则上不能被修改只能是一个固定的值。如果有运算式或者和parameter关联了,脚本无法解析。 参数只能在出现在两个位置:循环里和后面提到的IF选择中: ``` localparam LOOP_I_MIN = 3, LOOP_I_MAX = 5; localparam LOOP_J_MIN = 2; /* AUTO_UNFOLD #for i LOOP_I_MIN..LOOP_I_MAX #for j LOOP_J_MIN..5 wire [15:0] sig#j#_sub#i#; END */ //AUTO_UNFOLD_START wire [15:0] sig2_sub3; wire [15:0] sig2_sub4; wire [15:0] sig2_sub5; wire [15:0] sig3_sub3; wire [15:0] sig3_sub4; wire [15:0] sig3_sub5; wire [15:0] sig4_sub3; wire [15:0] sig4_sub4; wire [15:0] sig4_sub5; wire [15:0] sig5_sub3; wire [15:0] sig5_sub4; wire [15:0] sig5_sub5; //AUTO_UNFOLD_END ``` 支持循环变量的四则和取模运算: ``` /* AUTO_UNFOLD #for i LOOP_I_MIN..LOOP_I_MAX #for j LOOP_J_MIN..5 wire [15:0] sig#i%j#_#j#_sub#i#; END */ //AUTO_UNFOLD_START wire [15:0] sig1_2_sub3; wire [15:0] sig0_2_sub4; wire [15:0] sig1_2_sub5; wire [15:0] sig0_3_sub3; wire [15:0] sig1_3_sub4; wire [15:0] sig2_3_sub5; wire [15:0] sig3_4_sub3; wire [15:0] sig0_4_sub4; wire [15:0] sig1_4_sub5; wire [15:0] sig3_5_sub3; wire [15:0] sig4_5_sub4; wire [15:0] sig0_5_sub5; //AUTO_UNFOLD_END ``` 支持生成行里带有条件,但是只支持if一种,不支持elseif和else: ``` /* AUTO_UNFOLD #for i 3..2 #for j 0..0 #for k 5..4 IF(#i==3 and k==4#)reg [15:0] sig_k#k#_j#j#_i#i#; END */ //AUTO_UNFOLD_START reg [15:0] sig_k4_j0_i3; //AUTO_UNFOLD_END ``` IF中带有参数的情况,注意SEL_NUM_SUB1不能是SEL_NUM -1,脚本识别不了参数里的运算: ``` localparam SEL_NUM = 32; localparam SEL_NUM_SUB1 = 31; /* AUTO_UNFOLD #for i 0..SEL_NUM_SUB1 IF(#i==0#)assign out_sig = (sel == 3'd#i#) ? in_sig#i# : IF(#i>0 and i0#)tyrc_dffre #(.WD(DATA_W)) u_common_reg#i#(.clk(clk), .rst_n(rst_n), .d(d), .en(en[#i#]), .q(reg_q#i#)); IF(#i==0#)assign common_reg_q#i# = {DATA_W{1'b0}}; END */ //AUTO_UNFOLD_START wire [DATA_W -1:0]common_reg_q0; assign common_reg_q0 = {DATA_W{1'b0}}; wire [DATA_W -1:0]common_reg_q1; tyrc_dffre #(.WD(DATA_W)) u_common_reg1(.clk(clk), .rst_n(rst_n), .d(d), .en(en[1]), .q(reg_q1)); wire [DATA_W -1:0]common_reg_q2; tyrc_dffre #(.WD(DATA_W)) u_common_reg2(.clk(clk), .rst_n(rst_n), .d(d), .en(en[2]), .q(reg_q2)); wire [DATA_W -1:0]common_reg_q3; tyrc_dffre #(.WD(DATA_W)) u_common_reg3(.clk(clk), .rst_n(rst_n), .d(d), .en(en[3]), .q(reg_q3)); wire [DATA_W -1:0]common_reg_q4; tyrc_dffre #(.WD(DATA_W)) u_common_reg4(.clk(clk), .rst_n(rst_n), .d(d), .en(en[4]), .q(reg_q4)); wire [DATA_W -1:0]common_reg_q5; tyrc_dffre #(.WD(DATA_W)) u_common_reg5(.clk(clk), .rst_n(rst_n), .d(d), .en(en[5]), .q(reg_q5)); wire [DATA_W -1:0]common_reg_q6; tyrc_dffre #(.WD(DATA_W)) u_common_reg6(.clk(clk), .rst_n(rst_n), .d(d), .en(en[6]), .q(reg_q6)); wire [DATA_W -1:0]common_reg_q7; tyrc_dffre #(.WD(DATA_W)) u_common_reg7(.clk(clk), .rst_n(rst_n), .d(d), .en(en[7]), .q(reg_q7)); wire [DATA_W -1:0]common_reg_q8; tyrc_dffre #(.WD(DATA_W)) u_common_reg8(.clk(clk), .rst_n(rst_n), .d(d), .en(en[8]), .q(reg_q8)); wire [DATA_W -1:0]common_reg_q9; tyrc_dffre #(.WD(DATA_W)) u_common_reg9(.clk(clk), .rst_n(rst_n), .d(d), .en(en[9]), .q(reg_q9)); wire [DATA_W -1:0]common_reg_q10; tyrc_dffre #(.WD(DATA_W)) u_common_reg10(.clk(clk), .rst_n(rst_n), .d(d), .en(en[10]), .q(reg_q10)); wire [DATA_W -1:0]common_reg_q11; tyrc_dffre #(.WD(DATA_W)) u_common_reg11(.clk(clk), .rst_n(rst_n), .d(d), .en(en[11]), .q(reg_q11)); wire [DATA_W -1:0]common_reg_q12; tyrc_dffre #(.WD(DATA_W)) u_common_reg12(.clk(clk), .rst_n(rst_n), .d(d), .en(en[12]), .q(reg_q12)); wire [DATA_W -1:0]common_reg_q13; tyrc_dffre #(.WD(DATA_W)) u_common_reg13(.clk(clk), .rst_n(rst_n), .d(d), .en(en[13]), .q(reg_q13)); wire [DATA_W -1:0]common_reg_q14; tyrc_dffre #(.WD(DATA_W)) u_common_reg14(.clk(clk), .rst_n(rst_n), .d(d), .en(en[14]), .q(reg_q14)); wire [DATA_W -1:0]common_reg_q15; tyrc_dffre #(.WD(DATA_W)) u_common_reg15(.clk(clk), .rst_n(rst_n), .d(d), .en(en[15]), .q(reg_q15)); wire [DATA_W -1:0]common_reg_q16; tyrc_dffre #(.WD(DATA_W)) u_common_reg16(.clk(clk), .rst_n(rst_n), .d(d), .en(en[16]), .q(reg_q16)); wire [DATA_W -1:0]common_reg_q17; tyrc_dffre #(.WD(DATA_W)) u_common_reg17(.clk(clk), .rst_n(rst_n), .d(d), .en(en[17]), .q(reg_q17)); wire [DATA_W -1:0]common_reg_q18; tyrc_dffre #(.WD(DATA_W)) u_common_reg18(.clk(clk), .rst_n(rst_n), .d(d), .en(en[18]), .q(reg_q18)); wire [DATA_W -1:0]common_reg_q19; tyrc_dffre #(.WD(DATA_W)) u_common_reg19(.clk(clk), .rst_n(rst_n), .d(d), .en(en[19]), .q(reg_q19)); wire [DATA_W -1:0]common_reg_q20; tyrc_dffre #(.WD(DATA_W)) u_common_reg20(.clk(clk), .rst_n(rst_n), .d(d), .en(en[20]), .q(reg_q20)); wire [DATA_W -1:0]common_reg_q21; tyrc_dffre #(.WD(DATA_W)) u_common_reg21(.clk(clk), .rst_n(rst_n), .d(d), .en(en[21]), .q(reg_q21)); wire [DATA_W -1:0]common_reg_q22; tyrc_dffre #(.WD(DATA_W)) u_common_reg22(.clk(clk), .rst_n(rst_n), .d(d), .en(en[22]), .q(reg_q22)); wire [DATA_W -1:0]common_reg_q23; tyrc_dffre #(.WD(DATA_W)) u_common_reg23(.clk(clk), .rst_n(rst_n), .d(d), .en(en[23]), .q(reg_q23)); wire [DATA_W -1:0]common_reg_q24; tyrc_dffre #(.WD(DATA_W)) u_common_reg24(.clk(clk), .rst_n(rst_n), .d(d), .en(en[24]), .q(reg_q24)); wire [DATA_W -1:0]common_reg_q25; tyrc_dffre #(.WD(DATA_W)) u_common_reg25(.clk(clk), .rst_n(rst_n), .d(d), .en(en[25]), .q(reg_q25)); wire [DATA_W -1:0]common_reg_q26; tyrc_dffre #(.WD(DATA_W)) u_common_reg26(.clk(clk), .rst_n(rst_n), .d(d), .en(en[26]), .q(reg_q26)); wire [DATA_W -1:0]common_reg_q27; tyrc_dffre #(.WD(DATA_W)) u_common_reg27(.clk(clk), .rst_n(rst_n), .d(d), .en(en[27]), .q(reg_q27)); wire [DATA_W -1:0]common_reg_q28; tyrc_dffre #(.WD(DATA_W)) u_common_reg28(.clk(clk), .rst_n(rst_n), .d(d), .en(en[28]), .q(reg_q28)); wire [DATA_W -1:0]common_reg_q29; tyrc_dffre #(.WD(DATA_W)) u_common_reg29(.clk(clk), .rst_n(rst_n), .d(d), .en(en[29]), .q(reg_q29)); wire [DATA_W -1:0]common_reg_q30; tyrc_dffre #(.WD(DATA_W)) u_common_reg30(.clk(clk), .rst_n(rst_n), .d(d), .en(en[30]), .q(reg_q30)); wire [DATA_W -1:0]common_reg_q31; tyrc_dffre #(.WD(DATA_W)) u_common_reg31(.clk(clk), .rst_n(rst_n), .d(d), .en(en[31]), .q(reg_q31)); //AUTO_UNFOLD_END /* AUTO_UNFOLD #for i 0..31 IF(#i==0#)assign qa = (rna == 5'd#i#) ? common_reg_q#i# : IF(#i>0 and i<31#) (rna == 5'd#i#) ? common_reg_q#i# : IF(#i==31#) common_reg_q#i# ; END */ //AUTO_UNFOLD_START assign qa = (rna == 5'd0) ? common_reg_q0 : (rna == 5'd1) ? common_reg_q1 : (rna == 5'd2) ? common_reg_q2 : (rna == 5'd3) ? common_reg_q3 : (rna == 5'd4) ? common_reg_q4 : (rna == 5'd5) ? common_reg_q5 : (rna == 5'd6) ? common_reg_q6 : (rna == 5'd7) ? common_reg_q7 : (rna == 5'd8) ? common_reg_q8 : (rna == 5'd9) ? common_reg_q9 : (rna == 5'd10) ? common_reg_q10 : (rna == 5'd11) ? common_reg_q11 : (rna == 5'd12) ? common_reg_q12 : (rna == 5'd13) ? common_reg_q13 : (rna == 5'd14) ? common_reg_q14 : (rna == 5'd15) ? common_reg_q15 : (rna == 5'd16) ? common_reg_q16 : (rna == 5'd17) ? common_reg_q17 : (rna == 5'd18) ? common_reg_q18 : (rna == 5'd19) ? common_reg_q19 : (rna == 5'd20) ? common_reg_q20 : (rna == 5'd21) ? common_reg_q21 : (rna == 5'd22) ? common_reg_q22 : (rna == 5'd23) ? common_reg_q23 : (rna == 5'd24) ? common_reg_q24 : (rna == 5'd25) ? common_reg_q25 : (rna == 5'd26) ? common_reg_q26 : (rna == 5'd27) ? common_reg_q27 : (rna == 5'd28) ? common_reg_q28 : (rna == 5'd29) ? common_reg_q29 : (rna == 5'd30) ? common_reg_q30 : common_reg_q31 ; //AUTO_UNFOLD_END /* AUTO_UNFOLD #for i 0..31 IF(#i==0#)assign qb = (rnb == 5'd#i#) ? common_reg_q#i# : IF(#i>0 and i<31#) (rnb == 5'd#i#) ? common_reg_q#i# : IF(#i==31#) common_reg_q#i# ; END */ //AUTO_UNFOLD_START assign qb = (rnb == 5'd0) ? common_reg_q0 : (rnb == 5'd1) ? common_reg_q1 : (rnb == 5'd2) ? common_reg_q2 : (rnb == 5'd3) ? common_reg_q3 : (rnb == 5'd4) ? common_reg_q4 : (rnb == 5'd5) ? common_reg_q5 : (rnb == 5'd6) ? common_reg_q6 : (rnb == 5'd7) ? common_reg_q7 : (rnb == 5'd8) ? common_reg_q8 : (rnb == 5'd9) ? common_reg_q9 : (rnb == 5'd10) ? common_reg_q10 : (rnb == 5'd11) ? common_reg_q11 : (rnb == 5'd12) ? common_reg_q12 : (rnb == 5'd13) ? common_reg_q13 : (rnb == 5'd14) ? common_reg_q14 : (rnb == 5'd15) ? common_reg_q15 : (rnb == 5'd16) ? common_reg_q16 : (rnb == 5'd17) ? common_reg_q17 : (rnb == 5'd18) ? common_reg_q18 : (rnb == 5'd19) ? common_reg_q19 : (rnb == 5'd20) ? common_reg_q20 : (rnb == 5'd21) ? common_reg_q21 : (rnb == 5'd22) ? common_reg_q22 : (rnb == 5'd23) ? common_reg_q23 : (rnb == 5'd24) ? common_reg_q24 : (rnb == 5'd25) ? common_reg_q25 : (rnb == 5'd26) ? common_reg_q26 : (rnb == 5'd27) ? common_reg_q27 : (rnb == 5'd28) ? common_reg_q28 : (rnb == 5'd29) ? common_reg_q29 : (rnb == 5'd30) ? common_reg_q30 : common_reg_q31 ; //AUTO_UNFOLD_END ``` # auto_assert说明文档 ## 前言 不想写不会写断言,又想借助断言检查及早检查隐藏的问题加速定位。 对于这种既要又要的需求...是时候搬出auto_assert脚本了。 有了他,写断言不用一句话,各种检查哗啦啦的来呀! ## 工程路径 [src/auto_assert.py · 尼德兰的喵/rtl_note_script - Gitee.com](https://gitee.com/gjm9999/rtl_note_script/blob/master/src/auto_assert.py) ## 更新记录 | 时间 | 更新 | 说明 | | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | | 2025/06/24 | 1.增加脚本传参,指定ASSERTION使能的关键词
2.修改chk_xz断言,断言检查报错一次后就关闭自身
3.修改end_chk断言,不再依赖harness信号,通过final block实现
4.assert_clk和assert_rst_n手动修改assign后,重新生成时不会覆盖
5.生成代码美化
| 所有改动已编译并调试 | ## 功能列表 1. 仿真过程中的不定态检查; 2. 仿真过程中条件使能时的不定态检查; 3. 仿真过程中的取值范围检查; 4. 仿真过程中的条件使能时的取值范围检查; 5. 仿真结束后的信号值检查; ## 使用说明 脚本工作于linux环境,建议在gvim中使用,在.vimrc的尾部增加:: ``` command! AS :execute '%! {script_path}/auto_assert.py %' ``` 之后在gvim中键入:AS即可刷新文件。 脚本支持的语法适用范围:input/output/wire/reg,基本语法如下: **1.仿真过程中的不定态检查** 适用信号:valid/ready/配置信息 ``` input ddr_mst_link_valid;//ASSERT: chk_xz ``` **2.仿真过程中条件使能时的不定态检查** 适用信号:随路信息 ``` input  [CHANNEL_NUM -1:0]ddr_mst_link_strb ;//ASSERT: chk_xz while ddr_mst_link_valid ``` **3.仿真过程中的取值范围检查** 适用信号:配置信息/状态机/计数器等 ``` input  [CHANNEL_NUM -1:0]ddr_mst_tid_exist ;//ASSERT: chk_xz while ddr_mst_link_valid; == [0~10] input  [CHANNEL_NUM -1:0]ddr_mst_tid_repeat;//ASSERT:  == [5~(ddr_mst_tid_exist-2)] ``` **4.仿真过程中的条件使能时的取值范围检查** 适用信号:随路信息 ``` input  [CHANNEL_NUM -1:0]ddr_mst_tid_repeat;//ASSERT:  == [5~(ddr_mst_tid_exist-2)] while ddr_mst_link_valid ``` **5.仿真结束后的信号值检查** 适用信号:valid/ready/计数器/状态机等 ``` input  [CHANNEL_NUM -1:0]ddr_mst_tid_exist ;//ASSERT: chk_xz while ddr_mst_link_valid; == [0~10]; END = 10 ``` ## 使用示例 以如下代码为例(仅为举例,不是实际检查约束): ``` input ddr_mst_link_valid;//ASSERT: chk_xz output mst_ddr_link_ready;//ASSERT: chk_xz input [CHANNEL_NUM -1:0]ddr_mst_link_strb ;//ASSERT: chk_xz while ddr_mst_link_valid input [TID_WIDTH*CHANNEL_NUM -1:0]ddr_mst_tid ;//ASSERT: chk_xz while ddr_mst_link_valid input [INDEX_WIDTH*CHANNEL_NUM -1:0]ddr_mst_tid_index ;//ASSERT: chk_xz while ddr_mst_link_valid input [CHANNEL_NUM -1:0]ddr_mst_tid_exist ;//ASSERT: chk_xz while ddr_mst_link_valid; == [0~10]; END = 10 input [CHANNEL_NUM -1:0]ddr_mst_tid_repeat;//ASSERT: chk_xz while ddr_mst_link_valid input ddr_mst_link_last ;//ASSERT: chk_xz while ddr_mst_link_valid ``` 如果.vimrc配置完成了,则在gvim打开的文件内键入:AS后,会在endmodule上方填充如下代码: ``` `ifdef AUTO_ASSERT_ON // AUTO ADD wire assert_clk = clk; wire assert_rst_n = rst_n; reg after_rst = 1'b0; initial begin wait(assert_rst_n == 1'b0); wait(assert_rst_n == 1'b1); repeat(10) @(posedge assert_clk); after_rst = 1'b1; end always @(posedge assert_clk) if(assert_rst_n == 1'b0) after_rst <= 1'b1; property chk_xz(info); @(posedge assert_clk) disable iff(~after_rst) ~$isunknown(info); endproperty property chk_xz_valid(info, valid); @(posedge assert_clk) disable iff(~after_rst) valid |-> ~$isunknown(info); endproperty //CHACK SIGNAL X and Z assert property (chk_xz_valid(ddr_mst_link_strb, ddr_mst_link_valid)) else $error("ddr_mst_link_strb has xz error"); assert property (chk_xz_valid(ddr_mst_tid, ddr_mst_link_valid)) else $error("ddr_mst_tid has xz error"); assert property (chk_xz_valid(ddr_mst_tid_exist, ddr_mst_link_valid)) else $error("ddr_mst_tid_exist has xz error"); assert property (chk_xz_valid(ddr_mst_link_last, ddr_mst_link_valid)) else $error("ddr_mst_link_last has xz error"); assert property (chk_xz(ddr_mst_link_valid)) else $error("ddr_mst_link_valid has xz error"); assert property (chk_xz(mst_ddr_link_ready)) else $error("mst_ddr_link_ready has xz error"); assert property (chk_xz_valid(ddr_mst_tid_repeat, ddr_mst_link_valid)) else $error("ddr_mst_tid_repeat has xz error"); assert property (chk_xz_valid(ddr_mst_tid_index, ddr_mst_link_valid)) else $error("ddr_mst_tid_index has xz error"); //CHACK SIGNAL VALUE property chk_ddr_mst_tid_exist_value(ddr_mst_tid_exist); @(posedge assert_clk) disable iff(~after_rst) ((ddr_mst_tid_exist >= 0) && (ddr_mst_tid_exist <= 10)); endproperty chk_ddr_mst_tid_exist_value_assert: assert property (chk_ddr_mst_tid_exist_value(ddr_mst_tid_exist)) else $assertoff(0, chk_ddr_mst_tid_exist_value_assert); //CHACK SIGNAL END VALUE property chk_ddr_mst_tid_exist_end(ddr_mst_tid_exist); @(posedge assert_clk) disable iff(~after_rst | harness.end_of_check == 1'b0) ddr_mst_tid_exist == 10; endproperty chk_ddr_mst_tid_exist_end_assert: assert property (chk_ddr_mst_tid_exist_end(ddr_mst_tid_exist)) else $assertoff(0, chk_ddr_mst_tid_exist_end_assert); `endif //AUTO ADD AUTO_ASSERT_ON ``` 在Makefile或用例配置中增加compile宏定义: +define+AUTO_ASSERT_ON 如果涉及到end_of_check,请在harness中补充end_of_check信号,并在main_phase之后由环境主动将harness.end_of_check置1。 之后正常跑用例即可,发生断言问题时会自动报错。 ## 更新记录 ### 2025/06/24 现在可以在AS指令里指定关键词啦: ``` command! AS :execute '%! {script_path}/auto_assert.py % NZLCAT_ASSERT_EN' ``` 这样多个人可以共同调用一份脚本,无需各自使用本地副本。 生成的assertion里,如果和RTL的时钟不一致,也可以修改啦: ``` wire assert_clk = my_clk; wire assert_rst_n = my_rst_n; ``` 即使重新生成也不会覆盖这两句,省的每次都要去修改。 CHACK SIGNAL END VALUE也不需要依赖信号harness.end_of_check以及环境的操作了,把对应的逻辑直接封装到final block中,自动在仿真结束时调用: ``` //CHACK SIGNAL END VALUE final begin if(test_chan2 !== 2) $error("Error in end check: test_chan2 !== 2"); if(test_chan1 !== 5) $error("Error in end check: test_chan1 !== 5"); end ``` 还有之前使用宏的刷屏行为,现在也都调整为触发一次断言报错后,就自动关闭自身了: ``` chk_test_chan2_xz_assert: assert property (chk_xz_valid(test_chan2, data_in_valid)) else $assertoff(0, chk_test_chan2_xz_assert); chk_test_chan1_xz_assert: assert property (chk_xz(test_chan1)) else $assertoff(0, chk_test_chan1_xz_assert); ```