verilog电路设计:同/异步fifo、按键消抖、无毛刺时钟切换电路、二进制/格雷码转换

verilog电路设计:同/异步fifo、按键消抖、无毛刺时钟切换电路、二进制/格雷码转换

1、同步fifo

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

2、按键消抖电路

// 按键消抖电路,时钟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

3、无毛刺始终切换电路

// 用 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

3、跨时钟域传输

//=====================================//
//========== 跨时钟域传输 =============//
//=====================================//

//========== 慢时钟域到快时钟域:直接采样 =======//
// 避免多次采样:用异步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

4、异步fifo

原理参考: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 

5、二进制码 <-> 格雷码

// 二进制码 -> 格雷码:最高位不变,相邻两位异或, 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

6、状态机

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
    
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值