Vivado 下 IP核之双端口 RAM 读写

目录

Vivado 下 IP核之双端口 RAM 读写

1、RAM IP 核简介

2、实验任务

3、程序设计

3.1、RAM IP 核配置

3.2、顶层模块设计

(1)绘制波形图

4、编写代码

4.1、顶层模块 ip_2port_ram

4.2、RAM 写模块设计

4.3、ram_wr 模块代码

4.4、RAM 读模块设计

4.5、ram_rd 模块代码

5、仿真验证

5.1、编写 TB 文件

5.2、仿真验证

6、下载验证

6.1、引脚约束

6.2、添加 ILA IP 核进行在线调试

6.3、上板验证

7、本章总结


Vivado 下 IP核之双端口 RAM 读写

1、RAM IP 核简介

       双端口 RAM 是指拥有两个读写端口的 RAM,有伪双端口 RAM(一个端口只能读,另一个端口只能写)和真双端口 RAM(两个端口都可以进行读写操作)之分。一般当我们需要同时对存储器进行读写操作时会使用到双端口 RAM例如有一个 FIFO 存储器,我们需要同时对其进行数据的写入和读出,这时候就需要一个写端口和一个读端口了。接下来我们看下双端口 RAM 的框图。

       在上一章中我们介绍过了单端口 RAM 的框图,本章将带着大家一起了解一下双端口 RAM 的框图,为 了方便大家进行对比,这里我们将上一章介绍的单端口 RAM 的框图也展示出来,如下图所示:

       首先介绍的是简单双端口(也称为伪双端口)RAM,需要注意的是简单双端口 RAM 的端口 A 只能写不能读,端口 B 只能读不能写。BMG IP 核配置成简单双端口 RAM 的框图如下图所示。

       与单端口 RAM 不同的是,   伪双端口 RAM 输入有两路时钟信号 CLKA/CLKB;独立的两组地址信号 ADDRA/ADDRB;Port A 仅提供 DINA 写数据总线,作为数据的写入口;Port B 仅提供数据读的功能,读出的数据为 DOUTB这里我们仅对新出现的信号进行讲解,其它信号在单端口 RAM 中已经讲解过了,其中不同端口的同名(同功能)信号以 A B 做为区分,各个新端口(这些信号很少使用,我们一般不用关注)的功能描述如下:

  • INJECTSBITERR:Inject Single-Bit Error 的简写,即注入单 bit 错误,仅适用于 Xilinx Zynq-7000 7 系列芯片的 ECC 配置。
  • INJECTDBITERR:Inject Double-Bit Error 的简写,即注入双 bit 错误,同样仅适用于 Xilinx Zynq-7000 和 7 系列芯片的 ECC 配置。
  • SBITERR:Single-Bit Error 的简写,即单 bit 错误,标记内存中存在的单 bit 错误,该错误已在输出总线上自动更正。
  • DBITERR:Double-Bit Error 的简写,即双 bit 错误,标记内存中存在双 bit 错误,需要注意的是内置的ECC 解码模块不能自动纠正双 bit 错误。
  • RDADDRECC:Read Address for ECC Error output 的简写,即读地址 ECC 错误输出,同样仅适用于 Xilinx Zynq-7000 和 7 系列芯片的 ECC 配置。

       接着介绍一下真双端口 RAM,其两个端口(A B)均可进行读/写操作BMG IP 核配置成真双端口 RAM 的框图如下图所示。

       真双端口 RAM 提供了两个独立的读写端口(A B),既可以同时读,也可以同时写,也可以一个读一个写。通过框图对比可以发现,真双端口 RAM 只是将单端口 RAM 的所有信号做了一个复制处理,不同端口的同一信号以 A B 作为区分,所以这里我们就不再赘述各个端口的功能了。

        下方我们列出了三种静态 RAM 的端口对比表,让大家能更直观的看出各静态 RAM 的端口差异,其中 “√”表示有,“×”表示无。

        通过对比我们可以发现无论是哪种双端口 RAM,其地址线、时钟线和使能线等控制信号都有两组,所以双端口 RAM 可以实现在不同时钟域下的读/写,且可以同时对不同的地址进行读/写,这便大大提高了我们数据处理的灵活性。但是两组信号线也相应的加大了双端口 RAM 的使用难度,因为端口使能,读写使能,地址和写数据等控制信号我们都需要分别给出两组,这样才能驱使两个端口都处于我们需要的工作状态, 这里仅凭文字描述大家理解起来可能会有些吃力,所以在稍后的小节中我们会结合波形图(图 24.3.9)进行更详细的讲解。

       需要注意的是在伪双端口模式下我们需要避免读写冲突;在真双端口模式下我们需要避免读写冲突和写写冲突下面我们分别看下读写冲突和写写冲突是什么。

