// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2024 Alex Solomatnikov
// SPDX-License-Identifier: CC0-1.0

package z_pkg;
  localparam int OPCODEW = 3;
  localparam int CIDW = 4;
  localparam int SIDW = 10;
  localparam int CTAGW = 7;
  localparam int STAGW = 9;
  localparam int STATEW = 3;
  localparam int ADDRW = 37;
  localparam int DATAW = 256;
  localparam int MASKW = DATAW / 8;
  localparam int SIZEW = 4;
  typedef enum logic [OPCODEW-1:0] {
    A = 3'h0,
    B = 3'h1
  } a_op_t;
  typedef enum logic [OPCODEW-1:0] {
    C = 3'h0,
    D = 3'h1
  } b_op_t;
  typedef enum logic [OPCODEW-1:0] {
    E = 3'h0,
    F = 3'h1
  } c_op_t;
  typedef enum logic [OPCODEW-1:0] {
    G = 3'h0,
    H = 3'h1
  } d_op_t;
  typedef logic [CIDW-1:0] cid_t;
  typedef logic [SIDW-1:0] sid_t;
  typedef logic [CTAGW-1:0] ctag_t;
  typedef logic [STAGW-1:0] stag_t;
  typedef logic [STATEW-1:0] state_t;
  typedef logic [ADDRW-1:0] address_t;
  typedef logic [SIZEW-1:0] size_t;
  typedef logic [DATAW-1:0] data_t;
  typedef logic [MASKW-1:0] mask_t;
  typedef struct packed {
    cid_t cid;
    a_op_t opcode;
    address_t address;
  } x1_ch_t;
  typedef struct packed {
    cid_t cid;
    b_op_t opcode;
    address_t address;
  } x2_ch_t;
  typedef struct packed {
    cid_t cid;
    sid_t sid;
    ctag_t ctag;
    stag_t stag;
    c_op_t opcode;
    state_t state2;
    state_t state3;
    address_t address;
    logic f4;
    size_t size;
    logic f2;
  } x3_ch_t;
  typedef struct packed {
    cid_t cid;
    sid_t sid;
    ctag_t ctag;
    stag_t stag;
    d_op_t opcode;
    state_t state1;
    logic f4;
    logic f1;
    size_t size;
    logic f3;
  } x4_ch_t;
  typedef struct packed {
    cid_t cid;
    ctag_t ctag;
    stag_t stag;
    d_op_t opcode;
    logic f1;
    logic f3;
  } x5_ch_t;
  typedef struct packed {
    logic last;
    logic corrupt;
  } x6_ch_t;
  typedef struct packed {
    sid_t sid;
    stag_t stag;
  } x7_ch_t;
  typedef enum {
    CH_X1,
    CH_Y1,
    CH_Y2,
    CH_X2,
    CH_X3,
    CH_Y3,
    CH_X4,
    CH_X5,
    CH_X6,
    CH_X7
  } channel_t;
  parameter channel_t CH_ALL[CH_X7+1] = '{
      CH_X1,
      CH_Y1,
      CH_Y2,
      CH_X2,
      CH_X3,
      CH_Y3,
      CH_X4,
      CH_X5,
      CH_X6,
      CH_X7
  };
  typedef enum {
    TXN_0,
    TXN_1,
    TXN_2,
    TXN_3,
    TXN_4,
    TXN_5,
    TXN_6,
    TXN_7
  } txn_type_t;
  function txn_type_t txn_type(bit [2:0] opcode, channel_t ch);
    case (opcode)
      3'd0: begin
        case (ch)
          CH_X1: txn_type = TXN_1;
          CH_X2: txn_type = TXN_7;
          CH_X3: txn_type = TXN_7;
          CH_Y3: txn_type = TXN_7;
          CH_X4: txn_type = TXN_2;
          CH_X6: txn_type = TXN_2;
          default: txn_type = TXN_0;
        endcase
      end
      3'd1: begin
        case (ch)
          CH_Y1: txn_type = TXN_3;
          CH_X2: txn_type = TXN_4;
          CH_X3: txn_type = TXN_5;
          CH_Y3: txn_type = TXN_5;
          CH_X5: txn_type = TXN_6;
          default: txn_type = TXN_0;
        endcase
      end
      3'd2: begin
        case (ch)
          CH_Y1: txn_type = TXN_7;
          CH_X2: txn_type = TXN_7;
          CH_X3: txn_type = TXN_7;
          CH_Y3: txn_type = TXN_7;
          CH_X4: txn_type = TXN_7;
          CH_X6: txn_type = TXN_7;
          default: txn_type = TXN_0;
        endcase
      end
      3'd3: begin
        case (ch)
          CH_Y1: txn_type = TXN_7;
          CH_X2: txn_type = TXN_7;
          CH_X3: txn_type = TXN_7;
          CH_Y3: txn_type = TXN_7;
          CH_X5: txn_type = TXN_7;
          default: txn_type = TXN_0;
        endcase
      end
      3'd4: begin
        case (ch)
          CH_X1: txn_type = TXN_7;
          CH_X2: txn_type = TXN_7;
          CH_X3: txn_type = TXN_7;
          CH_Y3: txn_type = TXN_7;
          CH_X4: txn_type = TXN_7;
          CH_X6: txn_type = TXN_7;
          CH_X7: txn_type = TXN_7;
          default: txn_type = TXN_0;
        endcase
      end
      3'd5: begin
        case (ch)
          CH_Y1: txn_type = TXN_7;
          CH_X2: txn_type = TXN_7;
          CH_X3: txn_type = TXN_7;
          CH_Y3: txn_type = TXN_7;
          CH_X5: txn_type = TXN_7;
          default: txn_type = TXN_0;
        endcase
      end
      3'd6: begin
        case (ch)
          CH_X3: txn_type = TXN_7;
          CH_Y3: txn_type = TXN_7;
          CH_X5: txn_type = TXN_7;
          default: txn_type = TXN_0;
        endcase
      end
      3'd7: begin
        case (ch)
          CH_Y2: txn_type = TXN_7;
          default: txn_type = TXN_0;
        endcase
      end
    endcase
  endfunction
