About GBL Designer

GBL Designer is a graphical environment intended to simplify creating event-drivent designs which use GBL Simulation Library .

GBL Designer uses schematic approach to designing and testing of GBL modules. This approach frees a developer from the need to remember the details of GBL Simulation Library , generating the C++ library code and building binaries automatically, thus eliminating errors, saving time, and allowing a developer to focus on more creative work. Since GBL designer decouples schematic representation from the library implementation, it allows easier evolution of library code without breaking backward compatibility. GBL Designer also helps with design modifications, providing automatic changes that keep different parts of the design and imported modules in sync, making the necessary changes to the generated C++ code.

All that turns a tedious error-prone work of direct coding for the simulation library into an enjoyable experience working in GBL Designer.

Starting a new project

GBL Designer on start creates a new empty project and names it project1 . You can create a new project at any time by pressing the New button or selecting File/Start New Project command from the menu.

When a new project is created, the Project Manager  shows a design tree with only one subnode called project1 . You can change name, location, and other project properties by selecting the project node and modifying values in Properties Window .

Project Manager

Project Manager presents a hierarchical view of the design. It shows expandable tree nodes, corresponding to the modules, ports, wires, functions, instances, texts. Project Manager is synchronized with the Property Manger to allow editing properties of the selected node. It is also synchronized with Module View , so selecting an element in the Module View automatically selects the same element in the Project Manager tree.

There are five buttons on the Project Manager caption bar:

  • New -- to create a new empty module. Module is given name module with a unique number. The name and other module properties can be changed in the Properties Window .
  • Open -- open module from a file. If you open a module, which was previously removed from the same project, the module file will be overwritten when the project is saved. When you open a module from another project, from another directory, a new copy of the module will be created when the project is saved.
  • Delete -- removes selected module from the project. This command doesn't delete any of the module files, so deleted module can be added back to the project by using Open command.
  • Up and Down -- two arrow buttons allow changing the order of modules listed in the project.

Properties Window

Properties Window shows a list of properties of the selected node in the Project Manager . Some properties are read-only, other can be modified.

GBL Designer Properties :

  • Default Working Directory - this is the directory where GBL Designer initially creates a new project.
  • Compiler  - a selection box that lists supported compilers.
  • Help File - selection box local/web.

Project Properties:

  • Name - project name. The project file name will be "name.gbl"
  • Project Directory - this is the directory where GBL Designer saves all files related to the project
  • Number of Modules - read-only property
  • Imported Packages - list of packages used in the project. Click on ellipsis button to add or delete the packages.

Module Properties:

  • Name - module name.
  • - if module is a template, this property contains template parameters. It should be empty if module is not a template.
  • Page Height - number of pages module view window displays in vertical direction.
  • Page Width - number of pages module view window displays in horizontal direction.
  • Port Access - public/protected/private access to module ports in module class definition.
  • Inline Function - set this property to yes if you want event handlers, fibers and threads to be inlined in the module body. Most often it is used when module is a template.
  • Test - specifies whether this module is a testbench. Test modules are built into executable files, they are not included in the compiled library.
  • Trace File - used for test modules only. Specify the name of the trace file.
  • Log File - used for test modules only. Specify the name of the log file.
  • Additional Includes - you can add a list of files to be included in the header.

Port Properties:

  • Name - port name.
  • Data Type - port data type.
  • Default Value - you can assign a default value to the port. It's only useful when a port is not connected, otherwise it will receive the intial value of the signal it is conected to.
  • Event Type - output ports can additionally specify the event type of the signal it's allowed to connect to. For example, port may define fan-out by using EventN<n> event type.
  • Signal Condition - output ports can additionally specify the condition that causes event to fire. If no condition specified, port uses EventHandler::NotEqual predicate to signal changes.
  • Postion - port position in the Module View window.

Wire Properties:

  • Name - wire name.
  • Type - connector, signal, const signal, event, custom.
  • Data Type - signal data type.
  • Initial Value - signal initial value.
  • Signal Condition - signal condition that causes event to fire. If no condition specified, signal uses EventHandler::NotEqual predicate to signal changes.
  • Position - (x,y) position of the origin in the Module View window.

Function Properties:

  • Name - function name.
  • Action Type - handler/log/trace, applicable only to event handlers.
  • Triggers - list of triggers, applicable only to event handlers.
  • Function Type - fiber/thread.
  • Position - (x,y) position of the origin in the Module View window.

Text Properties:

  • Text Type - annotation/comment/preamble/private/public/protected.
  • Position - (x,y) position of the origin in the Module View window.
  • Strings - click on ellipsis button to edit text strings.

