计数器:Verilog常用写法

在FPGA设计中,计数器是非常常用的,没有计数器就无法处理时序,下面记录几种计数器的写法:

一、时序逻辑与组合逻辑分开

1.1、代码

//======================================================================
// --- 名称 : Count_1
// --- 作者 : 又菜又爱玩
// --- 日期 : 2024-8-28
// --- 描述 : 模10计数器,0到10循环累加
//======================================================================


module Count_1
(
    input               clk                 ,
    input               rst_n               ,
    output reg [ 3:0]   cnt
);

//----------------------------------------------------------------------
//--   组合电路
//----------------------------------------------------------------------
reg [ 3:0]              cnt_n                ;

always @(*)begin
    if(cnt == 4'd9)
        cnt_n = 4'd0;
    else
        cnt_n = cnt + 1'b1;
end

//----------------------------------------------------------------------
//--   时序电路
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt <= 4'b0;
    else
        cnt <= cnt_n;
end

endmodule

/*
//----------------------------------------------------------------------
//--   组合电路也可以这样写
//----------------------------------------------------------------------
wire [ 3:0]             cnt_n               ;

assign cnt_n = (cnt==4'd9)? 4'd0 : cnt+1'b1;

*/

1.2、写法1的RTL视图

 1.3:写法2的RTL视图

二、常用写法

2.1、代码

//======================================================================
// --- 名称 : Count_2
// --- 作者 : 又菜又爱玩
// --- 日期 : 2024-8-28
// --- 描述 : 模10计数器,0到10循环累加
//======================================================================

module Count_2
    (
        input               clk    ,
        input               rst_n  ,
        output   reg [3:0]  cnt
    );

always @ (posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt <= 4'd0;
    else if (cnt == 4'd9)
        cnt <= 4'd0;
    else
        cnt <= cnt + 1'b1;
end

    endmodule

2.2、RTL视图

 三、代码片段法

//======================================================================
// --- 名称 : Count_3
// --- 作者 : 又菜又爱玩
// --- 日期 : 2024-8_28
// --- 描述 : 模10计数器,0到10循环增加
//======================================================================

module  Count_3 
(
    clk,
    rst_n,
    cnt
);
//---------------------<端口声明>---------------------------------------
input               clk     ;
input               rst_n   ;
output  reg   [3:0] cnt     ;

//---------------------<信号定义>---------------------------------------
wire                add_cnt ;
wire                end_cnt ;

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt <= 'd0;
    else if(add_cnt)begin
        if(end_cnt)
            cnt <= 'd0;
        else
            cnt <= cnt + 1'b1;
    end
    else
        cnt <= cnt;
end

assign add_cnt = 1;
assign end_cnt = add_cnt && cnt==10-1;

endmodule

四、其它计数器

源自开源骚客《SDRAM那些事儿》系列教程。

要求:现在对OV5640摄像头进行上电控制,由数据手册得到上电时序图如下所示,现用Verilog实现其波形

 4.1、代码片段法

module power_ctrl
//========================< 端口 >==========================================
(
//system --------------------------------------------
input   wire                clk                     , // 50MHz
input   wire                rst_n                   ,
//ov5640 --------------------------------------------
output  reg                 ov5640_pwdn             , // ov5640上电
output  reg                 ov5640_rst_n            , // ov5640复位
output  reg                 power_done                // power_ctrl全面有效,SCCB可以开始工作
);
//========================< 参数 >==========================================
localparam T2_6MS           = 30_0000               ; // T2>5ms
localparam T3_2MS           = 10_0000               ; // T3>1ms
localparam T4_21MS          = 105_0000              ; // T4>20ms
//========================< 信号 >==========================================
reg     [18:0]              cnt_6ms                 ;
wire                        add_cnt_6ms             ;
wire                        end_cnt_6ms             ;
reg     [16:0]              cnt_2ms                 ;
wire                        add_cnt_2ms             ;
wire                        end_cnt_2ms             ;
reg     [20:0]              cnt_21ms                ;
wire                        add_cnt_21ms            ;
wire                        end_cnt_21ms            ;

//==========================================================================
//==    ov5640_pwdn
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_6ms <= 'd0;
    else if(add_cnt_6ms) begin
        if(end_cnt_6ms)
            cnt_6ms <= 'd0;
        else
            cnt_6ms <= cnt_6ms + 1;
    end
end

assign add_cnt_6ms = ov5640_pwdn == 1'b1;
assign end_cnt_6ms = add_cnt_6ms && cnt_6ms== T2_6MS-1;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        ov5640_pwdn <= 1'b1;
    end
    else if(end_cnt_6ms) begin
        ov5640_pwdn <= 1'b0;
    end
end

//==========================================================================
//==    ov5640_rst_n
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_2ms <= 'd0;
    else if(add_cnt_2ms) begin
        if(end_cnt_2ms)
            cnt_2ms <= 'd0;
        else
            cnt_2ms <= cnt_2ms + 1'b1;
    end
end

assign add_cnt_2ms = ov5640_rst_n == 1'b0 && ov5640_pwdn == 1'b0;
assign end_cnt_2ms = add_cnt_2ms && cnt_2ms== T3_2MS-1;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        ov5640_rst_n <= 1'b0;
    end
    else if(end_cnt_2ms) begin
        ov5640_rst_n <= 1'b1;
    end
end

//==========================================================================
//==    power_done
//==========================================================================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt_21ms <= 'd0;
    else if(add_cnt_21ms) begin
        if(end_cnt_21ms)
            cnt_21ms <= 'd0;
        else
            cnt_21ms <= cnt_21ms + 1'b1;
    end
end

assign add_cnt_21ms = power_done == 1'b0 && ov5640_rst_n == 1'b1;
assign end_cnt_21ms = add_cnt_21ms && cnt_21ms== T4_21MS-1;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        power_done <= 1'b0;
    end
    else if(end_cnt_21ms) begin
        power_done <= 1'b1;
    end
end

endmodule

仿真波形:

4.2、开源骚客计数器

module power_ctrl
//========================< 端口 >==========================================
(
//system --------------------------------------------
input   wire                clk                     , // 50MHz
input   wire                rst_n                   ,
//ov5640 --------------------------------------------
output  wire                ov5640_pwdn             , // ov5640上电
output  wire                ov5640_rst_n            , // ov5640复位
output  wire                power_done                // power_ctrl全面有效,SCCB可以开始工作
);
//========================< 参数 >==========================================
localparam T2_6MS           = 30_0000               ; // T2>5ms
localparam T3_2MS           = 10_0000               ; // T3>1ms
localparam T4_21MS          = 105_0000              ; // T4>20ms
//========================< 信号 >==========================================
reg     [18:0]              cnt_6ms                 ;
reg     [16:0]              cnt_2ms                 ;
reg     [20:0]              cnt_21ms                ;

//==========================================================================
//==    ov5640_pwdn
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_6ms <= 'd0;
    end
    else if(ov5640_pwdn == 1'b1) begin
        cnt_6ms <= cnt_6ms + 1'b1;
    end
end

assign ov5640_pwdn = (cnt_6ms >= T2_6MS) ? 1'b0 : 1'b1;

//==========================================================================
//==    ov5640_rst_n
//==========================================================================
always  @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_2ms <= 'd0;
    end
    else if(ov5640_rst_n == 1'b0 && ov5640_pwdn == 1'b0) begin
        cnt_2ms <= cnt_2ms + 1'b1;
    end
end

assign ov5640_rst_n = (cnt_2ms >= T3_2MS) ? 1'b1 : 1'b0;

//==========================================================================
//==    power_done
//==========================================================================
always  @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_21ms <= 'd0;
    end
    else if(power_done == 1'b0 && ov5640_rst_n == 1'b1) begin
        cnt_21ms <= cnt_21ms + 1'b1;
    end
end


assign power_done = (cnt_21ms >= T4_21MS) ? 1'b1 : 1'b0;



endmodule

可以看到开源骚客写法较为简洁,代码精简很多。仿真波形如下:

         从两个仿真波形可以看到,代码片段法计满之后会进行清0,而开源骚客的方法在计满之后会进行保持。

参考资料

[1]小梅哥FPGA教程

[2]明德扬FPGA教程

[3]开源骚客《SDRAM那些事儿》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值