Copyright GreenSocs Ltd 2008
GreenScript User Manual v0.3
GreenScript User Manual v0.3
System-level hardware modeling becomes more important each day. A lot
of effort is done nowadays to leverage the abstraction level and
simplify the architecture exploration. The GreenScript project aims in
this direction, by making legacy SystemC models to interoperate with
higher abstraction level models done in scripting languages.
The motivation is to be able to explore the advantages of a
dynamically-typed language with garbage-collection in the context of
systems modeling, recognizing that model development time is often
more important than model run time, especially for system architecture
exploration. GreenScript simulations are expected to run much slower
than SystemC simulations and this is considered acceptable. The idea
is that GreenScript can be used when a model is needed very quickly or
for coding parts of a model that are activated very infrequently and
thus have no significant impact on the overall simulation speed.
The syntax provided to the user will remain as close as possible to
SystemC, and semantics of events, threads, scheduling, delta cycles
and so on should be the same in GreenScript and SystemC. The user
interface is composed by classes and functions under the module
gs.
At the time of this writing, only the Python interface is
available. This project is in beta stage. There may be changes in the
interfaces described in this document to accommodate particularities
of other scripting languages like Perl, TCL, etc.
For examples how to use this interface, please take a look at the
examples directory. To run, import the example inside python or use
python example.py. The embedded examples have to be compiled
and run outside Python.
GreenScript can be used in two modes. One is the so-called
standalone mode, where the system modules are implemented by
Python scripts and the top-level file is also a Python script. This
mode doesn't need a compilation step, as the scripts are directly run
in the Python interpreter.
The other mode is called
embedded mode, where the system
designer is free to implement some parts of the system in Python and
others in SystemC. The top-level file must be SystemC in this
case. The C++ files are compiled into an executable binary and the
Python scripts are dynamically loaded into the embedded Python
interpreter.
Before going on detais on the GreenScript modes of operation, the next
section shows how to do a piece of code in GreenScript. Following that
the information how to use GreenScript in
standalone mode or
embedded mode.
2.1 Coding with GreenScript
The GreenScript framework does not enforce the user to create hardware
modules in Python. A system can be made of group of threads without a
hierarchy, for example. But the use of modules makes it possible to
instantiate the same code more then once. The concept becomes closer
to the way SystemC operates.
The example bellow shows a piece of code for a valid design in
GreenScript. It is a thread to write to an output signal the double of
what is read from an input signal, whenever this input signal
changes. The code defines all resources in top-level, without any
hierarchy.
import gs # Load GreenScript definitions
# Global signals
input = gs.signal()
output = gs.signal()
# Global thread
def double():
while(True):
gs.wait(input.write_event())
output.write(input.read() * 2)
gs.spawn(double) # Start global thread
# Other thread should be defined to drive the input signal (...)
This last example suites well for small designs. For bigger systems,
it may be better to group functionality in modules. This has some
advantages, like better reusability and higher abstraction level.
The following example is equivalent to the last one, but now it
embraces the thread in a class, thus defining a module. The module
input and output can be parametrized by using module ports, so the
module can be instanciated more then once when required. (note that it
was impossible in the last example because all threads would change
the same set of signals)
# Load GreenScript definitions
import gs
# Declare the module
class doubler:
def double(self):
while(True):
gs.wait(self.input.write_event())
self.output.write(self.input.read() * 2)
def __init__(self, input=None, output=None):
self.input = input
self.output = output
gs.spawn(self.double)
# Describe the system (in possibly other file)
s1 = gs.signal()
s2 = gs.signal()
# Instanciate module
# option 1: positional port binding
my_doubler = doubler(s1, s2)
# option 2: named port binding
my_doubler = doubler()
my_doubler.input = s1
my_doubler.output = s2
# Other module to drive input for doubler (...)
2.2 Standalone mode
The standalone mode means that the whole simulation runs inside the
Python interpreter command-line. All files describing the system are
Python scripts and thus they don't need a separate compilation step.
Just start the Python interpeter and load the necessary files to run
the simulation.
This was the first mode of operation available in GreenScript. It may
be easier to use this mode when the user is starting to test
GreenScript, or developing a small system. But for a more serious use,
the embedded mode is recomended.
2.3 Embedded mode
The embedded mode can run the same scripts from the standalone mode,
but normally some minor changes are done to allow the communication
with the SystemC world. In this mode, GreenScript modules are loaded
by the top-level SystemC file, inside de sc_main function.
In order to instanciate a GreenScript module, the designer first
instanciates a container module, then load scripts to this
container. Finaly, he instanciates the desired GreenScript module
running a script expression. This is done in the following code:
#include <systemc.h>
#include <greenscript.h>
(...)
int sc_main(int argc, char *argv[])
{
// Declare interfaces (signals, fifos, etc.)
sc_signal<int> in1("in1"), in2("in2"), out1("out1");
// Instanciate a container module to load Python scripts
GreenScriptModule gs_module("gs_module");
// Load desired scripts that defines modules
gs_module.load("adder.py");
// Instanciate Module (and do port binding)
PyRun_SimpleString("my_adder = adder('in1','in2','out1')");
//Instanciate other modules (either SystemC or GreenScript)
// to drive signals named in1 and in2.
(...)
// Run the simulation allowing Python threads (for GreenScript)
Py_BEGIN_ALLOW_THREADS;
sc_start(100);
Py_END_ALLOW_THREADS;
Py_Finalize();
return 0;
}
Note that the interfaces (signals, fifos, etc.) that will be acessed
in GreenScript must be named explicitly, or else SystemC will generate
a random name for them and GreenScript will have no way to find
them. The communication between SystemC and GreenScript in this last
example is done with the signals named
in1,
in2 and
out1. The first two are connected as input and the third as
output to the
adder GreenScript module. They can also be
connected to SystemC modules in order to drive the inputs and read the
output.
Only sc_signal < int > and sc_fifo < int > are recognized for
now. More interfaces and user-defined interfaces are work for future
GreenScript versions. The GreenScript module should use the classes
gs.sc_signal and
gs.sc_fifo to allow the
communication with SystemC modules. Please refer to Chapter
for more information on these classes.
The adder module is listed bellow:
import gs
class adder:
def doit(self):
while True:
gs.wait(self.in1.write_event() | self.in2.write_event())
self.out.write(self.in1.read() + self.in2.read())
def __init__(self, in1, in2, out):
self.in1 = gs.sc_signal(in1)
self.in2 = gs.sc_signal(in2)
self.out = gs.sc_signal(out)
gs.spawn(self.doit)
Chapter 3
Python interface reference
Each script that will describe a part of the system should import the
main GreenScript library module, using
import gs. This
defines the classes and functions described in the following sections.
3.1 Classes
gs.event
|
The event class. The objects os this class has a
correspondent sc_event in the SystemC world.
It has 2 optional constructor arguments, mainly used
internally. Without any argument, creates a new sc_event in
the SystemC world and binds it to this new event. The first argument
is a Python object that correspond to a sc_object, in this
case the new event will be bind to the default event of this
object. The second argument is a constant to select which event to
bind to: gs.EVENT_CHANGED (the default),
gs.EVENT_READ, gs.EVENT_WRITE. |
Methods:
| notify() | Notify immediate |
| notify(0) | Notify after one delta |
| notify(time, unit) | Notify after time. The unit is
an optional constant and can be one of gs.SC_FS,
gs.SC_PS, gs.SC_NS (the default),
gs.SC_US, gs.SC_MS, gs.SC_SEC. |
| wait() | Wait for this event |
|
gs.event_tree
|
This class cannot be directly
instanciated. Objects os this class are created automatically when
using event expressions with and and or operators,
eg:
e1 = gs.event()
e2 = gs.event()
e3 = gs.event()
my_tree = (e1 and e2) or e3
|
Methods:
| wait() | Wait for this event tree expression |
|
gs.primitive_channel
|
A base class for creating new
primitive channels. The user may derive a class from the
gs.primitive_channel and change the implementation of the
class's update() method. Unlike SystemC, in GreenScript
primitive channels can be created and deleted during the simulation
run. The gs.signal and gs.fifo classes are examples
of channels derived from this class. |
Methods:
| update() | Empty method in this class. Should be re-defined
in derived classes to model the needed channel. Should not be called
by the user. |
|
gs.signal
|
Derived from gs.primitive_channel. The
gs.signal behaves like an sc_signal except that
gs.signal can be instantiated and deleted during simulation
run, not only at elaboration time.
It has one constructor argument, the initial value, which defaults to
False.
A gs.signal can be set to any Python object and there is no
restriction on the type, which can change during the simulation.
This class does not comunicate with SystemC directly. Use
gs.sc_signal for this purpose. |
Methods:
| read() | Returns the value |
| write(object) | Stores any Python object as the signal value |
| write_event() | Returns an gs.event triggered when
this signal is written to |
|
gs.sc_signal
|
The gs.sc_signal class wraps
directly a SystemC sc_signal object. The corresponding
sc_signal object is searched in the SystemC object hierarchy
by it's name during inicialization, so the sc_signal must be
declared earlier in the SystemC world.
This class can be instanciated and destructed during elaboration or
simulation run, as long as the corresponding sc_signal is
declared earlier and in elaboration time.
It has one constructor argument, the name of the sc_signal
to capture. It differs from gs.signal because the initial value is not
an argument for this class.
The type of the object that is written to the gs.sc_signal
must be compatible with the original type of the sc_signal. |
Methods:
| read() | Returns the value |
| write(object) | Stores the object as the signal value, the
type must be convertible to the internal sc_signal type |
| write_event() | Returns an gs.event triggered when
this signal is written to |
|
gs.fifo
|
Derived from gs.primitive_channel. The
gs.fifo behaves like an sc_fifo except that
gs.fifo can be instantiated and deleted during simulation
run, not only at elaboration time.
One constructor argument, the FIFO size, which defaults to 0, meaning
infinite size.
A gs.fifo can contain up to "size" Python objects and there
is no restriction on the object types or mixture of types.
This class does not comunicate with SystemC directly. Use
gs.sc_fifo for this purpose. |
Methods:
| read() | Blocking read. Remove and return the first value from the
queue |
| nb_read() | Non-blocking read. Returns a tuple (bool, value) where
the first value shows success or failure of the read |
| peek() | Blocking peek. Just return the first value from the queue
without removing |
| nb_peek() | Non-blocking peek. Returns a tuple (bool, value) where
the first value shows success or failure of the peek |
| num_available() | The number of objects in the queue |
| write(object) | Blocking write. Stores any Python object in the
end of the queue |
| nb_write(object) | Non-blocking write. Returns a bool to
indicate success or failure of the write |
| num_free() | The number of free positions in the queue |
| write_event() | Returns an gs.event triggered when
this fifo is written to |
| read_event() | Returns an gs.event triggered when
this fifo is read from |
|
gs.sc_fifo
|
The gs.sc_fifo class wraps directly a
SystemC sc_fifo object. The corresponding sc_fifo
object is searched in the SystemC object hierarchy by it's name during
inicialization, so the sc_fifo must be declared earlier in
the SystemC world.
This class can be instanciated and destructed during elaboration or
simulation run, as long as the corresponding sc_fifo is
declared earlier and in elaboration time.
It has one constructor argument, the name of the sc_fifo to
capture. It differs from gs.fifo because the size of the
queue is not a argument for this class.
The type of the object that is written to the gs.sc_fifo
must be compatible with the original type of the sc_fifo.
It doesn't have peek and nb_peek like
gs.fifo, because sc_fifo does not support these
methods. |
Methods:
| read() | Blocking read. Remove and return the first value from
the queue |
| nb_read() | Non-blocking read. Returns a tuple (bool, value)
where the first value shows success or failure of the read |
| num_available() | The number of objects in the queue |
| write(object) | Blocking write. Stores the object in the end of
the queue. The type must be convertible to the internal
sc_fifo type |
| nb_write(object) | Non-blocking write. Returns a bool to
indicate success or failure of the write |
| num_free() | The number of free positions in the queue |
| write_event() | Returns an gs.event triggered when
this fifo is written to |
| read_event() | Returns an gs.event triggered when
this fifo is read from |
|
gs.spawn
|
The user creates processes in GreenScript using
gs.spawn. There is no distinction between threads and
methods (everything is a thread). Processes may be created at any
time; during elaboration or during the simulation.
Constructor arguments: runnable, name,
debug
runnable must be something the Python interpreter can
"execute", eg. a function without arguments or an object of a class
with an overwritten __run__ method (note that arguments
can be added to a function using lambda in Python). name
should be a string (it is optional, defaults to
runnable.func_name). debug is a boolean, default
False, that specifies that the runnable should be executed
within the Python debugger pdb.
Known Issue: When more then one thread is debugged at the same time,
the first one that finishes makes the rest of the threads execute
until the end, as if there is no debug enabled in any other thread. |
Methods:
| reset() | Cause the process to start again from the
beginning, as soon as its current wait() returns |
| kill() | Cause the process to stop and be deleted |
| pause() | Cause the process to stall |
| resume() | Cause a previously paused process to resume. Note
that the process was in a wait() when it was paused and this
wait() will in all cases complete as originally specified.
There is no way to abort a wait(). |
| | (deprecated: continue is
reserved word in Python, and cannot be used to name a method. This was
renamed to resume) |
|
gs.param
|
Create a configuration parameter. This uses
GreenConfig to create parameters that are treated internally as
strings, so config files are interpreted as strings for these
parameters. In the Python world, this string is evaluated to a Python
object by using eval().
One constructor argument, the name of the parameter.
Please see Chapter for more information. |
Methods:
| read() | Use the eval() function to evaluate the
string saved in the parameter as a Python object |
| write(object) | Write the string representation of the
object to the parameter, using str(object). |
|
gs.av.output
|
This is part of GreenAV, the Analysis and
Visibility project. One functionality of this project is to monitor
some parameters for changes. This class creates an output descriptor
that will contain the changes in the registered parameters along time.
Many types of output files are supported. Please consult the GreenAV
users guide for more information
It has two constructor arguments: the first is the type of the output
to be created (gs.av.NULL, gs.av.FILE,
gs.av.STDOUT, gs.av.CSV or gs.av.SCV) and
the second the name of the file to output. |
Methods:
| add(param_name) | Add the given parameter (by name) to the
list of parameters that are monitored for changes. When this
parameter changes, the output will be updated. |
| add(gs_param_obj) | Add the given parameter (by reference) to
the list of parameters that are monitored for changes. When this
parameter changes, the output will be updated. |
| add_all() | Add all existing parameters to the list of
parameters that are monitored for changes. |
| remove(param_name) | Remove the given parameter (by name)
from the list of parameters that are monitored for changes. |
| remove(gs_param_obj) | Remove the given parameter (by
reference) from the list of parameters that are monitored for
changes. |
| pause() | Pause the output. The parameters won't be
monitored. |
| pause(time, unit) | Pause the output for the given amount of
time. time is a floating-point number and unit is an
optional constant that can be one of gs.SC_FS,
gs.SC_PS, gs.SC_NS (the default),
gs.SC_US, gs.SC_MS, gs.SC_SEC. |
| pause(event) | Pause the output until the given
gs.event fires. Currently it does not accept
gs.event_tree. |
| resume() | Resume the output when it is paused. The
parameters will be monitored and changes outputed again. |
|
3.2 Functions
gs.simulation_time(unit)
|
Get the simulation time. The
unit is an optional constant and can be one of
gs.SC_FS, gs.SC_PS, gs.SC_NS (the
default), gs.SC_US, gs.SC_MS,
gs.SC_SEC. |
gs.wait(time, unit)
|
Wait for a given time. time is a
floating-point number and unit is an optional constant that can be
one of gs.SC_FS, gs.SC_PS, gs.SC_NS (the
default), gs.SC_US, gs.SC_MS,
gs.SC_SEC. Only call from a process. |
gs.wait(event)
|
Argument is a gs.event or a
gs.event_tree. Returns when the event fires. Only call from
a process. |
gs.fork(runnable_list, wait_for=-1, kill=False, debug=False)
|
Allows the user to create multiple related processes in parallel,
with a blocking semantic. Each runnable in the
runnable_list is used to start a process. The number of
processes to wait_for defaults to the length of the list if
set to a negative value. When this number of processes have
terminated, control returns to the calling process. The other
processes are killed at that time if the kill argument is
True. If debug is set to True, some debug messages are
printed to tell when processes start and finish.
Only call from a process. |
gs.start(time, unit)
|
Start the simulation and run it for a
certain time or until no more events left. time is a
floating-point number that defaults to 0, which means infinite
time. unit is an optional constant that can be one of
gs.SC_FS, gs.SC_PS, gs.SC_NS (the
default), gs.SC_US, gs.SC_MS, gs.SC_SEC.
This function is ignored when GreenScript is embedded in a SystemC
simulation. The sc_start() function takes precedence. Any
Python code after the gs.start() call would in the embedded
case execute immediately rather than after the end of the simulation. |
gs.stop()
|
Stop the simulation. Only call from a process. |
gs.create_thread(runnable, name)
|
Legacy support based on
SystemC-2.0.1, which does not support dynamic creation of processes.
Processes were created during elaboration using this function. This is
just a wrapper to gs.spawn now. |
gs.delta_count()
|
Return the delta cycle count. |
gs.time(unit)
|
Show a message with simulation time and delta
cycle count. unit is an optional constant that can be one of
gs.SC_FS, gs.SC_PS, gs.SC_NS (the
default), gs.SC_US, gs.SC_MS,
gs.SC_SEC. The format of the output is time=XX
(delta=YY). |
Chapter 4
Using parameters with the configure framework
The configure framework used in GreenScript is provided by
GreenControl. This chapter shows only the concepts that relates to
GreenScript. For more information on GreenConfig, please consult
the GreenControl Users's Manual.
The entry point to use parameters to configure a GreenScript module is
to instanciate a
gs.param class. The name of the parameter
must be given as the constructor argument. For example:
my_param = gs.param("my_param")
other = gs.param("other")
In the current version of GreenScript and GreenConfig, the hierarchy
of parameter names is not quite right, because GreenScript
descriptions can be done non-hierarchical, while GreenConfig does not
yet support parameters without a hierarchy. The parameters will be
created inside a module called
gs. This means that the full
name of the above parameters, to be used in configuration input files,
are
gs.my_param and
gs.other. As an alternative,
the user can write the full name of the parameter in the constructor.
The value of the parameter is treated internally as a C++
std::string. When reading the parameter with the
read()
method, the internal string is evaluated to a Python object. If the
parameter is not set, then the object read is the empty string. If the
string is a valid integer, float, and so forth, the object returned
will be of the evaluated type. It recognizes all the types that the
build-in Python
eval() function recognizes. If something goes
wrong with
eval(), the original string is returned.
When writing to a parameter with the
write(object) method,
the Python object given is first converted to string using
str(object), then written to the internal std::string.
Other available functionality for parameter objects are:
- applying str() to it returns the original std::string
- it can be in boolean contexts, like if-statements (defines
__nonzero__)
- it can access itens with braces operator (proxies the
__getitem__ function to the underlying object)
- it can be converted to simple data types (defines __int__,
__float__, __long__, __complex__)