verilog实战|verilog实现时钟的偶数与奇数分频

偶数分频 可以采用同步整数分频思路,使用摩尔状态机或者计数器(序列机)思想都可以,在这里提供一个同步7分频的摩尔状态机的实现思路:

module clk_divide( inputwireclk, inputwirerst_n,outputwireclk_o ); //==================1. 同步整数分频器====================// //基于摩尔状态机实现7分频(一段式状态机) 同样可以实现偶数分频 localparamS0=7'b0000_001,//S0状态:clk_o输出0 S1=7'b0000_010,//S1状态:clk_o输出0 S2=7'b0000_100,//S2状态:clk_o输出0 S3=7'b0001_000,//S3状态:clk_o输出0 S4=7'b0010_000,//S4状态:clk_o输出1 S5=7'b0100_000,//S5状态:clk_o输出1 S6=7'b1000_000; //S1状态:clk_o输出1reg[6:0]state; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= S0; clk_o <= 1'b0; end else begin case(state) S0:begin state <= S1; clk_o <= 1'b0; end S1:begin state <= S2; clk_o <= 1'b0; end S2:begin state <= S3; clk_o <= 1'b0; end S3:begin state <= S4; clk_o <= 1'b0; end S4:begin state <= S5; clk_o <= 1'b1; end S5:begin state <= S6; clk_o <= 1'b1; end S6:begin state <= S0; clk_o <= 1'b1; end default:begin state <= S0; clk_o <= 1'b1; end endcase end end

testbench如下所示:
`timescale 1ns/1ns`define CLK_CYCLE 20 module tb_clk_divide; regclk; regrst_n; wireclk_o; clk_divide u_clk_divide( .clk(clk), .rst_n(rst_n) ,.clk_o(clk_o) ); initial begin clk = 1'b0; rst_n = 1'b0; #201; rst_n = 1'b1; endalways #(`CLK_CYCLE / 2) clk = ~clk; endmodule

仿真波形为:
verilog实战|verilog实现时钟的偶数与奇数分频
文章图片

奇数分频 方法一 主要思路如下:
首先基于时钟的上升沿和时钟的下降沿构造两个计数器,两个计数器都是技术到分频的最大值置0.之后分别给予两个计数器,声明两个变量即clk_div1和clk_div2。两者在各自的时钟沿下,若小于最大计数值的一半,则拉高信号。否则拉低信号。最后将这两个变量信号相或就可以得到最后的分频时钟啦。
module clk_divide( inputwireclk, inputwirerst_n,outputwireclk_o ); //==================2. 奇数分频器=======================// //方法1 parameterCLK_DIV=7; //奇数分频的频数reg[3:0]cnt_pos; //上升沿计数器 reg[3:0]cnt_neg; //下降沿计数器 regclk_div1; //分频信号1 regclk_div2; //分频信号2always @(posedge clk or negedge rst_n) begin//时钟上升沿触发 if(!rst_n) begin cnt_pos <= 'd0; end else if(cnt_pos == CLK_DIV - 1'b1) begin//计数最大值置0 cnt_pos <= 'd0; end else cnt_pos <= cnt_pos + 1'b1; endalways @(posedge clk or negedge rst_n) begin if(!rst_n) begin clk_div1 <= 1'b0; end else if(cnt_pos < CLK_DIV / 2) begin//小于计数值的一半,拉高clk_div1 clk_div1 <= 1'b1; end else begin+ clk_div1 <= 1'b0; end endalways @(negedge clk or negedge rst_n) begin//时钟下降沿触发 if(!rst_n) begin cnt_neg <= 'd0; end else if(cnt_neg == CLK_DIV - 1) begin//计数到最大值置0 cnt_neg <= 'd0; end else begin cnt_neg <= cnt_neg + 1'b1; end endalways @(negedge clk or negedge rst_n) begin if(!rst_n) begin clk_div2 <= 1'b0; end else if(cnt_neg < CLK_DIV / 2) begin//小于计数值的一半,拉高clk_div2 clk_div2 <= 1'b1; end else begin clk_div2 <= 1'b0; end endassign clk_o = clk_div1 | clk_div2; //将clk_div1和clk_div2或运算

仿真截图:
verilog实战|verilog实现时钟的偶数与奇数分频
文章图片

方法二 【verilog实战|verilog实现时钟的偶数与奇数分频】第二个方法是来自《硬件架构的艺术》上的方法,基于其思路,实现代码如下,代码中有详细的注释,便不在赘述了。
module clk_divide( inputwireclk, inputwirerst_n,outputwireclk_o ); //方法2(来自《硬件架构的艺术》) //步骤1:创建由时钟上升沿触发的0-(N-1)的计数器,N为分频系数 parameterN=5; reg[3:0]cnt_up; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt_up <= 'd0; end else if(cnt_up == N-1) begin cnt_up <= 'd0; end else begin cnt_up <= cnt_up + 1'b1; end end//步骤2:使用两个触发器,按照如下方式产生使能信号 //tff1_en:在计数值为0时使能 //tff2_en:在计数值为(N+1)/2时使能 wiretff1_en; wiretff2_en; assign tff1_en = (cnt_up == 0) ? 1'b1 : 1'b0; assign tff2_en = (cnt_up == (N+1)/2) ? 1'b1 : 1'b0; //步骤三:产生以下信号 //div1:TFF1的输出--->由输入时钟的上升沿触发 //div2:TFF2的输出--->由输入时钟的下降沿触发 regdiv1; regdiv2; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin div1 <= 1'b0; end else if(tff1_en) begin div1 <= ~div1; end else begin div1 <= div1; end endalways @(negedge clk or negedge rst_n) begin if(!rst_n) begin div2 <= 1'b0; end else if(tff2_en) begin div2 <=~div2; end else begin div2 <= div2; end end//步骤四:将div1和div2信号异或 assign clk_o = div1 ^ div2; endmodule

