Foreword
距离上一篇CSDN又有两个月了,这两个月学了好多,感觉这半年我还是有很大进步了的,没有一开始那么心力憔悴了。但是水管依旧到处漏……
最近和老虞还有刘同学吃饭,两个不同领域下的大佬,一个做考古,一个做AI,虽然聊的内容差异巨大,但两个人给我的感触都是,他们好厉害,小汤要好好努力!同时也让我想到了,世界可以分为哲学和数学,但最后都会归结于美学。我身边几个特别厉害的程序员,往往审美也是很好的,可能不是那么艺术,但做出来的东西一定是让人看着舒服的。
分频原理
首先推荐一个画波形原理图的在线网站WaveDrom Online Editor,他们家也有软件可以下载WaveDrom Download Github,使用方法在他家官网上也有介绍Tutorial,可以保存多种格式: json/png/svg,很好用。
在时序电路里,一般系统时钟都是高频的,不同外设对时钟频率的要求不同,所以需要通过分频来获得相应的时钟频率,一般都是将高频的时钟转换为低频的时钟。最简单的分频就是2分频,也就是把时钟频率减半,输出时钟和输入时钟上升沿对齐,波形图是这样的:
当然也会有遇到需要奇数分频的时候,这时候上升沿和下降沿都需要用到,3分频波形图是这样的:
上面两张图都是50%占空比的,有的时候我们只需要用输出时钟的上升沿,不需要考虑下降沿,这样的话我们的输出时钟只需要是单脉冲的就可以了,设计的时候会简单很多,单脉冲的3分频波形图是这样的:
50%占空比的偶数分频方法
偶数分频又可以分成两种,一种是2的倍数,一种是2的次方。当分频系数是2的次方时,分频的方法会简单很多。这里还是要吐槽一下,CSDN为什么不支持verilog语法!!!
2的倍数分频
原理:
- 计数器在时钟上升沿+1,加到对应分频系数后,清零
- clkOut在计数器记到(N-1)/2和N-1的地方翻转
- 当分频系数为0或1时,clkOut = clkIn
`define __DIVEVEN_V__ module DivEven #( parameter PRRWIDTH = 4 )( input wire clk, input wire rst, // negetive valid input wire en, input wire [PRRWIDTH-1:0] prr, output reg clkOut ); reg [PRRWIDTH-1:0] cnt; wire [PRRWIDTH-1:0] cntValue; wire direct; // when prr is 0 or 1, clkOut = clk directly assign direct = (prr == {PRRWIDTH{1'b0}}) | (prr == {{(PRRWIDTH-1){1'b0}}, 1'b1}); assign cntValue = direct ? {PRRWIDTH{1'b0}} : (prr - 1'b1); // positive edge count always @(posedge clk or negedge rst) begin if (!rst) begin cnt <= {PRRWIDTH{1'b0}}; end else if (!en) begin cnt <= {PRRWIDTH{1'b0}}; end else if (cnt == cntValue) begin cnt <= {PRRWIDTH{1'b0}}; end else begin cnt <= cnt + 1'b1; end end // overturn clkOut when cnt = (cntValue - 1) / 2 or cntValue always @(posedge clk or negedge rst) begin if (!rst) begin clkOut <= 1'b0; end else if (!en) begin clkOut <= 1'b0; end else if (direct) begin clkOut <= clk; end else if ((cnt == (cntValue >> 1)) || (cnt == cntValue)) begin clkOut <= !clkOut; end end endmodule
2的次方分频
原理:
- 计数器在时钟上升沿+1,加到全1后,清零
- cnt[0]的值是2分频,cnt[1]的值是4分频,cnt[2]的值是8分频
- 例如4分频,每两个clkIn上升沿,cnt[1]翻转一次,所以得到4分频后的clkOut
`define __DIVEVEN_V__ module DivEven ( input wire clk, input wire rst, // negetive valid input wire en, input wire [1:0] div, // div = 0 means prr = 0; // div = 1 means prr = 2; // div = 2 means prr = 4; // div = 3 means prr = 8; output wire clkOut ); reg [2:0] cnt; assign clkOut = (!rst | !en) ? 1'b0 : div == 2'b00 ? clk : div == 2'b01 ? cnt[0] : // prr = 2 div == 2'b10 ? cnt[1] : // prr = 4 div == 2'b11 ? cnt[2] : // prr = 8 1'b0; // positive edge count always @(posedge clk or negedge rst) begin if (!rst) begin cnt <= 3'b000; end else if (!en) begin cnt <= 3'b000; end else if (cnt == 3'b111) begin cnt <= 3'b000; end else begin cnt <= cnt + 1'b1; end end endmodule
50%占空比的任意整数分频方法
原理:
- 偶数的时候只用上升沿得到的时钟,奇数的时候将上升沿得到的时钟和下降沿得到的时钟并起来
- 上升沿得到的时钟和下降沿得到的时钟都是在计数器记到(N-1)/2和N-1的地方翻转
- 当分频系数为0或1时,clkOut = clkIn
`define __DIV50PCT_V__
module Div50Pct #(
parameter PRRWIDTH = 4
)(
input wire clk,
input wire rst, // negetive valid
input wire en,
input wire [PRRWIDTH-1:0] prr,
output wire clkOut
);
reg [PRRWIDTH-1:0] cntP;
reg [PRRWIDTH-1:0] cntN;
reg clkP;
reg clkN;
wire [PRRWIDTH-1:0] cntValue;
wire direct;
// when prr is 0 or 1, clkOut = clk directly
assign direct = (prr == {PRRWIDTH{1'b0}}) | (prr == {{(PRRWIDTH-1){1'b0}}, 1'b1});
assign cntValue = direct ? {PRRWIDTH{1'b0}} : (prr - 1'b1);
// when prr is odd, clkOut = clkP || clkN
// when prr is even, clkOut = clkP
assign clkOut = direct ? clk : (prr[0] ? (clkP | clkN) : clkP);
// positive edge count
always @(posedge clk or negedge rst) begin
if (!rst) begin
cntP <= {PRRWIDTH{1'b0}};
end
else if (!en) begin
cntP <= {PRRWIDTH{1'b0}};
end
else if (cntP == cntValue) begin
cntP <= {PRRWIDTH{1'b0}};
end
else begin
cntP <= cntP + 1'b1;
end
end
// positive edge clock
// overturn clkP when cntP = (cntValue - 1) / 2 or cntValue
always @(posedge clk or negedge rst) begin
if (!rst) begin
clkP <= 1'b0;
end
else if (!en) begin
clkP <= 1'b0;
end
else if (direct) begin
clkP <= 1'b0;
end
else if ((cntP == (cntValue >> 1)) || (cntP == cntValue)) begin
clkP <= !clkP;
end
end
// negetive edge count for odd prr
always @(negedge clk or negedge rst) begin
if (!rst) begin
cntN <= {PRRWIDTH{1'b0}};
end
else if (!en) begin
cntN <= {PRRWIDTH{1'b0}};
end
else if (prr[0]) begin
if (cntN == cntValue) begin
cntN <= {PRRWIDTH{1'b0}};
end
else if (cntP != {PRRWIDTH{1'b0}})begin
cntN <= cntN + 1'b1;
end
end
else begin
cntN <= {PRRWIDTH{1'b0}};
end
end
// negetive edge clock for odd prr
// overturn clkN when cntN = (cntValue - 1) / 2 or cntValue
always @(negedge clk or negedge rst) begin
if (!rst) begin
clkN <= 1'b0;
end
else if (!en) begin
clkN <= 1'b0;
end
else if (direct) begin
clkN <= 1'b0;
end
else if (prr[0]) begin
clkN <= ((cntN == (cntValue >> 1)) || (cntN == cntValue)) ? !clkN : clkN;
end
else begin
clkN <= 1'b0;
end
end
endmodule
单脉冲的任意整数分频方法
思路:
- 计数器在时钟上升沿+1,加到对应分频值后,clkOutReg拉高,计数器清零
- 计数器清零后,clkOutReg拉低
- 当clkOutReg和clk都为高时,clkOut为高
- 当分频系数为0或1时,clkOut = clkIn
`define __DIVSINGLEPULSE_V__
module DivSinglePulse #(
parameter PRRWIDTH = 4
)(
input wire clk,
input wire rst, // negetive valid
input wire en,
input wire [PRRWIDTH-1:0] prr,
output wire clkOut
);
reg clkOutReg;
reg [PRRWIDTH-1:0] cnt;
wire [PRRWIDTH-1:0] cntValue;
wire direct;
// when prr is 0 or 1, clkOut = clk directly
assign direct = (prr == {PRRWIDTH{1'b0}}) | (prr == {{(PRRWIDTH-1){1'b0}}, 1'b1});
assign cntValue = direct ? {PRRWIDTH{1'b0}} : (prr - 1'b1);
assign clkOut = direct ? clk : clkOutReg & clk;
// positive edge count
always @(posedge clk or negedge rst) begin
if (!rst) begin
cnt <= {PRRWIDTH{1'b0}};
end
else if (!en) begin
cnt <= {PRRWIDTH{1'b0}};
end
else if (cnt == cntValue) begin
cnt <= {PRRWIDTH{1'b0}};
end
else begin
cnt <= cnt + 1'b1;
end
end
// clkOut is high when cnt == cntValue
always @(posedge clk or negedge rst) begin
if (!rst) begin
clkOutReg <= 1'b0;
end
else if (!en) begin
clkOutReg <= 1'b0;
end
else if (direct) begin
clkOutReg <= 1'b0;
end
else if (cnt == cntValue) begin
clkOutReg <= 1'b1;
end
else begin
clkOutReg <= 1'b0;
end
end
endmodule
代码下载
以上代码及testbench都可以在verilog任意整数分频及测试.zip中下载噢!
Conclusion
12月到处都是节日的氛围,相信圣诞老人,相信爱、童话和魔法。
昨晚听了Westlife线上演唱会,感觉回到了初中的时候,Seasons in the Sun可能是我最早接触的英文歌。十多年过去了,我工作了,西城男孩也有了岁月的痕迹。They have raised me up and hope to see them once again.