// 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:
// - Thomas Benz <tbenz@iis.ee.ethz.ch>
//  - Paul Scheffler <paulsc@iis.ee.ethz.ch>
// - Tim Fischer <fischeti@iis.ee.ethz.ch>

/// Non-synthesizable module comparing two AXI channels of the same type
module axi_chan_compare #(
    /// Ignore ID field if it was remapped
    parameter bit  IgnoreId  = 1'b0,
    /// Allow reordered responses of different IDs,
    /// not compatible with `IgnoreId`
    parameter bit  AllowReordering = 1'b0,
    /// AXI ID Width
    parameter int unsigned IdWidth = 1,
    parameter type aw_chan_t = logic,
    parameter type w_chan_t  = logic,
    parameter type b_chan_t  = logic,
    parameter type ar_chan_t = logic,
    parameter type r_chan_t  = logic,
    parameter type req_t     = logic,
    parameter type resp_t    = logic
)(
    input logic  clk_a_i,
    input logic  clk_b_i,
    input req_t  axi_a_req,
    input resp_t axi_a_res,
    input req_t  axi_b_req,
    input resp_t axi_b_res
);

    function automatic void print_aw (
        input aw_chan_t aw_expected,
        input aw_chan_t aw_received
    );
        // verilog_lint: waive-start line-length
        $display("AW      | expected                                                         | received                                                         ");
        $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------");
        $display("id:     | %64d | %64d", aw_expected.id, aw_received.id);
        $display("addr:   | %64x | %64x", aw_expected.addr, aw_received.addr);
        $display("len:    | %64d | %64d", aw_expected.len, aw_received.len);
        $display("size:   | %64d | %64d", aw_expected.size, aw_received.size);
        $display("burst:  | %64d | %64d", aw_expected.burst, aw_received.burst);
        $display("lock:   | %64d | %64d", aw_expected.lock, aw_received.lock);
        $display("cache:  | %64d | %64d", aw_expected.cache, aw_received.cache);
        $display("prot:   | %64d | %64d", aw_expected.prot, aw_received.prot);
        $display("qos:    | %64d | %64d", aw_expected.qos, aw_received.qos);
        $display("region: | %64d | %64d", aw_expected.region, aw_received.region);
        $display("user:   | %64d | %64d", aw_expected.user, aw_received.user);
        $display("atop:   | %64d | %64d", aw_expected.atop, aw_received.atop);
        $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------");
        // verilog_lint: waive-stop line-length
    endfunction

    function automatic void print_ar (
        input ar_chan_t ar_expected,
        input ar_chan_t ar_received
    );
        // verilog_lint: waive-start line-length
        $display("AR      | expected                                                         | received                                                         ");
        $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------");
        $display("id:     | %64d | %64d", ar_expected.id, ar_received.id);
        $display("addr:   | %64x | %64x", ar_expected.addr, ar_received.addr);
        $display("len:    | %64d | %64d", ar_expected.len, ar_received.len);
        $display("size:   | %64d | %64d", ar_expected.size, ar_received.size);
        $display("burst:  | %64d | %64d", ar_expected.burst, ar_received.burst);
        $display("lock:   | %64d | %64d", ar_expected.lock, ar_received.lock);
        $display("cache:  | %64d | %64d", ar_expected.cache, ar_received.cache);
        $display("prot:   | %64d | %64d", ar_expected.prot, ar_received.prot);
        $display("qos:    | %64d | %64d", ar_expected.qos, ar_received.qos);
        $display("region: | %64d | %64d", ar_expected.region, ar_received.region);
        $display("user:   | %64d | %64d", ar_expected.user, ar_received.user);
        $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------");
        // verilog_lint: waive-stop line-length
    endfunction

    function automatic void print_w (
        input w_chan_t w_expected,
        input w_chan_t w_received
    );
        // verilog_lint: waive-start line-length
        $display("W       | expected                                                         | received                                                         ");
        $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------");
        $display("data:   | %64x | %64x", w_expected.data, w_received.data);
        $display("strb:   | %64d | %64d", w_expected.strb, w_received.strb);
        $display("last:   | %64d | %64d", w_expected.last, w_received.last);
        $display("user:   | %64d | %64d", w_expected.user, w_received.user);
        $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------");
        // verilog_lint: waive-stop line-length
    endfunction

    function automatic void print_b (
        input b_chan_t b_expected,
        input b_chan_t b_received
    );
        // verilog_lint: waive-start line-length
        $display("B       | expected                                                         | received                                                         ");
        $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------");
        $display("id:     | %64d | %64d", b_expected.id, b_received.id);
        $display("resp:   | %64d | %64d", b_expected.resp, b_received.resp);
        $display("user:   | %64d | %64d", b_expected.user, b_received.user);
        $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------");
        // verilog_lint: waive-stop line-length
    endfunction

    function automatic void print_r (
        input r_chan_t r_expected,
        input r_chan_t r_received
    );
        // verilog_lint: waive-start line-length
        $display("R       | expected                                                         | received                                                         ");
        $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------");
        $display("id:     | %64d | %64d", r_expected.id, r_received.id);
        $display("data:   | %64x | %64x", r_expected.data, r_received.data);
        $display("resp:   | %64d | %64d", r_expected.resp, r_received.resp);
        $display("last:   | %64d | %64d", r_expected.last, r_received.last);
        $display("user:   | %64d | %64d", r_expected.user, r_received.user);
        $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------");
        // verilog_lint: waive-stop line-length
    endfunction

    localparam NumIds = (AllowReordering)? 2**IdWidth : 1;

    // queues
    aw_chan_t aw_queue [NumIds-1:0][$];
    w_chan_t  w_queue              [$];
    b_chan_t  b_queue  [NumIds-1:0][$];
    ar_chan_t ar_queue [NumIds-1:0][$];
    r_chan_t  r_queue  [NumIds-1:0][$];

    // requests generated at axi A: enqueue elements
    always_ff @(posedge clk_a_i) begin : proc_enqueue_a
        // aw
        if (axi_a_req.aw_valid & axi_a_res.aw_ready)
            if (AllowReordering) aw_queue[axi_a_req.aw.id].push_back(axi_a_req.aw);
            else aw_queue[0].push_back(axi_a_req.aw);
        // w
        if (axi_a_req.w_valid & axi_a_res.w_ready)
            w_queue.push_back(axi_a_req.w);
        // ar
        if (axi_a_req.ar_valid & axi_a_res.ar_ready)
            if (AllowReordering) ar_queue[axi_a_req.ar.id].push_back(axi_a_req.ar);
            else ar_queue[0].push_back(axi_a_req.ar);
    end

    // responses generated at axi B: enqueue elements
    always_ff @(posedge clk_b_i) begin : proc_enqueue_b
        // b
        if (axi_b_res.b_valid & axi_b_req.b_ready)
            if (AllowReordering) b_queue[axi_b_res.b.id].push_back(axi_b_res.b);
            else b_queue[0].push_back(axi_b_res.b);
        // r
        if (axi_b_res.r_valid & axi_b_req.r_ready)
            if (AllowReordering) r_queue[axi_b_res.r.id].push_back(axi_b_res.r);
            else r_queue[0].push_back(axi_b_res.r);
    end

    // requests arriving at axi B from A: dequeue elements and check
    always_ff @(posedge clk_b_i) begin : proc_dequeue_and_check_b
        // aw
        if (axi_b_req.aw_valid & axi_b_res.aw_ready) begin
            automatic aw_chan_t aw_exp, aw_recv;
            if (AllowReordering) begin
                if (aw_queue[axi_b_req.aw.id].size() == 0) $error("AW queue is empty!");
                aw_exp = aw_queue[axi_b_req.aw.id].pop_front(); // verilog_lint: waive always-ff-non-blocking
            end else begin
                if (aw_queue[0].size() == 0) $error("AW queue is empty!");
                aw_exp = aw_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking
            end
            aw_recv = axi_b_req.aw;
            if (IgnoreId) begin
                aw_exp.id = 'X;
                aw_recv.id = 'X;
            end
            if (aw_exp !== aw_recv) begin
                $error("AW mismatch!");
                print_aw(aw_exp, aw_recv);
            end
        end
        // w
        if (axi_b_req.w_valid & axi_b_res.w_ready) begin
            automatic w_chan_t w_exp, w_recv;
            if (w_queue.size() == 0) $error("W queue is empty!");
            w_exp = w_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking
            w_recv = axi_b_req.w;
            if (w_exp !== w_recv) begin
                $error("W mismatch!");
                print_w(w_exp, w_recv);
            end
        end
        // ar
        if (axi_b_req.ar_valid & axi_b_res.ar_ready) begin

            if (IgnoreId) begin
                ar_exp.id = 'X;
                ar_recv.id = 'X;
            end
            if (ar_exp !== ar_recv) begin
                $error("AR mismatch!");
                print_ar(ar_exp, ar_recv);
            end
        end
    end

    // responses arriving at axi A from B: dequeue elements and check
    always_ff @(posedge clk_a_i) begin : proc_dequeue_and_check_a
        // b
        if (axi_a_res.b_valid & axi_a_req.b_ready) begin
            automatic b_chan_t b_exp, b_recv;
            if (AllowReordering) begin
                if (b_queue[axi_a_res.b.id].size() == 0) $error("B queue is empty!");
                b_exp = b_queue[axi_a_res.b.id].pop_front(); // verilog_lint: waive always-ff-non-blocking
            end else begin
                if (b_queue[0].size() == 0) $error("B queue is empty!");
                b_exp = b_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking
            end
            b_recv = axi_a_res.b;
            if (IgnoreId) begin
                b_exp.id = 'X;
                b_recv.id = 'X;
            end
            if (b_exp !== b_recv) begin
                $error("B mismatch!");
                print_b(b_exp, b_recv);
            end
        end
        // r
        if (axi_a_res.r_valid & axi_a_req.r_ready) begin
            automatic r_chan_t r_exp, r_recv;
            if (AllowReordering) begin
                if (r_queue[axi_a_res.r.id].size() == 0) $error("R queue is empty!");
                r_exp = r_queue[axi_a_res.r.id].pop_front(); // verilog_lint: waive always-ff-non-blocking
            end else begin
                if (r_queue[0].size() == 0) $error("R queue is empty!");
                r_exp = r_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking
            end
            r_recv = axi_a_res.r;
            if (IgnoreId) begin
                r_exp.id = 'X;
                r_recv.id = 'X;
            end
            if (r_exp !== r_recv) begin
                $error("R mismatch!");
                print_r(r_exp, r_recv);
            end
        end
    end

endmodule : axi_chan_compare

            automatic ar_chan_t ar_exp, ar_recv;
            if (AllowReordering) begin
                if (ar_queue[axi_b_req.ar.id].size() == 0) $error("AR queue is empty!");
                ar_exp = ar_queue[axi_b_req.ar.id].pop_front(); // verilog_lint: waive always-ff-non-blocking
            end else begin


// Copyright 2019 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:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>