1、读写冲突:即同时刻读写同一地址所出现的冲突,例如理论上我们已经向某个地址写入了新的数据,我们也希望可以同时读到这个地址内新写入的数据,但实际上,这个新数据还没有写入 RAM 中,所以我们读出来的可能是 RAM 默认值,或者是 RAM 该地址中上一次的值,这便是读写冲突。读写冲突示意图如下所示:

       由上图可知当发生读写冲突时,读优先的模式下读出的是读地址中存储的上一个数据;写优先模式时读出的是未知的数据“XX”。

2、写写冲突:表示两个端口写使能同时有效且写地址相同,此时需要关断一个写,把两个写端口都需要更新的值处理到一个写端口上。切记任何双端口 RAM 都不支持写写冲突。写写冲突示意图如下所示

       由上图可知当发生写写冲突时,发生冲突的地址写入的是未知的数据“XX”。

       综上可知,真双端口 RAM 的读写更为灵活,但相应的也更加难以驾驭,因为真双端口不仅需要考虑读写冲突,还要考虑写写冲突,而在大部分设计中我们用的都是伪双端口RAM,所以本章我们以伪双端口RAM 的读写为例,讲解一下如何避免程序中的读写冲突。一般发生读写冲突的时候,我们可以通过错开读写地址的方法来避免读写冲突,我们在本次实验中所使用的就是这种方法,在后文中会进行讲解;或者通过写穿通到读方法来处理冲突数据这里我们简单做下讲解,如下图所示:

       从图中我们可以看出,Cycle 2 时,读和写地址不同,读可以正常读到数据,但是到 Cycle 3 时,读和写地址相同且读写都有效,此处如果不做特殊处理,那么读数据是无效的。需要我们把写数据寄存一拍同步到读侧,即把最新的写数据直接赋给读数据,这便是写穿通到读。

2、实验任务

       本章实验任务是将 Xilinx BMG IP 核配置成一个同步的伪双端口 RAM 并对其进行读写操作,然后通过仿真观察波形是否正确,最后将设计下载到 FPGA 开发板中,并通过在线调试工具对实验结果进行观察。

3、程序设计

3.1、RAM IP 核配置

      首先我们创建一个名为 “ip_2port_ram” 的空白工程,然后点击 Vivado 软件左侧“Flow Navigator”栏中的“IP Catalog”,如下图所示:

      在 “IP Catalog” 窗口的搜索栏中输入 “Block Memory” 关键字后,出现唯一匹配的 “Block Memory Generator”,如下图所示(图中出现的两个 IP 核为同一个 BMG IP 核):

      其实,前面的步骤与单端口 RAM 的配置是一样的,我们在 Memory Type 选择“Simple Dual Port RAM” 简单双端口 RAM,其余设置不变。

