写在前面
在自己准备写一些简单的verilog教程之前,参考了许多资料----asic-world网站的Verilog教程即是其一。这套教程写得极好,奈何没有中文,在下只好斗胆翻译过来(加了自己的理解)分享给大家。
这是网站原文:Verilog Tutorial
这是系列导航:Verilog教程系列文章导航
介绍
如果你去看任何有关编程语言的书籍,就会发现它们的第一个例子几乎都是“Hello World”程序。一旦你学会了这个程序,就可以说你已经对这种语言入门了。
接下来我会先展示如何在 Verilog 语言中编写“hello world”程序,然后再介绍一个稍微复杂点的“计数器”程序。
Hello World
下面是一个在Verilog中打印“hello world”的程序。
//-----------------------------------------------------
// 这是我的第一个Verilog程序
// 设计名: hello_world
// 文件名: hello_world.v
// 功能 : 这个程序会打印'hello world'
// 作者 : Deepak
//-----------------------------------------------------
module hello_world ;
initial begin
$display ("Hello World by Deepak");
#10 $finish;
end
endmodule
Verilog 的程序都以保留字 “module” 开头,后面跟上设计者自定义的模块名 。在上面的示例中,第 8 行即包含“module hello_world”。
第 10 行包含initial块:它只在仿真开始后执行一次,即仿真时间 = 0 (0ns)时。initial块中有两条语句,它们被第 10 行的 begin 和第 13 行的 end 包围起来了。在 Verilog 中,如果一个块中有多行,则需要使用 begin 和 end。
模块以“endmodule”保留字作为结尾,即本例的第 15 行。
这是程序打印的结果:
Hello World By Deepak
计数器
这是要设计的计数器的框图:
它的预期设计规格是这样的:
- 4 位同步递增计数器
- 高电平有效的同步复位信号
- 高电平有效的使能信号
预期的计数器设计Verilog程序如下:
//-----------------------------------------------------
// 这是我的第二个Verilog程序
// 设计名: first_counter
// 文件名: first_counter.v
// 作者 : Deepak
// 功能 : 这是一个4位宽的递增计数器
// 高有效的同步复位;高有效的使能信号
//-----------------------------------------------------
module first_counter (
clock , // 时钟
reset , // 高有效的同步复位
enable , // 高有效的使能信号
counter_out // 计数器输出
);
//-------------Input Ports-----------------------------
input clock ;
input reset ;
input enable ;
//-------------Output Ports----------------------------
output [3:0] counter_out ;
//-------------Input ports Data Type-------------------
// 输入必须是wire变量
wire clock ;
wire reset ;
wire enable ;
//-------------Output Ports Data Type------------------
// 输出可以是wire或者reg
reg [3:0] counter_out ;
//------------Code Starts Here-------------------------
// 这个计数器是时钟上升沿有效的
always @ (posedge clock)
begin : COUNTER // Block名
//在每一个时钟上升沿,如果复位有效,则输出是4'b0000
if (reset == 1'b1) begin
counter_out <= #1 4'b0000;
end
//如果使能有效,则输出递增
else if (enable == 1'b1) begin
counter_out <= #1 counter_out + 1;
end
end
endmodule
任何数字电路,无论复杂与否都需要进行测试以验证其功能的正确性。
对于计数器,我们需要提供时钟和复位信号。一旦计数器不在复位状态,我们将启用输入切换到计数器,并检查波形以查看计数器是否在正确计数。
这是在 Verilog 的 testbench 中完成的。对于这个计数器,我们设计的testbench是这样的:
`include "first_counter.v"
module first_counter_tb();
//声明输入(reg)与输出(wire)
reg clock, reset, enable;
wire [3:0] counter_out;
//初始化所有变量
initial begin
$display ("time\t clk reset enable counter");
$monitor ("%g\t %b %b %b %b",
$time, clock, reset, enable, counter_out);
clock = 1; // 初始化时钟信号
reset = 0; // 初始化复位信号
enable = 0; // 初始化使能信号
#5 reset = 1; // 置位复位信号
#10 reset = 0; // 失效复位信号
#10 enable = 1; // 置位使能信号
#100 enable = 0; // 失效使能信号
#5 $finish; // 结束仿真
end
// 生成时钟信号
always begin
#5 clock = ~clock; // 每5个单位时钟翻转一次
end
// 被测模块
first_counter U_counter (
clock,
reset,
enable,
counter_out
);
endmodule
这个testbench由时钟发生器(clock gen)、复位控制(reset logic)、使能控制(enable logic)和监控/验证逻辑(monitor/checker)组成。
这是仿真完成后打印的结果,可以看到计数器的工作是符合预期的。
time clk reset enable counter
0 1 0 0 xxxx
5 0 1 0 xxxx
10 1 1 0 xxxx
11 1 1 0 0000
15 0 0 0 0000
20 1 0 0 0000
25 0 0 1 0000
30 1 0 1 0000
31 1 0 1 0001
35 0 0 1 0001
40 1 0 1 0001
41 1 0 1 0010
45 0 0 1 0010
50 1 0 1 0010
51 1 0 1 0011
55 0 0 1 0011
60 1 0 1 0011
61 1 0 1 0100
65 0 0 1 0100
70 1 0 1 0100
71 1 0 1 0101
75 0 0 1 0101
80 1 0 1 0101
81 1 0 1 0110
85 0 0 1 0110
90 1 0 1 0110
91 1 0 1 0111
95 0 0 1 0111
100 1 0 1 0111
101 1 0 1 1000
105 0 0 1 1000
110 1 0 1 1000
111 1 0 1 1001
115 0 0 1 1001
120 1 0 1 1001
121 1 0 1 1010
125 0 0 0 1010
看数据多少有点不够直观,那我们也可以直接来看仿真的波形:
从波形图可以看到:首先复位;复位完成后从0开始依次递增计数。
- 📣您有任何问题,都可以在评论区和我交流📃!
- 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net
- 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!