module sfifo
#( parameter DEPTH = 32 ,
parameter WIDTH = 16 )(
input i_clk,
input i_rstn,
input i_clr,
input i_pop,
input i_push,
input [WIDTH-1:0] i_wdata,
output[WIDTH-1:0] o_rdata,
output o_full,
output o_afull,
output o_empty
);
reg [WIDTH-1:0] MEM [0:DEPTH-1];
reg [$clog2(DEPTH)-1:0] raddr, waddr;
reg [$clog2(DEPTH) :0] count;
always@(posedge i_clk or negedge i_rstn) begin
if(~i_rstn)
raddr <= 'd0;
else if(i_clr)
raddr <= 'd0;
else if(i_pop) begin
if(raddr = DEPTH-1)
raddr <= 'd0;
else
raddr <= raddr + 1'b1;
end
end
always@(posedge i_clk or negedge i_rstn) begin
if(~i_rstn)
waddr <= 'd0;
else if(i_clr)
waddr <= 'd0;
else if(i_push) begin
if(waddr = DEPTH-1)
waddr <= 'd0;
else
waddr <= waddr + 1'b1;
end
end
always@(posedge i_clk or negedge i_rstn) begin
if(~i_rstn)
count <= 'd0;
else if(i_clr)
count <= 'd0;
else if(i_pop & (~i_push))
count <= count - 1'b1;
else if((~i_pop) & i_push)
count <= count + 1'b1;
end
assign o_empty = (raddr == waddr) & (count = 0) ? 1'b1 : 1'b0;
assign o_full = (raddr == waddr) & (count = DEPTH) ? 1'b1 : 1'b0;
assign o_afull = (count >= DEPTH-1) ? 1'b1 : 1'b0;
always@(posedge clk) begin
if(i_push)
MEM[waddr] <= i_wdata;
end
assign o_rdata = MEM[raddr];
endmodule
// 按键消抖电路,时钟12MHz,抖动小于15ms
module debounce(
input i_clk
,input i_rstn
,input i_key_in,
,output o_key_out);
localparam JITTER = 24000; // 12MHz/(1/20ms)
reg [1:0] key_d;
wire change;
reg [$clog2(JITTER)-1:0] delay_cnt;
always@(posedge i_clk or negedge i_rstn) begin
if(~i_rstn)
key_d <= 'd0;
else
key_d <= {key_d[0],i_key_in};
end
assign change = (key_d[0] & (~key_d[1]) | ((~key_d[0]) & key_d[1]);
always@(posedge i_clk or negedge i_rstn) begin
if(~i_rstn)
delay_cnt <= 'd0;
else if( change == 1'b1)
delay_cnt <= 'd0;
else if(delay_cnt == JITTER)
delay_cnt <= delay_cnt;
else
delay_cnt <= JITTER + 1'b1;
end
assign o_key_out = (delay_cnt == (JITTER-1'b1)) & (i_key_in == 1'b1) ? 1'b1 : 1'b0;
endmodule
// 用 Verilog 实现 glitch free 时钟切换电路。输入 sel,clka, clkb, sel 为1时输出clka,sel为0时输出clkb
module change_clk_source(
input clka
,input clkb
,input rstn
,input sel
,output outclk);
//== 写法1(辣鸡) ==
assign outclk = (sel & clka) | ((~sel) & clkb);
//== 写法2(两个时钟源是倍数关系) ==
reg outa, outb;
always@(posedge clka or negedge rstn) begin
if(rstn)
outa <= 'd0;
else
outa <= sel & (~outb);
end
always@(posedge clkb or negedge rstn) begin
if(rstn)
outb <= 'd0;
else
outb <= (~sel) & (~outa);
end
assign outclk = (outa & clka) | (outb & clkb);
//== 写法3:两个时钟为异步时钟关系 ==//
reg out1,out1_r;
reg out2,out2_r;
always@(posedge clka or negedge rstn) begin
if(rstn) begin
out1_r <= 'd0;
out1 <= 'd0;
end
elsea begin
out1_r <= sel & ~(out2);
out1 <= out1_r;
end
end
always@@(posedge clkb or negedge rstn) begin
if(~rstn) begin
out2_r <= 'd0;
out2 <= 'd0;
end
else begin
out2_r <= (~sel) & (~out1);
out2 <= out2_r;
end
end
assign outclk = (out1&clka) | (out2&clkb);
endmodule
//=====================================//
//========== 跨时钟域传输 =============//
//=====================================//
//========== 慢时钟域到快时钟域:直接采样 =======//
// 避免多次采样:用异步FIFO
// 两级寄存器,消除亚稳态
module s2f_clk_domain(
input i_clk1
,input i_clk2
,input i_rstn
,input i_signal
,output o_signal );
reg [1:0] signal_r;
always@(posedge i_clk2 or negedge i_rstn) begin
if(~i_rstn)
signal_r <= 'd0;
else
signal_r <= {signal_r[0], i_signal};
end
assign o_signal = signal_r[1];
endmodule
//=========== 快时钟域到慢时钟域:生成展宽信号,握手保证采样 =======//
module f2s_clk_domain(
input i_clk1 // fast clk domain
,input i_clk2 // slow clk domain
,input i_rstn
,input i_signal
,output o_pulse
,output o_signal );
// 生成a的展宽信号,接收到b采集成功的握手信号后,拉低
reg signal_a;
wire signal_a_hs;
reg [1:0] signal_a_hs_r;
always@(posedge i_clk1 or negedge i_rstn) begin
if(~i_rstn)
signal_a <= 'd0;
else if(i_signal == 1'b1)
signal_a <= 1'b1;
else if(signal_a_hs)
signal_a <= 1'b0;
end
// b信号采样
reg signal_b;
reg [1:0] signal_b_r;
always@(posedge i_clk2 or negedge i_rstn) begin
if(~i_rstn)
signal_b <= 'd0;
else if(signal_a == 1'b1)
signal_b <= 1'b1;
else
signal_b <= 1'b0;
end
// b信号两级同步,消除亚稳态
always@(posedge i_clk2 or negedge i_rstn) begin
if(~i_rstn)
signal_b_r <= 'd0;
else
signal_b_r <= {signal_b_r[1], signal_b};
end
// b信号采集成功的握手信号,返回给a
always@(posedge i_clk1 or negedge i_rstn) begin
if(~i_rstn)
signal_a_hs_r <= 'd0;
else
signal_a_hs_r <= {signal_a_hs_r[0],signal_b_r[1]};
end
assign signal_a_hs = signal_a_hs_r[1];
assign o_signal = signal_b_r[1];
assign o_pulse = signal_b_r[0] & (~signal_b_r[1]);
endmodule
原理参考:verilog-2 FIFO原理与实现
//=============================================//
//============== 异步FIFO =====================//
//=============================================//
参考:https://blog.csdn.net/qq_32355037/article/details/125020970, 并改进
// 1、采用双端口ram或寄存器阵列实现
// 2、判断空满,要跨时钟域传输
module asfifo
#( parameter DW = 16, // data width
parameter DP = 8 , // depth
parameter AW = $clog2(DP), // addr width
parameter AFULL_TH = 6 , // afull threshold
parameter AEMPTY_TH = 2 // aempty threshold
)(
input rclk
,input rstn // 只有一个复位信号即可
,input wclk
,input re
,output[DW-1:0] rdata
,input wr
,input [DW-1:0] wdata
,output full
,output afull
,output empty );
// ram inst
reg [AW:0] raddr, waddr;
wire ren,wen;
ram #( .DW ( DW )
.DP ( DP ))
asfifo_ram(
.rclk ( rclk )
,.wclk ( wclk )
,.re ( ren )
,.raddr ( raddr[AW-1:0] )
,.rdata ( rdata )
,.wr ( wen )
,.waddr ( waddr[AW-1:0] )
,.wdata ( wdata ));
assign ren = re & ~empty;
assign wen = we & ~full;
/*
module ram
#( parameter DW = 8,
parameter DP = 16,
)(
input rclk
,input wclk
,input re
,input [AW-1:0] raddr
,output[DW-1:0] rdata
,input wr
,input [AW-1:0] waddr
,input [DW-1:0] wdata);
reg [DW-1:0] MEM [0:DP-1];
always@(posedge wclk) begin
if(wr)
MEM[waddr] < wdata;
end
always@(posedge rclk) begin
if(re)
rdata <= MEM[raddr];
end
endmodule
*/
//==================== read =======================//
// raddr
always@(posedge rclk or negedge rstn) begin
if(~rstn)
raddr <= {(AW+1){1'b0}};
else if(re & (~empty)) begin
if(raddr == DEPTH-1)
raddr <= {~raddr[AW],{(AW-1){1'b0}}};
else
raddr <= raddr + 1'b1;
end
end
//==================== write ======================//
// waddr
always@(posedge wclk or negedge rstn) begin
if(~rstn)
waddr <= {(AW+1){1'b0}};
else if(wr $(~full)) begin
if(waddr == DEPTH-1)
waddr <= {~waddr[AW],{(AW-1){1'b0}}};
else
waddr <= waddr + 1'b1;
end
end
//=================== full ========================//
// full: raddr(bin) -> rptr(gray) -> rptr2wclk
// empty: waddr(bin) -> wptr(gray) -> wptr2rclk
wire [AW:0] rptr, wptr;
reg [AW:0] rptr2wclk [0:1], wptr2rclk[0:1];
assign rptr = raddr ^ (raddr >> 1);
assign wptr = waddr ^ (waddr >> 1);
always@(posedge wclk or negedge rstn) begin
if(~rstn) begin
rptr2wclk[0] <= 'd0;
rptr2wclk[1] <= 'd0;
end
else begin
rptr2wclk[0] <= rptr;
rptr2wclk[1] <= rptr2wclk[0];
end
end
// for bin code: full: 最高一位相反,其余位相等, 如 0_0100 1_0100
// for gray code: full: 最高两位相反,其余相等, 如 0_0110 1_1110
assign full = {~wptr[AW-:2],wptr[AW-2:0]} == rptr2wclk[1];
//=================== afull =======================//
// 1. 读地址raddr -> rptr(gray) -> rptr2wclk[1] -> raddr2wclk : 根据 waddr 和 raddr2wclk 判断fifo剩余空间
// 2. 读命令 pop&(~empty) -> pop2clk -> count(pop2clk,push): 根据 pop2clk 和 push&(~full) 进行counter,判断fifo剩余空间
// 方法1比方法2好,但需要较多逻辑。原因:
// 若从快时钟域到慢时钟域,直接采样可能无法采样所有数据,对异步fifo来说,空满只是假空、假满,影响效率但不影响功能
// 使用第一种方法,可以直接将 raddr同步过来,即使隔几个地址采样,依旧可以较为准确(不是完全即时)的得到fifo空间
// 使用第二种方法,只能隔几个数据采样到pop信号,得到的fifo剩余空间会误差更大
// 方法1: rptr2wclk[1] -> raddr2wclk
reg [AW:0] raddr2wclk;
always@(*) begin
for(int i=0;i<=AW;i=i+1)
raddr2wclk[i] = ~(rptr2wclk >> i);
end
reg [AW:0] depth_use; // depth use,已使用地址量
reg [AW:0] depth_res; // depth residual, 剩余地址量
always@(posedge wclk or negedge rstn) begin
if(!rstn) begin
depth_use <= {AW{1'b0}};
depth_res <= {AW{1'b0}};
end
else begin
if(raddr2wclk[AW] == waddr[AW]) begin // 未绕回
depth_use <= waddr - raddr2wclk; // 写入数据的地址量
depth_res <= DP - waddr - raddr2wclk; // 未写入数据的地址量
end
else begin // 绕回
depth_use <= DP + waddr[AW-1:0] - raddr2wclk[AW-1:0]; // 写入数据的地址量
depth_res <= raddr2wclk[AW-1:0] - waddr[AW-1:0]; // 未写入数据的地址量
end
end
end
assign afull = (depth_res >= AFULL_TH) ? 1'b1 : 1'b0;
wire aempty = (depth_use <= AEMPTY_TH) ? 1'b1 : 1'b0;
//==================== empty =======================//
always@(posedge rclk or negedge rstn) begin
if(~r_rstn) begin
wptr2rclk[0] <= 'd0;
wptr2rclk[1] <= 'd0;
end
else begin
wptr2rclk[0] <= wptr;
wptr2rclk[1] <= wptr2rclk[0];
end
end
assign empty = rptr == wptr2rclk;
endmodule
// 二进制码 -> 格雷码:最高位不变,相邻两位异或, 1011 ^ 01011 = 1110
assign bin2gray = bin ^ (bin >> 1);
// 格雷码 -> 二进制码:最高位不变,二进制码的最高位与格雷码的次高位异或,得到二进制码此高位,依次类推 1110 -> 1011
// 简化:格雷码最高位至当前位的异或,得到当前位的二进制码
// bin[3] = gray[3];
// bin[2] = gray[3] ^ gray[2];
// bin[1] = gray[3] ^ gray[2] ^ gray[1];
// bin[0] = gray[3] ^ gray[2] ^ gray[1] ^ gray[0];
integer i;
always@(*) begin
for(int =0;i<=3;i=i+1) begin
bin[i] = ^(gray >> i);
end
end
module fsm(clk,rst_n,x,out);
input clk,rst_n;
input[1:0] x;
output[2:0] out;
reg[1:0] current_state,next_state;
always@(posedge clk or negedge rst_n) begin
if(rst_n)
current_state <= 2'b00;
else
current_state <= next_state;
end
always@(current_state or x) begin
case(current_state):
2'b00:if(x = 2'b00) begin
out <= 3'b001;
next_state <= 2'b01;
end
else if(x = 2'b01)begin
out <= 3'b010;
next_state <= 2'b10;
end
else if(x = 2'b10) begin
out <= 3'b011;
next_state <= 2'b11;
end
else begin
out <= 3'b100;
next_state <= 2'b00;
end
2'b01:if(x) begin
....
end
2'b10:if(x) begin
....
end
2'b11:if(x == )begin
...
end
endcase
end
endmodule