Bayfront Technologies, Inc. | 08/01/92v1.2 |
Application Note 5 | 1/15/95r |
CAPE c code generation style | JMN/BD |
This application note presents the general style of c code generation used by The Bayfront CAPE Tools(tm) (BCT). Complete details of this process are presented in chapters 4 though 8 of the BCT User's Guide.
The Bayfront CAPE Tools c code generator produces .c and .h output files from input protocol descriptions (.pdl files). These output files must be compiled with a supplied header file (cape.h) and a state machine interpreter. The state machine interpreter shipped with BCT (smexec.c) is intended as an example that may be easily modified by users for their environment. An example of these files resulting from an extremely simple protocol (.pdl) is presented below. A more complex but still simple protocol (dataxfer.pdl) is given at the end of this note. The files resulting from dataxfer are too large for presentation in this form. The description of IEEE802.2 logical link control (LLC) protocol pdl is roughly 40 times the size of dataxer. The LLC is one of three state machines in the link layer of the LAN protocol.
A simple protocol description (ex1.pdl) is shown below. Events in states trigger actions and transitions to new states.
ex1 { InitialState = State1; state State1:: Event1 -> Action1, Action2 >> State2; Event2 -> Action3; state State2:: Event1 -> Action4; Event2 -> Action5, Action6 >> State1; }The c code files (ex1.h and ex1.c) generated from the above input follow below.
The header file ex1.h
/* -------- ex1 state machine header file -------- */ /* ---- States ---- */ #define State1 0 #define State2 1 #define ex1_MaxStates 2 #define ex1_InitialState State1 /* ---- Events ---- */ #define Event1 0 #define Event2 1 #define ex1_MaxEvents 2 /* ---- Switch Labels ---- */ /* number of unique switch labels = 0 */ #define ex1_ActArraySize 7 /* ---- Action Subroutine Declarations ---- */ extern int Action1(); extern int Action2(); extern int Action3(); extern int Action4(); extern int Action5(); extern int Action6(); /* number of actions = 6 */The code file ex1.c
/* ---- ex1 state machine table file ---- */ #include "cape.h" #include "ex1.h" /* Switch Return Value Table (indices into the Action Vector Table) */ static int ex1_SwLblVect[1]; static SWTBL ex1_SwTbl[1]; /* ---- Action routines which pass arguments ---- */ /* ---- ex1 Action Vector Jump Table [MaxStates][MaxEvents] ---- */ static int ex1_State1[] = { 1, 3 }; static int ex1_State2[] = { 4, 5 }; static int *ex1_StateEvent[] = { ex1_State1, ex1_State2 }; /* ---- Action Vector Table ---- */ static AAEntry ex1_ActArray[] = { /* Null Entry */ {0, 0, 0}, /* fcn, type, jmp or newstate */ /* Action 1 */ {Action1, 1, 521}, /* Action 2 */ {Action2, 1, 1}, /* Action 3 */ {Action3, 1, 520}, /* Action 4 */ {Action4, 1, 520}, /* Action 5 */ {Action5, 1, 521}, /* Action 6 */ {Action6, 1, 0}, }; /* ---- ex1 state machine definitions ---- */ SM ex1 = { ex1_MaxStates, /* max states */ ex1_MaxEvents, /* max events */ ex1_ActArraySize, /* # of entries in the action array */ ex1_SwLblVect, /* Switch return value ActArray indicies */ ex1_SwTbl, /* SwLblVect index & # of rtn values */ ex1_StateEvent, /* state/event to action vector tbl */ ex1_ActArray /* action vector table */ };The Bayfront Technologies supplied header file (cape.h) is included by the output protocol code (ex1.c) and by the state machine interpreter (smexec.c)
/* Bayfront Technologies Protocol/State Machine Header File Version 1.2 */ /* -- sm_exec() return values -- */ #define smOK 0 /* return ok */ #define smActIndError 1 /* action table index error */ #define smSwRtnError 2 /* switch return value error */ #define smStateError 3 /* state out of bounds */ #define smEventError 4 /* event out of bounds */ #define smATypeError 5 /* unknown action type encountered */ /* ---- Interpreter constants ---- */ /* action types in the action tuple entry */ #define JmpOp 0 #define ActionOp 1 #define NullActOp 2 #define SwitchOp 3 #define ActionParamOp 5 #define SwitchParamOp 6 /* action array TNsi values (max # of states = 520) */ #define ContExec 521 /* continue exec of action array */ #define NoStateChg 520 /* no state change */ /* action array entry */ typedef struct AAEntry { int (*pTAct)(); int TType; int TNsi; } AAEntry; /* switch statement table */ typedef struct SWTBL { int SwInd; /* index into the SwLblVect structure */ int SwMax; /* max return values */ } SWTBL; /* data structures describing the state machine */ typedef struct SM { int smStates; /* max states in sm */ int smEvents; /* max events in sm */ int smAASize; /* action array size */ int *pSwLblVect; /* vectors into the action array */ struct SWTBL *pSwTbl; /* contains info on switch statements */ int **pStateEvent; /* state/event -> action array vector */ struct AAEntry *pActArray; /* action array */ } SM;The Bayfront Technologies supplied example state machine interpreter (smexec.c) is a model for user derived state machine interpreters.
/* * Bayfront Technologies * State Machine/Protocol Executor * Version 1.2 */ #include "cape.h" #define DEMAND(x, y) if (!(x)) return(y); int sm_exec(psm, curstate, event, pdata) SM *psm; int *curstate, event; unsigned char *pdata; { int i, ActInd, RtnVal; unsigned short OpType; /* get action index from state & event */ DEMAND(*curstate < psm->smStates, smStateError); DEMAND(event < psm->smEvents, smEventError); ActInd = (*(*(psm->pStateEvent+*curstate)+event)); do { /* loop until a new/no state termination */ switch (psm->pActArray[ActInd].TType) { case ActionOp: case ActionParamOp: DEMAND(ActInd < psm->smAASize, smActIndError); /* execute action */ RtnVal = (*psm->pActArray[ActInd].pTAct)(pdata); /* go to next tuple if new state not assigned */ if (psm->pActArray[ActInd].TNsi == ContExec) ActInd++; else if (psm->pActArray[ActInd].TNsi == NoStateChg) return(smOK); else { *curstate = psm->pActArray[ActInd].TNsi; return(smOK); } break; case SwitchOp: case SwitchParamOp: DEMAND(ActInd < psm->smAASize, smActIndError); RtnVal = (*psm->pActArray[ActInd].pTAct)(pdata); /* check RtnVal for range correctness */ DEMAND((RtnVal >= 0 && RtnVal <= psm->pSwTbl[psm->pActArray[ActInd].TNsi].SwMax-1), smSwRtnError); ActInd = psm->pSwLblVect[psm->pSwTbl[ psm->pActArray[ActInd].TNsi].SwInd + RtnVal]; break; case JmpOp: ActInd = psm->pActArray[ActInd].TNsi; if (ActInd == 0) return(smOK); break; case NullActOp: if (psm->pActArray[ActInd].TNsi == NoStateChg) return(smOK); else { *curstate = psm->pActArray[ActInd].TNsi; return(smOK); } default: return(smATypeError); } /* switch */ } while (1); } /* sm_exec */The files above implement the ex1 protocol from its simple 10 line pdl description. A more complex protocol, dataxer.pdl, is included below to better demonstrate the power of a pdl description. As a measure of complexity the IEEE802.2 Logical Link Control LAN protocol description is over 200 times the size of dataxfer.
The simple protocol dataxfer.pdl
[ simple data transfer protocol with timeouts, retry, and ] [ no error recovery ] dataxfer { InitialState = Idle; [ Idle state - no connections have been established ] state Idle:: recv(Connect,Net) -> send(Connect,Usr), StartTimer(Tconn) >> SetupReceive; recv(Connect,Usr) -> send(Connect,Net), StartTimer(Tconn) >> SetupSend; [ SetupReceive state - network has requested a connection with the user ] state SetupReceive:: timeout(Tconn) | recv(Refuse,Usr) -> send(Refuse,Net), StopTimer(Tconn) >> Idle; recv(Accept,Usr) -> send(Accept,Net), StopTimer(Tconn), StartTimer(Trecv) >> Receiving; macro EventDisc; [ Receiving state - user has accepted a connection from the network, ] ] [ network to user transmission is in progress ] state Receiving:: timeout(Trecv) -> macro ActionError; recv(Disconnect,Net) -> send(Disconnect,Usr), StopTimer(Trecv) >> Idle; recv(Message,Net) -> CheckMsg{ MsgOK -> send(Message,Usr), send(Ack,Net), RestartTimer(Trecv); MsgBad -> send(NotAck,Net); }; macro EventDisc; [ SetupSend state - user has requested a connection to the network ] state SetupSend:: timeout(Tconn) | recv(Refuse,Net) -> send(Refuse,Usr), StopTimer(Tconn) >> Idle; recv(Accept,Net) -> send(Accept,Usr), StopTimer(Tconn), StartTimer(Tsend) >> Sending; macro EventDisc; [ Sending state - network has accepted user connection request, ] [ user to network transmission is in progress. ] state Sending:: timeout(Tsend) -> macro ActionError; recv(Disconnect,Usr) -> send(Disconnect,Net), StopTimer(Tsend) >> Idle; recv(Message,Usr) -> send(Message,Net), send(Ack,Usr), StopTimer(Tsend), RetryReset, StartTimer(Tack) >> WaitAck ; macro EventDisc; [ WaitAck state - user trans to network is waiting for acknowledgement ] [ from network with the condition of the last message received ] state WaitAck:: timeout(Tack) -> macro ActionError; recv(Ack,Net) -> StopTimer(Tack), StartTimer(Tsend) >> Sending; recv(NotAck,Net) -> Retry{ YesRetry -> ResendMessage, RestartTimer(Tack); NoRetry -> macro ActionError; }; macro EventDisc; [ EventDisc event macro - either side of the connection may disconnect, ] [ option is only available in connected states ] event macro EventDisc: recv(Disc,Net) -> send(Disc,Usr), StopAllTimers >> Idle; recv(Disc,Usr) -> send(Disc,Net), StopAllTimers >> Idle; [ ActionError action macro - an error simply aborts and disconnects ] action macro ActionError: send(Abort,Usr), send(Abort,Net), StopAllTimers >> Idle; }