Instance Properties:

  • Name - instance name. When a new instance is added, it is named i followed by a number.
  • Module Name - name of the module, which was instantiated. This property is read-only.
  • Port Connections - a list of port-to-signal connections.
  • Position - (x,y) position of the origin in the Module View window.

Module View

Module View window displays the graphical view of the module. On the top bar, it contains buttons that allow adding basic elements to the module: ports, connections, functions, text, module instances . Select the element and click anywhere in the window to place it. By holding Ctrl key, you can add multiple instances of the selected element. Entering connections follows somewhat different procedure. Because it's likely a connection would have more than two points, each mouse click adds a connection vertex allowing to continue defining that connection. By holding down a Ctrl key when clicking left mouse button, a connection will be closed. Alternatevely by clicking on the right mouse button, unfinished segment is deleted and connection is closed. Pressing ESC key during any operation cancells it.

Click and drag left mouse button to select the elements. If you hold down Ctrl button, the new selection is added to the existing one. Otherwise the existing selection is de-selected. Selection can be moved, deleted, or duplicated. To move selection, click and drag it with the left mouse button. To delete or duplicate selection use the corresponding buttons on the toolbar. The Properties Window always displays the first element of the selection.

You can change the design size of the module canvas by changing Page Height and Page Width properties of the corresponding module.

Code Generation

Open Header and Source tabs to see code that GBL Designer generated for the current design. You can modify code inside the GBL User Code region:

#pragma region GBL User Code
#pragma endregion

This region will not be modified by the Designer. Any changes outside this portion of code will be discarded by the Designer.

Inline Functions property (see Module Porperties ) determines whether the code for event handlers , fibers , and threads goes to the header file or to the source file. Since most compilers at this time do not support exported templates, you will need to specify  Inline Functions = yes for template modules. If you decide to explicitly specialize template modules you can select either inline or outline definitions.

Building Project

Click Open Modules in IDE Build Targets Run Test Modules will run the test modules that were successfully compiled.

GBL Simulation Library

GBL Simulation Library provides a number of classes to simplify event-driven designs. It provides virtual and real-time schedulers, events, signals, ports, fibers, threads, modules and other classes implementing all essential parts of such designs. All facilities in GBL Simulation Library are declared in GBL namespace.

The GBL Simulation Library is automatically linked to the designs created in GBL Designer. If you create designs manually, you need to include gbl.h header file and link to the approprate version of gbl.lib. When building using dynamically linked library gbl.dll must be available during run time.

GBL Time Models

There two time classes provided in sim_time.h file. One is generic class template:
template<class T> class TimeT;
And the other is real-time class:
class RealTime : public TimeT<double>
The time span is described by nested class Diff.

Header file defaults.h defines default simulator virtual time typedef:

typedef TimeT<unsigned> SimTime;
and virtual and real-time constants:
const SimTime::Diff     Tick(1);

const RealTime::Diff    Day(1.);
const RealTime::Diff    Hour(1/24.);
const RealTime::Diff    Min(1/24./60.);
const RealTime::Diff    Sec(1/24./60./60.);
const RealTime::Diff    Msec(1/24./60./60./1000.);
	

If you change the default time classes used by GBL simulator, you will need to recompile the simulation library and the projects it is linked to.

GBL Data Types

GBL Simulation library provides additional data types for system modeling.

enum LogicValue { logic_0 = 0, logic_1, logic_x, logic_z };
class Bit;
class Logic;
template<size_t N> class BitSet;
template<size_t N> class LogicSet;

Types Bit and Logic represent 2-bit (logic_0, logic_1) and 4-bit (logic_0, logic_1, logic_x, logic_z) logic. These types have constructors and assignment operators from char and bool.

