ROM与RAM


前言

本实验在 ROM 中初始化一个周期的正弦数据,不断的从 ROM 读出数据,产生周期性的数字正弦波。本实验向 RAM 这个 IP 核写入 256 个数据,然后再读出数据,然后再写入 256 个数据,然后再读出 256 个数据,如此循环地读写数据。


一、ROM

1.IP核配置

1.在 IP Catalog 中搜索 rom,找到 Block Memory Generator
2.给 IP 命名,接口类型选择 Native,存储类型选择 Single Port ROM,为单时钟的单口 ROM
3.选取位宽为 8,深度为 1024,一直使能,去掉输出寄存器,输出寄存器相当于对输出的数据打了一拍,适用于速率较高的场合。
4.工程中提供了用来生成的 coe 文件的 matlab 程序,点击 Browse,选择用matlab 程序生成的 coe 文件。
matlab代码如下

X=linspace(0,2*pi,1024);%在0-2π之等间距生成1024个点
Y=(255/2)*(sin(X)+1);%0-255之间的正弦信号
Y=uint8(Y);        %把值转化为8位的无符号数
plot(X,Y);         %画出图像
fid0 = fopen('sin8x1024.coe', 'wt');%打开一个文件
fprintf(fid0,'MEMORY_INITIALIZATION_RADIX=16;\nMEMORY_INITIALIZATION_VECTOR=\n');%先写入coe文件都需要加的一句话
fprintf(fid0, '%02x,\n', Y);%将矩阵按照2位16进制数输出,填充0占位
fid0 = fclose(fid0);%关闭这个文件

5.然后点击 ok,创建 IP,点击 Generate,完成 IP 核的生成

2.Verilog代码

// 定义一个名为rd_rom的模块,它有两个输入端口:时钟信号clk和复位信号rst_n
module rd_rom(
    input clk,
    input rst_n
);

// 声明一个8位宽的wire类型变量rom_data,用于存储从ROM中读取的数据
    wire[7:0] rom_data;
    
// 声明一个10位宽的reg类型变量addr,用作ROM的地址寄存器
    reg[9:0] addr;
    
// 定义一个always块,它在时钟信号clk的上升沿或复位信号rst_n的下降沿触发
    always @(posedge clk or negedge rst_n) begin
        // 如果复位信号rst_n为低,则将地址寄存器addr清零
        if(!rst_n)
            addr <= 'd0;
        // 如果没有复位,地址寄存器addr每次时钟上升沿自增1
        else
            addr <= addr + 1'b1;
    end
    
// 实例化一个8位宽,1024个地址的ROM模块rom8_1024
    rom8_1024 rom8_1024(
        .clka(clk),    // 连接时钟信号到ROM模块
        .addra(addr),  // 连接地址寄存器到ROM模块的地址输入
        .douta(rom_data) // 连接ROM模块的数据输出到变量rom_data
    );


endmodule

3.仿真代码

