<commit_before>// vim : set fileencoding=utf-8 expandtab noai ts=4 sw=4 :
/// @addtogroup ahbctrl
/// @{
/// @file ahbctrl.cpp
/// AHB address decoder. The decoder collects all AHB request from the masters
/// and forwards them to the appropriate slave.
///
/// @date 2010-2014
/// @copyright All rights reserved.
///            Any reproduction, use, distribution or disclosure of this
///            program, without the express, prior written consent of the
///            authors is strictly prohibited.
/// @author <NAME>
///

#include <string>
#include <map>
#include <utility>
#include "gaisler/ahbctrl/ahbctrl.h"
#include "core/common/vendian.h"
#include "core/common/verbose.h"
#include "core/common/sr_registry.h"

SR_HAS_MODULE(AHBCtrl);

// Constructor of class AHBCtrl
AHBCtrl::AHBCtrl(
    ModuleName nm,  // SystemC name
    unsigned int ioaddr,   // The MSB address of the I/O area
    unsigned int iomask,   // The I/O area address mask
    unsigned int cfgaddr,  // The MSB address of the configuration area (PNP)
    unsigned int cfgmask,  // The address mask of the configuration area
    bool rrobin,           // 1 - round robin, 0 - fixed priority arbitration (only AT)
    bool split,            // Enable support for AHB SPLIT response (only AT)
    unsigned int defmast,  // ID of the default master
    bool ioen,             // AHB I/O area enable
    bool fixbrst,          // Enable support for fixed-length bursts
    bool fpnpen,           // Enable full decoding of PnP configuration records
    bool mcheck,           // Check if there are any intersections between core memory regions
    bool pow_mon,          // Enable power monitoring
    AbstractionLayer ambaLayer) :
  BaseModule<DefaultBase>(nm),
  ahbIN("ahbIN", amba::amba_AHB, ambaLayer, false),
  ahbOUT("ahbOUT", amba::amba_AHB, ambaLayer, false),
  snoop("snoop"),
  g_ioaddr("ioaddr", ioaddr, m_generics),
  g_iomask("iomask", iomask, m_generics),
  g_cfgaddr("cfgaddr", cfgaddr, m_generics),
  g_cfgmask("cfgmask", cfgmask, m_generics),
  g_rrobin("rrobin", rrobin, m_generics),
  g_split("split", split, m_generics),
  g_defmast("defmast", defmast, m_generics),
  g_ioen("ioen", ioen, m_generics),
  g_fixbrst("fixbrst", fixbrst, m_generics),
  g_fpnpen("fpnpen", fpnpen, m_generics),
  g_mcheck("mcheck", mcheck, m_generics),
  g_pow_mon("pow_mon",pow_mon, m_generics),
  arbiter_eval_delay(1, SC_PS),
  busy(false),
  robin(0),
  address_bus_owner(-1),
  m_AcceptPEQ("AcceptPEQ"),
  m_RequestPEQ("RequestPEQ"),
  m_ResponsePEQ("ResponsePEQ"),
  m_EndResponsePEQ("EndResponsePEQ"),
  m_total_wait("total_wait", SC_ZERO_TIME, m_counters),
  m_arbitrated("arbitrated", 0ull, m_counters),
  m_max_wait("maximum_waiting_time", SC_ZERO_TIME, m_counters),
  m_max_wait_master("maximum_waiting_master_id", defmast, m_counters),
  m_idle_count("idle_cycles", 0ull, m_counters),
  m_total_transactions("total_transactions", 0ull, m_counters),
  m_right_transactions("successful_transactions", 0ull, m_counters),
  m_writes("bytes_written", 0ull, m_counters),
  m_reads("bytes_read", 0ull, m_counters),
  is_lock(false),
  lock_master(0),
  m_ambaLayer(ambaLayer),
  sta_power_norm("sta_power_norm", 10714285.71, m_power),     // Normalized static power input
  int_power_norm("int_power_norm", 0.0, m_power),     // Normalized dyn power input (activation indep.)
  dyn_read_energy_norm("dyn_read_energy_norm", 9.10714e-10, m_power),     // Normalized read energy input
  dyn_write_energy_norm("dyn_write_energy_norm", 9.10714e-10, m_power),     // Normalized write energy input
  sta_power("sta_power", 0.0, m_power),     // Static power output
  int_power("int_power", 0.0, m_power),     // Internal power of module (dyn. switching independent)
  swi_power("swi_power", 0.0, m_power),     // Switching power of module
  power_frame_starting_time("power_frame_starting_time", SC_ZERO_TIME, m_power),
  dyn_read_energy("dyn_read_energy", 0.0, m_power),     // Energy per read access
  dyn_write_energy("dyn_write_energy", 0.0, m_power),     // Energy per write access
  dyn_reads("dyn_reads", 0ull, m_power),     // Read access counter for power computation
  dyn_writes("dyn_writes", 0ull, m_power) {   // Write access counter for power computation

  // Initialize slave and master table
  // (Pointers to deviceinfo fields will be set in start_of_simulation)
  for (int i = 0; i < 64; i++) {
    mSlaves[i] = NULL;
    mMasters[i] = NULL;
  }

  if (ambaLayer == amba::amba_LT) {
    // Register tlm blocking transport function
    ahbIN.register_b_transport(this, &AHBCtrl::b_transport);
    ahbIN.register_get_direct_mem_ptr((AHBCtrl *)this, &AHBCtrl::get_direct_mem_ptr);
    ahbOUT.register_invalidate_direct_mem_ptr((AHBCtrl *)this, &AHBCtrl::invalidate_direct_mem_ptr);

  }

  // Register non blocking transport functions
  if (ambaLayer == amba::amba_AT) {
    memset(request_map, 0, 16 * sizeof (connection_t));
    memset(response_map, 0, 16 * sizeof (connection_t));

    // Register tlm non blocking transport forward path
    ahbIN.register_nb_transport_fw(this, &AHBCtrl::nb_transport_fw, 0);
    // Register tlm non blocking transport backward path
    ahbOUT.register_nb_transport_bw(this, &AHBCtrl::nb_transport_bw, 0);
    // Register arbiter thread
    SC_THREAD(arbitrate);

    SC_THREAD(AcceptThread);

    // Register request thread
    SC_THREAD(RequestThread);

    // Register response thread
    SC_THREAD(ResponseThread);

    SC_THREAD(EndResponseThread);
  }

  // Register debug transport
  ahbIN.register_transport_dbg(this, &AHBCtrl::transport_dbg);

  // Register power callback functions
  if (g_pow_mon) {
    GC_REGISTER_TYPED_PARAM_CALLBACK(&sta_power, gs::cnf::pre_read, AHBCtrl, sta_power_cb);
    GC_REGISTER_TYPED_PARAM_CALLBACK(&int_power, gs::cnf::pre_read, AHBCtrl, int_power_cb);
    GC_REGISTER_TYPED_PARAM_CALLBACK(&swi_power, gs::cnf::pre_read, AHBCtrl, swi_power_cb);
  }

  requests_pending = 0;
  AHBCtrl::init_generics();
  srInfo()
    ("ioaddr", ioaddr)
    ("iomask", iomask)
    ("cfgaddr", cfgaddr)
    ("cfgmask", cfgmask)
    ("rrobin", rrobin)
    ("split", split)
    ("defmast", defmast)
    ("ioen", ioen)
    ("fixbrst", fixbrst)
    ("fpnpen", fpnpen)
    ("mcheck", mcheck)
    ("pow_mon", pow_mon)
    ("ambaLayer", ambaLayer)
    ("Created an AHBCtrl with this parameters");

  // initialize the slave_map_cache with some bogus numbers which will trigger MISS
  slave_info_t tmp;
  tmp.hindex = 0;
  tmp.hmask  = ~0;
  tmp.binding = ~0;
  slave_map_cache = std::pair<uint32_t, slave_info_t>(~0, tmp);
}

