轻量级Verilog仿真环境搭建iVerilog+GTKWave

Posted on Mar 25, 2021

由于体系结构课程要用到verilog来实现MIPSCPU,之前上组成原理课程与数字逻辑课程用过ModelSimVivado,这两软件实在是太笨重了,同学们基本都是只拿来做做仿真运行就行了,ModelSim软件bug巨多,而Vivado编译运行速度实在是一言难尽。所以在网上找了一些轻量级Verilog仿真运行方案,Icarus Verilog + GTKWave似乎是一个不错的选择

iVerilog开源、支持各平台,有语法检查,仿真,可以满足课程需求。

这里给出Windows环境搭建,并给出我个人的食用方案。

iVerilog 安装

iverlog,我个人下载的是iverilog-v11-20201123-x64_setup.exe [18.1MB]即可(不知道为什么2021版的突然突然44M,不过比 Vivado 10个G总舒服多了)。一路点下去,记得选中设置环境变量。

将会安装好以下 3 个工具:

  • iverlog:编译 verilog代码
  • vvp:将可执行文件生成仿真波形
  • gtkwave:打开仿真波形文件,显示波形

安装好后,可以在windows命令行中输入iverilog, vvp, gtkwave检查环境变量是否设置成功。

食用方式

iverilog可以指定一些参数编译运行代码,下面给出我个人的食用方式,以下是文件目录树,verilog代码参考《自己动手写CPU》第三章 demo代码:

├── pc_reg.v(PC寄存器实现)
├── rom.v(指令存储器实现)
├── rom.data(指令存储器数据)
├── inst_fetch.v(综合模块实现)
├── inst_fetch_tb.v(测试模块实现)
├── run.bat(编译运行脚本)
├── rmfile.bat(删除输出文件脚本)
  • pc_reg.v
module pc_reg(input wire clk,
              input wire rst,
              output reg[5:0] pc,
              output reg ce); // 指令存储器使能信号
    
    always @(posedge clk) begin
        if (rst == 1'b1) begin
            ce <= 1'b0; // 在复位信号有效的时候,指令存储器使能信号无效
        end else begin
            ce <= 1'b1; // 复位信号无效的时候,指令存储器使能信号有效
        end
    end
    
    always @(posedge clk) begin
        if (ce == 1'b0) begin
            pc <= 6'h00;    // 指令存储器使能信号有效时,pc保持为0
        end else begin
            pc <= pc + 1'b1;
        end
    end

endmodule
  • rom.v
module rom (input wire ce,
            input wire[5:0] addr,   // 要读取的指令地址
            output reg[31:0] inst); // 读出的指令
    reg[31:0] rom[63:0];

    initial begin
        $readmemh("rom.data", rom);
    end

    always @(*) begin
        if (ce == 1'b0) begin
            inst <= 32'h0;
        end else begin
            inst <= rom[addr];
        end
    end
endmodule
  • rom.data
01010101
02020202
03030303
04040404
  • inst_fetch.v
module inst_fetch (input wire clk,
                   input wire rst,
                   output wire[31:0] inst_o);
    wire[5:0] pc;
    wire rom_ce;
    
    // PC 模块的实例化
    pc_reg pc_reg0(.clk(clk), .rst(rst), .pc(pc), .ce(rom_ce));
    
    // 指令存储器ROM的实例化
    rom rom0(.ce(rom_ce), .addr(pc), .inst(inst_o));
    
endmodule
  • inst_fetch_tb.v:注意所有的测试文件中都要加这段代码,以便iVerilog编译。
/*iverilog */
initial
begin            
    $dumpfile("wave.vcd");        //生成的vcd文件名称
    $dumpvars(0, inst_fetch_tb);    //tb模块名称
end
/*iverilog */
module inst_fetch_tb;

    /*iverilog */
    initial
    begin            
        $dumpfile("wave.vcd");        //生成的vcd文件名称
        $dumpvars(0, inst_fetch_tb);    //tb模块名称
    end
    /*iverilog */

    /*
     * 第一段:数据说明
     */
    reg clk;    // 激励信号 clk
    reg rst;    // 激励信号 rst
    wire[31:0] inst; // 显示信号 inst, 取出的指令

    /*
     * 第二段:激励向量定义
     */
    // 定义 clk,每隔 10 个时间单位,翻转,即 50 MHz的时钟
    // 仿真的时候,一个时间单位默认是 1ns
    initial begin
        clk = 1'b0;
        forever begin
            #10 clk = ~clk;
        end
    end

    // 定义 rst 信号,最开始为 1,复位有效,过了 195个时间单位
    // 设置 rst 信号的值为0, 复位信号无效,复位结束,再运行1000ns,暂停仿真
    initial begin
        rst = 1'b1;
        #195 rst = 1'b0;
        #1000 $stop;
    end


    /*
     * 第三段:待测试模块实例化
     */
    inst_fetch inst_fetch0(
        .clk(clk),
        .rst(rst),
        .inst_o(inst)
    );
    
endmodule
  • run.bat: 这是一个简单的编译运行批处理脚本,这三个工具还有各种参数,后续再探索
% Start compiling %
iverilog -o wave %*
% Generating wave file %
vvp -n wave -lxt2
% Opening wave file %
gtkwave wave.vcd
  • rmfile.bat: 删除生成的文件,方便重新编译
del wave *.vcd

编译运行仿真

命令行输入.\run.bat file1 file2即可,这里是.\run.bat .\inst_fetch_tb.v .\inst_fetch.v .\pc_reg.v .\rom.v

运行结果

Insert=>Signals选中信号,即可显示波形,鼠标滚轮移动波形时序位置。

后记

目前发现在我的机器上,verilog的数组不能开太大了,大概只能承受 4K 左右的大小,否则编译会卡住

Reference