// 设置仿真的时间单位为1纳秒,时间精度为1皮秒
`timescale 1ns / 1ps

// 定义测试模块tb_rd_rom
module tb_rd_rom();
    // 定义测试模块中的全局时钟信号clk和复位信号rst_n
    reg clk;
    reg rst_n;

    // 实例化被测试模块rd_rom,并将全局时钟和复位信号连接到该模块
    rd_rom rd_rom(
        .clk(clk),
        .rst_n(rst_n)
    );

    // 第一个initial块,用于生成时钟信号
    initial begin
        clk = 1; // 初始化时钟信号为高电平
        forever begin // 创建一个永久循环,产生时钟信号
            #10 clk = ~clk; // 每10纳秒翻转一次时钟信号,产生50MHz的时钟频率
        end
    end
    
    // 第二个initial块,用于初始化复位信号并触发复位过程
    initial begin
        rst_n = 0; // 初始化复位信号为低电平,表示复位开始
        repeat(20) @(posedge clk); // 等待20个时钟周期,每个周期10纳秒,共200纳秒
        rst_n = 1; // 将复位信号设置为高电平,表示复位结束
    end

endmodule

4.仿真结果

仿真结果如下,找到从 ROM 中读出的数据右击把数设为无符号数,然后把波形以模拟波形的形式显示,最终得到正弦波,功能仿真完成

在这里插入图片描述

二、RAM

1.IP核配置

1.在 IP Catalog 中搜索 ram,找到 Block Memory Generator
2.给 IP 命名,接口类型选择 Native,存储类型选择 Single Port ROM,为单时钟的单口 RAM
3.选取位宽为 8,深度为 256,一直使能,去掉输出寄存器
4.不初始化 RAM 的数据
5.然后点击 ok,创建 IP,点击 Generate,完成 IP 核的生成

2.Verilog代码

// 定义模块wr_rd_ram,它有两个输入信号:时钟clk和复位信号rst_n
module wr_rd_ram(
    input clk,
    input rst_n
);

// 声明一个寄存器变量wr_en,用于控制写使能
    reg wr_en;
// 声明一个8位宽的寄存器变量wr_addr,用作写地址
    reg[7:0] wr_addr;
// 声明一个8位宽的wire变量din,用作数据输入
    wire[7:0] din;
// 声明一个8位宽的寄存器变量rd_addr,用作读地址
    reg[7:0] rd_addr;
// 声明一个8位宽的wire变量dout,用作数据输出
    wire[7:0] dout;
// 声明一个8位宽的wire变量addr,用作存储器的地址输入
    wire[7:0] addr;

// 定义一个always块,用于控制写使能信号wr_en
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            wr_en <= 1'b1; // 复位时,写使能设置为高电平
        else if (rd_addr == 255) // 如果读地址达到255,写使能设置为高电平
            wr_en <= 1'b1;
        else if (wr_addr == 255) // 如果写地址达到255,写使能设置为低电平
            wr_en <= 1'b0;
        else
            wr_en <= wr_en; // 否则保持当前状态
    end

// 定义一个always块,用于更新写地址寄存器wr_addr
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            wr_addr <= 'd0; // 复位时,写地址清零
        else if (wr_en) begin // 如果写使能为高
            if (wr_addr == 255) // 如果写地址达到255
                wr_addr <= 'd0; // 写地址重置为0
            else
                wr_addr <= wr_addr + 1'b1; // 否则写地址递增
        end
        else
            wr_addr <= 'd0; // 如果写使能为低,写地址清零
    end

// 定义一个always块,用于更新读地址寄存器rd_addr
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            rd_addr <= 'd0; // 复位时,读地址清零
        else if (!wr_en) begin // 如果写使能为低
            if (rd_addr == 255) // 如果读地址达到255
                rd_addr <= 'd0; // 读地址重置为0
            else
                rd_addr <= rd_addr + 1'b1; // 否则读地址递增
        end
        else
            rd_addr <= 'd0; // 如果写使能为高,读地址清零
    end

// 将写地址赋值给数据输入din
    assign din = wr_addr;
// 根据写使能wr_en,选择写地址或读地址作为存储器的地址输入addr
    assign addr = wr_en ? wr_addr : rd_addr;

// 实例化一个8位宽,256个地址的RAM模块ram8_256
    ram8_256 ram8_256(
        .clka(clk), // 连接时钟信号
        .wea(wr_en), // 连接写使能信号
        .addra(addr), // 连接地址输入
        .dina(din), // 连接数据输入
        .douta(dout) // 连接数据输出
    );

endmodule

3.仿真代码

`timescale 1ns / 1ps // 设置仿真的时间单位为1纳秒,时间精度为1皮秒

module tb_wr_rd_ram(); // 定义测试模块tb_wr_rd_ram

    // 定义测试模块中的全局时钟信号clk和复位信号rst_n
    reg clk;
    reg rst_n;

    // 实例化被测试模块wr_rd_ram,并将全局时钟和复位信号连接到该模块
    wr_rd_ram wr_rd_ram(
        .clk(clk),
        .rst_n(rst_n)
    );

    // 第一个initial块,用于生成时钟信号
    initial begin
        clk = 1; // 初始化时钟信号为高电平
        forever begin // 创建一个永久循环,产生时钟信号
            #10 clk = ~clk; // 每10纳秒翻转一次时钟信号,产生50MHz的时钟频率
        end
    end
    
    // 第二个initial块,用于初始化复位信号并触发复位过程
    initial begin
        rst_n = 0; // 初始化复位信号为低电平,表示复位开始
        repeat(20) @(posedge clk); // 等待20个时钟周期,每个周期10纳秒,共200纳秒
        rst_n = 1; // 将复位信号设置为高电平,表示复位结束
    end

endmodule // 结束测试模块tb_wr_rd_ram的定义

4.仿真结果

在这里插入图片描述
当 wr_en=1 的时候往 ram 中依次写入 0-255;当 wr_en=0 的时候,依次从地址 0-255 中读出数据

在这里插入图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值