时序逻辑——计数器
要求:用计数器作为示例,实现不同时序电路元件的fpga代码编写,用于自己后续开发的模块调用!!
模块代码
module led_flash(
clk,
reset_n,
led
);
input clk;
input reset_n;
output reg led;
reg [24:0] counter;
//此时,将counter和led的赋值集成在一个alway语句中
// always@(posedge clk or negedge reset_n)
// if(!reset_n) begin
// counter <= 0;
// led = 0;
// end
// else if (counter == 25000000-1) begin
// led <= !led;
// counter <=0;
// end
// else
// counter <= counter + 1'd1;
//建议将counter和led的赋值相关语句分开写,便于综合器生成更加高效、简洁的电路
//此时,将counter和led的赋值分散在两个alway语句中
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 = 0;
else if (counter == 25000000-1)
led <= !led;
endmodule
代码解析
-
一般描述复位时,使用Reset,而对于Reset_n则进一步指出了复位低电平有效,代码规范要求
-
Xilinx FPGA 的D触发器结构,决定其使用高电平复位能够节约一定的资源,为什么?
-
先确定端口,在确定位宽!
-
always@(posedge Clk or negedge Reset_n)
是对于时序的描述,posedge表示上升沿敏感,negedge表示下降沿敏感 -
阻塞赋值(b=a;)和非阻塞赋值(b<=a;)
-
阻塞赋值
- (b=a;) 赋值语句结束后,块才结束
- (b=a;) b的值在赋值语句执行完后立刻就改变的。
- (b=a;) 可能会产生意想不到的结果。
-
非阻塞赋值
- (b<=a;) 块结束后,才执行赋值语句
- (b<=a;) b的值并不是立刻就改变的。
- (b<=a;) 这是一种比较常用的赋值方法。(特别在编写可综合模块时)
-
使用
begin end
语句来描述语句块 -
第二版的代码,将
Led
和counter
分开在两个always语句块中,能够有利于综合器进行分析,从而实现更加精简高效的电路(在板载资源有限时更需要注意)
测试平台代码
`timescale 1ns/1ps
module led_flash_tb ();
reg clk;
reg reset_n;
wire led;
led_flash inst0(
.clk(clk),
.reset_n(reset_n),
.led(led)
);
initial begin
clk = 1;
forever #10 clk = !clk;
end
initial begin
reset_n = 0;
#201
reset_n = 1;
#2000000000
$stop;
end
endmodule
测试平台代码解析
-
wire型数据常用来表示用于以assign关键字指定的组合逻辑信号。Verilog程序模块中输入输出信号类型缺省时自动定义为wire型。wire型信号可以用作任何方程式的输入,也可以用作“assign”语句或实例元件的输出。
-
reg型,寄存器是数据储存单元的抽象。寄存器数据类型的关键字是reg.通过赋值语句可以改变寄存器储存的值,其作用与改变触发器储存的值相当。在“always”块内被赋值的每一个信号都必须定义成reg型。 reg类型数据的缺省初始值为不定值,x。
-
计数器的时间取值需要注意,例如计数4个周期,应该为
0-1,1-2,2-3,3-0
,总计4个周期,取值要落在3
上面 -
一般对时钟进行激励时,采用初始赋值,然后延迟反转的方式,尽量写在一个initial块中
-
常见使用复位信号进行激励控制,
reset_n=0
用于初始化各项输入值,reset_n=1
用于程序开启