当前位置:   article > 正文

IP核之ROM_rom ip核

rom ip核

ROM概述

        ROM 是只读存储器(Read-Only Memory)的简称,是一种只能读出事先所存数据的固态半导体存储器。其特性是一旦储存资料就无法再将之改变或删除,且资料不会因为电源关闭而消失。

而事实上在 FPGA 中通过 IP 核生成的 ROM RAM 调用的都是 FPGA 内部的 RAM 资源,掉电内容都会丢失(这也很容易解释, FPGA 芯片内部本来就没有掉电非易失存储器单元)。用 IP 核生成的 ROM 模块只是提前添加了数据文件(.mif .hex 格式),在 FPGA 运行时通过数据文件给 ROM 模块初始化,才使得 ROM 模块像个“真正”的掉电非易失存储器;也正是这个原因,ROM 模块的内容必须提前在数据文件中写死,无法在电路中修改。
       Altera 推出的 ROM IP 核分为两种类型:单端口 ROM 和双端口 ROM
单端口ROM :提供一个读地址端口和一个读数据端口,只能进行读操作;
双端口 ROM:  与单端口ROM 类似,区别是其提供两个读地址端口和两个读数据端口,基本上可以看做两个单口RAM 拼接而成。

                                                 单端口ROM接口信号

双端口ROM接口信号

单端口ROM的配置

我们在工程目录下新建一个 ip_core 文件夹,将 IP核保存在该文件夹下并命名为 rom_256x8 rom 是我们调用的 IP 核, 256 是调用的 IP 核容量,8 是调用的 IP 核数据位宽。这里这样命名是为了方便识别我们创建的 IP 核类型及资源量,我们制作的数据文件就是容量为 256 ,数据位宽为 8bit)。

       选择使用的时钟模式,可选择单时钟或双时钟。选择单时钟时用一个时钟控制存储块的所有寄存器,选择双时钟时输入时钟控制地址寄存器,输出时钟控制数据输出寄存器。ROM 模式没有写使能、字节使能和数据输入寄存器。这里我们选择默认选项Single clock(单时钟)。

上图是选择是否输出“q”寄存器。这里我们把输出端口的寄存器去掉(如果不去掉的 话,就会使输出延迟一拍。如果没有特别的需求的话我们是不需要延迟这一拍的).

不勾选时,数据输出会延时地址输入一个时钟周期,勾选时,时钟信号会输入更高的频率,数据输出会延时地址输入两个时钟周期

上图是添加文件路径 

提示了我们单独使用第三方仿真工具时需要添加名为 “altera_mf”的库

双端口ROM的配置

As a number of words”是按字数确定,“AS a number of bits”是按比特数确定。

