`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