/***************************************************************************************
* Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC)
* Copyright (c) 2020-2024 Institute of Computing Technology, Chinese Academy of Sciences
* Copyright (c) 2020-2021 Peng Cheng Laboratory
*
* DiffTest is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

`include "DifftestMacros.svh"
module tb_top();

`ifdef PALLADIUM
  `ifdef SYNTHESIS
  `define WIRE_CLK //clock will be generated by ixclkgen
  `endif // SYNTHESIS
`endif // PALLADIUM

`ifdef WIRE_CLK
wire        clock;
`else
reg         clock;
`endif // WIRE_CLK
reg         reset;
`ifdef ENABLE_WORKLOAD_SWITCH
wire        workload_switch;
`endif // ENABLE_WORKLOAD_SWITCH
wire [63:0] difftest_logCtrl_begin;
wire [63:0] difftest_logCtrl_end;
wire [63:0] difftest_logCtrl_level;
wire        difftest_perfCtrl_clean;
wire        difftest_perfCtrl_dump;
wire        difftest_uart_out_valid;
wire [ 7:0] difftest_uart_out_ch;
wire        difftest_uart_in_valid;
wire [ 7:0] difftest_uart_in_ch;
wire [63:0] difftest_exit;
wire [`CONFIG_DIFFTEST_STEPWIDTH - 1:0] difftest_step;

string wave_type;

initial begin
`ifndef WIRE_CLK
  clock = 0;
`endif // WIRE_CLK
  reset = 1;

`ifdef VCS
  // enable waveform
  if ($test$plusargs("dump-wave")) begin
    $value$plusargs("dump-wave=%s", wave_type);
    if (wave_type == "vpd") begin
      $vcdplusfile("simv.vpd");
      $vcdpluson;
    end
`ifdef CONSIDER_FSDB
    else if (wave_type == "fsdb") begin
      $fsdbDumpfile("simv.fsdb");
      $fsdbDumpvars(0,"+mda");
    end
`endif
    else begin
      $display("unknown wave file format, want [vpd, fsdb] but:%s\n", wave_type);
      $fatal;
    end
  end
`endif

`ifdef UVS
// enable waveform
if ($test$plusargs("dump-wave")) begin
  $value$plusargs("dump-wave=%s", wave_type);
`ifdef CONSIDER_USDB
  if (wave_type == "usdb") begin
    $usdbDumpfile("uvsim.usdb");
    $usdbDumpvars(0);
    $usdbDumpvars("+mda");
  end
  else begin
    $display("unknown wave file format, want [usdb] but:%s\n", wave_type);
    $fatal;
  end
`endif
end
`endif

`ifdef EMU_TRACE
  // enable waveform for verilator
  if ($test$plusargs("dump-wave")) begin
`ifdef ENABLE_FST
    $dumpfile("simv.fst");
`else
    $dumpfile("simv.vcd");
`endif // ENABLE_FST
    $dumpvars;
  end
`endif // EMU_TRACE
end

// Note: reset delay #100 should be larger than RANDOMIZE_DELAY
`ifdef PALLADIUM
  `define RESET_COUNTER
`elsif ENABLE_WORKLOAD_SWITCH
  `define RESET_COUNTER
`endif

`ifndef RESET_COUNTER
initial begin
  #100 reset = 0;
end
`else
reg [7:0] reset_counter;
initial reset_counter = 0;
always @(posedge clock) begin
`ifdef ENABLE_WORKLOAD_SWITCH
  if (workload_switch) begin
    reset_counter <= 8'd0;
    reset         <= 1'b1;
    $display("workload switch");
  end
  else
`endif // ENABLE_WORKLOAD_SWITCH
  begin
    reset_counter <= reset_counter + 8'd1;
    if (reset && (reset_counter == 8'd100)) begin
      reset <= 1'b0;
    end
  end
end
`endif // RESET_COUNTER

`ifndef WIRE_CLK
always #1 clock <= ~clock;
`endif // WIRE_CLK

wire sim_clock;

`ifdef FPGA_SIM
  wire difftest_pcie_clock;
  wire c2h_axi_tlast;
  wire c2h_axi_tready;
  wire c2h_axi_tvalid;
  wire [511:0] c2h_axi_tdata;
  wire [63:0] c2h_axi_tkeep;
  wire h2c_axi_tlast;
  wire h2c_axi_tready;
  wire h2c_axi_tvalid;
  wire [511:0] h2c_axi_tdata;
  wire [63:0] h2c_axi_tkeep;
  wire [31:0] cfg_awaddr;
  wire        cfg_awvalid;
  wire        cfg_awready;
  wire [31:0] cfg_wdata;
  wire [3:0]  cfg_wstrb;
  wire        cfg_wvalid;
  wire        cfg_wready;
  wire [1:0]  cfg_bresp;
  wire        cfg_bvalid;
  wire        cfg_bready;
  wire [31:0] cfg_araddr;
  wire        cfg_arvalid;
  wire        cfg_arready;
  wire [31:0] cfg_rdata;
  wire [1:0]  cfg_rresp;
  wire        cfg_rvalid;
  wire        cfg_rready;
  wire        host_ctrl_reset;
  wire        host_ctrl_diff_enable;
  wire        host_ctrl_ila_trigger;
  wire        host_ctrl_enable_squash;

  assign difftest_pcie_clock = clock;
xdma_clock xclk(
  .clock(difftest_pcie_clock),
  .clock_out(sim_clock)
);
xdma_wrapper xdma(
  .pcie_clock(difftest_pcie_clock),
  .axilite_clock(sim_clock),
  .reset(reset),
  .axi_c2h_tdata(c2h_axi_tdata),
  .axi_c2h_tlast(c2h_axi_tlast),
  .axi_c2h_tready(c2h_axi_tready),
  .axi_c2h_tvalid(c2h_axi_tvalid),
  .axi_h2c_tdata(h2c_axi_tdata),
  .axi_h2c_tkeep(h2c_axi_tkeep),
  .axi_h2c_tlast(h2c_axi_tlast),
  .axi_h2c_tready(h2c_axi_tready),
  .axi_h2c_tvalid(h2c_axi_tvalid),
  .cfg_awaddr(cfg_awaddr),
  .cfg_awvalid(cfg_awvalid),
  .cfg_awready(cfg_awready),
  .cfg_wdata(cfg_wdata),
  .cfg_wstrb(cfg_wstrb),
  .cfg_wvalid(cfg_wvalid),
  .cfg_wready(cfg_wready),
  .cfg_bresp(cfg_bresp),
  .cfg_bvalid(cfg_bvalid),
  .cfg_bready(cfg_bready),
  .cfg_araddr(cfg_araddr),
  .cfg_arvalid(cfg_arvalid),
  .cfg_arready(cfg_arready),
  .cfg_rdata(cfg_rdata),
  .cfg_rresp(cfg_rresp),
  .cfg_rvalid(cfg_rvalid),
  .cfg_rready(cfg_rready)
);
`else
  assign sim_clock = clock;
`endif // FPGA_SIM

wire core_clock;
`ifdef CONFIG_DIFFTEST_CLOCKGATE
wire clock_enable;
DifftestClockGate gate(
  .CK(sim_clock),
  .E(clock_enable),
  .Q(core_clock)
);
`else
  assign core_clock = sim_clock;
`endif // CONFIG_DIFFTEST_CLOCKGATE

SimTop sim(
  .clock(core_clock),
  .reset(reset),
`ifdef FPGA_SIM
  .difftest_pcie_clock(difftest_pcie_clock),
  .difftest_ref_reset(reset),
  .difftest_hostCtrl_reset(host_ctrl_reset),
  .difftest_hostCtrl_diffEnable(host_ctrl_diff_enable),
  .difftest_hostCtrl_ilaTrigger(host_ctrl_ila_trigger),
  .difftest_hostCtrl_enableSquash(host_ctrl_enable_squash),
  .difftest_to_host_axis_tvalid(c2h_axi_tvalid),
  .difftest_to_host_axis_tready(c2h_axi_tready),
  .difftest_to_host_axis_tdata(c2h_axi_tdata),
  .difftest_to_host_axis_tkeep(c2h_axi_tkeep),
  .difftest_to_host_axis_tlast(c2h_axi_tlast),
  .difftest_from_host_axis_tvalid(h2c_axi_tvalid),
  .difftest_from_host_axis_tready(h2c_axi_tready),
  .difftest_from_host_axis_tdata(h2c_axi_tdata),
  .difftest_from_host_axis_tkeep(h2c_axi_tkeep),
  .difftest_from_host_axis_tlast(h2c_axi_tlast),
  .difftest_cfg_axilite_awvalid(cfg_awvalid),
  .difftest_cfg_axilite_awaddr(cfg_awaddr),
  .difftest_cfg_axilite_awready(cfg_awready),
  .difftest_cfg_axilite_wvalid(cfg_wvalid),
  .difftest_cfg_axilite_wdata(cfg_wdata),
  .difftest_cfg_axilite_wstrb(cfg_wstrb),
  .difftest_cfg_axilite_wready(cfg_wready),
  .difftest_cfg_axilite_bvalid(cfg_bvalid),
  .difftest_cfg_axilite_bresp(cfg_bresp),
  .difftest_cfg_axilite_bready(cfg_bready),
  .difftest_cfg_axilite_arvalid(cfg_arvalid),
  .difftest_cfg_axilite_araddr(cfg_araddr),
  .difftest_cfg_axilite_arready(cfg_arready),
  .difftest_cfg_axilite_rvalid(cfg_rvalid),
  .difftest_cfg_axilite_rdata(cfg_rdata),
  .difftest_cfg_axilite_rresp(cfg_rresp),
  .difftest_cfg_axilite_rready(cfg_rready),
`endif // FPGA_SIM
`ifdef CONFIG_DIFFTEST_CLOCKGATE
  .difftest_ref_clock(sim_clock),
  .difftest_clock_enable(clock_enable),
`endif // CONFIG_DIFFTEST_CLOCKGATE
  .difftest_logCtrl_begin(difftest_logCtrl_begin),
  .difftest_logCtrl_end(difftest_logCtrl_end),
  .difftest_logCtrl_level(difftest_logCtrl_level),
  .difftest_perfCtrl_clean(difftest_perfCtrl_clean),
  .difftest_perfCtrl_dump(difftest_perfCtrl_dump),
  .difftest_uart_out_valid(difftest_uart_out_valid),
  .difftest_uart_out_ch(difftest_uart_out_ch),
  .difftest_uart_in_valid(difftest_uart_in_valid),
  .difftest_uart_in_ch(difftest_uart_in_ch),
  .difftest_exit(difftest_exit),
  .difftest_step(difftest_step)
);

DifftestEndpoint difftest(
  .clock(sim_clock),
  .reset(reset),
`ifdef ENABLE_WORKLOAD_SWITCH
  .workload_switch(workload_switch),
`endif // ENABLE_WORKLOAD_SWITCH
`ifdef FPGA_SIM
  .difftest_hostCtrl_reset(host_ctrl_reset),
  .difftest_hostCtrl_diffEnable(host_ctrl_diff_enable),
  .difftest_hostCtrl_ilaTrigger(host_ctrl_ila_trigger),
  .difftest_hostCtrl_enableSquash(host_ctrl_enable_squash),
`endif // FPGA_SIM
  .difftest_logCtrl_begin(difftest_logCtrl_begin),
  .difftest_logCtrl_end(difftest_logCtrl_end),
  .difftest_logCtrl_level(difftest_logCtrl_level),
  .difftest_perfCtrl_clean(difftest_perfCtrl_clean),
  .difftest_perfCtrl_dump(difftest_perfCtrl_dump),
  .difftest_uart_out_valid(difftest_uart_out_valid),
  .difftest_uart_out_ch(difftest_uart_out_ch),
  .difftest_uart_in_valid(difftest_uart_in_valid),
  .difftest_uart_in_ch(difftest_uart_in_ch),
  .difftest_exit(difftest_exit),
  .difftest_step(difftest_step)
);

endmodule
