FPGA学习笔记(六)—— 时序逻辑电路设计

例1.四位计数器(同步使能、异步复位)
// Module Name: counter_4bit
// Description: 4bit异步复位同步使能二进制计数器 module counter_4bit(
input clk, //系统时钟信号
input rst, //系统复位按键
input en, //计数器使能端
output reg [:]q //计数器计数值输出
);
//同步使能,异步复位
always@(posedge clk,posedge rst)
if(rst)
q <= ;
else if(en)
q <= q + 'b1; //计数器加一
endmodule
testbench测试代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: counter_4bit_tb
// Description: 4bit计数器模块测试文件
//////////////////////////////////////////////////////////////////////////////////
`define clk_period //宏定义时钟周期
module counter_4bit_tb(); reg clk; //用于产生时钟信号
reg rst; //用于产生复位信号
reg en; //用于产生使能信号
wire [:]q; //计数器计数值输出 //例化测试模块
counter_4bit counter_4bit_test(
.clk(clk), //系统时钟信号
.rst(rst), //系统复位按键
.en(en), //计数器使能端
.q(q) //计数器计数值输出
); //开始测试
//生成时钟信号
initial clk = ;
always #(`clk_period/) clk = ~clk; //clk每5ns翻转一次,产生100M时钟信号 initial begin
rst = ; en = ;
#(`clk_period * + );
rst = ;
#(`clk_period * );
en = ;
#(`clk_period *); //因为4bit计数16个clk就清零,所以延时20个时钟周期
$stop;
end
endmodule
测试结果如下:
综合的电路图如下:
计数器是我们设计的第一个时序逻辑电路,也是最基本、最重要的时序逻辑电路,由图中可以看到一个计数器由加法器和D触发器组成;
特别要注意的一点,在用verilog描述计数寄存器加一的时候,我们没有先写一个加法器,然后例化调用,而是直接采用 q <= q + 1'b1这样描述加法器,这点在综合出的电路图也可以看出,加法器不再是门电路的组合,而是用一个圆圈替代,这时,数字逻辑设计的思想就又抽象了一层,不再是门电路的组合,而是这些具有逻辑功能的小方块的组合,所以,抽象的思想在数字逻辑设计中至关重要;
在编写testbench仿真测试的时候,我们也不再像测试组合逻辑电路那样利用穷举法测试功能,而是利用时钟测试,比如测试4bit的加法器,一要测试它能不能在每个时钟沿加一,而要测试它计满时可不可以自动清零;所以,在测试时序逻辑电路的时候要准确把握clk时钟信号;
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Create Date: 2018/05/22 20:32:26
// Module Name: flashled
// Description: 产生一个时钟信号,让LED灯按1hz的频率闪烁
//////////////////////////////////////////////////////////////////////////////////
//`define SIMULATION /*** 仿真时保留,板级验证时注释 ***/
module flashled(
input clk, //时钟信号输入
input rst, //复位信号输入
input en, //使能信号输入
output [:]led //led信号输出
); `ifdef SIMULATION //仿真情况下
parameter CNT_MAX = 'd49;
`else //板级验证情况下
parameter CNT_MAX = 'd49_999_999;
`endif reg [:]cnt; //最大计数值49_999_999
reg [:]led_clk; //输出驱动led的1hz信号 //计数器功能描述
always@(posedge clk,posedge rst)
if(rst)
cnt <= ;
else if(en)begin
if(cnt == CNT_MAX)
cnt <= ;
else
cnt <= cnt + 'b1;
end //产生1hz信号
always@(posedge clk,posedge rst)
if(rst)
led_clk <= ;
else if(cnt == CNT_MAX)
led_clk <= ~led_clk; //每计满50_000_000个时钟周期(500ms),输出信号翻转一下
else
led_clk <= led_clk; //经1hz信号输出到led
assign led = led_clk; endmodule
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: flashled_tb
// Description: flashled模块测试文件
//////////////////////////////////////////////////////////////////////////////////
`define clk_period //100M时钟信号
module flashled_tb();
reg clk; //用于产生时钟信号
reg rst; //用于产生复位信号
reg en; //用于产生使能信号
wire [:]led; //观察led输出 //例化测试模块
flashled flashled_test(
.clk(clk), //时钟信号输入
.rst(rst), //复位信号输入
.en(en), //使能信号输入
.led(led) //led信号输出
); //产生时钟信号
initial clk = ;
always #(`clk_period/) clk = ~clk; //开始测试
initial begin
rst = ; //复位;
en = ;
#(`clk_period * );
rst = ;
#(`clk_period * );
en = ; //使能
#(`clk_period * * ); //仿真情况下计数器每计50次翻转,所以应该延时50 * 5个时钟周次观察翻转情况
$stop;
end endmodule

