用always@(posedge clk)描述
       时序逻辑电路的基础——计数器(在每个时钟的上升沿递增1)

  例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时钟信号;

   注:计数器只是基本电路,比如分频器、定时器、巴克码序列发生器在计数器基本电路上设计即可;
 
例2.基本分频操作——分频器(闪烁灯:LED以1hz的频率闪烁)
    分频器是计数器的一大应用,在FPGA中没有像C语言中delay()这样的延时,那如何延时呢?可以通过分频器,将系统100M的高速时钟信号经过分频器,变为1hz的输出信号,这样,LED就会按1s的周期闪烁;
  1hz的时钟信号是每500ms翻转1次,而在100M的时钟下,1个clk是10ns,所以应该每计数 500_000_000/10 = 50_000_000 次输出信号翻转,就产生了1hz的信号;50_000_000大概估算需要一个26位的寄存器;又因为计数器是从0开始计数的,所以最大计数值应该为49_999_999,设计如下:
`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 
  testbench测试文件如下:
`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
例3.基本移位操作——verilog位操作(流水灯:16个LED循环左移)
      //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学习笔记(六)—— 时序逻辑电路设计的更多相关文章

  1. java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

    java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...

  2. # go微服务框架kratos学习笔记六(kratos 服务发现 discovery)

    目录 go微服务框架kratos学习笔记六(kratos 服务发现 discovery) http api register 服务注册 fetch 获取实例 fetchs 批量获取实例 polls 批 ...

  3. Java IO学习笔记六:NIO到多路复用

    作者:Grey 原文地址:Java IO学习笔记六:NIO到多路复用 虽然NIO性能上比BIO要好,参考:Java IO学习笔记五:BIO到NIO 但是NIO也有问题,NIO服务端的示例代码中往往会包 ...

  4. Learning ROS for Robotics Programming Second Edition学习笔记(六) indigo xtion pro live

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

  5. Typescript 学习笔记六:接口

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  6. python3.4学习笔记(六) 常用快捷键使用技巧,持续更新

    python3.4学习笔记(六) 常用快捷键使用技巧,持续更新 安装IDLE后鼠标右键点击*.py 文件,可以看到Edit with IDLE 选择这个可以直接打开编辑器.IDLE默认不能显示行号,使 ...

  7. Go语言学习笔记六: 循环语句

    Go语言学习笔记六: 循环语句 今天学了一个格式化代码的命令:gofmt -w chapter6.go for循环 for循环有3种形式: for init; condition; increment ...

  8. 【opencv学习笔记六】图像的ROI区域选择与复制

    图像的数据量还是比较大的,对整张图片进行处理会影响我们的处理效率,因此常常只对图像中我们需要的部分进行处理,也就是感兴趣区域ROI.今天我们来看一下如何设置图像的感兴趣区域ROI.以及对ROI区域图像 ...

  9. Linux学习笔记(六) 进程管理

    1.进程基础 当输入一个命令时,shell 会同时启动一个进程,这种任务与进程分离的方式是 Linux 系统上重要的概念 每个执行的任务都称为进程,在每个进程启动时,系统都会给它指定一个唯一的 ID, ...

  10. Spring Boot 学习笔记(六) 整合 RESTful 参数传递

    Spring Boot 学习笔记 源码地址 Spring Boot 学习笔记(一) hello world Spring Boot 学习笔记(二) 整合 log4j2 Spring Boot 学习笔记 ...

随机推荐

  1. codeforces 570 E. Pig and Palindromes (DP)

    题目链接: 570 E. Pig and Palindromes 题目描述: 有一个n*m的矩阵,每个小格子里面都有一个字母.Peppa the Pig想要从(1,1)到(n, m).因为Peppa ...

  2. 树上最长链 Farthest Nodes in a Tree LightOJ - 1094 && [ZJOI2007]捉迷藏 && 最长链

    树上最远点对(树的直径) 做法1:树形dp 最长路一定是经过树上的某一个节点的. 因此: an1[i],an2[i]分别表示一个点向下的最长链和次长链,次长链不存在就设为0:这两者很容易求 an3[i ...

  3. List与类之间的运用,即与javabean的应用

    package com.wh.Object; public class Goods { private String name; private double price; private int n ...

  4. Repeater 的使用

    <HeaderTemplate></HeaderTemplate>  头模板——在加载开始执行一遍 <FooterTemplate></FooterTempl ...

  5. Spring Mvc相关随笔

    web.xml部分 1.欢迎界面 <welcome-file-list> <welcome-file>/views/login.jsp</welcome-file> ...

  6. .NET 原理之 ViewState

    1.从MSDN中我们可以知道一个页面生命周期大约可分为为:页请求.开始.初始化.加载.验证.回发事件处理.呈现.卸载这几个阶段.       HttpHandler是无状态的,aspx是高级的Http ...

  7. 详解Android Activity启动模式

    相关的基本概念: 1.任务栈(Task)   若干个Activity的集合的栈表示一个Task.   栈不仅仅只包含自身程序的Activity,它也可以跨应用包含其他应用的Activity,这样有利于 ...

  8. iOS programming Delegation and Text Input

    iOS programming Delegation and Text Input  1.1 Text Fields    CGRect textFieldRect = CGRectMake(40, ...

  9. new Buffer 生成二进制数据

    node编辑环境下: > new Buffer("admin")<Buffer 61 64 6d 69 6e> 通过post请求,服务端接收到是流数据,必须把流数 ...

  10. java中属性命名get字母大小写问题

    java文件 company.java private int sTime; public void setSTime (int sTime) {        this.sTime = sTime; ...