Templates BitSet<N> and LogicSet<N> represent arbitrary sized vectors of Bit and Logic values. They have constructors and assignment operators from integral types , from std::string and const char*, and from other bit types. Operators and functions that define interface for these types are listed below:

  • Bitwise operators &=, |=, ^=, &, |, ^

  • Shift operators <<=, >>=, <<, >>

  • value based comparison operators ==, !=, >, >=, <, <=

  • operator ~() and Negate() member function perform NOT logic function on each element.

  • operator [](size_t pos) subscript const and non-const operators. Non-const operator returns r-value proxy.

  • operator ()(size_t bit1, size_t bit2) performs range selection, returning r-value proxy. Normal range has bit1 <= bit2 . If bit1 > bit2 then the order of bits in the range is reversed.

  • operator size_t () converts BitSet<N> object to size_t for types N < sizeof(size_t) * CHAR_BIT

  • Set(ElementType val = logic_1) member function sets all bits to val (default logic_1)

  • Set(size_t pos, ElementType val) member function sets bit in pos position to val.

  • Reset() member function sets all bits to logic_0

  • Reset(size_t pos) member function sets bit at pos position to logic_0

  • ElementType Get(size_t pos) member function returns element at pos position.

  • AndReduce(), OrReduce(), XorReduce() member functions perform corresponding reduction operations.

  • size_t Count() member function returns number of elements equal to logic_1

  • size_t Size() member function returns the size of the set.

  • Swap(other_set) member function swaps *this and the other_set

  • unsigned arithmetic operators +=,-=,*=,/=,%=,+,-,*,/,%,++,-- are defined for BitSet types, which are not longer than size_t . Intermediate results are promoted to longer BitSet type in the expression and truncated on assignment.

  • Concat(other_bit_set) member function perfoms concatenation of two bitsets returning r-value proxy.

  • Reverse(bit_set) non-member function reverses order of bits.

  • ToString(bit_set) non-member function returns string representation using characters '0','1','X','Z'.

  • ToUlong() member function throws std::overflow_error if any bit in the bit sequence has a bit value that cannot be represented as a value of type unsigned long. Otherwise, it returns the sum of the bit values in the bit sequence.

  • Concat(set_type1, set_type2) non-member function performs concatenation and returns r-value proxy

  • Rotate(set_type&, int bits) non-member function rotates set left if bits is a positive number, or right - otherwise.

  • non-member stream operators <<(std::ostream&, data_type) and >>(std::istream&, data_type).

GBL Globals

Global functions in GBL namespace:
Simulator& GetSimulator()
-- function returns reference to the default simulator.
SimTime  Module::GetSimTime();
-- function returns current simulation time.
RealTime Module::GetRealTime()
-- function returns current real time.
Event&   eReset()

-- a reset event happens after calling Reset()function and at the simulator start.

Event&   eStop()

-- a stop event happens after calling Stop()function which is used to exit event loop.

Event&   eIdle()
-- an idle event, which occures when the event queues are empty.
Event&   eTick()
-- a tick event, which happens when the virtual time advances.
Event&   eRtTick()
-- a real time tick event, which happens when the real time advances.
void     Reset()
-- cleans the event queues and issue reset event.Fibers and Threads are not affected.
void     Run(SimTime::Diff t, RealTime::Diff tr)
-- run simulator until specified simulation or real time elapses.
void     Run(SimTime::Diff t)
-- runs simulator t ticks only. Real-time scheduler is not run.

Run(GBL::Tick) performs a single simulation step. Run()without parameters runs simulator until event queue is empty.

void*     RunAsync()

-- starts simulator run in a separate thread and exits immediately. Simulator can be stopped by calling Simulator::Stop() function. Returned value is Windows thread handle. It can be used in Windows API accepting thread handles. For example, you can use WaitForSingleObject to wait for thread completion.

void     Close()
-- function terminates all GBL Threads. Since threads cannot becontrolled by the simulator, they may continue to modify event queues after the simulator object has been already destroyed causing unhandled exceptions on program exit. Calling Close() before the termination of the program terminates all threads while the simulator is still alive. This function call is only necessary if the application creates GBL threads.

Global functions in defaults.h:

std::ostream&   Log()
-- returns ostream object that logs simulator messages.
void            SetLog(std::ostream&)
-- directs simulator messages to the specified ostream.
void            SetLog(const std::string& file_name)
-- directs simulator messages to the specified file.
void            LogException()
-- default exception dispatcher, can be used in custom OnException() function.

GBL Modules

GBL Module class is the base class for all user-defined modules.

class Module : public EventHandler
{
public:
    // module constructor accepts a string argument as instance name

    Module(const std::string& name = "");

    // GetName returns module instance name

    const std::string& GetName() const;

    // MakeThread creates preemptive thread out of member function

    template<class M>
    void MakeThread(void (M::*f)());

    // MakeFiber creates a non-preemptive fiber out of member function

    template<class M>
    void MakeFiber(void (M::*f)());

    // Enqueue schedules function to be executed in a thread pool
    // function is guaranteed to be executed in the current delta cycle
    // execution of enqueued functions is neither ordered nor sequenced

    template<class M>
    void Enqueue(void (M::*f)());

protected:
    virtual void OnException() const;
};
    
Override OnException() to provide specific exception handling in user-defined modules.

MakeFiber and MakeThread functions create threads and fibers from specified member functions. For example, the following simplified code prints message with 1 sec interval:

class Example1 : public GBL::Module
{
public:
	Example1() : GBL::Module("example1")
	{
		MakeThread(&Example1::Print);
	}
private:
	void Print()
	{
		GBL::Wait(GBL::Sec);
		std::cout << "Print() called ...\n";
	}
};
	

