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( SGPropertyNode& cfg_node,
268 const std::string& cfg_name,
269 SGPropertyNode& prop_root );
270 InputValueList _time;
274 * @brief constructor for a MonoFlopImplementation
275 * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
277 MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ), _t(0.0) {}
279 * @brief evaluates the output state from the input lines and returns to the stable state
280 * after expiry of the internal timer
281 * @param dt the elapsed time in seconds from since the last call
282 * @param input a map of named input lines
283 * @param q a reference to a boolean variable to receive the output state
284 * @return true if the state has changed, false otherwise
286 virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
291 using namespace FGXMLAutopilot;
293 //------------------------------------------------------------------------------
294 bool MonoFlopImplementation::configure( SGPropertyNode& cfg_node,
295 const std::string& cfg_name,
296 SGPropertyNode& prop_root )
298 if( JKFlipFlopImplementation::configure(cfg_node, cfg_name, prop_root) )
301 if (cfg_name == "time") {
302 _time.push_back( new InputValue(prop_root, cfg_node) );
309 bool MonoFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
311 if( JKFlipFlopImplementation::getState( dt, input, q ) ) {
312 _t = q ? _time.get_value() : 0;
326 bool RSFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
328 bool s = input.get_value("S");
329 bool r = input.get_value("R");
331 // s == false && q == false: no change, keep state
333 if( _rIsDominant ) { // RS: reset is dominant
334 if( s ) q = true; // set
335 if( r ) q = false; // reset
336 } else { // SR: set is dominant
337 if( r ) q = false; // reset
338 if( s ) q = true; // set
340 return true; // signal state changed
342 return false; // signal state unchagned
345 bool ClockedFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
347 bool c = input.get_value("clock");
348 bool raisingEdge = c && !_clock;
352 if( RSFlipFlopImplementation::getState( dt, input, q ) )
356 if( !raisingEdge ) return false; //signal no change
357 return onRaisingEdge( input, q );
360 bool JKFlipFlopImplementation::onRaisingEdge( DigitalComponent::InputMap input, bool & q )
362 bool j = input.get_value("J");
363 bool k = input.get_value("K");
365 // j == false && k == false: no change, keep state
370 if( j ) q = true; // set
371 if( k ) q = false; // reset
373 return true; // signal state changed
376 return false; // signal no change
379 //------------------------------------------------------------------------------
380 bool FlipFlopImplementation::configure( SGPropertyNode& prop_root,
381 SGPropertyNode& cfg )
383 for( int i = 0; i < cfg.nChildren(); ++i )
385 SGPropertyNode_ptr child = cfg.getChild(i);
386 string cname(child->getName());
388 if( configure(*child, cname, prop_root) )
396 static map<string,FunctorBase<FlipFlopImplementation> *> componentForge;
398 //------------------------------------------------------------------------------
399 bool FlipFlop::configure( SGPropertyNode& cfg_node,
400 const std::string& cfg_name,
401 SGPropertyNode& prop_root )
403 if( componentForge.empty() ) {
404 componentForge["RS"] = new CreateAndConfigureFunctor<RSFlipFlopImplementation,FlipFlopImplementation>();
405 componentForge["SR"] = new CreateAndConfigureFunctor<SRFlipFlopImplementation,FlipFlopImplementation>();
406 componentForge["JK"] = new CreateAndConfigureFunctor<JKFlipFlopImplementation,FlipFlopImplementation>();
407 componentForge["D"] = new CreateAndConfigureFunctor<DFlipFlopImplementation, FlipFlopImplementation>();
408 componentForge["T"] = new CreateAndConfigureFunctor<TFlipFlopImplementation, FlipFlopImplementation>();
409 componentForge["monostable"] = new CreateAndConfigureFunctor<MonoFlopImplementation, FlipFlopImplementation>();
412 if( DigitalComponent::configure(cfg_node, cfg_name, prop_root) )
415 if( cfg_name == "type" ) {
416 string type(cfg_node.getStringValue());
417 if( componentForge.count(type) == 0 ) {
422 "unhandled flip-flop type <" << type << ">"
426 _implementation = (*componentForge[type])(prop_root, *cfg_node.getParent());
430 if (cfg_name == "set"||cfg_name == "S") {
431 _input["S"] = sgReadCondition(&prop_root, &cfg_node);
435 if (cfg_name == "reset" || cfg_name == "R" ) {
436 _input["R"] = sgReadCondition(&prop_root, &cfg_node);
440 if (cfg_name == "J") {
441 _input["J"] = sgReadCondition(&prop_root, &cfg_node);
445 if (cfg_name == "K") {
446 _input["K"] = sgReadCondition(&prop_root, &cfg_node);
450 if (cfg_name == "D") {
451 _input["D"] = sgReadCondition(&prop_root, &cfg_node);
455 if (cfg_name == "clock") {
456 _input["clock"] = sgReadCondition(&prop_root, &cfg_node);
463 void FlipFlop::update( bool firstTime, double dt )
465 if( _implementation == NULL ) {
466 SG_LOG( SG_AUTOPILOT, SG_ALERT, "No flip-flop implementation for " << get_name() << endl );
472 q0 = q = get_output();
474 if( _implementation->getState( dt, _input, q ) && q0 != q ) {
478 cout << "updating flip-flop \"" << get_name() << "\"" << endl;
479 cout << "prev. Output:" << q0 << endl;
480 for( InputMap::const_iterator it = _input.begin(); it != _input.end(); ++it )
481 cout << "Input \"" << (*it).first << "\":" << (*it).second->test() << endl;
482 cout << "new Output:" << q << endl;