// AXI ID Prepend: This module prepends/strips the MSB from the AXI IDs.
// Constraints enforced through assertions: ID width of slave and master port

module axi_id_prepend #(
  parameter int unsigned NoBus             = 1,     // Can take multiple axi busses
  parameter int unsigned AxiIdWidthSlvPort = 4,     // AXI ID Width of the Slave Ports
  parameter int unsigned AxiIdWidthMstPort = 6,     // AXI ID Width of the Master Ports
  parameter type         slv_aw_chan_t     = logic, // AW Channel Type for slv port
  parameter type         slv_w_chan_t      = logic, //  W Channel Type for slv port
  parameter type         slv_b_chan_t      = logic, //  B Channel Type for slv port
  parameter type         slv_ar_chan_t     = logic, // AR Channel Type for slv port
  parameter type         slv_r_chan_t      = logic, //  R Channel Type for slv port
  parameter type         mst_aw_chan_t     = logic, // AW Channel Type for mst port
  parameter type         mst_w_chan_t      = logic, //  W Channel Type for mst port
  parameter type         mst_b_chan_t      = logic, //  B Channel Type for mst port
  parameter type         mst_ar_chan_t     = logic, // AR Channel Type for mst port
  parameter type         mst_r_chan_t      = logic, //  R Channel Type for mst port
  // DEPENDENT PARAMETER DO NOT OVERWRITE!
  parameter int unsigned PreIdWidth        = AxiIdWidthMstPort - AxiIdWidthSlvPort
) (
  input  logic [PreIdWidth-1:0] pre_id_i, // ID to be prepended
  // slave port (input), connect master modules here
  // AW channel
  input  slv_aw_chan_t [NoBus-1:0] slv_aw_chans_i,
  input  logic         [NoBus-1:0] slv_aw_valids_i,
  output logic         [NoBus-1:0] slv_aw_readies_o,
  //  W channel
  input  slv_w_chan_t  [NoBus-1:0] slv_w_chans_i,
  input  logic         [NoBus-1:0] slv_w_valids_i,
  output logic         [NoBus-1:0] slv_w_readies_o,
  //  B channel
  output slv_b_chan_t  [NoBus-1:0] slv_b_chans_o,
  output logic         [NoBus-1:0] slv_b_valids_o,
  input  logic         [NoBus-1:0] slv_b_readies_i,
  // AR channel
  input  slv_ar_chan_t [NoBus-1:0] slv_ar_chans_i,
  input  logic         [NoBus-1:0] slv_ar_valids_i,
  output logic         [NoBus-1:0] slv_ar_readies_o,
  //  R channel
  output slv_r_chan_t  [NoBus-1:0] slv_r_chans_o,
  output logic         [NoBus-1:0] slv_r_valids_o,
  input  logic         [NoBus-1:0] slv_r_readies_i,
  // master ports (output), connect slave modules here
  // AW channel
  output mst_aw_chan_t [NoBus-1:0] mst_aw_chans_o,
  output logic         [NoBus-1:0] mst_aw_valids_o,
  input  logic         [NoBus-1:0] mst_aw_readies_i,
  //  W channel
  output mst_w_chan_t  [NoBus-1:0] mst_w_chans_o,
  output logic         [NoBus-1:0] mst_w_valids_o,
  input  logic         [NoBus-1:0] mst_w_readies_i,
  //  B channel
  input  mst_b_chan_t  [NoBus-1:0] mst_b_chans_i,
  input  logic         [NoBus-1:0] mst_b_valids_i,
  output logic         [NoBus-1:0] mst_b_readies_o,
  // AR channel
  output mst_ar_chan_t [NoBus-1:0] mst_ar_chans_o,
  output logic         [NoBus-1:0] mst_ar_valids_o,
  input  logic         [NoBus-1:0] mst_ar_readies_i,
  //  R channel
  input  mst_r_chan_t  [NoBus-1:0] mst_r_chans_i,
  input  logic         [NoBus-1:0] mst_r_valids_i,
  output logic         [NoBus-1:0] mst_r_readies_o
);

  // prepend the ID
  for (genvar i = 0; i < NoBus; i++) begin : gen_id_prepend
    if (PreIdWidth == 0) begin : gen_no_prepend
      assign mst_aw_chans_o[i] = slv_aw_chans_i[i];
      assign mst_ar_chans_o[i] = slv_ar_chans_i[i];
    end else begin : gen_prepend
      always_comb begin
        mst_aw_chans_o[i] = mst_aw_chan_t'(slv_aw_chans_i[i]);
        mst_ar_chans_o[i] = mst_ar_chan_t'(slv_ar_chans_i[i]);

  // assign the handshaking's and w channel
  assign mst_w_chans_o    = slv_w_chans_i;
  assign mst_aw_valids_o  = slv_aw_valids_i;
  assign slv_aw_readies_o = mst_aw_readies_i;
  assign mst_w_valids_o   = slv_w_valids_i;
  assign slv_w_readies_o  = mst_w_readies_i;
  assign slv_b_valids_o   = mst_b_valids_i;
  assign mst_b_readies_o  = slv_b_readies_i;
  assign mst_ar_valids_o  = slv_ar_valids_i;
  assign slv_ar_readies_o = mst_ar_readies_i;
  assign slv_r_valids_o   = mst_r_valids_i;
  assign mst_r_readies_o  = slv_r_readies_i;

// pragma translate_off
`ifndef VERILATOR
  initial begin : p_assert
    assert(NoBus > 0)
      else $fatal(1, "Input must be at least one element wide.");
    assert(PreIdWidth == ($bits(mst_aw_chans_o[0].id) - $bits(slv_aw_chans_i[0].id)))
      else $fatal(1, "Prepend ID Width must be: $bits(mst_aw_chans_o.id)-$bits(slv_aw_chans_i.id)");
    assert ($bits(mst_aw_chans_o[0].id) > $bits(slv_aw_chans_i[0].id))
      else $fatal(1, "The master AXI port has to have a wider ID than the slave port.");
  end

  aw_id   : assert final(
      mst_aw_chans_o[0].id[$bits(slv_aw_chans_i[0].id)-1:0] === slv_aw_chans_i[0].id)
        else $fatal (1, "Something with the AW channel ID prepending went wrong.");
  aw_addr : assert final(mst_aw_chans_o[0].addr === slv_aw_chans_i[0].addr)
      else $fatal (1, "Something with the AW channel ID prepending went wrong.");
  aw_len  : assert final(mst_aw_chans_o[0].len === slv_aw_chans_i[0].len)
      else $fatal (1, "Something with the AW channel ID prepending went wrong.");
  aw_size : assert final(mst_aw_chans_o[0].size === slv_aw_chans_i[0].size)
      else $fatal (1, "Something with the AW channel ID prepending went wrong.");
  aw_qos  : assert final(mst_aw_chans_o[0].qos === slv_aw_chans_i[0].qos)
      else $fatal (1, "Something with the AW channel ID prepending went wrong.");

  b_id    : assert final(
      mst_b_chans_i[0].id[$bits(slv_b_chans_o[0].id)-1:0] === slv_b_chans_o[0].id)
        else $fatal (1, "Something with the B channel ID stripping went wrong.");
  b_resp  : assert final(mst_b_chans_i[0].resp === slv_b_chans_o[0].resp)
      else $fatal (1, "Something with the B channel ID stripping went wrong.");

  ar_id   : assert final(
      mst_ar_chans_o[0].id[$bits(slv_ar_chans_i[0].id)-1:0] === slv_ar_chans_i[0].id)
        else $fatal (1, "Something with the AR channel ID prepending went wrong.");
  ar_addr : assert final(mst_ar_chans_o[0].addr === slv_ar_chans_i[0].addr)
      else $fatal (1, "Something with the AR channel ID prepending went wrong.");
  ar_len  : assert final(mst_ar_chans_o[0].len === slv_ar_chans_i[0].len)
      else $fatal (1, "Something with the AR channel ID prepending went wrong.");
  ar_size : assert final(mst_ar_chans_o[0].size === slv_ar_chans_i[0].size)
      else $fatal (1, "Something with the AR channel ID prepending went wrong.");
  ar_qos  : assert final(mst_ar_chans_o[0].qos === slv_ar_chans_i[0].qos)
      else $fatal (1, "Something with the AR channel ID prepending went wrong.");

  r_id    : assert final(mst_r_chans_i[0].id[$bits(slv_r_chans_o[0].id)-1:0] === slv_r_chans_o[0].id)
      else $fatal (1, "Something with the R channel ID stripping went wrong.");
  r_data  : assert final(mst_r_chans_i[0].data === slv_r_chans_o[0].data)
      else $fatal (1, "Something with the R channel ID stripping went wrong.");
  r_resp  : assert final(mst_r_chans_i[0].resp === slv_r_chans_o[0].resp)
      else $fatal (1, "Something with the R channel ID stripping went wrong.");
`endif
// pragma translate_on
endmodule

        mst_aw_chans_o[i].id = {pre_id_i, slv_aw_chans_i[i].id[AxiIdWidthSlvPort-1:0]};
        mst_ar_chans_o[i].id = {pre_id_i, slv_ar_chans_i[i].id[AxiIdWidthSlvPort-1:0]};
      end
    end
    // The ID is in the highest bits of the struct, so an assignment from a channel with a wide ID


/*
     __        __   __   ___ ___ ___  __
    |__)  /\  |__) |__) |__   |   |  /  \
    |    /~~\ |  \ |  \ |___  |   |  \__/

    Module: DP PM Top
    (c) 2021 - 2025 by Parretto B.V.

    History
    =======
    v1.0 - Initial release
    v1.1 - Added MST feature

    License
    =======
    This License will apply to the use of the IP-core (as defined in the License).
    Please read the License carefully so that you know what your rights and obligations are when using the IP-core.
    The acceptance of this License constitutes a valid and binding agreement between Parretto and you for the use of the IP-core.
    If you download and/or make any use of the IP-core you agree to be bound by this License.
    The License is available for download and print at www.parretto.com/license
    Parretto grants you, as the Licensee, a free, non-exclusive, non-transferable, limited right to use the IP-core
    solely for internal business purposes for the term and conditions of the License.
    You are also allowed to create Modifications for internal business purposes, but explicitly only under the conditions of art. 3.2.
    You are, however, obliged to pay the License Fees to Parretto for the use of the IP-core, or any Modification, in, or embodied in,
    a physical or non-tangible product or service that has substantial commercial, industrial or non-consumer uses.
*/

`timescale 1ns/1ns

`default_nettype none