综合电路图如下:
在编写testbench测试分频器模块时,由于计数器计数值为49_999_999 ,所以花费长时间,因为只需要测试功能,看模块计到设定值后会不会翻转信号,所以这样就是在浪费时间;
为了加快仿真速度,可以在仿真时将最大值改为49,这样就非常快,为了方便,可以在verilog中使用条件编译:
`ifdef SIMULATION //仿真情况下
parameter CNT_MAX = 'd49;
`else //板级验证情况下
parameter CNT_MAX = 'd49_999_999;
`endif
//1.取某一位直接操作
wire [:]m;
assign m = out[:]; //2.循环移位(移位寄存器)
reg [:] shift_a;
always@(posedge clk)
shift_a <= {shifta[],shift[:]}; reg [:] shift_a;
wire data;
always@(posedge clk)
shift_a <= {data,shift[:]}; reg [:] shift_a;
wire data;
always@(posedge clk)
shift_a <= {shift[:],data}; //3、拼接
wire [:]x;
wire [:]y;
wire [:]z;
wire [:]n; assign z = {x,y};
assign n = {x,{x}};
verilog设计代码如下(计数器部分和分频器相同):
//移位寄存器功能描述
always@(posedge clk,posedge rst)
if(rst)
led_temp <= 'h0001;
else if(cnt == CNT_MAX)
led_temp <= {led_temp[:],led_temp[]}; //循环左移
else
led_temp <= led_temp;
//输出到led
assign led = led_temp;
testbench仿真测试文件和分频器相同;
测试结果如下:
综合出的电路图如下:
例4.BCD计数器
计数器是二进制的,但我们所熟悉的是十进制,所以4bit BCD计数器相当于十进制的计数器:从0计到9,然后进位,清零,设计图如下:
BCD计数器常常应用在数码管显示中,比如要要是1234,在单片机中通常会用C语言中的除法和取余提取每一位送到数码管显示,但是在数字逻辑电路中,除法器很耗费资源且效率不高,所以不采用除法,那如何进行显示呢?这个时候就可以将1234转换为对应的BCD码:0001_0010_0011_0100;这样只需要每次送入一个4bitBCD码就可以显示(如果以1ms的频率刷新,就是数码管动态扫描显示,也是下一个例子);
verilog代码:
//////////////////////////////////////////////////////////////////////////////////// Module Name: BCDcounter
// Description: BCD计数器
//////////////////////////////////////////////////////////////////////////////////
module BCDcounter(
input clk, //时钟信号输入
input rst, //复位信号输入
input cin, //进位信号输入
output cout, //进位信号输出
output [:]q //4bitBCD码输出
);
reg [:]cnt; //计数器最大值为9,所以需要4bit计数器 //BCD计数器计数功能描述
always@(posedge clk,posedge rst)
if(rst)
cnt <= ;
else if(cin)begin
if(cnt == 'd9)
cnt <= ;
else
cnt <= cnt + 'b1;
end assign cout = cin && (cnt == 'd9); /** 此处不能使用时序逻辑,需要在计满9的时候同步输出cout信号,然后在下次清零是cout恢复为0 **/ assign q = cnt; //BCD计数值输出 endmodule
testbench测试文件如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: BCDcounter_tb
// Description: BCDcounter模块测试文件
//////////////////////////////////////////////////////////////////////////////////
`define clk_period //100M时钟信号
module BCDcounter_tb();
reg clk; //时钟信号输入
reg rst; //复位信号输入
reg cin; //进位信号输入
wire cout; //进位信号输出
wire [:]q; //4bitBCD码输出 //例化测试模块
BCDcounter BCDcounter_test(
.clk(clk), //时钟信号输入
.rst(rst), //复位信号输入
.cin(cin), //进位信号输入
.cout(cout), //进位信号输出
.q(q) //4bitBCD码输出
);
//产生100M时钟信号
initial clk = ;
always #(`clk_period/) clk = ~clk; //开始测试
initial begin
rst = ; //复位
cin = ; //无进位
#(`clk_period * );
rst = ;
#(`clk_period * );
cin = ; //产生进位,计数器开始计数
#(`clk_period * ); //1个clk计一次数,总共计10次,所以延时15个clk观察波形
$stop;
end endmodule
测试结果如下:
综合电路图如下:
至此一个4bitBCDcounter就设计完成了,一般使用时,都是通过级联实现多位BCD计数器,比如四个BCD计数器级联起来最大值为9999,可以很方便的应用在数码管动态显示上;
verilog代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: BCDcounter_top
// Description: 4个4bit计数器进行级联,实现计数最大值为9999
////////////////////////////////////////////////////////////////////////////////// module BCDcounter_top(
input clk, //时钟信号输入
input rst, //复位信号输入
input cin, //进位信号输入
output cout, //进位信号输出
output [:]q //BCD码输出
); wire cout0,cout1,cout2; //4个BCD计数器级联之间连接线
wire [:]q0,q1,q2,q3; //每个BCD计数器的输出 assign q = {q3,q2,q1,q0}; //拼接成16位输出 //例化4个BCD计数器
BCDcounter BCDcounter0(
.clk(clk), //时钟信号输入
.rst(rst), //复位信号输入
.cin(cin), //进位信号输入
.cout(cout0), //进位信号输出
.q(q0) //4bitBCD码输出
);
BCDcounter BCDcounter1(
.clk(clk), //时钟信号输入
.rst(rst), //复位信号输入
.cin(cout0), //进位信号输入
.cout(cout1), //进位信号输出
.q(q1) //4bitBCD码输出
);
BCDcounter BCDcounter2(
.clk(clk), //时钟信号输入
.rst(rst), //复位信号输入
.cin(cout1), //进位信号输入
.cout(cout2), //进位信号输出
.q(q2) //4bitBCD码输出
);
BCDcounter BCDcounter3(
.clk(clk), //时钟信号输入
.rst(rst), //复位信号输入
.cin(cout2), //进位信号输入
.cout(cout), //进位信号输出
.q(q3) //4bitBCD码输出
);
endmodule
testbench测试文件与之前一样,但是需要增大延时时长,因为要计数到9999;
测试结果如下:
综合电路图如下:
例5.数码管动态扫描显示
设计图如上图,在数码管静态显示的基础上只需要添加一个分频器,将系统时钟分频为1ms的时钟信号,然后每1ms同时改变段选和位选,刷新显示,这就是数码管动态显示的原理;位选信号已经由2-4译码器控制,只有输入2位选择信号,所以还需一个四选一数据选择器,和2-4译码器共享一个选择信号sel,这样只需要改变sel的值就可以了;
verilog代码如下(这是顶层文件,还需要之前):
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: Dynamic_seg_display
// Description: 4位数码管动态扫描显示
//////////////////////////////////////////////////////////////////////////////////
`define SIMULATION /*** 仿真时保留,板级验证时注释 ***/
module Dynamic_seg_display(
input clk, //时钟信号
input rst, //复位信号
input [:]data_display, //16位待显示数据显示(4个BCD码)
output [:]segments, //数码管段码
output [:]wei_sel //数码管位码
);
`ifdef SIMULATION //仿真情况下
parameter CNT_MAX = 'd49;
`else //板级验证情况下
parameter CNT_MAX = 'd99_999;
`endif reg [:]cnt; //最大计数值4_999_999,需要23位计数器
reg [:]wei; //选择哪一位显示
wire [:]data_display_temp; //显示信号传输 //计数器功能描述
always@(posedge clk,posedge rst)
if(rst)
cnt <= ;
else if(cnt == CNT_MAX)
cnt <= ;
else
cnt <= cnt + 'b1; //控制数码管动态扫描显示(每隔1ms加一,刷新段选和位选)
always@(posedge clk,posedge rst)
if(rst)
wei <= 'b00;
else if(cnt == CNT_MAX)
wei <= wei + 'b1; //每计满100_000个时钟周期(1ms),位选加一,切换下一位
else
wei <= wei;
//例化四选一多路器,选择输入哪一位显示段码信号
mux4 mux4_1(
.sel(wei),
.data_in(data_display),
.data_out(data_display_temp)
);
//例化数码管译码模块
seg_display seg_display0(
.data_display(data_display_temp), //数码管待显示数据
.wei(wei), //选择哪一位显示
.segments(segments), //数码管段码
.wei_sel(wei_sel) //数码管位码
); endmodule
//四选一多路器
module mux4(
input [:]sel,
input [:]data_in,
output reg [:]data_out
);
always@(*)
case(sel)
//对应位
'h0: data_out = data_in[3:0];
'h1: data_out = data_in[7:4];
'h2: data_out = data_in[11:8];
'h3: data_out = data_in[15:12];
default: data_out = 'bz;
endcase
endmodule
testbench测试代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////// Module Name: Dynamic_seg_display_tb
// Description: 数码管动态扫描显示控制模块测试文件
//////////////////////////////////////////////////////////////////////////////////
`define clk_period //100M时钟信号 module Dynamic_seg_display_tb();
reg clk; //时钟信号
reg rst; //复位信号
reg [:]data_display; //16位待显示数据显示(4个BCD码)
wire [:]segments; //数码管段码
wire [:]wei_sel; //数码管位码 //例化测试模块
Dynamic_seg_display Dynamic_seg_display_test(
.clk(clk), //时钟信号
.rst(rst), //复位信号
.data_display(data_display), //16位待显示数据显示(4个BCD码)
.segments(segments), //数码管段码
.wei_sel(wei_sel) //数码管位码
);
//产生100M时钟信号
initial clk = ;
always #(`clk_period/) clk = ~clk; //开始测试
initial begin
rst = ;
data_display = 'h4321; //数码管上显示"1234"
#(`clk_period * );
rst = ;
#(`clk_period * * ); //计50次数刷新一下,观察5次
$stop;
end
endmodule
测试结果如下:
综合后的电路图如图:
注:
将上一例中四个级联BCD计数器的16bit输出和该例中数码管动态扫描控制模块的16bit输入接起来,控制BCD计数的频率为1hz,就形成一个简易计时器,从0000一直计到9999;
verilog代码如下(这是顶层文件,底层模块调用之前的数码管动态扫描模块和4个级联BCD计数器模块):
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: easyclock
// Description: 简易数字钟:按1hz的频率从0000-9999显示
//////////////////////////////////////////////////////////////////////////////////
//`define SIMULATION /*** 仿真时保留,板级验证时注释 ***/
module easyclock(
input clk, //时钟信号输入
input rst, //复位信号输入
output [:]segments, //数码管段码
output [:]wei_sel //数码管位码 );
`ifdef SIMULATION //仿真情况下
parameter CNT_MAX = 'd49;
`else //板级验证情况下
parameter CNT_MAX = 'd49_999_999;
`endif reg clk_1hz; //1hz时钟信号
reg [:]cnt; //计数器计数寄存起(用于分频器)
wire [:]data_display; //待显示数据 //计数器
always@(posedge clk,posedge rst)
if(rst)
cnt <= ;
else if(cnt == CNT_MAX)
cnt <= ;
else
cnt <= cnt + 'b1;
//产生1hz信号
always@(posedge clk,posedge rst)
if(rst)
clk_1hz <= ;
else if(cnt == CNT_MAX)
clk_1hz <= ~clk_1hz; //每计满50_000_000个时钟周期(500ms),输出信号翻转一下
else
clk_1hz <= clk_1hz; //例化4个BCD计数器
BCDcounter_top BCDcounter_top0(
.clk(clk_1hz), /** 因为BCD需要按1hz计数,所以需要一个1hz时钟信号 **/
.rst(rst), //复位信号输入
.cin('b1), //进位信号输入
.cout(), //进位信号输出
.q(data_display) //BCD码输出
);
//例化数码管动态扫描显示控制模块
Dynamic_seg_display Dynamic_seg_display0(
.clk(clk), //时钟信号
.rst(rst), //复位信号
.data_display(data_display), //16位待显示数据显示(4个BCD码)
.segments(segments), //数码管段码
.wei_sel(wei_sel) //数码管位码
); endmodule
综合电路图如下:
例6.74HC595驱动
在上一例中讲述了数码管动态扫描的设计,但是仅仅是4位数码管显示,就占用了8+4个IO,如果是8段8位数码管,就会占用16个IO,造成IO浪费,所以可以采用74HC595串转并芯片两片级联,只采用3个IO完成8段8位数码管的显示;
verilog描述如下:
//////////////////////////////////////////////////////////////////////////
//module: 74HC595驱动模块
//description: 将输入的16位数据串行输出到74HC595
//////////////////////////////////////////////////////////////////////////
module HC595drive(
input clk, //50M系统时钟
input rst, //低电平复位
input [:]seg, //8bit段选信号
input [:]sel, //8bit位选信号
output reg DIO, //74HC595数据引脚
output reg SHCP, //74HC595移位脉冲引脚
output reg STCP //74HC595输出脉冲引脚
); reg clk_595;
reg [:]divid_cnt;
reg [:]count; //产生74HC595工作时钟12.5M
always@(posedge clk,negedge rst)
if(!rst)begin
divid_cnt <= ;
clk_595 <= ;
end
else if(divid_cnt == 'd3)begin
clk_595 <= ;
divid_cnt <= ;
end
else begin
divid_cnt <= divid_cnt + 'b1;
clk_595 <= ;
end //对74HC595工作时钟clk_595计数
always@(posedge clk,negedge rst)
if(!rst)
count <= ;
else if(clk_595 == )begin //一共串行发送16bit数据
if(count == 'd31)
count <= ;
else
count <= count + 'b1;
end
else
count <= count; //count清零 //功能描述
always@(posedge clk,negedge rst)
if(!rst)begin
DIO <= ;
SHCP <= ;
STCP <= ;
end
else
case(count)
5'd0: begin DIO <= seg[7]; SHCP <= 0; STCP <= 1; end //HEX_DP,输出
5'd1: begin SHCP <= 1; STCP <= 0; end //移位
5'd2: begin DIO <= seg[6]; SHCP <= 0; STCP <= 0; end //HEX_G
5'd3: begin SHCP <= 1; end
5'd4: begin DIO <= seg[5]; SHCP <= 0; end //HEX_F
5'd5: begin SHCP <= 1; end
5'd6: begin DIO <= seg[4]; SHCP <= 0; end //HEX_E
5'd7: begin SHCP <= 1; end
5'd8: begin DIO <= seg[3]; SHCP <= 0; end //HEX_D
5'd9: begin SHCP <= 1; end
5'd10: begin DIO <= seg[2]; SHCP <= 0; end //HEX_C
5'd11: begin SHCP <= 1; end
5'd12: begin DIO <= seg[1]; SHCP <= 0; end //HEX_B
5'd13: begin SHCP <= 1; end
5'd14: begin DIO <= seg[0]; SHCP <= 0; end //HEX_A
5'd15: begin SHCP <= 1; end
5'd16: begin DIO <= sel[7]; SHCP <= 0; end //第7位
5'd17: begin SHCP <= 1; end
5'd18: begin DIO <= sel[6]; SHCP <= 0; end //第6位
5'd19: begin SHCP <= 1; end
5'd20: begin DIO <= sel[5]; SHCP <= 0; end //第5位
5'd21: begin SHCP <= 1; end
5'd22: begin DIO <= sel[4]; SHCP <= 0; end //第4位
5'd23: begin SHCP <= 1; end
5'd24: begin DIO <= sel[3]; SHCP <= 0; end //第3位
5'd25: begin SHCP <= 1; end
5'd26: begin DIO <= sel[2]; SHCP <= 0; end //第2位
5'd27: begin SHCP <= 1; end
5'd28: begin DIO <= sel[1]; SHCP <= 0; end //第1位
5'd29: begin SHCP <= 1; end
5'd30: begin DIO <= sel[0]; SHCP <= 0; end //第0位
5'd31: begin SHCP <= 1; end
endcase
endmodule
testbench如下:
`timescale 1ns/1ps
//////////////////////////////////////////////////////////////////////////
//module: 74HC595驱动测试模块
//////////////////////////////////////////////////////////////////////////
`define clk_period //50M系统时钟 module HC595drive_tb(); reg clk; //50M系统时钟
reg rst; //低电平复位
reg [:]seg; //8bit段选信号
reg [:]sel; //8bit位选信号
wire DIO; //74HC595数据引脚
wire SHCP; //74HC595移位脉冲引脚
wire STCP; //74HC595输出脉冲引脚 //例化测试模块
HC595drive HC595drive_test(
.clk(clk),
.rst(rst),
.seg(seg),
.sel(sel),
.DIO(DIO),
.SHCP(SHCP),
.STCP(STCP)
); //产生50M时钟信号
initial clk = ;
always #(`clk_period / ) clk = ~clk; //开始测试
initial begin
rst = ; //复位
seg = 'hc0; //显示数字"1"
sel = 'ha5; //10100101
#(`clk_period * );
rst = ;
#(`clk_period * * ); //传送16位需要32个clk,clk又被进行4分频
#(`clk_period * );
$stop;
end
endmodule
测试结果如下:
例7.定时器
玩过单片机的小伙伴都很熟悉,单片机的基本外设就是定时器,定时器的核心也是计数器,设定工作方式(单次计数和循环计数)和定时值后,就开始计数,计满之后产生计数慢标志信号,提示设定的定时时间到达;
所以,如果要用计数器来设计一个定时器,需要实现的功能有:
1、定时时间参数通过一个端口输入,调节该值可以修改定时时间;
2、设置一个计数模式控制信号,当该信号为1时为循环定时模式,当该信号为0时为单次定时模式;
3、设置一个计数启动信号,在循环定时模式下,该信号为高电平使能计时,为低电平则停止计时;当单次计数模式下,该信号的一个单基准时钟周期的脉冲使能一次定时;
4、输出计数器定时计数值,该值用于产生特定占空比的方波;
设计图如下:
verilog代码如下:
module timer(
input clk, //50M时钟信号
input rst, //低电平复位
input [:]timer_value, //定时值
input mode, //定时器模式选择
input en, //定时器使能控制端
output [:]cnt_value, //定时器计数值实时输出
output full_flag //定时器计满输出标志位
); reg [:]cnt; //32bit计数器
reg oneshot; //在单次定时模式中,使能端一个脉冲就启动,但是计数器需要en保持为1才会计数,所以需要另外一个单次使能信号,当外部一个脉冲时,它会变为1,保持至定时结束; //功能描述
assign full_flag = (cnt == timer_value)?'b1:1'b0; //输出计满标志位
assign cnt_value = cnt; //实时输出计数值 always@(posedge clk,negedge rst)
if(!rst)
cnt <= ;
else if(mode)begin //mode = 1,循环定时模式
if(en && cnt < timer_value)
cnt <= cnt +'b1; //使能状态下且计数值未满,加一
else
cnt <= ; //其余情况下cnt清零
end
else begin //mode = 0,单次定时模式
if(oneshot)
cnt <= cnt + 'b1;
else
cnt <= ;
end //利用外部一个脉冲产生oneshot信号供定时器单次定时模式下使用
//功能:当外部en产生一个高脉冲时,它会变为1,保持至定时结束;
always@(posedge clk,negedge rst)
if(!rst)
oneshot <= ;
else if(en)
oneshot <= 'b1;
else if(cnt >= timer_value) //计满清零
oneshot <= ;
endmodule
testbench测试文件如下:
`timescale 1ns/1ps
`define clk_period module timer_tb(); reg clk; //50M时钟信号
reg rst; //低电平复位
reg [:]timer_value; //定时值
reg mode; //定时器模式选择
reg en; //定时器使能控制端
wire [:]cnt_value; //定时器计数值实时输出
wire full_flag; //定时器计满输出标志位 //例化测试模块
timer timer_test(
.clk(clk), //50M时钟信号
.rst(rst), //低电平复位
.timer_value(timer_value), //定时值
.mode(mode), //定时器模式选择
.en(en), //定时器使能控制端
.cnt_value(cnt_value), //定时器计数值实时输出
.full_flag(full_flag) //定时器计满输出标志位
); //产生50M时钟信号
initial clk = ;
always #(`clk_period / ) clk = ~clk; //开始测试
initial begin
rst = ; //系统复位
mode = ; //首先测试模式0,单次计数模式
en = ; //未使能
timer_value = 'd10; //定时器计数值为10
#(`clk_period * );
rst = ;
#(`clk_period * );
en = ; //产生一个高脉冲,使能定时器
#(`clk_period * );
en = ;
#(`clk_period * ); //系统计数10个clk会输出计满flag,在延时观察5个clk观察定时器是否停止
mode = ; //开始测试定时器模式1,循环计数模式
#(`clk_period);
en = ; //使能定时器
#(`clk_period * * ); //系统计数10个clk会输出计满flag,一共观察5次flag
en = ; //停止使能
#(`clk_period * ); //等待10个clk,观察定时器是否已停止工作
$stop; //停止测试
end
endmodule
测试结果如下:
单次计数模式测试:
循环计数模式测试:
综合电路图如下:
定时器至此就设计结束,定时器有很多应用,比如产生PWM驱动无源蜂鸣器,驱动直流电机调速,驱动舵机,驱动步进电机,驱动RGBLED变色,总之利用定时器可以很方便产生可变PWM,利用好PWM也是很深的学问,如有兴趣自行深入;
FPGA学习笔记(六)—— 时序逻辑电路设计的更多相关文章
- java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)
java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...
- # go微服务框架kratos学习笔记六(kratos 服务发现 discovery)
目录 go微服务框架kratos学习笔记六(kratos 服务发现 discovery) http api register 服务注册 fetch 获取实例 fetchs 批量获取实例 polls 批 ...
- Java IO学习笔记六:NIO到多路复用
作者:Grey 原文地址:Java IO学习笔记六:NIO到多路复用 虽然NIO性能上比BIO要好,参考:Java IO学习笔记五:BIO到NIO 但是NIO也有问题,NIO服务端的示例代码中往往会包 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(六) indigo xtion pro live
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
- Typescript 学习笔记六:接口
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- python3.4学习笔记(六) 常用快捷键使用技巧,持续更新
python3.4学习笔记(六) 常用快捷键使用技巧,持续更新 安装IDLE后鼠标右键点击*.py 文件,可以看到Edit with IDLE 选择这个可以直接打开编辑器.IDLE默认不能显示行号,使 ...
- Go语言学习笔记六: 循环语句
Go语言学习笔记六: 循环语句 今天学了一个格式化代码的命令:gofmt -w chapter6.go for循环 for循环有3种形式: for init; condition; increment ...
- 【opencv学习笔记六】图像的ROI区域选择与复制
图像的数据量还是比较大的,对整张图片进行处理会影响我们的处理效率,因此常常只对图像中我们需要的部分进行处理,也就是感兴趣区域ROI.今天我们来看一下如何设置图像的感兴趣区域ROI.以及对ROI区域图像 ...
- Linux学习笔记(六) 进程管理
1.进程基础 当输入一个命令时,shell 会同时启动一个进程,这种任务与进程分离的方式是 Linux 系统上重要的概念 每个执行的任务都称为进程,在每个进程启动时,系统都会给它指定一个唯一的 ID, ...
- Spring Boot 学习笔记(六) 整合 RESTful 参数传递
Spring Boot 学习笔记 源码地址 Spring Boot 学习笔记(一) hello world Spring Boot 学习笔记(二) 整合 log4j2 Spring Boot 学习笔记 ...
随机推荐
- codeforces 570 E. Pig and Palindromes (DP)
题目链接: 570 E. Pig and Palindromes 题目描述: 有一个n*m的矩阵,每个小格子里面都有一个字母.Peppa the Pig想要从(1,1)到(n, m).因为Peppa ...
- 树上最长链 Farthest Nodes in a Tree LightOJ - 1094 && [ZJOI2007]捉迷藏 && 最长链
树上最远点对(树的直径) 做法1:树形dp 最长路一定是经过树上的某一个节点的. 因此: an1[i],an2[i]分别表示一个点向下的最长链和次长链,次长链不存在就设为0:这两者很容易求 an3[i ...
- List与类之间的运用,即与javabean的应用
package com.wh.Object; public class Goods { private String name; private double price; private int n ...
- Repeater 的使用
<HeaderTemplate></HeaderTemplate> 头模板——在加载开始执行一遍 <FooterTemplate></FooterTempl ...
- Spring Mvc相关随笔
web.xml部分 1.欢迎界面 <welcome-file-list> <welcome-file>/views/login.jsp</welcome-file> ...
- .NET 原理之 ViewState
1.从MSDN中我们可以知道一个页面生命周期大约可分为为:页请求.开始.初始化.加载.验证.回发事件处理.呈现.卸载这几个阶段. HttpHandler是无状态的,aspx是高级的Http ...
- 详解Android Activity启动模式
相关的基本概念: 1.任务栈(Task) 若干个Activity的集合的栈表示一个Task. 栈不仅仅只包含自身程序的Activity,它也可以跨应用包含其他应用的Activity,这样有利于 ...
- iOS programming Delegation and Text Input
iOS programming Delegation and Text Input 1.1 Text Fields CGRect textFieldRect = CGRectMake(40, ...
- new Buffer 生成二进制数据
node编辑环境下: > new Buffer("admin")<Buffer 61 64 6d 69 6e> 通过post请求,服务端接收到是流数据,必须把流数 ...
- java中属性命名get字母大小写问题
java文件 company.java private int sTime; public void setSTime (int sTime) { this.sTime = sTime; ...