aboutsummaryrefslogblamecommitdiff
path: root/src/models/lt_bus.h
blob: cf09d990d118bf94afdd0ddb4dbc7d6404c2e9d5 (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) {
    assert(start <= end);
    const range addr{start, end};

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

  // -- SHOW_MMAP --------------------------------------------------------------

  void show_mmap() const {
    // Dump memory map.
    for (const auto& mmap : m_mappings) {
      std::printf("%08lx - %08lx :[%2ld] %s\n", mmap.addr.start, mmap.addr.end,
                  mmap.idx, m_targets[mmap.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& mmap : m_mappings) {
      if (mmap.idx != id) {
        continue;
      }

      // Compute absolute start/end address based on memory map.
      const range abs_addr =
          compute_abs_mmap_address(range{start, end}, mmap.addr);

      for (auto& sock : m_initiators) {
        (*sock)->invalidate_direct_mem_ptr(abs_addr.start, abs_addr.end);
      }
    }
  }

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

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

      tx.set_address(tx_start - r.start);
      (*r.sock)->b_transport(tx, t);
      tx.set_address(tx_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 tx_start = tx.get_address();
      assert(r.start <= tx_start);

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

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

      // Compute absolute start/end address based on memory map.
      const range abs_addr = compute_abs_mmap_address(
          range{dmi.get_start_address(), dmi.get_end_address()},
          range{r.start, r.end});

      // Update dmi payload with absolute addresses.
      dmi.set_start_address(abs_addr.start);
      dmi.set_end_address(abs_addr.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 tx_start = tx.get_address();
      assert(r.start <= tx_start);

      tx.set_address(tx_start - r.start);
      ret = (*r.sock)->transport_dbg(tx);
      tx.set_address(tx_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& mmap : m_mappings) {
      if (mmap.addr.contains(addr)) {
        return {m_targets[mmap.idx].get(), mmap.addr.start, mmap.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});
  }

  // -- COMPUTE ABSOLUTE MEMORY MAP ADDRESS ------------------------------------

  static range compute_abs_mmap_address(range rel, range mmap) {
    // Compute absolute start address.
    const sc_dt::uint64 abs_start = mmap.start + rel.start;
    // Start address must be in bounds of the memory map mapping.
    assert(abs_start <= mmap.end);

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

    return range{abs_start, abs_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