GBL Module class inherits from EventHandler class, which provides facilities for event scheduling. In order to make an event handler out of member function use Handler() or AsyncHandler() functions.

GBL Events

Event classes contain ActionFunction objects, which are wrapped pointers to the event handlers.
	class EventHandler;
	typedef void (EventHandler::*Action)();
    	class ActionFunction;
Events provide facilities for binding and executing of the event handlers, according to the event type. Event classes in GBL are derived from class EventBase, which specifies the following virtual interface:
void Bind(const ActionFunction&)
-- binding of event handlers. Event handlers are bound permanently, unless Unbind()function removes binding.
void BindTemp(const ActionFunction&)

-- binding of temporary event handlers. Temporary binding is removed after event is raised.

void BindAsync(const ActionFunction&)

-- binding of asynchrous event handlers.

void Execute()
-- executing of the bound event handlers.
size_t ActionCount() const
-- number of event handlers event object holds.
size_t TempCount() const

-- number of temporary event handlers event object holds.

size_t AsyncCount() const

-- number of asynchronous event handlers event object holds. 

ActionFunction Unbind()
-- unbind and return the last bound event handler.
ActionFunction UnbindTemp()

-- unbind and return the last bound temporary event handler.

ActionFunction UnbindAsync()

-- unbind and return the last bound asynchronous event handler.

The following event classes are defined in GBL:

  • class EmptyEvent -- ignores all bindings
  • template<unsigned N = 0> class EventN-- can bind upto N event handlers. Temporary and asynchronous event handlers cannot be bound to this event type.
    If N is 0 (default value) then event can bind unlimited number of event handlers.
    EventN<1> and its typedef SimpleEvent is the fastest and leanest event type which can bind only one event handler.
  • class TempEvent -- a temporary event, which is destroyed after execution.
  • class Event-- the most general event type, that has unlimited binding of event handlers of all types.

GBL Signals

Signals just like events allow binding of the event handlers. Additionally they hold an object of specified type by value. For example, Signal<int> holds an int variable, Signal<vector<int>> holds a vector. Since signals implement value semantics, one must pay attention to avoid costly copying of the large objects.
Signals execute bound event handlers when the value of the variable that signal holds changes, or when an optional Condition is satisfied, i.e. Condition()(new_value, old_value) evaluates to true.
Initial value for the signals can be specified in their constructors. If no initial value specified the default initialization takes place.
Signals provide conversion operator T () to the signal data type, as well as Read() function for cases when implicit conversion cannot be performed.
Assignment operator for writable signals is resolved to Write() function. Writable delayed signals accept optional time parameter for delayed writing, as well as operator ()(TimeT delay), e.g. the expressions (1) and (2) below perform the same delayed writing to the signal, while expressions (3) and (4) perform zero-delayed writing:
	GBL::Signal<bool> signal;
	signal.Write(true, 5 * GBL::Tick);  // (1)
	signal(5 * GBL::Tick) = true;       // (2)
	signal.Write(true);                 // (3)
	signal = true;                      // (4)
	

Signal Types

The signal types defined in GBL are listed below.
template<class D> class ConstSignal;
-- constant signal, it never changes its value after initialization and therefore never raises events. Signal data type is  D.
template<class D, class E = Event> class ReadSignal;
-- a signal, which can be read, but cannot be written. In addition to data type D you can specify event type E
template<class D, class E = Event, class Condition = EventHandler::NotEqual> class DirectSignal;
-- DirectSignal is never scheduled, it triggers event handlers bound to it immediately. In addition to data and event types, you can specify a condition type, which triggers event handlers bound to the signal.
template<class D, class E = Event, class Condition = EventHandler::NotEqual> class Signal;
-- a general signal type, which can be read and written. It schedules bound handlers with a delay (zero-delay by default.)

Signal Condition

DirectSignal and Signal types accept optional Condition type, which allows to specify the trigger condition. Event handlers are invoked when Condition(new_value, old_value) evaluates to true. Default condition type is EventHandler::NotEqual, which means that every time a new value is not equal to the old value, the event handlers are invoked.

GBL library provides several predifined common conditions:

Always<true> and Always<false> -- always evaluate to true and false correspondingly;
PosEdge and NegEdge -- trigger event handlers on positive and negative edge correspondingly using operator< on signal data type;
Equal and NotEqual -- trigger event handlers using operators == and != correspondingly.
PosEdgePtr, NegEdgePtr, EqualPtr, NotEqualPtr -- same as above, but dereference signal values to work on data types which are iterators (pointers).

Using subscripts

