赞
踩
FIFO (First-In-First-Out) 是一种先进先出的数据缓存器,在数字ASIC设计中常常被使用。在实际开发中,多数都是直接使用公司经过top-out验证的FIFO IP或者是ISE/Vivado工具自带的FIFO IP,并不需要自己造轮子。但是,作为设计者,必须要掌握FIFO的设计方法,这样可以适配于各种类型的FPGA开发板,可以实现国产化。作为求职者,FIFO设计的相关知识在面试环节出现频率极高,需要格外关注。
FIFO按工作时钟域的不同可以分为:同步FIFO和异步FIFO。同步FIFO的写时钟和读时钟为同一个时钟,FIFO内部所有逻辑都是同步逻辑,常常用于交互数据缓冲。异步FIFO的写时钟和读时钟为异步时钟,FIFO内部的写逻辑和读逻辑的交互需要异步处理。
用途
跨时钟域
异步FIFO读写分别采用相互异步的不同时钟。在现代集成电路芯片中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步FIFO是这个问题的一种简便、快捷的解决方案,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据。
位宽变换
对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。
FIFO 与普通存储器 RAM 的区别是没有外部读写地址线,使用起来非常简单,但缺点就是只能顺序写入数据,顺序读出数据,其数据地址由内部读写指针自动加 1 完成,不能像普通存储器那样可 以由地址线决定读取或写入某个指定的地址。 FIFO 本质上是由 RAM 加读写控制逻辑构成的一种先进先出的数据缓冲器。因此,我们首先介绍下异步双口RAM。
FIFO的应用中所用的存储器一般都是RAM(Random Access Memory),所以异步的RAM对于异步FIFO实现是基础的。
这里的链接是有关不同RAM的介绍:单端口RAM、伪双端口RAM,双端口RAM和FIFO
下面实现一个16x8的异步双端口RAM
异步RAM有三部分组成:RAM写控制逻辑、RAM读控制逻辑、RAM存储实体(如Memory、Reg)。
写控制逻辑主要功能:产生RAM写地址、写有效信号。
读控制逻辑主要功能:产生RAM读地址、读有效信号。
module asyn_ram #( parameter DATA_WIDTH = 8, parameter ADDR_WIDTH = 4 ) ( input csen , //全局使能 input write_clock , //写时钟 input write_en , //写使能 input [ADDR_WIDTH-1:0] write_addr , //写地址 input [DATA_WIDTH-1:0] write_data , //写数据 input read_clock , //读时钟 input read_en , //读使能 input [ADDR_WIDTH-1:0] read_addr , //读地址 output reg [DATA_WIDTH-1:0] read_data //读数据 ); reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; //2^4-1=15 // look at the rising edge of the clock always@(posedge write_clock) begin if((write_en==1'b1)&&(csen==1'b1)) mem[write_addr] <= #1 write_data ; end always@(posedge read_clock) begin if((read_en==1'b1)&&(csen==1'b1)) read_data <= #1 mem[read_addr] ; end endmodule
`timescale 1ns/1ns module asyn_ram_tb(); reg csen ; reg write_clock ; reg [7:0] write_data ; reg write_en ; reg [3:0] write_addr ; reg read_clock ; wire [7:0] read_data ; reg read_en ; reg [3:0] read_addr ; asyn_ram u1( .csen(csen), .write_clock(write_clock), .write_en(write_en), .write_addr(write_addr), .write_data(write_data), .read_clock(read_clock), .read_en(read_en), .read_addr(read_addr), .read_data(read_data) ); initial begin write_clock=1'b0; write_en=1'b0; forever #5 write_clock=~write_clock; end initial begin read_clock=1'b0; read_en=1'b0; forever #8 read_clock=~read_clock; end always begin csen =1'b1; #15 write_en =1'b1; write_addr=4'd0; write_data=8'd1; #10 write_addr=4'd1;write_data=8'd2; #10 write_addr=4'd2;write_data=8'd3; #10 write_addr=4'd3;write_data=8'd4; #10 write_addr=4'd4;write_data=8'd5; end always begin #40 read_en =1'b1; read_addr=4'd0; #16 read_addr=4'd1; #16 read_addr=4'd2; #16 read_addr=4'd3; #16 read_addr=4'd4; $stop; end endmodule
仿真效果
格雷码简介:
在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code),另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环二进制码或反射二进制码。
格雷码用途:
格雷码的特点是从一个数变为相邻的一个数时,只有一个数据位发生跳变,由于这种特点,就可以避免二进制编码计数组合电路中出现的亚稳态。格雷码常用于通信,异步FIFO 或者 RAM 地址寻址计数器中。这里有一个链接,讲异步FIFO里为什么要用格雷码? 跨时钟域传输的黄金搭档:异步FIFO与格雷码 (qq.com)。
格雷码属于可靠性编码,是一种错误最小化的编码方式,因为,自然二进制码可以直接由数/模转换器转换成模拟信号,但某些情况,例如从十进制的 3 转换成 4 时二进制码的每一位都要变,使数字电路产生很大的尖峰电流脉冲。
而格雷码则没有这一缺点,**它是一种数字排序系统,其中的所有相邻整数在它们的数字表示中只有一个数字不同。它在任意两个相邻的数之间转换时,只有一个数位发生变化。**它大大地减少了由一个状态到下一个状态时逻辑的混淆。
十进制数 | 自然二进制数 | 格雷码 | 十进制数 | 自然二进制数 | 格雷码 |
---|---|---|---|---|---|
0 | 0000 | 0000 | 8 | 1000 | 1100 |
1 | 0001 | 0001 | 9 | 1001 | 1101 |
2 | 0010 | 0011 | 10 | 1010 | 1111 |
3 | 0011 | 0010 | 11 | 1011 | 1110 |
4 | 0100 | 0110 | 12 | 1100 | 1010 |
5 | 0101 | 0111 | 13 | 1101 | 1011 |
6 | 0110 | 0101 | 14 | 1110 | 1001 |
7 | 0111 | 0100 | 15 | 1111 | 1000 |
格雷码相邻两项只差一位。从0开始,观察每次改的是哪一位,你可以得到这个数列:
1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 是不是很有规律?这样便于记忆。数字代表写一个格雷码变化的数的位置。
二进制码->格雷码(编码):从最右边一位起,依次将每一位与左边一位异或(XOR),作为本位的格雷码值,最高位不变(相当于最高位的左边是 0);
module bin2gray(
bin_in,
gray_out
);
parameter WIDTH = 4;
input [WIDTH-1:0] bin_in;
output [WIDTH-1:0] gray_out;
//===================================================
// ------------------- MAIN CODE -------------------
//===================================================
assign gray_out = (bin_in >> 1) ^ bin_in;
endmodule
module bin2gray_tb(); reg [3:0] bin_in ; wire [3:0] gray_out ; initial begin bin_in = 4'd0; #100 bin_in = 4'd1; #100 bin_in = 4'd2; #100 bin_in = 4'd3; #100 bin_in = 4'd4; #100 bin_in = 4'd5; #100 bin_in = 4'd6; #100 bin_in = 4'd7; #100 bin_in = 4'd8; #100 bin_in = 4'd9; #100 bin_in = 4'd10; #100 bin_in = 4'd11; #100 bin_in = 4'd12; #100 bin_in = 4'd13; #100 bin_in = 4'd14; #100 bin_in = 4'd15; end bin2gray u_bin2gray ( .bin_in (bin_in ), .gray_out (gray_out ) ); endmodule
仿真效果
格雷码->二进制码(解码):从左边第二位起,将每位与左边一位解码后的值异或,作为该位解码后的值(最左边一位依然不变)。
module gray2bin( gray_in, bin_out ); parameter WIDTH = 4; input [WIDTH-1:0] gray_in; output reg [WIDTH-1:0] bin_out; //=================================================== // ------------------- MAIN CODE ------------------- //=================================================== integer i; always @(*) begin bin_out[WIDTH-1] = gray_in[WIDTH-1]; for (i = 1; i < WIDTH; i = i + 1) begin bin_out[WIDTH-i-1] = gray_in[WIDTH-i-1]^bin_out[WIDTH-i]; end end endmodule
module gray2bin_tb(); reg [3:0] gray_in ; wire [3:0] bin_out ; initial begin gray_in = 4'd0; #100 gray_in = 4'd1; #100 gray_in = 4'd3; #100 gray_in = 4'd2; #100 gray_in = 4'd6; #100 gray_in = 4'd7; #100 gray_in = 4'd5; #100 gray_in = 4'd4; #100 gray_in = 4'd12; #100 gray_in = 4'd13; #100 gray_in = 4'd15; #100 gray_in = 4'd14; #100 gray_in = 4'd10; #100 gray_in = 4'd11; #100 gray_in = 4'd9; #100 gray_in = 4'd8; #100 gray_in = 4'd0; end gray2bin u_gray2bin ( .gray_in (gray_in ), .bin_out (bin_out ) ); endmodule
仿真效果:
独热码,在英文文献中称做 one-hot code, 直观来说就是有多少个状态就有多少比特,而且只有一个比特为 1,其他全为 0 的一种码制。通常,在通信网络协议栈中,使用八位或者十六位状态的独热码。
例如,有 6 个状态的独热码状态编码为:000001,000010,000100,001000,010000,100000。
优点:状态比较时仅仅需要比较一个位,从而一定程度上简化了译码逻辑,译码简单,减少了毛刺产生的概率。
缺点:速度较慢,触发器资源占用较多,面积较大;
用途:在做仲裁时,独热码有
前面介绍到,FIFO 包括同步 FIFO 和异步 FIFO 两种。 而FIFO 本质上是由 RAM 加读写控制逻辑构成的一种先进先出的数据缓冲器。因此,首先介绍下同步FIFO的两种设计方法。
典型同步FIFO有三部分组成: (1) FIFO写控制逻辑; (2)FIFO读控制逻辑; (3)FIFO 存储实体(如Memory、Reg)。
FIFO写控制逻辑主要功能:产生FIFO写地址、写有效信号,同时产生FIFO写满、写错等状态信号;
FIFO读控制逻辑主要功能:产生FIFO读地址、读有效信号,同时产生FIFO读空、读错等状态信号。
FIFO参数解释:
FIFO设计的关键是正确的读写控制和生成FIFO“空/满”状态标志。基本原则是**“满不能写,空不能读。”**
FIFO读写控制
当FIFO初始化(复位)时fifo_write_addr与fifo_read_addr同指到0x0,此时FIFO为空状态;
当FIFO进行写操作时,fifo_write_addr递增(增加到FIFO深度时回绕),与fifo_read_addr错开,此时FIFO处于非空状态;
当FIFO进行读操作时,fifo_read_addr递增,当读到最后一个数据后,就追赶上fifo_write_addr,此时FIFO也是空状态。
FIFO空满状态判断
需要明确的是:
读指针:总是指向下一个将要被写入的单元,复位时,指向第1个单元(编号为’d0)。
写指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为’d0)
下面有两种判断空满状态判断的实现方法。
为产生FIFO空满标志,引入fifo_data_cnt
计数器,fifo_data_cnt
寄数器用于指示FIFO内部存储数据个数;
fifo_data_cnt
不变(FIFO中的数据个数不变)fifo_full
=0( FIFO 未满),则 fifo_data_cnt
+1;fifo_empty
=0(FIFO非空),则 fifo_data_cnt
-1;fifo_data_cnt
=0 的时候,表示 FIFO 为空,需要设置 fifo_empty
=1;fifo_data_cnt
= FIFO_ADDR_WIDTH
的时候,表示 FIFO 现在已经满,需要设置 fifo_full
=1//计数器法实现sync_fifo module sync_fifo_cnt #( parameter FIFO_DATA_WIDTH = 'd8 , parameter FIFO_ADDR_DEPTH = 'd16 ) ( input clk , //fifo clock input rst_n , //fifo clock reset (0: reset) input fifo_wr_en , //fifo write enable(1: enable) input fifo_rd_en , //fifo read enable(1: enable) input [FIFO_DATA_WIDTH-1:0] fifo_wr_data , //fifo write data output fifo_full , //fifo full status(1:full 0:no full) output fifo_wr_err , //fifo write error status output fifo_empty , //fifo empty status(1:empty 0:no empty) output fifo_rd_err , //fifo read error status output reg [$clog2(FIFO_ADDR_DEPTH) :0] fifo_data_cnt, //fifo valid data cnt output reg [FIFO_DATA_WIDTH-1:0] fifo_rd_data //fifo read data ); reg [$clog2(FIFO_ADDR_DEPTH)-1:0] fifo_wr_addr ; //fifo write addr reg [$clog2(FIFO_ADDR_DEPTH)-1:0] fifo_rd_addr ; //fifo write addr reg [FIFO_DATA_WIDTH-1:0] fifo_mem [FIFO_ADDR_DEPTH-1:0] ;//FIFO_ADDR_DEPTH*FIFO_DATA_WIDTH RAM //--=========================================-- // READ CONTROL : // Read address increase when read enable AND // Not empty; //--=========================================-- always @(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) fifo_rd_addr <= 0 ; else if (fifo_rd_en && (~ fifo_empty)) begin fifo_rd_addr <= fifo_rd_addr + 1'b1 ; fifo_rd_data <= fifo_mem[fifo_rd_addr] ; end end //--=========================================-- // WRITE CONTROL : // Write address increase when write enable AND // Not full. //--=========================================-- always @(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) fifo_wr_addr <= 0 ; else if (fifo_wr_en && (~ fifo_full)) begin fifo_wr_addr <= fifo_wr_addr + 1'b1 ; fifo_mem[fifo_wr_addr] <= fifo_wr_data ; end end //--=========================================-- // FIFO DATA CNT : // Valid Write Only, increase data cnt; // Valid Read Only, decrease data cnt; //--=========================================-- always @(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) fifo_data_cnt <= 0 ; else if (fifo_wr_en & (~ fifo_full) & (~(fifo_rd_en & (~fifo_empty)))) //Valid Write Only, increase data cnt; fifo_data_cnt <= fifo_data_cnt + 1'b1 ; else if (fifo_rd_en & (~ fifo_empty) & (~(fifo_wr_en & (~fifo_full)))) //Valid Read Only, decrease data cnt; fifo_data_cnt <= fifo_data_cnt - 1'b1 ; else fifo_data_cnt <= fifo_data_cnt; end //--=========================================-- // FIFO Status : // 1. fifo_empty when cnt ==0 ; // 2. fifo full when cnt == MAX ; //--=========================================-- assign fifo_empty = (fifo_data_cnt == 0 ) ; assign fifo_rd_err = (fifo_data_cnt == 0 ) & fifo_rd_en ; assign fifo_full = (fifo_data_cnt == FIFO_ADDR_DEPTH ) ; assign fifo_wr_err = (fifo_data_cnt == FIFO_ADDR_DEPTH ) & fifo_wr_en ; endmodule
Testbench借鉴自同步FIFO的两种Verilog设计方法(计数器法、高位扩展法)_同步fifo verilog代码_孤独的单刀的博客-CSDN博客
`timescale 1ns/1ns module sync_fifo_cnt_tb(); parameter FIFO_DATA_WIDTH = 8 ; parameter FIFO_ADDR_DEPTH = 8 ; reg clk ; reg rst_n ; reg [FIFO_DATA_WIDTH-1:0] fifo_wr_data ; reg fifo_rd_en ; reg fifo_wr_en ; wire [FIFO_DATA_WIDTH-1:0] fifo_rd_data ; wire fifo_empty ; wire fifo_full ; wire [$clog2(FIFO_ADDR_DEPTH) : 0] fifo_data_cnt; wire fifo_rd_err ; wire fifo_wr_err ; sync_fifo_cnt #( .FIFO_DATA_WIDTH (FIFO_DATA_WIDTH), .FIFO_ADDR_DEPTH (FIFO_ADDR_DEPTH) ) u_sync_fifo_cnt( .clk (clk ), .rst_n (rst_n ), .fifo_wr_data (fifo_wr_data ), .fifo_rd_en (fifo_rd_en ), .fifo_wr_en (fifo_wr_en ), .fifo_rd_data (fifo_rd_data ), .fifo_empty (fifo_empty ), .fifo_full (fifo_full ), .fifo_data_cnt (fifo_data_cnt ), .fifo_wr_err (fifo_wr_err ), .fifo_rd_err (fifo_rd_err ) ); always #10 clk = ~clk; initial begin clk = 1'b0; rst_n <= 1'b0; fifo_wr_data <= 'd0; fifo_wr_en <= 1'b0; fifo_rd_en <= 1'b0; //write full repeat(8) begin @(negedge clk)begin rst_n <= 1'b1; fifo_wr_en <= 1'b1; fifo_wr_data <= $random; end end //read empty repeat(8) begin @(negedge clk)begin fifo_wr_en <= 1'b0; fifo_rd_en <= 1'd1; end end repeat(4) begin @(negedge clk)begin fifo_wr_en <= 1'b1; fifo_wr_data <= $random; fifo_rd_en <= 1'b0; end end forever begin @(negedge clk)begin fifo_wr_en <= 1'b1; fifo_wr_data <= $random; fifo_rd_en <= 1'b1; end end end endmodule
仿真效果
因为FIFO的特点为先入先出,因此读写的开始的地址都是0。
当读地址与写地址相同时,则可判断FIFO为空。
当读地址与写地址除最高位相反,其他位相同时,则可判断FIFO为满。
假设RAM深度为8,扩展后的地址为4位。
module sync_fifo_ptr #( parameter FIFO_DATA_WIDTH = 'd8 , parameter FIFO_ADDR_DEPTH = 'd16 ) ( input clk , //fifo clock input rst_n , //fifo clock reset (0: reset) input fifo_wr_en , //fifo write enable(1: enable) input fifo_rd_en , //fifo read enable(1: enable) input [FIFO_DATA_WIDTH-1:0] fifo_wr_data , //fifo write data output fifo_full , //fifo full status(1:full 0:no full) output fifo_wr_err , //fifo write error status output fifo_empty , //fifo empty status(1:empty 0:no empty) output fifo_rd_err , //fifo read error status output reg [FIFO_DATA_WIDTH-1:0] fifo_rd_data //fifo read data ); wire [$clog2(FIFO_ADDR_DEPTH)-1:0] fifo_rd_addr ; //真实地址 wire [$clog2(FIFO_ADDR_DEPTH)-1:0] fifo_wr_addr ; reg [$clog2(FIFO_ADDR_DEPTH):0] fifo_rd_addr_e ; reg [$clog2(FIFO_ADDR_DEPTH):0] fifo_wr_addr_e ; //真实地址,高位拓展一位 reg [FIFO_DATA_WIDTH-1:0] fifo_mem [FIFO_ADDR_DEPTH-1:0] ; //FIFO_ADDR_DEPTH*FIFO_DATA_WIDTH RAM assign fifo_wr_addr = fifo_wr_addr_e[$clog2(FIFO_ADDR_DEPTH)-1:0]; assign fifo_rd_addr = fifo_rd_addr_e[$clog2(FIFO_ADDR_DEPTH)-1:0]; //读数据 always @(posedge clk or negedge rst_n) begin if(rst_n==1'b0) fifo_rd_addr_e <= 'd0; else if(fifo_rd_en && (~ fifo_empty))begin fifo_rd_addr_e <= fifo_rd_addr_e + 1'd1; fifo_rd_data <= fifo_mem[fifo_rd_addr]; end end //写数据 always @(posedge clk or negedge rst_n) begin if(rst_n==1'b0) fifo_wr_addr_e <= 'd0; else if(fifo_wr_en && (~ fifo_full))begin fifo_wr_addr_e <= fifo_wr_addr_e + 1'd1; fifo_mem[fifo_wr_addr] <= fifo_wr_data; end end assign fifo_empty = (fifo_wr_addr_e==fifo_rd_addr_e); assign fifo_full = ((fifo_wr_addr_e[$clog2(FIFO_ADDR_DEPTH)]!=fifo_rd_addr_e[$clog2(FIFO_ADDR_DEPTH)]) && (fifo_rd_addr==fifo_wr_addr)); assign fifo_wr_err = fifo_full & fifo_wr_en; assign fifo_rd_err = fifo_empty & fifo_rd_en; endmodule
使用相同的tb
`timescale 1ns/1ns module sync_fifo_ptr_tb(); parameter FIFO_DATA_WIDTH = 8 ; parameter FIFO_ADDR_DEPTH = 8 ; reg clk ; reg rst_n ; reg [FIFO_DATA_WIDTH-1:0] fifo_wr_data ; reg fifo_rd_en ; reg fifo_wr_en ; wire [FIFO_DATA_WIDTH-1:0] fifo_rd_data ; wire fifo_empty ; wire fifo_full ; wire fifo_rd_err ; wire fifo_wr_err ; sync_fifo_ptr #( .FIFO_DATA_WIDTH (FIFO_DATA_WIDTH), .FIFO_ADDR_DEPTH (FIFO_ADDR_DEPTH) ) u_sync_fifo_ptr( .clk (clk ), .rst_n (rst_n ), .fifo_wr_data (fifo_wr_data ), .fifo_rd_en (fifo_rd_en ), .fifo_wr_en (fifo_wr_en ), .fifo_rd_data (fifo_rd_data ), .fifo_empty (fifo_empty ), .fifo_full (fifo_full ), .fifo_wr_err (fifo_wr_err ), .fifo_rd_err (fifo_rd_err ) ); always #10 clk = ~clk; initial begin clk = 1'b0; rst_n <= 1'b0; fifo_wr_data <= 'd0; fifo_wr_en <= 1'b0; fifo_rd_en <= 1'b0; //write full repeat(8) begin @(negedge clk)begin rst_n <= 1'b1; fifo_wr_en <= 1'b1; fifo_wr_data <= $random; end end //read empty repeat(8) begin @(negedge clk)begin fifo_wr_en <= 1'b0; fifo_rd_en <= 1'd1; end end repeat(4) begin @(negedge clk)begin fifo_wr_en <= 1'b1; fifo_wr_data <= $random; fifo_rd_en <= 1'b0; end end forever begin @(negedge clk)begin fifo_wr_en <= 1'b1; fifo_wr_data <= $random; fifo_rd_en <= 1'b1; end end end endmodule
仿真效果:和上一个方法一样
异步FIFO这里,刀哥写的太好了,大家可以直接看他的博客,里面有verilog代码,也有testbench仿真软件,记得给他点赞!
异步FIFO的Verilg实现方法_verilog 异步fifo_孤独的单刀的博客-CSDN博客
这里可以看这个大佬总结的:FIFO深度计算 - 星雨夜澈 - 博客园 (cnblogs.com)
写时钟频率为100MHz,读时钟频率为80MHz。每100个时钟周期写入80个数据,每一个周期读出一个数据。那么FIFO的深度应该是多少?
首先我们要确定最极端的情况,也就是FIFO的Burst 会写了多少个数据。这里要考虑数据是“背靠背”传输的,如下:
这种情况是,前100个时钟周期,数据在后80个周期内发生了连续的写操作,在后100个时钟周期内,数据在前80个周期写入。这就造成了:
设计一个同步FIFO,每100个时钟周期可以写80个数据,每10个周期可以读8个数据,FIFO的深度为多少?
和上面的情况一样,burst长度为160。所以:
burst长度(B)为:B =160
写入B长度所需的时间:T = (1/fwr)*160
从FIFO中读出一个数据所需的时间:t = (1/frd)*(8/10)
在T时间读走的数据量:T/T= 160* (frd/fwr)(8/10)
FIFO的深度:160 - 160* (frd/fwr)(8/10)
因为是同步FIFO,所以frd/fwr = 1,代入得 深度为32。
由此我们可以推出FIFO的计算公式:
[2]同步FIFO学习 - IC新手 - 博客园 (cnblogs.com)
[4]同步FIFO的两种Verilog设计方法(计数器法、高位扩展法)_同步fifo verilog代码_孤独的单刀的博客-CSDN博客
[5]异步FIFO的Verilg实现方法_verilog 异步fifo_孤独的单刀的博客-CSDN博客
[6]异步FIFO的verilog代码实现(包含将满和将空逻辑) - love小酒窝 - 博客园 (cnblogs.com)
[7]IC基础(一):异步FIFO原理与代码实现 - 你好24h - 博客园 (cnblogs.com)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。