题解主体
其实本质上是一个简单的数学问题,即如何使用最小公倍数得到时钟周期的分别频比。
设小数为nn,此处以8.7倍分频的时钟周期为例。
首先,由于不能在硬件上进行小数的运算(比如2.1个时钟这种是不现实的,也不存在3.3个寄存器),小数分频不能做到分频后每个时钟周期都是源时钟的nn倍,也无法实现占空比为1/2,因此,考虑小数分频,其实现方式应当为87个clkout时钟周期是10个clkin时钟周期的8.7倍。
所以很简单,根据提示,我们可以进行如下假设:首先设置有两个整数分频电路,一个是奇数一个是偶数,设其分频比为a和b,然后其分别的时钟占N/M个周期。则我们可以得到:
a*N+b*M = 87
N+M = 10
如果使用 8 和 9分频,则有:
8*N+9*M = 87
N+M = 10
因此 M为7, N为3。
现在要做的是将8分频插入到9分频中,考虑相位抖动尽量少的话可以进行平均插值,这里我们就忽略这一步,有兴趣的童鞋可以自行研究一下,具体步骤:
1)设计一个计数次数87的计数器,即0-86
2)分别生成8分频和9分频的计数器,计数次数分别为0-6和0-2
3)输出波形根据总计数器和8、9分频计数器的数值控制输出脉冲翻转
将电路转换成Verilog代码描述如下
reg [6:0] cnt;
reg [3:0] cnt_e;
reg [3:0] cnt_o;
reg clk_MN;
assign clk_out = clk_MN;
parameter M_N = 8'd87;
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期
//总计数器
always @(posedge clk_in or negedge rst) begin
if(!rst)
cnt <= 6'b0;
else begin
if(cnt == M_N - 1'b1)
cnt <= 6'b0;
else
cnt <= cnt + 1'b1;
end
end
always @(posedge clk_in or negedge rst) begin
if(!rst) begin
cnt_e <= 4'b0;
cnt_o <= 4'b0;
end
else if(cnt<=c89-1'b1) begin
cnt_o <= 4'd0;
if(cnt_e == div_e - 1'b1)
cnt_e <= 4'd0;
else
cnt_e <= cnt_e + 1'b1;
end
else if(cnt>c89-1'b1) begin
cnt_e <= 4'd0;
if(cnt_o == div_o - 1'b1)
cnt_o <= 4'd0;
else
cnt_o <= cnt_o + 1'b1;
end
end
always @(posedge clk_in or negedge rst) begin
if(!rst)
clk_MN <= 1'b0;
else begin
if(cnt<c89) begin
if(cnt_e == 4'd0 || cnt_e == div_e/2)
clk_MN <= ~clk_MN;
end
if(cnt>=c89) begin
if(cnt_o == 4'd0 || cnt_o == (div_o - 1'b1)/2)
clk_MN <= ~clk_MN;
end
end
end
参考答案
`timescale 1ns/1ns module div_M_N( input wire clk_in, input wire 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; //奇数周期 reg [6:0] cnt; reg [3:0] cnt_e; reg [3:0] cnt_o; reg clk_MN; assign clk_out = clk_MN; //总计数器 always @(posedge clk_in or negedge rst) begin if(!rst) cnt <= 6'b0; else begin if(cnt == M_N - 1'b1) cnt <= 6'b0; else cnt <= cnt + 1'b1; end end always @(posedge clk_in or negedge rst) begin if(!rst) begin cnt_e <= 4'b0; cnt_o <= 4'b0; end else if(cnt<=c89-1'b1) begin cnt_o <= 4'd0; if(cnt_e == div_e - 1'b1) cnt_e <= 4'd0; else cnt_e <= cnt_e + 1'b1; end else if(cnt>c89-1'b1) begin cnt_e <= 4'd0; if(cnt_o == div_o - 1'b1) cnt_o <= 4'd0; else cnt_o <= cnt_o + 1'b1; end end always @(posedge clk_in or negedge rst) begin if(!rst) clk_MN <= 1'b0; else begin if(cnt<c89) begin if(cnt_e == 4'd0 || cnt_e == div_e/2) clk_MN <= ~clk_MN; end if(cnt>=c89) begin if(cnt_o == 4'd0 || cnt_o == (div_o - 1'b1)/2) clk_MN <= ~clk_MN; end end end endmodule