Files
BSI-901-fpga-software/c64_pro.srcs/sources_1/rtl/csi040/csi040_ctrl_tb.v
T

209 lines
6.7 KiB
Verilog
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
`timescale 1 ns / 1 ps
//------------------------------------------------------------------------------
// csi040_ctrl 的自检查 testbench。
//
// 这个 testbench 驱动 BRAM 风格控制口,监视 DUT 产生的 SPI 总线,
// 并检查每个期望的 32 bit 单字节 SPI 帧:
// [opcode][addr high][addr low][data]
//
// ASIC SO 只做了最小模型,用于 direct read 测试:
// 在读帧的 payload byte 阶段返回 read_response。
//------------------------------------------------------------------------------
module csi040_ctrl_tb;
localparam CLK_PERIOD = 10;
// DUT 寄存器总线和 ASIC 侧引脚。
reg clk;
reg rst_n;
reg [12:0] bram_addr;
reg [31:0] bram_din;
wire [31:0] bram_dout;
reg bram_en;
reg [3:0] bram_we;
wire busy;
wire spi_done;
wire error_busy;
wire asic_rst_n;
wire asic_trig;
wire asic_csn;
wire asic_sck;
wire asic_si;
reg asic_so;
// SPI monitor 状态。CSN 为低时,shift_in 捕获 SI bit。
integer bit_cnt;
integer frame_bits;
reg [31:0] shift_in;
reg [31:0] frame_in;
reg [7:0] read_response;
// 使用较小分频和常见 0x02/0x03 opcode,让仿真更短,
// 波形里的 SPI 帧也更好读。
csi040_ctrl#
(
.SPI_CLK_DIV ( 2 ),
.SPI_OPCODE_WRITE ( 8'h02 ),
.SPI_OPCODE_READ ( 8'h03 )
)
dut
(
.clk ( clk ),
.rst_n ( rst_n ),
.bram_addr ( bram_addr ),
.bram_clk ( clk ),
.bram_din ( bram_din ),
.bram_dout ( bram_dout ),
.bram_en ( bram_en ),
.bram_rst ( 1'b0 ),
.bram_we ( bram_we ),
.busy ( busy ),
.spi_done ( spi_done ),
.error_busy ( error_busy ),
.asic_rst_n ( asic_rst_n ),
.asic_trig ( asic_trig ),
.asic_csn ( asic_csn ),
.asic_sck ( asic_sck ),
.asic_si ( asic_si ),
.asic_so ( asic_so )
);
// 100 MHz 时钟。
initial begin
clk = 1'b0;
forever #(CLK_PERIOD/2) clk = ~clk;
end
// 主定向测试序列。每个 write_bram 要么启动命令,要么更新 shadow 寄存器;
// 每个 check_next_frame 等待 CSN 拉高,并比较 monitor 捕获到的 SPI bit。
initial begin
rst_n = 1'b0;
bram_addr = 0;
bram_din = 0;
bram_en = 1'b0;
bram_we = 4'h0;
asic_so = 1'b0;
read_response = 8'h28;
#(CLK_PERIOD*8);
rst_n = 1'b1;
// CONTROL 写:ASIC register 0x0002 <= 0x10。
write_bram(13'h0020, 32'h00000010);
check_next_frame(32'h02000210);
// CH_ENABLE 写会把 16 bit 拆到 ASIC register 0x0007/0x0008。
write_bram(13'h0024, 32'h0000A55A);
check_next_frame(32'h0200075A);
check_next_frame(32'h020008A5);
// 通道参数写。ch_index=3CH_CUR_A offset=1
// ASIC 地址 = 0x0010 + 3*0x10 + 1 = 0x0041。
write_bram(13'h0040, 32'h00000003);
write_bram(13'h0048, 32'h0000007F);
check_next_frame(32'h0200417F);
// DCS 解锁序列:向 key register 0x0009 连续写 3 个魔术字节。
write_bram(13'h002C, 32'h00000001);
check_next_frame(32'h02000955);
check_next_frame(32'h020009B3);
check_next_frame(32'h0200090A);
// Direct 方式从 Part ID register 0x011D 读 1 字节。
// 下面的 ASIC 模型返回 0x28,随后通过 BRAM 读 SPI_RDATA 检查。
write_bram(13'h0008, 32'h0000011D);
write_bram(13'h0010, 32'h00000100);
check_next_frame(32'h03011D00);
read_bram(13'h0014);
if (bram_dout[7:0] !== 8'h28) begin
$display("ERROR read data expected 28 got %h", bram_dout[7:0]);
$stop;
end
#(CLK_PERIOD*20);
$finish;
end
// CSN 拉低时,开始捕获一帧新的 SPI。
always @(negedge asic_csn) begin
bit_cnt <= 0;
shift_in <= 0;
end
// CSN 拉高时,锁存刚捕获完的一帧。
always @(posedge asic_csn) begin
frame_in <= shift_in;
frame_bits <= bit_cnt;
end
// SPI monitorCSN 有效期间,在 SCK 上升沿采样 MOSI/SI。
always @(posedge asic_sck) begin
if (!asic_csn) begin
shift_in <= {shift_in[30:0], asic_si};
bit_cnt <= bit_cnt + 1;
end
end
// 最小 ASIC 读模型。对当前 32 bit 测试帧来说,bit 24..31 是返回数据字节。
// 在下降沿驱动 SO,保证 DUT 在之后的上升沿能采到。
always @(negedge asic_sck) begin
if (!asic_csn && (bit_cnt >= 24) && (bit_cnt < 32))
asic_so <= read_response[31-bit_cnt];
else
asic_so <= 1'b0;
end
// 整字 BRAM 写 helper。先等待 DUT 空闲,避免定向测试误触发 busy-error。
task write_bram;
input [12:0] addr;
input [31:0] data;
begin
wait(busy == 1'b0);
@(negedge clk);
bram_addr = addr;
bram_din = data;
bram_en = 1'b1;
bram_we = 4'hF;
@(posedge clk);
@(negedge clk);
bram_en = 1'b0;
bram_we = 4'h0;
end
endtask
// BRAM 读 helper。bram_en 为高且 bram_we 为 0 的时钟沿更新 bram_dout。
task read_bram;
input [12:0] addr;
begin
@(negedge clk);
bram_addr = addr;
bram_din = 0;
bram_en = 1'b1;
bram_we = 4'h0;
@(posedge clk);
@(negedge clk);
bram_en = 1'b0;
end
endtask
// 等待下一帧 SPI 完成,并和期望的 32 bit 单字节帧比较。
task check_next_frame;
input [31:0] expected;
begin
@(posedge asic_csn);
#(CLK_PERIOD/2);
if (frame_bits != 32) begin
$display("ERROR frame expected 32 bits got %0d bits", frame_bits);
$stop;
end
if (frame_in !== expected) begin
$display("ERROR frame expected %h got %h", expected, frame_in);
$stop;
end
end
endtask
endmodule