URL
https://opencores.org/ocsvn/rio/rio/trunk
Subversion Repositories rio
[/] [rio/] [tags/] [1.0.2-release/] [sw/] [stack/] [riostack.c] - Rev 51
Go to most recent revision | Compare with Previous | Blame | View Log
/******************************************************************************* * * RapidIO IP Library Core * * This file is part of the RapidIO IP library project * http://www.opencores.org/cores/rio/ * * Description: * This file contains a software implementation of a RapidIO stack according to * the 2.2 version, part 6, of the standard. Only short control symbols are * supported. * * Symbols are in four flavors, idle, control, data and error. They are abstract * and should be serialized by any implementation to be sent on a transmission * channel. Error symbols are never generated by the stack and are used if the * symbol decoder encounters an error that the stack should be notified of. * * Symbols are inserted into the stack from the lower-half by calling * RIOSTACK_portAddSymbol() and symbols to transmit are fetched from the stack * using RIOSTACK_portGetSymbol(). These two functions are the low-level interface * towards a physical transmission channel. * The function RIOSTACK_portSetStatus() is used to indicate to the stack that initial * training of the symbol codec has been completed and that the transmission port * is ready to accept other symbols than idle. The procedure is to set the port * status to initialized once idle symbols are successfully received. * * On the upper-half interface are the RIOSTACK_setOutboundPacket() function used to * insert packets into the outbound transmission queue and RIOSTACK_getInboundPacket() * is used to get packet from the inbound reception queue. The * RIOSTACK_getInboundQueueLength() function is used to check if any packet is available * for reading in the inbound reception queue. * * ----------------- * | OS dependent | * | (your code) | * ----------------- * | * ----------------- * | RioStack | * ----------------- * | * ----------------- * | Symbol Codec | * | (your code) | * ----------------- * | * ----------------- * | Port driver | * ----------------- * | * ----------------- * | Physical port | * ----------------- * * The symbol codec maps a RapidIO symbol to the physical transmission media. * * Some typical patterns to handle this stack are: * Initialization: * RIOSTACK_open(...); * RIOSTACK_portSetTimeout(...); * ... * <Symbol transcoder is successfully decoding symbols from the link> * RIOSTACK_portSetStatus(1); * * Bottom-half traffic handling: * RIOSTACK_portSetTime(...); * <get symbol from decoder> * RIOSTACK_portAddSymbol(...); * s = RIOSTACK_portGetSymbol(...); * <send symbol to encoder> * * Receiving packets: * if(RIOSTACK_getInboundQueueLength(...) > 0) * { * RIOSTACK_getInboundPacket(...); * <process the new packet> * } * * Transmitting packets: * <create a new packet> * if(RIOSTACK_getOutboundQueueAvailable(...) > 0) * { * RIOSTACK_setOutboundPacket(...); * } * * More details about the usage can be found in the module tests in test_riostack.c. * * To Do: * - Optimize the packing of stype0 and stype1 into control symbols. * * Author(s): * - Magnus Rosenius, magro732@opencores.org * ******************************************************************************* * * Copyright (C) 2015 Authors and OPENCORES.ORG * * This source file may be used and distributed without * restriction provided that this copyright statement is not * removed from the file and that any derivative work contains * the original copyright notice and the associated disclaimer. * * This source file is free software; you can redistribute it * and/or modify it under the terms of the GNU Lesser General * Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any * later version. * * This source is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General * Public License along with this source; if not, download it * from http://www.opencores.org/lgpl.shtml * *******************************************************************************/ /** * \file riostack.c */ /******************************************************************************* * Includes *******************************************************************************/ #include "riostack.h" /*lint -e961 Allow function like macros. */ /*lint -e621 Long identifier names allowed to increase readability. */ /*lint -w2 */ /*lint --estring(960,17.4) It is not possible to implement a rio stack without some * pointer arithmetic */ /******************************************************************************* * Local macro definitions *******************************************************************************/ /* Macro to update 5-bit ackId counters. */ #define ACKID_INC(ackId) (((ackId)+1)&0x1f) /* Macros to get entries from a control symbol. */ #define STYPE0_GET(data) ((uint8_t) (((data) >> 21) & 0x00000007u)) #define PARAMETER0_GET(data) ((uint8_t) (((data) >> 16) & 0x00000001fu)) #define PARAMETER1_GET(data) ((uint8_t) (((data) >> 11) & 0x00000001fu)) #define STYPE1_GET(data) ((uint8_t) (((data) >> 8) & 0x00000007u)) #define CMD_GET(data) ((uint8_t) (((data) >> 5) & 0x00000007u)) #define CRC5_GET(data) ((uint8_t) (((data) >> 0) & 0x0000001fu)) /* Macros to get entries from a packet in a buffer. */ #define FTYPE_GET(p) (((p)[0] >> 16) & 0xf) #define DESTID_GET(p) ((p)[0] & 0xffff) #define SRCID_GET(p) (((p)[1] >> 16) & 0xffff) #define TRANSACTION_GET(p) (((p)[1] >> 12) & 0xf) #define MSGLEN_GET(p) TRANSACTION_GET(p) #define SSIZE_GET(p) (((p)[1] >> 8) & 0xf) #define LETTER_GET(p) (((p)[1] >> 6) & 0x3) #define MBOX_GET(p) (((p)[1] >> 4) & 0x3) #define MSGSEG_GET(p) ((p)[1] & 0xf) #define XMBOX_GET(p) MSGSEG_GET(p) #define RDSIZE_GET(p) SSIZE_GET(p) #define WRSIZE_GET(p) SSIZE_GET(p) #define TID_GET(p) ((p)[1] & 0xff) #define HOP_GET(p) (((p)[2] >> 24) & 0xff) #define CONFIG_OFFSET_GET(p) ((p)[2] & 0x00fffffcul) #define INFO_GET(p) (((p)[2] >> 16) & 0xffff) #define ADDRESS_GET(p) ((p)[2] & 0xfffffff8ul) #define WDPTR_GET(p) (((p)[2] >> 2) & 0x1) #define XAMBS_GET(p) ((p)[2] & 0x3) #define DOUBLE_WORD_MSB_GET(p, i) (p)[3+(2*i+0)] #define DOUBLE_WORD_LSB_GET(p, i) (p)[3+(2*i+1)] /* Transmitter frame states. */ #define TX_FRAME_START ((uint8_t)0u) #define TX_FRAME_BODY ((uint8_t)1u) /* Control symbol constants. */ #define STYPE0_PACKET_ACCEPTED ((uint8_t)0x00u) #define STYPE0_PACKET_RETRY ((uint8_t)0x01u) #define STYPE0_PACKET_NOT_ACCEPTED ((uint8_t)0x02u) #define STYPE0_RESERVED ((uint8_t)0x03u) #define STYPE0_STATUS ((uint8_t)0x04u) #define STYPE0_VC_STATUS ((uint8_t)0x05u) #define STYPE0_LINK_RESPONSE ((uint8_t)0x06u) #define STYPE0_IMPLEMENTATION_DEFINED ((uint8_t)0x07u) #define STYPE1_START_OF_PACKET ((uint8_t)0x00u) #define STYPE1_STOMP ((uint8_t)0x01u) #define STYPE1_END_OF_PACKET ((uint8_t)0x02u) #define STYPE1_RESTART_FROM_RETRY ((uint8_t)0x03u) #define STYPE1_LINK_REQUEST ((uint8_t)0x04u) #define STYPE1_MULTICAST_EVENT ((uint8_t)0x05u) #define STYPE1_RESERVED ((uint8_t)0x06u) #define STYPE1_NOP ((uint8_t)0x07u) /* Packet ftype constants. */ #define FTYPE_REQUEST 0x2 #define FTYPE_WRITE 0x5 #define FTYPE_MAINTENANCE 0x8 #define FTYPE_DOORBELL 0xa #define FTYPE_MESSAGE 0xb #define FTYPE_RESPONSE 0xd /* Transaction constants. */ #define TRANSACTION_MAINT_READ_REQUEST 0 #define TRANSACTION_MAINT_WRITE_REQUEST 1 #define TRANSACTION_MAINT_READ_RESPONSE 2 #define TRANSACTION_MAINT_WRITE_RESPONSE 3 #define TRANSACTION_WRITE_NWRITE 4 #define TRANSACTION_WRITE_NWRITER 5 #define TRANSACTION_REQUEST_NREAD 4 #define TRANSACTION_RESPONSE_NO_PAYLOAD 0 #define TRANSACTION_RESPONSE_MESSAGE_RESPONSE 1 #define TRANSACTION_RESPONSE_WITH_PAYLOAD 8 /* Maintenance transaction lengths. */ #define MAINTENANCE_TRANSACTION_READ_REQUEST_SIZE ((uint32_t) 4ul) #define MAINTENANCE_TRANSACTION_WRITE_REQUEST_SIZE ((uint32_t) 6ul) #define MAINTENANCE_TRANSACTION_READ_RESPONSE_SIZE ((uint32_t) 6ul) #define MAINTENANCE_TRANSACTION_WRITE_RESPONSE_SIZE ((uint32_t) 4ul) /* Constants used to forward different errors to the link partner. */ #define PACKET_NOT_ACCEPTED_CAUSE_RESERVED 0u #define PACKET_NOT_ACCEPTED_CAUSE_UNEXPECTED_ACKID 1u #define PACKET_NOT_ACCEPTED_CAUSE_CONTROL_CRC 2u #define PACKET_NOT_ACCEPTED_CAUSE_NON_MAINTENANCE 3u #define PACKET_NOT_ACCEPTED_CAUSE_PACKET_CRC 4u #define PACKET_NOT_ACCEPTED_CAUSE_ILLEGAL_CHARACTER 5u #define PACKET_NOT_ACCEPTED_CAUSE_NO_RESOURCE 6u #define PACKET_NOT_ACCEPTED_CAUSE_DESCRAMBLER 7u #define PACKET_NOT_ACCEPTED_CAUSE_GENERAL 31u /* Constants used to request link-responses. */ #define LINK_REQUEST_RESET_DEVICE 3u #define LINK_REQUEST_INPUT_STATUS 4u /* Constants used to forward a port status in a link-resonse. */ #define LINK_RESPONSE_PORT_STATUS_ERROR 2u #define LINK_RESPONSE_PORT_STATUS_RETRY_STOPPED 4u #define LINK_RESPONSE_PORT_STATUS_ERROR_STOPPED 5u #define LINK_RESPONSE_PORT_STATUS_OK 16u /******************************************************************************* * Local typedefs *******************************************************************************/ /******************************************************************************* * Global declarations *******************************************************************************/ /******************************************************************************* * Local declarations *******************************************************************************/ /******************************************************************************* * Local function prototypes *******************************************************************************/ /* Helper functions for protocol events. */ static void handleStatus(RioStack_t *stack, const uint8_t ackId, const uint8_t bufferStatus); static void handlePacketAccepted(RioStack_t *stack, const uint8_t ackId, const uint8_t bufferStatus); static void handlePacketRetry(RioStack_t *stack, const uint8_t ackId, const uint8_t bufferStatus); static void handlePacketNotAccepted(RioStack_t *stack, const uint8_t arbitrary, const uint8_t cause); static void handleLinkResponse(RioStack_t *stack, const uint8_t ackId, const uint8_t portStatus); static void handleStartOfPacket(RioStack_t *stack); static void handleEndOfPacket(RioStack_t *stack); static void handleLinkRequest(RioStack_t *stack, uint8_t cmd); static void handleNewPacketStart(RioStack_t *stack); static void handleNewPacketEnd(RioStack_t *stack); /** * \brief Create a control symbol. * * \param[in] stype0 The stype0 value. * \param[in] parameter0 The parameter0 value. * \param[in] parameter1 The parameter1 value. * \param[in] stype1 The stype1 value. * \param[in] cmd The cmd value. * \return The control symbol that were created from the input parameters. * * This function creates a control symbol with the specified arguments and * calculates a CRC-5 checksum according to the standard specification. */ static RioSymbol_t CreateControlSymbol(const uint8_t stype0, const uint8_t parameter0, const uint8_t parameter1, const uint8_t stype1, const uint8_t cmd); /** * \brief Function to calculate ITU-CRC5, polynom=0x15. * * \param[in] data The data of a control symbol. * \param[in] crc The crc to initiate the result with. * \return A new CRC-5 value. */ static uint8_t Crc5(const uint32_t data, const uint8_t crc); /** * \brief Create a queue with a specified size and a buffer attached to it. * * \param[in] size The number of entries in the queue. * \param[in] buffer A pointer to the buffer to store the content in. * \return A queue with the specified size and where new data will be stored * to the specified buffer. */ static Queue_t QueueCreate(const uint8_t size, uint32_t *buffer); /** * \brief Get number of available elements. * * \param[in] q The queue to operate on. * \return The number of free packet buffers in the queue. */ static uint8_t QueueAvailable(const Queue_t q ); /** * \brief Get if the queue is empty or not. * * \param[in] q The queue to operate on. * \return Non-zero if the queue is empty. */ static int QueueEmpty(const Queue_t q); /** * \brief Get the length of a queue. * * \param[in] q The queue to operate on. * \return The number of elements in the queue. */ static uint8_t QueueLength(const Queue_t q); /** * \brief Add a new element to the queue. * * \param[in] q The queue to operate on. * \return A queue with one added element. */ static Queue_t QueueEnqueue(Queue_t q); /** * \brief Remove an element from the queue. * * \param[in] q The queue to operate on. * \return A queue with on removed element. */ static Queue_t QueueDequeue(Queue_t q); /** * \brief Check if the readout window is empty. * * \param[in] q The queue to operate on. * \return If the readout window is empty. */ static int QueueWindowEmpty(const Queue_t q); /** * \brief Reset the window to none. * * \param[in] q The queue to operate on. * \return The updated Queue_t structure. */ static Queue_t QueueWindowReset(Queue_t q); /** * \brief Increase the window to the next pending element. * * \param[in] q The queue to operate on. * \return The updated Queue_t structure. */ static Queue_t QueueWindowNext(Queue_t q); /** * \brief Set actual size of the newest element. * * \param[in] q The queue to operate on. * \param[in] size The size to set the newest content size to. */ static void QueueSetSize(Queue_t q, const uint32_t size); /** * \brief Set content at a specified index in the newest element. * * \param[in] q The queue to operate on. * \param[in] index posititon into the element * \param[in] content The content to set at the specified index in the newest queue element. */ static void QueueSetContent(Queue_t q, const uint32_t index, const uint32_t content); /** * \brief Get a pointer to the buffer of the newest element. * * \param[in] q The queue to operate on. * \return A pointer to the content. */ static uint32_t *QueueGetBackBuffer(Queue_t q ); /** * \brief Get the size of the oldest element. * \param[in] q The queue to operate on. * \return The size of the element. */ static uint32_t QueueGetSize(Queue_t q ); /** * \brief Get the content of the oldest element at specified index. * \param[in] q The queue to operate on. * \param[in] index The index into the element to get the content from. * \return content of element at index position. */ static uint32_t QueueGetFrontContent(Queue_t q, const uint32_t index); /** * \brief Get a pointer to the buffer of the oldest element. * * \param[in] q The queue to operate on. * \return A pointer to the content. */ static uint32_t *QueueGetFrontBuffer(Queue_t q ); /******************************************************************************* * Global functions *******************************************************************************/ void RIOSTACK_open(RioStack_t *stack, void *private, const uint32_t rxPacketBufferSize, uint32_t *rxPacketBuffer, const uint32_t txPacketBufferSize, uint32_t *txPacketBuffer) { /* Port time and timeout limit. */ stack->portTime = 0u; stack->portTimeout = 1000u; /* Setup the receiver. */ stack->rxState = RX_STATE_UNINITIALIZED; stack->rxCounter = 0u; stack->rxCrc = 0xffffu; stack->rxStatusReceived = 0u; stack->rxAckId = 0u; stack->rxAckIdAcked = 0u; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_RESERVED; stack->rxQueue = QueueCreate((uint8_t) (rxPacketBufferSize/RIOSTACK_BUFFER_SIZE), rxPacketBuffer); /* Setup the transmitter. */ stack->txState = TX_STATE_UNINITIALIZED; stack->txCounter = 0u; stack->txStatusCounter = 0u; stack->txFrameState = TX_FRAME_START; stack->txAckId = 0u; stack->txAckIdWindow = 0u; stack->txQueue = QueueCreate((uint8_t) (txPacketBufferSize/RIOSTACK_BUFFER_SIZE), txPacketBuffer); /* Setup status counters for inbound direction. */ stack->statusInboundPacketComplete = 0ul; stack->statusInboundPacketRetry = 0ul; stack->statusInboundErrorControlCrc = 0ul; stack->statusInboundErrorPacketAckId = 0ul; stack->statusInboundErrorPacketCrc = 0ul; stack->statusInboundErrorIllegalCharacter = 0ul; stack->statusInboundErrorGeneral = 0ul; stack->statusInboundErrorPacketUnsupported = 0ul; /* Setup status counters for outbound direction. */ stack->statusOutboundPacketComplete = 0ul; stack->statusOutboundLinkLatencyMax = 0ul; stack->statusOutboundPacketRetry = 0ul; stack->statusOutboundErrorTimeout = 0ul; stack->statusOutboundErrorPacketAccepted = 0ul; stack->statusOutboundErrorPacketRetry = 0ul; /* Setup status counters for potential problems on the link-partner. */ stack->statusPartnerLinkRequest = 0ul; stack->statusPartnerErrorControlCrc = 0ul; stack->statusPartnerErrorPacketAckId = 0ul; stack->statusPartnerErrorPacketCrc = 0ul; stack->statusPartnerErrorIllegalCharacter = 0ul; stack->statusPartnerErrorGeneral = 0ul; /* Set pointer to user private data. */ stack->private = private; } /******************************************************************************************* * Stack status and queue access functions. * Note that status counters are accessed directly in the stack-structure. *******************************************************************************************/ int RIOSTACK_getStatus(RioStack_t *stack) { return !(((stack->rxState == RX_STATE_UNINITIALIZED) || (stack->rxState == RX_STATE_PORT_INITIALIZED)) && ((stack->txState == TX_STATE_UNINITIALIZED) || (stack->txState == TX_STATE_PORT_INITIALIZED))); } void RIOSTACK_clearOutboundQueue(RioStack_t *stack) { while(!QueueEmpty(stack->txQueue)) { stack->txQueue = QueueDequeue(stack->txQueue); } } uint8_t RIOSTACK_getOutboundQueueLength(RioStack_t *stack) { return QueueLength(stack->txQueue); } uint8_t RIOSTACK_getOutboundQueueAvailable(RioStack_t *stack) { return QueueAvailable(stack->txQueue); } void RIOSTACK_setOutboundPacket(RioStack_t *stack, RioPacket_t *packet) { uint32_t *src, *dst; uint32_t size; uint32_t i; ASSERT((QueueAvailable(stack->txQueue) > 0u), "Transmission queue packet overflow."); src = &packet->payload[0]; dst = QueueGetBackBuffer(stack->txQueue); size = packet->size; for(i = 0; i < size; i++) { dst[i] = src[i]; } QueueSetSize(stack->txQueue, size); stack->txQueue = QueueEnqueue(stack->txQueue); } void RIOSTACK_clearInboundQueue(RioStack_t *stack) { while(!QueueEmpty(stack->rxQueue)) { stack->rxQueue = QueueDequeue(stack->rxQueue); } } uint8_t RIOSTACK_getInboundQueueLength(RioStack_t *stack) { return QueueLength(stack->rxQueue); } uint8_t RIOSTACK_getInboundQueueAvailable(RioStack_t *stack) { return QueueAvailable(stack->rxQueue); } void RIOSTACK_getInboundPacket(RioStack_t *stack, RioPacket_t *packet) { uint32_t *src, *dst; uint32_t size; uint32_t i; ASSERT(!QueueEmpty(stack->rxQueue), "Reading from empty reception queue."); src = QueueGetFrontBuffer(stack->rxQueue); dst = &packet->payload[0]; size = QueueGetSize(stack->rxQueue); for(i = 0; i < size; i++) { dst[i] = src[i]; } packet->size = size; stack->rxQueue = QueueDequeue(stack->rxQueue); } /******************************************************************************************* * Packet port functions. *******************************************************************************************/ void RIOSTACK_portSetTime(RioStack_t *stack, const uint32_t time) { stack->portTime = time; } void RIOSTACK_portSetTimeout(RioStack_t *stack, const uint32_t time) { stack->portTimeout = time; } void RIOSTACK_portSetStatus(RioStack_t *stack, const uint8_t initialized) { /* REMARK: Clean the queues here as well??? */ if (initialized) { stack->rxState = RX_STATE_PORT_INITIALIZED; stack->rxCounter = 0u; stack->rxCrc = 0xffffu; stack->rxStatusReceived = 0; stack->rxAckId = 0u; stack->rxAckIdAcked = 0u; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_RESERVED; stack->txState = TX_STATE_PORT_INITIALIZED; stack->txCounter = 0u; stack->txStatusCounter = 0u; stack->txFrameState = TX_FRAME_START; stack->txAckId = 0u; stack->txAckIdWindow = 0u; } else { stack->rxState = RX_STATE_UNINITIALIZED; stack->txState = TX_STATE_UNINITIALIZED; } } void RIOSTACK_portAddSymbol(RioStack_t *stack, const RioSymbol_t s) { uint8_t stype0; uint8_t parameter0; uint8_t parameter1; uint8_t stype1; switch(stack->rxState) { case RX_STATE_PORT_INITIALIZED: /****************************************************************************** * PORT_INITIALIZED * This state is entered to initialize the link. Only status-control-symbols * are accepted in this state. When 7 error-free, i.e. with CRC5 correct, status * control symbols has been received, change state to linkInitialized. ******************************************************************************/ /* Check the type of symbol. */ if(s.type == RIOSTACK_SYMBOL_TYPE_CONTROL) { /* This is a control symbol. */ /* Check that the control symbol contains no errors. */ if(Crc5(s.data, 0x1fu) == (s.data & 0x1ful)) { /* Error-free control symbol. */ /* Check if the symbol is a status symbol. */ stype0 = STYPE0_GET(s.data); if(stype0 == STYPE0_STATUS) { /* Status symbol received. */ /* Indicate an error-free status has been received. */ stack->rxStatusReceived = 1; /* Check if enough status control symbols has been received. */ if(stack->rxCounter == 7u) { /* Enough correct status control symbols has been received without errors in between. */ /* Setup the transmitter with the content of the symbol. */ stack->txAckId = PARAMETER0_GET(s.data); stack->txAckIdWindow = stack->txAckId; stack->txBufferStatus = PARAMETER1_GET(s.data); /* Set the transmitter in its normal operational mode. */ stack->rxState = RX_STATE_LINK_INITIALIZED; stack->rxCounter = 0u; } else { /* Count the number of consequitive error-free status control symbols that has been received. */ stack->rxCounter++; } } else { /* The received symbol is not a status symbol. */ /* Discard it. */ } } else { /* CRC error in control symbol. */ /* Restart counting error-free status-control-symbols. */ stack->rxCounter = 0u; } } else { /* Not a control symbol. */ /* Discard the symbol. */ } break; case RX_STATE_LINK_INITIALIZED: /****************************************************************************** * LINK_INITIALIZED * The normal state. Accept packets and forward them. ******************************************************************************/ /* Check the type of symbol. */ switch(s.type) { case RIOSTACK_SYMBOL_TYPE_CONTROL: /************************************************************************** * This is a control symbol. **************************************************************************/ /* Check if the CRC is correct. */ if(Crc5(s.data, 0x1fu) == (s.data & (uint8_t)0x1ful)) { /* The CRC is correct. */ /* Get the content of the control symbol. */ stype0 = STYPE0_GET(s.data); parameter0 = PARAMETER0_GET(s.data); parameter1 = PARAMETER1_GET(s.data); stype1 = STYPE1_GET(s.data); /********************************************************************************** * Check the stype0 part of the symbol. * Note that errors in this should trigger OUTPUT_ERROR_STOPPED. **********************************************************************************/ switch(stype0) { case STYPE0_STATUS: /* A status containing the current ackId and the buffer status has been received. */ handleStatus(stack, parameter0, parameter1); break; case STYPE0_PACKET_ACCEPTED: /* A packet has been accepted by the link partner. */ handlePacketAccepted(stack, parameter0, parameter1); break; case STYPE0_PACKET_RETRY: /* The link partner wants us to initiate a restart of the received ackId. */ handlePacketRetry(stack, parameter0, parameter1); break; case STYPE0_PACKET_NOT_ACCEPTED: /* The link partner indicates that a packet has been rejected. */ handlePacketNotAccepted(stack, parameter0, parameter1); break; case STYPE0_LINK_RESPONSE: /* The link partner has sent a response to a link-request. */ handleLinkResponse(stack, parameter0, parameter1); break; case STYPE0_VC_STATUS: case STYPE0_RESERVED: case STYPE0_IMPLEMENTATION_DEFINED: default: /* Unsupported symbol received. */ /* Discard them. */ break; } /********************************************************************************** * Check the stype1 part of the symbol. * Note that errors in this should trigger INPUT_ERROR_STOPPED. **********************************************************************************/ switch(stype1) { case STYPE1_START_OF_PACKET: /* Start of a new packet. */ handleStartOfPacket(stack); break; case STYPE1_END_OF_PACKET: /* Ending a packet. */ handleEndOfPacket(stack); break; case STYPE1_STOMP: /* Cancel the currently received frame. */ stack->rxCounter = 0; break; case STYPE1_RESTART_FROM_RETRY: /* Cancel the currently received frame when in this state. */ stack->rxCounter = 0; break; case STYPE1_LINK_REQUEST: /* A link-request has been received. */ handleLinkRequest(stack, CMD_GET(s.data)); break; case STYPE1_NOP: /* No operation symbol. */ /* Discard these. */ break; case STYPE1_MULTICAST_EVENT: case STYPE1_RESERVED: default: /* Unsupported symbol received. */ /* Discard them. */ break; } } else { /* The control symbol CRC is incorrect. */ /* Corrupted control symbol. Discard the symbol and enter the input-error-stopped state. */ stack->txState = TX_STATE_SEND_PACKET_NOT_ACCEPTED; stack->rxState = RX_STATE_INPUT_ERROR_STOPPED; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_CONTROL_CRC; stack->statusInboundErrorControlCrc++; } break; case RIOSTACK_SYMBOL_TYPE_DATA: /************************************************************************** * This is a data symbol. **************************************************************************/ /* Check if a packet has been started and that it is not too long. */ if ((stack->rxCounter >= 1u) && (stack->rxCounter <= RIOPACKET_SIZE_MAX)) { /* A packet has been started. */ /* Check if the ackId is correct on the first part of the packet. */ if ((stack->rxCounter > 1u) || ((stack->rxCounter == 1u) && (((uint8_t)(s.data >> 27)) == stack->rxAckId))) { /* The ackId is the expected one. */ /* Check if this is the first symbol of a packet. */ if (stack->rxCounter == 1u) { /* This is the first symbol of the packet. */ /* Start to calculate the CRC of the packet. */ /* Note that the ackId should not be included in the CRC calculation. */ stack->rxCrc = RIOPACKET_Crc32(s.data & (uint32_t)0x03fffffful, 0xffffu); } else { /* This is not the first symbol. */ /* Continue to calculate the CRC of the packet. */ stack->rxCrc = RIOPACKET_Crc32(s.data, stack->rxCrc); } /* Save the new data in the packet queue and update the reception counter. */ QueueSetContent(stack->rxQueue, (uint32_t)stack->rxCounter - (uint32_t)1ul, s.data); stack->rxCounter++; } else { /* The ackId is not correct. */ /* Packet error. Enter input-error-stopped state. */ stack->txState = TX_STATE_SEND_PACKET_NOT_ACCEPTED; stack->rxState = RX_STATE_INPUT_ERROR_STOPPED; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_UNEXPECTED_ACKID; stack->statusInboundErrorPacketAckId++; } } else { /* No packet has been started or the packet is too long. */ /* Packet error. Enter input-error-stopped state. */ stack->txState = TX_STATE_SEND_PACKET_NOT_ACCEPTED; stack->rxState = RX_STATE_INPUT_ERROR_STOPPED; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_GENERAL; stack->statusInboundErrorGeneral++; } break; case RIOSTACK_SYMBOL_TYPE_ERROR: /************************************************************************** * The decoder has received a erronous symbol. **************************************************************************/ /* Idle symbol error. Place the receiver in input-error-stopped state. */ stack->txState = TX_STATE_SEND_PACKET_NOT_ACCEPTED; stack->rxState = RX_STATE_INPUT_ERROR_STOPPED; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_ILLEGAL_CHARACTER; stack->statusInboundErrorIllegalCharacter++; break; case RIOSTACK_SYMBOL_TYPE_IDLE: default: /************************************************************************** * Idle symbol or unsupported symbol. **************************************************************************/ /* Discard these for now. */ break; } break; case RX_STATE_INPUT_RETRY_STOPPED: /****************************************************************************** * INPUT_RETRY_STOPPED * This state is entered when no more buffers was available and a packet was * received. When in this state, all packets should be discarded until a * RESTART-FROM-RETRY symbol is received. See section 5.9.1.4 of the standard. * Note that it is only the input side of the port that are affected, not the * output side. Packets may still be transmitted and acknowledges should be * accepted. ******************************************************************************/ /* Check the type of symbol. */ switch(s.type) { case RIOSTACK_SYMBOL_TYPE_CONTROL: /* This is a control symbol. */ /* Check if the CRC is correct. */ if(Crc5(s.data, 0x1fu) == (s.data & (uint8_t)0x1ful)) { /* The CRC is correct. */ /* Get the content of the control symbol. */ stype0 = STYPE0_GET(s.data); parameter0 = PARAMETER0_GET(s.data); parameter1 = PARAMETER1_GET(s.data); stype1 = STYPE1_GET(s.data); /* Check the stype0 part of the symbol. */ switch(stype0) { case STYPE0_STATUS: /* A status containing the current ackId and the buffer status has been received. */ handleStatus(stack, parameter0, parameter1); break; case STYPE0_PACKET_ACCEPTED: /* A packet has been accepted by the link partner. */ handlePacketAccepted(stack, parameter0, parameter1); break; case STYPE0_PACKET_RETRY: /* The link partner wants us to initiate a restart of the received ackId. */ handlePacketRetry(stack, parameter0, parameter1); break; case STYPE0_PACKET_NOT_ACCEPTED: /* The link partner indicates that a packet has been rejected. */ handlePacketNotAccepted(stack, parameter0, parameter1); break; case STYPE0_LINK_RESPONSE: /* The link partner has sent a response to a link-request. */ handleLinkResponse(stack, parameter0, parameter1); break; case STYPE0_VC_STATUS: case STYPE0_RESERVED: case STYPE0_IMPLEMENTATION_DEFINED: default: /* Unsupported symbol received. */ /* Discard them. */ break; } /* Check the stype1 part of the symbol. */ switch(stype1) { case STYPE1_START_OF_PACKET: /* Starting new frames are ignored in this state. */ break; case STYPE1_END_OF_PACKET: /* Ending new frames are ignored in this state. */ break; case STYPE1_STOMP: /* Restarting frames are ignored in this state. */ break; case STYPE1_RESTART_FROM_RETRY: /* The link partner has confirmed our packet-retry-symbol. */ /* Go back to the normal state and reset the frame reception. */ stack->rxState = RX_STATE_LINK_INITIALIZED; stack->rxCounter = 0u; break; case STYPE1_LINK_REQUEST: /* A link-request has been received. */ handleLinkRequest(stack, CMD_GET(s.data)); stack->rxState = RX_STATE_LINK_INITIALIZED; break; case STYPE1_NOP: /* No operation symbol. */ /* Discard these. */ break; case STYPE1_MULTICAST_EVENT: case STYPE1_RESERVED: default: /* Unsupported symbol received. */ /* Discard them. */ break; } } else { /* The control symbol CRC is incorrect. */ /* Corrupted control symbol. Discard the symbol and enter the input-error-stopped state. */ stack->txState = TX_STATE_SEND_PACKET_NOT_ACCEPTED; stack->rxState = RX_STATE_INPUT_ERROR_STOPPED; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_CONTROL_CRC; stack->statusInboundErrorControlCrc++; } break; case RIOSTACK_SYMBOL_TYPE_ERROR: /* Idle symbol error. Place the receiver in input-error-stopped state. */ stack->txState = TX_STATE_SEND_PACKET_NOT_ACCEPTED; stack->rxState = RX_STATE_INPUT_ERROR_STOPPED; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_ILLEGAL_CHARACTER; stack->statusInboundErrorIllegalCharacter++; break; case RIOSTACK_SYMBOL_TYPE_DATA: case RIOSTACK_SYMBOL_TYPE_IDLE: default: /* Data or idle symbol. */ /* Discard these in this state. */ break; } break; case RX_STATE_INPUT_ERROR_STOPPED: /****************************************************************************** * INPUT_ERROR_STOPPED * This state is entered when an error situation has occurred. When in this * state, all symbols should be discarded until a link-request-symbols has * been received. See section 5.13.2.6 in part 6 of the standard. * Note that it is only the input side of the port that are affected, not the * output side. Packets may still be transmitted and acknowledges should be * accepted. ******************************************************************************/ /* Check the type of symbol. */ switch(s.type) { case RIOSTACK_SYMBOL_TYPE_CONTROL: /* This is a control symbol. */ /* Check if the CRC is correct. */ if(Crc5(s.data, 0x1fu) == (s.data & (uint8_t)0x1ful)) { /* The CRC is correct. */ /* Get the content of the control symbol. */ stype0 = STYPE0_GET(s.data); parameter0 = PARAMETER0_GET(s.data); parameter1 = PARAMETER1_GET(s.data); stype1 = STYPE1_GET(s.data); /* Check the stype0 part of the symbol. */ switch(stype0) { case STYPE0_STATUS: /* A status containing the current ackId and the buffer status has been received. */ handleStatus(stack, parameter0, parameter1); break; case STYPE0_PACKET_ACCEPTED: /* A packet has been accepted by the link partner. */ handlePacketAccepted(stack, parameter0, parameter1); break; case STYPE0_PACKET_RETRY: /* The link partner wants us to initiate a restart of the received ackId. */ handlePacketRetry(stack, parameter0, parameter1); break; case STYPE0_PACKET_NOT_ACCEPTED: /* The link partner indicates that a packet has been rejected. */ handlePacketNotAccepted(stack, parameter0, parameter1); break; case STYPE0_LINK_RESPONSE: /* The link partner has sent a response to a link-request. */ handleLinkResponse(stack, parameter0, parameter1); break; case STYPE0_VC_STATUS: case STYPE0_RESERVED: case STYPE0_IMPLEMENTATION_DEFINED: default: /* Unsupported symbol received. */ /* Discard them. */ break; } /* Check the stype1 part of the symbol. */ switch(stype1) { case STYPE1_START_OF_PACKET: /* Starting new frames are ignored in this state. */ break; case STYPE1_END_OF_PACKET: /* Ending new frames are ignored in this state. */ break; case STYPE1_STOMP: /* Restarting frames are ignored in this state. */ break; case STYPE1_RESTART_FROM_RETRY: /* Restart-from-retry are ignored in this state. */ break; case STYPE1_LINK_REQUEST: /* This is the symbol we have been waiting for. */ /* Force the transmitter to send a link-response and go back into the normal operational state. */ /* The transmitter will always send a status as the first symbol after this. */ handleLinkRequest(stack, CMD_GET(s.data)); stack->rxState = RX_STATE_LINK_INITIALIZED; break; case STYPE1_NOP: /* No operation symbol. */ /* Discard these. */ break; case STYPE1_MULTICAST_EVENT: case STYPE1_RESERVED: default: /* Unsupported symbol received. */ /* Discard them. */ break; } } else { /* The CRC is incorrect. */ /* Discard these in this state. */ } break; case RIOSTACK_SYMBOL_TYPE_DATA: case RIOSTACK_SYMBOL_TYPE_IDLE: case RIOSTACK_SYMBOL_TYPE_ERROR: default: /* Data, idle or error symbol. */ /* Discard these in this state. */ break; } break; case RX_STATE_UNINITIALIZED: default: /****************************************************************************** * Wait for the port to be initialized. ******************************************************************************/ /* Discard all incoming symbols. */ break; } } RioSymbol_t RIOSTACK_portGetSymbol(RioStack_t *stack ) { RioSymbol_t s; switch(stack->txState) { case TX_STATE_PORT_INITIALIZED: /****************************************************************************** * PORT_INITIALIZED * This state is entered to initialize the link. Send status-control-symbols * once in a while until the receiver has received enough error-free status- * control-symbols and we have transmitted enough status-control-symbols. Once * an error-free status-control-symbol has been received, the statuses are * transmitted more frequently to decrease the time for the link to be * initialized. ******************************************************************************/ /* Check if an idle symbol or a status control symbol should be sent. */ if(((stack->rxStatusReceived == 0) && (stack->txCounter == 255u)) || ((stack->rxStatusReceived == 1) && (stack->txCounter >= 15u))) { /* A control symbol should be sent. */ /* Create a new status symbol and reset the transmission counter. */ stack->txCounter = 0u; s = CreateControlSymbol(STYPE0_STATUS, stack->rxAckId, QueueAvailable(stack->rxQueue), STYPE1_NOP, 0u); /* Check if the receiver has received any error-free status and that we have sent at least 15 status control symbols. */ if((stack->rxStatusReceived == 1) && (stack->txStatusCounter < 15u)) { /* Has not sent enough status control symbols. */ stack->txStatusCounter++; } else { /* Has sent enough status control symbols. */ /* Dont do anything. */ } } else { /* Idle symbol should be sent. */ s.type = RIOSTACK_SYMBOL_TYPE_IDLE; stack->txCounter++; } /* Check if we are ready to set the transmitter in a link initialized state. */ if ((stack->rxState == RX_STATE_LINK_INITIALIZED) && (stack->txStatusCounter == 15u)) { /* Ready to go to link initialized. */ stack->txState = TX_STATE_LINK_INITIALIZED; stack->txFrameState = TX_FRAME_START; stack->txStatusCounter = 0u; } else { /* Not ready to go to link initialized. */ /* Dont do anything. */ } break; case TX_STATE_LINK_INITIALIZED: /****************************************************************************** * LINK_INITIALIZED * The normal state. Accept packets and forward them. Send acknowledges when * the receiver has received complete packets. ******************************************************************************/ /* Check if the receiver wants to acknowledge a packet. */ if(stack->rxAckId == stack->rxAckIdAcked) { /* The receiver does not want to acknowledge a packet. */ /* Check if there are any outstanding packets and if it has timed out. */ if((stack->txAckId == stack->txAckIdWindow) || ((stack->portTime - stack->txFrameTimeout[stack->txAckId]) < stack->portTimeout)) { /* There are no outstanding packets or there has been no timeout. */ /* Check if a packet is ongoing. */ if(stack->txFrameState == TX_FRAME_BODY) { /* A packet transmission is ongoing. */ /* Check if the packet has been completly sent. */ if(stack->txCounter != QueueGetSize(stack->txQueue)) { /* The packet has not been completly sent. */ /* Create a new data symbol to transmit. */ s.type = RIOSTACK_SYMBOL_TYPE_DATA; s.data = QueueGetFrontContent(stack->txQueue, (uint32_t)stack->txCounter); /* Check if this is the first symbol in a packet. */ if (stack->txCounter == 0u) { /* Place the correct ackId in the right place. */ s.data |= (uint32_t)stack->txAckIdWindow << 27; } else { /* Dont do anything. */ } /* Update the transmission counter. */ stack->txCounter++; /* A status control symbol was not sent. Update the status counter. */ stack->txStatusCounter++; } else { /* The packet has been sent. */ /* Save the timeout time and update to the next ackId. */ stack->txFrameTimeout[stack->txAckIdWindow] = stack->portTime; stack->txAckIdWindow = ACKID_INC(stack->txAckIdWindow); stack->txQueue = QueueWindowNext(stack->txQueue); /* Check if there are more packets pending to be sent. */ /* Also check that there are buffer available at the receiver and that not too many packets are outstanding. */ if(!QueueWindowEmpty(stack->txQueue) && (stack->txBufferStatus > 0) && (((stack->txAckIdWindow - stack->txAckId) & 0x1f) != 31)) { /* More pending packets. */ /* Create a control symbol to signal that the new packet has started. */ s = CreateControlSymbol(STYPE0_STATUS, stack->rxAckId, QueueAvailable(stack->rxQueue), STYPE1_START_OF_PACKET, 0u); /* Restart transmission counter. */ stack->txCounter = 0; } else { /* No more pending packets. */ /* Create a control symbol to signal that the packet has ended. */ s = CreateControlSymbol(STYPE0_STATUS, stack->rxAckId, QueueAvailable(stack->rxQueue), STYPE1_END_OF_PACKET, 0u); /* Go back to wait for a new frame. */ stack->txFrameState = TX_FRAME_START; } /* A status control symbol has been sent. Reset the status counter. */ stack->txStatusCounter = 0u; } } else { /* No packet is being sent. */ /* Check if there are any pending packets to start sending. */ /* Also check that there are buffer available at the receiver and that not too many packets are outstanding. */ if(!QueueWindowEmpty(stack->txQueue) && (stack->txBufferStatus > 0) && (((stack->txAckIdWindow - stack->txAckId) & 0x1f) != 31)) { /* There is a pending packet to send. */ /* Send a start-of-packet control symbol to start to send the packet. */ s = CreateControlSymbol(STYPE0_STATUS, stack->rxAckId, QueueAvailable(stack->rxQueue), STYPE1_START_OF_PACKET, 0u); stack->txFrameState = TX_FRAME_BODY; stack->txCounter = 0u; /* A status control symbol has been sent. Reset the status counter. */ stack->txStatusCounter = 0u; } else { /* There are no pending packets to send. */ /* Check if a status control symbol must be transmitted. */ if(stack->txStatusCounter < 255u) { /* Not required to send a status control symbol. */ /* Send an idle-symbol. */ s.type = RIOSTACK_SYMBOL_TYPE_IDLE; stack->txStatusCounter++; } else { /* Must send a status control symbol. */ /* Create a status control symbol. */ s = CreateControlSymbol(STYPE0_STATUS, stack->rxAckId, QueueAvailable(stack->rxQueue), STYPE1_NOP, 0u); /* A status control symbol has been sent. Reset the status counter. */ stack->txStatusCounter = 0u; } } } } else { /* There has been a timeout. */ /* A packet has been sent but no packet-accepted has been received. */ /* Send link-request-symbol (input-status). */ s = CreateControlSymbol(STYPE0_STATUS, stack->rxAckId, QueueAvailable(stack->rxQueue), STYPE1_LINK_REQUEST, LINK_REQUEST_INPUT_STATUS); /* Save the time when this was transmitted. */ stack->txFrameTimeout[stack->txAckId] = stack->portTime; /* Remember that this symbol has been transmitted. */ stack->txCounter = 1; /* Go into the output error stopped state. */ stack->txState = TX_STATE_OUTPUT_ERROR_STOPPED; stack->statusOutboundErrorTimeout++; } } else { /* The receiver wants us to send an acknowledgement. */ s = CreateControlSymbol(STYPE0_PACKET_ACCEPTED, stack->rxAckIdAcked, QueueAvailable(stack->rxQueue), STYPE1_NOP, 0u); stack->rxAckIdAcked = ACKID_INC(stack->rxAckIdAcked); /* A status control symbol was not sent. Update the status counter. */ stack->txStatusCounter++; } break; case TX_STATE_SEND_PACKET_RETRY: /****************************************************************************** * SEND_PACKET_RETRY * This state is set by the receiver to force a packet-retry-symbol to be * transmitted. ******************************************************************************/ /* Check if the receiver wants to acknowledge a packet. */ /* This must be done first or we will get an error for a missmatching ackId in the link-partner. */ if(stack->rxAckId == stack->rxAckIdAcked) { /* No pending acknowledge. */ /* Send a packet-retry symbol to tell the link partner to retry the last frame. */ s = CreateControlSymbol(STYPE0_PACKET_RETRY, stack->rxAckId, QueueAvailable(stack->rxQueue), STYPE1_NOP, 0u); /* Proceed with normal transmission. */ stack->txState = TX_STATE_LINK_INITIALIZED; /* A status control symbol was not sent. Update the status counter. */ stack->txStatusCounter++; } else { /* The receiver wants us to send an acknowledgement. */ s = CreateControlSymbol(STYPE0_PACKET_ACCEPTED, stack->rxAckIdAcked, QueueAvailable(stack->rxQueue), STYPE1_NOP, 0u); stack->rxAckIdAcked = ACKID_INC(stack->rxAckIdAcked); /* A status control symbol was not sent. Update the status counter. */ stack->txStatusCounter++; } break; case TX_STATE_SEND_PACKET_NOT_ACCEPTED: /****************************************************************************** * SEND_PACKET_NOT_ACCEPTED * This state is set by the receiver to force a packet-not-accepted-symbol to be * transmitted. ******************************************************************************/ /* Send a packet-not-accepted symbol to indicate an error on the link. */ s = CreateControlSymbol(STYPE0_PACKET_NOT_ACCEPTED, 0, stack->rxErrorCause, STYPE1_NOP, 0u); /* Proceed with normal transmission. */ stack->txState = TX_STATE_LINK_INITIALIZED; /* A status control symbol was not sent. Update the status counter. */ stack->txStatusCounter++; break; case TX_STATE_SEND_LINK_RESPONSE: /****************************************************************************** * SEND_LINK_RESPONSE * This state is set by the receiver to force a link-response-symbol to be * transmitted. ******************************************************************************/ /* Check the state of the receiver. */ /* REMARK: If a link-request gives this response and a link-request also makes the receiver enter the normal operational state, none of these states except the normal state will ever be used... */ if((stack->rxState == RX_STATE_LINK_INITIALIZED) || (stack->rxState == RX_STATE_PORT_INITIALIZED)) { /* Normal state. */ s = CreateControlSymbol(STYPE0_LINK_RESPONSE, stack->rxAckId, LINK_RESPONSE_PORT_STATUS_OK, STYPE1_NOP, 0u); } else if(stack->rxState == RX_STATE_INPUT_RETRY_STOPPED) { /* Input-retry-stopped state. */ s = CreateControlSymbol(STYPE0_LINK_RESPONSE, stack->rxAckId, LINK_RESPONSE_PORT_STATUS_RETRY_STOPPED, STYPE1_NOP, 0u); } else if(stack->rxState == RX_STATE_INPUT_ERROR_STOPPED) { /* Input-error-stopped state. */ s = CreateControlSymbol(STYPE0_LINK_RESPONSE, stack->rxAckId, LINK_RESPONSE_PORT_STATUS_ERROR_STOPPED, STYPE1_NOP, 0u); } else { /* Not in the defined states. */ s = CreateControlSymbol(STYPE0_LINK_RESPONSE, stack->rxAckId, LINK_RESPONSE_PORT_STATUS_ERROR, STYPE1_NOP, 0u); } /* Proceed with normal transmission. */ stack->txState = TX_STATE_LINK_INITIALIZED; /* Force a status to be transmitted the next time to comply to the input-error-stopped state rules. */ stack->txStatusCounter = 255; break; case TX_STATE_OUTPUT_RETRY_STOPPED: /****************************************************************************** * OUTPUT_RETRY_STOPPED * This state is entered when the link-partner has transmitted a * packet-retry-symbol. The packet-retry-symbol is acknowledged by sending a * restart-from-retry-symbol. * This state follows 5.9.1.5 in part 6. ******************************************************************************/ /* Send a restart-from-retry symbol to acknowledge. */ s = CreateControlSymbol(STYPE0_STATUS, stack->rxAckId, QueueAvailable(stack->rxQueue), STYPE1_RESTART_FROM_RETRY, 0u); /* Restart the current frame and proceed with normal operation. */ stack->txFrameState = TX_FRAME_START; stack->txState = TX_STATE_LINK_INITIALIZED; stack->txCounter = 0; /* Discard all packets that has not received a matching packet-accepted. */ stack->txAckIdWindow = stack->txAckId; stack->txQueue = QueueWindowReset(stack->txQueue); /* A status control symbol was sent. Reset the status counter. */ stack->txStatusCounter = 0; break; case TX_STATE_OUTPUT_ERROR_STOPPED: /****************************************************************************** * OUTPUT_ERROR_STOPPED * This state is entered when the link partner has encountered any problem * which is indicated by sending a packet-not-accepted symbol or if a packet * timeout has expired. The error condition is acknowledged by sending a * link-request-symbol and then wait for a link-response reply. * This state follows 5.13.2.7 in part 6. ******************************************************************************/ /* Check if a link-request-symbol has been transmitted. */ if(stack->txCounter == 0) { /* A link-request-symbol has not been transmitted. */ /* Send link-request-symbol (input-status). */ s = CreateControlSymbol(STYPE0_STATUS, stack->rxAckId, QueueAvailable(stack->rxQueue), STYPE1_LINK_REQUEST, LINK_REQUEST_INPUT_STATUS); /* Save the time when this was transmitted. */ stack->txFrameTimeout[stack->txAckId] = stack->portTime; /* Remember that this symbol has been transmitted. */ stack->txCounter = 1; } else { /* A link-request-symbol has been transmitted. */ /* Check if the link partner reply has timed out. */ if((stack->portTime - stack->txFrameTimeout[stack->txAckId]) < stack->portTimeout) { /* No timeout. */ /* A link-request-symbol has been transmitted. */ /* Send only idle-symbols until the link-response is received. */ s.type = RIOSTACK_SYMBOL_TYPE_IDLE; } else { /* Link response timeout. */ /* Check if the link-partner has not responded for too many times. */ if(stack->txCounter < 5) { /* Not too many timeouts. */ /* Retry and send a new link-request. */ /* Send link-request-symbol (input-status). */ s = CreateControlSymbol(STYPE0_STATUS, stack->rxAckId, QueueAvailable(stack->rxQueue), STYPE1_LINK_REQUEST, LINK_REQUEST_INPUT_STATUS); /* Save the time when this was transmitted. */ stack->txFrameTimeout[stack->txAckId] = stack->portTime; /* Increment the number of times we have retransmitted the link-request. */ stack->txCounter++; } else { /* The link partner has not answered for too many times. */ /* Give up and set the state to uninitialized. */ stack->txState = TX_STATE_UNINITIALIZED; s.type = RIOSTACK_SYMBOL_TYPE_IDLE; ASSERT0("No link-response received, giving up."); } } } break; case TX_STATE_UNINITIALIZED: default: /****************************************************************************** * Wait for the port to be initialized. ******************************************************************************/ /* Send only idle symbols. */ s.type = RIOSTACK_SYMBOL_TYPE_IDLE; break; } /* Return the created symbol. */ return s; } /******************************************************************************* * Local RapidIO stack helper functions. *******************************************************************************/ static void handleStatus(RioStack_t *stack, const uint8_t ackId, const uint8_t bufferStatus) { /* Update the buffer status of the link partner. */ (void) ackId; stack->txBufferStatus = bufferStatus; } static void handlePacketAccepted(RioStack_t *stack, const uint8_t ackId, const uint8_t bufferStatus) { uint32_t linkLatency; /* Check if an acknowledge is expected and that it is for a transmitted packet. */ if((stack->txAckId != stack->txAckIdWindow) && (ackId == stack->txAckId)) { /* Acknowledge for a recently transmitted packet received. */ /* Check if the latency of this packet is larger than the largest encountered so far. */ linkLatency = stack->portTime - stack->txFrameTimeout[ackId]; if(linkLatency > stack->statusOutboundLinkLatencyMax) { stack->statusOutboundLinkLatencyMax = linkLatency; } /* Remove the packet from the outbound queue and restart the transmission for a new packet. */ stack->txQueue = QueueDequeue(stack->txQueue); stack->txAckId = ACKID_INC(stack->txAckId); stack->statusOutboundPacketComplete++; } else { /* Acknowledge for an unexpected ackId or not waiting for an acknowledge. */ /* Link protocol violation. Discard the symbol and enter the output-error-stopped state. */ stack->txState = TX_STATE_OUTPUT_ERROR_STOPPED; stack->txCounter = 0u; stack->statusOutboundErrorPacketAccepted++; } /* Update the buffer status of the link partner. */ stack->txBufferStatus = bufferStatus; } static void handlePacketRetry(RioStack_t *stack, const uint8_t ackId, const uint8_t bufferStatus) { /* Check if the retried packet ackId is acceptable. */ if(ackId == stack->txAckId) { /* The request for retry is for the current packet. */ /* Force the transmitter to send a RESTART-FROM-RETRY symbol. */ stack->txState = TX_STATE_OUTPUT_RETRY_STOPPED; stack->statusOutboundPacketRetry++; } else { /* Link protocol violation. Discard the symbol and enter the output-error-stopped state. */ stack->txState = TX_STATE_OUTPUT_ERROR_STOPPED; stack->txCounter = 0u; stack->statusOutboundErrorPacketRetry++; } /* Update the buffer status of the link partner. */ stack->txBufferStatus = bufferStatus; } static void handlePacketNotAccepted(RioStack_t *stack, const uint8_t arbitrary, const uint8_t cause) { (void) arbitrary; /* Force the transmitter to enter output-error-stopped state. */ stack->txState = TX_STATE_OUTPUT_ERROR_STOPPED; stack->txCounter = 0u; /* Record the type of error that caused the packet to not being accepted. */ switch(cause) { case PACKET_NOT_ACCEPTED_CAUSE_UNEXPECTED_ACKID: stack->statusPartnerErrorPacketAckId++; break; case PACKET_NOT_ACCEPTED_CAUSE_CONTROL_CRC: stack->statusPartnerErrorControlCrc++; break; case PACKET_NOT_ACCEPTED_CAUSE_PACKET_CRC: stack->statusPartnerErrorPacketCrc++; break; case PACKET_NOT_ACCEPTED_CAUSE_ILLEGAL_CHARACTER: stack->statusPartnerErrorIllegalCharacter++; break; default: stack->statusPartnerErrorGeneral++; break; } } static void handleLinkResponse(RioStack_t *stack, const uint8_t ackId, const uint8_t portStatus) { uint8_t window; uint8_t windowReceived; (void) portStatus; /* Check if this symbols is expected. */ if(stack->txState == TX_STATE_OUTPUT_ERROR_STOPPED) { /* Calculate the number of packets that has not received an acknowledge on our side and on the link-partner side. */ window = (stack->txAckIdWindow - stack->txAckId) & 0x1f; windowReceived = (ackId - stack->txAckId) & 0x1f; /* Check if the link-partners response is acceptable. */ if(windowReceived <= window) { /* A link-response is expected. */ /* Remove entries in the queue that the link-partner has sent acknowledges for that has been lost. */ while(stack->txAckId != ackId) { stack->txQueue = QueueDequeue(stack->txQueue); stack->txAckId = ACKID_INC(stack->txAckId); stack->statusOutboundPacketComplete++; } /* Set the transmission window to the resend packets that has not been received. */ stack->txQueue = QueueWindowReset(stack->txQueue); stack->txAckIdWindow = ackId; stack->txFrameState = TX_FRAME_START; /* Set the transmitter back into normal operation. */ stack->txState = TX_STATE_LINK_INITIALIZED; } else { /* The link-partner response is unacceptable. */ /* Recovery is not possible. */ stack->txState = TX_STATE_UNINITIALIZED; ASSERT0("Unrecoverable protocol error."); } } else { /* Not expecting a link-response. */ /* Just discard this symbol. */ /* REMARK: Add status counter here??? */ } } static void handleStartOfPacket(RioStack_t *stack) { /* Check if a packet is already started. */ if(stack->rxCounter != 0u) { /* Packet has already started. */ /* This indicates an implicit end-of-packet symbol and signals the previous packet as ready. */ /* Check the packet crc. */ if(stack->rxCrc == 0x0000u) { /* The packet has a correct CRC. */ /* Check if the packet is long enough to contain a complete packet. */ if(stack->rxCounter > 3u) { /* Packet long enough to process. */ /* Process the newly received packet and start a new one. */ handleNewPacketEnd(stack); handleNewPacketStart(stack); } else { /* The packet has a valid CRC but is too short. */ /* Packet error. Enter input-error-stopped state. */ stack->txState = TX_STATE_SEND_PACKET_NOT_ACCEPTED; stack->rxState = RX_STATE_INPUT_ERROR_STOPPED; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_GENERAL; stack->statusInboundErrorGeneral++; } } else { /* The packet has an invalid CRC. */ /* Packet error. Enter input-error-stopped state. */ stack->txState = TX_STATE_SEND_PACKET_NOT_ACCEPTED; stack->rxState = RX_STATE_INPUT_ERROR_STOPPED; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_PACKET_CRC; stack->statusInboundErrorPacketCrc++; } } else { /* Packet has not already started. */ handleNewPacketStart(stack); } } static void handleEndOfPacket(RioStack_t *stack) { /* Check if the CRC is correct. */ if(stack->rxCrc == 0x0000u) { /* The packet has a correct CRC. */ /* Check if the packet is long enough to contain a complete packet. */ if(stack->rxCounter > 3u) { /* Packet long enough to process. */ /* Process the newly received packet. */ handleNewPacketEnd(stack); } else { /* The packet has a valid CRC but is too short. */ /* Packet error. Enter input-error-stopped state. */ stack->txState = TX_STATE_SEND_PACKET_NOT_ACCEPTED; stack->rxState = RX_STATE_INPUT_ERROR_STOPPED; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_GENERAL; stack->statusInboundErrorGeneral++; } } else { /* The packet has an invalid CRC. */ /* Packet error. Enter input-error-stopped state. */ stack->txState = TX_STATE_SEND_PACKET_NOT_ACCEPTED; stack->rxState = RX_STATE_INPUT_ERROR_STOPPED; stack->rxErrorCause = PACKET_NOT_ACCEPTED_CAUSE_PACKET_CRC; stack->statusInboundErrorPacketCrc++; } } static void handleNewPacketStart(RioStack_t *stack) { /* Check if there are buffers available to store the new frame. */ if (QueueAvailable(stack->rxQueue) > 0u) { /* There are buffers available to accept the new packet. */ /* Update the reception counter to indicate the frame has started. */ stack->rxCounter = 1; } else { /* There are no buffers available. */ /* Go to input retry stopped state. */ stack->statusInboundPacketRetry++; stack->txState = TX_STATE_SEND_PACKET_RETRY; stack->rxState = RX_STATE_INPUT_RETRY_STOPPED; } } static void handleNewPacketEnd(RioStack_t *stack) { /* Save the size of the packet. */ QueueSetSize(stack->rxQueue, (uint32_t)stack->rxCounter - (uint32_t)1ul); /* Always forward the packet to the top of the stack. */ stack->rxQueue = QueueEnqueue(stack->rxQueue); /* Make sure the CRC is reset to an invalid value to avoid a packet accidentally being accepted. */ stack->rxCrc = 0xffff; /* Reset the reception counter. */ stack->rxCounter = 0u; /* Update the ackId for the receiver. */ stack->rxAckId = ACKID_INC(stack->rxAckId); /* Update status counter. */ stack->statusInboundPacketComplete++; } static void handleLinkRequest(RioStack_t *stack, uint8_t cmd) { /* Check the command of the link-request. */ if(cmd == LINK_REQUEST_INPUT_STATUS) { /* Input-status requested. */ /* Return input port status. */ /* Force the transmitter to send a link-response-symbol. */ stack->txState = TX_STATE_SEND_LINK_RESPONSE; } else if(cmd == LINK_REQUEST_RESET_DEVICE) { /* Reset-device requested. */ /* REMARK: Support this??? */ } else { /* Unrecognized command. */ /* Dont do anything. */ } /* Always cancel an ongoing frame when a link-request has been received. */ stack->rxCounter = 0; /* Receiving this indicates the link partner having encountered a potential problem. */ /* Count the number of times this happens. */ stack->statusPartnerLinkRequest++; } static RioSymbol_t CreateControlSymbol(const uint8_t stype0, const uint8_t parameter0, const uint8_t parameter1, const uint8_t stype1, const uint8_t cmd) { RioSymbol_t s; s.type = RIOSTACK_SYMBOL_TYPE_CONTROL; s.data = ((uint32_t)stype0 & (uint32_t)0x07ul) << 21; s.data |= ((uint32_t)parameter0 & (uint32_t)0x1ful) << 16; s.data |= ((uint32_t)parameter1 & (uint32_t)0x1ful) << 11; s.data |= ((uint32_t)stype1 & (uint32_t)0x07ul) << 8; s.data |= ((uint32_t)cmd & (uint32_t)0x7ul) << 5; s.data |= Crc5(s.data, 0x1fu); return s; } static uint8_t Crc5(const uint32_t data, const uint8_t crc) { static const uint8_t crcTable[] = { 0x00u, 0x15u, 0x1fu, 0x0au, 0x0bu, 0x1eu, 0x14u, 0x01u, 0x16u, 0x03u, 0x09u, 0x1cu, 0x1du, 0x08u, 0x02u, 0x17u, 0x19u, 0x0cu, 0x06u, 0x13u, 0x12u, 0x07u, 0x0du, 0x18u, 0x0fu, 0x1au, 0x10u, 0x05u, 0x04u, 0x11u, 0x1bu, 0x0eu }; uint8_t result; uint8_t index; result = crc; index = (uint8_t)((data >> 19) & (uint32_t)0x1ful) ^ result; result = crcTable[index]; index = (uint8_t)((data >> 14) & (uint32_t)0x1ful) ^ result; result = crcTable[index]; index = (uint8_t)((data >> 9) & (uint32_t)0x1ful) ^ result; result = crcTable[index]; index = (uint8_t)((data >> 4) & (uint32_t)0x1eul) ^ result; result = crcTable[index]; return result; } /******************************************************************************************* * Internal queue functions. *******************************************************************************************/ static Queue_t QueueCreate(const uint8_t size, uint32_t *buffer) { Queue_t q; q.size = size; q.available = size; q.windowSize = 0u; q.windowIndex = 0u; q.frontIndex = 0u; q.backIndex = 0u; q.buffer_p = buffer; return q; } static uint8_t QueueAvailable(const Queue_t q) { return q.available; } static int QueueEmpty(const Queue_t q) { return (q.available == q.size); } static uint8_t QueueLength(const Queue_t q) { return q.size - q.available; } static Queue_t QueueEnqueue(Queue_t q) { q.backIndex++; if(q.backIndex == q.size) { q.backIndex = 0; } q.available--; return q; } static Queue_t QueueDequeue(Queue_t q) { q.frontIndex++; if(q.frontIndex == q.size) { q.frontIndex = 0; } q.available++; if(q.windowSize == 0) { q.windowIndex = q.frontIndex; } else { q.windowSize--; } return q; } static int QueueWindowEmpty(const Queue_t q) { return ((q.available + q.windowSize) == q.size); } static Queue_t QueueWindowReset(Queue_t q) { q.windowIndex = q.frontIndex; q.windowSize = 0; return q; } static Queue_t QueueWindowNext(Queue_t q) { q.windowIndex++; if(q.windowIndex == q.size) { q.windowIndex = 0; } q.windowSize++; return q; } static void QueueSetSize(Queue_t q, const uint32_t size) { (q.buffer_p+(RIOSTACK_BUFFER_SIZE*q.backIndex))[0] = size; } static void QueueSetContent(Queue_t q, const uint32_t index, const uint32_t content) { (q.buffer_p+(RIOSTACK_BUFFER_SIZE*q.backIndex))[index+1ul] = content; } static uint32_t QueueGetSize(Queue_t q) { return (q.buffer_p+(RIOSTACK_BUFFER_SIZE*q.windowIndex))[0]; } static uint32_t QueueGetFrontContent(const Queue_t q, const uint32_t index) { return (q.buffer_p+(RIOSTACK_BUFFER_SIZE*q.windowIndex))[index+1ul]; } static uint32_t *QueueGetFrontBuffer(const Queue_t q ) { return &((q.buffer_p+(RIOSTACK_BUFFER_SIZE*q.windowIndex))[1]); } static uint32_t *QueueGetBackBuffer(const Queue_t q ) { return &((q.buffer_p+(RIOSTACK_BUFFER_SIZE*q.backIndex))[1]); } /*************************** end of file **************************************/
Go to most recent revision | Compare with Previous | Blame | View Log