本章目录:
FIFO(First In First Out),即先进先出队列。FIFO存储器是一个先入先出的双口缓冲器,即第一个进入其内的数据第一个被移出,其中一个是存储器的输入口,另一个口是存储器的输出口。对于单片FIFO来说,主要有两种结构:触发导向结构和零导向传输结构。触发导向传输结构的FIFO是由寄存器阵列构成的,零导向传输结构的FIFO是由具有读和写地址指针的双口RAM构成。
FPGA 使用的 FIFO 一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存,或者高速异步数据的交互也即所谓的跨时钟域信号传递。它与 FPGA 内部的 RAM 和 ROM 的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像 RAM 和 ROM 那样可以由地址线决定读取或写入某个指定的地址。
根据 FIFO 工作的时钟域,可以将 FIFO 分为同步 FIFO 和异步 FIFO。同步 FIFO 是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作。异步 FIFO 是指读写时钟不一致,读写时钟是互相独立的。 Xilinx 的 FIFO IP 核可以被配置为同步 FIFO 或异步 FIFO,其信号框图如下图所示。从图中可以了解到,当被配置为同步 FIFO 时,只使用 wr_clk,所有的输入输出信号都同步于 wr_clk 信号。而当被配置为异步 FIFO 时,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟 wr_clk,所有与读相关的信号都是同步于读时钟 rd_clk。

宽度:一次读写操作的数据位
深度:可以存储的 N 位数据的数目(宽度为 N)
满标志: full。FIFO 已满时,由 FIFO 的状态电路送出的信号,阻止 FIFO 写操作
空标志: empty。FIFO 已空时,由 FIFO 的状态电路送出的信号,阻止 FIFO 读操作
读时钟:读操作所遵循的时钟
写时钟:写操作所遵循的时钟
典型同步FIFO由三部分组成:FIFO写控制逻辑、FIFO读控制逻辑、FIFO存储实体。

FIFO写控制逻辑主要功能:产生FIFO写地址、写有效信号,同时产生FIFO写满、写错等状态信号;
FIFO读控制逻辑主要功能:产生FIFO读地址、读有效信号,同时产生FIFO读空、读错等状态信号。
FIFO读写过程的地址控制如下图所示:
当FIFO初始化(复位)时,fifo_write_addr与fifo_read_addr同指到0x0,此时FIFO处于空状态;
当FIFO进行写操作时,fifo_write_addr递增(增加到FIFO DEPTH时回绕),与fifo_read_addr错开,此时FIFO处于非空状态;
当FIFO进行读操作时,fifo_read_addr递增。
FIFO空满状态产生:
为产生FIFO空满标志,引入Count 计数器,用于指示FIFO内部存储数据个数;
当只有写操作时,Count加1;只有读操作时,Count减1;其它情况下,保持不变;
Count为0时,说明FIFO为空,fifo_empty置位;
Count等于FIFO_DEPTH时,说明FIFO已满,fifo_full置位。
参照博客
异步FIFO的实现通常是利用双口RAM和读写地址产生模块来实现的。FIFO的接口包括异步的写时钟(wr_clk)和读时钟(rd_clk)、与写时钟同步的写有效(wr_en)和写数据(wr_data)、与读时钟同步的读有效(rd_en)和读数据(rd_data)。为了实现正确的读写和避免FIFO的上溢或下溢,通常还应该给出与读时钟和写时钟同步的FIFO的空标志(empty)和满标志(full)以禁止读写操作。
写地址产生模块还根据读地址和写地址关系产生FIFO的满标志。当wren有效时,若写地址+2=读地址时,full为1;当wren无效时,若写地址+ 1=读地址时,full为1。读地址产生模块还根据读地址和写地址的差产生FIFO的空标志。当rden有效时,若写地址-1=读地址时,empty为 1;当rden无效时,若写地址=读地址时,empty为1。按照以上方式产生标志信号是为了提前一个时钟周期产生对应的标志信号。
由于空标志和满标志控制了FIFO的操作,因此标志错误会引起操作的错误。如上所述,标志的产生是通过对读写地址的比较产生的,当读写时钟完全异步时,对读写地址进行比较时,可能得出错误的结果。例如,在读地址变化过程中,由于读地址的各位变化并不同步,计算读写地址的差值,可能产生错误的差值,导致产生错误的满标志信号。若将未满标志置为满标志时,可能降低了应用的性能,降低写数据速率;而将满置标志置为未满时,执行一次写操作,则可能产生溢出错误,这对于实际应用来说是绝对应该避免的。空标志信号的产生也可能产生类似的错误。

