偶分频——六分频
可以看到输出的频率对应六个系统时钟周期,即完成了六分频的任务,这里使计数器计数为2将输出波形进行反转,所以0,1,2半个周期就有了三个系统时钟周期。
此方法在低频信号中可以使用,但是在高频时钟下会出现失误, 在FPGA中所有的时钟都要连接到全局时钟网络中去,又叫全局时钟数,其目的是为了保证时钟信号到达每一个器件的时间都尽可能相同,而这种方法并没有连接到全局时钟网络中
module divider_six ( input wire sys_clk, input wire rst_n, output reg out_clk ); reg [1:0] cnt; //两位宽的计数器,使用寄存器类型数据 always@(posedge sys_clk or negedge rst_n) //对计数器进行判断赋值 if(rst_n == 1'b0) cnt <= 2'b0; else if(cnt == 2'd2) cnt <= 2'b0; else cnt <= cnt + 2'b1; always@(posedge sys_clk or negedge rst_n) //对输出进行判断赋值,由于输出中使用了always语句所以为reg类型变量 if(rst_n == 1'b0) out_clk <= 1'b0; else if(cnt == 2'd2) out_clk <= ~out_clk; else out_clk <= out_clk; endmodule
`timescale 1ns/1ns module tb_divider_six(); reg sys_clk; reg rst_n; wire out_clk; initial begin sys_clk = 1'b1; rst_n <= 1'b0; #20 rst_n <= 1'b1; end always #10 sys_clk = ~sys_clk; divider_six divider_six_1 ( .sys_clk(sys_clk), .rst_n(rst_n), .out_clk(out_clk) ); endmodule
对于上述方法的改进,可以使用脉冲标志信号
其中的clk_flag即为六分频输出,在两个clk_flag的上升沿即可视为一个周期
改进代码:
/* 通过时钟标志位的方法与直接进行波形翻转的六分频看上去效果差距不大但若是在接下来对频率进行计数时: 翻转产生的六分频:always always@(posedge out_clk or negedge rst_n) 利用标志位的六分频:always always@(posedge sys_clk or negedge rst_n) 加上对标志位的判断if(clk_flag = 1'b1) 可见标志位中使用的仍然是系统的时钟周期,这在高频时钟下可以保持系统的稳定,避免了时钟网络原因`带来的偏差 */ module divider_six ( input wire sys_clk, input wire rst_n, // output reg out_clk output reg clk_flag ); // reg [1:0] cnt; //两位宽的计数器,使用寄存器类型数据 reg [2:0] cnt; //三位宽的计数器,使用寄存器类型数据 /* always@(posedge sys_clk or negedge rst_n) //对计数器进行判断赋值 if(rst_n == 1'b0) cnt <= 2'b0; else if(cnt == 2'd2) cnt <= 2'b0; else cnt <= cnt + 2'b1; always@(posedge sys_clk or negedge rst_n) //对输出进行判断赋值,由于输出中使用了always语句所以为reg类型变量 if(rst_n == 1'b0) out_clk <= 1'b0; else if(cnt == 2'd2) out_clk <= ~out_clk; else out_clk <= out_clk; */ always@(posedge sys_clk or negedge rst_n) if(rst_n == 1'b0) cnt <= 3'b0; else if(cnt == 3'd5) cnt <= 3'b0; else cnt <= cnt + 3'b1; always@(posedge sys_clk or negedge rst_n) if(rst_n == 1'b0) clk_flag <= 1'b0; else if(cnt == 3'd4) // 在计数终止的前一个时钟产生高电平,为在后边对标志位进行利用时会额外占用一个时钟周期 clk_flag <= 1'b1; else clk_flag <= 1'b0; endmodule
奇分频——五分频——比偶分频稍难
分频法:
分别利用上升沿和下降沿作为触发,但是显然其占空比并不为50%,但是如果利用或运算可以发现其刚好可以合成最终的50%占空比的分频
module divider_five ( input wire sys_clk, input wire res_n, output wire out_clk ); reg [2:0] cnt; reg clk_1; reg clk_2; always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) cnt <= 3'd0; else if(cnt == 3'd4) cnt <= 3'd0; else cnt <= cnt + 1'b1; always@(posedge sys_clk or negedge res_n) //时钟上升沿时访问函数 if(res_n == 1'b0) clk_1 <= 1'b0; else if(cnt == 3'd2) clk_1 <= 1'b1; else if(cnt == 3'd4) clk_1 <= 1'b0; else clk_1 <= clk_1; always@(negedge sys_clk or negedge res_n) //时钟下降沿时访问函数 if(res_n == 1'b0) clk_2 <= 1'b0; else if(cnt == 3'd2) clk_2 <= 1'b1; else if(cnt == 3'd4) clk_2 <= 1'b0; else clk_2 <= clk_2; assign out_clk = (clk_1 | clk_2); //使用连续赋值语句取clk_1和clk_2的或运算 endmodule
`timescale 1ns/1ns module tb_divider_five(); reg sys_clk; reg rst_n; wire out_clk; initial begin sys_clk = 1'b1; rst_n <= 1'b0; #20 rst_n <= 1'b1; end always #10 sys_clk = ~sys_clk; divider_five divider_five_1 ( .sys_clk(sys_clk), .res_n(rst_n), .out_clk(out_clk) ); endmodule
降频法:
module divider_five ( input wire sys_clk, input wire res_n, //output wire out_clk output reg clk_flag ); reg [2:0] cnt; //分频法部分 /* reg clk_1; reg clk_2; always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) cnt <= 3'd0; else if(cnt == 3'd4) cnt <= 3'd0; else cnt <= cnt + 1'b1; always@(posedge sys_clk or negedge res_n) //时钟上升沿时访问函数 if(res_n == 1'b0) clk_1 <= 1'b0; else if(cnt == 3'd2) clk_1 <= 1'b1; else if(cnt == 3'd4) clk_1 <= 1'b0; else clk_1 <= clk_1; always@(negedge sys_clk or negedge res_n) //时钟下降沿时访问函数 if(res_n == 1'b0) clk_2 <= 1'b0; else if(cnt == 3'd2) clk_2 <= 1'b1; else if(cnt == 3'd4) clk_2 <= 1'b0; else clk_2 <= clk_2; assign out_clk = (clk_1 | clk_2); //使用连续赋值语句取clk_1和clk_2的或运算 */ //降频法 always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) cnt <= 3'd0; else if(cnt == 3'd4) cnt <= 3'd0; else cnt <= cnt + 1'b1; always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) clk_flag <= 1'b0; else if(cnt == 3'd3) clk_flag <= 1'b1; else clk_flag <= 1'b0; endmodule
`timescale 1ns/1ns module tb_divider_five(); reg sys_clk; reg rst_n; wire clk_flag; initial begin sys_clk = 1'b1; rst_n <= 1'b0; #20 rst_n <= 1'b1; end always #10 sys_clk = ~sys_clk; divider_five divider_five_1 ( .sys_clk(sys_clk), .res_n(rst_n), .clk_flag(clk_flag) ); endmodule