![20fc1e8ef476e086d6dc83ec45945ecf.png](https://i-blog.csdnimg.cn/blog_migrate/57d07cfa8cfd7ebae5bfdd86e19475c2.jpeg)
写在前面
相关博文 博客首页 注:学习交流使用!
正文
多功能计数器,英文名为:多功能计数器;所谓多功能,这里包括二进制计数,格雷码计数以及线性反馈移位寄存器(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
仿真图:
![6ab7a57e3fb4069bd4b35242956c1c64.png](https://i-blog.csdnimg.cn/blog_migrate/94f6913c89a6762ac0e9ac250ee40333.jpeg)
普通的格雷码计数器
任意位宽的格雷码计数器,实现的方式通常是设计一个普通的二进制计数器,同时将计数结果转化为格雷码。 二进制与格雷码的转换方式,详情见:格雷码和二进制转换。 为了方便给出原理图:
![bf400725c3ec2a72b41ec24cc22abdbe.png](https://i-blog.csdnimg.cn/blog_migrate/08626d82b32766b94d67a1940d4ffedd.png)
伪代码描述为:
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
行为仿真波形:
![1bfb36f14f437a32a2f9e7c5e059550a.png](https://i-blog.csdnimg.cn/blog_migrate/9755c1f8480f757eaee8691b374cd7a0.jpeg)
局部放大:
![afd23428cb95341d6d1625eb3e530a9a.png](https://i-blog.csdnimg.cn/blog_migrate/7cc40456816de8cbad2841e613ab65da.png)
![55daaa878fbb3f01101b7b46b6c0b447.png](https://i-blog.csdnimg.cn/blog_migrate/f720cfc7722bac75ed096ab538ef608d.jpeg)
LFSR
这个请参考上篇博文,单独做了一篇博客: Verilog设计实例(1)线性反馈移位寄存器(LFSR)
为了方便不跳转另外一个链接,这里给出设计:
电路设计
`timescale 1ns / 1ps
//
// Company:
// Engineer: Reborn Lee
// Create Date: 2020/06/01 12:50:38
// Design Name:
// Module Name: lfsr
// Revision 0.01 - File Created
// Additional Comments:
//
//
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
仿真波形:
![31de99c17767118f6a3c41534b08893d.png](https://i-blog.csdnimg.cn/blog_migrate/2f4990229b03789a334b95980f7eb2a5.png)
多功能计数器
有了上面三种计数器的单独设计,下面该考虑组合起来了,是用什么样的方式组合? 用户可以选择,可以通过定义条件编译的方式,定义了某个宏就执行某种计数器,计数位宽可选择,通过参数化的方式实现。
电路设计
本设计用到了上面的三个模块,例化到本模块使用;
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/06/02 16:22:52
// Design Name:
// Module Name: versatile_counter
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
// `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
仿真波形 :
![7fd92f93e760ca0d669bba4beb2d0efe.png](https://i-blog.csdnimg.cn/blog_migrate/f3cb212c04520848d1748f72e1028cd4.png)
确实是格雷码计数器 ,放大:
![5ade08df422e4b3a6709a8d18ec23b49.png](https://i-blog.csdnimg.cn/blog_migrate/f44c0b61410f63ca48e7c0c3bdb9d151.png)
![4edb7307c6eb23f5f213be9cce87c222.png](https://i-blog.csdnimg.cn/blog_migrate/19249db8aa66ed8a7cd2f61f7bb8f85e.png)
如果定义了宏BIN,
// `define LFSR_MACRO
// `define GRAY
`define BIN
则仿真图如下:
![e243d546da9285b3533d762442c8abce.png](https://i-blog.csdnimg.cn/blog_migrate/dc68fe5a59943a7b6a9fae47e966f24c.jpeg)
放大观测:
![81a37ccee7054f5f8c7101b78e92f19b.png](https://i-blog.csdnimg.cn/blog_migrate/a3cde51d9e99ec28e58bd4df8b5d5737.png)
![b5850c9e4a1bb0186b40fa93eefc0940.png](https://i-blog.csdnimg.cn/blog_migrate/d0a239eeb473dab4174d131933e10e60.png)
如果定义了宏LFSR_MACRO,则输出LFSR计数:
`define LFSR_MACRO
// `define GRAY
//`define BIN
![c380dfdcad802bf7f093c1cbe29ee3c4.png](https://i-blog.csdnimg.cn/blog_migrate/3b21ae8db06154c1c0d7341559e37510.jpeg)
![98f9e9b90ebab1d84e86816f658ed324.png](https://i-blog.csdnimg.cn/blog_migrate/ce5b9b0db4397de9e3b2ac7106e2e32e.png)
![543ae2146a2fcf79a8ab7fa70642144d.png](https://i-blog.csdnimg.cn/blog_migrate/5c498ea5bb88c1efd638f979f3b69daa.png)
生成语句实现方式
这里使用生成语句,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)
);
![af44583026855ae6ffd89533ba720cf5.png](https://i-blog.csdnimg.cn/blog_migrate/93dd07978638bc307e56dbd8736307fe.jpeg)
放大:
![46e2ee9afbe815efc4697f126b4555ae.png](https://i-blog.csdnimg.cn/blog_migrate/1f9aa1ed7d0ece54dcef9079ca2b700b.png)
![8f566c39dcf29ae3b02b4b1373dc3d2c.png](https://i-blog.csdnimg.cn/blog_migrate/a9db26184115b18f5384a1ae08d02abe.png)
![932827ef1a891c381c959b97ee553cd0.png](https://i-blog.csdnimg.cn/blog_migrate/70b09cec9ab1d740e2a70560ccc979aa.png)
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)
);
![2f45e98f520b66eae408cdc44bed1e35.png](https://i-blog.csdnimg.cn/blog_migrate/2558e9a72d5022167626c07a7ee0c3ac.jpeg)
放大:
![20fc2650906e47be245109700f69ff0c.png](https://i-blog.csdnimg.cn/blog_migrate/3835f4c184f42395ea105dec65430767.png)
![ffb26f4a77b7433e1d914611e5a932eb.png](https://i-blog.csdnimg.cn/blog_migrate/a790eae7a999ff1b78ad49fd6fab4859.png)
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)
);
![a525576ab557fa530004bcce30d8807c.png](https://i-blog.csdnimg.cn/blog_migrate/8bfba029660de01cc142603206122487.jpeg)
放大:
![e4aa7e4813d134ab99cc3f14ece4823b.png](https://i-blog.csdnimg.cn/blog_migrate/c864ec115445b98467bf02fc013a44d3.png)
![1051b7f348a589864ee143fc7b7033cf.png](https://i-blog.csdnimg.cn/blog_migrate/77d9abb0fa46643a0a8d3b560502a1ad.png)
注意事项
关于多功能计数器的注意事项,这里不做多说, 在debug的过程中,可以先进行elaborated design,
![fe39b263c4e516a3a797443b985fc124.png](https://i-blog.csdnimg.cn/blog_migrate/5ad268a13d4a02bb52012aebf5b3652c.png)
如果没有错误,在进行行为仿真; 其次需要注意,在宏定义过程中,别忘了`这个符号。 就这样吧,感觉如此实现也不是太难,我还记得使用OPENCORES里的那个设计实例,编译预处理指令一大堆,看得我头皮发麻,最关键的是我还没编译通过。
工程要不要分享了呢?暂时算了吧,反正代码已经贴出来了,实在需要的可以说一声。 工具 :vivado 2019.1
参考资料
参考资料1 参考资料2 参考资料3
交个朋友
FPGA/IC技术交流2020