单端口RAM实现FIFO

这篇具有很好参考价值的文章主要介绍了单端口RAM实现FIFO。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

RAM分类
单口ram:
ram实现fifo,数字IC刷题,fpga开发
单端口RAM只有一组数据线和一组地址线,只有一个时钟,读写共用地址线。输出只有一个端口。所以单端口RAM的读写操作不能同时进行。当wea拉高时,会将数据写入对应的地址,同时douta输出的数据与此时写入的数据是一致的,因此在读的时候需要重新生成对应的读地址给addra,并且disable掉wea。

module single_ram
(	
	input clk,
	input rstn,
	input enable_wr,
	input [7:0] addr,
	input [7:0]data,
	output reg[7:0]data_out
);
reg [7:0] mem [255:0];
always @(posedge clk, negedge rstn) begin
	if(!rst) begin
		data_out<='b0;
	end
	else if(enable_wr)begin
		data_out<=mem[addr];
	end
end

always @(posedge clk, negedge rstn) begin
	if(!enable_wr)begin
		mem[addr]<=data;
	end
end
endmodule

伪双口RAM
ram实现fifo,数字IC刷题,fpga开发
输入只有一组数据线,两组地址线,两个时钟。
所以一个端口只读,一个端口只写,但是写入和读取的时钟可以不同,且位宽比可以不是1:1。即允许写A的同时读B,且速率可以不相同。

module single_ram
(	
	input wrclk,
	input rstn,
	input wr,
	input [7:0] wr_addr,
	input [7:0]data,
	input rd_clk,
	input [7:0] rd_addr,
	input rd_en,
	output reg[7:0]data_out
);
reg [7:0] mem [255:0];
reg [7:0] i;
always @(posedge wr_clk, negedge rstn) begin
	if(!rstn) begin
		for(i='b0;i<256;i=i+1'b1)
			mem[i]<='b0;
	end
	else if(enable_wr)begin
		mem[wr_addr]<=data;
	end
end

always @(posedge rd_clk, negedge rstn) begin
	if(!rstn) begin
		data_out<='b0;
	end
	else if(rd_en)begin
		data_out<=mem[rd_addr];
	end
	else 
		data_out<=data_out;
end
endmodule

**真双口RAM:**有两组地址线和数据线,两个时钟。
输出有两个分别的数据线。
所以双口RAM两个端口都分别带有读写端口,可以在没有干扰的情况下进行读写,彼此互不干扰。
ram实现fifo,数字IC刷题,fpga开发

module double_ram(
	rst_n,
	cs_n,//片选信号,低有效
	//A port write signals 
	clk_a,
	wrdata_a,
	wraddr_a,
	wrena_n,
	//B port read signals 
	clk_b,
	rdaddr_b,
	rdenb_n,
	rddata_b);

parameter ADDR_WIDTH=7;
parameter DATA_WIDTH=16;
parameter DATA_DEPTH=128;
input clk_a;     // input signal  default type wire 
input clk_b;
input rst_n;
input cs_n;

input [DATA_WIDTH-1:0]wrdata_a;
input [ADDR_WIDTH-1:0]wraddr_a;
input wrena_n;

input [ADDR_WIDTH-1:0]rdaddr_b;
input rdenb_n;
output [DATA_WIDTH-1:0]rddata_b;

reg    [DATA_WIDTH-1:0]rddata;
integer i;
reg [DATA_WIDTH-1:0] mem [DATA_DEPTH-1:0];// 定义一个位宽为DATA_WIDTH,深度为DATA_DEPTH的存储器
always@(posedge clk_a or negedge rst_n) begin 
	if(~rst_n) begin 
		for(i=0;i<DATA_DEPTH;i=i+1)
		mem[i]<=0;
	end 
	else if(cs_n==1'b0 & wrena_n==1'b0 )//片选和写信号均低有效
	mem[wraddr_a]<=wrdata_a; //写入
