Next: , Previous: Basics, Up: Introduction to libLCS


3.2 A Simple Example

In this section, a simple example is presented which will help a new libLCS user to get started. Though simple, it will introduce the basic constructs and a few of the 'off-the-shelf' logic gate modules provided in libLCS.

3.2.1 XOR gate using off-the-shelf AND, OR and NOT gates

We will build and simulate a circuit which behaves like a 2-input XOR gate using only AND, OR and NOT gates. Let a and b be the inputs to our circuit and let s be the output. Then, the boolean function for s is:

     s = ab' + a'b 

Hence, to generate s from a and b we require two 2-input AND gates, one 2-input OR gate, and two NOT gates. The circuit diagram is as follows (the small circles/bubbles represent NOT gates):

xor_example.jpg
3.2.1.1 Circuit description

Each node in the above circuit translates into a bus in the case of libLCS. As marked in the circuit, seven single-line busses a, b, a_, b_, p1, p2 and s are required to describe the circuit. The first step in our program will be to declare these 7 single-line bus objects. Busses are encapsulated into a template class Bus<int bits>. The number bits denotes the number of lines in the bus and takes a default value of 1. We will declare the bus objects for our circuit as follows.

     Bus<1> a, b, a_, b_, p1, p2, s; // The bus names are same as 
                                     // that in the diagram above.
                                     // The template argument 1
                                     // denotes single-line busses.

After declaring the busses in the circuit, the next step is to initialise the logic gate modules in the circuit. We will first initialise the NOT gates. The NOT gate modules are provided as a template class Not<unsinged int delay>. The delay parameter denotes the propogation delay of the NOT gate. It takes a default value of zero. The NOT gate initialisation is done as follows.

     Not<> ng1(a_, a), ng2(b_, b); // We donot pass any template paremeters
                                   // to the NOT gate class as we shall use 
                                   // the default value of 0 for the delay 
                                   // parameter.

Notice the order of arguments being passed to the NOT gate constructors. The output bus is the first argument, and the input bus is the second argument.


We shall next initialise the AND gate modules of our circuit. The AND gate modules are provided as a class template And<int inbits, unsigned int delay>. The parameter inbits denotes the number of lines in the input bus to the AND gate. For our circuit we require two input AND gates. Hence, we will use a value of 2 for the template parameter inbits. The second template parameter denotes the input to output propogation delay taking a default value of zero.

A two input AND gate will require a two-line bus as input. However, we have declared only single-line busses. libLCS provides an overloaded operator, with which one can concatenate two busses to form wider busses. We will make use of this concatenation operator to initialise the two 2-input AND gates required for our circuit. The order of arguments being passed to the AND gate constructors is same as that in the case of NOT gates - the output bus is the first argument and the input bus is the second argument.
     And<2> ag1(p1, (a,b_)), ag2(p2, (a_,b)); // Notice the use of the template parameter of 
                                              // 2 to denote a 2-input AND gate. The second 
                                              // template parameter is not passed to the AND 
                                              // gate class as we shall make use of the default 
                                              // value of zero for the delay. Also, the ',' 
                                              // operator is used to concatenate single-line 
                                              // busses into two-line busses.

The 2-input OR gate module is initialised in a similar manner.

     lcs::Or<2> og(s, (p1,p2));

See the section Logic Gates for more information on logic gate class templates.

3.2.1.2 The class Tester

With the above OR gate initialisation, our circuit is completely described. However, we will also need a way to test our circuit with different inputs. For this, libLCS provides a template class Tester<int inbits>. A Tester object will have to be initialised by passing a bus object to the constructor. The template parameter inbits should be the same as the width of the bus which passed as an argument to the constructor.


At every clock state change, the Tester object feeds a different value onto the bus object with which it was created (A libLCS simulation system has a clock running in the background. This clock has a default pulse width of 100 system time units. The system clock and system time are introduced in detail in the section System Time and Sequential Circuit Elements). The values it feeds are in sequence from 0 to 2w - 1, where w is the width of the bus with which it was created. For our circuit, we will make use of a Tester object to feed different values to the inputs a and b as follows.
     Tester<2> tester((a,b)); // The template parameter 2 indicates the bus width of 
                              // the input bus to our circuit.

NOTE: The Tester class is a very simple utility class. No more than one Tester object should be initialised for a given simulation.

3.2.1.3 The class ChangeMonitor and exporting value changes to a VCD file

