VERDVANA'S BLOG Verdvana

数字IC设计/FPGA笔试代码汇总


1 前言

        总结一下数字IC设计和FPGA开发等笔试代码题。


2 边沿检测

        位宽可定制的多bit信号边沿检测,输出一个周期宽度的脉冲信号:

//------------------------------------------------------------------------------
//
//Module Name:					Edge_Detection.sv
//Department:					Xidian University
//Function Description:	   		边沿检测
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana		Verdvana      			2020-02-26
//
//-----------------------------------------------------------------------------------
`timescale 1ns/1ns

module Edge_Detection #(
    parameter   BIT_WIDTH = 8
)(
    input                   clk,
    input                   rst_n,

    input  [BIT_WIDTH-1:0]  edge_in,    //输入
    output [BIT_WIDTH-1:0]  edge_p,     //上升沿输出
    output [BIT_WIDTH-1:0]  edge_n      //下降沿输出
);

    logic [BIT_WIDTH-1:0] edge_r1,edge_r2;  //两级寄存

    always_ff@(posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            edge_r1 <= #1 '0;
            edge_r2 <= #1 '0;
        end
        else begin
            edge_r1 <= #1 edge_in;
            edge_r2 <= #1 edge_r1;
        end
    end

    assign edge_p = edge_r1 & ~edge_r2;
    assign edge_n = ~edge_r1 & edge_r2;

endmodule

3 串转并

        位宽可定制的串转并模块:

//------------------------------------------------------------------------------
//
//Module Name:					Deserializer.v
//Department:					Xidian University
//Function Description:	        解串器
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana	Verdvana		  			2020-02-26
//
//-----------------------------------------------------------------------------------
//
//Version	Modified History
//V1.0		位宽可定制的串转并模块
//
//-----------------------------------------------------------------------------------

`timescale 1ns/1ns

module Deserializer #(
    parameter   DATA_WIDTH = 8,
                MSB_LSB    = 1  //MSB先为1,LSB先为0
)(
    input                       clk,
    input                       rst_n,

    input                       data_in,
    output reg [DATA_WIDTH-1:0] data_out
);

    //位宽计算函数
    function integer clogb2 (input integer depth);
    begin
        for (clogb2=0; depth>0; clogb2=clogb2+1) 
            depth = depth >>1;                          
    end
    endfunction

    logic [clogb2(DATA_WIDTH)-1:0] cnt;

    always_ff@(posedge clk, negedge rst_n)begin
        if(!rst_n)
            cnt <= #1 '0;
        else if(cnt >= DATA_WIDTH-1)
            cnt <= #1 '0;
        else
            cnt <= #1 cnt + 1;
    end

    logic [DATA_WIDTH-1:0] data_out_r;

    always_ff@(posedge clk, negedge rst_n)begin
        if(!rst_n)
            data_out_r <= #1 '0;
        else
            case(MSB_LSB)
                1'b0:data_out_r <= #1 {data_in,data_out_r[DATA_WIDTH-1:1]};
                1'b1:data_out_r <= #1 {data_out_r[DATA_WIDTH-2:0],data_in};
            endcase
    end

    always_ff@(posedge clk, negedge rst_n)begin
        if(!rst_n)
            data_out <= #1 '0;
        else if(cnt == DATA_WIDTH-1)
            data_out <= #1 data_out_r;
        else
            data_out <= #1 data_out;
    end

endmodule

4 序列检测器

        有“1011”序列输入时输出为1,其他情况下输出为0:

//------------------------------------------------------------------------------
//
//Module Name:					Sequence_Detection.sv
//Department:					Xidian University
//Function Description:	        序列检测器
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana	Verdvana		  			2020-02-26
//
//-----------------------------------------------------------------------------------
//
//Version	Modified History
//V1.0		
//
//-----------------------------------------------------------------------------------