end 
always@(posedge clk_b or negedge rst_n)begin 
	if(~rst_n)begin 
	rddata<=0;
	end 
	else if(cs_n==1'b0 & rdenb_n==1'b0)begin 
	rddata<=mem[rdaddr_b];
	end
	else 
	rddata<=rddata;
end 
assign rddata_b=rddata;
endmodule

单口RAM实现异步FIFO:
重点区分出同时读写。
第一种办法:采用两个单口RAM,两个单口RAM分开奇偶,相当于乒乓意思,然后再加一个REG,这样就把REG分开了。我的思路是遇到同时读写的时候就对写打一拍这个时候是对奇ram写入,然后下一拍在遇到同时读写那么同样打一拍后对偶ram写入
那么可能分为以下几种情况:
①同时读写:读写同时为奇,这种情况就是在当前一拍,将写数据存入REG中,并将REG_VALID拉高告诉FIFO我下一拍要写数据,并在当前拍从奇数的FIFO中读取数据,那么下一拍如果再此发生同时读写,那么此时的同时读写就为偶,这一拍发生的情况就是将前一拍REG中的数据写入FIFO,然后将REG中数据更新新数据,然后将REG_VALID再拉高,告诉偶数FIFO下一拍要写数据了,并同时从偶数FIFO中取出要读的数据。

其实核心观点就是用两个单口RAM一个REG,用来区分最难的读写同时发生的情况,通过将RAM分为奇偶再加一个REG寄存器用来缓存,这就使不能同时读写的情况给解决了。

如果同时读写,且奇偶不同,那这种情况就更容易解决了,当前拍写数据的模块将数据写入REG,读模块的读出数据,然后下一拍将REG再写入单口RAM,和之前同时读写同时为奇偶的情况很相似。

②不同时读写:这种情况就是你只要写就先将数据写入REG,然后拉高VALID下一拍将REG中的数据写入单口RAM,如果读就直接读出数据。

通过这种方式就完美的解决了单口RAM没办法同时操作RAM的情况。
————————————————
版权声明:本文为CSDN博主「Brad.Ji」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Bradji/article/details/124717180文章来源地址https://www.toymoban.com/news/detail-631598.html

module FIFO_single_ram
#( parameter WIDTH = 8,
   parameter DEEPTH= 256
)
(
    input clk ,
    input rstn ,
    input [WIDTH-1:0] data,
    input wr,
    input rd,
    output reg [WIDTH-1:0] data_out,
    output full,
    output empty
    );
    //-------------下面是FIFO的操作------------------//
    reg [8:0] rd_cnt;  //多一位用来回卷判断空满
    reg [8:0] wr_cnt;
    reg [8:0] wr_cnt_r;
    wire [7:0] rd_addr;
    wire [7:0] wr_addr;
    wire [7:0] addr_odd;
    wire [7:0] addr_even;
    reg [WIDTH-1:0] data_reg;
    reg       reg_valid;
    reg       wr_flag; //用来判断读写的奇偶 0代表奇 1代表偶数
    reg       rd_flag;
    wire      wr_ram;  //用来判断是否可以写
    wire      rd_ram;  //用来判断是否可以读
    wire      wr_en_odd; //奇数写
    wire      wr_en_even;//偶数写
    wire      rd_en_odd; //奇数写
    wire      rd_en_even;//偶数写
    reg      rd_en_odd_r; //奇数写
    reg      rd_en_even_r;//偶数写
    wire [7:0] datao_odd;
    wire [7:0] datao_even;
    assign rd_addr = rd_cnt [7:0];
    assign wr_addr = wr_cnt_r [7:0];
    assign full = (wr_cnt[8]^rd_cnt[8])&&(wr_cnt[7:0]==rd_cnt[7:0]); //最高位不同 低位相同;
    assign empty= (wr_cnt == rd_cnt); //全等
    assign wr_ram = (~full)&(wr);
    assign rd_ram = (~empty)&(rd);
	
	
	
	
