diff options
Diffstat (limited to 'libs/Network/tx.c')
-rw-r--r-- | libs/Network/tx.c | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/libs/Network/tx.c b/libs/Network/tx.c new file mode 100644 index 0000000..a2e998f --- /dev/null +++ b/libs/Network/tx.c @@ -0,0 +1,498 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// @file tx.c +/// +/// @project +/// +/// @brief Network transmission routines +/// +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +/// +/// @copyright Copyright (c) 2019, Evan Lojewski +/// @cond +/// +/// All rights reserved. +/// +/// Redistribution and use in source and binary forms, with or without +/// modification, are permitted provided that the following conditions are met: +/// 1. Redistributions of source code must retain the above copyright notice, +/// this list of conditions and the following disclaimer. +/// 2. Redistributions in binary form must reproduce the above copyright notice, +/// this list of conditions and the following disclaimer in the documentation +/// and/or other materials provided with the distribution. +/// 3. Neither the name of the copyright holder nor the +/// names of its contributors may be used to endorse or promote products +/// derived from this software without specific prior written permission. +/// +//////////////////////////////////////////////////////////////////////////////// +/// +/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +/// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +/// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +/// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +/// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +/// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +/// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +/// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +/// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +/// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +/// POSSIBILITY OF SUCH DAMAGE. +/// @endcond +//////////////////////////////////////////////////////////////////////////////// + +#include <APE_APE.h> +#include <APE_TX_PORT.h> +#include <Ethernet.h> +#include <Network.h> +#include <stdbool.h> +#include <types.h> + +#ifdef CXX_SIMULATOR +#include <endian.h> +#include <stdio.h> +#else +/* ARM */ +static inline uint32_t be32toh(uint32_t be32) +{ + uint32_t he32 = ((be32 & 0xFF000000) >> 24) | + ((be32 & 0x00FF0000) >> 8) | + ((be32 & 0x0000FF00) << 8) | + ((be32 & 0x000000FF) << 24); + + return he32; +} +#endif + +#define FIRST_FRAME_MAX ((TX_PORT_OUT_ALL_BLOCK_WORDS - TX_PORT_OUT_ALL_FIRST_PAYLOAD_WORD) * sizeof(uint32_t)) +#define ADDITIONAL_FRAME_MAX ((TX_PORT_OUT_ALL_BLOCK_WORDS - TX_PORT_OUT_ALL_ADDITIONAL_PAYLOAD_WORD) * sizeof(uint32_t)) + +typedef union { + uint32_t r32; + struct + { + uint32_t payload_length:7; + uint32_t next_block:23; + uint32_t first:1; + uint32_t not_last:1; + } bits; +} control_t; + +uint32_t Network_TX_numBlocksNeeded(uint32_t frame_size) +{ + uint32_t blocks = 1; + + if (frame_size > FIRST_FRAME_MAX) + { + frame_size -= FIRST_FRAME_MAX; + + blocks += DIVIDE_RND_UP(frame_size, ADDITIONAL_FRAME_MAX); + } + +#ifdef CXX_SIMULATOR + printf("%d blocks needed for packet with frame size %d\n", blocks, + frame_size); +#endif + return blocks; +} + +int32_t __attribute__((noinline)) Network_TX_allocateBlock(void) +{ + int32_t block; + + // Set the alloc bit. + RegAPETxToNetBufferAllocator0_t alloc; + alloc.r32 = 0; + alloc.bits.RequestAllocation = 1; + APE.TxToNetBufferAllocator0 = alloc; + + // Wait for state machine to finish + RegAPETxToNetBufferAllocator0_t status; + do + { + status = APE.TxToNetBufferAllocator0; + } while (APE_TX_TO_NET_BUFFER_ALLOCATOR_0_STATE_PROCESSING == status.bits.State); + + if (APE_TX_TO_NET_BUFFER_ALLOCATOR_0_STATE_ALLOCATION_OK != status.bits.State) + { + block = -1; +#if CXX_SIMULATOR + printf("Error: Failed to allocate TX block.\n"); +#endif + } + else + { + block = status.bits.Index; +#if CXX_SIMULATOR + printf("Allocated TX block %d\n", block); +#endif + } + + return block; +} + +static uint32_t inline Network_TX_initFirstBlock(RegTX_PORTOut_t *block, + uint32_t length, + int32_t blocks, + int32_t next_block, + uint32_t *packet, + bool big_endian) +{ + control_t control; + int copy_length; + int i; + + control.r32 = 0; + control.bits.next_block = next_block >= 0 ? next_block : 0; + control.bits.first = 1; + + if (length > FIRST_FRAME_MAX) + { + copy_length = FIRST_FRAME_MAX; + control.bits.not_last = 1; + } + else + { + // Last. + copy_length = length; + control.bits.not_last = 0; + } + + // block[1] = uninitialized; + block[2].r32 = 0; + block[TX_PORT_OUT_ALL_FRAME_LEN_WORD].r32 = length; + block[4].r32 = 0; + block[5].r32 = 0; + block[6].r32 = 0; + block[7].r32 = 0; + block[8].r32 = 0; + block[TX_PORT_OUT_ALL_NUM_BLOCKS_WORD].r32 = blocks; + // block[10] = uninitialized; + // block[11] = uninitialized; + + // Copy Payload Data. + int num_words = (copy_length + sizeof(uint32_t) - 1) / sizeof(uint32_t); + for (i = 0; i < num_words; i++) + { + if (big_endian) + { +#if CXX_SIMULATOR + printf("1st[%d] = 0x%08X\n", i, be32toh(packet[i])); +#endif + block[TX_PORT_OUT_ALL_FIRST_PAYLOAD_WORD + i].r32 = be32toh(packet[i]); + } + else + { +#if CXX_SIMULATOR + printf("1LE[%d] = 0x%08X\n", i, (packet[i])); +#endif + block[TX_PORT_OUT_ALL_FIRST_PAYLOAD_WORD + i].r32 = (packet[i]); + } + } + + length -= control.bits.payload_length; + + // Pad if too small. + if (copy_length < ETHERNET_FRAME_MIN) + { + copy_length = ETHERNET_FRAME_MIN; + length = ETHERNET_FRAME_MIN; + + num_words = DIVIDE_RND_UP(copy_length, sizeof(uint32_t)); + for (; i < num_words; i++) + { + // Pad remaining with 0's + block[TX_PORT_OUT_ALL_FIRST_PAYLOAD_WORD + i].r32 = 0; + } + } + + control.bits.payload_length = copy_length; + + block[TX_PORT_OUT_ALL_CONTROL_WORD].r32 = control.r32; + + return copy_length; +} + +static uint32_t inline Network_TX_initAdditionalBlock(RegTX_PORTOut_t *block, + int32_t next_block, + uint32_t length, + uint32_t *packet, + bool big_endian) +{ + int i; + control_t control; + + control.r32 = 0; + control.bits.first = 0; + control.bits.next_block = next_block; + + if (length > ADDITIONAL_FRAME_MAX) + { + length = ADDITIONAL_FRAME_MAX; + control.bits.payload_length = ADDITIONAL_FRAME_MAX; + control.bits.not_last = 1; + } + else + { + // Last + control.bits.payload_length = length; + control.bits.not_last = 0; + } + + // block[1] = uninitialized; + + // Copy payload data. + int num_words = DIVIDE_RND_UP(length, sizeof(uint32_t)); + for (i = 0; i < num_words; i++) + { + if (big_endian) + { + block[TX_PORT_OUT_ALL_ADDITIONAL_PAYLOAD_WORD + i].r32 = be32toh(packet[i]); + } + else + { + block[TX_PORT_OUT_ALL_ADDITIONAL_PAYLOAD_WORD + i].r32 = (packet[i]); + } + } + + block[TX_PORT_OUT_ALL_CONTROL_WORD].r32 = control.r32; + + length -= control.bits.payload_length; + + return control.bits.payload_length; +} + +static inline void Network_TX_transmitPacket_internal(uint8_t *packet, + uint32_t length, + bool big_endian) +{ + if (!length) + { + return; + } + + uint32_t *packet_32 = (uint32_t *)packet; + uint32_t consumed = 0; + uint32_t blocks = Network_TX_numBlocksNeeded(length); + int total_blocks = blocks; + ; + + // First block + int32_t tail; + int32_t first = tail = Network_TX_allocateBlock(); + if (first <= 0) + { + // Error + return; + } + int32_t next_block = -1; + if (blocks > 1) + { + next_block = Network_TX_allocateBlock(); + } + RegTX_PORTOut_t *block = (RegTX_PORTOut_t *)&TX_PORT.Out[TX_PORT_OUT_ALL_BLOCK_WORDS * first]; + + consumed += Network_TX_initFirstBlock(block, length, blocks, next_block, + &packet_32[consumed / 4], big_endian); + blocks -= 1; + while (blocks--) + { + + block = (RegTX_PORTOut_t *)&TX_PORT + .Out[TX_PORT_OUT_ALL_BLOCK_WORDS * next_block]; + if (blocks) + { + next_block = Network_TX_allocateBlock(); + consumed += Network_TX_initAdditionalBlock( + block, next_block, length - consumed, &packet_32[consumed / 4], + big_endian); + } + else + { + Network_TX_initAdditionalBlock(block, 0, length - consumed, + &packet_32[consumed / 4], + big_endian); + } + + tail = next_block; + } + + RegAPETxToNetDoorbellFunc0_t doorbell; + doorbell.r32 = 0; + doorbell.bits.Head = first; + doorbell.bits.Tail = tail; + doorbell.bits.Length = total_blocks; + + APE.TxToNetDoorbellFunc0 = doorbell; +} + +void Network_TX_transmitBePacket(uint8_t *packet, uint32_t length) +{ + Network_TX_transmitPacket_internal(packet, length, true); +} + +void Network_TX_transmitLePacket(uint8_t *packet, uint32_t length) +{ + Network_TX_transmitPacket_internal(packet, length, false); +} + +static uint32_t inline Network_TX_initFirstPassthroughBlock( + RegTX_PORTOut_t *block, uint32_t length, int32_t blocks, int32_t next_block) +{ + control_t control; + int copy_length; + int i; + + control.r32 = 0; + control.bits.next_block = next_block >= 0 ? next_block : 0; + control.bits.first = 1; + + if (length > FIRST_FRAME_MAX) + { + copy_length = FIRST_FRAME_MAX; + control.bits.not_last = 1; + } + else + { + // Last. + copy_length = length; + control.bits.not_last = 0; + } + + // block[1] = uninitialized; + block[2].r32 = 0; + block[TX_PORT_OUT_ALL_FRAME_LEN_WORD].r32 = length; + block[4].r32 = 0; + block[5].r32 = 0; + block[6].r32 = 0; + block[7].r32 = 0; + block[8].r32 = 0; + block[TX_PORT_OUT_ALL_NUM_BLOCKS_WORD].r32 = blocks; + // block[10] = uninitialized; + // block[11] = uninitialized; + + // Copy Payload Data. + int num_words = DIVIDE_RND_UP(copy_length, sizeof(uint32_t)); + for (i = 0; i < num_words; i++) + { + block[TX_PORT_OUT_ALL_FIRST_PAYLOAD_WORD + i].r32 = APE_PERI.BmcToNcReadBuffer.r32; + } + + length -= control.bits.payload_length; + + // Pad if too small. + if (copy_length < ETHERNET_FRAME_MIN) + { + copy_length = ETHERNET_FRAME_MIN; + length = ETHERNET_FRAME_MIN; + + num_words = DIVIDE_RND_UP(copy_length, sizeof(uint32_t)); + for (; i < num_words; i++) + { + // Pad remaining with 0's + block[TX_PORT_OUT_ALL_FIRST_PAYLOAD_WORD + i].r32 = 0; + } + } + + control.bits.payload_length = copy_length; + + block[TX_PORT_OUT_ALL_CONTROL_WORD].r32 = control.r32; + + return copy_length; +} + +static uint32_t inline Network_TX_initAdditionalPassthroughBlock( + RegTX_PORTOut_t *block, int32_t next_block, uint32_t length) +{ + int i; + control_t control; + + control.r32 = 0; + control.bits.first = 0; + control.bits.next_block = next_block; + + if (length > ADDITIONAL_FRAME_MAX) + { + length = ADDITIONAL_FRAME_MAX; + control.bits.payload_length = ADDITIONAL_FRAME_MAX; + control.bits.not_last = 1; + } + else + { + // Last + control.bits.payload_length = length; + control.bits.not_last = 0; + } + + // block[1] = uninitialized; + + // Copy payload data. + int num_words = DIVIDE_RND_UP(length, sizeof(uint32_t)); + for (i = 0; i < num_words; i++) + { + block[TX_PORT_OUT_ALL_ADDITIONAL_PAYLOAD_WORD + i].r32 = APE_PERI.BmcToNcReadBuffer.r32; + } + + block[TX_PORT_OUT_ALL_CONTROL_WORD].r32 = control.r32; + + length -= control.bits.payload_length; + + return control.bits.payload_length; +} + +void Network_TX_transmitPassthroughPacket(uint32_t length) +{ + if (!length) + { + return; + } + + // Drop the FCS word. It will be generated by hardware. + length -= 4; + + int32_t tail; + int32_t first = tail = Network_TX_allocateBlock(); + int32_t next_block = -1; + uint32_t blocks = Network_TX_numBlocksNeeded(length); + int total_blocks = blocks; + ; + + if (blocks > 1) + { + next_block = Network_TX_allocateBlock(); + } + RegTX_PORTOut_t *block = (RegTX_PORTOut_t *)&TX_PORT.Out[TX_PORT_OUT_ALL_BLOCK_WORDS * first]; + + length -= Network_TX_initFirstPassthroughBlock(block, length, blocks, next_block); + blocks -= 1; + while (blocks--) + { + + block = (RegTX_PORTOut_t *)&TX_PORT.Out[TX_PORT_OUT_ALL_BLOCK_WORDS * next_block]; + if (blocks) + { + next_block = Network_TX_allocateBlock(); + length -= Network_TX_initAdditionalPassthroughBlock( + block, next_block, length); + } + else + { + Network_TX_initAdditionalPassthroughBlock(block, 0, length); + } + + tail = next_block; + } + + RegAPETxToNetDoorbellFunc0_t doorbell; + doorbell.r32 = 0; + doorbell.bits.Head = first; + doorbell.bits.Tail = tail; + doorbell.bits.Length = total_blocks; + + APE.TxToNetDoorbellFunc0 = doorbell; + + // Read last RX word (FCS) to clear the buffer + uint32_t data = APE_PERI.BmcToNcReadBuffer.r32; + (void)data; +} |