1.建立时间保持时间概念
建立时间(setup)是指在触发器的时钟信号上升沿到来以前,数据稳定不变的时间,如果建立时间不够,数据将不能在这个时钟上升沿被打入触器。
保持时间(hold)是指在触发器的时钟信号上升沿到来以后,数据稳定不变的时间,如果保持时间不够,数据同样不能被打入触发器。
每个时钟上升沿之前的是建立时间,上升沿之后的是保持时间,如果建立和保持时间都取极限最大值,把周期填满,它们相加肯定等于周期T。即Tsu(max)+Thold(max)=T。
2.FIFO
module syn_fifo#(
parameter WIDTH = 16,
parameter DEPTH = 1024,
parameter ADDR_WIDTH = clogb2(DEPTH),
parameter PROG_EMPTY = 100,
parameter PROG_FULL = 800
)(
input sys_clk,
input sys_rst,
input [WIDTH-1:0]din,
input wr_en,
input rd_en,
output reg [WIDTH-1:0]dout,
output reg full,
output reg empty,
output reg prog_full,
output reg prog_empty,
output reg [ADDR_WIDTH-1:0]fifo_cnt
);
//==========================================================\
//========== Define Parameter and Internal signals =========
//==========================================================/
reg [WIDTH-1:0] ram[DEPTH-1:0];
reg [ADDR_WIDTH-1:0] wr_addr;
reg [ADDR_WIDTH-1:0] rd_addr;
//==========================================================\
//===================== Main Code =========================
//==========================================================/
function integer clogb2;
input[31:0]value;
begin
value=value-1;
for(clogb2=0;value>0;clogb2=clogb2+1)
value=value>>1;
end
endfunction
//read
always@(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
rd_addr <= {ADDR_WIDTH{1'b0}};
else if(rd_en && !empty)begin
rd_addr <= rd_addr+1'd1;
dout <= ram[rd_addr];
end
else begin
rd_addr <= rd_addr;
dout <= dout;
end
end
//write
always@(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
wr_addr <= {ADDR_WIDTH{1'b0}};
else if(wr_en && !full) begin
wr_addr <= wr_addr+1'd1;
ram[wr_addr]<= din;
end
else
wr_addr <= wr_addr;
end
//fifo_cnt
always@(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
fifo_cnt <= {ADDR_WIDTH{1'b0}};
else if(wr_en && !full && !rd_en)
fifo_cnt <= fifo_cnt + 1'd1;
else if(rd_en && !empty && !wr_en)
fifo_cnt <= fifo_cnt - 1'd1;
else
fifo_cnt <= fifo_cnt;
end
//empty
always@(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
empty <= 1'b1;//reset:1
else
empty <= (!wr_en && (fifo_cnt[ADDR_WIDTH-1:1] == 'b0))&&((fifo_cnt[0] == 1'b0) || rd_en);
end
//full
always@(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
full <= 1'b1;//reset:1
else
full <= (!rd_en && (fifo_cnt[ADDR_WIDTH-1:1]=={(ADDR_WIDTH-1){1'b1}})) && ((fifo_cnt[0] == 1'b1) || wr_en);
end
//prog_full
always@(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
prog_full<= 1'b1;//reset:1
else if(fifo_cnt > PROG_FULL)
prog_full<= 1'b1;
else
prog_full<= 1'b0;
end
//prog_empty
always@(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
prog_empty<=1'b1;//reset:1
else if(fifo_cnt < PROG_EMPTY)
prog_empty<=1'b1;
else
prog_empty<=1'b0;
end
endmodule
异步fifo,读写时钟不一致;同步fifo,读写时钟一致
写入频率和读出频率不一致时要计算fifo数据深度
题目:100个写时钟周期可以写入80个数据,10个读时钟可以读出8个数据,计算FIFO深度
解析: 写时钟频率 w_clk,
读时钟频率 r_clk,
写时钟周期里,每B个时钟周期会有A个数据写入FIFO
读时钟周期里,每Y个时钟周期会有X个数据读出FIFO
则,FIFO的最小深度是?
计算公式如下:
fifo_depth = burst_length - burst_length * X/Y * r_clk/w_clk
所以带入:
fifo_depth = 160-160X(80%)=160-128= 32
3.跨时钟域
单比特信号:
慢时钟域到快时钟域:因为快时钟一定能采样到慢时钟域内的信号,我们用两级寄存器进行同步的目的在于消除亚稳态问题
快时钟域到慢时钟域:在快时钟域内先进行脉冲展宽,展宽到慢时钟内能采样到为止;展宽之后的信号在慢时钟域clkb下用两级寄存器同步下就好了
多比特信号的跨时钟域传输:从快到慢,还是从慢到快,都可以用异步FIFO
可以采用保持寄存器加握手信号的方法(多数据,控制,地址)
4.时序约束步骤
时钟约束 —> 输入/输出接口约束 —> 时钟分组和跨时钟约束 —> 时序例外约束。
5.奇偶分频
偶分频:四分频为例cnt计数到1拉高,记数到3拉低
奇分频 :五分频为例上升沿下降沿各一次,然后两个结果或运算
always@(posedge sys_clk or negedge sys_rst)
always@(negedge sys_clk or negedge sys_rst)