仿真如下:
verilog实战|verilog实现时钟的偶数与奇数分频
文章图片

全部代码
//时钟分频器 //2022/09/01 //======================================================// //==================1. 同步整数分频器====================// //==================2. 奇数分频器=======================//module clk_divide( inputwireclk, inputwirerst_n,outputwireclk_o ); /* //==================1. 同步整数分频器====================// //基于摩尔状态机实现7分频(一段式状态机) 同样可以实现偶数分频 localparamS0=7'b0000_001,//S0状态:clk_o输出0 S1=7'b0000_010,//S1状态:clk_o输出0 S2=7'b0000_100,//S2状态:clk_o输出0 S3=7'b0001_000,//S3状态:clk_o输出0 S4=7'b0010_000,//S4状态:clk_o输出1 S5=7'b0100_000,//S5状态:clk_o输出1 S6=7'b1000_000; //S1状态:clk_o输出1reg[6:0]state; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= S0; clk_o <= 1'b0; end else begin case(state) S0:begin state <= S1; clk_o <= 1'b0; end S1:begin state <= S2; clk_o <= 1'b0; end S2:begin state <= S3; clk_o <= 1'b0; end S3:begin state <= S4; clk_o <= 1'b0; end S4:begin state <= S5; clk_o <= 1'b1; end S5:begin state <= S6; clk_o <= 1'b1; end S6:begin state <= S0; clk_o <= 1'b1; end default:begin state <= S0; clk_o <= 1'b1; end endcase end end */ //==================2. 奇数分频器=======================// /* //方法1 parameterCLK_DIV=7; //奇数分频的频数reg[3:0]cnt_pos; //上升沿计数器 reg[3:0]cnt_neg; //下降沿计数器 regclk_div1; //分频信号1 regclk_div2; //分频信号2always @(posedge clk or negedge rst_n) begin//时钟上升沿触发 if(!rst_n) begin cnt_pos <= 'd0; end else if(cnt_pos == CLK_DIV - 1'b1) begin//计数最大值置0 cnt_pos <= 'd0; end else cnt_pos <= cnt_pos + 1'b1; endalways @(posedge clk or negedge rst_n) begin if(!rst_n) begin clk_div1 <= 1'b0; end else if(cnt_pos < CLK_DIV / 2) begin//小于计数值的一半,拉高clk_div1 clk_div1 <= 1'b1; end else begin+ clk_div1 <= 1'b0; end endalways @(negedge clk or negedge rst_n) begin//时钟下降沿触发 if(!rst_n) begin cnt_neg <= 'd0; end else if(cnt_neg == CLK_DIV - 1) begin//计数到最大值置0 cnt_neg <= 'd0; end else begin cnt_neg <= cnt_neg + 1'b1; end endalways @(negedge clk or negedge rst_n) begin if(!rst_n) begin clk_div2 <= 1'b0; end else if(cnt_neg < CLK_DIV / 2) begin//小于计数值的一半,拉高clk_div2 clk_div2 <= 1'b1; end else begin clk_div2 <= 1'b0; end endassign clk_o = clk_div1 | clk_div2; //将clk_div1和clk_div2或运算 *///方法2(来自《硬件架构的艺术》) //步骤1:创建由时钟上升沿触发的0-(N-1)的计数器,N为分频系数 parameterN=5; reg[3:0]cnt_up; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt_up <= 'd0; end else if(cnt_up == N-1) begin cnt_up <= 'd0; end else begin cnt_up <= cnt_up + 1'b1; end end//步骤2:使用两个触发器,按照如下方式产生使能信号 //tff1_en:在计数值为0时使能 //tff2_en:在计数值为(N+1)/2时使能 wiretff1_en; wiretff2_en; assign tff1_en = (cnt_up == 0) ? 1'b1 : 1'b0; assign tff2_en = (cnt_up == (N+1)/2) ? 1'b1 : 1'b0; //步骤三:产生以下信号 //div1:TFF1的输出--->由输入时钟的上升沿触发 //div2:TFF2的输出--->由输入时钟的下降沿触发 regdiv1; regdiv2; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin div1 <= 1'b0; end else if(tff1_en) begin div1 <= ~div1; end else begin div1 <= div1; end endalways @(negedge clk or negedge rst_n) begin if(!rst_n) begin div2 <= 1'b0; end else if(tff2_en) begin div2 <=~div2; end else begin div2 <= div2; end end//步骤四:将div1和div2信号异或 assign clk_o = div1 ^ div2; endmodule

    推荐阅读