`timescale 1ns/1ns 

module Sequence_Detection (
    input           clk,
    input           rst_n,

    input           data_in,    //数据输入
    output reg      flag        //检测到1011输出一个周期的高电平
);

    //===============================================
    //三段式状态机

    //-----------------------------------------------
    //枚举法 四个状态

    enum reg [1:0]{
        zero,
        one,
        two,
        three
    }state,next_state;

    //-----------------------------------------------
    //状态解码

    always_comb begin
        case(state)
            zero:begin
                if(data_in)
                    next_state = one;
                else
                    next_state = zero;
            end
            one:begin
                if(data_in)
                    next_state = one;
                else
                    next_state = two;
            end
            two:begin
                if(data_in)
                    next_state = three;
                else
                    next_state = zero;
            end
            three:  next_state = zero;
            default:next_state = zero; 
        endcase
    end


    //-----------------------------------------------
    //状态转换

    always_ff@(posedge clk, negedge rst_n)begin
        if(!rst_n)
            state <= #1 zero;
        else
            state <= #1 next_state;
    end


    //-----------------------------------------------
    //输出
    
    always_comb begin
        case(state)
            zero:   flag = '0;
            one:    flag = '0;
            two:    flag = '0;
            three:  begin
                if(data_in)
                    flag = '1;
                else
                    flag = '0;
            end    
            default:flag = '0;
        endcase
    end

endmodule

5 异步复位同步释放

        同步复位要求复位有效信号需要保持一个周期才能被采集,异步复位的问题在于,如果异步复位信号在触发器时钟有效沿附近“释放”(复位信号从有效变为无效)的话,可能会导致触发器输出的亚稳态。所以需要将异步复位的释放信号打一拍:

//------------------------------------------------------------------------------
//
//Module Name:					Asy_Rst_Syn.sv
//Department:					Xidian University
//Function Description:	        异步复位同步释放
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review	Rel data
//V1.0		Verdvana	Verdvana				            2019-7-8
//
//-----------------------------------------------------------------------------------
module Asy_Rst_Syn(
		input			clk,        //时钟
		input			rst_n,      //异步复位
		
		output		    asy_rst_n   //异步复位同步释放
				
);

    //----------------------------------------------

    reg [1:0] rst_shift;

    always_ff@(posedge clk, negedge rst_n) begin
	    if(!rst_n)
		    rst_shift[1:0] <= #1 2'b00;
	
	    else
		    rst_shift[1:0] <= #1 {rst_shift[0],1'b1};
    end

    assign asy_rst_n = rst_shift[1];

endmodule

6 门控时钟

        直接将时钟和使能信号相与显然不太行,会有毛刺,所以需要用到一个锁存器:

//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana	Verdvana		  			2020-02-28
//
//-----------------------------------------------------------------------------------
//
//Version	Modified History
//V1.0		
//
//-----------------------------------------------------------------------------------

`timescale 1ns/1ns 

module Clock_Gating_Cell(
    input           clk,    //时钟
    input           en,     //使能

    output          gclk    //门控时钟
);

    logic en_latch;         //锁存后的使能信号

    //=================================================
    //锁存器锁存使能信号
    always_latch begin      
        if(!clk)
            en_latch = en;
    end

    //=================================================
    //产生门控时钟
    assign gclk = clk & en_latch;

endmodule

7 时钟切换电路

        直接用MUX显然是不行,会产生毛刺。所以要用一個類似RS鎖存器的結構:

//------------------------------------------------------------------------------
//
//Module Name:					Clock_Switch.sv
//Department:					Xidian University
//Function Description:	        时钟切换
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana	Verdvana		  			2020-02-27
//
//-----------------------------------------------------------------------------------
//
//Version	Modified History
//V1.0		
//
//-----------------------------------------------------------------------------------