endpackage
interface z_if;
  import z_pkg::*;
  logic x1_valid;
  x1_ch_t x1;
  logic x2_valid;
  x2_ch_t x2;
  logic x3_valid;
  x3_ch_t x3;
  logic x4_valid;
  x4_ch_t x4;
  logic x5_valid;
  x5_ch_t x5;
  logic x6_valid;
  x6_ch_t x6;
  data_t x6_data;
  logic x7_valid;
  x7_ch_t x7;
  function automatic logic x2_trig();
    return x2_valid;
  endfunction
  function automatic logic x4_trig();
    return x4_valid;
  endfunction
  function automatic logic x5_trig();
    return x5_valid;
  endfunction
  function automatic logic x6_trig();
    return x6_valid;
  endfunction
  modport sender(
      output x1_valid,
      output x1,
      input x2_valid,
      input x2,
      output x3_valid,
      output x3,
      input x4_valid,
      input x4,
      input x5_valid,
      input x5,
      input x6_valid,
      input x6,
      input x6_data,
      output x7_valid,
      output x7,
      import x2_trig,
      import x4_trig,
      import x5_trig,
      import x6_trig
  );
  modport receiver(
      input x1_valid,
      input x1,
      output x2_valid,
      output x2,
      input x3_valid,
      input x3,
      output x4_valid,
      output x4,
      output x5_valid,
      output x5,
      output x6_valid,
      output x6,
      output x6_data,
      input x7_valid,
      input x7,
      import x2_trig,
      import x4_trig,
      import x5_trig,
      import x6_trig
  );
endinterface
class z_txn_class;
  rand z_pkg::txn_type_t req_txn_type;
  rand z_pkg::cid_t cid;
  rand z_pkg::sid_t sid;
  rand z_pkg::ctag_t ctag;
  rand z_pkg::stag_t stag;
  rand z_pkg::size_t size;
  rand z_pkg::address_t address;
  rand z_pkg::state_t state1;
  rand z_pkg::state_t state2;
  rand z_pkg::state_t state3;
  rand logic f1;
  rand logic f2;
  rand logic f3;
  rand logic f4;
  z_pkg::data_t data[];
  z_pkg::mask_t mask[];
  bit corrupt[];
  logic [2:0] req_opcode;
endclass

module z_bfm_sender
  import z_pkg::*;
