Verilog HDL

Verilog HDL 訊號命名原則

訊號命名原則跟 coding style 一樣重要,尤其代碼量多的工程,一看訊號名稱就大概知道此訊號的特徵也方便 debug 。以下是我個人常用的方法。

除特殊訊號外FPGA輸入實際名稱用大寫,字跟字間用底線區分。
input SIGNAL_NAME

除特殊訊號外FPGA輸出實際名稱用大寫,字跟字間用底線區分。
output SIGNAL_NAME

sub module 輸入訊號前面加i
input iSignalName

sub module 輸出訊號前面加o
output oSignalName

reg 類型訊號前面加r
reg rSignalName

wire 類型訊號前面加w
wire wSignalName

輸入訊號為 vectors 類型訊號前面加 iv
input [7:0] ivSignalName

輸出訊號為 vectors 類型訊號前面加 ov
output [7:0] ovSignalName

reg 為 vectors 類型前面加rv
reg [7:0] rvSignalName

wire 為 vectors 類型前面加 wv
wire [7:0] wvSignalName

輸入訊號為 low active 前面加大寫 N
input SignalNameN

D Flip Flop 輸出端後面加 _q
reg rSignalName_q

D Flip Flop 輸入端後面加 _d
reg rSignalName_d


Generate 用法

Generate 好用之處在於可以依不同條件下產生不同實體模組這樣對 coding 的靈活性有很大的幫助。
(1) generate 有 generate for, generate if 及 generate case 三種用法。
(2) generate for 變數要以 genvar 關鍵字定義。
(3) generate for 內容需加 begin end 包起來。
(4) generate for 後面需加名子 ex : begin : name

以下是使用 generate for, generate if 及 generate case 的範例

  • 二位元加法器
`timescale 1ns/100ps ////// ... . .. --.. . - .... . -- --- -- . -. - ////// // Author : TienYao // Source Code Name : adder_2.v // Function Description: 2 bits Adder function // =========================================================== module adder_2 ( input [1:0] a, input [1:0] b, output [2:0] c ); assign c = a + b; endmodule
  • 四位元加法器
`timescale 1ns/100ps ////// ... . .. --.. . - .... . -- --- -- . -. - ////// // Author : TienYao // Source Code Name : adder_4.v // Function Description: 4 bits Adder function // =========================================================== module adder_4 ( input [3:0] a, input [3:0] b, output [4:0] c ); assign c = a + b; endmodule
  • 十六位元加法器
`timescale 1ns/100ps ////// ... . .. --.. . - .... . -- --- -- . -. - ////// // Author : TienYao // Source Code Name : adder_16.v // Function Description: 2 byte Adder function // =========================================================== module adder_16 ( input [15:0] a, input [15:0] b, output [16:0] c ); assign c = a + b; endmodule
  • 八位元計數器
`timescale 1ns/100ps ////// ... . .. --.. . - .... . -- --- -- . -. - ////// // Author : TienYao // Source Code Name : counter_width_8.v // Function Description: counter // =========================================================== module counter_width_8 #(parameter width = 8) ( input clk, input rstN, output [width - 1 : 0] rvCounterOut ); reg [width - 1 : 0] rvCounter_d; reg [width - 1 : 0] rvCounter_q; always @(posedge clk or negedge rstN) begin if(!rstN) rvCounter_q <= {width{1'b0}}; else rvCounter_q <= rvCounter_d; end always @(*) begin rvCounter_d = rvCounter_q + 1; end assign rvCounterOut = rvCounter_q; endmodule
  • 十六位元計數器
