在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
没有回复内容