4.3 Verilog练习(1)
以后所有和FPGA相关的开发环境都为Vivado
目录
练习一.简单的组合逻辑设计
这是一个可综合的数据比较器,很容易看出它的功能是比较数据a与数据b,如果两个数据相同,则给出结果1,否则给出结果0。在Verilog HDL中,描述组合逻辑时常使用assign结构。注意equal=(a==b)?1:0,这是一种在组合逻辑实现分支判断时常使用的格式。
// compare.v
`timescale 1ns / 1ns
/*
1. `timescale 1ns / 1ps,含义为:时延单位为1ns,时延精度为1ps。
2. 在编译过程中,`timescale会影响其后面所有模块中的时延值,直至遇到另一个`timescale指令或`resetall指令。
3. 当一个设计中的多个模块带有自身的`timescale编译指令时,模拟器将定位在所有模块的最小时延精度上,并且所有时延都相应地换算为最小时延精度。
*/
module compare(a,b,equal);
input a,b;
output equal;
assign equal=(a==b)?1:0;
endmodule
// simu_compare.v
`timescale 1ns / 1ns
module simu_compare;
reg a,b;
wire equal;
initial
begin
a=0;
b=0;
#100 a=0;b=1;
#100 a=1;b=1;
#100 a=1;b=0;
#100 $stop;
end
compare u1(.equal(equal),.a(a),.b(b));
endmodule
练习二. 简单时序逻辑电路的设计
在Verilog HDL中,相对于组合逻辑电路,时序逻辑电路也有规定的表述方式。在可综合的Verilog HDL模型,我们通常使用always块和 @(posedge clk)或 @(negedge clk)的结构来表述时序逻辑。下面是一个1/2分频器的可综合模型。
//half_clk.v
`timescale 1ns / 1ps
module half_clk(reset,clk_in,clk_out);
input reset;
input clk_in;
output clk_out;
reg clk_out;
always@(posedge clk_in)
begin
if(!reset) clk_out = 0;
else clk_out = ~clk_out;
end
endmodule
//simu_half_clk.v
`timescale 1ns / 1ps
`define clk_cycle 50
module simu_half_clk;
reg clk,reset;
wire clk_out;
always #`clk_cycle clk = ~clk;
initial
begin
clk = 0;
reset = 1;
#100 reset = 0;
#100 reset = 1;
#1000 $stop;
end
half_clk u1(.reset(reset),.clk_in(clk),.clk_out(clk_out));
endmodule
练习三. 利用条件语句实现较复杂的时序逻辑电路
与常用的高级程序语言一样,为了描述较为复杂的时序关系,Verilog HDL提供了条件语句供分支判断时使用。在可综合风格的Verilog HDL模型中常用的条件语句有if…else和case…endcase两种结构,用法和C程序语言中类似。两者相较,if…else用于不很复杂的分支关系,实际编写可综合风格的模块、特别是用状态机构成的模块时,更常用的是case…endcase风格的代码。这一节我们给的是有关if…else的范例,有关case…endcase结构的代码已后会经常用到。
下面给出的范例也是一个可综合风格的分频器,是将10M的时钟分频为500K的时钟。基本原理与1/2分频器是一样的,但是需要定义一个计数器,以便准确获得1/20分频
//fdivision.v
`timescale 1ns / 100ps
module fdivision(
input F10M,
output F500K
);
reg F500K;
reg [7:0] j;
initial
begin
F500K <= 0;
j = 0;
end
always @(posedge F10M)
if(j != 19)
j <= j+1;
else
begin
j <= 0;
F500K <= ~F500K;
end
endmodule
//fdivison_simu.v
`timescale 1ns / 100ps
`define clk_cycle 50
module fdivison_simu;
reg F10M_clk;
wire F500K_clk;
always #`clk_cycle F10M_clk = ~F10M_clk;
initial
begin
F10M_clk = 0;
end
fdivision fdivision1(.F10M(F10M_clk),.F500K(F500K_clk));
endmodule
仿真的时候试了几次发现结果都不对,runtime总是在1000ns就停了。更改simulation setting的runtime = 10000后 结果就对了
练习四. 设计时序逻辑时采用阻塞赋值与非阻塞赋值的区别
目的:1.明确掌握阻塞赋值与非阻塞赋值的概念和区别;2.了解阻塞赋值的使用情况。
阻塞赋值与非阻塞赋值,在教材中我们已经了解了它们之间在语法上的区别以及综合后所得到的电路结构上的区别。在always块中,阻塞赋值可以理解为赋值语句是顺序执行的,而非阻塞赋值可以理解为赋值语句是并发执行的。实际的时序逻辑设计中,一般的情况下非阻塞赋值语句被更多地使用,有时为了在同一周期实现相互关联的操作,也使用了阻塞赋值语句。(注意:在实现组合逻辑的assign结构中,无一例外地都必须采用阻塞赋值语句。
下例通过分别采用阻塞赋值语句和非阻塞赋值语句的两个看上去非常相似的两个模块blocking.v和non_blocking.v来阐明两者之间的区别。
// Module Name: blocking.v
`timescale 1ns / 1ps
module blocking(
input clk,
input [3:0] a,
output [3:0] b,c
);
reg [3:0] b,c;
always @(posedge clk)
begin
b = a;
c = b;
$display("Blocking: a = %d, b = %d, c = %d.",a,b,c);
end
endmodule
//noblocking.v
`timescale 1ns / 1ps
module noblocking(
input [3:0] a,
output [3:0] b,c,
input clk
);
reg [3:0] b,c;
always@(posedge clk)
begin
b <= a;
c <= b;
$display("Non_Blocking: a = %d, b = %d, c = %d.",a,b,c);
end
endmodule
//compare_blocking.v
`timescale 1ns / 1ps
module compare_blocking;
wire [3:0] b1,c1,b2,c2;
reg [3:0] a;
reg clk;
initial
begin
clk = 0;
forever #50 clk = ~clk;
end
initial
begin
a = 4'h3;
$display("____________________________");
# 100 a = 4'h7;
$display("____________________________");
# 100 a = 4'hf;
$display("____________________________");
# 100 a = 4'ha;
$display("____________________________");
# 100 a = 4'h2;
$display("____________________________");
# 100 $display("____________________________");
$stop;
end
noblocking noblocking(.clk(clk),.a(a),.b(b2),.c(c2));
blocking blocking(.clk(clk),.a(a),.b(b1),.c(c1));
endmodule
从下图的结果里也验证了之前的知识点。阻塞赋值和非阻塞赋值
a-->b1-->c1为阻塞赋值,把a的值赋给b1工作完成后,b1的值更新,再将b1的值赋给c1
a-->b2-->c2为非阻塞赋值,a的值赋给b2的同时 b2的值赋给c2,所以b2为a的新值 c2为b2的旧值。