(注意:选择的容量需大于我们需要写入的数据文件的数据量

 Single clock(单时钟):使用一个时钟控制。

Dual clock use separate input and output clocks (双时钟:使用单独的输入时钟和输出时钟):输入和输出时钟分别控制存储块的数据输入和输出的相关寄存器。
Dual clock use separate clocks for A and B ports (双时钟:端口 A 和端口 B 使用不同的独立时钟):时钟 A 控制端口 A 的所有寄存器,时钟 B 控制端口 B 的所有寄存器。
ROM IP核的使用
实验目标
ROM 内的数据读取出来显示在数码管上。我们 ROM 的初始化数据是 0~255,也就是存入数据
0~255 。然后每隔 0.2s 我们从 0 地址开始往下读取数据显示在数码管上,我们再利用两个按键信号来读取指定地址的数据,每按一个按键就读取一个地址的数据显示在数码管上。再次按下按键后,以当前地址继续以 0.2s 的时间间隔往下读取数据并显示出来。

 ROM IP核模块

 ROM控制模块

ROM 的数据手册可知,读操作是在时钟的上升沿触发的,而我们在调用 ROM 时是没有生成读使能的,所以在 读时钟上升沿我们只要给相应的地址就能在时钟的上升沿读出该地址内的数据了。在该模块我们只需要控制生成读地址即可。

  ROM控制模块输入输出信号     

      ROM 并不是只能从 0 地址开始读取,它能读取指定的任意地址,为了验证这个功能,我们加入两个按键信号,每按一个按键就读出一个指定地址的数据,再次按下就又沿当前地址顺序读取。
      addr_flag1 addr_flag2 : 读 地 址 的 标 志 信 号 。 当 按 下 按 键 1/ 按 键 2
(key1_flag/key2_flag=1)时,让读地址的标志信号为高,再次按下时让读地址的标志信号为低,我们使用一个取反操作即可完成。 当按键 1 按下后, addr_flag1 为高读取地址 99 的数据;当按下按键 2 后, addr_flag2 为高读取地址 199 的数据(地址大家可任意去取,只要在 ROM 的地址范围即可)。这里需要注意的是 因为每次我们只能读取一个地址的信号,所以我们在拉高一个地址标志信号时要将另一个地址标志信号置为 0,这样读取的地址才不会有冲突。
ROM控制模块参考代码(rom_ctrl.v)
  1. module rom_ctrl
  2. #(
  3. parameter CNT_MAX = 24'd9_999_999
  4. )
  5. (
  6. input wire sys_clk ,
  7. input wire sys_rst_n,
  8. input wire key1 , //按键1消抖后有效信号
  9. input wire key2 , //按键2消抖后有效信号
  10. output reg [7:0] addr //输出读ROM地址
  11. );
  12. reg [23:0] cnt_200ms ;
  13. reg key1_en; //特定地址 1 标志信号
  14. reg key2_en; //特定地址 2 标志信号
  15. //0.2s 循环计数
  16. always@(posedge sys_clk or negedge sys_rst_n)
  17. if(sys_rst_n == 1'b0)
  18. cnt_200ms <= 24'd0;
  19. else if(cnt_200ms == CNT_MAX || key1_en == 1'b1 || key2_en == 1'b1)
  20. cnt_200ms <= 24'd0;
  21. else
  22. cnt_200ms <= cnt_200ms + 1'b1;
  23. //产生特定地址 1 标志信号
  24. always@(posedge sys_clk or negedge sys_rst_n)
  25. if(sys_rst_n == 1'b0)
  26. key1_en <= 1'b0;
  27. else if(key2 == 1'b1)
  28. key1_en <= 1'b0;
  29. else if(key1 == 1'b1)
  30. key1_en <= ~key1_en;
  31. else
  32. key1_en <= key1_en;
  33. /产生特定地址 2 标志信号
  34. always@(posedge sys_clk or negedge sys_rst_n)
  35. if(sys_rst_n == 1'b0)
  36. key2_en <= 1'b0;
  37. else if(key1 == 1'b1)
  38. key2_en <= 1'b0;
  39. else if(key2 == 1'b1)
  40. key2_en <= ~key2_en;
  41. else
  42. key2_en <= key2_en;
  43. //让地址从 0~255 循环,其中两个按键控制两个特定地址的跳转
  44. always@(posedge sys_clk or negedge sys_rst_n)
  45. if(sys_rst_n == 1'b0)
  46. addr <= 8'd0;
  47. else if(addr == 8'd255 && cnt_200ms == CNT_MAX)
  48. addr <= 8'd0;
  49. else if(key1_en == 1'b1)
  50. addr <= 8'd99;
  51. else if(key2_en == 1'b1)
  52. addr <=8'd199;
  53. else if(cnt_200ms == CNT_MAX)
  54. addr <= addr + 1'b1;
  55. endmodule

顶层模块

rom 顶层模块主要是对各个子功能模块的实例化,以及对应信号的连接

 

rom 顶层模块主要是对各个子功能模块的实例化,以及对应信号的连接,代码编写较为容易,无需波形图的绘制
  1. module rom
  2. (
  3. input wire sys_clk,
  4. input wire sys_rst_n,
  5. input wire key1 ,
  6. input wire key2 ,
  7. output wire ds,
  8. output wire oe,
  9. output wire shcp,
  10. output wire stcp
  11. );
  12. wire key1_flag; //按键 1 消抖信号
  13. wire key2_flag; //按键 2 消抖信号
  14. wire [7:0] addr;
  15. wire [7:0] data; //读出 ROM 数据
  16. key_filter
  17. #(
  18. .CNT_MAX (20'd999_999)
  19. )
  20. key_filter_inst1
  21. (
  22. .sys_clk (sys_clk),
  23. .sys_rst_n(sys_rst_n),
  24. .key_in (key1),
  25. .key_flag (key1_flag) //key_flag 为 1 时表示消抖后检测到按键被按下
  26. );
  27. key_filter
  28. #(
  29. .CNT_MAX (20'd999_999)
  30. )
  31. key_filter_inst2
  32. (
  33. .sys_clk (sys_clk),
  34. .sys_rst_n(sys_rst_n),
  35. .key_in (key2),
  36. .key_flag (key2_flag) //key_flag 为 1 时表示消抖后检测到按键被按下
  37. );
  38. rom_ctrl
  39. #(
  40. .CNT_MAX (24'd9_999_999)
  41. )
  42. rom_ctrl_inst
  43. (
  44. .sys_clk (sys_clk),
  45. .sys_rst_n(sys_rst_n),
  46. .key1 (key1_flag), //按键 1 消抖后有效信号
  47. .key2 (key2_flag), //按键 2 消抖后有效信号
  48. .addr (addr) //输出读 ROM 地址
  49. );
  50. rom_8x256 rom_8x256_inst
  51. (
  52. .address ( addr ),
  53. .clock ( sys_clk ),
  54. .q ( data )
  55. );
  56. seg_595_dynamic seg_595_dynamic
  57. (
  58. .sys_clk (sys_clk),
  59. .sys_rst_n(sys_rst_n),
  60. .data ({12'b0,data}),
  61. .point (6'b000_000), //小数点显示,高电平有效
  62. .sign (1'b0), //符号位,高电平显示负号
  63. .seg_en (1'b1), //数码管使能信号,高电平有效
  64. .ds (ds ), //串行数据输入
  65. .oe (oe ), //输出使能信号
  66. .shcp (shcp), //移位寄存器的时钟输入
  67. .stcp (stcp) //输出数据存储寄时钟
  68. );
  69. endmodule

 RTL视图     

 ROM IP核仿真

  1. `timescale 1ns/1ns
  2. module tb_rom();
  3. reg sys_clk;
  4. reg sys_rst_n;
  5. reg key1;
  6. reg key2;
  7. wire ds;
  8. wire oe;
  9. wire shcp;
  10. wire stcp;
  11. initial
  12. begin
  13. sys_clk = 1'b1;
  14. sys_rst_n <= 1'b0;
  15. key1 <= 1'b1;
  16. key2 <= 1'b1;
  17. #20
  18. sys_rst_n <= 1'b1;
  19. #700000 //仿真时是100个时钟周期,一个时钟周期20ns,也就是一个
  20. //数据的显示时间是100*20=2000ns,
  21. //256个数据显示时间是256*2000=512000ns,故延时的时间需要大于此值
  22. //key1
  23. key1 <= 1'b0;
  24. #20
  25. key1 <= 1'b1;
  26. #20
  27. key1 <= 1'b0;
  28. #20
  29. key1 <= 1'b1;
  30. #20
  31. key1 <= 1'b0;
  32. #200 //以上是模拟按键前抖动,以下是模拟按键后抖动
  33. key1 <= 1'b1;
  34. #20
  35. key1 <= 1'b0;
  36. #20
  37. key1 <= 1'b1;
  38. #20
  39. key1 <= 1'b0;
  40. #20
  41. key1 <= 1'b1;
  42. //key2
  43. #20000
  44. key2 <= 1'b0;
  45. #20
  46. key2 <= 1'b1;
  47. #20
  48. key2 <= 1'b0;
  49. #20
  50. key2 <= 1'b1;
  51. #20
  52. key2 <= 1'b0;
  53. #200 //以上是模拟按键前抖动,以下是模拟按键后抖动
  54. key2 <= 1'b1;
  55. #20
  56. key2 <= 1'b0;
  57. #20
  58. key2 <= 1'b1;
  59. #20
  60. key2 <= 1'b0;
  61. #20
  62. key2 <= 1'b1;
  63. //key2
  64. #20000
  65. key2 <= 1'b0;
  66. #20
  67. key2 <= 1'b1;
  68. #20
  69. key2 <= 1'b0;
  70. #20
  71. key2 <= 1'b1;
  72. #20
  73. key2 <= 1'b0;
  74. #200 //以上是模拟按键前抖动,以下是模拟按键后抖动
  75. key2 <= 1'b1;
  76. #20
  77. key2 <= 1'b0;
  78. #20
  79. key2 <= 1'b1;
  80. #20
  81. key2 <= 1'b0;
  82. #20
  83. key2 <= 1'b1;
  84. end
  85. always #10 sys_clk = ~sys_clk;
  86. //defparam rom_inst.key_filter_indt1.CNT_MAX = 9;
  87. rom rom_inst
  88. (
  89. .sys_clk (sys_clk ),
  90. .sys_rst_n(sys_rst_n),
  91. .key1 (key1 ),
  92. .key2 (key2 ),
  93. .ds (ds ),
  94. .oe (oe ),
  95. .shcp (shcp ),
  96. .stcp (stcp )
  97. );
  98. endmodule

接下来仿真验证

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

闽ICP备14008679号