Files
IC_PRJ/tb/tb_array_ctrl.v

257 lines
11 KiB
Coq
Raw Permalink Normal View History

`timescale 1ns/1ps
module tb_array_ctrl;
// 时钟与复位信号
reg clk;
reg rst_n;
// AXI到array的帧信号
reg axi2array_frame_valid;
reg [152:0] axi2array_frame_data;
wire axi2array_frame_ready;
// array到AXI的读数据信号
wire array2axi_rdata_valid;
wire [127:0] array2axi_rdata;
// array接口信号
wire array_csn;
wire [15:0] array_raddr;
wire array_caddr_vld_wr;
wire [5:0] array_caddr_wr;
wire array_wdata_vld;
wire [127:0] array_wdata;
wire array_caddr_vld_rd;
wire [5:0] array_caddr_rd;
reg array_rdata_vld;
reg [127:0] array_rdata;
// APB配置信号内存控制器时序参数
reg mc_work_en;
reg [7:0] array_inner_tras; // 行预充电时间
reg [7:0] array_inner_trp; // 预充电到激活时间
reg [7:0] array_inner_trcd_wr;// 激活到写命令时间
reg [7:0] array_inner_twr; // 写恢复时间
reg [7:0] array_inner_trcd_rd;// 激活到读命令时间
reg [7:0] array_inner_trtp; // 读预充电时间
reg array_ref_en; // 刷新使能
reg [24:0] array_inner_tref0; // 刷新间隔0
reg [24:0] array_inner_tref1; // 刷新间隔1
reg array_inner_ref_sel;// 刷新间隔选择
// 实例化被测模块
array_ctrl u_array_ctrl (
.clk (clk),
.rst_n (rst_n),
.axi2array_frame_valid (axi2array_frame_valid),
.axi2array_frame_data (axi2array_frame_data),
.axi2array_frame_ready (axi2array_frame_ready),
.array2axi_rdata_valid (array2axi_rdata_valid),
.array2axi_rdata (array2axi_rdata),
.array_csn (array_csn),
.array_raddr (array_raddr),
.array_caddr_vld_wr (array_caddr_vld_wr),
.array_caddr_wr (array_caddr_wr),
.array_wdata_vld (array_wdata_vld),
.array_wdata (array_wdata),
.array_caddr_vld_rd (array_caddr_vld_rd),
.array_caddr_rd (array_caddr_rd),
.array_rdata_vld (array_rdata_vld),
.array_rdata (array_rdata),
.mc_work_en (mc_work_en),
.array_inner_tras (array_inner_tras),
.array_inner_trp (array_inner_trp),
.array_inner_trcd_wr (array_inner_trcd_wr),
.array_inner_twr (array_inner_twr),
.array_inner_trcd_rd (array_inner_trcd_rd),
.array_inner_trtp (array_inner_trtp),
.array_ref_en (array_ref_en),
.array_inner_tref0 (array_inner_tref0),
.array_inner_tref1 (array_inner_tref1),
.array_inner_ref_sel (array_inner_ref_sel)
);
// 时钟生成50MHz周期20ns
initial begin
clk = 1'b0;
forever #1.25 clk = ~clk;
end
// 复位与初始化流程
initial begin
// 初始复位状态
rst_n = 1'b0;
axi2array_frame_valid = 1'b0;
axi2array_frame_data = 153'd0;
array_rdata_vld = 1'b0;
array_rdata = 128'd0;
mc_work_en = 1'b0;
// 初始化时序参数示例值模拟真实内存时序
array_inner_tras = 8'd16; // 行预充电时间2个时钟周期
array_inner_trp = 8'd6; // 预充电到激活时间1个时钟周期
array_inner_trcd_wr = 8'd7; // 激活到写命令时间1个时钟周期
array_inner_twr = 8'd6; // 写恢复时间3个时钟周期
array_inner_trcd_rd = 8'd7; // 激活到读命令时间1个时钟周期
array_inner_trtp = 8'd3; // 读预充电时间1个时钟周期
array_ref_en = 1'b0; // 初始关闭刷新
array_inner_tref0 = 25'd100; // 刷新间隔05个时钟周期100ns
array_inner_tref1 = 25'd60; // 刷新间隔110个时钟周期200ns
array_inner_ref_sel = 1'b0; // 默认选择刷新间隔0
// 释放复位并启动控制器
#100 rst_n = 1'b1; // 100ns后释放复位
#50 mc_work_en = 1'b1; // 50ns后使能控制器工作
// 测试场景1单周期写操作完整帧含SOF和EOF
@(posedge clk);
// 帧数据格式[152]操作类型(1=), [151]SOF, [150]EOF, [149:134]行地址, [133:128]列地址, [127:0]数据
axi2array_frame_data = {1'b1, 1'b1, 1'b1, 16'h1234, 6'h0A, 128'h5A5A_5A5A_5A5A_5A5A_5A5A_5A5A_5A5A_5A5A};
axi2array_frame_valid = 1'b1; // 发送写请求
@(posedge axi2array_frame_ready); // 等待控制器接收
axi2array_frame_valid = 1'b0; // 撤销请求
#200; // 等待写操作完成含时序延迟
// 测试场景2多周期写操作分帧传输SOF=首帧EOF=尾帧
@(posedge clk);
// 首帧SOF=1EOF=0
axi2array_frame_data = {1'b1, 1'b1, 1'b0, 16'h5678, 6'h0B, 128'hA5A5_A5A5_A5A5_A5A5_A5A5_A5A5_A5A5_A5A5};
axi2array_frame_valid = 1'b1;
@(posedge axi2array_frame_ready);
// 次帧SOF=0EOF=0
axi2array_frame_data = {1'b1, 1'b0, 1'b0, 16'h5678, 6'h0C, 128'hAA55_AA55_AA55_AA55_AA55_AA55_AA55_AA55};
@(posedge axi2array_frame_ready);
// 尾帧SOF=0EOF=1
axi2array_frame_data = {1'b1, 1'b0, 1'b1, 16'h5678, 6'h0D, 128'h55AA_55AA_55AA_55AA_55AA_55AA_55AA_55AA};
@(posedge axi2array_frame_ready);
axi2array_frame_valid = 1'b0;
#300; // 等待多帧写完成
// 测试场景3单周期读操作读取场景1写入的数据
@(posedge clk);
// 帧数据格式[152]操作类型(0=), [151]SOF, [150]EOF, [149:134]行地址, [133:128]列地址, [127:0]无效读数据由array返回
axi2array_frame_data = {1'b0, 1'b1, 1'b1, 16'h1234, 6'h0A, 128'd0};
axi2array_frame_valid = 1'b1; // 发送读请求
@(posedge axi2array_frame_ready);
axi2array_frame_valid = 1'b0;
// 模拟array返回读数据延迟1个时钟周期符合时序
#20; // 等待控制器发起读命令
array_rdata = 128'h5A5A_5A5A_5A5A_5A5A_5A5A_5A5A_5A5A_5A5A; // 预期数据与场景1写入一致
array_rdata_vld = 1'b1;
@(posedge clk);
array_rdata_vld = 1'b0; // 撤销读数据有效
#200; // 等待读响应返回AXI
// 测试场景4读写混合操作写后立即读验证数据一致性
@(posedge clk);
// 先写
axi2array_frame_data = {1'b1, 1'b1, 1'b1, 16'h9ABC, 6'h0E, 128'h1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0};
axi2array_frame_valid = 1'b1;
@(posedge axi2array_frame_ready);
axi2array_frame_valid = 1'b0;
#100; // 等待写完成
// 立即读
axi2array_frame_data = {1'b0, 1'b1, 1'b1, 16'h9ABC, 6'h0E, 128'd0};
axi2array_frame_valid = 1'b1;
@(posedge axi2array_frame_ready);
axi2array_frame_valid = 1'b0;
#20;
array_rdata = 128'h1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0; // 验证写后读一致性
array_rdata_vld = 1'b1;
@(posedge clk);
array_rdata_vld = 1'b0;
#200;
// 测试场景5刷新操作使能刷新验证控制器响应
@(posedge clk);
array_ref_en = 1'b1; // 开启刷新
#(2.5*array_inner_tref0); // 等待至少1个刷新周期根据tref0=5个时钟周期
array_ref_en = 1'b0; // 关闭刷新
#100;
// 测试场景6刷新与读写冲突刷新期间发起读写验证优先级
@(posedge clk);
array_ref_en = 1'b1; // 开启刷新
#50; // 确保刷新已启动
// 尝试写入预期被阻塞直到刷新完成
axi2array_frame_data = {1'b1, 1'b1, 1'b1, 16'hDEF0, 6'h0F, 128'hFEDC_BA98_7654_3210_FEDC_BA98_7654_3210};
axi2array_frame_valid = 1'b1;
@(posedge axi2array_frame_ready); // 可能延迟因刷新优先级更高
axi2array_frame_valid = 1'b0;
array_ref_en = 1'b0; // 关闭刷新
#200;
// 测试场景7边界地址读写最大行/列地址
@(posedge clk);
// 写最大地址
axi2array_frame_data = {1'b1, 1'b1, 1'b1, 16'hFFFF, 6'h3F, 128'hFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF};
axi2array_frame_valid = 1'b1;
@(posedge axi2array_frame_ready);
axi2array_frame_valid = 1'b0;
#200;
// 读最大地址
axi2array_frame_data = {1'b0, 1'b1, 1'b1, 16'hFFFF, 6'h3F, 128'd0};
axi2array_frame_valid = 1'b1;
@(posedge axi2array_frame_ready);
axi2array_frame_valid = 1'b0;
#20;
array_rdata = 128'hFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF; // 验证边界地址数据
array_rdata_vld = 1'b1;
@(posedge clk);
array_rdata_vld = 1'b0;
#200;
// 测试结束
#1000;
$display("所有测试场景完成!");
$finish;
end
// 波形记录用于仿真后分析
initial begin
$fsdbDumpfile("tb.fsdb"); // 记录FSDB格式波形
$fsdbDumpvars(0, tb_array_ctrl, "+all"); // 记录所有信号
$vcdpluson; // 记录VCD+格式波形
$vcdplusmemon; // 记录内存信号
end
// 信号监控与断言验证关键功能
initial begin
$monitor(
"Time: %0t, 写有效: %b, 读有效: %b, 刷新使能: %b, CSN: %b, 行地址: %h, 列地址(写): %h, 列地址(读): %h",
$time, array_wdata_vld, array_caddr_vld_rd, array_ref_en, array_csn, array_raddr, array_caddr_wr, array_caddr_rd
);
// 断言1复位期间片选信号CSN应为高无效
@(negedge rst_n);
if (array_csn !== 1'b1) begin
$error("复位期间CSN应为高Time: %0t", $time);
end
// 断言2写操作时CSN应为低有效且写数据有效
@(posedge array_wdata_vld);
if (array_csn !== 1'b0) begin
$error("写操作时CSN应为低Time: %0t", $time);
end
// 断言3读操作时CSN应为低有效且读地址有效
@(posedge array_caddr_vld_rd);
if (array_csn !== 1'b0) begin
$error("读操作时CSN应为低Time: %0t", $time);
end
// 断言4读响应数据应与写入数据一致场景3验证
@(posedge array2axi_rdata_valid);
if (array2axi_rdata !== 128'h5A5A_5A5A_5A5A_5A5A_5A5A_5A5A_5A5A_5A5A) begin
$error("读数据与写入数据不一致Time: %0t", $time);
end
// 断言5刷新期间不应有读写操作CSN保持高或读写信号无效
@(posedge array_ref_en);
#10; // 等待刷新启动
if (array_wdata_vld === 1'b1 || array_caddr_vld_rd === 1'b1) begin
$error("刷新期间不应有读写操作Time: %0t", $time);
end
end
endmodule