diff options
author | Johannes Stoelp <johannes.stoelp@gmail.com> | 2023-10-26 01:35:28 +0200 |
---|---|---|
committer | Johannes Stoelp <johannes.stoelp@gmail.com> | 2023-10-26 01:35:28 +0200 |
commit | 49c7fc8a1c06912caa89664af85801032a7bb7f8 (patch) | |
tree | 8775163b41bdfb964f7535b1f860f9feb1272d3e /src | |
parent | 486d6fdd590270c925b4306096ba8bf6d69125ef (diff) | |
download | sysc-playground-49c7fc8a1c06912caa89664af85801032a7bb7f8.tar.gz sysc-playground-49c7fc8a1c06912caa89664af85801032a7bb7f8.zip |
initially add simple lt_bus
Diffstat (limited to 'src')
-rw-r--r-- | src/lt_bus.cc | 85 | ||||
-rw-r--r-- | src/models/lt_bus.h | 298 | ||||
-rw-r--r-- | src/utils.h | 2 |
3 files changed, 384 insertions, 1 deletions
diff --git a/src/lt_bus.cc b/src/lt_bus.cc new file mode 100644 index 0000000..078a8d8 --- /dev/null +++ b/src/lt_bus.cc @@ -0,0 +1,85 @@ +#include "models/lt_bus.h" +#include "utils.h" + +#include <tlm_utils/simple_initiator_socket.h> +#include <tlm_utils/simple_target_socket.h> + +// -- TARGET ------------------------------------------------------------------- + +struct target : public sc_core::sc_module { + explicit target(sc_core::sc_module_name nm) : sc_module(std::move(nm)) { + p_sock.register_b_transport(this, &target::b_transport); + } + + tlm_utils::simple_target_socket<target> p_sock{"sock"}; + + private: + void b_transport(tlm::tlm_generic_payload& tx, sc_core::sc_time& t) { + LOGM("transport 0x%llx w: %d r: %d", tx.get_address(), tx.is_write(), + tx.is_read()); + tx.set_response_status(tlm::TLM_OK_RESPONSE); + } +}; + +// -- INITIATOR ---------------------------------------------------------------- + +struct initiator : public sc_core::sc_module { + SC_HAS_PROCESS(initiator); + + explicit initiator(sc_core::sc_module_name nm) : sc_module(std::move(nm)) { + SC_THREAD(run); + } + + tlm_utils::simple_initiator_socket<initiator> p_sock{"sock"}; + + private: + void send_tx(tlm::tlm_command cmd, std::uint64_t addr) { + unsigned char data[4]; + tlm::tlm_generic_payload tx; + tx.set_command(cmd); + tx.set_address(addr); + tx.set_data_ptr(data); + tx.set_data_length(sizeof(data)); + tx.set_byte_enable_ptr(nullptr); + tx.set_byte_enable_length(0); + tx.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE); + tx.set_dmi_allowed(false); + + sc_core::sc_time t; + p_sock->b_transport(tx, t); + + LOGM("ACCESS @%lx ok: %d (%s)", addr, tx.is_response_ok(), + tx.get_response_string().c_str()); + } + + void run() { + send_tx(tlm::TLM_WRITE_COMMAND, 0x1000); + send_tx(tlm::TLM_WRITE_COMMAND, 0x3000); + send_tx(tlm::TLM_WRITE_COMMAND, 0x4000); + send_tx(tlm::TLM_WRITE_COMMAND, 0x9000); + send_tx(tlm::TLM_WRITE_COMMAND, 0x80fc); + send_tx(tlm::TLM_WRITE_COMMAND, 0x80fd); + } +}; + +// -- SC_MAIN ------------------------------------------------------------------ + +extern "C" int sc_main(int, char*[]) { + lt_bus bus{"bus"}; + + target target1{"target1"}; + target target2{"target2"}; + target target3{"target3"}; + + initiator init1{"init1"}; + + bus.attach_target(target2.p_sock, 0x4000, 0x4fff); + bus.attach_target(target1.p_sock, 0x0000, 0x3fff); + bus.attach_target(target3.p_sock, 0x8000, 0x80ff); + + bus.attach_initiator(init1.p_sock); + + sc_core::sc_start(); + + return 0; +} diff --git a/src/models/lt_bus.h b/src/models/lt_bus.h new file mode 100644 index 0000000..9ae66b0 --- /dev/null +++ b/src/models/lt_bus.h @@ -0,0 +1,298 @@ +#ifndef SYSC_PLAYGROUND_LT_BUS +#define SYSC_PLAYGROUND_LT_BUS + +#include <tlm_core/tlm_2/tlm_generic_payload/tlm_generic_payload.h> +#include <tlm_core/tlm_2/tlm_sockets/tlm_sockets.h> + +#include <algorithm> +#include <cstdint> +#include <memory> +#include <vector> + +struct scoped_push_hierarchy { + [[nodiscard]] explicit scoped_push_hierarchy(sc_core::sc_module& mod) + : m_mod(mod), m_simctx(sc_core::sc_get_curr_simcontext()) { + assert(m_simctx); + m_simctx->hierarchy_push(&m_mod); + } + + ~scoped_push_hierarchy() { + const auto* top = m_simctx->hierarchy_pop(); + assert(top == &m_mod); + } + + private: + sc_core::sc_simcontext* m_simctx{nullptr}; + sc_core::sc_module& m_mod; +}; + +struct range { + constexpr explicit range(std::uint64_t start, std::uint64_t end) + : start{start}, end{end} { + assert(start < end); + } + + constexpr bool overlaps(range rhs) const { + return start <= rhs.end && rhs.start <= end; + } + + constexpr bool contains(range rhs) const { + return start <= rhs.start && rhs.end <= end; + } + + std::uint64_t start; + std::uint64_t end; +}; + +template <typename Module> +class tlm_target_socket_tagged : public tlm::tlm_target_socket<>, + public tlm::tlm_fw_transport_if<> { + using cb_b_transport = void (Module::*)(std::size_t, + tlm::tlm_generic_payload&, + sc_core::sc_time&); + + using cb_get_direct_mem_ptr = bool (Module::*)(std::size_t, + tlm::tlm_generic_payload&, + tlm::tlm_dmi&); + using cb_transport_dbg = unsigned int (Module::*)(std::size_t, + tlm::tlm_generic_payload&); + + public: + explicit tlm_target_socket_tagged(const char* name, + std::size_t id, + Module* mod, + cb_b_transport b, + cb_get_direct_mem_ptr m, + cb_transport_dbg d) + : tlm_target_socket<>{name}, + m_mod{mod}, + m_id{id}, + m_b_transport{b}, + m_get_direct_mem_ptr{m}, + m_transport_dbg{d} { + bind(*static_cast<tlm::tlm_fw_transport_if<>*>(this)); + } + + private: + // -- TLM_FW_TRANSPORT_IF ---------------------------------------------------- + + virtual void b_transport(tlm::tlm_generic_payload& tx, + sc_core::sc_time& t) override { + return (m_mod->*m_b_transport)(m_id, tx, t); + } + + virtual bool get_direct_mem_ptr(tlm::tlm_generic_payload& tx, + tlm::tlm_dmi& dmi_data) override { + return (m_mod->*m_get_direct_mem_ptr)(m_id, tx, dmi_data); + } + + virtual unsigned int transport_dbg(tlm::tlm_generic_payload& tx) override { + return (m_mod->*m_transport_dbg)(m_id, tx); + } + + virtual tlm::tlm_sync_enum nb_transport_fw(tlm::tlm_generic_payload& tx, + tlm::tlm_phase& phase, + sc_core::sc_time& t) override { + std::fprintf(stderr, + "tlm_target_socket_tagged: nb_transport_fw not supported\n"); + std::abort(); + } + + // -- MEMBER ----------------------------------------------------------------- + + std::size_t m_id{0}; + Module* m_mod{nullptr}; + cb_b_transport m_b_transport{nullptr}; + cb_get_direct_mem_ptr m_get_direct_mem_ptr{nullptr}; + cb_transport_dbg m_transport_dbg{nullptr}; +}; + +class lt_bus : public sc_core::sc_module, public tlm::tlm_bw_transport_if<> { + using target_socket = tlm_target_socket_tagged<lt_bus>; + using target_socket_ptr = std::unique_ptr<target_socket>; + + using initiator_socket = tlm::tlm_initiator_socket<>; + 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 std::size_t 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 g(*this); + + // Add new target socket to connect BUS INITIATOR. + m_initiators.push_back(std::make_unique<target_socket>( + 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, + std::uint64_t start, + std::uint64_t 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 std::size_t 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 g(*this); + + // Add new initiator socket to connect BUS TARGET. + m_targets.push_back(std::make_unique<initiator_socket>(name.c_str())); + } + + // Bind sockets. + auto& init = m_targets.back(); + init->bind(*this); + init->bind(target); + + // Insert new mapping, id is equal to idx into socket vector. + m_mappings.push_back({addr, id}); + } + + private: + // -- TLM_BW_TRANSPORT_IF ---------------------------------------------------- + + virtual tlm::tlm_sync_enum nb_transport_bw(tlm::tlm_generic_payload&, + tlm::tlm_phase&, + sc_core::sc_time&) override { + std::fprintf(stderr, "lt_bus: nb_transport_bw not supported\n"); + std::abort(); + } + + virtual void invalidate_direct_mem_ptr(sc_dt::uint64 start, + sc_dt::uint64 end) override { + assert(false); + } + + // -- TLM_FW_TRANSPORT_IF ---------------------------------------------------- + + void b_transport(std::size_t id, + tlm::tlm_generic_payload& tx, + sc_core::sc_time& t) { + std::uint64_t start = tx.get_address(); + std::uint64_t 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(std::size_t, + tlm::tlm_generic_payload& tx, + tlm::tlm_dmi& dmi) { + std::uint64_t start = tx.get_address(); + std::uint64_t 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(std::size_t, tlm::tlm_generic_payload& tx) { + std::uint64_t start = tx.get_address(); + std::uint64_t 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}; + std::uint64_t 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; + }); + + // 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()); + // } + } + + // -- LOCAL CLASSES ---------------------------------------------------------- + + struct mapping { + range addr; + std::size_t 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 diff --git a/src/utils.h b/src/utils.h index 5584027..1373b90 100644 --- a/src/utils.h +++ b/src/utils.h @@ -5,7 +5,7 @@ #define LOG(FMT, ...) \ do { \ - std::fprintf(stderr, "%10s | %4lld:%2lld | %10s | " FMT "\n", \ + std::fprintf(stderr, "%10s | %4lld:%2lld | %12s | " FMT "\n", \ sc_core::sc_time_stamp().to_string().c_str(), \ sc_core::sc_delta_count(), \ sc_core::sc_delta_count_at_current_time(), __FUNCTION__, \ |