//    assign data_out = (rd_en_odd_r)? datao_odd:datao_even;
    always@(*) begin
    if(rd_en_odd_r)
        data_out = datao_odd;
    else if(rd_en_even_r)
        data_out = datao_even;
    else
        data_out = data_out;
    end
    always@(posedge clk or negedge rstn)begin
    if(~rstn) begin
        wr_cnt <= 9'b0;
        data_reg <= {WIDTH{1'b0}};
        reg_valid <= 1'b0;
        wr_cnt_r <= 9'b0;
        end
    else if(wr_ram)
        begin
        wr_cnt <= wr_cnt + 1'b1;
        data_reg <= data;
        reg_valid <= 1'b1;
        wr_cnt_r <= wr_cnt;
        end
    else if(!rd_en_odd & reg_valid & (~wr_flag))
        begin
        reg_valid <= 1'b0;
        end
	else if(!rd_en_even & reg_valid & (~wr_flag))
        begin
        reg_valid <= 1'b0;
        end
	
    end
    always@(posedge clk or negedge rstn)begin
    if(~rstn)
        rd_cnt <= 9'b0;
    else if(rd_ram)
        rd_cnt <= rd_cnt + 1'b1;
    end
 
    //------------------------读写奇偶判断----------------------
    always@(posedge clk or negedge rstn) begin
    if(~rstn)
        wr_flag <= 1'b0;
    else if(reg_valid)
        wr_flag <=wr_flag + 1'b1; //往里面写数据的同时奇偶改变
    else 
        wr_flag <= wr_flag ; //往里面读数据的同时奇偶改变
    end
    always@(posedge clk or negedge rstn) begin
    if(~rstn)
        rd_flag <= 1'b0;
    else if(rd_ram)
        rd_flag <= rd_flag + 1'b1; //往里面读数据的同时奇偶改变
    else
        rd_flag <= rd_flag;
    end
    //--------------------RAM读写使能控制 分为奇偶两种类型
    always@(posedge clk or negedge rstn) begin
    if(~rstn)begin
        rd_en_odd_r <= 1'b0;
        rd_en_even_r <= 1'b0;
        end
    else
    begin
        rd_en_odd_r <= rd_en_odd;
        rd_en_even_r<=rd_en_even;
    end
    end
    
	assign rd_en_odd = rd_ram & (~rd_flag);
    assign rd_en_even = rd_ram & (rd_flag);
    assign wr_en_odd =(rd_en_odd)? 1'b0:( reg_valid & (~wr_flag));
    assign wr_en_even =(rd_en_even)? 1'b0:( reg_valid & (wr_flag));

    wire ena_odd = wr_en_odd | rd_en_odd;
    wire ena_even = wr_en_even | rd_en_even;
    assign addr_odd= (wr_en_odd)? wr_addr:rd_addr;
    assign addr_even= (wr_en_even)?wr_addr:rd_addr;
    SINGLE_RAM odd_ram (
      .clka(clk),    // input wire clka
      .ena(ena_odd),      // input wire ena
      .wea(wr_en_odd),      // input wire [0 : 0] wea
      .addra(addr_odd),  // input wire [7 : 0] addra
      .dina(data_reg),    // input wire [7 : 0] dina
      .douta(datao_odd)  // output wire [7 : 0] douta
    );
    SINGLE_RAM even_ram (
      .clka(clk),    // input wire clka
      .ena(ena_even),      // input wire ena
      .wea(wr_en_even),      // input wire [0 : 0] wea
      .addra(addr_even),  // input wire [7 : 0] addra
      .dina(data_reg),    // input wire [7 : 0] dina
      .douta(datao_even)  // output wire [7 : 0] douta
    );
endmodule

到了这里,关于单端口RAM实现FIFO的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA简单双端口RAM——IP核

    环境: 1、Quartus18.0 2、vscode 3、板子型号:原子哥开拓者2(EP4CE10F17C8) 要求: 使用 Altera RAM IP 核生成一个简单双端口的 RAM,然后对 RAM 进行读写操作,并通过 Modelsim 软件进行仿真及 SignalTap 软件进行在线调试。 由前面的单端口学习我们知道RAM IP核分为单端口RAM以及双端口RAM,

    2024年02月07日
    浏览(41)
  • 【Verilog】用双口RAM实现同步FIFO

    端口说明如下表。 双口RAM端口说明: 同步FIFO端口说明: 输入描述: 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 双口RAM和代码框架: 同步FIFO,就是我们学习其他经典计算机语言(如C语言)的数据结

    2024年02月07日
    浏览(36)
  • FPGA | RAM IP端口输出延迟问题解决

    Vivado中调用RAM IP,端口输出有延迟 版本器件 Version Vivado 2021.2 ZYNQ 7020 问题描述 ram_wea 信号拉低后,RAM读出数据有两个时钟的延迟 解决方式 在 Summary 中可以看到端口的读出延迟,关于该选项的具体使用方式可以参考Xilinx的数据手册:

    2024年02月16日
    浏览(40)
  • FPGA vivado IP核学习笔记——单端口RAM

    1. 新建IP 在IP Catalog中找到Block Memory Generator 2. 基本配置 ①在 Component Name 位置可以修改IP名字 ② Interface Type 选择接口类型,有Native(常规)和AXI4两种,AXI4常用于软核控制FPGA或ZYNQ中PS端控制FPGA时使用 ③ Generate address interface with 31 bits ,将地址深度固定在32bit ④ Memory Type : 有一

    2024年04月29日
    浏览(39)
  • 数字IC/FPGA面试题目合集解析(一)

    1,计算题:计算该触发器等效的建立保持时间(西安某Fabless面试笔试题) 2,计算题:计算组合逻辑的延时时间范围 3,选择题:Which of following ways cannot be used to improve timing of a hold violation path 问题:原触发器,即对于D点的建立时间,保持时间均为2ns,先由于存在线延时,对

    2024年02月06日
    浏览(42)
  • 【数字 IC / FPGA】 有关建立/保持时间计算的思考

    最近准备一些数字IC的机试,刷到了一些有关静态时序分析的题目。有一些比较经典的题目,在这里整理分享一下。 有什么疑问可以在评论区交流~互相进步 假设时钟周期为Tcycle,Tsetup,Thold分别为触发器建立保持时间,为保证时需满足要求,需要满足什么样的时序关系?(T1~

    2024年02月06日
    浏览(45)
  • 数字IC/FPGA面试宝典--经典60道例题详解

    1.关于亚稳态的描述错误的是(A) A、多用几级寄存器打拍可以消除亚稳态。 B、亚稳态是极不稳定的,理论上来讲处在亚稳态的时间可以无限长。 C、亚稳态稳定到0或者1,是随机的,与输入没有必然的关系。 D、如果数据传输中不满足触发器的建文时间Tsu和保持时间Th,可能

    2024年01月16日
    浏览(51)
  • 【数字IC/FPGA】Verilog中的force和release

    在Verilog中,将 force 用于variable会覆盖掉 过程赋值 ,或者assign引导的 连续(procedural assign)赋值 ,直到 release 。 下面通过一个简单的例子展示其用法: 加法器代码 测试平台代码(主要用于产生激励) 如上所示,正常情况下,u_adder模块的a和b端口由testbench中的a和b信号驱动,

    2024年02月09日
    浏览(42)
  • 【数字IC/FPGA】百度昆仑芯手撕代码--累加器

    已知一个加法器IP,其功能是计算两个数的和,但这个和延迟两个周期才会输出。现在有一串连续的数据输入,每个周期都不间断,试问最少需要例化几个上述的加法器IP,才可以实现累加的功能。 由于加法器两个周期后才能得到结果(再将该结果作为加法器的输入进行累加

    2024年02月09日
    浏览(39)
  • 【数字IC/FPGA】什么是无符号数?什么是有符号数?

    虽然在日常生活中,我们已经习惯了使用10进制数字,但在由数字电路构成的数字世界中,2进制才是效率更高的选择。 10进制(decimal)计数法(一般也叫阿拉伯计数法)是在日常生活中使用得最多的一种计数法,它是一种 位值记数法 (positional notation)。位值计数法的意思是

    2024年04月09日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包