“Basic”选项卡配置界面如下图所示。

      因为本章是创建一个同步的伪双端口 RAM,所以 “Memory Type(存储类型)” 我们选择 “Simple Dual Port RAM(伪双端口 RAM)”,并勾选 “Common Clock(同步时钟)” 选项,其余设置保存默认即可。下面我们对 “ECC Options” 做一下扩充讲解,感兴趣的同学可以看一下。

       当存储类型(Memory Type)设置为伪双端口时才可以用 ECCECC 的主要作用是单 bit 纠错和双 bit 检错,在启用后我们可以在写操作期间将单 bitSingle Bit Error Injection)或双 bitDouble Bit Error Injection)错误注入到指定位置。这里我们不需要注入错误码,所以保存默认选项“No ECC”即可。 接下来我们对“Port A Options”和“Port B Options”选项卡进行配置,如下图所示:

        “Port A Options” 和 “Port B Options” 选项卡下各参数含义在上一章中我们已经做过详细的讲解了, 还不熟悉的同学可以回顾一下上一章的内容。

       需要注意的是,只有端口 A 的写数据位宽和写深度是可以任意配置的。端口 B 的读数据位宽必须与端口 A 的写数据位宽存在比例关系(上一节中已讲解过支持的比例关系);端口 B 的读深度是当端口 A 的写数据位宽、端口 A 的写深度和对端口 B 的读数据位宽确定后,自动确定的。

       接下来是“Other Options”选项卡,同上一章一样,该选项卡无需配置,保存默认即可。

       最后一个是“Summary”选项卡,该界面显示了我们配置的存储器的类型,消耗的 BRAM 资源等信息,我们直接点击“OK”按钮完成 BMG IP 核的配置,如下图所示:

3.2、顶层模块设计

       本次实验的目的是为了将 Xilinx BMG IP 核配置成一个伪双端口 RAM 并对其进行读写操作,因此可以给模块命名为 ip_2port_ram。因为伪双端口的数据线,地址线及其他信号线都是相互独立的,所以这里我们将读写分为两个子模块,分别命名为 ram_rd(读模块)和 ram_wr(写模块);系统时钟和系统复位信号大家已经很熟悉了,这里就不再多讲了;本次的实验结果我们仍是通过在线调试工具进行观察,所以没有需要输出到 IO 上的信号。由上述分析我们可以画出一个大致的模块框图,如下图所示:

(1)绘制波形图

       首先我们梳理一下顶层模块中各个信号的波形变化。因为本次实验中的信号皆为内部信号,所以为了方便区分,我们用绿色标记端口 A(写端口)相关的信号名,用红色标记端口 B(读端口)的信号名,用蓝色标记两个端口之间的交互信号。这里我们以读写 64 个(0~63)数据为例,绘制出如下波形图:

       由上图可以看出,除了和伪双端口有关的信号外,我们还引入了一个读启动信号(rd_flag),该信号的作用是错开两个端口的启动时间,以此达到错开读写地址,防止读写冲突的目的。回到波形图上,为了能读出有效数据,我们先使能了端口 A,因为端口 A 只能进行写操作,且我们也需要它一直向 RAM 中写入数据,所以当端口 A 使能后,我们就一直拉高 WEA 信号,让其一直处于写状态,并从 0 开始累加地址,向 0~63 地址中循环写入数据。当写完一半的数据时,我们通过拉高 rd_flag 信号 来使能端口 B,让端口 B 开始工作,并从 0 地址开始循环读出 0~63 地址内存储的数据。因为启动两个端口的时间存在误差,且读写的时钟速率相同,所以读写地址就会相应的错开,以此达到避免读写冲突的目的。

这里有几点需要大家注意:

  • 1、伪双端 RAM 时,因为端口 A 只能写不能读,所以 WEA 可以理解为端口 A 的写使能信号(高有效)。
  • 2、哪怕我们只错开一个地址,也是可以避免读写冲突的,这里我们在写完一半的数据时拉高 rd_flag 信号,只是为了方便我们进行观察而多错开了一些地址而已。
  • 3、端口 B 只能读,其没有像 WEA 一样的读写使能信号,所以当端口使能后,便一直处于读状态。

4、编写代码

4.1、顶层模块 ip_2port_ram

         因为本次实验除了调用 BMG IP 核外还需要例化一个读模块(ram_rd)和一个写模块(ram_wr),所 以我们需要创建一个顶层模块来例化 IP 核与读/写模块,这里我们可以将顶层模块命名为 ip_2port_ram,代码如下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/09 17:23:17
