寫在前面
相關(guān)博文
博客首頁
注:學(xué)習(xí)交流使用!
正文
多功能計數(shù)器司草,英文名為:Versatile Counter没陡;所謂多功能,這里包括二進制計數(shù)番甩,格雷碼計數(shù)以及線性反饋移位寄存器(LFSR)三種畴蒲,本文通過從普通的計數(shù)器開始,也就是單個功能的計數(shù)器開始对室,一步一步過渡到多功能計數(shù)器模燥。
作為對以下相關(guān)博文的延伸練習(xí):
Verilog設(shè)計實例(1)線性反饋移位寄存器(LFSR)
FPGA設(shè)計心得(8)Verilog中的編譯預(yù)處理語句
普通的二進制計數(shù)器
這個作為開頭,不必多說掩宜,計數(shù)就完事了蔫骂。
電路設(shè)計
設(shè)計文件:
`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
仿真圖:
普通的格雷碼計數(shù)器
任意位寬的格雷碼計數(shù)器,實現(xiàn)的方式通常是設(shè)計一個普通的二進制計數(shù)器牺汤,同時將計數(shù)結(jié)果轉(zhuǎn)化為格雷碼辽旋。
二進制與格雷碼的轉(zhuǎn)換方式,詳情見:格雷碼和二進制轉(zhuǎn)換檐迟。
為了方便給出原理圖:
偽代碼描述為:
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]};
電路設(shè)計
一種簡單的設(shè)計方式為:
`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塊內(nèi)补胚,因此使用非阻塞賦值。
又和二進制計數(shù)在一起always內(nèi)追迟,且緊鄰分布溶其,因此計數(shù)相較于二進制慢一拍,但毫無影響(不影響計數(shù)總數(shù))敦间。
注: 三種二進制轉(zhuǎn)換為格雷碼的實現(xiàn)原理一致瓶逃,效果等價。
行為仿真
TestBench設(shè)計:
`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設(shè)計實例(1)線性反饋移位寄存器(LFSR)
為了方便不跳轉(zhuǎn)另外一個鏈接厢绝,這里給出設(shè)計:
電路設(shè)計
`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
仿真波形:
多功能計數(shù)器
有了上面三種計數(shù)器的單獨設(shè)計,下面該考慮組合起來了带猴,是用什么樣的方式組合昔汉?
用戶可以選擇,可以通過定義條件編譯的方式拴清,定義了某個宏就執(zhí)行某種計數(shù)器靶病,計數(shù)位寬可選擇,通過參數(shù)化的方式實現(xiàn)贷掖。
電路設(shè)計
本設(shè)計用到了上面的三個模塊嫡秕,例化到本模塊使用;
`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的程序。
行為仿真
先假設(shè)定義了宏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
仿真波形 :
確實是格雷碼計數(shù)器 ,放大:
如果定義了宏BIN且轨,
// `define LFSR_MACRO
// `define GRAY
`define BIN
則仿真圖如下:
放大觀測:
如果定義了宏LFSR_MACRO浮声,則輸出LFSR計數(shù):
`define LFSR_MACRO
// `define GRAY
//`define BIN
生成語句實現(xiàn)方式
這里使用生成語句,generate case來實現(xiàn)多功能計數(shù)器旋奢,我們需要定義一個參數(shù)SEL泳挥,當(dāng)SEL為0的時候,輸出為LFSR至朗;當(dāng)SEL為1時屉符,輸出為格雷碼計數(shù)器;當(dāng)SEL為2時候锹引,輸出為二進制計數(shù)器矗钟。
電路設(shè)計
`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)
);
放大:
SEL為1嫌变,也即格雷碼計數(shù)器:
仿真文件例化改為:
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吨艇,也即 二進制計數(shù)器:
仿真文件例化改為:
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)
);
放大:
注意事項
關(guān)于多功能計數(shù)器的注意事項,這里不做多說腾啥,
在debug的過程中秸应,可以先進行elaborated design,
如果沒有錯誤,在進行行為仿真碑宴;
其次需要注意软啼,在宏定義過程中,別忘了`這個符號延柠。
就這樣吧祸挪,感覺如此實現(xiàn)也不是太難,我還記得使用OPENCORES里的那個設(shè)計實例贞间,編譯預(yù)處理指令一大堆贿条,看得我頭皮發(fā)麻,最關(guān)鍵的是我還沒編譯通過增热。
工程要不要分享了呢整以?暫時算了吧,反正代碼已經(jīng)貼出來了峻仇,實在需要的可以說一聲公黑。
工具 :vivado 2019.1
參考資料
交個朋友
個人微信公眾號:FPGA LAB
FPGA/IC技術(shù)交流2020