#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 #include #include class lt_bus : public sc_core::sc_module { using target_socket = tlm_target_socket_tagged; using target_socket_ptr = std::unique_ptr; using initiator_socket = tlm_initiator_socket_tagged; using initiator_socket_ptr = std::unique_ptr; 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( name.c_str(), id, this, <_bus::b_transport, <_bus::get_direct_mem_ptr, <_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( name.c_str(), id, this, <_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 m_initiators; // INITIATOR sockets to bind BUS TARGET against. std::vector m_targets; // Address range mappings to BUS TARGETs (m_tragets). std::vector m_mappings; }; #endif