ReChannel does not alter the simulation kernel of SystemC in any way, as it was designed under the premise to be compatible with any SystemC implementation that is compliant to the current language standard (IEEE 1666-2005). The class library has been tested with the standard compliant OSCI SystemC implementation (version 2.2) and the following C++ compilers: Microsoft Visual C++ 2003 .NET (SP1), Microsoft Visual C++ 2005, GNU GCC 3.4, GNU GCC 4.1 and Intel C++ Compiler 8.0.
The dynamic side is represented by a group of modules, the so-called reconfigurable modules. Among them only one single module can be loaded at a time, all others have to be unloaded. In simulation semantics a loaded module is regarded as being existent, whereas an unloaded module is regarded as being nonexistent. A loaded module can be in one of two states: active or inactive. Only if a module is currently active, it is allowed to communicate with channels and modules on the static side. The inactive module is regarded as existent but not yet ready for regular operation and is hence still blocked from the outside world. Reconfiguration is defined as the operation of replacing two reconfigurable modules. In simulation semantics this means unloading the currently loaded module and subsequently loading one of the unloaded modules of the group. The dynamic side is said to be undefined if no module is currently loaded. A reconfiguration control is a component that initiates and executes the reconfiguration for a set of associated modules.
A switch is a helper component that serves as a connector of the dynamic and the static side. The basic principle of a switch is similar to that of a multiplexer (or demultiplexer, respectively). The switch's task is to control communication between the static side and an arbitrary number of reconfigurable modules on the dynamic side. Thereby, it ensures that only the currently active module is allowed to communicate. Unloaded modules are blocked from any external events and access attempts. Thus, a switch allows isolation of unloaded and inactive modules from the rest of the design. As a consequence of the way switches are employed in ReChannel, a dedicated group of switches technically forms the boundary of a particular reconfigurable design part.
ReChannel distinguishes between two classes of switches: portal and exportal. A portal is used to connect a channel (or port) of the static side with a port of the dynamic side. In contrast, an exportal is used to access an exported channel (sc_export<IF>) on the dynamic side (note: an exportal may optionally be bound to a static port).
To overcome SystemC's design rule checks, every reconfigurable module's port has to be bound to a so-called accessor object. The binding of accessors to ports is automatically performed by a switch and is fully transparent to the designer. An accessor is an auxiliary object. It implements a channel interface and aids the portal in controlling the communication which passes through the port.
If custom channels are to be used with ReChannel, a designer has to define a specialised portal (and/or exportal), as well as an accessor implementation. (Further details on this can be found in section Custom Channels.)
#include <ReChannel.h>
Theoretically, any object can be used with ReChannel's reconfiguration algorithm. The only technical precondition is that it has to be derived from class rc_reconfigurable. This class contains all the behaviour and state information that is necessary for the simulation of reconfiguration in ReChannel to work.
In contrast to that, for modules it is recommended to automate the derivation of rc_reconfigurable by simply using the class template rc_reconfigurable_module. Using rc_reconfigurable_module for the derivation is more convenient and, furthermore, also is a requirement for some more advanced ReChannel features to work.
First variant:
For example, let M be a static module. All that has to be done is to state M as a template parameter for class rc_reconfigurable_module. An object of type rc_reconfigurable_module<M> actually represents a reconfigurable and ready-to-use version of M. Moreover, by using the "typedef" keyword, this class composition can be simply given a new name, e.g.: typedef rc_reconfigurable_module<M> M_rc;
Second variant:
In cases where additional code and/or properties have to be added to the reconfigurable version of a module, the module may be directly derived from rc_reconfigurable_module, as the following example illustrates: class M_rc
: public rc_reconfigurable_module<M>
{
[...] // additional members
M_rc(const sc_module_name& name_)
: rc_reconfigurable_module<M>(name_) {
[...] // additional initialisations
}
[...] // additional code and members
};
Third variant:
The macros RC_RECONFIGURABLE_MODULE_DERIVED() and RC_RECONFIGURABLE_CTOR_DERIVED() are provided for convenience to simplify the declaration for the designer. The following notation is equivalent to the declaration shown above: RC_RECONFIGURABLE_MODULE_DERIVED(M_rc, M) // declaration of reconf. module
{
[...] // additional members
RC_RECONFIGURABLE_CTOR_DERIVED(M_rc, M) { // standard module constructor
[...] // additional initialisations
}
[...] // additional code and members
};
class M_rc : public rc_reconfigurable_module<> { [...] };
Additionally, the macros RC_RECONFIGURABLE_MODULE() and RC_RECONFIGURABLE_CTOR() are available to ease the definition of pure reconfigurable modules, which can be used as follows: RC_RECONFIGURABLE_MODULE(M_rc) // declares the reconfigurable module M_rc
{
[...]
RC_RECONFIGURABLE_CTOR(M_rc) { // constructor
[...]
}
[...]
};
ReChannel already comes with a set of predefined portals, one for each named SystemC port. For the declaration of a portal type, class rc_portal<PORT> is parametrised with the type of a connectable port, e.g.: rc_portal<sc_in<int> > in_portal;
rc_portal<sc_out<int> > out_portal;
Portals can be connected to a static channel/port by the method bind_static(). Alternatively, the portal's child port static_port can be used for the direct binding with a static port of type PORT.
With method bind_dynamic() a portal is connected to the port of a reconfigurable module. dynamic_port is available, too, as an alias for method bind_dynamic(). dynamic_port is in reality no port but the name of a method.
E.g., let m1 and m2 be modules, with two ports of type sc_in<int> and sc_out<int> each. On the static side there are two signal channels, in_signal and out_signal, to which the modules are to be connected to. The binding will look as follows: // connecting the static side
in_portal.bind_static(in_signal);
out_portal.bind_static(out_signal);
// connecting the dynamic side
// binding of reconfigurable module m1
in_portal.bind_dynamic(m1.in_port());
out_portal.bind_dynamic(m1.out_port();
// binding of reconfigurable module m2
in_portal.bind_dynamic(m2.in_port());
out_portal.bind_dynamic(m2.out_port(););
ReChannel also provides predefined exportals for each standard SystemC interface. For the declaration of an exportal, class rc_exportal<IF> is parametrised with the interface of the specific channel the export (see sc_export<IF>) has been bound to, e.g.: rc_exportal<sc_fifo_in_if<bool> > fifo_in_exportal;
rc_exportal<sc_signal_inout_if<sc_lv<8> > signal_exportal;
The binding of exportals is analogous to the binding of portals, with the difference that instead of ports exports are used.
The following example adds the reconfigurable modules m1, m2, m3 to the control. The '+' operator can be used to construct a rc_module_set, by which several modules can be passed to a controller's method at once. rc_control ctrl; // declaration of ctrl (e.g., in the top module)
[...]
ctrl.add(m1 + m2 + m3); // adding multiple modules at once
ctrl.add(m4); // adding module m4
ctrl.activate(m3); // initially, module m3 will be active
The delay of a reconfiguration operation can be set independently for each module by method rc_set_delay(), either inside the constructor of the module or alternatively after construction from outside. rc_set_delay()'s first parameter is a constant identifying a reconfiguration operation (RC_LOAD, RC_ACTIVATE, RC_DEACTIVATE or RC_UNLOAD) and the second parameter is the delay. The delays default value is always SC_ZERO_TIME.
In the following example the default loading delay of modules m1 and m2 is overwritten by a user specified time:
The class rc_control provides the blocking methods load(), activate(), deactivate() and unload() to control the reconfiguration of a module or a set of modules.
An example of a possible control sequence at simulation time: // Start of simulation
// (m3 is initially active)
ctrl.lock(m1 + m2 + m3); // acquire exclusive access to m1, m2 and m3
wait(2, SC_MS); // wait for a while
ctrl.unload(m3); // unload module m3
ctrl.load(m1); // load module m1
ctrl.activate(m1); // activate module m1
wait(2, SC_MS); //
ctrl.unload(m1); // unload module m2
ctrl.activate(m2) // load and activate module m2
wait(2, SC_MS); //
ctrl.deactivate(m2); // deactivate module m2
ctrl.unlock(m1 + m2 + m3); // release exclusive access
The use of lock() and unlock() is required if a control sequence has to be executed and multiple processes have concurrent access to a particular control instance. However, a single load(), activate(), etc. operation is atomic and cannot be interrupted by another process.
The control commands may result in a SystemC run-time error in cases where a reconfigurable module
(a) has not been previously added to the control,
(b) is currently in use by another thread (i.e. another control operation is in progress), or
(c) conflicts with other modules (i.e. its connected switches are currently in use by (an)other loaded module(s)).
Thus, the loading of two modules that share one or more switches at the same time is not allowed and will result in an error.
The following subsections give a short introduction to the proceedings of implementing custom portals, exportals and accessors.
With macros a specialised portal class is defined as follows: RC_PORTAL(myport_type) //defines the class rc_portal<myport_type>
{ //
RC_PORTAL_CTOR(myport_type) { // constructor
[...] // custom initialisations
} //
[...] // custom members
};
If myport_type is a class template (e.g., myport_type<T>) the macros RC_PORTAL_TEMPLATE() and RC_PORTAL_TEMPLATE_CTOR() have to be used instead. The template parameter declaration simply precedes the invocation of the declaration macro: template<class T> // defines the class
RC_PORTAL_TEMPLATE(myport_type<T>) // rc_portal<myport_type<T> >
{ //
RC_PORTAL_TEMPLATE_CTOR(myport_type<T>) { // constructor
[...] // custom initialisations
} //
[...] // custom members
};
A declaration without the help of macros may look like this: template<class T>
class rc_portal<myport_type<T> >
: public rc_abstract_portal<myport_type<T> >
{
// typedef for convenience
typedef rc_abstract_portal<myport_type<T> > base_type;
public:
// typedefs for port, interface and accessor type
typedef typename base_type::port_type port_type;
typedef typename base_type::if_type if_type;
typedef typename base_type::accessor_type accessor_type;
// note:
// - if_type is required by some other ReChannel macros
// - port_type and accessor_type are declared to provide more
// generality in the use of types
rc_portal(const sc_module_name& name_) // constructor
: base_type(name_) // calling constructor of the base class
{
[...] //(1) initialisations (event forwarders, etc.)
}
[...] //(2) custom members (methods, ports, data, etc.)
};
In the constructor - at code position (1) - all events shall be stated that have to be forwarded from the static to the dynamic side. Without the declaration of the so-called event forwarders the triggering of the static channel's events would not be noticed on the dynamic side.
For this purpose the macro RC_PORTAL_FORWARD_EVENT() can be invoked within the portal's constructor. The event name, which is passed as argument, has to correspond to the name of the event getter method of the channel's interface.
Consider the following declaration of two event forwarders: RC_PORTAL_FORWARD_EVENT(event1); // creates an event forwarder for event1
RC_PORTAL_FORWARD_EVENT(event2); // creates an event forwarder for event2
The macro RC_PORTAL_FORWARD_EVENT() expands to a call to method add_event_forwarder() (which represents an alternative notation). The first parameter is a function pointer (or function object) to the channel interface's event getter method. The second is the name of the event (for later referencing).
The same event forwarders as above, declared without macros: add_event_forwarder(&if_type::event1, "event1");
add_event_forwarder(&if_type::event2, "event2");
At code position (2) the portal's "switch behaviour" can be defined. For every switch's state change a callback can be specified. The following methods (and macros) are available: virtual void rc_on_open(); // macro: RC_ON_OPEN() {...}
virtual void rc_on_close(); // macro: RC_ON_CLOSE() {...}
virtual void rc_on_undef(); // macro: RC_ON_UNDEF() {...}
virtual void rc_on_refresh_notify(); // macro: RC_ON_REFRESH_NOTIFY() {...}
Example of a portal with events and behaviour: RC_PORTAL(myport_type)
{
RC_PORTAL_CTOR(myport_type) {
// declaration of the event forwarders
RC_PORTAL_FORWARD_EVENT(event1);
RC_PORTAL_FORWARD_EVENT(event2);
}
RC_ON_OPEN() { // what has to be done on opening up
[...] // do something
this->refresh_notify(); // call for the refresh of all events
}
RC_ON_REFRESH_NOTIFY() { // what has to be done on a refresh
if ([...]) {
this->notify_event("event1"); // notify event1
}
if ([...]) {
this->notify_event("event2"); // notify event2
}
}
};
Exportals are defined analogously to portals. In contrast to portals, exportals are parametrised with an interface type IF, since they have to be connected to SystemC exports of type sc_export<IF>. The following special macros are available to aid in the implementation of an exportal:
RC_EXPORTAL(), RC_EXPORTAL_CTOR(), RC_EXPORTAL_TEMPLATE(), RC_EXPORTAL_TEMPLATE_CTOR(), RC_EXPORTAL_FORWARD_EVENT().
The callback declaration macros RC_ON_OPEN(), RC_ON_CLOSE(), etc. may be used with exportals, too.
The structure of the definition of an exportal without the use of macros would look like this: class rc_exportal<myinterface_type>
: public rc_abstract_exportal<myinterface_type>
{
// typedef for convenience
typedef rc_abstract_exportal<myinterface_type> base_type;
public:
// the required typedefs for the interface and accessor type
typedef base_type::if_type if_type;
typedef base_type::accessor_type accessor_type;
rc_exportal(const sc_module_name& name_) // constructor
: base_type(name_) // invoking base class's constructor
{
[...] // initialisations (event forwarders, etc.)
}
[...] // custom members (methods, ports, data, etc.)
};
Conceptually, an accessor is a forwarder for communication that passes the boundary between the static and dynamic side, and vice versa. Simply speaking, an accessor is the medium connecting a dynamic side's communication object, like port or export, to a portal or exportal switch. ReChannel makes use of accessor objects to obtain full control over the communication that flows through portals and exportals. A connected reconfigurable module can be the communication endpoint as well as the initiator of the call.
The behaviour of switch and accessor is directly linked with the state of the reconfigurable module that is either active, inactive or unloaded. An Interface Method Call (IMC) on a connected port's or export's interface therefore will be forwarded or blocked, respectively.
The accessor is represented by class rc_accessor<IF>. rc_accessor is parameterised with the custom communication interface type (IF). Ready-to-use accessor implementations are already available for all native SystemC channels. The creation of a custom accessor implementation is required in cases where static and dynamic side are connected by user defined channels. For this purpose a specialisation of class rc_accessor<IF> has to be defined that is derived from rc_abstract_accessor<IF>.
Definition macros are provided for convenience. The following code demonstrates their use: RC_ACCESSOR(myChannel_if) // declares the accessor
{
RC_ACCESSOR_CTOR(myChannel_if) { } // constructor
[...] // event declarations
// and interface methods
};
If the interface is a template, the macros RC_ACCESSOR_TEMPLATE() and RC_ACCESSOR_TEMPLATE_CTOR() have to be used: template<class T>
RC_ACCESSOR_TEMPLATE(myChannel_if<T>) // declares the accessor
{
RC_ACCESSOR_TEMPLATE_CTOR(myChannel_if<T>) { } // constructor
[...] // event declarations
// and interface methods
};
The accessor's base class rc_abstract_accessor<IF> is an abstract class, as it is derived from the pure virtual interface IF. Hence, the user has to implement all interface methods of rc_accessor<IF>. Fortunately, there is no need for a fully-fledged reimplementation, since he accessor shall simply forward the call according to its access type. The four supported access types are "blocking", "non-blocking", "blocking driver" and "non-blocking driver". The respective forwarder methods are rc_forward(), rc_nb_forward(), rc_forward_driver() and rc_nb_forward_driver(). The first parameter is a pointer to the interface method that is called, followed by up to ten optional parameters representing the method's arguments.
The following example illustrates how a non-blocking IMC is forwarded: virtual const T& read() const {
return this->rc_nb_forward(&if_type::read);
}
The use of rc_forward_driver() and rc_nb_forward_driver() enables the forwarding of driver accesses. These two so-called driver forwarder methods are provided to solve a problem that directly arises from ReChannel's simulation mechanism. Multiple reconfigurable modules accessing one static channel are always a potential source for driver conflicts. In general, driver forwarding is applied if driver identity is required, i.e. the channel's functioning relies on the distinction of process identities. As an example, consider the non-blocking write method of a signal channel's interface: virtual void write(const T& value) const {
this->rc_nb_forward_driver(&if_type::write, rc_cref(value));
}
In the following, some examples of possible calls are listed that may be used for IMC forwarding within accessor implementations.
this->rc_forward(&if_type::calculate, op1, op2, rc_ref(ret));
return this->rc_forward(&if_type::call, a, b, c);
this->rc_nb_forward(&if_type::call, rc_cref(arg));
this->rc_forward_driver(&if_type::bus_write, value);
In contrast to the above forwarding technique, the event getter methods of an interface have to be handled differently, as they supply events which have to be forwarded by other means. Since processes of the dynamic side can be sensitive to events of the static side, event notifications have to be forwarded or suppressed as well. To gain control over events that are listended beyond reconfiguraton boundaries, event getter methods may not return the actual channel's event but rather return a clone of the event. The event's clone as well as the event getter method are declared by invoking convenience macro RC_EVENT() within the accessor's class scope. If the identical event is also returned by one or more other event getter methods, RC_EVENT_ALIAS() shall be used for each additional declaration to avoid unnecessary duplicates.
// the interface myChannel_if<T>
template<class T>
class myChannel_if<T>
: virtual public sc_interface
{
public:
virtual const sc_event& value_changed_event() const = 0;
virtual const T& value read() const = 0;
virtual void write(const T& value) const = 0;
};
// the accessor for interface myChannel_if<T>
template<class T>
RC_ACCESSOR_TEMPLATE(myChannel_if<T>)
{
// constructor
RC_ACCESSOR_TEMPLATE_CTOR(myChannel_if<T>) { }
// event declarations
RC_EVENT(value_changed);
RC_EVENT_ALIAS(value_changed_event, default_event);
// interface methods
virtual const T& value read() const
{ return this->rc_nb_forward(&if_type::read); }
virtual void write(const T& value) const
{ this->rc_nb_forward_driver(&if_type::write, rc_cref(value)); }
};
Therefore, ReChannel allows explicit description of dynamically reconfigurable behaviour in SystemC. This is preferentially applied if a reconfigurable module is build from scratch, or if it is augmented with additional dynamic behaviour. For this purpose, ReChannel comes with a set of language extensions intended for explicit description of reconfiguration. In constrast to native SystemC, ReChannel language constructs possess an implicit reset mechanism being triggered on reconfiguration.
ReChannel's modelling constructs are primarily comprised of classes, functions and macros corresponding to a particular functionality already known from SystemC. They are named like their match in SystemC but can be recognised by a different prefix ("rc_") taking the place of the native one ("sc_"). In addition to a collection of basic components - like rc_module, rc_event, rc_signal, etc. - ReChannel provides several functions and macros, e.g., rc_spawn() or RC_THREAD(), each of which corresponds to a particular functionality in SystemC with the same name.
With the availability of resettable components and processes, both structure and behaviour of reconfigurable modules can be modelled in an intuitive way without the need to care about additional logic that deals with reconfigurable behaviour itself. And due to the strong syntactical similarity, modelling DRHW with ReChannel may appear quite familiar to those who are already used to SystemC.
The current reconfigurable context can be obtained by a call to the global function rc_get_reconfigurable_context() which returns a pointer to a rc_reconfigurable instance if found, or NULL otherwise. If this function is called during the construction of a reconfigurable module, that object is returned as the current reconfigurable context . If rc_get_reconfigurable_context() is called on other occasions - e.g., during SystemC's simulation phase - the current position in the hierarchy tree is unknown, since a function call is not associated with a particular location in the object model. To handle this second case, an argument of type sc_object can be passed to specify a starting point for the necessary hierarchy tree traversal.
class rc_resettable { friend class rc_reconfigurable; protected: virtual void rc_on_reset() = 0; virtual void rc_on_init_resettable() = 0; };
For the reset mechanism to work, resettable components have to be registered at a rc_reconfigurable instance. Usually a resettable component automatically registers itself to the current reconfigurable context by invoking function rc_register_resettable() at construction.
The particular state such a component is reset to can be assigned beforehand during the construction phase. At start of simulation the callback method rc_on_init_resettable() is invoked once on all resettables to give them opportunity to store their initial state after construction has finished. The request for an immediate reset is propagated by a call to rc_on_reset().
Example of a simple custom resettable component: class myComponent
: public sc_object,
public rc_resettable //implement abstract base interface rc_resettable
{
myComponent() // constructor
: p_curr_value(0), p_reset_value(0)
{
// register the resettable component with the reconfigurable context
rc_register_resettable(*this, this->get_parent_object());
}
[...] // implementation of myComponent
protected:
// preservation of initial state
virtual void rc_on_init_resettable() {
p_reset_value = p_curr_value;
}
// definition of reset functionality
virtual void rc_on_reset() {
p_curr_value = p_reset_value;
}
private:
int p_curr_value;
int p_reset_value;
};
If the custom component is a channel, deriving from base class rc_prim_channel is more convenient.
Here is an example that speaks for itself: class myComponent
: public rc_prim_channel // derive from rc_prim_channel
{
myComponent() // constructor
: p_curr_value(0), p_reset_value(0)
{ } // the registration is done by base class rc_prim_channel
[...] // implementation of myComponent
protected:
// preservation of initial state
virtual void rc_on_init_resettable() {
p_reset_value = p_curr_value;
}
// definition of reset functionality
virtual void rc_on_reset() {
p_curr_value = p_reset_value;
}
private:
int p_curr_value;
int p_reset_value;
};
Example: // create the switch connector
rc_switch_connector<myPortMap> connector(
"connector", clk_portal, reset_portal, A_portal, B_portal, C_portal);
// bind static ports and channels
connector.bind_static(
myPortMap_static(clk_port, reset_signal, A_fifo, B_fifo, C_fifo));
// bind reconfigurable modules
connector.bind_dynamic(m1);
connector.bind_dynamic(m2);
Author: Armin Felke