// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (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-0.51. 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.
//
// Authors:
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Florian Zaruba <zarubaf@iis.ee.ethz.ch>

`include "axi/assign.svh"

module tb_axi_to_axi_lite;

  parameter AW = 32;
  parameter DW = 32;
  parameter IW = 8;
  parameter UW = 8;

  localparam tCK = 1ns;
  localparam TA = tCK * 1/4;
  localparam TT = tCK * 3/4;

  localparam     MAX_READ_TXNS  = 32'd20;
  localparam     MAX_WRITE_TXNS = 32'd20;
  localparam bit AXI_ATOPS      = 1'b1;

  logic clk = 0;
  logic rst = 1;
  logic done = 0;

  AXI_LITE_DV #(
    .AXI_ADDR_WIDTH(AW),
    .AXI_DATA_WIDTH(DW)
  ) axi_lite_dv(clk);

  AXI_LITE #(
    .AXI_ADDR_WIDTH(AW),
    .AXI_DATA_WIDTH(DW)
  ) axi_lite();

  `AXI_LITE_ASSIGN(axi_lite_dv, axi_lite)

  AXI_BUS_DV #(
    .AXI_ADDR_WIDTH(AW),
    .AXI_DATA_WIDTH(DW),
    .AXI_ID_WIDTH(IW),
    .AXI_USER_WIDTH(UW)
  ) axi_dv(clk);

  AXI_BUS #(
    .AXI_ADDR_WIDTH(AW),
    .AXI_DATA_WIDTH(DW),
    .AXI_ID_WIDTH(IW),
    .AXI_USER_WIDTH(UW)
  ) axi();

  `AXI_ASSIGN(axi, axi_dv)

  axi_to_axi_lite_intf #(
    .AXI_ID_WIDTH       ( IW      ),
    .AXI_ADDR_WIDTH     ( AW      ),
    .AXI_DATA_WIDTH     ( DW      ),
    .AXI_USER_WIDTH     ( UW      ),
    .AXI_MAX_WRITE_TXNS ( 32'd10  ),
    .AXI_MAX_READ_TXNS  ( 32'd10  ),
    .FALL_THROUGH       ( 1'b1    )
  ) i_dut (
    .clk_i      ( clk      ),
    .rst_ni     ( rst      ),
    .testmode_i ( 1'b0     ),
    .slv        ( axi      ),
    .mst        ( axi_lite )
  );

  typedef axi_test::axi_rand_master #(
    // AXI interface parameters
    .AW ( AW ),
    .DW ( DW ),
    .IW ( IW ),
    .UW ( UW ),
    // Stimuli application and test time
    .TA ( TA ),
    .TT ( TT ),
    // Maximum number of read and write transactions in flight
    .MAX_READ_TXNS  ( MAX_READ_TXNS  ),
    .MAX_WRITE_TXNS ( MAX_WRITE_TXNS ),
    .AXI_ATOPS      ( AXI_ATOPS      )
  ) axi_rand_master_t;
  typedef axi_test::axi_lite_rand_slave #(.AW(AW), .DW(DW), .TA(TA), .TT(TT)) axi_lite_rand_slv_t;

  axi_lite_rand_slv_t axi_lite_drv = new(axi_lite_dv, "axi_lite_rand_slave");
  axi_rand_master_t   axi_drv      = new(axi_dv);

  initial begin
    #tCK;
    rst <= 0;
    #tCK;
    rst <= 1;
    #tCK;
    while (!done) begin
      clk <= 1;
      #(tCK/2);
      clk <= 0;
      #(tCK/2);
    end
  end

  initial begin
    axi_drv.add_memory_region(32'h0000_0000,
                                      32'h1000_0000,
                                      axi_pkg::NORMAL_NONCACHEABLE_NONBUFFERABLE);
    axi_drv.add_memory_region(32'h1000_0000,
                                      32'h2000_0000,
                                      axi_pkg::NORMAL_NONCACHEABLE_BUFFERABLE);
    axi_drv.add_memory_region(32'h3000_0000,
                                      32'h4000_0000,
                                      axi_pkg::WBACK_RWALLOCATE);
    axi_drv.reset();
    @(posedge rst);
    axi_drv.run(1000, 2000);

    repeat (4) @(posedge clk);
    done = 1'b1;
    $info("All AXI4+ATOP Bursts converted to AXI4-Lite");
    repeat (4) @(posedge clk);
    $stop();
  end

  initial begin
    axi_lite_drv.reset();
    @(posedge clk);

    axi_lite_drv.run();
  end

  initial begin : proc_count_lite_beats
    automatic longint aw_cnt = 0;
    automatic longint w_cnt  = 0;
    automatic longint b_cnt  = 0;
    automatic longint ar_cnt = 0;
    automatic longint r_cnt  = 0;
    @(posedge rst);
    while (!done) begin
      @(posedge clk);
      #TT;
      if (axi_lite.aw_valid && axi_lite.aw_ready) begin
        aw_cnt++;
      end
      if (axi_lite.w_valid && axi_lite.w_ready) begin
        w_cnt++;
      end
      if (axi_lite.b_valid && axi_lite.b_ready) begin
        b_cnt++;
      end
      if (axi_lite.ar_valid && axi_lite.ar_ready) begin
        ar_cnt++;
      end
      if (axi_lite.r_valid && axi_lite.r_ready) begin
        r_cnt++;
      end
    end
    assert (aw_cnt == w_cnt && w_cnt == b_cnt);
    assert (ar_cnt == r_cnt);
    $display("AXI4-Lite AW count: %0d", aw_cnt );
    $display("AXI4-Lite  W count: %0d", w_cnt  );
    $display("AXI4-Lite  B count: %0d", b_cnt  );
    $display("AXI4-Lite AR count: %0d", ar_cnt );
    $display("AXI4-Lite  R count: %0d", r_cnt  );
  end

endmodule