Data types that implement subscript operator can be efficiently accessed through signal's operator[]. Signal class returns proxies which provide access to the data by index and perform delayed writing.

Execute Functions

Signal behave like value types. Read() function returns a const reference to the object and Write() functions assign a new object. So, for example, to modify an object by calling its member function would involve reading to a temporary object, modifying and writing it to a signal. This can be problematic when a copy expensive object is hold by a signal. In order to avoid expensive copying, Signal class provides Execute functions, which accept a functional object and performed its delayed execution:
template<class F, class T> 
void Execute(const F& fun, T delay); // execute with specified delay

template<class F> 
void Execute(const F& fun); // execute with 0 delay
        
A function object must define bool operator(D&), which will be called by the scheduler. Return value of operator(D&) defines whether the execution of this function object should trigger bound event handlers or not. In C++0x it can be a lambda function. For example, using VC++ 2010, we can create a module that counts characters in every word in input text and keeps a map in its output port:
        class count_module : public GBL::Module 
        { 
        private: 
            GBL::InPort< const char* > text; 
            GBL::OutPort< std::map<unsigned, std::string> stat; 
            GBL::OutPort< bool, GBL::Event, std::greater<bool> > ready; 
            void onText(); 
            void PostConstructor(); 
        public: 
            GBL_CONNECTOR3(text, stat, ready) 
            count_module(const std::string& gbl_module_name = ""); 
            #pragma region GBL User Code 
            std::map<std::string, unsigned> weights; 
            #pragma endregion 
        };

        // constructor
        count_module::count_module(const std::string& gbl_module_name) 
            : GBL::Module(gbl_module_name), ready(false)
        {
	        Handler(&count_module::onText) << text;
	        PostConstructor();
        }
        
        // onText event handler
        void count_module::onText() 
        { 
            std::stringstream ss(*text); 
            std::string str; 
            while(ss >> str) 
            { 
                if(weights.count(str)) 
                    weights[str] += str.length(); 
                else 
                    weights[str] = str.length(); 
            }

            // using lambda function for delayed execution

            stat.Execute([this](std::map unsigned, std::string& m) -> bool 
            { 
                for(auto i = weights.begin(); i != weights.end(); ++i) 
                { 
                    m[i->second] = i->first; 
                }
                return false; // don't signal 
            } );
            ready = true; 
            ready = false;
        } 
        

GBL Ports

Port classes implement generalized signal pointer types and provide means for delayed bindings of the signals. They provide the same Read, Write, and Execute facilities as the underlying signals. From a design point of view, ports represent public module interface, through which the signal flow occurs. Thus ports efficiently decouple module design from its environment.

The following port types are defined in GBL:

class UnconnectedPort;
-- represents unconnect port. For unconnected ports constants N_C or _ of UnconnectedPort type can be used.
template<class S> class Port;
-- a general type port, that can be bound to any signal type S
template<class D> class InPort;
-- input port (can be read, but not written) accepting signal with data type D
template<class D, class E = Event, class C = EventHandler::NotEqual> class OutPort;
-- output port, accepting Signal<D,E,C>.

Ports can be bound to other ports or signals. GBL defines macros GBL_CONNECTORN(p1, p2, ... , pN) for port binding in the module. For example, an inverter   with one input and one output port would have GBL_CONNECTOR2(in, out) statement. To connect external signals to an inverter  instance, they must be listed in the same order (positional binding) using operator ()(), e.g.

GBL::Signal<bool> sig_in;
GBL::Signal<bool> sig_out;
inverter inv;
inv(sig_in, sig_out);

Alternatively, if ports are made public (not recommended), a binding can be done by name (nominal binding):

GBL::Signal<bool> sig_in;
GBL::Signal<bool> sig_out;
inverter inv;
inv.in(sig_in);
inv.out(sig_out);

GBL Event Handlers

Event handlers are member functions of modules derived from Module (or EventHandler ) class, that have the following signatures:
     void (user_defined_module_class::*)();
    void (user_defined_module_class::*)()const;
	 
You can also use lambda functions and binders in event handlers if target comiler supports these C++0x features (VC++ 2010).
Use Handler() function to form an event handler. To bind an event handler to the events, signals, or ports use operator << (). For example, the following code creates event handler from OnSomeEvent() function and binds it to some_event event.
class MyModule : public GBL::Module
{
    void        OnSomeEvent() { /* do synchronous processing */ }
    void        OnSomeEventAsync() { /* do asynchronous processing */ }
    void        OnOtherEvent() { Enqueue(&MyModule::OnOtherEventAsync); }
    void        OnOtherEventAsync() { /* async executed */ }
    
