Verilog和FPGA的自学笔记3——仿真文件Testbench的编写

记录一下学习仿真文件的vlog

仿真是FPGA开发的重要环节,帮助我们正式下载数据流之前发现问题。仿真文件的编写尤为重要,这篇博客就聊聊关于仿真文件的基本写法。

同样,先瞟一眼我上篇博客中点亮led.v的仿真文件(想看上篇博客的欢迎拖到文章底部的超链接~~~)

`timescale 1ns / 1ns        //仿真单位/仿真精度

module tb_led();

reg           key;
wire          led;

//信号初始化
initial begin
    key <= 1'b1;   //按键上电默认高电平
//key信号变化
    #200           //延迟200ns
    key <= 1'b1;   //按键没有被按下
    #1000  
    key <= 1'b0;   //按键被按下
    #600  
    key <= 1'b1;
    #1000  
    key <= 1'b0;
end

//例化led模块
led  u_led(
    .key          (key),
    .led          (led)
    );

endmodule

注释倒是写的挺详细的 看了跟没看一样 啥也看不懂!!!

看不懂没关系~ 且听细细道来!
看着挺多,不过仿真文件只有两部分组成:

  1. 时间尺度指令(Time Scale Directive)
  2. 仿真模块(Testbench)

你没发现整个程序除了`timescale 1ns / 1ns(时间尺度指令)就一个module tb_led()模块吗?
再次体现Verilog的模块化编程模式:连仿真文件都得写成模块的形式。
确实,不过……tb_led里面都是坨啥??

别急,先说说时间尺度指令。

时间尺度指令

其语法结构如下:

`timescale <时间单位> / <时间精度>

尤其需要注意的是最前面的标点符号,不是单引号‘

这玩意在哪找捏?按键如下:
在这里插入图片描述这个
尤其注意!!
时间单位是之后写的仿真延时的单位,比如这个:

`timescale 1ns/1ns
……
key <= 1'b1;
#100  
key <= 1'b0

就代表key <= 1’b1key <= 1’b0之间间隔100ns(仿真时候的,跟真实硬件没关系)
如果写成这个样子:

`timescale 10ns/1ns
……
key <= 1'b1;
#100      //就代表1000ns了
key <= 1'b0

key <= 1’b1key <= 1’b0之间就间隔1000ns了。

“/”后面的是时间精度,比如这样写:

`timescale 1ns/1ps    //时间精度改成ps
……
key <= 1'b1;
#1.001  
key <= 1'b0

但这样就不行了:

`timescale 1ns/1ns    //时间精度改回ns
……
key <= 1'b1;
#1.001  //错误,因为时间精度最小就是ns,无法再精确了!
key <= 1'b0

仿真模块的编写

最重要的仿真模块来啦!
仿真模块里的主要三部分:

  1. 信号类型定义
  2. 信号初始化
  3. 模块例化

要讲Verilog的信号类型,就不得不说说Verilog的数据类型了。
类比C++的int、long、char、string.etc(中英夹杂~),Verilog中数据类型主要以下三种:

Verilog的数据类型作用
reg寄存器类型
wire线网类型
parameter参数类型

说白了reg对应FPGA里的D触发器和寄存器(触发器级联),wire用来连接查找表和触发器(或者别的什么)。
区别在于:reg可以存储数据,而wire不能
但wire可以承载数据
注意“承载”和“存储”不是一个意思 (你品,你仔细品~)

那参数是干啥的?答:存个常量。
个人理解,作用类似与嵌入式开发的宏定义。(就是给数字起个字母的名字)

举个栗子:

parameter  COUNT_MAX = 25_000_000; //但这是常量不能运算

相信聪明的你一眼就明白了。

通常,我们会把输入信号(比如点亮led中的key)定义为寄存器类型reg,输出信号(比如led)定义为线网类型wire。

信号初始化

这里介绍一个新语句initial。 此乃专用于初始化操作仿真测试场景之语句。
再介绍两个关键字begin和end。
后面这俩完全等于C++的花括号“{}”。至于为啥Verilog不用花括号,咱也不知道,咱也不敢问……
如果后面只有一行,则不用写begin和end。如下:

initial
key = 1'b0;   //虽然对于intial啥用没有

行数多了就得写:
(注意是行数不是语句!!)

initial begin
key = 1'b0;     //信号初始化区域
#100
key = 1'b1;
#100
key = 1'b0;
end

begin不一定要和initial写在一行,不过业界都这么写,咱也随波逐流吧哈哈~

通常initial下面紧跟着的地方为信号初始化区域,还没写延时语句(#100)之前最好把每个信号初始化,否则将在仿真软件(Modelsim)里显示为红色……

同样在这里给大家提个醒啊,类似module-endmodule,写完begin之后直接回车两行写end,免得后面忘了。

中间的#100是延时语句,语法就是一个#再加一个数字,没啥好说的。

(不明白“1’b0”啥意思的欢迎浏览我下篇博客~~)

注意initial语句只用在仿真文件,不用于RTL代码!!一定注意!!

模块例化

有个博主方法很好,打开RTL代码(就点亮led的代码),把下面这个模块:

module led(
    input key,
    output led
);

从led开始,到;结束,复制然后粘贴到仿真文件,删掉端口类型,最后长成这个德行:

led(
    key,
    led
);

给例化模块起个名字(我习惯加“u_”跟原子哥学的哈哈 ),每个端口前加点“.”,后面加括号:

led u_led(
    .key(),
    .led()
);

里面写要连接的信号(就是你上面操作的信号):

led u_led(
    .key(key),
    .led(key)
);

我知道这里大家会有些糊涂,给大家解释下哈:

“.”之后的key对应led.v文件module里咱写的"key",而括号里的key是咱仿真文件里定义的key。

对啦,就是initial里操作的那些"key"。

如果你“.”后面不写key,写了点别的(比如k),编译器并不会报错,但仿真会报错,说没找到这没找到那的……

关于模块例化,其实还有一种简单的方法,还是上面关于led模块的例化,可以像下面这样写:

led u_led(key,led);

它的顺序对应你led.v文件里module定义时的顺序。所以我个人并不推荐这样写,不如第一种清晰明了(本人就踩过循序的坑……)

仿真文件就搞定啦!

我个人喜欢用Modelsim这个仿真软件,仿真波形如下:
Modelsim里的波形图
期待和大家早日学会Verilog和FPGA!!

如果有不明白或错误之处,也希望大家在评论区给出,帮助大家的同时也能再次提升自己对于FPGA和Verilog的理解,感谢大家!!

系列链接:
上一篇:Verilog和FPGA的自学笔记2——点亮LED
下一篇:Verilog和FPGA的自学笔记4——多路选择器(always语句)

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值