要开始编写verilog代码了,以常用的分频为例,编写一个简单的代码。
FPGA设计中,分频分为偶数分频和奇数分频。
偶数分频只要计上升沿的个数,然后按照分频要求的不同计相应个数的上升沿并进行波形翻转即可。
例如二分频,每计到一个上升沿,波形翻转一次;
例如四分频,每计到两个上升沿,波形翻转一次。
奇数分频相对比较麻烦,因为奇数分频经常要在给出时钟的下降沿进行翻转,而Verilog本质上是硬件描述,设计的本质仍然是硬件设计。在综合设计时,Verilog代码必须映射到FPGA上的可用硬件上。
FPGA常用的电路是触发器,触发器是一个具有时钟且仅对该时钟的一个边沿敏感的器件。
always@(posedge clk)综合后映射到只对一个时钟边沿敏感的触发器,always@(posedge
clk or negedge rst)综合后映射到有一个异步控制端且仅对时钟的一个边沿敏感的触发器。
不管always@()综合后映射到哪种触发器,它都只能使用一个时钟沿触发。
奇数分频注定要比偶数分频复杂,可以采用这样的方式进行奇数分频:
假设要对给定的时钟进行2N+1分频:先计数;将计数分成N、N+1的两段;前一段将变量div1置0,后一段将变量div1置1;用一个下降沿触发的触发器,div1延迟半个时钟周期后输出,生成div2;div1和div2相与,即得到分频后的时钟。
module FreDivDou(
input clk_i,
input rst_n_i,
output div2_o,
output div3_o,
output div4_o,
output dou2_o,
output dou3_o
);
// 二分频:在原来时钟的上升沿翻转
reg div2_o_r;
always @(posedge clk_i or negedge rst_n_i)
begin
if(!rst_n_i)
div2_o_r <= 0;
else
div2_o_r <= ~div2_o_r;
end
//div2_o_r要在always块内赋值,需要定义成reg型
//assign相当于一条连线,将表达式右边的电路直接通过wire(线网型)连接到左边,
//左边信号必须是wire型(output属于wire型)。当右边变化时,左边立刻发生变化。
assign div2_o = div2_o_r;
//四分频,计数,每计够两个上升沿翻转一次
reg [1:0] counter_div4;
reg div4_o_r;
always @(posedge clk_i or negedge rst_n_i)
begin
if(!rst_n_i) begin
counter_div4 <= 0;
div4_o_r <= 0;
end
else
if(counter_div4 <= 0)begin
counter_div4 <= 'b1;
div4_o_r <= div4_o_r;
end
else if(counter_div4 <='b1) begin
counter_div4 <= 0;
div4_o_r <=~div4_o_r;
end
end
assign div4_o = div4_o_r;
//3分频,第0个上升沿div1为1,第1、2个上升沿div1为0
reg div1,div2;
reg [1:0] counter_div3;
always @(posedge clk_i or negedge rst_n_i)
begin
if(!rst_n_i) begin
div1 <= 0;
counter_div3 <= 0;
end
else if(counter_div3 == 'd0)begin
div1 <= 'b1;
counter_div3 <=counter_div3 + 'b1;
end
else if(counter_div3 == 'd1)begin
div1 <= 0;
counter_div3 <=counter_div3 + 'b1;
end
else begin
div1 <= 0;
counter_div3 <= 0;
end
end
//3分频,div1在时钟的下降沿产生div2
always @(negedge clk_i or negedge rst_n_i)
if(!rst_n_i)
div2 <= 0;
else
div2 <= div1;
assign div3_o = div1 | div2;
endmodule