赞
踩
目录
在了解 RAM IP 核之前,我们先来看下存储器的大致分类,如下图所示:
关于具体每一张图中的具体选项的含义可详见正点原子的《领航者ZYNQ 之 FPGA 开发指南》P542
本次实验写入32个数据所以深度为32,数据位宽为8
至此,ramIP核配置成功。生成ramIP核文件。
本次实验任务是在1~31个地址中写入1~31个数,然后再在1~31个地址中读取这些数据。
读写波形图如下图所示
RAM 读写模块设计代码(代码的输入是RAMIP核的输出,代码的输出是RAMIP核的输入)
- module ram_rw(
- input clk, //系统时钟,50MHz
- input reset_n, //系统复位按键,低电平有效
- input [7 : 0] ram_rd_data, //ram读数据
- output reg ram_en, //ram端口使能信号,高有效
- output wire ram_we, //ram读写使能信号,1为写,0为读
- output reg [4 : 0] ram_addr, //ram读写地址
- output reg [7 : 0] ram_wr_data //ram写数据
- );
-
- reg [5:0] rw_cnt ; //读写控制计数器
-
- always @(posedge clk or negedge reset_n)
- if(!reset_n)
- ram_en <= 0;
- else
- ram_en <= 1;
-
- assign ram_we = (rw_cnt <= 6'd31 && ram_en == 1'b1) ? 1'b1 : 1'b0;//组合逻辑实现会和ram_en同步
-
- //时序逻辑am_we实现会使得其不在ram_en信号拉高的同时立马拉高,会晚半拍
- /*always @(posedge clk or negedge reset_n)
- if(!reset_n)
- ram_we <= 0;
- else if(ram_en && rw_cnt <= 31)
- ram_we <= 1;
- else
- ram_we <= 0;
- */
- //读写控制计数器,计数器范围 0~63
- always @(posedge clk or negedge reset_n)
- if(!reset_n)
- rw_cnt <= 0;
- else if(ram_en)begin
- if(rw_cnt >= 63 )
- rw_cnt <= 0;
- else
- rw_cnt <= rw_cnt + 1;
- end
- else
- rw_cnt <= 0;
-
- //读写地址信号 范围:0~31
- always @(posedge clk or negedge reset_n) begin
- if(!reset_n)
- ram_addr <= 0;
- else if(ram_addr == 5'd31 && ram_en)
- ram_addr <= 5'b0;
- else if (ram_en)
- ram_addr <= ram_addr + 1'b1;
- else
- ram_addr <= 5'b0;
- end
-
- //在 WE 拉高期间产生 RAM 写数据,变化范围是 0~31
- always @(posedge clk or negedge reset_n) begin
- if(!reset_n)
- ram_wr_data <= 8'b0;
- else if(ram_wr_data < 8'd31 && ram_we)
- ram_wr_data <= ram_wr_data + 1'b1;
- else
- ram_wr_data <= 8'b0 ;
- end
-
- endmodule
设计代码
- module zdyz_ip_ram(
- input clk,
- input reset_n
- );
-
- wire ram_en;
- wire ram_we;
- wire [4 : 0] ram_addr;
- wire [7 : 0] ram_wr_data ;
- wire [7 : 0] ram_rd_data;
- //例化ram 读写模块
- ram_rw ram_rw(
- . clk(clk), //系统时钟,50MHz
- . reset_n(reset_n), //系统复位按键,低电平有效
- . ram_rd_data(ram_rd_data), //ram读数据
- . ram_en(ram_en), //ram端口使能信号,高有效
- . ram_we(ram_we), //ram读写使能信号,1为写,0为读
- . ram_addr(ram_addr), //ram读写地址
- . ram_wr_data(ram_wr_data) //ram写数据
- );
-
- blk_mem_gen_0 blk_mem_gen_0 (
- .clka(clk), // input wire clka
- .ena(ram_en), // input wire ena
- .wea(ram_we), // input wire [0 : 0] wea
- .addra(ram_addr), // input wire [4 : 0] addra
- .dina(ram_wr_data), // input wire [7 : 0] dina
- .douta(ram_rd_data) // output wire [7 : 0] douta
- );
- endmodule
- `timescale 1ns / 1ps
- module zdyz_ip_ram_tb();
-
- reg clk;
- reg reset_n;
-
- initial begin
- clk = 1'b0;
- reset_n = 1'b0;
- #200
- reset_n = 1'b1;
- end
- //产生时钟
- always #20 clk = ~clk;
- zdyz_ip_ram zdyz_ip_ram(
- .clk(clk),
- .reset_n(reset_n)
- );
- endmodule
仿真通过
关于具体每一张图中的具体选项的含义可详见小梅哥的《基于HDL的FPGA逻辑设计与验证教程》P314
选择 Block Memory Generator 双击鼠标进入到 RAM IP 配置界面。
这里我们选择简单双端口 RAM(Simple Dual Port RAM)
这里选择NO Change(其他选项的具体说明可参考文档教程)
- `timescale 1ns / 1ps
- module xmg_ram_ip_tb();
-
- reg clka ;
- reg clkb ;
- reg wea ;
- reg [7 : 0] addra ;
- reg [7 : 0] dina ;
- reg [7 : 0] addrb ;
- wire [7 : 0] doutb ;
- integer i;//integer类型用于表示整数值。在FPGA设计中,integer类型通常用于计数器、延时器等电路中。作用:用于表示整数。
-
-
- blk_mem_gen_0 blk_mem_gen_0 (
- .clka(clka), // input wire clka
- .wea(wea), // input wire [0 : 0] wea
- .addra(addra), // input wire [7 : 0] addra
- .dina(dina), // input wire [7 : 0] dina
- .clkb(clkb), // input wire clkb
- .addrb(addrb), // input wire [7 : 0] addrb
- .doutb(doutb) // output wire [7 : 0] doutb
- );
-
-
- initial clka = 1;
- always #10 clka = ~clka;
-
- initial clkb = 1;
- always #10 clkb = ~clkb;
-
- initial begin
- wea=0;
- addra=0;
- dina=0;
- addrb=0; //255
- #201;
- wea = 1;
- for (i=0;i<=15;i=i+1)begin
- dina=255-i;//写入数据
- addra = i;//选择地址
- #20;
- end
- wea=0;
- #1;
- for (i=0;i<=15;i=i+1)begin
- addrb=i;//读取相应地址的数据
- #40;
- end
- #200;
- $stop;
- end
- endmodule
模块框图:
模块代码
- module zdyz_ram_wr(
- input clk , //时钟信号
- input reset_n , //复位信号,低电平有效
- //RAM 写端口操作
- output ram_wr_we , //ram 写使能
- output reg ram_wr_en , //端口使能
- output reg [5:0] ram_wr_addr , //ram 写地址
- output [7:0] ram_wr_data ,//ram 写数据
- output reg rd_flag //读启动信号
- );
-
- //ram_wr_we 为高电平表示写数据
- assign ram_wr_we = ram_wr_en;
- //写数据与写地址相同,因位宽不等,所以高位补 0
- assign ram_wr_data = {2'b0,ram_wr_addr};
-
- //控制 RAM 使能信号
- always @(posedge clk or negedge reset_n)
- if(!reset_n)
- ram_wr_en <= 1'b0;
- else
- ram_wr_en <= 1'b1;
-
- //写地址信号 范围:0~63
- always @(posedge clk or negedge reset_n)
- if(!reset_n)
- ram_wr_addr <= 0;
- else if(ram_wr_en && ram_wr_addr < 63)
- ram_wr_addr <= ram_wr_addr + 1;
- else
- ram_wr_addr <= 0;
-
- //当写入 32 个数据(0~31)后,拉高读启动信号
- always @(posedge clk or negedge reset_n)
- if(!reset_n)
- rd_flag <= 0;
- else if(ram_wr_addr == 31)
- rd_flag <= 1;
- else
- rd_flag <= rd_flag;
-
- endmodule
- module zdyz_ram_rd(
- input clk , //时钟信号
- input reset_n , //复位信号,低电平有效
- //RAM 读端口操作
- input rd_flag , //读启动标志
- input [7:0] ram_rd_data ,//ram 读数据
- output wire ram_rd_en , //端口使能
- output reg [5:0] ram_rd_addr //ram 读地址
- );
-
- assign ram_rd_en = rd_flag;
-
- //读地址信号 范围:0~63
- always @(posedge clk or negedge reset_n)
- if(!reset_n)
- ram_rd_addr <= 0;
- else if(rd_flag && ram_rd_addr < 63)
- ram_rd_addr <= ram_rd_addr + 1;
- else
- ram_rd_addr <= 0;
-
- endmodule
- module zdyz_ip_2port_ram(
- input clk , //系统时钟
- input reset_n //系统复位,低电平有效
- );
- wire ram_wr_we;
- wire ram_wr_en;
- wire [5:0]ram_wr_addr; //ram 写地址
- wire [7:0]ram_wr_data; //ram 写数据
- wire [5:0]ram_rd_addr; //ram 读地址
- wire [7:0]ram_rd_data; //ram 读数据
- wire rd_flag; //读启动标志
- wire ram_rd_en;
-
- //RAM 写模块例化
- zdyz_ram_wr zdyz_ram_wr(
- .clk (clk) , //时钟信号
- .reset_n (reset_n) , //复位信号,低电平有效
- .ram_wr_we (ram_wr_we) , //ram 写使能
- .ram_wr_en (ram_wr_en) , //端口使能
- .ram_wr_addr (ram_wr_addr) , //ram 写地址
- .ram_wr_data (ram_wr_data) , //ram 写数据
- .rd_flag (rd_flag) //读启动信号
- );
-
- //RAM 读模块例化
- zdyz_ram_rd zdyz_ram_rd(
- .clk (clk) ,//时钟信号
- .reset_n (reset_n) ,//复位信号,低电平有效
- .rd_flag (rd_flag) ,//读启动标志
- .ram_rd_data (ram_rd_data) ,//ram 读数据
- .ram_rd_en (ram_rd_en) ,//端口使能
- .ram_rd_addr (ram_rd_addr) //ram 读地址
- );
-
- //RAM IP核例化
- zdyz_blk_mem_gen_2 your_instance_name (
- .clka(clk), // input wire clka
- .ena(ram_wr_en), // input wire ena
- .wea(ram_wr_we), // input wire [0 : 0] wea
- .addra(ram_wr_addr), // input wire [5 : 0] addra
- .dina(ram_wr_data), // input wire [7 : 0] dina
- .clkb(clk), // input wire clkb
- .enb(ram_rd_en), // input wire enb
- .addrb(ram_rd_addr), // input wire [5 : 0] addrb
- .doutb(ram_rd_data) // output wire [7 : 0] doutb
- );
-
- endmodule
- `timescale 1ns / 1ps
- module zdyz_ip_2port_ram_tb();
-
- //parameter define
- parameter CLK_PERIOD = 20; //时钟周期 20ns
-
- //reg define
- reg clk;
- reg reset_n;
-
- //信号初始化
- initial begin
- clk = 1'b0;
- reset_n = 1'b0;
- #200
- reset_n = 1'b1;
- end
- //产生时钟
- always #(CLK_PERIOD/2) clk = ~clk;
- zdyz_ip_2port_ram zdyz_ip_2port_ram(
- .clk (clk ),
- .reset_n(reset_n)
- );
- endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。