aboutsummaryrefslogblamecommitdiff
path: root/src/models/lt_bus.h
blob: 37cfe8456a6228a609326429fd51eb0190560ed7 (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) {
    assert(start <= end);

    for (const auto& map : m_mappings) {
      if (map.idx != id) {
        continue;
      }

      // Compute absolute start address.
      const sc_dt::uint64 abs_start = map.addr.start + start;
      assert(abs_start <= map.addr.end);

      // Compute absolute end address. Limit if mapping range exceeded.
      const auto comp_abs_end = [&map](sc_dt::uint64 abs_end) -> sc_dt::uint64 {
        if (abs_end > map.addr.end /* exceeds mapping */ ||
            abs_end < map.addr.start /* wraps around */) {
          return map.addr.end;
        }
        return abs_end;
      };
      const sc_dt::uint64 abs_end = comp_abs_end(map.addr.start + end);

      for (auto& sock : m_initiators) {
        (*sock)->invalidate_direct_mem_ptr(abs_start, abs_end);
      }
    }
  }

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

  void b_transport(usize id,
                   tlm::tlm_generic_payload& tx,
                   sc_core::sc_time& t) {
    if (const auto r = decode(tx)) {
      const auto start = tx.get_address();
      assert(r.start <= start);

      tx.set_address(start - r.start);
      (*r.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) {
    if (const auto r = decode(tx)) {
      const auto start = tx.get_address();
      assert(r.start <= start);

      tx.set_address(start - r.start);
      const bool ok = (*r.sock)->get_direct_mem_ptr(tx, dmi);
      tx.set_address(start);

      // Early return, dmi request failed, no need to fixup dmi addresses.
      if (!ok) {
        return false;
      }

      // Compute absolute start address.
      const sc_dt::uint64 abs_start = r.start + dmi.get_start_address();
      assert(abs_start < r.end);

      // Compute absolute end address. Limit if mapping range exceeded.
      const auto comp_abs_end = [&r](sc_dt::uint64 abs_end) -> sc_dt::uint64 {
        if (abs_end > r.end /* exceeds mapping */ ||
            abs_end < r.start /* wraps around */) {
          return r.end;
        }
        return abs_end;
      };
      const sc_dt::uint64 abs_end =
          comp_abs_end(r.start + dmi.get_end_address());

      dmi.set_start_address(abs_start);
      dmi.set_end_address(abs_end);

      return true;
    }
    return false;
  }

  unsigned int transport_dbg(usize, tlm::tlm_generic_payload& tx) {
    unsigned int ret = 0;
    if (const auto r = decode(tx)) {
      const auto start = tx.get_address();
      assert(r.start <= start);

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

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

  struct decode_result {
    initiator_socket* sock{nullptr};
    u64 start{0ull};
    u64 end{0ull};

    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, map.addr.end};
      }
    }
    return {};
  }

  decode_result decode(const tlm::tlm_generic_payload& tx) const {
    const u64 start = tx.get_address();
    const u64 end = start + tx.get_data_length() - 1;
    return decode(range{start, end});
  }

  // -- 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