aboutsummaryrefslogblamecommitdiff
path: root/src/models/lt_bus.h
blob: f52f8b00c7d04fda38fdc53bd3904e055f1ab5bd (plain) (tree)
1
2
3
4
5
6
7
8
9
10


                              




                                              

                    


                 
                                          


                                                           
                                                               








                                                                                
                                         


                                                                   
                                     














                                                                                

                               













                                                                        
                                      


                                                                   
                                     

                                                        

                                                                       



                                  





                                                                 









                                                                                


                                                                                
                                          











                                                     



                                                                                
                            

                                                

                                               











                                                              
                                

                                                       

                                               











                                                     


                                                                   















                                                                                
             






















                                                                                





                                                                                
              












                                                                                
#ifndef SYSC_PLAYGROUND_LT_BUS
#define SYSC_PLAYGROUND_LT_BUS

#include "utils/range.h"
#include "utils/sysc.h"
#include "utils/tlm_initiator_socket_tagged.h"
#include "utils/tlm_target_socket_tagged.h"
#include "utils/types.h"

#include <algorithm>
#include <memory>
#include <vector>

class lt_bus : public sc_core::sc_module {
  using target_socket = tlm_target_socket_tagged<lt_bus>;
  using target_socket_ptr = std::unique_ptr<target_socket>;

  using initiator_socket = tlm_initiator_socket_tagged<lt_bus>;
  using initiator_socket_ptr = std::unique_ptr<initiator_socket>;

 public:
  explicit lt_bus(sc_core::sc_module_name nm)
      : sc_core::sc_module(std::move(nm)) {}

  // -- ATTACH BUS INITIATOR ---------------------------------------------------

  void attach_initiator(tlm::tlm_base_initiator_socket_b<>& init) {
    const usize id = m_initiators.size();
    const std::string name = "init" + std::to_string(id);
    {
      // Add current module on top of module stack for tlm sockets.
      scoped_push_hierarchy h(*this);

      // Add new target socket to connect BUS INITIATOR.
      m_initiators.push_back(std::make_unique<target_socket>(
          name.c_str(), id, this, &lt_bus::b_transport,
          &lt_bus::get_direct_mem_ptr, &lt_bus::transport_dbg));
    }

    // Bind sockets.
    auto& target = m_initiators.back();
    target->bind(init);
  }

  // -- ATTACH BUS TARGET ------------------------------------------------------

  void attach_target(tlm::tlm_base_target_socket_b<>& target,
                     u64 start,
                     u64 end) {
    const range addr{start, end};

    // Check if new range overlaps with any registered memory map range.
    for (const auto& map : m_mappings) {
      if (map.addr.overlaps(addr)) {
        std::fprintf(stderr,
                     "lt_bus: memory map conflict detected\n"
                     "old: %08lx - %08lx\n"
                     "new: %08lx - %08lx\n",
                     map.addr.start, map.addr.end, start, end);
        std::abort();
      }
    }

    const usize id = m_targets.size();
    const std::string name = "target" + std::to_string(id);
    {
      // Add current module on top of module stack for tlm sockets.
      scoped_push_hierarchy h(*this);

      // Add new initiator socket to connect BUS TARGET.
      m_targets.push_back(std::make_unique<initiator_socket>(
          name.c_str(), id, this, &lt_bus::invalidate_direct_mem_ptr));
    }

    // Bind sockets.
    auto& init = m_targets.back();
    init->bind(target);

    // Insert new mapping, id is equal to idx into socket vector.
    m_mappings.push_back({addr, id});
  }

  // -- DUMP -------------------------------------------------------------------

  void dump() const {
    // Dump memory map.
    for (const auto& map : m_mappings) {
      std::printf("%08lx - %08lx :[%2ld] %s\n", map.addr.start, map.addr.end,
                  map.idx, m_targets[map.idx].get()->name());
    }
  }

 private:
  // -- TLM_BW_TRANSPORT_IF ----------------------------------------------------

  void invalidate_direct_mem_ptr(usize id,
                                 sc_dt::uint64 start,
                                 sc_dt::uint64 end) {
    for (const auto& map : m_mappings) {
      if (map.idx != id) {
        continue;
      }
      const sc_dt::uint64 s = map.addr.start + start;
      const sc_dt::uint64 e = map.addr.start + end;
      for (auto& sock : m_initiators) {
        (*sock)->invalidate_direct_mem_ptr(s, e);
      }
    }
  }

  // -- TLM_FW_TRANSPORT_IF ----------------------------------------------------

  void b_transport(usize id,
                   tlm::tlm_generic_payload& tx,
                   sc_core::sc_time& t) {
    u64 start = tx.get_address();
    u64 end = start + tx.get_data_length() - 1;

    if (auto res = decode(range{start, end})) {
      assert(res.base <= start);

      tx.set_address(start - res.base);
      (*res.sock)->b_transport(tx, t);
      tx.set_address(start);
    } else {
      tx.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
    }
  }

  bool get_direct_mem_ptr(usize,
                          tlm::tlm_generic_payload& tx,
                          tlm::tlm_dmi& dmi) {
    u64 start = tx.get_address();
    u64 end = start + tx.get_data_length() - 1;

    bool ret = false;
    if (auto res = decode(range{start, end})) {
      assert(res.base <= start);

      tx.set_address(start - res.base);
      ret = (*res.sock)->get_direct_mem_ptr(tx, dmi);
      tx.set_address(start);
    }
    return ret;
  }

  unsigned int transport_dbg(usize, tlm::tlm_generic_payload& tx) {
    u64 start = tx.get_address();
    u64 end = start + tx.get_data_length() - 1;

    unsigned int ret = 0;
    if (auto res = decode(range{start, end})) {
      assert(res.base <= start);

      tx.set_address(start - res.base);
      ret = (*res.sock)->transport_dbg(tx);
      tx.set_address(start);
    }
    return ret;
  }

  // -- DECODE BUS TARGET ------------------------------------------------------

  struct decode_result {
    initiator_socket* sock{nullptr};
    u64 base;

    constexpr explicit operator bool() const {
      return sock != nullptr;
    }
  };

  decode_result decode(range addr) const {
    for (const auto& map : m_mappings) {
      if (map.addr.contains(addr)) {
        return {m_targets[map.idx].get(), map.addr.start};
      }
    }
    return {nullptr, 0ull};
  }

  // -- SC_MODULE CALLBACKS ----------------------------------------------------

  virtual void start_of_simulation() override {
    // Sort memory mappings by start address.
    std::sort(m_mappings.begin(), m_mappings.end(),
              [](const mapping& lhs, const mapping& rhs) {
                return lhs.addr.start < rhs.addr.start;
              });
  }

  // -- LOCAL CLASSES ----------------------------------------------------------

  struct mapping {
    range addr;
    usize idx;
  };

  // -- MEMBER -----------------------------------------------------------------

  // TARGET sockets to bind BUS INITIATORS against.
  std::vector<target_socket_ptr> m_initiators;
  // INITIATOR sockets to bind BUS TARGET against.
  std::vector<initiator_socket_ptr> m_targets;
  // Address range mappings to BUS TARGETs (m_tragets).
  std::vector<mapping> m_mappings;
};

#endif