159 lines
4.7 KiB
Systemverilog
159 lines
4.7 KiB
Systemverilog
`timescale 1ns / 1ps
|
||
|
||
module tb_digital_top();
|
||
|
||
// 参数定义
|
||
parameter CLK_PERIOD = 20; // 100MHz 系统时钟
|
||
parameter SCLK_PERIOD = 200; // 10MHz SPI时钟 (必须比系统时钟慢)
|
||
|
||
// 信号声明
|
||
logic clk;
|
||
logic rst_n;
|
||
logic [4:0] cfgid;
|
||
|
||
logic sclk;
|
||
logic csn;
|
||
logic mosi;
|
||
logic miso;
|
||
logic oen;
|
||
logic sig_in;
|
||
|
||
// 实例化 DUT
|
||
digital_top u_digital_top (
|
||
.clk (clk ),
|
||
.rst_n (rst_n ),
|
||
.cfgid (cfgid ),
|
||
.sclk (sclk ),
|
||
.csn (csn ),
|
||
.mosi (mosi ),
|
||
.miso (miso ),
|
||
.oen (oen ),
|
||
.sig_in ( sig_in )
|
||
);
|
||
|
||
// 系统时钟产生
|
||
initial begin
|
||
clk = 0;
|
||
forever #(CLK_PERIOD/2) clk = ~clk;
|
||
end
|
||
|
||
// 初始复位与赋值
|
||
initial begin
|
||
rst_n = 0;
|
||
sclk = 0;
|
||
csn = 1;
|
||
mosi = 0;
|
||
cfgid = 5'h00; // 设定芯片ID为 10
|
||
#(CLK_PERIOD * 10);
|
||
rst_n = 1;
|
||
#(CLK_PERIOD * 10);
|
||
fork
|
||
begin // --- 开始测试 ---
|
||
#2ms;
|
||
spi_read(25'h08, 5'h00);
|
||
#(CLK_PERIOD * 10);
|
||
spi_read(25'h0c, 5'h00);
|
||
#(CLK_PERIOD * 10);
|
||
spi_write(25'h08, 5'h00, 32'h61a8);
|
||
end
|
||
|
||
begin
|
||
gen_pulses(400,5);
|
||
end
|
||
join
|
||
|
||
|
||
// spi_read(25'h04, 5'h00);
|
||
//aa
|
||
// spi_read(25'h00, 5'h0A);
|
||
// #(SCLK_PERIOD * 5);
|
||
// spi_read(25'h00, 5'h0A);
|
||
// // 1. 测试写操作: 写入 0x12345678 到 地址 0x100
|
||
// spi_write(25'h00, 5'h0A, 32'h12345678);
|
||
|
||
// #(SCLK_PERIOD * 5);
|
||
|
||
// // 2. 测试读操作: 从地址 0x100 读取 (验证读使能和地址)
|
||
// spi_read(25'h00, 5'h0A);
|
||
|
||
#(SCLK_PERIOD * 20);
|
||
$display("Test Bench Finished.");
|
||
$finish;
|
||
end
|
||
|
||
// ---------------------------------------------------------
|
||
// Task: SPI 发送/接收 32位数据
|
||
// ---------------------------------------------------------
|
||
task spi_xfer_32(input [31:0] data_in, output [31:0] data_out);
|
||
for (int i = 31; i >= 0; i--) begin
|
||
mosi = data_in[i]; // MSB First ,psclk前给数据 psclk让人采样. 自己什么时候采?要看别人什么时候给数据
|
||
#(SCLK_PERIOD/2);
|
||
sclk = 1; // 上升沿,DUT采样
|
||
data_out[i] = miso;
|
||
// 采样从机发回的数据
|
||
#(SCLK_PERIOD/2);
|
||
sclk = 0;
|
||
end
|
||
endtask
|
||
|
||
// ---------------------------------------------------------
|
||
// Task: 写操作 (wnr=0)
|
||
// ---------------------------------------------------------
|
||
task spi_write(input [24:0] target_addr, input [4:0] id, input [31:0] data);
|
||
logic [31:0] header;
|
||
logic [31:0] dummy;
|
||
|
||
header = {1'b0, target_addr, id, 1'b0}; // 构造命令帧
|
||
|
||
$display("[WRITE] Addr: %h, Data: %h", target_addr, data);
|
||
|
||
csn = 0;
|
||
spi_xfer_32(header, dummy); // 发送命令
|
||
spi_xfer_32(data, dummy); // 发送数据
|
||
csn = 1;
|
||
mosi = 0;
|
||
endtask
|
||
|
||
// ---------------------------------------------------------
|
||
// Task: 读操作 (wnr=1)
|
||
// ---------------------------------------------------------
|
||
task spi_read(input [24:0] target_addr, input [4:0] id);
|
||
logic [31:0] header;
|
||
logic [31:0] read_val;
|
||
|
||
header = {1'b1, target_addr, id, 1'b0}; // 构造命令帧 (wnr=1)
|
||
|
||
$display("[READ] Addr: %h", target_addr);
|
||
|
||
csn = 0;
|
||
spi_xfer_32(header, read_val); // 发送命令帧,此时读回的可能是无用数据
|
||
spi_xfer_32(32'h0, read_val); // 发送Dummy,接收真正的读数据
|
||
csn = 1;
|
||
mosi = 0;
|
||
|
||
$display("[READ] Result: %h", read_val);
|
||
endtask
|
||
|
||
|
||
task automatic gen_pulses(input int freq_khz, input int duration_ms);
|
||
int half_period_ns;
|
||
longint end_time_ns;
|
||
begin
|
||
if (freq_khz <= 0) begin
|
||
sig_in = 0;
|
||
#(duration_ms * 1000000);
|
||
end else begin
|
||
half_period_ns = 500000 / freq_khz;
|
||
end_time_ns = $time + (longint'(duration_ms) * 1000000);
|
||
|
||
$display("[%0t] Start generating signal: %0d kHz", $time, freq_khz);
|
||
while ($time < end_time_ns) begin
|
||
sig_in = 1;
|
||
#(half_period_ns);
|
||
sig_in = 0;
|
||
#(half_period_ns);
|
||
end
|
||
end
|
||
end
|
||
endtask
|
||
endmodule |