`timescale 1ns/1ns 

module Clock_Switch(
    input       clk_a,      //时钟a
    input       clk_b,      //时钟b
    input       rst_n,      //复位

    input       select,     //时钟切换,0为时钟a

    output      clk_out     //切换后的时钟
);

    logic decide_a,decide_b;    //select和另一时钟域同步后的decide相与的结果
    logic out_a_p,out_a_n;      //时钟a域对decide的两级同步
    logic out_b_p,out_b_n;      //时钟b域对decide的两级同步


    assign decide_a = ~select & ~out_b_n;   //select为0且同步后的信号为0
    assign decide_b = select & ~out_a_n;    //select为0且同步后的信号为1


    //=================================================================
    //A时钟域正负沿两级同步

    always_ff@(posedge clk_a, negedge rst_n)begin
        if(!rst_n)
            out_a_p <= #1 '0;
        else 
            out_a_p <= #1 decide_a;
    end

    always_ff@(negedge clk_a, negedge rst_n)begin
        if(!rst_n)
            out_a_n <= #1 '0;
        else 
            out_a_n <= #1 out_a_p;
    end


    //=================================================================
    //B时钟域正负沿两级同步

    always_ff@(posedge clk_b, negedge rst_n)begin
        if(!rst_n)
            out_b_p <= #1 '0;
        else 
            out_b_p <= #1 decide_b;
    end  

    always_ff@(negedge clk_b, negedge rst_n)begin
        if(!rst_n)
            out_b_n <= #1 '0;
        else 
            out_b_n <= #1 out_b_p;
    end   

    assign clk_out = (out_a_n & clk_a) | (out_b_n & clk_b); //输出

endmodule

        其中两级寄存是为了消除ab时钟不同源情况下的亚稳态;负边沿触发的寄存器不能改为正边沿触发,否则还是会产生毛刺;前一级寄存正负边沿触发均可,这里用正边沿触发是为了缩短间隔时间。实现电路参考站点:防止毛刺的时钟切换电路的设计思想


8 跨时钟域信号传输

8.1 慢时钟域到快时钟域

        1bit信号从慢时钟域同步到快时钟域:

//------------------------------------------------------------------------------
//
//Module Name:					Asy_Signal_Transmission.sv
//Department:					Xidian University
//Function Description:	   		跨时钟域信号传输(慢到快)
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana	Verdvana		        	2020-3-1
//
//-----------------------------------------------------------------------------------

`timescale 1ns/1ns

module Asy_Signal_Transmission(
    input   clk,
    input   rst_n,

    input   data_in,
    input   data_out
);

    reg [1:0] data_r;
    
    always_ff@(posedge clk, negedge rst_n)begin
        if(!rst_n)
            data_r <= #1 '0;
        else
            data_r <= #1 {data_r[0],data_in};
    end

    assign data_out = data_r[1];

endmodule

8.2 快时钟域到慢时钟域

        1bit信号从快时钟域同步到慢时钟域,这时候用双寄存是不可靠的,例如快时钟域的信号高电平持续时间远小于慢时钟域的时钟周期且不在慢时钟的上升沿发生。

        第一个解决方法是结绳法:

//------------------------------------------------------------------------------
//
//Module Name:					Asy_Signal_Transmission.sv
//Department:					Xidian University
//Function Description:	   		跨时钟域信号传输(快到慢)
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana	Verdvana		        	2020-3-1
//
//------------------------------------------------------------------------------

