FPGA——时钟分频

时钟分频也是笔试题里面经常出现的,(我已经遇到过了),看完这一篇,分分钟掌握!
目录
偶数分频
D触发器级联法
计数器法
奇数分频
占空比50% 的奇数分频
占空比无要求的奇数分频
任意小数分频

偶数分频 FPGA——时钟分频
文章图片
如上图输入给clk,输出将其变成周期为2倍的clk_out2, 将其变成周期为4倍的clk_out4,将其变成周期为8倍的clk_out8,占空比为50%
D触发器级联法
对于偶数分频电路,在不考虑时钟同步时钟延迟的问题时,直接使用D触发器级联来实现:关键在于,上升沿才能触发翻转,但是一个周期只有一个上升沿,因此周期翻倍
FPGA——时钟分频
文章图片

`timescale 1ns/1nsmodule even_div ( inputwire rst , inputwire clk_in, outputwire clk_out2, outputwire clk_out4, outputwire clk_out8 ); reg clk_out2_r, clk_out4_r, clk_out8_r; always@(posedge clk_in or negedge rst) begin if(~rst) clk_out2_r <= 0; else clk_out2_r <= ~clk_out2_r; endalways@(posedge clk_out2 or negedge rst) begin if(~rst) clk_out4_r <= 0; else clk_out4_r <= ~clk_out4_r; endalways@(posedge clk_out4 or negedge rst) begin if(~rst) clk_out8_r <= 0; else clk_out8_r <= ~clk_out8_r; endassign clk_out2 = clk_out2_r; assign clk_out4 = clk_out4_r; assign clk_out8 = clk_out8_r; endmodule

计数器法
2、4、8恰好是2的次方,可构造一个3位计数器,依靠第0位,第1位,第2位的变化速度来实现:
2分频的输出=cnt第0位(由于计数器在clk=高电平时才+1,因此本身就是2倍的周期)
【FPGA——时钟分频】4分频输出=cnt第1位
8分频输出=cnt第2位
eg 4分频:000 001 010 011 100 101 110 111 000
`timescale 1ns/1ns module even_div ( inputwire rst , inputwire clk_in, outputwire clk_out2, outputwire clk_out4, outputwire clk_out8 ); reg [2:0]cnt; always@(posedge clk_in or negedge rst) if(!rst) cnt <= 3'b111; //初始赋全1 else cnt <= cnt+1; assign clk_out2 = ~cnt[0]; //根据波形,一开始全为高电平 assign clk_out4 = ~cnt[1]; assign clk_out8 = ~cnt[2]; endmodule


奇数分频 占空比50% 的奇数分频
FPGA——时钟分频
文章图片
设计一个同时输出7分频的时钟分频器,占空比要求为50%
对于奇数分频电路,主要难点在于50%占空比的实现。单触发沿在奇数分频中是没有办法实现50%占空比的,因此需要考虑使用双边沿加组合逻辑实现50%占空比
FPGA——时钟分频
文章图片

设置计数器,计数器从0→6,当计数器==3,上升沿触发得到clk_pos,下降沿触发得到clk_neg,
而输出的7分频clk,上升沿在clk_neg触发,下降沿在clk_pos触发,是clk_neg和clk_pos进行或运算的结果
根据这个思路,我们可以得到任意奇数分频,占空比为50%的输出,修改N值即可,代码如下:
`timescale 1ns/1nsmodule odo_div_or #(parameter N = 7) ( inputwirerst , inputwireclk_in, outputwireclk_out7 ); reg [3:0]cnt ; always @(posedge clk_in or negedge rst) begin if (!rst) begin cnt<= 'b0 ; end else if (cnt == N-1) begin cnt<= 'b0 ; end else begin cnt<= cnt + 1'b1 ; end endregclkp ; always @(posedge clk_in or negedge rst) begin if (!rst) begin clkp <= 1'b0 ; end else if (cnt == (N>>1)) begin clkp <= 1 ; end else if (cnt == N-1) begin clkp <= 0 ; end endregclkn ; always @(negedge clk_in or negedge rst) begin if (!rst) begin clkn <= 1'b0 ; end else if (cnt == (N>>1) ) begin clkn <= 1 ; end else if (cnt == N-1) begin clkn <= 0 ; end endassign clk_out7 = clkp | clkn ; endmodule

占空比无要求的奇数分频
实现一个5分频clk,占空比可修改
由于一个cnt其实=2个clk_in ,所以在占空比不要求的情况下,即使是奇数分频也=一个偶数,我们可以把这个偶数分成2个偶数的和,(例如5分频相当于10clk_in,10=4+6,可以4clk_in低电平,6clk_in高电平)(而要求占空比的时候,10=5+5,需要参考上面的做法)
一个完整的clk周期要包含5个clk_in,但是占空比不要求,那么设置一个计数器cnt,计数范围0-4,每个clk_in周期加1。当计数器数值在0~1时,clk_out5==1;当计数器数值在2~4时,clk_out5==0。这样就实现了周期为5、占空比为3/5 ?的clk_out5
`timescale 1ns/1nsmodule odd_div ( inputwire rst , inputwire clk_in, outputwire clk_out5 ); reg [2:0] cnt; reg clk_out5_r; always@(posedge clk_in or negedge rst) begin if(~rst) clk_out5_r <= 0; else clk_out5_r <=1; endalways@(posedge clk_in or negedge rst) begin if(~rst) cnt <= 0; else cnt <= cnt==4? 0: cnt+1; endassign clk_out5 = clk_out5_r; endmodule

任意小数分频 FPGA——时钟分频
文章图片

FPGA——时钟分频
文章图片

小数分频器是分频器中最难的一种,而且不能像整数分频那样得到均匀且占空比为50%的分频输出
FPGA——时钟分频
文章图片

FPGA——时钟分频
文章图片

FPGA——时钟分频
文章图片


`timescale 1ns/1nsmodule div_M_N( inputwire clk_in, inputwire rst, output wire clk_out ); parameter M_N = 8'd87; parameter c89 = 8'd24; // 8/9时钟切换点 parameter div_e = 5'd8; //偶数周期 parameter div_o = 5'd9; //奇数周期 //*************code***********// reg [3:0] clk_cnt; reg [6:0] cyc_cnt; reg div_flag; reg clk_out_r; always@(posedge clk_in or negedge rst) begin if(~rst) clk_cnt <= 0; else if(~div_flag) clk_cnt <= clk_cnt==(div_e-1)? 0: clk_cnt+1; else clk_cnt <= clk_cnt==(div_o-1)? 0: clk_cnt+1; endalways@(posedge clk_in or negedge rst) begin if(~rst) cyc_cnt <= 0; else cyc_cnt <= cyc_cnt==(M_N-1)? 0: cyc_cnt+1; endalways@(posedge clk_in or negedge rst) begin if(~rst) div_flag <= 0; else div_flag <= cyc_cnt==(M_N-1)||cyc_cnt==(c89-1)? ~div_flag: div_flag; endalways@(posedge clk_in or negedge rst) begin if(~rst) clk_out_r <= 0; else if(~div_flag) clk_out_r <= clk_cnt<=((div_e>>2)+1); else clk_out_r <= clk_cnt<=((div_o>>2)+1); endassign clk_out = clk_out_r; //*************code***********// endmodule


    推荐阅读