Finish top-module(axi_slave array_ctrl apb_cfg): 2025-08-13 16:39:12

This commit is contained in:
Core_kingdom
2025-08-13 16:39:12 +08:00
parent c84096d1f9
commit de5211af62
69 changed files with 3355 additions and 1631 deletions

110
tb/tb_apb_cfg.v Normal file
View File

@@ -0,0 +1,110 @@
module tb_apb_cfg;
reg apb_pclk;
reg apb_prstn;
//from apb master
reg apb_psel;
reg apb_penable;
reg apb_pwrite;
reg [7:0] apb_paddr;
reg [31:0] apb_wdata;
//to apb master
wire [31:0] apb_rdata;
wire apb_pready;
//cfg
//mode ctrl
wire mc_work_en;
wire [1:0] axi_rw_priority;
//
wire [7:0] array_ras_cfg;
wire [7:0] array_rp_cfg;
wire [7:0] array_rc_cfg;
//
wire [7:0] array_rcd_wr_cfg;
wire [7:0] array_wr_cfg;
//
wire [7:0] array_rcd_rd_cfg;
wire [7:0] array_rtp_cfg;
//
wire [25:0] array_ref_period0;
wire [25:0] array_ref_period1;
wire array_ref_sel;
apb_cfg u_apb_cfg(
.apb_pclk(apb_pclk),
.apb_prstn(apb_prstn),
.apb_psel(apb_psel),
.apb_penable(apb_penable),
.apb_pwrite(apb_pwrite),
.apb_paddr(apb_paddr),
.apb_wdata(apb_wdata),
.apb_rdata(apb_rdata),
.apb_pready(apb_pready),
.mc_work_en(mc_work_en),
.axi_rw_priority(axi_rw_priority),
.array_ras_cfg(array_ras_cfg),
.array_rp_cfg(array_rp_cfg),
.array_rc_cfg(array_rc_cfg),
.array_rcd_wr_cfg(array_rcd_wr_cfg),
.array_wr_cfg(array_wr_cfg),
.array_rcd_rd_cfg(array_rcd_rd_cfg),
.array_rtp_cfg(array_rtp_cfg),
.array_ref_period0(array_ref_period0),
.array_ref_period1(array_ref_period1),
.array_ref_sel(array_ref_sel)
);
initial begin
apb_pclk = 1'b0;
apb_prstn = 1'b0;
#20
apb_prstn = 1'b1;
end
always #10 apb_pclk = ~apb_pclk;
initial begin
apb_psel = 1'b0;
apb_penable = 1'b0;
apb_pwrite = 1'b0;
apb_paddr = 'b0;
apb_wdata = 'b0;
end
//task wr
task apb_wr;
input apb_paddr_temp;
input apb_wdata_temp;
begin
wait(apb_pready);
@(negedge apb_pclk);
apb_paddr = apb_paddr_temp;
apb_wdata = apb_wdata_temp;
apb_psel = 1'b1;
apb_pwrite = 1'b1;
apb_penable = 1'b0;
@(posedge apb_pclk);
#0.7
apb_penable = 1'b1;
wait(apb_pready);
@(posedge apb_pclk);
apb_psel = 1'b0;
apb_pwrite = 1'b0;
apb_penable = 1'b0;
end
endtask
initial begin
#50
apb_wr(8'h00,{31'b0,1'b1});
$finish;
end
endmodule

257
tb/tb_array_ctrl.v Normal file
View File

@@ -0,0 +1,257 @@
`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

100
tb/tb_array_mux.v Normal file
View File

@@ -0,0 +1,100 @@
`timescale 1ns/1ps
module tb_array_mux;
// 输入信号定义
reg array_wr_csn; // 写操作片选
reg [15:0] array_wr_raddr; // 写操作地址
reg array_rd_csn; // 读操作片选
reg [15:0] array_rd_raddr; // 读操作地址
reg array_ref_csn; // 刷新操作片选
reg [15:0] array_ref_raddr; // 刷新操作地址
reg [1:0] array_mux_sel; // 选择控制信号
// 输出信号定义
wire array_csn; // 输出片选
wire [15:0] array_raddr; // 输出地址
// 例化被测试模块
array_mux uut (
.array_wr_csn (array_wr_csn),
.array_wr_raddr (array_wr_raddr),
.array_rd_csn (array_rd_csn),
.array_rd_raddr (array_rd_raddr),
.array_ref_csn (array_ref_csn),
.array_ref_raddr (array_ref_raddr),
.array_mux_sel (array_mux_sel),
.array_csn (array_csn),
.array_raddr (array_raddr)
);
// 初始化测试向量
initial begin
// 初始值设置
array_wr_csn = 1'b0;
array_wr_raddr = 16'h1234;
array_rd_csn = 1'b1;
array_rd_raddr = 16'h5678;
array_ref_csn = 1'b0;
array_ref_raddr = 16'h9ABC;
array_mux_sel = 2'b00;
// 等待10ns确保初始状态稳定
#10;
// 测试场景1选择刷新操作2'b01
array_mux_sel = 2'b01;
#10;
$display("Test Case 1 (sel=01): csn=%b, addr=%h (Expected: csn=%b, addr=%h)",
array_csn, array_raddr, array_ref_csn, array_ref_raddr);
// 测试场景2选择写操作2'b10
array_mux_sel = 2'b10;
#10;
$display("Test Case 2 (sel=10): csn=%b, addr=%h (Expected: csn=%b, addr=%h)",
array_csn, array_raddr, array_wr_csn, array_wr_raddr);
// 测试场景3选择读操作2'b11
array_mux_sel = 2'b11;
#10;
$display("Test Case 3 (sel=11): csn=%b, addr=%h (Expected: csn=%b, addr=%h)",
array_csn, array_raddr, array_rd_csn, array_rd_raddr);
// 测试场景4默认情况2'b00
array_mux_sel = 2'b00;
#10;
$display("Test Case 4 (sel=00): csn=%b, addr=%h (Expected: csn=1, addr=0000)",
array_csn, array_raddr);
// 测试场景5动态改变输入值验证实时性
array_ref_csn = 1'b1;
array_ref_raddr = 16'hDEF0;
array_mux_sel = 2'b01;
#10;
$display("Test Case 5 (sel=01 updated): csn=%b, addr=%h (Expected: csn=%b, addr=%h)",
array_csn, array_raddr, array_ref_csn, array_ref_raddr);
// 测试场景6覆盖所有可能的选择信号
array_mux_sel = 2'b00; #5;
array_mux_sel = 2'b01; #5;
array_mux_sel = 2'b10; #5;
array_mux_sel = 2'b11; #5;
// 结束仿真
$finish;
end
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars(0,tb_array_mux,"+all");
$vcdpluson;
$vcdplusmemon;
end
// 监控信号变化可选用于波形分析
initial begin
$monitor("Time: %0t, sel=%b, csn=%b, addr=%h",
$time, array_mux_sel, array_csn, array_raddr);
end
endmodule

175
tb/tb_array_rd.v Normal file
View File

@@ -0,0 +1,175 @@
`timescale 1ns/1ps
module tb_array_rd();
// 时钟和复位信号
reg clk;
reg rst_n;
// 输入信号DUT 的输入
reg array_rd_frame_valid;
reg [151:0] array_rd_frame_data;
reg array_rdata_vld;
reg [127:0] array_rdata;
reg [7:0] array_inner_tras;
reg [7:0] array_inner_trp;
reg [7:0] array_inner_trcd_rd;
reg [7:0] array_inner_trtp;
// 输出信号DUT 的输出
wire array_rd_frame_ready;
wire array_rd_done;
wire array_rd_csn;
wire [15:0] array_rd_raddr;
wire array_caddr_vld_rd;
wire [5:0] array_caddr_rd;
wire array2axi_rdata_valid;
wire [127:0] array2axi_rdata;
// 实例化被测试模块DUT
array_rd u_array_rd(
.clk (clk),
.rst_n (rst_n),
.array_rd_frame_valid(array_rd_frame_valid),
.array_rd_frame_data(array_rd_frame_data),
.array_rd_frame_ready(array_rd_frame_ready),
.array_rd_done (array_rd_done),
.array_rd_csn (array_rd_csn),
.array_rd_raddr (array_rd_raddr),
.array_caddr_vld_rd (array_caddr_vld_rd),
.array_caddr_rd (array_caddr_rd),
.array_rdata_vld (array_rdata_vld),
.array_rdata (array_rdata),
.array_inner_tras (array_inner_tras),
.array_inner_trp (array_inner_trp),
.array_inner_trcd_rd(array_inner_trcd_rd),
.array_inner_trtp (array_inner_trtp),
.array2axi_rdata_valid(array2axi_rdata_valid),
.array2axi_rdata (array2axi_rdata)
);
// 生成时钟50MHz周期 20ns
initial begin
clk = 1'b0;
forever #10 clk = ~clk;
end
// 主测试流程
initial begin
// 初始化信号
rst_n = 1'b0;
array_rd_frame_valid = 1'b0;
array_rd_frame_data = 152'd0;
array_rdata_vld = 1'b0;
array_rdata = 128'd0;
array_inner_tras = 8'd3; // 示例值TRAS = 3 个时钟周期
array_inner_trp = 8'd2; // 示例值TRP = 2 个时钟周期
array_inner_trcd_rd = 8'd2; // 示例值TRCD_RD = 2 个时钟周期
array_inner_trtp = 8'd1; // 示例值TRTP = 1 个时钟周期
// 复位释放10 个时钟周期后
#200;
rst_n = 1'b1;
#20;
// 测试场景 1单帧数据读取 SOF EOF
$display("=== 测试场景 1单帧数据读取 ===");
send_frame(
1'b1, // 起始标志
1'b1, // 结束标志单帧
16'h1234,// 读取地址
6'h05, // 列地址
128'h0 // 读取操作的帧数据中数据段无效填0
);
#20;
drive_rdata(128'hA5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5); // 返回读取数据
wait(array_rd_done); // 等待读取完成
#100;
// 测试场景 2多帧数据读取首帧 SOF末帧 EOF
$display("=== 测试场景 2多帧数据读取 ===");
// 第一帧SOF=1EOF=0
send_frame(
1'b1,
1'b0,
16'h5678,
6'h0A,
128'h0 // 数据段无效
);
// 模拟返回第一帧数据
drive_rdata(128'h55AA55AA55AA55AA55AA55AA55AA55AA);
#10;
// 第二帧SOF=0EOF=1
send_frame(
1'b0,
1'b1,
16'h5678, // 地址与前一帧相同连续读取
6'h0B,
128'h0 // 数据段无效
);
drive_rdata(128'hAA55AA55AA55AA55AA55AA55AA55AA55);
wait(array_rd_done); // 等待读取完成
#100;
// 测试场景 3无输入时的空闲状态
$display("=== 测试场景 3空闲状态验证 ===");
array_rd_frame_valid = 1'b0;
#200;
// 测试结束
$display("=== 所有测试场景完成 ===");
$finish;
end
// 任务发送一帧读取请求
task send_frame;
input rsof; // 帧起始标志
input reof; // 帧结束标志
input [15:0] rraddr; // 读取地址
input [5:0] rcaddr; // 列地址
input [127:0] wdata; // 占位读取操作中无效
begin
// 等待模块就绪ready 信号为高
wait(array_rd_frame_ready);
#10; // 延迟一小段时间
// 驱动输入信号
array_rd_frame_valid = 1'b1;
array_rd_frame_data = {rsof, reof, rraddr, rcaddr, wdata}; // 拼接帧数据
#20; // 保持一个时钟周期
// 撤销有效信号
array_rd_frame_valid = 1'b0;
array_rd_frame_data = 152'd0;
end
endtask
// 任务驱动读取数据返回
task drive_rdata;
input [127:0] data; // 要返回的读取数据
begin
array_rdata_vld = 1'b1;
array_rdata = data;
#20; // 保持一个时钟周期
array_rdata_vld = 1'b0;
array_rdata = 128'd0;
end
endtask
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars(0,tb_array_rd,"+all");
$vcdpluson;
$vcdplusmemon;
end
// 监控关键信号变化用于调试
initial begin
$monitor(
"Time: %0t, State: %h, CSN: %b, Ready: %b, Done: %b, CaddrVld: %b, FIFOValid: %b",
$time, u_array_rd.cur_state, array_rd_csn, array_rd_frame_ready,
array_rd_done, array_caddr_vld_rd, array2axi_rdata_valid
);
end
endmodule

103
tb/tb_array_ref.v Normal file
View File

@@ -0,0 +1,103 @@
`timescale 1ns/1ps
module tb_array_ref();
// 输入信号定义
reg clk;
reg rst_n;
reg array_ref_start;
reg [7:0] array_inner_tras;
reg [7:0] array_inner_trp;
// 输出信号定义
wire array_ref_done;
wire array_ref_csn;
wire [15:0] array_ref_raddr;
// 实例化待测试模块
array_ref uut (
.clk (clk),
.rst_n (rst_n),
.array_ref_start (array_ref_start),
.array_ref_done (array_ref_done),
.array_ref_csn (array_ref_csn),
.array_ref_raddr (array_ref_raddr),
.array_inner_tras (array_inner_tras),
.array_inner_trp (array_inner_trp)
);
// 时钟生成10ns周期100MHz
initial begin
clk = 0;
forever #1.25 clk = ~clk;
end
// 主测试流程
initial begin
// 初始化信号
rst_n = 0;
array_ref_start = 0;
array_inner_tras = 8'd2; // RAS周期为2个时钟
array_inner_trp = 8'd1; // RP周期为1个时钟
// 复位释放
#20;
rst_n = 1;
#10;
// 启动刷新操作
@(posedge clk);
array_ref_start = 1;
@(posedge clk);
array_ref_start = 0; // 释放启动信号
// 等待刷新完成全地址遍历
wait(array_ref_done);
$display("=== 第一次完整刷新完成 ===");
#50;
// // 测试不同的tras和trp参数
// array_inner_tras = 8'd3;
// array_inner_trp = 8'd2;
// @(posedge clk);
// array_ref_start = 1;
// @(posedge clk);
// array_ref_start = 0;
// wait(array_ref_done);
// $display("=== 第二次完整刷新完成 ===");
// #50;
// // 测试中途复位
// @(posedge clk);
// array_ref_start = 1;
// @(posedge clk);
// array_ref_start = 0;
// #30;
// rst_n = 0;
// #20;
// rst_n = 1;
// $display("=== 复位测试完成 ===");
// 结束仿真
#100;
$display("=== 仿真结束 ===");
$finish;
end
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars(0,tb_array_ref,"+all");
$vcdpluson;
$vcdplusmemon;
end
// 监控信号变化
initial begin
$monitor(
"Time: %0t, State: %b, raddr: %h, csn: %b, done: %b, ras_cnt: %h, rp_cnt: %h",
$time, uut.cur_state, array_ref_raddr, array_ref_csn, array_ref_done,
uut.ref_ras_cnt, uut.ref_rp_cnt
);
end
endmodule

182
tb/tb_array_status_ctrl.v Normal file
View File

@@ -0,0 +1,182 @@
`timescale 1ns/1ps
module tb_array_status_ctrl();
// 时钟与复位信号
reg clk;
reg rst_n;
// AXI 到数组的帧信号
reg axi2array_frame_valid;
reg [152:0] axi2array_frame_data;
wire axi2array_frame_ready;
// 数组读写信号
wire array_wr_frame_valid;
wire [151:0] array_wr_frame_data;
reg array_wr_frame_ready;
reg array_wr_done;
wire array_rd_frame_valid;
wire [151:0] array_rd_frame_data;
reg array_rd_frame_ready;
reg array_rd_done;
// 数组刷新信号
wire array_ref_start;
reg array_ref_done;
// 其他控制信号
wire [1:0] array_mux_sel;
reg mc_work_en;
reg array_ref_en;
reg [24:0] array_inner_tref0;
reg [24:0] array_inner_tref1;
reg array_inner_ref_sel;
// 例化被测试模块
array_status_ctrl uut (
.clk (clk),
.rst_n (rst_n),
.axi2array_frame_valid (axi2array_frame_valid),
.axi2array_frame_data (axi2array_frame_data),
.axi2array_frame_ready (axi2array_frame_ready),
.array_wr_frame_valid (array_wr_frame_valid),
.array_wr_frame_data (array_wr_frame_data),
.array_wr_frame_ready (array_wr_frame_ready),
.array_wr_done (array_wr_done),
.array_rd_frame_valid (array_rd_frame_valid),
.array_rd_frame_data (array_rd_frame_data),
.array_rd_frame_ready (array_rd_frame_ready),
.array_rd_done (array_rd_done),
.array_ref_start (array_ref_start),
.array_ref_done (array_ref_done),
.array_mux_sel (array_mux_sel),
.mc_work_en (mc_work_en),
.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 = 0;
forever #10 clk = ~clk;
end
// 主测试流程
initial begin
// 初始化信号
rst_n = 0;
axi2array_frame_valid = 0;
axi2array_frame_data = 0;
array_wr_frame_ready = 0;
array_wr_done = 0;
array_rd_frame_ready = 0;
array_rd_done = 0;
array_ref_done = 0;
mc_work_en = 0;
array_ref_en = 0;
array_inner_tref0 = 25'd5; // 刷新计数阈值
array_inner_tref1 = 25'd10;
array_inner_ref_sel = 0;
// 释放复位
#100 rst_n = 1;
#20;
// 测试场景1写操作
$display("Test Case 1: Write Operation");
mc_work_en = 1; // 使能工作模式
#20;
// 发送写请求bit152=1
axi2array_frame_data = {1'b1, 152'h123456}; // 写数据
axi2array_frame_valid = 1;
array_wr_frame_ready = 1; // 准备接收写数据
#20;
array_wr_done = 1; // 写完成
#20;
axi2array_frame_valid = 0;
array_wr_done = 0;
#100;
// 测试场景2读操作
$display("Test Case 2: Read Operation");
// 发送读请求bit152=0
axi2array_frame_data = {1'b0, 152'hABCDEF}; // 读数据
axi2array_frame_valid = 1;
array_rd_frame_ready = 1; // 准备接收读数据
#20;
array_rd_done = 1; // 读完成
#20;
axi2array_frame_valid = 0;
array_rd_done = 0;
#100;
// 测试场景3刷新操作达到计数阈值
$display("Test Case 3: Refresh Operation (by counter)");
array_ref_en = 1; // 使能刷新
#200; // 等待刷新计数器达到阈值tref0=5约100ns后触发
array_ref_done = 1; // 刷新完成
#20;
array_ref_done = 0;
array_ref_en = 0;
#100;
// 测试场景4状态优先级刷新 > >
$display("Test Case 4: State Priority (Refresh > Write > Read)");
array_ref_en = 1;
mc_work_en = 1;
// 同时触发刷新请求写请求和读请求
axi2array_frame_data = {1'b1, 152'hFEDCBA}; // 写请求
axi2array_frame_valid = 1;
#20;
array_ref_done = 1; // 先完成刷新
#20;
array_ref_done = 0;
#20;
array_wr_done = 1; // 再完成写操作
#20;
axi2array_frame_valid = 0;
array_wr_done = 0;
#100;
// 结束测试
$display("All Test Cases Completed!");
$finish;
end
// 监控状态变化
always @(posedge clk) begin
if (rst_n) begin
case (uut.cur_state)
2'b00: $display("[%0t] State: IDLE", $time);
2'b01: $display("[%0t] State: WRITE", $time);
2'b10: $display("[%0t] State: READ", $time);
2'b11: $display("[%0t] State: REFRESH", $time);
endcase
end
end
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars(0,tb_array_status_ctrl,"+all");
$vcdpluson;
$vcdplusmemon;
end
// 监控关键信号
always @(posedge clk) begin
if (array_ref_start) begin
$display("[%0t] Refresh Start Triggered", $time);
end
if (array_wr_frame_valid && array_wr_frame_ready) begin
$display("[%0t] Write Data Transferred: %h", $time, array_wr_frame_data);
end
if (array_rd_frame_valid && array_rd_frame_ready) begin
$display("[%0t] Read Data Transferred: %h", $time, array_rd_frame_data);
end
end
endmodule

151
tb/tb_array_wr.v Normal file
View File

@@ -0,0 +1,151 @@
`timescale 1ns/1ps
module tb_array_wr();
// 时钟和复位信号
reg clk;
reg rst_n;
// 输入信号DUT 的输入
reg array_wr_frame_valid;
reg [151:0] array_wr_frame_data;
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;
// 输出信号DUT 的输出
wire array_wr_frame_ready;
wire array_wr_done;
wire array_wr_csn;
wire [15:0] array_wr_raddr;
wire array_caddr_vld_wr;
wire [5:0] array_caddr_wr;
wire array_wdata_vld;
wire [127:0] array_wdata;
// 实例化被测试模块DUT
array_wr u_array_wr(
.clk (clk),
.rst_n (rst_n),
.array_wr_frame_valid(array_wr_frame_valid),
.array_wr_frame_data(array_wr_frame_data),
.array_wr_frame_ready(array_wr_frame_ready),
.array_wr_done (array_wr_done),
.array_wr_csn (array_wr_csn),
.array_wr_raddr (array_wr_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_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)
);
// 生成时钟50MHz周期 20ns
initial begin
clk = 1'b0;
forever #10 clk = ~clk;
end
// 主测试流程
initial begin
// 初始化信号
rst_n = 1'b0;
array_wr_frame_valid = 1'b0;
array_wr_frame_data = 152'd0;
array_inner_tras = 8'd3; // 示例值TRAS = 3 个时钟周期
array_inner_trp = 8'd2; // 示例值TRP = 2 个时钟周期
array_inner_trcd_wr = 8'd2; // 示例值TRCD_WR = 2 个时钟周期
array_inner_twr = 8'd2; // 示例值TWR = 2 个时钟周期
// 复位释放10 个时钟周期后
#200;
rst_n = 1'b1;
#20;
// 测试场景 1单帧数据写入 SOF EOF
$display("=== 测试场景 1单帧数据写入 ===");
send_frame(
1'b1, // 起始标志
1'b1, // 结束标志单帧
16'h1234,// 地址
6'h05, // 列地址
128'hA5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5 // 数据
);
wait(array_wr_done); // 等待写入完成
#100;
// 测试场景 2多帧数据写入首帧 SOF末帧 EOF
$display("=== 测试场景 2多帧数据写入 ===");
// 第一帧SOF=1EOF=0
send_frame(
1'b1,
1'b0,
16'h5678,
6'h0A,
128'h55AA55AA55AA55AA55AA55AA55AA55AA
);
#20;
// 第二帧SOF=0EOF=1
send_frame(
1'b0,
1'b1,
16'h5678, // 地址与前一帧相同连续写入
6'h0B,
128'hAA55AA55AA55AA55AA55AA55AA55AA55
);
wait(array_wr_done); // 等待写入完成
#100;
// 测试场景 3无输入时的空闲状态
$display("=== 测试场景 3空闲状态验证 ===");
array_wr_frame_valid = 1'b0;
#200;
// 测试结束
$display("=== 所有测试场景完成 ===");
$finish;
end
// 任务发送一帧数据封装输入信号逻辑
task send_frame;
input wsof; // 帧起始标志
input weof; // 帧结束标志
input [15:0] wraddr; // 写入地址
input [5:0] wcaddr; // 列地址
input [127:0] wdata; // 写入数据
begin
// 等待模块就绪ready 信号为高
wait(array_wr_frame_ready);
#10; // 延迟一小段时间
// 驱动输入信号
array_wr_frame_valid = 1'b1;
array_wr_frame_data = {wsof, weof, wraddr, wcaddr, wdata}; // 拼接帧数据
#20; // 保持一个时钟周期
// 撤销有效信号
array_wr_frame_valid = 1'b0;
array_wr_frame_data = 152'd0;
end
endtask
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars(0,tb_array_wr,"+all");
$vcdpluson;
$vcdplusmemon;
end
// 监控关键信号变化可选用于调试
initial begin
$monitor(
"Time: %0t, State: %h, CSN: %b, Ready: %b, Done: %b, Valid: %b",
$time, u_array_wr.cur_state, array_wr_csn, array_wr_frame_ready, array_wr_done, array_wdata_vld
);
end
endmodule

197
tb/tb_axi_slv.v Normal file
View File

@@ -0,0 +1,197 @@
module tb_axi_slv;
reg clk;
reg rst_n;
reg axi_s_awvalid;
reg [7:0] axi_s_awlen;
reg [25:0] axi_s_awaddr;
wire axi_s_awready;
reg axi_s_wvalid;
reg axi_s_wlast;
reg [63:0] axi_s_wdata;
wire axi_s_wready;
reg axi_s_arvalid;
reg [7:0] axi_s_arlen;
reg [25:0] axi_s_araddr;
wire axi_s_arready;
wire axi_s_rvalid;
wire axi_s_rlast;
wire [63:0] axi_s_rdata;
wire axi2array_frame_valid;
wire [152:0] axi2array_frame_data;
reg axi2array_frame_ready;
reg array2axi_rdata_valid;
reg [127:0] array2axi_rdata;
reg [1:0] axi_bus_rw_priority;
reg mc_work_en;
axi_slv u_axi_slv(
.clk (clk),
.rst_n (rst_n),
.axi_s_awvalid (axi_s_awvalid),
.axi_s_awlen (axi_s_awlen),
.axi_s_awaddr (axi_s_awaddr),
.axi_s_awready (axi_s_awready),
.axi_s_wvalid (axi_s_wvalid),
.axi_s_wlast (axi_s_wlast),
.axi_s_wdata (axi_s_wdata),
.axi_s_wready (axi_s_wready),
.axi_s_arvalid (axi_s_arvalid),
.axi_s_arlen (axi_s_arlen),
.axi_s_araddr (axi_s_araddr),
.axi_s_arready (axi_s_arready),
.axi_s_rvalid (axi_s_rvalid),
.axi_s_rlast (axi_s_rlast),
.axi_s_rdata (axi_s_rdata),
.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),
.axi_bus_rw_priority (axi_bus_rw_priority),
.mc_work_en (mc_work_en)
);
task aw;
input [7:0] awlen;
input [25:0] awaddr;
begin
@(posedge clk) begin
axi_s_awvalid <= 1'b1;
axi_s_awaddr <= awaddr;
axi_s_awlen <= awlen;
end
#1;
wait(axi_s_awready);
@(posedge clk) begin
axi_s_awvalid <= 1'b0;
end
end
endtask
task w;
input [63:0] wdata;
input wlast;
begin
@(posedge clk) begin
axi_s_wvalid <= 1'b1;
axi_s_wdata <= wdata;
axi_s_wlast <= wlast;
end
#0.1;
wait(axi_s_wready);
@(posedge clk) begin
axi_s_wvalid <= 1'b0;
end
end
endtask
task ar;
input [25:0] araddr;
input [7:0] arlen;
begin
@(posedge clk) begin
axi_s_arvalid <= 1'b1;
axi_s_araddr <= araddr;
axi_s_arlen <= arlen;
end
#1;
wait(axi_s_arready);
@(posedge clk) begin
axi_s_arvalid <= 1'b0;
end
end
endtask
task arrayrdata;
input [127:0] rdata;
begin
@(posedge clk) begin
array2axi_rdata_valid <= 1'b1;
array2axi_rdata <= rdata;
end
@(posedge clk) begin
array2axi_rdata_valid <= 1'b0;
end
end
endtask
initial begin
clk = 0;
forever begin
#1.25 clk = ~clk;
end
end
initial begin
rst_n = 'd0;
axi_s_awvalid = 'd0;
axi_s_awlen ='d0;
axi_s_awaddr ='d0;
axi_s_wvalid = 'd0;
axi_s_wdata = 'd0;
axi_s_wlast = 'd0;
axi_s_arvalid = 'd0;
axi_s_araddr = 'd0;
axi_s_arlen = 'd0;
array2axi_rdata ='d0;
array2axi_rdata_valid = 'd0;
axi2array_frame_ready = 1'd1;
mc_work_en = 1'b1;
axi_bus_rw_priority = 2'b10;
@(posedge clk) begin
rst_n = 1'b1;
end
aw(8'd5,{16'h0,6'h3f,4'h0});
w(64'd1,0);
w(64'd2,0);
w(64'd3,0);
w(64'd4,0);
w(64'd5,0);
w(64'd6,1);
@(posedge clk) begin
axi_s_wvalid <= 1'b0;
end
ar({16'h1,6'h3f,4'h0},8'd9);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
arrayrdata({64'h2,64'h1});
arrayrdata({64'h4,64'h3});
arrayrdata({64'h6,64'h5});
arrayrdata({64'h8,64'h7});
arrayrdata({64'ha,64'h9});
#15;
$finish;
end
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars(0,tb_axi_slv,"+all");
$vcdpluson;
$vcdplusmemon;
end
endmodule