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>
27 namespace FGXMLAutopilot {
30 * @brief Flip flop implementation for a RS flip flop with dominant RESET
32 * RS (reset-set) flip flops act as a fundamental latch. It has two input lines,
33 * S (set) and R (reset). Activating the set input sets the output while activating
34 * the reset input resets the output. If both inputs are activated, the output
35 * is deactivated, too. This is why the RESET line is called dominant. Use a
36 * SRFlipFlopImplementation for a dominant SET line.
40 * <td colspan="3">Logictable</td>
43 * <td>S</td><td>R</td><td>Q</td>
46 * <td>false</td><td>false</td><td>unchanged</td>
49 * <td>false</td><td>true</td><td>false</td>
52 * <td>true</td><td>false</td><td>true</td>
55 * <td>true</td><td>true</td><td>false</td>
59 class RSFlipFlopImplementation : public FlipFlopImplementation {
63 RSFlipFlopImplementation( bool rIsDominant = true ) : _rIsDominant( rIsDominant ) {}
64 virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
68 * @brief Flip flop implementation for a RS flip flop with dominant SET
70 * SR (set-reset) flip flops act as a fundamental latch. It has two input lines,
71 * S (set) and R (reset). Activating the set input sets the output while activating
72 * the reset input resets the output. If both inputs are activated, the output
73 * is activated, too. This is why the SET line is called dominant. Use a
74 * RSFlipFlopImplementation for a dominant RESET line.
78 * <td colspan="3">Logictable</td>
81 * <td>S</td><td>R</td><td>Q</td>
84 * <td>false</td><td>false</td><td>unchanged</td>
87 * <td>false</td><td>true</td><td>false</td>
90 * <td>true</td><td>false</td><td>true</td>
93 * <td>true</td><td>true</td><td>true</td>
97 class SRFlipFlopImplementation : public RSFlipFlopImplementation {
99 SRFlipFlopImplementation() : RSFlipFlopImplementation( false ) {}
103 * @brief Base class for clocked flip flop implementation
105 * A clocked flip flop computes it's output on the raising edge (false/true transition)
106 * of the clock input. If such a transition is detected, the onRaisingEdge method is called
107 * by this implementation. All clocked flip flops inherit from the RS flip flop and may
108 * be set or reset by the respective set/reset lines. Note that the RS implementation
109 * ignores the clock, The output is set immediately, regardless of the state of the clock
110 * input. The "clock" input is mandatory for clocked flip flops.
113 class ClockedFlipFlopImplementation : public RSFlipFlopImplementation {
116 * @brief the previous state of the clock input
122 * @brief pure virtual function to be implemented from the implementing class, gets called
123 * from the update method if the raising edge of the clock input was detected.
124 * @param input a map of named input lines
125 * @param q a reference to a boolean variable to receive the output state
126 * @return true if the state has changed, false otherwise
128 virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) = 0;
132 * @brief constructor for a ClockedFlipFlopImplementation
133 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
135 ClockedFlipFlopImplementation( bool rIsDominant = true ) : RSFlipFlopImplementation( rIsDominant ), _clock(false) {}
138 * @brief evaluates the output state from the input lines.
139 * This method basically waits for a raising edge and calls onRaisingEdge
140 * @param dt the elapsed time in seconds from since the last call
141 * @param input a map of named input lines
142 * @param q a reference to a boolean variable to receive the output state
143 * @return true if the state has changed, false otherwise
145 virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
149 * @brief Implements a JK flip flop as a clocked flip flop
151 * The JK flip flop has five input lines: R, S, clock, J and K. The R and S lines work as described
152 * in the RS flip flop. Setting the J line to true sets the output to true on the next raising
153 * edge of the clock line. Setting the K line to true sets the output to false on the next raising
154 * edge of the clock line. If both, J and K are true, the output is toggled at with every raising
155 * edge of the clock line.
157 * Undefined inputs default to false.
161 * <td colspan="7">Logictable</td>
164 * <td>S</td><td>R</td><td>J</td><td>K</td><td>clock</td><td>Q (previous)</td><td>Q</td>
167 * <td>false</td><td>false</td><td>false</td><td>false</td><td>any</td><td>any</td><td>unchanged</td>
170 * <td>true</td><td>false</td><td>any</td><td>any</td><td>any</td><td>any</td><td>true</td>
173 * <td>any</td><td>true</td><td>any</td><td>any</td><td>any</td><td>any</td><td>false</td>
176 * <td>false</td><td>false</td><td>true</td><td>false</td><td>^</td><td>any</td><td>true</td>
179 * <td>false</td><td>false</td><td>false</td><td>true</td><td>^</td><td>any</td><td>false</td>
182 * <td>false</td><td>false</td><td>true</td><td>true</td><td>^</td><td>false</td><td>true</td>
185 * <td>false</td><td>false</td><td>true</td><td>true</td><td>^</td><td>true</td><td>false</td>
189 class JKFlipFlopImplementation : public ClockedFlipFlopImplementation {
192 * @brief constructor for a JKFlipFlopImplementation
193 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
195 JKFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
198 * @brief compute the output state according to the logic table on the raising edge of the clock
199 * @param input a map of named input lines
200 * @param q a reference to a boolean variable to receive the output state
201 * @return true if the state has changed, false otherwise
203 virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q );
207 * @brief Implements a D (delay) flip flop.
210 class DFlipFlopImplementation : public ClockedFlipFlopImplementation {
213 * @brief constructor for a DFlipFlopImplementation
214 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
216 DFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
219 * @brief compute the output state according to the logic table on the raising edge of the clock
220 * @param input a map of named input lines
221 * @param q a reference to a boolean variable to receive the output state
222 * @return true if the state has changed, false otherwise
224 virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
225 q = input.get_value("D");
231 * @brief Implements a T (toggle) flip flop.
234 class TFlipFlopImplementation : public ClockedFlipFlopImplementation {
237 * @brief constructor for a TFlipFlopImplementation
238 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
240 TFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
243 * @brief compute the output state according to the logic table on the raising edge of the clock
244 * @param input a map of named input lines
245 * @param q a reference to a boolean variable to receive the output state
246 * @return true if the state has changed, false otherwise
248 virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
255 * @brief Implements a monostable flip flop
257 * The stable output state is false.
260 class MonoFlopImplementation : public JKFlipFlopImplementation {
262 virtual bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode );
263 InputValueList _time;
267 * @brief constructor for a MonoFlopImplementation
268 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
270 MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ) {}
272 * @brief evaluates the output state from the input lines and returns to the stable state
273 * after expiry of the internal timer
274 * @param dt the elapsed time in seconds from since the last call
275 * @param input a map of named input lines
276 * @param q a reference to a boolean variable to receive the output state
277 * @return true if the state has changed, false otherwise
279 virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
284 using namespace FGXMLAutopilot;
286 bool MonoFlopImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
288 if( JKFlipFlopImplementation::configure( nodeName, configNode ) )
291 if (nodeName == "time") {
292 _time.push_back( new InputValue( configNode ) );
299 bool MonoFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
301 if( JKFlipFlopImplementation::getState( dt, input, q ) ) {
302 _t = q ? _time.get_value() : 0;
316 bool RSFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
318 bool s = input.get_value("S");
319 bool r = input.get_value("R");
321 // s == false && q == false: no change, keep state
323 if( _rIsDominant ) { // RS: reset is dominant
324 if( s ) q = true; // set
325 if( r ) q = false; // reset
326 } else { // SR: set is dominant
327 if( r ) q = false; // reset
328 if( s ) q = true; // set
330 return true; // signal state changed
332 return false; // signal state unchagned
335 bool ClockedFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
337 bool c = input.get_value("clock");
338 bool raisingEdge = c && !_clock;
342 if( RSFlipFlopImplementation::getState( dt, input, q ) )
346 if( !raisingEdge ) return false; //signal no change
347 return onRaisingEdge( input, q );
350 bool JKFlipFlopImplementation::onRaisingEdge( DigitalComponent::InputMap input, bool & q )
352 bool j = input.get_value("J");
353 bool k = input.get_value("K");
355 // j == false && k == false: no change, keep state
360 if( j ) q = true; // set
361 if( k ) q = false; // reset
363 return true; // signal state changed
366 return false; // signal no change
369 bool FlipFlopImplementation::configure( SGPropertyNode_ptr configNode )
371 for (int i = 0; i < configNode->nChildren(); ++i ) {
372 SGPropertyNode_ptr prop;
374 SGPropertyNode_ptr child = configNode->getChild(i);
375 string cname(child->getName());
377 if( configure( cname, child ) )
380 } // for configNode->nChildren()
386 static map<string,FunctorBase<FlipFlopImplementation> *> componentForge;
388 bool FlipFlop::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
390 if( componentForge.empty() ) {
391 componentForge["RS"] = new CreateAndConfigureFunctor<RSFlipFlopImplementation,FlipFlopImplementation>();
392 componentForge["SR"] = new CreateAndConfigureFunctor<SRFlipFlopImplementation,FlipFlopImplementation>();
393 componentForge["JK"] = new CreateAndConfigureFunctor<JKFlipFlopImplementation,FlipFlopImplementation>();
394 componentForge["D"] = new CreateAndConfigureFunctor<DFlipFlopImplementation, FlipFlopImplementation>();
395 componentForge["T"] = new CreateAndConfigureFunctor<TFlipFlopImplementation, FlipFlopImplementation>();
396 componentForge["monostable"] = new CreateAndConfigureFunctor<MonoFlopImplementation, FlipFlopImplementation>();
399 if( DigitalComponent::configure( nodeName, configNode ) )
402 if( nodeName == "type" ) {
403 string type(configNode->getStringValue());
404 if( componentForge.count(type) == 0 ) {
405 SG_LOG( SG_AUTOPILOT, SG_BULK, "unhandled flip-flop type <" << type << ">" << endl );
408 _implementation = (*componentForge[type])( configNode->getParent() );
412 if (nodeName == "set"||nodeName == "S") {
413 _input["S"] = sgReadCondition( fgGetNode("/"), configNode );
417 if (nodeName == "reset" || nodeName == "R" ) {
418 _input["R"] = sgReadCondition( fgGetNode("/"), configNode );
422 if (nodeName == "J") {
423 _input["J"] = sgReadCondition( fgGetNode("/"), configNode );
427 if (nodeName == "K") {
428 _input["K"] = sgReadCondition( fgGetNode("/"), configNode );
432 if (nodeName == "D") {
433 _input["D"] = sgReadCondition( fgGetNode("/"), configNode );
437 if (nodeName == "clock") {
438 _input["clock"] = sgReadCondition( fgGetNode("/"), configNode );
445 void FlipFlop::update( bool firstTime, double dt )
447 if( _implementation == NULL ) {
448 SG_LOG( SG_AUTOPILOT, SG_ALERT, "No flip-flop implementation for " << get_name() << endl );
454 q0 = q = get_output();
456 if( _implementation->getState( dt, _input, q ) && q0 != q ) {
460 cout << "updating flip-flop \"" << get_name() << "\"" << endl;
461 cout << "prev. Output:" << q0 << endl;
462 for( InputMap::const_iterator it = _input.begin(); it != _input.end(); it++ )
463 cout << "Input \"" << (*it).first << "\":" << (*it).second->test() << endl;
464 cout << "new Output:" << q << endl;