// (C) 2001-2025 Altera Corporation. All rights reserved.
// Your use of Altera Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Altera Program License Subscription 
// Agreement, Altera IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Altera and sold by 
// Altera or its authorized distributors.  Please refer to the applicable 
// agreement for further details.


module avmm_pacer
(
  input               clk_i,
  input               rstn_i,
  input        [23:0] avmm_addr_i,
  input               avmm_read_i,
  output logic [31:0] avmm_rdata_o,
  output logic        avmm_rdata_valid_o,
  input               avmm_write_i,
  input        [31:0] avmm_wdata_i,
  output logic        avmm_waitreq_o,

  output logic        slw_clk_o,
  output logic [23:0] slw_avmm_addr_o,
  output logic        slw_avmm_read_o,
  input        [31:0] slw_avmm_rdata_i,
  input               slw_avmm_rdata_valid_i,
  output logic        slw_avmm_write_o,
  output logic [31:0] slw_avmm_wdata_o,
  input               slw_avmm_waitreq_i
);


  typedef enum {IDLE, PRE_WAIT, WAIT_REQ} estates;
  estates state;
  logic slw_clk, mask, avmm_rdata_valid;

  always_comb avmm_rdata_o     = slw_avmm_rdata_i;
  always_comb slw_clk_o        = !slw_clk;
  always_comb slw_avmm_addr_o  = avmm_addr_i;
  always_comb slw_avmm_read_o  = avmm_read_i  && mask;
  always_comb slw_avmm_write_o = avmm_write_i && mask;
  always_comb slw_avmm_wdata_o = avmm_wdata_i;

  wire avmm_read_write = avmm_read_i || avmm_write_i;
  wire nxt_slw_clk     = state == IDLE && slw_clk ? avmm_read_write ^   mask : !slw_clk;


  always @(posedge clk_i)
  if (!rstn_i) begin
    slw_clk <= 1'b1;
    avmm_rdata_valid_o <= 1'b0;
  end else begin
    slw_clk            <= nxt_slw_clk;
    avmm_rdata_valid_o <= nxt_slw_clk && !slw_clk ? slw_avmm_rdata_valid_i : 1'b0;
  end


  always @(posedge clk_i) // or negedge rstn_i)
  if (!rstn_i) begin
    mask               <= 1'b1;
    state              <= IDLE;
    avmm_waitreq_o     <= 1'b1;
  end else begin

    case (state)
      IDLE : begin
        if (slw_clk) begin
          mask               <= avmm_read_write;
          state              <= avmm_read_write &&  mask ? PRE_WAIT : IDLE;
          avmm_waitreq_o     <= avmm_read_write &&  mask ? slw_avmm_waitreq_i : 1'b1;
        end else begin
          mask               <= 1'b1;
          state              <= IDLE;
          avmm_waitreq_o     <= 1'b1;
        end
      end

      PRE_WAIT : begin
        mask               <= avmm_waitreq_o || avmm_read_write;
        state              <= avmm_waitreq_o ? WAIT_REQ : IDLE;
        avmm_waitreq_o     <= 1'b1;
      end

      WAIT_REQ : begin
        mask               <= 1'b1;
        if (slw_clk) begin
          state              <= WAIT_REQ;
          avmm_waitreq_o     <= slw_avmm_waitreq_i;
        end else begin
          state              <= avmm_waitreq_o ? WAIT_REQ : IDLE;
          avmm_waitreq_o     <= 1'b1;
        end
      end

    endcase
  end

`ifdef DTK_SIM_ONLY
  assert property (@(posedge clk_i) $rose(slw_clk_o) |-> $stable((slw_avmm_read_o & slw_avmm_addr_o)));
  assert property (@(posedge clk_i) $rose(slw_clk_o) |-> $stable(slw_avmm_read_o) );
  assert property (@(posedge clk_i) $rose(avmm_rdata_valid_o) |=> !avmm_rdata_valid_o);
  assert property (@(posedge clk_i) $rose(slw_clk_o) |-> $stable(slw_avmm_write_o));
  assert property (@(posedge clk_i) $rose(slw_clk_o) |-> $stable((slw_avmm_read_o & slw_avmm_wdata_o)));
  assert property (@(posedge clk_i) $rose(slw_clk_o) |-> $stable((slw_avmm_read_o & slw_avmm_be_o))   );
`endif