`timescale 1ns/1ns

module Asy_Signal_Transmission(
    input   clk,        //慢时钟

    input   data_in,    //数据输入
    output  data_out    //数据输出
);


    logic clr_n;        //寄存器清零
    logic data_r [4];   //四级寄存

    assign clr_n = data_r[2] & data_r[3];   //输出一个脉冲后立即清零清零

    //===================================================
    //两级寄存器同步
    
    always_ff@(posedge data_in, posedge clr_n)begin
        if(clr_n)
            data_r[0] <= #1 '0;
        else
            data_r[0] <= #1 '1;
    end

    always_ff@(posedge clk, posedge clr_n)begin
        if(clr_n)
            data_r[1] <= #1 '0;
        else
            data_r[1] <= #1 data_r[0];
    end

    //===================================================
    //多一级寄存判断输出

    always_ff@(posedge clk, posedge clr_n)begin
        if(clr_n)
            data_r[2] <= #1 '0;
        else
            data_r[2] <= #1 data_r[1];
    end

    always_ff@(posedge clk, posedge clr_n)begin
        if(clr_n)
            data_r[3] <= #1 '0;
        else
            data_r[3] <= #1 data_r[2];
    end

    //===================================================
    //输出

    assign data_out = data_r[2] && ~data_r[3];

endmodule

        结绳法不需要用到快时钟,而输出信号的脉冲只有一个时钟周期。

        还有一种方法是对输入信号的脉冲进行拓展:

//------------------------------------------------------------------------------
//
//Module Name:					Asy_Signal_Transmission.sv
//Department:					Xidian University
//Function Description:	   		跨时钟域信号传输(快到慢)
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana	Verdvana		        	2020-3-1
//
//------------------------------------------------------------------------------

`timescale 1ns/1ns

module Asy_Signal_Transmission(
    input   clk_a,      //慢时钟
    input   clk_b,      //快时钟
    input   rst_n,

    input   data_in,    //数据输入
    output  data_out    //数据输出
);

    logic       data_expand_a;
    logic [1:0] data_r_b;
    logic [1:0] data_r_a;

    //================================================
    //在慢时钟域拓展输入信号

    always_ff@(posedge clk_a, negedge rst_n)begin
        if(!rst_n)
            data_expand_a = #1 '0;
        else if(data_in)
            data_expand_a = #1 '1;
        else if(data_r_a[1])
            data_expand_a = #1 '0;
        else
            data_expand_a = #1 data_expand_a;
    end


    //================================================
    //将拓展后的信号同步到快时钟域

    always_ff@(posedge clk_b, negedge rst_n)begin
        if(!rst_n)
            data_r_b = #1 '0;
        else
            data_r_b = #1 {data_r_b[0],data_expand_a};
    end

    //================================================
    //输出

    assign data_out = data_r_b[1];


    //================================================
    //将快时钟域的拓展信号同步到慢时钟域

    always_ff@(posedge clk_a, negedge rst_n)begin
        if(!rst_n)
            data_r_a = #1 '0;
        else
            data_r_a = #1 {data_r_a[0],data_r_b[1]};
    end

endmodule   

        当输入信号的脉冲大于三个时钟周期时,输出信号才能正确反映脉冲宽度。


9 异步双端口RAM

        位宽、深度可定制,A口读出,B口写入,支持片选和读写请求的异步双端口RAM:

//------------------------------------------------------------------------------
//
//Module Name:					Asy_DPRAM.sv
//Department:					Xidian University
//Function Description:	   		异步双端口RAM
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana	Verdvana		        	2020-2-26
//
//-----------------------------------------------------------------------------------

`timescale 1ns/1ns

module Asy_DPRAM #(
    parameter   DATA_WIDTH = 8,
                ADDR_WIDTH = 8
)(
    input                       clk_a,
    input                       clk_b,
    input                       rst_n,
    input                       cs_n,

    input                       en_a,
    input      [ADDR_WIDTH-1:0] addr_a,
    output reg [DATA_WIDTH-1:0] data_a,

    input                       en_b,
    input      [ADDR_WIDTH-1:0] addr_b,
    input      [DATA_WIDTH-1:0] data_b,
);

    parameter DATA_DIPTH = 1 << ADDR_WIDTH;

    reg [DATA_WIDTH-1:0]    ram [DATA_DIPTH];

    always_ff@(posedge clk_a, negedge rst_n)begin
        if(!rst_n)
            data_a <= #1 '0;
        else if(!cs_n&en_a)
            data_a <= #1 ram[addr_a];
        else
            data_a <= #1 data_a;
    end

    always_ff@(posedge clk_b, negedge rst_n) begin
        if(!rst_n)begin
            for(int i=0;i<DATA_DIPTH;i++)
                ram[addr_b] <= #1 '0;
        end
        else if(!cs_n&en_b)
            ram[addr_b] <= #1 data_b;
    end

endmodule

10 任意奇数分频器

//------------------------------------------------------------------------------
//
//Module Name:					Odd_Frequency_Divider.v
//Department:					Xidian University
//Function Description:	   		任意奇数分频器
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana	Verdvana		        	2020-2-22
//
//-----------------------------------------------------------------------------------

`timescale 1ns/1ns