module prt_dp_pm_top
#
(
    parameter                           P_VENDOR        = "none",    // Vendor - "AMD", "ALTERA" or "LSC"
    parameter                           P_BEAT          = 'd125,     // Beat value
    parameter                           P_HW_VER_MAJOR  = 1,         // Hardware version major
    parameter                           P_HW_VER_MINOR  = 0,         // Hardware version minor
    parameter                           P_CFG           = "tx",      // Configuration TX / RX
    parameter                           P_SIM           = 0,
    parameter                           P_ROM_INIT_FILE = "none",
    parameter                           P_RAM_INIT_FILE = "none",
    parameter                           P_PIO_IN_WIDTH  = 8,
    parameter                           P_PIO_OUT_WIDTH = 8,
    parameter                           P_SPL = 2,                   // Symbols per lane
    parameter                           P_MST = 0                    // MST
)
(
    // Reset and clock
    input wire                          RST_IN,
    input wire                          CLK_IN,

    // Interrupt
    input wire [1:0]                    IRQ_IN,

    // PIO
    input wire [P_PIO_IN_WIDTH-1:0]     PIO_IN,
    output wire [P_PIO_OUT_WIDTH-1:0]   PIO_OUT,

    // Host
    prt_dp_lb_if.lb_in                  HOST_IF,
    output wire                         HOST_IRQ_OUT,

    // HPD
    input wire                          HPD_IN,
    output wire                         HPD_OUT,

    // AUX
    output wire                         AUX_EN_OUT,
    output wire                         AUX_TX_OUT,
    input wire                          AUX_RX_IN,

    // Message
    prt_dp_msg_if.src                   MSG_SRC_IF,
    prt_dp_msg_if.snk                   MSG_SNK_IF
);

// Localparam
localparam P_MAX_DEV        = 8;
localparam P_MAX_DEV_ADR    = $clog2(P_MAX_DEV) + 1;
localparam P_RAM_BASE       = 'h0;      // 0x8000
localparam P_PIO_BASE       = 'h1;      // 0xc100
localparam P_TMR_BASE       = 'h2;      // 0xc200
localparam P_IRQ_BASE       = 'h3;      // 0xc300
localparam P_EXCH_BASE      = 'h4;      // 0xc400
localparam P_HPD_BASE       = 'h5;      // 0xc500
localparam P_AUX_BASE       = 'h6;      // 0xc600
localparam P_MSG_BASE       = 'h7;      // 0xc700
localparam P_MUTEX_BASE     = 'h8;      // 0xc800

localparam P_ROM_SIZE       = (P_MST) ? 32*1024 : 16*1024;  // ROM size : MST - 32KBytes / SST - 16KBytes
localparam P_ROM_ADR        = $clog2(P_ROM_SIZE)-2;         // ROM address in words
localparam P_RAM_SIZE       = 4*1024;                       // RAM size : 4KBytes
localparam P_RAM_ADR        = $clog2(P_RAM_SIZE)-2;         // RAM address in words

localparam P_PIO_IN_WIDTH_INT  = P_PIO_IN_WIDTH + 3;        // Internal PIO in width
localparam P_PIO_OUT_WIDTH_INT = P_PIO_OUT_WIDTH + 3;       // Internal PIO out width

// Signals

// Hart ROM
prt_dp_rom_if
#(
  .P_ADR_WIDTH  (16)
)
hart_rom_if();

// Hart RAM
prt_dp_ram_if
#(
  .P_ADR_WIDTH  (16)
)
hart_ram_if();

wire [3:0]  thread_en_to_hart;

// ROM
prt_dp_rom_if
#(
  .P_ADR_WIDTH  (P_ROM_ADR)
)
rom_if();

// RAM
prt_dp_ram_if
#(
  .P_ADR_WIDTH  (P_RAM_ADR)
)
ram_if();

// PIO
prt_dp_lb_if
#(
  .P_ADR_WIDTH  (4)
)
pio_lb();

wire [P_PIO_OUT_WIDTH_INT-1:0]   dat_from_pio;
wire [P_PIO_IN_WIDTH_INT-1:0]    dat_to_pio;
wire irq_from_pio;

// Timer
prt_dp_lb_if
#(
  .P_ADR_WIDTH  (3)
)
tmr_lb();

wire beat_from_tmr;
wire irq_from_tmr;

// IRQ
prt_dp_lb_if
#(
  .P_ADR_WIDTH  (2)
)
irq_lb();

wire [7:0]  irq_to_irq;

// Exchange
prt_dp_lb_if
#(
  .P_ADR_WIDTH  (3)
)
exch_lb();

wire            rst_from_exch;
wire            irq_from_exch;
wire            mem_str_from_exch;
wire [31:0]     mem_dat_from_exch;
wire [1:0]      mem_vld_from_exch;

// HPD
prt_dp_lb_if
#(
  .P_ADR_WIDTH  (2)
)
hpd_lb();

wire irq_from_hpd;

// AUX
prt_dp_lb_if
#(
  .P_ADR_WIDTH  (2)
)
aux_lb();

wire irq_from_aux;

// MSG
prt_dp_lb_if
#(
  .P_ADR_WIDTH  (3)
)
msg_lb();

wire    irq_from_msg;

// Mutex
prt_dp_lb_if
#(
  .P_ADR_WIDTH  (2)
)
mutex_lb();

// HART
    prt_dp_pm_hart
    #(
        .P_VENDOR              (P_VENDOR)
    )
    HART_INST
    (
    	// Clocks and reset
    	.RST_IN                (rst_from_exch),	// Reset
    	.CLK_IN                (CLK_IN),		// Clock

    	// ROM interface
    	.ROM_IF                (hart_rom_if),

    	// RAM interface
    	.RAM_IF                (hart_ram_if),

        // Config
        .CFG_THREAD_EN_IN      (thread_en_to_hart),    // Thread enable

    	// Status
    	.STA_ERR_OUT           ()		// Error
    );

    // Thread enable
    // Thread 0 is always running
    assign thread_en_to_hart = {dat_from_pio[0+:3], 1'b1};

    // ROM
    assign hart_rom_if.dat = rom_if.dat;

// ROM
    prt_dp_pm_rom
    #(
        .P_VENDOR       (P_VENDOR),         // Vendor
        .P_ADR          (P_ROM_ADR),        // Address bits
        .P_INIT_FILE    (P_ROM_INIT_FILE)   // Init file
    )
    ROM_INST
    (
        // Clock
    	.CLK_IN        (CLK_IN),			// Clock

    	// ROM interface
    	.ROM_IF        (rom_if),

        // Initialization
        .INIT_STR_IN   (mem_str_from_exch),    // Start
        .INIT_DAT_IN   (mem_dat_from_exch),    // Data
        .INIT_VLD_IN   (mem_vld_from_exch[0])  // Valid
    );

    assign rom_if.adr = hart_rom_if.adr[2+:P_ROM_ADR];

