在verilog程序设计中,我们往往要对一个频率进行任意分频,而且占空比也有一定的要求这样的话,对于程序有一定的要求,现在我在前人经验的基础上做一个简单的总结,实现对一个频率的任意占空比的任意分频。
比如:我们FPGA系统时钟是50M Hz,而我们要产生的频率是880Hz,那么,我们需要对系统时钟进行分频。我们很容易想到用计数的方式来分频:
50000000/880 = 56818
这个数字不是2的整幂次方,那么怎么办呢?我们可以设定一个参数,让它到56818的时候重新计数不就完了吗?呵呵,程序如下:
module div(clk, clk_div);
input clk;
output clk_div;
reg [15:0] counter;
always @(posedge clk)
if(counter==56817) counter <= 0;
else counter <= counter+1;
assign clk_div = counter[15];
endmodule
下面我们来算一下它的占空比:我们清楚地知道,这个输出波形在counter为0到32767的时候为低,在32767到56817的时候为高,占空比为40%多一些,如果我们需要占空比为50%,那么怎么办呢?不用急,慢慢来。
我们再设定一个参数,使它为56817的一半,使达到它的时候波形翻转,那不就完了吗?呵呵,再看看:
module div(clk, clk_div); input clk; output clk_div; reg [14:0] counter; always @(posedge clk) if(counter==28408) counter <= 0; else counter <= counter+1; reg clk_div; always @(posedge clk) if(counter==28408) clk_div <= ~clk_div; endmodule
占空比是不是神奇地变成50%了呢?呵呵。
继续让我们来看如何实现任意占空比,比如还是由50 M 分频产生880 Hz,而分频得到的信号的占空比为30%。
56818×30%=17045
module div(clk,reset,clk_div,counter); input clk,reset; output clk_div; output [15:0] counter; reg [15:0] counter; reg clk_div; always @(posedge clk) if(!reset) counter <= 0; else if(counter==56817) counter <= 0; else counter <= counter+1; always @(posedge clk) if(!reset) clk_div <= 0; else if(counter<17045) clk_div <= 1; else clk_div <= 0; endmodule
三分频的Verilog实现
//很实用也是笔试面试时常考的,已经经过仿真
占空比要求50%和不要求占空比差别会很大,先看一个占空比50%的描述
module div3(CLKIN,CLKOUT,RESETn); input CLKIN,RESETn; output CLKOUT; //internal counter signals reg[1:0] count_a; reg[1:0] count_b; reg CLKOUT; always @(negedge RESETn or posedge CLKIN) begin if (RESETn==1'b0) count_a<=2'b00; else if (count_a==2'b10) count_a<=2'b00; else count_a<=count_a+1; end always @(negedge RESETn or negedge CLKIN) begin if (RESETn==1'b0) count_b<=2'b0; else if (count_b==2'b10) count_b<=2'b00; else count_b<=count_b+1; end always @(count_a or count_b or RESETn) begin if (RESETn==1'b0) CLKOUT=1'b0; else if((count_a+count_b==4)||(count_a+ count_b==1)) CLKOUT=~CLKOUT; end endmodule
0 1 2 0 1 2
/ / / /
0 1 2 0 1 2
下面是一个非50%的描述,只用了上升沿
module div3(CLKIN,CLKOUT,RESETn); input CLKIN,RESETn; output CLKOUT; wire d; reg q1,q2; wire CLKOUT; always @(negedge RESETn or posedge CLKIN) begin if (RESETn==1'b0) q1<=1'b0; else q1<=d; end always @(negedge RESETn or posedge CLKIN) begin if (RESETn==1'b0) q2<=1'b0; else q2<=q1; end assign d=~q1 & ~q2; assign CLKOUT=q2; endmodule
占空比不是50%,只用了单沿触发器,寄存器输出。
至于其他奇数要求50%的或者不要求的占空比的,都可以参照上面两个例子做出。
占空比为50%的一个更好的实现。
module div3(CLKIN,CLKOUT,RESETn); input CLKIN,RESETn; output CLKOUT; //internal counter signals reg[1:0] count_a; reg b,c; //reg CLKOUT; wire CLKOUT; always @(negedge RESETn or posedge CLKIN) begin if (RESETn==1'b0) count_a<=2'b00; else if (count_a==2'b10) count_a<=2'b00; else count_a<=count_a+1; end always @(negedge RESETn or negedge CLKIN) begin if (RESETn==1'b0) b<=1'b0; else if (count_a==2'b01) b<=2'b0; else b<=1'b1; end always @(negedge RESETn or posedge CLKIN) begin if (RESETn==1'b0) c<=1'b0; else if (count_a==2'b10) c<=1'b1; else if (count_a==2'b01) c<=1'b0; end assign CLKOUT=b & c; endmodule
时钟选择器的Verilog写法!
CPRI有3种数据时钟,61.44M 122.88M 245.76M,需要模块能够根据外部的速率指示信号(一个2位的输入信号,由模块外部给定)选择其中的一种时钟作为模块的工作时钟
但由于所选用的FPGA工作频率关系,不能超过400M,无法通过寄存器方式实现时钟源的选择.
使用双边触发的方式将最高的频率进行分频,代码如下,已经通过ModelSim的仿真,可以实现。
module clk_div( reset, //复位信号 data_rate, //数据速率指示 clk2, //245.76M的时钟输入 time1, //分频计数器 clk //选择后的时钟输出 ); input reset; input [1:0] data_rate; input clk2; output [1:0] time1; output clk; reg [1:0] time1; reg clk; always@(clk2 or reset) if(reset) begin time1=2'b00; clk=1'b0; end else begin time1=time1+1'b1; case(data_rate) 2'b00:if(time1==2'b00) clk=~clk; else clk=clk; 2'b01:if(time1[0]==1'b1) clk=~clk; else clk=clk; 2'b10:clk=clk; 2'b11:clk=~clk; endcase end endmodule
verilog 实现gray码计数器
//16位gray码计数器,gary码状态改变时候每次只改变一个bit //,可以有效防止竞争和毛刺的产生。 module gray_counter(clk,clr,start,stop,q,cout); input clk; input clr; input start,stop; output reg [3:0] q; output reg cout; reg flag=1; reg [3:0] s,next_s; //parameter S0=0, S1=1, S2=2, S3=3, S4=4, S5=5, S6=6, S7=7; //parameter S8=8, S9=9, S10=10, S11=11, S12=12, S13=13, S14=14, S15=15; parameter s0=0000, s1=0001, s2=0011, s3=0010; parameter s4=0110, s5=0111, s6=0101, s7=0100; parameter s8=1100, s9=1101, s10=1111,s11=1110; parameter s12=1010, s13=1011, s14=1001, s15=1000; always @(posedge clk) begin if (clr) s <= s0; else s <= next_s; end /*always @(posedge start or posedge stop) begin if(start) flag=1; else if (stop) flag=0; end*/ always @(s or flag) /*该进程实现状态的转换*/ begin case (s) s0: begin if (flag) next_s <=s1; // else next_s <=s0; end s1: begin if (flag) next_s <= s2; end s2: begin if (flag) next_s <=s3; ////else next_s <=s0; end s3: begin if (flag) next_s <=s4; //else next_s <=s3; end s4: begin if (flag) next_s <= s5; end s5: begin if (flag) next_s <=s6; //else next_s <=s0; end s6: begin if (flag) next_s <=s7; //else next_s <=s3; end s7: begin if (flag) next_s <=s8; //else next_s <=s0; end s8: begin if (flag) next_s <= s9; end s9: begin if (flag) next_s <=s10; ////else next_s <=s0; end s10: begin if (flag) next_s <=s11; //else next_s <=s3; end s11: begin if (flag) next_s <= s12; end s12: begin if (flag) next_s <=s13; //else next_s <=s0; end s13: begin if (flag) next_s <=s14; //else next_s <=s3; end s14: begin if (flag) next_s <=s15; ////else next_s <=s0; end s15: begin if (flag) next_s <=s0; //else next_s <=s3; end default: next_s <=s0; /*default语句*/ endcase end always @(s) /*该进程定义组合逻辑(FSM的输出)*/ begin case(s) s0: q=0; s1: q=4; s2: q=2; s3: q=3; s4: q=4; s5: q=5; s6: q=6; s7: q=7; s8: q=8; s9: q=9; s10: q=10; s11: q=11; s12: q=12; s13: q=13; s14: q=14; s15: q=15; default:q=0; /*default语句,避免锁存器的产生*/ endcase end always @(s) begin if (s==s15) cout=1;//assign cout=q; else cout=0; end endmodule
审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !