`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;  // 刷新间隔0：5个时钟周期（100ns）
        array_inner_tref1 = 25'd60; // 刷新间隔1：10个时钟周期（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=1，EOF=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=0，EOF=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=0，EOF=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