1 // flipflop.hxx - implementation of multiple flip flop types
3 // Written by Torsten Dreyer
5 // Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "flipflop.hxx"
23 #include "functor.hxx"
24 #include "inputvalue.hxx"
25 #include <Main/fg_props.hxx>
32 namespace FGXMLAutopilot {
35 * @brief Flip flop implementation for a RS flip flop with dominant RESET
37 * RS (reset-set) flip flops act as a fundamental latch. It has two input lines,
38 * S (set) and R (reset). Activating the set input sets the output while activating
39 * the reset input resets the output. If both inputs are activated, the output
40 * is deactivated, too. This is why the RESET line is called dominant. Use a
41 * SRFlipFlopImplementation for a dominant SET line.
45 * <td colspan="3">Logictable</td>
48 * <td>S</td><td>R</td><td>Q</td>
51 * <td>false</td><td>false</td><td>unchanged</td>
54 * <td>false</td><td>true</td><td>false</td>
57 * <td>true</td><td>false</td><td>true</td>
60 * <td>true</td><td>true</td><td>false</td>
64 class RSFlipFlopImplementation : public FlipFlopImplementation {
68 RSFlipFlopImplementation( bool rIsDominant = true ) : _rIsDominant( rIsDominant ) {}
69 virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
73 * @brief Flip flop implementation for a RS flip flop with dominant SET
75 * SR (set-reset) flip flops act as a fundamental latch. It has two input lines,
76 * S (set) and R (reset). Activating the set input sets the output while activating
77 * the reset input resets the output. If both inputs are activated, the output
78 * is activated, too. This is why the SET line is called dominant. Use a
79 * RSFlipFlopImplementation for a dominant RESET line.
83 * <td colspan="3">Logictable</td>
86 * <td>S</td><td>R</td><td>Q</td>
89 * <td>false</td><td>false</td><td>unchanged</td>
92 * <td>false</td><td>true</td><td>false</td>
95 * <td>true</td><td>false</td><td>true</td>
98 * <td>true</td><td>true</td><td>true</td>
102 class SRFlipFlopImplementation : public RSFlipFlopImplementation {
104 SRFlipFlopImplementation() : RSFlipFlopImplementation( false ) {}
108 * @brief Base class for clocked flip flop implementation
110 * A clocked flip flop computes it's output on the raising edge (false/true transition)
111 * of the clock input. If such a transition is detected, the onRaisingEdge method is called
112 * by this implementation. All clocked flip flops inherit from the RS flip flop and may
113 * be set or reset by the respective set/reset lines. Note that the RS implementation
114 * ignores the clock, The output is set immediately, regardless of the state of the clock
115 * input. The "clock" input is mandatory for clocked flip flops.
118 class ClockedFlipFlopImplementation : public RSFlipFlopImplementation {
121 * @brief the previous state of the clock input
127 * @brief pure virtual function to be implemented from the implementing class, gets called
128 * from the update method if the raising edge of the clock input was detected.
129 * @param input a map of named input lines
130 * @param q a reference to a boolean variable to receive the output state
131 * @return true if the state has changed, false otherwise
133 virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) = 0;
137 * @brief constructor for a ClockedFlipFlopImplementation
138 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
140 ClockedFlipFlopImplementation( bool rIsDominant = true ) : RSFlipFlopImplementation( rIsDominant ), _clock(false) {}
143 * @brief evaluates the output state from the input lines.
144 * This method basically waits for a raising edge and calls onRaisingEdge
145 * @param dt the elapsed time in seconds from since the last call
146 * @param input a map of named input lines
147 * @param q a reference to a boolean variable to receive the output state
148 * @return true if the state has changed, false otherwise
150 virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
154 * @brief Implements a JK flip flop as a clocked flip flop
156 * The JK flip flop has five input lines: R, S, clock, J and K. The R and S lines work as described
157 * in the RS flip flop. Setting the J line to true sets the output to true on the next raising
158 * edge of the clock line. Setting the K line to true sets the output to false on the next raising
159 * edge of the clock line. If both, J and K are true, the output is toggled at with every raising
160 * edge of the clock line.
162 * Undefined inputs default to false.
166 * <td colspan="7">Logictable</td>
169 * <td>S</td><td>R</td><td>J</td><td>K</td><td>clock</td><td>Q (previous)</td><td>Q</td>
172 * <td>false</td><td>false</td><td>false</td><td>false</td><td>any</td><td>any</td><td>unchanged</td>
175 * <td>true</td><td>false</td><td>any</td><td>any</td><td>any</td><td>any</td><td>true</td>
178 * <td>any</td><td>true</td><td>any</td><td>any</td><td>any</td><td>any</td><td>false</td>
181 * <td>false</td><td>false</td><td>true</td><td>false</td><td>^</td><td>any</td><td>true</td>
184 * <td>false</td><td>false</td><td>false</td><td>true</td><td>^</td><td>any</td><td>false</td>
187 * <td>false</td><td>false</td><td>true</td><td>true</td><td>^</td><td>false</td><td>true</td>
190 * <td>false</td><td>false</td><td>true</td><td>true</td><td>^</td><td>true</td><td>false</td>
194 class JKFlipFlopImplementation : public ClockedFlipFlopImplementation {
197 * @brief constructor for a JKFlipFlopImplementation
198 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
200 JKFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
203 * @brief compute the output state according to the logic table on the raising edge of the clock
204 * @param input a map of named input lines
205 * @param q a reference to a boolean variable to receive the output state
206 * @return true if the state has changed, false otherwise
208 virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q );
212 * @brief Implements a D (delay) flip flop.
215 class DFlipFlopImplementation : public ClockedFlipFlopImplementation {
218 * @brief constructor for a DFlipFlopImplementation
219 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
221 DFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
224 * @brief compute the output state according to the logic table on the raising edge of the clock
225 * @param input a map of named input lines
226 * @param q a reference to a boolean variable to receive the output state
227 * @return true if the state has changed, false otherwise
229 virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
230 q = input.get_value("D");
236 * @brief Implements a T (toggle) flip flop.
239 class TFlipFlopImplementation : public ClockedFlipFlopImplementation {
242 * @brief constructor for a TFlipFlopImplementation
243 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
245 TFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
248 * @brief compute the output state according to the logic table on the raising edge of the clock
249 * @param input a map of named input lines
250 * @param q a reference to a boolean variable to receive the output state
251 * @return true if the state has changed, false otherwise
253 virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
260 * @brief Implements a monostable flip flop
262 * The stable output state is false.
265 class MonoFlopImplementation : public JKFlipFlopImplementation {
267 virtual bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode );
268 InputValueList _time;
272 * @brief constructor for a MonoFlopImplementation
273 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
275 MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ), _t(0.0) {}
277 * @brief evaluates the output state from the input lines and returns to the stable state
278 * after expiry of the internal timer
279 * @param dt the elapsed time in seconds from since the last call
280 * @param input a map of named input lines
281 * @param q a reference to a boolean variable to receive the output state
282 * @return true if the state has changed, false otherwise
284 virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
289 using namespace FGXMLAutopilot;
291 bool MonoFlopImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
293 if( JKFlipFlopImplementation::configure( nodeName, configNode ) )
296 if (nodeName == "time") {
297 _time.push_back( new InputValue( configNode ) );
304 bool MonoFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
306 if( JKFlipFlopImplementation::getState( dt, input, q ) ) {
307 _t = q ? _time.get_value() : 0;
321 bool RSFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
323 bool s = input.get_value("S");
324 bool r = input.get_value("R");
326 // s == false && q == false: no change, keep state
328 if( _rIsDominant ) { // RS: reset is dominant
329 if( s ) q = true; // set
330 if( r ) q = false; // reset
331 } else { // SR: set is dominant
332 if( r ) q = false; // reset
333 if( s ) q = true; // set
335 return true; // signal state changed
337 return false; // signal state unchagned
340 bool ClockedFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
342 bool c = input.get_value("clock");
343 bool raisingEdge = c && !_clock;
347 if( RSFlipFlopImplementation::getState( dt, input, q ) )
351 if( !raisingEdge ) return false; //signal no change
352 return onRaisingEdge( input, q );
355 bool JKFlipFlopImplementation::onRaisingEdge( DigitalComponent::InputMap input, bool & q )
357 bool j = input.get_value("J");
358 bool k = input.get_value("K");
360 // j == false && k == false: no change, keep state
365 if( j ) q = true; // set
366 if( k ) q = false; // reset
368 return true; // signal state changed
371 return false; // signal no change
374 bool FlipFlopImplementation::configure( SGPropertyNode_ptr configNode )
376 for (int i = 0; i < configNode->nChildren(); ++i ) {
377 SGPropertyNode_ptr prop;
379 SGPropertyNode_ptr child = configNode->getChild(i);
380 string cname(child->getName());
382 if( configure( cname, child ) )
385 } // for configNode->nChildren()
391 static map<string,FunctorBase<FlipFlopImplementation> *> componentForge;
393 bool FlipFlop::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
395 if( componentForge.empty() ) {
396 componentForge["RS"] = new CreateAndConfigureFunctor<RSFlipFlopImplementation,FlipFlopImplementation>();
397 componentForge["SR"] = new CreateAndConfigureFunctor<SRFlipFlopImplementation,FlipFlopImplementation>();
398 componentForge["JK"] = new CreateAndConfigureFunctor<JKFlipFlopImplementation,FlipFlopImplementation>();
399 componentForge["D"] = new CreateAndConfigureFunctor<DFlipFlopImplementation, FlipFlopImplementation>();
400 componentForge["T"] = new CreateAndConfigureFunctor<TFlipFlopImplementation, FlipFlopImplementation>();
401 componentForge["monostable"] = new CreateAndConfigureFunctor<MonoFlopImplementation, FlipFlopImplementation>();
404 if( DigitalComponent::configure( nodeName, configNode ) )
407 if( nodeName == "type" ) {
408 string type(configNode->getStringValue());
409 if( componentForge.count(type) == 0 ) {
410 SG_LOG( SG_AUTOPILOT, SG_BULK, "unhandled flip-flop type <" << type << ">" << endl );
413 _implementation = (*componentForge[type])( configNode->getParent() );
417 if (nodeName == "set"||nodeName == "S") {
418 _input["S"] = sgReadCondition( fgGetNode("/"), configNode );
422 if (nodeName == "reset" || nodeName == "R" ) {
423 _input["R"] = sgReadCondition( fgGetNode("/"), configNode );
427 if (nodeName == "J") {
428 _input["J"] = sgReadCondition( fgGetNode("/"), configNode );
432 if (nodeName == "K") {
433 _input["K"] = sgReadCondition( fgGetNode("/"), configNode );
437 if (nodeName == "D") {
438 _input["D"] = sgReadCondition( fgGetNode("/"), configNode );
442 if (nodeName == "clock") {
443 _input["clock"] = sgReadCondition( fgGetNode("/"), configNode );
450 void FlipFlop::update( bool firstTime, double dt )
452 if( _implementation == NULL ) {
453 SG_LOG( SG_AUTOPILOT, SG_ALERT, "No flip-flop implementation for " << get_name() << endl );
459 q0 = q = get_output();
461 if( _implementation->getState( dt, _input, q ) && q0 != q ) {
465 cout << "updating flip-flop \"" << get_name() << "\"" << endl;
466 cout << "prev. Output:" << q0 << endl;
467 for( InputMap::const_iterator it = _input.begin(); it != _input.end(); ++it )
468 cout << "Input \"" << (*it).first << "\":" << (*it).second->test() << endl;
469 cout << "new Output:" << q << endl;