    GBL::Event  some_event;
    GBL::Event  other_event;
    GBL::Signal<bool> some_signal;

public:
    MyModule()
    {
        // create a handler from OnSomeEvent and make it sensitive to some_event event
        Handler(&MyModule::OnSomeEvent) << some_event;

        // create an asynchronous handler from OnSomeEvent and make it sensitive to some_event event
        AsyncHandler(&MyModule::OnSomeEventAsync) << some_event;

        // create a handler from OnOtherEvent and make it sensitive to other_event event
        Handler(&MyModule::OnOtherEvent) << other_event;

        // create a handler from lambda function (C++0x)
        Handler([this](){ cout << "\nsome_signal changed at " << GetSimTime(); }) << some_signal;
    }
};

When some_event is raised, OnSomeEvent() function is executed in the scheduler thread and OnSomeEvenAsync function is scheduled to be executed asynchronously, both are guaranteed to be executed in the current delta cycle. Similarly, when other_event is raised, OnOtherEvent function is executed and it schedules OnOtherEventAsync function to be executed asyncronously in the current delta cycle.

The last line demonstrates the use of lambda function as a handler for some_event. If MyModule was created in GBL Designer, additional handlers can be specified in PostConstructor() function.

Function execution in the current delta cycle is neither ordered nor sequenced.

 

GBL Fibers and Threads

GBL provides two classes for building asynchronous designs: class Fiber and class Thread.
The main difference between Thread and Fiber classes is that the first is preemptive and the second is not. All fibers run within the main thread, they yield control only when encounter Wait() statement. Therefore fibers are free from thread problems, they don't need locks, don't cause collisions, deadlocks, etc. Fibers are also lighter than threads and provide for faster switching. On the other hand, fibers unlike threads cannot take advantage of multiple processors, and since they are non-preemptive, incorrect design may cause event scheduler starvation.

Unlike asynchronous event handlers, which are executed in thread pool, Thread function creates an physical thread and runs in infinite loop. So the number of Thread objects that can be created is significantly smaller than the number of asynchronous event handlers. You can also use Wait()functions with Threads , attempt to use them in asynchronous event handlers would block the physical threads in the thread pool, which would affect execution of all asynchronous event handlers.

Module class provide two functions, MakeFiber() and MakeThread(), which create fiber and thread out of specified functions. The signatures of underlying functions for threads and fibers are the same as for event handlers.

Example:

class MyModule: public GBL::Module
{
   GBL::InPort<bool> in;

   void fiber() { Wait(in); std::cout << "\nInput changed: " << in; }
   void thread(){ Wait(GBL::Hour); std::cout << "\nOne hour elapsed"; }

public:
   MyModule()
   {
      MakeFiber(&MyModule::fiber);
      MakeThread(&MyModule::thread);

      // using lambda function to print simulation time every second
      MakeThread([this](){ Wait(GBL::Sec); std::cout << "\nSimTime: " << GetSimTime(); });
   }
}

Since fibers and threads always run in an infinite loop, they must have Wait() statements to suspend execution. There are several Wait() overloads:

template<class E> void Wait(E& e)
-- waiting on event/signal/port
template<class S, class BinaryPredicate>
void Wait(S& s, typename RefTraits<typename S::DataType>::cref value,
BinaryPredicate p)
-- waiting on signal/port until p(s, value) turns to true
template<class S>
void Wait(S& s, typename RefTraits<typename S::DataType>::cref value)
-- waiting until signal/port value becomes equal to value
void Wait()
-- waiting until the current cycle of zero-queue
void Wait(SimTime::Diff t)
-- waiting for t simulator ticks
void Wait(RealTime::Diff t)

-- waiting in real time

Threads might also need to lock shared data to prevent collisions. GBL provides the following classes (in lock.h ) to lock shared resources:

class Mutex-- a mutex, based on CRITICAL_SECTION

class Lock -- scoped lock, e.g.

Mutex m;
{
   Lock l(m);
   locked_scope;
}

class Unlock-- scoped un-lock to unlock a portion of locked scope, e.g.

Mutex m;
Lock l(m);
locked_scope;
{
   Unlock u(l);
   unlocked_scope;
}
locked_scope_continued;

Examples

Examples shown in this section are created for demonstration purposes only.

Download project file: examples.gbl

Download invertor module file: inverter.gblmod

Download And2 module file: and2.gblmod

Download Selector2 module file: selector2.gblmod

Download Handshake module file: handshake.gblmod

Download test module file: test.gblmod

Inverter

Inverter module performs boolean function out = !in.

In GBL Designer create one InPort<bool>, called in, and one OutPort<bool>, called out. Add event handler, called OnInput, and add one line out = !in in the function body. Select in port and eReset() event as event handler triggers.