endmodule



`ifdef DTK_SIM_ONLY
module avmm_pacer_tb ();

  logic        clk;
  logic        rstn;
  logic [20:0] avmm_addr;
  logic [ 3:0] avmm_be;
  logic        avmm_read;
  logic [31:0] avmm_rdata;
  logic        avmm_rdata_valid;
  logic        avmm_write;
  logic [31:0] avmm_wdata;
  logic        avmm_waitreq;
  logic        slw_clk;
  logic [20:0] slw_avmm_addr;
  logic [ 3:0] slw_avmm_be;
  logic        slw_avmm_read;
  logic [31:0] slw_avmm_rdata;
  logic        slw_avmm_rdata_valid;
  logic        slw_avmm_write;
  logic [31:0] slw_avmm_wdata;
  logic        slw_avmm_waitreq;

  logic [31:0] memory [0:4095];
  logic [31:0] exp_memory [0:4095];
  logic [31:0] random;
  integer i;

  initial begin
    clk = 1'b0;
    #2ns;
    forever begin
      #2ns;
      clk = !clk;
    end
  end 

  // Models the Host avmm interface
  initial begin
    rstn        <= 1'b0;
    avmm_addr   <= 21'h0;
    avmm_be     <= 4'hf;
    avmm_read   <= 1'b0;
    avmm_write  <= 1'b0;
    avmm_wdata  <= 32'hdeadbeef;

    for (i=0; i<4096; i=i+1) begin
      memory[i][31:16] = 16'hffff^i;
      memory[i][15: 0] =          i;
      exp_memory[i] = memory[i];
    end

    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
       @(posedge clk);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
      @(posedge clk); @(posedge clk);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
      @(posedge clk); @(posedge clk); @(posedge clk);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write({9'd0,random[31:20]}, {12'd0,random[19:0]});
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write({9'd0,random[31:20]}, {12'd0,random[19:0]});
      @(posedge clk);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write({9'd0,random[31:20]}, {12'd0,random[19:0]});
      @(posedge clk); @(posedge clk);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write({9'd0,random[31:20]}, {12'd0,random[19:0]});
      @(posedge clk); @(posedge clk); @(posedge clk);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write({9'd0,random[31:20]}, {12'd0,random[19:0]});
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write({9'd0,random[31:20]}, {12'd0,random[19:0]});
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write({9'd0,random[31:20]}, {12'd0,random[19:0]});
      @(posedge clk);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write({9'd0,random[31:20]}, {12'd0,random[19:0]});
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
      @(posedge clk);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
      @(posedge clk);
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write({9'd0,random[31:20]}, {12'd0,random[19:0]});
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (9) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write({9'd0,random[31:20]}, {12'd0,random[19:0]});
      @(posedge clk);
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
    end
    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10) @(posedge clk);
    rstn = 1;
    repeat (10) @(posedge clk);

    repeat (16384) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      if (random[0])
        read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
      else
        write({9'd0,random[31:20]}, {12'd0,random[19:1],1'b0});
      repeat ({$random}%8) @(posedge clk);
      @(posedge clk);
    end

    repeat (16384) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      if (random[0])
        read ({9'd0,random[31:20]}, exp_memory[random[31:20]]);
      else
        write({9'd0,random[31:20]}, {12'd0,random[19:1],1'b0});
      repeat ({$random}%32) @(posedge clk);
      repeat (5) @(posedge clk);
    end

    for (i=0; i<4096; i=i+1) begin
      random = $random; // 31:20 : addr, 19:1 : data, 0 : rnw
      write(i^21'h000456, random);
      repeat ({$random}%7) @(posedge clk);
    end

    for (i=0; i<4096; i=i+1) begin
      random = i^21'h000176;
      read (random, exp_memory[random]);
      repeat ({$random}%7) @(posedge clk);
    end

    for (i=0; i<4096; i=i+1) begin
      assert (exp_memory[i] == memory[i]) $display("memory data %h matches expectation %h", memory[i], exp_memory[i]);
                             else $error  ("memory data %h does not matches expectation %h", memory[i], exp_memory[i]);
 
    end

    repeat (10) @(posedge clk);
    rstn = 0;
    repeat (10000) @(posedge clk);

    $finish;
  end

  // watchdog
  initial begin
    fork
      begin
        #6000000ns;
        $error("%t: Simulation timed out!!", $time);
        $finish;
      end
      /*forever begin
        @(;
      end*/
    join
  end

  avmm_pacer avmm_pacer_inst (
    .clk_i                  (clk),
    .rstn_i                 (rstn),
    .avmm_addr_i            (avmm_addr),
    .avmm_be_i              (avmm_be),
    .avmm_read_i            (avmm_read),
    .avmm_rdata_o           (avmm_rdata),
    .avmm_rdata_valid_o     (avmm_rdata_valid),
    .avmm_write_i           (avmm_write),
    .avmm_wdata_i           (avmm_wdata),
    .avmm_waitreq_o         (avmm_waitreq),

    .slw_clk_o              (slw_clk),
    .slw_avmm_addr_o        (slw_avmm_addr),
    .slw_avmm_be_o          (slw_avmm_be),
    .slw_avmm_read_o        (slw_avmm_read),
    .slw_avmm_rdata_i       (slw_avmm_rdata),
    .slw_avmm_rdata_valid_i (slw_avmm_rdata_valid),
    .slw_avmm_write_o       (slw_avmm_write),
    .slw_avmm_wdata_o       (slw_avmm_wdata),
    .slw_avmm_waitreq_i     (slw_avmm_waitreq)
  );

  // Models the slow AVMM target interfaces

  integer rd_cnt, wr_cnt;
  integer push_cnt, pop_cnt;
  integer addr_push, addr_pop;
  // drives  the waitreq
  initial begin
    slw_avmm_waitreq     <= 1'b1;

    @(posedge rstn);

    forever begin
      while (!(slw_avmm_write || slw_avmm_read)) @(posedge slw_clk);
      slw_avmm_waitreq     <= #1 1'b1;
      if (rd_cnt>100 && wr_cnt>100)
        repeat ({$random}%8) @(posedge slw_clk);
      slw_avmm_waitreq     <= #1 1'b0;
      @(posedge slw_clk);
    end
  end

  logic [31:0] rdata, rdata_fifo [0:15];
  logic [20:0] raddr_fifo [0:15];
  // for read,  return rdata & rdata_valid
  // for write, update memory
  initial begin
    rd_cnt               <= 0;
    slw_avmm_rdata       <= 32'hdeadbeef;
    slw_avmm_rdata_valid <= 1'b0;
    @(posedge rstn);
    forever begin
      while (!(slw_avmm_read && !slw_avmm_waitreq)) @(posedge slw_clk);
      //while (slw_avmm_waitreq) @(posedge slw_clk);
      //rdata = memory[raddr_fifo[]%4096];
      rdata = memory[slw_avmm_addr%4096];
      if (rd_cnt > 100)
        repeat ({$random}%8) @(posedge slw_clk);
      slw_avmm_rdata       <= #1 rdata;
      slw_avmm_rdata_valid <= #1 1'b1;
      @(posedge slw_clk);
      slw_avmm_rdata       <= #1 32'hdeadbeef;
      slw_avmm_rdata_valid <= #1 1'b0;
      rd_cnt               <= rd_cnt + 1;
    end
  end

  initial begin
    wr_cnt               <= 0;
    @(posedge rstn);
    forever begin
      while (!slw_avmm_write)  @(posedge slw_clk);
      while (slw_avmm_waitreq) @(posedge slw_clk);
      memory[slw_avmm_addr%4096] <= slw_avmm_wdata;
      @(posedge slw_clk);
      wr_cnt               <= wr_cnt + 1;
    end
  end

  always @(posedge clk) begin
    if (!rstn) begin
      push_cnt             <= 0;
      pop_cnt              <= 0;
    end else begin
      if (avmm_rdata_valid) begin
        assert (avmm_rdata == rdata_fifo[pop_cnt]) $display("read back data %h matches expectation %h", avmm_rdata, rdata_fifo[pop_cnt]);
                                              else $error  ("read back data %h does not matches expectation %h", avmm_rdata, rdata_fifo[pop_cnt]);
        pop_cnt <= (pop_cnt+1)%16;
      end
    end
  end

  task write (input [20:0] addr, input [31:0] data);
    begin
      $display("%t: write(%h, %h)", $time, addr, data);
      exp_memory[addr%4096] <= data;
      avmm_addr  <= #1 addr;
      avmm_wdata <= #1 data;
      avmm_write <= #1 1'b1;
      @(posedge clk);
      while (avmm_waitreq) @(posedge clk);
      avmm_addr  <= #1 21'h1dbeef;
      avmm_wdata <= #1 32'hdeadbeef;
      avmm_write <= #1 1'b0;
    end
  endtask

  task read (input [20:0] addr, input [31:0] data);
    begin
      $display("%t: read(%h, %h)", $time, addr, data);
      avmm_addr  <= #1 addr;
      avmm_read  <= #1 1'b1;
      rdata_fifo[push_cnt] <= data; push_cnt <= (push_cnt+1) % 16;
      @(posedge clk);
      while (avmm_waitreq) @(posedge clk);
      avmm_addr  <= #1 21'h1dbeef;
      avmm_read  <= #1 1'b0;
      while (!avmm_rdata_valid) @(posedge clk);
//      assert (avmm_rdata == data) $display("read back data %h matches expectation %h", avmm_rdata, data);
//                             else $error  ("read back data %h does not matches expectation %h", avmm_rdata, data);
    end
  endtask

endmodule
`endif
`ifdef QUESTA_INTEL_OEM
`pragma questa_oem_00 "PNVV7/bs9iBHIRlA5ZBsyN/GPZ0TXi6qn2Dh2M7pAeu7gJ2ZBA7h3JGkvjXkuC5UIXo/z5gQmp1UZ7sTAaTT/jY49W2SxnQK1Yi3m9AqA7DQvdVNQSIP0StiA6cd/Bmb1xOyX01r0Q1kSbh1Xg00173NRrSBh6I2laKRBqIVNUuwBG6+CnWpVjqQa+TAR5sCjTNP+I8jQLtXq7doP9+lP7YXqXhRmwId5c9JhVEr4VmrNa2C0oToojEMf4kBvCcILFzBrvhRYzEXN9ucnQMKQxc7kAvRoNhxbLLZ9MdE1fNCRh/TOtre1gODDJZg5riA/a/fue1hw9081YjkyQaV7Af8FiShgb4nZdO92T84K6z3hHrCud35YafgoGFNQorTbsyjw8JlclI+asooMALFGgzRpuY/Pv/HbZ6/IwOpw6G++8Ia710IF53aTKkP9tYolJt81XSEIgc+JoNpH0QT3ya1yIpB/9yyRhrYnG58oBLiuarizVY5xikpQQFXpiX+WkyG10cj5VLVld0zahxDhb/pvAjwkN6MaOQyFP6HgfcjDt+wdEmUdb6r5s0qpHqMrUauNyqkaMqkRBSO+UQjybb+qyXxDe4JOvSi23yr9+NyiMobtkuG5hI0A+lekuc0ek0XzADuvoP3ePQ4f8/Mktl2QZstBSVgsx/5liz+GbNACMmMdlI+fV7BRS48tZBCm6Nzuig4022F0VzWFJIkCqyDYXEImSWQX1oSnoWFwr0KazskksOojb9BKYZH1yWnzw9CpSutbZMJU8N6YEmeaZSKB5BEG/NEWDy2CIwPA4ir7tyzRsn+nA3MJg5EdFrQ5QzbIMD7TB5OatCBJWLljtghNd9xwXgM6mI6CZQQpWGmGcGy/siu8lgC/Qnp3ouND4PejUiZ053BwAd8j/UYWYXgmoB8ACVpY8vRfi1x/FOdEs0mEKWpns40dw9/bJcUCXDvpvhtIbhMjOxgpOZv2+14xIeiktFeKKSjPMO2CcccNWvT9Q2z07/18eX1oUCa"
`endif