您好,欢迎来到华拓科技网。
搜索
您的当前位置:首页vivado RAM ip核的使用

vivado RAM ip核的使用

来源:华拓科技网


RAM(Random Access Memory )简介 

        当我们做相关项目时,经常会遇到存储数据的问题,数据量过大时,我们可以将其存储在 FPGA 芯片的外设存储器上,比如sdram、 ddr sdram 等,然而访问这些外设存储器相对比较麻烦,因此当数据量较小时,我们可以直接使用 FPGA 芯片内部自带的 IP 生成的 RAM。 RAM (random access memory)是随机存储器的意思, ram 可以按照所需进行随机读/写,掉电后数据丢失。

配置RAM

打开IP Catalog,配置ROM用的也是这个IP核

 配置IP核,有多种模式可选择:单口RAM,简单双口RAM,真双口RAM,单口ROM,双口ROM

单口RAM

 

summary中检查无误点击OK生成IP核

程序设计 

深度为16384,位宽为16bit的RAM,写数据为wr_addr,写完就读,读完就写,并使用testbench进行仿真

`timescale 1ns / 1ps

// 单口RAM
module ram_ctrl(
    input clk,
    input rst_n,

    //input [15:0] wr_data,  // 写入数据
    output [15:0] rd_data // 读出数据

);



parameter Depth = 16384;


reg wr_en; //高电平时向RAM中写入数据,低电平时从RAM中读出数据
reg [13:0] wr_addr; // 写地址  ---深度为16384
reg [13:0] rd_addr; // 读地址
wire [13:0] addr; // 地址

wire [15:0] wr_data;
assign wr_data = wr_addr ;  //写入数据

//----------------- wr_en
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_en <= 1'b1;
    else if(rd_addr == Depth - 1)     // 读完了
        wr_en <= 1'b1;
    else if(wr_addr == Depth - 1)     // 写完了
        wr_en <= 1'b0;
    else
        wr_en <= wr_en;
end

//----------------- wr_addr
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_addr <= 'b0;
    else if(wr_en) begin
        if(wr_addr == Depth - 1)
            wr_addr <= 'b0;
        else 
            wr_addr <= wr_addr + 1'b1;
    end
    else
        wr_addr <= 'b0;
end




//----------------- rd_addr
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rd_addr <= 'b0;
    else if(!wr_en) begin
        if(rd_addr == Depth - 1)
            rd_addr <= 'b0;
        else 
            rd_addr <= rd_addr + 1'b1;
    end
    else
        rd_addr <= 'b0;
end

// ------------------ addr
assign addr = wr_en?wr_addr:rd_addr;


adc_data_ram adc_data_ram_inst(
    .clka(clk),   // 50Mhz
    .ena(1'b1),
    .wea(wr_en), // 写使能
    .addra(addr), // 地址 -------读/写
    .dina(wr_data), // 写数据
    .douta(rd_data) // 读数据
);

endmodule

testbench

`timescale 1ns / 1ps

module tb_ram_ctrl();

reg clk;
reg rst_n;
wire [15:0] rd_data;



initial begin
    clk = 1;
    rst_n = 0;
    #3000;
    rst_n = 1;

end

always#10 clk = ~clk;




ram_ctrl ram_ctrl_inst(
    .clk(clk),
    .rst_n(rst_n),
    .rd_data(rd_data)
);

endmodule
 仿真结果

 把显示格式改成analog,可以直观地看看

应用举例

fpga通过ADC采样得到波形数据,先存储到RAM中,再通过spi将波形数据发送给stm32,stm32每完成一次接受数据,rd_addr就加1,继续读出下一个数据,再由stm32串口打印数据,这样我们便可以在上位机上直观看到stm32接受到的波形。在这个应用场景中,fpga充当了一个数据采集模块。

ram_ctrl 

`timescale 1ns / 1ps

// 单口RAM
module ram_ctrl(
    input clk,
    input rst_n,
    input tx_done,

    input [15:0] wr_data,  // 写入数据
    output [15:0] rd_data // 读出数据

);



parameter Depth = 16384;


reg wr_en; //高电平时向RAM中写入数据,低电平时从RAM中读出数据
reg [13:0] wr_addr; // 写地址  ---深度为16384
reg [13:0] rd_addr; // 读地址
wire [13:0] addr; // 地址

// wire [15:0] wr_data;
// assign wr_data = wr_addr ;  //写入数据


//----------------- wr_en
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_en <= 1'b1;
    else if(rd_addr == Depth - 1)     // 读完了
        wr_en <= 1'b1;
    else if(wr_addr == Depth - 1)     // 写完了
        wr_en <= 1'b0;
    else
        wr_en <= wr_en;
end

//----------------- wr_addr
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_addr <= 'b0;
    else if(wr_en) begin
        if(wr_addr == Depth - 1)
            wr_addr <= 'b0;
        else 
            wr_addr <= wr_addr + 1'b1;
    end
    else
        wr_addr <= 'b0;
end




//----------------- rd_addr
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rd_addr <= 'b0;
    else if(!wr_en) begin
        if(rd_addr == Depth - 1)
            rd_addr <= 'b0;
        else if(tx_done)
            rd_addr <= rd_addr + 1'b1;
    end
    else
        rd_addr <= 'b0;
end

// ------------------ addr
assign addr = wr_en?wr_addr:rd_addr;


adc_data_ram adc_data_ram_inst(
    .clka(clk),   // 50Mhz
    .ena(1'b1),
    .wea(wr_en), // 写使能
    .addra(addr), // 地址 -------读/写
    .dina(wr_data), // 写数据
    .douta(rd_data) // 读数据
);

endmodule

主程序

`timescale 1ns / 1ps

