当前位置:   article > 正文

基于Xilinx的RAM IP核的使用_xilinx 双口ram

xilinx 双口ram

1.RAM的介绍

RAM 是随机存取存储器( Random Access Memory)的简称,是一个易失性存储器。RAM 工作时可以随时从任何一个指定的地址写入或读出数据,同时我们还能修改其存储的数据,即写入新的数据,这是 ROM 所并不具备的功能。在 FPGA 中这也是其与 ROM 的最大区别。 ROM 是只读存储器,而 RAM 是可写可读存储器,在我们 FPGA 中使用这两个存储器主要也是要区分这一点,因为这两个存储器使用的都是我们 FPGA 内部的 RAM 资源,不同的是 ROM 是只用到了 RAM 资源的读数据端口。

Xilinx 推出的 RAM IP 核分为两种类型:单端口 RAM 和双端口 RAM。其中双端口RAM 又分为简单双端口 RAM 和真正双端口 RAM。

对于单端口 RAM,读写操作共用一组地址线,读写操作不能同时进行;对于简单双端口 RAM,读操作和写操作有专用地址端口(一个读端口和一个写端口),即写端口只能写不能读,而读端口只能读不能写;对于真正双端口 RAM,有两个地址端口用于读写操作(两个读/写端口),即两个端口都可以进行读写。

下图是单端口RAM模块端口:

简单双端口RAM模块接口图:

真双端口模块接口图:

2.IP核配置

创建RAM IP核,首先搜索block

然后双击,我们选择单端口RAM。

选择位宽为8bit,深度为256;在模式选择中,RAM 读写操作模式共分为三种,非别是 Write First(写优先模式) 、 Read First(读优先模式) 和 No Change(不变模式)。

Write First(写优先模式) : 若我们在在同一个时钟沿下对同一个地址进行读写,则读出的数据为写入的数据。

Read First(读优先模式) : 若我们在在同一个时钟沿下对同一个地址进行读写, 则读出的数据为该地址写入数据前存储的数据。

No Change(不变模式) : 在该模式下不能同时进行读写操作, 输出数据为同时读写操作前输出的数据。

这里我们选择不变模式,主要是为了后面的设计任务。

使能接口直接选择总是使能,当然根据设计需求可以使用使能信号。

然后进入Other Options选项,和ROM一样,这里为了后续代码的实验需求,我们导入一个初始化文件。因为RAM是可以写入数据的,所以这里根据需求而定。

最后点击OK,然后就创建好了我们的单端口RAM IP。

3.IP核的使用

本次RAM IP的使用依然基于野火征途系列RAM IP使用设计一个:按下按键 1 时往 RAM 地址 0~255 里写入数据 0~255;按下按键 2 时读取 RAM 内的数据,从地址 0 开始每隔 0.2s 地址加 1 往下进行读取;再次按下按键 1 时停止读取重新写入数据 0~255;再次按下按键 2 时从头开始读取数据,使用数码管显示。

顺便提一下,如果是入门FPGA的初学者的话,推荐你们可以在小破站看一下野火征途Pro版本的免费视频,反正是免费的白嫖嘛还是可以的,讲的挺细的,这里的文档都是基于野火E10的开发指南来写的,但我没有使用过intel的FPGA芯片,所以例程都是基于AMD家的vivado来开发的。

OK,进入正题,根据任务要求可以知道,我们肯定要使用按键,那么肯定要有消抖模块;然后又是使用数码管显示,那么肯定还是需要数码管驱动模块的,上一小节视频是使用的ROM,本章是RAM,这不是换汤不换药吗?那来个RAM控制模块不久OK了吗。

接下来我们绘制模块图:

我承认模块框图是我复制上一章的,草率了,但是我真的不想话,哭哭~如果初学者的话,一定不要学我这样。

在绘制波形图之前,我们先来找到.veo文件来看一下单端口RAM IP的端口。

  1. ram_256x8 your_instance_name (
  2. .clka(clka), // input wire clka
  3. .wea(wea), // input wire [0 : 0] wea
  4. .addra(addra), // input wire [7 : 0] addra
  5. .dina(dina), // input wire [7 : 0] dina
  6. .douta(douta) // output wire [7 : 0] douta
  7. );
  1. clka:时钟管脚

  1. wea:这个需要注意一下,在vivado中wea是一个读写使能的信号。当wea拉高时,为写使能;拉低则为读使能。因为单端口RAM IP读写操作是共用一组地址线,所以要特别注意一下。

而对于简单双端口 RAM,读操作和写操作有专用地址端口(一个读端口和一个写端口),即写端口只能写不能读,而读端口只能读不能写;对于真正双端口 RAM,有两个地址端口用于读写操作(两个读/写端口),即两个端口都可以进行读写。

  1. addra:为读写地址线

  1. dina:为写数据

  1. douta: 为读数据

本次使用的IP管脚就这些,下面根据IP的管脚来绘制波形图。

