#pragma once #include #include #include #include #include #include namespace vortex { class SimObjectBase; /////////////////////////////////////////////////////////////////////////////// class SimPortBase { public: virtual ~SimPortBase() {} SimObjectBase* module() const { return module_; } SimPortBase* peer() const { return peer_; } bool connected() const { return (peer_ != nullptr); } protected: SimPortBase(SimObjectBase* module) : module_(module) , peer_(nullptr) {} void connect(SimPortBase* peer) { assert(peer_ == nullptr); peer_ = peer; } void disconnect() { assert(peer_ == nullptr); peer_ = nullptr; } SimPortBase& operator=(const SimPortBase&) = delete; SimObjectBase* module_; SimPortBase* peer_; template friend class SlavePort; template friend class MasterPort; }; /////////////////////////////////////////////////////////////////////////////// template class SimPort : public SimPortBase { public: void send(const Pkt& pkt, uint64_t delay) const; bool read(Pkt* out) { if (!valid_) return false; *out = data_; valid_ = false; return true; } protected: SimPort(SimObjectBase* module) : SimPortBase(module) , valid_(false) {} void write(const Pkt& data) { assert(!valid_); data_ = data; valid_ = true; } SimPort& operator=(const SimPort&) = delete; Pkt data_; bool valid_; template friend class SimPortEvent; }; /////////////////////////////////////////////////////////////////////////////// template class SlavePort : public SimPort { public: SlavePort(SimObjectBase* module) : SimPort(module) {} void bind(SlavePort* peer) { this->connect(peer); } void unbind() { this->disconnect(); } protected: SlavePort& operator=(const SlavePort&) = delete; }; /////////////////////////////////////////////////////////////////////////////// template class MasterPort : public SimPort { public: MasterPort(SimObjectBase* module) : SimPort(module) {} void bind(SlavePort* peer) { this->connect(peer); } void bind(MasterPort* peer) { this->connect(peer); } void unbind() { this->disconnect(); } protected: MasterPort& operator=(const MasterPort&) = delete; }; /////////////////////////////////////////////////////////////////////////////// class SimEventBase { public: typedef std::shared_ptr Ptr; virtual ~SimEventBase() {} virtual void fire() const = 0; bool step() { return (0 == --delay_); } protected: SimEventBase(uint64_t delay) : delay_(delay) {} uint64_t delay_; }; /////////////////////////////////////////////////////////////////////////////// template class SimCallEvent : public SimEventBase { public: typedef std::function Func; template static Ptr Create(const Func& func, const Pkt& pkt, uint64_t delay) { return std::make_shared(func, pkt, delay); } SimCallEvent(const Func& func, const Pkt& pkt, uint64_t delay) : SimEventBase(delay) , func_(func) , pkt_(pkt) {} void fire() const override { func_(pkt_); } protected: Func func_; Pkt pkt_; }; /////////////////////////////////////////////////////////////////////////////// template class SimPortEvent : public SimEventBase { public: static Ptr Create(const SimPort* port, const Pkt& pkt, uint64_t delay) { return std::make_shared(port, pkt, delay); } SimPortEvent(const SimPort* port, const Pkt& pkt, uint64_t delay) : SimEventBase(delay) , port_(port) , pkt_(pkt) {} void fire() const override { const_cast*>(port_)->write(pkt_); } private: const SimPort* port_; Pkt pkt_; }; /////////////////////////////////////////////////////////////////////////////// class SimContext; class SimObjectBase { public: typedef std::shared_ptr Ptr; virtual ~SimObjectBase() {} template void schedule(T *obj, void (T::*entry)(const Pkt&), const Pkt& pkt, uint64_t delay); const std::string& name() const { return name_; } protected: virtual void step(uint64_t cycle) = 0; SimObjectBase(const SimContext& ctx, const char* name); private: std::string name_; friend class SimPlatform; friend class SimPortBase; }; /////////////////////////////////////////////////////////////////////////////// template class SimObject : public SimObjectBase { public: typedef std::shared_ptr Ptr; template static Ptr Create(Args&&... args); protected: SimObject(const SimContext& ctx, const char* name) : SimObjectBase(ctx, name) {} void step(uint64_t cycle) override { this->impl().step(cycle); } private: const Impl& impl() const { return static_cast(*this); } Impl& impl() { return static_cast(*this); } }; class SimContext { private: SimContext() {} template template friend typename SimObject::Ptr SimObject::Create(Args&&... args); }; /////////////////////////////////////////////////////////////////////////////// class SimPlatform { public: static SimPlatform& instance() { static SimPlatform s_inst; return s_inst; } bool initialize() { //-- return true; } void flush() { instance().clear(); } void finalize() { instance().clear(); } void register_object(const SimObjectBase::Ptr& obj) { objects_.push_back(obj); } template void schedule(const typename SimCallEvent::Func& callback, const Pkt& pkt, uint64_t delay) { auto evt = SimCallEvent::Create(callback, pkt, delay); assert(delay != 0); events_.emplace_back(evt); } template void schedule(const SimPort* port, const Pkt& pkt, uint64_t delay) { auto evt = SimPortEvent::Create(port, pkt, delay); assert(delay != 0); events_.emplace_back(evt); } void step() { // evaluate events auto evt_it = events_.begin(); auto evt_it_end = events_.end(); while (evt_it != evt_it_end) { auto& event = *evt_it; if (event->step()) { event->fire(); evt_it = events_.erase(evt_it); } else { ++evt_it; } } // evaluate components for (auto& object : objects_) { object->step(cycles_); } // advance clock ++cycles_; } uint64_t cycles() const { return cycles_; } private: SimPlatform() : cycles_(0) {} virtual ~SimPlatform() { this->clear(); } void clear() { objects_.clear(); events_.clear(); } std::vector objects_; std::list events_; uint64_t cycles_; }; /////////////////////////////////////////////////////////////////////////////// inline SimObjectBase::SimObjectBase(const SimContext&, const char* name) : name_(name) {} template template typename SimObject::Ptr SimObject::Create(Args&&... args) { auto obj = std::make_shared(SimContext{}, std::forward(args)...); SimPlatform::instance().register_object(obj); return obj; } template void SimPort::send(const Pkt& pkt, uint64_t delay) const { if (peer_) { reinterpret_cast*>(peer_)->send(pkt, delay); } else { SimPlatform::instance().schedule(this, pkt, delay); } } template void SimObjectBase::schedule(T *obj, void (T::*entry)(const Pkt&), const Pkt& pkt, uint64_t delay) { auto callback = std::bind(entry, obj, std::placeholders::_1); SimPlatform::instance().schedule(callback, pkt, delay); } }