Modelsim自动化仿真脚本(TCL)——简单实例

目录

[1. Modelsim与TCL脚本的关系](#1. Modelsim与TCL脚本的关系)

2.实验文件

2.1设计文件

2.2仿真测试文件

[2.3. 脚本文件](#2.3. 脚本文件)

[3. 实验步骤](#3. 实验步骤)

[3.1. 创建文件夹](#3.1. 创建文件夹)

[3.2. 指定路径](#3.2. 指定路径)

[3.3. 创建工程](#3.3. 创建工程)

[3.4. 运行命令](#3.4. 运行命令)

[3.4. 实验效果](#3.4. 实验效果)


1. Modelsim与TCL脚本的关系

TCL(Tool Command Language)是一种脚本编程语言,由John Ousterhout在1988年开发。TCL是一种通用的、高级的、解释执行的脚本语言,它特别适合用于快速原型开发、测试、自动化任务以及GUI开发。TCL语言设计简单,易于学习和使用,它具有可扩展性,可以通过添加库来扩展其功能。

ModelSim是由Mentor Graphics(现在是Siemens EDA的一部分)开发的一款行业标准的硬件描述语言(HDL)仿真工具。它支持多种HDL语言,包括VHDL、Verilog和SystemVerilog,用于验证数字电路和系统的设计。

TCL脚本与ModelSim的关系在于,ModelSim提供了对TCL脚本的支持,使得用户可以使用TCL脚本来控制仿真环境、运行仿真、管理项目、处理结果等。通过编写TCL脚本,用户可以自动化ModelSim的许多操作,提高工作效率,实现复杂的测试流程,以及进行批量处理。例如,用户可以编写TCL脚本来自动化测试套件的执行,收集和分析仿真结果,甚至修改仿真参数并重新运行仿真,从而实现更加高效的验证流程。

2.实验文件

2.1. 设计文件

`timescale  1ns/1ns

module  complex_fsm(
    input   wire    sys_clk         ,   //系统时钟50MHz
    input   wire    sys_rst_n       ,   //全局复位
    input   wire    pi_money_one    ,   //投币1元
    input   wire    pi_money_half   ,   //投币0.5元
                    
    output  reg     po_money        ,   //po_money为1时表示找零
                                        //po_money为0时表示不找零
    output  reg     po_cola             //po_cola为1时出可乐
                                        //po_cola为0时不出可乐
);

//----------------------------------------------------------------------
//parameter define
//只有五种状态,使用独热码
parameter   IDLE     = 5'b00001;
parameter   HALF     = 5'b00010;
parameter   ONE      = 5'b00100;
parameter   ONE_HALF = 5'b01000;
parameter   TWO      = 5'b10000;

//wire  define
wire    [1:0]   pi_money;
//reg   define
reg     [4:0]   state;


//----------------------------------------------------------------------
//maincode
//pi_money:为了减少变量的个数,我们用位拼接把输入的两个1bit信号拼接成1个2bit信号。投币方式可以为:不投币(00)、投0.5元(01)、投1元(10),每次只投一个币
assign pi_money = {pi_money_one, pi_money_half};

//第一段状态机,描述当前状态state如何根据输入跳转到下一状态
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state <= IDLE;  //任何情况下只要按复位就回到初始状态
    else	case(state)
                IDLE    : if(pi_money == 2'b01)   //判断一种输入情况
                              state <= HALF;
                          else    if(pi_money == 2'b10)//判断另一种输入情况
                              state <= ONE;
                          else
                              state <= IDLE;
    
                HALF    : if(pi_money == 2'b01)
                              state <= ONE;
                          else    if(pi_money == 2'b10)
                              state <= ONE_HALF;
                          else
                              state <= HALF;
    
                ONE     : if(pi_money == 2'b01)
                              state <= ONE_HALF;
                          else    if(pi_money == 2'b10)
                              state <= TWO;
                          else
                              state <= ONE;
    
                ONE_HALF: if(pi_money == 2'b01)
                              state <= TWO;
                          else    if(pi_money == 2'b10)
                              state <= IDLE;
                          else
                              state <= ONE_HALF;
    
                TWO     : if((pi_money == 2'b01) || (pi_money == 2'b10))
                              state <= IDLE;
                          else
                              state <= TWO;
        //如果状态机跳转到编码的状态之外也回到初始状态
                default :       state <= IDLE;
            endcase

//第二段状态机,描述当前状态state和输入pi_money如何影响po_cola输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_cola <= 1'b0;
    else    if((state == TWO && pi_money == 2'b01) || (state == TWO && 
          pi_money == 2'b10) || (state == ONE_HALF && pi_money == 2'b10))
        po_cola <= 1'b1;
    else
        po_cola <= 1'b0;

//第二段状态机,描述当前状态state和输入pi_money如何影响po_money输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n ==	1'b0)
        po_money <= 1'b0;
    else if((state == TWO) && (pi_money == 2'b10))
        po_money <= 1'b1;
    else
        po_money <= 1'b0;

endmodule

2.2. 仿真测试文件

关于随机数可参考:Modelsim怎样在测试平台文件中快捷使用随机数?-CSDN博客

`timescale  1ns/1ns

module  tb_complex_fsm();

//----------------------------------------------------------------------
//reg   define
reg         sys_clk;
reg         sys_rst_n;
reg         pi_money_one;
reg         pi_money_half;
reg         random_data_gen;

//wire  define
wire        po_cola;
wire        po_money;
//----------------------------------------------------------------------
//初始化系统时钟、全局复位
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    #20
    sys_rst_n <= 1'b1;
end

//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50MHz
always  #10 sys_clk = ~sys_clk;

//random_data_gen:产生非负随机数0、1
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        random_data_gen <= 1'b0;
    else
        random_data_gen <= {$random} % 2;

//pi_money_one:模拟投入1元的情况
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        pi_money_one <= 1'b0;
    else
        pi_money_one <= random_data_gen;

//pi_money_half:模拟投入0.5元的情况
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        pi_money_half <= 1'b0;
    else
        pi_money_half <= ~random_data_gen;  //取反是因为一次只能投一个币,即pi_money_one和pi_money_half不能同时为1

//------------------------------------------------------------
//将RTL模块中的内部信号引入到Testbench模块中进行观察打印
wire    [4:0]   state    = complex_fsm_inst.state;
wire    [1:0]   pi_money = complex_fsm_inst.pi_money;

initial begin
    $timeformat(-9, 0, "ns", 6);
    $monitor("@time %t: pi_money_one=%b pi_money_half=%b pi_money=%b state=%b po_cola=%b po_money=%b", $time, pi_money_one, pi_money_half, pi_money, state, po_cola, po_money);
end
//------------------------------------------------------------
complex_fsm complex_fsm_inst(
    .sys_clk        (sys_clk        ),  //input     sys_clk
    .sys_rst_n      (sys_rst_n      ),  //input     sys_rst_n
    .pi_money_one   (pi_money_one   ),  //input     pi_money_one
    .pi_money_half  (pi_money_half  ),  //input     pi_money_half
                    
    .po_cola        (po_cola        ),  //output    po_money
    .po_money       (po_money       )   //output    po_cola
);  

endmodule

2.3. 脚本文件

  1. `quit -sim` :退出仿真,如果当前modelsim中具有仿真运行,可以将其中止并退出仿真界面。

2)`.main clear ` :清除modelsim Transcript中的内容

3)`vlog "../src*.v" `:vlog为编译的意思,则../src/*.v代表路径。因为FPGA设计文件在src中,所以需要用../退到上一级文件夹,再选择src/*.v(即该文件夹下的所有.v文件)。如果不需要全部编译,也可以指定文件(vlog "../src/complex_fsm.v)。

  1. `vsim`:这是ModelSim/QuestaSim的仿真命令,用于启动仿真。

5)`-t ns`这个选项指定了仿真的时间单位。在这个例子中,`-t ns`表示时间单位是纳秒(nanoseconds)。

6)`-voptargs=+acc`:这个选项用于传递参数给仿真优化工具(vopt)。`+acc`是一个特定的参数,它启用了额外的信号可访问性,这通常用于波形查看或交互式调试。`+acc`选项会使得更多的信号在仿真中可见,这可能会影响仿真的性能,因为需要跟踪更多的信号。

7)`work.tb_complex_fsm`: 这部分指定了要仿真的测试台(testbench)。`work`是ModelSim/QuestaSim中默认的库名,`tb_complex_fsm`是测试台的名字。在ModelSim/QuestaSim中,所有编译好的设计和测试台都存储在一个名为"work"的库中,除非你在编译时指定了其他的库名。

8)`add wave -driver {tb_complex_fsm}`:这条命令在波形显示中添加一个分隔线,用以区分不同的信号组。`{tb_complex_fsm}`是分隔线的标签。

9)`add wave tb_complex_fsm/*`: 这条命令将`tb_complex_fsm`测试台中所有的信号添加到波形显示中。``是一个通配符,表示添加所有信号。

10)`add wave -divider {complex_fsm}`: 这条命令在波形显示中添加一个分隔线,用以区分不同的信号组。`{complex_fsm}`是分隔线的标签。

11)`add wave -radix decimal tb_complex_fsm/complex_fsm_inst/*`: 这条命令将`tb_complex_fsm/complex_fsm_inst/`下的所有信号添加到波形显示中,并且设置这些信号的显示基数为十进制。这意味着这些信号的值将以十进制形式显示,而不是默认的二进制或十六进制。

12)`virtual function {(vir_new_signal)tb_complex_fsm/complex_fsm_inst/state} new_state`:这条命令创建了一个虚拟信号`new_state`,它是基于`tb_complex_fsm/complex_fsm_inst/state`信号的函数。这里的`vir_new_signal`可能是一个自定义的函数,用于处理`state`信号并生成`new_state`虚拟信号。

13)`add wave -color red -itemcolor blue tb_complex_fsm/complex_fsm_inst/new_state`: 这条命令将虚拟信号`new_state`添加到波形显示中,并设置该信号的波形颜色为红色,信号项的颜色为蓝色。

#---------------------------------------------------------------------
#基础配置
quit -sim
.main clear

#---------------------------------------------------------------------
#包含文件
vlog "../src/*.v"
vlog "*.v"
#开始仿真
vsim -t ns -voptargs=+acc work.tb_complex_fsm

#---------------------------------------------------------------------
# 添加虚拟类型
virtual    type {
{01 IDLE}
{02 HALF}
{04 ONE}
{08 ONE_HALF}
{16 TWO}
} vir_new_signal

#----------------------------------------------------------------------
#添加波形
add wave -divider {tb_complex_fsm} 

add wave tb_complex_fsm/*
add wave -divider {complex_fsm_inst}
add wave -radix decimal tb_complex_fsm/complex_fsm_inst/* 
virtual    function {(vir_new_signal)tb_complex_fsm/complex_fsm_inst/state} new_state
add wave  -color red  -itemcolor blue  tb_complex_fsm/complex_fsm_inst/new_state

configure wave -timelineunits us
#----------------------------------------------------------------------
#运行
run 10us

3. 实验步骤

3.1. 创建文件夹

按照如图方式创建modelsim_test、src、sim三个文件夹,并将FPGA设计文件和测试平台文件和自动化脚本放入如图文件夹

3.2. 指定路径

打开软件更改Change Directory路径为3.1.的sim文件夹

3.3. 创建工程

3.4. 运行命令

3.4. 实验效果

相关推荐
fei_sun12 小时前
【Verilog】第一章作业
fpga开发·verilog
深圳市雷龙发展有限公司longsto13 小时前
基于FPGA(现场可编程门阵列)的SD NAND图片显示系统是一个复杂的项目,它涉及硬件设计、FPGA编程、SD卡接口、NAND闪存控制以及图像显示等多个方面
fpga开发
坐公交也用券14 小时前
使用Python3实现Gitee码云自动化发布
运维·gitee·自动化
施努卡机器视觉16 小时前
电解车间铜业机器人剥片技术是现代铜冶炼过程中自动化和智能化的重要体现
运维·机器人·自动化
徐浪老师16 小时前
深入实践 Shell 脚本编程:高效自动化操作指南
运维·chrome·自动化
King's King16 小时前
自动化立体仓库:详解
运维·自动化
东隆科技16 小时前
晶圆测试中自动化上下料的重要性与应用
运维·自动化
9527华安18 小时前
FPGA实现PCIE3.0视频采集转10G万兆UDP网络输出,基于XDMA+GTH架构,提供工程源码和技术支持
网络·fpga开发·udp·音视频·xdma·pcie3.0·万兆网
able陈18 小时前
为什么verilog中递归函数需要定义为automatic?
fpga开发
fei_sun18 小时前
【Verilog】第二章作业
fpga开发·verilog