这里需要说明一下,当读写使能为低时,前面讲过了IP是进行读操作的。所以在本次的设计中,初始化状态也应该是一直在进行读操作的。

根据我们的波形图,可以很简单的编写一下ram_ctrl模块的代码。

  1. module ram_ctrl(
  2. input sys_clk ,
  3. input sys_rst_n ,
  4. input wr_flag ,
  5. input rd_flag ,
  6. output reg wea , //写数据
  7. output reg [7:0] addra ,
  8. output [7:0] wr_data ,
  9. output [7:0] rd_data
  10. );
  11. parameter CNT_MAX = 24'd9_999_999;
  12. reg [23:0] cnt_200ms;
  13. //cnt_200ms
  14. always@(posedge sys_clk or negedge sys_rst_n) begin
  15. if(sys_rst_n == 1'b0)
  16. cnt_200ms <= 24'd0;
  17. else if((cnt_200ms == CNT_MAX) || (wr_flag == 1'b1) || (rd_flag == 1'b1))
  18. cnt_200ms <= 24'd0;
  19. else if(wea == 1'b0)
  20. cnt_200ms <= cnt_200ms + 24'd1;
  21. end
  22. //wea 高为写,低为读
  23. always@(posedge sys_clk or negedge sys_rst_n) begin
  24. if(sys_rst_n == 1'b0)
  25. wea <= 1'b0;
  26. else if((addra == 8'd255 && cnt_200ms == CNT_MAX) || rd_flag == 1'b1)
  27. wea <= 1'b0;
  28. else if(wr_flag == 1'b1)
  29. wea <= 1'b1;
  30. end
  31. //addra
  32. always@(posedge sys_clk or negedge sys_rst_n) begin
  33. if(sys_rst_n == 1'b0)
  34. addra <= 8'd0;
  35. else if((addra == 8'd255 && cnt_200ms == CNT_MAX) || (wr_flag == 1'b1) || (rd_flag == 1'b1))
  36. addra <= 8'd0;
  37. else if((wea == 1'b0 && cnt_200ms == CNT_MAX) || (wea == 1'b1 && cnt_200ms == CNT_MAX))
  38. addra <= addra + 8'd1;
  39. end
  40. assign wr_data = (wea == 1'b1) ? addra : 8'd0;
  41. //ram
  42. ram_256x8 ram_256x8_inst (
  43. .clka (sys_clk), // input wire clka
  44. .wea (wea), // input wire [0 : 0] wea
  45. .addra (addra), // input wire [7 : 0] addra
  46. .dina (wr_data), // input wire [7 : 0] dina
  47. .douta (rd_data) // output wire [7 : 0] douta
  48. );
  49. endmodule

仿真代码:

  1. `timescale 1ns / 1ns
  2. module tb_ctrl_ram();
  3. reg sys_clk ;
  4. reg sys_rst_n ;
  5. reg wr_flag ;
  6. reg rd_flag ;
  7. wire wea ;
  8. wire [7:0] addra ;
  9. wire [7:0] dina ;
  10. wire [7:0] douta ;
  11. initial begin
  12. sys_clk = 1'b1;
  13. sys_rst_n <= 1'b0;
  14. wr_flag <= 1'b0;
  15. rd_flag <= 1'b0;
  16. #201
  17. sys_rst_n <= 1'b1;
  18. #1000
  19. //rd_flag
  20. rd_flag <= 1'b1;
  21. #20
  22. rd_flag <= 1'b0;
  23. #60_000
  24. //wr_flag
  25. wr_flag <= 1'b1;
  26. #20
  27. wr_flag <= 1'b0;
  28. #60_000
  29. //rd_flag
  30. rd_flag <= 1'b1;
  31. #20
  32. rd_flag <= 1'b0;
  33. #60_000
  34. //wr_flag
  35. wr_flag <= 1'b1;
  36. #20
  37. wr_flag <= 1'b0;
  38. #70_000
  39. //rd_flag
  40. rd_flag <= 1'b1;
  41. #20
  42. rd_flag <= 1'b0;
  43. end
  44. always #10 sys_clk <= ~sys_clk;
  45. defparam ram_ctrl_inst.CNT_MAX = 9;
  46. ram_ctrl ram_ctrl_inst(
  47. .sys_clk (sys_clk ),
  48. .sys_rst_n (sys_rst_n),
  49. .wr_flag (wr_flag),
  50. .rd_flag (rd_flag),
  51. .wea (wea) , //写数据
  52. .addra (addra) ,
  53. .wr_data (dina) ,
  54. .rd_data (douta)
  55. );
  56. endmodule

然后我们来看一下波形,首先是开始读的初始化数据:

然后再看一下按下写信号之后的波形:

最后就是将几个模块统一例化到顶层,然后约束管脚然后上板验证,这里就不演示了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/586023
推荐阅读
相关标签
  

闽ICP备14008679号