之前写这个系列的时候忘记上传了,刚好五一补一下。
文章目录
1 MIG IP核的接口
想应用IP核,首先学习IP核的接口。
内存控制器可以使用AXI4 slave、UI、或者直接使用native接口进行连接。
- AXI4从接口提供了一个符合AXI4内存映射的从接口,用于连接到处理器子系统。AXI4从接口将其事务转换为在UI上传递它们。
- UI类似于一个简单的FIFO接口,并且总是按顺序返回数据。
- native接口在某些情况下提供了更高的性能,但使用起来更难。
native接口不包含缓冲区并尽快返回数据,但返回的数据可能顺序错误。如果使用native接口并启用了重新排序,应用程序必须在内部重新排序接收到的数据。下面的章节描述了每个接口的时序协议以及应该如何控制它们。
1.1 AXI4 slave 接口
来自AXI master的AXI地址是一个byte address。AXI shim根据AXI SIZE和存储器数据宽度将地址从AXI master地址转换到存储器地址。根据存储器阵列的数据宽度,AXI byte address的LSB被屏蔽为0。如果存储器阵列是64bit位宽(8字节),则AXI address[2:0]=0。如果存储器阵列是16bit位宽(2字节),则AXI address[0]=0。
DDR3 DRAM以8个DRAM word的块访问,burst长度为8。UI数据端口宽为8个DRAM word,PHY到内存控制器(MC)时钟比例为4:1,PHY到MC时钟比例为2:1。
1.2 Upsizing
当User Interface侧的数据宽度大于AXI Interface侧的数据宽度时,在AXI Shim接口中进行放大。对INCR和
WRAP burst执行数据打包。
在最终向User Interface端发出的事务中,因为数据位宽更大,所以数据拍数相应减少:
- 对于写,发生数据合并。
- 对于读,发生数据串行化。
这里是理解错了还是手册写反了,User Interface端更大的位宽,不应该进行数据串行化后发给AXI Interface吗?
1.3 User Interface
User Interface 与物理存储器row、bank、column地址的映射关系是可以配置的,两种映射方式:
-
Bank- Row-Column
-
Row-Bank-Column
1.3.1 Command Path
当用户逻辑的app_en信号被断言 且 UI的app_rdy信号被断言时,UI接受一个命令并写入到FIFO。
发出一个非back-to-back的写命令,下图描述了app_wdf_data、app_wdf_wren和app_wdf_end信号的三种场景,如下所示:
- 写数据与相应的写命令一起出现。
- 写数据出现在相应的写命令之前。
- 写数据出现在相应的写命令之后。但不应超过两个时钟周期的限制。
对于写命令寄存后输出的写数据,情景3,最大延迟为两个时钟周期。
*什么是back-to-back?
背靠背是两个不同的事务连续执行,中间不经历总线的idle态。突发是一个事务中地址连续的数据的连续传输。
1.3.2 Write Path
当断言app_wdf_wren且app_wdf_rdy为High时,写入数据寄存在写入FIFO中。如果app_wdf_rdy被解除断言,用户逻辑需要保持app_wdf_wren和app_wdf_end High以及有效的app_wdf_data值,直到app_wdf_rdy被断言。app_wdf_data数据可以在app_cmd“写命令”之前被断言。唯一的条件是,对于每个app_cmd“写命令”,关联的app_wdf_data“写数据”必须存在。app_wdf_mask信号可用于屏蔽要写入外部内存的字节。
在写数据和相关的写命令之间的单个写的最大延迟是两个时钟周期。
app_wdf_end信号必须用于指示内存写入突发的结束。对于2:1模式下的8内存突发类型,app_wdf_end信号必须在第二个写入数据字上断言。
1.3.3 Read Path
读取的数据由UI以请求的顺序返回,并且在断言app_rd_data_valid时有效。app_rd_data_end信号表示每个读命令突发的结束,在用户逻辑中不需要这个信号。
返回的读数据总是与地址/控制总线上发出的请求顺序相同。
1.4 Native interface
native interface协议:
请求以地址和命令的形式呈现给本机接口。地址由bank, row, 和 column输入组成。该命令在cmd输入中编码。
上图,在使用use_addr信号进行验证之前,地址和命令给Native interface一个状态。memory interface通过断言accept信号指示接受请求。当use_addr和accept都在同一个时钟周期中断言时,请求被确认为接受。如果use_addr被断言,但是accept没有,请求将不被接受,必须重复。
请求1和2通常被接受。第一次提出请求3时,accept被驱动为Low,请求不被接受。用户设计重试请求3,该请求在下一次尝试时被接受。请求4随后在第一次尝试时被接受。
data_buf_addr总线必须与请求一起提供。这个地址总线是一个指向用户设计中存在的缓冲区的地址指针。它告诉IP核在处理写命令时把数据放在哪里,在处理读命令时把数据放在哪里。当IP核处理一个命令时,IP核将data_buf_addr反馈给由wr_data_addr(写命令)和rd_data_addr(读命令)组成的用户设计。这种行为如下图所示。写数据必须在断言wr_data_en的同一时钟周期内提供。
可以使用 non-activity间隙间隔传输,也可以使用没有间隙的长时间突发。用户设计可以通过监视rd_data_en和wr_data_en信号来确定何时处理请求以及何时结束请求。当rd_data_en信号被断言时,内存控制器已经完成了一个读命令请求的处理。类似地,当wr_data_en信号被断言时,内存控制器正在处理一个写命令请求。
当NORM排序模式启用时,内存控制器重新排序接收到的请求,以优化FPGA和内存设备之间的吞吐量。数据以处理的顺序而不是收到的顺序返回给用户设计。用户设计可以通过监视rd_data_addr和wr_data_addr来识别正在处理的特定请求。这些字段对应于用户设计向native interface提交请求时提供的data_buf_addr。
1.5 Physical Layer Interface
2 仿真
2.1 模块结构
(控制/数据)流产生操作:
包含于可综合 testbench 上的 traffic generator module可以被参数化,以创建用于存储器设计的各种激励模式。它可以产生用于验证设计完整性的重复测试模式,以及仿真真实世界流的伪随机数据流。
可以通过BEGIN_ADDRESS和END_ADDRESS参数定义地址范围。Init Memory Pattern Control块引导流生成器按顺序遍历地址空间中的所有地址,根据所选的数据模式将适当的数据值写入存储器器件中的各位置。默认情况下,testbench 使用地址作为 数据 ,但是这个示例设计中的数据模式可以使用vio_data_mode信号进行修改,该信号可以在Vivado逻辑分析器特性中进行修改。
当存储器被初始化后,流产生器开始激励用户接口端口来创建进出存储器设备的流。缺省情况下,流生成器向端口发送伪随机命令,即指令序列(R/W、R、W)和地址由流量生成器模块中的PRBS生成器逻辑决定。
从存储器设备返回的读数据由流生成器通过用户接口读数据端口访问,并与内部生成的数据进行比较。如果检测到错误(即读取数据和预期数据之间存在不匹配),则断言错误信号,并将回读地址、回读数据和预期数据锁存到error_status输出中。
可以修改示例设计的参数进行不同的仿真。
2.2 mig_7series_v4_2_traffic_gen_top
这个模块可以产生很多模式的数据流,可以通过参数和VIO配置。
参数列表:
参数 | 含义 | 值 | 描述 |
---|---|---|---|
FAMILY | 器件族类型 | VIRTEX7 | 只有VIRTEX6和VIRTEX7两个选项,使用的是k7,这个参数也是VIRTEX7 |
MEM_TYPE | 存储器控制器类型 | DDR3 | |
TST_MEM_INSTR_MODE | 测试模式 | R_W_INSTR_MODE | R_W_INSTR_MODE:读写测试。FIXED_INSTR_R_MODE:只读。FIXED_INSTR_W_MODE:只写。FIXED_INSTR_R_EYE_MODE:读眼图测试 |
nCK_PER_CLK | 存储器时钟与此逻辑时钟的比例 | 4 | 设置IP核的时候设置的 |
NUM_DQ_PINS | 存储器DQ引脚的位宽 | 64 | |
MEM_BURST_LEN | 存储器数据突发长度 | 8 | 必须为8 |
MEM_COL_WIDTH | 存储器列地址的位宽 | 10 | 取决于选的存储器 |
DATA_WIDTH | user interface数据总线的位宽 | 512 | DATA_WIDTH = NUM_DQ_PINS × nCK_PER_CLK x2 |
ADDR_WIDTH | user interface地址总线的位宽 | 29 | RANK_WIDTH + BANK_WIDTH + ROW_WIDTH + COL_WIDTH |
MASK_SIZE | user interface数据总线的掩码位宽 | 64 | DATA_WIDTH/8 |
PORT_MODE | 端口模式 | BI_MODE | 生成WRITE数据模式并监视READ数据进行比较。 |
BEGIN_ADDRESS | 设置存储器起始地址 | 32’h00000000 | Bits[3:0]无效 |
END_ADDRESS | 设置存储器结束地址 | 32’h00ffffff | Bits[3:0]无效 |
PRBS_EADDR_MASK_POS | 设置结束地址32位AND掩码位置 | 32’hff000000 | 该参数与伪随机位序列(pseudo-random bit sequence, PRBS)地址生成器配合使用,将随机地址向下移动到端口地址空间中, PRBS_EADDR_MASK_POS 是 PRBS address的掩码。END_ADDRESS 与 PRBS address相与 |
PRBS_SADDR_MASK_POS | 设置结束地址32位OR掩码位置 | PRBS_EADDR_MASK_POS | 该参数与伪随机位序列(pseudo-random bit sequence, PRBS)地址生成器配合使用,将随机地址向上移动到端口地址空间中, PRBS_SADDR_MASK_POS 是 PRBS address的掩码。START_ADDRESS 与 PRBS address相与 |
CMD_PATTERN | 这个参数设置要生成的命令模式电路。 | 这个参数设置要生成的命令模式电路。 | CGEN_FIXED: 地址、突发长度和指令直接从fixed_addr_i、fixed_bl_i和fixed_instr_i输入中获取。CGEN_SEQUENTIAL: 地址按顺序递增,增量大小由数据端口大小决定。CGEN_PRBS:32级线性反馈移位寄存器(LFSR)产生伪随机地址、突发长度和指令序列。可以从32位的cmd_seed输入设置种子。CGEN_ALL (默认): 该选项将打开上述所有选项,并允许addr_mode_i、instr_mode_i和bl_mode_i在运行时选择生成类型。 |
DATA_PATTERN | 该参数设置通过RTL逻辑生成的数据模式电路。 | DGEN_ALL | ADDR:地址用作数据模式。HAMMER: DQS上升沿DQ引脚上都是1,DQS下降沿DQ引脚上都是0。WALKING1: 在DQ引脚上移动1s,1的起始位置取决于地址值。WALKING0: 在DQ引脚上移动0s,0的起始位置取决于地址值。NEIGHBOR: HAMMER模式,但是有一个DQ pin异常,地址决定了异常引脚的位置。PRBS:32级LFSR生成随机数据,并由起始地址作为种子。DGEN_ALL: 这个选项打开所有可用选项 |
CMDS_GAP_DELAY | 这个参数允许每个用户突发命令之间的暂停延迟。 | 6’d0 | 0 到 32 |
SEL_VICTIM_LINE | 选择一个异常DQ线,其状态总是在逻辑高 | 8 | 此参数仅适用于Hammer模式。此参数的有效设置为0到NUM_DQ_PINS。当value = NUM_DQ_PINS时,所有DQ引脚具有相同的Hammer模式。 |
EYE_TEST | 强制流生成器只生成对单个位置的写操作,不生成读事务 | FALSE | 该参数的有效设置是“TRUE”和“FALSE”。当设置为“TRUE”时,vio_instr_mode_value中的任何设置都将被覆盖 |
VIO控制列表:
信号 | 含义 | 值 | 描述 |
---|---|---|---|
vio_modify_enable | 允许vio_xxxx_mode_value改变流模式。 | 1’b0 | 不允许 |
vio_data_mode_value[3:0] | 数据流模式,DATA_PATTERN开启的模式 | 4’b0010 | 1: FIXED data mode。2: DGEN_ADDR,地址作为数据。3: DGEN_HAMMER。4: DGEN_NEIGHBOR。5: DGEN_WALKING1。6: DGEN_WALKING0。7: DGEN_PRBS。 |
vio_addr_mode_value[2:0] | 地址流模式,CMD_PATTERN开启的模式 | 3’b011 | 1: Fixed_address。2: PRBS address。3: Sequential address。 |
vio_instr_mode_value[3:0] | 指令模式,CMD_PATTERN开启的模式 | 4’b0010 | |
vio_bl_mode_value[3:0] | 突发长度,CMD_PATTERN开启的模式 | 2’b10 | 1: Fixed 突发长度;2: PRBS 突发长度,如果为2,addr_mode强制为2以产生PRBS地址。 |
vio_fixed_instr_value | fixed指令的值,0x0: Write指令,0x1: Read指令 | 3’b001 | |
vio_fixed_bl_value | 突发长度的值,1-256 | 8’d16 | |
vio_pause_traffic | 即时(on-the-fly)暂停动态流生成 | 1’b0 | |
vio_data_mask_gen | 数据掩码产生使能 | 1’b0 | 此模式仅在数据模式模式为address as data时使用。如果启用此选项,则在存储器模式填充到存储器后将生成一个随机memc_wr_mask。如果断言对应的memc_write_mask,则写入数据字节通道将被8’hFF阻塞。 |
修改端口地址空间
端口的地址空间可以通过更改顶层testbench文件中的BEGIN_ADDRESS和END_ADDRESS参数来修改。这两个值必须与端口的数据位宽对齐。另外还有两个额外的参数,PRBS_SADDR_MASK_POS和PRBS_EADDR_MASK_POS,用于默认的PRBS地址模式确保没有向端口发送超出范围的地址。
PRBS_SADDR_MASK_POS创建一个OR掩码,该掩码将PRBS生成的值低于BEGIN_ADDRESS的地址向上移动到端口的有效地址空间中。PRBS_SADDR_MASK_POS应该设为与BEGIN_ADDRESS参数相等的32位值。掩码高电平有效,小于开始地址的值与掩码(起始地址)或逻辑运算之后,就会大于起始地址。
PRBS_EADDR_MASK_POS创建一个AND掩码,该掩码将PRBS生成的具有END_ADDRESS以上值的地址向下移动到端口的有效地址空间中。PRBS_EADDR_MASK_POS应该设为32位值,其中END_ADDRESS的最高有效地址位之上的所有位都被设置为1,其余的位都被设置为0。这个掩码是对应位低电平有效,将地址值与END_ADDRESS对应位AND运算,使得到的地址值小于结束地址。
//path: [component name]_ex\imports\mig_7series_v4_2_cmd_prbs_gen.v
for(i = logb2(MEM_BURST_LEN) - 2; i <= SEED_WIDTH - 1; i = i + 1)
for(i = 3; i <= SEED_WIDTH - 1; i = i + 1)
if(PRBS_SADDR_MASK_POS[i] == 1)
prbs[i] = PRBS_SADDR[i] | lfsr_q[i+1];
else if(PRBS_EADDR_MASK_POS[i] == 0)
prbs[i] = PRBS_EADDR[i] & lfsr_q[i+1];
else
prbs[i] = 1'b0;// lfsr_q[i+1];
prbs[logb2(MEM_BURST_LEN)-3:0] = 'b0;//{logb2(MEM_BURST_LEN) -3{1'b0}};
end
工作过程
这个测试程序非常庞大,因为可以设置很多测试类型进行测试,导致逻辑也非常复杂。大致了解示例的控制流程即可。
-
首先等待IP核校准初始化完成(memc_init_done == 1’b1)。
-
初始化后mig_7series_v4_2_init_mem_pattern_ctr中的状态机根据参数的设置设置测试的模式。
-
mig_7series_v4_2_memc_traffic_gen根据设置的测试模式产生对应的数据流。
总体时序图:
2.2.1 mig_7series_v4_2_init_mem_pattern_ctr
功能:这个模块有一个FSM来控制memc_traffic_gen模块的操作。它首先用选定的DATA模式填充内存,然后启动内存测试状态。
首先根据输入的参数获取本模块的指令模式:
assign test_mem_instr_mode = (vio_instr_mode_value[3:2] == 2'b11) ? 4'b1111:
(vio_instr_mode_value[3:2] == 2'b10) ? 4'b1011:
(TST_MEM_INSTR_MODE == "BRAM_INSTR_MODE") ? 4'b0000:
(TST_MEM_INSTR_MODE == "FIXED_INSTR_R_MODE" ||
TST_MEM_INSTR_MODE == "FIXED_INSTR_W_MODE") ? 4'b0001:
(TST_MEM_INSTR_MODE == "R_W_INSTR_MODE") ? 4'b0010:
(TST_MEM_INSTR_MODE == "RP_WP_INSTR_MODE" && FAMILY == "SPARTAN6") ? 4'b0011:
(TST_MEM_INSTR_MODE == "R_RP_W_WP_INSTR_MODE" && FAMILY == "SPARTAN6") ? 4'b0100:
(TST_MEM_INSTR_MODE == "R_RP_W_WP_REF_INSTR_MODE" && FAMILY == "SPARTAN6") ? 4'b0101:
4'b0010;
- IDLE
上电为空闲状态。
当初始化完成memc_init_done_reg == 1’b1。因为vio_instr_mode_value == 4’b0010 且 TST_MEM_INSTR_MODE == “R_W_INSTR_MODE”, 所以工作模式为4’b0010。
又PORT_MODE = “BI_MODE”,所以下一状态为INIT_MEM_WRITE。 - INIT_MEM_WRITE
addr_mode = SEQUENTIAL_ADDR;地址模式设置为连续地址。
upper_end_matched && lower_end_matched,下一状态为TEST_MEM - TEST_MEM
进行内存读写测试。
2.2.2 mig_7series_v4_2_memc_traffic_gen
这是存储区数据/控制流生成器的顶层模块,可以对存储器控制器核心产生不同的CMD_PATTERN和DATA_PATTERN。
3 调试
因为为了IP核的简洁所以在配置IP核时没有加默认的VIO和ILA。所以直接把User Interface的app_*读写信号以及对比结果信号tg_compare_error抓出来分析就行。
默认的测试模式是地址顺序增加,然后地址值作为数据。对比读写的地址和数据值是否一致以及对比结果即可。
测试调通之后就可以进行自己项目的开发了。