文章目录
一、verilog语法基础
1.基本语法
- Verilog程序由模块(module)组成,每个模块描述了电路中的一个功能单元。我们将从最简单的模块开始学习,逐渐深入了解Verilog的不同特性。
- 在Verilog中,语句以分号(;)结尾,模块由关键字module开始,后面跟着模块的名称和用括号括起来的端口列表。例如,下面是一个简单的Verilog模块的示例:
module AndGate(input a, b, output y);
assign y = a & b; // 逻辑与门
endmodule
- 在上面的示例中,模块名为AndGate,有两个输入端口a和b,一个输出端口y。使用assign关键字,我将y连接到了a和b的逻辑与操作(&)的结果。
- 在Verilog中,我也可以使用always块来描述组合逻辑和时序逻辑。组合逻辑是根据当前输入立即计算得出的逻辑,而时序逻辑则依赖于时钟信号和存储元件。下面是一个使用always块描述的时序逻辑的示例:
module Counter(input clk, output reg [3:0] count);
always @(posedge clk) begin
if (reset) begin
count <= 0; // 复位
end else begin
count <= count + 1; // 计数
end
end
endmodule
- 在上面的示例中,模块名为Counter,有一个时钟输入端口clk和一个四位的寄存器型输出端口count。通过always块和posedge关键字,我定义了在时钟上升沿触发时执行的行为。当reset信号为1时,计数器复位为0;否则,计数器每次递增1。
- Verilog还提供了一些用于组合逻辑操作的内置运算符,如逻辑与(&)、逻辑或(|)、逻辑非(!)和异或(^)等。此外,我还可以使用if语句、for循环等结构来实现更复杂的逻辑。
2.实现简单项目
3-8译码器
module decoder( //3-8译码器
input wire[2:0] a,//三位输入
output reg[7:0] b//八位输出
);
always @(*) begin//对寄存器类型进行赋值
case(a)
3'b000: b = 8'b1111_1110;
3'b001: b = 8'b1111_1101;
3'b010: b = 8'b1111_1011;
3'b011: b = 8'b1111_0111;
3'b100: b = 8'b1110_1111;
3'b101: b = 8'b1101_1111;
3'b110: b = 8'b1011_1111;
3'b111: b = 8'b0111_1111;
default:b = 8'b0000_0000;
endcase
end
endmodule
二、LED灯
1.流水灯
流水灯的效果。
模块描述
- 输入信号分别为时钟信号和复位信号,设计一个计时器。每0.2s改变四个led的状态,同一时刻下只能有一只led亮,其余的led灭。最后通过移位寄存器输出信号给四个led灯。
模块设计
module led (
input wire clk ,
input wire rst_n ,
output reg [3:0] led
);
parameter MAX_NUM = 26'd49_999_999;
reg [25:0] cnt ;
reg [1:0 ] state ;//保存四个状态
//1s计数器并亮灭led灯
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt <= 26'd0;
end
else if(cnt == MAX_NUM)begin
cnt <= 26'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
//状态的切换
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
state <= 2'd0;
end
else if(cnt == MAX_NUM)begin
state <= state + 1'd1;
end
else begin
state <= state;
end
end
//根据状态对led赋值
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
led <= 4'b0000;
end
else begin
case (state)
2'd0 : led <= 4'b0001;
2'd1 : led <= 4'b0010;
2'd2 : led <= 4'b0100;
2'd3 : led <= 4'b1000;
default : led <= 4'b0000;
endcase
end
end
endmodule
仿真文件
`timescale 1ns/1ns //单位精度
module led_tb ();
reg clk ; //50MHz时钟信号
reg rst_n ; //复位信号
wire [3:0] led ;
parameter MAX_NUM = 4'd9;
always #10 clk = ~clk;
initial begin
clk = 1'b0;
rst_n = 1'b0;//开始复位
#10;
rst_n = 1'b1;//结束复位
#1000;
$stop;//停止仿真
end
led #(.MAX_NUM (MAX_NUM))u_led(
.clk (clk),
.rst_n (rst_n),
.led (led)
);
endmodule
2.按键控制led灯
模块描述
- 利用按键控制led的变化,使用开发板上的四个按键控制四个LED灯,按下不同的按键时,四个LED灯显示不同效果。当没有按键按下时,四个led灯全灭;按下key0时,自右向左的流水灯;按下key1时,自左向右的流水灯;按下key2时,四个led灯同时闪烁;
按下key3时,四个led灯全亮。
模块设计
module key_led (
input wire clk ,
input wire rst_n ,
input wire [3:0] key ,
output reg [3:0] led
);
parameter MAX_NUM = 24'd9_999_999;
reg [1:0] state;//保存当前状态
reg [23:0] cnt;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt <= 24'd0;
end
else if(cnt == MAX_NUM)begin
cnt <= 24'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
state <= 2'd0;
end
else if(cnt == MAX_NUM)begin
state <= state + 2'd1;
end
else begin
state <= state;
end
end
//结合按键判断led的赋值
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
led <= 4'b0000;
end
else if(key[0] == 0)begin
case (state)
2'd0 : led <= 4'b0001;
2'd1 : led <= 4'b0010;
2'd2 : led <= 4'b0100;
2'd3 : led <= 4'b1000;
default : led <= 4'b0000;
endcase
end
else if(key[1] == 0)begin
case (state)
2'd0 : led <= 4'b1000;
2'd1 : led <= 4'b0100;
2'd2 : led <= 4'b0010;
2'd3 : led <= 4'b0001;
default : led <= 4'b0000;
endcase
end
else if(key[2] == 0)begin
case (state)
2'd0 : led <= 4'b1111;
2'd1 : led <= 4'b0000;
2'd2 : led <= 4'b1111;
2'd3 : led <= 4'b0000;
default : led <= 4'b0000;
endcase
end
else if(key[3] == 0)begin
led <= 4'b1111;
end
end
endmodule
仿真文件
`timescale 1ns/1ns //单位精度
module key_led_tb ();
reg clk;
reg rst_n;
reg [3:0] key;
wire [3:0] led;
parameter MAX_NUM = 9 ;
parameter CYCLE = 20;
always #(CYCLE/2) clk = ~clk;
initial begin
clk = 1'b0;
rst_n = 1'b0;//开始复位
#(CYCLE);
rst_n = 1'b1;//结束复位
key = 4'b1111;//没有按下按键
#(4*(MAX_NUM + 1)*CYCLE);
key = 4'b1110;//按下第一个按键
#(4*(MAX_NUM + 1)*CYCLE);
key = 4'b1101;//按下第二个按键
#(4*(MAX_NUM + 1)*CYCLE);
key = 4'b1011;//按下第三个按键
#(4*(MAX_NUM + 1)*CYCLE);
key = 4'b0111;//按下第四个按键
#(4*(MAX_NUM + 1)*CYCLE);
$stop;
end
key_led #(.MAX_NUM(MAX_NUM)) u_key_led (
.clk (clk) ,
.rst_n (rst_n) ,
.key (key) ,
.led (led)
);
endmodule
3.呼吸灯
呼吸灯是指灯光在微电脑的控制之下完成由亮到暗的逐渐变化,感觉好像是人在呼吸。其广泛应用于手机之上,并成为各大品牌新款手机的卖点之一,起到一个通知提醒的作用。
模块描述
使用开发板上的四个led灯实现1s间隔的呼吸灯。
模块设计
module breath_led (
input clk,
input rst_n,
output reg [3:0] led
);
parameter TIME_US = 6'd49;
parameter TIME_MS = 10'd999;
parameter TIME_S = 10'd999;
reg [5:0] cnt_us;
reg [9:0] cnt_ms;
reg [9:0] cnt_s;
reg flag;
wire add_cnt_us;//微秒计数器开始计数的标志
wire end_cnt_us;//微秒计数器结束计数的标志
wire add_cnt_ms;//毫秒计数器开始计数的标志
wire end_cnt_ms;//毫秒计数器结束计数的标志
wire add_cnt_s;//秒计数器开始计数的标志
wire end_cnt_s;//秒计数器结束计数的标志
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_us <= 6'd0;
end
else if(add_cnt_us)begin
if(end_cnt_us)begin
cnt_us <= 6'd0;
end
else begin
cnt_us <= cnt_us +1'd1;
end
end
else begin
cnt_us <= cnt_us;
end
end
assign add_cnt_us = 1'b1; //开始计数的条件
assign end_cnt_us = add_cnt_us && cnt_us == TIME_US;//结束计数的条件
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_ms <= 10'd0;
end
else if(add_cnt_ms)begin
if(end_cnt_ms)begin
cnt_ms <= 10'd0;
end
else begin
cnt_ms <= cnt_ms +1'd1;
end
end
else begin
cnt_ms <= cnt_ms;
end
end
assign add_cnt_ms = end_cnt_us;
assign end_cnt_ms = add_cnt_ms && cnt_ms == TIME_MS;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_s <= 10'd0;
end
else if(add_cnt_s)begin
if(end_cnt_s)begin
cnt_s <= 10'd0;
end
else begin
cnt_s <= cnt_s +1'd1;
end
end
else begin
cnt_s <= cnt_s;
end
end
assign add_cnt_s = end_cnt_ms;
assign end_cnt_s = add_cnt_s && cnt_s == TIME_S;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
flag <= 1'b0;
end
else if(end_cnt_s)begin
flag <= ~flag;
end
else begin
flag <= flag;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
led <= 4'b0000;
end
else if(!flag) begin
led <= (cnt_s >cnt_ms)?4'b1111:4'b0000;
end
else if(flag)begin
led <= (cnt_s > cnt_ms)?4'b0000:4'b1111;
end
else begin
led <= led;
end
end
endmodule
仿真文件
`timescale 1ns/1ns
module breath_led_tb ();
reg clk;
reg rst_n;
wire [3:0] led;
parameter CYCLE = 20;
parameter TIME_US = 5;
parameter TIME_MS = 10;
parameter TIME_S = 10;
always #(CYCLE/2) clk = ~clk;
initial begin
clk = 1'b0;
rst_n = 1'b0;
#(CYCLE);
rst_n = 1'b1;
#(2*(TIME_US + 1)*(TIME_MS + 1)*(TIME_S + 1)*CYCLE);
$stop;
end
breath_led #(
.TIME_US(TIME_US),
.TIME_MS(TIME_MS),
.TIME_S(TIME_S)
) u_breath_led(
.clk (clk),
.rst_n(rst_n),
.led (led)
);
endmodule
三、数码管
- 数码管的显示,数码管分七段数码管和八段数码管。七段和八段的区别在于,是否包括小数点DP(Digital Point)。本实验中使用的是数码管是8段数码管,每段是由led组成。通过控制每段led的亮灭,来控制数码管显示不同的数字和字母。
静态显示
- 在静态显示中,只考虑段选信号。在不同的时刻,各个位选信号保持不变,并根据真值表,选择要显示的数字或者字母。
1.模块描述
六个数码管同时间隔0.5s显示0-f。要求:使用一个顶层模块,调用计时器模块和数码管静态显示模块。
2.模块设计
time_count计数器模块
module time_count ( //计时0.5s,计满后输出高电平
input clk,
input rst_n,
output reg flag
);
parameter MAX_NUM = 26'd24_999_999;
reg [25:0] cnt;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt <= 26'd0;
flag <= 1'b0;
end
else if(cnt == MAX_NUM)begin
cnt <= 26'd0;
flag <= 1'b1;
end
else begin
cnt <= cnt + 1'd1;
flag <= 1'b0;
end
end
endmodule
seg_led_static数码管驱动模块
module seg_led_static (
input clk,
input rst_n,
input flag, //计时0.5s后输入的高电平
output reg [5:0] sel, //六位位选信号
output reg [7:0] seg //八位段选信号
);
reg [3:0] num; //保存当前数码管要显示的数字
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
sel <= 6'b111_111;
end
else begin
sel <= 6'b000_000;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
num <= 4'h0;
end
else if(flag)begin
num <= num +1'h1;
end
else begin
num <= num;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
seg <= 8'b0;
end
else begin
case (num)
4'h0: seg <= 8'b1100_0000;//匹配到后参考共阳极真值表
4'h1: seg <= 8'b1111_1001;
4'h2: seg <= 8'b1010_0100;
4'h3: seg <= 8'b1011_0000;
4'h4: seg <= 8'b1001_1001;
4'h5: seg <= 8'b1001_0010;
4'h6: seg <= 8'b1000_0010;
4'h7: seg <= 8'b1111_1000;
4'h8: seg <= 8'b1000_0000;
4'h9: seg <= 8'b1001_0000;
4'ha: seg <= 8'b1000_1000;
4'hb: seg <= 8'b1000_0011;
4'hc: seg <= 8'b1100_0110;
4'hd: seg <= 8'b1010_0001;
4'he: seg <= 8'b1000_0110;
4'hf: seg <= 8'b1000_1110;
default :seg <= 8'b1100_0000;
endcase
end
end
endmodule
顶层模块
module top_seg_led_static (
input clk ,
input rst_n ,
output [5:0] sel ,
output [7:0] seg
);
parameter MAX_NUM = 26'd24_999_999;
wire flag_reg;
time_count #(.MAX_NUM(MAX_NUM)) u_time_count(
.clk (clk) ,
.rst_n (rst_n) ,
.flag (flag_reg)
);
seg_led_static u_seg_led_static(
.clk (clk) ,
.rst_n (rst_n) ,
.flag (flag_reg) ,
.sel (sel) ,
.seg (seg)
);
endmodule
3.仿真文件
`timescale 1ns/1ns
module top_seg_led_static_tb();
reg clk ;
reg rst_n ;
wire [5:0] sel ;
wire [7:0] seg ;
parameter CYCLE = 20;
parameter MAX_NUM = 9;
always #(CYCLE/2) clk = ~clk;//翻转时钟
initial begin
clk = 0 ;//时钟初始为0
rst_n = 0 ;//复位初始为0
#(CYCLE) ;//延迟20ns
rst_n = 1 ;//复位置1
#(16*(MAX_NUM + 1)*CYCLE);//显示0-f时间
$stop ;//停止
end
top_seg_led_static#(.MAX_NUM (MAX_NUM)) u_top_seg_led_static(
.clk (clk) ,//50MHz系统时钟
.rst_n (rst_n),//系统复位信号(低有效)
.sel (sel) ,//数码管位选
.seg (seg) //数码管段选
);
endmodule
四、总结
此次FPGA学习主要是对基础知识的理解与掌握,学习了led灯的一些操作,也对数码管的静态显示有了了解,对verilog语言的学习也更加深刻,收获很多。