// license:GPL-2.0+ // copyright-holders:Couriersud /// /// \file nl_interface.h /// /// This header contains objects for interfacing with the netlist core. /// #ifndef NLINTERFACE_H_ #define NLINTERFACE_H_ #include "nl_base.h" #include "nl_setup.h" #include "core/setup.h" #include #include namespace netlist { namespace interface { /// \brief analog_callback device /// /// This device is used to call back into the application which /// is controlling the execution of the netlist. /// /// The device will call the provided lambda with a reference to /// itself and the current value of the net it is connected to. /// /// The following code is an example on how to add the device to /// the netlist factory. /// /// const pstring pin(m_in); /// pstring dname = pstring("OUT_") + pin; /// /// const auto lambda = [this](auto &in, netlist::nl_fptype val) /// { /// this->cpu()->update_icount(in.exec().time()); /// this->m_delegate(val, this->cpu()->local_time()); /// this->cpu()->check_mame_abort_slice(); /// }; /// /// using lb_t = decltype(lambda); /// using cb_t = netlist::interface::NETLIB_NAME(analog_callback); /// /// parser.factory().add(dname, /// netlist::factory::properties("-", PSOURCELOC()), 1e-6, std::forward(lambda)); /// template NETLIB_OBJECT(analog_callback) { public: NETLIB_CONSTRUCTOR_EX(analog_callback, nl_fptype threshold, FUNC &&func) , m_in(*this, "IN", NETLIB_DELEGATE(in)) , m_threshold(threshold) , m_last(*this, "m_last", 0) , m_func(func) { } NETLIB_RESETI() { m_last = 0.0; } NETLIB_HANDLERI(in) { const nl_fptype cur = m_in(); if (plib::abs(cur - m_last) > m_threshold) { m_last = cur; m_func(*this, cur); } } private: analog_input_t m_in; nl_fptype m_threshold; state_var m_last; FUNC m_func; }; /// \brief logic_callback device /// /// This device must be connected to a logic net. It has no power terminals /// and conversion with proxies will not work. /// /// Background: This device may be inserted later into the driver and should /// not modify the resulting analog representation of the netlist. /// /// If you get error messages on missing power terminals you have to use the /// analog callback device instead. template NETLIB_OBJECT(logic_callback) { public: NETLIB_CONSTRUCTOR_EX(logic_callback, FUNC &&func) , m_in(*this, "IN", NETLIB_DELEGATE(in)) , m_func(func) { } NETLIB_HANDLERI(in) { const netlist_sig_t cur = m_in(); m_func(*this, cur); } private: logic_input_t m_in; FUNC m_func; }; /// \brief Set parameters to buffers contents at regular intervals /// /// This device will update a parameter from a buffers passed to the device. /// It is the responsibility of the controlling application to ensure that /// the buffer is filled at regular intervals. /// /// \tparam T The buffer type /// \tparam N Maximum number of supported buffers /// template NETLIB_OBJECT(buffered_param_setter) { public: NETLIB_CONSTRUCTOR(buffered_param_setter) , m_sample_time(netlist_time::zero()) , m_feedback(*this, "FB", NETLIB_DELEGATE(feedback)) // clock part , m_Q(*this, "Q") , m_pos(0) , m_samples(0) , m_param_name(*this, "CHAN", "") , m_param_mult(*this, "MULT", 1.0) , m_param_offset(*this, "OFFSET", 0.0) , m_param(nullptr) , m_id(*this, "ID", 0) { connect("FB", "Q"); m_buffer = nullptr; } protected: NETLIB_RESETI() { } NETLIB_HANDLERI(feedback) { if (m_pos < m_samples) { // check if called outside of stream_update if (m_buffer != nullptr) { const nl_fptype v = (*m_buffer)[m_pos]; //m_params[i]->set(v * m_param_mults[i]() + m_param_offsets[i]()); m_param_setter(v * m_param_mult() + m_param_offset()); } } else { // FIXME: The logic has a rounding issue because time resolution divided // by 48,000 is not a natural number. The fractional part // adds up to one samples every 13 seconds for 100 ps resolution. // Fixing this is possible but complicated and expensive. } m_pos++; m_Q.net().toggle_and_push_to_queue(m_sample_time); } public: /// \brief resolve parameter names to pointers /// /// This function must be called after all device were constructed but /// before reset is called. void resolve_params(netlist_time sample_time) { m_pos = 0; m_sample_time = sample_time; if (m_param_name() != pstring("")) { param_t *p = &state().setup().find_param(m_param_name()).param(); m_param = p; if (dynamic_cast(p) != nullptr) m_param_setter = setter_t(&NETLIB_NAME(buffered_param_setter)::setter, this); else if (dynamic_cast(p) != nullptr) m_param_setter = setter_t(&NETLIB_NAME(buffered_param_setter)::setter, this); } } void buffer_reset(netlist_time sample_time, std::size_t num_samples, T *inputs) { m_samples = num_samples; m_sample_time = sample_time; m_pos = 0; m_buffer = inputs; } std::size_t id() const { return m_id; } private: using setter_t = plib::pmfp; template void setter(nl_fptype v) { static_cast(m_param)->set(v); } netlist_time m_sample_time; logic_input_t m_feedback; logic_output_t m_Q; std::size_t m_pos; std::size_t m_samples; param_str_t m_param_name; param_fp_t m_param_mult; param_fp_t m_param_offset; param_t * m_param; setter_t m_param_setter; T * m_buffer; param_num_t m_id; }; } // namespace interface } // namespace netlist #endif // NLINTERFACE_H_