// Design Name: 
// Module Name: ip_2port_ram
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
//实验任务:
//本章实验任务是将 Xilinx BMG IP 核配置成一个同步的伪双端口 RAM 并对其进行读写操作,
//然后通过仿真观察波形是否正确,最后将设计下载到 FPGA 开发板中,
//并通过在线调试工具对实验结果进行观察。

//本次实验除了调用 BMG IP 核外还需要例化一个读模块(ram_rd)和一个写模块(ram_wr),
//所以我们需要创建一个顶层模块来例化 IP 核与读/写模块。
module ip_2port_ram(
   input     sys_clk,       //系统时钟
   input     sys_rst_n      //系统复位,低电平有效
   );

//wire define
wire ram_wr_en ; //端口 A 使能
wire ram_wr_we ; //ram 端口 A 写使能
wire ram_rd_en ; //端口 B 使能
wire rd_flag ;   //读启动标志
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 读数据

//*****************************************************
//** main code
//*****************************************************

//RAM 写模块
ram_wr u_ram_wr(
    .clk            (sys_clk       ),
    .rst_n          (sys_rst_n     ),
    
    .rd_flag        (rd_flag       ),
    .ram_wr_en      (ram_wr_en     ),
    .ram_wr_we      (ram_wr_we     ),
    .ram_wr_addr    (ram_wr_addr   ),
    .ram_wr_data    (ram_wr_data   )
    );

