`timescale 1ns / 1ps module tb_reflectometer; // parameters localparam int unsigned DAC_DATA_WIDTH = 14; localparam int unsigned ADC_DATA_WIDTH = 12; localparam PACK_FACTOR = 1; // not used in TB localparam PROCESS_MODE = 0; // 0 - uint, 1 - int localparam ZERO_LEVEL = 8192; // DAC zero voltage representation (2^14 / 2) localparam ACCUM_WIDTH = 32; // accumulator number bit witdth localparam N_MAX = 4096; // max value of windows to average by experiments localparam WINDOW_SIZE = 65; // fixed subwindow size to average by time localparam PACKET_SIZE = 1024; // bytes per UDP packet localparam int unsigned ADC_CLK_MHZ = 65; localparam int unsigned DAC_CLK_MHZ = 125; // may be changed for test purposes localparam int unsigned PULSE_WIDTH = 2**6; localparam int unsigned PULSE_PERIOD = 2**8; localparam int unsigned PULSE_NUM = 10; localparam int unsigned PULSE_HEIGHT = 2**12; localparam int unsigned PULSE_PERIOD_ADC = (int'(real'(ADC_CLK_MHZ) / real'(DAC_CLK_MHZ) * real'(PULSE_PERIOD)) / int'(WINDOW_SIZE)) * int'(WINDOW_SIZE); initial begin if (PULSE_WIDTH <= 0) $fatal(1, "PULSE_WIDTH should be positive"); if (PULSE_PERIOD <= 0) $fatal(1, "PULSE_PERIOD should be positive"); if (PULSE_NUM <= 0) $fatal(1, "PULSE_NUM should be positive"); if (PULSE_HEIGHT <= 0) $fatal(1, "PULSE_HEIGHT should be positive"); if (PULSE_WIDTH >= 2**32-1) $fatal(1, "PULSE_WIDTH too high"); if (PULSE_PERIOD >= 2**32-1) $fatal(1, "PULSE_PERIOD too high"); if (PULSE_NUM >= 2**16-1) $fatal(1, "PULSE_NUM too high"); if (PULSE_HEIGHT >= 2**DAC_DATA_WIDTH-1) $fatal(1, "PULSE_HEIGHT too high"); if (PULSE_PERIOD_ADC % WINDOW_SIZE == 0) $fatal(1, "PULSE_PERIOD_ADC isn't multiple of WINDOW_SIZE"); end // DUT signals logic clk200, clk_eth_phy_tx, clk_eth_phy_rx; // GMII clocks logic rst_n; wire [3:0] status_leds; // [ None, dac_start, m_axis_valid, clk_wiz_locked ] wire dac_clk, dac_en; wire [DAC_DATA_WIDTH-1:0] dac_data; wire adc_clk; logic adc_otr; logic [ADC_DATA_WIDTH-1:0] adc_data; wire [7:0] s_axis_tx_tdata; wire s_axis_tx_tvalid; logic s_axis_tx_tready; wire s_axis_tx_tlast; logic phy_ready; wire accum_tx_start; logic [7:0] m_axis_rx_tdata; logic m_axis_rx_tvalid; logic m_axis_rx_tlast; logic m_axis_rx_tready; logic [127:0] dut_config = 0; // DUT reflectometer_top #( .DAC_DATA_WIDTH(DAC_DATA_WIDTH), .ADC_DATA_WIDTH(ADC_DATA_WIDTH), .PACK_FACTOR(PACK_FACTOR), .PROCESS_MODE(PROCESS_MODE), .ZERO_LEVEL(ZERO_LEVEL), .ACCUM_WIDTH(ACCUM_WIDTH), .N_MAX(N_MAX), .WINDOW_SIZE(WINDOW_SIZE), .PACKET_SIZE(PACKET_SIZE) ) DUT ( .sys_clk(clk200), // main clk 200 mhz .rst_n(rst_n), // rst_n .led(status_leds), // indication [3:0] .gmii_rx_clk(clk_eth_phy_rx), // ext. clk from PHY .gmii_tx_clk(clk_eth_phy_tx), // ext. clk from PHY // accumulated data stream .s_axis_tx_tdata(s_axis_tx_tdata), .s_axis_tx_tvalid(s_axis_tx_tvalid), .s_axis_tx_tready(s_axis_tx_tready), .s_axis_tx_tlast(s_axis_tx_tlast), // controller data stream .m_axis_rx_tdata(m_axis_rx_tdata), .m_axis_rx_tvalid(m_axis_rx_tvalid), .m_axis_rx_tlast(m_axis_rx_tlast), .m_axis_rx_tready(m_axis_rx_tready), .req_ready(phy_ready), // AXI-stream requester ready .send_req(accum_tx_start), // AXI-stream start transmit .p2_clk(dac_clk), // DAC clk .p2_data(dac_data), // DAC [DAC_DATA_WIDTH-1:0] data .p2_wrt(dac_en), // DAC write enable .ch2_clk(adc_clk), // ADC clk .ch2_data(adc_data), // ADC [ADC_DATA_WIDTH-1:0] data .ch2_otr(adc_otr) // ADC signal out-of-range ); // clocks initial begin // 200 MHz clk200 = 1'b0; forever #2.5 clk200 = ~clk200; end initial begin // 125 MHz clk_eth_phy_tx = 1'b0; forever #4 clk_eth_phy_tx = ~clk_eth_phy_tx; end initial begin // 125 MHz clk_eth_phy_rx = 1'b0; forever #4 clk_eth_phy_rx = ~clk_eth_phy_rx; end // ADC input noise simulation always @(posedge adc_clk or negedge rst_n) begin if (!rst_n) begin adc_data <= '0; end else begin adc_data <= $urandom() & ((1 << ADC_DATA_WIDTH) - 1); end end assign adc_otr = 1'b0; // AXIS tasks task automatic axis_send_byte( ref logic clk, input logic [7:0] data, input logic last, ref logic tvalid, ref logic [7:0] tdata, ref logic tlast, input logic tready ); @(posedge clk); tdata <= data; tlast <= last; tvalid <= 1'b1; // Ждем готовности приемника wait(tready === 1'b1); @(posedge clk); tvalid <= 1'b0; tlast <= 1'b0; endtask task automatic dut_soft_reset(); axis_send_byte( .clk(clk_eth_phy_rx), .data(8'b00001111), .last(1'b1), .tvalid(m_axis_rx_tvalid), .tdata(m_axis_rx_tdata), .tlast(m_axis_rx_tlast), .tready(m_axis_rx_tready) ); endtask task automatic dut_start(); axis_send_byte( .clk(clk_eth_phy_rx), .data(8'b11110000), .last(1'b1), .tvalid(m_axis_rx_tvalid), .tdata(m_axis_rx_tdata), .tlast(m_axis_rx_tlast), .tready(m_axis_rx_tready) ); endtask // task automatic dut_send_config( // input logic [127:0] ctrl_config // ); // // команда set_data // axis_send_byte( // .clk(clk_eth_phy_rx), // .data(8'b10001000), // .last(1'b0), // .tvalid(m_axis_rx_tvalid), // .tdata(m_axis_rx_tdata), // .tlast(m_axis_rx_tlast), // .tready(m_axis_rx_tready) // ); // // config burst // for (int i = 0; i < 16; i++) begin // logic [7:0] byte_to_send; // logic is_last; // // get byte // byte_to_send = ctrl_config[i*8 +: 8]; // // tlast for last byte // is_last = (i == 15); // axis_send_byte( // .clk(clk_eth_phy_rx), // .data(byte_to_send), // .last(is_last), // .tvalid(m_axis_rx_tvalid), // .tdata(m_axis_rx_tdata), // .tlast(m_axis_rx_tlast), // .tready(m_axis_rx_tready) // ); // end // endtask // some helpers for controller axis // GAME PLAN // 1. setup reflectometer // 2. create some reference signal with noise + virtual ADC // 3. setup m_axis endpoint for controller to start reflectometer (create multiple tasks) // 4. setup s_axis endpoint for data gathering and plotting // 5. check standalone reflectometer // 6. add reference signal averaging loop throw generator pulse posedge detection // 7. visual comparision of reference VS reflectometer // 8. add statistics for signal comparision (MSE/RMSE) // main TB initial begin // setup rst_n = 1'b0; s_axis_tx_tready = 1'b0; m_axis_rx_tdata = 1'b0; m_axis_rx_tvalid = 1'b0; m_axis_rx_tlast = 1'b0; phy_ready = 1'b0; // startup #100; rst_n = 1'b1; wait(DUT.clk_wiz_ctrl_inst.locked == 1'b1); #20; $display("=== clocks ready / wiz. locked ==="); #40; // ready to work dut_config[31:0] = PULSE_WIDTH; dut_config[63:32] = PULSE_PERIOD; dut_config[79:64] = PULSE_NUM; dut_config[79+DAC_DATA_WIDTH:80] = PULSE_HEIGHT; dut_config[127:96] = PULSE_PERIOD_ADC; // dut_send_config(dut_config); dut_start(); // dut_start(); #1000; // dut_soft_reset(); $display("=== ALL BASIC TESTS PASSED ==="); $finish; end endmodule