Channel-Communicating Incrementer

This example is a simple introduction to hardware modelling in LARD. It describes the hardware using the channel communication library, so the file starts with a use expression that includes it:

	use(channels).
The operations declared by this library are similar to those provided by Tangram and Occam.

Here is the declaration of a block with two ports, i and o. It reads a value from i, waits for 10 time units, and sends the incremented value to port o:

	incblock(i:var(chan(int)),o:var(chan(int))):expr(void)=(
	    v:var(int).
	    
	    forever(
	      i?(v:=?i);
	      wait_for(10);
	      o!(v+1)
	    )
	).
The main program declares two channels and instances two of these incrementer blocks to join them in a ring. Finally it sends an initial value to one of the channels to start the system running:

	C1:var(chan(int)).
	C2:var(chan(int)).
	
	init_chan(C1,"C1");
	init_chan(C2,"C2");
	
	( 
	  incblock(C1,C2) |
	  incblock(C2,C1) |
	  C1 ! 0
	)

Using The Channel Library

Declaring Channels

Channels are implemented on top of shared varaibles, so a channel declaration is just a variable declaration whose type is chan. chan is a parameterised type whose parameter is the type of the values transmitted by the channel - in this case integers. Hence the declarations of C1 and C2 above.

Channels as Parameters

Once again since channels are just shared variables channel parameters are declared like ordinary variable parameters.

Initialisation

The implementation of channels requires that they are initialised before first use by the function init_chan. For channels that you want to view in the trace display (see below) init_chan also records the name that it should use.

Sending

Values are sent to a channel using the ! operator, as in Tangram and Occam. The syntax is channel ! value, which means

Receiving

Values are received using the ? operator, but with a slightly different style than in Tangram and Occam. The syntax is channel ? expression, which means

Within the body expression the received value can be read using the expression ? channel.

To understand the difference between this channel communication model and the Tangram/Occam model, consider these two code fragments:

	i ? ( o ! ?i )
	i ? ( v:=?i ) ;  o ! v
In the first case the block sending to channel i cannot continue until after the block receiving from channel o has completed receiving - there is no pipeline stage within the code shown. In the second example the varaible v explicitly describes a pipeline latch allowing the sender to i to continue as soon as the value has been latched. Tangram and Occam allow only the latter style of description.

Concurrency

It is of course necessary for the various blocks in a hardware description to operate concurrently. Like Tangram and Occam, LARD provides a simple concurrency operator, |. Whereas expressions separated by ; are executed sequentially, those separated by | are executed concurrently. This is used in the above example to make the two instances of the incblock concurrent.

Compilation and Execution

As before compilation can be acomplished using lcd, with the optional -O flag for increased execution speed. It is necessary to link with the channels and time libraries, so either use -lchannels -ltime on the command line or add these to your LCD_OPTS environment variable.

To observe activity on the channels during execution you can use li's channel viewer display. Invoke this by adding the module name channels to the li command line.

	ernie$ lcd -lchannels -ltime -O incchan.l
	ernie$ li incchan.bcode channels
Three windows appear; the main window for I/O interaction, the Selector window and the Time View window. Running the simulation (Simulation->Run from the main window menubar) causes the structure of the program to be displayed in the Selector window:

This display shows the hierarchy of the program (not very complex in this case). advance_time is an internal process that can be ignored. The names of the two channels, C1 and C2, are displayed. Alongside their names are buttons that enable their display on the time view window.

The triangular buttons can be used to hide regions of the structure that are not of interest.

After enabling the display of C1 and C2 and scrolling the Time View window right to show the new data that window appears something like this:

To the right of the channel name is a symbol which conceals a pop-up menu. This menu provides facilities to hide, delete, and reformat the display of that channel. Deleting and hiding a channel both remove the channel from the display, but a hidden channel can be recalled using the Unhide menu and its data will be intact. Information about a deleted channel is discarded.

The coloured rectangles in the trace display give information about the timing of the communications - this is described in another example. The actual values that are transfered chan be shown by enabling either Show Data or Show Blobs from the pop-up menu. Enabling Show Data displays all values which can look rather cluttered in some cases. Show Blobs displays a red blob for each value, and pointing at this shows the data value in a transient window as shown in the following screenshot:

The Checking Channel Library

A very common mistake is to forget to initialise a channel in your model. This normally results in undefined behaviour or a run-time crash. To help spot this error, link with the library channels_chk rather than the normal channels. This library tries to spot any uses of uninitialised channels. It isn't infalible and does impose a performance penalty, but if you get inexplicable run-time crashes it is something to try.