3、添加 ILA IP 核
①、点击左侧 PROJECT MANAGER 栏 –> IP Catalog 或者菜单栏下 Window –> IP Catalog 然后在右侧出现的 IP Catalog 窗口下搜索 ILA,双击选择 Debug 下的 ILA 进行 IP 配置操作步骤如下图所示
②、General Option 添加两个探针去采集我们读的地址和数据,采样数据的长度我们设置大一些,如下图所示
③、Probe_Ports(0…7) 中PROBE0 用来采集9 位地址,PROBE1 用来采集 16 位数据,如下图所示
④、点击 OK,再点击 Generate,生成 ILA IP
二、程序编写
编写程序之前我们先介绍下我们涉及到哪些信号
Simple Dual Port RAM 模块端口的说明如下:
信号名称 | 方向 | 说明 |
clka | in | 端口 A 时钟输入 |
wea | in | 端口 A 使能 |
addra | in | 端口 A 地址输入 |
dina | in | 端口 A 数据输入 |
clkb | in | 端口 B 时钟输入 |
addrb | in | 端口 B 地址输入 |
doutb | out | 端口 B 数据输出 |
RAM 的数据写入和读出都是按时钟的上升沿操作的,端口 A 数据写入的时候需要置高 wea 信号,同时提供地址和要写入的数据。下图为输入写入到 RAM 的时序图。
而端口 B 是不能写入数据的,只能从 RAM 中读出数据,只要提供地址就可以了,一般情况下可以在下一个周期采集 到有效的数据 。
1、新建测试程序
新建 ram_test.v 源文件并将下面的程序块拷贝过去
ram_test.v
`timescale 1ns / 1ps module ram_test( input clk, // 50 MHz 时钟 input rst_n // 复位信号,低电平有效 ); reg [8 : 0] w_addr; // RAM PORTA 写地址 reg [15 : 0] w_data; // RAM PORTA 写数据 reg wea; // RAM PORTA 使能 reg [8 : 0] r_addr; // RAM PORTB 读地址 wire [15 : 0] r_data; // RAM PORTB 读数据 // ************************************************************************ // ** main // ************************************************************************ // 产生RAM PORTB读地址,读的地址 + 1,模拟写的地址滞后一个周期 always @ (posedge clk or negedge rst_n) begin if( !rst_n ) r_addr <= 9'd0; else if( |w_addr ) // w_addr 位或,不等于 0 r_addr <= r_addr + 1'b1; else r_addr <= 9'd0; end // 产生RAM PORTA写使能信号 always @ (posedge clk or negedge rst_n) begin if( !rst_n ) wea <= 1'b0; else begin if( &w_addr ) // w_addr 的 bit 位全为1,共写 512 个数据,写入完成 wea <= 1'b0; else wea <= 1'b1; // ram 写使能 end end // 产生RAM PORTA写入的地址及数据 always @ (posedge clk or negedge rst_n) begin if( !rst_n ) begin w_addr <= 9'd0; w_data <= 16'd1; end else begin if( wea ) begin // ram 写使能有效 if( &w_addr ) begin // w_addr 的 bit 位全为 1,共写入 512 个数据,写入完成 w_addr <= w_addr; // 将地址和数据的值保持住,只写一次 RAM w_data <= w_data; end else begin w_addr <= w_addr + 1'b1; w_data <= w_data + 1'b1; end end end end // 实例化 RAM ram_ip ram_ip_instance ( .clka(clk ), // input wire clka .wea(wea ), // input wire [0 : 0] wea .addra(w_addr ), // input wire [8 : 0] addra .dina(w_data ), // input wire [15 : 0] dina .clkb(clk ), // input wire clkb .addrb(r_addr ), // input wire [8 : 0] addrb .doutb(r_data ) // output wire [15 : 0] doutb ); // 实例化 ila 逻辑分析仪 ila_0 ila_0_instance ( .clk(clk), // input wire clk .probe0(r_addr), // input wire [8:0] probe0 .probe1(r_data) // input wire [15:0] probe1 ); endmodule
2、新建仿真文件
新建 vtf_ram_tb.v 仿真文件并将下面的程序块拷贝过去
vtf_ram_tb.v
`timescale 1ns / 1ps module vtf_ram_tb; // Inputs reg clk; reg rst_n; // Instantiate the Unit Under Test (UUT) ram_test uut ( .clk (clk), .rst_n (rst_n) ); initial begin // Initialize Inputs clk = 0; rst_n = 0; // Wait 100ns for global reset to finish #100; rst_n = 1; end always #10 clk = ~clk; // 20ns 一个周期,产生 50MHz 时钟源 endmodule
三、进行仿真
①、点击 Run Simulation -> Run Behavioral Simulation,进行仿真
②、将 RAM 里面相关信号拖拽进观察窗口
(clocka 和 clockb 用的就是系统时钟,因此这里无需拖入到观察窗口)
③、设置个 200us 跑一下
④、分析波形
开始的波形信息
可以看到读地址是滞后于写地址的一个时钟周期的,读出的数据是滞后于写地址的一个时钟周期的
结束的波形信息
我们这里把这些信号以 10 进制方式进行查看
可以看到最后写的一个地址是 511,写入的内容是 512,最后读的一个地址是 511,读取的内容是 512
可以发现我们的写只写第一,而读一直在进行。
四、下载到 FPGA
1、引脚约束及时序约束
rst_n -> T11
clk -> U18
新建引脚约束文件 ram.xdc,将下面语句拷贝过去并保存
set_property PACKAGE_PIN T11 [get_ports rst_n] set_property IOSTANDARD LVCMOS33 [get_ports rst_n] set_property PACKAGE_PIN U18 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] create_clock -period 20.000 -name clk -waveform {0.000 10.000} [get_ports clk]
2、生成比特文件
点击 “Generate Bitstream”,直接生成 bit 文件
3、下载程序
连接上 JTAG 以及电源线,将板子上电,下载程序
4、逻辑分析仪分析波形
以 10 进制方式查看读取的内存及数据,设置触发条件为 “读取的地址为 0”,可以看到读取地址 0 的数据为 1,读取地址 511 时的数据为 512,可见满足了预期结果。