//简单双端口 RAM
blk_mem_gen_0 u_blk_mem_gen_0 (
     .clka      (sys_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      (sys_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
     );

//RAM 读模块 
ram_rd u_ram_rd(
.clk (sys_clk ),
.rst_n (sys_rst_n ),

.rd_flag (rd_flag ),
.ram_rd_en (ram_rd_en ),
.ram_rd_addr (ram_rd_addr),
.ram_rd_data (ram_rd_data)
);

endmodule

可以看出 ip_2port_ram 顶层模块只是例化了 IP 核(blk_mem_gen_0读模块(ram_rd写模块(ram_wr,其中写模块负责产生 RAM IP 核写操作所需的所有数据、地址、写使能信号以及启动读模块的标志信号(rd_flag读模块负责产生 RAM IP 核读操作所需的地址,并将读出的数据也连接至读模块。

4.2、RAM 写模块设计

       首先介绍下 RAM 写模块的设计,在 RAM 写模块中,我们的输入信号主要有系统时钟信号和系统复位 信号;输出有控制写 RAM 所需的 ram_wr_en(写端口使能)、ram_wr_we(写使能)、ram_wr_addr(写地址)和 ram_wr_data(写数据)这四个信号,以及控制读模块启动 rd_flag(读启动)信号。由上述分析绘制出如下图所示的模块框图:

绘制波形图

在编写代码前,我们先大致梳理一下模块的端口时序,并绘制出如下波形图:

        因为我们需要一直向 RAM 中写入数据,所以当复位结束后,我们就将 ram_wr_en(写端口使能)和 ram_wr_we(ram 写使能)一直置为高。当写使能拉高后,写地址一直在 0~63 之间循环计数,并向对应的RAM 地址中写入数据,当写地址第一次计数到 31 时,将 rd_flag 信号拉高并保持,以启动读模块进行读操作。

4.3、ram_wr 模块代码

  ram_wr 模块用于产生 RAM 写操作所需的信号以及读启动信号  其代码如下所示:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/09 17:46:51
// Design Name: 
// Module Name: ram_wr
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

//ram_wr 模块用于产生 RAM 写操作所需的信号以及读启动信号
module ram_wr(
    input      clk,       //时钟信号
    input      rst_n,     //复位信号,低电平有效
    
    //RAM 写端口操作 
    output ram_wr_we ,       //ram 写使能
    output reg ram_wr_en ,   //端口使能
    output reg rd_flag ,     //读启动信号
    output reg [5:0] ram_wr_addr ,   //ram 写地址 
    output [7:0] ram_wr_data         //ram 写数据
    );

//*****************************************************
//** main code
//*****************************************************

//ram_wr_we 为高电平表示写数据
assign ram_wr_we = ram_wr_en ; 
//因为写模块要循环向 ram 的 0~63 地址中写入数据,
//所以当写端口使能后,写使能信号就一直为高.

//写数据与写地址相同,因位宽不等,所以高位补 0
assign ram_wr_data = {2'b0,ram_wr_addr} ;
//当写使能信号拉高后,写地址就会在 0~63 之间循环计数(如代码第 32~39 行所示),
//并向 RAM 中写入相应的数据。

//控制 RAM 使能信号
always @(posedge clk or negedge rst_n) begin
     if(!rst_n)
          ram_wr_en <= 1'b0;
     else
          ram_wr_en <= 1'b1;
end

//写地址信号 范围:0~63 
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) 
         ram_wr_addr <= 6'd0;
    else if(ram_wr_addr < 6'd63 && ram_wr_we)
         ram_wr_addr <= ram_wr_addr + 1'b1;
    else
         ram_wr_addr <= 6'd0;
end

//当写入 32 个数据(0~31)后,拉高读启动信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
         rd_flag <= 1'b0;
    else if(ram_wr_addr == 6'd31) 
         rd_flag <= 1'b1;
    else
         rd_flag <= rd_flag;
end 
//为了避免读写冲突,我们引入了读启动信号(rd_flag),
//当地址第一次计数到31 时,拉高 rd_flag 信号,使读模块开始工作,
//这样做就可以将读地址和写地址错开,防止在同一时刻对en同一地址进行读和写。

endmodul

4.4、RAM 读模块设计

       首先介绍下 RAM 读模块的设计,RAM 读模块中,我们的输入信号主要有系统时钟信号、系统复位信号、从 RAM 中读出的数据(ram_rd_data)以及我们自己定义的读启动标志信号(rd_flag);输出有控制读 RAM 所需的 ram_rd_en(读端口使能)和 ram_rd_addr(读地址)这两个信号。由上述分析绘制出如下图所示的模块框图:

 模块端口与功能描述如下表所示:

 绘制波形图

在编写代码前,我们先大致梳理一下模块的端口时序,并绘制出如下波形图:

        因为我们需要一直从 RAM 中读出数据,所以当复位结束且读启动信号拉高后,我们就将 ram_rd_en(读端口使能)一直置为高。当读端口使能后,读地址就会一直在 0~63 之间循环计数,并读出对应 RAM地址中的数据,需要注意的是读数据的输出会比读地址晚一个时钟周期

4.5、ram_rd 模块代码

ram_rd 模块用于产生 RAM 读操作所需的信号,并接引从 RAM 中读出的数据,其代码如下所示:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/09 17:56:49
// Design Name: 
// Module Name: ram_rd
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

//ram_rd 模块用于产生 RAM 读操作所需的信号,并接引从 RAM 中读出的数据.
module ram_rd(
   input    clk ,     //时钟信号
   input    rst_n ,   //复位信号,低电平有效
   
   //RAM 读端口操作 
   input             rd_flag ,      //读启动标志
   input [7:0]       ram_rd_data,   //ram 读数据
   output            ram_rd_en ,    //端口使能
   output reg [5:0]  ram_rd_addr    //ram 读地址 
   );
//*****************************************************
//** main code
//*****************************************************

//控制 RAM 使能信号
assign ram_rd_en = rd_flag; 

//读地址信号 范围:0~63 
always @(posedge clk or negedge rst_n) begin
     if(!rst_n) 
          ram_rd_addr <= 6'd0;
     else if(ram_rd_addr < 6'd63 && ram_rd_en)
          ram_rd_addr <= ram_rd_addr + 1'b1;
     else
          ram_rd_addr <= 6'd0;
end

endmodule
//当读启动信号拉高后,读端口将被使能;当读端口使能后,
//读地址会在 0~63 之间循环计算,从而读出对应 RAM 地址中的数据。

5、仿真验证

5.1、编写 TB 文件

        我们接下来先对代码进行仿真,因为本章实验我们只有系统时钟和系统复位这两个输入信号,所以仿真文件也只需要编写这两个信号的激励即可,TestBench 代码如下:

`timescale 1ns / 1ps      //仿真单位/仿真精度
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/09 18:01:40
// Design Name: 
// Module Name: tb_ip_2port_ram
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

//本实验我们只有系统时钟和系统复位这两个输入信号,
//所以仿真文件也只需要编写这两个信号的激励即可。
module tb_ip_2port_ram();

//parameter define
parameter CLK_PERIOD = 20; //50MHz系统时钟(一个周期是20ns:1/50MHz=0.02us=20ns)
                            //200MHz系统时钟(一个周期是5ns:1/200MHz=0.005us=5ns)

//reg define
reg sys_clk;
reg sys_rst_n;

//信号初始化
initial begin
     sys_clk = 1'b0;
     sys_rst_n = 1'b0;
     #200
     sys_rst_n = 1'b1;
end

//产生时钟
always #(CLK_PERIOD/2) sys_clk = ~sys_clk;

ip_2port_ram u_ip_2port_ram(
    .sys_clk      (sys_clk      ),
    .sys_rst_n    (sys_rst_n    )
    );

endmodule

5.2、仿真验证

通过仿真我们得到了以下波形图:

6、下载验证

6.1、引脚约束

在仿真验证完成后,接下来对引脚进行分配,并上板验证。本实验中,系统时钟、按键复位的管脚分配如下表所示:

约束文件 ip_2port_ram.xdc 内容如下:

############## clock define 时钟引脚、电平信号约束##### Pro-FPGA##################
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports sys_clk]
############## reset key define##########################
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]

6.2、添加 ILA IP 核进行在线调试

添加 ILA IP 核的方法见: Vivado 下 IP核之单端口 RAM 读写_OliverH-yishuihan的博客-CSDN博客

中的:“5.2、添加 ILA IP 核进行在线调试”

接下来添加 ILA IP 核,将 ram_wr_en1位)、ram_wr_we1位)、ram_rd_en(1位)、rd_flag1位)、ram_wr_addr(6 位)、ram_wr_data(8 位)、ram_rd_addr(6 位)和 ram_rd_data(8 位)信号添加至观察列表中,添加 ILA IP 核的方法这里不再赘述,本例程是将 ILA 例化在了顶层模块(ip_2port_ram)中, 例化代码如下所示

ila_0 u_ila_0 (
     .clk         (sys_clk     ),     // input wire clk
     
     .probe0      (ram_wr_en    ),    // input wire [0:0] probe0 
     .probe1      (ram_wr_we    ),    // input wire [0:0] probe1 
     .probe2      (ram_rd_en    ),    // input wire [0:0] probe2 
     .probe3      (rd_flag      ),    // input wire [0:0] probe3 
     .probe4      (ram_wr_addr  ),    // input wire [5:0] probe4 
     .probe5      (ram_wr_data  ),    // input wire [7:0] probe5 
     .probe6      (ram_rd_addr  ),    // input wire [5:0] probe6 
     .probe7      (ram_rd_data  )     // input wire [7:0] probe7
);

6.3、上板验证

        编译工程并生成比特流文件后,将下载器一端连接电脑,另一端与开发板上的 JTAG 下载口连接,连接电源线,并打开开发板的电源开关。

        点击 Vivado 左侧“Flow Navigator”窗口最下面的“Open Hardware Manager”,如果此时 Vivado 软件识别到了下载器,则点击“Hardware”窗口中“Progam Device”,在弹出的界面中选择“Program”下载程序。

       双端口 ram 的读写在 ILA 中观察到的波形如下图所示:

​​​​​​​

       由在线调试观察可知,上板验证结果与仿真结果是一致的,写端口和读端口能够同时对 ram 进行相应的操作,且没有发生读写冲突现象,至此说明了 IP 核之双端口 RAM 实验验证成功。

7、本章总结

       本章节我们主要讲解了双端口 RAM 的概念,使用场景,读写冲突处理,也介绍了 Vivado 软件中如何将 BGM IP 核配置成伪双端口 RAM 的方法,并通过错时的方法避免了双端口 RAM 发生读写冲突的情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/27104.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Goby 漏洞发布|maxView Storage Manager 系统 dynamiccontent.properties.xhtml 远程代码执行漏洞

漏洞名称&#xff1a;maxView Storage Manager 系统 dynamiccontent.properties.xhtml 远程代码执行漏洞 English Name&#xff1a;maxView Storage Manager dynamiccontent.properties.xhtml RCE CVSS core: 9.8 影响资产数&#xff1a;1465 漏洞描述&#xff1a; maxVie…

架构之冷热分离

本文依据《从程序员到架构师》阅读有感&#xff0c;记录书中案例并且结合作者工作经历进行分析。 当数据量过大&#xff0c;业务查询慢甚至导致数据库服务器CPU飙升&#xff0c;导致数据库宕机&#xff0c;影响用户体验。 场景&#xff1a; 1.客户两年多产生了近2000万的工单…

Django - 页面静态化和crontab定时任务(二)

一. 前言 一个网页会有很多数据是不需要经常变动的&#xff0c;比如说首页&#xff0c;变动频率低而访问量大&#xff0c;我们可以把它静态化&#xff0c;这样就不需要每次有请求都要查询数据库再返回&#xff0c;可以减少服务器压力 我们可以使用Django的模板渲染功能完成页面…

Amazon SageMaker:探索AI绘画云端部署新方案

目录 1 从艺术实验到AI绘画2 什么是Amazon SageMaker&#xff1f;3 云端部署AI绘画应用3.1 模型构建与部署3.2 AI绘画测试(文生图) 4 亚马逊云科技中国峰会 1 从艺术实验到AI绘画 在过去&#xff0c;人们只希望基于已有的给定数据做一些预测和拟合&#xff0c;因此判别式模型得…

怎么登录远程轻量云服务器?

​  轻量云服务器是一种基于云计算技术的服务器&#xff0c;具有价格低廉、配置灵活、易于管理等优点。但是&#xff0c;由于轻量云服务器通常是在云端运行&#xff0c;需要通过远程连接才能进行管理和操作。那么&#xff0c;怎么登录远程轻量云服务器呢? 要远程连接轻量云服…

JMeter测试笔记(五):JDBC请求

引言&#xff1a; 进行性能测试时&#xff0c;我们有时需要模拟数据库的访问和操作&#xff0c;而JMeter中的JDBC请求可以帮助我们实现这个目的。 在本文中&#xff0c;我们将深入了解JDBC请求的使用方式&#xff0c;并学习如何正确配置它们来模拟对数据库的访问和操作。 如…

C#,彩票数学——彩票预测是玄学还是数学?什么是彩票分析?怎么实现彩票号码的预测?

彩票原理系列文章 彩票与数学——彩票预测是玄学还是数学&#xff1f;https://mp.csdn.net/mp_blog/creation/editor/122517043彩票与数学——常用彩票术语的统计学解释https://mp.csdn.net/mp_blog/creation/editor/122474853彩票与数学——彩票缩水的数学概念与原理https://…

Codeforces Round 877 div2 C No Prime Differences

目录 一、题目 二、题目分析 三、 一、题目 传送门 C. No Prime Differences time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output You are given integers n and m. Fill an n by m grid with the…

连杆滑块伸缩模组的制作

1. 运动功能说明 连杆滑块伸缩模组的主要运动方式为舵机带动滑块沿着光轴平行方向做伸缩运动。 2. 结构说明 本模组主要是由舵机、滑块、光轴、连杆等组成。 3. 电子硬件 在这个示例中&#xff0c;我们采用了以下硬件&#xff0c;请大家参考&#xff1a; 主控板 Basra主控板&…

java ssm贸易平台-物流管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java ssm贸易平台-物流管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主…

工业深度学习软件 从标注 训练 到测试 再到现场部署

工业深度学习软件 从标注 训练 到测试 再到现场部署 M7000技术规格表 Producer Specification 影像系统 Imaging Sys 适配相机 supported cameras 支持海康&#xff0c;迈德威视&#xff0c;度申2D相机&#xff08;可根据需求增加适配其他厂家相机&#xff09; Support for Hi…

为什么有了IP地址,还需要MAC地址呢?

不知道大家有没有困惑&#xff1a;为什么有了IP地址&#xff0c;还需要MAC地址呢&#xff1f;他们之间到底有什么联系&#xff1f;又有什么区别&#xff1f;是不是有一个是多余的&#xff1f; 流言传到了“IP地址”和“MAC地址”的耳朵里&#xff0c;他俩也非常苦恼&#xff0c…

php通过cURL爬取数据(2):CURLINFO_HTTP_CODE返回0

CURLINFO_HTTP_CODE返回0 一、项目说明二、curl_getinfo返回异常1.小鹅通SDK2.CURLINFO_HTTP_CODE为0的原因有哪些&#xff1f;3.返回CURLINFO_HTTP_CODE0的解决方案4.请求超时和服务器配置&#xff0c;CPU的使用率有关系吗5.结论 三、阿里云短信发送延迟后而集中发送1.发送集中…

【回眸】Python入门(五)基础语法列表和词典:Python如何消灭重复性劳动

前言 本篇博客为填坑篇&#xff0c;这个系列的上一篇竟然是2021年的9月30更新的&#xff0c;离谱&#xff0c;差点就到断更两周年纪念日了&#xff0c;后续逐渐走向填坑的每一天&#xff0c;继续创作&#xff0c;希望这个系列的专栏文章能帮助到更多有需要的人。 列表 什么是列…

操作系统的发展史(DOS/Windows篇)

█ DOS操作系统 20世纪70年代&#xff0c;伴随着计算机技术的成熟&#xff0c;操作系统也进入了一个快速发展阶段。现代操作系统的概念&#xff0c;也在那一时期逐渐形成。 1975年初&#xff0c;MITS电脑公司推出了基于Intel 8080芯片的Altair 8800微型计算机。这是人类历史上…

电子招标采购系统源码之从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。

统一供应商门户 便捷动态、呈现丰富 供应商门户具备内外协同的能力&#xff0c;为外部供应商集中推送展示与其相关的所有采购业务信息&#xff08;历史合作、考察整改&#xff0c;绩效评价等&#xff09;&#xff0c;支持供应商信息的自助维护&#xff0c;实时风险自动提示。…

CSAPP - AttackLab实验(阶段1-5)

AttackLab实验 实验内容 官网&#xff1a;http://csapp.cs.cmu.edu/3e/labs.html “AttackLab”是一个Linux下的可执行C程序&#xff0c;包含了5个阶段&#xff08;phase1~phase5&#xff09;的不同内容。程序运行过程中&#xff0c;要求学生能够根据缓冲区的工作方式和程序…

Sui Builder House日本京都站开启报名

下一站Sui Builder House将于6月29-30日在日本京都举行&#xff0c;为世界各地的开发者提供身临其境地学习和交流的机会。 春日的樱花已经绽放&#xff0c;黄金周也已经过去&#xff0c;现在是时候来京都参加Sui Builder House一起庆祝夏天的来临了。来自日本和周边地区的开发…

我用ChatGPT写2023高考语文作文(一):全国甲卷

2023年 全国甲卷 适用地区&#xff1a;广西、贵州、四川、西藏 人们因技术发展得以更好地掌控时间&#xff0c;但也有人因此成了时间的仆人。 这句话引发了你怎样的联想与思考&#xff1f;请写一篇文章。 要求&#xff1a;选准角度&#xff0c;确定立意&#xff0c;明确文体&am…

【TaskMatrix.AI - Visual ChatGPT】连接超大模型和超多API来完成任务

Github项目地址&#xff1a;https://github.com/microsoft/TaskMatrix 目前大规模预训练模型&#xff08;比如ChatGPT&#xff09;已经能够完成多个任务&#xff0c;例如 提供强大的对话功能&#xff0c;in-context learning能力和代码生成能力生成高层次的解决问题框架 然而…