module Odd_Frequency_Divider #(
    parameter   DIV_COEFF   = 3
)(
    input       clk,
    input       rst_n,

    input       en,
    output      clk_out
);

    //位宽计算函数
    function integer clogb2 (input integer depth);
    begin
        for (clogb2=0; depth>0; clogb2=clogb2+1) 
            depth = depth >>1;                          
    end
    endfunction

    reg [clogb2(DIV_COEFF)-1:0] cnt;

    always_ff@(posedge clk, negedge rst_n)begin
        if(!rst_n)
            cnt <= #1 '0;
        else if(cnt >= DIV_COEFF-1)
            cnt <= #1 '0;
        else
            cnt <= cnt + 1;
    end

    logic clk_pos,clk_neg;

    always_ff@(posedge clk, negedge rst_n)begin
        if(!rst_n)
            clk_pos <= #1 '0;
        else if(cnt <= (DIV_COEFF-1)/2)
            clk_pos <= #1 '1;
        else
            clk_pos <= #1 '0;
    end

    always_ff@(negedge clk, negedge rst_n)begin
        if(!rst_n)
            clk_neg <= #1 '0;
        else if(cnt <= (DIV_COEFF-1)/2)
            clk_neg <= #1 '1;
        else
            clk_neg <= #1 '0;
    end

    assign clk_out = clk_pos & clk_neg;


endmodule

11 同步FIFO

        位宽、深度可定制的同步FIFO:

//------------------------------------------------------------------------------
//
//Module Name:					Syn_FIFO.sv
//Department:					Xidian University
//Function Description:	   		同步FIFO
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana		Verdvana      			2019-11-2
//
//-----------------------------------------------------------------------------------

