File indexing completed on 2024-05-19 13:33:11

0001 /***************************************************************************
0002  *   Copyright (C) 2005 by David Saxton                                    *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #ifndef SIMULATOR_H
0012 #define SIMULATOR_H
0013 
0014 #include <list>
0015 
0016 #include "circuit.h"
0017 #include "logic.h"
0018 
0019 /**
0020 This should be a multiple of 1000. It is the number of times a second that
0021 linear elements are updated.
0022 */
0023 const int LINEAR_UPDATE_RATE = int(10000);
0024 const double LINEAR_UPDATE_PERIOD = 1.0 / LINEAR_UPDATE_RATE;
0025 
0026 const int SIMULATOR_STEP_INTERVAL_MS = 20;
0027 
0028 /**
0029 This should be a multiple of 1000. It is the number of times a second that
0030 logic elements are updated.
0031 */
0032 const int LOGIC_UPDATE_RATE = int(1000000);
0033 
0034 const int LOGIC_UPDATE_PER_STEP = int(LOGIC_UPDATE_RATE / LINEAR_UPDATE_RATE);
0035 
0036 class QTimer;
0037 
0038 class Circuit;
0039 
0040 class CircuitDocument;
0041 
0042 class Component;
0043 
0044 class ComponentCallback;
0045 
0046 class ECNode;
0047 
0048 class GpsimProcessor;
0049 
0050 class LogicIn;
0051 
0052 class LogicOut;
0053 
0054 class Switch;
0055 
0056 class Wire;
0057 
0058 typedef QList<ECNode *> ECNodeList;
0059 typedef QList<LogicIn *> LogicInList;
0060 typedef void (Component::*VoidCallbackPtr)();
0061 
0062 class ComponentCallback
0063 {
0064 public:
0065     ComponentCallback(Component *component, VoidCallbackPtr function)
0066     {
0067         m_pComponent = component;
0068         m_pFunction = function;
0069     }
0070 
0071     void callback()
0072     {
0073         (m_pComponent->*m_pFunction)();
0074     }
0075 
0076     Component *component() const
0077     {
0078         return m_pComponent;
0079     }
0080 
0081 protected:
0082     Component *m_pComponent;
0083     VoidCallbackPtr m_pFunction;
0084 };
0085 
0086 /**
0087 This singleton class oversees all simulation (keeping in sync linear, nonlinear,
0088 logic, external simulators (such as gpsim), mechanical simulation, etc).
0089 @author David Saxton
0090 */
0091 
0092 class Simulator : public QObject
0093 {
0094     Q_OBJECT
0095 
0096 public:
0097     static bool isDestroyedSim();
0098     static Simulator *self();
0099     ~Simulator() override;
0100 
0101     /**
0102      * Number of (1/LOGIC_UPDATE_RATE) intervals that the simulator has been
0103      * stepping for.
0104      */
0105     long long time() const; /* {
0106        return m_stepNumber * LOGIC_UPDATE_PER_STEP + m_llNumber;
0107    } */
0108 
0109     /**
0110      * Initializes a new logic chain.
0111      */
0112     void createLogicChain(LogicOut *logicOut, const LogicInList &logicInList, const PinList &pinList);
0113     /**
0114      * Adds the given LogicOut to the list of changed LogicOuts
0115      */
0116     void addChangedLogic(LogicOut *changed)
0117     {
0118         m_pChangedLogicLast->setNextChanged(changed, m_currentChain);
0119         m_pChangedLogicLast = changed;
0120     }
0121 
0122     /**
0123      * Remove pointers to the given LogicOut, called when it is deleted for
0124      * safety reasons.
0125      */
0126     void removeLogicOutReferences(LogicOut *logic);
0127     /**
0128      * Remove pointers to the given LogicIn, called when it is deleted for
0129      * safety reasons. Simulator does not have any references to LogicIns
0130      * itself - instead, they are removed from logic chains which are
0131      * currently marked as changed.
0132      */
0133     void removeLogicInReferences(LogicIn *logic);
0134     /**
0135      * Adds the given Circuit to the list of changed Circuits
0136      */
0137     void addChangedCircuit(Circuit *changed)
0138     {
0139         m_pChangedCircuitLast->setNextChanged(changed, m_currentChain);
0140         m_pChangedCircuitLast = changed;
0141     }
0142 
0143     /**
0144      * add a callback to be executed at the current step, at the given logic update number
0145      * @param at the logic update number
0146      * @param ccb the callback that shold be called; note that the ownership of the callback
0147      *      object remains at the caller
0148      */
0149     inline void addStepCallback(int at, ComponentCallback *ccb);
0150     /**
0151      * Add the given processor to the simulator. GpsimProcessor::step will
0152      * be called while present in the simulator (it is at GpsimProcessor's
0153      * disgression whether to actually step, depending on its running
0154      * status).
0155      * @see detachGpsimProcessor( GpsimProcessor * cpu );
0156      */
0157     void attachGpsimProcessor(GpsimProcessor *cpu);
0158     /**
0159      * Remove the given processor from the simulation loop
0160      */
0161     void detachGpsimProcessor(GpsimProcessor *cpu);
0162     /**
0163      * Attach the component callback to the simulator. This will be called
0164      * during the logic update loop, at LOGIC_UPDATE_RATE times per second (so
0165      * make sure the function passed is an efficient one!).
0166      */
0167     void attachComponentCallback(Component *component, VoidCallbackPtr function);
0168     /**
0169      * Removes the callbacks for the given component from the simulator.
0170      */
0171     void detachComponentCallbacks(Component &component);
0172     /**
0173      * Attach the component to the simulator.
0174      */
0175     void attachComponent(Component *component);
0176     /**
0177      * Detaches the component from the simulator.
0178      */
0179     void detachComponent(Component *component);
0180     /**
0181      * Attach a circuit to the simulator
0182      */
0183     void attachCircuit(Circuit *circuit);
0184     /**
0185      * Detach a circuit from the simulator.
0186      */
0187     void detachCircuit(Circuit *circuit);
0188 
0189     /**
0190      * @return whether or not we are currently simulating stuff
0191      * @see slotSetSimulating
0192      */
0193     bool isSimulating() const
0194     {
0195         return m_bIsSimulating;
0196     }
0197 
0198 signals:
0199     /**
0200      * Emitted when the simulating state changes.
0201      * @see slotSetSimulating
0202      */
0203     void simulatingStateChanged(bool isSimulating);
0204 
0205 public slots:
0206     /**
0207      * Set whether or not to simulate at the moment.
0208      * @see isSimulating
0209      */
0210     void slotSetSimulating(bool simulate);
0211 
0212 private slots:
0213     void step();
0214     void printTimingStatistics();
0215 
0216 private:
0217     bool m_bIsSimulating;
0218     //  static Simulator *m_pSelf;
0219 
0220     QTimer *m_stepTimer;
0221 
0222     /// List of LogicOuts that are at the start of a LogicChain
0223     QList<LogicOut *> m_logicChainStarts;
0224     std::list<GpsimProcessor *> *m_gpsimProcessors;
0225 
0226     // doesn't look too appropriate.
0227     // essentially a grab bag of every odd *component* that answers "true" to does step non-logic,
0228     // Which is every component that has special UI-related code that needs to be called every time the simulator steps.
0229     // this is not to be confused with elements which have nonLinear and Reactive components. =P
0230     std::list<Component *> *m_components;
0231     std::list<ComponentCallback> *m_componentCallbacks;
0232     std::list<Circuit *> *m_ordinaryCircuits;
0233 
0234     // allow a variable number of callbacks be scheduled at each possible time.
0235     std::list<ComponentCallback *> *m_pStartStepCallback[LOGIC_UPDATE_PER_STEP];
0236 
0237     Circuit *m_pChangedCircuitStart;
0238     Circuit *m_pChangedCircuitLast;
0239 
0240     LogicOut *m_pChangedLogicStart;
0241     LogicOut *m_pChangedLogicLast;
0242 
0243     QTimer *m_printTimingStatsTimer;
0244     qint64 m_stepMaxNs;
0245     double m_stepRollingAvgNs;
0246     qint64 m_stepLastNs;
0247     qint64 m_stepsSinceStart;
0248     qint64 m_stepsSincePrint;
0249 
0250 public:
0251     Simulator();
0252 
0253 private:
0254     unsigned long m_llNumber; // simulation clock; Exists only to support the time() callback.
0255     long long m_stepNumber;
0256 
0257     // looks like there are only ever two chains, 0 and 1, code elsewhere toggles between the two...
0258     unsigned char m_currentChain;
0259 };
0260 
0261 inline void Simulator::addStepCallback(int at, ComponentCallback *ccb)
0262 {
0263     // code was buggy[er], don't really know what variables are for, rewritten to make it work,
0264     // OK for now.
0265     if ((at < 0) || (at >= LOGIC_UPDATE_PER_STEP)) {
0266         return; // note: maybe log here the error
0267     }
0268     if (!m_pStartStepCallback[at]) {
0269         m_pStartStepCallback[at] = new std::list<ComponentCallback *>;
0270     }
0271 
0272     m_pStartStepCallback[at]->push_back(ccb);
0273 }
0274 
0275 #endif