GBL Designer generated code is shown below.

//--------------------------------------------------------------------------------
// GBL Designer generated module
// Module: inverter
// 2006-2010(c) GB RESEARCH, LLC. ALL RIGHTS RESERVED
//--------------------------------------------------------------------------------
#ifndef inverter_H9727
#define inverter_H9727
#include "gbl.h"
namespace examples {
class inverter : public GBL::Module
{
    GBL::InPort< bool > in;
    GBL::OutPort< bool > out;
public:
    GBL_CONNECTOR2(in, out)
    inverter(const std::string& name = "") : GBL::Module(name)
    {
        Handler(&inverter::OnInput) << GBL::eReset() << in;
    }
#pragma region GBL User Code
private:
	//----------------------------------------
	void OnInput()
	{
		out = !in;
	}
#pragma endregion
};
} // namespace examples
#endif
	

And2

And2 module performs boolean function out = in1 && in2.

In GBL Designer create two InPort<bool>in1 and in2, OutPort<bool>out. Add event handler, called OnInput. For demostration purposes, we make output of the And2 module to have 2 ticks delay. Add this line in the event handler body: out.Write(in1 && in2, GBL::Tick * 2) . If selected C++ compiler toolset supports ADL, you can change this line of code to look more natural: out.Write(in1 && in2, 2 * GBL::Tick)

Select in1, in2 ports and eReset() event as event handler triggers.

GBL Designer generated code is shown below.

//--------------------------------------------------------------------------------
// GBL Designer generated module
// Module: And2
// 2006-2010(c) GB RESEARCH, LLC. ALL RIGHTS RESERVED
//--------------------------------------------------------------------------------
#ifndef And2_H9728
#define And2_H9728
#include "gbl.h"
namespace examples {
class And2 : public GBL::Module
{
    GBL::InPort< bool > in1;
    GBL::InPort< bool > in2;
    GBL::OutPort< bool > out;
public:
    GBL_CONNECTOR3(in1, in2, out)
    And2(const std::string& name = "") : GBL::Module(name)
    {
        Handler(&And2::OnInput) << GBL::eReset() << in1 << in2;
    }
#pragma region GBL User Code
private:
	//----------------------------------------
    void OnInput()
    {
        out.Write(in1 && in2, GBL::Tick * 2);
    }

#pragma endregion
};
} // namespace examples
#endif
			  

Selector2

Selector2 module performs selection operation: out = select ? in1 : in2. We make it generic in order to work with signals of any data types.

In GBL Designer create two InPort<T> ports in1 and in2, one   port select and one OutPort<T>  port out.

Add event handler OnSelect. Add this line in the event handler body: out = select ? in1 : in2;.

Select select port and eReset() event as event handler triggers.

GBL Designer generated code is shown below.

//--------------------------------------------------------------------------------
// GBL Designer generated module
// Module: Selector2
// 2006-2010(c) GB RESEARCH, LLC. ALL RIGHTS RESERVED
//--------------------------------------------------------------------------------
#ifndef Selector2_H9728
#define Selector2_H9728
#include "gbl.h"
namespace examples {
export template< class T >
class Selector2 : public GBL::Module
{
    GBL::InPort< T > in1;
    GBL::InPort< T > in2;
    GBL::InPort< bool > select;
    GBL::OutPort< T > out;
public:
    GBL_CONNECTOR4(in1, in2, select, out)
    Selector2(const std::string& name = "") : GBL::Module(name)
    {
        Handler(&Selector2::OnSelect) << GBL::eReset() << select;
    }
#pragma region GBL User Code
private:
    //----------------------------------------
    void OnSelect()
    {
        out = select ? in1 : in2;
    }

#pragma endregion
};
} // namespace examples
#endif

Handshake

Handshake module is an example of using fibers. In this example there are two fibers writer and reader. Module contains two signals w_en, which enables writing and r_en, which enables reading. reader and fibers wait on the corresponding signals to read from or write to the port datawriter writes random numbers.

GBL Designer generated code is shown below.