`timescale 1ns/1ns 

module Syn_FIFO# (
	parameter DATA_WIDTH = 8,					//定义FIFO数据位宽、地址位宽
	parameter ADDR_WIDTH = 8
)(
	/************ 时钟和复位 ************/
	input							clk,		//时钟
	input							rst_n,		//复位						
	/************ 读写使能 *************/					
	input							wr_en,		//写使能
	input							rd_en,		//读使能
	/*********** 数据输入输出 ***********/	
	input  		[DATA_WIDTH-1:0]	data_in,	//数据输入
	output reg 	[DATA_WIDTH-1:0]	data_out,	//数据输出
	/************* 标志位 *************/	
	output							full,		//满标志
	output							empty 		//空标志
);


	
	parameter FIFO_DEPTH = (1 << ADDR_WIDTH); 	//根据FIFO地址位宽定制FIFO深度

	logic						wr_en_r;
	logic						rd_en_r;
	logic	[ADDR_WIDTH  :0] 	wr_pointer;		//写指针
	logic  	[ADDR_WIDTH  :0] 	rd_pointer;		//读指针
	logic	[ADDR_WIDTH-1:0]	wr_addr;
	logic	[ADDR_WIDTH-1:0]	rd_addr;


	reg 	[DATA_WIDTH-1:0]	fifo [FIFO_DEPTH];	//寄存器组


	//==============================================================
	//使能同步

	always_ff@(posedge clk, negedge rst_n) begin
		if(!rst_n) begin
			wr_en_r <= #1 1'b0;
			rd_en_r <= #1 1'b0;
		end
		else begin
			wr_en_r <= #1 wr_en;
      		rd_en_r <= #1 rd_en;
		end	
	end	


	//==============================================================
	//指针递增

	always_ff@(posedge clk, negedge rst_n) begin
		if(!rst_n)begin
			wr_pointer <= #1 '0;
		end
		else if(wr_en_r && (!full)) begin
			wr_pointer <= #1 wr_pointer + 1;
		end
	end

	always_ff@(posedge clk, negedge rst_n) begin
		if(!rst_n)begin
			rd_pointer <= #1 '0;
		end
		else if(rd_en_r && (!empty)) begin
			rd_pointer <= #1 rd_pointer + 1;
		end
	end


	//==============================================================
	//读写地址

	assign	wr_addr = wr_pointer[ADDR_WIDTH-1:0];
	assign  rd_addr = rd_pointer[ADDR_WIDTH-1:0];


	//==============================================================
	//读写

	always_ff@(posedge clk)begin
		if(wr_en_r && (!full))
			fifo[wr_addr] <= #1 data_in;
	end

	always_ff@(posedge clk)begin
		if(rd_en_r && (!empty))
			data_out <= #1 fifo[rd_addr];
	end


	//==============================================================
	//空满判断

	assign full  = ( wr_pointer == {~rd_pointer[ADDR_WIDTH],rd_pointer[ADDR_WIDTH-1:0]} );
	assign empty = ( wr_pointer == rd_pointer );


endmodule

12 异步FIFO

        位宽、深度可定制的异步FIFO:

//------------------------------------------------------------------------------
//
//Module Name:					Asy_FIFO.v
//Department:					Xidian University
//Function Description:	   		异步FIFO
//
//------------------------------------------------------------------------------
//
//Version 	Design		Coding		Simulata	  Review		Rel data
//V1.0		Verdvana	Verdvana	Verdvana		        	2019-6-22
//
//-----------------------------------------------------------------------------------

`timescale 1ns/1ns