// Reset handler
void AHBCtrl::dorst() {
  // nothing to do
}

// Destructor
AHBCtrl::~AHBCtrl() {
  GC_UNREGISTER_CALLBACKS();
}

void AHBCtrl::init_generics() {
  g_ioaddr.add_properties()
    ("name", "AHB IO Area Address")
    ("range", "0..0xFFF")
    ("vhdl_name","ioaddr")
    ("base","hex")
    ("The MSB address of the I/O area. Sets the 12 most significant bits in the 32-bit AHB address (e.g. 31 downto 20).");

  g_iomask.add_properties()
    ("name", "AHB IO Area Mask")
    ("range", "0..0xFFF")
    ("vhdl_name","iomask")
    ("base","hex")
    ("The I/O area address mask. Sets the size of the I/O area and the start address together with ioaddr.");

  g_cfgaddr.add_properties()
    ("name", "AHB CFG Area Address")
    ("range", "0..0xFF0")
    ("vhdl_name", "cfgaddr")
    ("base","hex")
    ("The MSB address of the confgiuration area. Sets 12 bits in the 32-bit AHB address (19 downto 8)");

  g_cfgmask.add_properties()
    ("name", "AHB CFG Area Mask")
    ("range", "0..0xFF0")
    ("vhdl_name","cfgmask")
    ("base","hex")
    ("The address mask of the configuration area. Sets the size of the configuration area and "
     "the start address together with cfgaddr.");

  g_rrobin.add_properties()
    ("name", "Round robin arbitration.")
    ("vhdl_name", "rrobin")
    ("True  - round-robin arbitration\n"
     "False - priority arbitration (highest master id has highest priority)\n"
     "Arbitration is only supported in AT mode!! (no effect on LT simulation)");

  g_defmast.add_properties()
    ("name", "Default AHB Master")
    ("range", "0..16")
    ("vhdl_name","defmast")
    ("The default AHB master index (bus parking). This feature is not modeled at transaction level."
     "Parameter is only used for reporting.");

  g_ioen.add_properties()
    ("name", "AHB IO area enable")
    ("vhdl_name","ioen")
    ("AHB I/O area enable. Set to 0 to disable the I/O area.");

  g_fixbrst.add_properties()
    ("name", "Enable support for fixed burst length.")
    ("vhdl_name", "fixbrst")
    ("Enable support for fixed-length bursts at RTL-Level. This feature is not supported at transaction-level.");

  g_split.add_properties()
    ("name", "Enable support for split transactions in AT models")
    ("vhdl_name","split")
    ("Enables support for AHB SPLIT response at RTL-Level. This feature is not supported at transaction-level.");

  g_fpnpen.add_properties()
    ("name", "Enable full decoding of the PnP configuration records")
    ("vhdl_name","fpnpen")
    ("When disabled the user-defined register in the PnP configuration records are not mapped in the configuration area.");

  g_mcheck.add_properties()
    ("name", "Intersection checking")
    ("Check if there are any intersections between core memory areas. If two areas intersect an assert with level failure will be generated.");

  g_pow_mon.add_properties()
    ("name", "Power Monitoring")
    ("If true enable power monitoring");

}

// Helper function for creating slave map decoder entries
void AHBCtrl::setAddressMap(const uint32_t binding, const uint32_t hindex, const uint32_t haddr, const uint32_t hmask) {
  slave_info_t tmp;

  tmp.hindex = hindex;
  tmp.hmask  = hmask;
  tmp.binding = binding;
  
  // Create slave map entry from slave ID and address range descriptor (slave_info_t)
  slave_map.insert(std::pair<uint32_t, slave_info_t>(haddr, tmp));
}

// Find slave index by address
int AHBCtrl::get_index(const uint32_t address) {
  m_total_transactions++;

  // Use 12 bit segment address for decoding
  uint32_t addr = address >> 20;

  // check cache, otherwise search within map
  slave_info_t info = slave_map_cache.second;
  if ( ! ((addr ^ slave_map_cache.first) & info.hmask) ) {
    // There may be up to four BARs per device.
    // Only return device ID.
    m_right_transactions++;
    return (info.binding) >> 2;
  }

  std::map<uint32_t, slave_info_t>::iterator it = --(slave_map.upper_bound( addr ));
  info = it->second;
  if ( ! ((addr ^ it->first) & info.hmask) ) {
    // MISS in the cache: update cache
    slave_map_cache = *it;
  
    // There may be up to four BARs per device.
    // Only return device ID.
    m_right_transactions++;
    return (info.binding) >> 2;
  }

  // no slave found
  return -1;
}

// Returns a PNP register from the slave configuration area
unsigned int AHBCtrl::getPNPReg(const uint32_t address) {
  uint32_t result;

  m_total_transactions++;

  if ((address >= 0xfffffff0)&&(address <= 0xfffffff3)) return 0x30100000;

  // Calculate address offset in configuration area (slave info starts from 0x800)
  unsigned int addr   = address - (
      ((static_cast<uint32_t>(g_ioaddr) << 20) |
       (static_cast<uint32_t>(g_cfgaddr) << 8)) &
       ((static_cast<uint32_t>(g_iomask) << 20) | (static_cast<uint32_t>(g_cfgmask) << 8)));
  srDebug()("addr", addr)("Accessing PNP area");

  // Slave area
  if (addr >= 0x800) {
    addr -= 0x800;

    // Calculate index of the device in mSlaves pointer array (32 byte per device)
    // Shift first to get word addresses.
    unsigned int device = (addr >> 2) >> 3;
    // Calculate offset within device information
    unsigned int offset = (addr >> 2) & 0x7;

    srDebug()("addr", addr)("device", device)("offset", offset)("Access mSlaves");

    if (device >= num_of_slave_bindings) {
      srWarn()("addr", addr)("device", device)("slavecount", num_of_slave_bindings)("Access to unregistered PNP Slave Register!");
      return 0;
    }

    // If the device exists, access deviceinfo (otherwise 0)
    if (mSlaves[device] != NULL) {
      result =  mSlaves[device][offset];
    } else {
      result = 0;
    }

#ifdef LITTLE_ENDIAN_BO
    swap_Endianess(result);
#endif
    m_right_transactions++;
    return result;
  } else {
    // Calculate index of the device in mMasters pointer array (32 byte per device)
    unsigned int device = (addr >> 2) >> 3;
    // Calculate offset within device information
    unsigned int offset = (addr >> 2) & 0x7;

    if (device >= num_of_master_bindings) {
      srWarn()("addr", addr)("device", device)("slavecount", num_of_master_bindings)("Access to unregistered PNP Slave Register!");
      return 0;
    }

    if (mMasters[device] != NULL) {
      result = mMasters[device][offset];
    } else {
      result = 0;
    }

#ifdef LITTLE_ENDIAN_BO
    swap_Endianess(result);
#endif
    return result;
  }
}