// RAM
    prt_dp_pm_ram
    #(
        .P_VENDOR       (P_VENDOR),         // Vendor
        .P_ADR          (P_RAM_ADR),        // Address bits
        .P_INIT_FILE    (P_RAM_INIT_FILE)   // Init file
    )
    RAM_INST
    (
        // Clock
    	.CLK_IN        (CLK_IN),			// Clock

    	// ROM interface
    	.RAM_IF        (ram_if),

        // Initialization
        .INIT_STR_IN   (mem_str_from_exch),    // Start
        .INIT_DAT_IN   (mem_dat_from_exch),    // Data
        .INIT_VLD_IN   (mem_vld_from_exch[1])  // Valid
    );

    assign ram_if.wr_adr    = hart_ram_if.wr_adr[2+:P_RAM_ADR];
    assign ram_if.rd_adr    = hart_ram_if.rd_adr[2+:P_RAM_ADR];
    assign ram_if.din       = hart_ram_if.dout;
    assign ram_if.wr        = ((hart_ram_if.wr_adr[15:14] == 'b10) && hart_ram_if.wr) ? 1 : 0;
    assign ram_if.rd        = ((hart_ram_if.rd_adr[15:14] == 'b10) && hart_ram_if.rd) ? 1 : 0;
    assign ram_if.strb      = hart_ram_if.strb;

// Data in mux
    always_comb
    begin
        // PIO
        if (pio_lb.vld)
            hart_ram_if.din = pio_lb.dout;

        // Timer
        else if (tmr_lb.vld)
            hart_ram_if.din = tmr_lb.dout;

        // Interrupt
        else if (irq_lb.vld)
            hart_ram_if.din = irq_lb.dout;

        // Exchange
        else if (exch_lb.vld)
            hart_ram_if.din = exch_lb.dout;

        // HPD
        else if (hpd_lb.vld)
            hart_ram_if.din = hpd_lb.dout;

        // AUX
        else if (aux_lb.vld)
            hart_ram_if.din = aux_lb.dout;

        // Message
        else if (msg_lb.vld)
            hart_ram_if.din = msg_lb.dout;

        // Mutex
        else if (mutex_lb.vld)
            hart_ram_if.din = mutex_lb.dout;

        // RAM
        else
            hart_ram_if.din = ram_if.dout;
    end

// PIO
    prt_dp_pm_pio
    #(
        .P_HW_VER_MAJOR    (P_HW_VER_MAJOR),   // Hardware version major
        .P_HW_VER_MINOR    (P_HW_VER_MINOR),   // Hardware version minor
        .P_IN_WIDTH        (P_PIO_IN_WIDTH_INT),
        .P_OUT_WIDTH       (P_PIO_OUT_WIDTH_INT)
    )
    PIO_INST
    (
        // Clock and reset
        .RST_IN            (rst_from_exch),
        .CLK_IN            (CLK_IN),

        // Local bus
        .LB_IF             (pio_lb),

        // PIO
        .PIO_DAT_IN        (dat_to_pio),
        .PIO_DAT_OUT       (dat_from_pio),

        // Interrupt
        .IRQ_OUT           (irq_from_pio)
    );

  // Local bus mapping
    assign pio_lb.adr   = (pio_lb.wr) ? hart_ram_if.wr_adr[2+:$size(pio_lb.adr)] : hart_ram_if.rd_adr[2+:$size(pio_lb.adr)];
    assign pio_lb.din   = hart_ram_if.dout;
    assign pio_lb.wr    = ((hart_ram_if.wr_adr[15:14] == 'b11) && (hart_ram_if.wr_adr[8+:P_MAX_DEV_ADR] == P_PIO_BASE) && hart_ram_if.wr) ? 1 : 0;
    assign pio_lb.rd    = ((hart_ram_if.rd_adr[15:14] == 'b11) && (hart_ram_if.rd_adr[8+:P_MAX_DEV_ADR] == P_PIO_BASE) && hart_ram_if.rd) ? 1 : 0;

// PIO bit 0 is set when the system is run in simulation
    assign dat_to_pio[0] = (P_SIM) ? 1 : 0;

// PIO bit 1 is set according to the symbols per lane
    assign dat_to_pio[1] = (P_SPL == 4) ? 1 : 0;

// PIO bit 2 is set according when the MST feature is enable
    assign dat_to_pio[2] = (P_MST) ? 1 : 0;

// External PIO inputs
    assign dat_to_pio[3+:P_PIO_IN_WIDTH] = PIO_IN[0+:P_PIO_IN_WIDTH];

// Timer
    prt_dp_pm_tmr
    #(
        .P_SIM             (P_SIM),
        .P_BEAT            (P_BEAT)     // Beat value
    )
    TMR_INST
    (
        // Clock and reset
        .RST_IN            (rst_from_exch),
        .CLK_IN            (CLK_IN),

        // Local bus
        .LB_IF             (tmr_lb),

    	// Beat
    	.BEAT_OUT          (beat_from_tmr),	// 1 MHz output

        // Interrupt
        .IRQ_OUT           (irq_from_tmr)
    );

  // Local bus mapping
    assign tmr_lb.adr   = (tmr_lb.wr) ? hart_ram_if.wr_adr[2+:$size(tmr_lb.adr)] : hart_ram_if.rd_adr[2+:$size(tmr_lb.adr)];
    assign tmr_lb.din   = hart_ram_if.dout;
    assign tmr_lb.wr    = ((hart_ram_if.wr_adr[15:14] == 'b11) && (hart_ram_if.wr_adr[8+:P_MAX_DEV_ADR] == P_TMR_BASE) && hart_ram_if.wr) ? 1 : 0;
    assign tmr_lb.rd    = ((hart_ram_if.rd_adr[15:14] == 'b11) && (hart_ram_if.rd_adr[8+:P_MAX_DEV_ADR] == P_TMR_BASE) && hart_ram_if.rd) ? 1 : 0;

// IRQ
    prt_dp_pm_irq
    IRQ_INST
    (
        // Clock and reset
        .RST_IN            (rst_from_exch),
        .CLK_IN            (CLK_IN),

        // Local bus
        .LB_IF             (irq_lb),

        // Interrupt
        .IRQ_REQ_IN        (irq_to_irq)
    );

  // Local bus mapping
    assign irq_lb.adr   = (irq_lb.wr) ? hart_ram_if.wr_adr[2+:$size(irq_lb.adr)] : hart_ram_if.rd_adr[2+:$size(irq_lb.adr)];
    assign irq_lb.din   = hart_ram_if.dout;
    assign irq_lb.wr    = ((hart_ram_if.wr_adr[15:14] == 'b11) && (hart_ram_if.wr_adr[8+:P_MAX_DEV_ADR] == P_IRQ_BASE) && hart_ram_if.wr) ? 1 : 0;
    assign irq_lb.rd    = ((hart_ram_if.rd_adr[15:14] == 'b11) && (hart_ram_if.rd_adr[8+:P_MAX_DEV_ADR] == P_IRQ_BASE) && hart_ram_if.rd) ? 1 : 0;

 // Interrupt
    assign irq_to_irq[0]    = irq_from_pio;
    assign irq_to_irq[1]    = irq_from_tmr;
    assign irq_to_irq[2]    = irq_from_msg;
    assign irq_to_irq[3]    = irq_from_hpd;
    assign irq_to_irq[4]    = irq_from_aux;
    assign irq_to_irq[5]    = irq_from_exch;
    assign irq_to_irq[7:6]  = IRQ_IN;

// Exchange
    prt_dp_pm_exch
    #(
        .P_VENDOR           (P_VENDOR)         // Vendor
    )
    EXCH_INST
    (
    	// Reset and clock
        .RST_IN             (RST_IN),              // Reset (from host)
        .CLK_IN             (CLK_IN),              // Clock

        // Host local bus interface
        .HOST_IF            (HOST_IF),
        .HOST_IRQ_OUT       (HOST_IRQ_OUT),

    	// Policy maker local bus interface
    	.PM_IF              (exch_lb),
        .PM_IRQ_OUT         (irq_from_exch),
        .PM_RST_OUT         (rst_from_exch),

    	// Memory update
    	.MEM_STR_OUT       (mem_str_from_exch),	   // Start
    	.MEM_DAT_OUT       (mem_dat_from_exch),	   // Data
    	.MEM_VLD_OUT	   (mem_vld_from_exch)     // Valid 0 - ROM / 1 - RAM
    );

  // Local bus mapping
    assign exch_lb.adr   = (exch_lb.wr) ? hart_ram_if.wr_adr[2+:$size(exch_lb.adr)] : hart_ram_if.rd_adr[2+:$size(exch_lb.adr)];
    assign exch_lb.din   = hart_ram_if.dout;
    assign exch_lb.wr    = ((hart_ram_if.wr_adr[15:14] == 'b11) && (hart_ram_if.wr_adr[8+:P_MAX_DEV_ADR] == P_EXCH_BASE) && hart_ram_if.wr) ? 1 : 0;
    assign exch_lb.rd    = ((hart_ram_if.rd_adr[15:14] == 'b11) && (hart_ram_if.rd_adr[8+:P_MAX_DEV_ADR] == P_EXCH_BASE) && hart_ram_if.rd) ? 1 : 0;

// HPD
generate
    if (P_CFG == "tx")
    begin : gen_hpd_tx
        prt_dp_pm_hpd_tx
        #(
            // Simulation
            .P_SIM              (P_SIM)          // Simulation parameter
        )
        HPD_INST
        (
            // Reset and clock
            .RST_IN             (rst_from_exch),
            .CLK_IN             (CLK_IN),

            // Local bus interface
            .LB_IF              (hpd_lb),

            // Beat
            .BEAT_IN            (beat_from_tmr),    // Beat 1 MHz

            // HPD
            .HPD_IN             (HPD_IN),           // HPD in

            // IRQ
            .IRQ_OUT            (irq_from_hpd)      // Interrupt
        );

        assign HPD_OUT = 0; // Not used
    end

    else
    begin : gen_hpd_rx
        prt_dp_pm_hpd_rx
        #(
            // Simulation
            .P_SIM              (P_SIM)             // Simulation parameter
        )
        HPD_INST
        (
            // Reset and clock
            .RST_IN             (rst_from_exch),
            .CLK_IN             (CLK_IN),

            // Local bus interface
            .LB_IF              (hpd_lb),

            // Beat
            .BEAT_IN            (beat_from_tmr),    // Beat 1 MHz

            // HPD
            .HPD_OUT            (HPD_OUT),          // HPD out

            // IRQ
            .IRQ_OUT            (irq_from_hpd)      // Interrupt
        );
    end

endgenerate

  // Local bus mapping
    assign hpd_lb.adr   = (hpd_lb.wr) ? hart_ram_if.wr_adr[2+:$size(hpd_lb.adr)] : hart_ram_if.rd_adr[2+:$size(hpd_lb.adr)];
    assign hpd_lb.din   = hart_ram_if.dout;
    assign hpd_lb.wr    = ((hart_ram_if.wr_adr[15:14] == 'b11) && (hart_ram_if.wr_adr[8+:P_MAX_DEV_ADR] == P_HPD_BASE) && hart_ram_if.wr) ? 1 : 0;
    assign hpd_lb.rd    = ((hart_ram_if.rd_adr[15:14] == 'b11) && (hart_ram_if.rd_adr[8+:P_MAX_DEV_ADR] == P_HPD_BASE) && hart_ram_if.rd) ? 1 : 0;

// AUX
    prt_dp_pm_aux
    #(
        .P_VENDOR               (P_VENDOR),     // Vendor
        .P_SIM                  (P_SIM),
        .P_BEAT                 (P_BEAT)        // Beat value
    )
    AUX_INST
    (
        // Reset and clock
        .RST_IN                 (rst_from_exch),
        .CLK_IN                 (CLK_IN),

        // Local bus interface
        .LB_IF                  (aux_lb),

        // Beat
        .BEAT_IN                (beat_from_tmr), // Beat 1 MHz

        // AUX
        .AUX_EN_OUT             (AUX_EN_OUT),   // Enable
        .AUX_TX_OUT             (AUX_TX_OUT),   // Transmit
        .AUX_RX_IN              (AUX_RX_IN),    // Receive

        // IRQ
        .IRQ_OUT                (irq_from_aux)   // Interrupt
    );

  // Local bus mapping
    assign aux_lb.adr   = (aux_lb.wr) ? hart_ram_if.wr_adr[2+:$size(aux_lb.adr)] : hart_ram_if.rd_adr[2+:$size(aux_lb.adr)];
    assign aux_lb.din   = hart_ram_if.dout;
    assign aux_lb.wr    = ((hart_ram_if.wr_adr[15:14] == 'b11) && (hart_ram_if.wr_adr[8+:P_MAX_DEV_ADR] == P_AUX_BASE) && hart_ram_if.wr) ? 1 : 0;
    assign aux_lb.rd    = ((hart_ram_if.rd_adr[15:14] == 'b11) && (hart_ram_if.rd_adr[8+:P_MAX_DEV_ADR] == P_AUX_BASE) && hart_ram_if.rd) ? 1 : 0;

// Message
    prt_dp_pm_msg
    #(
        .P_VENDOR          (P_VENDOR)         // Vendor
    )
    MSG_INST
    (
        // Clock and reset
        .RST_IN            (rst_from_exch),
        .CLK_IN            (CLK_IN),

        // Local bus
        .LB_IF             (msg_lb),

        // Message source
        .MSG_SRC_IF        (MSG_SRC_IF),

        // Message sink
        .MSG_SNK_IF        (MSG_SNK_IF),

        // Interrupt
        .IRQ_OUT           (irq_from_msg)
    );

  // Local bus mapping
    assign msg_lb.adr   = (msg_lb.wr) ? hart_ram_if.wr_adr[2+:$size(msg_lb.adr)] : hart_ram_if.rd_adr[2+:$size(msg_lb.adr)];
    assign msg_lb.din   = hart_ram_if.dout;
    assign msg_lb.wr    = ((hart_ram_if.wr_adr[15:14] == 'b11) && (hart_ram_if.wr_adr[8+:P_MAX_DEV_ADR] == P_MSG_BASE) && hart_ram_if.wr) ? 1 : 0;

// Mutex
    prt_dp_pm_mutex
    MUTEX_INST
    (
        // Clock and reset
        .RST_IN            (rst_from_exch),
        .CLK_IN            (CLK_IN),

        // Local bus
        .LB_IF             (mutex_lb)
    );

  // Local bus mapping
    assign mutex_lb.adr   = (mutex_lb.wr) ? hart_ram_if.wr_adr[2+:$size(mutex_lb.adr)] : hart_ram_if.rd_adr[2+:$size(mutex_lb.adr)];
    assign mutex_lb.din   = hart_ram_if.dout;
    assign mutex_lb.wr    = ((hart_ram_if.wr_adr[15:14] == 'b11) && (hart_ram_if.wr_adr[8+:P_MAX_DEV_ADR] == P_MUTEX_BASE) && hart_ram_if.wr) ? 1 : 0;
    assign mutex_lb.rd    = ((hart_ram_if.rd_adr[15:14] == 'b11) && (hart_ram_if.rd_adr[8+:P_MAX_DEV_ADR] == P_MUTEX_BASE) && hart_ram_if.rd) ? 1 : 0;

// Outputs
    assign PIO_OUT = dat_from_pio[3+:P_PIO_OUT_WIDTH];

endmodule

`default_nettype wire

assign msg_lb.rd    = ((hart_ram_if.rd_adr[15:14] == 'b11) && (hart_ram_if.rd_adr[8+:P_MAX_DEV_ADR] == P_MSG_BASE) && hart_ram_if.rd) ? 1 : 0;


/*
     __        __   __   ___ ___ ___  __
    |__)  /\  |__) |__) |__   |   |  /  \
    |    /~~\ |  \ |  \ |___  |   |  \__/

    Module: Scaler agent lookup
    (c) 2022, 2023 by Parretto B.V.

    History
    =======
    v1.0 - Initial release

    License
    =======
    This License will apply to the use of the IP-core (as defined in the License).
    Please read the License carefully so that you know what your rights and obligations are when using the IP-core.
    The acceptance of this License constitutes a valid and binding agreement between Parretto and you for the use of the IP-core.
    If you download and/or make any use of the IP-core you agree to be bound by this License.
    The License is available for download and print at www.parretto.com/license.html
    Parretto grants you, as the Licensee, a free, non-exclusive, non-transferable, limited right to use the IP-core
    solely for internal business purstepes for the term and conditions of the License.
    You are also allowed to create Modifications for internal business purstepes, but explicitly only under the conditions of art. 3.2.
    You are, however, obliged to pay the License Fees to Parretto for the use of the IP-core, or any Modification, in, or embodied in,
    a physical or non-tangible product or service that has substantial commercial, industrial or non-consumer uses.
*/

`default_nettype none

module prt_scaler_agnt_lut
#(
    parameter P_ID = 0,             // Index
    parameter P_COEF = 5,           // Coefficient width
    parameter P_MUX = 4,            // MUX width
    parameter P_DAT = 4             // Data width
)
(
   input wire               CLK_IN,
   input wire  [7:0]        SEL_IN,
   output wire [P_DAT-1:0]  DAT_OUT
);

// Sinals
logic [$size(SEL_IN)-1:0]   clk_sel;    // Ratio [2], row_idx[3], blk_idx[3]
logic [P_DAT-1:0]           clk_dat;   // mux3, mux2, mux1, mux0, coef3, coef2, coef1, coef0

// Logic
    always_ff @ (posedge CLK_IN)
    begin
        clk_sel <= SEL_IN;
    end

generate

    // Pixel 1
    if (P_ID == 1)
    begin : gen_lut_p1
        always_ff @ (posedge CLK_IN)
        begin
            case (clk_sel)
                // Ratio 3/2
                // Ratio [2], row_idx[3], blk_idx[3] - mux3, mux2, mux1, mux0, coef3, coef2, coef1, coef0
                {2'd0, 3'd0, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};
                {2'd0, 3'd0, 3'd1} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(5)), (P_COEF'(4))};
                {2'd0, 3'd0, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(4)), (P_COEF'(5))};
                {2'd0, 3'd0, 3'd3} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};

                {2'd0, 3'd1, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(9)), (P_COEF'(8)), (P_COEF'(7)), (P_COEF'(6))};
                {2'd0, 3'd1, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(13)), (P_COEF'(12)), (P_COEF'(11)), (P_COEF'(10))};
                {2'd0, 3'd1, 3'd2} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(12)), (P_COEF'(13)), (P_COEF'(10)), (P_COEF'(11))};
                {2'd0, 3'd1, 3'd3} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(9)), (P_COEF'(8)), (P_COEF'(7)), (P_COEF'(6))};

                {2'd0, 3'd2, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(13)), (P_COEF'(11)), (P_COEF'(12)), (P_COEF'(10))};
                {2'd0, 3'd2, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(17)), (P_COEF'(16)), (P_COEF'(15)), (P_COEF'(14))};
                {2'd0, 3'd2, 3'd2} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(16)), (P_COEF'(17)), (P_COEF'(14)), (P_COEF'(15))};
                {2'd0, 3'd2, 3'd3} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(13)), (P_COEF'(11)), (P_COEF'(12)), (P_COEF'(10))};

                {2'd0, 3'd3, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(12)), (P_COEF'(10)), (P_COEF'(13)), (P_COEF'(11))};
                {2'd0, 3'd3, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(16)), (P_COEF'(14)), (P_COEF'(17)), (P_COEF'(15))};
                {2'd0, 3'd3, 3'd2} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(14)), (P_COEF'(15)), (P_COEF'(16)), (P_COEF'(17))};
                {2'd0, 3'd3, 3'd3} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(12)), (P_COEF'(10)), (P_COEF'(13)), (P_COEF'(11))};

                // Ratio 2/1
                // Ratio [2], row_idx[3], blk_idx[3] - mux3, mux2, mux1, mux0, coef3, coef2, coef1, coef0
                {2'd1, 3'd0, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(2)), (P_COEF'(1))};
                {2'd1, 3'd0, 3'd1} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(2)), (P_COEF'(1))};
                {2'd1, 3'd0, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(2)), (P_COEF'(1))};

                {2'd1, 3'd1, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(3)), (P_COEF'(2)), (P_COEF'(2)), (P_COEF'(1))};
                {2'd1, 3'd1, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(3)), (P_COEF'(2)), (P_COEF'(2)), (P_COEF'(1))};
                {2'd1, 3'd1, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(3)), (P_COEF'(2)), (P_COEF'(2)), (P_COEF'(1))};

                {2'd1, 3'd2, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(2)), (P_COEF'(1)), (P_COEF'(3)), (P_COEF'(2))};
                {2'd1, 3'd2, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(2)), (P_COEF'(1)), (P_COEF'(3)), (P_COEF'(2))};
                {2'd1, 3'd2, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(2)), (P_COEF'(1)), (P_COEF'(3)), (P_COEF'(2))};

                // Ratio 3/1
                // Ratio [2], row_idx[3], blk_idx[3] - mux3, mux2, mux1, mux0, coef3, coef2, coef1, coef0
                {2'd2, 3'd0, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(0)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(1))};
                {2'd2, 3'd0, 3'd1} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};
                {2'd2, 3'd0, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(2)), (P_COEF'(3))};
                {2'd2, 3'd0, 3'd3} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(1))};

                {2'd2, 3'd1, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(0)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(1))};
                {2'd2, 3'd1, 3'd1} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};
                {2'd2, 3'd1, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(2)), (P_COEF'(3))};
                {2'd2, 3'd1, 3'd3} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(1))};

                {2'd2, 3'd2, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(8)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(7)), (P_COEF'(6))};
                {2'd2, 3'd2, 3'd1} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(11)), (P_COEF'(10)), (P_COEF'(9)), (P_COEF'(8))};
                {2'd2, 3'd2, 3'd2} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(10)), (P_COEF'(11)), (P_COEF'(8)), (P_COEF'(9))};
                {2'd2, 3'd2, 3'd3} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(9)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(7)), (P_COEF'(6))};

                {2'd2, 3'd3, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(8)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(6)), (P_COEF'(7))};
                {2'd2, 3'd3, 3'd1} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(9)), (P_COEF'(8)), (P_COEF'(11)), (P_COEF'(10))};
                {2'd2, 3'd3, 3'd2} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(8)), (P_COEF'(9)), (P_COEF'(10)), (P_COEF'(11))};
                {2'd2, 3'd3, 3'd3} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(9)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(6)), (P_COEF'(7))};

                // Ratio 4/3
                // Ratio [2], row_idx[3], blk_idx[3] - mux3, mux2, mux1, mux0, coef3, coef2, coef1, coef0
                {2'd3, 3'd0, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};
                {2'd3, 3'd0, 3'd1} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};
                {2'd3, 3'd0, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};

                {2'd3, 3'd1, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(10)), (P_COEF'(9)), (P_COEF'(9)), (P_COEF'(8))};
                {2'd3, 3'd1, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(10)), (P_COEF'(9)), (P_COEF'(9)), (P_COEF'(8))};
                {2'd3, 3'd1, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(10)), (P_COEF'(9)), (P_COEF'(9)), (P_COEF'(8))};

                {2'd3, 3'd2, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(28)), (P_COEF'(27)), (P_COEF'(26)), (P_COEF'(25))};
                {2'd3, 3'd2, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(28)), (P_COEF'(27)), (P_COEF'(26)), (P_COEF'(25))};
                {2'd3, 3'd2, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(28)), (P_COEF'(27)), (P_COEF'(26)), (P_COEF'(25))};

                {2'd3, 3'd3, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(46)), (P_COEF'(45)), (P_COEF'(44)), (P_COEF'(43))};
                {2'd3, 3'd3, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(46)), (P_COEF'(45)), (P_COEF'(44)), (P_COEF'(43))};
                {2'd3, 3'd3, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(46)), (P_COEF'(45)), (P_COEF'(44)), (P_COEF'(43))};

                {2'd3, 3'd4, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(64)), (P_COEF'(63)), (P_COEF'(62)), (P_COEF'(61))};
                {2'd3, 3'd4, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(64)), (P_COEF'(63)), (P_COEF'(62)), (P_COEF'(61))};
                {2'd3, 3'd4, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(64)), (P_COEF'(63)), (P_COEF'(62)), (P_COEF'(61))};

                default : clk_dat <= 'd0;
            endcase
        end
    end

    // Pixel 2
    else if (P_ID == 2)
    begin : gen_lut_p2
        always_ff @ (posedge CLK_IN)
        begin
            case (clk_sel)
                // Ratio 3/2
                // Ratio [2], row_idx[3], blk_idx[3] - mux3, mux2, mux1, mux0, coef3, coef2, coef1, coef0
                {2'd0, 3'd0, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(5)), (P_COEF'(4))};
                {2'd0, 3'd0, 3'd1} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(4)), (P_COEF'(5))};
                {2'd0, 3'd0, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};
                {2'd0, 3'd0, 3'd3} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(5)), (P_COEF'(4))};

                {2'd0, 3'd1, 3'd0} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(13)), (P_COEF'(12)), (P_COEF'(11)), (P_COEF'(10))};
                {2'd0, 3'd1, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(12)), (P_COEF'(13)), (P_COEF'(10)), (P_COEF'(11))};
                {2'd0, 3'd1, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(9)), (P_COEF'(8)), (P_COEF'(7)), (P_COEF'(6))};
                {2'd0, 3'd1, 3'd3} : clk_dat <= {(P_MUX'(11)), (P_MUX'(10)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(13)), (P_COEF'(12)), (P_COEF'(11)), (P_COEF'(10))};

                {2'd0, 3'd2, 3'd0} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(17)), (P_COEF'(16)), (P_COEF'(15)), (P_COEF'(14))};
                {2'd0, 3'd2, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(16)), (P_COEF'(17)), (P_COEF'(14)), (P_COEF'(15))};
                {2'd0, 3'd2, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(13)), (P_COEF'(11)), (P_COEF'(12)), (P_COEF'(10))};
                {2'd0, 3'd2, 3'd3} : clk_dat <= {(P_MUX'(11)), (P_MUX'(10)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(17)), (P_COEF'(16)), (P_COEF'(15)), (P_COEF'(14))};

                {2'd0, 3'd3, 3'd0} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(15)), (P_COEF'(14)), (P_COEF'(17)), (P_COEF'(16))};
                {2'd0, 3'd3, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(14)), (P_COEF'(15)), (P_COEF'(16)), (P_COEF'(17))};
                {2'd0, 3'd3, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(12)), (P_COEF'(10)), (P_COEF'(13)), (P_COEF'(11))};
                {2'd0, 3'd3, 3'd3} : clk_dat <= {(P_MUX'(11)), (P_MUX'(10)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(15)), (P_COEF'(14)), (P_COEF'(17)), (P_COEF'(16))};

                // Ratio 2/1
                // Ratio [2], row_idx[3], blk_idx[3] - mux3, mux2, mux1, mux0, coef3, coef2, coef1, coef0
                {2'd1, 3'd0, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(1)), (P_COEF'(2))};
                {2'd1, 3'd0, 3'd1} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(1)), (P_COEF'(2))};
                {2'd1, 3'd0, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(1)), (P_COEF'(2))};

                {2'd1, 3'd1, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(2)), (P_COEF'(3)), (P_COEF'(1)), (P_COEF'(2))};
                {2'd1, 3'd1, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(2)), (P_COEF'(3)), (P_COEF'(1)), (P_COEF'(2))};
                {2'd1, 3'd1, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(2)), (P_COEF'(3)), (P_COEF'(1)), (P_COEF'(2))};

                {2'd1, 3'd2, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(1)), (P_COEF'(2)), (P_COEF'(2)), (P_COEF'(3))};
                {2'd1, 3'd2, 3'd1} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(1)), (P_COEF'(2)), (P_COEF'(2)), (P_COEF'(3))};
                {2'd1, 3'd2, 3'd2} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(1)), (P_COEF'(2)), (P_COEF'(2)), (P_COEF'(3))};

                // Ratio 3/1
                // Ratio [2], row_idx[3], blk_idx[3] - mux3, mux2, mux1, mux0, coef3, coef2, coef1, coef0
                {2'd2, 3'd0, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};
                {2'd2, 3'd0, 3'd1} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(2)), (P_COEF'(3))};
                {2'd2, 3'd0, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(1))};
                {2'd2, 3'd0, 3'd3} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};

                {2'd2, 3'd1, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};
                {2'd2, 3'd1, 3'd1} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(2)), (P_COEF'(3))};
                {2'd2, 3'd1, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(0)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(1))};
                {2'd2, 3'd1, 3'd3} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(3)), (P_COEF'(2))};

                {2'd2, 3'd2, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(11)), (P_COEF'(10)), (P_COEF'(9)), (P_COEF'(8))};
                {2'd2, 3'd2, 3'd1} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(10)), (P_COEF'(11)), (P_COEF'(8)), (P_COEF'(9))};
                {2'd2, 3'd2, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(9)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(7)), (P_COEF'(6))};
                {2'd2, 3'd2, 3'd3} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(11)), (P_COEF'(10)), (P_COEF'(9)), (P_COEF'(8))};

                {2'd2, 3'd3, 3'd0} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(10)), (P_COEF'(8)), (P_COEF'(11)), (P_COEF'(9))};
                {2'd2, 3'd3, 3'd1} : clk_dat <= {(P_MUX'(9)), (P_MUX'(8)), (P_MUX'(1)), (P_MUX'(0)), (P_COEF'(8)), (P_COEF'(9)), (P_COEF'(10)), (P_COEF'(11))};
                {2'd2, 3'd3, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(9)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(6)), (P_COEF'(7))};
                {2'd2, 3'd3, 3'd3} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(9)), (P_COEF'(8)), (P_COEF'(11)), (P_COEF'(10))};

                // Ratio 4/3
                // Ratio [2], row_idx[3], blk_idx[3] - mux3, mux2, mux1, mux0, coef3, coef2, coef1, coef0
                {2'd3, 3'd0, 3'd0} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(4)), (P_COEF'(3))};
                {2'd3, 3'd0, 3'd1} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(4)), (P_COEF'(3))};
                {2'd3, 3'd0, 3'd2} : clk_dat <= {(P_MUX'(0)), (P_MUX'(0)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(0)), (P_COEF'(0)), (P_COEF'(4)), (P_COEF'(3))};

                {2'd3, 3'd1, 3'd0} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(14)), (P_COEF'(13)), (P_COEF'(12)), (P_COEF'(11))};
                {2'd3, 3'd1, 3'd1} : clk_dat <= {(P_MUX'(11)), (P_MUX'(10)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(14)), (P_COEF'(13)), (P_COEF'(12)), (P_COEF'(11))};
                {2'd3, 3'd1, 3'd2} : clk_dat <= {(P_MUX'(11)), (P_MUX'(10)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(14)), (P_COEF'(13)), (P_COEF'(12)), (P_COEF'(11))};

                {2'd3, 3'd2, 3'd0} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(32)), (P_COEF'(31)), (P_COEF'(30)), (P_COEF'(29))};
                {2'd3, 3'd2, 3'd1} : clk_dat <= {(P_MUX'(11)), (P_MUX'(10)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(32)), (P_COEF'(31)), (P_COEF'(30)), (P_COEF'(29))};
                {2'd3, 3'd2, 3'd2} : clk_dat <= {(P_MUX'(11)), (P_MUX'(10)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(32)), (P_COEF'(31)), (P_COEF'(30)), (P_COEF'(29))};

                {2'd3, 3'd3, 3'd0} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(50)), (P_COEF'(49)), (P_COEF'(48)), (P_COEF'(47))};
                {2'd3, 3'd3, 3'd1} : clk_dat <= {(P_MUX'(11)), (P_MUX'(10)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(50)), (P_COEF'(49)), (P_COEF'(48)), (P_COEF'(47))};
                {2'd3, 3'd3, 3'd2} : clk_dat <= {(P_MUX'(11)), (P_MUX'(10)), (P_MUX'(3)), (P_MUX'(2)), (P_COEF'(50)), (P_COEF'(49)), (P_COEF'(48)), (P_COEF'(47))};

                {2'd3, 3'd4, 3'd0} : clk_dat <= {(P_MUX'(10)), (P_MUX'(9)), (P_MUX'(2)), (P_MUX'(1)), (P_COEF'(68)), (P_COEF'(67)), (P_COEF'(66)), (P_COEF'(65))};


interface udp_ifc;
  import udp_vlg_pkg::*;
  import eth_vlg_pkg::*;

  stream_t   strm;

  logic      req;  // Data request for tx when done with header
  logic      acc;
  logic      err;
  logic      done;
  udp_meta_t meta;

  modport in_rx  (input  strm, meta);
  modport out_rx (output strm, meta);
  modport in_tx  (input  strm, meta, rdy, output acc, err, req, done);
  modport out_tx (output strm, meta, rdy, input  acc, err, req, done);
  modport sva    (input  strm, meta, rdy,        acc, err, req, done);
endinterface : udp_ifc


#(
  parameter D = 16,
  parameter W = 16 )
();
  logic         rst;
  logic         clk;

  logic         write;
  logic [W-1:0] data_in;

  logic         read;
  logic [W-1:0] data_out;
  logic         valid_out;

  logic         full;
  logic         empty;

  modport fifo (input rst, clk, write, data_in, read, output data_out, valid_out, full, empty);
  modport sys  (input data_out, valid_out, full, empty, output rst, clk, write, data_in, read);
  modport tb   (output data_out, valid_out, full, empty, rst, clk, write, data_in, read);

endinterface : fifo_sc_ifc


//Legal Notice: (C)2019 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 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 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.

// synthesis translate_off
`timescale 1ns / 1ps
// synthesis translate_on

// turn off superfluous verilog processor warnings
// altera message_level Level1
// altera message_off 10034 10035 10036 10037 10230 10240 10030 13469 16735 16788

module test_fifo_0_altera_avalon_fifo_181_hicnqoy_dual_clock_fifo (
                                                                    // inputs:
                                                                     aclr,
                                                                     data,
                                                                     rdclk,
                                                                     rdreq,
                                                                     wrclk,
                                                                     wrreq,

                                                                    // outputs:
                                                                     q,
                                                                     rdempty,
                                                                     wrfull
                                                                  )
  /* synthesis ALTERA_ATTRIBUTE = "SUPPRESS_DA_RULE_INTERNAL=R101" */ ;

  output  [ 31: 0] q;
  output           rdempty;
  output           wrfull;
  input            aclr;
  input   [ 31: 0] data;
  input            rdclk;
  input            rdreq;
  input            wrclk;
  input            wrreq;

wire             int_wrfull;
wire    [ 31: 0] q;
wire             rdempty;
wire             wrfull;
wire    [  2: 0] wrusedw;
  assign wrfull = (wrusedw >= 8-3) | int_wrfull;
  dcfifo dual_clock_fifo
    (
      .aclr (aclr),
      .data (data),
      .q (q),
      .rdclk (rdclk),
      .rdempty (rdempty),
      .rdreq (rdreq),
      .wrclk (wrclk),
      .wrfull (int_wrfull),
      .wrreq (wrreq),
      .wrusedw (wrusedw)
    );

  defparam dual_clock_fifo.add_ram_output_register = "OFF",
           dual_clock_fifo.clocks_are_synchronized = "FALSE",
           dual_clock_fifo.intended_device_family = "STRATIX10",
           dual_clock_fifo.lpm_hint = "DISABLE_DCFIFO_EMBEDDED_TIMING_CONSTRAINT",
           dual_clock_fifo.lpm_numwords = 8,
           dual_clock_fifo.lpm_showahead = "OFF",
           dual_clock_fifo.lpm_type = "dcfifo",
           dual_clock_fifo.lpm_width = 32,
           dual_clock_fifo.lpm_widthu = 3,
           dual_clock_fifo.overflow_checking = "ON",
           dual_clock_fifo.rdsync_delaypipe = 4,
           dual_clock_fifo.read_aclr_synch = "ON",
           dual_clock_fifo.underflow_checking = "ON",
           dual_clock_fifo.use_eab = "OFF",
           dual_clock_fifo.write_aclr_synch = "ON",
           dual_clock_fifo.wrsync_delaypipe = 4;

endmodule

// synthesis translate_off
`timescale 1ns / 1ps
// synthesis translate_on

// turn off superfluous verilog processor warnings
// altera message_level Level1
// altera message_off 10034 10035 10036 10037 10230 10240 10030 13469 16735 16788

module test_fifo_0_altera_avalon_fifo_181_hicnqoy_dcfifo_with_controls (
                                                                         // inputs:
                                                                          data,
                                                                          rdclk,
                                                                          rdreq,
                                                                          rdreset_n,
                                                                          wrclk,
                                                                          wrreq,
                                                                          wrreset_n,

                                                                         // outputs:
                                                                          q,
                                                                          rdempty,
                                                                          wrfull
                                                                       )
;

  output  [ 31: 0] q;
  output           rdempty;
  output           wrfull;
  input   [ 31: 0] data;
  input            rdclk;
  input            rdreq;
  input            rdreset_n;
  input            wrclk;
  input            wrreq;
  input            wrreset_n;

wire    [ 31: 0] q;
wire             rdempty;
wire             wrfull;
  //the_dcfifo, which is an e_instance
  test_fifo_0_altera_avalon_fifo_181_hicnqoy_dual_clock_fifo the_dcfifo
    (
      .aclr    (~(rdreset_n && wrreset_n)),
      .data    (data),
      .q       (q),
      .rdclk   (rdclk),
      .rdempty (rdempty),
      .rdreq   (rdreq),
      .wrclk   (wrclk),
      .wrfull  (wrfull),
      .wrreq   (wrreq)
    );

endmodule

// synthesis translate_off
`timescale 1ns / 1ps
// synthesis translate_on

// turn off superfluous verilog processor warnings
// altera message_level Level1
// altera message_off 10034 10035 10036 10037 10230 10240 10030 13469 16735 16788

module dc_fifo_reg_core (
                                                    // inputs:
                                                     avalonst_sink_data,
                                                     avalonst_sink_valid,
                                                     rdclock,
                                                     rdreset_n,
                                                     wrclock,
                                                     wrreset_n,

                                                    // outputs:
                                                     avalonst_source_data,
                                                     avalonst_source_valid
                                                  )
;

  output  [ 31: 0] avalonst_source_data;
  output           avalonst_source_valid;
  input   [ 31: 0] avalonst_sink_data;
  input            avalonst_sink_valid;
  input            rdclock;
  input            rdreset_n;
  input            wrclock;
  input            wrreset_n;

wire    [ 31: 0] avalonst_sink_signals;
wire    [ 31: 0] avalonst_source_data;
wire    [ 31: 0] avalonst_source_signals;
reg              avalonst_source_valid;
wire    [ 31: 0] data;
wire    [ 31: 0] q;
wire             rdclk;
wire             rdempty;
wire             rdreq;
wire             wrclk;
wire             wrfull;
wire             wrreq;
  //the_dcfifo_with_controls, which is an e_instance

      .rdempty   (rdempty),
      .rdreq     (rdreq),
      .rdreset_n (rdreset_n),
      .wrclk     (wrclk),
      .wrfull    (wrfull),
      .wrreq     (wrreq),
      .wrreset_n (wrreset_n)
    );

  //in, which is an e_atlantic_slave
  //out, which is an e_atlantic_master
  assign avalonst_sink_signals = avalonst_sink_data;
  assign avalonst_source_data = avalonst_source_signals;
  assign wrreq = avalonst_sink_valid;
  assign data = avalonst_sink_signals;
  assign avalonst_source_signals = q;
  assign wrclk = wrclock;
  assign rdclk = rdclock;
  always @(posedge rdclock or negedge rdreset_n)
    begin
      if (rdreset_n == 0)
          avalonst_source_valid <= 0;
      else
        avalonst_source_valid <= !rdempty;
    end

  assign rdreq = !rdempty;

endmodule

  test_fifo_0_altera_avalon_fifo_181_hicnqoy_dcfifo_with_controls the_dcfifo_with_controls
    (
      .data      (data),


//Copy from Intel s10_hp_hb.pdf
module hyper_pipe #(
    parameter WIDTH = 1,

        if (NUM_PIPES == 0) begin
            assign dout = din;
        end else begin
            always @ (posedge clk)
                hp[0] <= din;
            for (i=1;i < NUM_PIPES;i++) begin : hregs
                always @ ( posedge clk) begin
                    hp[i] <= hp[i-1];
                end
            end
            assign dout = hp[NUM_PIPES-1];
        end
    endgenerate
endmodule

    parameter NUM_PIPES = 1)
(
    input clk,
    input [WIDTH-1:0] din,
    output [WIDTH-1:0] dout);


`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 12/03/2019 11:22:43 AM
// Design Name:
// Module Name: router_psum
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////

module router_psum #( parameter DATA_BITWIDTH = 16,
						parameter ADDR_BITWIDTH_GLB = 10,
						parameter ADDR_BITWIDTH_SPAD = 9,

						parameter int X_dim = 5,
                        parameter int Y_dim = 3,
                        parameter int kernel_size = 3,
                        parameter int act_size = 5,

//						parameter A_READ_ADDR =100,

//                        parameter A_LOAD_ADDR = 0,

						parameter PSUM_READ_ADDR = 0,
						parameter PSUM_LOAD_ADDR = 0
					)

					(	input clk,
						input reset,

						//for reading glb
						input [DATA_BITWIDTH-1 : 0] r_data_spad_psum[0:kernel_size-1],
						output logic [ADDR_BITWIDTH_GLB-1 : 0] w_addr_glb_psum,
						output logic write_en_glb_psum,

						//for writing to spad
						output logic [DATA_BITWIDTH-1 : 0] w_data_glb_psum,
//						output logic load_en_spad,

						//Input from PE cluster to write psums to glb
						input write_psum_ctrl

					);

		enum logic [2:0] {IDLE=3'b000, WRITE_GLB=3'b001, READ_PSUM=3'b010} state;

		logic [4:0] psum_count;
		logic [DATA_BITWIDTH-1 : 0] pe_psum[0:kernel_size-1];
		logic [2:0] iter;

		always@(posedge clk) begin
//			$display("State of router_psum: %s", state.name());
			if(reset) begin
				w_addr_glb_psum <= PSUM_LOAD_ADDR;
				psum_count <= 0;
				write_en_glb_psum <= 0;
				iter <= 0;
				state <= IDLE;
			end else begin
				case(state)
					IDLE:begin
						if(write_psum_ctrl) begin
							//write_en_glb_psum <= 1;
							state <= READ_PSUM;
						end else begin
							psum_count <= 0;
							write_en_glb_psum <= 0;
							w_addr_glb_psum <= PSUM_LOAD_ADDR;
							state <= IDLE;
						end
					end

					READ_PSUM:begin
						pe_psum <= r_data_spad_psum;
//						$display("Psum read in router:%d",pe_psum[0:kernel_size-1]);
						psum_count <= 0;
						state <= WRITE_GLB;
					end

					WRITE_GLB:begin
						write_en_glb_psum <= 1;
//						$display("Psum written to address %d; Iter is %d",w_addr_glb_psum, iter);
						if(psum_count == (kernel_size-1)) begin
							w_data_glb_psum <= pe_psum[psum_count];

								state <= IDLE;
							end else if(psum_count == 0) begin
								w_addr_glb_psum <= PSUM_LOAD_ADDR+iter*kernel_size;
								state <= WRITE_GLB;
							end else begin
								w_addr_glb_psum <= w_addr_glb_psum + 1;
								state <= WRITE_GLB;
							end

						end
					end
				endcase
			end
		end

endmodule

							psum_count <= 0;
							w_addr_glb_psum <= w_addr_glb_psum + 1;
							iter <= iter + 1;
							state <= IDLE;
						end else begin


`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 12/13/2019 04:47:06 AM
// Design Name:
// Module Name: router_pe_4_clusters
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////

module router_pe_4_clusters_tb();

	parameter DATA_WIDTH = 16;

	///////////////      WGHT ROUTER WEST 0      ///////////////////////////////////

	logic [3:0] router_mode_west_0_whgt;

	//Interface with West
	//Source ports
	logic [DATA_WIDTH-1:0] west_data_i_west_0_whgt;
	logic west_enable_i_west_0_whgt;

	//Destination ports
	logic [DATA_WIDTH-1:0] west_data_o_west_0_whgt;
	logic west_enable_o_west_0_whgt;

		///////////////      WGHT ROUTER WEST 1     ///////////////////////////////////

	logic [3:0] router_mode_west_1_whgt;

	//Interface with West
	//Source ports
	logic [DATA_WIDTH-1:0] west_data_i_west_1_whgt;
	logic west_enable_i_west_1_whgt;

	//Destination ports
	logic [DATA_WIDTH-1:0] west_data_o_west_1_whgt;
	logic west_enable_o_west_1_whgt;

			///////////////      WGHT ROUTER EAST 0    ///////////////////////////////////

	logic [3:0] router_mode_east_0_whgt;

	//Interface with East
	//Source ports
	logic [DATA_WIDTH-1:0] east_data_i_east_0_whgt;
	logic east_enable_i_east_0_whgt;

	//Destination ports
	logic [DATA_WIDTH-1:0] east_data_o_east_0_whgt;
	logic east_enable_o_east_0_whgt;

			///////////////      WGHT ROUTER EAST 1     ///////////////////////////////////

	logic [3:0] router_mode_east_1_whgt;

	//Interface with East
	//Source ports
	logic [DATA_WIDTH-1:0] east_data_i_east_1_whgt;
	logic east_enable_i_east_1_whgt;

	//Destination ports
	logic [DATA_WIDTH-1:0] east_data_o_east_1_whgt;
	logic east_enable_o_east_1_whgt;

	router_network4
		#(
			.DATA_WIDTH(DATA_WIDTH)
		)
	network_wght
		(
			.router_mode_west_0(router_mode_west_0_whgt),
			.router_mode_west_1(router_mode_west_1_whgt),
			.router_mode_east_0(router_mode_east_0_whgt),
			.router_mode_east_1(router_mode_east_1_whgt),

			//WEST 0
			//Source Ports
			.west_data_i_west_0(west_data_i_west_0_whgt),
			.west_enable_i_west_0(west_enable_i_west_0_whgt),

			//Destination ports
			.west_data_o_west_0(west_data_o_west_0_whgt),

			//WEST 1
			//Source Ports
			.west_data_i_west_1(west_data_i_west_1_whgt),
			.west_enable_i_west_1(west_enable_i_west_1_whgt),

			//Destination ports
			.west_data_o_west_1(west_data_o_west_1_whgt),
			.west_enable_o_west_1(west_enable_o_west_1_whgt),

			//EAST 0
			//Source Ports
			.east_data_i_east_0(east_data_i_east_0_whgt),
			.east_enable_i_east_0(east_enable_i_east_0_whgt),

			//Destination ports
			.east_data_o_east_0(east_data_o_east_0_whgt),
			.east_enable_o_east_0(east_enable_o_east_0_whgt),

			//east 1
			//Source Ports
			.east_data_i_east_1(east_data_i_east_1_whgt),
			.east_enable_i_east_1(east_enable_i_east_1_whgt),

			//Destination ports
			.east_data_o_east_1(east_data_o_east_1_whgt),
			.east_enable_o_east_1(east_enable_o_east_1_whgt)

		);

		///////////////      IACT ROUTER WEST 0      ///////////////////////////////////

	logic [3:0] router_mode_west_0_iact;

	//Interface with West
	//Source ports
	logic [DATA_WIDTH-1:0] west_data_i_west_0_iact;
	logic west_enable_i_west_0_iact;

	//Destination ports
	logic [DATA_WIDTH-1:0] west_data_o_west_0_iact;
	logic west_enable_o_west_0_iact;

		///////////////      IACT ROUTER WEST 1     ///////////////////////////////////

	logic [3:0] router_mode_west_1_iact;

	//Interface with West
	//Source ports
	logic [DATA_WIDTH-1:0] west_data_i_west_1_iact;
	logic west_enable_i_west_1_iact;

	//Destination ports
	logic [DATA_WIDTH-1:0] west_data_o_west_1_iact;
	logic west_enable_o_west_1_iact;

			///////////////      IACT ROUTER EAST 0    ///////////////////////////////////

	logic [3:0] router_mode_east_0_iact;

	//Interface with East
	//Source ports
	logic [DATA_WIDTH-1:0] east_data_i_east_0_iact;
	logic east_enable_i_east_0_iact;

	//Destination ports
	logic [DATA_WIDTH-1:0] east_data_o_east_0_iact;
	logic east_enable_o_east_0_iact;

			///////////////      IACT ROUTER EAST 1     ///////////////////////////////////

	logic [3:0] router_mode_east_1_iact;

	//Interface with East
	//Source ports
	logic [DATA_WIDTH-1:0] east_data_i_east_1_iact;
	logic east_enable_i_east_1_iact;

	//Destination ports
	logic [DATA_WIDTH-1:0] east_data_o_east_1_iact;
	logic east_enable_o_east_1_iact;

	router_network4
		#(
			.DATA_WIDTH(DATA_WIDTH)
		)
	network_iact
		(
			.router_mode_west_0(router_mode_west_0_iact),
			.router_mode_west_1(router_mode_west_1_iact),
			.router_mode_east_0(router_mode_east_0_iact),
			.router_mode_east_1(router_mode_east_1_iact),

			//WEST 0
			//Source Ports
			.west_data_i_west_0(west_data_i_west_0_iact),
			.west_enable_i_west_0(west_enable_i_west_0_iact),

			//Destination ports
			.west_data_o_west_0(west_data_o_west_0_iact),
			.west_enable_o_west_0(west_enable_o_west_0_iact),

			//WEST 1
			//Source Ports
			.west_data_i_west_1(west_data_i_west_1_iact),
			.west_enable_i_west_1(west_enable_i_west_1_iact),

			//Destination ports
			.west_data_o_west_1(west_data_o_west_1_iact),
			.west_enable_o_west_1(west_enable_o_west_1_iact),

			//EAST 0
			//Source Ports
			.east_data_i_east_0(east_data_i_east_0_iact),
			.east_enable_i_east_0(east_enable_i_east_0_iact),

			//Destination ports
			.east_data_o_east_0(east_data_o_east_0_iact),
			.east_enable_o_east_0(east_enable_o_east_0_iact),

			//east 1
			//Source Ports
			.east_data_i_east_1(east_data_i_east_1_iact),
			.east_enable_i_east_1(east_enable_i_east_1_iact),

			//Destination ports
			.east_data_o_east_1(east_data_o_east_1_iact),
			.east_enable_o_east_1(east_enable_o_east_1_iact)

		);

	logic clk, reset;

	/////// INST PE CLUSTER
	parameter ADDR_WIDTH = 9;

    parameter int X_dim = 3;
    parameter int Y_dim = 3;

    parameter int kernel_size = 3;
    parameter int act_size = 5;

//    logic [DATA_WIDTH-1:0] act_in;
//    logic [DATA_WIDTH-1:0] filt_in;

	logic start;
	logic load_en_wght, load_en_iact;

	logic compute_done;
	logic load_done;

	logic [DATA_WIDTH-1:0] pe_out[X_dim-1:0];
	logic [DATA_WIDTH-1:0] pe_out_west_0[X_dim-1:0];
	logic [DATA_WIDTH-1:0] pe_out_west_1[X_dim-1:0];
	logic [DATA_WIDTH-1:0] pe_out_east_0[X_dim-1:0];
	logic [DATA_WIDTH-1:0] pe_out_east_1[X_dim-1:0];

	// PE CLUSTER WEST 0
	PE_cluster #(
					.DATA_WIDTH(DATA_WIDTH),
					.ADDR_WIDTH(ADDR_WIDTH),

					.kernel_size(kernel_size),
					.act_size(act_size),

					.X_dim(X_dim),
					.Y_dim(Y_dim)
    			)
	pe_cluster_west_0
    			(
					.clk(clk),
				    .reset(reset),

				    .act_in(network_iact.west_data_o_west_0),
				    .filt_in(network_wght.west_data_o_west_0),

					.load_en_wght(load_en_wght),
					.load_en_act(load_en_iact),

					.start(start),
                    .pe_out(pe_out_west_0),
					.compute_done(compute_done),
					.load_done(load_done)
    			);

	// PE CLUSTER WEST 1
	PE_cluster #(
					.DATA_WIDTH(DATA_WIDTH),
					.ADDR_WIDTH(ADDR_WIDTH),

					.kernel_size(kernel_size),
					.act_size(act_size),

					.X_dim(X_dim),
					.Y_dim(Y_dim)
    			)
	pe_cluster_west_1
    			(
					.clk(clk),
				    .reset(reset),

				    .act_in(network_iact.west_data_o_west_1),
				    .filt_in(network_wght.west_data_o_west_1),

					.load_en_wght(load_en_wght),
					.load_en_act(load_en_iact),

					.start(start),
                    .pe_out(pe_out_west_1),
					.compute_done(),
					.load_done()
    			);

	// PE CLUSTER EAST 0
	PE_cluster #(
					.DATA_WIDTH(DATA_WIDTH),
					.ADDR_WIDTH(ADDR_WIDTH),

					.kernel_size(kernel_size),
					.act_size(act_size),

					.X_dim(X_dim),
					.Y_dim(Y_dim)
    			)
	pe_cluster_east_0
    			(
					.clk(clk),
				    .reset(reset),

				    .act_in(network_iact.east_data_o_east_0),
				    .filt_in(network_wght.east_data_o_east_0),

					.load_en_wght(load_en_wght),
					.load_en_act(load_en_iact),

					.start(start),
                    .pe_out(pe_out_east_0),
					.compute_done(),
					.load_done()
    			);

	// PE CLUSTER EAST 1
	PE_cluster #(
					.DATA_WIDTH(DATA_WIDTH),
					.ADDR_WIDTH(ADDR_WIDTH),

					.kernel_size(kernel_size),
					.act_size(act_size),

					.X_dim(X_dim),
					.Y_dim(Y_dim)
    			)
	pe_cluster_east_1
    			(
					.clk(clk),
				    .reset(reset),

				    .act_in(network_iact.east_data_o_east_1),
				    .filt_in(network_wght.east_data_o_east_1),

					.load_en_wght(load_en_wght),
					.load_en_act(load_en_iact),

					.start(start),
                    .pe_out(pe_out_east_1),
					.compute_done(),
					.load_done()
    			);

/* 	logic [DATA_WIDTH-1:0] temp;
	assign temp = wght_router_east_0.south_data_o;

	logic temp_en;
	assign temp_en = wght_router_east_0.south_enable_o; */

/* 	initial begin
		for(int i=0; i<20; i++) begin
			west_data_i_west_0 = i;
//			south_data_i = i*2;
//			west_data_i = i*5;
//			east_data_i = i*100;
			#50;
		end
	end */

	integer clk_prd = 10;
	logic [DATA_WIDTH-1:0] cluster_out_1[0:8];

	always begin
		clk = 0; #(clk_prd/2);
		clk = 1; #(clk_prd/2);
		//0.1GHz
	end

	enum logic [3:0] {ALL=0, NORTH=1, SOUTH=2, WEST=3, EAST=4,
						EASTNORTH=5, EASTSOUTH=6, EASTWEST=7,
						WESTNORTH=8, WESTSOUTH=9, WESTEAST=10} direction;

	initial begin

		reset = 1; #30;
		reset = 0;
		start = 0;

		router_mode_west_0_whgt = ALL;

		router_mode_east_0_whgt = EASTSOUTH;

		router_mode_west_1_whgt = WEST;

		router_mode_east_1_whgt = EAST;

		#100;

		$display("\n\nLoading Begins: Weights.....\n\n");

		load_en_wght = 1;
		west_enable_i_west_0_whgt = 1;

		for(int i=0; i<kernel_size**2; i++) begin
			west_data_i_west_0_whgt = i+1;
			#(clk_prd);
			load_en_wght = 0;
		end

		router_mode_east_0_iact = EAST;
		router_mode_east_1_iact = EAST;

		router_mode_west_0_iact = WEST;
		router_mode_west_1_iact = WEST;

		#100;

		$display("\n\nLoading Begins: Iacts.....\n\n");

		load_en_iact = 1;

		east_enable_i_east_0_iact = 1;
		east_enable_i_east_1_iact = 1;

		west_enable_i_west_0_iact = 1;
		west_enable_i_west_1_iact = 1;

		for(int i=1; i<act_size**2+1; i++) begin
			east_data_i_east_0_iact = i;
			east_data_i_east_1_iact = i+2;
			west_data_i_west_0_iact = i*5;
			west_data_i_west_1_iact = i+3;
			#(clk_prd);
			load_en_iact = 0;
		end

		#50;

		assign pe_out = pe_out_east_0;

		start = 1; #25;
		$display("\n\nReading & Computing Begins.....\n\n");
		start = 0;

		wait (compute_done == 1);

		$display("\n\nFinal PSUM of Iteration 1");
 		for(int i=0; i<X_dim; i++) begin
			cluster_out_1[i] = pe_out[X_dim-i-1];
			$display("\npsum from column %d is:%d",i+1,pe_out[X_dim-i-1]);
		end

		#40;
		start = 1; #25;
		$display("\n\nReading & Computing Begins for iter 2.....\n\n");
		start = 0;

				wait (compute_done == 1);
		$display("\n\nFinal PSUM of Iteration 2:");
 		for(int i=0; i<X_dim; i++) begin
			cluster_out_1[i+3] = pe_out[X_dim-i-1];
			$display("\npsum from column %d is:%d",i+1,pe_out[X_dim-i-1]);
		end

		#40;
		start = 1; #25;
		$display("\n\nReading & Computing Begins for iter 3.....\n\n");
		start = 0;

		wait (compute_done == 1);
		$display("\n\nFinal PSUM of Iteration 3:");
 		for(int i=0; i<X_dim; i++) begin
		cluster_out_1[i+6] = pe_out[X_dim-i-1];
			$display("\npsum from column %d is:%d",i+1,pe_out[X_dim-i-1]);
		end

		$display("\tFinal psums of Cluster 1:\n");
		for(int a=0; a<kernel_size**2; a++) begin
			$display("\t\t %d \n",cluster_out_1[a]);
		end

		$display("\tTotal #cycles taken: %d",cycles);
		$stop;

	end

		int cycles;
		// track # of cycles
	always @(posedge clk)
	begin
		if (reset)
			cycles = 0;
		else
			cycles = cycles + 1;
	end

endmodule