//--------------------------------------------------------------------------------
// GBL Designer generated module
// Module: HandShake
// 2006-2010(c) GB RESEARCH, LLC. ALL RIGHTS RESERVED
//--------------------------------------------------------------------------------
#ifndef HandShake_H9727
#define HandShake_H9727
#include "gbl.h"
#include <stdlib.h>
namespace examples {
class HandShake : public GBL::Module
{
    GBL::OutPort< int > data;
    GBL::Signal< bool > w_en;
    GBL::Signal< bool > r_en;
public:
    GBL_CONNECTOR1(data)
    HandShake(const std::string& name = "") : GBL::Module(name)
    {
        Handler(&HandShake::OnReset) << GBL::eReset();
        MakeFiber(&HandShake::writer);
        MakeFiber(&HandShake::reader);
    }
//<GBL_user>
private:
    //----------------------------------------
    void writer()
    {
        if(!w_en)
            GBL::Wait(w_en, true);
        int r = rand();
        data = r;
        std::cout << "\nwriter: " << r;
        w_en = false;
        r_en.Write(true, GBL::Tick);
    }
    //----------------------------------------
    void reader()
    {
        if(!r_en)
            GBL::Wait(r_en, true);
        std::cout << "\nreader: " << data.Read();
        r_en = false;
        w_en.Write(true, GBL::Tick);
    }
    //----------------------------------------
    void OnReset()
    {
        w_en = true;
    }
//</GBL_user>
};
} // namespace examples
#endif
             

Test

test shows how to create modules out of existing ones without writing any code. We also change the module type to test examples.lib library, will build this module into an executable, called test.exe .

Add instances of all previously created modules and add ClockGen from BasicModules package. Using wire tool, connect previously created modules as it is shown in the picture. Change period, const_10, and const_1 signal types to const signal and assign initial values 10 , 10 , and 1 OnReset and trace event handlers. Change type of the trace event handler to trace . GBL Designer automatically writes corresponding code to trace signals, which are listed as triggers. Set Trace File property (see Module Properties ) to the trace file name.

GBL Designer generated code is shown below.

test.h file
//--------------------------------------------------------------------------------
// GBL Designer generated module
// Module: test
// 2006-2010(c) GB RESEARCH, LLC. ALL RIGHTS RESERVED
//--------------------------------------------------------------------------------
#ifndef test_H9727
#define test_H9727
#include "gbl.h"
#include "examples.h"
#include "BasicModules.h"
namespace examples {
class test : public GBL::Module
{
    BasicModules::ClockGen i1;
    inverter i2;
    And2 i3;
    Selector2< int > i4;
    HandShake i5;
    And2 i6;
    GBL::Signal< bool > clk;
    GBL::Signal< bool > inv_out;
    GBL::Signal< bool > w1;
    GBL::Signal< bool > clk_;
    GBL::Signal< int > selector_out;
    GBL::ConstSignal< unsigned > period;
    GBL::Signal< bool > enable;
    GBL::ConstSignal< int > const_10;
    GBL::ConstSignal< int > const_1;
    GBL::Signal< bool > and2_out;
    GBL::Signal< int > hs_out;
    void trace() { GBL::AddTrace(GetName() + ".and2_out", and2_out);
        GBL::AddTrace(GetName() + ".clk", clk);
        GBL::AddTrace(GetName() + ".clk_", clk_);
        GBL::AddTrace(GetName() + ".hs_out", hs_out);
        GBL::AddTrace(GetName() + ".inv_out", inv_out);
        GBL::AddTrace(GetName() + ".selector_out", selector_out);
        GBL::AddTrace(GetName() + ".w1", w1); }
public:
    test(const std::string& name = "") : GBL::Module(name)
    , i1(name + ".i1"), i2(name + ".i2"), i3(name + ".i3"), i4(name + ".i4")
    , i5(name + ".i5"), i6(name + ".i6")
    , period(10), const_10(10), const_1(1)
    {
        i1 ( period, enable, clk, clk_ );
        i2 ( clk, inv_out );
        i3 ( w1, clk, and2_out );
        i4 ( const_10, const_1, clk_, selector_out );
        i5 ( hs_out );
        i6 ( inv_out, inv_out, w1 );
        Handler(&test::OnReset) << GBL::eReset();
        Handler(&test::trace) << and2_out << clk << clk_
		<< hs_out << inv_out << selector_out << w1;
    }
//<GBL_user>
private:
    //----------------------------------------
    void OnReset()
    {
        enable = true;
    }
    //----------------------------------------
    void Run()
    {
        GBL::Run(GBL::Tick * 1000, GBL::Sec * 10);
    }
    //</GBL_user>
};
} // namespace examples
#endif
test.cpp file
//--------------------------------------------------------------------------------
// GBL Designer generated module implementation
// Module: test
// 2006-2010(c) GB RESEARCH, LLC. ALL RIGHTS RESERVED
//--------------------------------------------------------------------------------
#include "test.h"
using namespace examples;
//----------------------------------------main
int main(int argc, char* argv[])
{
    GBL::SetTraceFile("C:\\GBL\\Examples\\examples.trace");
    examples::test("test").Run();
    GBL::Close();
}
 
//<GBL_user>
//</GBL_user>
After building and running test.exe , click on Tracer icon to open saved traces from this example. They should look like in the picture below.