FPGA巩固及新知识学习
该篇包括3-8译码器、计数器完成跑马灯、参数化设计、例化3-8译码器完成跑马灯。
3-8译码器
本科数电都学过三八译码器,他就是将三位的输入信号翻译位8位的输出信号。
真值表:
开始编写
创建项目
需要注意的几项是:
开始编程
点击create design sources;
点击create file-选择语言(verilog),文件名称与工程名一致;
最后点击finish即可。
编程:
程序如下:
module decoder_3_8(
a,
b,
c,
out
);
input a;
input b;
input c;
output [7:0]out;
reg [7:0]out;
//以always块描述的信号赋值,被赋值的对象必须定义为reg型
always@(*)begin //always@(a,b,c)
case({a,b,c})//{a,b,c}独立的三个信号变成了一个三位的信号:位拼接 此外,还有wire [3:0]d: assign d= {a,1'b0,b,c}使得第二位始终为0
3'b000:out = 8'b0000_0001;//b,d,h都是格式限定符,分别为二进制,十进制,十六进制 3'b101=3'd5 ; 8'b0000_1100=8'd12=8'hc
3'b001:out = 8'b0000_0010;
3'b010:out = 8'b0000_0100;
3'b011:out = 8'b0000_1000;
3'b100:out = 8'b0001_0000;
3'b101:out = 8'b0010_0000;
3'b110:out = 8'b0100_0000;
3'b111:out = 8'b1000_0000;
endcase
end
endmodule
分析综合
功能仿真
与创建功能源文件一样,不过点击add source后修改为创建仿真源文件
`timescale 1ns/1ns
module decoder_3_8_tb;
reg s_a;
reg s_b;
reg s_c;
wire [7:0]out;
decoder_3_8 decoder_3_8(
.a(s_a),
.b(s_b),
.c(s_c),
.out(out)
);
initial begin
s_a = 0;s_b = 0;s_c = 0;
#200;
s_a = 0;s_b = 0;s_c = 1;
#200;
s_a = 0;s_b = 1;s_c = 0;
#200;
s_a = 1;s_b = 0;s_c = 0;
#200;
s_a = 0;s_b = 1;s_c = 1;
#200;
s_a = 1;s_b = 0;s_c = 1;
#200;
s_a = 1;s_b = 1;s_c = 0;
#200;
s_a = 1;s_b = 1;s_c = 1;
#200;
$stop;
end
endmodule
分析综合,与上述内容一样
查看波形
布局布线和实际仿真略;
扳机调试
修改端口。
IMPLEMENTATION =>点击Open Implemented Design=>Layout=>I/O planing=>根据自己的板子设置端口=>ctrl+S保存=>Gennerate Bitstream=>open hardware manager=>open target
本文再次详细的介绍了整个项目的创建及调试过程,之后的项目会省略相关步骤。
时序逻辑设计之计数器
1.时序逻辑基本概念(相比于之前的三八译码器)
2.计数器基本概念,基本的四位加法器结构图
cnt <= cnt + 1
此图略:一个上升沿到来自加1.
3.设计一个一1秒频率闪烁的LED灯(亮和灭各500ms)
4.计数值与计数器的关系。
实现
50MHz 20ns:500ms->计数为25000000时
module led_flash(
Clk,
Reset_n,
Led
);
input Clk;
input Reset_n;
output reg Led;
reg[24:0]counter;
// always@(posedge Clk or negedge Reset_n)
// if(!Reset_n)begin
// counter <= 0 ; //<=是非阻塞赋值的意思
// Led <= 0;
// end
// else if(counter == 25000000)begin
// Led <= !Led;
// counter <= 0;
// end
// else
// counter <= counter + 1'd1; //也可以分开写,推荐分开写
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0 ; //<=是非阻塞赋值的意思
else if(counter == 25000000 - 1)
counter <= 0;
else
counter <= counter + 1'd1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <= 1;
else if(counter == 25000000 - 1) //0-1 1-2 2-3 3-0
Led <= !Led;
endmodule
====================================================
`timescale 1ns/1ns
module led_flash_tb;
reg Clk;
reg Reset_n;
wire Led;
led_flash led_flash(
.Clk(Clk),
.Reset_n(Reset_n),
.Led(Led)
);
initial Clk = 1;
always #10 Clk =!Clk;
initial begin
Reset_n = 0;
#201;
Reset_n = 1;
#2000000000;
$stop;
end
endmodule
时序逻辑设计之计数器(Verilog语法学习与应用)
计数器实验升级,设计4个LED灯以每个0.5s的速率循环闪烁(跑马灯)。
module led_run(
Clk,
Reset_n,
Led
);
input Clk;
input Reset_n;
output reg [3:0]Led;
reg [24:0]counter;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == 25'd24999999)
counter <= 0;
else
counter <= counter + 1'd1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <= 4'b0001;
else if(counter == 25'd24999999)begin
if(Led == 4'b1000)
Led <= 4'b0001;
else
Led <= Led << 1;
end
else
Led <= Led;
endmodule
==================================================
`timescale 1ns/1ns
module led_run_tb;
reg Clk;
reg Reset_n;
wire [3:0]Led;
led_run led_run(
.Clk(Clk),
.Reset_n(Reset_n),
.Led(Led)
);
initial Clk = 1;
always #10Clk = ~Clk;
initial begin
Reset_n = 0;
#201;
Reset_n = 1;
#2000000000;
$stop;
end
endmodule
改进
module led_run(
Clk,
Reset_n,
Led
);
input Clk;
input Reset_n;
output reg [3:0]Led;
reg [24:0]counter;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == 25'd24999999)
counter <= 0;
else
counter <= counter + 1'd1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <= 4'b0001;
else if(counter == 25'd24999999)begin
// if(Led == 4'b1000)
// Led <= 4'b0001;
// else
// Led <= Led << 1;
Led <= {Led[2:0],Led[3]};
end
else
Led <= Led;
endmodule
例化3-8译码器(模块中调用模块)
复制文件到相应目录下;
ADD sources添加已有文件即可。
修改代码为:
module led_run2(
Clk,
Reset_n,
Led
);
input Clk;
input Reset_n;
output [7:0]Led;/此处不需要再写reg,因为译码器中已有
reg [24:0]counter;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == 25'd24999)
counter <= 0;
else
counter <= counter + 1'd1;
reg [2:0]counter2;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter2 <= 0;
else if(counter == 25'd24999)
counter2 <= counter2 + 1'd1;
decoder_3_8 decoder_3_8(
.a(counter2[2]),
.b(counter2[1]),
.c(counter2[0]),
.out(Led)
); //此处
endmodule
参数化设计
我们在仿真时为了避免时间过长,通常会把时间缩小,但是在板机调试时又会把时间恢复,在忘记恢复时间的情况下会导致实际错误,因此这里提出参数化设计方法。
module led_run2(
Clk,
Reset_n,
Led
);
input Clk;
input Reset_n;
output [7:0]Led;
reg [24:0]counter;
parameter MCNT = 25'd24999999;//此处为修改的地方
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == MCNT)//此处为修改的地方
counter <= 0;
else
counter <= counter + 1'd1;
reg [2:0]counter2;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter2 <= 0;
else if(counter == MCNT) //此处为修改的地方
counter2 <= counter2 + 1'd1;
decoder_3_8 decoder_3_8(
.a(counter2[2]),
.b(counter2[1]),
.c(counter2[0]),
.out(Led)
);
endmodule
===================================
`timescale 1ns/1ns
module led_run_tb;
reg Clk;
reg Reset_n;
wire [3:0]Led;
led_run2 led_run_inst0(
.Clk(Clk),
.Reset_n(Reset_n),
.Led(Led)
);
defparam led_run_inst0.MCNT = 24999;
initial Clk = 1;
always #10Clk = ~Clk;
initial begin
Reset_n = 0;
#201;
Reset_n = 1;
#2000000000;
$stop;
end
endmodule
或者:
`timescale 1ns/1ns
module led_run_tb;
reg Clk;
reg Reset_n;
wire [3:0]Led;
led_run2
#(
.MCNT(2499)
)
led_run_inst0(
.Clk(Clk),
.Reset_n(Reset_n),
.Led(Led)
);
initial Clk = 1;
always #10Clk = ~Clk;
initial begin
Reset_n = 0;
#201;
Reset_n = 1;
#2000000000;
$stop;
end
endmodule
假如3-8译码器也有一个
parameter WIDTH = 4;
reg [WIDTH - 1 : 0]OUT;
那么在led_run2对译码器例化时
加上一个 defparam 名字.WIDTH = ?;也是可以的
参数化设计嵌套