// TLM blocking transport function (multi-sock)
void AHBCtrl::b_transport(uint32_t id,
    tlm::tlm_generic_payload &trans,  // NOLINT(runtime/references)
    sc_core::sc_time &delay) {        // NOLINT(runtime/references)
  // master-address pair for dcache snooping
  t_snoop snoopy;

  uint32_t a = 0;
  socket_t *other_socket;
  sc_core::sc_object *slvobj = NULL;
  sc_core::sc_object *mstobj = NULL;

  //if (v::debug) {
    other_socket = ahbIN.get_other_side(id, a);
    mstobj = other_socket->get_parent();
  //}

  srDebug()("pointer", reinterpret_cast<size_t>(&trans))("busy", busy)("is_lock", is_lock)("id", id)("lock_master", lock_master)("delay", delay)(__PRETTY_FUNCTION__);
  // Bus occupied or locked by other master
  while (busy || (is_lock && (id != lock_master))) {
    wait(clock_cycle);
  }

  busy = true;
  is_lock = ahbIN.get_extension<amba::amba_lock>(lock, trans);

  lock_master = id;

  // Collect transport statistics
  transport_statistics(trans);

  // Extract address from payload
  uint32_t addr   = trans.get_address();
  // Extract length from payload
  uint32_t length = trans.get_data_length();
  srDebug()("pointer", reinterpret_cast<size_t>(&trans))("busy", busy)("is_lock", is_lock)("id", id)("lock_master", lock_master)("addr", addr)("delay", delay)(__PRETTY_FUNCTION__);
  
  // Is this an access to configuration area
  if (g_fpnpen && ((
          (addr ^ ((static_cast<uint32_t>(g_ioaddr) << 20) |
                   (static_cast<uint32_t>(g_cfgaddr) << 8))) &
          ((static_cast<uint32_t>(g_iomask) << 20) |
           (static_cast<uint32_t>(g_cfgmask) << 8))) == 0)) {
    // Configuration area is read only
    if (trans.get_command() == tlm::TLM_READ_COMMAND) {
      // addr = addr - (((g_ioaddr << 20) | (g_cfgaddr << 8)) & ((g_iomask << 20) | (g_cfgmask << 8)));
      // Extract data pointer from payload
      uint8_t *data  = trans.get_data_ptr();

      // Get registers from config area
      for (uint32_t i = 0; i < length; i++) {
        // uint32_t word = (addr + i) >> 2;
        uint32_t byte = (addr + i) & 0x3;
        uint32_t reg = getPNPReg(addr + i);
        data[i] = (reinterpret_cast<uint8_t *>(&reg))[byte];

        // one cycle delay per 32bit register
        delay += clock_cycle;
      }

      // and return
      trans.set_response_status(tlm::TLM_OK_RESPONSE);

      wait(delay);
      delay = SC_ZERO_TIME;

      msclogger::return_backward(this, &ahbIN, &trans, tlm::TLM_COMPLETED, delay);

      busy = false;
      return;
    } else {
      v::error << name() << " Forbidden write to AHBCTRL configuration area (PNP)!" << v::endl;
      trans.set_response_status(tlm::TLM_COMMAND_ERROR_RESPONSE);

      delay = SC_ZERO_TIME;

      msclogger::return_backward(this, &ahbIN, &trans, tlm::TLM_COMPLETED, delay);

      busy = false;
      return;
    }
  }

  // Find slave by address / returns slave index or -1 for not mapped
  int index = get_index(trans.get_address());

  // For valid slave index
  if (index >= 0) {
    //if (v::debug) {
      other_socket = ahbOUT.get_other_side(index, a);
      slvobj = other_socket->get_parent();

      srDebug()("addr", trans.get_address())("master", mstobj->name())("slave", slvobj->name())("AHBRequest, b_transport");
    //}

    // Broadcast master_id and address for dcache snooping
    if (trans.get_command() == tlm::TLM_WRITE_COMMAND) {
      snoopy.master_id  = id;
      snoopy.address = addr;
      snoopy.length = length;

      // Send to signal socket
      snoop.write(snoopy);
    }

    // Power event start
    // const char *event_name = "ahb_trans";
    // size_t data_int = (size_t)trans.get_data_ptr();
    // uint32_t id = data_int & 0xFFFFFFFF;
    delay+=clock_cycle;

    srDebug()("addr", trans.get_address())("master", mstobj->name())("slave", slvobj->name())("Outbound b_tranport");
    // Forward request to the selected slave
    ahbOUT[index]->b_transport(trans, delay);

    srDebug()("addr", trans.get_address())("master", mstobj->name())("slave", slvobj->name())("Outbound b_tranport called");
    // v::debug << name() << "Delay after return from slave: " << delay << v::endl;

    // Power event end
    // PM::send(this,event_name,0,sc_time_stamp()+delay,id,g_pow_mon);

    wait(delay);
    delay = SC_ZERO_TIME;

    busy = false;
    return;
  } else {
    other_socket = ahbIN.get_other_side(id, a);
    mstobj = other_socket->get_parent();

    v::error << name() << "AHB Request 0x" << hex << v::setfill('0')
             << v::setw(8) << trans.get_address() << ", from master:"
             << mstobj->name() << ": Unmapped address space." << endl;
    srWarn()("addr", trans.get_address())("master", mstobj->name())("AHBRequest, b_transport to unmapped address space");

    // Invalid index
    trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);

    wait(delay);
    delay = SC_ZERO_TIME;

    busy = false;
    return;
  }
}

