Verilog设计实例(2)一步一步实现一个多功能通用计数器

写在前面

相关博文
博客首页
注:学习交流使用!

正文

多功能计数器,英文名为:Versatile Counter;所谓多功能,这里包括二进制计数,格雷码计数以及线性反馈移位寄存器(LFSR)三种,本文通过从普通的计数器开始,也就是单个功能的计数器开始,一步一步过渡到多功能计数器。
作为对以下相关博文的延伸练习:
Verilog设计实例(1)线性反馈移位寄存器(LFSR)
FPGA设计心得(8)Verilog中的编译预处理语句

普通的二进制计数器

这个作为开头,不必多说,计数就完事了。

电路设计

设计文件:

`timescale 1ns/1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: Reborn Lee
// Module Name: binary counter
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module binary_counter#(parameter N_BITS = 4)(
    input i_clk,
    input i_rst,
    output [N_BITS - 1 : 0] o_cnt,
    output o_cnt_done
    );

    reg [N_BITS - 1 : 0] bin_cnt = 0;
    always@(posedge i_clk) begin
        if(i_rst) begin
            bin_cnt <= 0;
        end
        else begin
            bin_cnt <= bin_cnt + 1;
        end
    end

    assign o_cnt_done = (bin_cnt == 0)? 1:0;
    assign o_cnt = bin_cnt;


endmodule

行为仿真

tb文件:

`timescale 1ns/1ps
module bin_cnt_tb;

    parameter N_BITS = 4;
    reg i_clk;
    reg i_rst;
    wire [N_BITS - 1 : 0] o_cnt;
    wire o_cnt_done;

    initial begin
        i_clk = 0;
        forever begin
            # 2 i_clk = ~ i_clk;
        end
    end

    initial begin
        i_rst = 1;

        # 8
        i_rst = 0;

    end

    binary_counter #(.N_BITS(N_BITS))
    inst_bin_cnt(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_cnt(o_cnt),
        .o_cnt_done(o_cnt_done)
        );
endmodule

仿真图:

普通二进制计数器仿真波形

普通的格雷码计数器

任意位宽的格雷码计数器,实现的方式通常是设计一个普通的二进制计数器,同时将计数结果转化为格雷码。
二进制与格雷码的转换方式,详情见:格雷码和二进制转换
为了方便给出原理图:

二进制转格雷码原理图

伪代码描述为:

assign gray_value = binary_value ^ (binary_value>>1);

或者:

assign gray_cnt = { bin_cnt[N_BITS - 1], bin_cnt[N_BITS - 1 : 1]^bin_cnt[N_BITS - 2 : 0]};

电路设计

一种简单的设计方式为:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: Reborn Lee
// Create Date: 2020/06/02 13:46:10
// Module Name: gray_counter
// Additional Comments: common gray counter
// 
//////////////////////////////////////////////////////////////////////////////////


module gray_counter #(parameter N_BITS = 4)(
    input i_clk,
    input i_rst,
    output [N_BITS - 1 : 0] o_cnt,
    output o_cnt_done
    );
    
    reg [N_BITS - 1 : 0] bin_cnt = 0;
    reg [N_BITS - 1 : 0] gray_cnt;
    always@(posedge i_clk) begin
        if(i_rst) begin
            bin_cnt <= 0;
            gray_cnt <= 0;
        end
        else begin
            bin_cnt <= bin_cnt + 1;
            // translate binary counter into  gray counter  
            gray_cnt <= bin_cnt ^ bin_cnt >>> 1; 

            //or 
            // gray_cnt <= { bin_cnt[N_BITS - 1], bin_cnt[N_BITS - 1 : 1]^bin_cnt[N_BITS - 2 : 0]};

            //or 

            // for(int i = 0; i < N_BITS - 1; i = i + 1) begin
            //  gray_cnt[i] <= bin_cnt[i+1]^bin_cnt[i];
            // end
            // gray_cnt[N_BITS - 1] <= bin_cnt[N_BITS - 1];

        end

    end

    assign o_cnt = gray_cnt;
    // or 
    assign o_cnt_done = (gray_cnt == 0) ? 1 : 0;



    
endmodule

注释部分解释:

            for(int i = 0; i < N_BITS - 1; i = i + 1) begin
                gray_cnt[i] <= bin_cnt[i+1]^bin_cnt[i];
            end
            gray_cnt[N_BITS - 1] <= bin_cnt[N_BITS - 1];

以及:

gray_cnt <= { bin_cnt[N_BITS - 1], bin_cnt[N_BITS - 1 : 1]^bin_cnt[N_BITS - 2 : 0]};

均在always块内,因此使用非阻塞赋值。
又和二进制计数在一起always内,且紧邻分布,因此计数相较于二进制慢一拍,但毫无影响(不影响计数总数)。

注: 三种二进制转换为格雷码的实现原理一致,效果等价。

行为仿真

TestBench设计:

`timescale 1ns/1ps
module gray_cnt_tb;

    parameter N_BITS = 4;
    reg i_clk;
    reg i_rst;
    wire [N_BITS - 1 : 0] o_cnt;
    wire o_cnt_done;

    initial begin
        i_clk = 0;
        forever begin
            # 2 i_clk = ~ i_clk;
        end
    end

    initial begin
        i_rst = 1;

        # 8
        i_rst = 0;

    end

    gray_counter #(.N_BITS(N_BITS))
    inst_gray_cnt(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_cnt(o_cnt),
        .o_cnt_done(o_cnt_done)
        );
endmodule

行为仿真波形:


格雷码计数器仿真波形

局部放大:


局部放大

局部放大

LFSR

这个请参考上篇博文,单独做了一篇博客:
Verilog设计实例(1)线性反馈移位寄存器(LFSR)

为了方便不跳转另外一个链接,这里给出设计:

电路设计

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: Reborn Lee
// Create Date: 2020/06/01 12:50:38
// Module Name: lfsr
//////////////////////////////////////////////////////////////////////////////////


module lfsr #(parameter NUM_BITS = 3)(
   input i_Clk,
   input i_Enable,
 
   // data valid
   input i_Seed_DV,

   // Optional Seed Value
   input [NUM_BITS-1:0] i_Seed_Data,
 
   output [NUM_BITS-1:0] o_LFSR_Data,
   output o_LFSR_Done

    );

  // internal variables
  reg [NUM_BITS:1] r_LFSR = 0;
  reg              r_XNOR;
 
 
  // Purpose: Load up LFSR with Seed if Data Valid (DV) pulse is detected.
  // Othewise just run LFSR when enabled.
    always @(posedge i_Clk)
    begin
      if (i_Enable == 1'b1)
      begin
          if (i_Seed_DV == 1'b1)
            r_LFSR <= i_Seed_Data;
          else
            r_LFSR <= {r_LFSR[NUM_BITS-1:1],r_XNOR}; //left right
      end
    end

// Create Feedback Polynomials.  Based on Application Note:
  // http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf
    always @(*)
    begin
      case (NUM_BITS)
        3: begin
          r_XNOR = r_LFSR[3] ^~ r_LFSR[2];
        end
        4: begin
          r_XNOR = r_LFSR[4] ^~ r_LFSR[3];
        end
        5: begin
          r_XNOR = r_LFSR[5] ^~ r_LFSR[3];
        end
        6: begin
          r_XNOR = r_LFSR[6] ^~ r_LFSR[5];
        end
        7: begin
          r_XNOR = r_LFSR[7] ^~ r_LFSR[6];
        end
        8: begin
          r_XNOR = r_LFSR[8] ^~ r_LFSR[6] ^~ r_LFSR[5] ^~ r_LFSR[4];
        end
        9: begin
          r_XNOR = r_LFSR[9] ^~ r_LFSR[5];
        end
        10: begin
          r_XNOR = r_LFSR[10] ^~ r_LFSR[7];
        end
        11: begin
          r_XNOR = r_LFSR[11] ^~ r_LFSR[9];
        end
        12: begin
          r_XNOR = r_LFSR[12] ^~ r_LFSR[6] ^~ r_LFSR[4] ^~ r_LFSR[1];
        end
        13: begin
          r_XNOR = r_LFSR[13] ^~ r_LFSR[4] ^~ r_LFSR[3] ^~ r_LFSR[1];
        end
        14: begin
          r_XNOR = r_LFSR[14] ^~ r_LFSR[5] ^~ r_LFSR[3] ^~ r_LFSR[1];
        end
        15: begin
          r_XNOR = r_LFSR[15] ^~ r_LFSR[14];
        end
        16: begin
          r_XNOR = r_LFSR[16] ^~ r_LFSR[15] ^~ r_LFSR[13] ^~ r_LFSR[4];
          end
        17: begin
          r_XNOR = r_LFSR[17] ^~ r_LFSR[14];
        end
        18: begin
          r_XNOR = r_LFSR[18] ^~ r_LFSR[11];
        end
        19: begin
          r_XNOR = r_LFSR[19] ^~ r_LFSR[6] ^~ r_LFSR[2] ^~ r_LFSR[1];
        end
        20: begin
          r_XNOR = r_LFSR[20] ^~ r_LFSR[17];
        end
        21: begin
          r_XNOR = r_LFSR[21] ^~ r_LFSR[19];
        end
        22: begin
          r_XNOR = r_LFSR[22] ^~ r_LFSR[21];
        end
        23: begin
          r_XNOR = r_LFSR[23] ^~ r_LFSR[18];
        end
        24: begin
          r_XNOR = r_LFSR[24] ^~ r_LFSR[23] ^~ r_LFSR[22] ^~ r_LFSR[17];
        end
        25: begin
          r_XNOR = r_LFSR[25] ^~ r_LFSR[22];
        end
        26: begin
          r_XNOR = r_LFSR[26] ^~ r_LFSR[6] ^~ r_LFSR[2] ^~ r_LFSR[1];
        end
        27: begin
          r_XNOR = r_LFSR[27] ^~ r_LFSR[5] ^~ r_LFSR[2] ^~ r_LFSR[1];
        end
        28: begin
          r_XNOR = r_LFSR[28] ^~ r_LFSR[25];
        end
        29: begin
          r_XNOR = r_LFSR[29] ^~ r_LFSR[27];
        end
        30: begin
          r_XNOR = r_LFSR[30] ^~ r_LFSR[6] ^~ r_LFSR[4] ^~ r_LFSR[1];
        end
        31: begin
          r_XNOR = r_LFSR[31] ^~ r_LFSR[28];
        end
        32: begin
          r_XNOR = r_LFSR[32] ^~ r_LFSR[22] ^~ r_LFSR[2] ^~ r_LFSR[1];
        end
 
      endcase // case (NUM_BITS)
    end // always @ (*)

    assign o_LFSR_Data = r_LFSR[NUM_BITS:1];
 
    // Conditional Assignment (?)
    assign o_LFSR_Done = (r_LFSR[NUM_BITS:1] == i_Seed_Data) ? 1'b1 : 1'b0;


endmodule


行为仿真

`timescale 1ns / 1ps
module lfsr_tb ();
 
  parameter c_NUM_BITS = 4;
   
  reg r_Clk = 1'b0;
   
  wire [c_NUM_BITS-1:0] w_LFSR_Data;
  wire w_LFSR_Done;
   
  lfsr #(.NUM_BITS(c_NUM_BITS)) LFSR_inst
         (.i_Clk(r_Clk),
          .i_Enable(1'b1),
          .i_Seed_DV(1'b0),
          .i_Seed_Data({c_NUM_BITS{1'b0}}), // Replication
          .o_LFSR_Data(w_LFSR_Data),
          .o_LFSR_Done(w_LFSR_Done)
          );
  
  always @(*)
    #10 r_Clk <= ~r_Clk; 
   
endmodule // LFSR_TB

仿真波形:


LFSR仿真波形

多功能计数器

有了上面三种计数器的单独设计,下面该考虑组合起来了,是用什么样的方式组合?
用户可以选择,可以通过定义条件编译的方式,定义了某个宏就执行某种计数器,计数位宽可选择,通过参数化的方式实现。

电路设计

本设计用到了上面的三个模块,例化到本模块使用;

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Create Date: 2020/06/02 16:22:52
// Module Name: versatile_counter
//////////////////////////////////////////////////////////////////////////////////

// `define LFSR_MACRO
`define GRAY
// `define BIN
module versatile_counter #(parameter N_BITS = 4)(
    input i_clk,
    input i_rst,
    output [N_BITS - 1 : 0] o_out,
    output o_out_done

    );

    `ifdef LFSR_MACRO

    lfsr #(.NUM_BITS(N_BITS)) LFSR_inst
         (.i_Clk(i_clk),
          .i_Enable(1'b1),
          .i_Seed_DV(1'b0),
          .i_Seed_Data({N_BITS{1'b0}}), // Replication
          .o_LFSR_Data(o_out),
          .o_LFSR_Done(o_out_done)
          );


    `elsif GRAY

    gray_counter #(.N_BITS(N_BITS))
    inst_gray_cnt(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_cnt(o_out),
        .o_cnt_done(o_out_done)
        );


    `else
    binary_counter #(.N_BITS(N_BITS))
    inst_bin_cnt(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_cnt(o_out),
        .o_cnt_done(o_out_done)
        );

    `endif


endmodule

这里约定定义了宏GRAY,就是跑格雷码的代码,定义了宏BIN,就是跑二进制的代码,定义了LFSR_MACRO,就是跑LFSR的程序。

行为仿真

先假设定义了宏GRAY,仿真程序通用,如下:

`timescale 1ns/1ps
module sim_versatile_counter;

    parameter N_BITS = 4;
    reg i_clk;
    reg i_rst;
    wire [N_BITS - 1 : 0] o_out;
    wire o_out_done;

    initial begin
        i_clk = 0;
        forever begin
            # 2 i_clk = ~ i_clk;
        end
    end

    initial begin
        i_rst = 1;

        # 8
        i_rst = 0;

    end

    versatile_counter #(.N_BITS(N_BITS))
    inst_vc(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_out(o_out),
        .o_out_done(o_out_done)
        );
endmodule

仿真波形 :

格雷码计数器

确实是格雷码计数器 ,放大:


局部放大
局部放大

如果定义了宏BIN,

// `define LFSR_MACRO
// `define GRAY
`define BIN

则仿真图如下:


二进制计数器

放大观测:

局部放大

局部放大

如果定义了宏LFSR_MACRO,则输出LFSR计数:

 `define LFSR_MACRO
// `define GRAY
//`define BIN
LFSR

局部放大
局部放大

生成语句实现方式

这里使用生成语句,generate case来实现多功能计数器,我们需要定义一个参数SEL,当SEL为0的时候,输出为LFSR;当SEL为1时,输出为格雷码计数器;当SEL为2时候,输出为二进制计数器。

电路设计

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Create Date: 2020/06/02 16:22:52
// Design Name: 
// Module Name: versatile_counter
// Revision 0.01 - File Created
// Additional Comments:
// Reborn Lee
//////////////////////////////////////////////////////////////////////////////////
module versatile_counter #(
    parameter N_BITS = 4,
    parameter SEL = 0

    )(
    input i_clk,
    input i_rst,
    output [N_BITS - 1 : 0] o_out,
    output o_out_done

    );


generate

    case(SEL)

        0: begin
            lfsr #(.NUM_BITS(N_BITS)) LFSR_inst
         (.i_Clk(i_clk),
          .i_Enable(1'b1),
          .i_Seed_DV(1'b0),
          .i_Seed_Data({N_BITS{1'b0}}), // Replication
          .o_LFSR_Data(o_out),
          .o_LFSR_Done(o_out_done)
          );
        end
        1: begin
            gray_counter #(.N_BITS(N_BITS))
        inst_gray_cnt(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_cnt(o_out),
        .o_cnt_done(o_out_done)
        );
        end
        2: begin
            binary_counter #(.N_BITS(N_BITS))
        inst_bin_cnt(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_cnt(o_out),
        .o_cnt_done(o_out_done)
        );

        end


    endcase

endgenerate



endmodule

行为仿真

SEL为0,也即LFSR:
仿真文件例化改为:

    versatile_counter #(
        .N_BITS(N_BITS),
        .SEL(0)
        )
    inst_vc(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_out(o_out),
        .o_out_done(o_out_done)
        );
LFSR

放大:


局部放大

局部放大

局部放大

SEL为1,也即格雷码计数器:
仿真文件例化改为:

    versatile_counter #(
        .N_BITS(N_BITS),
        .SEL(1)
        )
    inst_vc(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_out(o_out),
        .o_out_done(o_out_done)
        );
格雷码计数器

放大:


局部放大

局部放大

SEL为2,也即 二进制计数器:

仿真文件例化改为:

    versatile_counter #(
        .N_BITS(N_BITS),
        .SEL(2)
        )
    inst_vc(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_out(o_out),
        .o_out_done(o_out_done)
        );
二进制计数器

放大:


局部放大

局部放大

注意事项

关于多功能计数器的注意事项,这里不做多说,
在debug的过程中,可以先进行elaborated design,

RTL检测

如果没有错误,在进行行为仿真;
其次需要注意,在宏定义过程中,别忘了`这个符号。
就这样吧,感觉如此实现也不是太难,我还记得使用OPENCORES里的那个设计实例,编译预处理指令一大堆,看得我头皮发麻,最关键的是我还没编译通过。

工程要不要分享了呢?暂时算了吧,反正代码已经贴出来了,实在需要的可以说一声。
工具 :vivado 2019.1

参考资料

参考资料1
参考资料2
参考资料3

交个朋友

个人微信公众号:FPGA LAB
FPGA/IC技术交流2020

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350