AvrX Real Time Kernel |
|
|
|
Sept, 14, 2005 AvrXFifo requires AvrX 2.6f or newer. AvrX 2.6f contains some significant changes to support AvrXFifo's. The changes should be 100% backward compatible to previous 2.6 versions. The change primarily allows interrupt handler to directly call AvrXSetSemaphore(). Older versions of AvrX probably will work, but an extra context will be temporarily pushed onto the kernel stack during the Set Semaphore processing. Files: AvrXFifo.h AvrXFifo.c Notes on declaration & usage of AvrXFifo's: Maximum fifo size is 255 bytes. Most practical systems only need a few bytes to buffer interrupt handlers or protocols (e.g. large enough for the biggest outgoing packet, for example). If this limit is too small, see "Future Expansion" below. The user can declare and initialize Fifo's explicitly, in code, or use some handy #define MACROs to do the job. To do this manually follow this template: Outside of your code (e.g. global data memory) uint8_t SomeBuffer[sizeof(AvrXFifo) + DesiredBufferSize]; static const pAvrXFifo FifoName = (pAvrXFifo)SomeBuffer; Or use the macro AVRX_DECL_FIFO(FifoName, FifoSize); or if external extern uint8_t SomeBuffer[]; static const pAvrXFifo FifoName = (pAvrXFifo)SomeBuffer; Or use the macro AVRX_EXT_FIFO(FifoName); In your code, initialize the fifo: FifoName->size = DesiredBufferSize; AvrXFlushFifo(FifoName); Or use the macro AVRX_INIT_FIFO(FifoName); To use the fifo: int retc = AvrXPutFifo(FifoName, data); // Place data in fifo int foo = AvrXPullFifo(FifoName); // Get data from fifo AvrXWaitPutFifo(FifoName, data); // Place data in fifo, blocks if full int foo = AvrXWaitPullFifo(FifoName); // Get data from fifo, blocks if empty int size = AvrXPeekFifo(FifoName); // Check size of fifo AvrXFlushFifo(FifoName); // Flush fifo & release producer. int foo = AvrXDelFifo(FifoName); // Remove last item placed in fifo Return Values: FIFO_ERR (-1) fifo full or empty and operation can't be completed FIFO_OK (0) operation successful unsigned int Char data, zero extended for successful removal GCC produces poor AVR code when comparing to literal 0 (there are reasons why this is correct behavior). GCC generates good AVR code when testing results for < 0. e.g. if ((c = AvrXPullFifo(FifoName)) < FIFO_OK) // rather than == // empty... else // Do something with c which is unsigned char data. Because the pointer to the FIFO is declared as a static const value, no actual storage is used. The compiler is smart enough to just use the address of the byte buffer when referencing the FIFO. Optimal code is generated. Macros: Macros hide the need to record the size for initialization, otherwise are identical to the explicit C code. The only caveat is that the init macro needs to be in the same file as the declaration macro. This is also good programming practice to keep declarations and initialization in one file. Also the External declaration macro cannot be used in the same file as the declaration macro. Synchronization: AvrXFifo's have built in synchronization between producers (put) and consumers (pull). Producers and consumers can be either tasks or interrupt handlers. Interrupt handlers, of course, cannot use the blocking FIFO API. When blocked, the producer/consumer will be come unblocked when the opposite side either removes or adds a data item (respectively). There is no mechanism to notify an interrupt handler that something has been added or removed from a FIFO. See the example of the buffered serial I/O to see how that is handled. In short, it is up to the task end of the FIFO to manage coordination with the interrupt handler. Future expansion possibilities: By using the FIFO code as a starting point some interesting things could be added: 1. FIFO could be defined as word or even arbitrary data size buffers. The size could be declared as multiples of the data and new get/put routines defined that remove the data item. This could be made generic by adding the element size to the FIFO data structure, at the expense of more code for the simple case. 2. Routine addresses could be added to the FIFO structure as call-backs for interrupt handlers when the FIFO fills or empties. This would allow a FIFO to be put between two interrupt handlers. Say a serial to serial buffer. Simple tasks between handlers could be written as entirely kernel context code avoiding the overhead of task switching. 3. FIFO size could be expanded beyond 255 by changing the internal size, in and out from uint8_t to uint16_t and modifying the FIFO code appropriately (internal temporary values). Also, race conditions in loading pointers must be taken into account. With byte pointers CPU reads are atomic and no critical sections are needed. The current byte size data was chosen as a compromise between small system code and decent sized buffers. CAVEATS: The FIFO code has not been tested with a buffer size of 255. It should work. There might be race conditions Last update 16-Mar-2007 .
|