2. VCS运行基础时序逻辑仿真

任务目标

本案例通过一个最小可运行的SystemVerilog设计+测试平台,演示VCS三个最常用编译选项的实际作用:-sverilog(启用SystemVerilog语法)、-debug_access+all(生成可交互调试的数据库)、-timescale=1ns/1ps(设置时间单位和精度)。完成编译、运行后,通过观察日志和调试数据库文件验证各选项生效。

环境依赖

需要已安装Synopsys VCS(本案例以2020版及以上语法为例)和Make工具。在终端执行以下命令前,确保vcs和simv在PATH中。

设计文件:counter.sv

module counter #(parameter WIDTH = 4) (
    input  logic             clk,
    input  logic             rst_n,
    output logic [WIDTH-1:0] count
);
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) count <= '0;
        else        count <= count + 1'b1;
    end
endmodule

测试平台:tb_top.sv

module tb_top;
    logic        clk = 0;
    logic        rst_n;
    logic [3:0]  count;
    int          error_cnt = 0;

    counter #(.WIDTH(4)) dut (.*);

    always #5 clk = ~clk;  // 10ns周期

    initial begin
        $timeformat(-9, 0, "ns", 6);
        rst_n = 0;
        repeat (2) @(posedge clk);
        rst_n = 1;

        // 等待16个时钟周期,观察计数器从0到15回绕
        repeat (16) @(posedge clk);

        // 检查最终值(16个周期后应回到0)
        if (count !== 4'b0000) begin
            $error("FAIL: count = %0d, expected 0", count);
            error_cnt++;
        end else begin
            $display("PASS: counter wrapped correctly");
        end

        // 打印采样到的中间值(用于人工核对)
        $display("Simulation finished. error_cnt = %0d", error_cnt);
        $finish;
    end

    // 在每个上升沿采样并打印计数值(仅前8个周期)
    always @(posedge clk) begin
        if (rst_n && (tb_top.dut.count < 8))
            $display("t=%0t count=%0d", $time, tb_top.dut.count);
    end
endmodule

编译与运行命令

# 1. 不使用任何选项编译(预期失败:无法识别logic/always_ff等SVG语法)
vcs -sverilog counter.sv tb_top.sv -o simv_noopt 2>&1 | head -20

# 2. 正确编译(使用-sverilog和-timescale)
vcs -sverilog -timescale=1ns/1ps -debug_access+all counter.sv tb_top.sv -o simv

# 3. 运行仿真
./simv

验证方法与判定标准

验证-sverilog的作用:执行第1条命令,若VCS报错(如syntax error或unexpected ),说明没有该选项时无法识别SystemVerilog关键字;第2条命令编译成功,证明-sverilog生效。

验证-timescale的作用:运行./simv后,观察打印的时间戳。由于设置了-timescale=1ns/1ps且代码中#5延迟,时钟周期应为10ns。若打印的t=值以ns为单位递增(如t=10ns、t=20ns),且每10ns变化一次,则说明时间单位正确。若未设置该选项,VCS默认timescale为1ns/1ns,结果相同;但若代码中使用了更精细的延迟(如#0.1),则精度差异会显现。本案例中可通过$timeformat输出确认单位。

验证-debug_access+all的作用:编译完成后,检查当前目录是否生成simv.daidir目录(内含调试数据库)。运行./simv后,再执行dve -vpd vcdplus.vpd(若需要波形)或使用verdi加载simv,若能打开并查看信号波形,则说明调试数据已生成。更简单的验证:编译时若不加-debug_access+all,则simv.daidir不存在或无法使用UCLI命令行调试;加上后,可在仿真运行时用./simv -ucli进入交互模式,输入get tb_top.dut.count查看当前值。

功能正确性:仿真日志应显示PASS: counter wrapped correctly且error_cnt = 0。同时打印的中间计数值应为0,1,2,...,7(在0-7范围内),时间戳间隔为10ns。

预期输出(确定性结果)

t=10ns count=0
t=20ns count=1
t=30ns count=2
t=40ns count=3
t=50ns count=4
t=60ns count=5
t=70ns count=6
t=80ns count=7
PASS: counter wrapped correctly
Simulation finished. error_cnt = 0

常见问题

  • 编译报错“Unknown type logic”:确认已加-sverilog选项。
  • 时间戳显示为x10ns而非10ns:检查是否在代码中使用了$timeformat,且编译时-timescale设置正确。
  • 无法生成调试数据库:确认编译命令中包含-debug_access+all,且当前目录可写。