module Asy_FIFO #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 8
)(
/************* 时钟 *************/
	input							wr_clk,
	input							rd_clk,
/************* 使能 *************/	
	input							wr_en,
	input							rd_en,
/************* 复位 *************/	
	input							rst_n,
/************* 标志 *************/	
	output							full,
	output							empty,
/************* 数据 *************/	
	input		[DATA_WIDTH-1:0] 	data_in,
	output reg	[DATA_WIDTH-1:0] 	data_out

);

	//==============================================================
	//信號定义

	parameter FIFO_DEPTH = ( 1 << ADDR_WIDTH ); 		//FIFO深度定义
	
	reg 	[DATA_WIDTH-1:0]	fifo [FIFO_DEPTH];		//寄存器组,存储数据

	logic 						wr_en_r;				//写使能寄存
	logic						rd_en_r;				//读使能寄存
	
	logic	[ADDR_WIDTH-1:0]	wr_addr;				//写地址
	logic	[ADDR_WIDTH-1:0]	rd_addr;				//读地址

	logic	[ADDR_WIDTH  :0]	wr_pointer;				//写指针	
	logic	[ADDR_WIDTH  :0]	rd_pointer;				//读指针

	logic 	[ADDR_WIDTH  :0]	wr_addr_gray;			//写地址格雷码
	logic 	[ADDR_WIDTH  :0]	rd_addr_gray;			//读地址格雷码

	logic  	[ADDR_WIDTH  :0]	wr_addr_gray_d1;		//消除亚稳态一级寄存器
	logic  	[ADDR_WIDTH  :0]	wr_addr_gray_d2;		//消除亚稳态二级寄存器
	logic  	[ADDR_WIDTH  :0]	rd_addr_gray_d1;		//消除亚稳态一级寄存器
	logic  	[ADDR_WIDTH  :0]	rd_addr_gray_d2;		//消除亚稳态二级寄存器	


	//==============================================================
	//寄存使能信号

	always_ff@(posedge wr_clk)begin
		wr_en_r 	<=	#1 wr_en;
	end

	always_ff@(posedge rd_clk)begin
		rd_en_r		<=	#1 rd_en;
	end


	//==============================================================
	//读写指针递增

	always_ff@(posedge wr_clk, negedge rst_n) begin
		if(!rst_n)
			wr_pointer <= #1 'h0;
		else if(wr_en_r && (~full))
			wr_pointer <= #1 wr_pointer + 1;
		else 
			wr_pointer <= #1 wr_pointer;
	end


	always_ff@(posedge rd_clk, negedge rst_n) begin
		if(!rst_n)
			rd_pointer <= #1 'h0;
		else if(rd_en_r && (~empty))
			rd_pointer <= #1 rd_pointer + 1;
		else 
			rd_pointer <= #1 rd_pointer;
	end


	//==============================================================
	//产生读写地址

	assign wr_addr = wr_pointer[ADDR_WIDTH-1:0];	//写地址为写指针去掉最高位
	assign rd_addr = rd_pointer[ADDR_WIDTH-1:0];	//读地址为读指针去掉最高位



	//==============================================================
	//读写
	
	always_ff@(posedge wr_clk) begin
		if(wr_en_r && (~full))
			fifo[wr_addr] <= #1 data_in;
	end


	always_ff@(posedge rd_clk) begin
		if(rd_en_r && (~empty))
			data_out <= #1 fifo[rd_addr];
	end


	//==============================================================
	//产生读写地址格雷码

	assign wr_addr_gray = (wr_pointer >> 1) ^ wr_pointer;	//产生写地址格雷码
	assign rd_addr_gray = (rd_pointer >> 1) ^ rd_pointer;	//产生读地址格雷码


	//==============================================================
	//寫指针格雷码同步化

	always_ff@(posedge rd_clk ) begin
		wr_addr_gray_d1 <= #1 wr_addr_gray;		//将写地址的格雷码转移到读时钟域,方便与读地址格雷码比较
		wr_addr_gray_d2 <= #1 wr_addr_gray_d1; 	//两级触发消除亚稳态                               
	end


	//==============================================================
	//读指针格雷码同步化

	always_ff@(posedge wr_clk) begin
		rd_addr_gray_d1 <= #1 rd_addr_gray;		//将读地址的格雷码转移到写时钟域,方便与写地址格雷码比较
		rd_addr_gray_d2 <= #1 rd_addr_gray_d1;	//两级触发消除亚稳态                               
	end


	//==============================================================
	//空满判断

	assign full = (wr_addr_gray == {~(rd_addr_gray_d2[ADDR_WIDTH-:2]),rd_addr_gray_d2[ADDR_WIDTH-2:0]});	//写地址格雷码与读地址同步后的格雷码的高两位不同 即为满
	assign empty = ( rd_addr_gray == wr_addr_gray_d2 );	

endmodule

13 加法器

13.1 半加器

module Half_Adder(
    input   logic       a,b,    //输入
    output  logic       c,s     //结果位和进位位
);

    assign  c = a && b;
    assign  s = a ^ b;

    //assign  {c,s} = a + b;

endmodule

13.2 全加器

module Full_Adder(
    input   logic       a,b,cin //输入
    output  logic       cout,s  //结果位和进位位
);

    assign  c = (a && b) || (cin && (a || b));
    assign  s = a ^ b ^ cin;


endmodule

14 减法器

14.1 半减器

module Half_Subtractor(
    input   logic       a,b,    //输入
    output  logic       c,s     //结果位和进位位
);

    assign  c = !a && b;
    assign  s = a ^ b;

endmodule

14.2 全减器

module Full_Subtractor(
    input   logic       a,b,cin //输入
    output  logic       cout,s  //结果位和进位位
);

    assign  c = (a && !b) || (!cin && a) || (cin && b);
    assign  s = a ^ b ^ cin;


endmodule

15 乘法器

15.1 移位相加乘法器


        告辞