对于异步FIFO而言,无非就是写入频率大于或者小于读出频率呗!然而,对于写入频率 小于 读出频率,意味着写的慢,读的快,会怎样?那肯定是对你的FIFO深度没有要求了呀!你没了我就不读了呗。重要的是看另一种情况:写入大于读出,那就需要FIFO进行储存,既然储存,那肯定得有深度要求!
大概有以下四种情况:(以上图为例)

上边这种情况,通过一个例子可以看出来,此时需要FIFO深度。



通过理解这个例子,我们就可以知道为什么会有下边总结的这些公式了===>
写时钟频率w_clk
读时钟频率 r_clk,
写时钟周期里,每B个时钟周期会有A个数据写入FIFO
读时钟周期里,每Y个时钟周期会有X个数据读出FIFO

举例说明:
假设 FIFO 的写时钟为 100MHZ,读时钟为 80MHZ。在 FIFO 输入侧,每 100 个时钟,写入80 个数据;FIFO 读入测,每个时钟读取一个数据。设计合理的 FIFO 深度,使 FIFO 不会溢出:考虑背靠背(20个clk不发数据+80clk发数据+80clk发数据+20个clk不发数据的200个clk)代入公式可计算FIFO的深度:160-1601(80/100)=32.
参考博客
基础知识学完之后,那就小试牛刀!
VL22 同步FIFO
代码如下:
`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 <= 1'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 <= 1'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 <= (waddr == {~raddr[ADDR_WIDTH], raddr[ADDR_WIDTH-1:0]});
rempty <= (raddr == waddr);
end
end
// 实例化
dual_port_RAM #(.DEPTH(DEPTH),
.WIDTH(WIDTH)) dual_port_RAM_0 (.wclk(clk),
.wenc(winc),
.waddr(waddr[ADDR_WIDTH-1:0]),
.wdata(wdata),
.rclk(clk),
.renc(rinc),
.raddr(raddr[ADDR_WIDTH-1:0]),
.rdata(rdata));
endmodule
VL21 异步FIFO
代码如下:
`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
/***************************************AFIFO*****************************************/
module asyn_fifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input wclk ,
input rclk ,
input wrstn ,
input rrstn ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata ,
output wire wfull ,
output wire rempty ,
output wire [WIDTH-1:0] rdata
);
// 本地参数
localparam ADDR_WIDTH = $clog2(DEPTH);
reg [ADDR_WIDTH:0] waddr;
reg [ADDR_WIDTH:0] raddr;
// 写地址 操作
always @ (posedge wclk or negedge wrstn) begin
if(!wrstn) begin
waddr <= 'b0;
end
else begin
if(winc && !wfull) begin
waddr <= waddr + 1'b1;
end
else begin
waddr <= waddr;
end
end
end
// 读地址 操作
always @ (posedge rclk or negedge rrstn) begin
if(!rrstn) begin
raddr <= 'b0;
end
else begin`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
/***************************************AFIFO*****************************************/
module asyn_fifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input wclk ,
input rclk ,
input wrstn ,
input rrstn ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata ,
output wire wfull ,
output wire rempty ,
output wire [WIDTH-1:0] rdata
);
// 本地参数
localparam ADDR_WIDTH = $clog2(DEPTH);
reg [ADDR_WIDTH:0] waddr;
reg [ADDR_WIDTH:0] raddr;
// 写地址 操作
always @ (posedge wclk or negedge wrstn) begin
if(~wrstn) begin
waddr <= 'b0;
end
else begin
if(winc && ~wfull) begin
waddr <= waddr + 1'b1;
end
else begin
waddr <= waddr;
end
end
end
// 读地址 操作
always @ (posedge rclk or negedge rrstn) begin
if(~rrstn) begin
raddr <= 'b0;
end
else begin
if(rinc && ~rempty) begin
raddr <= raddr + 1'b1;
end
else begin
raddr <= raddr;
end
end
end
// 二进制 转 格雷码
wire [ADDR_WIDTH:0] waddr_gray;
wire [ADDR_WIDTH:0] raddr_gray;
assign waddr_gray = waddr ^ (waddr>>1);
assign raddr_gray = raddr ^ (raddr>>1);
// 因为上边转码的时候是 组合逻辑赋值,为了避免出现亚稳态,这里打一拍
reg [ADDR_WIDTH:0] waddr_gray_reg;
always @ (posedge wclk or negedge wrstn) begin
if(~wrstn) begin
waddr_gray_reg <= 'd0;
end
else begin
waddr_gray_reg <= waddr_gray;
end
end
//因为上边转码的时候是 组合逻辑赋值,为了避免出现亚稳态,这里打一拍
reg [ADDR_WIDTH:0] raddr_gray_reg;
always @ (posedge rclk or negedge rrstn) begin
if(~rrstn) begin
raddr_gray_reg <= 'd0;
end
else begin
raddr_gray_reg <= raddr_gray;
end
end
// 读时钟域 同步到 写时钟域 ===> 跨时钟域,打两拍
reg [ADDR_WIDTH:0] addr_r2w_temp;
reg [ADDR_WIDTH:0] addr_r2w;
always @ (posedge wclk or negedge wrstn) begin
if(~wrstn) begin
addr_r2w_temp <= 'd0;
addr_r2w <= 'd0;
end
else begin
addr_r2w_temp <= raddr_gray_reg;
addr_r2w <= addr_r2w_temp;
end
end
// 写时钟域 同步到 读时钟域 ===> 跨时钟域,打两拍
reg [ADDR_WIDTH:0] addr_w2r_temp;
reg [ADDR_WIDTH:0] addr_w2r;
always @ (posedge rclk or negedge rrstn) begin
if(~rrstn) begin
addr_w2r_temp <= 'd0;
addr_w2r <= 'd0;
end
else begin
addr_w2r_temp <= waddr_gray_reg;
addr_w2r <= addr_w2r_temp;
end
end
// 空满判断
assign wfull = (waddr_gray_reg == {~addr_r2w[ADDR_WIDTH:ADDR_WIDTH-1], addr_r2w[ADDR_WIDTH-2:0]});
assign rempty = (raddr_gray_reg == addr_w2r);
dual_port_RAM #(.DEPTH(DEPTH),
.WIDTH(WIDTH)) dual_port_RAM_0 (.wclk(wclk),
.wenc(winc && ~wfull),
.waddr(waddr[ADDR_WIDTH-1:0]),
.wdata(wdata),
.rclk(rclk),
.renc(rinc && ~rempty),
.raddr(raddr[ADDR_WIDTH-1:0]),
.rdata(rdata));
endmodule
这里遇到一个问题,反复查看代码,怎么也找不出来,哭了!!!
最后一查,实例化RAM的时候,有个线连错了,最后通过,奥里给!!!
==========================================================================================
本人所有系列的文章,仅供学习,不可商用,如有侵权,请告知,立删!!!
本人主要是记录学习过程,以供自己回头复习,再就是提供给后人参考,不喜勿喷!!!
如果觉得对你有用的话,记得收藏+评论!!!
1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,
Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功
ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我最近开始学习Ruby,这是我的第一门编程语言。我对语法感到满意,并且我已经完成了许多只教授相同基础知识的教程。我已经写了一些小程序(包括我自己的数组排序方法,在有人告诉我谷歌“冒泡排序”之前我认为它非常聪明),但我觉得我需要尝试更大更难的东西来理解更多关于Ruby.关于如何执行此操作的任何想法?
(本文是网络的宏观的概念铺垫)目录计算机网络背景网络发展认识"协议"网络协议初识协议分层OSI七层模型TCP/IP五层(或四层)模型报头以太网碰撞路由器IP地址和MAC地址IP地址与MAC地址总结IP地址MAC地址计算机网络背景网络发展 是最开始先有的计算机,计算机后来因为多项技术的水平升高,逐渐的计算机变的小型化、高效化。后来因为计算机其本身的计算能力比较的快速:独立模式:计算机之间相互独立。 如:有三个人,每个人做的不同的事物,但是是需要协作的完成。 而这三个人所做的事是需要进行协作的,然而刚开始因为每一台计算机之间都是互相独立的。所以前面的人处理完了就需要将数据
文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就
目录1关系运算符2运算符优先级3关系表达式的书写代码实例:下面是面试中可能遇到的问题:1关系运算符C++中有6个关系运算符,用于比较两个值的大小关系,它们分别是:运算符描述==等于!=不等于小于>大于小于等于>=大于等于这些运算符返回一个布尔值,即true或false。例如,当x等于y时,x==y的结果为true,否则结果为false。2运算符优先级在C++中,关系运算符的优先级高于赋值运算符,但低于算术运算符。以下是关系运算符的优先级,从高到低排列:运算符描述>,,>=,关系运算符==,!=相等性运算符&&逻辑与`如果在表达式中有多个运算符,则按照优先级顺序依次进行运算。3关系表达式的书写在
一.计算机组成原理 这本书利用组合逻辑、同步时序逻辑电路设计的相关知识,从逻辑门开始逐步构建运算器、存储器、数据通路和控制器,最终集成为完整的CU原型系统,使读者从设计者的角度理解计算机部件构成及运行的基本原理,掌握软硬件协同的概念。 全书共9章,主要内容包括计算机系统概述、数据信息的表示、运算方法与运算器、存储系统、指令系统、中央处理器、指令流水线、总线系统、输入输出系统。1.计算机系统概述1.1计算机发展历程 计算机是一种能够按照事先存储的程序,自动、高速、准确地对相关信息进行处理的电子设备。1946年2月,世界上第一台电子数字计算机ENIAC(ElectronicNum
其实现在基础的资料和视频到处都是,就是看你有没有认真的去找学习资源了,去哪里学习都是要看你个人靠谱不靠谱,再好的教程和老师,你自己学习不进去也是白搭在正式选择之前,大可以在各种学习网站里面找找学习资源先自己学习一下为什么选择学软件测试?同学们理由众多!大概分这几类:①不受开发语言、行业产品变化限制;②入门更简单,对零基础、女生都友好;③软件项目都需要测试人员,职业生涯稳;④学习周期短,但薪资并不低。要想“肩扛”一条线?需掌握三大技能:技能1:掌握测试流程,熟悉系统框架能提前与开发人员一起制定测试计划,通过测试左移,推动代码评审,代码审计,单元测试,自动化冒烟测试,来保证研发阶段的质量。技能2:
目录FIFO一.自定义同步FIFO1.1代码设计1.2Testbech1.3行为仿真***学习位宽计算函数$clog2()***$clog2()系统函数使用,可以不关注***分布式资源或者BLOCKBRAM二.异步FIFO2.1在FIFO判满的时候有两种方式:2.2异步FIFO为什么要使用格雷码2.2.1介绍格雷码2.2.2格雷码在异步FIFO中的应用2.2.2格雷码判满2.4二进制与格雷码之间的转换2.4.1二进制码转换为格雷码的方法2.4.2格雷码转换为二进制码的方法2.3实现框图2.5实现及仿真代码2.6仿真图验证2.7结论FIFO 这篇更多的是记录FIFO学习,参考了众多优秀的文章,