// Non-blocking forward transport function for ahb_slave multi-socket
// A master may send BEGIN_REQ or END_RESP. The model replies with
// TLM_ACCEPTED or TLM_COMPLETED, respectively.
tlm::tlm_sync_enum AHBCtrl::nb_transport_fw(
    uint32_t master_id,
    tlm::tlm_generic_payload &trans,  // NOLINT(runtime/references)
    tlm::tlm_phase &phase,            // NOLINT(runtime/references)
    sc_core::sc_time &delay) {        // NOLINT(runtime/references)
  srDebug()("pointer", reinterpret_cast<size_t>(&trans))("phase", phase)("delay", delay)(__PRETTY_FUNCTION__);

  if (phase == tlm::BEGIN_REQ) {
    // Increment reference counter
    trans.acquire();

    // Validate master_id extension
    amba::amba_id *m_id;
    ahbIN.validate_extension<amba::amba_id>(trans);
    ahbIN.get_extension<amba::amba_id>(m_id, trans);
    m_id->value = master_id;

    srDebug()("pointer", reinterpret_cast<size_t>(&trans))("master_id", master_id)("refcount", trans.get_ref_count());

    // In communication with the ahbctrl, BEGIN_REQ marks the begin of the bus request.
    // Transaction is send to request thread, where it is going to be decoded and put in PENDING state.
    m_AcceptPEQ.notify(trans, delay);

    // Collect transport statistics
    transport_statistics(trans);

    // Reset delay
    delay = SC_ZERO_TIME;

    // Draw msc retrun arrow
    msclogger::return_backward(this, &ahbIN, &trans, tlm::TLM_ACCEPTED, delay, master_id);

    // Transaction accepted
    return tlm::TLM_ACCEPTED;
  } else if (phase == tlm::END_RESP) {
    // Let the response thread know that END_RESP
    // came in on the forward path.
    m_EndResponsePEQ.notify(trans, delay);

    msclogger::return_backward(this, &ahbIN, &trans, tlm::TLM_COMPLETED, delay, master_id);

    delay = SC_ZERO_TIME;

    // Transaction completed
    return tlm::TLM_COMPLETED;
  } else {
    srError()("Illegal phase in call to nb_transport_fw!");
    trans.set_response_status(tlm::TLM_COMMAND_ERROR_RESPONSE);
  }

  msclogger::return_backward(this, &ahbIN, &trans, tlm::TLM_ACCEPTED, delay, master_id);

  return tlm::TLM_COMPLETED;
}

// Non-blocking backward transport function for ahb_master multi-socket
// A slave may send END_REQ or BEGIN_RESP. In both cases the model replies
// with TLM_ACCEPTED.
tlm::tlm_sync_enum AHBCtrl::nb_transport_bw(
    uint32_t id,
    tlm::tlm_generic_payload &trans,  // NOLINT(runtime/references)
    tlm::tlm_phase &phase,            // NOLINT(runtime/references)
    sc_core::sc_time &delay) {        // NOLINT(runtime/references)
  srDebug()("pointer", reinterpret_cast<size_t>(&trans))("phase", phase)("delay", delay)(__PRETTY_FUNCTION__);

  // The slave has sent END_REQ
  if (phase == tlm::END_REQ) {
    m_RequestPEQ.notify(trans, delay);
    delay = SC_ZERO_TIME;
  } else if (phase == amba::DATA_SPLIT) {
    // Ignore DATA_SPLIT!
    // Current version of AHBCTRL does not rearbitrate on DATA_SPLIT!
    // Slave expected to continue with BEGIN_RESP.

    // New response - goes into response PEQ
  } else if (phase == tlm::BEGIN_RESP) {
    m_ResponsePEQ.notify(trans, delay);
    delay = SC_ZERO_TIME;
  } else {
    srError()("Invalid phase in call to nb_transport_bw!");
    trans.set_response_status(tlm::TLM_COMMAND_ERROR_RESPONSE);
  }

  msclogger::return_forward(this, &ahbOUT, &trans, tlm::TLM_ACCEPTED, delay, id);

  return tlm::TLM_ACCEPTED;
}

// Helper function for printing requests
void AHBCtrl::print_requests() {
  v::info << name() << " ---------------------------------------------------- " << v::endl;
  v::info << name() << "NEW ARBITER CYCLE:" << v::endl;

  for (int i = 0; i < 16; i++) {
    v::info << name() << "Master " << i << " Transaction: " <<  request_map[i].trans << " State: " <<
      request_map[i].state << v::endl;
  }

  v::info << name() << " ---------------------------------------------------- " << v::endl;
}