`timescale 1ns/100ps ////// ... . .. --.. . - .... . -- --- -- . -. - ////// // Author : TienYao // Source Code Name : counter_width_16.v // Function Description: counter // =========================================================== module counter_width_16 #(parameter width = 16) ( input clk, input rstN, output [width - 1 : 0] rvCounterOut ); reg [width - 1 : 0] rvCounter_d; reg [width - 1 : 0] rvCounter_q; always @(posedge clk or negedge rstN) begin if(!rstN) rvCounter_q <= {width{1'b0}}; else rvCounter_q <= rvCounter_d; end always @(*) begin rvCounter_d = rvCounter_q + 1; end assign rvCounterOut = rvCounter_q; endmodule
  • 使用範例
`timescale 1ns/100ps ////// ... . .. --.. . - .... . -- --- -- . -. - ////// // Author : TienYao // Source Code Name : generate_test.v // Function Description: generate method test // =========================================================== module generate_test #( parameter length_for = 8, parameter select_if = 16, parameter select_case = 4 ) ( input CLK, input RESET_N, input [length_for - 1 : 0] INPUT_FOR, output [length_for - 1 : 0] OUTPUT_FOR, output [select_if - 1 : 0] OUTPUT_IF, input [select_case - 1 : 0] A_CASE, input [select_case - 1 : 0] B_CASE, output [select_case : 0] OUTPUT_CASE ); generate genvar i; for(i = 0; i <= length_for - 1 ; i = i + 1) begin : generate_for assign OUTPUT_FOR[i] = INPUT_FOR[i]; end endgenerate generate if(select_if == 8) begin wire [select_if - 1 : 0] wvCounterWidth; counter_width_8 #(.width(select_if)) counter_width_8_isnt ( .clk (CLK), .rstN (RESET_N), .rvCounterOut (wvCounterWidth) ); assign OUTPUT_IF = wvCounterWidth; end else begin wire [select_if - 1 : 0] wvCounterWidth; counter_width_16 #(.width(select_if)) counter_width_16_isnt ( .clk (CLK), .rstN (RESET_N), .rvCounterOut (wvCounterWidth) ); assign OUTPUT_IF = wvCounterWidth; end endgenerate generate case(select_case) 2: adder_2 adder_2_inst (.a(A_CASE), .b(B_CASE), .c(OUTPUT_CASE)); 4: adder_4 adder_4_inst (.a(A_CASE), .b(B_CASE), .c(OUTPUT_CASE)); 6: adder_6 adder_6_inst (.a(A_CASE), .b(B_CASE), .c(OUTPUT_CASE)); 8: adder_8 adder_8_inst (.a(A_CASE), .b(B_CASE), .c(OUTPUT_CASE)); default: adder_16 (.a(A_CASE), .b(B_CASE), .c(OUTPUT_CASE)); endcase endgenerate endmodule

計數器設計

FPGA 幾乎會用到計數器相關設計,如 Watch dog、 PWM 、除頻器等等。
而計數器設計大致會有二個條件 :
1.什麼條件下計數
2.什麼條件下重新計數
掌握了這二個條件就可以設計計數器,以下為一個簡單計數器設計。
(1)在 iEn 為 1 時計數器加一。
assign AddCnt = iEn;
(2)在計數器數到使用者設定的值時,下一個clock進來時重新計數。
assign EndCnt = AddCnt && (rvCnt_q == MOD — 1);
而 iClr 為 1 時計數器清 0,iLoad 為 1 時可填入計數初始值。
掌握了這二個條件就可以設計計數器了。

`timescale 1ns/100ps ////// ... . .. --.. . - .... . -- --- -- . -. - ////// // Author : TienYao // Source Code Name : counter.v // Function Description: Mod [3:0] Counter // =========================================================== module counter #(parameter MOD = 10) ( input clk, input rstN, input iClr, input iEn, input iLoad, input [3:0] iModValue, output oCntDone, output [3:0] ovCntValue ); wire AddCnt; wire EndCnt; reg [3:0] rvCnt_d , rvCnt_q; assign AddCnt = iEn; assign EndCnt = AddCnt && (rvCnt_q == MOD - 1); always @(posedge clk or negedge rstN) begin if(!rstN) rvCnt_q <= 4'h0; else rvCnt_q <= rvCnt_d; end always @(*) begin if(EndCnt || iClr) rvCnt_d = 4'h0; else if(iLoad) rvCnt_d = iModValue; else if(AddCnt) rvCnt_d = rvCnt_q + 1'b1; else rvCnt_d = rvCnt_q; end assign ovCntValue = rvCnt_q; assign oCntDone = AddCnt && (rvCnt_q == MOD - 1) ? 1'b1 : 1'b0; endmodule