题解主体

其实本质上是一个简单的数学问题,即如何使用最小公倍数得到时钟周期的分别频比。

设小数为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