// Arbitration thread (AT only)
void AHBCtrl::arbitrate() {
  tlm::tlm_phase phase;
#ifndef NDEBUG // assert specific macro
  tlm::tlm_sync_enum status;
#endif
  sc_core::sc_time delay;

  payload_t *trans = NULL;
  int slave_id = 0;

  sc_core::sc_time tmp;
  sc_core::sc_time waiting_time;
  sc_core::sc_time request_delay;
  sc_core::sc_time response_delay;

  wait(1, SC_PS);

  // Arbiter phase shift:
  // --------------------
  while (true) {
    wait(clock_cycle);

    // print_requests();

    // Last address of current transfer must have been sampled.
    // Last data sample is on the way.
    // v::debug << name() << " address_bus_owner: " << address_bus_owner << " data_bus_state: "
    //          << data_bus_state << v::endl;
    // if (sc_time_stamp() > sc_core::sc_time(83880, SC_NS)) sc_stop();

    if ((address_bus_owner == -1) && ((data_bus_state == RESPONSE) || (data_bus_state == IDLE))) {
      // Priority arbitration
      if (g_rrobin == 0) {
        if (!is_lock) {
          // Master with the highest ID has the highest priority
          for (int i = 15; i >= 0; i--) {
            if (request_map[i].state == TRANS_PENDING) {
              address_bus_owner = i;
              request_map[i].state = TRANS_SCHEDULED;
              trans = request_map[i].trans;
              slave_id = request_map[i].slave_id;

              is_lock = ahbIN.get_extension<amba::amba_lock>(lock, *trans);
              lock_master = i;

              break;
            }
          }
        } else {
          if (request_map[lock_master].state == TRANS_PENDING) {
            address_bus_owner = lock_master;
            request_map[lock_master].state = TRANS_SCHEDULED;
            trans = request_map[lock_master].trans;
            slave_id = request_map[lock_master].slave_id;

            is_lock = ahbIN.get_extension<amba::amba_lock>(lock, *trans);

            break;
          }
        }

        // Round-robin
      } else {
        if (!is_lock) {
          for (uint32_t i = 0; i < num_of_master_bindings; i++) {
            robin = (robin + 1) % num_of_master_bindings;

            srDebug()("robin", robin)(__PRETTY_FUNCTION__);

            if (request_map[robin].state == TRANS_PENDING) {
              srDebug()("robin", robin)("Selected for robin");

              address_bus_owner = robin;
              request_map[robin].state = TRANS_SCHEDULED;
              trans = request_map[robin].trans;
              slave_id = request_map[robin].slave_id;

              is_lock = ahbIN.get_extension<amba::amba_lock>(lock, *trans);

              lock_master = robin;

              break;
            }
          }
        } else {
          if (request_map[lock_master].state == TRANS_PENDING) {
            srDebug()("robin", robin)("Selected for robin");<commit_msg>Lillian discovered a loose break<commit_after>// vim : set fileencoding=utf-8 expandtab noai ts=4 sw=4 :
/// @addtogroup ahbctrl
/// @{
/// @file ahbctrl.cpp
/// AHB address decoder. The decoder collects all AHB request from the masters
/// and forwards them to the appropriate slave.
///
/// @date 2010-2014
/// @copyright All rights reserved.
///            Any reproduction, use, distribution or disclosure of this
///            program, without the express, prior written consent of the
///            authors is strictly prohibited.
/// @author <NAME>
///

#include <string>
#include <map>
#include <utility>
#include "gaisler/ahbctrl/ahbctrl.h"
#include "core/common/vendian.h"
#include "core/common/verbose.h"
#include "core/common/sr_registry.h"

SR_HAS_MODULE(AHBCtrl);

// Constructor of class AHBCtrl
AHBCtrl::AHBCtrl(
    ModuleName nm,  // SystemC name
    unsigned int ioaddr,   // The MSB address of the I/O area
    unsigned int iomask,   // The I/O area address mask
    unsigned int cfgaddr,  // The MSB address of the configuration area (PNP)
    unsigned int cfgmask,  // The address mask of the configuration area
    bool rrobin,           // 1 - round robin, 0 - fixed priority arbitration (only AT)
    bool split,            // Enable support for AHB SPLIT response (only AT)
    unsigned int defmast,  // ID of the default master
    bool ioen,             // AHB I/O area enable
    bool fixbrst,          // Enable support for fixed-length bursts
    bool fpnpen,           // Enable full decoding of PnP configuration records
    bool mcheck,           // Check if there are any intersections between core memory regions
    bool pow_mon,          // Enable power monitoring
    AbstractionLayer ambaLayer) :
  BaseModule<DefaultBase>(nm),
  ahbIN("ahbIN", amba::amba_AHB, ambaLayer, false),
  ahbOUT("ahbOUT", amba::amba_AHB, ambaLayer, false),
  snoop("snoop"),
  g_ioaddr("ioaddr", ioaddr, m_generics),
  g_iomask("iomask", iomask, m_generics),
  g_cfgaddr("cfgaddr", cfgaddr, m_generics),
  g_cfgmask("cfgmask", cfgmask, m_generics),
  g_rrobin("rrobin", rrobin, m_generics),
  g_split("split", split, m_generics),
  g_defmast("defmast", defmast, m_generics),
  g_ioen("ioen", ioen, m_generics),
  g_fixbrst("fixbrst", fixbrst, m_generics),
  g_fpnpen("fpnpen", fpnpen, m_generics),
  g_mcheck("mcheck", mcheck, m_generics),
  g_pow_mon("pow_mon",pow_mon, m_generics),
  arbiter_eval_delay(1, SC_PS),
  busy(false),
  robin(0),
  address_bus_owner(-1),
  m_AcceptPEQ("AcceptPEQ"),
  m_RequestPEQ("RequestPEQ"),
  m_ResponsePEQ("ResponsePEQ"),
  m_EndResponsePEQ("EndResponsePEQ"),
  m_total_wait("total_wait", SC_ZERO_TIME, m_counters),
  m_arbitrated("arbitrated", 0ull, m_counters),
  m_max_wait("maximum_waiting_time", SC_ZERO_TIME, m_counters),
  m_max_wait_master("maximum_waiting_master_id", defmast, m_counters),
  m_idle_count("idle_cycles", 0ull, m_counters),
  m_total_transactions("total_transactions", 0ull, m_counters),
  m_right_transactions("successful_transactions", 0ull, m_counters),
  m_writes("bytes_written", 0ull, m_counters),
  m_reads("bytes_read", 0ull, m_counters),
  is_lock(false),
  lock_master(0),
  m_ambaLayer(ambaLayer),
  sta_power_norm("sta_power_norm", 10714285.71, m_power),     // Normalized static power input
  int_power_norm("int_power_norm", 0.0, m_power),     // Normalized dyn power input (activation indep.)
  dyn_read_energy_norm("dyn_read_energy_norm", 9.10714e-10, m_power),     // Normalized read energy input
  dyn_write_energy_norm("dyn_write_energy_norm", 9.10714e-10, m_power),     // Normalized write energy input
  sta_power("sta_power", 0.0, m_power),     // Static power output
  int_power("int_power", 0.0, m_power),     // Internal power of module (dyn. switching independent)
  swi_power("swi_power", 0.0, m_power),     // Switching power of module
  power_frame_starting_time("power_frame_starting_time", SC_ZERO_TIME, m_power),
  dyn_read_energy("dyn_read_energy", 0.0, m_power),     // Energy per read access
  dyn_write_energy("dyn_write_energy", 0.0, m_power),     // Energy per write access
  dyn_reads("dyn_reads", 0ull, m_power),     // Read access counter for power computation
  dyn_writes("dyn_writes", 0ull, m_power) {   // Write access counter for power computation

  // Initialize slave and master table
  // (Pointers to deviceinfo fields will be set in start_of_simulation)
  for (int i = 0; i < 64; i++) {
    mSlaves[i] = NULL;
    mMasters[i] = NULL;
  }

  if (ambaLayer == amba::amba_LT) {
    // Register tlm blocking transport function
    ahbIN.register_b_transport(this, &AHBCtrl::b_transport);
    ahbIN.register_get_direct_mem_ptr((AHBCtrl *)this, &AHBCtrl::get_direct_mem_ptr);
    ahbOUT.register_invalidate_direct_mem_ptr((AHBCtrl *)this, &AHBCtrl::invalidate_direct_mem_ptr);

  }

  // Register non blocking transport functions
  if (ambaLayer == amba::amba_AT) {
    memset(request_map, 0, 16 * sizeof (connection_t));
    memset(response_map, 0, 16 * sizeof (connection_t));

    // Register tlm non blocking transport forward path
    ahbIN.register_nb_transport_fw(this, &AHBCtrl::nb_transport_fw, 0);
    // Register tlm non blocking transport backward path
    ahbOUT.register_nb_transport_bw(this, &AHBCtrl::nb_transport_bw, 0);
    // Register arbiter thread
    SC_THREAD(arbitrate);

    SC_THREAD(AcceptThread);

    // Register request thread
    SC_THREAD(RequestThread);

    // Register response thread
    SC_THREAD(ResponseThread);

    SC_THREAD(EndResponseThread);
  }

  // Register debug transport
  ahbIN.register_transport_dbg(this, &AHBCtrl::transport_dbg);

  // Register power callback functions
  if (g_pow_mon) {
    GC_REGISTER_TYPED_PARAM_CALLBACK(&sta_power, gs::cnf::pre_read, AHBCtrl, sta_power_cb);
    GC_REGISTER_TYPED_PARAM_CALLBACK(&int_power, gs::cnf::pre_read, AHBCtrl, int_power_cb);
    GC_REGISTER_TYPED_PARAM_CALLBACK(&swi_power, gs::cnf::pre_read, AHBCtrl, swi_power_cb);
  }

  requests_pending = 0;
  AHBCtrl::init_generics();
  srInfo()
    ("ioaddr", ioaddr)
    ("iomask", iomask)
    ("cfgaddr", cfgaddr)
    ("cfgmask", cfgmask)
    ("rrobin", rrobin)
    ("split", split)
    ("defmast", defmast)
    ("ioen", ioen)
    ("fixbrst", fixbrst)
    ("fpnpen", fpnpen)
    ("mcheck", mcheck)
    ("pow_mon", pow_mon)
    ("ambaLayer", ambaLayer)
    ("Created an AHBCtrl with this parameters");

  // initialize the slave_map_cache with some bogus numbers which will trigger MISS
  slave_info_t tmp;
  tmp.hindex = 0;
  tmp.hmask  = ~0;
  tmp.binding = ~0;
  slave_map_cache = std::pair<uint32_t, slave_info_t>(~0, tmp);
}

// Reset handler
void AHBCtrl::dorst() {
  // nothing to do
}

// Destructor
AHBCtrl::~AHBCtrl() {
  GC_UNREGISTER_CALLBACKS();
}

void AHBCtrl::init_generics() {
  g_ioaddr.add_properties()
    ("name", "AHB IO Area Address")
    ("range", "0..0xFFF")
    ("vhdl_name","ioaddr")
    ("base","hex")
    ("The MSB address of the I/O area. Sets the 12 most significant bits in the 32-bit AHB address (e.g. 31 downto 20).");

  g_iomask.add_properties()
    ("name", "AHB IO Area Mask")
    ("range", "0..0xFFF")
    ("vhdl_name","iomask")
    ("base","hex")
    ("The I/O area address mask. Sets the size of the I/O area and the start address together with ioaddr.");

  g_cfgaddr.add_properties()
    ("name", "AHB CFG Area Address")
    ("range", "0..0xFF0")
    ("vhdl_name", "cfgaddr")
    ("base","hex")
    ("The MSB address of the confgiuration area. Sets 12 bits in the 32-bit AHB address (19 downto 8)");

  g_cfgmask.add_properties()
    ("name", "AHB CFG Area Mask")
    ("range", "0..0xFF0")
    ("vhdl_name","cfgmask")
    ("base","hex")
    ("The address mask of the configuration area. Sets the size of the configuration area and "
     "the start address together with cfgaddr.");

  g_rrobin.add_properties()
    ("name", "Round robin arbitration.")
    ("vhdl_name", "rrobin")
    ("True  - round-robin arbitration\n"
     "False - priority arbitration (highest master id has highest priority)\n"
     "Arbitration is only supported in AT mode!! (no effect on LT simulation)");

  g_defmast.add_properties()
    ("name", "Default AHB Master")
    ("range", "0..16")
    ("vhdl_name","defmast")
    ("The default AHB master index (bus parking). This feature is not modeled at transaction level."
     "Parameter is only used for reporting.");

  g_ioen.add_properties()
    ("name", "AHB IO area enable")
    ("vhdl_name","ioen")
    ("AHB I/O area enable. Set to 0 to disable the I/O area.");

  g_fixbrst.add_properties()
    ("name", "Enable support for fixed burst length.")
    ("vhdl_name", "fixbrst")
    ("Enable support for fixed-length bursts at RTL-Level. This feature is not supported at transaction-level.");

  g_split.add_properties()
    ("name", "Enable support for split transactions in AT models")
    ("vhdl_name","split")
    ("Enables support for AHB SPLIT response at RTL-Level. This feature is not supported at transaction-level.");

  g_fpnpen.add_properties()
    ("name", "Enable full decoding of the PnP configuration records")
    ("vhdl_name","fpnpen")
    ("When disabled the user-defined register in the PnP configuration records are not mapped in the configuration area.");

  g_mcheck.add_properties()
    ("name", "Intersection checking")
    ("Check if there are any intersections between core memory areas. If two areas intersect an assert with level failure will be generated.");

  g_pow_mon.add_properties()
    ("name", "Power Monitoring")
    ("If true enable power monitoring");

}

// Helper function for creating slave map decoder entries
void AHBCtrl::setAddressMap(const uint32_t binding, const uint32_t hindex, const uint32_t haddr, const uint32_t hmask) {
  slave_info_t tmp;

  tmp.hindex = hindex;
  tmp.hmask  = hmask;
  tmp.binding = binding;
  
  // Create slave map entry from slave ID and address range descriptor (slave_info_t)
  slave_map.insert(std::pair<uint32_t, slave_info_t>(haddr, tmp));
}

// Find slave index by address
int AHBCtrl::get_index(const uint32_t address) {
  m_total_transactions++;

  // Use 12 bit segment address for decoding
  uint32_t addr = address >> 20;

  // check cache, otherwise search within map
  slave_info_t info = slave_map_cache.second;
  if ( ! ((addr ^ slave_map_cache.first) & info.hmask) ) {
    // There may be up to four BARs per device.
    // Only return device ID.
    m_right_transactions++;
    return (info.binding) >> 2;
  }

  std::map<uint32_t, slave_info_t>::iterator it = --(slave_map.upper_bound( addr ));
  info = it->second;
  if ( ! ((addr ^ it->first) & info.hmask) ) {
    // MISS in the cache: update cache
    slave_map_cache = *it;
  
    // There may be up to four BARs per device.
    // Only return device ID.
    m_right_transactions++;
    return (info.binding) >> 2;
  }

  // no slave found
  return -1;
}

// Returns a PNP register from the slave configuration area
unsigned int AHBCtrl::getPNPReg(const uint32_t address) {
  uint32_t result;

  m_total_transactions++;

  if ((address >= 0xfffffff0)&&(address <= 0xfffffff3)) return 0x30100000;

  // Calculate address offset in configuration area (slave info starts from 0x800)
  unsigned int addr   = address - (
      ((static_cast<uint32_t>(g_ioaddr) << 20) |
       (static_cast<uint32_t>(g_cfgaddr) << 8)) &
       ((static_cast<uint32_t>(g_iomask) << 20) | (static_cast<uint32_t>(g_cfgmask) << 8)));
  srDebug()("addr", addr)("Accessing PNP area");

  // Slave area
  if (addr >= 0x800) {
    addr -= 0x800;

    // Calculate index of the device in mSlaves pointer array (32 byte per device)
    // Shift first to get word addresses.
    unsigned int device = (addr >> 2) >> 3;
    // Calculate offset within device information
    unsigned int offset = (addr >> 2) & 0x7;

    srDebug()("addr", addr)("device", device)("offset", offset)("Access mSlaves");

    if (device >= num_of_slave_bindings) {
      srWarn()("addr", addr)("device", device)("slavecount", num_of_slave_bindings)("Access to unregistered PNP Slave Register!");
      return 0;
    }

    // If the device exists, access deviceinfo (otherwise 0)
    if (mSlaves[device] != NULL) {
      result =  mSlaves[device][offset];
    } else {
      result = 0;
    }

#ifdef LITTLE_ENDIAN_BO
    swap_Endianess(result);
#endif
    m_right_transactions++;
    return result;
  } else {
    // Calculate index of the device in mMasters pointer array (32 byte per device)
    unsigned int device = (addr >> 2) >> 3;
    // Calculate offset within device information
    unsigned int offset = (addr >> 2) & 0x7;

    if (device >= num_of_master_bindings) {
      srWarn()("addr", addr)("device", device)("slavecount", num_of_master_bindings)("Access to unregistered PNP Slave Register!");
      return 0;
    }

    if (mMasters[device] != NULL) {
      result = mMasters[device][offset];
    } else {
      result = 0;
    }

#ifdef LITTLE_ENDIAN_BO
    swap_Endianess(result);
#endif
    return result;
  }
}

// TLM blocking transport function (multi-sock)
void AHBCtrl::b_transport(uint32_t id,
    tlm::tlm_generic_payload &trans,  // NOLINT(runtime/references)
    sc_core::sc_time &delay) {        // NOLINT(runtime/references)
  // master-address pair for dcache snooping
  t_snoop snoopy;

  uint32_t a = 0;
  socket_t *other_socket;
  sc_core::sc_object *slvobj = NULL;
  sc_core::sc_object *mstobj = NULL;

  //if (v::debug) {
    other_socket = ahbIN.get_other_side(id, a);
    mstobj = other_socket->get_parent();
  //}

  srDebug()("pointer", reinterpret_cast<size_t>(&trans))("busy", busy)("is_lock", is_lock)("id", id)("lock_master", lock_master)("delay", delay)(__PRETTY_FUNCTION__);
  // Bus occupied or locked by other master
  while (busy || (is_lock && (id != lock_master))) {
    wait(clock_cycle);
  }

  busy = true;
  is_lock = ahbIN.get_extension<amba::amba_lock>(lock, trans);

  lock_master = id;

  // Collect transport statistics
  transport_statistics(trans);

  // Extract address from payload
  uint32_t addr   = trans.get_address();
  // Extract length from payload
  uint32_t length = trans.get_data_length();
  srDebug()("pointer", reinterpret_cast<size_t>(&trans))("busy", busy)("is_lock", is_lock)("id", id)("lock_master", lock_master)("addr", addr)("delay", delay)(__PRETTY_FUNCTION__);
  
  // Is this an access to configuration area
  if (g_fpnpen && ((
          (addr ^ ((static_cast<uint32_t>(g_ioaddr) << 20) |
                   (static_cast<uint32_t>(g_cfgaddr) << 8))) &
          ((static_cast<uint32_t>(g_iomask) << 20) |
           (static_cast<uint32_t>(g_cfgmask) << 8))) == 0)) {
    // Configuration area is read only
    if (trans.get_command() == tlm::TLM_READ_COMMAND) {
      // addr = addr - (((g_ioaddr << 20) | (g_cfgaddr << 8)) & ((g_iomask << 20) | (g_cfgmask << 8)));
      // Extract data pointer from payload
      uint8_t *data  = trans.get_data_ptr();

      // Get registers from config area
      for (uint32_t i = 0; i < length; i++) {
        // uint32_t word = (addr + i) >> 2;
        uint32_t byte = (addr + i) & 0x3;
        uint32_t reg = getPNPReg(addr + i);
        data[i] = (reinterpret_cast<uint8_t *>(&reg))[byte];

        // one cycle delay per 32bit register
        delay += clock_cycle;
      }

      // and return
      trans.set_response_status(tlm::TLM_OK_RESPONSE);

      wait(delay);
      delay = SC_ZERO_TIME;

      msclogger::return_backward(this, &ahbIN, &trans, tlm::TLM_COMPLETED, delay);

      busy = false;
      return;
    } else {
      v::error << name() << " Forbidden write to AHBCTRL configuration area (PNP)!" << v::endl;
      trans.set_response_status(tlm::TLM_COMMAND_ERROR_RESPONSE);

      delay = SC_ZERO_TIME;

      msclogger::return_backward(this, &ahbIN, &trans, tlm::TLM_COMPLETED, delay);

      busy = false;
      return;
    }
  }

  // Find slave by address / returns slave index or -1 for not mapped
  int index = get_index(trans.get_address());

  // For valid slave index
  if (index >= 0) {
    //if (v::debug) {
      other_socket = ahbOUT.get_other_side(index, a);
      slvobj = other_socket->get_parent();

      srDebug()("addr", trans.get_address())("master", mstobj->name())("slave", slvobj->name())("AHBRequest, b_transport");
    //}

    // Broadcast master_id and address for dcache snooping
    if (trans.get_command() == tlm::TLM_WRITE_COMMAND) {
      snoopy.master_id  = id;
      snoopy.address = addr;
      snoopy.length = length;

      // Send to signal socket
      snoop.write(snoopy);
    }

    // Power event start
    // const char *event_name = "ahb_trans";
    // size_t data_int = (size_t)trans.get_data_ptr();
    // uint32_t id = data_int & 0xFFFFFFFF;
    delay+=clock_cycle;

    srDebug()("addr", trans.get_address())("master", mstobj->name())("slave", slvobj->name())("Outbound b_tranport");
    // Forward request to the selected slave
    ahbOUT[index]->b_transport(trans, delay);

    srDebug()("addr", trans.get_address())("master", mstobj->name())("slave", slvobj->name())("Outbound b_tranport called");
    // v::debug << name() << "Delay after return from slave: " << delay << v::endl;

    // Power event end
    // PM::send(this,event_name,0,sc_time_stamp()+delay,id,g_pow_mon);

    wait(delay);
    delay = SC_ZERO_TIME;

    busy = false;
    return;
  } else {
    other_socket = ahbIN.get_other_side(id, a);
    mstobj = other_socket->get_parent();

    v::error << name() << "AHB Request 0x" << hex << v::setfill('0')
             << v::setw(8) << trans.get_address() << ", from master:"
             << mstobj->name() << ": Unmapped address space." << endl;
    srWarn()("addr", trans.get_address())("master", mstobj->name())("AHBRequest, b_transport to unmapped address space");

    // Invalid index
    trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);

    wait(delay);
    delay = SC_ZERO_TIME;

    busy = false;
    return;
  }
}

// Non-blocking forward transport function for ahb_slave multi-socket
// A master may send BEGIN_REQ or END_RESP. The model replies with
// TLM_ACCEPTED or TLM_COMPLETED, respectively.
tlm::tlm_sync_enum AHBCtrl::nb_transport_fw(
    uint32_t master_id,
    tlm::tlm_generic_payload &trans,  // NOLINT(runtime/references)
    tlm::tlm_phase &phase,            // NOLINT(runtime/references)
    sc_core::sc_time &delay) {        // NOLINT(runtime/references)
  srDebug()("pointer", reinterpret_cast<size_t>(&trans))("phase", phase)("delay", delay)(__PRETTY_FUNCTION__);

  if (phase == tlm::BEGIN_REQ) {
    // Increment reference counter
    trans.acquire();

    // Validate master_id extension
    amba::amba_id *m_id;
    ahbIN.validate_extension<amba::amba_id>(trans);
    ahbIN.get_extension<amba::amba_id>(m_id, trans);
    m_id->value = master_id;

    srDebug()("pointer", reinterpret_cast<size_t>(&trans))("master_id", master_id)("refcount", trans.get_ref_count());

    // In communication with the ahbctrl, BEGIN_REQ marks the begin of the bus request.
    // Transaction is send to request thread, where it is going to be decoded and put in PENDING state.
    m_AcceptPEQ.notify(trans, delay);

    // Collect transport statistics
    transport_statistics(trans);

    // Reset delay
    delay = SC_ZERO_TIME;

    // Draw msc retrun arrow
    msclogger::return_backward(this, &ahbIN, &trans, tlm::TLM_ACCEPTED, delay, master_id);

    // Transaction accepted
    return tlm::TLM_ACCEPTED;
  } else if (phase == tlm::END_RESP) {
    // Let the response thread know that END_RESP
    // came in on the forward path.
    m_EndResponsePEQ.notify(trans, delay);

    msclogger::return_backward(this, &ahbIN, &trans, tlm::TLM_COMPLETED, delay, master_id);

    delay = SC_ZERO_TIME;

    // Transaction completed
    return tlm::TLM_COMPLETED;
  } else {
    srError()("Illegal phase in call to nb_transport_fw!");
    trans.set_response_status(tlm::TLM_COMMAND_ERROR_RESPONSE);
  }

  msclogger::return_backward(this, &ahbIN, &trans, tlm::TLM_ACCEPTED, delay, master_id);

  return tlm::TLM_COMPLETED;
}

// Non-blocking backward transport function for ahb_master multi-socket
// A slave may send END_REQ or BEGIN_RESP. In both cases the model replies
// with TLM_ACCEPTED.
tlm::tlm_sync_enum AHBCtrl::nb_transport_bw(
    uint32_t id,
    tlm::tlm_generic_payload &trans,  // NOLINT(runtime/references)
    tlm::tlm_phase &phase,            // NOLINT(runtime/references)
    sc_core::sc_time &delay) {        // NOLINT(runtime/references)
  srDebug()("pointer", reinterpret_cast<size_t>(&trans))("phase", phase)("delay", delay)(__PRETTY_FUNCTION__);

  // The slave has sent END_REQ
  if (phase == tlm::END_REQ) {
    m_RequestPEQ.notify(trans, delay);
    delay = SC_ZERO_TIME;
  } else if (phase == amba::DATA_SPLIT) {
    // Ignore DATA_SPLIT!
    // Current version of AHBCTRL does not rearbitrate on DATA_SPLIT!
    // Slave expected to continue with BEGIN_RESP.

    // New response - goes into response PEQ
  } else if (phase == tlm::BEGIN_RESP) {
    m_ResponsePEQ.notify(trans, delay);
    delay = SC_ZERO_TIME;
  } else {
    srError()("Invalid phase in call to nb_transport_bw!");
    trans.set_response_status(tlm::TLM_COMMAND_ERROR_RESPONSE);
  }

  msclogger::return_forward(this, &ahbOUT, &trans, tlm::TLM_ACCEPTED, delay, id);

  return tlm::TLM_ACCEPTED;
}

// Helper function for printing requests
void AHBCtrl::print_requests() {
  v::info << name() << " ---------------------------------------------------- " << v::endl;
  v::info << name() << "NEW ARBITER CYCLE:" << v::endl;

  for (int i = 0; i < 16; i++) {
    v::info << name() << "Master " << i << " Transaction: " <<  request_map[i].trans << " State: " <<
      request_map[i].state << v::endl;
  }

  v::info << name() << " ---------------------------------------------------- " << v::endl;
}

// Arbitration thread (AT only)
void AHBCtrl::arbitrate() {
  tlm::tlm_phase phase;
#ifndef NDEBUG // assert specific macro
  tlm::tlm_sync_enum status;
#endif
  sc_core::sc_time delay;

  payload_t *trans = NULL;
  int slave_id = 0;

  sc_core::sc_time tmp;
  sc_core::sc_time waiting_time;
  sc_core::sc_time request_delay;
  sc_core::sc_time response_delay;

  wait(1, SC_PS);

  // Arbiter phase shift:
  // --------------------
  while (true) {
    wait(clock_cycle);

    // print_requests();

    // Last address of current transfer must have been sampled.
    // Last data sample is on the way.
    // v::debug << name() << " address_bus_owner: " << address_bus_owner << " data_bus_state: "
    //          << data_bus_state << v::endl;
    // if (sc_time_stamp() > sc_core::sc_time(83880, SC_NS)) sc_stop();

    if ((address_bus_owner == -1) && ((data_bus_state == RESPONSE) || (data_bus_state == IDLE))) {
      // Priority arbitration
      if (g_rrobin == 0) {
        if (!is_lock) {
          // Master with the highest ID has the highest priority
          for (int i = 15; i >= 0; i--) {
            if (request_map[i].state == TRANS_PENDING) {
              address_bus_owner = i;
              request_map[i].state = TRANS_SCHEDULED;
              trans = request_map[i].trans;
              slave_id = request_map[i].slave_id;

              is_lock = ahbIN.get_extension<amba::amba_lock>(lock, *trans);
              lock_master = i;

              break;
            }
          }
        } else {
          if (request_map[lock_master].state == TRANS_PENDING) {
            address_bus_owner = lock_master;
            request_map[lock_master].state = TRANS_SCHEDULED;
            trans = request_map[lock_master].trans;
            slave_id = request_map[lock_master].slave_id;

            is_lock = ahbIN.get_extension<amba::amba_lock>(lock, *trans);
          }
        }

        // Round-robin
      } else {
        if (!is_lock) {
          for (uint32_t i = 0; i < num_of_master_bindings; i++) {
            robin = (robin + 1) % num_of_master_bindings;

            srDebug()("robin", robin)(__PRETTY_FUNCTION__);

            if (request_map[robin].state == TRANS_PENDING) {
              srDebug()("robin", robin)("Selected for robin");

              address_bus_owner = robin;
              request_map[robin].state = TRANS_SCHEDULED;
              trans = request_map[robin].trans;
              slave_id = request_map[robin].slave_id;

              is_lock = ahbIN.get_extension<amba::amba_lock>(lock, *trans);

              lock_master = robin;

              break;
            }
          }
        } else {
          if (request_map[lock_master].state == TRANS_PENDING) {
            srDebug()("robin", robin)("Selected for robin");
{'fraction_non_alphanumeric': 0.13870842922021057, 'fraction_numerical': 0.01111948353461092, 'mean_word_length': 3.3009547166506086, 'pattern_counts': {'":': 2, '<': 277, '<?xml version=': 0, '>': 143, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 6, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3191482', 'n_tokens_mistral': 17296, 'n_tokens_neox': 16535, 'n_words': 4060}