LIME / doc / C-Semantics

A file describing various LIME sytax constructs (not up-to-date)

Copyright (C) 2008 NXP Semiconductors B.V.

This file is part of LIME.

LIME is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

LIME is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with LIME.  If not, see <>.

Meaning of type-qualifiers and static storage class in LIME:

1. const type array[size]
   (standard C) regular input port. Without const, its a regular
   output port.

2. volatile type array[size]

   (standard C) device I/O port. Otherwise its a regular port.
   Must be used when the other side is an asynchronous entity
   such as I/O device or an interrupt

These only work for the first dimension of the array.

3. type array[restrict size]

   (standard C99) non-aliased pointer. Otherwise, it can be aliased by
   other ports in the same function. All "state" ports must be unrestricted.
   All other shared ports must specify restrict.

1, 2, 3, 4, 6 can be freely combined

4. type array[const size]

   array itself is const (elements are not const). It can not be re-assigned
   withing this function. This should probably be the default semantic...

     One use for it could be static or dynamic multi-ports, specified via a
   multi-dimensional array. The const specifier is used then to distinguish 
   a multi-port from a single ports transporting a multi-dimensional array:

   type port[const nports][size] declares a multi-port with nports each having
                                 the same type and the same size.

   type port[size1][size2] on the other hand declares a single port transporting
                           a multi-dimentsional array.

   According to C the array pointer can not be reassigned, but that's good;-)

   Also works for static multi-ports:

   fun(type array[const 5][size]) declares 5 ports each having the same type and 
                                  the same size, named array_1, array_2, etc...

5. type array[volatile size]

   array itself is volatile (elements are not volatile). accesses through it go
   directly to memory. Elements, however, can still be cached in registers...

   Can be useful when array pointer is asynchronously changing
   while function is executing, which can not happen, since the
   function gets its own copy...

6. type array[static size]

   array is non-null and contains at least that many elements, and possibly more.

   1. Could be used to specify relaxed type matching semantics, where the array (of
      size 1) can have different, but overlapping types at the consumer and the
      producer's sides.

   2. Also, can be used to silence the warning when the sizes of types match, but the
      exact type name doesn't, irrespective of the array size.

   3. When the array size is not 1, and the types have different sizes then this can be 
      used to trigger insertion of data realignment operations.


7. struct sel { 
	enum { E1, E2 } sel; 
	union { type1 array1[size1]; 
		type2 array2[size2];
	} ports;
   } array[nsel]

   specifies a selective multi-port, i.e., a (de-)multiplexor, where a function
   can select the port to be activated (either of array1 or array2). The selection
   process can happen nsel times, where nsel must not be larger that the number 
   of possibilities (2 in this case).

   can be combined with other multi-ports by specifying:

   struct sel array[const nports][nsel]

8. union cast {
	type1 array1[size1];
	type2 array2[size2];
    } array[const nports]

    specifies a multi-cast port, where a function can once initialize (presumably
    the largest object) which would then be automatically multi-casted to ports
    array1 and array2. 

    a) The meaning of nports in this case is unclear. Should always be 1 I think...
    Alternatively, it could indicate the number of such multi-cast ports...

    b) This has interesting implications for the case of: const union cast array[1].
    The implementation should then ensure that all producers produce the same value...

    It is thus a mechanism to do a MAP/REDUCE operation, with the operation of == by 
    default for the reading side. Writing side is a simple multicast.


    Combining a) and b), we could say that every array[i] should get the same value
    from those ports. The real ports (array1 and array2) should be indexed then:
    array1_1, array1_2,.... array2_1, array2_2... So, array1_1==array2_2, 
    array1_2==array_2_2 etc...

    This can also be used to relax type checking the producer is under control (can
    be given this type).

9. fun(struct {
	type1 array1[size1];
	type2 array2[size2];
    } array[const nports])

    specifies a multi-port having nports*size1 ports of type1, nports*size2 ports of

    When size is 1 it is basically equivalent to:

      fun(type1 array1[size1], type2 array2[size2])

10. fun(int size, type array[size])
    (Standard C) specifies a VLA parameter

11. fun(enum {ZERO=0, ONE=1} size, type array[size])

    (Standard C) specifies a VLA where size can be either 0 or 1


. type array[]

   undefined size, leads to an incomplete type...

   Probably not useful at all.

. type array[*]

   unknown size, presumed variable. Only possible in prototypes...

   Probably not useful at all.


(0) Fixed ports:

Fixed-rate: 			fun(int port[SIZE])

1. Flexible ports:

Variable-rate:	 		fun(int size, int port[size])
  Optional:			fun(enum {Z=0,C=1} size, int port[size])
  Enumerated:			fun(enum {C1=1,C2=2} size, int port[size])
  Ranged:			fun(enum {R_START=11, R_END=21} size, int port[size])

2. Multi-dimensional ports:

Fixed-rate:			fun(int port[SIZE1][SIZE2])
	... (see 1) ...

3. Multi-ports:

Fixed-rate:			fun(int port[const NPORTS][SIZE])
Dynamic fixed-rate:		fun(int nports, int port[const nports][SIZE])
Variable-rate:			fun(int size, int port[const NPORTS][size])
Dynamic variable-rate:		fun(int nports, int size, int port[const nports][size]) 
	... all combinations from above ...

If sizes or types differ, a list of normal ports should be used, or, these can be packed 
into a struct:

4. Grouped multi-ports: fun(struct { int ibuf[ISIZE]; float fbuf[FSIZE]; } port[const NPORTS])

   All tricks from 1, 2 and 3 can be applied...

   A grouped multi-port cardinality must always be "const" as it always specifies NPORTS

(5) Selective multi-ports: fun(struct { enum {S1, S2} sel; union { int b1[SIZE1]; int b2[SIZE2]; } buf; } port[NSEL])

   A grouped multi-port cardinality must not be "const" as it always specifies NSEL, however, it can be
   combined with 3 by specifying port[const NPORTS][NSEL].

6. Map/Reduce multi-ports: fun(union { int b1[SIZE1]; int b2[SIZE2]; } port[const NPORTS])

   Reduction operation OP is by default "==", otherwise it can be specified in the DSL XML. The semantic is to
   apply the OP to the contents of the union and "this" data, and store it in the union, if the union is already
   "initialized". Otherwise, just initialize it.

   A map/reduce multi-port cardinality must always be "const" as it always specifies NPORTS.