Testing a circuit would not only require feeding proper inputs, one should also be able to monitor the different signals in the circuit to verify the correctness. libLCS provides a class ChangeMonitor<int width> for this. An object of this class should be instantiated by passing two arguments to the constructor - a bus object and an std::string variable. Then, the initialised ChangeMonitor object will monitor the bus line states and reports whenever a change occurs. The std::string variable passed to the constructor is used as a name for the bus when reporting changes. The template parameter width should be the same as the width of the bus being monitered. For our circuit, we will make use of two ChangeMonitor objects: one to monitor the inputs, the other to monitor the output. This is done as follows.

     ChangeMonitor<2> inputMonitor((a,b), "Input", DUMP_ON); // The template parameter 2 indicates the  
                                                             // bus width of the input bus to our circuit.
     ChangeMonitor<1> outputMonitor(s, "Output", DUMP_ON);   // The template parameter 1 indicates the bus  
                                                             // width of the output bus to our circuit.

Note the additional parameter, DUMP_ON, passed to the ChangeMonitor constructors. This indicates that the states of the bus lines, which are being monitored, should be dumped into a VCD file. If you do not want them to be dumped, then you can either omit the last parameter, or pass DUMP_OFF instead of DUMP_ON.

3.2.1.4 The simulation

After all the circuit elements are setup, we need to start the simulation. Before we start, we also need to set the time at which the simulation should stop. This is done by a call to the static function Simulation::setStopTime. After setting the stop time, we should start the simulation by a call to the static function Simulation::start.


For our simulation, we will set the stop time at 1000 time units. The complete program which simulates our circuit is as follows.
     
     // Include the only required header file.
     #include <lcs/lcs.h>
     
     // All libLCS constructs are defined under
     // the namespace lcs.
     using namespace lcs;
     
     int main()
     {
         Bus<1> a, b, a_, b_, p1, p2, s;
         
         Not<> ng1(a_, a), ng2(b_, b);
         And<2> ag1(p1, (a,b_)), ag2(p2, (a_,b));
         Or<2> og(s, (p1,p2));
     
         ChangeMonitor<2> inputMonitor((a,b), "Input", DUMP_ON);
         ChangeMonitor<1> outputMonitor(s, "Output", DUMP_ON);
     
         Tester<2> tester((a,b));
     
         Simulation::setStopTime(1000);
         Simulation::start();
     
         return 0;
     }

When the above program is compiled and run, the following output will be obtained on the standard output device. (See the section Basics for information on compiling a program which uses libLCS.)

     At time: 0,     Input: 00
     At time: 0,     Output: 0
     At time: 200,   Input: 01
     At time: 200,   Output: 1
     At time: 300,   Input: 10
     At time: 400,   Input: 11
     At time: 400,   Output: 0

We will now try to understand what the above output means. When the circuit is turned on (ie. when that the simulation starts at time 0), the object tester sets the busses a and b to a value of zero each. Correspondingly, the output changes to zero from an unknown value at time 0.


The object tester feeds sequential values at every clock state change, starting from a value of 0 . The default pulse width of the clock in libLCS is 100 system time units. Hence, a value of 0 is fed onto the input bus (a,b) at the first clock pulse occuring at 100 time units. The output corresponding to this input is 0. However, since the output and input values were 0 even before the first clock pulse, the ChangeMonitor objects does not display the input and output states at the time of 100 units.

When the input changes to a value 01 at 200 time units, the output correspondingly changes to 1. When the input changes to 10 at 300 time units, the correct output should be 1, which is the same as that before 300 time units. Hence the change monitor does not report anything about the output as the output did not change from the previous value. When the input changes to 11 at 400 time units, the output changes to 0 from a value of 1. This is reported accordingly as there is a change from the previous value.
3.2.1.5 The VCD file

Apart from the display of the simlation results on the standard output, we requested that the line states of the busses being monitored should be dumped into a VCD file. By default, the values are dumped into a file with name "dump.vcd" in the directory in which the simulation was executed. If one wants a different name for the VCD file, they should set it using the static function Simulation::setDumpFileName(filename), where filename is of type std::string. The dumped VCD file can be viewed using a VCD viewer. GTKWave is one such excellent tool which can plot the VCD wave forms. The screenshot of the GTKWave plot of the VCD file which was generated from the above simulation is as follows:

jpg