05 06 Verilog基础语法与应用讲解
05. 1. 位操作
计数器实验升级,设计8个LED灯以每个0.5s的速率循环闪烁(跑马灯)
1.1 方法1:使用移位操作符<<来控制led灯的循环亮灭
设计代码
-
Verilog中,判断操作的时候不加位宽限定是可以的,比如if(counter == 24999999)或者if(counter == 25’d24999999)都可以,但是在赋值的时候要加,比如counter <= counter + 1’d1;(赋0可以不加)
-
[7:0] led,led有八个,所以用8'b0000_0001来代表八个led,使用移位操作符<<来控制led灯的循环亮灭。
module led_run(
clk,
rstn,
led
);
input clk;
input rstn;
output reg[7:0] led;
reg[25:0] counter;
always@(posedge clk or negedge rstn)
if(!rstn)
counter <= 0;
else if(counter == 24999999)
//else if(counter == 24999)
counter <= 0;
else
counter <= counter + 1'd1;
always@(posedge clk or negedge rstn)
if(!rstn)
led <= 8'b0000_0001;
else if(counter == 24999999)begin
//else if(counter == 24999)begin
if(led == 8'b1000_0000)
led <= 8'b0000_0001;
else
led <= led << 1;
end
else
led <= led;
endmodule
仿真代码:
`timescale 1ns/1ns
module led_run_tb();
reg clk;
reg rstn;
wire[7:0] led;
led_run led_run(
.clk(clk),
.rstn(rstn),
.led(led)
);
initial clk = 1;
always #10 clk = !clk;
initial begin
rstn = 0;
#201;
rstn = 1;
#4000000;
$stop;
end
endmodule
仿真波形:
1.2 方法2:利用位拼接{led[6:0], led[7]}实现循环移位
设计代码
该方法应用场景不多,了解即可。
module led_run_1(
clk,
rstn,
led
);
input clk;
input rstn;
output reg[7:0] led;
reg[25:0] counter;
always@(posedge clk or negedge rstn)
if(!rstn)
counter <= 0;
else if(counter == 24999999)
//else if(counter == 24999)
counter <= 0;
else
counter <= counter + 1'd1;
always@(posedge clk or negedge rstn)
if(!rstn)
led <= 8'b0000_0001;
else if(counter == 24999999)begin
//else if(counter == 24999)
led <= {led[6:0], led[7]};
else
led <= led;
endmodule
1.3 方法3:调用其他模块
设计代码
思路:用计数器产生8种状态,然后再把这8种状态拿去用三八译码器译码,就刚好能够去对应点亮8个led灯。
- 调用了底层模块3_8译码器后(3位输入[2:0],八位输出8'b0000_0001),顶层led_run_2的输出led的数据类型需要由reg变为wire形(或者不写wire)
- 模块的调用与tb里的调用方式一样,做好对应端口的连接即可。
module led_run_2(
clk,
rstn,
led
);
input clk;
input rstn;
output wire[7:0] led; //注意1
reg[25:0] counter;
always@(posedge clk or negedge rstn)
if(!rstn)
counter <= 0;
else if(counter == 24999999)
//else if(counter == 24999)
counter <= 0;
else
counter <= counter + 1'd1;
reg[2:0] counter2;
always@(posedge clk or negedge rstn)
if(!rstn)
counter2 <= 0;
else if(counter == 24999999)
//else if(counter == 24999)
counter2 <= counter2 + 1'd1;
decoder_3_8 decoder_3_8( //注意2
.a(counter2[2]),
.b(counter2[1]),
.c(counter2[0]),
.out(led)
);
endmodule
调用方法:
05. 2. 参数化设计
设计代码
- 将24999999用参数代替,好处1是增加代码复用性与可读性。好处2是我们可以在板级验证的时候使用24999999,在写tb做调试的时候将参数改为2499,节约仿真时间
module led_run(
clk,
rstn,
led
);
parameter MCNT = 25'd24999999; //注意1
input clk;
input rstn;
output reg[7:0] led;
reg[25:0] counter;
always@(posedge clk or negedge rstn)
if(!rstn)
counter <= 0;
else if(counter == MCNT)
//else if(counter == 24999)
counter <= 0;
else
counter <= counter + 1'd1;
always@(posedge clk or negedge rstn)
if(!rstn)
led <= 8'b0000_0001;
else if(counter == MCNT)begin
//else if(counter == 24999)begin
if(led == 8'b1000_0000)
led <= 8'b0000_0001;
else
led <= led << 1;
end
else
led <= led;
endmodule
仿真代码
- 写法一,修改例化后的模块参数,defparam led_run_inst.MCNT = 2499;
- 写法二,例化前修改module,led_run里的参数
`timescale 1ns/1ns
module led_run_tb();
reg clk;
reg rstn;
wire[7:0] led;
led_run led_run_inst(
.clk(clk),
.rstn(rstn),
.led(led)
);
defparam led_run_inst.MCNT = 2499; //写法一
initial clk = 1;
always #10 clk = !clk;
initial begin
rstn = 0;
#201;
rstn = 1;
#4000000;
$stop;
end
endmodule
`timescale 1ns/1ns
module led_run_tb();
reg clk;
reg rstn;
wire[7:0] led;
led_run //写法二
#(
.MCNT(2499)
)
led_run_inst(
.clk(clk),
.rstn(rstn),
.led(led)
);
initial clk = 1;
always #10 clk = !clk;
initial begin
rstn = 0;
#201;
rstn = 1;
#4000000;
$stop;
end
endmodule
仿真波形
06 使用参数化的设计实现模块的重用
让八个led灯分别以不同的频率闪烁
思路:使用八个led灯闪烁模块,为了简化,我们分别以0.1s,0.2s,0.3s,0.4s让四个led闪烁。
1.设计代码
- 先构建一个led闪烁的设计代码,再给与不同的计数参数例化4次即可
module led_run8(
clk,
rstn,
led
);
parameter MCNT = 24999999;
input clk;
input rstn;
output led;
reg[25:0] counter;
reg led;
always@(posedge clk or negedge rstn)
if(!rstn)
counter <= 0;
else if(counter == MCNT)
counter <= 0;
else
counter <= counter + 1'd1;
always@(posedge clk or negedge rstn)
if(!rstn)
led <= 0;
else if(counter == MCNT)
led <= !led;
endmodule
- 例化4个模块,使用参数化的设计实现模块的重用
module led_run8_test(
clk,
rstn,
led
);
input clk;
input rstn;
output wire[3:0] led;
led_run8 led_run8_inst0(
.clk(clk),
.rstn(rstn),
.led(led[0])
);
defparam led_run8_inst0.MCNT = 25'd2499999;
led_run8 led_run8_inst1(
.clk(clk),
.rstn(rstn),
.led(led[1])
);
defparam led_run8_inst1.MCNT = 25'd4999999;
led_run8 led_run8_inst2(
.clk(clk),
.rstn(rstn),
.led(led[2])
);
defparam led_run8_inst2.MCNT = 25'd7499999;
led_run8 led_run8_inst3(
.clk(clk),
.rstn(rstn),
.led(led[3])
);
defparam led_run8_inst3.MCNT = 25'd9999999;
endmodule
2.仿真代码
`timescale 1ns/1ns
module led_run8_test_tb();
reg clk;
reg rstn;
wire[3:0] led;
led_run8_test led_run8_test_inst(
.clk(clk),
.rstn(rstn),
.led(led)
);
initial clk = 1;
always #10 clk = !clk;
initial begin
rstn = 0;
#201;
rstn = 1;
#400000000;
$stop;
end
endmodule
3.仿真波形
4.布置引脚(通过代码来布置引脚)
到此布置引脚就布置好了,后续生成bit流文件,打开硬件管理器之后就可以板级验证了。