牛客网 Verilog刷题 跨时钟域传输专题2 VL45——49

FIFO设计

首先推荐一篇文献,Sunburst Design大佬的杰作,其在IC行业做出了很多漂亮的工作。 

Simulation and Synthesis Techniques for Asynchronous FIFO Design

 同步 FIFO 指针

同步 FIFO 设计(写入和读取 FIFO 缓冲区在同一时钟域中进行的 FIFO),一种实现计算写入和读取 FIFO 缓冲区的次数以递增(在 FIFO 写入时,但没有读取)、递减(在 FIFO 读取但不写入)或保持(不写入和读取,或同时写入和读取操作)FIFO 缓冲区的当前填充值。

当 FIFO 计数器达到预定的满值时,FIFO 是满的,而当 FIFO 计数器为零时,FIFO 是空的。不幸的是,对于异步 FIFO 设计,不能使用递增递减 FIFO 填充计数器,因为需要两个不同的异步时钟来控制计数器。要确定异步 FIFO 设计的满和空状态,必须比较写和读指针

异步 FIFO 指针

写指针总是指向下一个要写的字;因此复位时,两个指针都设置为零,这也恰好是要写入的下一个 FIFO 字位置。在 FIFO 写入操作中,写入指针指向的内存位置 被写入,然后写入指针递增以指向下一个要写入的位置。

读指针始终指向要读取的当前 FIFO 字。再次复位时,两个指针都复位为零,FIFO 为空且读指针指向无效数据(因为 FIFO 为空且空标志置位)。第一个数据字写入 FIFO 后,写指针递增,清空标志,仍在寻址第一个 FIFO 存储器字内容的读指针立即将第一个有效字驱动到 FIFO数据输出端口,由接收逻辑读取。读取指针始终指向下一个要读取的 FIFO 字,这一事实意味着接收器逻辑不必使用两个时钟周期来读取数据字。如果接收器在读取FIFO数据字之前首先必须递增读指针,则接收器将计时一次以从FIFO输出数据字,并计时第二次以将数据字捕获到接收器中。这将是不必要的低效。

当读指针和写指针都相等时,FIFO为空。当两个指针在重置操作期间被重置为零时,或者当读指针在从FIFO读取最后一个字后赶上写指针时就为空。

当指针再次相等时,即当写指针绕回并赶上读指针时,FIFO是满的。这是个问题。当指针相等时,FIFO要么为空,要么为满,但具体是哪一个?

一种区分满和空的设计方法是向每个指针添加额外的位。当写指针递增超过最终FIFO地址时,写指针将递增未使用的MSB(二进制数的最左侧),同时将其余位回零,如图所示(FIFO已包裹并切换指针MSB)。

读指针也是这样。如果两个指针的MSB不同,则意味着写指针比读指针又绕回了一次。如果两个指针的MSB相同,则意味着两个指针的回绕次数相同。

 使用n位指针,其中(n-1)是访问整个FIFO存储器缓冲区所需的地址位数,当两个指针(包括MSB)相等时,FIFO为空。除MSB外,当两个指针相同时,FIFO为满。本文中的FIFO设计对具有2(n-1)个可写位置的FIFO使用n位指针,以帮助处理满和空情况。

VL46 同步FIFO

根据题目提供的双口RAM代码和接口描述,实现同步FIFO,要求FIFO位宽和深度参数化可配置。电路的接口如下图所示。

注意:

在例化双口 RAM 的时候,读写使能 renc/wenc 信号如果只是传入 rinc 和 winc 也能通过题目的波形测试,但是仔细思考其实不对,此时如果是满的情况下有外部的写使能 winc,虽然写地址不变,但是对于 RAM 来讲写使能还是有效的,会覆盖数据,所以这里的 RAM 写使能信号 wenc = winc && (~wfull),RAM 读使能 renc = rinc && (~rempty),这样才能保证不会在空满情况下对RAM的误操作。

`timescale 1ns/1ns
/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 
endmodule  
/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty,
	output wire [WIDTH-1:0]	rdata
);
    localparam addr_width = $clog2(DEPTH);
    reg [addr_width:0] waddr;
    reg [addr_width:0] raddr;
    //写(对于地址的操作)
    always @ (posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            waddr <= 'b0;
        end
        else begin
            if(winc && ~wfull) begin
                waddr <= waddr +1'b1;
            end
            else begin
               waddr <= waddr; 
            end
        end
    end
    //读(对于地址的操作)
    always @ (posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            raddr <= 'b0;
        end
        else begin
            if(rinc && ~rempty) begin
                raddr <= raddr +1'b1;
            end
            else begin
               raddr <= raddr; 
            end
        end
    end
    //设定空和满两种状态
    always @ (posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            wfull <= 1'b0;
            rempty <= 1'b0;
        end
        else begin
            wfull <= (raddr == {~waddr[addr_width],waddr[addr_width-1:0]});
            rempty <= (raddr == waddr);
        end
    end
    //模块的调用与例化
    dual_port_RAM
    #(.DEPTH(DEPTH),
      .WIDTH(WIDTH)
     )
    dual_port_RAM_U0
    (
        .wclk(clk),
        .wenc(winc && ~wfull),
        .waddr(waddr[addr_width-1:0]),
        .wdata(wdata),
        .rclk(clk),
        .renc(rinc && ~rempty),
        .raddr(raddr[addr_width-1:0]),//fifo的addr比RAM的addr多一位,最高位是空和满指示位。
        .rdata(rdata)
    );
endmodule

代码的最后一块是模块的例化与端口的连接。 接下来借用HDLbits的习题进行介绍。

例1:单模块连接

一般都使用通过端口名字的一一对应来实现连接,通过该方法就不需要考虑模块端口的位置变化。

module top_module ( input a, input b, output out );

    mod_a ins2 (.in1(a),.in2(b),.out(out));

例2:多模块连接

module top_module ( input clk, input d, output q );
	wire qd1,qd2;
    	my_dff ins1 (
        .clk(clk),
        .d(d),
        .q(qd1)
    	);
	my_dff ins2 (
        .clk(clk),
        .d(qd1),
        .q(qd2)
    	);
    	my_dff ins3 (
        .clk(clk),z
        .d(qd2),
        .q(q)
    	);
endmodule

 

 

 

 

 

 

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值