EE249 Fall '04, Lab1

General tips to install Metropolis.
The beta release is available at the hidden link I gave you in class.
You need few more packages. First of all the Java development kit that you can download from here.
Then you need to install systemC which is available here. Install java and systems (I would install systemC in /usr/local ). Then save the metropolis beta distribution in a directory that I will refer to as _dir_ in the sequel:
>cd _dir_
Now use
>tar -zxvf metro-1.0-beta.tar.gz
This command will create a directory called metro.
Now you have to configure and compiler it. The configuration step will try to find java and systemC but you might have installed those two packages in some strange directory that it's hard to find. First, set the variable METRO_JDK:
>setenv METRO_JDK _yourjavainstallationdirectory_
Then there is a configuration option to tell where the systemC library is. If you log into fortytwo and you use this machine for working with metropolis, then you don't need to do anything special, just run

Following the orthogonalization of concerns principle, we define now some components for doing computation and communication.
The Metropolis Meta-Model (MMM) language defines basic objects to write programs that represent models of what we want to specify. The MMM language semantics is defined in terms of action automata that are basically finite state machines.
Briefly, an action is any statement, sub-expression, function call etc. Each action has a begin and an end event. Semantics is defined in terms of events.
A process is an object that that runs on its own thread of execution which is a sequence of statements and hence actions.
A process is defined as follows:
package _mypackage_;
process _myprocess_ {
//ports declaration
_myprocess_(String n,/*other parameters*/){
//constructor definition
void thread () {
//definition of thread;

Consider now a process whose thread is like the following (this is a very classic example that you will find in almost all metropolis documents):
void thread() {
y=x+1; }
There are two actions there: the assignment y=sub-expr and the sub-expression x+1. Each action has a begin and an end event. So in this case we have four events: beg(=), end(=), beg(x+1), end(x+1).
Now consider three processes P1, P2, P3 that are totally concurrent. Each of them has a thread of execution. Such a program cannot be described using just a sequence of events but we need a vector of events. A special event is defined in the semantics of MMM: NOP. This event correspond to the stalling of a process. For instance, consider the case where Pi's thread calls function fi. A valid sequence of event vectors is the following:

beg(f1) | beg(f2) | beg(f3)
end(f1) | end(f2) | end(f3)

But another valid execution is

beg(f1) | NOP | beg(f3)
end(f1) | beg(f2) | end(f3)
NOP | end(f2) | NOP(f3)

Every time an event vector is executed, the entire system jumps from one state to another of the action automaton (don't worry about this now, you will see the formal definition later in the class).
A process can have ports to communicate with the rest of the system. A port definition looks as follows:

port _port_type_ _port_name_;
where the _port_type_ is an interface.
Processes cannot be connected together directly by they need to use communication objects to keep computation and communication orthogonalized.
An interface declares a set of services but doesn't implement it. The implementation is written in a medium. Interfaces are specified as follows:

package _mypackage_;
interface _myinterface_ extends _thesuperclass_ {
_modifier_ _services_signature;

_thesuperclass_ is an interface that _myinterface_ extends. If this interface is going to be used as a port type, the it must extend the superclass Port.
_modifier_ can be any modifier but there are two of them that are really important: eval and update. The former is used for services that don't change the state of the medium implementing the interface whereas the latter is used for those service that will change such state.
An example of interface is the following:

package _mypackage_;
interface FIFOInterface extends Port {
update void write(int data);
update int read();
eval int nspace(); }

Note that reading an integer from the FIFO will change the number of tokens in it, so the modifier has to be update. On the other hand, nspace will just read and return the number of available spaces in the FIFO and does not change any variable.
A medium implements interfaces. What it means is that the body of a function declared in an interface is defined in a medium. These objects are declared as follows:

package _mypackage_;
medium _mymedium_ implements FIFOInterface {
//port declaration
_mymedium_ (String n, /*other parameters*/){
//constructor body
update void write(in date){
//implementation of the service

Now id a process has a port p of type FIFOInterface and it wants to write a data into a medium implementing the interface, the it just calld p.write(thedata).

A very important statement defined in the MMM is the await statement.
Its syntax is as follows:

await {
//body of the critical section }

_guard_ is a boolean expression. In order to enter the critical section, the guard condition must be true. _test_list_ and _set_list_ are comma separated lists of interfaces.
Imagine that there is a flag for each interface. The flag of interface _IF_ is set if the interface was in the set list of a critical section that is currently running.
So, even if the guard is true, before entering a critical section all interfaces in the test list are checked. If none of them has its flag raised then all flags of the interface in the set list are raised and the critical section is entered. When the critical section execution finishes, all flags are reset.
Await statements are used to synchronize the access of processes to shared variables. In the case of a FIFO, for instance, we want to prevent both reader and writer to access the same data at the same time. We can use then an await and a semaphore interface to protect the data access in a critical section.