module adc_top(
    input clk,
    input rst_n,
    input [11:0] adc_data,   //采集到的adc数据 --- 12bit ADC

    output adc_clk,          //adc驱动时钟
    input spi_sck,
    output spi_miso,
    input spi_mosi,
    input spi_cs


);



reg [15:0] num;
reg [15:0] cnt;
wire [15:0] adc_data_16;
assign adc_data_16 = {4'b0000, adc_data};   // 数据拼接,便于spi发送16bit数据
reg [15:0] adc_data_16_reg;
wire [15:0] data_input;      // fpga从机接受主机发送数据
wire [15:0] rd_data;   //   RAM/fifo读出的数据
wire tx_done;

wire rst;
assign rst = ~rst_n;

wire clk_20m;
wire clk_10m;
wire clk_5m;
wire locked;

// // 写时钟域的信号
wire wr_clk;
assign wr_clk = adc_clk;  //65Mhz

wire rd_clk;







// 寄存器
always @ (posedge adc_clk or negedge rst_n) begin
  if (!rst_n)
    adc_data_16_reg <= 16'd0;
  else
    adc_data_16_reg <= adc_data_16;
end

// 16位波形数据存在RAM中,spi发送完成后才重新开始写RAM,
ram_ctrl ram_ctrl_inst(
    .clk(clk),
    .rst_n(rst_n),
    .tx_done(tx_done),        //  spi发送完成信号,rd_addr加1继续读
    .wr_data(adc_data_16_reg),
    .rd_data(rd_data)
);




//   感觉用fifo的意义不大,还是用RAM好了


// fifo_ctrl fifo_ctrl_inst(
//     .wr_clk(wr_clk),
//     .rd_clk(rd_clk),
//     .rst_n(rst_n),
//     .fifo_data_in(adc_data_16_reg),
//     .rd_data(rd_data)
// );





// spi从机
spi_slave spi_slave_inst(
    .clk(clk),     // 50Mhz
    .rst_n(rst_n),
    .data_in(rd_data),     // fpga发送数据
    .data_out(data_input),
    .tx_en(1'b1),                // fpga 发送使能
    .tx_done(tx_done),     // 发送完成标志位
    .spi_sck(spi_sck),
    .spi_miso(spi_miso),
    .spi_mosi(spi_mosi)
);


clock_0 clock_0_inst(
    .clk_65m(adc_clk),
    .clk_5m(clk_5m),
    .clk_20m(rd_clk),
    .clk_10m(clk_10m),

    .resetn(rst_n),
    .locked(locked),
    .clk_in1(clk)
);


endmodule

简单双口RAM

端口A配置,和端口B配置,均和单口RAM的配置差不多,模仿上面即可。 

应用举例

RAM ip核例化

//-----------------------------双口RAM
ram_data ram_data_inst(
  .clka(clk_100m),    // 写时钟
  .ena(clk_en),      // 
  .wea(wr_en),      // wr_en
  .addra(wr_addr),  // 写地址
  .dina(wr_data),    // 写数据
  .clkb(rd_clk),    // 读时钟
  .enb(1'b1),      // 
  .addrb(rd_addr),  // 
  .doutb(rd_data)  //
);

 sample模块采样数据存入RAM中,控制wr_addr,wr_data,wr_en等信号

//-----------------------------采样模块
sample sample_inst(
    .clk(clk_100m),
    .rst_n(rst_n),
    .act(act),            //启动触发
    .clk_en(clk_en),              //时钟使能
    .channel_sel(channel_sel),   //通道选择   channel_sel
    .mode_sel(mode_sel),      //模式选择
    .data_in(data_in),       // 10路 信号输入
    .wr_addr(wr_addr),      //写RAM地址
    .wr_data(wr_data),      //写RAM数据
    .wr_en(wr_en)              //RAM写使能
);

显示模块读取RAM数据,控制rd_addr,rd_data,rd_clk等信号

//------------------------------显示驱动
display_ctrl display_ctrl_inst(
    .vga_clk(clk_9m),
    .rst_n(rst_n),
    .display_en(vga_en),           //显示使能
    .offset(offset),              // 显示区域偏移
    .marker(marker),              //显示垂直基准线
    .vcount(vcount),            // vga 行扫描计数器
    .hcount(hcount),            // vga 场扫描计数器
    .rd_data(rd_data),            // 波形存储器读数据

    .rd_clk(rd_clk),              // 读数据时钟
    .rd_addr(rd_addr),             // 读数据地址
    .display_data(vga_data_in)         // 显示数据

);

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo6.cn 版权所有 赣ICP备2024042791号-9

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务