(
    input logic clk,
    input logic reset_l,
    z_if.sender z_if_sender
);
  channel_t ch;
  typedef z_txn_class z_txn_q_t[$];
  z_txn_q_t z_txn_qs[ch.num()];
  z_txn_class z_txn[ch.num()];
  always @(posedge clk or negedge reset_l) begin
    if (!reset_l) begin
      z_if_sender.x1_valid <= '0;
      z_if_sender.x3_valid <= '0;
      z_if_sender.x7_valid <= '0;
    end
    else begin
      foreach (CH_ALL[i]) begin
        case (CH_ALL[i])
          CH_X1: begin
            if (z_txn_qs[i].size() > 0) begin
              z_txn[i] = z_txn_qs[i].pop_front();
              z_if_sender.x1_valid <= '1;
              z_if_sender.x1.cid <= z_txn[i].cid;
              z_if_sender.x1.opcode <= a_op_t'(z_txn[i].req_opcode);
              z_if_sender.x1.address <= z_txn[i].address;
            end
          end
          CH_X3: begin
            if (z_txn_qs[i].size() > 0) begin
              z_txn[i] = z_txn_qs[i].pop_front();
              z_if_sender.x3_valid <= '1;
              z_if_sender.x3.cid <= z_txn[i].cid;
              z_if_sender.x3.sid <= z_txn[i].sid;
              z_if_sender.x3.ctag <= z_txn[i].ctag;
              z_if_sender.x3.stag <= z_txn[i].stag;
              z_if_sender.x3.opcode <= c_op_t'(z_txn[i].req_opcode);
              z_if_sender.x3.state2 <= z_txn[i].state2;
              z_if_sender.x3.state3 <= z_txn[i].state3;
              z_if_sender.x3.address <= z_txn[i].address;
              z_if_sender.x3.f4 <= z_txn[i].f4;
              z_if_sender.x3.size <= z_txn[i].size;
              z_if_sender.x3.f2 <= z_txn[i].f2;
            end
          end
          CH_X7: begin
            if (z_txn_qs[i].size() > 0) begin
              z_txn[i] = z_txn_qs[i].pop_front();
              z_if_sender.x7.sid <= z_txn[i].sid;
              z_if_sender.x7.stag <= z_txn[i].stag;
            end
          end
          CH_X2: begin
            if (z_if_sender.x2_trig()) begin
              z_txn[i] = new;
              z_txn[i].req_txn_type = txn_type(z_if_sender.x2.opcode, ch);
              z_txn[i].cid = z_if_sender.x2.cid;
              z_txn[i].address = z_if_sender.x2.address;
              z_txn_qs[i].push_back(z_txn[i]);
            end
          end
          CH_X4: begin
            if (z_if_sender.x4_trig()) begin
              z_txn[i] = new;
              z_txn[i].req_txn_type = txn_type(z_if_sender.x4.opcode, ch);
              z_txn[i].cid = z_if_sender.x4.cid;
              z_txn[i].sid = z_if_sender.x4.sid;
              z_txn[i].ctag = z_if_sender.x4.ctag;
              z_txn[i].stag = z_if_sender.x4.stag;
              z_txn[i].state1 = z_if_sender.x4.state1;
              z_txn[i].f1 = z_if_sender.x4.f1;
              z_txn[i].f4 = z_if_sender.x4.f4;
              z_txn[i].size = z_if_sender.x4.size;
              z_txn[i].f3 = z_if_sender.x4.f3;
              z_txn_qs[i].push_back(z_txn[i]);
            end
          end
          CH_X5: begin
            if (z_if_sender.x5_trig()) begin
              z_txn[i] = new;
              z_txn[i].req_txn_type = txn_type(z_if_sender.x5.opcode, ch);
              z_txn[i].cid = z_if_sender.x5.cid;
              z_txn[i].ctag = z_if_sender.x5.ctag;
              z_txn[i].f1 = z_if_sender.x5.f1;
              z_txn_qs[i].push_back(z_txn[i]);
            end
          end
          CH_X6: begin
            if (z_if_sender.x6_trig()) begin
              z_txn[i] = new;
              z_txn[i].data = new[1];
              z_txn[i].corrupt = new[1];
              z_txn[i].data[0] = z_if_sender.x6_data;
              z_txn[i].corrupt[0] = z_if_sender.x6.corrupt;
              z_txn_qs[i].push_back(z_txn[i]);
            end
          end
        endcase
      end
    end
  end
endmodule
module test_core_wrapper (
    input logic clk,
    input logic reset_l,
    z_if.sender z,
    mmio_z
);
  z_bfm_sender mem_z_bfm (
      .z_if_sender(z),
      .*
  );
  z_bfm_sender mmio_z_bfm (
      .z_if_sender(mmio_z),
      .*
  );
endmodule
module t (
    input clk,
    input reset_l
);
  z_if z (), mmio_z ();
  test_core_wrapper tile (
      .z(z.sender),
      .mmio_z(mmio_z.sender),
      .*
  );
endmodule
