//------------------------------------------------------------------------------
// CERN BE-CO-HT
// White Rabbit Trigger Distribution (WRTD)
// https://ohwr.org/projects/wrtd
//------------------------------------------------------------------------------
//
// unit name:   dut_env
//
// description: Wrapper for the Device Under Test (SPEC150T-based FMC ADC WRTD
// reference design) and its peripherals.
//
//------------------------------------------------------------------------------
// Copyright CERN 2019
//------------------------------------------------------------------------------
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-2.0.
// Unless required by applicable law or agreed to in writing, software,
// hardware and materials distributed under this License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions
// and limitations under the License.
//------------------------------------------------------------------------------

`timescale 1ns/1ps

`include "gn4124_bfm.svh"

module dut_env
  (
   IGN4124PCIMaster i_gn4124,
   output[2:0] acq_fsm_state,
   output sfp_txp_o, sfp_txn_o,
   input  sfp_rxp_i, sfp_rxn_i,
   input  ext_trigger_i
   );

   reg clk_125m_pll = 0;
   reg clk_125m_gtp = 0;
   reg clk_20m_vcxo = 0;
   reg clk_400m_adc = 0;
   reg clk_62m5_sys = 0;

   always #1.25ns clk_400m_adc <= ~clk_400m_adc;
   always #4ns    clk_125m_pll <= ~clk_125m_pll;
   always #4ns    clk_125m_gtp <= ~clk_125m_gtp;
   always #8ns    clk_62m5_sys <= ~clk_62m5_sys;
   always #25ns   clk_20m_vcxo <= ~clk_20m_vcxo;

   reg adc0_fr      = 1'b0;
   reg adc_data_dir = 1'b0;

   reg[3:0] adc0_dat_odd  = 4'h0;
   reg[3:0] adc0_dat_even = 4'h0;

   reg signed [13:0] adc0_data = 0;

   int adc_div = 0;

   wire ddr_cas_n, ddr_ck_p, ddr_ck_n, ddr_cke;
   wire [1:0] ddr_dm, ddr_dqs_p, ddr_dqs_n;
   wire ddr_odt, ddr_ras_n, ddr_reset_n, ddr_we_n;
   wire [15:0] ddr_dq;
   wire [13:0] ddr_a;
   wire [2:0]  ddr_ba;
   wire        ddr_rzq;

   pulldown(ddr_rzq);

   wire sfp_scl, sfp_sda, sfp_sda_en;

   //---------------------------------------------------------------------------
   // The DUT
   //---------------------------------------------------------------------------

   wrtd_ref_spec150t_adc #
     (
      .g_SIMULATION (1),
      .g_WRPC_INITF ("../../../dependencies/wr-cores/bin/wrpc/wrc_phy8_sim.bram")
      )
   DUT
     (
      .button1_n_i               (1'b1),
      .clk_20m_vcxo_i            (clk_20m_vcxo),
      .clk_125m_pllref_p_i       (clk_125m_pll),
      .clk_125m_pllref_n_i       (~clk_125m_pll),
      .clk_125m_gtp_n_i          (clk_125m_gtp),
      .clk_125m_gtp_p_i          (~clk_125m_gtp),
      .pll25dac_sync_n_o         (),
      .pll20dac_sync_n_o         (),
      .plldac_din_o              (),
      .plldac_sclk_o             (),
      .led_sfp_red_o             (),
      .led_sfp_green_o           (),
      .aux_leds_o                (),
      .pcbrev_i                  (4'b0),
      .carrier_onewire_b         (),
      .sfp_txp_o                 (sfp_txp_o),
      .sfp_txn_o                 (sfp_txn_o),
      .sfp_rxp_i                 (sfp_rxp_i),
      .sfp_rxn_i                 (sfp_rxn_i),
      .sfp_mod_def0_i            (1'b0),
      .sfp_mod_def1_b            (sfp_scl),
      .sfp_mod_def2_b            (sfp_sda),
      .sfp_rate_select_o         (),
      .sfp_tx_fault_i            (1'b0),
      .sfp_tx_disable_o          (),
      .sfp_los_i                 (1'b0),
      .spi_sclk_o                (),
      .spi_ncs_o                 (),
      .spi_mosi_o                (),
      .spi_miso_i                (1'b0),
      .uart_rxd_i                (1'b0),
      .uart_txd_o                (),
      .gn_rst_n_i                (i_gn4124.rst_n),
      .gn_p2l_clk_n_i            (i_gn4124.p2l_clk_n),
      .gn_p2l_clk_p_i            (i_gn4124.p2l_clk_p),
      .gn_p2l_rdy_o              (i_gn4124.p2l_rdy),
      .gn_p2l_dframe_i           (i_gn4124.p2l_dframe),
      .gn_p2l_valid_i            (i_gn4124.p2l_valid),
      .gn_p2l_data_i             (i_gn4124.p2l_data),
      .gn_p_wr_req_i             (i_gn4124.p_wr_req),
      .gn_p_wr_rdy_o             (i_gn4124.p_wr_rdy),
      .gn_rx_error_o             (i_gn4124.rx_error),
      .gn_l2p_clk_n_o            (i_gn4124.l2p_clk_n),
      .gn_l2p_clk_p_o            (i_gn4124.l2p_clk_p),
      .gn_l2p_dframe_o           (i_gn4124.l2p_dframe),
      .gn_l2p_valid_o            (i_gn4124.l2p_valid),
      .gn_l2p_edb_o              (i_gn4124.l2p_edb),
      .gn_l2p_data_o             (i_gn4124.l2p_data),
      .gn_l2p_rdy_i              (i_gn4124.l2p_rdy),
      .gn_l_wr_rdy_i             (i_gn4124.l_wr_rdy),
      .gn_p_rd_d_rdy_i           (i_gn4124.p_rd_d_rdy),
      .gn_tx_error_i             (i_gn4124.tx_error),
      .gn_vc_rdy_i               (i_gn4124.vc_rdy),
      .gn_gpio_b                 (),
      .ddr0_a_o                  (ddr_a),
      .ddr0_ba_o                 (ddr_ba),
      .ddr0_cas_n_o              (ddr_cas_n),
      .ddr0_ck_n_o               (ddr_ck_n),
      .ddr0_ck_p_o               (ddr_ck_p),
      .ddr0_cke_o                (ddr_cke),
      .ddr0_dq_b                 (ddr_dq),
      .ddr0_ldm_o                (ddr_dm[0]),
      .ddr0_ldqs_n_b             (ddr_dqs_n[0]),
      .ddr0_ldqs_p_b             (ddr_dqs_p[0]),
      .ddr0_odt_o                (ddr_odt),
      .ddr0_ras_n_o              (ddr_ras_n),
      .ddr0_reset_n_o            (ddr_reset_n),
      .ddr0_rzq_b                (ddr_rzq),
      .ddr0_udm_o                (ddr_dm[1]),
      .ddr0_udqs_n_b             (ddr_dqs_n[1]),
      .ddr0_udqs_p_b             (ddr_dqs_p[1]),
      .ddr0_we_n_o               (ddr_we_n),
      .fmc0_adc_ext_trigger_p_i  (ext_trigger_i),
      .fmc0_adc_ext_trigger_n_i  (~ext_trigger_i),
      .fmc0_adc_dco_p_i          (clk_400m_adc),
      .fmc0_adc_dco_n_i          (~clk_400m_adc),
      .fmc0_adc_fr_p_i           (~adc0_fr),
      .fmc0_adc_fr_n_i           (adc0_fr),
      .fmc0_adc_outa_p_i         (adc0_dat_odd),
      .fmc0_adc_outa_n_i         (~adc0_dat_odd),
      .fmc0_adc_outb_p_i         (adc0_dat_even),
      .fmc0_adc_outb_n_i         (~adc0_dat_even),
      .fmc0_adc_spi_din_i        (),
      .fmc0_adc_spi_dout_o       (),
      .fmc0_adc_spi_sck_o        (),
      .fmc0_adc_spi_cs_adc_n_o   (),
      .fmc0_adc_spi_cs_dac1_n_o  (),
      .fmc0_adc_spi_cs_dac2_n_o  (),
      .fmc0_adc_spi_cs_dac3_n_o  (),
      .fmc0_adc_spi_cs_dac4_n_o  (),
      .fmc0_adc_gpio_dac_clr_n_o (),
      .fmc0_adc_gpio_led_acq_o   (),
      .fmc0_adc_gpio_led_trig_o  (),
      .fmc0_adc_gpio_ssr_ch1_o   (),
      .fmc0_adc_gpio_ssr_ch2_o   (),
      .fmc0_adc_gpio_ssr_ch3_o   (),
      .fmc0_adc_gpio_ssr_ch4_o   (),
      .fmc0_adc_gpio_si570_oe_o  (),
      .fmc0_adc_si570_scl_b      (),
      .fmc0_adc_si570_sda_b      (),
      .fmc0_adc_one_wire_b       (),
      .fmc0_prsnt_m2c_n_i        (1'b0),
      .fmc0_scl_b                (),
      .fmc0_sda_b                ()
      );

   assign acq_fsm_state = DUT.cmp0_fmc_adc_mezzanine.cmp_fmc_adc_100Ms_core.acq_fsm_state;

   //---------------------------------------------------------------------------
   // DDR memory model
   //---------------------------------------------------------------------------

   ddr3 #
     (
      .DEBUG(0),
      .check_strict_timing(0),
      .check_strict_mrbits(0)
      )
   DDR_MEM
     (
      .rst_n   (ddr_reset_n),
      .ck      (ddr_ck_p),
      .ck_n    (ddr_ck_n),
      .cke     (ddr_cke),
      .cs_n    (1'b0),
      .ras_n   (ddr_ras_n),
      .cas_n   (ddr_cas_n),
      .we_n    (ddr_we_n),
      .dm_tdqs (ddr_dm),
      .ba      (ddr_ba),
      .addr    (ddr_a),
      .dq      (ddr_dq),
      .dqs     (ddr_dqs_p),
      .dqs_n   (ddr_dqs_n),
      .tdqs_n  (),
      .odt     (ddr_odt)
      );

   //---------------------------------------------------------------------------
   // SFP I2C Adapter
   //---------------------------------------------------------------------------

   gc_sfp_i2c_adapter
     SFP_I2C
       (
        .clk_i           (clk_125m_pll),
        .rst_n_i         (1'b1),
        .scl_i           (sfp_scl),
        .sda_i           (sfp_sda),
        .sda_en_o        (sfp_sda_en),
        .sfp_det_valid_i (1'b1),
        .sfp_data_i      (128'h0123456789ABCDEF0123456789ABCDEF)
        );

   assign sfp_sda = (sfp_sda_en) ? 1'b0:1'bz;

   //---------------------------------------------------------------------------
   // Dummy ADC data generator
   //---------------------------------------------------------------------------

   always@(negedge clk_400m_adc)
     begin
        #625ps;
        if(adc_div == 1) begin
           adc0_fr <= ~adc0_fr;
           adc_div <= 0;
        end
        else begin
           adc_div <= adc_div + 1;
        end
     end

   always@(posedge adc0_fr)
     begin
        if ((adc0_data > 400) || (adc0_data < -400)) begin
           adc_data_dir = ~adc_data_dir;
        end
        if (adc_data_dir == 0) begin
           adc0_data = adc0_data + 8;
        end
        else begin
           adc0_data = adc0_data - 8;
        end
        adc0_dat_odd  = {4{adc0_data[13]}};
        adc0_dat_even = {4{adc0_data[12]}};
        #1250ps;
        adc0_dat_odd  = {4{adc0_data[11]}};
        adc0_dat_even = {4{adc0_data[10]}};
        #1250ps;
        adc0_dat_odd  = {4{adc0_data[9]}};
        adc0_dat_even = {4{adc0_data[8]}};
        #1250ps;
        adc0_dat_odd  = {4{adc0_data[7]}};
        adc0_dat_even = {4{adc0_data[6]}};
        #1250ps;
        adc0_dat_odd  = {4{adc0_data[5]}};
        adc0_dat_even = {4{adc0_data[4]}};
        #1250ps;
        adc0_dat_odd  = {4{adc0_data[3]}};
        adc0_dat_even = {4{adc0_data[2]}};
        #1250ps;
        adc0_dat_odd  = {4{adc0_data[1]}};
        adc0_dat_even = {4{adc0_data[0]}};
        #1250ps;
        adc0_dat_odd  = {4{1'b0}};
        adc0_dat_even = {4{1'b0}};
     end

   initial begin
      // Skip WR SoftPLL lock
      force DUT.cmp_xwrc_board_spec.cmp_board_common.cmp_xwr_core.
        WRPC.U_SOFTPLL.U_Wrapped_Softpll.out_locked_o = 3'b111;
      // Silence Xilinx unisim DSP48A1 warnings about invalid OPMODE
      force DUT.cmp_xwrc_board_spec.cmp_board_common.cmp_xwr_core.
        WRPC.LM32_CORE.gen_profile_medium_icache.U_Wrapped_LM32.cpu.
          multiplier.D1.OPMODE_dly = 0;
      force DUT.cmp_xwrc_board_spec.cmp_board_common.cmp_xwr_core.
        WRPC.LM32_CORE.gen_profile_medium_icache.U_Wrapped_LM32.cpu.
          multiplier.D2.OPMODE_dly = 0;
      force DUT.cmp_xwrc_board_spec.cmp_board_common.cmp_xwr_core.
        WRPC.LM32_CORE.gen_profile_medium_icache.U_Wrapped_LM32.cpu.
          multiplier.D3.OPMODE_dly = 0;
   end // initial begin

endmodule // dut_env
