Part 1: Using External Types ============================ External types are an extension to nesC for platform-independent type representation: you can define your packet layout using syntax very similar to existing C type declarations, and just accesses it like a regular C type. The compiler ensures that this type has the same representation on all platforms and generates any necessary conversion code. Additionally, these types have no alignment restrictions. This makes them particularly suitable for defining and accessing network packets in a platform-independent way. For instance, the SurgeCmdMsg from the Surge application can be defined and accessed as follows using external types: typedef nx_struct { nx_uint8_t type; nx_union { nx_uint32_t newrate; nx_uint16_t focusaddr; } args; } SurgeCmdMsg; ... SurgeCmdMsg *pCmdMsg = (SurgeCmdMsg *)payload; if (pCmdMsg->type == SURGE_TYPE_SETRATE) { timer_rate = pCmdMsg->args.newrate; ... More formally, nesC includes three kinds of external types. o External base types are similar to the fixed size in8_t, uint16_t, etc types. They include 8, 16, 32 and 64-bit signed and unsigned integers denoted by the types nx_[u]int[8/16/32/64]_t. o External array types are any array built from an external type, using the usual C syntax, e.g, nx_int16_t x[10]. o External structures are declared like C structures and unions, but using the nx_struct and nx_union keywords (as in the SurgeCmdMsg example above). An external structure can only contain external types as elements. External types have no alignment restrictions, external structures contain no padding, and the external base types use a 2's complement, big-endian representation (there are also little-endian versions of the base types available, using the nxle_ prefix, but their use is discouraged for networking purposes). Bit-fields in external structures are maximally packed. A 0-width bit-field moves allocation on to the next byte. Thus the representation of external types is platform-independent, and any arbitrary section of memory can be accessed via an external type. All these external types can be used exactly like regular C types, with a few restrictions: o Bit-fields can only be created from big-endian external base types (i.e., `nx_struct { nxle_uint8_t f1 : 2; }' is not allowed). Having big- and little-endian bit-fields adjacent in a type would be problematic... o Bit-fields of external types cannot be used inside non-external structures (i.e., `struct { nx_uint8_t f1 : 2; }' is not allowed). o External type variables cannot have initialisers. Part 2: Implementation ====================== In the generated C code for nesC programs using external types, we simply define a C type for each external base type, translate nx_struct and nx_union to struct and union and leave array types unchanged. The external base types are replaced by structures containing char arrays. Reads and writes of external base types are replaced by calls to inline functions like NTOH32 in the example below. For instance, the example from Surge becomes: typedef struct { char data[4]; } nx_uint32_t; static inline unsigned short NTOUH32(void *target) { unsigned char *base = target; return (unsigned long)base[3] << 24 | (unsigned long)base[2] << 16 | base[1] << 8 | base[0]; } ... typedef struct { nx_uint8_t type; union { nx_uint32_t newrate; nx_uint16_t focusaddr; } args; } SurgeCmdMsg; ... SurgeCmdMsg *pCmdMsg = (SurgeCmdMsg *)payload; if (NTOUH8(&pCmdMsg->type) == SURGE_TYPE_SETRATE) { timer_rate = NTOUH32(&pCmdMsg->args.newrate); ... For this translation to be correct, a structure containing only characters (such as nx_uint32_t) should have no alignment restrictions, and the same must hold for structures containing such structures (e.g., SurgeCmdMsg). This is true on many, but not all platforms: on ARM processors, all structures are aligned to 4-byte boundaries, and on Motorola 68K processors they are aligned to 2-byte boundaries. We currently work around this problem by using gcc's non-standard packed attribute. Bit-fields are a little more complex: space used by bit-fields inside external structures are replaced by "filler" fields. Accesses to external bit-fields is done by taking the address of the enclosing external structure, treating it as an array of bytes and accessing the appropriate offsets.