From e2de1c0424ff1f0ee78a7d6cec76e2d4421a2472 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Fri, 1 Nov 2024 09:19:29 -0700 Subject: [PATCH 01/47] Start on EMAC rewrite, but realized I can't do zero copy Tx Make progress on driver More progress, but I realized that I can't free packets from an ISR... Driver building, now I can start testing... Add CompositeEthMac MAC boots and can send! Rx not working yet though Sending and receiving sort of working! Clean up CTP code to use structs Fix dumb cache and off by one issues with Rx Fix a couple memory issues, making progress... Fix double free when transmission uses more than 1 descriptor Start adding multicast support Making progress on multicast support Multicast filter working! Tests passing! Improve mcast filter test --- .../drivers/emac/TARGET_STM/CMakeLists.txt | 4 + .../emac/TARGET_STM/CompositeEthMac.cpp | 18 + .../drivers/emac/TARGET_STM/CompositeEthMac.h | 194 ++++++ .../emac/TARGET_STM/STM32EthIPv2DMARings.cpp | 591 ++++++++++++++++++ .../emac/TARGET_STM/STM32EthIPv2DMARings.h | 180 ++++++ .../emac/TARGET_STM/STM32IPv2EthDescriptors.h | 170 +++++ .../TARGET_DISCO_H747I/stm32h7_eth_init.c | 7 - .../TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c | 5 - .../TARGET_NUCLEO_H743ZI/stm32h7_eth_init.c | 5 - .../TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c | 5 - .../TARGET_PORTENTA_H7/stm32h7_eth_init.c | 7 - .../{mbed_lib.json => mbed_lib.json5} | 13 +- .../drivers/emac/TARGET_STM/stm32xx_emac.cpp | 335 ++-------- .../drivers/emac/TARGET_STM/stm32xx_emac.h | 16 +- connectivity/lwipstack/mbed_lib.json5 | 4 + .../nanostack/mbed-mesh-api/mbed_lib.json | 8 + .../emac/emac_test_unicast_frame_len.cpp | 2 + targets/TARGET_STM/README.md | 15 - .../STM32Cube_FW/CMakeLists.txt | 7 +- .../STM32Cube_FW/stm32h7xx_hal_conf.h | 5 +- .../STM32H743_H72x.ld | 15 - .../CM4/STM32H745_H747_CM4.ld | 15 - .../CM7/STM32H745_H747_CM7.ld | 16 - .../STM32H7Ax_FAMILY/STM32H7Ax.ld | 14 - .../stm32_eth_region_size_calcs.h | 51 -- tools/cmake/toolchains/GCC_ARM.cmake | 1 + 26 files changed, 1260 insertions(+), 443 deletions(-) create mode 100644 connectivity/drivers/emac/TARGET_STM/CompositeEthMac.cpp create mode 100644 connectivity/drivers/emac/TARGET_STM/CompositeEthMac.h create mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp create mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h create mode 100644 connectivity/drivers/emac/TARGET_STM/STM32IPv2EthDescriptors.h rename connectivity/drivers/emac/TARGET_STM/{mbed_lib.json => mbed_lib.json5} (91%) delete mode 100644 targets/TARGET_STM/TARGET_STM32H7/linker_scripts/stm32_eth_region_size_calcs.h diff --git a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt index bc7b057676e..a5df08c0f90 100644 --- a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt @@ -19,4 +19,8 @@ target_include_directories(mbed-emac target_sources(mbed-emac PRIVATE stm32xx_emac.cpp + STM32EthIPv2DMARings.cpp + CompositeEthMac.cpp ) + +target_compile_options(mbed-emac PRIVATE -Wno-packed-bitfield-compat) diff --git a/connectivity/drivers/emac/TARGET_STM/CompositeEthMac.cpp b/connectivity/drivers/emac/TARGET_STM/CompositeEthMac.cpp new file mode 100644 index 00000000000..fb441fd6688 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/CompositeEthMac.cpp @@ -0,0 +1,18 @@ +/* Copyright (c) 2024 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "CompositeEthMac.h" \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/CompositeEthMac.h b/connectivity/drivers/emac/TARGET_STM/CompositeEthMac.h new file mode 100644 index 00000000000..8b5cce272b4 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/CompositeEthMac.h @@ -0,0 +1,194 @@ +/* Copyright (c) 2024 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_COMPOSITEETHMAC_H +#define MBED_OS_COMPOSITEETHMAC_H + +#include "EMAC.h" + +namespace mbed +{ + +/** + * @brief Implementation of the EMAC API built up from several components implemented by device-specific classes. + * + * \par Motivation + * Originally, Mbed interfaced with the Ethernet hardware on an MCU via a single class, an implementation of the + * EMAC interface. However, EMAC is a large interface, encompassing many responsibilities: setting up the pins, + * managing the Ethernet peripheral, controlling the phy chip, and putting data into and out of DMA. Many of these + * pieces, such as PHY control and managing memory buffers, are common, but others are unique to each MCU family. + * To better divide responsibility into common and target-specific parts, and to allow Ethernet drivers to + * be organized more logically, the CompositeEMAC class was created. + * + * \par Division + * CompositeEMAC divides Ethernet functionality up into several different classes, each with different + * responsibilities: + * + * + * \note CompositeEMAC itself does not use any global data and supports multiple instances for MCUs that have + * multiple EMACs. However, the implementation for a specific MCU may or may not use global data -- if + * there's only one EMAC on the MCU, there isn't really a reason not to. + */ +class CompositeEMAC : public EMAC +{ + enum class ErrCode + { + SUCCESS = 0, + TIMEOUT = 1, + HW_ERROR = 2, + PHY_NOT_RESPONDING = 3, + OUT_OF_MEMORY = 4, + INVALID_ARGUMENT = 5, + INVALID_USAGE = 6 + }; + + enum class LinkSpeed + { + LINK_10MBIT, + LINK_100MBIT, + LINK_1GBIT + }; + + enum class Duplex + { + HALF, + FULL + }; + + typedef std::array MACAddress; + + class MACDriver + { + /** + * @brief Initialize the MAC, map pins, and prepare it to send and receive packets. + * It should not be enabled yet. + * + * @param ownAddress MAC address that this device should use + * + * @return Error code or SUCCESS + */ + virtual ErrCode init(MACAddress const & ownAddress) = 0; + + /** + * @brief Deinit the MAC so that it's not using any clock/power. Should prepare for init() to be called + * again. + * + * @return Error code or SUCCESS + */ + virtual ErrCode deinit() = 0; + + /** + * @brief Enable the MAC so that it can send and receive packets + * + * @param speed Speed of the link + * @param duplex Duplex of the link + * + * @return Error code or SUCCESS + */ + virtual ErrCode enable(LinkSpeed speed, Duplex duplex) = 0; + + /** + * @brief Disable the MAC so that it will not send or receive packets + * + * @return Error code or SUCCESS + */ + virtual ErrCode disable() = 0; + + /** + * @brief Read a register from the PHY over the MDIO bus. + * + * @param devAddr PHY device address to read. This will usually be set via the phy strapping pins. + * @param regAddr Register address from 0-31 to read. + * @param result Result is returned here. Note that because MDIO is an open drain bus, a result of + * 0xFFFF usually means the phy didn't respond at all. + * + * @return Error code or success. + */ + virtual ErrCode mdioRead(uint16_t devAddr, uint8_t regAddr, uint16_t & result) = 0; + + /** + * @brief Write a register to the PHY over the MDIO bus. + * + * @param devAddr PHY device address to write. This will usually be set via the phy strapping pins. + * @param regAddr Register address from 0-31 to write. + * @param data Data to write + * + * @return Error code or success. + */ + virtual ErrCode mdioWrite(uint16_t devAddr, uint8_t regAddr, uint16_t data) = 0; + + /** + * @brief Get the reset pin for the Ethernet PHY. + * + * @return Reset pin, or NC if the reset pin is not mapped + */ + virtual PinName getPhyResetPin() = 0; + + /** + * @brief Add a multicast MAC address that should be accepted by the MAC. + * + * @param mac MAC address to accept + * + * @return Error code or success + */ + virtual ErrCode addMcastMAC(MACAddress mac) = 0; + + /** + * @brief Clear the MAC multicast filter, removing all multicast subscriptions + * + * @return Error code or success + */ + virtual ErrCode clearMcastFilter() = 0; + + /** + * @brief Set whether the MAC passes all multicast traffic up to the application. + * + * @param pass True to pass all mcasts, false otherwise + * + * @return Error code or success + */ + virtual ErrCode setPassAllMcast(bool pass); + + /** + * @brief Set promiscuous mode (where the Eth MAC passes all traffic up to the application, regardless + * of its destination address). + * + * @param enable True to pass all traffic, false otherwise + * + * @return Error code or success + */ + virtual ErrCode setPromiscuous(bool enable); + }; + + class PhyDriver + { + /** + * @brief Get the expected + * @return + */ + virtual std::pair getOUIAndModel() = 0; + }; +}; + +} +#endif //MBED_OS_COMPOSITEETHMAC_H diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp new file mode 100644 index 00000000000..cad3c8fbd50 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp @@ -0,0 +1,591 @@ +/* Copyright (c) 2024 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "STM32EthIPv2DMARings.h" + +#include "ThisThread.h" +#include "mbed_trace.h" + +#define TRACE_GROUP "CEMAC" + +namespace mbed { + +// Thread flag constants +static uint32_t THREAD_FLAG_TX_DESC_AVAILABLE = 1; +static uint32_t THREAD_FLAG_RX_DESC_AVAILABLE = 2; +static uint32_t THREAD_FLAG_SHUTDOWN = 4; + +// Event flag constants +static uint32_t EVENT_FLAG_TX_DESC_AVAILABLE = 1; + +void STM32EthIPv2DMARings::buildRxDescriptors() { + + // Note: With this Ethernet peripheral, you can never give back every single descriptor to + // the hardware, because then it thinks there are 0 descriptors left. + while (rxDescsOwnedByApplication > 1) { + auto &currRxDesc = rxDescs[rxBuildIndex]; + + // Allocate new buffer + auto *const buffer = memory_manager.alloc_pool(rxPoolPayloadSize, RX_BUFFER_ALIGN); + if (buffer == nullptr) { + // No memory, cannot return any more descriptors. + return; + } + + // Clear out any bits previously set in the descriptor + memset(&currRxDesc.rxDesc.toDMAFmt, 0, sizeof(EthRxDescriptor)); + + // Store buffer address + currRxDesc.buffer = buffer; + currRxDesc.rxDesc.toDMAFmt.buffer1Addr = memory_manager.get_ptr(buffer); + + // Configure descriptor + currRxDesc.rxDesc.toDMAFmt.buffer1Valid = true; + currRxDesc.rxDesc.toDMAFmt.buffer2Valid = false; + currRxDesc.rxDesc.toDMAFmt.intrOnCompletion = true; + currRxDesc.rxDesc.toDMAFmt.dmaOwn = true; + +#if __DCACHE_PRESENT + // Flush to main memory + SCB_CleanDCache_by_Addr(&currRxDesc, __SCB_DCACHE_LINE_SIZE); +#endif + + // Move to next descriptor + --rxDescsOwnedByApplication; + rxBuildIndex = (rxBuildIndex + 1) % rxPoolSize; + + // Update tail ptr to issue "rx poll demand" and mark this descriptor for receive. + // Rx stops when the current and tail pointers are equal, so we want to set the tail pointer + // to one location after the last DMA-owned descriptor in the FIFO. + heth.Instance->DMACRDTPR = reinterpret_cast(&rxDescs[rxBuildIndex]); + } +} + +// TODO monitor for Rx buffer exhaustion conditions. This happens when we have too few +// built Rx buffers and won't be able to receive another packet. Example: if the size of +// one buffer is 512 bytes, then we need at least 3 built buffers to be able to receive +// the next packet if it's 1 MTU. +// If we are in a potential buffer exhaustion situation, we need to poll at a reasonably +// high rate (10ms) until the application has freed up at least some of the pool buffers. +// NOTE: In LwIP, the failed allocation of a buffer from the pool will trigger a cleanup +// of the TCP reassembly buffer, potentially freeing some memory in the near future. + +net_stack_mem_buf_t *STM32EthIPv2DMARings::rxNextPacket() { + // Indices of the first and last descriptors for the packet will be saved here + size_t firstDescIdx = rxPoolSize; + size_t lastDescIdx = rxPoolSize; + + // Prevent looping around into descriptors waiting for rebuild by limiting how many + // we can process. + const size_t maxDescsToProcess = rxPoolSize - rxDescsOwnedByApplication; + + const size_t startIdx = rxNextIndex; + + for (size_t descCount = 0; descCount < maxDescsToProcess && lastDescIdx == rxPoolSize; descCount++) { + size_t descIdx = (startIdx + descCount) % rxPoolSize; + auto &descriptor = rxDescs[descIdx]; + +#if __DCACHE_PRESENT + SCB_InvalidateDCache_by_Addr(&descriptor, sizeof(EthRxDescriptor)); +#endif + + if (descriptor.rxDesc.fromDMAFmt.dmaOwn) { + // Descriptor owned by DMA and has not been filled in yet. We are out of descriptors to process. + break; + } + + if (descriptor.rxDesc.fromDMAFmt.context || descriptor.rxDesc.fromDMAFmt.errorSummary || + (!descriptor.rxDesc.fromDMAFmt.firstDescriptor && firstDescIdx == rxPoolSize)) { + // Context or error descriptor, or a non-first-descriptor before a first descriptor + // (could be caused by incomplete packets/junk in the DMA buffer). + // Ignore, free associated memory, and schedule for rebuild. + memory_manager.free(descriptor.buffer); + ++rxDescsOwnedByApplication; + ++rxNextIndex; + + // We should only get one of these error descriptors before the start of the packet, not + // during it. + MBED_ASSERT(firstDescIdx == rxPoolSize); + + continue; + } + + if (descriptor.rxDesc.fromDMAFmt.firstDescriptor) { + // We should see first descriptor only once and before last descriptor. If this rule is violated, it's + // because we ran out of descriptors during receive earlier and the MAC tossed out the rest of the packet. + if(firstDescIdx != rxPoolSize) { + // Clean up the old first descriptor + auto & oldFirstDesc = rxDescs[firstDescIdx]; + memory_manager.free(oldFirstDesc.buffer); + ++rxDescsOwnedByApplication; + ++rxNextIndex; + } + + firstDescIdx = descIdx; + } + + if (descriptor.rxDesc.fromDMAFmt.lastDescriptor) { + lastDescIdx = descIdx; + } + } + + if (lastDescIdx == rxPoolSize) { + // No complete packet identified. + // Take the chance to rebuild any available descriptors, then return. + tr_debug("No complete packets in Rx descs\n"); + buildRxDescriptors(); + return nullptr; + } + + // We will receive next into the descriptor after this one. + // Update this now to tell the ISR to search for descriptors after lastDescIdx only. + rxNextIndex = (lastDescIdx + 1) % rxPoolSize; + + // Set length of first buffer + net_stack_mem_buf_t *const headBuffer = rxDescs[firstDescIdx].buffer; + size_t lenRemaining = rxDescs[lastDescIdx].rxDesc.fromDMAFmt.pktLength; + memory_manager.set_len(headBuffer, std::min(lenRemaining, rxPoolPayloadSize)); + lenRemaining -= std::min(lenRemaining, rxPoolPayloadSize); + + // Iterate through the subsequent descriptors in this packet and link the buffers + // Note that this also transfers ownership of subsequent buffers to the first buffer, + // so if the first buffer is deleted, the others will be as well. + ++rxDescsOwnedByApplication; // for first buffer + rxDescs[firstDescIdx].buffer = nullptr; + for (size_t descIdx = (firstDescIdx + 1) % rxPoolSize; + descIdx != (lastDescIdx + 1) % rxPoolSize; + descIdx = (descIdx + 1) % rxPoolSize) { + + // We have to set the buffer length first before concatenating it to the chain + memory_manager.set_len(rxDescs[descIdx].buffer, std::min(lenRemaining, rxPoolPayloadSize)); + lenRemaining -= std::min(lenRemaining, rxPoolPayloadSize); + + memory_manager.cat(headBuffer, rxDescs[descIdx].buffer); + rxDescs[descIdx].buffer = nullptr; + ++rxDescsOwnedByApplication; + } + + // Invalidate cache for all data buffers, as these were written by the DMA to main memory +#if __DCACHE_PRESENT + auto * bufToInvalidate = headBuffer; + while(bufToInvalidate != nullptr) + { + SCB_InvalidateDCache_by_Addr(memory_manager.get_ptr(bufToInvalidate), rxPoolPayloadSize); + bufToInvalidate = memory_manager.get_next(bufToInvalidate); + } +#endif + + tr_info("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu (%p-%p)\n", + memory_manager.get_total_len(headBuffer), memory_manager.get_ptr(headBuffer), firstDescIdx, lastDescIdx, + &rxDescs[firstDescIdx], &rxDescs[lastDescIdx]); + + // Rebuild descriptors if possible + buildRxDescriptors(); + + return headBuffer; +} + +void STM32EthIPv2DMARings::reclaimTxDescs() { + bool returnedAnyDescriptors = false; + while (true) + { + if (txReclaimIndex == txSendIndex && txDescsOwnedByApplication > 0) { + // If we have reached the Tx send index, we want to stop iterating as this is + // the next descriptor that has not been populated by the application yet. + // The only exception is if the Tx ring is completely full, in which case we want + // to process the entire ring. In the case where the Tx ring is full, + // txDescsOwnedByApplication will be 0. + // Note that txSendIndex and txDescsOwnedByApplication are updated in a critical + // section so their values will always be in sync with each other. + break; + } + + auto &currDesc = txDescs[txReclaimIndex]; + +#if __DCACHE_PRESENT + SCB_InvalidateDCache_by_Addr(&currDesc, sizeof(EthTxDescriptor)); +#endif + + if (currDesc.txDesc.fromDMAFmt.dmaOwn) { + // This desc is owned by the DMA, so we have reached the part of the ring buffer + // that is still being transmitted. + // Done for now! + break; + } + + // Free any buffers associated with the descriptor + if (currDesc.packetFirstBuf != nullptr) { + memory_manager.free(currDesc.packetFirstBuf); + } + + // Update counters + txReclaimIndex = (txReclaimIndex + 1) % MBED_CONF_STM32_EMAC_ETH_TXBUFNB; + txDescsOwnedByApplication++; + + returnedAnyDescriptors = true; + } + + if (returnedAnyDescriptors) { + eventFlags.set(EVENT_FLAG_TX_DESC_AVAILABLE); + } +} + +void STM32EthIPv2DMARings::macThread() +{ + while(true) + { + // Wait for something to happen + uint32_t flags = rtos::ThisThread::flags_wait_any(THREAD_FLAG_TX_DESC_AVAILABLE | THREAD_FLAG_SHUTDOWN | THREAD_FLAG_RX_DESC_AVAILABLE); + if(flags & THREAD_FLAG_SHUTDOWN) + { + return; + } + if(flags & (THREAD_FLAG_RX_DESC_AVAILABLE | THREAD_FLAG_TX_DESC_AVAILABLE)) // TODO temp + { + // Receive any available packets. + // Note that if the ISR was delayed, we might get multiple packets per ISR, so we need to loop. + while(true) + { + auto * packet = rxNextPacket(); + if(!packet) { + break; + } + + if(emac_link_input_cb) + { + emac_link_input_cb(packet); + } + else + { + memory_manager.free(packet); + } + } + } + if(flags & THREAD_FLAG_TX_DESC_AVAILABLE) + { + reclaimTxDescs(); + } + } +} + + +STM32EthIPv2DMARings::STM32EthIPv2DMARings(EMACMemoryManager &memory_manager, ETH_HandleTypeDef & heth, EMAC::emac_link_input_cb_t emac_link_input_cb): + memory_manager(memory_manager), + heth(heth), + emac_link_input_cb(emac_link_input_cb), + thread(osPriorityHigh, MBED_CONF_STM32_EMAC_THREAD_STACKSIZE, nullptr, "stm32_emac_rx_thread"), + rxPoolSize(memory_manager.get_pool_size() - RX_POOL_EXTRA_BUFFERS + 1), // + 1 because we have to always keep one descriptor owned by the application + rxDescs(rxPoolSize), + rxPoolPayloadSize(memory_manager.get_pool_alloc_unit(RX_BUFFER_ALIGN)) +{ + // Make sure we have enough space in the pool for RX_POOL_EXTRA_BUFFERS plus a minimum of two descriptors + MBED_ASSERT(memory_manager.get_pool_size() >= RX_POOL_EXTRA_BUFFERS + 2); +} + +STM32EthIPv2DMARings::~STM32EthIPv2DMARings() +{ + if(thread.get_state() != rtos::Thread::Deleted) + { + thread.flags_set(THREAD_FLAG_SHUTDOWN); + thread.join(); + } +} + + +HAL_StatusTypeDef STM32EthIPv2DMARings::startDMA() +{ + if (heth.gState == HAL_ETH_STATE_READY) + { + heth.gState = HAL_ETH_STATE_BUSY; + + // At the start, we own all the descriptors + rxDescsOwnedByApplication = rxPoolSize; + txDescsOwnedByApplication = MBED_CONF_STM32_EMAC_ETH_TXBUFNB; + + // Flush Tx queue + heth.Instance->MTLTQOMR |= ETH_MTLTQOMR_FTQ; + + // Configure Rx buffer size. Per the datasheet and HAL code, we need to round this down to + // the nearest multiple of 4. + size_t rxBufferSize = memory_manager.get_pool_alloc_unit(RX_BUFFER_ALIGN); + rxBufferSize = (rxBufferSize / sizeof(uint32_t)) * sizeof(uint32_t); + heth.Instance->DMACRCR |= rxBufferSize << ETH_DMACRCR_RBSZ_Pos; + + // Configure spacing between descriptors. This will be different depending on + // cache line sizes. + // NOTE: Cast pointers to uint8_t so that the difference will be returned in bytes instead + // of elements. + const size_t rxSpacing = reinterpret_cast(&rxDescs[1]) - reinterpret_cast(&rxDescs[0]); + + // Check that spacing seems valid +#ifndef NDEBUG + const size_t txSpacing = reinterpret_cast(&txDescs[1]) - reinterpret_cast(&txDescs[0]); + MBED_ASSERT(rxSpacing == txSpacing); + MBED_ASSERT(rxSpacing % sizeof(uint32_t) == 0); +#endif + + // The spacing bitfield is configured as the number of 32-bit words to skip between descriptors. + // The descriptors have a default size of 16 bytes. + const size_t wordsToSkip = (rxSpacing - 16) / sizeof(uint32_t); + MBED_ASSERT(wordsToSkip <= 7); + heth.Instance->DMACCR &= ~ETH_DMACCR_DSL_Msk; + heth.Instance->DMACCR |= wordsToSkip << ETH_DMACCR_DSL_Pos; + + // Configure Rx descriptor ring + heth.Instance->DMACRDRLR = rxPoolSize - 1; // Ring size + heth.Instance->DMACRDLAR = reinterpret_cast(&rxDescs[0]); // Ring base address + heth.Instance->DMACRDTPR = reinterpret_cast(&rxDescs[0]); // Next descriptor (tail) pointer + + buildRxDescriptors(); + + // Configure Tx descriptor ring + heth.Instance->DMACTDRLR = MBED_CONF_STM32_EMAC_ETH_TXBUFNB - 1; // Ring size + heth.Instance->DMACTDLAR = reinterpret_cast(&txDescs[0]); // Ring base address + heth.Instance->DMACTDTPR = reinterpret_cast(&txDescs[0]); // Next descriptor (tail) pointer + + // Enable Rx and Tx DMA. NOTE: Typo in C++ headers, these should be called + // "DMACTXCR" and "DMACRXCR" + heth.Instance->DMACTCR |= ETH_DMACTCR_ST; + heth.Instance->DMACRCR |= ETH_DMACRCR_SR; + + // Clear Tx and Rx process stopped flags + heth.Instance->DMACSR = (ETH_DMACSR_TPS | ETH_DMACSR_RPS); + + // Start Rx thread + thread.start(mbed::callback(this, &STM32EthIPv2DMARings::macThread)); + + heth.gState = HAL_ETH_STATE_STARTED; + + return HAL_OK; + } + else + { + return HAL_ERROR; + } +} + +void STM32EthIPv2DMARings::rxISR() +{ + // First, we need to check if at least one DMA descriptor that is owned by the application + // has its last descriptor flag or error flag set, indicating we have received at least one complete packet + // or there is an error descriptor that can be reclaimed by the application. + // Note that we want to bias towards false positives here, because false positives just waste CPU time, + // while false negatives would cause packets to be dropped. + // So, for simplicity, we just check every descriptor currently owned by the application until we + // find one with the FS bit set or the error bits set. + // This could potentially produce a false positive if we do this in the middle of receiving + // an existing packet, but that is unlikely and will not cause anything bad to happen if it does. + + for(size_t descCount = 0; descCount < rxPoolSize; descCount++) + { + auto &descriptor = rxDescs[rxNextIndex]; + +#if __DCACHE_PRESENT + SCB_InvalidateDCache_by_Addr(&descriptor, sizeof(ETH_DMADescTypeDef)); +#endif + + if (descriptor.rxDesc.fromDMAFmt.dmaOwn) + { + // Descriptor owned by DMA. We are out of descriptors to process. + return; + } + if (descriptor.rxDesc.fromDMAFmt.context || descriptor.rxDesc.fromDMAFmt.errorSummary || descriptor.rxDesc.fromDMAFmt.lastDescriptor) + { + // Reclaimable descriptor or complete packet detected. + thread.flags_set(THREAD_FLAG_RX_DESC_AVAILABLE); + return; + } + } +} + +void STM32EthIPv2DMARings::txISR() +{ + thread.flags_set(THREAD_FLAG_TX_DESC_AVAILABLE); +} + +HAL_StatusTypeDef STM32EthIPv2DMARings::txPacket(net_stack_mem_buf_t * buf) +{ + // Step 1: Figure out if we can send this zero-copy, or if we need to copy it. + // Also note that each descriptor can store 2 buffers, so we need half as many descriptors + // as we have buffers, rounding up. + size_t neededDescs = (memory_manager.count_buffers(buf) + 1) / 2; + bool needToCopy = false; + if(neededDescs > MBED_CONF_STM32_EMAC_ETH_TXBUFNB) + { + // Packet uses too many buffers, we have to copy it into a continuous buffer. + needToCopy = true; + } + + if(!needToCopy) + { + net_stack_mem_buf_t * currBuf = buf; + while(currBuf != nullptr) + { + // If this buffer is passed down direct from the application, we will need to + // copy the packet. + if(memory_manager.get_lifetime(currBuf) == NetStackMemoryManager::Lifetime::VOLATILE) + { + needToCopy = true; + } + + // On STM32H7, the Ethernet DMA cannot access data in DTCM. So, if someone sends + // a packet with a data pointer in DTCM (e.g. a stack allocated payload), everything + // will break if we don't copy it first. +#ifdef MBED_RAM_BANK_SRAM_DTC_START + if(reinterpret_cast(memory_manager.get_ptr(currBuf)) >= MBED_RAM_BANK_SRAM_DTC_START && + reinterpret_cast(memory_manager.get_ptr(currBuf)) <= MBED_RAM_BANK_SRAM_DTC_START + MBED_RAM_BANK_SRAM_DTC_SIZE) + { + needToCopy = true; + } +#endif + + currBuf = memory_manager.get_next(currBuf); + } + } + + printf("Transmitting packet of length %lu in %zu buffers and %zu descs\n", + memory_manager.get_total_len(buf), memory_manager.count_buffers(buf), neededDescs); + + // Step 2: Copy packet if needed + if(needToCopy) + { + auto * newBuf = memory_manager.alloc_heap(memory_manager.get_total_len(buf), 0); + if(newBuf == nullptr) + { + // No free memory, drop packet + memory_manager.free(newBuf); + return HAL_ERROR; + } + + // We should have gotten just one contiguous buffer + MBED_ASSERT(memory_manager.get_next(newBuf) == nullptr); + neededDescs = 1; + + // Copy data over + memory_manager.copy_from_buf(memory_manager.get_ptr(newBuf), memory_manager.get_len(newBuf), buf); + memory_manager.free(buf); + buf = newBuf; + } + + // Step 3: Wait for needed amount of buffers to be available. + // Note that, in my experience, it's better to block here, as dropping the packet + // due to not having enough buffers can create weird effects when the application sends + // lots of packets at once. + while(txDescsOwnedByApplication < neededDescs) + { + eventFlags.wait_any_for(EVENT_FLAG_TX_DESC_AVAILABLE, rtos::Kernel::wait_for_u32_forever); + } + + // Step 4: Load buffer into descriptors and send + net_stack_mem_buf_t * currBuf = buf; + for(size_t descCount = 0; descCount < neededDescs; descCount++) + { + auto & currDesc = txDescs[txSendIndex]; + + // Set buffer 1 + currDesc.txDesc.toDMAFmt.buffer1Addr = static_cast(memory_manager.get_ptr(currBuf)); + currDesc.txDesc.toDMAFmt.buffer1Len = memory_manager.get_len(currBuf); + + // Set buffer 2 + currBuf = memory_manager.get_next(currBuf); + if(currBuf != nullptr) + { + currDesc.txDesc.toDMAFmt.buffer2Addr = memory_manager.get_ptr(currBuf); + currDesc.txDesc.toDMAFmt.buffer2Len = memory_manager.get_len(currBuf); + + // Move to next buffer + currBuf = memory_manager.get_next(currBuf); + } + else + { + currDesc.txDesc.toDMAFmt.buffer2Addr = nullptr; + currDesc.txDesc.toDMAFmt.buffer2Len = 0; + } + + if(currBuf == nullptr) + { + // Last descriptor, store buffer address for freeing + currDesc.packetFirstBuf = buf; + } + else + { + currDesc.packetFirstBuf = nullptr; + } + +// printf("Tx Ethernet buffer:"); +// for(size_t byteIdx = 0; byteIdx < currDesc.txDesc.toDMAFmt.buffer1Len; ++byteIdx) +// { +// printf(" %02" PRIx8, reinterpret_cast(currDesc.txDesc.toDMAFmt.buffer1Addr)[byteIdx]); +// } +// printf("\n"); +// +// printf("Tx Ethernet buffer2:"); +// for(size_t byteIdx = 0; byteIdx < currDesc.txDesc.toDMAFmt.buffer2Len; ++byteIdx) +// { +// printf(" %02" PRIx8, reinterpret_cast(currDesc.txDesc.toDMAFmt.buffer2Addr)[byteIdx]); +// } +// printf("\n"); + +#if __DCACHE_PRESENT + // Write buffers back to main memory + SCB_CleanDCache_by_Addr(const_cast(currDesc.txDesc.toDMAFmt.buffer1Addr), currDesc.txDesc.toDMAFmt.buffer1Len); + if(currDesc.txDesc.toDMAFmt.buffer2Addr != nullptr) + { + SCB_CleanDCache_by_Addr(const_cast(currDesc.txDesc.toDMAFmt.buffer2Addr), currDesc.txDesc.toDMAFmt.buffer2Len); + } +#endif + + // Enter a critical section, because we could run into weird corner cases if the + // interrupt executes while we are half done configuring this descriptor and updating + // the counters. + core_util_critical_section_enter(); + + // Configure settings. Note that we have to configure these every time as + // they get wiped away when the DMA gives back the descriptor + currDesc.txDesc.toDMAFmt._reserved = 0; + currDesc.txDesc.toDMAFmt.checksumInsertionCtrl = 0; // Mbed does not do checksum offload for now + currDesc.txDesc.toDMAFmt.tcpSegmentationEnable = false; // No TCP offload + currDesc.txDesc.toDMAFmt.tcpUDPHeaderLen = 0; // No TCP offload + currDesc.txDesc.toDMAFmt.srcMACInsertionCtrl = 0; // No MAC insertion + currDesc.txDesc.toDMAFmt.crcPadCtrl = 0; // Insert CRC and padding + currDesc.txDesc.toDMAFmt.lastDescriptor = currBuf == nullptr; + currDesc.txDesc.toDMAFmt.firstDescriptor = descCount == 0; + currDesc.txDesc.toDMAFmt.isContext = false; + currDesc.txDesc.toDMAFmt.vlanTagCtrl = 0; // No VLAN tag + currDesc.txDesc.toDMAFmt.intrOnCompletion = true; + currDesc.txDesc.toDMAFmt.timestampEnable = false; + currDesc.txDesc.toDMAFmt.dmaOwn = true; + +#if __DCACHE_PRESENT + // Write descriptor back to main memory + SCB_CleanDCache_by_Addr(&currDesc, sizeof(EthTxDescriptor)); +#endif + + // Update descriptor count and index + txDescsOwnedByApplication--; + txSendIndex = (txSendIndex + 1) % MBED_CONF_STM32_EMAC_ETH_TXBUFNB; + + core_util_critical_section_exit(); + + // Move tail pointer register to point to the descriptor after this descriptor. + // This tells the MAC to transmit until it reaches the given descriptor, then stop. + heth.Instance->DMACTDTPR = reinterpret_cast(&txDescs[txSendIndex]); + } + + return HAL_OK; +} + +} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h new file mode 100644 index 00000000000..188553a4938 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h @@ -0,0 +1,180 @@ +/* Copyright (c) 2024 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef MBED_OS_STM32ETHIPV2DMARINGS_H +#define MBED_OS_STM32ETHIPV2DMARINGS_H + +#include "EMACMemoryManager.h" +#include "EMAC.h" +#include "CacheAlignedBuffer.h" + +#include "stm32xx_emac_config.h" +#include "STM32IPv2EthDescriptors.h" + +#include "rtos/Thread.h" +#include "rtos/EventFlags.h" + +#include + + +#ifdef ETH_IP_VERSION_V2 + +namespace mbed +{ + +struct WrappedEthTxDescriptor +{ + EthTxDescriptor txDesc; + + // Pointer to first memory buffer in the chain associated with this descriptor. + // This shall only be filled in on the *last* packet, so that the entire chain is freed + // when the last descriptor is returned. + net_stack_mem_buf_t * packetFirstBuf; + + // If we have a data cache, we need each descriptor to be in its own cache line. So, + // pad up to 32 byte cache line size +#if __DCACHE_PRESENT + uint8_t _padding[__SCB_DCACHE_LINE_SIZE - sizeof(EthTxDescriptor) - sizeof(net_stack_mem_buf_t *)]; +#endif +}; + +#if __DCACHE_PRESENT +static_assert(sizeof(WrappedEthTxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Tx descriptor size must equal cache line size"); +#endif + +struct WrappedEthRxDescriptor +{ + EthRxDescriptor rxDesc; + + // Memory buffers filled in to this descriptor. + // These will be passed to the application if the reception was successful. + net_stack_mem_buf_t * buffer; + + // If we have a data cache, we need each descriptor to be in its own cache line. So, + // pad up to 32 byte cache line size +#if __DCACHE_PRESENT + uint8_t _padding[__SCB_DCACHE_LINE_SIZE - sizeof(EthRxDescriptor) - sizeof(net_stack_mem_buf_t *)]; +#endif +}; + +#if __DCACHE_PRESENT +static_assert(sizeof(WrappedEthRxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Rx descriptor size must equal cache line size"); +#endif + + +/** + * @brief Implementation of Ethernet DMA rings for STM32 Ethernet IP v2. + * + * This class contains logic largely adapted from the STM32 Ethernet HAL driver. + * It would have been nice to just use that driver directly, but it cannot adapt to + * support Mbed's memory manager implementation. + */ +class STM32EthIPv2DMARings +{ + EMACMemoryManager & memory_manager; /**< Memory manager */ + ETH_HandleTypeDef & heth; ///< Handle to Ethernet peripheral + EMAC::emac_link_input_cb_t emac_link_input_cb; /**< Callback for incoming packets */ + + // Event flags used to signal application threads from ISRs + rtos::EventFlags eventFlags; + + // Thread which runs the receive loop and the transmit buffer reclamation process + rtos::Thread thread; + + const size_t rxPoolSize; ///< Number of entries in the Rx buffer pool + + // Indexes for descriptor rings. + // NOTE: when working with these indices, it's important to consider the case where e.g. the send and reclaim indexes are + // equal. This could mean *either* that the Tx ring is completely full of data, or that the Tx ring is empty. + // To resolve this ambiguity, we maintain separate count variables that track how many entries are in the ring at present. + size_t rxBuildIndex; ///< Index of the next Rx descriptor that needs to be built. Updated by application and used by ISR. + size_t rxDescsOwnedByApplication; ///< Number of Rx descriptors owned by the application and needing buffers allocated. + mstd::atomic rxNextIndex; ///< Index of the next frame that the DMA shall populate. Updated by application but used by ISR. + + size_t txSendIndex; ///< Index of the next Tx descriptor that can be filled with data + mstd::atomic txDescsOwnedByApplication; ///< Number of Tx descriptors owned by the application. Incremented by the mac thread and decremented by the application thread. + size_t txReclaimIndex; ///< Index of the next Tx descriptor that will be reclaimed by the mac thread. + + // Descriptors + DynamicCacheAlignedBuffer rxDescs; + StaticCacheAlignedBuffer txDescs; + + // Tx buffers just need to be aligned to the nearest 4 bytes. + uint32_t txBuffers[MBED_CONF_STM32_EMAC_ETH_TXBUFNB][ETH_MAX_PACKET_SIZE / sizeof(uint32_t)]; + + // Return Rx descriptors to the Ethernet MAC. + // Descriptors can only be returned if there are free buffers in the pool to allocate to them. + // The first descriptor returned will be the one at RxBuildDescIdx + void buildRxDescriptors(); + + /// Receive the next packet. Call from the Rx thread when signal is delivered. + /// Returns nullptr if nothing was received. + net_stack_mem_buf_t * rxNextPacket(); + + /// Reclaims Tx buffers and frees their memory after packet transmission. + /// Invoked by the MAC thread when it sees a Tx interrupt. + void reclaimTxDescs(); + + /// MAC thread loop + void macThread(); + +public: + // Alignment required for Rx memory buffers. Normally they don't need alignment but + // if we are doing cache operations they need to be cache aligned. +#if __DCACHE_PRESENT + static constexpr size_t RX_BUFFER_ALIGN = __SCB_DCACHE_LINE_SIZE; +#else + static constexpr size_t RX_BUFFER_ALIGN = 2; +#endif + + /// How many extra buffers to leave in the Rx pool, relative to how many we keep assigned to Rx descriptors. + /// We want to keep some amount of extra buffers because constantly hitting the network stack with failed pool + /// allocations can produce some negative consequences in some cases. + static constexpr size_t RX_POOL_EXTRA_BUFFERS = 3; + + /// Payload size of buffers allocated from the Rx pool. This is the allocation unit size + /// of the pool minus any overhead needed for alignment. + const size_t rxPoolPayloadSize; + + STM32EthIPv2DMARings(EMACMemoryManager & memory_manager, ETH_HandleTypeDef & heth, EMAC::emac_link_input_cb_t emac_link_input_cb); + + ~STM32EthIPv2DMARings(); + + /** + * @brief Start DMA rings going. Based on HAL_ETH_Start(). + */ + HAL_StatusTypeDef startDMA(); + + /// Call when EMAC generates receive interrupt. Signals the Rx thread if there is a + /// new packet to receive. + void rxISR(); + + /// Call when EMAC generates transmit interrupt + void txISR(); + + /// Transmit a packet out of the Tx DMA ring. Note that this function + /// *takes ownership of* the passed packet and will free it either now or after + /// it's been transmitted. + /// Will block until there is space to transmit the packet. + HAL_StatusTypeDef txPacket(net_stack_mem_buf_t * buf); +}; + +} + +#endif + +#endif //MBED_OS_STM32ETHIPV2DMARINGS_H diff --git a/connectivity/drivers/emac/TARGET_STM/STM32IPv2EthDescriptors.h b/connectivity/drivers/emac/TARGET_STM/STM32IPv2EthDescriptors.h new file mode 100644 index 00000000000..87dccfa6eac --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/STM32IPv2EthDescriptors.h @@ -0,0 +1,170 @@ +/* Copyright (c) 2024 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM32IPV2ETHDESCRIPTORS_H +#define MBED_OS_STM32IPV2ETHDESCRIPTORS_H + +#include "stm32xx_emac_config.h" +#include "EMACMemoryManager.h" + +#ifdef ETH_IP_VERSION_V2 + +namespace mbed +{ + +// Tx descriptor ------------------------------------------------------------------------------------------ + +// Descriptor format written by the application to queue a packet for transmission. +// Note: Datasheet calls this the "read format" which is just nuts... +struct __attribute__((packed)) EthTxDescriptorToDMAFmt +{ + void const * buffer1Addr; + void const * buffer2Addr; + // TDES2 fields + uint16_t buffer1Len : 14; + uint8_t vlanTagCtrl : 2; + uint16_t buffer2Len : 14; + bool timestampEnable : 1; + bool intrOnCompletion : 1; + // TDES3 fields (not dealing with TCP offload for now) + uint16_t _reserved : 16; + uint8_t checksumInsertionCtrl : 2; + bool tcpSegmentationEnable : 1; + uint8_t tcpUDPHeaderLen : 4; + uint8_t srcMACInsertionCtrl : 3; + uint8_t crcPadCtrl : 2; + bool lastDescriptor : 1; + bool firstDescriptor: 1; + bool isContext : 1; + bool dmaOwn : 1; +}; + +// Write-back descriptor (DMA updates the desc with this format when complete) +struct __attribute__((packed)) EthTxDescriptorFromDMAFmt +{ + uint32_t timestampLow; + uint32_t timestampHigh; + uint32_t _reserved; + // TDES3 fields + bool ipHeaderError : 1; + bool deferred : 1; + bool underflowError : 1; + bool excessiveDeferral : 1; + uint8_t collisionCount : 4; + bool excessiveCollisions : 1; + bool lateCollision : 1; + bool noCarrier : 1; + bool lossOfCarrier : 1; + bool payloadChecksumError : 1; + bool packetFlushed : 1; + bool jabberTimeout : 1; + bool errorSummary: 1; + uint8_t _reserved0: 1; + bool txTimestampCaptured : 1; + uint16_t _reserved1 : 10; + bool lastDescriptor: 1; + bool firstDescriptor : 1; + bool context : 1; + bool dmaOwn : 1; +}; + +// Union of all possible descriptor formats. +// Note that per the datasheet, Tx descriptors must be word aligned. +union alignas(uint32_t) EthTxDescriptor { + EthTxDescriptorToDMAFmt toDMAFmt; + EthTxDescriptorFromDMAFmt fromDMAFmt; +}; + +// Rx descriptor ------------------------------------------------------------------------------------------ + +// Format when an Rx descriptor is returned to the DMA. +// This is called the "read format" in the datasheet. +struct __attribute__((packed)) EthRxDescriptorToDMAFmt +{ + void * buffer1Addr; + uint32_t _reserved0; + void * buffer2Addr; + uint32_t _reserved1: 24; + bool buffer1Valid: 1; + bool buffer2Valid: 1; + uint8_t _reserved2: 4; + bool intrOnCompletion: 1; + bool dmaOwn: 1; +}; + +// Format when an Rx descriptor is given to the application +// This is called the "write-back format" in the datasheet. +struct __attribute__((packed)) EthRxDescriptorFromDMAFmt +{ + // RDES0 fields + uint16_t outerVLANTag; + uint16_t innerVLANTag; + + // RDES1 fields + uint8_t payloadType: 3; + bool ipHeaderError: 1; + bool ipv4HeaderPresent: 1; + bool ipv6HeaderPresent: 1; + bool checksumOffloadBypassed: 1; + bool ipPayloadError: 1; + uint8_t ptpMsgType: 4; + bool ptpType: 1; + bool isPTPv2: 1; + bool tsAvailable: 1; + bool tsDropped: 1; + uint16_t oamOrMacCtrl; + + // RDES2 fields + uint16_t _reserved0: 10; + bool arpNotGenerated: 1; + uint8_t _reserved1: 4; + bool vlanFiltPassed : 1; + bool sourceAddrFail: 1; + bool destAddrFail: 1; + bool hashFilterStatus: 1; + uint8_t hashValOrMatchIdx: 8; + bool l3FiltMatch: 1; + bool l4FiltMatch: 1; + uint8_t filterMatchNo: 3; + + // RDES3 fields + uint16_t pktLength: 15; + bool errorSummary: 1; + uint8_t lengthType: 3; + bool dribbleError: 1; + bool phyRxErr: 1; + bool overflowErr: 1; + bool rxWdogTimeout: 1; + bool giantPkt: 1; + bool crcErr: 1; + bool rdes0Valid: 1; + bool rdes1Valid: 1; + bool rdes2Valid: 1; + bool lastDescriptor: 1; + bool firstDescriptor: 1; + bool context: 1; + bool dmaOwn: 1; +}; + +union alignas(uint32_t) EthRxDescriptor { + EthRxDescriptorToDMAFmt toDMAFmt; + EthRxDescriptorFromDMAFmt fromDMAFmt; +}; + +} + +#endif +#endif //MBED_OS_STM32IPV2ETHDESCRIPTORS_H diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c index a7d1444d7c8..0f05fa1e7dc 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c @@ -67,13 +67,6 @@ void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) { GPIO_InitTypeDef GPIO_InitStruct; if (heth->Instance == ETH) { -#if defined(CORE_CM7) - /* Disable DCache for STM32H7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); -#endif - /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); // __HAL_RCC_GPIOB_CLK_ENABLE(); diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c index b0fc3347124..36ff287e766 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c @@ -65,11 +65,6 @@ void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) { GPIO_InitTypeDef GPIO_InitStruct; if (heth->Instance == ETH) { - /* Disable DCache for STM32H7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); - /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/stm32h7_eth_init.c index 81be13b3c5b..6cc137bc24e 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/stm32h7_eth_init.c @@ -65,11 +65,6 @@ void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) { GPIO_InitTypeDef GPIO_InitStruct; if (heth->Instance == ETH) { - /* Disable DCache for STM32H7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); - /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c index a06114909c6..84937336706 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c @@ -65,11 +65,6 @@ void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) { GPIO_InitTypeDef GPIO_InitStruct; if (heth->Instance == ETH) { - /* Disable DCache for STM32H7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); - /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c index f9891ff757e..deb742200de 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c @@ -64,13 +64,6 @@ void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) if (heth->Instance == ETH) { enableEthPowerSupply(); -#if !(defined(DUAL_CORE) && defined(CORE_CM4)) - /* Disable DCache for STM32H7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); -#endif - /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); // __HAL_RCC_GPIOB_CLK_ENABLE(); diff --git a/connectivity/drivers/emac/TARGET_STM/mbed_lib.json b/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 similarity index 91% rename from connectivity/drivers/emac/TARGET_STM/mbed_lib.json rename to connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 index 1875925cf6c..859d9df0382 100644 --- a/connectivity/drivers/emac/TARGET_STM/mbed_lib.json +++ b/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 @@ -1,8 +1,10 @@ { "name": "stm32-emac", "config": { - "eth-rxbufnb": 4, - "eth-txbufnb": 4, + "eth-txbufnb": { + "help": "Number of Tx descriptors in the Ethernet MAC DMA ring.", + "value": 10 + }, "thread-stacksize": { "help": "Stack size for stm32_emac_thread", "value": 1024 @@ -49,13 +51,6 @@ } }, "target_overrides": { - "NUCLEO_F207ZG": { - "eth-rxbufnb": 2, - "eth-txbufnb": 4 - }, - "STM32H7": { - "eth-txbufnb": 10 - }, "ARCH_MAX": { "eth-phy-address": 1 } diff --git a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp index bcb6086bfd0..65d754f0268 100644 --- a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp @@ -63,13 +63,11 @@ #include "lan8742/lan8742.h" #include "lwip/memp.h" #include "lwip/api.h" -#include "linker_scripts/stm32_eth_region_size_calcs.h" #endif using namespace std::chrono; -/* \brief Flags for worker thread */ -#define FLAG_RX 1 + /** \brief Driver thread priority */ #define THREAD_PRIORITY (osPriorityHigh) @@ -80,18 +78,6 @@ using namespace std::chrono; #define STM_ETH_MTU_SIZE 1500 #define STM_ETH_IF_NAME "st" -#define ETH_RX_DESC_CNT MBED_CONF_STM32_EMAC_ETH_RXBUFNB -#define ETH_TX_DESC_CNT MBED_CONF_STM32_EMAC_ETH_TXBUFNB - -ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT] __attribute__((section(".EthDescriptors"))); /* Ethernet Rx DMA Descriptors */ -ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT] __attribute__((section(".EthDescriptors"))); /* Ethernet Tx DMA Descriptors */ - -// Rx buffer addresses need to be aligned 4 bytes and to cache lines because we cache invalidate the buffers after receiving them. -mbed::StaticCacheAlignedBuffer Rx_Buff[ETH_RX_DESC_CNT] __attribute__((section(".EthBuffers"))); /* Ethernet Receive Buffers */ - -// Tx buffers just need to be aligned to the nearest 4 bytes. -uint32_t Tx_Buff[ETH_TX_DESC_CNT][ETH_MAX_PACKET_SIZE / sizeof(uint32_t)] __attribute__((section(".EthBuffers"))); - #if defined(ETH_IP_VERSION_V2) static lan8742_Object_t LAN8742; @@ -110,9 +96,12 @@ static lan8742_IOCtx_t LAN8742_IOCtx = { ETH_PHY_IO_GetTick }; -static ETH_TxPacketConfig TxConfig; +#else -#endif // ETH_IP_VERSION_V2 +// For IP v1, we do not do zero copy Rx, so we have to allocate all the Tx buffers ahead of time. +// Rx buffer addresses need to be aligned 4 bytes and to cache lines because we cache invalidate the buffers after receiving them. +mbed::StaticCacheAlignedBuffer Rx_Buff[ETH_RX_DESC_CNT] __attribute__((section(".EthBuffers"))); /* Ethernet Receive Buffers */ +#endif MBED_WEAK uint8_t mbed_otp_mac_address(char *mac); void mbed_default_mac_address(char *mac); @@ -128,12 +117,6 @@ void ETH_IRQHandler(void); // weak symbol overrides and would otherwise be ignored. void stm32_eth_init_weak_symbol_helper(); -#ifdef USE_USER_DEFINED_HAL_ETH_IRQ_CALLBACK -MBED_WEAK void STM_HAL_ETH_Handler(); -#else -void STM_HAL_ETH_Handler(); -#endif - #ifdef __cplusplus } #endif @@ -184,61 +167,11 @@ bool _phy_is_up(int32_t phy_state) return phy_state > LAN8742_STATUS_LINK_DOWN; } -// Integer log2 of an integer. -// from https://stackoverflow.com/questions/994593/how-to-do-an-integer-log2-in-c -static inline uint32_t log2i(uint32_t x) { - return sizeof(uint32_t) * 8 - __builtin_clz(x) - 1; -} - -static void MPU_Config(void) -{ - MPU_Region_InitTypeDef MPU_InitStruct; - - /* Disable the MPU */ - HAL_MPU_Disable(); - - /* Configure the MPU attributes as Device not cacheable - for ETH DMA descriptors. The linker script puts these into their own - cordoned off, power-of-2 sized region. */ - MPU_InitStruct.Enable = MPU_REGION_ENABLE; - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; - MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; - MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; - MPU_InitStruct.Number = 4; // Mbed OS MPU config can use regions 0 through 3 - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; - MPU_InitStruct.SubRegionDisable = 0x00; - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; - - extern uint8_t __eth_descriptors_start[0]; // <-- defined in linker script - MPU_InitStruct.BaseAddress = reinterpret_cast(__eth_descriptors_start); - - // Use a logarithm to calculate the region size - MPU_InitStruct.Size = log2i(STM32_DMA_DESCRIP_REGION_SIZE) - 1; - - HAL_MPU_ConfigRegion(&MPU_InitStruct); - - /* Enable the MPU */ - HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); -} - #endif -/** - * Ethernet IRQ Handler - * - * @param None - * @retval None - */ -void ETH_IRQHandler(void) -{ - STM_HAL_ETH_Handler(); -} - -STM32_EMAC::STM32_EMAC() - : thread(0) +STM32_EMAC::STM32_EMAC(): #ifdef ETH_IP_VERSION_V2 - , phy_status(0) +phy_status(0) #endif { } @@ -336,13 +269,9 @@ bool STM32_EMAC::low_level_init_successful() } #else // ETH_IP_VERSION_V2 { - uint32_t idx; - // Generate a reference to this empty function so the linker pulls it in. stm32_eth_init_weak_symbol_helper(); - MPU_Config(); - /* Init ETH */ uint8_t MACAddr[6]; EthHandle.Instance = ETH; @@ -358,12 +287,8 @@ bool STM32_EMAC::low_level_init_successful() #endif EthHandle.Init.MACAddr = &MACAddr[0]; EthHandle.Init.MediaInterface = HAL_ETH_RMII_MODE; - EthHandle.Init.RxDesc = DMARxDscrTab; - EthHandle.Init.TxDesc = DMATxDscrTab; - EthHandle.Init.RxBuffLen = 1524; tr_debug("MAC Addr %02x:%02x:%02x:%02x:%02x:%02x", MACAddr[0], MACAddr[1], MACAddr[2], MACAddr[3], MACAddr[4], MACAddr[5]); - tr_info("ETH buffers : %u Rx %u Tx", ETH_RX_DESC_CNT, ETH_TX_DESC_CNT); if (HAL_ETH_Init(&EthHandle) != HAL_OK) { return false; @@ -375,15 +300,26 @@ bool STM32_EMAC::low_level_init_successful() // Enable multicast hash and perfect filter EthHandle.Instance->MACPFR = ETH_MACPFR_HMC | ETH_MACPFR_HPF; - memset(&TxConfig, 0, sizeof(ETH_TxPacketConfig)); - TxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD; - TxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC; - TxConfig.CRCPadCtrl = ETH_CRC_PAD_INSERT; + // Enable Tx, Rx, and fatal bus error interrupts. + // However, don't enable receive buffer unavailable interrupt, because that can + // trigger if we run out of Rx descriptors, and we don't want to fatal error + // in that case. + EthHandle.Instance->DMACIER = (ETH_DMACIER_NIE | ETH_DMACIER_RIE | ETH_DMACIER_TIE | + ETH_DMACIER_FBEE | ETH_DMACIER_AIE); + + // Disable checksum offload, this is enabled by HAL_ETH_Init + EthHandle.Instance->MACCR &= ~ETH_MACCR_IPC; - for (idx = 0; idx < ETH_RX_DESC_CNT; idx++) { - HAL_ETH_DescAssignMemory(&EthHandle, idx, reinterpret_cast(Rx_Buff[idx].data()), NULL); + // Init the DMA rings + dmaRings.emplace(*memory_manager, EthHandle, emac_link_input_cb); + if (dmaRings->startDMA() != HAL_OK) { + return false; } + // Enable the MAC transmission & reception + // TODO should only do this once the link goes up + EthHandle.Instance->MACCR |= ETH_MACCR_TE | ETH_MACCR_RE; + tr_info("low_level_init_successful"); return _phy_init(); } @@ -484,63 +420,10 @@ bool STM32_EMAC::link_out(emac_mem_buf_t *buf) } #else // ETH_IP_VERSION_V2 { - bool success = false; - uint32_t i = 0; - uint32_t frameLength = 0; - struct pbuf *q; - ETH_BufferTypeDef Txbuffer[ETH_TX_DESC_CNT]; - HAL_StatusTypeDef status; - struct pbuf *p = NULL; - p = (struct pbuf *)buf; /* Get exclusive access */ TXLockMutex.lock(); - memset(Txbuffer, 0, ETH_TX_DESC_CNT * sizeof(ETH_BufferTypeDef)); - - /* copy frame from pbufs to driver buffers */ - for (q = p; q != NULL; q = q->next) { - if (i >= ETH_TX_DESC_CNT) { - tr_error("Error : ETH_TX_DESC_CNT not sufficient"); - goto error; - } - - Txbuffer[i].buffer = (uint8_t *)q->payload; - Txbuffer[i].len = q->len; - frameLength += q->len; - - if (i > 0) { - Txbuffer[i - 1].next = &Txbuffer[i]; - } - - if (q->next == NULL) { - Txbuffer[i].next = NULL; - } - -#if defined(__DCACHE_PRESENT) - // For chips with a cache, we need to evict the Tx data from cache to main memory. - // This ensures that the DMA controller can see the most up-to-date copy of the data. - SCB_CleanDCache_by_Addr(Txbuffer[i].buffer, Txbuffer[i].len); -#endif - - i++; - } - - TxConfig.Length = frameLength; - TxConfig.TxBuffer = Txbuffer; - - status = HAL_ETH_Transmit(&EthHandle, &TxConfig, 50); - if (status == HAL_OK) { - success = 1; - } else { - tr_error("Error returned by HAL_ETH_Transmit (%d)", status); - success = 0; - } - -error: - - if (p->ref > 1) { - pbuf_free(p); - } + bool success = dmaRings->txPacket(buf) == HAL_OK; /* Restore access */ TXLockMutex.unlock(); @@ -549,6 +432,7 @@ bool STM32_EMAC::link_out(emac_mem_buf_t *buf) } #endif // ETH_IP_VERSION_V2 +#ifndef ETH_IP_VERSION_V2 /** * Should allocate a contiguous memory buffer and transfer the bytes of the incoming * packet to the buffer. @@ -559,7 +443,6 @@ bool STM32_EMAC::link_out(emac_mem_buf_t *buf) * zero when frame is received */ int STM32_EMAC::low_level_input(emac_mem_buf_t **buf) -#ifndef ETH_IP_VERSION_V2 { uint32_t len = 0; uint8_t *buffer; @@ -636,75 +519,8 @@ int STM32_EMAC::low_level_input(emac_mem_buf_t **buf) return 0; } #else // ETH_IP_VERSION_V2 -{ - ETH_BufferTypeDef RxBuff; - uint32_t frameLength = 0; - - if (HAL_ETH_GetRxDataBuffer(&EthHandle, &RxBuff) == HAL_OK) { - if (HAL_ETH_GetRxDataLength(&EthHandle, &frameLength) != HAL_OK) { - tr_error("Error: returned by HAL_ETH_GetRxDataLength"); - return -1; - } - - /* Build Rx descriptor to be ready for next data reception */ - HAL_ETH_BuildRxDescriptors(&EthHandle); - -#if defined(__DCACHE_PRESENT) - /* Invalidate data cache for ETH Rx Buffers */ - SCB_InvalidateDCache_by_Addr((uint32_t *)RxBuff.buffer, frameLength); -#endif - - *buf = pbuf_alloc(PBUF_RAW, frameLength, PBUF_POOL); - if (*buf) { - pbuf_take((struct pbuf *)*buf, RxBuff.buffer, frameLength); - } - } else { - return -1; - } - - return 0; -} #endif // ETH_IP_VERSION_V2 -/** \brief Attempt to read a packet from the EMAC interface. - * - */ -void STM32_EMAC::packet_rx() -{ - /* move received packet into a new buf */ - while (1) { - emac_mem_buf_t *p = NULL; - RXLockMutex.lock(); - if (low_level_input(&p) < 0) { - RXLockMutex.unlock(); - break; - } - if (p) { - emac_link_input_cb(p); - } - RXLockMutex.unlock(); - } -} - -/** \brief Worker thread. - * - * Woken by thread flags to receive packets or clean up transmit - * - * \param[in] pvParameters pointer to the interface data - */ -void STM32_EMAC::thread_function(void *pvParameters) -{ - static struct STM32_EMAC *stm32_enet = static_cast(pvParameters); - - for (;;) { - uint32_t flags = osThreadFlagsWait(FLAG_RX, osFlagsWaitAny, osWaitForever); - - if (flags & FLAG_RX) { - stm32_enet->packet_rx(); - } - } -} - /** * This task checks phy link status and updates net status */ @@ -879,13 +695,8 @@ bool STM32_EMAC::power_up() return false; } - /* Worker thread */ -#if MBED_CONF_MBED_TRACE_ENABLE - thread = create_new_thread("stm32_emac_thread", &STM32_EMAC::thread_function, this, MBED_CONF_STM32_EMAC_THREAD_STACKSIZE * 2, THREAD_PRIORITY, &thread_cb); -#else - thread = create_new_thread("stm32_emac_thread", &STM32_EMAC::thread_function, this, MBED_CONF_STM32_EMAC_THREAD_STACKSIZE, THREAD_PRIORITY, &thread_cb); -#endif - + /* Allow the PHY task to detect the initial link state and set up the proper flags */ + phy_task(); phy_task_handle = mbed::mbed_event_queue()->call_every(PHY_TASK_PERIOD, mbed::callback(this, &STM32_EMAC::phy_task)); @@ -894,11 +705,17 @@ bool STM32_EMAC::power_up() rmii_watchdog_thread = create_new_thread("stm32_rmii_watchdog", &STM32_EMAC::rmii_watchdog_thread_function, this, 128, THREAD_PRIORITY, &rmii_watchdog_thread_cb); #endif - /* Allow the PHY task to detect the initial link state and set up the proper flags */ - osDelay(10); - + // Set up interrupt handler + NVIC_SetVector(ETH_IRQn, reinterpret_cast(&STM32_EMAC::irqHandler)); enable_interrupts(); + /* Enable ETH DMA interrupts: + - Tx complete interrupt + - Rx complete interrupt + - Fatal bus interrupt + */ + ETH->DMACIER = ETH_DMACIER_NIE | ETH_DMACIER_RIE | ETH_DMACIER_TIE | ETH_DMACIER_FBEE | ETH_DMACIER_AIE; + return true; } @@ -1028,6 +845,36 @@ STM32_EMAC &STM32_EMAC::get_instance() return emac; } +void STM32_EMAC::irqHandler() +{ + uint32_t dma_flag = ETH->DMACSR; + + /* Packet received */ + if ((dma_flag & ETH_DMACSR_RI) != 0U) + { + /* Clear the Eth DMA Rx IT pending bits */ + ETH->DMACSR = ETH_DMACSR_RI | ETH_DMACSR_NIS; + + get_instance().dmaRings->rxISR(); + } + + /* Packet transmitted */ + if ((dma_flag & ETH_DMACSR_TI) != 0U) + { + /* Clear the Eth DMA Tx IT pending bits */ + ETH->DMACSR = ETH_DMACSR_TI | ETH_DMACSR_NIS; + + get_instance().dmaRings->txISR(); + } + + /* ETH DMA Error */ + if(dma_flag & ETH_DMACSR_FBE) + { + MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_ETHERNET, EIO), \ + "STM32 EMAC: Hardware reports fatal DMA Error\n"); + } +} + void STM32_EMAC::populateMcastFilterRegs() { const size_t NUM_PERFECT_FILTER_REGS = 3; @@ -1120,9 +967,7 @@ void STM32_EMAC::writeMACAddress(const uint8_t *MAC, volatile uint32_t *addrHigh /* Set MAC addr bits 0 to 31 */ *addrLowReg = (static_cast(MAC[3]) << 24) | (static_cast(MAC[2]) << 16) | (static_cast(MAC[1]) << 8) | static_cast(MAC[0]); -} - -// Weak so a module can override +}// Weak so a module can override MBED_WEAK EMAC &EMAC::get_default_instance() { return STM32_EMAC::get_instance(); @@ -1223,46 +1068,4 @@ void HAL_ETH_MACErrorCallback(ETH_HandleTypeDef *heth) } #endif // ETH_IP_VERSION_V2 -#ifndef USE_USER_DEFINED_HAL_ETH_IRQ_CALLBACK - -#define FLAG_RX 1 - -/** - * Override Ethernet Rx Transfer completed callback - * @param heth: ETH handle - * @retval None - */ -void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth) -{ - STM32_EMAC &emac = STM32_EMAC::get_instance(); - if (emac.thread) { - osThreadFlagsSet(emac.thread, FLAG_RX); - } -} - -/** - * Override the IRQ Handler - * @param None - * @retval None - */ -void STM_HAL_ETH_Handler() -{ - STM32_EMAC &emac = STM32_EMAC::get_instance(); - HAL_ETH_IRQHandler(&emac.EthHandle); -} - -#else /* USE_USER_DEFINED_HAL_ETH_IRQ_CALLBACK */ - -/** - * IRQ Handler - * - * @param heth: ETH handle - * @retval None - */ -MBED_WEAK void STM_HAL_ETH_Handler() -{ -} - -#endif /* USE_USER_DEFINED_HAL_ETH_IRQ_CALLBACK */ - #endif /* DEVICE_EMAC */ diff --git a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h index e4c81646a45..596dd150cd0 100644 --- a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h +++ b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h @@ -20,6 +20,11 @@ #include "EMAC.h" #include "rtos/Mutex.h" +#include "rtos/Thread.h" + +#include "STM32EthIPv2DMARings.h" + +#include class STM32_EMAC : public EMAC { public: @@ -150,17 +155,19 @@ class STM32_EMAC : public EMAC { // Called from driver functions ETH_HandleTypeDef EthHandle; - osThreadId_t thread; /**< Processing thread */ + private: + bool low_level_init_successful(); void packet_rx(); int low_level_input(emac_mem_buf_t **buf); - static void thread_function(void *pvParameters); + void thread_function(); static void rmii_watchdog_thread_function(void *pvParameters); void phy_task(); void enable_interrupts(); void disable_interrupts(); + static void irqHandler(); // Populate multicast filter registers with the contents of mcastMacs. // Uses the perfect filter registers first, then the hash filter. @@ -176,11 +183,12 @@ class STM32_EMAC : public EMAC { osThreadId_t rmii_watchdog_thread; /**< Watchdog processing thread */ #endif rtos::Mutex TXLockMutex;/**< TX critical section mutex */ - rtos::Mutex RXLockMutex;/**< RX critical section mutex */ - emac_link_input_cb_t emac_link_input_cb; /**< Callback for incoming data */ emac_link_state_change_cb_t emac_link_state_cb; /**< Link state change callback */ + emac_link_input_cb_t emac_link_input_cb; /**< Callback for incoming packets */ EMACMemoryManager *memory_manager; /**< Memory manager */ + std::optional dmaRings; + uint32_t phy_status; int phy_task_handle; /**< Handle for phy task event */ diff --git a/connectivity/lwipstack/mbed_lib.json5 b/connectivity/lwipstack/mbed_lib.json5 index 5ea4a99ec98..0de4f1750a2 100644 --- a/connectivity/lwipstack/mbed_lib.json5 +++ b/connectivity/lwipstack/mbed_lib.json5 @@ -207,6 +207,10 @@ }, "MTS_DRAGONFLY_F411RE": { "tcpip-thread-stacksize": 1600 + }, + "STM32H7": { + // On STM32H7, we have lots of RAM, so we can make the pool size relatively large + "pbuf-pool-size": 16, } } } diff --git a/connectivity/nanostack/mbed-mesh-api/mbed_lib.json b/connectivity/nanostack/mbed-mesh-api/mbed_lib.json index 59db804a0c1..e053940ec26 100644 --- a/connectivity/nanostack/mbed-mesh-api/mbed_lib.json +++ b/connectivity/nanostack/mbed-mesh-api/mbed_lib.json @@ -236,6 +236,14 @@ "radius-retry-count": { "help": "RADIUS retry trickle count; default 3", "value": 3 + }, + "emac-rx-pool-size": { + "help": "Number of Rx buffers that will allocated to the EMAC receive buffer pool when using an Ethernet MAC interface", + "value": 5 + }, + "emac-rx-pool-bufsize": { + "help": "Size of each buffer in the EMAC receive pool.", + "value": 512 } }, "target_overrides": { diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast_frame_len.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast_frame_len.cpp index d013dd1b304..c51e4166b58 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast_frame_len.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast_frame_len.cpp @@ -65,6 +65,8 @@ void test_emac_unicast_frame_len_cb(int opt) retries = 0; send_request = true; } + + printf("Regs: ETH_RX_UNICAST_PACKETS_GOOD=%lu, ETH_DMACCARXDR=%p\n", ETH->MMCRUPGR, (void*)ETH->DMACCARDR); } void test_emac_unicast_frame_len() diff --git a/targets/TARGET_STM/README.md b/targets/TARGET_STM/README.md index 88639ebccc8..57513f45587 100644 --- a/targets/TARGET_STM/README.md +++ b/targets/TARGET_STM/README.md @@ -504,21 +504,6 @@ https://github.com/ARMmbed/mbed-os/blob/master/connectivity/drivers/emac/TARGET_ Option is also to define your own `HAL_ETH_MspInit` function, you then have to add **USE_USER_DEFINED_HAL_ETH_MSPINIT** macro. -#### Custom IRQ Handler and Callback from user application -To use the custom IRQ Handler and the callbacks, you need to add -**USE_USER_DEFINED_HAL_ETH_IRQ_CALLBACK** macro -inside any of the JASON file in either targets.json or in mbed_app.json file. - -For example in the targets.json, -you need to add this line in your target: -```"macros_add": ["USE_USER_DEFINED_HAL_ETH_IRQ_CALLBACK"],``` -or for example in the mbed_app.json, you need to add: -``` "macros": ["USE_USER_DEFINED_HAL_ETH_IRQ_CALLBACK"]``` - -By doing the any of the above json files, the corresponding user defined custom apis like -HAL_ETH_RxCpltCallback() and STM_HAL_ETH_Handler() can be called from -the user application. - #### Changing default MAC address in STM32 To change the default MAC address in STM32, If we have the function mbed_otp_mac_address() in the user application,the default ethernet address diff --git a/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/CMakeLists.txt b/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/CMakeLists.txt index 7ef51581452..e7f395df3bc 100644 --- a/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/CMakeLists.txt +++ b/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/CMakeLists.txt @@ -26,11 +26,8 @@ target_sources(mbed-stm32h7cube-fw STM32H7xx_HAL_Driver/stm32h7xx_hal_dma_ex.c STM32H7xx_HAL_Driver/stm32h7xx_hal_dsi.c STM32H7xx_HAL_Driver/stm32h7xx_hal_dts.c - - # Temporary: using legacy Ethernet driver for now - STM32H7xx_HAL_Driver/Legacy/stm32h7xx_hal_eth.c - STM32H7xx_HAL_Driver/Legacy/stm32h7xx_hal_eth_ex.c - + STM32H7xx_HAL_Driver/stm32h7xx_hal_eth.c + STM32H7xx_HAL_Driver/stm32h7xx_hal_eth_ex.c STM32H7xx_HAL_Driver/stm32h7xx_hal_exti.c STM32H7xx_HAL_Driver/stm32h7xx_hal_fdcan.c STM32H7xx_HAL_Driver/stm32h7xx_hal_flash.c diff --git a/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/stm32h7xx_hal_conf.h b/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/stm32h7xx_hal_conf.h index a37776edf7e..41d1fbc2a46 100644 --- a/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/stm32h7xx_hal_conf.h +++ b/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/stm32h7xx_hal_conf.h @@ -50,10 +50,7 @@ #define HAL_DTS_MODULE_ENABLED #define HAL_DSI_MODULE_ENABLED -// Temporary: using legacy Ethernet driver for now -// #define HAL_ETH_MODULE_ENABLED -#define HAL_ETH_LEGACY_MODULE_ENABLED - +#define HAL_ETH_MODULE_ENABLED #define HAL_EXTI_MODULE_ENABLED #define HAL_FDCAN_MODULE_ENABLED #define HAL_FLASH_MODULE_ENABLED diff --git a/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H743_STM32H72x_FAMILIES/STM32H743_H72x.ld b/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H743_STM32H72x_FAMILIES/STM32H743_H72x.ld index 33988534985..2ffdfe63eea 100644 --- a/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H743_STM32H72x_FAMILIES/STM32H743_H72x.ld +++ b/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H743_STM32H72x_FAMILIES/STM32H743_H72x.ld @@ -16,7 +16,6 @@ */ #include "cmsis_nvic.h" -#include "../stm32_eth_region_size_calcs.h" M_CRASH_DATA_RAM_SIZE = 0x100; @@ -212,20 +211,6 @@ SECTIONS __StackLimit = __StackTop - MBED_CONF_TARGET_BOOT_STACK_SIZE; PROVIDE(__stack = __StackTop); - /* Ethernet DMA descriptors should be at the start of SRAM_D2 because they need an MPU region - and because the CM4 and CM7 have to agree on their location.*/ - .eth_descriptors (NOLOAD) : { - ASSERT(. == ORIGIN(SRAM_D2), "Eth Descriptors region must be at the start of SRAM_D2"); - PROVIDE(__eth_descriptors_start = .); - *(.EthDescriptors) - . = __eth_descriptors_start + STM32_DMA_DESCRIP_REGION_SIZE; - } >SRAM_D2 - - /* Ethernet buffers are recommended to be in SRAM_D2 but don't need any special MPU region. */ - .eth_buffers (NOLOAD) : { - *(.EthBuffers) - } >SRAM_D2 - /* Use SRAM_D2 as additional heap */ .heap (NOLOAD): { diff --git a/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H745_47_FAMILY/CM4/STM32H745_H747_CM4.ld b/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H745_47_FAMILY/CM4/STM32H745_H747_CM4.ld index 0d5afd00959..6b999870b7e 100644 --- a/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H745_47_FAMILY/CM4/STM32H745_H747_CM4.ld +++ b/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H745_47_FAMILY/CM4/STM32H745_H747_CM4.ld @@ -16,7 +16,6 @@ */ #include "cmsis_nvic.h" -#include "../../stm32_eth_region_size_calcs.h" M_CRASH_DATA_RAM_SIZE = 0x100; @@ -111,15 +110,6 @@ SECTIONS __etext = .; - /* Ethernet DMA descriptors should be at the start of SRAM_D2 because they need an MPU region - and because the CM4 and CM7 have to agree on their location.*/ - .eth_descriptors (NOLOAD) : { - ASSERT(. == ORIGIN(SRAM_D2), "Eth Descriptors region must be at the start of SRAM_D2"); - PROVIDE(__eth_descriptors_start = .); - *(.EthDescriptors) - . = __eth_descriptors_start + STM32_DMA_DESCRIP_REGION_SIZE; - } >SRAM_D2 - _sidata = .; .data : AT (__etext) @@ -222,9 +212,4 @@ SECTIONS . = ALIGN(8); __CRASH_DATA_RAM_END__ = .; /* Define a global symbol at data end */ } > SRAM_D3 - - /* Ethernet buffers are recommended to be in SRAM_D2 but don't need any special MPU region. */ - .eth_buffers (NOLOAD) : { - *(.EthBuffers) - } >SRAM_D2 } diff --git a/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H745_47_FAMILY/CM7/STM32H745_H747_CM7.ld b/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H745_47_FAMILY/CM7/STM32H745_H747_CM7.ld index 144b0fdf3e8..dadb368cb41 100644 --- a/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H745_47_FAMILY/CM7/STM32H745_H747_CM7.ld +++ b/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H745_47_FAMILY/CM7/STM32H745_H747_CM7.ld @@ -16,7 +16,6 @@ */ #include "cmsis_nvic.h" -#include "../../stm32_eth_region_size_calcs.h" M_CRASH_DATA_RAM_SIZE = 0x100; @@ -203,21 +202,6 @@ SECTIONS /* Check if data + heap + stack exceeds SRAM limit */ ASSERT(__StackLimit >= __HeapLimit, "region SRAM overflowed with stack") - /* Ethernet DMA descriptors should be at the start of SRAM_D2 because they need an MPU region - and because the CM4 and CM7 have to agree on their location.*/ - .eth_descriptors (NOLOAD) : { - ASSERT(. == ORIGIN(SRAM_D2), "Eth Descriptors region must be at the start of SRAM_D2"); - PROVIDE(__eth_descriptors_start = .); - *(.EthDescriptors) - . = __eth_descriptors_start + STM32_DMA_DESCRIP_REGION_SIZE; - } >SRAM_D2 - - /* Ethernet buffers are recommended to be in SRAM_D2 but not required to be, so we will - put them in SRAM since SRAM2 is used by the CM4 */ - .eth_buffers (NOLOAD) : { - *(.EthBuffers) - } >SRAM - /* Put crash data in the otherwise unused D3 SRAM */ .crash_data_ram : { diff --git a/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H7Ax_FAMILY/STM32H7Ax.ld b/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H7Ax_FAMILY/STM32H7Ax.ld index 07a4d5039ee..f930e0e28c7 100644 --- a/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H7Ax_FAMILY/STM32H7Ax.ld +++ b/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/STM32H7Ax_FAMILY/STM32H7Ax.ld @@ -16,7 +16,6 @@ */ #include "cmsis_nvic.h" -#include "../stm32_eth_region_size_calcs.h" M_CRASH_DATA_RAM_SIZE = 0x100; @@ -212,19 +211,6 @@ SECTIONS __CRASH_DATA_RAM_END__ = .; /* Define a global symbol at data end */ } > SRAM_D3 - /* Ethernet DMA descriptors should be at the start of SRAM_AXI because they need an MPU region.*/ - .eth_descriptors (NOLOAD) : { - ASSERT(. == ORIGIN(SRAM_AXI), "Eth Descriptors region must be at the start of SRAM_D2"); - PROVIDE(__eth_descriptors_start = .); - *(.EthDescriptors) - . = __eth_descriptors_start + STM32_DMA_DESCRIP_REGION_SIZE; - } >SRAM_AXI - - /* Ethernet buffers are recommended to be in SRAM_AXI but don't need any special MPU region. */ - .eth_buffers (NOLOAD) : { - *(.EthBuffers) - } >SRAM_AXI - /* Use SRAM_AXI as additional heap */ .heap (NOLOAD): { diff --git a/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/stm32_eth_region_size_calcs.h b/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/stm32_eth_region_size_calcs.h deleted file mode 100644 index 16f3499be33..00000000000 --- a/targets/TARGET_STM/TARGET_STM32H7/linker_scripts/stm32_eth_region_size_calcs.h +++ /dev/null @@ -1,51 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2024 Jamie Smith - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * This file contains calculations for the size of the memory region used for the Ethernet buffer descriptors. - * This region must be a power-of-2 size so that it can be used as an MPU region. Also, in dual core - * CPUs, the CM4 and CM7 have to agree on its size so they don't define conflicting memory regions. - * - * So, this header has some fancy math to calculate it. Note that this header is included by - * linker scripts so it can only contain preprocessor code. - */ - -/* from here: https://stackoverflow.com/questions/22925016/rounding-up-to-powers-of-2-with-preprocessor-constants */ -#define POW2_CEIL(v) (1 + \ -(((((((((v) - 1) | (((v) - 1) >> 0x10) | \ - (((v) - 1) | (((v) - 1) >> 0x10) >> 0x08)) | \ - ((((v) - 1) | (((v) - 1) >> 0x10) | \ - (((v) - 1) | (((v) - 1) >> 0x10) >> 0x08)) >> 0x04))) | \ - ((((((v) - 1) | (((v) - 1) >> 0x10) | \ - (((v) - 1) | (((v) - 1) >> 0x10) >> 0x08)) | \ - ((((v) - 1) | (((v) - 1) >> 0x10) | \ - (((v) - 1) | (((v) - 1) >> 0x10) >> 0x08)) >> 0x04))) >> 0x02))) | \ - ((((((((v) - 1) | (((v) - 1) >> 0x10) | \ - (((v) - 1) | (((v) - 1) >> 0x10) >> 0x08)) | \ - ((((v) - 1) | (((v) - 1) >> 0x10) | \ - (((v) - 1) | (((v) - 1) >> 0x10) >> 0x08)) >> 0x04))) | \ - ((((((v) - 1) | (((v) - 1) >> 0x10) | \ - (((v) - 1) | (((v) - 1) >> 0x10) >> 0x08)) | \ - ((((v) - 1) | (((v) - 1) >> 0x10) | \ - (((v) - 1) | (((v) - 1) >> 0x10) >> 0x08)) >> 0x04))) >> 0x02))) >> 0x01)))) - -/* Size of an ETH_DMADescTypeDef structure in bytes*/ -#define STM32_SIZEOF_ETH_DMA_DESCRIPTOR 24 - -/* Calculation of RAM size */ -#define STM32_TOTAL_DMA_DESCRIPTOR_RAM_NEEDED (STM32_SIZEOF_ETH_DMA_DESCRIPTOR * (MBED_CONF_STM32_EMAC_ETH_RXBUFNB + MBED_CONF_STM32_EMAC_ETH_TXBUFNB)) -#define STM32_DMA_DESCRIP_REGION_SIZE POW2_CEIL(STM32_TOTAL_DMA_DESCRIPTOR_RAM_NEEDED) \ No newline at end of file diff --git a/tools/cmake/toolchains/GCC_ARM.cmake b/tools/cmake/toolchains/GCC_ARM.cmake index 8669f03eed2..ae807b01be7 100644 --- a/tools/cmake/toolchains/GCC_ARM.cmake +++ b/tools/cmake/toolchains/GCC_ARM.cmake @@ -61,6 +61,7 @@ list(APPEND common_options "-Wno-unused-parameter" "-Wno-missing-field-initializers" "-Wno-psabi" # Disable "parameter passing changed in GCC 7.1" warning + "-Wno-packed-bitfield-compat" # Disable "offset of packed bitfield changed in GCC 4.4" warning "-fmessage-length=0" "-fno-exceptions" "-ffunction-sections" From 9d65ab2197194776a998e34d32b80f93737eab46 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Mon, 20 Jan 2025 10:03:35 -0800 Subject: [PATCH 02/47] multicast test working now! --- connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp index 65d754f0268..69499d55e1a 100644 --- a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp @@ -905,7 +905,7 @@ void STM32_EMAC::populateMcastFilterRegs() { if(perfFiltIdx < numPerfectFilterMacs) { - tr_debug("Using perfect filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, + tr_info("Using perfect filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, mcastMacs[perfFiltIdx][0], mcastMacs[perfFiltIdx][1], mcastMacs[perfFiltIdx][2], mcastMacs[perfFiltIdx][3], mcastMacs[perfFiltIdx][4], mcastMacs[perfFiltIdx][5]); writeMACAddress(mcastMacs[perfFiltIdx].data(), highReg, lowReg); @@ -945,7 +945,7 @@ void STM32_EMAC::populateMcastFilterRegs() { { auto & currMacAddr = mcastMacs[hashFiltIdx + numPerfectFilterMacs]; - tr_debug("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, + tr_info("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, currMacAddr[0], currMacAddr[1], currMacAddr[2], currMacAddr[3], currMacAddr[4], currMacAddr[5]); From 970511595e40874f832b2f89f54c43c8a675775e Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Mon, 20 Jan 2025 11:07:29 -0800 Subject: [PATCH 03/47] Working with no hacky workarounds!!!11!11! --- .../emac/TARGET_STM/STM32EthIPv2DMARings.cpp | 52 +++++++++---------- .../emac/TARGET_STM/STM32EthIPv2DMARings.h | 6 ++- .../drivers/emac/TARGET_STM/stm32xx_emac.cpp | 6 +-- .../include/netsocket/NetStackMemoryManager.h | 12 +++++ .../emac/emac_test_unicast_frame_len.cpp | 2 - 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp index cad3c8fbd50..5b89a828e99 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp @@ -24,15 +24,18 @@ namespace mbed { // Thread flag constants -static uint32_t THREAD_FLAG_TX_DESC_AVAILABLE = 1; -static uint32_t THREAD_FLAG_RX_DESC_AVAILABLE = 2; -static uint32_t THREAD_FLAG_SHUTDOWN = 4; +static uint32_t THREAD_FLAG_TX_DESC_AVAILABLE = 1 << 0; +static uint32_t THREAD_FLAG_RX_DESC_AVAILABLE = 1 << 1; +static uint32_t THREAD_FLAG_RX_MEM_AVAILABLE = 1 << 2; +static uint32_t THREAD_FLAG_SHUTDOWN = 1 << 3; // Event flag constants static uint32_t EVENT_FLAG_TX_DESC_AVAILABLE = 1; void STM32EthIPv2DMARings::buildRxDescriptors() { + const size_t origRxDescsOwnedByApplication = rxDescsOwnedByApplication; + // Note: With this Ethernet peripheral, you can never give back every single descriptor to // the hardware, because then it thinks there are 0 descriptors left. while (rxDescsOwnedByApplication > 1) { @@ -72,6 +75,8 @@ void STM32EthIPv2DMARings::buildRxDescriptors() { // to one location after the last DMA-owned descriptor in the FIFO. heth.Instance->DMACRDTPR = reinterpret_cast(&rxDescs[rxBuildIndex]); } + + tr_debug("buildRxDescriptors(): Returned %zu descriptors.", origRxDescsOwnedByApplication - rxDescsOwnedByApplication); } // TODO monitor for Rx buffer exhaustion conditions. This happens when we have too few @@ -146,7 +151,6 @@ net_stack_mem_buf_t *STM32EthIPv2DMARings::rxNextPacket() { // No complete packet identified. // Take the chance to rebuild any available descriptors, then return. tr_debug("No complete packets in Rx descs\n"); - buildRxDescriptors(); return nullptr; } @@ -188,13 +192,10 @@ net_stack_mem_buf_t *STM32EthIPv2DMARings::rxNextPacket() { } #endif - tr_info("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu (%p-%p)\n", + tr_debug("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu (%p-%p)\n", memory_manager.get_total_len(headBuffer), memory_manager.get_ptr(headBuffer), firstDescIdx, lastDescIdx, &rxDescs[firstDescIdx], &rxDescs[lastDescIdx]); - // Rebuild descriptors if possible - buildRxDescriptors(); - return headBuffer; } @@ -248,12 +249,12 @@ void STM32EthIPv2DMARings::macThread() while(true) { // Wait for something to happen - uint32_t flags = rtos::ThisThread::flags_wait_any(THREAD_FLAG_TX_DESC_AVAILABLE | THREAD_FLAG_SHUTDOWN | THREAD_FLAG_RX_DESC_AVAILABLE); + uint32_t flags = rtos::ThisThread::flags_wait_any(THREAD_FLAG_TX_DESC_AVAILABLE | THREAD_FLAG_SHUTDOWN | THREAD_FLAG_RX_DESC_AVAILABLE | THREAD_FLAG_RX_MEM_AVAILABLE); if(flags & THREAD_FLAG_SHUTDOWN) { return; } - if(flags & (THREAD_FLAG_RX_DESC_AVAILABLE | THREAD_FLAG_TX_DESC_AVAILABLE)) // TODO temp + if(flags & THREAD_FLAG_RX_DESC_AVAILABLE) { // Receive any available packets. // Note that if the ISR was delayed, we might get multiple packets per ISR, so we need to loop. @@ -264,6 +265,9 @@ void STM32EthIPv2DMARings::macThread() break; } + // Rebuild descriptors if possible + buildRxDescriptors(); + if(emac_link_input_cb) { emac_link_input_cb(packet); @@ -278,9 +282,16 @@ void STM32EthIPv2DMARings::macThread() { reclaimTxDescs(); } + if(flags & THREAD_FLAG_RX_MEM_AVAILABLE) { + buildRxDescriptors(); + } } } +void STM32EthIPv2DMARings::onPoolBufferAvail() { + thread.flags_set(THREAD_FLAG_RX_MEM_AVAILABLE); +} + STM32EthIPv2DMARings::STM32EthIPv2DMARings(EMACMemoryManager &memory_manager, ETH_HandleTypeDef & heth, EMAC::emac_link_input_cb_t emac_link_input_cb): memory_manager(memory_manager), @@ -364,6 +375,9 @@ HAL_StatusTypeDef STM32EthIPv2DMARings::startDMA() // Clear Tx and Rx process stopped flags heth.Instance->DMACSR = (ETH_DMACSR_TPS | ETH_DMACSR_RPS); + // Register memory available callback + memory_manager.set_on_pool_space_avail_cb(callback(this, &STM32EthIPv2DMARings::onPoolBufferAvail)); + // Start Rx thread thread.start(mbed::callback(this, &STM32EthIPv2DMARings::macThread)); @@ -391,7 +405,7 @@ void STM32EthIPv2DMARings::rxISR() for(size_t descCount = 0; descCount < rxPoolSize; descCount++) { - auto &descriptor = rxDescs[rxNextIndex]; + auto &descriptor = rxDescs[(rxNextIndex + descCount) % rxPoolSize]; #if __DCACHE_PRESENT SCB_InvalidateDCache_by_Addr(&descriptor, sizeof(ETH_DMADescTypeDef)); @@ -456,7 +470,7 @@ HAL_StatusTypeDef STM32EthIPv2DMARings::txPacket(net_stack_mem_buf_t * buf) } } - printf("Transmitting packet of length %lu in %zu buffers and %zu descs\n", + tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", memory_manager.get_total_len(buf), memory_manager.count_buffers(buf), neededDescs); // Step 2: Copy packet if needed @@ -525,20 +539,6 @@ HAL_StatusTypeDef STM32EthIPv2DMARings::txPacket(net_stack_mem_buf_t * buf) currDesc.packetFirstBuf = nullptr; } -// printf("Tx Ethernet buffer:"); -// for(size_t byteIdx = 0; byteIdx < currDesc.txDesc.toDMAFmt.buffer1Len; ++byteIdx) -// { -// printf(" %02" PRIx8, reinterpret_cast(currDesc.txDesc.toDMAFmt.buffer1Addr)[byteIdx]); -// } -// printf("\n"); -// -// printf("Tx Ethernet buffer2:"); -// for(size_t byteIdx = 0; byteIdx < currDesc.txDesc.toDMAFmt.buffer2Len; ++byteIdx) -// { -// printf(" %02" PRIx8, reinterpret_cast(currDesc.txDesc.toDMAFmt.buffer2Addr)[byteIdx]); -// } -// printf("\n"); - #if __DCACHE_PRESENT // Write buffers back to main memory SCB_CleanDCache_by_Addr(const_cast(currDesc.txDesc.toDMAFmt.buffer1Addr), currDesc.txDesc.toDMAFmt.buffer1Len); diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h index 188553a4938..c3ac05f4b0b 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h @@ -118,7 +118,8 @@ class STM32EthIPv2DMARings // Return Rx descriptors to the Ethernet MAC. // Descriptors can only be returned if there are free buffers in the pool to allocate to them. - // The first descriptor returned will be the one at RxBuildDescIdx + // The first descriptor returned will be the one at RxBuildDescIdx. + // After init, this shall only be called by the MAC thread. void buildRxDescriptors(); /// Receive the next packet. Call from the Rx thread when signal is delivered. @@ -132,6 +133,9 @@ class STM32EthIPv2DMARings /// MAC thread loop void macThread(); + /// Callback from memory manager when a pool buffer becomes available + void onPoolBufferAvail(); + public: // Alignment required for Rx memory buffers. Normally they don't need alignment but // if we are doing cache operations they need to be cache aligned. diff --git a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp index 69499d55e1a..a86be016784 100644 --- a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp @@ -828,8 +828,6 @@ void STM32_EMAC::set_all_multicast(bool all) void STM32_EMAC::power_down() { - tr_info("power_down"); - /* No-op at this stage */ sleep_manager_unlock_deep_sleep(); } @@ -905,7 +903,7 @@ void STM32_EMAC::populateMcastFilterRegs() { if(perfFiltIdx < numPerfectFilterMacs) { - tr_info("Using perfect filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, + tr_debug("Using perfect filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, mcastMacs[perfFiltIdx][0], mcastMacs[perfFiltIdx][1], mcastMacs[perfFiltIdx][2], mcastMacs[perfFiltIdx][3], mcastMacs[perfFiltIdx][4], mcastMacs[perfFiltIdx][5]); writeMACAddress(mcastMacs[perfFiltIdx].data(), highReg, lowReg); @@ -945,7 +943,7 @@ void STM32_EMAC::populateMcastFilterRegs() { { auto & currMacAddr = mcastMacs[hashFiltIdx + numPerfectFilterMacs]; - tr_info("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, + tr_debug("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, currMacAddr[0], currMacAddr[1], currMacAddr[2], currMacAddr[3], currMacAddr[4], currMacAddr[5]); diff --git a/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h b/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h index c58397b736f..8548b3d0cd3 100644 --- a/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h +++ b/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h @@ -259,6 +259,18 @@ class NetStackMemoryManager { onPoolSpaceAvailCallback = cb; } + /** + * @brief Set callback which will be called when pool space becomes available + * + * \warning The callback could be called from any thread, and should make no assumptions about + * being in the same thread as anything else. + * + * @param cb Callback to call + */ + void set_on_pool_space_avail_cb(mbed::Callback cb) { + onPoolSpaceAvailCallback = cb; + } + protected: ~NetStackMemoryManager() = default; }; diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast_frame_len.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast_frame_len.cpp index c51e4166b58..d013dd1b304 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast_frame_len.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast_frame_len.cpp @@ -65,8 +65,6 @@ void test_emac_unicast_frame_len_cb(int opt) retries = 0; send_request = true; } - - printf("Regs: ETH_RX_UNICAST_PACKETS_GOOD=%lu, ETH_DMACCARXDR=%p\n", ETH->MMCRUPGR, (void*)ETH->DMACCARDR); } void test_emac_unicast_frame_len() From 80b2514226b193bbe97601406184c2c494fc0b0d Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 21 Jan 2025 23:20:21 -0800 Subject: [PATCH 04/47] Update Nanostack memory manager, run formatter, make tests test nanostack --- connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp | 2 -- connectivity/netsocket/CMakeLists.txt | 4 +++- .../tests/TESTS/netsocket/tcp/tcpsocket_send_timeout.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp index a86be016784..f90106d54de 100644 --- a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp @@ -61,8 +61,6 @@ #if defined(ETH_IP_VERSION_V2) #include "lan8742/lan8742.h" -#include "lwip/memp.h" -#include "lwip/api.h" #endif using namespace std::chrono; diff --git a/connectivity/netsocket/CMakeLists.txt b/connectivity/netsocket/CMakeLists.txt index 0069c22d4a5..ae2a36b741e 100644 --- a/connectivity/netsocket/CMakeLists.txt +++ b/connectivity/netsocket/CMakeLists.txt @@ -90,7 +90,9 @@ if("DEVICE_EMAC=1" IN_LIST MBED_TARGET_DEFINITIONS) ) endif() -add_library(mbed-netsocket INTERFACE) +add_library(mbed-netsocket INTERFACE + tests/TESTS/common/greentea_network_stack_nanostack.cpp + tests/TESTS/common/greentea_network_stack_lwipstack.cpp) target_link_libraries(mbed-netsocket INTERFACE diff --git a/connectivity/netsocket/tests/TESTS/netsocket/tcp/tcpsocket_send_timeout.cpp b/connectivity/netsocket/tests/TESTS/netsocket/tcp/tcpsocket_send_timeout.cpp index 962ff316e7e..e7ecaae1c46 100644 --- a/connectivity/netsocket/tests/TESTS/netsocket/tcp/tcpsocket_send_timeout.cpp +++ b/connectivity/netsocket/tests/TESTS/netsocket/tcp/tcpsocket_send_timeout.cpp @@ -47,7 +47,7 @@ void TCPSOCKET_SEND_TIMEOUT() (timer.elapsed_time() <= 800ms)) { continue; } - tr_error("send: err %d, time %d", err, timer.read_ms()); + tr_error("send: err %d, time %" PRIi64, err, std::chrono::duration_cast(timer.elapsed_time()).count()); TEST_FAIL(); break; } From dfcd9eb374431ebad1d8c14b98ccdae884a3f834 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 21 Jan 2025 23:25:43 -0800 Subject: [PATCH 05/47] Fix build --- .../tests/TESTS/netsocket/tls/tlssocket_recv_timeout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connectivity/netsocket/tests/TESTS/netsocket/tls/tlssocket_recv_timeout.cpp b/connectivity/netsocket/tests/TESTS/netsocket/tls/tlssocket_recv_timeout.cpp index 222198acaa6..18f1b3572bf 100644 --- a/connectivity/netsocket/tests/TESTS/netsocket/tls/tlssocket_recv_timeout.cpp +++ b/connectivity/netsocket/tests/TESTS/netsocket/tls/tlssocket_recv_timeout.cpp @@ -69,7 +69,7 @@ void TLSSOCKET_RECV_TIMEOUT() TEST_FAIL(); goto CLEANUP; } - tr_info("MBED: recv() took: %dus\n", timer.read_us()); + tr_info("MBED: recv() took: %" PRIi64 "us\n", std::chrono::duration_cast(timer.elapsed_time()).count()); continue; } else if (recvd < 0) { tr_error("[pkt#%02d] network error %d\n", i, recvd); From 60372ead5b1032e9d9c092fc8a629827225e01ae Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Thu, 30 Jan 2025 09:13:26 -0800 Subject: [PATCH 06/47] Keep workin' on CompositeEMAC --- connectivity/drivers/emac/CMakeLists.txt | 6 +- .../drivers/emac/TARGET_STM/CMakeLists.txt | 2 +- .../emac/TARGET_STM/STM32EthIPv2DMARings.cpp | 6 +- .../{TARGET_STM => include}/CompositeEthMac.h | 135 +++++++++++++++++- .../drivers/emac/include/GenericEthPhy.h | 59 ++++++++ .../CompositeEthMac.cpp | 0 .../drivers/emac/sources/GenericEthPhy.cpp | 21 +++ 7 files changed, 219 insertions(+), 10 deletions(-) rename connectivity/drivers/emac/{TARGET_STM => include}/CompositeEthMac.h (56%) create mode 100644 connectivity/drivers/emac/include/GenericEthPhy.h rename connectivity/drivers/emac/{TARGET_STM => sources}/CompositeEthMac.cpp (100%) create mode 100644 connectivity/drivers/emac/sources/GenericEthPhy.cpp diff --git a/connectivity/drivers/emac/CMakeLists.txt b/connectivity/drivers/emac/CMakeLists.txt index 109a35bb0fa..8c5460a063e 100644 --- a/connectivity/drivers/emac/CMakeLists.txt +++ b/connectivity/drivers/emac/CMakeLists.txt @@ -5,7 +5,11 @@ if(NOT "DEVICE_EMAC=1" IN_LIST MBED_TARGET_DEFINITIONS) return() endif() -add_library(mbed-emac STATIC EXCLUDE_FROM_ALL) +add_library(mbed-emac STATIC EXCLUDE_FROM_ALL + sources/CompositeEthMac.cpp + sources/GenericEthPhy.cpp) + +target_include_directories(mbed-emac PUBLIC include) if("ARM_FM" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_ARM_FM) diff --git a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt index a5df08c0f90..558f9b2e70f 100644 --- a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt @@ -20,7 +20,7 @@ target_sources(mbed-emac PRIVATE stm32xx_emac.cpp STM32EthIPv2DMARings.cpp - CompositeEthMac.cpp + ../sources/CompositeEthMac.cpp ) target_compile_options(mbed-emac PRIVATE -Wno-packed-bitfield-compat) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp index 5b89a828e99..9dcce68bf61 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp @@ -265,9 +265,6 @@ void STM32EthIPv2DMARings::macThread() break; } - // Rebuild descriptors if possible - buildRxDescriptors(); - if(emac_link_input_cb) { emac_link_input_cb(packet); @@ -276,6 +273,9 @@ void STM32EthIPv2DMARings::macThread() { memory_manager.free(packet); } + + // Rebuild descriptors if possible + buildRxDescriptors(); } } if(flags & THREAD_FLAG_TX_DESC_AVAILABLE) diff --git a/connectivity/drivers/emac/TARGET_STM/CompositeEthMac.h b/connectivity/drivers/emac/include/CompositeEthMac.h similarity index 56% rename from connectivity/drivers/emac/TARGET_STM/CompositeEthMac.h rename to connectivity/drivers/emac/include/CompositeEthMac.h index 8b5cce272b4..31a57dd1fa4 100644 --- a/connectivity/drivers/emac/TARGET_STM/CompositeEthMac.h +++ b/connectivity/drivers/emac/include/CompositeEthMac.h @@ -51,6 +51,7 @@ namespace mbed */ class CompositeEMAC : public EMAC { +public: enum class ErrCode { SUCCESS = 0, @@ -77,6 +78,11 @@ class CompositeEMAC : public EMAC typedef std::array MACAddress; + /** + * @brief Abstract interface for a driver for the low level ethernet MAC hardware. + * + * Thread safety: CompositeEMAC will guarantee only one thread is utilizing this class at a time. + */ class MACDriver { /** @@ -124,7 +130,7 @@ class CompositeEMAC : public EMAC * * @return Error code or success. */ - virtual ErrCode mdioRead(uint16_t devAddr, uint8_t regAddr, uint16_t & result) = 0; + virtual ErrCode mdioRead(uint8_t devAddr, uint8_t regAddr, uint16_t & result) = 0; /** * @brief Write a register to the PHY over the MDIO bus. @@ -135,7 +141,7 @@ class CompositeEMAC : public EMAC * * @return Error code or success. */ - virtual ErrCode mdioWrite(uint16_t devAddr, uint8_t regAddr, uint16_t data) = 0; + virtual ErrCode mdioWrite(uint8_t devAddr, uint8_t regAddr, uint16_t data) = 0; /** * @brief Get the reset pin for the Ethernet PHY. @@ -180,14 +186,133 @@ class CompositeEMAC : public EMAC virtual ErrCode setPromiscuous(bool enable); }; + /** + * @brief Interface for a driver for the Ethernet PHY. + * + * Thread safety: CompositeEMAC will guarantee only one thread is utilizing this class at a time. + */ class PhyDriver { + protected: + /// MAC driver. Shall be set in init(). + MACDriver * mac = nullptr; + + public: + /// Set the MAC driver of this PHY. Will be called by CompositeEMAC before init(). + void setMAC(MACDriver * mac) { this->mac = mac; } + + /** + * @brief Initialize the PHY and set it up for Ethernet operation. + * + * @return Error code or success + */ + ErrCode init(); + /** - * @brief Get the expected - * @return + * @brief Check whether the link is up or down + * + * @param[out] status Set to true or false depending on whether the link is up or down + * + * @return Error code or success */ - virtual std::pair getOUIAndModel() = 0; + ErrCode checkLinkStatus(bool & status); + + /** + * @brief Get the negotiated (or preset) Ethernet speed and duplex, given that the link is up + * + * @param[out] speed Link speed + * @param[out] duplex Link duplex + * + * @return Error code or success + */ + ErrCode checkLinkType(LinkSpeed & speed, Duplex & duplex); + }; + + /** + * @brief Abstract interface for a driver for the Tx DMA ring in the Ethernet MAC. + * + * Thread safety: CompositeEMAC will guarantee only one thread is utilizing this class at a time. + */ + class TxDMA { + protected: + /// Pointer to memory manager for the EMAC + EMACMemoryManager * memory_manager = nullptr; + + public: + /// Set the mem manager of this DMA ring. Will be called by CompositeEMAC before init(). + void setMemoryManager(EMACMemoryManager * memory_manager) { this->memory_manager = memory_manager; } + + /// Initialize this Tx DMA ring. + ErrCode init(); + + /// Stop the DMA running. init() should be able to be called again after this function completes to restart DMA. + ErrCode deinit(); + + /// Called by CompositeEMAC when the MAC generates a transmit complete interrupt + void txISR(); + + /// Transmit a packet out of the Tx DMA ring. Note that this function + /// *takes ownership of* the passed packet and must free it either now or after + /// it's been transmitted. + /// Should block until there is space to transmit the packet. + HAL_StatusTypeDef txPacket(net_stack_mem_buf_t * buf); }; + + /** + * @brief Abstract interface for a driver for the Rx DMA ring in the Ethernet MAC. + * + * Thread safety: CompositeEMAC will guarantee only one thread is utilizing this class at a time. + */ + class RxDMA { + protected: + /// Pointer to memory manager for the EMAC + EMACMemoryManager * memory_manager = nullptr; + + public: + /// Set the mem manager of this DMA ring. Will be called by CompositeEMAC before init(). + void setMemoryManager(EMACMemoryManager * memory_manager) { this->memory_manager = memory_manager; } + + /// Initialize this Rx DMA ring. + ErrCode init(); + + /// Stop the DMA running. init() should be able to be called again after this function completes to restart DMA. + ErrCode deinit(); + + /** + * @brief Check if the MAC may have a packet to receive. Called from the Rx ISR. + * + * This function is called to provide a hint to CompositeEMAC about whether it needs to call + * dequeuePacket() after an Rx ISR is received. + * + * @return True if the MAC might have a descriptor to receive. False if there is definitely no complete packet yet. + */ + bool rxHasPackets_ISR(); + + /** + * @brief Dequeue a packet, if one is ready to be received. + * + * This function should also dequeue and dispose of any error or incomplete DMA descriptors. + * The MAC thread calls it over and over again until it returns nullptr. + * + * @return Packet pointer, or nullptr if there were no packets. + */ + net_stack_mem_buf_t * dequeuePacket(); + + /** + * @brief Rebuild DMA descriptors, if there are descriptors that need building and there is free pool memory. + * + * This function is called by the MAC thread after a packet has been dequeued and afte r + */ + void rebuildDescriptors(); + }; + +protected: + + /// Subclass should call this when a receive interrupt is detected + void rxISR(); + + /// Subclass should call this when a transmit complete interrupt is detected + void txISR(); }; } diff --git a/connectivity/drivers/emac/include/GenericEthPhy.h b/connectivity/drivers/emac/include/GenericEthPhy.h new file mode 100644 index 00000000000..3093154c71d --- /dev/null +++ b/connectivity/drivers/emac/include/GenericEthPhy.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2024 Jamie Smith +* SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_GENERICETHPHY_H +#define MBED_OS_GENERICETHPHY_H + +#include "CompositeEthMac.h" + +namespace mbed { + +class GenericEthPhy : public mbed::CompositeEMAC::PhyDriver { +protected: + + // Methods for subclasses to implement + /** + * @brief Get the expected OUI and model number for this phy chip. These will be used + * to verify that the phy available on the board matches the expected model. + * + * @return Pair of OUI and model number. + */ + virtual std::pair getOUIAndModel() const = 0; + + /** + * @brief Get the default MDIO address of this phy chip + * + * @return Pair of OUI and model number. + */ + virtual uint8_t getDefaultAddress() const = 0; + + /** + * @brief Get the reset times for this phy + * + * @return Pair of (time reset should be held low, time after reset until we should try to contact the phy) + */ + virtual std::pair getResetTimes() const { + return {100us, 500ms}; // Apparently the 802.3u standard dictates PHYs should boot in <=0.5s + } + +public: + + +}; + +}; + +#endif //MBED_OS_GENERICETHPHY_H diff --git a/connectivity/drivers/emac/TARGET_STM/CompositeEthMac.cpp b/connectivity/drivers/emac/sources/CompositeEthMac.cpp similarity index 100% rename from connectivity/drivers/emac/TARGET_STM/CompositeEthMac.cpp rename to connectivity/drivers/emac/sources/CompositeEthMac.cpp diff --git a/connectivity/drivers/emac/sources/GenericEthPhy.cpp b/connectivity/drivers/emac/sources/GenericEthPhy.cpp new file mode 100644 index 00000000000..9adbd6b3100 --- /dev/null +++ b/connectivity/drivers/emac/sources/GenericEthPhy.cpp @@ -0,0 +1,21 @@ +/* Copyright (c) 2025 Jamie Smith +* SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GenericEthPhy.h" + +namespace mbed { + +} \ No newline at end of file From 2bdca414a15d9938503804dbec65d42eb4b41bb6 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 2 Feb 2025 16:00:29 -0800 Subject: [PATCH 07/47] Implement GenericEthPhy --- connectivity/drivers/emac/CMakeLists.txt | 5 +- .../drivers/emac/TARGET_STM/CMakeLists.txt | 1 - .../{CompositeEthMac.h => CompositeEMAC.h} | 96 +++++++++- .../drivers/emac/include/GenericEthPhy.h | 140 +++++++++++--- ...{CompositeEthMac.cpp => CompositeEMAC.cpp} | 14 +- .../drivers/emac/sources/GenericEthPhy.cpp | 175 +++++++++++++++++- .../drivers/emac/sources/PhyDrivers.cpp | 68 +++++++ platform/include/platform/mbed_wait_api.h | 9 + 8 files changed, 468 insertions(+), 40 deletions(-) rename connectivity/drivers/emac/include/{CompositeEthMac.h => CompositeEMAC.h} (79%) rename connectivity/drivers/emac/sources/{CompositeEthMac.cpp => CompositeEMAC.cpp} (55%) create mode 100644 connectivity/drivers/emac/sources/PhyDrivers.cpp diff --git a/connectivity/drivers/emac/CMakeLists.txt b/connectivity/drivers/emac/CMakeLists.txt index 8c5460a063e..fdedde4c50e 100644 --- a/connectivity/drivers/emac/CMakeLists.txt +++ b/connectivity/drivers/emac/CMakeLists.txt @@ -6,8 +6,9 @@ if(NOT "DEVICE_EMAC=1" IN_LIST MBED_TARGET_DEFINITIONS) endif() add_library(mbed-emac STATIC EXCLUDE_FROM_ALL - sources/CompositeEthMac.cpp - sources/GenericEthPhy.cpp) + sources/CompositeEMAC.cpp + sources/GenericEthPhy.cpp + sources/PhyDrivers.cpp) target_include_directories(mbed-emac PUBLIC include) diff --git a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt index 558f9b2e70f..bf46a2b62a7 100644 --- a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt @@ -20,7 +20,6 @@ target_sources(mbed-emac PRIVATE stm32xx_emac.cpp STM32EthIPv2DMARings.cpp - ../sources/CompositeEthMac.cpp ) target_compile_options(mbed-emac PRIVATE -Wno-packed-bitfield-compat) diff --git a/connectivity/drivers/emac/include/CompositeEthMac.h b/connectivity/drivers/emac/include/CompositeEMAC.h similarity index 79% rename from connectivity/drivers/emac/include/CompositeEthMac.h rename to connectivity/drivers/emac/include/CompositeEMAC.h index 31a57dd1fa4..db3c82ceb1d 100644 --- a/connectivity/drivers/emac/include/CompositeEthMac.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -18,6 +18,7 @@ #define MBED_OS_COMPOSITEETHMAC_H #include "EMAC.h" +#include "NonCopyable.h" namespace mbed { @@ -60,9 +61,11 @@ class CompositeEMAC : public EMAC PHY_NOT_RESPONDING = 3, OUT_OF_MEMORY = 4, INVALID_ARGUMENT = 5, - INVALID_USAGE = 6 + INVALID_USAGE = 6, + NEGOTIATION_FAILED = 7, }; + /// Enumeration of possible Ethernet link speeds enum class LinkSpeed { LINK_10MBIT, @@ -70,13 +73,16 @@ class CompositeEMAC : public EMAC LINK_1GBIT }; + /// Enumeration of possible Ethernet link duplexes enum class Duplex { HALF, FULL }; - typedef std::array MACAddress; + /// Basic MAC address type + static constexpr size_t MAC_ADDR_SIZE = 6; + typedef std::array MACAddress; /** * @brief Abstract interface for a driver for the low level ethernet MAC hardware. @@ -85,15 +91,14 @@ class CompositeEMAC : public EMAC */ class MACDriver { + public: /** * @brief Initialize the MAC, map pins, and prepare it to send and receive packets. * It should not be enabled yet. * - * @param ownAddress MAC address that this device should use - * * @return Error code or SUCCESS */ - virtual ErrCode init(MACAddress const & ownAddress) = 0; + virtual ErrCode init() = 0; /** * @brief Deinit the MAC so that it's not using any clock/power. Should prepare for init() to be called @@ -120,6 +125,15 @@ class CompositeEMAC : public EMAC */ virtual ErrCode disable() = 0; + /** + * @brief Set the own address of this MAC. + * + * \note This shall be called by CompositeEMAC after init but before enable. + * + * @param ownAddress Address this MAC will use for itself on the network. + */ + virtual void setOwnMACAddr(MACAddress const & ownAddress) = 0; + /** * @brief Read a register from the PHY over the MDIO bus. * @@ -191,13 +205,15 @@ class CompositeEMAC : public EMAC * * Thread safety: CompositeEMAC will guarantee only one thread is utilizing this class at a time. */ - class PhyDriver + class PhyDriver : NonCopyable { protected: /// MAC driver. Shall be set in init(). MACDriver * mac = nullptr; public: + virtual ~PhyDriver() = default; + /// Set the MAC driver of this PHY. Will be called by CompositeEMAC before init(). void setMAC(MACDriver * mac) { this->mac = mac; } @@ -206,7 +222,7 @@ class CompositeEMAC : public EMAC * * @return Error code or success */ - ErrCode init(); + virtual ErrCode init() = 0; /** * @brief Check whether the link is up or down @@ -215,17 +231,19 @@ class CompositeEMAC : public EMAC * * @return Error code or success */ - ErrCode checkLinkStatus(bool & status); + virtual ErrCode checkLinkStatus(bool & status) = 0; /** * @brief Get the negotiated (or preset) Ethernet speed and duplex, given that the link is up * + * \note Result speed and duplex undefined if the link is not up. + * * @param[out] speed Link speed * @param[out] duplex Link duplex * * @return Error code or success */ - ErrCode checkLinkType(LinkSpeed & speed, Duplex & duplex); + virtual ErrCode checkLinkType(LinkSpeed & speed, Duplex & duplex) = 0; }; /** @@ -301,18 +319,76 @@ class CompositeEMAC : public EMAC /** * @brief Rebuild DMA descriptors, if there are descriptors that need building and there is free pool memory. * - * This function is called by the MAC thread after a packet has been dequeued and afte r + * This function is called by the MAC thread after a packet has been dequeued and when memory in the Rx + * pool becomes free. */ void rebuildDescriptors(); }; protected: + // Instances of each of the 4 component classes + MACDriver & mac; + PhyDriver & phy; + TxDMA & txDMA; + RxDMA & rxDMA; + + // State of the MAC + enum PowerState { + + }; + /// Subclass should call this when a receive interrupt is detected void rxISR(); /// Subclass should call this when a transmit complete interrupt is detected void txISR(); + +public: + uint32_t get_mtu_size() const override { + // Regular Ethernet has an MTU of 1500. + // Some MACs support Jumbo Frames of up to 9000 bytes; this might be worth adding at some point + return 1500; + } + + uint32_t get_align_preference() const override { + // Most DMAs require or work best with word-aligned buffers. + // NOTE: As of Feb 2025, nothing in Mbed actually uses this value. + return 4; + } + + void get_ifname(char *name, uint8_t size) const override; + + uint8_t get_hwaddr_size() const override { + return MAC_ADDR_SIZE; + } + + bool get_hwaddr(uint8_t *addr) const override { + // Return false to tell upper layer code to use mbed_mac_address() to get the MAC address. + // TODO once we support more than 1 ethernet port per device, this will need to be updated + // to make sure each interface has a different MAC address. + return false; + } + + void set_hwaddr(const uint8_t *addr) override; + + bool link_out(emac_mem_buf_t *buf) override; + + bool power_up() override; + + void power_down() override; + + void set_link_input_cb(emac_link_input_cb_t input_cb) override; + + void set_link_state_cb(emac_link_state_change_cb_t state_cb) override; + + void add_multicast_group(const uint8_t *address) override; + + void remove_multicast_group(const uint8_t *address) override; + + void set_all_multicast(bool all) override; + + void set_memory_manager(EMACMemoryManager &mem_mngr) override; }; } diff --git a/connectivity/drivers/emac/include/GenericEthPhy.h b/connectivity/drivers/emac/include/GenericEthPhy.h index 3093154c71d..e04a38ab4be 100644 --- a/connectivity/drivers/emac/include/GenericEthPhy.h +++ b/connectivity/drivers/emac/include/GenericEthPhy.h @@ -17,41 +17,131 @@ #ifndef MBED_OS_GENERICETHPHY_H #define MBED_OS_GENERICETHPHY_H -#include "CompositeEthMac.h" +#include "CompositeEMAC.h" +#include "DigitalOut.h" + +#include namespace mbed { +namespace GenPhyRegs { + /// Generic Ethernet phy register definitions + /// @{ + inline constexpr uint8_t BMCR = 0; ///< Basic Mode Control Register + + inline constexpr size_t BMCR_SW_RST_Pos = 15; + inline constexpr uint16_t BMCR_SW_RST_Msk = 1 << BMCR_SW_RST_Pos; + + inline constexpr size_t BMCR_SPEED_100M_Pos = 13; + inline constexpr uint16_t BMCR_SPEED_100M_Msk = 1 << BMCR_SPEED_100M_Pos; + + inline constexpr size_t BMCR_ANEG_EN_Pos = 12; + inline constexpr uint16_t BMCR_ANEG_EN_Msk = 1 << BMCR_ANEG_EN_Pos; + + inline constexpr size_t BMCR_ANEG_RESTART_Pos = 9; + inline constexpr uint16_t BMCR_ANEG_RESTART_Msk = 1 << BMCR_ANEG_RESTART_Pos; + + inline constexpr size_t BMCR_DUPLEX_FULL_Pos = 8; + inline constexpr uint16_t BMCR_DUPLEX_FULL_Msk = 1 << BMCR_DUPLEX_FULL_Pos; + + inline constexpr uint8_t BMSR = 1; ///< Basic Mode Status Register + + inline constexpr size_t BMCR_LINK_UP_Pos = 2; + inline constexpr uint16_t BMCR_LINK_UP_Msk = 1 << BMCR_LINK_UP_Pos; + + inline constexpr uint8_t PHYIDR1 = 2; ///< PHY ID Register 1 + inline constexpr uint8_t PHYIDR2 = 3; ///< PHY ID Register 2 + inline constexpr uint8_t ANAR = 4; ///< AutoNegotiation Advertisement Register + + inline constexpr size_t ANAR_100BTX_FD_Pos = 8; + inline constexpr uint16_t ANAR_100BTX_FD_Msk = 1 << ANAR_100BTX_FD_Pos; + + inline constexpr size_t ANAR_100BTX_Pos = 7; + inline constexpr uint16_t ANAR_100BTX_Msk = 1 << ANAR_100BTX_Pos; + + inline constexpr size_t ANAR_10BT_FD_Pos = 6; + inline constexpr uint16_t ANAR_10BT_FD_Msk = 1 << ANAR_10BT_FD_Pos; + + inline constexpr size_t ANAR_10BT_Pos = 5; + inline constexpr uint16_t ANAR_10BT_Msk = 1 << ANAR_10BT_Pos; + + /// Value for ANAR[4:0] that specifies Ethernet as the protocol + inline constexpr uint16_t ANAR_PROTOCOL_IEE_802_3U_Val = 1; + + inline constexpr uint8_t ANLPAR = 5; ///< AutoNegotiation Link Partner Advertisement Register + /// @} +} + +/** + * @brief Driver for a generic, Ethernet-standard compliant PHY chip. + * + * Passed a configuration which sets most attributes of the phy. + * May be extended to handle chip-specific quirks. + */ class GenericEthPhy : public mbed::CompositeEMAC::PhyDriver { +public: + /// Configuration structure for a generic Ethernet PHY + struct Config { + /// 24-bit OUI of the organization that produced the ethernet PHY. + uint32_t OUI; + + /// 5-bit model number of the phy. This plus the OUI is used to verify that the + /// chip in hardware matches what's expected. + uint8_t model; + + /// MDIO address of the phy chip. + /// NOTE: 0 is *supposed* to be reserved as the general call address but lots of phy chips use + /// it anyway. + uint8_t address; + + /// Time to hold reset low on this phy + std::chrono::microseconds resetLowTime = std::chrono::microseconds(100); + + /// Time to wait after resetting the phy before trying to talk to it. + /// Also used as the time to wait for power on reset if there is no reset pin. + /// Note that standards compliant PHYs must reset in <500ms. + std::chrono::microseconds resetHighTime = std::chrono::milliseconds(500); + + /// Whether autonegotiation shall be enabled on the phy. + /// NOTE: If this is set to false, the other end of the connection MUST be manually configured to match. + /// Otherwise you will get sporadic data loss due to an Ethernet duplex mismatch! + /// However, setting this to false will make the Ethernet link come up much faster (milliseconds instead of seconds, at least if you don't do DHCP) + bool autonegEnabled = true; + + /// Whether the PHY should advertise full duplex modes + bool advertiseFullDuplex = true; + + /// Whether the PHY should advertise half duplex modes. + /// Half duplex allows running only over 1 or 2 twisted pairs, but introduces the possibility of collisions. + bool advertiseHalfDuplex = true; + + /// Whether the PHY should advertise 100Mbit modes + bool advertise100M = true; + + /// Whether the PHY should advertise 10Mbit modes. + /// 10Mbit can be a fallback if signal conditions are poor, but this fallback may not be desired. + bool advertise10M = true; + + /// If autonegEnabled is false, this gives the fixed link settings to advertise. + std::pair fixedEthSettings = {CompositeEMAC::LinkSpeed::LINK_100MBIT, CompositeEMAC::Duplex::FULL}; + }; + protected: - // Methods for subclasses to implement - /** - * @brief Get the expected OUI and model number for this phy chip. These will be used - * to verify that the phy available on the board matches the expected model. - * - * @return Pair of OUI and model number. - */ - virtual std::pair getOUIAndModel() const = 0; - - /** - * @brief Get the default MDIO address of this phy chip - * - * @return Pair of OUI and model number. - */ - virtual uint8_t getDefaultAddress() const = 0; - - /** - * @brief Get the reset times for this phy - * - * @return Pair of (time reset should be held low, time after reset until we should try to contact the phy) - */ - virtual std::pair getResetTimes() const { - return {100us, 500ms}; // Apparently the 802.3u standard dictates PHYs should boot in <=0.5s - } + Config const & config; + + std::optional resetDigitalOut; public: + GenericEthPhy(Config const & config): + config(config) + {} + + CompositeEMAC::ErrCode init() override; + CompositeEMAC::ErrCode checkLinkStatus(bool &status) override; + CompositeEMAC::ErrCode checkLinkType(CompositeEMAC::LinkSpeed &speed, CompositeEMAC::Duplex &duplex) override; }; }; diff --git a/connectivity/drivers/emac/sources/CompositeEthMac.cpp b/connectivity/drivers/emac/sources/CompositeEMAC.cpp similarity index 55% rename from connectivity/drivers/emac/sources/CompositeEthMac.cpp rename to connectivity/drivers/emac/sources/CompositeEMAC.cpp index fb441fd6688..01698843703 100644 --- a/connectivity/drivers/emac/sources/CompositeEthMac.cpp +++ b/connectivity/drivers/emac/sources/CompositeEMAC.cpp @@ -15,4 +15,16 @@ */ -#include "CompositeEthMac.h" \ No newline at end of file +#include "CompositeEMAC.h" + +void mbed::CompositeEMAC::get_ifname(char *name, uint8_t size) const { + // Note that LwIP only supports a two character interface name prefix. + // So, no point in going longer than that. + // Also note that we don't want to copy the terminating null if it doesn't fit. + const char * const ifPrefix = "en"; + memcpy(name, ifPrefix, (size < strlen(ifPrefix) + 1) ? size : strlen(ifPrefix) + 1); +} + +void mbed::CompositeEMAC::set_hwaddr(const uint8_t *addr) { + if(mac.se) +} diff --git a/connectivity/drivers/emac/sources/GenericEthPhy.cpp b/connectivity/drivers/emac/sources/GenericEthPhy.cpp index 9adbd6b3100..43c1edc48c2 100644 --- a/connectivity/drivers/emac/sources/GenericEthPhy.cpp +++ b/connectivity/drivers/emac/sources/GenericEthPhy.cpp @@ -15,7 +15,180 @@ */ #include "GenericEthPhy.h" +#include "CompositeEMAC.h" + +#include +#include +#include +#include + +#define TRACE_GROUP "GEPHY" namespace mbed { -} \ No newline at end of file +using namespace std::chrono_literals; + +#define FORWARD_ERR(call) {auto const err_code = call; if(err_code != CompositeEMAC::ErrCode::SUCCESS) { return err_code; }} + +CompositeEMAC::ErrCode GenericEthPhy::init() { + + // If we have a hardware reset pin, reset the PHY in hardware + if(mac->getPhyResetPin() != NC) { + // Output low on the reset pin, then bring high and wait + resetDigitalOut.emplace(mac->getPhyResetPin(), 0); + wait_us(config.resetLowTime); + resetDigitalOut->write(1); + } + + // Wait for the reset time to expire. Even if we didn't just actuate the hardware reset line, + // this is important because some PHYs need a long power on reset time, and the board may have + // just powered on. + wait_us(config.resetHighTime); + + // Try and detect the phy. + // ID1 should be the upper 18 MSBits of the OUI, with the two MSBits chopped off. + const uint16_t expectedID1 = config.OUI >> 6; + // Bits 10-15 of ID2 are the 6 LSBits of the OUI. Bits 4-9 are the model number. Bits 0-3 are the revision and may be anything. + const uint16_t expectedID2 = (config.OUI << 10) | (config.model << 4); + const uint16_t expectedID2Mask = 0xFFF0; + + // Read IDs + uint16_t actualID1; + uint16_t actualID2; + FORWARD_ERR(mac->mdioRead(config.address, GenPhyRegs::PHYIDR1, actualID1)); + FORWARD_ERR(mac->mdioRead(config.address, GenPhyRegs::PHYIDR2, actualID2)); + + if(actualID1 == expectedID1 && (actualID2 & expectedID2Mask) == expectedID2) { + // OK + tr_info("Detected ethernet PHY at MDIO addr %" PRIu8 " with OUI %" PRIu32 ", model %" PRIu8 ", and revision number %" PRIu8, config.OUI, config.model, actualID2 % 0xF); + } + else if(actualID1 == std::numeric_limits::max() && actualID2 == std::numeric_limits::max()) { + tr_error("Got all 0xFFs when reading Ethernet PHY. Since MDIO is an open drain bus, this means the phy is not connected or not responding."); + return CompositeEMAC::ErrCode::PHY_NOT_RESPONDING; + } + else { + tr_error("Ethernet phy model number verification mismatch. Expected PHYIDR1 = %" PRIu16 ", PHYIDR2 = %" PRIu16 ", got PHYIDR1 = %" PRIu16 ", PHYIDR2 = %" PRIu16 " [note: bottom 4 bits of PHYIDR2 ignored]", expectedID1, expectedID2, actualID1, actualID2); + return CompositeEMAC::ErrCode::PHY_NOT_RESPONDING; + } + + // Software reset, if we couldn't use the hardware reset line earlier + if(mac->getPhyResetPin() != NC) { + uint16_t bmcrVal; + FORWARD_ERR(mac->mdioRead(config.address, GenPhyRegs::BMCR, bmcrVal)); + FORWARD_ERR(mac->mdioWrite(config.address, GenPhyRegs::BMCR, bmcrVal | GenPhyRegs::BMCR_SW_RST_Msk)); + + // Wait for SW reset bit to clear + Timer timer; + timer.start(); + do { + FORWARD_ERR(mac->mdioRead(config.address, GenPhyRegs::BMCR, bmcrVal)); + rtos::ThisThread::sleep_for(1ms); + } + while(timer.elapsed_time() < config.resetHighTime && (bmcrVal & GenPhyRegs::BMCR_SW_RST_Msk)); + + // If the reset bit has not cleared yet, we have hit a timeout. Check one more time to avoid race condition. + FORWARD_ERR(mac->mdioRead(config.address, GenPhyRegs::BMCR, bmcrVal)); + if(bmcrVal & GenPhyRegs::BMCR_SW_RST_Msk) { + return CompositeEMAC::ErrCode::TIMEOUT; + } + } + + // If using autonegotiation, program ANAR and then restart autonegotiation + if(config.autonegEnabled) { + uint16_t anarVal = GenPhyRegs::ANAR_PROTOCOL_IEE_802_3U_Val; + if(config.advertise100M && config.advertiseFullDuplex) { + anarVal |= GenPhyRegs::ANAR_100BTX_FD_Msk; + } + if(config.advertise100M && config.advertiseHalfDuplex) { + anarVal |= GenPhyRegs::ANAR_100BTX_Msk; + } + if(config.advertise10M && config.advertiseFullDuplex) { + anarVal |= GenPhyRegs::ANAR_10BT_FD_Msk; + } + if(config.advertise10M && config.advertiseHalfDuplex) { + anarVal |= GenPhyRegs::ANAR_10BT_Msk; + } + FORWARD_ERR(mac->mdioWrite(config.address, GenPhyRegs::ANAR, anarVal)); + + // Now restart and enable autoneg + uint16_t bmcrVal; + FORWARD_ERR(mac->mdioRead(config.address, GenPhyRegs::BMCR, bmcrVal)); + bmcrVal |= GenPhyRegs::BMCR_ANEG_EN_Msk | GenPhyRegs::BMCR_ANEG_RESTART_Msk; + FORWARD_ERR(mac->mdioWrite(config.address, GenPhyRegs::BMCR, bmcrVal)); + } + else { + // Set fixed speed and duplex + uint16_t bmcrVal; + FORWARD_ERR(mac->mdioRead(config.address, GenPhyRegs::BMCR, bmcrVal)); + + // Disable autonegotiation + bmcrVal &= ~GenPhyRegs::BMCR_ANEG_EN_Msk; + + if(config.fixedEthSettings.first == CompositeEMAC::LinkSpeed::LINK_100MBIT) { + bmcrVal |= GenPhyRegs::BMCR_SPEED_100M_Msk; + } + else { // 10Mbit. TODO handle 1Gbit + bmcrVal &= ~GenPhyRegs::BMCR_SPEED_100M_Msk; + } + if(config.fixedEthSettings.second == CompositeEMAC::Duplex::FULL) { + bmcrVal |= GenPhyRegs::BMCR_DUPLEX_FULL_Msk; + } + else { // half duplex + bmcrVal &= ~GenPhyRegs::BMCR_DUPLEX_FULL_Msk; + } + FORWARD_ERR(mac->mdioWrite(config.address, GenPhyRegs::BMCR, bmcrVal)); + } + + return CompositeEMAC::ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode GenericEthPhy::checkLinkStatus(bool &status) { + // Note: PHYs latch the link state bit as low, so if the link has ever gone down here since the + // last poll, we will read 0 for the link status. + uint16_t bmsrVal; + FORWARD_ERR(mac->mdioRead(config.address, GenPhyRegs::BMSR, bmsrVal)); + status = bmsrVal & GenPhyRegs::BMCR_LINK_UP_Msk; + + return CompositeEMAC::ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode GenericEthPhy::checkLinkType(CompositeEMAC::LinkSpeed &speed, CompositeEMAC::Duplex &duplex) { + + if(config.autonegEnabled) { + // What a lot of people don't know is, you can actually get the link type of a phy just by reading its + // ANLPAR register! You don't need to read implementation specific registers. + // I got this trick from the Linux kernel: https://github.com/torvalds/linux/blob/d79bc8f79baacdd2549ec4af6d963ce3e69d7330/drivers/net/phy/phy-core.c#L475 + uint16_t anlparVal; + FORWARD_ERR(mac->mdioRead(config.address, GenPhyRegs::ANLPAR, anlparVal)); + + // Check settings in order of priority. This mirrors the logic done inside the PHY to determine the + // negotiated link type. + if((anlparVal & GenPhyRegs::ANAR_100BTX_FD_Msk) && config.advertise100M && config.advertiseFullDuplex) { + speed = CompositeEMAC::LinkSpeed::LINK_100MBIT; + duplex = CompositeEMAC::Duplex::FULL; + } + else if((anlparVal & GenPhyRegs::ANAR_100BTX_Msk) && config.advertise100M && config.advertiseHalfDuplex) { + speed = CompositeEMAC::LinkSpeed::LINK_100MBIT; + duplex = CompositeEMAC::Duplex::HALF; + } + else if((anlparVal & GenPhyRegs::ANAR_10BT_FD_Msk) && config.advertise10M && config.advertiseFullDuplex) { + speed = CompositeEMAC::LinkSpeed::LINK_10MBIT; + duplex = CompositeEMAC::Duplex::FULL; + } + else if((anlparVal & GenPhyRegs::ANAR_10BT_Msk) && config.advertise10M && config.advertiseHalfDuplex) { + speed = CompositeEMAC::LinkSpeed::LINK_10MBIT; + duplex = CompositeEMAC::Duplex::HALF; + } + else { + // No matching duplex settings + return CompositeEMAC::ErrCode::NEGOTIATION_FAILED; + } + } + else { + // Use configured setting + speed = config.fixedEthSettings.first; + duplex = config.fixedEthSettings.second; + } + return CompositeEMAC::ErrCode::SUCCESS; +} +} diff --git a/connectivity/drivers/emac/sources/PhyDrivers.cpp b/connectivity/drivers/emac/sources/PhyDrivers.cpp new file mode 100644 index 00000000000..f6e79629eea --- /dev/null +++ b/connectivity/drivers/emac/sources/PhyDrivers.cpp @@ -0,0 +1,68 @@ +/* Copyright (c) 2025 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GenericEthPhy.h" + +namespace mbed { + +using namespace std::chrono_literals; + +namespace LAN8742 { + +/// Driver for the Microchip LAN8742 PHY +/// Datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/DS_LAN8742_00001989A.pdf +/// @{ + +inline constexpr GenericEthPhy::Config DefaultConfig = { + .OUI = 0x1F0, + .model = 0x13, + .address = 0, // Address set via PHYAD[0] strap. +}; + +class Driver : public GenericEthPhy { +public: + explicit Driver(GenericEthPhy::Config const & config = DefaultConfig): + GenericEthPhy(config) + {} +}; + + +/// @} + +} + + +class PhyLAN8742 : public GenericEthPhy { +protected: + std::pair getOUIAndModel() const override { + return {0x1F0, 0x13}; + } + + uint8_t getAddress() const override { + // Address set via PHYAD[0] strap. + return 0; + } + + std::pair getResetTimes() const override { + // Per Table 5-11, reset must be low for at least 100us + // Per section 3.8.6.2, "a software reset will be completed within 0.5s." + // That's apparently all we get for the post-reset time + return {100us, 500ms}; + } +}; + + +} \ No newline at end of file diff --git a/platform/include/platform/mbed_wait_api.h b/platform/include/platform/mbed_wait_api.h index a6f85be326c..f2f26010bb0 100644 --- a/platform/include/platform/mbed_wait_api.h +++ b/platform/include/platform/mbed_wait_api.h @@ -146,6 +146,15 @@ inline void _wait_us_inline(unsigned int us) #define wait_us(us) _wait_us_inline(us) #endif // Known-rate, initialised timer +#ifdef __cplusplus +#include + +// Override of wait_us() allowing a std::chrono type convertible to microseconds to be passed in. +static inline void _wait_us_inline(std::chrono::microseconds us) { + _wait_us_inline(us.count()); +} +#endif + #ifdef __cplusplus } #endif From c8498127d8b0a5580d75ef64e5fe36aeb44da9f3 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 9 Feb 2025 17:13:57 -0800 Subject: [PATCH 08/47] Implement Tx DMA --- .../drivers/emac/TARGET_STM/CMakeLists.txt | 11 +- .../emac/TARGET_STM/STM32EthIPv2DMARings.h | 3 - .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 85 ++++++ .../drivers/emac/TARGET_STM/STM32EthMACv2.h | 48 +++ .../emac/TARGET_STM/STM32EthV2Descriptors.h | 181 ++++++++++++ .../emac/TARGET_STM/STM32IPv2EthDescriptors.h | 170 ----------- .../TARGET_STM/TARGET_STM32H7/CMakeLists.txt | 2 - .../TARGET_DISCO_H747I/stm32h7_eth_init.c | 147 +++++----- .../TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c | 178 ++++++------ .../TARGET_NUCLEO_H743ZI/CMakeLists.txt | 7 - .../TARGET_NUCLEO_H743ZI/stm32h7_eth_init.c | 164 ----------- .../TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c | 175 ++++++----- .../TARGET_PORTENTA_H7/stm32h7_eth_init.c | 201 ++++++------- .../drivers/emac/TARGET_STM/mbed_lib.json5 | 44 --- .../drivers/emac/TARGET_STM/stm32xx_emac.h | 1 + .../drivers/emac/include/CompositeEMAC.h | 24 +- .../drivers/emac/include/GenericEthDMA.h | 275 ++++++++++++++++++ .../drivers/emac/sources/CompositeEMAC.cpp | 13 +- .../drivers/emac/sources/GenericEthPhy.cpp | 2 +- .../drivers/emac/sources/PhyDrivers.cpp | 22 +- .../include/netsocket/NetStackMemoryManager.h | 12 - connectivity/netsocket/mbed_lib.json5 | 4 + platform/include/platform/mbed_wait_api.h | 10 +- .../STM32Cube_FW/stm32h5xx_hal_conf.h | 5 +- .../STM32Cube_FW/stm32h7xx_hal_conf.h | 4 +- 25 files changed, 969 insertions(+), 819 deletions(-) create mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp create mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h create mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h delete mode 100644 connectivity/drivers/emac/TARGET_STM/STM32IPv2EthDescriptors.h delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/CMakeLists.txt delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/stm32h7_eth_init.c create mode 100644 connectivity/drivers/emac/include/GenericEthDMA.h diff --git a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt index bf46a2b62a7..e8f343ac1ab 100644 --- a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt @@ -16,10 +16,11 @@ target_include_directories(mbed-emac . ) -target_sources(mbed-emac - PRIVATE - stm32xx_emac.cpp - STM32EthIPv2DMARings.cpp -) +if("STM32H7" IN_LIST MBED_TARGET_LABELS OR "STM32H5" IN_LIST MBED_TARGET_LABELS) + target_sources(mbed-emac + PRIVATE + STM32EthMACv2.cpp + ) +endif() target_compile_options(mbed-emac PRIVATE -Wno-packed-bitfield-compat) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h index c3ac05f4b0b..62eb2db2631 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h @@ -113,9 +113,6 @@ class STM32EthIPv2DMARings DynamicCacheAlignedBuffer rxDescs; StaticCacheAlignedBuffer txDescs; - // Tx buffers just need to be aligned to the nearest 4 bytes. - uint32_t txBuffers[MBED_CONF_STM32_EMAC_ETH_TXBUFNB][ETH_MAX_PACKET_SIZE / sizeof(uint32_t)]; - // Return Rx descriptors to the Ethernet MAC. // Descriptors can only be returned if there are free buffers in the pool to allocate to them. // The first descriptor returned will be the one at RxBuildDescIdx. diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp new file mode 100644 index 00000000000..b8f99170abf --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -0,0 +1,85 @@ +/* Copyright (c) 2025 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "STM32EthMACv2.h" + +namespace mbed { + void STM32EthMacV2::TxDMA::startDMA() { + // Flush Tx queue + base->MTLTQOMR |= ETH_MTLTQOMR_FTQ; + + // Configure Tx descriptor ring + base->DMACTDRLR = MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS - 1; // Ring size + base->DMACTDLAR = reinterpret_cast(&txDescs[0]); // Ring base address + base->DMACTDTPR = reinterpret_cast(&txDescs[0]); // Next descriptor (tail) pointer + + // Enable Tx DMA + // NOTE: Typo in C++ headers, should be called "DMACTXCR" + base->DMACTCR |= ETH_DMACTCR_ST; + + // Clear Tx process stopped flag + base->DMACSR = ETH_DMACSR_TPS; + } + + void STM32EthMacV2::TxDMA::stopDMA() { + // Disable Tx DMA + base->DMACTCR &= ~ETH_DMACTCR_ST; + } + + bool STM32EthMacV2::TxDMA::isDMAReadableBuffer(uint8_t const *start, size_t size) const + { + // On STM32H7, the Ethernet DMA cannot access data in DTCM. So, if someone sends + // a packet with a data pointer in DTCM (e.g. a stack allocated payload), everything + // will break if we don't copy it first. +#ifdef MBED_RAM_BANK_SRAM_DTC_START + if(reinterpret_cast(start) >= MBED_RAM_BANK_SRAM_DTC_START && + reinterpret_cast(start + size) <= MBED_RAM_BANK_SRAM_DTC_START + MBED_RAM_BANK_SRAM_DTC_SIZE) + { + return false; + } +#endif + return true; + } + + void STM32EthMacV2::TxDMA::giveToDMA(size_t descIdx, bool firstDesc, bool lastDesc) { + auto & desc = txDescs[descIdx]; + + // Note that we have to configure these every time as + // they get wiped away when the DMA gives back the descriptor + desc.formats.toDMA._reserved = 0; + desc.formats.toDMA.checksumInsertionCtrl = 0; // Mbed does not do checksum offload for now + desc.formats.toDMA.tcpSegmentationEnable = false; // No TCP offload + desc.formats.toDMA.tcpUDPHeaderLen = 0; // No TCP offload + desc.formats.toDMA.srcMACInsertionCtrl = 0; // No MAC insertion + desc.formats.toDMA.crcPadCtrl = 0; // Insert CRC and padding + desc.formats.toDMA.lastDescriptor = lastDesc; + desc.formats.toDMA.firstDescriptor = firstDesc; + desc.formats.toDMA.isContext = false; + desc.formats.toDMA.vlanTagCtrl = 0; // No VLAN tag + desc.formats.toDMA.intrOnCompletion = true; + desc.formats.toDMA.timestampEnable = false; + desc.formats.toDMA.dmaOwn = true; + +#if __DCACHE_PRESENT + // Write descriptor back to main memory + SCB_CleanDCache_by_Addr(&desc, sizeof(stm32_ethv2::EthTxDescriptor)); +#endif + + // Move tail pointer register to point to the descriptor after this descriptor. + // This tells the MAC to transmit until it reaches the given descriptor, then stop. + base->DMACTDTPR = reinterpret_cast(&txDescs[txSendIndex]); + } +} diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h new file mode 100644 index 00000000000..42c013306ba --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2025 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM32ETHMACV2_H +#define MBED_OS_STM32ETHMACV2_H + +#include "CompositeEMAC.h" +#include "GenericEthDMA.h" +#include "STM32EthV2Descriptors.h" + +namespace mbed { + /** + * @brief EMAC implementation for STM32 MCUs with Ethernet IP v2 + */ + class STM32EthMacV2 : public CompositeEMAC { + + class TxDMA : public GenericTxDMALoop + { + protected: + ETH_TypeDef * const base; // Base address of Ethernet peripheral + + void startDMA() override; + + void stopDMA() override; + + bool isDMAReadableBuffer(uint8_t const * start, size_t size) const override; + + void giveToDMA(size_t descIdx, bool firstDesc, bool lastDesc) override; + }; + }; +} + + + +#endif // MBED_OS_STM32ETHMACV2_H \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h b/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h new file mode 100644 index 00000000000..1fe742b1a32 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h @@ -0,0 +1,181 @@ +/* Copyright (c) 2024 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM32ETHV2DESCRIPTORS_H +#define MBED_OS_STM32ETHV2DESCRIPTORS_H + +#include +#include + +namespace mbed { + namespace stm32_ethv2 + { + + // Tx descriptor ------------------------------------------------------------------------------------------ + + // Descriptor format written by the application to queue a packet for transmission. + // Note: Datasheet calls this the "read format" which is just nuts... + struct __attribute__((packed)) EthTxDescriptorToDMAFmt + { + void const * buffer1Addr; + void const * buffer2Addr; + // TDES2 fields + uint16_t buffer1Len : 14; + uint8_t vlanTagCtrl : 2; + uint16_t buffer2Len : 14; + bool timestampEnable : 1; + bool intrOnCompletion : 1; + // TDES3 fields (not dealing with TCP offload for now) + uint16_t _reserved : 16; + uint8_t checksumInsertionCtrl : 2; + bool tcpSegmentationEnable : 1; + uint8_t tcpUDPHeaderLen : 4; + uint8_t srcMACInsertionCtrl : 3; + uint8_t crcPadCtrl : 2; + bool lastDescriptor : 1; + bool firstDescriptor: 1; + bool isContext : 1; + bool dmaOwn : 1; + }; + + // Write-back descriptor (DMA updates the desc with this format when complete) + struct __attribute__((packed)) EthTxDescriptorFromDMAFmt + { + uint32_t timestampLow; + uint32_t timestampHigh; + uint32_t _reserved; + // TDES3 fields + bool ipHeaderError : 1; + bool deferred : 1; + bool underflowError : 1; + bool excessiveDeferral : 1; + uint8_t collisionCount : 4; + bool excessiveCollisions : 1; + bool lateCollision : 1; + bool noCarrier : 1; + bool lossOfCarrier : 1; + bool payloadChecksumError : 1; + bool packetFlushed : 1; + bool jabberTimeout : 1; + bool errorSummary: 1; + uint8_t _reserved0: 1; + bool txTimestampCaptured : 1; + uint16_t _reserved1 : 10; + bool lastDescriptor: 1; + bool firstDescriptor : 1; + bool context : 1; + bool dmaOwn : 1; + }; + + // Top-level descriptor type + // Note that per the datasheet, Tx descriptors must be word aligned. + struct alignas(uint32_t) EthTxDescriptor { + union { + EthTxDescriptorToDMAFmt toDMA; + EthTxDescriptorFromDMAFmt fromDMA; + } formats; + + bool ownedByDMA() const { + return formats.toDMA.dmaOwn; + } + + void setBuffer(uint8_t const * addr, size_t len) + { + formats.toDMA.buffer1Addr = addr; + formats.toDMA.buffer1Len = len; + } + }; + + // Rx descriptor ------------------------------------------------------------------------------------------ + + // Format when an Rx descriptor is returned to the DMA. + // This is called the "read format" in the datasheet. + struct __attribute__((packed)) EthRxDescriptorToDMAFmt + { + void * buffer1Addr; + uint32_t _reserved0; + void * buffer2Addr; + uint32_t _reserved1: 24; + bool buffer1Valid: 1; + bool buffer2Valid: 1; + uint8_t _reserved2: 4; + bool intrOnCompletion: 1; + bool dmaOwn: 1; + }; + + // Format when an Rx descriptor is given to the application + // This is called the "write-back format" in the datasheet. + struct __attribute__((packed)) EthRxDescriptorFromDMAFmt + { + // RDES0 fields + uint16_t outerVLANTag; + uint16_t innerVLANTag; + + // RDES1 fields + uint8_t payloadType: 3; + bool ipHeaderError: 1; + bool ipv4HeaderPresent: 1; + bool ipv6HeaderPresent: 1; + bool checksumOffloadBypassed: 1; + bool ipPayloadError: 1; + uint8_t ptpMsgType: 4; + bool ptpType: 1; + bool isPTPv2: 1; + bool tsAvailable: 1; + bool tsDropped: 1; + uint16_t oamOrMacCtrl; + + // RDES2 fields + uint16_t _reserved0: 10; + bool arpNotGenerated: 1; + uint8_t _reserved1: 4; + bool vlanFiltPassed : 1; + bool sourceAddrFail: 1; + bool destAddrFail: 1; + bool hashFilterStatus: 1; + uint8_t hashValOrMatchIdx: 8; + bool l3FiltMatch: 1; + bool l4FiltMatch: 1; + uint8_t filterMatchNo: 3; + + // RDES3 fields + uint16_t pktLength: 15; + bool errorSummary: 1; + uint8_t lengthType: 3; + bool dribbleError: 1; + bool phyRxErr: 1; + bool overflowErr: 1; + bool rxWdogTimeout: 1; + bool giantPkt: 1; + bool crcErr: 1; + bool rdes0Valid: 1; + bool rdes1Valid: 1; + bool rdes2Valid: 1; + bool lastDescriptor: 1; + bool firstDescriptor: 1; + bool context: 1; + bool dmaOwn: 1; + }; + + union alignas(uint32_t) EthRxDescriptor { + EthRxDescriptorToDMAFmt toDMAFmt; + EthRxDescriptorFromDMAFmt fromDMAFmt; + }; + + } +} + +#endif diff --git a/connectivity/drivers/emac/TARGET_STM/STM32IPv2EthDescriptors.h b/connectivity/drivers/emac/TARGET_STM/STM32IPv2EthDescriptors.h deleted file mode 100644 index 87dccfa6eac..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/STM32IPv2EthDescriptors.h +++ /dev/null @@ -1,170 +0,0 @@ -/* Copyright (c) 2024 Jamie Smith - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MBED_OS_STM32IPV2ETHDESCRIPTORS_H -#define MBED_OS_STM32IPV2ETHDESCRIPTORS_H - -#include "stm32xx_emac_config.h" -#include "EMACMemoryManager.h" - -#ifdef ETH_IP_VERSION_V2 - -namespace mbed -{ - -// Tx descriptor ------------------------------------------------------------------------------------------ - -// Descriptor format written by the application to queue a packet for transmission. -// Note: Datasheet calls this the "read format" which is just nuts... -struct __attribute__((packed)) EthTxDescriptorToDMAFmt -{ - void const * buffer1Addr; - void const * buffer2Addr; - // TDES2 fields - uint16_t buffer1Len : 14; - uint8_t vlanTagCtrl : 2; - uint16_t buffer2Len : 14; - bool timestampEnable : 1; - bool intrOnCompletion : 1; - // TDES3 fields (not dealing with TCP offload for now) - uint16_t _reserved : 16; - uint8_t checksumInsertionCtrl : 2; - bool tcpSegmentationEnable : 1; - uint8_t tcpUDPHeaderLen : 4; - uint8_t srcMACInsertionCtrl : 3; - uint8_t crcPadCtrl : 2; - bool lastDescriptor : 1; - bool firstDescriptor: 1; - bool isContext : 1; - bool dmaOwn : 1; -}; - -// Write-back descriptor (DMA updates the desc with this format when complete) -struct __attribute__((packed)) EthTxDescriptorFromDMAFmt -{ - uint32_t timestampLow; - uint32_t timestampHigh; - uint32_t _reserved; - // TDES3 fields - bool ipHeaderError : 1; - bool deferred : 1; - bool underflowError : 1; - bool excessiveDeferral : 1; - uint8_t collisionCount : 4; - bool excessiveCollisions : 1; - bool lateCollision : 1; - bool noCarrier : 1; - bool lossOfCarrier : 1; - bool payloadChecksumError : 1; - bool packetFlushed : 1; - bool jabberTimeout : 1; - bool errorSummary: 1; - uint8_t _reserved0: 1; - bool txTimestampCaptured : 1; - uint16_t _reserved1 : 10; - bool lastDescriptor: 1; - bool firstDescriptor : 1; - bool context : 1; - bool dmaOwn : 1; -}; - -// Union of all possible descriptor formats. -// Note that per the datasheet, Tx descriptors must be word aligned. -union alignas(uint32_t) EthTxDescriptor { - EthTxDescriptorToDMAFmt toDMAFmt; - EthTxDescriptorFromDMAFmt fromDMAFmt; -}; - -// Rx descriptor ------------------------------------------------------------------------------------------ - -// Format when an Rx descriptor is returned to the DMA. -// This is called the "read format" in the datasheet. -struct __attribute__((packed)) EthRxDescriptorToDMAFmt -{ - void * buffer1Addr; - uint32_t _reserved0; - void * buffer2Addr; - uint32_t _reserved1: 24; - bool buffer1Valid: 1; - bool buffer2Valid: 1; - uint8_t _reserved2: 4; - bool intrOnCompletion: 1; - bool dmaOwn: 1; -}; - -// Format when an Rx descriptor is given to the application -// This is called the "write-back format" in the datasheet. -struct __attribute__((packed)) EthRxDescriptorFromDMAFmt -{ - // RDES0 fields - uint16_t outerVLANTag; - uint16_t innerVLANTag; - - // RDES1 fields - uint8_t payloadType: 3; - bool ipHeaderError: 1; - bool ipv4HeaderPresent: 1; - bool ipv6HeaderPresent: 1; - bool checksumOffloadBypassed: 1; - bool ipPayloadError: 1; - uint8_t ptpMsgType: 4; - bool ptpType: 1; - bool isPTPv2: 1; - bool tsAvailable: 1; - bool tsDropped: 1; - uint16_t oamOrMacCtrl; - - // RDES2 fields - uint16_t _reserved0: 10; - bool arpNotGenerated: 1; - uint8_t _reserved1: 4; - bool vlanFiltPassed : 1; - bool sourceAddrFail: 1; - bool destAddrFail: 1; - bool hashFilterStatus: 1; - uint8_t hashValOrMatchIdx: 8; - bool l3FiltMatch: 1; - bool l4FiltMatch: 1; - uint8_t filterMatchNo: 3; - - // RDES3 fields - uint16_t pktLength: 15; - bool errorSummary: 1; - uint8_t lengthType: 3; - bool dribbleError: 1; - bool phyRxErr: 1; - bool overflowErr: 1; - bool rxWdogTimeout: 1; - bool giantPkt: 1; - bool crcErr: 1; - bool rdes0Valid: 1; - bool rdes1Valid: 1; - bool rdes2Valid: 1; - bool lastDescriptor: 1; - bool firstDescriptor: 1; - bool context: 1; - bool dmaOwn: 1; -}; - -union alignas(uint32_t) EthRxDescriptor { - EthRxDescriptorToDMAFmt toDMAFmt; - EthRxDescriptorFromDMAFmt fromDMAFmt; -}; - -} - -#endif -#endif //MBED_OS_STM32IPV2ETHDESCRIPTORS_H diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/CMakeLists.txt index f4271f1b683..0802f6ca7ac 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/CMakeLists.txt @@ -5,8 +5,6 @@ if("DISCO_H747I" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_DISCO_H747I) elseif("PORTENTA_H7" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_PORTENTA_H7) -elseif("NUCLEO_H743ZI" IN_LIST MBED_TARGET_LABELS) - add_subdirectory(TARGET_NUCLEO_H743ZI) elseif("NUCLEO_H743ZI2" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_NUCLEO_H743ZI2) elseif("NUCLEO_H723ZG" IN_LIST MBED_TARGET_LABELS) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c index 0f05fa1e7dc..2e5b2ba0503 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c @@ -35,8 +35,6 @@ #endif #endif -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT - #include "stm32h7xx_hal.h" #include "platform/mbed_critical.h" @@ -63,89 +61,78 @@ /** * Override HAL Eth Init function */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { GPIO_InitTypeDef GPIO_InitStruct; - if (heth->Instance == ETH) { - /* GPIO Ports Clock Enable */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - // __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); - // __HAL_RCC_GPIOH_CLK_ENABLE(); - - /* Enable Peripheral clock */ - __HAL_RCC_ETH1MAC_CLK_ENABLE(); - __HAL_RCC_ETH1TX_CLK_ENABLE(); - __HAL_RCC_ETH1RX_CLK_ENABLE(); - - /**ETH GPIO Configuration - PG11 ------> ETH_TX_EN - PG12 ------> ETH_TXD1 - PG13 ------> ETH_TXD0 - PC1 ------> ETH_MDC - PA2 ------> ETH_MDIO - PA1 ------> ETH_REF_CLK - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - */ - GPIO_InitStruct.Pin = ETH_TX_EN_Pin | ETH_TXD1_Pin | ETH_TXD0_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = ETH_MDC_SAI4_D1_Pin | ETH_RXD0_Pin | ETH_RXD1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = ETH_MDIO_Pin | ETH_REF_CLK_Pin | ETH_CRS_DV_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - - } + /* GPIO Ports Clock Enable */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + // __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); + // __HAL_RCC_GPIOH_CLK_ENABLE(); + + /* Enable Peripheral clock */ + __HAL_RCC_ETH1MAC_CLK_ENABLE(); + __HAL_RCC_ETH1TX_CLK_ENABLE(); + __HAL_RCC_ETH1RX_CLK_ENABLE(); + + /**ETH GPIO Configuration + PG11 ------> ETH_TX_EN + PG12 ------> ETH_TXD1 + PG13 ------> ETH_TXD0 + PC1 ------> ETH_MDC + PA2 ------> ETH_MDIO + PA1 ------> ETH_REF_CLK + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + */ + GPIO_InitStruct.Pin = ETH_TX_EN_Pin | ETH_TXD1_Pin | ETH_TXD0_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = ETH_MDC_SAI4_D1_Pin | ETH_RXD0_Pin | ETH_RXD1_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = ETH_MDIO_Pin | ETH_REF_CLK_Pin | ETH_CRS_DV_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } /** * Override HAL Eth DeInit function */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH1MAC_CLK_DISABLE(); - __HAL_RCC_ETH1TX_CLK_DISABLE(); - __HAL_RCC_ETH1RX_CLK_DISABLE(); - - /**ETH GPIO Configuration - PG11 ------> ETH_TX_EN - PG12 ------> ETH_TXD1 - PG13 ------> ETH_TXD0 - PC1 ------> ETH_MDC - PA2 ------> ETH_MDIO - PA1 ------> ETH_REF_CLK - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - */ - HAL_GPIO_DeInit(GPIOG, ETH_TX_EN_Pin | ETH_TXD1_Pin | ETH_TXD0_Pin); - - HAL_GPIO_DeInit(GPIOC, ETH_MDC_SAI4_D1_Pin | ETH_RXD0_Pin | ETH_RXD1_Pin); - - HAL_GPIO_DeInit(GPIOA, ETH_MDIO_Pin | ETH_REF_CLK_Pin | ETH_CRS_DV_Pin); - } -} - -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} \ No newline at end of file + /* Peripheral clock disable */ + __HAL_RCC_ETH1MAC_CLK_DISABLE(); + __HAL_RCC_ETH1TX_CLK_DISABLE(); + __HAL_RCC_ETH1RX_CLK_DISABLE(); + + /**ETH GPIO Configuration + PG11 ------> ETH_TX_EN + PG12 ------> ETH_TXD1 + PG13 ------> ETH_TXD0 + PC1 ------> ETH_MDC + PA2 ------> ETH_MDIO + PA1 ------> ETH_REF_CLK + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + */ + HAL_GPIO_DeInit(GPIOG, ETH_TX_EN_Pin | ETH_TXD1_Pin | ETH_TXD0_Pin); + + HAL_GPIO_DeInit(GPIOC, ETH_MDC_SAI4_D1_Pin | ETH_RXD0_Pin | ETH_RXD1_Pin); + + HAL_GPIO_DeInit(GPIOA, ETH_MDIO_Pin | ETH_REF_CLK_Pin | ETH_CRS_DV_Pin); +} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c index 36ff287e766..62394c057f5 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c @@ -28,8 +28,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT - #include "stm32h7xx_hal.h" #include "platform/mbed_critical.h" @@ -61,104 +59,94 @@ /** * Override HAL Eth Init function */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { GPIO_InitTypeDef GPIO_InitStruct; - if (heth->Instance == ETH) { - /* GPIO Ports Clock Enable */ - __HAL_RCC_GPIOH_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); - - /* Enable Peripheral clock */ - __HAL_RCC_ETH1MAC_CLK_ENABLE(); - __HAL_RCC_ETH1TX_CLK_ENABLE(); - __HAL_RCC_ETH1RX_CLK_ENABLE(); - - /**ETH GPIO Configuration - PC1 ------> ETH_MDC - PA1 ------> ETH_REF_CLK - PA2 ------> ETH_MDIO - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - PB13 ------> ETH_TXD1 - PG11 ------> ETH_TX_EN - PG13 ------> ETH_TXD0 - */ - GPIO_InitStruct.Pin = RMII_MDC_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(RMII_MDC_GPIO_Port, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_RXD0_Pin | RMII_RXD1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_TXD1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(RMII_TXD1_GPIO_Port, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_TX_EN_Pin | RMII_TXD0_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); - } + /* GPIO Ports Clock Enable */ + __HAL_RCC_GPIOH_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); + + /* Enable Peripheral clock */ + __HAL_RCC_ETH1MAC_CLK_ENABLE(); + __HAL_RCC_ETH1TX_CLK_ENABLE(); + __HAL_RCC_ETH1RX_CLK_ENABLE(); + + /**ETH GPIO Configuration + PC1 ------> ETH_MDC + PA1 ------> ETH_REF_CLK + PA2 ------> ETH_MDIO + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + PB13 ------> ETH_TXD1 + PG11 ------> ETH_TX_EN + PG13 ------> ETH_TXD0 + */ + GPIO_InitStruct.Pin = RMII_MDC_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(RMII_MDC_GPIO_Port, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_RXD0_Pin | RMII_RXD1_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_TXD1_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(RMII_TXD1_GPIO_Port, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_TX_EN_Pin | RMII_TXD0_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); } /** * Override HAL Eth DeInit function */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Disable Peripheral clock */ - __HAL_RCC_ETH1MAC_CLK_DISABLE(); - __HAL_RCC_ETH1TX_CLK_DISABLE(); - __HAL_RCC_ETH1RX_CLK_DISABLE(); - - /**ETH GPIO Configuration - PC1 ------> ETH_MDC - PA1 ------> ETH_REF_CLK - PA2 ------> ETH_MDIO - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - PB13 ------> ETH_TXD1 - PG11 ------> ETH_TX_EN - PG13 ------> ETH_TXD0 - */ - HAL_GPIO_DeInit(GPIOC, RMII_MDC_Pin | RMII_RXD0_Pin | RMII_RXD1_Pin); - - HAL_GPIO_DeInit(GPIOA, RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin); - - HAL_GPIO_DeInit(RMII_TXD1_GPIO_Port, RMII_TXD1_Pin); - - HAL_GPIO_DeInit(GPIOG, RMII_TX_EN_Pin | RMII_TXD0_Pin); - } -} - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} - -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ + /* Disable Peripheral clock */ + __HAL_RCC_ETH1MAC_CLK_DISABLE(); + __HAL_RCC_ETH1TX_CLK_DISABLE(); + __HAL_RCC_ETH1RX_CLK_DISABLE(); + + /**ETH GPIO Configuration + PC1 ------> ETH_MDC + PA1 ------> ETH_REF_CLK + PA2 ------> ETH_MDIO + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + PB13 ------> ETH_TXD1 + PG11 ------> ETH_TX_EN + PG13 ------> ETH_TXD0 + */ + HAL_GPIO_DeInit(GPIOC, RMII_MDC_Pin | RMII_RXD0_Pin | RMII_RXD1_Pin); + + HAL_GPIO_DeInit(GPIOA, RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin); + + HAL_GPIO_DeInit(RMII_TXD1_GPIO_Port, RMII_TXD1_Pin); + + HAL_GPIO_DeInit(GPIOG, RMII_TX_EN_Pin | RMII_TXD0_Pin); +} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/CMakeLists.txt deleted file mode 100644 index 09efa30253f..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) 2020 ARM Limited. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -target_sources(mbed-emac - PRIVATE - stm32h7_eth_init.c -) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/stm32h7_eth_init.c deleted file mode 100644 index 6cc137bc24e..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI/stm32h7_eth_init.c +++ /dev/null @@ -1,164 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2022, STMicroelectronics - * All rights reserved. - * - * SPDX-License-Identifier: Apache-2.0 - * - * 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 STMicroelectronics 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. - */ - -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT - -#include "stm32h7xx_hal.h" -#include "platform/mbed_critical.h" - -#define MCO_Pin GPIO_PIN_0 -#define MCO_GPIO_Port GPIOH -#define RMII_MDC_Pin GPIO_PIN_1 -#define RMII_MDC_GPIO_Port GPIOC -#define RMII_REF_CLK_Pin GPIO_PIN_1 -#define RMII_REF_CLK_GPIO_Port GPIOA -#define RMII_MDIO_Pin GPIO_PIN_2 -#define RMII_MDIO_GPIO_Port GPIOA -#define RMII_CRS_DV_Pin GPIO_PIN_7 -#define RMII_CRS_DV_GPIO_Port GPIOA -#define RMII_RXD0_Pin GPIO_PIN_4 -#define RMII_RXD0_GPIO_Port GPIOC -#define RMII_RXD1_Pin GPIO_PIN_5 -#define RMII_RXD1_GPIO_Port GPIOC -#define RMII_TXD1_Pin GPIO_PIN_13 -#define RMII_TXD1_GPIO_Port GPIOB -#define TMS_Pin GPIO_PIN_13 -#define TMS_GPIO_Port GPIOA -#define TCK_Pin GPIO_PIN_14 -#define TCK_GPIO_Port GPIOA -#define RMII_TX_EN_Pin GPIO_PIN_11 -#define RMII_TX_EN_GPIO_Port GPIOG -#define RMII_TXD0_Pin GPIO_PIN_13 -#define RMII_TXD0_GPIO_Port GPIOG - -/** - * Override HAL Eth Init function - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) -{ - GPIO_InitTypeDef GPIO_InitStruct; - if (heth->Instance == ETH) { - /* GPIO Ports Clock Enable */ - __HAL_RCC_GPIOH_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); - - /* Enable Peripheral clock */ - __HAL_RCC_ETH1MAC_CLK_ENABLE(); - __HAL_RCC_ETH1TX_CLK_ENABLE(); - __HAL_RCC_ETH1RX_CLK_ENABLE(); - - /**ETH GPIO Configuration - PC1 ------> ETH_MDC - PA1 ------> ETH_REF_CLK - PA2 ------> ETH_MDIO - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - PB13 ------> ETH_TXD1 - PG11 ------> ETH_TX_EN - PG13 ------> ETH_TXD0 - */ - GPIO_InitStruct.Pin = RMII_MDC_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(RMII_MDC_GPIO_Port, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_RXD0_Pin | RMII_RXD1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_TXD1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(RMII_TXD1_GPIO_Port, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_TX_EN_Pin | RMII_TXD0_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); - } -} - -/** - * Override HAL Eth DeInit function - */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) -{ - if (heth->Instance == ETH) { - /* Disable Peripheral clock */ - __HAL_RCC_ETH1MAC_CLK_DISABLE(); - __HAL_RCC_ETH1TX_CLK_DISABLE(); - __HAL_RCC_ETH1RX_CLK_DISABLE(); - - /**ETH GPIO Configuration - PC1 ------> ETH_MDC - PA1 ------> ETH_REF_CLK - PA2 ------> ETH_MDIO - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - PB13 ------> ETH_TXD1 - PG11 ------> ETH_TX_EN - PG13 ------> ETH_TXD0 - */ - HAL_GPIO_DeInit(GPIOC, RMII_MDC_Pin | RMII_RXD0_Pin | RMII_RXD1_Pin); - - HAL_GPIO_DeInit(GPIOA, RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin); - - HAL_GPIO_DeInit(RMII_TXD1_GPIO_Port, RMII_TXD1_Pin); - - HAL_GPIO_DeInit(GPIOG, RMII_TX_EN_Pin | RMII_TXD0_Pin); - } -} - -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c index 84937336706..65c736e89f7 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c @@ -28,8 +28,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT - #include "stm32h7xx_hal.h" #include "platform/mbed_critical.h" @@ -61,104 +59,95 @@ /** * Override HAL Eth Init function */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { GPIO_InitTypeDef GPIO_InitStruct; - if (heth->Instance == ETH) { - /* GPIO Ports Clock Enable */ - __HAL_RCC_GPIOH_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); - - /* Enable Peripheral clock */ - __HAL_RCC_ETH1MAC_CLK_ENABLE(); - __HAL_RCC_ETH1TX_CLK_ENABLE(); - __HAL_RCC_ETH1RX_CLK_ENABLE(); - - /**ETH GPIO Configuration - PC1 ------> ETH_MDC - PA1 ------> ETH_REF_CLK - PA2 ------> ETH_MDIO - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - PB13 ------> ETH_TXD1 - PG11 ------> ETH_TX_EN - PG13 ------> ETH_TXD0 - */ - GPIO_InitStruct.Pin = RMII_MDC_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(RMII_MDC_GPIO_Port, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_RXD0_Pin | RMII_RXD1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_TXD1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(RMII_TXD1_GPIO_Port, &GPIO_InitStruct); - GPIO_InitStruct.Pin = RMII_TX_EN_Pin | RMII_TXD0_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); - } + /* GPIO Ports Clock Enable */ + __HAL_RCC_GPIOH_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); + + /* Enable Peripheral clock */ + __HAL_RCC_ETH1MAC_CLK_ENABLE(); + __HAL_RCC_ETH1TX_CLK_ENABLE(); + __HAL_RCC_ETH1RX_CLK_ENABLE(); + + /**ETH GPIO Configuration + PC1 ------> ETH_MDC + PA1 ------> ETH_REF_CLK + PA2 ------> ETH_MDIO + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + PB13 ------> ETH_TXD1 + PG11 ------> ETH_TX_EN + PG13 ------> ETH_TXD0 + */ + GPIO_InitStruct.Pin = RMII_MDC_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(RMII_MDC_GPIO_Port, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_RXD0_Pin | RMII_RXD1_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_TXD1_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(RMII_TXD1_GPIO_Port, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_TX_EN_Pin | RMII_TXD0_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); } /** * Override HAL Eth DeInit function */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Disable Peripheral clock */ - __HAL_RCC_ETH1MAC_CLK_DISABLE(); - __HAL_RCC_ETH1TX_CLK_DISABLE(); - __HAL_RCC_ETH1RX_CLK_DISABLE(); - - /**ETH GPIO Configuration - PC1 ------> ETH_MDC - PA1 ------> ETH_REF_CLK - PA2 ------> ETH_MDIO - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - PB13 ------> ETH_TXD1 - PG11 ------> ETH_TX_EN - PG13 ------> ETH_TXD0 - */ - HAL_GPIO_DeInit(GPIOC, RMII_MDC_Pin | RMII_RXD0_Pin | RMII_RXD1_Pin); - - HAL_GPIO_DeInit(GPIOA, RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin); - - HAL_GPIO_DeInit(RMII_TXD1_GPIO_Port, RMII_TXD1_Pin); - - HAL_GPIO_DeInit(GPIOG, RMII_TX_EN_Pin | RMII_TXD0_Pin); - } + /* Disable Peripheral clock */ + __HAL_RCC_ETH1MAC_CLK_DISABLE(); + __HAL_RCC_ETH1TX_CLK_DISABLE(); + __HAL_RCC_ETH1RX_CLK_DISABLE(); + + /**ETH GPIO Configuration + PC1 ------> ETH_MDC + PA1 ------> ETH_REF_CLK + PA2 ------> ETH_MDIO + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + PB13 ------> ETH_TXD1 + PG11 ------> ETH_TX_EN + PG13 ------> ETH_TXD0 + */ + HAL_GPIO_DeInit(GPIOC, RMII_MDC_Pin | RMII_RXD0_Pin | RMII_RXD1_Pin); + + HAL_GPIO_DeInit(GPIOA, RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin); + + HAL_GPIO_DeInit(RMII_TXD1_GPIO_Port, RMII_TXD1_Pin); + + HAL_GPIO_DeInit(GPIOG, RMII_TX_EN_Pin | RMII_TXD0_Pin); } - -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c index deb742200de..b2abbcb3755 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c @@ -30,7 +30,6 @@ #define ETHERNET 1 -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT #include "stm32h7xx_hal.h" #include "portenta_power.h" @@ -58,117 +57,107 @@ /** * Override HAL Eth Init function */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { GPIO_InitTypeDef GPIO_InitStruct; - if (heth->Instance == ETH) { - enableEthPowerSupply(); - - /* GPIO Ports Clock Enable */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - // __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); - // __HAL_RCC_GPIOH_CLK_ENABLE(); - - /* Enable Peripheral clock */ - __HAL_RCC_ETH1MAC_CLK_ENABLE(); - __HAL_RCC_ETH1TX_CLK_ENABLE(); - __HAL_RCC_ETH1RX_CLK_ENABLE(); - - /* Set pinstrap for 100mbit */ - // TODO - - /* Reset ETH Phy */ - __HAL_RCC_GPIOJ_CLK_ENABLE(); - GPIO_InitTypeDef gpio_eth_rst_init_structure; - gpio_eth_rst_init_structure.Pin = GPIO_PIN_15; - gpio_eth_rst_init_structure.Mode = GPIO_MODE_OUTPUT_PP; - gpio_eth_rst_init_structure.Pull = GPIO_NOPULL; - gpio_eth_rst_init_structure.Speed = GPIO_SPEED_FREQ_LOW; - HAL_GPIO_Init(GPIOJ, &gpio_eth_rst_init_structure); - - gpio_eth_rst_init_structure.Pin = ETH_RXD0_Pin | ETH_RXD1_Pin; - HAL_GPIO_Init(GPIOC, &gpio_eth_rst_init_structure); - HAL_GPIO_WritePin(GPIOC, ETH_RXD0_Pin, 1); - HAL_GPIO_WritePin(GPIOC, ETH_RXD1_Pin, 1); - gpio_eth_rst_init_structure.Pin = ETH_CRS_DV_Pin; - HAL_GPIO_Init(GPIOA, &gpio_eth_rst_init_structure); - HAL_GPIO_WritePin(GPIOA, ETH_CRS_DV_Pin, 1); - - HAL_Delay(25); - HAL_GPIO_WritePin(GPIOJ, GPIO_PIN_15, 0); - HAL_Delay(100); - HAL_GPIO_WritePin(GPIOJ, GPIO_PIN_15, 1); - - /**ETH GPIO Configuration - PG11 ------> ETH_TX_EN - PG12 ------> ETH_TXD1 - PG13 ------> ETH_TXD0 - PC1 ------> ETH_MDC - PA2 ------> ETH_MDIO - PA1 ------> ETH_REF_CLK - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - */ - GPIO_InitStruct.Pin = ETH_TX_EN_Pin | ETH_TXD1_Pin | ETH_TXD0_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = ETH_MDC_SAI4_D1_Pin | ETH_RXD0_Pin | ETH_RXD1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = ETH_MDIO_Pin | ETH_REF_CLK_Pin | ETH_CRS_DV_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - } + enableEthPowerSupply(); + + /* GPIO Ports Clock Enable */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + // __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); + // __HAL_RCC_GPIOH_CLK_ENABLE(); + + /* Enable Peripheral clock */ + __HAL_RCC_ETH1MAC_CLK_ENABLE(); + __HAL_RCC_ETH1TX_CLK_ENABLE(); + __HAL_RCC_ETH1RX_CLK_ENABLE(); + + /* Set pinstrap for 100mbit */ + // TODO + + /* Reset ETH Phy */ + __HAL_RCC_GPIOJ_CLK_ENABLE(); + GPIO_InitTypeDef gpio_eth_rst_init_structure; + gpio_eth_rst_init_structure.Pin = GPIO_PIN_15; + gpio_eth_rst_init_structure.Mode = GPIO_MODE_OUTPUT_PP; + gpio_eth_rst_init_structure.Pull = GPIO_NOPULL; + gpio_eth_rst_init_structure.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(GPIOJ, &gpio_eth_rst_init_structure); + + gpio_eth_rst_init_structure.Pin = ETH_RXD0_Pin | ETH_RXD1_Pin; + HAL_GPIO_Init(GPIOC, &gpio_eth_rst_init_structure); + HAL_GPIO_WritePin(GPIOC, ETH_RXD0_Pin, 1); + HAL_GPIO_WritePin(GPIOC, ETH_RXD1_Pin, 1); + gpio_eth_rst_init_structure.Pin = ETH_CRS_DV_Pin; + HAL_GPIO_Init(GPIOA, &gpio_eth_rst_init_structure); + HAL_GPIO_WritePin(GPIOA, ETH_CRS_DV_Pin, 1); + + HAL_Delay(25); + HAL_GPIO_WritePin(GPIOJ, GPIO_PIN_15, 0); + HAL_Delay(100); + HAL_GPIO_WritePin(GPIOJ, GPIO_PIN_15, 1); + + /**ETH GPIO Configuration + PG11 ------> ETH_TX_EN + PG12 ------> ETH_TXD1 + PG13 ------> ETH_TXD0 + PC1 ------> ETH_MDC + PA2 ------> ETH_MDIO + PA1 ------> ETH_REF_CLK + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + */ + GPIO_InitStruct.Pin = ETH_TX_EN_Pin | ETH_TXD1_Pin | ETH_TXD0_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = ETH_MDC_SAI4_D1_Pin | ETH_RXD0_Pin | ETH_RXD1_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = ETH_MDIO_Pin | ETH_REF_CLK_Pin | ETH_CRS_DV_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } /** * Override HAL Eth DeInit function */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH1MAC_CLK_DISABLE(); - __HAL_RCC_ETH1TX_CLK_DISABLE(); - __HAL_RCC_ETH1RX_CLK_DISABLE(); - - /**ETH GPIO Configuration - PG11 ------> ETH_TX_EN - PG12 ------> ETH_TXD1 - PG13 ------> ETH_TXD0 - PC1 ------> ETH_MDC - PA2 ------> ETH_MDIO - PA1 ------> ETH_REF_CLK - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - */ - HAL_GPIO_DeInit(GPIOG, ETH_TX_EN_Pin | ETH_TXD1_Pin | ETH_TXD0_Pin); - - HAL_GPIO_DeInit(GPIOC, ETH_MDC_SAI4_D1_Pin | ETH_RXD0_Pin | ETH_RXD1_Pin); - - HAL_GPIO_DeInit(GPIOA, ETH_MDIO_Pin | ETH_REF_CLK_Pin | ETH_CRS_DV_Pin); - - HAL_GPIO_WritePin(GPIOJ, GPIO_PIN_15, 0); - } + /* Peripheral clock disable */ + __HAL_RCC_ETH1MAC_CLK_DISABLE(); + __HAL_RCC_ETH1TX_CLK_DISABLE(); + __HAL_RCC_ETH1RX_CLK_DISABLE(); + + /**ETH GPIO Configuration + PG11 ------> ETH_TX_EN + PG12 ------> ETH_TXD1 + PG13 ------> ETH_TXD0 + PC1 ------> ETH_MDC + PA2 ------> ETH_MDIO + PA1 ------> ETH_REF_CLK + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + */ + HAL_GPIO_DeInit(GPIOG, ETH_TX_EN_Pin | ETH_TXD1_Pin | ETH_TXD0_Pin); + + HAL_GPIO_DeInit(GPIOC, ETH_MDC_SAI4_D1_Pin | ETH_RXD0_Pin | ETH_RXD1_Pin); + + HAL_GPIO_DeInit(GPIOA, ETH_MDIO_Pin | ETH_REF_CLK_Pin | ETH_CRS_DV_Pin); + + HAL_GPIO_WritePin(GPIOJ, GPIO_PIN_15, 0); } - -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 b/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 index 859d9df0382..376df44a506 100644 --- a/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 +++ b/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 @@ -1,50 +1,6 @@ { "name": "stm32-emac", "config": { - "eth-txbufnb": { - "help": "Number of Tx descriptors in the Ethernet MAC DMA ring.", - "value": 10 - }, - "thread-stacksize": { - "help": "Stack size for stm32_emac_thread", - "value": 1024 - }, - "eth-phy-address": { - "help" : "Configures actual PHY address according to pullup/down status of PHYAD pin(s)", - "value" : 0 - }, - "eth-phy-media-interface": { - "help" : "Selects Connection to PHY Chip: ETH_MEDIA_INTERFACE_RMII / ETH_MEDIA_INTERFACE_MII", - "value" : "ETH_MEDIA_INTERFACE_RMII" - }, - "eth-phy-AutoNegotiation": { - "help" : "Selects AutoNegotiation mode : ETH_AUTONEGOTIATION_ENABLE / ETH_AUTONEGOTIATION_DISABLE", - "value" : "ETH_AUTONEGOTIATION_ENABLE" - }, - "eth-phy-DuplexMode": { - "help" : "Selects DuplexMode mode : ETH_MODE_FULLDUPLEX / ETH_MODE_HALFDUPLEX", - "value" : "ETH_MODE_FULLDUPLEX" - }, - "eth-phy-Speed": { - "help" : "Selects Speed mode : ETH_SPEED_100M / ETH_SPEED_10M", - "value" : "ETH_SPEED_100M" - }, - "eth-phy-reset-delay": { - "help" : "Reset process time - Default value: 0.5s as specified in LAN8742A datasheet", - "value" : "500" - }, - "eth-phy-status-register": { - "help" : "PHY register Offset with auto-negotiation result - Default value is LAN8742A PHY Special Control/Status Register", - "value" : "31" - }, - "eth-phy-speed-status": { - "help" : "Speed mask information in eth-phy-status-register", - "value" : "0x0004" - }, - "eth-phy-duplex-status": { - "help" : "Duplex mask information in eth-phy-status-register", - "value" : "0x0010" - }, "max-mcast-subscribes": { "help" : "Maximum supported number of multicast addresses that the application can subscribe to", "value" : "8" diff --git a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h index 596dd150cd0..6a6e7e11561 100644 --- a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h +++ b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h @@ -23,6 +23,7 @@ #include "rtos/Thread.h" #include "STM32EthIPv2DMARings.h" +#include "GenericEthDMA.h" #include diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index db3c82ceb1d..ecd4f6c1892 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -19,6 +19,7 @@ #include "EMAC.h" #include "NonCopyable.h" +#include namespace mbed { @@ -261,19 +262,22 @@ class CompositeEMAC : public EMAC void setMemoryManager(EMACMemoryManager * memory_manager) { this->memory_manager = memory_manager; } /// Initialize this Tx DMA ring. - ErrCode init(); + virtual ErrCode init() = 0; - /// Stop the DMA running. init() should be able to be called again after this function completes to restart DMA. - ErrCode deinit(); + /// Stop the DMA running. This is called after the MAC is disabled. + /// init() should be able to be called again after this function completes to restart DMA. + virtual ErrCode deinit() = 0; - /// Called by CompositeEMAC when the MAC generates a transmit complete interrupt - void txISR(); + /// Reclaims the Tx buffers for any transmitted packets and frees their memory. + /// Invoked by the CompositeEMAC internal thread after a Tx interrupt happens. + /// Returns true if any descriptors became available, false otherwise + virtual bool reclaimTxDescs() = 0; /// Transmit a packet out of the Tx DMA ring. Note that this function /// *takes ownership of* the passed packet and must free it either now or after /// it's been transmitted. /// Should block until there is space to transmit the packet. - HAL_StatusTypeDef txPacket(net_stack_mem_buf_t * buf); + virtual ErrCode txPacket(net_stack_mem_buf_t * buf) = 0; }; /** @@ -334,10 +338,14 @@ class CompositeEMAC : public EMAC RxDMA & rxDMA; // State of the MAC - enum PowerState { - + enum class PowerState { + OFF = 0, + ON_NO_LINK, + ON_LINK_UP }; + std::atomic state = PowerState::OFF; + /// Subclass should call this when a receive interrupt is detected void rxISR(); diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h new file mode 100644 index 00000000000..714305b8be1 --- /dev/null +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -0,0 +1,275 @@ +/* Copyright (c) 2025 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_GENERICETHDMA_H +#define MBED_OS_GENERICETHDMA_H + +#include "CompositeEMAC.h" +#include "mbed_trace.h" +#include "CacheAlignedBuffer.h" +#include "mbed_critical.h" +#include + +#define TRACE_GROUP "GEDMA" + +namespace mbed { + /** + * @brief Generic transmit DMA loop + * + * This implementation of Tx DMA should work for the large majority of embedded MCUs that use a DMA ring-based + * ethernet MAC. + * + * @tparam DescriptorT Type representing an Ethernet descriptor. + * + * @note If the MCU has a D-cache, then \c DescriptorT must be padded to an exact number of cache lines in size! + */ + template + class GenericTxDMALoop : public CompositeEMAC::TxDMA + { + protected: + /// Tx descriptor array. It's up to the subclass to allocate these in the correct location. + CacheAlignedBuffer & txDescs; + + /// Pointer to first memory buffer in the chain associated with descriptor n. + /// The buffer address shall only be set for the *last* descriptor, so that the entire chain is freed + /// when the last descriptor is returned. + std::array descStackBuffers{}; + + /// EventFlag used to signal when a Tx descriptor becomes available + rtos::EventFlags txDescAvailFlag; + + size_t txSendIndex; ///< Index of the next Tx descriptor that can be filled with data + std::atomic txDescsOwnedByApplication; ///< Number of Tx descriptors owned by the application. Incremented by the mac thread and decremented by the application thread. + size_t txReclaimIndex; ///< Index of the next Tx descriptor that will be reclaimed by the mac thread. + + /// Configure DMA registers to point to the DMA ring, + /// and enable DMA. This is done before the MAC itself is enabled. + virtual void startDMA() = 0; + + /// Stop the DMA running. This is done after MAC transmit & recieve are disabled. + virtual void stopDMA() = 0; + + /// Get whether the given buffer is in a memory region readable by the Ethernet DMA. + /// If this returns false for a buffer being transmitted, the buffer will be copied into a new + /// heap-allocated buffer. + virtual bool isDMAReadableBuffer(uint8_t const * start, size_t size) const = 0; + + /// Give the descriptor at the given index to DMA to be transmitted. This is called after + /// the buffer has been set. + /// Note: if the descriptor needs to be flushed from CPU cache, you need to do that + /// at the correct point in the implementation of this method! + virtual void giveToDMA(size_t descIdx, bool firstDesc, bool lastDesc) = 0; + + public: + /** + * @brief Construct GenericTxDMALoop + * @param txDescs Tx descriptor buffer, containing exactly MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS + * descriptors. Subclass must allocate this with the correct configuration and location. + */ + GenericTxDMALoop(CacheAlignedBuffer & txDescs): + txDescs(txDescs) + {} + + CompositeEMAC::ErrCode init() override { + // At the start, we own all the descriptors + txDescsOwnedByApplication = MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; + + // Next descriptor will be descriptor 0 + txSendIndex = 0; + txReclaimIndex = 0; + + startDMA(); + + return CompositeEMAC::ErrCode::SUCCESS; + } + + CompositeEMAC::ErrCode deinit() override { + stopDMA(); + + // Deallocate all buffers currently assigned to the DMA ring + for(auto & buf_addr : descStackBuffers) { + if(buf_addr != nullptr) { + memory_manager->free(buf_addr); + buf_addr = nullptr; + } + } + + return CompositeEMAC::ErrCode::SUCCESS; + } + + bool reclaimTxDescs() override { + bool returnedAnyDescriptors = false; + while (true) + { + if (txReclaimIndex == txSendIndex && txDescsOwnedByApplication > 0) { + // If we have reached the Tx send index, we want to stop iterating as this is + // the next descriptor that has not been populated by the application yet. + // The only exception is if the Tx ring is completely full, in which case we want + // to process the entire ring. In the case where the Tx ring is full, + // txDescsOwnedByApplication will be 0. + // Note that txSendIndex and txDescsOwnedByApplication are updated in a critical + // section so their values will always be in sync with each other. + break; + } + + auto &currDesc = txDescs[txReclaimIndex]; + +#if __DCACHE_PRESENT + SCB_InvalidateDCache_by_Addr(&currDesc, sizeof(DescriptorT)); +#endif + + if (currDesc.ownedByDMA()) { + // This desc is owned by the DMA, so we have reached the part of the ring buffer + // that is still being transmitted. + // Done for now! + break; + } + + // Free any buffers associated with the descriptor + if (descStackBuffers[txReclaimIndex] != nullptr) { + memory_manager->free(descStackBuffers[txReclaimIndex]); + } + + // Update counters + txReclaimIndex = (txReclaimIndex + 1) % MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; + ++txDescsOwnedByApplication; + + returnedAnyDescriptors = true; + } + + if(returnedAnyDescriptors) { + txDescAvailFlag.set(1); + } + + return returnedAnyDescriptors; + } + + CompositeEMAC::ErrCode txPacket(net_stack_mem_buf_t * buf) { + // Step 1: Figure out if we can send this zero-copy, or if we need to copy it. + size_t neededDescs = memory_manager->count_buffers(buf); + bool needToCopy = false; + if(neededDescs >= MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS) + { + // Packet uses too many buffers, we have to copy it into a continuous buffer. + // Note: Some Eth DMAs (e.g. STM32 v2) cannot enqueue all the descs in the ring at the same time + // so we can't use every single descriptor to send the packet. + needToCopy = true; + } + + if(!needToCopy) { + net_stack_mem_buf_t * currBuf = buf; + while(currBuf != nullptr) { + // If this buffer is passed down direct from the application, we will need to + // copy the packet. + if(memory_manager->get_lifetime(currBuf) == NetStackMemoryManager::Lifetime::VOLATILE) + { + needToCopy = true; + break; + } + + // Or, if the buffer is in DMA-inaccessible RAM, we will need to copy it + if(!isDMAReadableBuffer(static_cast(memory_manager->get_ptr(currBuf)), memory_manager->get_len(currBuf))) { + needToCopy = true; + break; + } + + currBuf = memory_manager->get_next(currBuf); + } + } + + tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", + memory_manager.get_total_len(buf), memory_manager.count_buffers(buf), neededDescs); + + // Step 2: Copy packet if needed + if(needToCopy) + { + auto * newBuf = memory_manager->alloc_heap(memory_manager->get_total_len(buf), 0); + if(newBuf == nullptr) + { + // No free memory, drop packet + memory_manager->free(newBuf); + return CompositeEMAC::ErrCode::OUT_OF_MEMORY; + } + + // We should have gotten just one contiguous buffer + MBED_ASSERT(memory_manager->get_next(newBuf) == nullptr); + neededDescs = 1; + + // Copy data over + memory_manager->copy_from_buf(static_cast(memory_manager->get_ptr(newBuf)), memory_manager->get_len(newBuf), buf); + memory_manager->free(buf); + buf = newBuf; + } + + // Step 3: Wait for needed amount of buffers to be available. + // Note that, in my experience, it's better to block here, as dropping the packet + // due to not having enough buffers can create weird effects when the application sends + // lots of packets at once. + while(txDescsOwnedByApplication < neededDescs) + { + txDescAvailFlag.wait_any_for(1, rtos::Kernel::wait_for_u32_forever); + } + + // Step 4: Load buffer into descriptors and send + net_stack_mem_buf_t * currBuf = buf; + for(size_t descCount = 0; descCount < neededDescs; descCount++) + { + auto & currDesc = txDescs[txSendIndex]; + + // Set buffer 1 + currDesc.setBuffer(static_cast(memory_manager->get_ptr(currBuf)), memory_manager->get_len(currBuf)); +#if __DCACHE_PRESENT + // Write buffer back to main memory + SCB_CleanDCache_by_Addr(memory_manager->get_ptr(currBuf), memory_manager->get_len(currBuf)); +#endif + + // Move to next buffer + currBuf = memory_manager->get_next(currBuf); + + if(currBuf == nullptr) + { + // Last descriptor, store buffer address for freeing + descStackBuffers[txSendIndex] = buf; + } + else + { + descStackBuffers[txSendIndex] = nullptr; + } + + // Enter a critical section, because we could run into weird corner cases if the + // interrupt executes while we are half done configuring this descriptor and updating + // the counters. + core_util_critical_section_enter(); + + // Configure settings. + giveToDMA(txSendIndex, descCount == 0, currBuf == nullptr); + + // Update descriptor count and index + --txDescsOwnedByApplication; + txSendIndex = (txSendIndex + 1) % MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; + + core_util_critical_section_exit(); + } + + return CompositeEMAC::ErrCode::SUCCESS; + } + + }; +} + +#undef TRACE_GROUP + +#endif //MBED_OS_GENERICETHDMA_H diff --git a/connectivity/drivers/emac/sources/CompositeEMAC.cpp b/connectivity/drivers/emac/sources/CompositeEMAC.cpp index 01698843703..34d0b61026e 100644 --- a/connectivity/drivers/emac/sources/CompositeEMAC.cpp +++ b/connectivity/drivers/emac/sources/CompositeEMAC.cpp @@ -17,6 +17,10 @@ #include "CompositeEMAC.h" +#include + +#define TRACE_GROUP "CEMAC" + void mbed::CompositeEMAC::get_ifname(char *name, uint8_t size) const { // Note that LwIP only supports a two character interface name prefix. // So, no point in going longer than that. @@ -26,5 +30,12 @@ void mbed::CompositeEMAC::get_ifname(char *name, uint8_t size) const { } void mbed::CompositeEMAC::set_hwaddr(const uint8_t *addr) { - if(mac.se) + if(state != PowerState::ON_NO_LINK) { + tr_err("MAC address can only be set after power up, before link up!"); + return; + } + + MACAddress macAddr; + memcpy(macAddr.data(), addr, MAC_ADDR_SIZE); + mac.setOwnMACAddr(macAddr); } diff --git a/connectivity/drivers/emac/sources/GenericEthPhy.cpp b/connectivity/drivers/emac/sources/GenericEthPhy.cpp index 43c1edc48c2..2b380108a8b 100644 --- a/connectivity/drivers/emac/sources/GenericEthPhy.cpp +++ b/connectivity/drivers/emac/sources/GenericEthPhy.cpp @@ -60,7 +60,7 @@ CompositeEMAC::ErrCode GenericEthPhy::init() { if(actualID1 == expectedID1 && (actualID2 & expectedID2Mask) == expectedID2) { // OK - tr_info("Detected ethernet PHY at MDIO addr %" PRIu8 " with OUI %" PRIu32 ", model %" PRIu8 ", and revision number %" PRIu8, config.OUI, config.model, actualID2 % 0xF); + tr_info("Detected ethernet PHY at MDIO addr %" PRIu8 " with OUI %" PRIu32 ", model %" PRIu8 ", and revision number %" PRIu8, config.address, config.OUI, config.model, actualID2 % 0xF); } else if(actualID1 == std::numeric_limits::max() && actualID2 == std::numeric_limits::max()) { tr_error("Got all 0xFFs when reading Ethernet PHY. Since MDIO is an open drain bus, this means the phy is not connected or not responding."); diff --git a/connectivity/drivers/emac/sources/PhyDrivers.cpp b/connectivity/drivers/emac/sources/PhyDrivers.cpp index f6e79629eea..a962cbbd919 100644 --- a/connectivity/drivers/emac/sources/PhyDrivers.cpp +++ b/connectivity/drivers/emac/sources/PhyDrivers.cpp @@ -29,6 +29,7 @@ namespace LAN8742 { inline constexpr GenericEthPhy::Config DefaultConfig = { .OUI = 0x1F0, .model = 0x13, + // TODO this is 1 on ARCH_MAX .address = 0, // Address set via PHYAD[0] strap. }; @@ -44,25 +45,4 @@ class Driver : public GenericEthPhy { } - -class PhyLAN8742 : public GenericEthPhy { -protected: - std::pair getOUIAndModel() const override { - return {0x1F0, 0x13}; - } - - uint8_t getAddress() const override { - // Address set via PHYAD[0] strap. - return 0; - } - - std::pair getResetTimes() const override { - // Per Table 5-11, reset must be low for at least 100us - // Per section 3.8.6.2, "a software reset will be completed within 0.5s." - // That's apparently all we get for the post-reset time - return {100us, 500ms}; - } -}; - - } \ No newline at end of file diff --git a/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h b/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h index 8548b3d0cd3..c58397b736f 100644 --- a/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h +++ b/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h @@ -259,18 +259,6 @@ class NetStackMemoryManager { onPoolSpaceAvailCallback = cb; } - /** - * @brief Set callback which will be called when pool space becomes available - * - * \warning The callback could be called from any thread, and should make no assumptions about - * being in the same thread as anything else. - * - * @param cb Callback to call - */ - void set_on_pool_space_avail_cb(mbed::Callback cb) { - onPoolSpaceAvailCallback = cb; - } - protected: ~NetStackMemoryManager() = default; }; diff --git a/connectivity/netsocket/mbed_lib.json5 b/connectivity/netsocket/mbed_lib.json5 index 09041a195e8..22befc80a36 100644 --- a/connectivity/netsocket/mbed_lib.json5 +++ b/connectivity/netsocket/mbed_lib.json5 @@ -85,6 +85,10 @@ "emac-rx-pool-num-bufs": { "help": "Number of buffers (of size netsocket.emac-rx-pool-buf-size) in the EMAC receive pool. This controls how much memory is preallocated for Ethernet reception. A larger number means that more Ethernet packets can be received per second without dropping any. Some EMACs need up to 4 extra buffers, so this should be set such that this value minus 4 times the buffer size is at least 1514 (so we can receive one full Ethernet frame).", "value": 7 + }, + "emac-tx-num-descs": { + "help": "Number of Tx descriptors in the Ethernet MAC DMA ring. This affects how much data can be queued for Tx at one time", + "value": 8 } }, "target_overrides": { diff --git a/platform/include/platform/mbed_wait_api.h b/platform/include/platform/mbed_wait_api.h index f2f26010bb0..db857714152 100644 --- a/platform/include/platform/mbed_wait_api.h +++ b/platform/include/platform/mbed_wait_api.h @@ -147,15 +147,15 @@ inline void _wait_us_inline(unsigned int us) #endif // Known-rate, initialised timer #ifdef __cplusplus -#include - -// Override of wait_us() allowing a std::chrono type convertible to microseconds to be passed in. -static inline void _wait_us_inline(std::chrono::microseconds us) { - _wait_us_inline(us.count()); } #endif #ifdef __cplusplus +#include + +// Override of wait_us() allowing a std::chrono type convertible to microseconds to be passed in. +static inline void _wait_us_inline(std::chrono::microseconds const us) { + _wait_us_inline(us.count()); } #endif diff --git a/targets/TARGET_STM/TARGET_STM32H5/STM32Cube_FW/stm32h5xx_hal_conf.h b/targets/TARGET_STM/TARGET_STM32H5/STM32Cube_FW/stm32h5xx_hal_conf.h index 5161c4110f6..3bb4424f36a 100644 --- a/targets/TARGET_STM/TARGET_STM32H5/STM32Cube_FW/stm32h5xx_hal_conf.h +++ b/targets/TARGET_STM/TARGET_STM32H5/STM32Cube_FW/stm32h5xx_hal_conf.h @@ -47,7 +47,10 @@ extern "C" { #define HAL_DMA_MODULE_ENABLED #define HAL_DTS_MODULE_ENABLED #define HAL_EXTI_MODULE_ENABLED -#define HAL_ETH_MODULE_ENABLED + +// Mbed uses a handwritten driver for Eth V2 +// #define HAL_ETH_MODULE_ENABLED + #define HAL_FDCAN_MODULE_ENABLED #define HAL_FLASH_MODULE_ENABLED #define HAL_FMAC_MODULE_ENABLED diff --git a/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/stm32h7xx_hal_conf.h b/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/stm32h7xx_hal_conf.h index 41d1fbc2a46..e8ab7e968b7 100644 --- a/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/stm32h7xx_hal_conf.h +++ b/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/stm32h7xx_hal_conf.h @@ -50,7 +50,9 @@ #define HAL_DTS_MODULE_ENABLED #define HAL_DSI_MODULE_ENABLED -#define HAL_ETH_MODULE_ENABLED +// Mbed uses a handwritten driver for Eth V2 +// #define HAL_ETH_MODULE_ENABLED + #define HAL_EXTI_MODULE_ENABLED #define HAL_FDCAN_MODULE_ENABLED #define HAL_FLASH_MODULE_ENABLED From 693d111528ae1d1ccca37a2fe03019e42279c2a5 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 9 Feb 2025 18:50:18 -0800 Subject: [PATCH 09/47] Start on Rx DMA --- .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 44 +++ .../drivers/emac/TARGET_STM/STM32EthMACv2.h | 29 ++ .../emac/TARGET_STM/STM32EthV2Descriptors.h | 50 +++- .../drivers/emac/include/CompositeEMAC.h | 12 +- .../drivers/emac/include/GenericEthDMA.h | 282 +++++++++++++++++- .../include/netsocket/NetStackMemoryManager.h | 4 +- .../network/emac/emac_test_initialize.cpp | 1 - .../emac_test_utils/EmacTestMemoryManager.cpp | 10 - .../emac_test_utils/EmacTestMemoryManager.h | 12 - 9 files changed, 408 insertions(+), 36 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index b8f99170abf..768a4ecca51 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -82,4 +82,48 @@ namespace mbed { // This tells the MAC to transmit until it reaches the given descriptor, then stop. base->DMACTDTPR = reinterpret_cast(&txDescs[txSendIndex]); } + + void STM32EthMacV2::RxDMA::startDMA() + { + // Configure Rx buffer size. Per the datasheet and HAL code, we need to round this down to + // the nearest multiple of 4. + MBED_ASSERT(rxPoolPayloadSize % sizeof(uint32_t) == 0); + base->DMACRCR |= rxPoolPayloadSize << ETH_DMACRCR_RBSZ_Pos; + + // Configure Rx descriptor ring + base->DMACRDRLR = RX_NUM_DESCS - 1; // Ring size + base->DMACRDLAR = reinterpret_cast(&rxDescs[0]); // Ring base address + base->DMACRDTPR = reinterpret_cast(&rxDescs[0]); // Next descriptor (tail) pointer + + // Enable Rx DMA. + base->DMACRCR |= ETH_DMACRCR_SR; + + // Clear Rx process stopped flag + base->DMACSR = ETH_DMACSR_RPS; + } + + void STM32EthMacV2::RxDMA::returnDescriptor(size_t descIdx, uint8_t *buffer) { + auto & desc = rxDescs[descIdx]; + + // Clear out any bits previously set in the descriptor (from when the DMA gave it back to us) + memset(&desc, 0, sizeof(stm32_ethv2::EthRxDescriptor)); + + // Store buffer address + desc.formats.toDMA.buffer1Addr = memory_manager->get_ptr(buffer); + + // Configure descriptor + desc.formats.toDMA.buffer1Valid = true; + desc.formats.toDMA.intrOnCompletion = true; + desc.formats.toDMA.dmaOwn = true; + +#if __DCACHE_PRESENT + // Flush to main memory + SCB_CleanDCache_by_Addr(&desc, __SCB_DCACHE_LINE_SIZE); +#endif + + // Update tail ptr to issue "rx poll demand" and mark this descriptor for receive. + // Rx stops when the current and tail pointers are equal, so we want to set the tail pointer + // to one location after the last DMA-owned descriptor in the FIFO. + base->DMACRDTPR = reinterpret_cast(&rxDescs[rxBuildIndex]); + } } diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h index 42c013306ba..0fa5e775ed2 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -31,6 +31,7 @@ namespace mbed { { protected: ETH_TypeDef * const base; // Base address of Ethernet peripheral + StaticCacheAlignedBuffer txDescs; // Tx descriptors void startDMA() override; @@ -39,7 +40,35 @@ namespace mbed { bool isDMAReadableBuffer(uint8_t const * start, size_t size) const override; void giveToDMA(size_t descIdx, bool firstDesc, bool lastDesc) override; + public: + explicit TxDMA(ETH_TypeDef * const base): + GenericTxDMALoop(txDescs), + base(base) + {} }; + + class RxDMA : public GenericRxDMALoop { + protected: + ETH_TypeDef * const base; // Base address of Ethernet peripheral + StaticCacheAlignedBuffer rxDescs; // Rx descriptors + + void startDMA() override; + + void stopDMA() override; + + void returnDescriptor(size_t descIdx, uint8_t *buffer) override; + + DescriptorType getType(const stm32_ethv2::EthRxDescriptor &desc) override; + + public: + explicit RxDMA(ETH_TypeDef * const base): + GenericRxDMALoop(rxDescs), + base(base) + {} + }; + + // Components of the ethernet MAC + TxDMA txDMA; }; } diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h b/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h index 1fe742b1a32..e242d755af4 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h @@ -88,6 +88,12 @@ namespace mbed { EthTxDescriptorFromDMAFmt fromDMA; } formats; + // If we have a data cache, we need each descriptor to be in its own cache line. So, + // pad up to 32 byte cache line size +#if __DCACHE_PRESENT + uint8_t _padding[__SCB_DCACHE_LINE_SIZE - sizeof(decltype(formats))]; +#endif + bool ownedByDMA() const { return formats.toDMA.dmaOwn; } @@ -98,6 +104,9 @@ namespace mbed { formats.toDMA.buffer1Len = len; } }; +#if __DCACHE_PRESENT + static_assert(sizeof(EthTxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Tx descriptor size must equal cache line size"); +#endif // Rx descriptor ------------------------------------------------------------------------------------------ @@ -170,11 +179,46 @@ namespace mbed { bool dmaOwn: 1; }; - union alignas(uint32_t) EthRxDescriptor { - EthRxDescriptorToDMAFmt toDMAFmt; - EthRxDescriptorFromDMAFmt fromDMAFmt; + struct alignas(uint32_t) EthRxDescriptor { + union { + EthRxDescriptorToDMAFmt toDMA; + EthRxDescriptorFromDMAFmt fromDMA; + } formats; + + // If we have a data cache, we need each descriptor to be in its own cache line. So, + // pad up to 32 byte cache line size +#if __DCACHE_PRESENT + uint8_t _padding[__SCB_DCACHE_LINE_SIZE - sizeof(decltype(formats))]; +#endif + + bool ownedByDMA() const { + return formats.fromDMA.dmaOwn; + } + + bool isErrorDesc() const { + // For right now, we treat context descriptors equivalent to error descs. + // Currently we do not use them, so if we did get one, we just want to get rid of it. + return formats.fromDMA.errorSummary || formats.fromDMA.context; + } + + bool isFirstDesc() const { + return formats.fromDMA.firstDescriptor; + } + + bool isLastDesc() const { + return formats.fromDMA.lastDescriptor; + } + + bool getPayloadLength() const { + return formats.fromDMA.pktLength; + } }; +#if __DCACHE_PRESENT + static_assert(sizeof(EthRxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Tx descriptor size must equal cache line size"); +#endif + + } } diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index ecd4f6c1892..f545f729290 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -295,10 +295,10 @@ class CompositeEMAC : public EMAC void setMemoryManager(EMACMemoryManager * memory_manager) { this->memory_manager = memory_manager; } /// Initialize this Rx DMA ring. - ErrCode init(); + virtual ErrCode init() = 0; /// Stop the DMA running. init() should be able to be called again after this function completes to restart DMA. - ErrCode deinit(); + virtual ErrCode deinit() = 0; /** * @brief Check if the MAC may have a packet to receive. Called from the Rx ISR. @@ -308,7 +308,7 @@ class CompositeEMAC : public EMAC * * @return True if the MAC might have a descriptor to receive. False if there is definitely no complete packet yet. */ - bool rxHasPackets_ISR(); + virtual bool rxHasPackets_ISR() = 0; /** * @brief Dequeue a packet, if one is ready to be received. @@ -318,15 +318,15 @@ class CompositeEMAC : public EMAC * * @return Packet pointer, or nullptr if there were no packets. */ - net_stack_mem_buf_t * dequeuePacket(); + virtual net_stack_mem_buf_t * dequeuePacket() = 0; /** * @brief Rebuild DMA descriptors, if there are descriptors that need building and there is free pool memory. * - * This function is called by the MAC thread after a packet has been dequeued and when memory in the Rx + * This function is called by the MAC thread after a packet has been dequeued, and also when memory in the Rx * pool becomes free. */ - void rebuildDescriptors(); + virtual void rebuildDescriptors() = 0; }; protected: diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 714305b8be1..8fb930d89c7 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -22,6 +22,7 @@ #include "CacheAlignedBuffer.h" #include "mbed_critical.h" #include +#include #define TRACE_GROUP "GEDMA" @@ -51,9 +52,13 @@ namespace mbed { /// EventFlag used to signal when a Tx descriptor becomes available rtos::EventFlags txDescAvailFlag; + // Indexes for descriptor rings. + // NOTE: when working with these indices, it's important to consider the case where e.g. the send and reclaim indexes are + // equal. This could mean *either* that the Tx ring is completely full of data, or that the Tx ring is empty. + // To resolve this ambiguity, we maintain separate count variables that track how many entries are in the ring at present. size_t txSendIndex; ///< Index of the next Tx descriptor that can be filled with data - std::atomic txDescsOwnedByApplication; ///< Number of Tx descriptors owned by the application. Incremented by the mac thread and decremented by the application thread. - size_t txReclaimIndex; ///< Index of the next Tx descriptor that will be reclaimed by the mac thread. + std::atomic txDescsOwnedByApplication; ///< Number of Tx descriptors owned by the application. Decremented by txPacket() and incremented by reclaimTxDescs() + size_t txReclaimIndex; ///< Index of the next Tx descriptor that will be reclaimed by the mac thread calling reclaimTxDescs(). /// Configure DMA registers to point to the DMA ring, /// and enable DMA. This is done before the MAC itself is enabled. @@ -71,6 +76,8 @@ namespace mbed { /// the buffer has been set. /// Note: if the descriptor needs to be flushed from CPU cache, you need to do that /// at the correct point in the implementation of this method! + /// Also, if the DMA ran out of data to transmit, you may need to do a "poke"/"wake" operation + /// to tell it to start running again. virtual void giveToDMA(size_t descIdx, bool firstDesc, bool lastDesc) = 0; public: @@ -268,6 +275,277 @@ namespace mbed { } }; + + /** + * @brief Generic receive DMA loop + * + * This implementation of Rx DMA should work for the large majority of embedded MCUs that use a DMA ring-based + * ethernet MAC. + * + * @tparam DescriptorT Type representing an Ethernet descriptor. + * + * @note If the MCU has a D-cache, then \c DescriptorT must be padded to an exact number of cache lines in size! + */ + template + class GenericRxDMALoop : public CompositeEMAC::RxDMA { + protected: + /// Rx descriptor array. It's up to the subclass to allocate these in the correct location. + CacheAlignedBuffer & rxDescs = nullptr; + + /// How many extra buffers to leave in the Rx pool, relative to how many we keep assigned to Rx descriptors. + /// We want to keep some amount of extra buffers because constantly hitting the network stack with failed pool + /// allocations can produce some negative consequences in some cases. + static constexpr size_t RX_POOL_EXTRA_BUFFERS = 3; + + /// Number of entries in the Rx descriptor ring + /// Note: + 1 because for some EMACs (STM32 v2) we have to always keep one descriptor owned by the application + // TODO: When we add multiple Ethernet support, this calculation may need to be changed, because the pool buffers will be split between multiple EMACs + static constexpr size_t RX_NUM_DESCS = MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS - RX_POOL_EXTRA_BUFFERS + 1; + + /// Pointer to the network stack buffer associated with the corresponding Rx descriptor. + net_stack_mem_buf_t * rxDescStackBufs[RX_NUM_DESCS]; + + // Indexes for descriptor rings. + size_t rxBuildIndex; ///< Index of the next Rx descriptor that needs to be built. Updated by application and used by ISR. + size_t rxDescsOwnedByApplication; ///< Number of Rx descriptors owned by the application and needing buffers allocated. + std::atomic rxNextIndex; ///< Index of the next descriptor that the DMA will populate. Updated by application but used by ISR. + + // Alignment required for Rx memory buffers. Normally they don't need alignment but + // if we are doing cache operations they need to be cache aligned. +#if __DCACHE_PRESENT + static constexpr size_t RX_BUFFER_ALIGN = __SCB_DCACHE_LINE_SIZE; +#else + static constexpr size_t RX_BUFFER_ALIGN = 2; +#endif + + /// Payload size of buffers allocated from the Rx pool. This is the allocation unit size + /// of the pool minus any overhead needed for alignment. + size_t rxPoolPayloadSize; + + /// Constructor. Subclass must allocate descriptor array of size RX_NUM_DESCS + GenericRxDMALoop(CacheAlignedBuffer & rxDescs) : rxDescs(rxDescs) {} + + /// Configure DMA registers to point to the DMA ring, + /// and enable DMA. This is done before the MAC itself is enabled. + virtual void startDMA() = 0; + + /// Stop the DMA running. This is done after MAC transmit & receive are disabled. + virtual void stopDMA() = 0; + + /// Return a descriptor to DMA so that DMA can receive into it. + /// Is passed the buffer address (fixed size) to attach to this descriptor. + /// Note: if the descriptor needs to be flushed from CPU cache, you need to do that + /// at the correct point in the implementation of this method! + /// Also, if the DMA ran out of data to transmit, you may need to do a "poke"/"wake" operation + /// to tell it to start running again. + virtual void returnDescriptor(size_t descIdx, uint8_t * buffer) = 0; + + public: + CompositeEMAC::ErrCode init() override { + rxPoolPayloadSize = memory_manager->get_pool_alloc_unit(RX_BUFFER_ALIGN); + + // At the start, we own all the descriptors + rxDescsOwnedByApplication = RX_NUM_DESCS; + + // Build all descriptors + rebuildDescriptors(); + + // init DMA peripheral + startDMA(); + + return CompositeEMAC::ErrCode::SUCCESS; + } + + CompositeEMAC::ErrCode deinit() override { + stopDMA(); + + // Deallocate buffers associated with all descriptors + for(size_t descIdx = 0; descIdx < RX_NUM_DESCS; ++descIdx) { + if(rxDescStackBufs[descIdx] != nullptr) { + memory_manager->free(rxDescStackBufs[descIdx]); + } + } + + return CompositeEMAC::ErrCode::SUCCESS; + } + + void rebuildDescriptors() override { + const size_t origRxDescsOwnedByApplication [[maybe_unused]] = rxDescsOwnedByApplication; + + // Note: With some Ethernet peripherals, you can never give back every single descriptor to + // the hardware, because then it thinks there are 0 descriptors left. + while (rxDescsOwnedByApplication > 1) { + // Allocate new buffer + auto *const buffer = memory_manager->alloc_pool(rxPoolPayloadSize, RX_BUFFER_ALIGN); + if (buffer == nullptr) { + // No memory, cannot return any more descriptors. + return; + } + + // Store buffer address + rxDescStackBufs[rxBuildIndex] = buffer; + + // Send descriptor to DMA + returnDescriptor(rxBuildIndex, static_cast(memory_manager->get_ptr(buffer))); + + // Move to next descriptor + --rxDescsOwnedByApplication; + rxBuildIndex = (rxBuildIndex + 1) % RX_NUM_DESCS; + } + + tr_debug("buildRxDescriptors(): Returned %zu descriptors.", origRxDescsOwnedByApplication - rxDescsOwnedByApplication); + } + + bool rxHasPackets_ISR() override { + // First, we need to check if at least one DMA descriptor that is owned by the application + // has its last descriptor flag or error flag set, indicating we have received at least one complete packet + // or there is an error descriptor that can be reclaimed by the application. + // Note that we want to bias towards false positives here, because false positives just waste CPU time, + // while false negatives would cause packets to be dropped. + // So, for simplicity, we just check every descriptor currently owned by the application until we + // find one with the FS bit set or the error bits set. + // This could potentially produce a false positive if we do this in the middle of receiving + // an existing packet, but that is unlikely and will not cause anything bad to happen if it does. + + for(size_t descCount = 0; descCount < RX_NUM_DESCS; descCount++) + { + auto &descriptor = rxDescs[(rxNextIndex + descCount) % RX_NUM_DESCS]; + +#if __DCACHE_PRESENT + SCB_InvalidateDCache_by_Addr(&descriptor, sizeof(DescriptorT)); +#endif + + if(descriptor.ownedByDMA()) + { + // Descriptor owned by DMA. We are out of descriptors to process. + return false; + } + if(descriptor.isErrorDesc() or descriptor.isLastDesc()) + { + // Reclaimable descriptor or complete packet detected. + return true; + } + } + + // Processed all descriptors. + return false; + } + + net_stack_mem_buf_t * dequeuePacket() override { + // Indices of the first and last descriptors for the packet will be saved here + std::optional firstDescIdx, lastDescIdx; + + // Prevent looping around into descriptors waiting for rebuild by limiting how many + // we can process. + const size_t maxDescsToProcess = RX_NUM_DESCS - rxDescsOwnedByApplication; + + const size_t startIdx = rxNextIndex; + + for (size_t descCount = 0; descCount < maxDescsToProcess && !lastDescIdx.has_value(); descCount++) { + size_t descIdx = (startIdx + descCount) % RX_NUM_DESCS; + auto &descriptor = rxDescs[descIdx]; + + #if __DCACHE_PRESENT + SCB_InvalidateDCache_by_Addr(&descriptor, sizeof(DescriptorT)); + #endif + + if (descriptor.ownedByDMA()) { + // Descriptor owned by DMA and has not been filled in yet. We are out of descriptors to process. + break; + } + const auto type = getType(descriptor); + + if (descriptor.isErrorDesc() || + (!descriptor.isFirstDesc() && !firstDescIdx.has_value())) { + // Context or error descriptor, or a non-first-descriptor before a first descriptor + // (could be caused by incomplete packets/junk in the DMA buffer). + // Ignore, free associated memory, and schedule for rebuild. + memory_manager->free(rxDescStackBufs[descIdx]); + rxDescStackBufs[descIdx] = nullptr; + ++rxDescsOwnedByApplication; + ++rxNextIndex; + + // We should only get one of these error descriptors before the start of the packet, not + // during it. + if(descriptor.isErrorDesc()) { + MBED_ASSERT(!firstDescIdx.has_value()); + } + + continue; + } + + if (descriptor.isFirstDesc()) { + // We should see first descriptor only once and before last descriptor. If this rule is violated, it's likely + // because we ran out of descriptors during receive earlier and the MAC tossed out the rest of the packet. + if(firstDescIdx.has_value()) { + // Clean up the old first descriptor and any descriptors between there and here + for(size_t descToCleanIdx = *firstDescIdx; descToCleanIdx != descIdx; descToCleanIdx = (descToCleanIdx + 1) % RX_NUM_DESCS) { + memory_manager->free(rxDescStackBufs[descToCleanIdx]); + rxDescStackBufs[descToCleanIdx] = nullptr; + ++rxDescsOwnedByApplication; + ++rxNextIndex; + } + } + firstDescIdx = descIdx; + } + + if (descriptor.isLastDesc()) { + lastDescIdx = descIdx; + } + } + + if (!lastDescIdx.has_value()) { + // No complete packet identified. + // Take the chance to rebuild any available descriptors, then return. + rebuildDescriptors(); + tr_debug("No complete packets in Rx descs\n"); + return nullptr; + } + + // We will receive next into the descriptor after this one. + // Update this now to tell the ISR to search for descriptors after lastDescIdx only. + rxNextIndex = (*lastDescIdx + 1) % RX_NUM_DESCS; + + // NOTE: Currently we do not make any attempt to set the length of the Rx buffer to match + // how many bytes were actually received. This is because different MACs provide this info differently: + // some provide a byte count in each descriptor while others provide one in the first descriptor + // for the entire chain. It's easier to simply ignore this information as the network stack can + // figure it out. + // So, the buffers we pass up will always have a langth that's a multiple of the pool alloc unit. + + + // Iterate through the subsequent descriptors in this packet and link the buffers + // Note that this also transfers ownership of subsequent buffers to the first buffer, + // so if the first buffer is deleted, the others will be as well. + net_stack_mem_buf_t *const headBuffer = rxDescStackBufs[*firstDescIdx]; + ++rxDescsOwnedByApplication; // for first buffer + rxDescStackBufs[*firstDescIdx] = nullptr; + for (size_t descIdx = (*firstDescIdx + 1) % RX_NUM_DESCS; + descIdx != (*lastDescIdx + 1) % RX_NUM_DESCS; + descIdx = (descIdx + 1) % RX_NUM_DESCS) { + + memory_manager->cat(headBuffer, rxDescs[descIdx].buffer); + rxDescs[descIdx].buffer = nullptr; + ++rxDescsOwnedByApplication; + } + + // Invalidate cache for all data buffers, as these were written by the DMA to main memory + #if __DCACHE_PRESENT + auto * bufToInvalidate = headBuffer; + while(bufToInvalidate != nullptr) + { + SCB_InvalidateDCache_by_Addr(memory_manager.get_ptr(bufToInvalidate), rxPoolPayloadSize); + bufToInvalidate = memory_manager.get_next(bufToInvalidate); + } + #endif + + tr_debug("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu (%p-%p)\n", + memory_manager.get_total_len(headBuffer), memory_manager.get_ptr(headBuffer), firstDescIdx, lastDescIdx, + &rxDescs[firstDescIdx], &rxDescs[lastDescIdx]); + + return headBuffer; + } + }; } #undef TRACE_GROUP diff --git a/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h b/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h index c58397b736f..9d8eb44e417 100644 --- a/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h +++ b/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h @@ -100,9 +100,9 @@ class NetStackMemoryManager { /** * Get memory buffer pool size. * - * @return The maximum size of contiguous memory that can be allocated from a pool. + * @return The number of pool buffers that can be allocated at any one time */ - virtual uint32_t get_pool_size() const = 0; + uint32_t get_pool_size() { return MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS; } /** * Free memory buffer chain diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp index 5fe7b4535d1..c4bc0208b57 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp @@ -38,7 +38,6 @@ void test_emac_initialize() // Set memory manager parameters EmacTestMemoryManager::get_instance().set_alloc_unit(256); // Use a relatively small allocation unit size so packets have to be split up into a lot of buffers - EmacTestMemoryManager::get_instance().set_pool_size(10); // Start with 10 buffers in the Rx pool. One max-len Eth packet uses 6 256-byte buffers, and some MACs need up to 4 extra ones. static NetworkInterface *network_interface = get_network_interface(); diff --git a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp index bdbc26ff29e..a99afd2c028 100644 --- a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp +++ b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp @@ -486,11 +486,6 @@ void EmacTestMemoryManager::set_len(emac_mem_buf_t *buf, uint32_t len) mem_buf->len = len; } -uint32_t EmacTestMemoryManager::get_pool_size() const -{ - return m_pool_size; -} - NetStackMemoryManager::Lifetime EmacTestMemoryManager::get_lifetime(const net_stack_mem_buf_t *buf) const { return static_cast(buf)->lifetime; @@ -503,11 +498,6 @@ void EmacTestMemoryManager::set_alloc_unit(uint32_t alloc_unit) m_alloc_unit = alloc_unit; } -void EmacTestMemoryManager::set_pool_size(size_t size) -{ - m_pool_size = size; -} - void EmacTestMemoryManager::set_memory_available(bool memory) { m_memory_available = memory; diff --git a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h index 741a1b3b7a6..ecb78d1b01e 100644 --- a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h +++ b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h @@ -70,8 +70,6 @@ class EmacTestMemoryManager : public EMACMemoryManager { void set_len(emac_mem_buf_t *buf, uint32_t len) override; - uint32_t get_pool_size() const override; - Lifetime get_lifetime(const net_stack_mem_buf_t *buf) const override; /** @@ -109,16 +107,6 @@ class EmacTestMemoryManager : public EMACMemoryManager { */ virtual void set_alloc_unit(uint32_t alloc_unit); - /** - * Sets memory buffer pool size - * - * Sets the number of buffers that may be allocated from the pool. If the number of buffers currently - * in use is >= this number, new pool allocations will fail. - * - * @param size Pool size - */ - virtual void set_pool_size(size_t size); - /** * Sets whether memory is available * From f9686ed64363532e13bbf0a9272119de43ca9112 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 11 Feb 2025 10:03:39 -0800 Subject: [PATCH 10/47] Finish Rx DMA and multicast subscribes --- .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 19 ++- .../drivers/emac/TARGET_STM/STM32EthMACv2.h | 8 +- .../emac/TARGET_STM/STM32EthV2Descriptors.h | 4 - .../drivers/emac/TARGET_STM/mbed_lib.json5 | 6 - .../drivers/emac/include/CompositeEMAC.h | 34 ++++-- .../drivers/emac/include/GenericEthDMA.h | 47 ++++---- .../drivers/emac/sources/CompositeEMAC.cpp | 110 ++++++++++++++++++ connectivity/netsocket/mbed_lib.json5 | 4 + 8 files changed, 191 insertions(+), 41 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index 768a4ecca51..711bc21af0c 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -39,7 +39,7 @@ namespace mbed { base->DMACTCR &= ~ETH_DMACTCR_ST; } - bool STM32EthMacV2::TxDMA::isDMAReadableBuffer(uint8_t const *start, size_t size) const + bool STM32EthMacV2::TxDMA::isDMAReadableBuffer(uint8_t const *start, const size_t size) const { // On STM32H7, the Ethernet DMA cannot access data in DTCM. So, if someone sends // a packet with a data pointer in DTCM (e.g. a stack allocated payload), everything @@ -54,7 +54,7 @@ namespace mbed { return true; } - void STM32EthMacV2::TxDMA::giveToDMA(size_t descIdx, bool firstDesc, bool lastDesc) { + void STM32EthMacV2::TxDMA::giveToDMA(const size_t descIdx, const bool firstDesc, const bool lastDesc) { auto & desc = txDescs[descIdx]; // Note that we have to configure these every time as @@ -102,7 +102,7 @@ namespace mbed { base->DMACSR = ETH_DMACSR_RPS; } - void STM32EthMacV2::RxDMA::returnDescriptor(size_t descIdx, uint8_t *buffer) { + void STM32EthMacV2::RxDMA::returnDescriptor(const size_t descIdx, uint8_t * const buffer) { auto & desc = rxDescs[descIdx]; // Clear out any bits previously set in the descriptor (from when the DMA gave it back to us) @@ -126,4 +126,17 @@ namespace mbed { // to one location after the last DMA-owned descriptor in the FIFO. base->DMACRDTPR = reinterpret_cast(&rxDescs[rxBuildIndex]); } + + size_t STM32EthMacV2::RxDMA::getTotalLen(const size_t firstDescIdx) { + // Total length of the packet is in the first descriptor + return rxDescs[firstDescIdx].formats.fromDMA.pktLength; + } + + STM32EthMacV2::STM32EthMacV2(): + CompositeEMAC(txDMA, rxDMA), + base(ETH), + txDMA(base), + rxDMA(base) + { + } } diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h index 0fa5e775ed2..53b66aceaf7 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -58,7 +58,7 @@ namespace mbed { void returnDescriptor(size_t descIdx, uint8_t *buffer) override; - DescriptorType getType(const stm32_ethv2::EthRxDescriptor &desc) override; + size_t getTotalLen(size_t firstDescIdx) override; public: explicit RxDMA(ETH_TypeDef * const base): @@ -67,8 +67,14 @@ namespace mbed { {} }; + ETH_TypeDef * const base; // Base address of Ethernet peripheral + // Components of the ethernet MAC TxDMA txDMA; + RxDMA rxDMA; + + public: + STM32EthMacV2(); }; } diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h b/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h index e242d755af4..d951e4cb027 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h @@ -208,10 +208,6 @@ namespace mbed { bool isLastDesc() const { return formats.fromDMA.lastDescriptor; } - - bool getPayloadLength() const { - return formats.fromDMA.pktLength; - } }; #if __DCACHE_PRESENT diff --git a/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 b/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 index 376df44a506..adcbdae1625 100644 --- a/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 +++ b/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 @@ -1,11 +1,5 @@ { "name": "stm32-emac", - "config": { - "max-mcast-subscribes": { - "help" : "Maximum supported number of multicast addresses that the application can subscribe to", - "value" : "8" - } - }, "target_overrides": { "ARCH_MAX": { "eth-phy-address": 1 diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index f545f729290..30ece452f68 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -184,21 +184,21 @@ class CompositeEMAC : public EMAC /** * @brief Set whether the MAC passes all multicast traffic up to the application. * - * @param pass True to pass all mcasts, false otherwise + * CompositeEMAC will ensure this is called only after init(). * - * @return Error code or success + * @param pass True to pass all mcasts, false otherwise */ - virtual ErrCode setPassAllMcast(bool pass); + virtual void setPassAllMcast(bool pass); /** * @brief Set promiscuous mode (where the Eth MAC passes all traffic up to the application, regardless * of its destination address). * - * @param enable True to pass all traffic, false otherwise + * CompositeEMAC will ensure this is called only after init(). * - * @return Error code or success + * @param enable True to pass all traffic, false otherwise */ - virtual ErrCode setPromiscuous(bool enable); + virtual void setPromiscuous(bool enable); }; /** @@ -331,6 +331,9 @@ class CompositeEMAC : public EMAC protected: + /// Pointer to memory manager for the EMAC + EMACMemoryManager * memory_manager = nullptr; + // Instances of each of the 4 component classes MACDriver & mac; PhyDriver & phy; @@ -346,12 +349,26 @@ class CompositeEMAC : public EMAC std::atomic state = PowerState::OFF; + // Multicast subscribe information + MACAddress mcastMacs[MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES]; + + // Note: if this variable becomes >= MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES, we are in multicast + // fallback mode and are accepting all multicasts. The only way to get out of this mode is to power down + // and power up the MAC. + size_t numSubscribedMcastMacs; + /// Subclass should call this when a receive interrupt is detected void rxISR(); /// Subclass should call this when a transmit complete interrupt is detected void txISR(); + /// Constructor. Should be called by subclass. + CompositeEMAC(TxDMA & txDMA, RxDMA & rxDMA): + txDMA(txDMA), + rxDMA(rxDMA) + {} + public: uint32_t get_mtu_size() const override { // Regular Ethernet has an MTU of 1500. @@ -396,7 +413,10 @@ class CompositeEMAC : public EMAC void set_all_multicast(bool all) override; - void set_memory_manager(EMACMemoryManager &mem_mngr) override; + void set_memory_manager(EMACMemoryManager &mem_mngr) override { + MBED_ASSERT(state == PowerState::OFF); + memory_manager = &mem_mngr; + } }; } diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 8fb930d89c7..00dfcf0bd86 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -197,8 +197,8 @@ namespace mbed { } } - tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", - memory_manager.get_total_len(buf), memory_manager.count_buffers(buf), neededDescs); + tr_info("Transmitting packet of length %lu in %zu buffers and %zu descs\n", + memory_manager->get_total_len(buf), memory_manager->count_buffers(buf), neededDescs); // Step 2: Copy packet if needed if(needToCopy) @@ -340,6 +340,11 @@ namespace mbed { /// to tell it to start running again. virtual void returnDescriptor(size_t descIdx, uint8_t * buffer) = 0; + /// Get the length of the packet starting at firstDescIdx and continuing until the + /// next descriptor with the last descriptor flag set. Descriptors have already been validated to contain a + /// complete packet at this point. + virtual size_t getTotalLen(size_t firstDescIdx) = 0; + public: CompositeEMAC::ErrCode init() override { rxPoolPayloadSize = memory_manager->get_pool_alloc_unit(RX_BUFFER_ALIGN); @@ -453,7 +458,6 @@ namespace mbed { // Descriptor owned by DMA and has not been filled in yet. We are out of descriptors to process. break; } - const auto type = getType(descriptor); if (descriptor.isErrorDesc() || (!descriptor.isFirstDesc() && !firstDescIdx.has_value())) { @@ -506,42 +510,45 @@ namespace mbed { // Update this now to tell the ISR to search for descriptors after lastDescIdx only. rxNextIndex = (*lastDescIdx + 1) % RX_NUM_DESCS; - // NOTE: Currently we do not make any attempt to set the length of the Rx buffer to match - // how many bytes were actually received. This is because different MACs provide this info differently: - // some provide a byte count in each descriptor while others provide one in the first descriptor - // for the entire chain. It's easier to simply ignore this information as the network stack can - // figure it out. - // So, the buffers we pass up will always have a langth that's a multiple of the pool alloc unit. - + // Set length of first buffer + net_stack_mem_buf_t *const headBuffer = rxDescStackBufs[*firstDescIdx]; + size_t lenRemaining = getTotalLen(*firstDescIdx); + memory_manager->set_len(headBuffer, std::min(lenRemaining, rxPoolPayloadSize)); + lenRemaining -= std::min(lenRemaining, rxPoolPayloadSize); // Iterate through the subsequent descriptors in this packet and link the buffers // Note that this also transfers ownership of subsequent buffers to the first buffer, // so if the first buffer is deleted, the others will be as well. - net_stack_mem_buf_t *const headBuffer = rxDescStackBufs[*firstDescIdx]; + ++rxDescsOwnedByApplication; // for first buffer rxDescStackBufs[*firstDescIdx] = nullptr; for (size_t descIdx = (*firstDescIdx + 1) % RX_NUM_DESCS; descIdx != (*lastDescIdx + 1) % RX_NUM_DESCS; descIdx = (descIdx + 1) % RX_NUM_DESCS) { - memory_manager->cat(headBuffer, rxDescs[descIdx].buffer); - rxDescs[descIdx].buffer = nullptr; + // We have to set the buffer length first before concatenating it to the chain + MBED_ASSERT(lenRemaining > 0); + memory_manager->set_len(rxDescStackBufs[descIdx], std::min(lenRemaining, rxPoolPayloadSize)); + lenRemaining -= std::min(lenRemaining, rxPoolPayloadSize); + + memory_manager->cat(headBuffer, rxDescStackBufs[descIdx]); + rxDescStackBufs[descIdx] = nullptr; ++rxDescsOwnedByApplication; } // Invalidate cache for all data buffers, as these were written by the DMA to main memory - #if __DCACHE_PRESENT +#if __DCACHE_PRESENT auto * bufToInvalidate = headBuffer; while(bufToInvalidate != nullptr) { - SCB_InvalidateDCache_by_Addr(memory_manager.get_ptr(bufToInvalidate), rxPoolPayloadSize); - bufToInvalidate = memory_manager.get_next(bufToInvalidate); + SCB_InvalidateDCache_by_Addr(memory_manager->get_ptr(bufToInvalidate), rxPoolPayloadSize); + bufToInvalidate = memory_manager->get_next(bufToInvalidate); } - #endif +#endif - tr_debug("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu (%p-%p)\n", - memory_manager.get_total_len(headBuffer), memory_manager.get_ptr(headBuffer), firstDescIdx, lastDescIdx, - &rxDescs[firstDescIdx], &rxDescs[lastDescIdx]); + tr_info("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu (%p-%p)\n", + memory_manager->get_total_len(headBuffer), memory_manager->get_ptr(headBuffer), *firstDescIdx, *lastDescIdx, + &rxDescs[*firstDescIdx], &rxDescs[*lastDescIdx]); return headBuffer; } diff --git a/connectivity/drivers/emac/sources/CompositeEMAC.cpp b/connectivity/drivers/emac/sources/CompositeEMAC.cpp index 34d0b61026e..767db292813 100644 --- a/connectivity/drivers/emac/sources/CompositeEMAC.cpp +++ b/connectivity/drivers/emac/sources/CompositeEMAC.cpp @@ -19,6 +19,8 @@ #include +#include + #define TRACE_GROUP "CEMAC" void mbed::CompositeEMAC::get_ifname(char *name, uint8_t size) const { @@ -39,3 +41,111 @@ void mbed::CompositeEMAC::set_hwaddr(const uint8_t *addr) { memcpy(macAddr.data(), addr, MAC_ADDR_SIZE); mac.setOwnMACAddr(macAddr); } + +bool mbed::CompositeEMAC::link_out(emac_mem_buf_t *buf) +{ + +} + +bool mbed::CompositeEMAC::power_up() +{ + if(state != PowerState::OFF) { + tr_err("power_up(): Already powered up!"); + return false; + } + + // Set memory manager everywhere + if(memory_manager == nullptr) { + tr_err("power_up(): Memory manager not set yet!"); + return false; + } + txDMA.setMemoryManager(memory_manager); + rxDMA.setMemoryManager(memory_manager); + + // Power up the MAC + +} + +void mbed::CompositeEMAC::add_multicast_group(const uint8_t *address) +{ + if(state == PowerState::OFF) { + tr_err("Not available while MAC is off!"); + return; + } + + ++numSubscribedMcastMacs; + + if(numSubscribedMcastMacs >= MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES) + { + tr_warn("Out of multicast group entries (currently have %d). Increase the 'nsapi.emac-max-mcast-subscribes' JSON option!", MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES); + // Fall back to accepting all multicasts + set_all_multicast(true); + return; + } + + memcpy(mcastMacs[numSubscribedMcastMacs - 1].data(), address, 6); + + if(mac.addMcastMAC(mcastMacs[numSubscribedMcastMacs - 1]) != ErrCode::SUCCESS) { + tr_err("addMcastMAC() failed!"); + } +} + +void mbed::CompositeEMAC::remove_multicast_group(const uint8_t *address) { + if(state == PowerState::OFF) { + tr_err("Not available while MAC is off!"); + return; + } + + if(numSubscribedMcastMacs >= MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES) { + // We are in fallback mode, so we can no longer unsusbscribe at the MAC level because we don't know + // which MACs are subscribed anymore. + return; + } + + // Find MAC address in the subscription list + auto macsEndIter = std::begin(mcastMacs) + numSubscribedMcastMacs; + auto toRemoveIter = std::find_if(std::begin(mcastMacs), macsEndIter, [&](auto element) { + return memcmp(element.data(), address, 6) == 0; + }); + + if(toRemoveIter == macsEndIter) + { + tr_warning("Tried to remove mcast group that was not added"); + return; + } + + // Swap the MAC addr to be removed to the end of the list, if it is not there already + auto lastElementIter = macsEndIter - 1; + if(toRemoveIter != std::begin(mcastMacs) && toRemoveIter != lastElementIter) + { + std::swap(*toRemoveIter, *lastElementIter); + } + + // 'remove' the last element by changing the length + numSubscribedMcastMacs--; + + // Clear the filter and re-add all the addresses except the desired one + if(mac.clearMcastFilter() != ErrCode::SUCCESS) { + tr_err("clearMcastFilter() failed!"); + return; + } + for(size_t macIdx = 0; macIdx < numSubscribedMcastMacs; ++macIdx) { + if(mac.addMcastMAC(mcastMacs[macIdx]) != ErrCode::SUCCESS) { + tr_err("addMcastMAC() failed!"); + } + } +} + +void mbed::CompositeEMAC::set_all_multicast(bool all) { + if(state == PowerState::OFF) { + tr_err("Not available while MAC is off!"); + return; + } + + if(numSubscribedMcastMacs >= MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES && !all) { + // Don't allow setting pass all multicast to off while we are in fallback mode + return; + } + + mac.setPassAllMcast(all); +} diff --git a/connectivity/netsocket/mbed_lib.json5 b/connectivity/netsocket/mbed_lib.json5 index 22befc80a36..d80739c75dd 100644 --- a/connectivity/netsocket/mbed_lib.json5 +++ b/connectivity/netsocket/mbed_lib.json5 @@ -89,6 +89,10 @@ "emac-tx-num-descs": { "help": "Number of Tx descriptors in the Ethernet MAC DMA ring. This affects how much data can be queued for Tx at one time", "value": 8 + }, + "emac-max-mcast-subscribes": { + "help" : "Maximum supported number of multicast addresses that the application can subscribe to. If this limit is reached, we give up and enable all mcast groups.", + "value" : 8 } }, "target_overrides": { From 599fc10ede78c872f4e4b8192aa9d8dfdc8fb654 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Thu, 13 Feb 2025 09:40:23 -0800 Subject: [PATCH 11/47] Progress on MAC driver --- .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 227 +++++++++++++++++- .../drivers/emac/TARGET_STM/STM32EthMACv2.h | 50 ++++ .../drivers/emac/include/CompositeEMAC.h | 9 +- targets/TARGET_STM/mbed_overrides.c | 31 +++ 4 files changed, 313 insertions(+), 4 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index 711bc21af0c..72bd9637cf9 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -16,6 +16,15 @@ #include "STM32EthMACv2.h" +#include "mbed_power_mgmt.h" +#include "Timer.h" +#include "mbed_error.h" + +using namespace std::chrono_literals; + +// Defined in stm32_eth_init.c +extern "C" void EthInitPinmappings(void); + namespace mbed { void STM32EthMacV2::TxDMA::startDMA() { // Flush Tx queue @@ -132,11 +141,227 @@ namespace mbed { return rxDescs[firstDescIdx].formats.fromDMA.pktLength; } + void STM32EthMacV2::MacDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) + { + uint32_t hclk; + uint32_t tmpreg; + + /* Get the ETHERNET MACMDIOAR value */ + tmpreg = base->MACMDIOAR; + + /* Clear CSR Clock Range bits */ + tmpreg &= ~ETH_MACMDIOAR_CR; + + /* Get hclk frequency value */ + hclk = HAL_RCC_GetHCLKFreq(); + + /* Set CR bits depending on hclk value */ + if (hclk < 35000000U) + { + /* CSR Clock Range between 0-35 MHz */ + tmpreg |= (uint32_t)ETH_MACMDIOAR_CR_DIV16; + } + else if (hclk < 60000000U) + { + /* CSR Clock Range between 35-60 MHz */ + tmpreg |= (uint32_t)ETH_MACMDIOAR_CR_DIV26; + } + else if (hclk < 100000000U) + { + /* CSR Clock Range between 60-100 MHz */ + tmpreg |= (uint32_t)ETH_MACMDIOAR_CR_DIV42; + } + else if (hclk < 150000000U) + { + /* CSR Clock Range between 100-150 MHz */ + tmpreg |= (uint32_t)ETH_MACMDIOAR_CR_DIV62; + } + else if (hclk < 250000000U) + { + /* CSR Clock Range between 150-250 MHz */ + tmpreg |= (uint32_t)ETH_MACMDIOAR_CR_DIV102; + } + else /* (hclk >= 250000000U) */ + { + /* CSR Clock >= 250 MHz */ + tmpreg |= (uint32_t)(ETH_MACMDIOAR_CR_DIV124); + } + + /* Configure the CSR Clock Range */ + base->MACMDIOAR = (uint32_t)tmpreg; + } + + + CompositeEMAC::ErrCode STM32EthMacV2::MacDriver::init() { + sleep_manager_lock_deep_sleep(); + + // Note: Following code is based on HAL_Eth_Init() from the HAL + /* Init the low level hardware : GPIO, CLOCK, NVIC. */ + EthInitPinmappings(); + + // Use RMII + HAL_SYSCFG_ETHInterfaceSelect(SYSCFG_ETH_RMII); + + /* Dummy read to sync with ETH */ + (void)SYSCFG->PMCR; + + /* Ethernet Software reset */ + /* Set the SWR bit: resets all MAC subsystem internal registers and logic */ + /* After reset all the registers holds their respective reset values */ + base->DMAMR |= ETH_DMAMR_SWR; + + const auto ETH_SW_RESET_TIMEOUT = 500us; // used by STM32 HAL + Timer timeoutTimer; + timeoutTimer.start(); + while(timeoutTimer.elapsed_time() < ETH_SW_RESET_TIMEOUT && (base->DMAMR & ETH_DMAMR_SWR)) {} + if(base->DMAMR & ETH_DMAMR_SWR) { + // Reset failed to complete within expected timeout + return ErrCode::TIMEOUT; + } + + /*------------------ MDIO CSR Clock Range Configuration --------------------*/ + ETH_SetMDIOClockRange(base); + + /*------------------ MAC LPI 1US Tic Counter Configuration --------------------*/ + base->MAC1USTCR = (HAL_RCC_GetHCLKFreq() / 1000000U) - 1U; + + // MAC configuration + base->MACCR = ETH_MACCR_SARC_REPADDR0; // Replace the SA field in Tx packets with the configured source address + base->MTLTQOMR |= ETH_MTLTQOMR_TSF_Msk; // Enable store and forward mode for transmission (default in the HAL) + + // Enable multicast hash and perfect filter + base->MACPFR = ETH_MACPFR_HMC | ETH_MACPFR_HPF; + + // Default CubeHAL DMA settings + base->DMASBMR = ETH_DMASBMR_AAL_Msk | ETH_DMASBMR_FB_Msk; + base->DMACTCR = ETH_DMACTCR_TPBL_32PBL; + base->DMACRCR = ETH_DMACRCR_RPBL_32PBL; + + // Configure spacing between DMA descriptors. This will be different depending on + // cache line sizes. + // NOTE: Cast pointers to uint8_t so that the difference will be returned in bytes instead + // of elements. + const size_t rxSpacing = sizeof(stm32_ethv2::EthRxDescriptor); + + // Check that spacing seems valid +#ifndef NDEBUG + const size_t txSpacing = sizeof(stm32_ethv2::EthTxDescriptor); + MBED_ASSERT(rxSpacing == txSpacing); + MBED_ASSERT(rxSpacing % sizeof(uint32_t) == 0); +#endif + + // The spacing bitfield is configured as the number of 32-bit words to skip between descriptors. + // The descriptors have a default size of 16 bytes. + const size_t wordsToSkip = (rxSpacing - 16) / sizeof(uint32_t); + MBED_ASSERT(wordsToSkip <= 7); + base->DMACCR &= ~ETH_DMACCR_DSL_Msk; + base->DMACCR |= wordsToSkip << ETH_DMACCR_DSL_Pos; + + // Set up interrupt handler + NVIC_SetVector(ETH_IRQn, reinterpret_cast(&STM32EthMacV2::irqHandler)); + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); + + // Enable Tx, Rx, and fatal bus error interrupts. + // However, don't enable receive buffer unavailable interrupt, because that can + // trigger if we run out of Rx descriptors, and we don't want to fatal error + // in that case. + base->DMACIER = ETH_DMACIER_NIE | ETH_DMACIER_RIE | ETH_DMACIER_TIE | ETH_DMACIER_FBEE | ETH_DMACIER_AIE; + + + return ErrCode::SUCCESS; + } + + CompositeEMAC::ErrCode STM32EthMacV2::MacDriver::deinit() + { + // Disable transmission and reception + disable(); + + // Disable interrupt + HAL_NVIC_DisableIRQ(ETH_IRQn); + + // Unlock deep sleep + sleep_manager_unlock_deep_sleep(); + + return ErrCode::SUCCESS; + } + + CompositeEMAC::ErrCode STM32EthMacV2::MacDriver::enable(LinkSpeed speed, Duplex duplex) + { + if(speed == LinkSpeed::LINK_1GBIT) { + return ErrCode::INVALID_ARGUMENT; + } + + auto maccrVal = base->MACCR; + if(speed == LinkSpeed::LINK_100MBIT) { + maccrVal |= ETH_MACCR_FES_Msk; + } + else { + maccrVal &= ~ETH_MACCR_FES_Msk; + } + if(duplex == Duplex::FULL) { + maccrVal |= ETH_MACCR_DM_Msk; + } + else { + maccrVal &= ~ETH_MACCR_DM_Msk; + } + + // Enable the MAC transmission & reception + maccrVal |= ETH_MACCR_TE | ETH_MACCR_RE; + base->MACCR = maccrVal; + return ErrCode::SUCCESS; + } + + CompositeEMAC::ErrCode STM32EthMacV2::MacDriver::disable() + { + base->MACCR &= ~(ETH_MACCR_TE | ETH_MACCR_RE); + return ErrCode::SUCCESS; + } + + void STM32EthMacV2::MacDriver::setOwnMACAddr(const MACAddress &ownAddress) { + } + + STM32EthMacV2 * STM32EthMacV2::instance = nullptr; + STM32EthMacV2::STM32EthMacV2(): CompositeEMAC(txDMA, rxDMA), base(ETH), txDMA(base), - rxDMA(base) + rxDMA(base), + macDriver(base) { + instance = this; + } + + void STM32EthMacV2::irqHandler() + { + const auto emacInst = instance; + uint32_t dma_flag = emacInst->base->DMACSR; + + /* Packet received */ + if ((dma_flag & ETH_DMACSR_RI) != 0U) + { + /* Clear the Eth DMA Rx IT pending bits */ + ETH->DMACSR = ETH_DMACSR_RI | ETH_DMACSR_NIS; + + emacInst->rxISR(); + } + + /* Packet transmitted */ + if ((dma_flag & ETH_DMACSR_TI) != 0U) + { + /* Clear the Eth DMA Tx IT pending bits */ + ETH->DMACSR = ETH_DMACSR_TI | ETH_DMACSR_NIS; + + emacInst->txISR(); + } + + /* ETH DMA Error */ + if(dma_flag & ETH_DMACSR_FBE) + { + MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_ETHERNET, EIO), \ + "STM32 EMAC v2: Hardware reports fatal DMA error\n"); + } } + } diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h index 53b66aceaf7..239b537643f 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -67,14 +67,64 @@ namespace mbed { {} }; + class MacDriver : public CompositeEMAC::MACDriver { + ETH_TypeDef * const base; // Base address of Ethernet peripheral + + /** + * @brief Configures the Clock range of ETH MDIO interface. + * + * Copied from STM32CubeHAL. + * + * @param base Base address of Ethernet peripheral + */ + static void ETH_SetMDIOClockRange(ETH_TypeDef * const base); + + public: + explicit MacDriver(ETH_TypeDef * const base): + base(base) + {} + + ErrCode init() override; + + ErrCode deinit() override; + + ErrCode enable(LinkSpeed speed, Duplex duplex) override; + + ErrCode disable() override; + + void setOwnMACAddr(const MACAddress &ownAddress) override; + + ErrCode mdioRead(uint8_t devAddr, uint8_t regAddr, uint16_t &result) override; + + ErrCode mdioWrite(uint8_t devAddr, uint8_t regAddr, uint16_t data) override; + + PinName getPhyResetPin() override; + + ErrCode addMcastMAC(MACAddress mac) override; + + ErrCode clearMcastFilter() override; + + void setPassAllMcast(bool pass) override; + + void setPromiscuous(bool enable) override; + }; + + // Pointer to global instance, for ISR to use. + // TODO if we support more than 1 EMAC per MCU, this will need to be an array + static STM32EthMacV2 * instance; + ETH_TypeDef * const base; // Base address of Ethernet peripheral // Components of the ethernet MAC TxDMA txDMA; RxDMA rxDMA; + MACDriver macDriver; public: STM32EthMacV2(); + + // Interrupt callback + static void STM32EthMacV2::irqHandler(); }; } diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index 30ece452f68..4cafeb8c12d 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -90,9 +90,11 @@ class CompositeEMAC : public EMAC * * Thread safety: CompositeEMAC will guarantee only one thread is utilizing this class at a time. */ - class MACDriver + class MACDriver : NonCopyable { public: + virtual ~MACDriver() = default; + /** * @brief Initialize the MAC, map pins, and prepare it to send and receive packets. * It should not be enabled yet. @@ -364,9 +366,10 @@ class CompositeEMAC : public EMAC void txISR(); /// Constructor. Should be called by subclass. - CompositeEMAC(TxDMA & txDMA, RxDMA & rxDMA): + CompositeEMAC(TxDMA & txDMA, RxDMA & rxDMA, MACDriver & macDriver): txDMA(txDMA), - rxDMA(rxDMA) + rxDMA(rxDMA), + mac(macDriver) {} public: diff --git a/targets/TARGET_STM/mbed_overrides.c b/targets/TARGET_STM/mbed_overrides.c index 62ce3af0753..74748cdefec 100644 --- a/targets/TARGET_STM/mbed_overrides.c +++ b/targets/TARGET_STM/mbed_overrides.c @@ -299,3 +299,34 @@ void mbed_sdk_init() mbed_sdk_inited = 1; } + +// Override MAC default MAC address based on chip unique ID +#if defined (TARGET_STM32F2) || defined (TARGET_STM32F4) || defined (TARGET_STM32F7) || defined (TARGET_STM32H7) || defined(TARGET_STM32H5) +void mbed_mac_address(char *mac) +{ + unsigned char ST_mac_addr[3] = {0x00, 0x80, 0xe1}; // default STMicro mac address + + // Read unic id +#if defined (TARGET_STM32F2) + uint32_t word0 = *(uint32_t *)0x1FFF7A10; +#elif defined (TARGET_STM32F4) + uint32_t word0 = *(uint32_t *)0x1FFF7A10; +#elif defined (TARGET_STM32F7) + uint32_t word0 = *(uint32_t *)0x1FF0F420; +#elif defined (TARGET_STM32H7) || defined(TARGET_STM32H5) + uint32_t word0 = *(uint32_t *)UID_BASE; +#else +#error MAC address can not be derived from target unique Id +#endif + + mac[0] = ST_mac_addr[0]; + mac[1] = ST_mac_addr[1]; + mac[2] = ST_mac_addr[2]; + + // TODO this code is only using 24 bits of the 96 bit unique identifier, so collisions are possible. + // It should be updated to use a hash. + mac[3] = (word0 & 0x00ff0000) >> 16; + mac[4] = (word0 & 0x0000ff00) >> 8; + mac[5] = (word0 & 0x000000ff); +} +#endif From 8f30f73f35f12c9d917f32967f66e9894a7855c5 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Fri, 14 Feb 2025 09:44:36 -0800 Subject: [PATCH 12/47] Initial implementation of MAC driver complete! --- .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 173 ++++++++++++++++-- .../drivers/emac/TARGET_STM/STM32EthMACv2.h | 17 +- .../TARGET_DISCO_H747I/stm32h7_eth_init.c | 7 + .../TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c | 7 + .../TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c | 7 + .../TARGET_PORTENTA_H7/stm32h7_eth_init.c | 7 + .../drivers/emac/include/CompositeEMAC.h | 9 +- .../drivers/emac/sources/CompositeEMAC.cpp | 38 ++++ 8 files changed, 248 insertions(+), 17 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index 72bd9637cf9..868632a1011 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -19,11 +19,14 @@ #include "mbed_power_mgmt.h" #include "Timer.h" #include "mbed_error.h" +#include "MbedCRC.h" using namespace std::chrono_literals; // Defined in stm32_eth_init.c -extern "C" void EthInitPinmappings(void); +extern "C" void EthInitPinmappings(); +extern "C" void EthDeinitPinmappings(); +extern "C" PinName EthGetPhyResetPin(); namespace mbed { void STM32EthMacV2::TxDMA::startDMA() { @@ -141,7 +144,7 @@ namespace mbed { return rxDescs[firstDescIdx].formats.fromDMA.pktLength; } - void STM32EthMacV2::MacDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) + void STM32EthMacV2::MACDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) { uint32_t hclk; uint32_t tmpreg; @@ -191,8 +194,46 @@ namespace mbed { base->MACMDIOAR = (uint32_t)tmpreg; } + void STM32EthMacV2::MACDriver::writeMACAddress(const MACAddress &mac, volatile uint32_t *addrHighReg, + volatile uint32_t *addrLowReg) { + /* Set MAC addr bits 32 to 47 */ + *addrHighReg = (static_cast(mac[5]) << 8) | static_cast(mac[4]) | ETH_MACA1HR_AE_Msk; + /* Set MAC addr bits 0 to 31 */ + *addrLowReg = (static_cast(mac[3]) << 24) | (static_cast(mac[2]) << 16) | + (static_cast(mac[1]) << 8) | static_cast(mac[0]); + } + + void STM32EthMacV2::MACDriver::addHashFilterMAC(const MACAddress &mac) { + + uint32_t volatile * hashRegs[] = { + &base->MACHT0R, + &base->MACHT1R + }; + + // Note: as always, the datasheet description of how to do this CRC was vague and slightly wrong. + // This forum thread figured it out: https://community.st.com/t5/stm32-mcus-security/calculating-ethernet-multicast-filter-hash-value/td-p/416984 + // What the datasheet SHOULD say is: + // Compute the Ethernet CRC-32 of the MAC address, with initial value of 1s, final XOR of ones, and input reflection on but output reflection off + // Then, take the upper 6 bits and use that to index the hash table. + + mbed::MbedCRC crcCalc(0xFFFFFFFF, 0xFFFFFFFF, true, false); + tr_debug("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, + currMacAddr[0], currMacAddr[1], currMacAddr[2], + currMacAddr[3], currMacAddr[4], currMacAddr[5]); + + // Compute Ethernet CRC-32 of the MAC address + uint32_t crc; + crcCalc.compute(mac.data(), mac.size(), &crc); + + // Take upper 6 bits + uint32_t hashVal = crc >> 26; - CompositeEMAC::ErrCode STM32EthMacV2::MacDriver::init() { + // Set correct bit in hash filter + *hashRegs[hashVal >> 5] |= (1 << (hashVal & 0x1F)); + } + + + CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::init() { sleep_manager_lock_deep_sleep(); // Note: Following code is based on HAL_Eth_Init() from the HAL @@ -272,21 +313,21 @@ namespace mbed { return ErrCode::SUCCESS; } - CompositeEMAC::ErrCode STM32EthMacV2::MacDriver::deinit() + CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::deinit() { - // Disable transmission and reception - disable(); - // Disable interrupt HAL_NVIC_DisableIRQ(ETH_IRQn); // Unlock deep sleep sleep_manager_unlock_deep_sleep(); + // Unmap pins and turn off clock + EthDeinitPinmappings(); + return ErrCode::SUCCESS; } - CompositeEMAC::ErrCode STM32EthMacV2::MacDriver::enable(LinkSpeed speed, Duplex duplex) + CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::enable(LinkSpeed speed, Duplex duplex) { if(speed == LinkSpeed::LINK_1GBIT) { return ErrCode::INVALID_ARGUMENT; @@ -312,19 +353,129 @@ namespace mbed { return ErrCode::SUCCESS; } - CompositeEMAC::ErrCode STM32EthMacV2::MacDriver::disable() + CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::disable() { base->MACCR &= ~(ETH_MACCR_TE | ETH_MACCR_RE); return ErrCode::SUCCESS; } - void STM32EthMacV2::MacDriver::setOwnMACAddr(const MACAddress &ownAddress) { + void STM32EthMacV2::MACDriver::setOwnMACAddr(const MACAddress &ownAddress) { + // Set MAC address + writeMACAddress(ownAddress, &base->MACA0HR, &base->MACA0LR); + } + + const auto MDIO_TRANSACTION_TIMEOUT = 1ms; // used by STMicro HAL + + CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::mdioRead(uint8_t devAddr, uint8_t regAddr, uint16_t &result) { + // This code based on HAL_ETH_ReadPHYRegister() + if(base->MACMDIOAR & ETH_MACMDIOAR_MB_Msk) { + // MDIO operation already in progress + return ErrCode::INVALID_USAGE; + } + + uint32_t tmpreg = base->MACMDIOAR; + + /* Prepare the MDIO Address Register value + - Set the PHY device address + - Set the PHY register address + - Set the read mode + - Set the MII Busy bit */ + tmpreg &= ~(ETH_MACMDIOAR_PA_Msk | ETH_MACMDIOAR_RDA_Msk | ETH_MACMDIOAR_MOC_Msk); + tmpreg |= (devAddr << ETH_MACMDIOAR_PA_Pos) | (regAddr << ETH_MACMDIOAR_RDA_Pos) | ETH_MACMDIOAR_MOC_RD | ETH_MACMDIOAR_MB_Msk; + base->MACMDIOAR = tmpreg; + + Timer timeoutTimer; + timeoutTimer.start(); + while(timeoutTimer.elapsed_time() < MDIO_TRANSACTION_TIMEOUT && (base->MACMDIOAR & ETH_MACMDIOAR_MB_Msk)) {} + if(base->MACMDIOAR & ETH_MACMDIOAR_MB_Msk) { + // Transaction failed to complete within expected timeout + return ErrCode::TIMEOUT; + } + + // Get result + result = base->MACMDIODR & ETH_MACMDIODR_MD_Msk; + + return ErrCode::SUCCESS; + } + + CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::mdioWrite(uint8_t devAddr, uint8_t regAddr, uint16_t data) { + // This code based on HAL_ETH_WritePHYRegister() + if(base->MACMDIOAR & ETH_MACMDIOAR_MB_Msk) { + // MDIO operation already in progress + return ErrCode::INVALID_USAGE; + } + + /* Give the value to the MII data register */ + base->MACMDIODR = data << ETH_MACMDIODR_MD_Pos; + + uint32_t tmpreg = base->MACMDIOAR; + + /* Prepare the MDIO Address Register value + - Set the PHY device address + - Set the PHY register address + - Set the write mode + - Set the MII Busy bit */ + tmpreg &= ~(ETH_MACMDIOAR_PA_Msk | ETH_MACMDIOAR_RDA_Msk | ETH_MACMDIOAR_MOC_Msk); + tmpreg |= (devAddr << ETH_MACMDIOAR_PA_Pos) | (regAddr << ETH_MACMDIOAR_RDA_Pos) | ETH_MACMDIOAR_MOC_WR | ETH_MACMDIOAR_MB_Msk; + base->MACMDIOAR = tmpreg; + + Timer timeoutTimer; + timeoutTimer.start(); + while(timeoutTimer.elapsed_time() < MDIO_TRANSACTION_TIMEOUT && (base->MACMDIOAR & ETH_MACMDIOAR_MB_Msk)) {} + if(base->MACMDIOAR & ETH_MACMDIOAR_MB_Msk) { + // Transaction failed to complete within expected timeout + return ErrCode::TIMEOUT; + } + + return ErrCode::SUCCESS; + } + + PinName STM32EthMacV2::MACDriver::getPhyResetPin() { + return EthGetPhyResetPin(); + } + + inline constexpr size_t NUM_PERFECT_FILTER_REGS = 3; + std::pair MAC_ADDR_PERF_FILTER_REGS[NUM_PERFECT_FILTER_REGS] = { + {Ð->MACA1HR, Ð->MACA1LR}, + {Ð->MACA2HR, Ð->MACA2LR}, + {Ð->MACA3HR, Ð->MACA3LR} + }; + + CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::addMcastMAC(MACAddress mac) { + if(numPerfectFilterRegsUsed < NUM_PERFECT_FILTER_REGS) { + size_t perfFiltIdx = numPerfectFilterRegsUsed; + ++numPerfectFilterRegsUsed; + + tr_debug("Using perfect filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + writeMACAddress(mac, MAC_ADDR_PERF_FILTER_REGS[perfFiltIdx].first, MAC_ADDR_PERF_FILTER_REGS[perfFiltIdx].second); + } + else { + // Out of spaces in perfect filter, use hash filter instead + addHashFilterMAC(mac); + } + return ErrCode::SUCCESS; + } + + CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::clearMcastFilter() { + // Reset perfect filter registers + for(auto regPair : MAC_ADDR_PERF_FILTER_REGS) { + *regPair.first = 0; + *regPair.second = 0; + } + numPerfectFilterRegsUsed = 0; + + // Reset hash filter + base->MACHT0R = 0; + base->MACHT1R = 0; + + return ErrCode::SUCCESS; } STM32EthMacV2 * STM32EthMacV2::instance = nullptr; STM32EthMacV2::STM32EthMacV2(): - CompositeEMAC(txDMA, rxDMA), + CompositeEMAC(txDMA, rxDMA, macDriver), base(ETH), txDMA(base), rxDMA(base), diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h index 239b537643f..63c1268b15b 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -67,9 +67,12 @@ namespace mbed { {} }; - class MacDriver : public CompositeEMAC::MACDriver { + class MACDriver : public CompositeEMAC::MACDriver { ETH_TypeDef * const base; // Base address of Ethernet peripheral + // Number of MAC address perfect filter registers used + size_t numPerfectFilterRegsUsed = 0; + /** * @brief Configures the Clock range of ETH MDIO interface. * @@ -79,8 +82,14 @@ namespace mbed { */ static void ETH_SetMDIOClockRange(ETH_TypeDef * const base); + /// Write a MAC address into the given registers with the needed encoding + void writeMACAddress(const MACAddress & mac, volatile uint32_t *addrHighReg, volatile uint32_t *addrLowReg); + + /// Add a MAC address to the multicast hash filter. + void addHashFilterMAC(const MACAddress & mac); + public: - explicit MacDriver(ETH_TypeDef * const base): + explicit MACDriver(ETH_TypeDef * const base): base(base) {} @@ -118,13 +127,13 @@ namespace mbed { // Components of the ethernet MAC TxDMA txDMA; RxDMA rxDMA; - MACDriver macDriver; + STM32EthMacV2::MACDriver macDriver; public: STM32EthMacV2(); // Interrupt callback - static void STM32EthMacV2::irqHandler(); + static void irqHandler(); }; } diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c index 2e5b2ba0503..2b239110804 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_DISCO_H747I/stm32h7_eth_init.c @@ -37,6 +37,7 @@ #include "stm32h7xx_hal.h" #include "platform/mbed_critical.h" +#include "PinNames.h" #define ETH_TX_EN_Pin GPIO_PIN_11 #define ETH_TX_EN_GPIO_Port GPIOG @@ -135,4 +136,10 @@ void EthDeinitPinmappings() HAL_GPIO_DeInit(GPIOC, ETH_MDC_SAI4_D1_Pin | ETH_RXD0_Pin | ETH_RXD1_Pin); HAL_GPIO_DeInit(GPIOA, ETH_MDIO_Pin | ETH_REF_CLK_Pin | ETH_CRS_DV_Pin); +} + +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board } \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c index 62394c057f5..66cbd3ae5b7 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/stm32h7_eth_init.c @@ -30,6 +30,7 @@ #include "stm32h7xx_hal.h" #include "platform/mbed_critical.h" +#include "PinNames.h" #define MCO_Pin GPIO_PIN_0 #define MCO_GPIO_Port GPIOH @@ -149,4 +150,10 @@ void EthDeinitPinmappings() HAL_GPIO_DeInit(RMII_TXD1_GPIO_Port, RMII_TXD1_Pin); HAL_GPIO_DeInit(GPIOG, RMII_TX_EN_Pin | RMII_TXD0_Pin); +} + +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board } \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c index 65c736e89f7..94fb130e9f6 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c @@ -30,6 +30,7 @@ #include "stm32h7xx_hal.h" #include "platform/mbed_critical.h" +#include "PinNames.h" #define MCO_Pin GPIO_PIN_0 #define MCO_GPIO_Port GPIOH @@ -151,3 +152,9 @@ void EthDeinitPinmappings() HAL_GPIO_DeInit(GPIOG, RMII_TX_EN_Pin | RMII_TXD0_Pin); } + +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board +} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c index b2abbcb3755..bf5f85156a3 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/stm32h7_eth_init.c @@ -34,6 +34,7 @@ #include "stm32h7xx_hal.h" #include "portenta_power.h" #include "platform/mbed_critical.h" +#include "PinNames.h" #define ETH_TX_EN_Pin GPIO_PIN_11 #define ETH_TX_EN_GPIO_Port GPIOG @@ -161,3 +162,9 @@ void EthDeinitPinmappings() HAL_GPIO_WritePin(GPIOJ, GPIO_PIN_15, 0); } + +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return PJ_15; +} \ No newline at end of file diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index 4cafeb8c12d..02b60479cb9 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -107,6 +107,9 @@ class CompositeEMAC : public EMAC * @brief Deinit the MAC so that it's not using any clock/power. Should prepare for init() to be called * again. * + * After this function is called (from EMAC::power_down()), the MAC will not be used again + * until power_up() is called and the process starts again. + * * @return Error code or SUCCESS */ virtual ErrCode deinit() = 0; @@ -266,8 +269,9 @@ class CompositeEMAC : public EMAC /// Initialize this Tx DMA ring. virtual ErrCode init() = 0; - /// Stop the DMA running. This is called after the MAC is disabled. + /// Stop the DMA running. /// init() should be able to be called again after this function completes to restart DMA. + /// This shall be called after the MAC is disabled but before it is powered down. virtual ErrCode deinit() = 0; /// Reclaims the Tx buffers for any transmitted packets and frees their memory. @@ -300,6 +304,7 @@ class CompositeEMAC : public EMAC virtual ErrCode init() = 0; /// Stop the DMA running. init() should be able to be called again after this function completes to restart DMA. + /// This shall be called after the MAC is disabled but before it is powered down. virtual ErrCode deinit() = 0; /** @@ -367,8 +372,8 @@ class CompositeEMAC : public EMAC /// Constructor. Should be called by subclass. CompositeEMAC(TxDMA & txDMA, RxDMA & rxDMA, MACDriver & macDriver): - txDMA(txDMA), rxDMA(rxDMA), + txDMA(txDMA), mac(macDriver) {} diff --git a/connectivity/drivers/emac/sources/CompositeEMAC.cpp b/connectivity/drivers/emac/sources/CompositeEMAC.cpp index 767db292813..02b51859fa0 100644 --- a/connectivity/drivers/emac/sources/CompositeEMAC.cpp +++ b/connectivity/drivers/emac/sources/CompositeEMAC.cpp @@ -63,7 +63,45 @@ bool mbed::CompositeEMAC::power_up() rxDMA.setMemoryManager(memory_manager); // Power up the MAC + if(mac.init() != ErrCode::SUCCESS) { + tr_err("power_up(): Failed to init MAC!"); + return false; + } + + state = PowerState::ON_NO_LINK; + return true; +} + +void mbed::CompositeEMAC::power_down() { + // TODO stop phy task + + state = PowerState::OFF; + + // TODO sync with other thread(s), ensure that no other threads are accessing the MAC + // (lock mutex?) + + // Clear multicast filter, so that we start with a clean slate next time + if(mac.clearMcastFilter() != ErrCode::SUCCESS) { + tr_err("power_down(): Failed to clear mcast filter"); + return; + } + + // Disable tx & rx + if(mac.disable() != ErrCode::SUCCESS) { + tr_err("power_down(): Failed to disable MAC"); + return; + } + // Disable DMA + if(txDMA.deinit() != ErrCode::SUCCESS || rxDMA.deinit() != ErrCode::SUCCESS) { + tr_err("power_down(): Failed to disable DMA"); + return; + } + + // Finally, disable the MAC itself + if(mac.deinit() != ErrCode::SUCCESS) { + tr_err("power_down(): Failed to disable MAC"); + } } void mbed::CompositeEMAC::add_multicast_group(const uint8_t *address) From 7b0d5340b6cfd9ac0c66c4035acf5894ddc8dd75 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sat, 15 Feb 2025 19:14:10 -0800 Subject: [PATCH 13/47] CompositeEMAC inits! --- .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 39 +++ .../drivers/emac/TARGET_STM/mbed_lib.json5 | 8 - .../drivers/emac/include/CompositeEMAC.h | 14 +- .../drivers/emac/sources/CompositeEMAC.cpp | 279 ++++++++++-------- .../drivers/emac/sources/GenericEthPhy.cpp | 2 +- .../drivers/emac/sources/PhyDrivers.cpp | 24 +- connectivity/netsocket/mbed_lib.json5 | 13 + .../netsocket/tests/TESTS/CMakeLists.txt | 6 - 8 files changed, 240 insertions(+), 145 deletions(-) delete mode 100644 connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index 868632a1011..055c0eb2d57 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -23,6 +23,8 @@ using namespace std::chrono_literals; +#define TRACE_GROUP "STEMACv2" + // Defined in stm32_eth_init.c extern "C" void EthInitPinmappings(); extern "C" void EthDeinitPinmappings(); @@ -114,6 +116,11 @@ namespace mbed { base->DMACSR = ETH_DMACSR_RPS; } + void STM32EthMacV2::RxDMA::stopDMA() { + // Disable Rx DMA + base->DMACTCR &= ~ETH_DMACRCR_SR; + } + void STM32EthMacV2::RxDMA::returnDescriptor(const size_t descIdx, uint8_t * const buffer) { auto & desc = rxDescs[descIdx]; @@ -395,6 +402,8 @@ namespace mbed { // Get result result = base->MACMDIODR & ETH_MACMDIODR_MD_Msk; + tr_debug("MDIO read devAddr %" PRIu8 ", regAddr 0x%" PRIx8 " -> 0x%" PRIx16, devAddr, regAddr, result); + return ErrCode::SUCCESS; } @@ -427,6 +436,8 @@ namespace mbed { return ErrCode::TIMEOUT; } + tr_debug("MDIO write devAddr %" PRIu8 ", regAddr 0x%" PRIx8 " <- 0x%" PRIx16, devAddr, regAddr, data); + return ErrCode::SUCCESS; } @@ -472,6 +483,28 @@ namespace mbed { return ErrCode::SUCCESS; } + void STM32EthMacV2::MACDriver::setPassAllMcast(bool pass) { + if(pass) + { + base->MACPFR |= ETH_MACPFR_PM; + } + else + { + base->MACPFR &= ~ETH_MACPFR_PM; + } + } + + void STM32EthMacV2::MACDriver::setPromiscuous(bool enable) { + if(enable) + { + base->MACPFR |= ETH_MACPFR_PR; + } + else + { + base->MACPFR &= ~ETH_MACPFR_PR; + } + } + STM32EthMacV2 * STM32EthMacV2::instance = nullptr; STM32EthMacV2::STM32EthMacV2(): @@ -514,5 +547,11 @@ namespace mbed { "STM32 EMAC v2: Hardware reports fatal DMA error\n"); } } +} +// Provide default EMAC driver +MBED_WEAK EMAC &EMAC::get_default_instance() +{ + static mbed::STM32EthMacV2 emac; + return emac; } diff --git a/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 b/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 deleted file mode 100644 index adcbdae1625..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/mbed_lib.json5 +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "stm32-emac", - "target_overrides": { - "ARCH_MAX": { - "eth-phy-address": 1 - } - } -} diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index 02b60479cb9..47450133c73 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -173,6 +173,10 @@ class CompositeEMAC : public EMAC /** * @brief Add a multicast MAC address that should be accepted by the MAC. * + * \note Only a maximum of \c nsapi.emac-max-mcast-subscribes multicast addresses will be subscribed to + * at once by the upper layer. If the application tried to subscribe to more than that, CEMAC will fall + * back to enabling pass all mcast. + * * @param mac MAC address to accept * * @return Error code or success @@ -193,7 +197,7 @@ class CompositeEMAC : public EMAC * * @param pass True to pass all mcasts, false otherwise */ - virtual void setPassAllMcast(bool pass); + virtual void setPassAllMcast(bool pass) = 0; /** * @brief Set promiscuous mode (where the Eth MAC passes all traffic up to the application, regardless @@ -203,7 +207,7 @@ class CompositeEMAC : public EMAC * * @param enable True to pass all traffic, false otherwise */ - virtual void setPromiscuous(bool enable); + virtual void setPromiscuous(bool enable) = 0; }; /** @@ -342,10 +346,10 @@ class CompositeEMAC : public EMAC EMACMemoryManager * memory_manager = nullptr; // Instances of each of the 4 component classes - MACDriver & mac; - PhyDriver & phy; - TxDMA & txDMA; + PhyDriver * phy = nullptr; RxDMA & rxDMA; + TxDMA & txDMA; + MACDriver & mac; // State of the MAC enum class PowerState { diff --git a/connectivity/drivers/emac/sources/CompositeEMAC.cpp b/connectivity/drivers/emac/sources/CompositeEMAC.cpp index 02b51859fa0..9d8a42d38d5 100644 --- a/connectivity/drivers/emac/sources/CompositeEMAC.cpp +++ b/connectivity/drivers/emac/sources/CompositeEMAC.cpp @@ -23,167 +23,198 @@ #define TRACE_GROUP "CEMAC" -void mbed::CompositeEMAC::get_ifname(char *name, uint8_t size) const { - // Note that LwIP only supports a two character interface name prefix. - // So, no point in going longer than that. - // Also note that we don't want to copy the terminating null if it doesn't fit. - const char * const ifPrefix = "en"; - memcpy(name, ifPrefix, (size < strlen(ifPrefix) + 1) ? size : strlen(ifPrefix) + 1); -} - -void mbed::CompositeEMAC::set_hwaddr(const uint8_t *addr) { - if(state != PowerState::ON_NO_LINK) { - tr_err("MAC address can only be set after power up, before link up!"); - return; +namespace mbed { + // Defined in PhyDrivers.cpp + CompositeEMAC::PhyDriver * mbed_get_eth_phy_driver(); + + void CompositeEMAC::rxISR() { } - MACAddress macAddr; - memcpy(macAddr.data(), addr, MAC_ADDR_SIZE); - mac.setOwnMACAddr(macAddr); -} + void CompositeEMAC::txISR() { + } -bool mbed::CompositeEMAC::link_out(emac_mem_buf_t *buf) -{ + void CompositeEMAC::get_ifname(char *name, uint8_t size) const { + // Note that LwIP only supports a two character interface name prefix. + // So, no point in going longer than that. + // Also note that we don't want to copy the terminating null if it doesn't fit. + const char * const ifPrefix = "en"; + memcpy(name, ifPrefix, (size < strlen(ifPrefix) + 1) ? size : strlen(ifPrefix) + 1); + } -} + void CompositeEMAC::set_hwaddr(const uint8_t *addr) { + if(state != PowerState::ON_NO_LINK) { + tr_err("MAC address can only be set after power up, before link up!"); + return; + } -bool mbed::CompositeEMAC::power_up() -{ - if(state != PowerState::OFF) { - tr_err("power_up(): Already powered up!"); - return false; + MACAddress macAddr; + memcpy(macAddr.data(), addr, MAC_ADDR_SIZE); + mac.setOwnMACAddr(macAddr); } - // Set memory manager everywhere - if(memory_manager == nullptr) { - tr_err("power_up(): Memory manager not set yet!"); - return false; - } - txDMA.setMemoryManager(memory_manager); - rxDMA.setMemoryManager(memory_manager); + bool CompositeEMAC::link_out(emac_mem_buf_t *buf) + { - // Power up the MAC - if(mac.init() != ErrCode::SUCCESS) { - tr_err("power_up(): Failed to init MAC!"); - return false; } - state = PowerState::ON_NO_LINK; - return true; -} + bool CompositeEMAC::power_up() + { + if(state != PowerState::OFF) { + tr_err("power_up(): Already powered up!"); + return false; + } -void mbed::CompositeEMAC::power_down() { - // TODO stop phy task + // Get phy + phy = mbed_get_eth_phy_driver(); + if(phy == nullptr) { + tr_err("power_up(): No Ethernet PHY driver configured! Either set nsapi.emac-phy-model or override mbed_get_eth_phy_driver()."); + return false; + } - state = PowerState::OFF; + // Set memory manager everywhere + if(memory_manager == nullptr) { + tr_err("power_up(): Memory manager not set yet!"); + return false; + } + txDMA.setMemoryManager(memory_manager); + rxDMA.setMemoryManager(memory_manager); - // TODO sync with other thread(s), ensure that no other threads are accessing the MAC - // (lock mutex?) + // Power up the MAC + if(mac.init() != ErrCode::SUCCESS) { + tr_err("power_up(): Failed to init MAC!"); + return false; + } - // Clear multicast filter, so that we start with a clean slate next time - if(mac.clearMcastFilter() != ErrCode::SUCCESS) { - tr_err("power_down(): Failed to clear mcast filter"); - return; - } + // Initialize the PHY + phy->setMAC(&mac); + if(phy->init() != ErrCode::SUCCESS) { + tr_err("power_up(): Failed to init PHY!"); + return false; + } - // Disable tx & rx - if(mac.disable() != ErrCode::SUCCESS) { - tr_err("power_down(): Failed to disable MAC"); - return; + state = PowerState::ON_NO_LINK; + return true; } - // Disable DMA - if(txDMA.deinit() != ErrCode::SUCCESS || rxDMA.deinit() != ErrCode::SUCCESS) { - tr_err("power_down(): Failed to disable DMA"); - return; - } + void CompositeEMAC::power_down() { + // TODO stop phy task - // Finally, disable the MAC itself - if(mac.deinit() != ErrCode::SUCCESS) { - tr_err("power_down(): Failed to disable MAC"); - } -} + state = PowerState::OFF; -void mbed::CompositeEMAC::add_multicast_group(const uint8_t *address) -{ - if(state == PowerState::OFF) { - tr_err("Not available while MAC is off!"); - return; - } + // TODO sync with other thread(s), ensure that no other threads are accessing the MAC + // (lock mutex?) - ++numSubscribedMcastMacs; + // Clear multicast filter, so that we start with a clean slate next time + if(mac.clearMcastFilter() != ErrCode::SUCCESS) { + tr_err("power_down(): Failed to clear mcast filter"); + return; + } - if(numSubscribedMcastMacs >= MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES) - { - tr_warn("Out of multicast group entries (currently have %d). Increase the 'nsapi.emac-max-mcast-subscribes' JSON option!", MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES); - // Fall back to accepting all multicasts - set_all_multicast(true); - return; - } + // Disable tx & rx + if(mac.disable() != ErrCode::SUCCESS) { + tr_err("power_down(): Failed to disable MAC"); + return; + } - memcpy(mcastMacs[numSubscribedMcastMacs - 1].data(), address, 6); + // Disable DMA + if(txDMA.deinit() != ErrCode::SUCCESS || rxDMA.deinit() != ErrCode::SUCCESS) { + tr_err("power_down(): Failed to disable DMA"); + return; + } - if(mac.addMcastMAC(mcastMacs[numSubscribedMcastMacs - 1]) != ErrCode::SUCCESS) { - tr_err("addMcastMAC() failed!"); + // Finally, disable the MAC itself + if(mac.deinit() != ErrCode::SUCCESS) { + tr_err("power_down(): Failed to disable MAC"); + } } -} -void mbed::CompositeEMAC::remove_multicast_group(const uint8_t *address) { - if(state == PowerState::OFF) { - tr_err("Not available while MAC is off!"); - return; + void CompositeEMAC::set_link_input_cb(emac_link_input_cb_t input_cb) { } - if(numSubscribedMcastMacs >= MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES) { - // We are in fallback mode, so we can no longer unsusbscribe at the MAC level because we don't know - // which MACs are subscribed anymore. - return; + void CompositeEMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb) { } - // Find MAC address in the subscription list - auto macsEndIter = std::begin(mcastMacs) + numSubscribedMcastMacs; - auto toRemoveIter = std::find_if(std::begin(mcastMacs), macsEndIter, [&](auto element) { - return memcmp(element.data(), address, 6) == 0; - }); - - if(toRemoveIter == macsEndIter) + void CompositeEMAC::add_multicast_group(const uint8_t *address) { - tr_warning("Tried to remove mcast group that was not added"); - return; - } + if(state == PowerState::OFF) { + tr_err("Not available while MAC is off!"); + return; + } - // Swap the MAC addr to be removed to the end of the list, if it is not there already - auto lastElementIter = macsEndIter - 1; - if(toRemoveIter != std::begin(mcastMacs) && toRemoveIter != lastElementIter) - { - std::swap(*toRemoveIter, *lastElementIter); - } + ++numSubscribedMcastMacs; - // 'remove' the last element by changing the length - numSubscribedMcastMacs--; + if(numSubscribedMcastMacs >= MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES) + { + tr_warn("Out of multicast group entries (currently have %d). Increase the 'nsapi.emac-max-mcast-subscribes' JSON option!", MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES); + // Fall back to accepting all multicasts + set_all_multicast(true); + return; + } - // Clear the filter and re-add all the addresses except the desired one - if(mac.clearMcastFilter() != ErrCode::SUCCESS) { - tr_err("clearMcastFilter() failed!"); - return; - } - for(size_t macIdx = 0; macIdx < numSubscribedMcastMacs; ++macIdx) { - if(mac.addMcastMAC(mcastMacs[macIdx]) != ErrCode::SUCCESS) { + memcpy(mcastMacs[numSubscribedMcastMacs - 1].data(), address, 6); + + if(mac.addMcastMAC(mcastMacs[numSubscribedMcastMacs - 1]) != ErrCode::SUCCESS) { tr_err("addMcastMAC() failed!"); } } -} -void mbed::CompositeEMAC::set_all_multicast(bool all) { - if(state == PowerState::OFF) { - tr_err("Not available while MAC is off!"); - return; - } + void CompositeEMAC::remove_multicast_group(const uint8_t *address) { + if(state == PowerState::OFF) { + tr_err("Not available while MAC is off!"); + return; + } + + if(numSubscribedMcastMacs >= MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES) { + // We are in fallback mode, so we can no longer unsusbscribe at the MAC level because we don't know + // which MACs are subscribed anymore. + return; + } + + // Find MAC address in the subscription list + auto macsEndIter = std::begin(mcastMacs) + numSubscribedMcastMacs; + auto toRemoveIter = std::find_if(std::begin(mcastMacs), macsEndIter, [&](auto element) { + return memcmp(element.data(), address, 6) == 0; + }); - if(numSubscribedMcastMacs >= MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES && !all) { - // Don't allow setting pass all multicast to off while we are in fallback mode - return; + if(toRemoveIter == macsEndIter) + { + tr_warning("Tried to remove mcast group that was not added"); + return; + } + + // Swap the MAC addr to be removed to the end of the list, if it is not there already + auto lastElementIter = macsEndIter - 1; + if(toRemoveIter != std::begin(mcastMacs) && toRemoveIter != lastElementIter) + { + std::swap(*toRemoveIter, *lastElementIter); + } + + // 'remove' the last element by changing the length + numSubscribedMcastMacs--; + + // Clear the filter and re-add all the addresses except the desired one + if(mac.clearMcastFilter() != ErrCode::SUCCESS) { + tr_err("clearMcastFilter() failed!"); + return; + } + for(size_t macIdx = 0; macIdx < numSubscribedMcastMacs; ++macIdx) { + if(mac.addMcastMAC(mcastMacs[macIdx]) != ErrCode::SUCCESS) { + tr_err("addMcastMAC() failed!"); + } + } } - mac.setPassAllMcast(all); -} + void CompositeEMAC::set_all_multicast(bool all) { + if(state == PowerState::OFF) { + tr_err("Not available while MAC is off!"); + return; + } + + if(numSubscribedMcastMacs >= MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES && !all) { + // Don't allow setting pass all multicast to off while we are in fallback mode + return; + } + + mac.setPassAllMcast(all); + } +} \ No newline at end of file diff --git a/connectivity/drivers/emac/sources/GenericEthPhy.cpp b/connectivity/drivers/emac/sources/GenericEthPhy.cpp index 2b380108a8b..93608a1a303 100644 --- a/connectivity/drivers/emac/sources/GenericEthPhy.cpp +++ b/connectivity/drivers/emac/sources/GenericEthPhy.cpp @@ -60,7 +60,7 @@ CompositeEMAC::ErrCode GenericEthPhy::init() { if(actualID1 == expectedID1 && (actualID2 & expectedID2Mask) == expectedID2) { // OK - tr_info("Detected ethernet PHY at MDIO addr %" PRIu8 " with OUI %" PRIu32 ", model %" PRIu8 ", and revision number %" PRIu8, config.address, config.OUI, config.model, actualID2 % 0xF); + tr_info("Detected ethernet PHY at MDIO addr %" PRIu8 " with OUI 0x%" PRIx32 ", model 0x%" PRIx8 ", and revision number %" PRIu8, config.address, config.OUI, config.model, actualID2 % 0xF); } else if(actualID1 == std::numeric_limits::max() && actualID2 == std::numeric_limits::max()) { tr_error("Got all 0xFFs when reading Ethernet PHY. Since MDIO is an open drain bus, this means the phy is not connected or not responding."); diff --git a/connectivity/drivers/emac/sources/PhyDrivers.cpp b/connectivity/drivers/emac/sources/PhyDrivers.cpp index a962cbbd919..878bb7e8e6e 100644 --- a/connectivity/drivers/emac/sources/PhyDrivers.cpp +++ b/connectivity/drivers/emac/sources/PhyDrivers.cpp @@ -29,7 +29,6 @@ namespace LAN8742 { inline constexpr GenericEthPhy::Config DefaultConfig = { .OUI = 0x1F0, .model = 0x13, - // TODO this is 1 on ARCH_MAX .address = 0, // Address set via PHYAD[0] strap. }; @@ -45,4 +44,27 @@ class Driver : public GenericEthPhy { } +/** + * @brief Obtains the PHY driver for Ethernet port 0. + * + * The default implementation constructs a built-in driver (given by \c nsapi.emac-phy-model ) using the + * configured MDIO address ( \c nsapi.emac-phy-mdio-address ). However, it can be overridden by the + * application if it wishes to use another phy driver class! + * + * @return Phy driver class instance, or nullptr if none is configured. + */ +MBED_WEAK CompositeEMAC::PhyDriver * mbed_get_eth_phy_driver() +{ +#ifdef MBED_CONF_NSAPI_EMAC_PHY_MODEL + static GenericEthPhy::Config driverConfig = MBED_CONF_NSAPI_EMAC_PHY_MODEL::DefaultConfig; +#ifdef MBED_CONF_NSAPI_EMAC_PHY_MDIO_ADDRESS + driverConfig.address = MBED_CONF_NSAPI_EMAC_PHY_MDIO_ADDRESS; +#endif + static MBED_CONF_NSAPI_EMAC_PHY_MODEL::Driver driver(driverConfig); + return &driver; +#else + return nullptr; +#endif +}; + } \ No newline at end of file diff --git a/connectivity/netsocket/mbed_lib.json5 b/connectivity/netsocket/mbed_lib.json5 index d80739c75dd..b3925020491 100644 --- a/connectivity/netsocket/mbed_lib.json5 +++ b/connectivity/netsocket/mbed_lib.json5 @@ -93,11 +93,24 @@ "emac-max-mcast-subscribes": { "help" : "Maximum supported number of multicast addresses that the application can subscribe to. If this limit is reached, we give up and enable all mcast groups.", "value" : 8 + }, + "emac-phy-model": { + "help": "Model of the Ethernet PHY on the board. Should correspond to a driver defined in PhyDrivers.cpp", + "value": null + }, + "emac-phy-mdio-address": { + "help": "MDIO address of the phy chip. Usually set with strapping pins. NOTE: 0 is *supposed* to be reserved as the general call address but lots of phy chips use it anyway.", + "value": null } }, "target_overrides": { "TB_SENSE_12": { "nsapi.default-mesh-type": "LOWPAN" + }, + "MCU_STM32": { + // First-party STM32 boards generally use a LAN8742 PHY at MDIO address 0 + "nsapi.emac-phy-model": "LAN8742", + "nsapi.emac-phy-mdio-address": 0 } } } diff --git a/connectivity/netsocket/tests/TESTS/CMakeLists.txt b/connectivity/netsocket/tests/TESTS/CMakeLists.txt index 1e8b234db06..6ab74e044ad 100644 --- a/connectivity/netsocket/tests/TESTS/CMakeLists.txt +++ b/connectivity/netsocket/tests/TESTS/CMakeLists.txt @@ -5,12 +5,6 @@ if(DEFAULT_IFC_IDX EQUAL -1) set(TEST_SKIPPED "No default network interface on this target") endif() -# TEMPORARY: netsocket tests will not compile on STM32H7 until new EMAC driver is merged because the old EMAC driver -# pulls in a dependency on lwip -if("TARGET_STM32H7" IN_LIST MBED_TARGET_DEFINITIONS) - set(TEST_SKIPPED "Will not compile on STM32H7") -endif() - add_subdirectory(common) # List of libraries for all netsocket tests to link. From 1a2df8bd6e2f40ed4fb6edb0f644ad2ac049e233 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 16 Feb 2025 09:46:26 -0800 Subject: [PATCH 14/47] Phy task and Tx seem to work! --- .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 2 +- .../drivers/emac/include/CompositeEMAC.h | 30 ++++ .../drivers/emac/sources/CompositeEMAC.cpp | 168 +++++++++++++++++- .../netsocket/include/netsocket/EMAC.h | 27 +-- connectivity/netsocket/mbed_lib.json5 | 4 + .../emac_test_utils/EmacTestMemoryManager.cpp | 26 +-- rtos/include/rtos/Mutex.h | 4 +- 7 files changed, 228 insertions(+), 33 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index 055c0eb2d57..ef93b2737d6 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -128,7 +128,7 @@ namespace mbed { memset(&desc, 0, sizeof(stm32_ethv2::EthRxDescriptor)); // Store buffer address - desc.formats.toDMA.buffer1Addr = memory_manager->get_ptr(buffer); + desc.formats.toDMA.buffer1Addr = buffer; // Configure descriptor desc.formats.toDMA.buffer1Valid = true; diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index 47450133c73..8160ab2b49c 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -19,6 +19,8 @@ #include "EMAC.h" #include "NonCopyable.h" +#include "Thread.h" + #include namespace mbed @@ -345,12 +347,22 @@ class CompositeEMAC : public EMAC /// Pointer to memory manager for the EMAC EMACMemoryManager * memory_manager = nullptr; + // Callbacks to the MAC + emac_link_state_change_cb_t linkStateCallback{}; + emac_link_input_cb_t linkInputCallback{}; + // Instances of each of the 4 component classes PhyDriver * phy = nullptr; RxDMA & rxDMA; TxDMA & txDMA; MACDriver & mac; + // Event queue handle for phy task + int phyTaskHandle; + + // Thread running inside the MAC. Processes interrupts (both Tx and Rx) and receives packets. + rtos::Thread * macThread = nullptr; + // State of the MAC enum class PowerState { OFF = 0, @@ -360,6 +372,14 @@ class CompositeEMAC : public EMAC std::atomic state = PowerState::OFF; + // State of the link + enum class LinkState { + DOWN, + UP + }; + + std::atomic linkState = LinkState::DOWN; + // Multicast subscribe information MACAddress mcastMacs[MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES]; @@ -368,12 +388,22 @@ class CompositeEMAC : public EMAC // and power up the MAC. size_t numSubscribedMcastMacs; + // Mutex that must be acquired before interacting with the MAC. This is used to protect against, for example, + // one thread calling power_down() while the phy task is still running. + rtos::Mutex macOpsMutex; + /// Subclass should call this when a receive interrupt is detected void rxISR(); /// Subclass should call this when a transmit complete interrupt is detected void txISR(); + /// Called periodically to poll the phy and bring link up/down + void phyTask(); + + /// Run in its own thread to service the MAC. + void macTask(); + /// Constructor. Should be called by subclass. CompositeEMAC(TxDMA & txDMA, RxDMA & rxDMA, MACDriver & macDriver): rxDMA(rxDMA), diff --git a/connectivity/drivers/emac/sources/CompositeEMAC.cpp b/connectivity/drivers/emac/sources/CompositeEMAC.cpp index 9d8a42d38d5..4032d7524c9 100644 --- a/connectivity/drivers/emac/sources/CompositeEMAC.cpp +++ b/connectivity/drivers/emac/sources/CompositeEMAC.cpp @@ -18,19 +18,131 @@ #include "CompositeEMAC.h" #include +#include +#include #include #define TRACE_GROUP "CEMAC" +// Flags for the MAC thread +static uint32_t THREAD_FLAG_TX_DESC_AVAILABLE = 1 << 0; +static uint32_t THREAD_FLAG_RX_DESC_AVAILABLE = 1 << 1; +static uint32_t THREAD_FLAG_RX_MEM_AVAILABLE = 1 << 2; +static uint32_t THREAD_FLAG_SHUTDOWN = 1 << 3; + namespace mbed { // Defined in PhyDrivers.cpp CompositeEMAC::PhyDriver * mbed_get_eth_phy_driver(); void CompositeEMAC::rxISR() { + // Note: Not locking mutex here as this is an ISR and should be able to run while the MAC thread is executing. + if(rxDMA.rxHasPackets_ISR()) { + // Reclaimable descriptor or complete packet detected. + macThread->flags_set(THREAD_FLAG_RX_DESC_AVAILABLE); + } } void CompositeEMAC::txISR() { + // Reclaimable Tx descriptor detected + macThread->flags_set(THREAD_FLAG_TX_DESC_AVAILABLE); + } + + void CompositeEMAC::phyTask() { + rtos::ScopedMutexLock lock(macOpsMutex); + + // If the MAC has been powered off, bail immediately (this event is about to be canceled) + if(state == PowerState::OFF) { + return; + } + + bool phyLinkState = false; + if(phy->checkLinkStatus(phyLinkState) != ErrCode::SUCCESS) { + tr_error("phyTask(): Phy failed to check link status"); + } + + if(linkState == LinkState::UP) { + if(!phyLinkState) { + tr_info("Link down"); + linkState = LinkState::DOWN; + + if(mac.disable() != ErrCode::SUCCESS) { + tr_error("phyTask(): Mac failed to disable"); + } + + linkStateCallback(false); + } + } + else { // LinkState::DOWN + if(phyLinkState) { + Duplex duplex; + LinkSpeed speed; + if(phy->checkLinkType(speed, duplex)!= ErrCode::SUCCESS) { + tr_error("phyTask(): Phy failed to check link type"); + return; + } + + char const * speedStr; + if(speed == LinkSpeed::LINK_10MBIT) { + speedStr = "10Mbps"; + } + else if(speed == LinkSpeed::LINK_100MBIT) { + speedStr = "100Mbps"; + } + else { + speedStr = "1Gbps"; + } + + tr_info("Link up at %s %s duplex", speedStr, duplex == Duplex::FULL ? "full" : "half"); + + linkState = LinkState::UP; + if(mac.enable(speed, duplex) != ErrCode::SUCCESS) { + tr_error("phyTask(): Mac failed to enable"); + } + + linkStateCallback(true); + } + } + } + + void CompositeEMAC::macTask() { + while(true) + { + // Wait for something to happen + uint32_t flags = rtos::ThisThread::flags_wait_any(THREAD_FLAG_TX_DESC_AVAILABLE | THREAD_FLAG_SHUTDOWN | THREAD_FLAG_RX_DESC_AVAILABLE | THREAD_FLAG_RX_MEM_AVAILABLE); + if(flags & THREAD_FLAG_SHUTDOWN) + { + return; + } + + // Now lock the mutex for the other cases + rtos::ScopedMutexLock lock(macOpsMutex); + + if(flags & THREAD_FLAG_RX_DESC_AVAILABLE) + { + // Receive any available packets. + // Note that if the ISR was delayed, we might get multiple packets per ISR, so we need to loop. + while(true) + { + auto * packet = rxDMA.dequeuePacket(); + if(!packet) { + break; + } + + linkInputCallback(packet); + + // Rebuild descriptors if possible + rxDMA.rebuildDescriptors(); + } + } + if(flags & THREAD_FLAG_TX_DESC_AVAILABLE) + { + txDMA.reclaimTxDescs(); + } + if(flags & THREAD_FLAG_RX_MEM_AVAILABLE) { + rxDMA.rebuildDescriptors(); + } + } } void CompositeEMAC::get_ifname(char *name, uint8_t size) const { @@ -42,6 +154,8 @@ namespace mbed { } void CompositeEMAC::set_hwaddr(const uint8_t *addr) { + rtos::ScopedMutexLock lock(macOpsMutex); + if(state != PowerState::ON_NO_LINK) { tr_err("MAC address can only be set after power up, before link up!"); return; @@ -54,11 +168,19 @@ namespace mbed { bool CompositeEMAC::link_out(emac_mem_buf_t *buf) { + rtos::ScopedMutexLock lock(macOpsMutex); + const auto ret = txDMA.txPacket(buf); + if(ret != ErrCode::SUCCESS) { + tr_warn("link_out(): Tx failed."); + } + return ret == ErrCode::SUCCESS; } bool CompositeEMAC::power_up() { + rtos::ScopedMutexLock lock(macOpsMutex); + if(state != PowerState::OFF) { tr_err("power_up(): Already powered up!"); return false; @@ -85,6 +207,12 @@ namespace mbed { return false; } + // Init DMA rungs + if(txDMA.init() != ErrCode::SUCCESS || rxDMA.init() != ErrCode::SUCCESS) { + tr_err("power_up(): Failed to init DMA!"); + return false; + } + // Initialize the PHY phy->setMAC(&mac); if(phy->init() != ErrCode::SUCCESS) { @@ -93,16 +221,32 @@ namespace mbed { } state = PowerState::ON_NO_LINK; + + // Start phy task + phyTaskHandle = mbed_event_queue()->call_every(std::chrono::milliseconds(MBED_CONF_NSAPI_EMAC_PHY_POLL_PERIOD), + callback(this, &CompositeEMAC::phyTask)); + + // Start MAC thread. + // We want to run this thread at high priority since reclaiming descriptors generally needs to happen quickly + // for the application to use the network at full speed. + macThread = new rtos::Thread(osPriorityHigh, 2048, nullptr, "EMAC Thread"); + macThread->start(callback(this, &CompositeEMAC::macTask)); + return true; } void CompositeEMAC::power_down() { - // TODO stop phy task + // Stop MAC thread (don't need to lock mutex for this) + macThread->flags_set(THREAD_FLAG_SHUTDOWN); + macThread->join(); + delete macThread; - state = PowerState::OFF; + rtos::ScopedMutexLock lock(macOpsMutex); - // TODO sync with other thread(s), ensure that no other threads are accessing the MAC - // (lock mutex?) + mbed_event_queue()->cancel(phyTaskHandle); + + state = PowerState::OFF; + linkState = LinkState::DOWN; // Clear multicast filter, so that we start with a clean slate next time if(mac.clearMcastFilter() != ErrCode::SUCCESS) { @@ -129,13 +273,25 @@ namespace mbed { } void CompositeEMAC::set_link_input_cb(emac_link_input_cb_t input_cb) { + if(state != PowerState::OFF) { + tr_err("Not available while MAC is on!"); + return; + } + linkInputCallback = input_cb; } void CompositeEMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb) { + if(state != PowerState::OFF) { + tr_err("Not available while MAC is on!"); + return; + } + linkStateCallback = state_cb; } void CompositeEMAC::add_multicast_group(const uint8_t *address) { + rtos::ScopedMutexLock lock(macOpsMutex); + if(state == PowerState::OFF) { tr_err("Not available while MAC is off!"); return; @@ -159,6 +315,8 @@ namespace mbed { } void CompositeEMAC::remove_multicast_group(const uint8_t *address) { + rtos::ScopedMutexLock lock(macOpsMutex); + if(state == PowerState::OFF) { tr_err("Not available while MAC is off!"); return; @@ -205,6 +363,8 @@ namespace mbed { } void CompositeEMAC::set_all_multicast(bool all) { + rtos::ScopedMutexLock lock(macOpsMutex); + if(state == PowerState::OFF) { tr_err("Not available while MAC is off!"); return; diff --git a/connectivity/netsocket/include/netsocket/EMAC.h b/connectivity/netsocket/include/netsocket/EMAC.h index 9adf83143d1..e7b5e5c4832 100644 --- a/connectivity/netsocket/include/netsocket/EMAC.h +++ b/connectivity/netsocket/include/netsocket/EMAC.h @@ -118,10 +118,10 @@ class EMAC { /** * Sends the packet over the link * - * That can not be called from an interrupt context. + * Will not be called from an interrupt context. * - * @param buf Packet to be send - * @return True if the packet was send successfully, False otherwise + * @param buf Packet to be sent. This packet is now owned by the MAC and must be freed in all cases. + * @return True if the packet was sent successfully, False otherwise */ virtual bool link_out(emac_mem_buf_t *buf) = 0; @@ -139,16 +139,24 @@ class EMAC { virtual void power_down() = 0; /** - * Sets a callback that needs to be called for packets received for that interface + * Sets a callback that needs to be called for packets received for this interface. * - * @param input_cb Function to be register as a callback + * Note that the callback function will contain appropriate locking such that may be called from any OS thread. + * However, it shall not be called from an interrupt. + * + * Also note that the callback will take ownership of the passed packet and must free it in all cases. + * + * @param input_cb Function to be registered as a callback */ virtual void set_link_input_cb(emac_link_input_cb_t input_cb) = 0; /** * Sets a callback that needs to be called on link status changes for given interface * - * @param state_cb Function to be register as a callback + * Note that the callback function will contain appropriate locking such that may be called from any OS thread. + * However, it shall not be called from an interrupt. + * + * @param state_cb Function to be registered as a callback */ virtual void set_link_state_cb(emac_link_state_change_cb_t state_cb) = 0; @@ -184,11 +192,4 @@ class EMAC { virtual void set_memory_manager(EMACMemoryManager &mem_mngr) = 0; }; - -/** These need to be defined by targets wishing to provide an Ethernet driver using EMAC interface. It will - * be used by the EMACInterface class's default constructor to initialize the networking subsystem. - */ -//extern const emac_interface_ops_t mbed_emac_eth_ops_default; -//extern void *mbed_emac_eth_hw_default; - #endif /* EMAC_H */ diff --git a/connectivity/netsocket/mbed_lib.json5 b/connectivity/netsocket/mbed_lib.json5 index b3925020491..680db2a60ce 100644 --- a/connectivity/netsocket/mbed_lib.json5 +++ b/connectivity/netsocket/mbed_lib.json5 @@ -101,6 +101,10 @@ "emac-phy-mdio-address": { "help": "MDIO address of the phy chip. Usually set with strapping pins. NOTE: 0 is *supposed* to be reserved as the general call address but lots of phy chips use it anyway.", "value": null + }, + "emac-phy-poll-period": { + "help": "How often (in ms) to poll the Ethernet PHY and check for link status changes.", + "value": 100 } }, "target_overrides": { diff --git a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp index a99afd2c028..2b1846b520d 100644 --- a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp +++ b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp @@ -326,7 +326,7 @@ uint32_t EmacTestMemoryManager::get_total_len(const emac_mem_buf_t *buf) const validate_list(); if (!validate_ptr(buf)) { - CHECK_ASSERT(0, "get_total_len(): %p invalid buffer", buf); + CHECK_ASSERT(0, "get_total_len(): %p invalid buffer\n", buf); return 0; } @@ -344,17 +344,17 @@ void EmacTestMemoryManager::copy(emac_mem_buf_t *to_buf, const emac_mem_buf_t *f validate_list(); if (!validate_ptr(to_buf)) { - CHECK_ASSERT(0, "copy(): %p invalid to buffer", to_buf); + CHECK_ASSERT(0, "copy(): %p invalid to buffer\n", to_buf); return; } if (!validate_ptr(from_buf)) { - CHECK_ASSERT(0, "copy(): %p invalid from buffer", from_buf); + CHECK_ASSERT(0, "copy(): %p invalid from buffer\n", from_buf); return; } if (get_total_len(to_buf) != get_total_len(from_buf)) { - CHECK_ASSERT(0, "copy(): %p to and %p from buffer total lengths not same", to_buf, from_buf); + CHECK_ASSERT(0, "copy(): %p to and %p from buffer total lengths not same\n", to_buf, from_buf); return; } @@ -397,19 +397,19 @@ void EmacTestMemoryManager::cat(emac_mem_buf_t *to_buf, emac_mem_buf_t *cat_buf) validate_list(); if (!validate_ptr(to_buf)) { - CHECK_ASSERT(0, "cat(): %p invalid to buffer", to_buf); + CHECK_ASSERT(0, "cat(): %p invalid to buffer\n", to_buf); return; } if (!validate_ptr(cat_buf)) { - CHECK_ASSERT(0, "cat(): %p invalid cat buffer", cat_buf); + CHECK_ASSERT(0, "cat(): %p invalid cat buffer\n", cat_buf); return; } emac_memory_t *cat_mem_buf = static_cast(cat_buf); if (!cat_mem_buf->first) { - CHECK_ASSERT(0, "cat(): %p cat buffer does not point to head of chain", cat_buf); + CHECK_ASSERT(0, "cat(): %p cat buffer does not point to head of chain\n", cat_buf); return; } @@ -428,7 +428,7 @@ emac_mem_buf_t *EmacTestMemoryManager::get_next(const emac_mem_buf_t *buf) const validate_list(); if (!validate_ptr(buf)) { - CHECK_ASSERT(0, "get_next(): %p invalid buffer", buf); + CHECK_ASSERT(0, "get_next(): %p invalid buffer\n", buf); return NULL; } @@ -441,7 +441,7 @@ void *EmacTestMemoryManager::get_ptr(const emac_mem_buf_t *buf) const validate_list(); if (!validate_ptr(buf)) { - CHECK_ASSERT(0, "get_ptr(): %p invalid buffer", buf); + CHECK_ASSERT(0, "get_ptr(): %p invalid buffer\n", buf); return NULL; } @@ -454,7 +454,7 @@ uint32_t EmacTestMemoryManager::get_len(const emac_mem_buf_t *buf) const validate_list(); if (!validate_ptr(buf)) { - CHECK_ASSERT(0, "get_len(): %p invalid buffer", buf); + CHECK_ASSERT(0, "get_len(): %p invalid buffer\n", buf); return 0; } @@ -467,19 +467,19 @@ void EmacTestMemoryManager::set_len(emac_mem_buf_t *buf, uint32_t len) validate_list(); if (!validate_ptr(buf)) { - CHECK_ASSERT(0, "set_len(): %p invalid buffer", buf); + CHECK_ASSERT(0, "set_len(): %p invalid buffer\n", buf); return; } emac_memory_t *mem_buf = static_cast(buf); if (len > mem_buf->orig_len) { - CHECK_ASSERT(0, "set_len(): %p new length %i must be less or equal allocated size %i", buf, len, mem_buf->orig_len); + CHECK_ASSERT(0, "set_len(): %p new length %i must be less or equal allocated size %i\n", buf, len, mem_buf->orig_len); return; } if (!mem_buf->first) { - CHECK_ASSERT(0, "set_len(): %p buffer does not point to head of chain", buf); + CHECK_ASSERT(0, "set_len(): %p buffer does not point to head of chain\n", buf); return; } diff --git a/rtos/include/rtos/Mutex.h b/rtos/include/rtos/Mutex.h index 6071bf697c9..7dd7af2cdae 100644 --- a/rtos/include/rtos/Mutex.h +++ b/rtos/include/rtos/Mutex.h @@ -67,8 +67,8 @@ typedef mbed::ScopedLock ScopedMutexLock; * \par * Mbed Mutexes are recursive. So, if you call the \c lock() function multiple times, * you must call \c unlock() the same number of times to unlock the mutex. This means that it's okay to lock - * a mutex, then call another function that also locks and unlocks the mutex, and the mutex won't actually - * get unlocked until your function unlocks it. + * a mutex, then call another function that also locks and unlocks the mutex. The mutex won't actually + * get unlocked until the top level function unlocks it. * * @note You cannot use member functions of this class in ISR context. If you require Mutex functionality within ISR handler, consider using @a Semaphore. * From 26d4a22d88ec6a10d6d5c649a80587b4695c0e98 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 16 Feb 2025 13:38:25 -0800 Subject: [PATCH 15/47] Rx kinda working --- connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 3 ++- connectivity/drivers/emac/include/GenericEthDMA.h | 6 +++--- .../tests/emac_test_utils/EmacTestMemoryManager.cpp | 4 ++-- .../netsocket/tests/emac_test_utils/EmacTestMemoryManager.h | 1 - 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index ef93b2737d6..8e41223bfc2 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -143,7 +143,8 @@ namespace mbed { // Update tail ptr to issue "rx poll demand" and mark this descriptor for receive. // Rx stops when the current and tail pointers are equal, so we want to set the tail pointer // to one location after the last DMA-owned descriptor in the FIFO. - base->DMACRDTPR = reinterpret_cast(&rxDescs[rxBuildIndex]); + const auto nextDescIdx = (descIdx + 1) % RX_NUM_DESCS; + base->DMACRDTPR = reinterpret_cast(&rxDescs[nextDescIdx]); } size_t STM32EthMacV2::RxDMA::getTotalLen(const size_t firstDescIdx) { diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 00dfcf0bd86..ddfad18f609 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -352,12 +352,12 @@ namespace mbed { // At the start, we own all the descriptors rxDescsOwnedByApplication = RX_NUM_DESCS; - // Build all descriptors - rebuildDescriptors(); - // init DMA peripheral startDMA(); + // Build all descriptors + rebuildDescriptors(); + return CompositeEMAC::ErrCode::SUCCESS; } diff --git a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp index 2b1846b520d..a5f418de4f0 100644 --- a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp +++ b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp @@ -181,7 +181,7 @@ emac_mem_buf_t *EmacTestMemoryManager::alloc_pool(uint32_t size, uint32_t align, // Contiguous allocation if (size + align <= m_alloc_unit) { - if (m_pool_bufs_used > m_pool_size) { + if (m_pool_bufs_used > MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS) { return nullptr; } @@ -214,7 +214,7 @@ emac_mem_buf_t *EmacTestMemoryManager::alloc_pool(uint32_t size, uint32_t align, size_left = 0; } - if (m_pool_bufs_used > m_pool_size) { + if (m_pool_bufs_used > MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS) { // No simulated pool space left, free and return nullptr if (first_buf != nullptr) { free(first_buf); diff --git a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h index ecb78d1b01e..95cc27bb21d 100644 --- a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h +++ b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h @@ -134,7 +134,6 @@ class EmacTestMemoryManager : public EMACMemoryManager { mutable rtos::Mutex m_mem_mutex; std::list m_mem_buffers; unsigned int m_alloc_unit; - size_t m_pool_size; size_t m_pool_bufs_used = 0; bool m_memory_available; }; From aeff333ff11dbd313e24bfe0b3dc80c2c3bdb205 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 16 Feb 2025 14:22:57 -0800 Subject: [PATCH 16/47] Almost working! Just missing memory freed callback --- .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 12 +++++++----- connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h | 2 +- connectivity/drivers/emac/include/GenericEthDMA.h | 10 +++++----- connectivity/netsocket/mbed_lib.json5 | 2 +- .../TESTS/network/emac/emac_test_initialize.cpp | 3 --- .../tests/emac_test_utils/EmacTestMemoryManager.cpp | 1 - targets/targets.json5 | 11 +++++++---- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index 8e41223bfc2..ef04a43ec64 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -94,7 +94,8 @@ namespace mbed { // Move tail pointer register to point to the descriptor after this descriptor. // This tells the MAC to transmit until it reaches the given descriptor, then stop. - base->DMACTDTPR = reinterpret_cast(&txDescs[txSendIndex]); + const auto nextDescIdx = (descIdx + 1) % MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; + base->DMACTDTPR = reinterpret_cast(&txDescs[nextDescIdx]); } void STM32EthMacV2::RxDMA::startDMA() @@ -147,9 +148,9 @@ namespace mbed { base->DMACRDTPR = reinterpret_cast(&rxDescs[nextDescIdx]); } - size_t STM32EthMacV2::RxDMA::getTotalLen(const size_t firstDescIdx) { - // Total length of the packet is in the first descriptor - return rxDescs[firstDescIdx].formats.fromDMA.pktLength; + size_t STM32EthMacV2::RxDMA::getTotalLen(const size_t firstDescIdx, const size_t lastDescIdx) { + // Total length of the packet is in the last descriptor + return rxDescs[lastDescIdx].formats.fromDMA.pktLength; } void STM32EthMacV2::MACDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) @@ -275,7 +276,8 @@ namespace mbed { base->MAC1USTCR = (HAL_RCC_GetHCLKFreq() / 1000000U) - 1U; // MAC configuration - base->MACCR = ETH_MACCR_SARC_REPADDR0; // Replace the SA field in Tx packets with the configured source address + base->MACCR = ETH_MACCR_SARC_REPADDR0 | // Replace the SA field in Tx packets with the configured source address + ETH_MACCR_CST_Msk; // Don't include the CRC when forwarding Rx packets to the application base->MTLTQOMR |= ETH_MTLTQOMR_TSF_Msk; // Enable store and forward mode for transmission (default in the HAL) // Enable multicast hash and perfect filter diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h index 63c1268b15b..9f534d86d98 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -58,7 +58,7 @@ namespace mbed { void returnDescriptor(size_t descIdx, uint8_t *buffer) override; - size_t getTotalLen(size_t firstDescIdx) override; + size_t getTotalLen(size_t firstDescIdx, size_t lastDescIdx) override; public: explicit RxDMA(ETH_TypeDef * const base): diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index ddfad18f609..0a0de59debb 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -197,7 +197,7 @@ namespace mbed { } } - tr_info("Transmitting packet of length %lu in %zu buffers and %zu descs\n", + tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", memory_manager->get_total_len(buf), memory_manager->count_buffers(buf), neededDescs); // Step 2: Copy packet if needed @@ -341,9 +341,9 @@ namespace mbed { virtual void returnDescriptor(size_t descIdx, uint8_t * buffer) = 0; /// Get the length of the packet starting at firstDescIdx and continuing until the - /// next descriptor with the last descriptor flag set. Descriptors have already been validated to contain a + /// given last descriptor. Descriptors have already been validated to contain a /// complete packet at this point. - virtual size_t getTotalLen(size_t firstDescIdx) = 0; + virtual size_t getTotalLen(size_t firstDescIdx, size_t lastDescIdx) = 0; public: CompositeEMAC::ErrCode init() override { @@ -512,7 +512,7 @@ namespace mbed { // Set length of first buffer net_stack_mem_buf_t *const headBuffer = rxDescStackBufs[*firstDescIdx]; - size_t lenRemaining = getTotalLen(*firstDescIdx); + size_t lenRemaining = getTotalLen(*firstDescIdx, *lastDescIdx); memory_manager->set_len(headBuffer, std::min(lenRemaining, rxPoolPayloadSize)); lenRemaining -= std::min(lenRemaining, rxPoolPayloadSize); @@ -546,7 +546,7 @@ namespace mbed { } #endif - tr_info("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu (%p-%p)\n", + tr_debug("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu (%p-%p)\n", memory_manager->get_total_len(headBuffer), memory_manager->get_ptr(headBuffer), *firstDescIdx, *lastDescIdx, &rxDescs[*firstDescIdx], &rxDescs[*lastDescIdx]); diff --git a/connectivity/netsocket/mbed_lib.json5 b/connectivity/netsocket/mbed_lib.json5 index 680db2a60ce..3b1097f8ab9 100644 --- a/connectivity/netsocket/mbed_lib.json5 +++ b/connectivity/netsocket/mbed_lib.json5 @@ -83,7 +83,7 @@ "value": 592 // LwIP default value (assuming default TCP MSS) }, "emac-rx-pool-num-bufs": { - "help": "Number of buffers (of size netsocket.emac-rx-pool-buf-size) in the EMAC receive pool. This controls how much memory is preallocated for Ethernet reception. A larger number means that more Ethernet packets can be received per second without dropping any. Some EMACs need up to 4 extra buffers, so this should be set such that this value minus 4 times the buffer size is at least 1514 (so we can receive one full Ethernet frame).", + "help": "Number of buffers (of size netsocket.emac-rx-pool-buf-size) in the EMAC receive pool. This controls how much memory is preallocated for Ethernet reception. A larger number means that more Ethernet packets can be received per second without dropping any. Some EMACs need up to 4 extra buffers, so this should be set such that (this value minus 4) times the buffer size is at least 1514 (so we can receive one full Ethernet frame).", "value": 7 }, "emac-tx-num-descs": { diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp index c4bc0208b57..7e7f96ec1e0 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp @@ -36,9 +36,6 @@ void test_emac_initialize() worker_loop_init(); mbed_trace_init(); - // Set memory manager parameters - EmacTestMemoryManager::get_instance().set_alloc_unit(256); // Use a relatively small allocation unit size so packets have to be split up into a lot of buffers - static NetworkInterface *network_interface = get_network_interface(); // Power up the interface and emac driver diff --git a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp index a5f418de4f0..236aa055304 100644 --- a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp +++ b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp @@ -73,7 +73,6 @@ EmacTestMemoryManager::EmacTestMemoryManager() : m_mem_mutex(), m_mem_buffers(), m_alloc_unit(MBED_CONF_NSAPI_EMAC_RX_POOL_BUF_SIZE), - m_pool_size(MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS), m_memory_available(true) { #ifdef ETHMEM_SECTION diff --git a/targets/targets.json5 b/targets/targets.json5 index 1c0a9172d9e..124806e4359 100644 --- a/targets/targets.json5 +++ b/targets/targets.json5 @@ -3111,9 +3111,7 @@ mode is recommended for target MCUs with small amounts of flash and RAM.", "LPTICKER", "CAN", "SERIAL_FC", - "WATCHDOG", - "ETHERNET", - "EMAC" + "WATCHDOG" ], "is_mcu_family_target": true }, @@ -3128,6 +3126,10 @@ mode is recommended for target MCUs with small amounts of flash and RAM.", ], "macros_add": [ "STM32H503xx" + ], + "device_has_remove": [ + "ETHERNET", + "EMAC" ] }, "NUCLEO_H503RB": { @@ -3141,7 +3143,8 @@ mode is recommended for target MCUs with small amounts of flash and RAM.", // ADC reference voltage is same as MCU VDD. // MCU VDD defaults to 3.3V though can be changed to 1.8V based on JP2 setting on nucleo board. - "default-adc-vref": 3.3 + "default-adc-vref": 3.3, + "network-default-interface-type": "ETHERNET" }, "device_has_remove": [ "ANALOGOUT" // both DAC pins are in conflict with LED1 and STDIO_UART_TX From 8ed9b4450b99b8c0960d1898e7f5828b3b554211 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 16 Feb 2025 14:42:57 -0800 Subject: [PATCH 17/47] Woring! Add initial support for STM32H5 as well --- .../drivers/emac/TARGET_STM/CMakeLists.txt | 2 + .../emac/TARGET_STM/STM32EthIPv2DMARings.cpp | 591 ------------------ .../emac/TARGET_STM/STM32EthIPv2DMARings.h | 181 ------ .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 4 + .../TARGET_STM/TARGET_STM32F4/CMakeLists.txt | 4 +- .../TARGET_ARCH_MAX/CMakeLists.txt | 13 - .../TARGET_ARCH_MAX/stm32f4_eth_conf.c | 61 -- .../TARGET_ARCH_MAX/stm32f4_eth_init.c | 115 ---- .../TARGET_STM/TARGET_STM32H5/CMakeLists.txt | 6 + .../TARGET_NUCLEO_H563ZI/CMakeLists.txt | 7 + .../TARGET_NUCLEO_H563ZI/stm32h7_eth_init.c | 147 +++++ .../TARGET_STM/TARGET_STM32H7/CMakeLists.txt | 11 - .../TARGET_STM32H7/lan8742/lan8742.c | 590 ----------------- .../TARGET_STM32H7/lan8742/lan8742.h | 464 -------------- .../drivers/emac/include/CompositeEMAC.h | 3 + .../drivers/emac/sources/CompositeEMAC.cpp | 14 + targets/targets.json5 | 11 +- 17 files changed, 190 insertions(+), 2034 deletions(-) delete mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp delete mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/CMakeLists.txt delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/stm32f4_eth_conf.c delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/stm32f4_eth_init.c create mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/CMakeLists.txt create mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/CMakeLists.txt create mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h7_eth_init.c delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/lan8742/lan8742.c delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/lan8742/lan8742.h diff --git a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt index e8f343ac1ab..de8bc065aed 100644 --- a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt @@ -9,6 +9,8 @@ elseif("STM32F7" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_STM32F7) elseif("STM32H7" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_STM32H7) +elseif("STM32H5" IN_LIST MBED_TARGET_LABELS) + add_subdirectory(TARGET_STM32H5) endif() target_include_directories(mbed-emac diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp deleted file mode 100644 index 9dcce68bf61..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.cpp +++ /dev/null @@ -1,591 +0,0 @@ -/* Copyright (c) 2024 Jamie Smith - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "STM32EthIPv2DMARings.h" - -#include "ThisThread.h" -#include "mbed_trace.h" - -#define TRACE_GROUP "CEMAC" - -namespace mbed { - -// Thread flag constants -static uint32_t THREAD_FLAG_TX_DESC_AVAILABLE = 1 << 0; -static uint32_t THREAD_FLAG_RX_DESC_AVAILABLE = 1 << 1; -static uint32_t THREAD_FLAG_RX_MEM_AVAILABLE = 1 << 2; -static uint32_t THREAD_FLAG_SHUTDOWN = 1 << 3; - -// Event flag constants -static uint32_t EVENT_FLAG_TX_DESC_AVAILABLE = 1; - -void STM32EthIPv2DMARings::buildRxDescriptors() { - - const size_t origRxDescsOwnedByApplication = rxDescsOwnedByApplication; - - // Note: With this Ethernet peripheral, you can never give back every single descriptor to - // the hardware, because then it thinks there are 0 descriptors left. - while (rxDescsOwnedByApplication > 1) { - auto &currRxDesc = rxDescs[rxBuildIndex]; - - // Allocate new buffer - auto *const buffer = memory_manager.alloc_pool(rxPoolPayloadSize, RX_BUFFER_ALIGN); - if (buffer == nullptr) { - // No memory, cannot return any more descriptors. - return; - } - - // Clear out any bits previously set in the descriptor - memset(&currRxDesc.rxDesc.toDMAFmt, 0, sizeof(EthRxDescriptor)); - - // Store buffer address - currRxDesc.buffer = buffer; - currRxDesc.rxDesc.toDMAFmt.buffer1Addr = memory_manager.get_ptr(buffer); - - // Configure descriptor - currRxDesc.rxDesc.toDMAFmt.buffer1Valid = true; - currRxDesc.rxDesc.toDMAFmt.buffer2Valid = false; - currRxDesc.rxDesc.toDMAFmt.intrOnCompletion = true; - currRxDesc.rxDesc.toDMAFmt.dmaOwn = true; - -#if __DCACHE_PRESENT - // Flush to main memory - SCB_CleanDCache_by_Addr(&currRxDesc, __SCB_DCACHE_LINE_SIZE); -#endif - - // Move to next descriptor - --rxDescsOwnedByApplication; - rxBuildIndex = (rxBuildIndex + 1) % rxPoolSize; - - // Update tail ptr to issue "rx poll demand" and mark this descriptor for receive. - // Rx stops when the current and tail pointers are equal, so we want to set the tail pointer - // to one location after the last DMA-owned descriptor in the FIFO. - heth.Instance->DMACRDTPR = reinterpret_cast(&rxDescs[rxBuildIndex]); - } - - tr_debug("buildRxDescriptors(): Returned %zu descriptors.", origRxDescsOwnedByApplication - rxDescsOwnedByApplication); -} - -// TODO monitor for Rx buffer exhaustion conditions. This happens when we have too few -// built Rx buffers and won't be able to receive another packet. Example: if the size of -// one buffer is 512 bytes, then we need at least 3 built buffers to be able to receive -// the next packet if it's 1 MTU. -// If we are in a potential buffer exhaustion situation, we need to poll at a reasonably -// high rate (10ms) until the application has freed up at least some of the pool buffers. -// NOTE: In LwIP, the failed allocation of a buffer from the pool will trigger a cleanup -// of the TCP reassembly buffer, potentially freeing some memory in the near future. - -net_stack_mem_buf_t *STM32EthIPv2DMARings::rxNextPacket() { - // Indices of the first and last descriptors for the packet will be saved here - size_t firstDescIdx = rxPoolSize; - size_t lastDescIdx = rxPoolSize; - - // Prevent looping around into descriptors waiting for rebuild by limiting how many - // we can process. - const size_t maxDescsToProcess = rxPoolSize - rxDescsOwnedByApplication; - - const size_t startIdx = rxNextIndex; - - for (size_t descCount = 0; descCount < maxDescsToProcess && lastDescIdx == rxPoolSize; descCount++) { - size_t descIdx = (startIdx + descCount) % rxPoolSize; - auto &descriptor = rxDescs[descIdx]; - -#if __DCACHE_PRESENT - SCB_InvalidateDCache_by_Addr(&descriptor, sizeof(EthRxDescriptor)); -#endif - - if (descriptor.rxDesc.fromDMAFmt.dmaOwn) { - // Descriptor owned by DMA and has not been filled in yet. We are out of descriptors to process. - break; - } - - if (descriptor.rxDesc.fromDMAFmt.context || descriptor.rxDesc.fromDMAFmt.errorSummary || - (!descriptor.rxDesc.fromDMAFmt.firstDescriptor && firstDescIdx == rxPoolSize)) { - // Context or error descriptor, or a non-first-descriptor before a first descriptor - // (could be caused by incomplete packets/junk in the DMA buffer). - // Ignore, free associated memory, and schedule for rebuild. - memory_manager.free(descriptor.buffer); - ++rxDescsOwnedByApplication; - ++rxNextIndex; - - // We should only get one of these error descriptors before the start of the packet, not - // during it. - MBED_ASSERT(firstDescIdx == rxPoolSize); - - continue; - } - - if (descriptor.rxDesc.fromDMAFmt.firstDescriptor) { - // We should see first descriptor only once and before last descriptor. If this rule is violated, it's - // because we ran out of descriptors during receive earlier and the MAC tossed out the rest of the packet. - if(firstDescIdx != rxPoolSize) { - // Clean up the old first descriptor - auto & oldFirstDesc = rxDescs[firstDescIdx]; - memory_manager.free(oldFirstDesc.buffer); - ++rxDescsOwnedByApplication; - ++rxNextIndex; - } - - firstDescIdx = descIdx; - } - - if (descriptor.rxDesc.fromDMAFmt.lastDescriptor) { - lastDescIdx = descIdx; - } - } - - if (lastDescIdx == rxPoolSize) { - // No complete packet identified. - // Take the chance to rebuild any available descriptors, then return. - tr_debug("No complete packets in Rx descs\n"); - return nullptr; - } - - // We will receive next into the descriptor after this one. - // Update this now to tell the ISR to search for descriptors after lastDescIdx only. - rxNextIndex = (lastDescIdx + 1) % rxPoolSize; - - // Set length of first buffer - net_stack_mem_buf_t *const headBuffer = rxDescs[firstDescIdx].buffer; - size_t lenRemaining = rxDescs[lastDescIdx].rxDesc.fromDMAFmt.pktLength; - memory_manager.set_len(headBuffer, std::min(lenRemaining, rxPoolPayloadSize)); - lenRemaining -= std::min(lenRemaining, rxPoolPayloadSize); - - // Iterate through the subsequent descriptors in this packet and link the buffers - // Note that this also transfers ownership of subsequent buffers to the first buffer, - // so if the first buffer is deleted, the others will be as well. - ++rxDescsOwnedByApplication; // for first buffer - rxDescs[firstDescIdx].buffer = nullptr; - for (size_t descIdx = (firstDescIdx + 1) % rxPoolSize; - descIdx != (lastDescIdx + 1) % rxPoolSize; - descIdx = (descIdx + 1) % rxPoolSize) { - - // We have to set the buffer length first before concatenating it to the chain - memory_manager.set_len(rxDescs[descIdx].buffer, std::min(lenRemaining, rxPoolPayloadSize)); - lenRemaining -= std::min(lenRemaining, rxPoolPayloadSize); - - memory_manager.cat(headBuffer, rxDescs[descIdx].buffer); - rxDescs[descIdx].buffer = nullptr; - ++rxDescsOwnedByApplication; - } - - // Invalidate cache for all data buffers, as these were written by the DMA to main memory -#if __DCACHE_PRESENT - auto * bufToInvalidate = headBuffer; - while(bufToInvalidate != nullptr) - { - SCB_InvalidateDCache_by_Addr(memory_manager.get_ptr(bufToInvalidate), rxPoolPayloadSize); - bufToInvalidate = memory_manager.get_next(bufToInvalidate); - } -#endif - - tr_debug("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu (%p-%p)\n", - memory_manager.get_total_len(headBuffer), memory_manager.get_ptr(headBuffer), firstDescIdx, lastDescIdx, - &rxDescs[firstDescIdx], &rxDescs[lastDescIdx]); - - return headBuffer; -} - -void STM32EthIPv2DMARings::reclaimTxDescs() { - bool returnedAnyDescriptors = false; - while (true) - { - if (txReclaimIndex == txSendIndex && txDescsOwnedByApplication > 0) { - // If we have reached the Tx send index, we want to stop iterating as this is - // the next descriptor that has not been populated by the application yet. - // The only exception is if the Tx ring is completely full, in which case we want - // to process the entire ring. In the case where the Tx ring is full, - // txDescsOwnedByApplication will be 0. - // Note that txSendIndex and txDescsOwnedByApplication are updated in a critical - // section so their values will always be in sync with each other. - break; - } - - auto &currDesc = txDescs[txReclaimIndex]; - -#if __DCACHE_PRESENT - SCB_InvalidateDCache_by_Addr(&currDesc, sizeof(EthTxDescriptor)); -#endif - - if (currDesc.txDesc.fromDMAFmt.dmaOwn) { - // This desc is owned by the DMA, so we have reached the part of the ring buffer - // that is still being transmitted. - // Done for now! - break; - } - - // Free any buffers associated with the descriptor - if (currDesc.packetFirstBuf != nullptr) { - memory_manager.free(currDesc.packetFirstBuf); - } - - // Update counters - txReclaimIndex = (txReclaimIndex + 1) % MBED_CONF_STM32_EMAC_ETH_TXBUFNB; - txDescsOwnedByApplication++; - - returnedAnyDescriptors = true; - } - - if (returnedAnyDescriptors) { - eventFlags.set(EVENT_FLAG_TX_DESC_AVAILABLE); - } -} - -void STM32EthIPv2DMARings::macThread() -{ - while(true) - { - // Wait for something to happen - uint32_t flags = rtos::ThisThread::flags_wait_any(THREAD_FLAG_TX_DESC_AVAILABLE | THREAD_FLAG_SHUTDOWN | THREAD_FLAG_RX_DESC_AVAILABLE | THREAD_FLAG_RX_MEM_AVAILABLE); - if(flags & THREAD_FLAG_SHUTDOWN) - { - return; - } - if(flags & THREAD_FLAG_RX_DESC_AVAILABLE) - { - // Receive any available packets. - // Note that if the ISR was delayed, we might get multiple packets per ISR, so we need to loop. - while(true) - { - auto * packet = rxNextPacket(); - if(!packet) { - break; - } - - if(emac_link_input_cb) - { - emac_link_input_cb(packet); - } - else - { - memory_manager.free(packet); - } - - // Rebuild descriptors if possible - buildRxDescriptors(); - } - } - if(flags & THREAD_FLAG_TX_DESC_AVAILABLE) - { - reclaimTxDescs(); - } - if(flags & THREAD_FLAG_RX_MEM_AVAILABLE) { - buildRxDescriptors(); - } - } -} - -void STM32EthIPv2DMARings::onPoolBufferAvail() { - thread.flags_set(THREAD_FLAG_RX_MEM_AVAILABLE); -} - - -STM32EthIPv2DMARings::STM32EthIPv2DMARings(EMACMemoryManager &memory_manager, ETH_HandleTypeDef & heth, EMAC::emac_link_input_cb_t emac_link_input_cb): - memory_manager(memory_manager), - heth(heth), - emac_link_input_cb(emac_link_input_cb), - thread(osPriorityHigh, MBED_CONF_STM32_EMAC_THREAD_STACKSIZE, nullptr, "stm32_emac_rx_thread"), - rxPoolSize(memory_manager.get_pool_size() - RX_POOL_EXTRA_BUFFERS + 1), // + 1 because we have to always keep one descriptor owned by the application - rxDescs(rxPoolSize), - rxPoolPayloadSize(memory_manager.get_pool_alloc_unit(RX_BUFFER_ALIGN)) -{ - // Make sure we have enough space in the pool for RX_POOL_EXTRA_BUFFERS plus a minimum of two descriptors - MBED_ASSERT(memory_manager.get_pool_size() >= RX_POOL_EXTRA_BUFFERS + 2); -} - -STM32EthIPv2DMARings::~STM32EthIPv2DMARings() -{ - if(thread.get_state() != rtos::Thread::Deleted) - { - thread.flags_set(THREAD_FLAG_SHUTDOWN); - thread.join(); - } -} - - -HAL_StatusTypeDef STM32EthIPv2DMARings::startDMA() -{ - if (heth.gState == HAL_ETH_STATE_READY) - { - heth.gState = HAL_ETH_STATE_BUSY; - - // At the start, we own all the descriptors - rxDescsOwnedByApplication = rxPoolSize; - txDescsOwnedByApplication = MBED_CONF_STM32_EMAC_ETH_TXBUFNB; - - // Flush Tx queue - heth.Instance->MTLTQOMR |= ETH_MTLTQOMR_FTQ; - - // Configure Rx buffer size. Per the datasheet and HAL code, we need to round this down to - // the nearest multiple of 4. - size_t rxBufferSize = memory_manager.get_pool_alloc_unit(RX_BUFFER_ALIGN); - rxBufferSize = (rxBufferSize / sizeof(uint32_t)) * sizeof(uint32_t); - heth.Instance->DMACRCR |= rxBufferSize << ETH_DMACRCR_RBSZ_Pos; - - // Configure spacing between descriptors. This will be different depending on - // cache line sizes. - // NOTE: Cast pointers to uint8_t so that the difference will be returned in bytes instead - // of elements. - const size_t rxSpacing = reinterpret_cast(&rxDescs[1]) - reinterpret_cast(&rxDescs[0]); - - // Check that spacing seems valid -#ifndef NDEBUG - const size_t txSpacing = reinterpret_cast(&txDescs[1]) - reinterpret_cast(&txDescs[0]); - MBED_ASSERT(rxSpacing == txSpacing); - MBED_ASSERT(rxSpacing % sizeof(uint32_t) == 0); -#endif - - // The spacing bitfield is configured as the number of 32-bit words to skip between descriptors. - // The descriptors have a default size of 16 bytes. - const size_t wordsToSkip = (rxSpacing - 16) / sizeof(uint32_t); - MBED_ASSERT(wordsToSkip <= 7); - heth.Instance->DMACCR &= ~ETH_DMACCR_DSL_Msk; - heth.Instance->DMACCR |= wordsToSkip << ETH_DMACCR_DSL_Pos; - - // Configure Rx descriptor ring - heth.Instance->DMACRDRLR = rxPoolSize - 1; // Ring size - heth.Instance->DMACRDLAR = reinterpret_cast(&rxDescs[0]); // Ring base address - heth.Instance->DMACRDTPR = reinterpret_cast(&rxDescs[0]); // Next descriptor (tail) pointer - - buildRxDescriptors(); - - // Configure Tx descriptor ring - heth.Instance->DMACTDRLR = MBED_CONF_STM32_EMAC_ETH_TXBUFNB - 1; // Ring size - heth.Instance->DMACTDLAR = reinterpret_cast(&txDescs[0]); // Ring base address - heth.Instance->DMACTDTPR = reinterpret_cast(&txDescs[0]); // Next descriptor (tail) pointer - - // Enable Rx and Tx DMA. NOTE: Typo in C++ headers, these should be called - // "DMACTXCR" and "DMACRXCR" - heth.Instance->DMACTCR |= ETH_DMACTCR_ST; - heth.Instance->DMACRCR |= ETH_DMACRCR_SR; - - // Clear Tx and Rx process stopped flags - heth.Instance->DMACSR = (ETH_DMACSR_TPS | ETH_DMACSR_RPS); - - // Register memory available callback - memory_manager.set_on_pool_space_avail_cb(callback(this, &STM32EthIPv2DMARings::onPoolBufferAvail)); - - // Start Rx thread - thread.start(mbed::callback(this, &STM32EthIPv2DMARings::macThread)); - - heth.gState = HAL_ETH_STATE_STARTED; - - return HAL_OK; - } - else - { - return HAL_ERROR; - } -} - -void STM32EthIPv2DMARings::rxISR() -{ - // First, we need to check if at least one DMA descriptor that is owned by the application - // has its last descriptor flag or error flag set, indicating we have received at least one complete packet - // or there is an error descriptor that can be reclaimed by the application. - // Note that we want to bias towards false positives here, because false positives just waste CPU time, - // while false negatives would cause packets to be dropped. - // So, for simplicity, we just check every descriptor currently owned by the application until we - // find one with the FS bit set or the error bits set. - // This could potentially produce a false positive if we do this in the middle of receiving - // an existing packet, but that is unlikely and will not cause anything bad to happen if it does. - - for(size_t descCount = 0; descCount < rxPoolSize; descCount++) - { - auto &descriptor = rxDescs[(rxNextIndex + descCount) % rxPoolSize]; - -#if __DCACHE_PRESENT - SCB_InvalidateDCache_by_Addr(&descriptor, sizeof(ETH_DMADescTypeDef)); -#endif - - if (descriptor.rxDesc.fromDMAFmt.dmaOwn) - { - // Descriptor owned by DMA. We are out of descriptors to process. - return; - } - if (descriptor.rxDesc.fromDMAFmt.context || descriptor.rxDesc.fromDMAFmt.errorSummary || descriptor.rxDesc.fromDMAFmt.lastDescriptor) - { - // Reclaimable descriptor or complete packet detected. - thread.flags_set(THREAD_FLAG_RX_DESC_AVAILABLE); - return; - } - } -} - -void STM32EthIPv2DMARings::txISR() -{ - thread.flags_set(THREAD_FLAG_TX_DESC_AVAILABLE); -} - -HAL_StatusTypeDef STM32EthIPv2DMARings::txPacket(net_stack_mem_buf_t * buf) -{ - // Step 1: Figure out if we can send this zero-copy, or if we need to copy it. - // Also note that each descriptor can store 2 buffers, so we need half as many descriptors - // as we have buffers, rounding up. - size_t neededDescs = (memory_manager.count_buffers(buf) + 1) / 2; - bool needToCopy = false; - if(neededDescs > MBED_CONF_STM32_EMAC_ETH_TXBUFNB) - { - // Packet uses too many buffers, we have to copy it into a continuous buffer. - needToCopy = true; - } - - if(!needToCopy) - { - net_stack_mem_buf_t * currBuf = buf; - while(currBuf != nullptr) - { - // If this buffer is passed down direct from the application, we will need to - // copy the packet. - if(memory_manager.get_lifetime(currBuf) == NetStackMemoryManager::Lifetime::VOLATILE) - { - needToCopy = true; - } - - // On STM32H7, the Ethernet DMA cannot access data in DTCM. So, if someone sends - // a packet with a data pointer in DTCM (e.g. a stack allocated payload), everything - // will break if we don't copy it first. -#ifdef MBED_RAM_BANK_SRAM_DTC_START - if(reinterpret_cast(memory_manager.get_ptr(currBuf)) >= MBED_RAM_BANK_SRAM_DTC_START && - reinterpret_cast(memory_manager.get_ptr(currBuf)) <= MBED_RAM_BANK_SRAM_DTC_START + MBED_RAM_BANK_SRAM_DTC_SIZE) - { - needToCopy = true; - } -#endif - - currBuf = memory_manager.get_next(currBuf); - } - } - - tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", - memory_manager.get_total_len(buf), memory_manager.count_buffers(buf), neededDescs); - - // Step 2: Copy packet if needed - if(needToCopy) - { - auto * newBuf = memory_manager.alloc_heap(memory_manager.get_total_len(buf), 0); - if(newBuf == nullptr) - { - // No free memory, drop packet - memory_manager.free(newBuf); - return HAL_ERROR; - } - - // We should have gotten just one contiguous buffer - MBED_ASSERT(memory_manager.get_next(newBuf) == nullptr); - neededDescs = 1; - - // Copy data over - memory_manager.copy_from_buf(memory_manager.get_ptr(newBuf), memory_manager.get_len(newBuf), buf); - memory_manager.free(buf); - buf = newBuf; - } - - // Step 3: Wait for needed amount of buffers to be available. - // Note that, in my experience, it's better to block here, as dropping the packet - // due to not having enough buffers can create weird effects when the application sends - // lots of packets at once. - while(txDescsOwnedByApplication < neededDescs) - { - eventFlags.wait_any_for(EVENT_FLAG_TX_DESC_AVAILABLE, rtos::Kernel::wait_for_u32_forever); - } - - // Step 4: Load buffer into descriptors and send - net_stack_mem_buf_t * currBuf = buf; - for(size_t descCount = 0; descCount < neededDescs; descCount++) - { - auto & currDesc = txDescs[txSendIndex]; - - // Set buffer 1 - currDesc.txDesc.toDMAFmt.buffer1Addr = static_cast(memory_manager.get_ptr(currBuf)); - currDesc.txDesc.toDMAFmt.buffer1Len = memory_manager.get_len(currBuf); - - // Set buffer 2 - currBuf = memory_manager.get_next(currBuf); - if(currBuf != nullptr) - { - currDesc.txDesc.toDMAFmt.buffer2Addr = memory_manager.get_ptr(currBuf); - currDesc.txDesc.toDMAFmt.buffer2Len = memory_manager.get_len(currBuf); - - // Move to next buffer - currBuf = memory_manager.get_next(currBuf); - } - else - { - currDesc.txDesc.toDMAFmt.buffer2Addr = nullptr; - currDesc.txDesc.toDMAFmt.buffer2Len = 0; - } - - if(currBuf == nullptr) - { - // Last descriptor, store buffer address for freeing - currDesc.packetFirstBuf = buf; - } - else - { - currDesc.packetFirstBuf = nullptr; - } - -#if __DCACHE_PRESENT - // Write buffers back to main memory - SCB_CleanDCache_by_Addr(const_cast(currDesc.txDesc.toDMAFmt.buffer1Addr), currDesc.txDesc.toDMAFmt.buffer1Len); - if(currDesc.txDesc.toDMAFmt.buffer2Addr != nullptr) - { - SCB_CleanDCache_by_Addr(const_cast(currDesc.txDesc.toDMAFmt.buffer2Addr), currDesc.txDesc.toDMAFmt.buffer2Len); - } -#endif - - // Enter a critical section, because we could run into weird corner cases if the - // interrupt executes while we are half done configuring this descriptor and updating - // the counters. - core_util_critical_section_enter(); - - // Configure settings. Note that we have to configure these every time as - // they get wiped away when the DMA gives back the descriptor - currDesc.txDesc.toDMAFmt._reserved = 0; - currDesc.txDesc.toDMAFmt.checksumInsertionCtrl = 0; // Mbed does not do checksum offload for now - currDesc.txDesc.toDMAFmt.tcpSegmentationEnable = false; // No TCP offload - currDesc.txDesc.toDMAFmt.tcpUDPHeaderLen = 0; // No TCP offload - currDesc.txDesc.toDMAFmt.srcMACInsertionCtrl = 0; // No MAC insertion - currDesc.txDesc.toDMAFmt.crcPadCtrl = 0; // Insert CRC and padding - currDesc.txDesc.toDMAFmt.lastDescriptor = currBuf == nullptr; - currDesc.txDesc.toDMAFmt.firstDescriptor = descCount == 0; - currDesc.txDesc.toDMAFmt.isContext = false; - currDesc.txDesc.toDMAFmt.vlanTagCtrl = 0; // No VLAN tag - currDesc.txDesc.toDMAFmt.intrOnCompletion = true; - currDesc.txDesc.toDMAFmt.timestampEnable = false; - currDesc.txDesc.toDMAFmt.dmaOwn = true; - -#if __DCACHE_PRESENT - // Write descriptor back to main memory - SCB_CleanDCache_by_Addr(&currDesc, sizeof(EthTxDescriptor)); -#endif - - // Update descriptor count and index - txDescsOwnedByApplication--; - txSendIndex = (txSendIndex + 1) % MBED_CONF_STM32_EMAC_ETH_TXBUFNB; - - core_util_critical_section_exit(); - - // Move tail pointer register to point to the descriptor after this descriptor. - // This tells the MAC to transmit until it reaches the given descriptor, then stop. - heth.Instance->DMACTDTPR = reinterpret_cast(&txDescs[txSendIndex]); - } - - return HAL_OK; -} - -} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h b/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h deleted file mode 100644 index 62eb2db2631..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthIPv2DMARings.h +++ /dev/null @@ -1,181 +0,0 @@ -/* Copyright (c) 2024 Jamie Smith - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef MBED_OS_STM32ETHIPV2DMARINGS_H -#define MBED_OS_STM32ETHIPV2DMARINGS_H - -#include "EMACMemoryManager.h" -#include "EMAC.h" -#include "CacheAlignedBuffer.h" - -#include "stm32xx_emac_config.h" -#include "STM32IPv2EthDescriptors.h" - -#include "rtos/Thread.h" -#include "rtos/EventFlags.h" - -#include - - -#ifdef ETH_IP_VERSION_V2 - -namespace mbed -{ - -struct WrappedEthTxDescriptor -{ - EthTxDescriptor txDesc; - - // Pointer to first memory buffer in the chain associated with this descriptor. - // This shall only be filled in on the *last* packet, so that the entire chain is freed - // when the last descriptor is returned. - net_stack_mem_buf_t * packetFirstBuf; - - // If we have a data cache, we need each descriptor to be in its own cache line. So, - // pad up to 32 byte cache line size -#if __DCACHE_PRESENT - uint8_t _padding[__SCB_DCACHE_LINE_SIZE - sizeof(EthTxDescriptor) - sizeof(net_stack_mem_buf_t *)]; -#endif -}; - -#if __DCACHE_PRESENT -static_assert(sizeof(WrappedEthTxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Tx descriptor size must equal cache line size"); -#endif - -struct WrappedEthRxDescriptor -{ - EthRxDescriptor rxDesc; - - // Memory buffers filled in to this descriptor. - // These will be passed to the application if the reception was successful. - net_stack_mem_buf_t * buffer; - - // If we have a data cache, we need each descriptor to be in its own cache line. So, - // pad up to 32 byte cache line size -#if __DCACHE_PRESENT - uint8_t _padding[__SCB_DCACHE_LINE_SIZE - sizeof(EthRxDescriptor) - sizeof(net_stack_mem_buf_t *)]; -#endif -}; - -#if __DCACHE_PRESENT -static_assert(sizeof(WrappedEthRxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Rx descriptor size must equal cache line size"); -#endif - - -/** - * @brief Implementation of Ethernet DMA rings for STM32 Ethernet IP v2. - * - * This class contains logic largely adapted from the STM32 Ethernet HAL driver. - * It would have been nice to just use that driver directly, but it cannot adapt to - * support Mbed's memory manager implementation. - */ -class STM32EthIPv2DMARings -{ - EMACMemoryManager & memory_manager; /**< Memory manager */ - ETH_HandleTypeDef & heth; ///< Handle to Ethernet peripheral - EMAC::emac_link_input_cb_t emac_link_input_cb; /**< Callback for incoming packets */ - - // Event flags used to signal application threads from ISRs - rtos::EventFlags eventFlags; - - // Thread which runs the receive loop and the transmit buffer reclamation process - rtos::Thread thread; - - const size_t rxPoolSize; ///< Number of entries in the Rx buffer pool - - // Indexes for descriptor rings. - // NOTE: when working with these indices, it's important to consider the case where e.g. the send and reclaim indexes are - // equal. This could mean *either* that the Tx ring is completely full of data, or that the Tx ring is empty. - // To resolve this ambiguity, we maintain separate count variables that track how many entries are in the ring at present. - size_t rxBuildIndex; ///< Index of the next Rx descriptor that needs to be built. Updated by application and used by ISR. - size_t rxDescsOwnedByApplication; ///< Number of Rx descriptors owned by the application and needing buffers allocated. - mstd::atomic rxNextIndex; ///< Index of the next frame that the DMA shall populate. Updated by application but used by ISR. - - size_t txSendIndex; ///< Index of the next Tx descriptor that can be filled with data - mstd::atomic txDescsOwnedByApplication; ///< Number of Tx descriptors owned by the application. Incremented by the mac thread and decremented by the application thread. - size_t txReclaimIndex; ///< Index of the next Tx descriptor that will be reclaimed by the mac thread. - - // Descriptors - DynamicCacheAlignedBuffer rxDescs; - StaticCacheAlignedBuffer txDescs; - - // Return Rx descriptors to the Ethernet MAC. - // Descriptors can only be returned if there are free buffers in the pool to allocate to them. - // The first descriptor returned will be the one at RxBuildDescIdx. - // After init, this shall only be called by the MAC thread. - void buildRxDescriptors(); - - /// Receive the next packet. Call from the Rx thread when signal is delivered. - /// Returns nullptr if nothing was received. - net_stack_mem_buf_t * rxNextPacket(); - - /// Reclaims Tx buffers and frees their memory after packet transmission. - /// Invoked by the MAC thread when it sees a Tx interrupt. - void reclaimTxDescs(); - - /// MAC thread loop - void macThread(); - - /// Callback from memory manager when a pool buffer becomes available - void onPoolBufferAvail(); - -public: - // Alignment required for Rx memory buffers. Normally they don't need alignment but - // if we are doing cache operations they need to be cache aligned. -#if __DCACHE_PRESENT - static constexpr size_t RX_BUFFER_ALIGN = __SCB_DCACHE_LINE_SIZE; -#else - static constexpr size_t RX_BUFFER_ALIGN = 2; -#endif - - /// How many extra buffers to leave in the Rx pool, relative to how many we keep assigned to Rx descriptors. - /// We want to keep some amount of extra buffers because constantly hitting the network stack with failed pool - /// allocations can produce some negative consequences in some cases. - static constexpr size_t RX_POOL_EXTRA_BUFFERS = 3; - - /// Payload size of buffers allocated from the Rx pool. This is the allocation unit size - /// of the pool minus any overhead needed for alignment. - const size_t rxPoolPayloadSize; - - STM32EthIPv2DMARings(EMACMemoryManager & memory_manager, ETH_HandleTypeDef & heth, EMAC::emac_link_input_cb_t emac_link_input_cb); - - ~STM32EthIPv2DMARings(); - - /** - * @brief Start DMA rings going. Based on HAL_ETH_Start(). - */ - HAL_StatusTypeDef startDMA(); - - /// Call when EMAC generates receive interrupt. Signals the Rx thread if there is a - /// new packet to receive. - void rxISR(); - - /// Call when EMAC generates transmit interrupt - void txISR(); - - /// Transmit a packet out of the Tx DMA ring. Note that this function - /// *takes ownership of* the passed packet and will free it either now or after - /// it's been transmitted. - /// Will block until there is space to transmit the packet. - HAL_StatusTypeDef txPacket(net_stack_mem_buf_t * buf); -}; - -} - -#endif - -#endif //MBED_OS_STM32ETHIPV2DMARINGS_H diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index ef04a43ec64..dd5d0219f57 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -253,7 +253,11 @@ namespace mbed { HAL_SYSCFG_ETHInterfaceSelect(SYSCFG_ETH_RMII); /* Dummy read to sync with ETH */ +#ifdef TARGET_STM32H5 + (void)SBS->PMCR; +#else (void)SYSCFG->PMCR; +#endif /* Ethernet Software reset */ /* Set the SWR bit: resets all MAC subsystem internal registers and logic */ diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/CMakeLists.txt index d952229bba9..bf398c55be1 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/CMakeLists.txt @@ -1,9 +1,7 @@ # Copyright (c) 2020 ARM Limited. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -if("ARCH_MAX" IN_LIST MBED_TARGET_LABELS) - add_subdirectory(TARGET_ARCH_MAX) -elseif("NUCLEO_F429ZI" IN_LIST MBED_TARGET_LABELS) +if("NUCLEO_F429ZI" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_NUCLEO_F429ZI) elseif("NUCLEO_F439ZI" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_NUCLEO_F439ZI) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/CMakeLists.txt deleted file mode 100644 index 07d2f1d1b26..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2020 ARM Limited. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -target_include_directories(mbed-emac - PUBLIC - . -) - -target_sources(mbed-emac - PRIVATE - stm32f4_eth_conf.c - stm32f4_eth_init.c -) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/stm32f4_eth_conf.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/stm32f4_eth_conf.c deleted file mode 100644 index f41db94ac67..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/stm32f4_eth_conf.c +++ /dev/null @@ -1,61 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stm32f4xx_hal.h" - -void _eth_config_mac(ETH_HandleTypeDef *heth) -{ - ETH_MACInitTypeDef macconf = { - .Watchdog = ETH_WATCHDOG_ENABLE, - .Jabber = ETH_JABBER_ENABLE, - .InterFrameGap = ETH_INTERFRAMEGAP_96BIT, - .CarrierSense = ETH_CARRIERSENCE_ENABLE, - .ReceiveOwn = ETH_RECEIVEOWN_ENABLE, - .LoopbackMode = ETH_LOOPBACKMODE_DISABLE, - .ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE, - .RetryTransmission = ETH_RETRYTRANSMISSION_DISABLE, - .AutomaticPadCRCStrip = ETH_AUTOMATICPADCRCSTRIP_DISABLE, - .BackOffLimit = ETH_BACKOFFLIMIT_10, - .DeferralCheck = ETH_DEFFERRALCHECK_DISABLE, - .ReceiveAll = ETH_RECEIVEAll_DISABLE, - .SourceAddrFilter = ETH_SOURCEADDRFILTER_DISABLE, - .PassControlFrames = ETH_PASSCONTROLFRAMES_BLOCKALL, - .BroadcastFramesReception = ETH_BROADCASTFRAMESRECEPTION_ENABLE, - .DestinationAddrFilter = ETH_DESTINATIONADDRFILTER_NORMAL, - .PromiscuousMode = ETH_PROMISCUOUS_MODE_DISABLE, - .MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE, - .UnicastFramesFilter = ETH_UNICASTFRAMESFILTER_PERFECT, - .HashTableHigh = 0x0U, - .HashTableLow = 0x0U, - .PauseTime = 0x0U, - .ZeroQuantaPause = ETH_ZEROQUANTAPAUSE_DISABLE, - .PauseLowThreshold = ETH_PAUSELOWTHRESHOLD_MINUS4, - .UnicastPauseFrameDetect = ETH_UNICASTPAUSEFRAMEDETECT_DISABLE, - .ReceiveFlowControl = ETH_RECEIVEFLOWCONTROL_DISABLE, - .TransmitFlowControl = ETH_TRANSMITFLOWCONTROL_DISABLE, - .VLANTagComparison = ETH_VLANTAGCOMPARISON_16BIT, - .VLANTagIdentifier = 0x0U, - }; - - if (heth->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE) { - macconf.ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE; - } else { - macconf.ChecksumOffload = ETH_CHECKSUMOFFLAOD_DISABLE; - } - - (void) HAL_ETH_ConfigMAC(heth, &macconf); -} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/stm32f4_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/stm32f4_eth_init.c deleted file mode 100644 index 59c233e7890..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_ARCH_MAX/stm32f4_eth_init.c +++ /dev/null @@ -1,115 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2022, STMicroelectronics - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - * 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 STMicroelectronics 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. - */ - -#include "stm32f4xx_hal.h" - -/** - * Override HAL Eth Init function - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) -{ - GPIO_InitTypeDef GPIO_InitStructure; - if (heth->Instance == ETH) { - - /* Enable GPIOs clocks */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PB11 - RMII_MII_TXD0 ---------------------> PB12 - RMII_MII_TXD1 ---------------------> PB13 - */ - /* Configure PA1, PA2 and PA7 */ - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; - GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; - GPIO_InitStructure.Pull = GPIO_NOPULL; - GPIO_InitStructure.Alternate = GPIO_AF11_ETH; - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - - /* Configure PB11, PB12 and PB13 */ - GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13; - HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); - - /* Configure PC1, PC4 and PC5 */ - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - - /* Enable the Ethernet global Interrupt */ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); - - /* Enable ETHERNET clock */ - __HAL_RCC_ETH_CLK_ENABLE(); - } -} - -/** - * Override HAL Eth DeInit function - */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) -{ - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH_CLK_DISABLE(); - - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PB11 - RMII_MII_TXD0 ---------------------> PB12 - RMII_MII_TXD1 ---------------------> PB13 - */ - HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); - HAL_GPIO_DeInit(GPIOB, GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13); - HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); - - /* Disable the Ethernet global Interrupt */ - NVIC_DisableIRQ(ETH_IRQn); - } -} - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/CMakeLists.txt new file mode 100644 index 00000000000..4d5bb6aeb92 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2025 Jamie Smith +# SPDX-License-Identifier: Apache-2.0 + +if("NUCLEO_H563ZI" IN_LIST MBED_TARGET_LABELS) + add_subdirectory(TARGET_NUCLEO_H563ZI) +endif() \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/CMakeLists.txt new file mode 100644 index 00000000000..3638f10b920 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2025 Jamie Smith +# SPDX-License-Identifier: Apache-2.0 + +target_sources(mbed-emac + PUBLIC + stm32h7_eth_init.c +) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h7_eth_init.c new file mode 100644 index 00000000000..3dbbe19690c --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h7_eth_init.c @@ -0,0 +1,147 @@ +/* mbed Microcontroller Library + * Copyright (c) 2022, STMicroelectronics + * All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 STMicroelectronics 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. + */ + +#include "stm32h5xx_hal.h" +#include "platform/mbed_critical.h" +#include "PinNames.h" + +#define RMII_MDC_Pin GPIO_PIN_1 +#define RMII_MDC_GPIO_Port GPIOC +#define RMII_REF_CLK_Pin GPIO_PIN_1 +#define RMII_REF_CLK_GPIO_Port GPIOA +#define RMII_MDIO_Pin GPIO_PIN_2 +#define RMII_MDIO_GPIO_Port GPIOA +#define RMII_CRS_DV_Pin GPIO_PIN_7 +#define RMII_CRS_DV_GPIO_Port GPIOA +#define RMII_RXD0_Pin GPIO_PIN_4 +#define RMII_RXD0_GPIO_Port GPIOC +#define RMII_RXD1_Pin GPIO_PIN_5 +#define RMII_RXD1_GPIO_Port GPIOC +#define RMII_TXD1_Pin GPIO_PIN_13 +#define RMII_TXD1_GPIO_Port GPIOB +#define TMS_Pin GPIO_PIN_13 +#define TMS_GPIO_Port GPIOA +#define TCK_Pin GPIO_PIN_14 +#define TCK_GPIO_Port GPIOA +#define RMII_TX_EN_Pin GPIO_PIN_11 +#define RMII_TX_EN_GPIO_Port GPIOG +#define RMII_TXD0_Pin GPIO_PIN_13 +#define RMII_TXD0_GPIO_Port GPIOG + +/** + * Override HAL Eth Init function + */ +void EthInitPinmappings(void) +{ + GPIO_InitTypeDef GPIO_InitStruct; + + /* GPIO Ports Clock Enable */ + __HAL_RCC_GPIOH_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); + + /* Enable Peripheral clock */ + __HAL_RCC_ETH_CLK_ENABLE(); + __HAL_RCC_ETHTX_CLK_ENABLE(); + __HAL_RCC_ETHRX_CLK_ENABLE(); + + /**ETH GPIO Configuration + PC1 ------> ETH_MDC + PA1 ------> ETH_REF_CLK + PA2 ------> ETH_MDIO + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + PB13 ------> ETH_TXD1 + PG11 ------> ETH_TX_EN + PG13 ------> ETH_TXD0 + */ + GPIO_InitStruct.Pin = RMII_MDC_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(RMII_MDC_GPIO_Port, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_RXD0_Pin | RMII_RXD1_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_TXD1_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(RMII_TXD1_GPIO_Port, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = RMII_TX_EN_Pin | RMII_TXD0_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF11_ETH; + HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); +} + +/** + * Override HAL Eth DeInit function + */ +void EthDeinitPinmappings() +{ + /* Disable Peripheral clock */ + __HAL_RCC_ETH_CLK_DISABLE(); + __HAL_RCC_ETHTX_CLK_DISABLE(); + __HAL_RCC_ETHRX_CLK_DISABLE(); + + HAL_GPIO_DeInit(GPIOC, RMII_MDC_Pin | RMII_RXD0_Pin | RMII_RXD1_Pin); + + HAL_GPIO_DeInit(GPIOA, RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin); + + HAL_GPIO_DeInit(RMII_TXD1_GPIO_Port, RMII_TXD1_Pin); + + HAL_GPIO_DeInit(GPIOG, RMII_TX_EN_Pin | RMII_TXD0_Pin); +} + +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board +} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/CMakeLists.txt index 0802f6ca7ac..9181cace75d 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/CMakeLists.txt @@ -10,14 +10,3 @@ elseif("NUCLEO_H743ZI2" IN_LIST MBED_TARGET_LABELS) elseif("NUCLEO_H723ZG" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_NUCLEO_H723ZG) endif() - -target_include_directories(mbed-emac - PUBLIC - . - ./lan8742 -) - -target_sources(mbed-emac - PRIVATE - lan8742/lan8742.c -) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/lan8742/lan8742.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/lan8742/lan8742.c deleted file mode 100644 index 0fd341cdc46..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/lan8742/lan8742.c +++ /dev/null @@ -1,590 +0,0 @@ -/** - ****************************************************************************** - * @file lan8742.c - * @author MCD Application Team - * @version V1.0.0 - * @date 08-March-2017 - * @brief This file provides a set of functions needed to manage the LAN742 - * PHY devices. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2017 STMicroelectronics

- * - * 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 STMicroelectronics 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. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "lan8742.h" - -/** @addtogroup BSP - * @{ - */ - -/** @addtogroup Component - * @{ - */ - -/** @defgroup LAN8742 LAN8742 - * @{ - */ - -/* Private typedef -----------------------------------------------------------*/ -/* Private define ------------------------------------------------------------*/ -/** @defgroup LAN8742_Private_Defines LAN8742 Private Defines - * @{ - */ -#define LAN8742_SW_RESET_TO ((uint32_t)500U) -#define LAN8742_INIT_TO ((uint32_t)2000U) -#define LAN8742_MAX_DEV_ADDR ((uint32_t)31U) -/** - * @} - */ - -/* Private macro -------------------------------------------------------------*/ -/* Private variables ---------------------------------------------------------*/ -/* Private function prototypes -----------------------------------------------*/ -/* Private functions ---------------------------------------------------------*/ -/** @defgroup LAN8742_Private_Functions LAN8742 Private Functions - * @{ - */ - -/** - * @brief Register IO functions to component object - * @param pObj: device object of LAN8742_Object_t. - * @param ioctx: holds device IO functions. - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_ERROR if missing mandatory function - */ -int32_t LAN8742_RegisterBusIO(lan8742_Object_t *pObj, lan8742_IOCtx_t *ioctx) -{ - if (!pObj || !ioctx->ReadReg || !ioctx->WriteReg || !ioctx->GetTick) { - return LAN8742_STATUS_ERROR; - } - - pObj->IO.Init = ioctx->Init; - pObj->IO.DeInit = ioctx->DeInit; - pObj->IO.ReadReg = ioctx->ReadReg; - pObj->IO.WriteReg = ioctx->WriteReg; - pObj->IO.GetTick = ioctx->GetTick; - - return LAN8742_STATUS_OK; -} - -/** - * @brief Initialize the lan8742 and configure the needed hardware resources - * @param pObj: device object LAN8742_Object_t. - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_ADDRESS_ERROR if cannot find device address - * LAN8742_STATUS_READ_ERROR if connot read register - * LAN8742_STATUS_WRITE_ERROR if connot write to register - * LAN8742_STATUS_RESET_TIMEOUT if cannot perform a software reset - */ -int32_t LAN8742_Init(lan8742_Object_t *pObj) -{ - uint32_t tickstart = 0, regvalue = 0, addr = 0; - int32_t status = LAN8742_STATUS_OK; - - if (pObj->Is_Initialized == 0) { - if (pObj->IO.Init != 0) { - /* GPIO and Clocks initialization */ - pObj->IO.Init(); - } - - /* for later check */ - pObj->DevAddr = LAN8742_MAX_DEV_ADDR + 1; - - /* Get the device address from special mode register */ - for (addr = 0; addr <= LAN8742_MAX_DEV_ADDR; addr ++) { - if (pObj->IO.ReadReg(addr, LAN8742_SMR, ®value) < 0) { - status = LAN8742_STATUS_READ_ERROR; - /* Can't read from this device address - continue with next address */ - continue; - } - - if ((regvalue & LAN8742_SMR_PHY_ADDR) == addr) { - pObj->DevAddr = addr; - status = LAN8742_STATUS_OK; - break; - } - } - - if (pObj->DevAddr > LAN8742_MAX_DEV_ADDR) { - status = LAN8742_STATUS_ADDRESS_ERROR; - } - - /* if device address is matched */ - if (status == LAN8742_STATUS_OK) { - /* set a software reset */ - if (pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, LAN8742_BCR_SOFT_RESET) >= 0) { - /* get software reset status */ - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, ®value) >= 0) { - tickstart = pObj->IO.GetTick(); - - /* wait until software reset is done or timeout occured */ - while (regvalue & LAN8742_BCR_SOFT_RESET) { - if ((pObj->IO.GetTick() - tickstart) <= LAN8742_SW_RESET_TO) { - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, ®value) < 0) { - status = LAN8742_STATUS_READ_ERROR; - break; - } - } else { - status = LAN8742_STATUS_RESET_TIMEOUT; - } - } - } else { - status = LAN8742_STATUS_READ_ERROR; - } - } else { - status = LAN8742_STATUS_WRITE_ERROR; - } - } - } - - if (status == LAN8742_STATUS_OK) { - tickstart = pObj->IO.GetTick(); - - /* Wait for 2s to perform initialization */ - while ((pObj->IO.GetTick() - tickstart) <= LAN8742_INIT_TO) { - } - pObj->Is_Initialized = 1; - } - - return status; -} - -/** - * @brief De-Initialize the lan8742 and it's hardware resources - * @param pObj: device object LAN8742_Object_t. - * @retval None - */ -int32_t LAN8742_DeInit(lan8742_Object_t *pObj) -{ - if (pObj->Is_Initialized) { - if (pObj->IO.DeInit != 0) { - if (pObj->IO.DeInit() < 0) { - return LAN8742_STATUS_ERROR; - } - } - - pObj->Is_Initialized = 0; - } - - return LAN8742_STATUS_OK; -} - -/** - * @brief Disable the LAN8742 power down mode. - * @param pObj: device object LAN8742_Object_t. - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_READ_ERROR if connot read register - * LAN8742_STATUS_WRITE_ERROR if connot write to register - */ -int32_t LAN8742_DisablePowerDownMode(lan8742_Object_t *pObj) -{ - uint32_t readval = 0; - int32_t status = LAN8742_STATUS_OK; - - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &readval) >= 0) { - readval &= ~LAN8742_BCR_POWER_DOWN; - - /* Apply configuration */ - if (pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, readval) < 0) { - status = LAN8742_STATUS_WRITE_ERROR; - } - } else { - status = LAN8742_STATUS_READ_ERROR; - } - - return status; -} - -/** - * @brief Enable the LAN8742 power down mode. - * @param pObj: device object LAN8742_Object_t. - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_READ_ERROR if connot read register - * LAN8742_STATUS_WRITE_ERROR if connot write to register - */ -int32_t LAN8742_EnablePowerDownMode(lan8742_Object_t *pObj) -{ - uint32_t readval = 0; - int32_t status = LAN8742_STATUS_OK; - - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &readval) >= 0) { - readval |= LAN8742_BCR_POWER_DOWN; - - /* Apply configuration */ - if (pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, readval) < 0) { - status = LAN8742_STATUS_WRITE_ERROR; - } - } else { - status = LAN8742_STATUS_READ_ERROR; - } - - return status; -} - -/** - * @brief Start the auto negotiation process. - * @param pObj: device object LAN8742_Object_t. - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_READ_ERROR if connot read register - * LAN8742_STATUS_WRITE_ERROR if connot write to register - */ -int32_t LAN8742_StartAutoNego(lan8742_Object_t *pObj) -{ - uint32_t readval = 0; - int32_t status = LAN8742_STATUS_OK; - - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &readval) >= 0) { - readval |= LAN8742_BCR_AUTONEGO_EN; - - /* Apply configuration */ - if (pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, readval) < 0) { - status = LAN8742_STATUS_WRITE_ERROR; - } - } else { - status = LAN8742_STATUS_READ_ERROR; - } - - return status; -} - -/** - * @brief Get the link state of LAN8742 device. - * @param pObj: Pointer to device object. - * @param pLinkState: Pointer to link state - * @retval LAN8742_STATUS_LINK_DOWN if link is down - * LAN8742_STATUS_AUTONEGO_NOTDONE if Auto nego not completed - * LAN8742_STATUS_100MBITS_FULLDUPLEX if 100Mb/s FD - * LAN8742_STATUS_100MBITS_HALFDUPLEX if 100Mb/s HD - * LAN8742_STATUS_10MBITS_FULLDUPLEX if 10Mb/s FD - * LAN8742_STATUS_10MBITS_HALFDUPLEX if 10Mb/s HD - * LAN8742_STATUS_READ_ERROR if connot read register - * LAN8742_STATUS_WRITE_ERROR if connot write to register - */ -int32_t LAN8742_GetLinkState(lan8742_Object_t *pObj) -{ - uint32_t readval = 0; - - /* Read Status register */ - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0) { - return LAN8742_STATUS_READ_ERROR; - } - - /* Read Status register again */ - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0) { - return LAN8742_STATUS_READ_ERROR; - } - - if ((readval & LAN8742_BSR_LINK_STATUS) == 0) { - /* Return Link Down status */ - return LAN8742_STATUS_LINK_DOWN; - } - - /* Check Auto negotiaition */ - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &readval) < 0) { - return LAN8742_STATUS_READ_ERROR; - } - - if ((readval & LAN8742_BCR_AUTONEGO_EN) != LAN8742_BCR_AUTONEGO_EN) { - if (((readval & LAN8742_BCR_SPEED_SELECT) == LAN8742_BCR_SPEED_SELECT) && ((readval & LAN8742_BCR_DUPLEX_MODE) == LAN8742_BCR_DUPLEX_MODE)) { - return LAN8742_STATUS_100MBITS_FULLDUPLEX; - } else if ((readval & LAN8742_BCR_SPEED_SELECT) == LAN8742_BCR_SPEED_SELECT) { - return LAN8742_STATUS_100MBITS_HALFDUPLEX; - } else if ((readval & LAN8742_BCR_DUPLEX_MODE) == LAN8742_BCR_DUPLEX_MODE) { - return LAN8742_STATUS_10MBITS_FULLDUPLEX; - } else { - return LAN8742_STATUS_10MBITS_HALFDUPLEX; - } - } else { /* Auto Nego enabled */ - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_PHYSCSR, &readval) < 0) { - return LAN8742_STATUS_READ_ERROR; - } - - /* Check if auto nego not done */ - if ((readval & LAN8742_PHYSCSR_AUTONEGO_DONE) == 0) { - return LAN8742_STATUS_AUTONEGO_NOTDONE; - } - - if ((readval & LAN8742_PHYSCSR_HCDSPEEDMASK) == LAN8742_PHYSCSR_100BTX_FD) { - return LAN8742_STATUS_100MBITS_FULLDUPLEX; - } else if ((readval & LAN8742_PHYSCSR_HCDSPEEDMASK) == LAN8742_PHYSCSR_100BTX_HD) { - return LAN8742_STATUS_100MBITS_HALFDUPLEX; - } else if ((readval & LAN8742_PHYSCSR_HCDSPEEDMASK) == LAN8742_PHYSCSR_10BT_FD) { - return LAN8742_STATUS_10MBITS_FULLDUPLEX; - } else { - return LAN8742_STATUS_10MBITS_HALFDUPLEX; - } - } -} - -/** - * @brief Set the link state of LAN8742 device. - * @param pObj: Pointer to device object. - * @param pLinkState: link state can be one of the following - * LAN8742_STATUS_100MBITS_FULLDUPLEX if 100Mb/s FD - * LAN8742_STATUS_100MBITS_HALFDUPLEX if 100Mb/s HD - * LAN8742_STATUS_10MBITS_FULLDUPLEX if 10Mb/s FD - * LAN8742_STATUS_10MBITS_HALFDUPLEX if 10Mb/s HD - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_ERROR if parameter error - * LAN8742_STATUS_READ_ERROR if connot read register - * LAN8742_STATUS_WRITE_ERROR if connot write to register - */ -int32_t LAN8742_SetLinkState(lan8742_Object_t *pObj, uint32_t LinkState) -{ - uint32_t bcrvalue = 0; - int32_t status = LAN8742_STATUS_OK; - - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &bcrvalue) >= 0) { - /* Disable link config (Auto nego, speed and duplex) */ - bcrvalue &= ~(LAN8742_BCR_AUTONEGO_EN | LAN8742_BCR_SPEED_SELECT | LAN8742_BCR_DUPLEX_MODE); - - if (LinkState == LAN8742_STATUS_100MBITS_FULLDUPLEX) { - bcrvalue |= (LAN8742_BCR_SPEED_SELECT | LAN8742_BCR_DUPLEX_MODE); - } else if (LinkState == LAN8742_STATUS_100MBITS_HALFDUPLEX) { - bcrvalue |= LAN8742_BCR_SPEED_SELECT; - } else if (LinkState == LAN8742_STATUS_10MBITS_FULLDUPLEX) { - bcrvalue |= LAN8742_BCR_DUPLEX_MODE; - } else { - /* Wrong link status parameter */ - status = LAN8742_STATUS_ERROR; - } - } else { - status = LAN8742_STATUS_READ_ERROR; - } - - if (status == LAN8742_STATUS_OK) { - /* Apply configuration */ - if (pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, bcrvalue) < 0) { - status = LAN8742_STATUS_WRITE_ERROR; - } - } - - return status; -} - -/** - * @brief Enable loopback mode. - * @param pObj: Pointer to device object. - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_READ_ERROR if connot read register - * LAN8742_STATUS_WRITE_ERROR if connot write to register - */ -int32_t LAN8742_EnableLoopbackMode(lan8742_Object_t *pObj) -{ - uint32_t readval = 0; - int32_t status = LAN8742_STATUS_OK; - - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &readval) >= 0) { - readval |= LAN8742_BCR_LOOPBACK; - - /* Apply configuration */ - if (pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, readval) < 0) { - status = LAN8742_STATUS_WRITE_ERROR; - } - } else { - status = LAN8742_STATUS_READ_ERROR; - } - - return status; -} - -/** - * @brief Disable loopback mode. - * @param pObj: Pointer to device object. - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_READ_ERROR if connot read register - * LAN8742_STATUS_WRITE_ERROR if connot write to register - */ -int32_t LAN8742_DisableLoopbackMode(lan8742_Object_t *pObj) -{ - uint32_t readval = 0; - int32_t status = LAN8742_STATUS_OK; - - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &readval) >= 0) { - readval &= ~LAN8742_BCR_LOOPBACK; - - /* Apply configuration */ - if (pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, readval) < 0) { - status = LAN8742_STATUS_WRITE_ERROR; - } - } else { - status = LAN8742_STATUS_READ_ERROR; - } - - return status; -} - -/** - * @brief Enable IT source. - * @param pObj: Pointer to device object. - * @param Interrupt: IT source to be enabled - * should be a value or a combination of the following: - * LAN8742_WOL_IT - * LAN8742_ENERGYON_IT - * LAN8742_AUTONEGO_COMPLETE_IT - * LAN8742_REMOTE_FAULT_IT - * LAN8742_LINK_DOWN_IT - * LAN8742_AUTONEGO_LP_ACK_IT - * LAN8742_PARALLEL_DETECTION_FAULT_IT - * LAN8742_AUTONEGO_PAGE_RECEIVED_IT - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_READ_ERROR if connot read register - * LAN8742_STATUS_WRITE_ERROR if connot write to register - */ -int32_t LAN8742_EnableIT(lan8742_Object_t *pObj, uint32_t Interrupt) -{ - uint32_t readval = 0; - int32_t status = LAN8742_STATUS_OK; - - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_IMR, &readval) >= 0) { - readval |= Interrupt; - - /* Apply configuration */ - if (pObj->IO.WriteReg(pObj->DevAddr, LAN8742_IMR, readval) < 0) { - status = LAN8742_STATUS_WRITE_ERROR; - } - } else { - status = LAN8742_STATUS_READ_ERROR; - } - - return status; -} - -/** - * @brief Disable IT source. - * @param pObj: Pointer to device object. - * @param Interrupt: IT source to be disabled - * should be a value or a combination of the following: - * LAN8742_WOL_IT - * LAN8742_ENERGYON_IT - * LAN8742_AUTONEGO_COMPLETE_IT - * LAN8742_REMOTE_FAULT_IT - * LAN8742_LINK_DOWN_IT - * LAN8742_AUTONEGO_LP_ACK_IT - * LAN8742_PARALLEL_DETECTION_FAULT_IT - * LAN8742_AUTONEGO_PAGE_RECEIVED_IT - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_READ_ERROR if connot read register - * LAN8742_STATUS_WRITE_ERROR if connot write to register - */ -int32_t LAN8742_DisableIT(lan8742_Object_t *pObj, uint32_t Interrupt) -{ - uint32_t readval = 0; - int32_t status = LAN8742_STATUS_OK; - - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_IMR, &readval) >= 0) { - readval &= ~Interrupt; - - /* Apply configuration */ - if (pObj->IO.WriteReg(pObj->DevAddr, LAN8742_IMR, readval) < 0) { - status = LAN8742_STATUS_WRITE_ERROR; - } - } else { - status = LAN8742_STATUS_READ_ERROR; - } - - return status; -} - -/** - * @brief Clear IT flag. - * @param pObj: Pointer to device object. - * @param Interrupt: IT flag to be cleared - * should be a value or a combination of the following: - * LAN8742_WOL_IT - * LAN8742_ENERGYON_IT - * LAN8742_AUTONEGO_COMPLETE_IT - * LAN8742_REMOTE_FAULT_IT - * LAN8742_LINK_DOWN_IT - * LAN8742_AUTONEGO_LP_ACK_IT - * LAN8742_PARALLEL_DETECTION_FAULT_IT - * LAN8742_AUTONEGO_PAGE_RECEIVED_IT - * @retval LAN8742_STATUS_OK if OK - * LAN8742_STATUS_READ_ERROR if connot read register - */ -int32_t LAN8742_ClearIT(lan8742_Object_t *pObj, uint32_t Interrupt) -{ - uint32_t readval = 0; - int32_t status = LAN8742_STATUS_OK; - - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_ISFR, &readval) < 0) { - status = LAN8742_STATUS_READ_ERROR; - } - - return status; -} - -/** - * @brief Get IT Flag status. - * @param pObj: Pointer to device object. - * @param Interrupt: IT Flag to be checked, - * should be a value or a combination of the following: - * LAN8742_WOL_IT - * LAN8742_ENERGYON_IT - * LAN8742_AUTONEGO_COMPLETE_IT - * LAN8742_REMOTE_FAULT_IT - * LAN8742_LINK_DOWN_IT - * LAN8742_AUTONEGO_LP_ACK_IT - * LAN8742_PARALLEL_DETECTION_FAULT_IT - * LAN8742_AUTONEGO_PAGE_RECEIVED_IT - * @retval 1 IT flag is SET - * 0 IT flag is RESET - * LAN8742_STATUS_READ_ERROR if connot read register - */ -int32_t LAN8742_GetITStatus(lan8742_Object_t *pObj, uint32_t Interrupt) -{ - uint32_t readval = 0; - int32_t status = 0; - - if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_ISFR, &readval) >= 0) { - status = ((readval & Interrupt) == Interrupt); - } else { - status = LAN8742_STATUS_READ_ERROR; - } - - return status; -} - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/lan8742/lan8742.h b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/lan8742/lan8742.h deleted file mode 100644 index 0214cffdea4..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/lan8742/lan8742.h +++ /dev/null @@ -1,464 +0,0 @@ -/** - ****************************************************************************** - * @file lan8742.h - * @author MCD Application Team - * @version V1.0.0 - * @date 08-March-2017 - * @brief This file contains all the functions prototypes for the - * lan8742.c PHY driver. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2017 STMicroelectronics

- * - * 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 STMicroelectronics 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. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __LAN8742_H -#define __LAN8742_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include - -/** @addtogroup BSP - * @{ - */ - -/** @addtogroup Component - * @{ - */ - -/** @defgroup LAN8742 - * @{ - */ -/* Exported constants --------------------------------------------------------*/ -/** @defgroup LAN8742_Exported_Constants LAN8742 Exported Constants - * @{ - */ - -/** @defgroup LAN8742_Registers_Mapping LAN8742 Registers Mapping - * @{ - */ -#define LAN8742_BCR ((uint16_t)0x0000U) -#define LAN8742_BSR ((uint16_t)0x0001U) -#define LAN8742_PHYI1R ((uint16_t)0x0002U) -#define LAN8742_PHYI2R ((uint16_t)0x0003U) -#define LAN8742_ANAR ((uint16_t)0x0004U) -#define LAN8742_ANLPAR ((uint16_t)0x0005U) -#define LAN8742_ANER ((uint16_t)0x0006U) -#define LAN8742_ANNPTR ((uint16_t)0x0007U) -#define LAN8742_ANNPRR ((uint16_t)0x0008U) -#define LAN8742_MMDACR ((uint16_t)0x000DU) -#define LAN8742_MMDAADR ((uint16_t)0x000EU) -#define LAN8742_ENCTR ((uint16_t)0x0010U) -#define LAN8742_MCSR ((uint16_t)0x0011U) -#define LAN8742_SMR ((uint16_t)0x0012U) -#define LAN8742_TPDCR ((uint16_t)0x0018U) -#define LAN8742_TCSR ((uint16_t)0x0019U) -#define LAN8742_SECR ((uint16_t)0x001AU) -#define LAN8742_SCSIR ((uint16_t)0x001BU) -#define LAN8742_CLR ((uint16_t)0x001CU) -#define LAN8742_ISFR ((uint16_t)0x001DU) -#define LAN8742_IMR ((uint16_t)0x001EU) -#define LAN8742_PHYSCSR ((uint16_t)0x001FU) -/** - * @} - */ - -/** @defgroup LAN8742_BCR_Bit_Definition LAN8742 BCR Bit Definition - * @{ - */ -#define LAN8742_BCR_SOFT_RESET ((uint16_t)0x8000U) -#define LAN8742_BCR_LOOPBACK ((uint16_t)0x4000U) -#define LAN8742_BCR_SPEED_SELECT ((uint16_t)0x2000U) -#define LAN8742_BCR_AUTONEGO_EN ((uint16_t)0x1000U) -#define LAN8742_BCR_POWER_DOWN ((uint16_t)0x0800U) -#define LAN8742_BCR_ISOLATE ((uint16_t)0x0400U) -#define LAN8742_BCR_RESTART_AUTONEGO ((uint16_t)0x0200U) -#define LAN8742_BCR_DUPLEX_MODE ((uint16_t)0x0100U) -/** - * @} - */ - -/** @defgroup LAN8742_BSR_Bit_Definition LAN8742 BSR Bit Definition - * @{ - */ -#define LAN8742_BSR_100BASE_T4 ((uint16_t)0x8000U) -#define LAN8742_BSR_100BASE_TX_FD ((uint16_t)0x4000U) -#define LAN8742_BSR_100BASE_TX_HD ((uint16_t)0x2000U) -#define LAN8742_BSR_10BASE_T_FD ((uint16_t)0x1000U) -#define LAN8742_BSR_10BASE_T_HD ((uint16_t)0x0800U) -#define LAN8742_BSR_100BASE_T2_FD ((uint16_t)0x0400U) -#define LAN8742_BSR_100BASE_T2_HD ((uint16_t)0x0200U) -#define LAN8742_BSR_EXTENDED_STATUS ((uint16_t)0x0100U) -#define LAN8742_BSR_AUTONEGO_CPLT ((uint16_t)0x0020U) -#define LAN8742_BSR_REMOTE_FAULT ((uint16_t)0x0010U) -#define LAN8742_BSR_AUTONEGO_ABILITY ((uint16_t)0x0008U) -#define LAN8742_BSR_LINK_STATUS ((uint16_t)0x0004U) -#define LAN8742_BSR_JABBER_DETECT ((uint16_t)0x0002U) -#define LAN8742_BSR_EXTENDED_CAP ((uint16_t)0x0001U) -/** - * @} - */ - -/** @defgroup LAN8742_PHYI1R_Bit_Definition LAN8742 PHYI1R Bit Definition - * @{ - */ -#define LAN8742_PHYI1R_OUI_3_18 ((uint16_t)0xFFFFU) -/** - * @} - */ - -/** @defgroup LAN8742_PHYI2R_Bit_Definition LAN8742 PHYI2R Bit Definition - * @{ - */ -#define LAN8742_PHYI2R_OUI_19_24 ((uint16_t)0xFC00U) -#define LAN8742_PHYI2R_MODEL_NBR ((uint16_t)0x03F0U) -#define LAN8742_PHYI2R_REVISION_NBR ((uint16_t)0x000FU) -/** - * @} - */ - -/** @defgroup LAN8742_ANAR_Bit_Definition LAN8742 ANAR Bit Definition - * @{ - */ -#define LAN8742_ANAR_NEXT_PAGE ((uint16_t)0x8000U) -#define LAN8742_ANAR_REMOTE_FAULT ((uint16_t)0x2000U) -#define LAN8742_ANAR_PAUSE_OPERATION ((uint16_t)0x0C00U) -#define LAN8742_ANAR_PO_NOPAUSE ((uint16_t)0x0000U) -#define LAN8742_ANAR_PO_SYMMETRIC_PAUSE ((uint16_t)0x0400U) -#define LAN8742_ANAR_PO_ASYMMETRIC_PAUSE ((uint16_t)0x0800U) -#define LAN8742_ANAR_PO_ADVERTISE_SUPPORT ((uint16_t)0x0C00U) -#define LAN8742_ANAR_100BASE_TX_FD ((uint16_t)0x0100U) -#define LAN8742_ANAR_100BASE_TX ((uint16_t)0x0080U) -#define LAN8742_ANAR_10BASE_T_FD ((uint16_t)0x0040U) -#define LAN8742_ANAR_10BASE_T ((uint16_t)0x0020U) -#define LAN8742_ANAR_SELECTOR_FIELD ((uint16_t)0x000FU) -/** - * @} - */ - -/** @defgroup LAN8742_ANLPAR_Bit_Definition LAN8742 ANLPAR Bit Definition - * @{ - */ -#define LAN8742_ANLPAR_NEXT_PAGE ((uint16_t)0x8000U) -#define LAN8742_ANLPAR_REMOTE_FAULT ((uint16_t)0x2000U) -#define LAN8742_ANLPAR_PAUSE_OPERATION ((uint16_t)0x0C00U) -#define LAN8742_ANLPAR_PO_NOPAUSE ((uint16_t)0x0000U) -#define LAN8742_ANLPAR_PO_SYMMETRIC_PAUSE ((uint16_t)0x0400U) -#define LAN8742_ANLPAR_PO_ASYMMETRIC_PAUSE ((uint16_t)0x0800U) -#define LAN8742_ANLPAR_PO_ADVERTISE_SUPPORT ((uint16_t)0x0C00U) -#define LAN8742_ANLPAR_100BASE_TX_FD ((uint16_t)0x0100U) -#define LAN8742_ANLPAR_100BASE_TX ((uint16_t)0x0080U) -#define LAN8742_ANLPAR_10BASE_T_FD ((uint16_t)0x0040U) -#define LAN8742_ANLPAR_10BASE_T ((uint16_t)0x0020U) -#define LAN8742_ANLPAR_SELECTOR_FIELD ((uint16_t)0x000FU) -/** - * @} - */ - -/** @defgroup LAN8742_ANER_Bit_Definition LAN8742 ANER Bit Definition - * @{ - */ -#define LAN8742_ANER_RX_NP_LOCATION_ABLE ((uint16_t)0x0040U) -#define LAN8742_ANER_RX_NP_STORAGE_LOCATION ((uint16_t)0x0020U) -#define LAN8742_ANER_PARALLEL_DETECT_FAULT ((uint16_t)0x0010U) -#define LAN8742_ANER_LP_NP_ABLE ((uint16_t)0x0008U) -#define LAN8742_ANER_NP_ABLE ((uint16_t)0x0004U) -#define LAN8742_ANER_PAGE_RECEIVED ((uint16_t)0x0002U) -#define LAN8742_ANER_LP_AUTONEG_ABLE ((uint16_t)0x0001U) -/** - * @} - */ - -/** @defgroup LAN8742_ANNPTR_Bit_Definition LAN8742 ANNPTR Bit Definition - * @{ - */ -#define LAN8742_ANNPTR_NEXT_PAGE ((uint16_t)0x8000U) -#define LAN8742_ANNPTR_MESSAGE_PAGE ((uint16_t)0x2000U) -#define LAN8742_ANNPTR_ACK2 ((uint16_t)0x1000U) -#define LAN8742_ANNPTR_TOGGLE ((uint16_t)0x0800U) -#define LAN8742_ANNPTR_MESSAGGE_CODE ((uint16_t)0x07FFU) -/** - * @} - */ - -/** @defgroup LAN8742_ANNPRR_Bit_Definition LAN8742 ANNPRR Bit Definition - * @{ - */ -#define LAN8742_ANNPTR_NEXT_PAGE ((uint16_t)0x8000U) -#define LAN8742_ANNPRR_ACK ((uint16_t)0x4000U) -#define LAN8742_ANNPRR_MESSAGE_PAGE ((uint16_t)0x2000U) -#define LAN8742_ANNPRR_ACK2 ((uint16_t)0x1000U) -#define LAN8742_ANNPRR_TOGGLE ((uint16_t)0x0800U) -#define LAN8742_ANNPRR_MESSAGGE_CODE ((uint16_t)0x07FFU) -/** - * @} - */ - -/** @defgroup LAN8742_MMDACR_Bit_Definition LAN8742 MMDACR Bit Definition - * @{ - */ -#define LAN8742_MMDACR_MMD_FUNCTION ((uint16_t)0xC000U) -#define LAN8742_MMDACR_MMD_FUNCTION_ADDR ((uint16_t)0x0000U) -#define LAN8742_MMDACR_MMD_FUNCTION_DATA ((uint16_t)0x4000U) -#define LAN8742_MMDACR_MMD_DEV_ADDR ((uint16_t)0x001FU) -/** - * @} - */ - -/** @defgroup LAN8742_ENCTR_Bit_Definition LAN8742 ENCTR Bit Definition - * @{ - */ -#define LAN8742_ENCTR_TX_ENABLE ((uint16_t)0x8000U) -#define LAN8742_ENCTR_TX_TIMER ((uint16_t)0x6000U) -#define LAN8742_ENCTR_TX_TIMER_1S ((uint16_t)0x0000U) -#define LAN8742_ENCTR_TX_TIMER_768MS ((uint16_t)0x2000U) -#define LAN8742_ENCTR_TX_TIMER_512MS ((uint16_t)0x4000U) -#define LAN8742_ENCTR_TX_TIMER_265MS ((uint16_t)0x6000U) -#define LAN8742_ENCTR_RX_ENABLE ((uint16_t)0x1000U) -#define LAN8742_ENCTR_RX_MAX_INTERVAL ((uint16_t)0x0C00U) -#define LAN8742_ENCTR_RX_MAX_INTERVAL_64MS ((uint16_t)0x0000U) -#define LAN8742_ENCTR_RX_MAX_INTERVAL_256MS ((uint16_t)0x0400U) -#define LAN8742_ENCTR_RX_MAX_INTERVAL_512MS ((uint16_t)0x0800U) -#define LAN8742_ENCTR_RX_MAX_INTERVAL_1S ((uint16_t)0x0C00U) -#define LAN8742_ENCTR_EX_CROSS_OVER ((uint16_t)0x0002U) -#define LAN8742_ENCTR_EX_MANUAL_CROSS_OVER ((uint16_t)0x0001U) -/** - * @} - */ - -/** @defgroup LAN8742_MCSR_Bit_Definition LAN8742 MCSR Bit Definition - * @{ - */ -#define LAN8742_MCSR_EDPWRDOWN ((uint16_t)0x2000U) -#define LAN8742_MCSR_FARLOOPBACK ((uint16_t)0x0200U) -#define LAN8742_MCSR_ALTINT ((uint16_t)0x0040U) -#define LAN8742_MCSR_ENERGYON ((uint16_t)0x0002U) -/** - * @} - */ - -/** @defgroup LAN8742_SMR_Bit_Definition LAN8742 SMR Bit Definition - * @{ - */ -#define LAN8742_SMR_MODE ((uint16_t)0x00E0U) -#define LAN8742_SMR_PHY_ADDR ((uint16_t)0x001FU) -/** - * @} - */ - -/** @defgroup LAN8742_TPDCR_Bit_Definition LAN8742 TPDCR Bit Definition - * @{ - */ -#define LAN8742_TPDCR_DELAY_IN ((uint16_t)0x8000U) -#define LAN8742_TPDCR_LINE_BREAK_COUNTER ((uint16_t)0x7000U) -#define LAN8742_TPDCR_PATTERN_HIGH ((uint16_t)0x0FC0U) -#define LAN8742_TPDCR_PATTERN_LOW ((uint16_t)0x003FU) -/** - * @} - */ - -/** @defgroup LAN8742_TCSR_Bit_Definition LAN8742 TCSR Bit Definition - * @{ - */ -#define LAN8742_TCSR_TDR_ENABLE ((uint16_t)0x8000U) -#define LAN8742_TCSR_TDR_AD_FILTER_ENABLE ((uint16_t)0x4000U) -#define LAN8742_TCSR_TDR_CH_CABLE_TYPE ((uint16_t)0x0600U) -#define LAN8742_TCSR_TDR_CH_CABLE_DEFAULT ((uint16_t)0x0000U) -#define LAN8742_TCSR_TDR_CH_CABLE_SHORTED ((uint16_t)0x0200U) -#define LAN8742_TCSR_TDR_CH_CABLE_OPEN ((uint16_t)0x0400U) -#define LAN8742_TCSR_TDR_CH_CABLE_MATCH ((uint16_t)0x0600U) -#define LAN8742_TCSR_TDR_CH_STATUS ((uint16_t)0x0100U) -#define LAN8742_TCSR_TDR_CH_LENGTH ((uint16_t)0x00FFU) -/** - * @} - */ - -/** @defgroup LAN8742_SCSIR_Bit_Definition LAN8742 SCSIR Bit Definition - * @{ - */ -#define LAN8742_SCSIR_AUTO_MDIX_ENABLE ((uint16_t)0x8000U) -#define LAN8742_SCSIR_CHANNEL_SELECT ((uint16_t)0x2000U) -#define LAN8742_SCSIR_SQE_DISABLE ((uint16_t)0x0800U) -#define LAN8742_SCSIR_XPOLALITY ((uint16_t)0x0010U) -/** - * @} - */ - -/** @defgroup LAN8742_CLR_Bit_Definition LAN8742 CLR Bit Definition - * @{ - */ -#define LAN8742_CLR_CABLE_LENGTH ((uint16_t)0xF000U) -/** - * @} - */ - -/** @defgroup LAN8742_IMR_ISFR_Bit_Definition LAN8742 IMR ISFR Bit Definition - * @{ - */ -#define LAN8742_INT_8 ((uint16_t)0x0100U) -#define LAN8742_INT_7 ((uint16_t)0x0080U) -#define LAN8742_INT_6 ((uint16_t)0x0040U) -#define LAN8742_INT_5 ((uint16_t)0x0020U) -#define LAN8742_INT_4 ((uint16_t)0x0010U) -#define LAN8742_INT_3 ((uint16_t)0x0008U) -#define LAN8742_INT_2 ((uint16_t)0x0004U) -#define LAN8742_INT_1 ((uint16_t)0x0002U) -/** - * @} - */ - -/** @defgroup LAN8742_PHYSCSR_Bit_Definition LAN8742 PHYSCSR Bit Definition - * @{ - */ -#define LAN8742_PHYSCSR_AUTONEGO_DONE ((uint16_t)0x1000U) -#define LAN8742_PHYSCSR_HCDSPEEDMASK ((uint16_t)0x001CU) -#define LAN8742_PHYSCSR_10BT_HD ((uint16_t)0x0004U) -#define LAN8742_PHYSCSR_10BT_FD ((uint16_t)0x0014U) -#define LAN8742_PHYSCSR_100BTX_HD ((uint16_t)0x0008U) -#define LAN8742_PHYSCSR_100BTX_FD ((uint16_t)0x0018U) -/** - * @} - */ - -/** @defgroup LAN8742_Status LAN8742 Status - * @{ - */ - -#define LAN8742_STATUS_READ_ERROR ((int32_t)-5) -#define LAN8742_STATUS_WRITE_ERROR ((int32_t)-4) -#define LAN8742_STATUS_ADDRESS_ERROR ((int32_t)-3) -#define LAN8742_STATUS_RESET_TIMEOUT ((int32_t)-2) -#define LAN8742_STATUS_ERROR ((int32_t)-1) -#define LAN8742_STATUS_OK ((int32_t) 0) -#define LAN8742_STATUS_LINK_DOWN ((int32_t) 1) -#define LAN8742_STATUS_100MBITS_FULLDUPLEX ((int32_t) 2) -#define LAN8742_STATUS_100MBITS_HALFDUPLEX ((int32_t) 3) -#define LAN8742_STATUS_10MBITS_FULLDUPLEX ((int32_t) 4) -#define LAN8742_STATUS_10MBITS_HALFDUPLEX ((int32_t) 5) -#define LAN8742_STATUS_AUTONEGO_NOTDONE ((int32_t) 6) -/** - * @} - */ - -/** @defgroup LAN8742_IT_Flags LAN8742 IT Flags - * @{ - */ -#define LAN8742_WOL_IT LAN8742_INT_8 -#define LAN8742_ENERGYON_IT LAN8742_INT_7 -#define LAN8742_AUTONEGO_COMPLETE_IT LAN8742_INT_6 -#define LAN8742_REMOTE_FAULT_IT LAN8742_INT_5 -#define LAN8742_LINK_DOWN_IT LAN8742_INT_4 -#define LAN8742_AUTONEGO_LP_ACK_IT LAN8742_INT_3 -#define LAN8742_PARALLEL_DETECTION_FAULT_IT LAN8742_INT_2 -#define LAN8742_AUTONEGO_PAGE_RECEIVED_IT LAN8742_INT_1 -/** - * @} - */ - -/** - * @} - */ - -/* Exported types ------------------------------------------------------------*/ -/** @defgroup LAN8742_Exported_Types LAN8742 Exported Types - * @{ - */ -typedef int32_t (*lan8742_Init_Func)(void); -typedef int32_t (*lan8742_DeInit_Func)(void); -typedef int32_t (*lan8742_ReadReg_Func)(uint32_t, uint32_t, uint32_t *); -typedef int32_t (*lan8742_WriteReg_Func)(uint32_t, uint32_t, uint32_t); -typedef int32_t (*lan8742_GetTick_Func)(void); - -typedef struct { - lan8742_Init_Func Init; - lan8742_DeInit_Func DeInit; - lan8742_WriteReg_Func WriteReg; - lan8742_ReadReg_Func ReadReg; - lan8742_GetTick_Func GetTick; -} lan8742_IOCtx_t; - - -typedef struct { - uint32_t DevAddr; - uint32_t Is_Initialized; - lan8742_IOCtx_t IO; - void *pData; -} lan8742_Object_t; -/** - * @} - */ - -/* Exported macro ------------------------------------------------------------*/ -/* Exported functions --------------------------------------------------------*/ -/** @defgroup LAN8742_Exported_Functions LAN8742 Exported Functions - * @{ - */ -int32_t LAN8742_RegisterBusIO(lan8742_Object_t *pObj, lan8742_IOCtx_t *ioctx); -int32_t LAN8742_Init(lan8742_Object_t *pObj); -int32_t LAN8742_DeInit(lan8742_Object_t *pObj); -int32_t LAN8742_DisablePowerDownMode(lan8742_Object_t *pObj); -int32_t LAN8742_EnablePowerDownMode(lan8742_Object_t *pObj); -int32_t LAN8742_StartAutoNego(lan8742_Object_t *pObj); -int32_t LAN8742_GetLinkState(lan8742_Object_t *pObj); -int32_t LAN8742_SetLinkState(lan8742_Object_t *pObj, uint32_t LinkState); -int32_t LAN8742_EnableLoopbackMode(lan8742_Object_t *pObj); -int32_t LAN8742_DisableLoopbackMode(lan8742_Object_t *pObj); -int32_t LAN8742_EnableIT(lan8742_Object_t *pObj, uint32_t Interrupt); -int32_t LAN8742_DisableIT(lan8742_Object_t *pObj, uint32_t Interrupt); -int32_t LAN8742_ClearIT(lan8742_Object_t *pObj, uint32_t Interrupt); -int32_t LAN8742_GetITStatus(lan8742_Object_t *pObj, uint32_t Interrupt); -/** - * @} - */ - -#ifdef __cplusplus -} -#endif -#endif /* __LAN8742_H */ - - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index 8160ab2b49c..7190eb215c3 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -404,6 +404,9 @@ class CompositeEMAC : public EMAC /// Run in its own thread to service the MAC. void macTask(); + /// Callback from memory manager when Rx pool space frees up + void onRxPoolSpaceAvail(); + /// Constructor. Should be called by subclass. CompositeEMAC(TxDMA & txDMA, RxDMA & rxDMA, MACDriver & macDriver): rxDMA(rxDMA), diff --git a/connectivity/drivers/emac/sources/CompositeEMAC.cpp b/connectivity/drivers/emac/sources/CompositeEMAC.cpp index 4032d7524c9..1f8fe67ffaf 100644 --- a/connectivity/drivers/emac/sources/CompositeEMAC.cpp +++ b/connectivity/drivers/emac/sources/CompositeEMAC.cpp @@ -145,6 +145,17 @@ namespace mbed { } } + void CompositeEMAC::onRxPoolSpaceAvail() { + rtos::ScopedMutexLock lock(macOpsMutex); + + if(state == PowerState::OFF) { + // MAC is off, not interested in callbacks + return; + } + + macThread->flags_set(THREAD_FLAG_RX_MEM_AVAILABLE); + } + void CompositeEMAC::get_ifname(char *name, uint8_t size) const { // Note that LwIP only supports a two character interface name prefix. // So, no point in going longer than that. @@ -201,6 +212,9 @@ namespace mbed { txDMA.setMemoryManager(memory_manager); rxDMA.setMemoryManager(memory_manager); + // Register memory available callback + memory_manager->set_on_pool_space_avail_cb(callback(this, &CompositeEMAC::onRxPoolSpaceAvail)); + // Power up the MAC if(mac.init() != ErrCode::SUCCESS) { tr_err("power_up(): Failed to init MAC!"); diff --git a/targets/targets.json5 b/targets/targets.json5 index 124806e4359..499e3c18e9a 100644 --- a/targets/targets.json5 +++ b/targets/targets.json5 @@ -3126,10 +3126,6 @@ mode is recommended for target MCUs with small amounts of flash and RAM.", ], "macros_add": [ "STM32H503xx" - ], - "device_has_remove": [ - "ETHERNET", - "EMAC" ] }, "NUCLEO_H503RB": { @@ -3144,7 +3140,6 @@ mode is recommended for target MCUs with small amounts of flash and RAM.", // ADC reference voltage is same as MCU VDD. // MCU VDD defaults to 3.3V though can be changed to 1.8V based on JP2 setting on nucleo board. "default-adc-vref": 3.3, - "network-default-interface-type": "ETHERNET" }, "device_has_remove": [ "ANALOGOUT" // both DAC pins are in conflict with LED1 and STDIO_UART_TX @@ -3169,6 +3164,12 @@ mode is recommended for target MCUs with small amounts of flash and RAM.", ], "macros_add": [ "STM32H563xx" + ], + "overrides": { + "network-default-interface-type": "ETHERNET" + }, + "device_has_add": [ + "EMAC" ] }, "NUCLEO_H563ZI": { From 733b1c0537984b30e1f566cc877a20dad2d0df53 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Mon, 17 Feb 2025 20:01:04 -0800 Subject: [PATCH 18/47] Fix STM32H5 init --- .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 11 ++++++++--- connectivity/drivers/emac/include/GenericEthDMA.h | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index dd5d0219f57..d8f3076be1d 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -249,14 +249,19 @@ namespace mbed { /* Init the low level hardware : GPIO, CLOCK, NVIC. */ EthInitPinmappings(); + +#ifdef TARGET_STM32H7 // Use RMII HAL_SYSCFG_ETHInterfaceSelect(SYSCFG_ETH_RMII); /* Dummy read to sync with ETH */ -#ifdef TARGET_STM32H5 - (void)SBS->PMCR; -#else (void)SYSCFG->PMCR; +#else + __HAL_RCC_SBS_CLK_ENABLE(); + HAL_SBS_ETHInterfaceSelect(SBS_ETH_RMII); + + /* Dummy read to sync with ETH */ + (void)SBS->PMCR; #endif /* Ethernet Software reset */ diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 0a0de59debb..abbfbc641d0 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -310,12 +310,12 @@ namespace mbed { size_t rxDescsOwnedByApplication; ///< Number of Rx descriptors owned by the application and needing buffers allocated. std::atomic rxNextIndex; ///< Index of the next descriptor that the DMA will populate. Updated by application but used by ISR. - // Alignment required for Rx memory buffers. Normally they don't need alignment but + // Alignment required for Rx memory buffers. Normally they don't need more than word alignment but // if we are doing cache operations they need to be cache aligned. #if __DCACHE_PRESENT static constexpr size_t RX_BUFFER_ALIGN = __SCB_DCACHE_LINE_SIZE; #else - static constexpr size_t RX_BUFFER_ALIGN = 2; + static constexpr size_t RX_BUFFER_ALIGN = sizeof(uint32_t); #endif /// Payload size of buffers allocated from the Rx pool. This is the allocation unit size From c94b760703981d06c3ab29b81a8adfda1b5144b3 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 18 Feb 2025 01:39:24 -0800 Subject: [PATCH 19/47] Ethernet working on STM32H5! --- .../TARGET_NUCLEO_H563ZI/CMakeLists.txt | 4 +- ...{stm32h7_eth_init.c => stm32h5_eth_init.c} | 100 +++++++----------- .../TARGET_NUCLEO_H723ZG/CMakeLists.txt | 2 +- .../TARGET_NUCLEO_H743ZI2/CMakeLists.txt | 2 +- .../TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c | 6 -- .../TARGET_PORTENTA_H7/CMakeLists.txt | 2 +- .../drivers/emac/include/GenericEthDMA.h | 44 ++++---- .../include/lwipstack/LWIPMemoryManager.h | 2 - .../lwipstack/source/LWIPMemoryManager.cpp | 5 - .../source/NanostackMemoryManager.cpp | 5 - .../source/include/NanostackMemoryManager.h | 2 - 11 files changed, 68 insertions(+), 106 deletions(-) rename connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/{stm32h7_eth_init.c => stm32h5_eth_init.c} (60%) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/CMakeLists.txt index 3638f10b920..48a8fc06595 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/CMakeLists.txt @@ -2,6 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 target_sources(mbed-emac - PUBLIC - stm32h7_eth_init.c + PRIVATE + stm32h5_eth_init.c ) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h5_eth_init.c similarity index 60% rename from connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h7_eth_init.c rename to connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h5_eth_init.c index 3dbbe19690c..0e3ddcdc1b6 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h5_eth_init.c @@ -1,5 +1,5 @@ /* mbed Microcontroller Library - * Copyright (c) 2022, STMicroelectronics + * Copyright (c) 2025, STMicroelectronics * All rights reserved. * * SPDX-License-Identifier: Apache-2.0 @@ -32,88 +32,55 @@ #include "platform/mbed_critical.h" #include "PinNames.h" -#define RMII_MDC_Pin GPIO_PIN_1 -#define RMII_MDC_GPIO_Port GPIOC -#define RMII_REF_CLK_Pin GPIO_PIN_1 -#define RMII_REF_CLK_GPIO_Port GPIOA -#define RMII_MDIO_Pin GPIO_PIN_2 -#define RMII_MDIO_GPIO_Port GPIOA -#define RMII_CRS_DV_Pin GPIO_PIN_7 -#define RMII_CRS_DV_GPIO_Port GPIOA -#define RMII_RXD0_Pin GPIO_PIN_4 -#define RMII_RXD0_GPIO_Port GPIOC -#define RMII_RXD1_Pin GPIO_PIN_5 -#define RMII_RXD1_GPIO_Port GPIOC -#define RMII_TXD1_Pin GPIO_PIN_13 -#define RMII_TXD1_GPIO_Port GPIOB -#define TMS_Pin GPIO_PIN_13 -#define TMS_GPIO_Port GPIOA -#define TCK_Pin GPIO_PIN_14 -#define TCK_GPIO_Port GPIOA -#define RMII_TX_EN_Pin GPIO_PIN_11 -#define RMII_TX_EN_GPIO_Port GPIOG -#define RMII_TXD0_Pin GPIO_PIN_13 -#define RMII_TXD0_GPIO_Port GPIOG - /** * Override HAL Eth Init function */ void EthInitPinmappings(void) { - GPIO_InitTypeDef GPIO_InitStruct; - - /* GPIO Ports Clock Enable */ - __HAL_RCC_GPIOH_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); + GPIO_InitTypeDef GPIO_InitStruct = {0}; - /* Enable Peripheral clock */ + /* Peripheral clock enable */ __HAL_RCC_ETH_CLK_ENABLE(); __HAL_RCC_ETHTX_CLK_ENABLE(); __HAL_RCC_ETHRX_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); /**ETH GPIO Configuration - PC1 ------> ETH_MDC - PA1 ------> ETH_REF_CLK - PA2 ------> ETH_MDIO - PA7 ------> ETH_CRS_DV - PC4 ------> ETH_RXD0 - PC5 ------> ETH_RXD1 - PB13 ------> ETH_TXD1 - PG11 ------> ETH_TX_EN - PG13 ------> ETH_TXD0 + PC1 ------> ETH_MDC + PA1 ------> ETH_REF_CLK + PA2 ------> ETH_MDIO + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + PB15 ------> ETH_TXD1 + PG11 ------> ETH_TX_EN + PG13 ------> ETH_TXD0 */ - GPIO_InitStruct.Pin = RMII_MDC_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(RMII_MDC_GPIO_Port, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin; + GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - GPIO_InitStruct.Pin = RMII_RXD0_Pin | RMII_RXD1_Pin; + GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - GPIO_InitStruct.Pin = RMII_TXD1_Pin; + GPIO_InitStruct.Pin = GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF11_ETH; - HAL_GPIO_Init(RMII_TXD1_GPIO_Port, &GPIO_InitStruct); + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - GPIO_InitStruct.Pin = RMII_TX_EN_Pin | RMII_TXD0_Pin; + GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; @@ -126,18 +93,29 @@ void EthInitPinmappings(void) */ void EthDeinitPinmappings() { - /* Disable Peripheral clock */ + /* Peripheral clock disable */ __HAL_RCC_ETH_CLK_DISABLE(); __HAL_RCC_ETHTX_CLK_DISABLE(); __HAL_RCC_ETHRX_CLK_DISABLE(); - HAL_GPIO_DeInit(GPIOC, RMII_MDC_Pin | RMII_RXD0_Pin | RMII_RXD1_Pin); + /**ETH GPIO Configuration + PC1 ------> ETH_MDC + PA1 ------> ETH_REF_CLK + PA2 ------> ETH_MDIO + PA7 ------> ETH_CRS_DV + PC4 ------> ETH_RXD0 + PC5 ------> ETH_RXD1 + PB15 ------> ETH_TXD1 + PG11 ------> ETH_TX_EN + PG13 ------> ETH_TXD0 + */ + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5); - HAL_GPIO_DeInit(GPIOA, RMII_REF_CLK_Pin | RMII_MDIO_Pin | RMII_CRS_DV_Pin); + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7); - HAL_GPIO_DeInit(RMII_TXD1_GPIO_Port, RMII_TXD1_Pin); + HAL_GPIO_DeInit(GPIOB, GPIO_PIN_15); - HAL_GPIO_DeInit(GPIOG, RMII_TX_EN_Pin | RMII_TXD0_Pin); + HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11|GPIO_PIN_13); } // Get Ethernet PHY reset pin diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/CMakeLists.txt index 5e6c9d6be72..09efa30253f 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H723ZG/CMakeLists.txt @@ -2,6 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 target_sources(mbed-emac - INTERFACE + PRIVATE stm32h7_eth_init.c ) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/CMakeLists.txt index 635a02bd7a3..09efa30253f 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/CMakeLists.txt @@ -2,6 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 target_sources(mbed-emac - PUBLIC + PRIVATE stm32h7_eth_init.c ) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c index 94fb130e9f6..5a77f1cdc08 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_NUCLEO_H743ZI2/stm32h7_eth_init.c @@ -32,8 +32,6 @@ #include "platform/mbed_critical.h" #include "PinNames.h" -#define MCO_Pin GPIO_PIN_0 -#define MCO_GPIO_Port GPIOH #define RMII_MDC_Pin GPIO_PIN_1 #define RMII_MDC_GPIO_Port GPIOC #define RMII_REF_CLK_Pin GPIO_PIN_1 @@ -48,10 +46,6 @@ #define RMII_RXD1_GPIO_Port GPIOC #define RMII_TXD1_Pin GPIO_PIN_13 #define RMII_TXD1_GPIO_Port GPIOB -#define TMS_Pin GPIO_PIN_13 -#define TMS_GPIO_Port GPIOA -#define TCK_Pin GPIO_PIN_14 -#define TCK_GPIO_Port GPIOA #define RMII_TX_EN_Pin GPIO_PIN_11 #define RMII_TX_EN_GPIO_Port GPIOG #define RMII_TXD0_Pin GPIO_PIN_13 diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/CMakeLists.txt index 635a02bd7a3..09efa30253f 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/TARGET_PORTENTA_H7/CMakeLists.txt @@ -2,6 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 target_sources(mbed-emac - PUBLIC + PRIVATE stm32h7_eth_init.c ) diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index abbfbc641d0..46ceae48492 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -154,6 +154,8 @@ namespace mbed { txReclaimIndex = (txReclaimIndex + 1) % MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; ++txDescsOwnedByApplication; + tr_debug("Reclaimed descriptor %zu", txReclaimIndex); + returnedAnyDescriptors = true; } @@ -459,9 +461,8 @@ namespace mbed { break; } - if (descriptor.isErrorDesc() || - (!descriptor.isFirstDesc() && !firstDescIdx.has_value())) { - // Context or error descriptor, or a non-first-descriptor before a first descriptor + if (!firstDescIdx.has_value() && (descriptor.isErrorDesc() || !descriptor.isFirstDesc())) { + // Error or non-first-descriptor before a first descriptor // (could be caused by incomplete packets/junk in the DMA buffer). // Ignore, free associated memory, and schedule for rebuild. memory_manager->free(rxDescStackBufs[descIdx]); @@ -469,27 +470,30 @@ namespace mbed { ++rxDescsOwnedByApplication; ++rxNextIndex; - // We should only get one of these error descriptors before the start of the packet, not - // during it. - if(descriptor.isErrorDesc()) { - MBED_ASSERT(!firstDescIdx.has_value()); - } - continue; } + else if(firstDescIdx.has_value() && (descriptor.isErrorDesc() || descriptor.isFirstDesc())) + { + // Already seen a first descriptor, but we have an error descriptor or another first descriptor. + // So, delete the in-progress packet up to this point. + + // Clean up the old first descriptor and any descriptors between there and here + const size_t endIdx = descriptor.isFirstDesc() ? descIdx : (descIdx + 1) % RX_NUM_DESCS; - if (descriptor.isFirstDesc()) { - // We should see first descriptor only once and before last descriptor. If this rule is violated, it's likely - // because we ran out of descriptors during receive earlier and the MAC tossed out the rest of the packet. - if(firstDescIdx.has_value()) { - // Clean up the old first descriptor and any descriptors between there and here - for(size_t descToCleanIdx = *firstDescIdx; descToCleanIdx != descIdx; descToCleanIdx = (descToCleanIdx + 1) % RX_NUM_DESCS) { - memory_manager->free(rxDescStackBufs[descToCleanIdx]); - rxDescStackBufs[descToCleanIdx] = nullptr; - ++rxDescsOwnedByApplication; - ++rxNextIndex; - } + for(size_t descToCleanIdx = *firstDescIdx; descToCleanIdx != endIdx; descToCleanIdx = (descToCleanIdx + 1) % RX_NUM_DESCS) { + memory_manager->free(rxDescStackBufs[descToCleanIdx]); + rxDescStackBufs[descToCleanIdx] = nullptr; + ++rxDescsOwnedByApplication; + ++rxNextIndex; } + + if(!descriptor.isErrorDesc()) + { + firstDescIdx = descIdx; + } + } + else if(descriptor.isFirstDesc()) + { firstDescIdx = descIdx; } diff --git a/connectivity/lwipstack/include/lwipstack/LWIPMemoryManager.h b/connectivity/lwipstack/include/lwipstack/LWIPMemoryManager.h index 64f4f4eddd3..5e38a4320b9 100644 --- a/connectivity/lwipstack/include/lwipstack/LWIPMemoryManager.h +++ b/connectivity/lwipstack/include/lwipstack/LWIPMemoryManager.h @@ -31,8 +31,6 @@ class LWIPMemoryManager final : public EMACMemoryManager { uint32_t get_pool_alloc_unit(uint32_t align) const override; - uint32_t get_pool_size() const override; - void free(net_stack_mem_buf_t *buf) override; uint32_t get_total_len(const net_stack_mem_buf_t *buf) const override; diff --git a/connectivity/lwipstack/source/LWIPMemoryManager.cpp b/connectivity/lwipstack/source/LWIPMemoryManager.cpp index ede74916329..054b84b4d21 100644 --- a/connectivity/lwipstack/source/LWIPMemoryManager.cpp +++ b/connectivity/lwipstack/source/LWIPMemoryManager.cpp @@ -51,11 +51,6 @@ uint32_t LWIPMemoryManager::get_pool_alloc_unit(uint32_t align) const return alloc_unit; } -uint32_t LWIPMemoryManager::get_pool_size() const -{ - return PBUF_POOL_SIZE; -} - void LWIPMemoryManager::free(net_stack_mem_buf_t *buf) { pbuf_free(static_cast(buf)); diff --git a/connectivity/nanostack/mbed-mesh-api/source/NanostackMemoryManager.cpp b/connectivity/nanostack/mbed-mesh-api/source/NanostackMemoryManager.cpp index 1e2a71fe435..dcb93b1cbe1 100644 --- a/connectivity/nanostack/mbed-mesh-api/source/NanostackMemoryManager.cpp +++ b/connectivity/nanostack/mbed-mesh-api/source/NanostackMemoryManager.cpp @@ -65,11 +65,6 @@ uint32_t NanostackMemoryManager::get_pool_alloc_unit(uint32_t align) const return MBED_CONF_NSAPI_EMAC_RX_POOL_BUF_SIZE; } -uint32_t NanostackMemoryManager::get_pool_size() const -{ - return MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS; -} - void NanostackMemoryManager::free(emac_mem_buf_t *mem) { ns_dyn_mem_free(mem); diff --git a/connectivity/nanostack/mbed-mesh-api/source/include/NanostackMemoryManager.h b/connectivity/nanostack/mbed-mesh-api/source/include/NanostackMemoryManager.h index cd426b50909..1589e166f3e 100644 --- a/connectivity/nanostack/mbed-mesh-api/source/include/NanostackMemoryManager.h +++ b/connectivity/nanostack/mbed-mesh-api/source/include/NanostackMemoryManager.h @@ -35,8 +35,6 @@ class NanostackMemoryManager final : public EMACMemoryManager { uint32_t get_pool_alloc_unit(uint32_t align) const override; - uint32_t get_pool_size() const override; - void free(emac_mem_buf_t *buf) override; uint32_t get_total_len(const emac_mem_buf_t *buf) const override; From 5c65117b27877c3dfc928955396903faad03bb28 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 18 Feb 2025 01:40:32 -0800 Subject: [PATCH 20/47] Run formatter --- .../TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h5_eth_init.c | 4 +++- .../netsocket/include/netsocket/NetStackMemoryManager.h | 5 ++++- platform/include/platform/mbed_wait_api.h | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h5_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h5_eth_init.c index 0e3ddcdc1b6..c936a6886cf 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h5_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H5/TARGET_NUCLEO_H563ZI/stm32h5_eth_init.c @@ -33,7 +33,9 @@ #include "PinNames.h" /** - * Override HAL Eth Init function + * Override HAL Eth Init function. + * + * Note: This was copied from HAL_ETH_MspInit() in a project for the NUCLEO-H563ZI in STM32CubeIDE */ void EthInitPinmappings(void) { diff --git a/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h b/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h index 9d8eb44e417..13f2fcd56ff 100644 --- a/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h +++ b/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h @@ -102,7 +102,10 @@ class NetStackMemoryManager { * * @return The number of pool buffers that can be allocated at any one time */ - uint32_t get_pool_size() { return MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS; } + uint32_t get_pool_size() + { + return MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS; + } /** * Free memory buffer chain diff --git a/platform/include/platform/mbed_wait_api.h b/platform/include/platform/mbed_wait_api.h index db857714152..9c1f7621647 100644 --- a/platform/include/platform/mbed_wait_api.h +++ b/platform/include/platform/mbed_wait_api.h @@ -154,7 +154,8 @@ inline void _wait_us_inline(unsigned int us) #include // Override of wait_us() allowing a std::chrono type convertible to microseconds to be passed in. -static inline void _wait_us_inline(std::chrono::microseconds const us) { +static inline void _wait_us_inline(std::chrono::microseconds const us) +{ _wait_us_inline(us.count()); } #endif From eb03e1296a990b6cd13ccebe074f7ca4a1fbaf74 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 18 Feb 2025 09:28:17 -0800 Subject: [PATCH 21/47] Fix us ticker build error --- platform/include/platform/mbed_wait_api.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/platform/include/platform/mbed_wait_api.h b/platform/include/platform/mbed_wait_api.h index 9c1f7621647..23a7fb1bbcb 100644 --- a/platform/include/platform/mbed_wait_api.h +++ b/platform/include/platform/mbed_wait_api.h @@ -154,10 +154,18 @@ inline void _wait_us_inline(unsigned int us) #include // Override of wait_us() allowing a std::chrono type convertible to microseconds to be passed in. +#if defined US_TICKER_PERIOD_NUM static inline void _wait_us_inline(std::chrono::microseconds const us) { _wait_us_inline(us.count()); } +#else +static inline void wait_us(std::chrono::microseconds const us) +{ + wait_us(us.count()); +} +#endif + #endif #endif From 997ae9c5df2469b0a761090457740c52be3accae Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Thu, 20 Feb 2025 09:36:01 -0800 Subject: [PATCH 22/47] Start on MAC v1 driver --- .../drivers/emac/TARGET_STM/CMakeLists.txt | 8 + .../emac/TARGET_STM/STM32EthMACCommon.h | 84 +++++ .../drivers/emac/TARGET_STM/STM32EthMACv1.cpp | 311 ++++++++++++++++++ .../drivers/emac/TARGET_STM/STM32EthMACv1.h | 80 +++++ .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 116 ++----- .../drivers/emac/TARGET_STM/STM32EthMACv2.h | 14 +- .../TARGET_STM32F2/stm32xx_emac_config.h | 22 -- .../TARGET_STM32F4/stm32xx_emac_config.h | 22 -- .../TARGET_NUCLEO_F767ZI/stm32f7_eth_init.c | 143 ++++---- .../TARGET_STM32F7/stm32xx_emac_config.h | 22 -- .../TARGET_STM32H7/stm32xx_emac_config.h | 22 -- .../drivers/emac/include/CompositeEMAC.h | 10 +- 12 files changed, 593 insertions(+), 261 deletions(-) create mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthMACCommon.h create mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp create mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/stm32xx_emac_config.h delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/stm32xx_emac_config.h delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/stm32xx_emac_config.h delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/stm32xx_emac_config.h diff --git a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt index de8bc065aed..dc12b1d4cdb 100644 --- a/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/CMakeLists.txt @@ -25,4 +25,12 @@ if("STM32H7" IN_LIST MBED_TARGET_LABELS OR "STM32H5" IN_LIST MBED_TARGET_LABELS) ) endif() +if("STM32F2" IN_LIST MBED_TARGET_LABELS OR "STM32F4" IN_LIST MBED_TARGET_LABELS OR "STM32F7" IN_LIST MBED_TARGET_LABELS) + target_sources(mbed-emac + PRIVATE + STM32EthMACv1.cpp + ) +endif() + + target_compile_options(mbed-emac PRIVATE -Wno-packed-bitfield-compat) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACCommon.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACCommon.h new file mode 100644 index 00000000000..d327094a168 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACCommon.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2025 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "device.h" +#include "CompositeEMAC.h" +#include "MbedCRC.h" + +// Figure out the Ethernet IP version in use +#ifdef TARGET_STM32H5 || TARGET_STM32H7 +#define ETH_IP_VERSION_V2 +#else +#define ETH_IP_VERSION_V1 +#endif + +namespace mbed +{ +constexpr auto MDIO_TRANSACTION_TIMEOUT = std::chrono::milliseconds(1); // used by STMicro HAL + +inline constexpr size_t NUM_PERFECT_FILTER_REGS = 3; +static const std::pair MAC_ADDR_PERF_FILTER_REGS[NUM_PERFECT_FILTER_REGS] = { + {Ð->MACA1HR, Ð->MACA1LR}, + {Ð->MACA2HR, Ð->MACA2LR}, + {Ð->MACA3HR, Ð->MACA3LR} +}; + +/// Write a MAC address into the given registers with the needed encoding +static inline void writeMACAddress(const CompositeEMAC::MACAddress & mac, volatile uint32_t *addrHighReg, volatile uint32_t *addrLowReg) +{ + /* Set MAC addr bits 32 to 47 */ + *addrHighReg = (static_cast(mac[5]) << 8) | static_cast(mac[4]) | ETH_MACA1HR_AE_Msk; + /* Set MAC addr bits 0 to 31 */ + *addrLowReg = (static_cast(mac[3]) << 24) | (static_cast(mac[2]) << 16) | + (static_cast(mac[1]) << 8) | static_cast(mac[0]); +} + +/// Add a MAC address to the multicast hash filter. +void addHashFilterMAC(ETH_TypeDef * base, const CompositeEMAC::MACAddress & mac) { +#if defined(ETH_IP_VERSION_V2) + uint32_t volatile * hashRegs[] = { + &base->MACHT0R, + &base->MACHT1R + }; +#else + uint32_t volatile * hashRegs[] = { + &base->MACHTLR, + &base->MACHTHR + }; +#endif + + // Note: as always, the datasheet description of how to do this CRC was vague and slightly wrong. + // This forum thread figured it out: https://community.st.com/t5/stm32-mcus-security/calculating-ethernet-multicast-filter-hash-value/td-p/416984 + // What the datasheet SHOULD say is: + // Compute the Ethernet CRC-32 of the MAC address, with initial value of 1s, final XOR of ones, and input reflection on but output reflection off + // Then, take the upper 6 bits and use that to index the hash table. + + mbed::MbedCRC crcCalc(0xFFFFFFFF, 0xFFFFFFFF, true, false); + + // Compute Ethernet CRC-32 of the MAC address + uint32_t crc; + crcCalc.compute(mac.data(), mac.size(), &crc); + + // Take upper 6 bits + uint32_t hashVal = crc >> 26; + + // Set correct bit in hash filter + *hashRegs[hashVal >> 5] |= (1 << (hashVal & 0x1F)); +} + +} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp new file mode 100644 index 00000000000..af49bd469fb --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp @@ -0,0 +1,311 @@ +/* Copyright (c) 2025 Jamie Smith +* SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "STM32EthMACv1.h" +#include "STM32EthMACCommon.h" + +#include +#include +#include + +#define TRACE_GROUP "STEMACv1" + +using namespace std::chrono_literals; + +// Defined in stm32_eth_init.c +extern "C" void EthInitPinmappings(); +extern "C" void EthDeinitPinmappings(); +extern "C" PinName EthGetPhyResetPin(); + +namespace mbed { + void STM32EthMACv1::MACDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) { + /* Get the ETHERNET MACMIIAR value */ + uint32_t tempreg = base->MACMIIAR; + /* Clear CSR Clock Range CR[2:0] bits */ + tempreg &= ETH_MACMIIAR_CR_MASK; + + /* Get hclk frequency value */ + uint32_t hclk = HAL_RCC_GetHCLKFreq(); + + /* Set CR bits depending on hclk value */ + if((hclk >= 20000000)&&(hclk < 35000000)) + { + /* CSR Clock Range between 20-35 MHz */ + tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div16; + } + else if((hclk >= 35000000)&&(hclk < 60000000)) + { + /* CSR Clock Range between 35-60 MHz */ + tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div26; + } + else if((hclk >= 60000000)&&(hclk < 100000000)) + { + /* CSR Clock Range between 60-100 MHz */ + tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div42; + } + else if((hclk >= 100000000)&&(hclk < 150000000)) + { + /* CSR Clock Range between 100-150 MHz */ + tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div62; + } + else /* ((hclk >= 150000000)&&(hclk <= 216000000)) */ + { + /* CSR Clock Range between 150-216 MHz */ + tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div102; + } + + /* Write to ETHERNET MAC MIIAR: Configure the ETHERNET CSR Clock Range */ + base->MACMIIAR = (uint32_t)tempreg; + } + +CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::init() { + sleep_manager_lock_deep_sleep(); + + // Note: Following code is based on HAL_Eth_Init() from the HAL + + /* Enable SYSCFG Clock */ + __HAL_RCC_SYSCFG_CLK_ENABLE(); + + /* Select RMII Mode*/ + SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; + + /* Init the low level hardware : GPIO, CLOCK, NVIC. */ + EthInitPinmappings(); + + /* Ethernet Software reset */ + /* Set the SWR bit: resets all MAC subsystem internal registers and logic */ + /* After reset all the registers holds their respective reset values */ + base->DMABMR |= ETH_DMABMR_SR; + + const auto ETH_SW_RESET_TIMEOUT = 500us; // used by STM32 HAL + Timer timeoutTimer; + timeoutTimer.start(); + while(timeoutTimer.elapsed_time() < ETH_SW_RESET_TIMEOUT && (base->DMABMR & ETH_DMABMR_SR)) {} + if(base->DMABMR & ETH_DMABMR_SR) { + // Reset failed to complete within expected timeout. + // Note: This is usually because of a missing RMII clock from the PHY. + return ErrCode::TIMEOUT; + } + + // Configure MDIO clock + ETH_SetMDIOClockRange(base); + + // Configure MAC settings + base->MACCR |= ETH_MACCR_APCS_Msk; // Strip padding and CRC from frames + base->MACFFR = ETH_MACFFR_HPF_Msk | ETH_MACFFR_HM_Msk; // Use perfect and hash filters for multicast + + // Configure DMA settings. Default STM32CubeHAL settings used. + base->DMAOMR = ETH_DMAOMR_RSF_Msk | + ETH_DMAOMR_TSF_Msk; + + base->DMABMR = ETH_DMABMR_AAB_Msk | + ETH_DMABMR_USP_Msk | + ETH_DMABMR_RDP_32Beat | + ETH_DMABMR_FB_Msk | + ETH_DMABMR_PBL_32Beat | + ETH_DMABMR_EDE_Msk; + + // Set up interrupt handler + NVIC_SetVector(ETH_IRQn, reinterpret_cast(&STM32EthMACv1::irqHandler)); + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); + + // Enable Tx, Rx, and fatal bus error interrupts. + // However, don't enable receive buffer unavailable interrupt, because that can + // trigger if we run out of Rx descriptors, and we don't want to fatal error + // in that case. + base->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE | ETH_DMAIER_FBEIE | ETH_DMAIER_AISE; +} + +CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::deinit() { + // Disable interrupt + HAL_NVIC_DisableIRQ(ETH_IRQn); + + // Unlock deep sleep + sleep_manager_unlock_deep_sleep(); + + // Unmap pins and turn off clock + EthDeinitPinmappings(); + + return ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::enable(LinkSpeed speed, Duplex duplex) { + if(speed == LinkSpeed::LINK_1GBIT) { + return ErrCode::INVALID_ARGUMENT; + } + + auto maccrVal = base->MACCR; + if(speed == LinkSpeed::LINK_100MBIT) { + maccrVal |= ETH_MACCR_FES_Msk; + } + else { + maccrVal &= ~ETH_MACCR_FES_Msk; + } + if(duplex == Duplex::FULL) { + maccrVal |= ETH_MACCR_DM_Msk; + } + else { + maccrVal &= ~ETH_MACCR_DM_Msk; + } + + // Enable the MAC transmission & reception + maccrVal |= ETH_MACCR_TE | ETH_MACCR_RE; + base->MACCR = maccrVal; + return ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::disable() { + base->MACCR &= ~(ETH_MACCR_TE | ETH_MACCR_RE); + + // Note: Don't flush Tx FIFO because of STM32F7 errata 2.21.3, which can cause + // the MAC to get stuck if the Tx FIFO is flushed at the wrong time + + return ErrCode::SUCCESS; +} + +void STM32EthMACv1::MACDriver::setOwnMACAddr(const MACAddress &ownAddress) { + // Set MAC address + writeMACAddress(ownAddress, &base->MACA0HR, &base->MACA0LR); +} + +CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::mdioRead(uint8_t devAddr, uint8_t regAddr, uint16_t &result) { + // This code based on HAL_ETH_ReadPHYRegister() + if(base->MACMIIAR & ETH_MACMIIAR_MB_Msk) { + // MDIO operation already in progress + return ErrCode::INVALID_USAGE; + } + + uint32_t tmpreg = base->MACMIIAR; + + /* Prepare the MDIO Address Register value + - Set the PHY device address + - Set the PHY register address + - Set the read mode + - Set the MII Busy bit */ + tmpreg &= ~(ETH_MACMIIAR_MW_Msk | ETH_MACMIIAR_PA_Msk | ETH_MACMIIAR_MR_Msk); + tmpreg |= (devAddr << ETH_MACMIIAR_PA_Pos) | (regAddr << ETH_MACMIIAR_MR_Pos) | ETH_MACMIIAR_MB_Msk; + base->MACMIIAR = tmpreg; + + Timer timeoutTimer; + timeoutTimer.start(); + while(timeoutTimer.elapsed_time() < MDIO_TRANSACTION_TIMEOUT && (base->MACMIIAR & ETH_MACMIIAR_MB_Msk)) {} + if(base->MACMIIAR & ETH_MACMIIAR_MB_Msk) { + // Transaction failed to complete within expected timeout + return ErrCode::TIMEOUT; + } + + // Get result + result = base->MACMIIDR; + + tr_info("MDIO read devAddr %" PRIu8 ", regAddr 0x%" PRIx8 " -> 0x%" PRIx16, devAddr, regAddr, result); + + return ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::mdioWrite(uint8_t devAddr, uint8_t regAddr, uint16_t data) { + // This code based on HAL_ETH_WritePHYRegister() + if(base->MACMIIAR & ETH_MACMIIAR_MB_Msk) { + // MDIO operation already in progress + return ErrCode::INVALID_USAGE; + } + + /* Give the value to the MII data register */ + base->MACMIIDR = data; + + uint32_t tmpreg = base->MACMIIAR; + + /* Prepare the MDIO Address Register value + - Set the PHY device address + - Set the PHY register address + - Set the write mode + - Set the MII Busy bit */ + tmpreg &= ~(ETH_MACMIIAR_MW_Msk | ETH_MACMIIAR_PA_Msk | ETH_MACMIIAR_MR_Msk); + tmpreg |= (devAddr << ETH_MACMIIAR_PA_Pos) | (regAddr << ETH_MACMIIAR_MR_Pos) | ETH_MACMIIAR_MB_Msk | ETH_MACMIIAR_MW_Msk; + base->MACMIIAR = tmpreg; + + Timer timeoutTimer; + timeoutTimer.start(); + while(timeoutTimer.elapsed_time() < MDIO_TRANSACTION_TIMEOUT && (base->MACMIIAR & ETH_MACMIIAR_MB_Msk)) {} + if(base->MACMIIAR & ETH_MACMIIAR_MB_Msk) { + // Transaction failed to complete within expected timeout + return ErrCode::TIMEOUT; + } + + tr_debug("MDIO write devAddr %" PRIu8 ", regAddr 0x%" PRIx8 " <- 0x%" PRIx16, devAddr, regAddr, data); + + return ErrCode::SUCCESS; +} + +PinName STM32EthMACv1::MACDriver::getPhyResetPin() { + return EthGetPhyResetPin(); +} + +CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::addMcastMAC(MACAddress mac) { + if(numPerfectFilterRegsUsed < NUM_PERFECT_FILTER_REGS) { + size_t perfFiltIdx = numPerfectFilterRegsUsed; + ++numPerfectFilterRegsUsed; + + tr_debug("Using perfect filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + writeMACAddress(mac, MAC_ADDR_PERF_FILTER_REGS[perfFiltIdx].first, MAC_ADDR_PERF_FILTER_REGS[perfFiltIdx].second); + } + else { + // Out of spaces in perfect filter, use hash filter instead + tr_debug("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + addHashFilterMAC(base, mac); + } + return ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::clearMcastFilter() { + // Reset perfect filter registers + for(auto regPair : MAC_ADDR_PERF_FILTER_REGS) { + *regPair.first = 0; + *regPair.second = 0; + } + numPerfectFilterRegsUsed = 0; + + // Reset hash filter + base->MACHTLR = 0; + base->MACHTHR = 0; + + return ErrCode::SUCCESS; +} + +void STM32EthMACv1::MACDriver::setPassAllMcast(bool pass) { + if(pass) + { + base->MACFFR |= ETH_MACFFR_PAM_Msk; + } + else + { + base->MACFFR &= ~ETH_MACFFR_PAM_Msk; + } +} + +void STM32EthMACv1::MACDriver::setPromiscuous(bool enable) { + if(enable) + { + base->MACFFR |= ETH_MACFFR_PM_Msk; + } + else + { + base->MACFFR &= ~ETH_MACFFR_PM_Msk; + } +} +} diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h new file mode 100644 index 00000000000..091fb631e31 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h @@ -0,0 +1,80 @@ +/* Copyright (c) 2025 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CompositeEMAC.h" + +namespace mbed +{ + +/** + * @brief EMAC implementation for STM32 MCUs with Ethernet IP v2 + */ +class STM32EthMACv1 : public CompositeEMAC +{ + class MACDriver : public CompositeEMAC::MACDriver { + ETH_TypeDef * const base; // Base address of Ethernet peripheral + + // Number of MAC address perfect filter registers used + size_t numPerfectFilterRegsUsed = 0; + + /** + * @brief Configures the Clock range of ETH MDIO interface. + * + * Copied from STM32CubeHAL. + * + * @param base Base address of Ethernet peripheral + */ + static void ETH_SetMDIOClockRange(ETH_TypeDef * const base); + + public: + explicit MACDriver(ETH_TypeDef * const base): + base(base) + {} + + ErrCode init() override; + + ErrCode deinit() override; + + ErrCode enable(LinkSpeed speed, Duplex duplex) override; + + ErrCode disable() override; + + void setOwnMACAddr(const MACAddress &ownAddress) override; + + ErrCode mdioRead(uint8_t devAddr, uint8_t regAddr, uint16_t &result) override; + + ErrCode mdioWrite(uint8_t devAddr, uint8_t regAddr, uint16_t data) override; + + PinName getPhyResetPin() override; + + ErrCode addMcastMAC(MACAddress mac) override; + + ErrCode clearMcastFilter() override; + + void setPassAllMcast(bool pass) override; + + void setPromiscuous(bool enable) override; + }; + +public: + // Interrupt callback + static void irqHandler(); +}; + + +} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index d8f3076be1d..3c8fe73cf07 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -15,11 +15,11 @@ */ #include "STM32EthMACv2.h" +#include "STM32EthMACCommon.h" #include "mbed_power_mgmt.h" #include "Timer.h" #include "mbed_error.h" -#include "MbedCRC.h" using namespace std::chrono_literals; @@ -31,10 +31,7 @@ extern "C" void EthDeinitPinmappings(); extern "C" PinName EthGetPhyResetPin(); namespace mbed { - void STM32EthMacV2::TxDMA::startDMA() { - // Flush Tx queue - base->MTLTQOMR |= ETH_MTLTQOMR_FTQ; - + void STM32EthMACv2::TxDMA::startDMA() { // Configure Tx descriptor ring base->DMACTDRLR = MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS - 1; // Ring size base->DMACTDLAR = reinterpret_cast(&txDescs[0]); // Ring base address @@ -48,12 +45,12 @@ namespace mbed { base->DMACSR = ETH_DMACSR_TPS; } - void STM32EthMacV2::TxDMA::stopDMA() { + void STM32EthMACv2::TxDMA::stopDMA() { // Disable Tx DMA base->DMACTCR &= ~ETH_DMACTCR_ST; } - bool STM32EthMacV2::TxDMA::isDMAReadableBuffer(uint8_t const *start, const size_t size) const + bool STM32EthMACv2::TxDMA::isDMAReadableBuffer(uint8_t const *start, const size_t size) const { // On STM32H7, the Ethernet DMA cannot access data in DTCM. So, if someone sends // a packet with a data pointer in DTCM (e.g. a stack allocated payload), everything @@ -68,7 +65,7 @@ namespace mbed { return true; } - void STM32EthMacV2::TxDMA::giveToDMA(const size_t descIdx, const bool firstDesc, const bool lastDesc) { + void STM32EthMACv2::TxDMA::giveToDMA(const size_t descIdx, const bool firstDesc, const bool lastDesc) { auto & desc = txDescs[descIdx]; // Note that we have to configure these every time as @@ -98,7 +95,7 @@ namespace mbed { base->DMACTDTPR = reinterpret_cast(&txDescs[nextDescIdx]); } - void STM32EthMacV2::RxDMA::startDMA() + void STM32EthMACv2::RxDMA::startDMA() { // Configure Rx buffer size. Per the datasheet and HAL code, we need to round this down to // the nearest multiple of 4. @@ -117,12 +114,12 @@ namespace mbed { base->DMACSR = ETH_DMACSR_RPS; } - void STM32EthMacV2::RxDMA::stopDMA() { + void STM32EthMACv2::RxDMA::stopDMA() { // Disable Rx DMA base->DMACTCR &= ~ETH_DMACRCR_SR; } - void STM32EthMacV2::RxDMA::returnDescriptor(const size_t descIdx, uint8_t * const buffer) { + void STM32EthMACv2::RxDMA::returnDescriptor(const size_t descIdx, uint8_t * const buffer) { auto & desc = rxDescs[descIdx]; // Clear out any bits previously set in the descriptor (from when the DMA gave it back to us) @@ -148,12 +145,12 @@ namespace mbed { base->DMACRDTPR = reinterpret_cast(&rxDescs[nextDescIdx]); } - size_t STM32EthMacV2::RxDMA::getTotalLen(const size_t firstDescIdx, const size_t lastDescIdx) { + size_t STM32EthMACv2::RxDMA::getTotalLen(const size_t firstDescIdx, const size_t lastDescIdx) { // Total length of the packet is in the last descriptor return rxDescs[lastDescIdx].formats.fromDMA.pktLength; } - void STM32EthMacV2::MACDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) + void STM32EthMACv2::MACDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) { uint32_t hclk; uint32_t tmpreg; @@ -203,46 +200,7 @@ namespace mbed { base->MACMDIOAR = (uint32_t)tmpreg; } - void STM32EthMacV2::MACDriver::writeMACAddress(const MACAddress &mac, volatile uint32_t *addrHighReg, - volatile uint32_t *addrLowReg) { - /* Set MAC addr bits 32 to 47 */ - *addrHighReg = (static_cast(mac[5]) << 8) | static_cast(mac[4]) | ETH_MACA1HR_AE_Msk; - /* Set MAC addr bits 0 to 31 */ - *addrLowReg = (static_cast(mac[3]) << 24) | (static_cast(mac[2]) << 16) | - (static_cast(mac[1]) << 8) | static_cast(mac[0]); - } - - void STM32EthMacV2::MACDriver::addHashFilterMAC(const MACAddress &mac) { - - uint32_t volatile * hashRegs[] = { - &base->MACHT0R, - &base->MACHT1R - }; - - // Note: as always, the datasheet description of how to do this CRC was vague and slightly wrong. - // This forum thread figured it out: https://community.st.com/t5/stm32-mcus-security/calculating-ethernet-multicast-filter-hash-value/td-p/416984 - // What the datasheet SHOULD say is: - // Compute the Ethernet CRC-32 of the MAC address, with initial value of 1s, final XOR of ones, and input reflection on but output reflection off - // Then, take the upper 6 bits and use that to index the hash table. - - mbed::MbedCRC crcCalc(0xFFFFFFFF, 0xFFFFFFFF, true, false); - tr_debug("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, - currMacAddr[0], currMacAddr[1], currMacAddr[2], - currMacAddr[3], currMacAddr[4], currMacAddr[5]); - - // Compute Ethernet CRC-32 of the MAC address - uint32_t crc; - crcCalc.compute(mac.data(), mac.size(), &crc); - - // Take upper 6 bits - uint32_t hashVal = crc >> 26; - - // Set correct bit in hash filter - *hashRegs[hashVal >> 5] |= (1 << (hashVal & 0x1F)); - } - - - CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::init() { + CompositeEMAC::ErrCode STM32EthMACv2::MACDriver::init() { sleep_manager_lock_deep_sleep(); // Note: Following code is based on HAL_Eth_Init() from the HAL @@ -274,7 +232,8 @@ namespace mbed { timeoutTimer.start(); while(timeoutTimer.elapsed_time() < ETH_SW_RESET_TIMEOUT && (base->DMAMR & ETH_DMAMR_SWR)) {} if(base->DMAMR & ETH_DMAMR_SWR) { - // Reset failed to complete within expected timeout + // Reset failed to complete within expected timeout. + // Note: This is usually because of a missing RMII clock from the PHY. return ErrCode::TIMEOUT; } @@ -318,7 +277,7 @@ namespace mbed { base->DMACCR |= wordsToSkip << ETH_DMACCR_DSL_Pos; // Set up interrupt handler - NVIC_SetVector(ETH_IRQn, reinterpret_cast(&STM32EthMacV2::irqHandler)); + NVIC_SetVector(ETH_IRQn, reinterpret_cast(&STM32EthMACv2::irqHandler)); HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); HAL_NVIC_EnableIRQ(ETH_IRQn); @@ -332,7 +291,7 @@ namespace mbed { return ErrCode::SUCCESS; } - CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::deinit() + CompositeEMAC::ErrCode STM32EthMACv2::MACDriver::deinit() { // Disable interrupt HAL_NVIC_DisableIRQ(ETH_IRQn); @@ -346,7 +305,7 @@ namespace mbed { return ErrCode::SUCCESS; } - CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::enable(LinkSpeed speed, Duplex duplex) + CompositeEMAC::ErrCode STM32EthMACv2::MACDriver::enable(LinkSpeed speed, Duplex duplex) { if(speed == LinkSpeed::LINK_1GBIT) { return ErrCode::INVALID_ARGUMENT; @@ -372,20 +331,22 @@ namespace mbed { return ErrCode::SUCCESS; } - CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::disable() + CompositeEMAC::ErrCode STM32EthMACv2::MACDriver::disable() { base->MACCR &= ~(ETH_MACCR_TE | ETH_MACCR_RE); + + // Get rid of any packets still in the transmit FIFO + base->MTLTQOMR |= ETH_MTLTQOMR_FTQ; + return ErrCode::SUCCESS; } - void STM32EthMacV2::MACDriver::setOwnMACAddr(const MACAddress &ownAddress) { + void STM32EthMACv2::MACDriver::setOwnMACAddr(const MACAddress &ownAddress) { // Set MAC address writeMACAddress(ownAddress, &base->MACA0HR, &base->MACA0LR); } - const auto MDIO_TRANSACTION_TIMEOUT = 1ms; // used by STMicro HAL - - CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::mdioRead(uint8_t devAddr, uint8_t regAddr, uint16_t &result) { + CompositeEMAC::ErrCode STM32EthMACv2::MACDriver::mdioRead(uint8_t devAddr, uint8_t regAddr, uint16_t &result) { // This code based on HAL_ETH_ReadPHYRegister() if(base->MACMDIOAR & ETH_MACMDIOAR_MB_Msk) { // MDIO operation already in progress @@ -419,7 +380,7 @@ namespace mbed { return ErrCode::SUCCESS; } - CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::mdioWrite(uint8_t devAddr, uint8_t regAddr, uint16_t data) { + CompositeEMAC::ErrCode STM32EthMACv2::MACDriver::mdioWrite(uint8_t devAddr, uint8_t regAddr, uint16_t data) { // This code based on HAL_ETH_WritePHYRegister() if(base->MACMDIOAR & ETH_MACMDIOAR_MB_Msk) { // MDIO operation already in progress @@ -453,18 +414,11 @@ namespace mbed { return ErrCode::SUCCESS; } - PinName STM32EthMacV2::MACDriver::getPhyResetPin() { + PinName STM32EthMACv2::MACDriver::getPhyResetPin() { return EthGetPhyResetPin(); } - inline constexpr size_t NUM_PERFECT_FILTER_REGS = 3; - std::pair MAC_ADDR_PERF_FILTER_REGS[NUM_PERFECT_FILTER_REGS] = { - {Ð->MACA1HR, Ð->MACA1LR}, - {Ð->MACA2HR, Ð->MACA2LR}, - {Ð->MACA3HR, Ð->MACA3LR} - }; - - CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::addMcastMAC(MACAddress mac) { + CompositeEMAC::ErrCode STM32EthMACv2::MACDriver::addMcastMAC(MACAddress mac) { if(numPerfectFilterRegsUsed < NUM_PERFECT_FILTER_REGS) { size_t perfFiltIdx = numPerfectFilterRegsUsed; ++numPerfectFilterRegsUsed; @@ -475,12 +429,14 @@ namespace mbed { } else { // Out of spaces in perfect filter, use hash filter instead - addHashFilterMAC(mac); + tr_debug("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + addHashFilterMAC(base, mac); } return ErrCode::SUCCESS; } - CompositeEMAC::ErrCode STM32EthMacV2::MACDriver::clearMcastFilter() { + CompositeEMAC::ErrCode STM32EthMACv2::MACDriver::clearMcastFilter() { // Reset perfect filter registers for(auto regPair : MAC_ADDR_PERF_FILTER_REGS) { *regPair.first = 0; @@ -495,7 +451,7 @@ namespace mbed { return ErrCode::SUCCESS; } - void STM32EthMacV2::MACDriver::setPassAllMcast(bool pass) { + void STM32EthMACv2::MACDriver::setPassAllMcast(bool pass) { if(pass) { base->MACPFR |= ETH_MACPFR_PM; @@ -506,7 +462,7 @@ namespace mbed { } } - void STM32EthMacV2::MACDriver::setPromiscuous(bool enable) { + void STM32EthMACv2::MACDriver::setPromiscuous(bool enable) { if(enable) { base->MACPFR |= ETH_MACPFR_PR; @@ -517,9 +473,9 @@ namespace mbed { } } - STM32EthMacV2 * STM32EthMacV2::instance = nullptr; + STM32EthMACv2 * STM32EthMACv2::instance = nullptr; - STM32EthMacV2::STM32EthMacV2(): + STM32EthMACv2::STM32EthMACv2(): CompositeEMAC(txDMA, rxDMA, macDriver), base(ETH), txDMA(base), @@ -529,7 +485,7 @@ namespace mbed { instance = this; } - void STM32EthMacV2::irqHandler() + void STM32EthMACv2::irqHandler() { const auto emacInst = instance; uint32_t dma_flag = emacInst->base->DMACSR; @@ -564,6 +520,6 @@ namespace mbed { // Provide default EMAC driver MBED_WEAK EMAC &EMAC::get_default_instance() { - static mbed::STM32EthMacV2 emac; + static mbed::STM32EthMACv2 emac; return emac; } diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h index 9f534d86d98..a8cf578f7f9 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -25,7 +25,7 @@ namespace mbed { /** * @brief EMAC implementation for STM32 MCUs with Ethernet IP v2 */ - class STM32EthMacV2 : public CompositeEMAC { + class STM32EthMACv2 : public CompositeEMAC { class TxDMA : public GenericTxDMALoop { @@ -82,12 +82,6 @@ namespace mbed { */ static void ETH_SetMDIOClockRange(ETH_TypeDef * const base); - /// Write a MAC address into the given registers with the needed encoding - void writeMACAddress(const MACAddress & mac, volatile uint32_t *addrHighReg, volatile uint32_t *addrLowReg); - - /// Add a MAC address to the multicast hash filter. - void addHashFilterMAC(const MACAddress & mac); - public: explicit MACDriver(ETH_TypeDef * const base): base(base) @@ -120,17 +114,17 @@ namespace mbed { // Pointer to global instance, for ISR to use. // TODO if we support more than 1 EMAC per MCU, this will need to be an array - static STM32EthMacV2 * instance; + static STM32EthMACv2 * instance; ETH_TypeDef * const base; // Base address of Ethernet peripheral // Components of the ethernet MAC TxDMA txDMA; RxDMA rxDMA; - STM32EthMacV2::MACDriver macDriver; + STM32EthMACv2::MACDriver macDriver; public: - STM32EthMacV2(); + STM32EthMACv2(); // Interrupt callback static void irqHandler(); diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/stm32xx_emac_config.h b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/stm32xx_emac_config.h deleted file mode 100644 index c4fe5e7a013..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/stm32xx_emac_config.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef STM32XX_EMAC_CONFIG_H__ -#define STM32XX_EMAC_CONFIG_H__ - -#define ETH_IP_VERSION_V1 - -#endif // #define STM32XX_EMAC_CONFIG_H__ diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/stm32xx_emac_config.h b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/stm32xx_emac_config.h deleted file mode 100644 index c4fe5e7a013..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/stm32xx_emac_config.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef STM32XX_EMAC_CONFIG_H__ -#define STM32XX_EMAC_CONFIG_H__ - -#define ETH_IP_VERSION_V1 - -#endif // #define STM32XX_EMAC_CONFIG_H__ diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F767ZI/stm32f7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F767ZI/stm32f7_eth_init.c index 70eef691701..d31ab3187aa 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F767ZI/stm32f7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F767ZI/stm32f7_eth_init.c @@ -28,103 +28,82 @@ * 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. */ -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT #include "stm32f7xx_hal.h" -#include "platform/mbed_critical.h" +#include "PinNames.h" -/** - * Override HAL Eth Init function - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { GPIO_InitTypeDef GPIO_InitStructure; - if (heth->Instance == ETH) { - /* Disable DCache for STM32F7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); - - /* Enable GPIOs clocks */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - /* Configure PA1, PA2 and PA7 */ - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; - GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; - GPIO_InitStructure.Pull = GPIO_NOPULL; - GPIO_InitStructure.Alternate = GPIO_AF11_ETH; - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + /* Enable GPIOs clocks */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); - /* Configure PB13 */ - GPIO_InitStructure.Pin = GPIO_PIN_13; - HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + /* Configure PA1, PA2 and PA7 */ + GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Alternate = GPIO_AF11_ETH; + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Configure PC1, PC4 and PC5 */ - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Configure PB13 */ + GPIO_InitStructure.Pin = GPIO_PIN_13; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); - /* Configure PG11 and PG13 */ - GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; - HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); + /* Configure PC1, PC4 and PC5 */ + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - /* Enable the Ethernet global Interrupt */ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); + /* Configure PG11 and PG13 */ + GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; + HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); - /* Enable ETHERNET clock */ - __HAL_RCC_ETH_CLK_ENABLE(); - } + /* Enable ETHERNET clock */ + __HAL_RCC_ETH_CLK_ENABLE(); } -/** - * Override HAL Eth DeInit function - */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH_CLK_DISABLE(); + /* Peripheral clock disable */ + __HAL_RCC_ETH_CLK_DISABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); - HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); - HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); - HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); - - /* Disable the Ethernet global Interrupt */ - NVIC_DisableIRQ(ETH_IRQn); - } + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); + HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); + HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); } -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board +} -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/stm32xx_emac_config.h b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/stm32xx_emac_config.h deleted file mode 100644 index c4fe5e7a013..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/stm32xx_emac_config.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef STM32XX_EMAC_CONFIG_H__ -#define STM32XX_EMAC_CONFIG_H__ - -#define ETH_IP_VERSION_V1 - -#endif // #define STM32XX_EMAC_CONFIG_H__ diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/stm32xx_emac_config.h b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/stm32xx_emac_config.h deleted file mode 100644 index cd943ccc85b..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32H7/stm32xx_emac_config.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef STM32XX_EMAC_CONFIG_H__ -#define STM32XX_EMAC_CONFIG_H__ - -#define ETH_IP_VERSION_V2 - -#endif // #define STM32XX_EMAC_CONFIG_H__ diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index 7190eb215c3..e6631b418c0 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -101,6 +101,12 @@ class CompositeEMAC : public EMAC * @brief Initialize the MAC, map pins, and prepare it to send and receive packets. * It should not be enabled yet. * + * Implementations of this method should: + * - Lock deep sleep (unless the MAC functions in deep sleep) + * - Enable clock to the MAC + * - Mux all MAC and PHY pins appropriately + * - Configure MAC registers for operation (but NOT enable tx or rx, yet) + * * @return Error code or SUCCESS */ virtual ErrCode init() = 0; @@ -195,7 +201,9 @@ class CompositeEMAC : public EMAC /** * @brief Set whether the MAC passes all multicast traffic up to the application. * - * CompositeEMAC will ensure this is called only after init(). + * CompositeEMAC will ensure this is called only after init(). It will call this either if + * the network stack requests it, or if it can no longer track the mcast addresses that have + * been added and wants to fall back to enabling all multicasts. * * @param pass True to pass all mcasts, false otherwise */ From 0e78241d49549840d5771a7c6afd6ac05aac1e03 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 23 Feb 2025 18:31:20 -0800 Subject: [PATCH 23/47] Working on v1 DMA --- .../emac/TARGET_STM/STM32EthMACCommon.h | 2 +- .../drivers/emac/TARGET_STM/STM32EthMACv1.cpp | 133 ++++++++++++++- .../drivers/emac/TARGET_STM/STM32EthMACv1.h | 59 +++++++ .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 60 ++++++- .../drivers/emac/TARGET_STM/STM32EthMACv2.h | 26 ++- .../emac/TARGET_STM/STM32EthV1Descriptors.h | 154 ++++++++++++++++++ .../emac/TARGET_STM/STM32EthV2Descriptors.h | 38 +---- .../drivers/emac/include/CompositeEMAC.h | 4 + .../drivers/emac/include/GenericEthDMA.h | 122 +++++++------- targets/cmsis_mcu_descriptions.json5 | 20 ++- 10 files changed, 507 insertions(+), 111 deletions(-) create mode 100644 connectivity/drivers/emac/TARGET_STM/STM32EthV1Descriptors.h diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACCommon.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACCommon.h index d327094a168..28d9e6b3e4c 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACCommon.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACCommon.h @@ -21,7 +21,7 @@ #include "MbedCRC.h" // Figure out the Ethernet IP version in use -#ifdef TARGET_STM32H5 || TARGET_STM32H7 +#if defined(TARGET_STM32H5) || defined(TARGET_STM32H7) #define ETH_IP_VERSION_V2 #else #define ETH_IP_VERSION_V1 diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp index af49bd469fb..5f5958d04f0 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp @@ -20,6 +20,7 @@ #include #include #include +#include "mbed_error.h" #define TRACE_GROUP "STEMACv1" @@ -31,7 +32,129 @@ extern "C" void EthDeinitPinmappings(); extern "C" PinName EthGetPhyResetPin(); namespace mbed { - void STM32EthMACv1::MACDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) { +void STM32EthMACv1::TxDMA::startDMA() { + // Zero all the Tx descriptors + memset(txDescs.data(), 0, sizeof(stm32_ethv1::TxDescriptor) * TX_NUM_DESCS); + + // Set the end-of-ring bit on the last descriptor + txDescs[TX_NUM_DESCS - 1].endOfRing = true; + + // Set descriptor list address register + base->DMARDLAR = reinterpret_cast(&txDescs[0]); + + // Start Tx DMA + base->DMAOMR |= ETH_DMAOMR_ST_Msk; +} + +void STM32EthMACv1::TxDMA::stopDMA() { + base->DMAOMR &= ~ETH_DMAOMR_ST_Msk; +} + +#if __DCACHE_PRESENT +void STM32EthMACv1::TxDMA::cacheInvalidateDescriptor(const size_t descIdx) { + SCB_InvalidateDCache_by_Addr(&txDescs[descIdx], sizeof(stm32_ethv1::TxDescriptor)); +} + +bool STM32EthMACv1::TxDMA::descOwnedByDMA(size_t descIdx) { + return txDescs[descIdx].dmaOwn; +} + +bool STM32EthMACv1::TxDMA::isDMAReadableBuffer(uint8_t const *start, size_t size) const { +#ifdef TARGET_STM32F7 + if(reinterpret_cast(start) < 1024*16) { + // In ITCM memory, not accessible by DMA. Note that ITCM is not included in the CMSIS memory map (yet). + return false; + } +#endif + +#if TARGET_STM32F2 || TARGET_STM32F4 + // On STM32F2 and F2, ethernet DMA cannot access the flash memory. + if(reinterpret_cast(start) >= MBED_ROM_START || + reinterpret_cast(start + size) <= MBED_ROM_START + MBED_ROM_SIZE) + { + return false; + } +#endif + + return true; +} + +void STM32EthMACv1::TxDMA::giveToDMA(size_t descIdx, uint8_t const *buffer, size_t len, bool firstDesc, bool lastDesc) { + +} +#endif + +void STM32EthMACv1::RxDMA::startDMA() { + + // Rx buffer size must be a multiple of 4, per the descriptor definition + MBED_ASSERT(rxPoolPayloadSize % sizeof(uint32_t) == 0); + + // Zero all the Rx descriptors + memset(rxDescs.data(), 0, sizeof(stm32_ethv1::RxDescriptor) * RX_NUM_DESCS); + + // Set the end-of-ring bit on the last descriptor + rxDescs[RX_NUM_DESCS - 1].endOfRing = true; + + // Set descriptor list address register + base->DMARDLAR = reinterpret_cast(&rxDescs[0]); + + // Start Rx DMA + base->DMAOMR |= ETH_DMAOMR_SR_Msk; +} + +void STM32EthMACv1::RxDMA::stopDMA() { + base->DMAOMR &= ~ETH_DMAOMR_SR_Msk; +} + +#if __DCACHE_PRESENT +void STM32EthMACv1::RxDMA::cacheInvalidateDescriptor(const size_t descIdx) { + SCB_InvalidateDCache_by_Addr(&rxDescs[descIdx], sizeof(stm32_ethv1::RxDescriptor)); +} +#endif + +bool STM32EthMACv1::RxDMA::descOwnedByDMA(const size_t descIdx) { + return rxDescs[descIdx].dmaOwn; +} + +bool STM32EthMACv1::RxDMA::isFirstDesc(const size_t descIdx) { + return rxDescs[descIdx].firstDescriptor; +} + +bool STM32EthMACv1::RxDMA::isLastDesc(const size_t descIdx) { + return rxDescs[descIdx].lastDescriptor; +} + +bool STM32EthMACv1::RxDMA::isErrorDesc(const size_t descIdx) { + return rxDescs[descIdx].errSummary; +} + +void STM32EthMACv1::RxDMA::returnDescriptor(const size_t descIdx, uint8_t *buffer) +{ + // Configure descriptor + rxDescs[descIdx].buffer1 = buffer; + rxDescs[descIdx].buffer1Size = rxPoolPayloadSize; + rxDescs[descIdx].dmaOwn = true; + + // Flush back to main memory +#ifdef __DCACHE_PRESENT + SCB_CleanDCache_by_Addr(&rxDescs[descIdx], sizeof(stm32_ethv1::RxDescriptor)); +#else + __DMB(); // Make sure descriptor is written before the below lines +#endif + + // Clear buffer unavailable flag (I think this is for information only though) + base->DMASR = ETH_DMASR_RBUS_Msk; + + // Demand (good sir!) an Rx descriptor poll + base->DMARPDR = 1; +} + +size_t STM32EthMACv1::RxDMA::getTotalLen(const size_t firstDescIdx, const size_t lastDescIdx) { + // Total length of the packet is in the last descriptor + return rxDescs[lastDescIdx].frameLen; +} + +void STM32EthMACv1::MACDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) { /* Get the ETHERNET MACMIIAR value */ uint32_t tempreg = base->MACMIIAR; /* Clear CSR Clock Range CR[2:0] bits */ @@ -61,11 +184,17 @@ namespace mbed { /* CSR Clock Range between 100-150 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div62; } - else /* ((hclk >= 150000000)&&(hclk <= 216000000)) */ +#ifdef ETH_MACMIIAR_CR_Div102 + else if((hclk >= 150000000)&&(hclk <= 216000000)) { /* CSR Clock Range between 150-216 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div102; } +#endif + else { + MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_ETHERNET, EIO), \ + "STM32 EMAC v1: Unsupported HCLK range\n"); + } /* Write to ETHERNET MAC MIIAR: Configure the ETHERNET CSR Clock Range */ base->MACMIIAR = (uint32_t)tempreg; diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h index 091fb631e31..d5e587fea7d 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h @@ -17,6 +17,9 @@ #pragma once #include "CompositeEMAC.h" +#include "STM32EthV1Descriptors.h" +#include "CacheAlignedBuffer.h" +#include "GenericEthDMA.h" namespace mbed { @@ -26,6 +29,62 @@ namespace mbed */ class STM32EthMACv1 : public CompositeEMAC { + class TxDMA : public GenericTxDMALoop + { + protected: + ETH_TypeDef * const base; // Base address of Ethernet peripheral + StaticCacheAlignedBuffer txDescs; // Tx descriptors + + void startDMA() override; + + void stopDMA() override; + +#if __DCACHE_PRESENT + void cacheInvalidateDescriptor(size_t descIdx) override; +#endif + + bool descOwnedByDMA(size_t descIdx) override; + + bool isDMAReadableBuffer(uint8_t const * start, size_t size) const override; + + void giveToDMA(size_t descIdx, uint8_t const * buffer, size_t len, bool firstDesc, bool lastDesc) override; + public: + explicit TxDMA(ETH_TypeDef * const base): + base(base) + {} + }; + + class RxDMA : public GenericRxDMALoop { + protected: + ETH_TypeDef * const base; // Base address of Ethernet peripheral + StaticCacheAlignedBuffer rxDescs; // Rx descriptors + + void startDMA() override; + + void stopDMA() override; + +#if __DCACHE_PRESENT + void cacheInvalidateDescriptor(size_t descIdx) override; +#endif + + bool descOwnedByDMA(size_t descIdx) override; + + bool isFirstDesc(size_t descIdx) override; + + bool isLastDesc(size_t descIdx) override; + + bool isErrorDesc(size_t descIdx) override; + + void returnDescriptor(size_t descIdx, uint8_t * buffer) override; + + size_t getTotalLen(size_t firstDescIdx, size_t lastDescIdx) override; + + public: + explicit RxDMA(ETH_TypeDef * const base): + base(base) + {} + }; + class MACDriver : public CompositeEMAC::MACDriver { ETH_TypeDef * const base; // Base address of Ethernet peripheral diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index 3c8fe73cf07..c64bee16a11 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -50,24 +50,48 @@ namespace mbed { base->DMACTCR &= ~ETH_DMACTCR_ST; } +#if __DCACHE_PRESENT + void STM32EthMACv2::TxDMA::cacheInvalidateDescriptor(size_t descIdx) { + SCB_InvalidateDCache_by_Addr(&txDescs[descIdx], sizeof(stm32_ethv2::EthTxDescriptor)); + } +#endif + + bool STM32EthMACv2::TxDMA::descOwnedByDMA(size_t descIdx) { + return txDescs[descIdx].formats.fromDMA.dmaOwn; + } + bool STM32EthMACv2::TxDMA::isDMAReadableBuffer(uint8_t const *start, const size_t size) const { +#ifdef TARGET_STM32H7 // On STM32H7, the Ethernet DMA cannot access data in DTCM. So, if someone sends // a packet with a data pointer in DTCM (e.g. a stack allocated payload), everything // will break if we don't copy it first. -#ifdef MBED_RAM_BANK_SRAM_DTC_START - if(reinterpret_cast(start) >= MBED_RAM_BANK_SRAM_DTC_START && + if(reinterpret_cast(start) >= MBED_RAM_BANK_SRAM_DTC_START || reinterpret_cast(start + size) <= MBED_RAM_BANK_SRAM_DTC_START + MBED_RAM_BANK_SRAM_DTC_SIZE) { return false; } #endif + +#ifdef TARGET_STM32H5 + // On STM32H7, the Ethernet DMA cannot access data in backup SRAM. + if(reinterpret_cast(start) >= MBED_RAM_BANK_SRAM_BKUP_START || + reinterpret_cast(start + size) <= MBED_RAM_BANK_SRAM_BKUP_START + MBED_RAM_BANK_SRAM_BKUP_SIZE) + { + return false; + } +#endif + return true; } - void STM32EthMACv2::TxDMA::giveToDMA(const size_t descIdx, const bool firstDesc, const bool lastDesc) { + void STM32EthMACv2::TxDMA::giveToDMA(const size_t descIdx, uint8_t const * const buffer, const size_t len, const bool firstDesc, const bool lastDesc) { auto & desc = txDescs[descIdx]; + // Set buffer + desc.formats.toDMA.buffer1Addr = buffer; + desc.formats.toDMA.buffer1Len = len; + // Note that we have to configure these every time as // they get wiped away when the DMA gives back the descriptor desc.formats.toDMA._reserved = 0; @@ -84,9 +108,11 @@ namespace mbed { desc.formats.toDMA.timestampEnable = false; desc.formats.toDMA.dmaOwn = true; -#if __DCACHE_PRESENT // Write descriptor back to main memory +#if __DCACHE_PRESENT SCB_CleanDCache_by_Addr(&desc, sizeof(stm32_ethv2::EthTxDescriptor)); +#else + __DMB(); // Make sure descriptor is written before the below lines #endif // Move tail pointer register to point to the descriptor after this descriptor. @@ -119,6 +145,30 @@ namespace mbed { base->DMACTCR &= ~ETH_DMACRCR_SR; } +#if __DCACHE_PRESENT + void STM32EthMACv2::RxDMA::cacheInvalidateDescriptor(size_t descIdx) { + SCB_InvalidateDCache_by_Addr(&rxDescs[descIdx], sizeof(stm32_ethv2::EthRxDescriptor)); + } +#endif + + bool STM32EthMACv2::RxDMA::descOwnedByDMA(size_t descIdx) { + return rxDescs[descIdx].formats.toDMA.dmaOwn; + } + + bool STM32EthMACv2::RxDMA::isFirstDesc(size_t descIdx) { + return rxDescs[descIdx].formats.fromDMA.firstDescriptor; + } + + bool STM32EthMACv2::RxDMA::isLastDesc(size_t descIdx) { + return rxDescs[descIdx].formats.fromDMA.lastDescriptor; + } + + bool STM32EthMACv2::RxDMA::isErrorDesc(size_t descIdx) { + // For right now, we treat context descriptors equivalent to error descs. + // Currently we do not use them, so if we did get one, we just want to get rid of it. + return rxDescs[descIdx].formats.fromDMA.errorSummary || rxDescs[descIdx].formats.fromDMA.context; + } + void STM32EthMACv2::RxDMA::returnDescriptor(const size_t descIdx, uint8_t * const buffer) { auto & desc = rxDescs[descIdx]; @@ -136,6 +186,8 @@ namespace mbed { #if __DCACHE_PRESENT // Flush to main memory SCB_CleanDCache_by_Addr(&desc, __SCB_DCACHE_LINE_SIZE); +#else + __DMB(); // Make sure descriptor is written before the below lines #endif // Update tail ptr to issue "rx poll demand" and mark this descriptor for receive. diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h index a8cf578f7f9..6fc0802738a 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -27,7 +27,7 @@ namespace mbed { */ class STM32EthMACv2 : public CompositeEMAC { - class TxDMA : public GenericTxDMALoop + class TxDMA : public GenericTxDMALoop { protected: ETH_TypeDef * const base; // Base address of Ethernet peripheral @@ -37,17 +37,22 @@ namespace mbed { void stopDMA() override; +#if __DCACHE_PRESENT + void cacheInvalidateDescriptor(size_t descIdx) override; +#endif + + bool descOwnedByDMA(size_t descIdx) override; + bool isDMAReadableBuffer(uint8_t const * start, size_t size) const override; - void giveToDMA(size_t descIdx, bool firstDesc, bool lastDesc) override; + void giveToDMA(size_t descIdx, uint8_t const * buffer, size_t len, bool firstDesc, bool lastDesc) override; public: explicit TxDMA(ETH_TypeDef * const base): - GenericTxDMALoop(txDescs), base(base) {} }; - class RxDMA : public GenericRxDMALoop { + class RxDMA : public GenericRxDMALoop { protected: ETH_TypeDef * const base; // Base address of Ethernet peripheral StaticCacheAlignedBuffer rxDescs; // Rx descriptors @@ -56,13 +61,24 @@ namespace mbed { void stopDMA() override; +#if __DCACHE_PRESENT + void cacheInvalidateDescriptor(size_t descIdx) override; +#endif + + bool descOwnedByDMA(size_t descIdx) override; + + bool isFirstDesc(size_t descIdx) override; + + bool isLastDesc(size_t descIdx) override; + + bool isErrorDesc(size_t descIdx) override; + void returnDescriptor(size_t descIdx, uint8_t *buffer) override; size_t getTotalLen(size_t firstDescIdx, size_t lastDescIdx) override; public: explicit RxDMA(ETH_TypeDef * const base): - GenericRxDMALoop(rxDescs), base(base) {} }; diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthV1Descriptors.h b/connectivity/drivers/emac/TARGET_STM/STM32EthV1Descriptors.h new file mode 100644 index 00000000000..64a75dd7735 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthV1Descriptors.h @@ -0,0 +1,154 @@ +/* Copyright (c) 2025 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STM32ETHV1DESCRIPTORS_H +#define STM32ETHV1DESCRIPTORS_H + +#include + +namespace mbed { +namespace stm32_ethv1 { + /// Struct for an enhanced Tx descriptor in the STM32 Eth IP v1. + /// From STM32F7 Reference Manual, section 42.6.7. + /// Note that even when there is no CPU cache, descriptors must be 32 bit aligned. + struct __attribute__ ((packed)) alignas(uint32_t) TxDescriptor + { + // TDES0 fields + bool deferred : 1; + bool underflowErr : 1; + bool excessiveDeferral : 1; + uint8_t collisionCount : 4; + bool vlanFrame : 1; + bool excessiveCollision : 1; + bool lateCollision : 1; + bool noCarrier : 1; + bool lossOfCarrier : 1; + bool ipPayloadErr : 1; + bool frameFlushed : 1; + bool jabberTimeout : 1; + bool errorSummary : 1; + bool ipHeaderError : 1; + bool txTimestampStatus : 1; + uint8_t : 2; // reserved + bool secAddressChained : 1; + bool endOfRing : 1; + uint8_t checksumInsCtrl : 2; + uint8_t : 1; // reserved + bool txTimestampEn : 1; + bool disablePad : 1; + bool disableCRC : 1; + bool firstSegment : 1; + bool lastSegment : 1; + bool intrOnComplete : 1; + bool dmaOwn : 1; + + // TDES1 fields + uint16_t buffer1Size : 13; + uint8_t : 3; + uint16_t buffer2Size : 13; + uint8_t : 3; + + // TDES2 fields + uint8_t const * buffer1; + + // TDES3 fields + uint8_t const * buffer2OrNextDesc; + + // TDES4 fields + uint32_t : 32; + + // TDES5 fields + uint32_t : 32; + + // TDES6 fields + uint32_t timestampLow; + + // TDES7 fields + uint32_t timestampHigh; + }; + + /// Struct for an enhanced Rx descriptor in the STM32 Eth IP v1. + /// From STM32F7 Reference Manual, section 42.6.8 + /// Note that even when there is no CPU cache, descriptors must be 32 bit aligned. + struct __attribute__ ((packed)) alignas(uint32_t) RxDescriptor + { + // RDES0 fields + bool extStatusAvail : 1; + bool crcErr : 1; + bool dribbleErr : 1; + bool rxErr : 1; + bool rxWatchdogTimeout : 1; + bool isEthernetFrame : 1; + bool lateCollision : 1; + bool timestampValid : 1; + bool lastDescriptor : 1; + bool firstDescriptor : 1; + bool hasVLANTag : 1; + bool overflowErr : 1; + bool lengthErr : 1; + bool saFilterFail : 1; + bool frameTruncated : 1; + bool errSummary : 1; + uint16_t frameLen : 14; + bool daFilterFail : 1; + bool dmaOwn : 1; + + // RDES1 fields + uint16_t buffer1Size : 13; + bool : 1; + bool secAddressChained : 1; + bool endOfRing : 1; + uint16_t buffer2Size : 13; + uint8_t : 2; + bool disableInterruptOnComplete : 1; + + // RDES2 fields + uint8_t const * buffer1; + + // RDES3 fields + uint8_t const * buffer2OrNextDesc; + + // RDES4 fields + uint8_t ipPayloadType : 3; + bool ipHeaderErr : 1; + bool ipPayloadErr : 1; + bool ipChksumBypassed : 1; + bool ipv4Pkt : 1; + bool ipv6Pkt : 1; + uint8_t ptpMsgType : 4; + bool ptpLayer2 : 1; + bool ptpv2: 1; + uint32_t : 18; + + // RDES5 fields + uint32_t : 32; + + // RDES6 fields + uint32_t timestampLow; + + // RDES7 fields + uint32_t timestampHigh; + }; + +#if __DCACHE_PRESENT + static_assert(sizeof(RxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Rx descriptor size must equal cache line size"); + static_assert(sizeof(TxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Tx descriptor size must equal cache line size"); +#endif + +} +} + +#endif //STM32ETHV1DESCRIPTORS_H diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h b/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h index d951e4cb027..e8b94f5b055 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthV2Descriptors.h @@ -30,8 +30,8 @@ namespace mbed { // Note: Datasheet calls this the "read format" which is just nuts... struct __attribute__((packed)) EthTxDescriptorToDMAFmt { - void const * buffer1Addr; - void const * buffer2Addr; + uint8_t const * buffer1Addr; + uint8_t const * buffer2Addr; // TDES2 fields uint16_t buffer1Len : 14; uint8_t vlanTagCtrl : 2; @@ -93,16 +93,6 @@ namespace mbed { #if __DCACHE_PRESENT uint8_t _padding[__SCB_DCACHE_LINE_SIZE - sizeof(decltype(formats))]; #endif - - bool ownedByDMA() const { - return formats.toDMA.dmaOwn; - } - - void setBuffer(uint8_t const * addr, size_t len) - { - formats.toDMA.buffer1Addr = addr; - formats.toDMA.buffer1Len = len; - } }; #if __DCACHE_PRESENT static_assert(sizeof(EthTxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Tx descriptor size must equal cache line size"); @@ -114,9 +104,9 @@ namespace mbed { // This is called the "read format" in the datasheet. struct __attribute__((packed)) EthRxDescriptorToDMAFmt { - void * buffer1Addr; + uint8_t * buffer1Addr; uint32_t _reserved0; - void * buffer2Addr; + uint8_t * buffer2Addr; uint32_t _reserved1: 24; bool buffer1Valid: 1; bool buffer2Valid: 1; @@ -190,28 +180,10 @@ namespace mbed { #if __DCACHE_PRESENT uint8_t _padding[__SCB_DCACHE_LINE_SIZE - sizeof(decltype(formats))]; #endif - - bool ownedByDMA() const { - return formats.fromDMA.dmaOwn; - } - - bool isErrorDesc() const { - // For right now, we treat context descriptors equivalent to error descs. - // Currently we do not use them, so if we did get one, we just want to get rid of it. - return formats.fromDMA.errorSummary || formats.fromDMA.context; - } - - bool isFirstDesc() const { - return formats.fromDMA.firstDescriptor; - } - - bool isLastDesc() const { - return formats.fromDMA.lastDescriptor; - } }; #if __DCACHE_PRESENT - static_assert(sizeof(EthRxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Tx descriptor size must equal cache line size"); + static_assert(sizeof(EthRxDescriptor) == __SCB_DCACHE_LINE_SIZE, "Rx descriptor size must equal cache line size"); #endif diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index e6631b418c0..cc2d6f60d0d 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -277,6 +277,8 @@ class CompositeEMAC : public EMAC EMACMemoryManager * memory_manager = nullptr; public: + virtual ~TxDMA() = default; + /// Set the mem manager of this DMA ring. Will be called by CompositeEMAC before init(). void setMemoryManager(EMACMemoryManager * memory_manager) { this->memory_manager = memory_manager; } @@ -311,6 +313,8 @@ class CompositeEMAC : public EMAC EMACMemoryManager * memory_manager = nullptr; public: + virtual ~RxDMA() = default; + /// Set the mem manager of this DMA ring. Will be called by CompositeEMAC before init(). void setMemoryManager(EMACMemoryManager * memory_manager) { this->memory_manager = memory_manager; } diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 46ceae48492..35ef0c8671f 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -32,17 +32,12 @@ namespace mbed { * * This implementation of Tx DMA should work for the large majority of embedded MCUs that use a DMA ring-based * ethernet MAC. - * - * @tparam DescriptorT Type representing an Ethernet descriptor. - * - * @note If the MCU has a D-cache, then \c DescriptorT must be padded to an exact number of cache lines in size! */ - template class GenericTxDMALoop : public CompositeEMAC::TxDMA { protected: - /// Tx descriptor array. It's up to the subclass to allocate these in the correct location. - CacheAlignedBuffer & txDescs; + /// Number of entries in the Tx descriptor ring + static constexpr size_t TX_NUM_DESCS = MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; /// Pointer to first memory buffer in the chain associated with descriptor n. /// The buffer address shall only be set for the *last* descriptor, so that the entire chain is freed @@ -67,29 +62,28 @@ namespace mbed { /// Stop the DMA running. This is done after MAC transmit & recieve are disabled. virtual void stopDMA() = 0; +#if __DCACHE_PRESENT + /// Invalidate cache for the descriptor at the given index so it gets reloaded from main memory + virtual void cacheInvalidateDescriptor(size_t descIdx) = 0; +#endif + + /// Is the given descriptor owned by DMA? + /// Note that the descriptor will already have been invalidated in cache if needed. + virtual bool descOwnedByDMA(size_t descIdx) = 0; + /// Get whether the given buffer is in a memory region readable by the Ethernet DMA. /// If this returns false for a buffer being transmitted, the buffer will be copied into a new /// heap-allocated buffer. virtual bool isDMAReadableBuffer(uint8_t const * start, size_t size) const = 0; - /// Give the descriptor at the given index to DMA to be transmitted. This is called after - /// the buffer has been set. + /// Give the descriptor at the given index to DMA to be transmitted with the given buffer. /// Note: if the descriptor needs to be flushed from CPU cache, you need to do that /// at the correct point in the implementation of this method! /// Also, if the DMA ran out of data to transmit, you may need to do a "poke"/"wake" operation /// to tell it to start running again. - virtual void giveToDMA(size_t descIdx, bool firstDesc, bool lastDesc) = 0; + virtual void giveToDMA(size_t descIdx, uint8_t const * buffer, size_t len, bool firstDesc, bool lastDesc) = 0; public: - /** - * @brief Construct GenericTxDMALoop - * @param txDescs Tx descriptor buffer, containing exactly MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS - * descriptors. Subclass must allocate this with the correct configuration and location. - */ - GenericTxDMALoop(CacheAlignedBuffer & txDescs): - txDescs(txDescs) - {} - CompositeEMAC::ErrCode init() override { // At the start, we own all the descriptors txDescsOwnedByApplication = MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; @@ -132,13 +126,12 @@ namespace mbed { break; } - auto &currDesc = txDescs[txReclaimIndex]; #if __DCACHE_PRESENT - SCB_InvalidateDCache_by_Addr(&currDesc, sizeof(DescriptorT)); + cacheInvalidateDescriptor(txReclaimIndex); #endif - if (currDesc.ownedByDMA()) { + if (descOwnedByDMA(txReclaimIndex)) { // This desc is owned by the DMA, so we have reached the part of the ring buffer // that is still being transmitted. // Done for now! @@ -199,7 +192,7 @@ namespace mbed { } } - tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", + tr_info("Transmitting packet of length %lu in %zu buffers and %zu descs\n", memory_manager->get_total_len(buf), memory_manager->count_buffers(buf), neededDescs); // Step 2: Copy packet if needed @@ -236,10 +229,6 @@ namespace mbed { net_stack_mem_buf_t * currBuf = buf; for(size_t descCount = 0; descCount < neededDescs; descCount++) { - auto & currDesc = txDescs[txSendIndex]; - - // Set buffer 1 - currDesc.setBuffer(static_cast(memory_manager->get_ptr(currBuf)), memory_manager->get_len(currBuf)); #if __DCACHE_PRESENT // Write buffer back to main memory SCB_CleanDCache_by_Addr(memory_manager->get_ptr(currBuf), memory_manager->get_len(currBuf)); @@ -247,7 +236,6 @@ namespace mbed { // Move to next buffer currBuf = memory_manager->get_next(currBuf); - if(currBuf == nullptr) { // Last descriptor, store buffer address for freeing @@ -264,7 +252,7 @@ namespace mbed { core_util_critical_section_enter(); // Configure settings. - giveToDMA(txSendIndex, descCount == 0, currBuf == nullptr); + giveToDMA(txSendIndex, static_cast(memory_manager->get_ptr(currBuf)), memory_manager->get_len(currBuf), descCount == 0, currBuf == nullptr); // Update descriptor count and index --txDescsOwnedByApplication; @@ -284,16 +272,11 @@ namespace mbed { * This implementation of Rx DMA should work for the large majority of embedded MCUs that use a DMA ring-based * ethernet MAC. * - * @tparam DescriptorT Type representing an Ethernet descriptor. - * - * @note If the MCU has a D-cache, then \c DescriptorT must be padded to an exact number of cache lines in size! + * The subclass must allocate the DMA descriptors, and all access to them is done through virtual functions + * that the subclass must override. */ - template class GenericRxDMALoop : public CompositeEMAC::RxDMA { protected: - /// Rx descriptor array. It's up to the subclass to allocate these in the correct location. - CacheAlignedBuffer & rxDescs = nullptr; - /// How many extra buffers to leave in the Rx pool, relative to how many we keep assigned to Rx descriptors. /// We want to keep some amount of extra buffers because constantly hitting the network stack with failed pool /// allocations can produce some negative consequences in some cases. @@ -325,26 +308,48 @@ namespace mbed { size_t rxPoolPayloadSize; /// Constructor. Subclass must allocate descriptor array of size RX_NUM_DESCS - GenericRxDMALoop(CacheAlignedBuffer & rxDescs) : rxDescs(rxDescs) {} + GenericRxDMALoop() = default; /// Configure DMA registers to point to the DMA ring, - /// and enable DMA. This is done before the MAC itself is enabled. + /// and enable DMA. This is done before the MAC itself is enabled, and before any descriptors + /// are given to DMA. virtual void startDMA() = 0; /// Stop the DMA running. This is done after MAC transmit & receive are disabled. virtual void stopDMA() = 0; +#if __DCACHE_PRESENT + /// Invalidate cache for the descriptor at the given index so it gets reloaded from main memory + virtual void cacheInvalidateDescriptor(size_t descIdx) = 0; +#endif + + /// Is the given descriptor owned by DMA? + /// Note that the descriptor will already have been invalidated in cache if needed. + virtual bool descOwnedByDMA(size_t descIdx) = 0; + + /// Does the given descriptor contain the start of a packet? + /// Note that the descriptor will already have been invalidated in cache if needed. + virtual bool isFirstDesc(size_t descIdx) = 0; + + /// Does the given descriptor contain the end of a packet? + /// /// Note that the descriptor will already have been invalidated in cache if needed. + virtual bool isLastDesc(size_t descIdx) = 0; + + /// Is the given descriptor an error descriptor? + /// /// Note that the descriptor will already have been invalidated in cache if needed. + virtual bool isErrorDesc(size_t descIdx) = 0; + /// Return a descriptor to DMA so that DMA can receive into it. - /// Is passed the buffer address (fixed size) to attach to this descriptor. + /// Is passed the buffer address (fixed size equal to rxPoolPayloadSize) to attach to this descriptor. /// Note: if the descriptor needs to be flushed from CPU cache, you need to do that /// at the correct point in the implementation of this method! /// Also, if the DMA ran out of data to transmit, you may need to do a "poke"/"wake" operation /// to tell it to start running again. virtual void returnDescriptor(size_t descIdx, uint8_t * buffer) = 0; - /// Get the length of the packet starting at firstDescIdx and continuing until the - /// given last descriptor. Descriptors have already been validated to contain a - /// complete packet at this point. + /// Get the length of the packet starting at firstDescIdx and continuing until + /// lastDescIdx (which might or might not be the same as firstDescIdx). Descriptors have already been + /// validated to contain a complete packet at this point. virtual size_t getTotalLen(size_t firstDescIdx, size_t lastDescIdx) = 0; public: @@ -416,18 +421,18 @@ namespace mbed { for(size_t descCount = 0; descCount < RX_NUM_DESCS; descCount++) { - auto &descriptor = rxDescs[(rxNextIndex + descCount) % RX_NUM_DESCS]; + size_t descIdx = (rxNextIndex + descCount) % RX_NUM_DESCS; #if __DCACHE_PRESENT - SCB_InvalidateDCache_by_Addr(&descriptor, sizeof(DescriptorT)); + cacheInvalidateDescriptor(descIdx); #endif - if(descriptor.ownedByDMA()) + if(descOwnedByDMA(descIdx)) { // Descriptor owned by DMA. We are out of descriptors to process. return false; } - if(descriptor.isErrorDesc() or descriptor.isLastDesc()) + if(isErrorDesc(descIdx) || isLastDesc(descIdx)) { // Reclaimable descriptor or complete packet detected. return true; @@ -450,18 +455,21 @@ namespace mbed { for (size_t descCount = 0; descCount < maxDescsToProcess && !lastDescIdx.has_value(); descCount++) { size_t descIdx = (startIdx + descCount) % RX_NUM_DESCS; - auto &descriptor = rxDescs[descIdx]; - #if __DCACHE_PRESENT - SCB_InvalidateDCache_by_Addr(&descriptor, sizeof(DescriptorT)); - #endif +#if __DCACHE_PRESENT + cacheInvalidateDescriptor(descIdx); +#endif - if (descriptor.ownedByDMA()) { + if (descOwnedByDMA(descIdx)) { // Descriptor owned by DMA and has not been filled in yet. We are out of descriptors to process. break; } - if (!firstDescIdx.has_value() && (descriptor.isErrorDesc() || !descriptor.isFirstDesc())) { + const bool isError = isErrorDesc(descIdx); + const bool isFirst = isFirstDesc(descIdx); + const bool isLast = isLastDesc(descIdx); + + if (!firstDescIdx.has_value() && (isError || !isFirst)) { // Error or non-first-descriptor before a first descriptor // (could be caused by incomplete packets/junk in the DMA buffer). // Ignore, free associated memory, and schedule for rebuild. @@ -472,13 +480,13 @@ namespace mbed { continue; } - else if(firstDescIdx.has_value() && (descriptor.isErrorDesc() || descriptor.isFirstDesc())) + else if(firstDescIdx.has_value() && (isError || isFirst)) { // Already seen a first descriptor, but we have an error descriptor or another first descriptor. // So, delete the in-progress packet up to this point. // Clean up the old first descriptor and any descriptors between there and here - const size_t endIdx = descriptor.isFirstDesc() ? descIdx : (descIdx + 1) % RX_NUM_DESCS; + const size_t endIdx = isFirst ? descIdx : (descIdx + 1) % RX_NUM_DESCS; for(size_t descToCleanIdx = *firstDescIdx; descToCleanIdx != endIdx; descToCleanIdx = (descToCleanIdx + 1) % RX_NUM_DESCS) { memory_manager->free(rxDescStackBufs[descToCleanIdx]); @@ -487,17 +495,17 @@ namespace mbed { ++rxNextIndex; } - if(!descriptor.isErrorDesc()) + if(!isError) { firstDescIdx = descIdx; } } - else if(descriptor.isFirstDesc()) + else if(isFirst) { firstDescIdx = descIdx; } - if (descriptor.isLastDesc()) { + if (isLast) { lastDescIdx = descIdx; } } diff --git a/targets/cmsis_mcu_descriptions.json5 b/targets/cmsis_mcu_descriptions.json5 index 9467a53617e..9122b6d93cc 100644 --- a/targets/cmsis_mcu_descriptions.json5 +++ b/targets/cmsis_mcu_descriptions.json5 @@ -7704,7 +7704,8 @@ "start": 134217728, "startup": true }, - "SRAM1_2": { + // Combined SRAMs 1, 2, and 3 + "SRAM1_2_3": { "access": { "execute": true, "non_secure": false, @@ -7716,13 +7717,16 @@ }, "default": true, "p_name": null, - "size": 327680, - "start": 536870912, + "size": 0xA0000, // 640 kiB + "start": 0x20000000, "startup": false }, - "SRAM3": { + // Backup SRAM. This is mentioned in the datasheet but neither the reference manual nor the CMSIS pack + // gives its address... + // Got this info from a forum post: https://community.st.com/t5/stm32-mcus-products/backup-sram-on-stm32h563/td-p/580633 + "SRAM_BKUP": { "access": { - "execute": true, + "execute": false, "non_secure": false, "non_secure_callable": false, "peripheral": false, @@ -7730,10 +7734,8 @@ "secure": false, "write": true }, - "default": false, - "p_name": null, - "size": 327680, - "start": 537198592, + "size": 0x1000, + "start": 0x40036400, "startup": false } }, From 34999a37e7a262ca069cbd8696c3ad3161a2a036 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Mon, 24 Feb 2025 08:56:43 -0800 Subject: [PATCH 24/47] Initial implementation of v1 DMA --- .../drivers/emac/TARGET_STM/STM32EthMACv1.cpp | 71 ++++++++++++++++++- .../drivers/emac/TARGET_STM/STM32EthMACv1.h | 13 ++++ .../include/mbed-trace/mbed_trace.h | 2 +- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp index 5f5958d04f0..990d9b02086 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp @@ -54,6 +54,7 @@ void STM32EthMACv1::TxDMA::stopDMA() { void STM32EthMACv1::TxDMA::cacheInvalidateDescriptor(const size_t descIdx) { SCB_InvalidateDCache_by_Addr(&txDescs[descIdx], sizeof(stm32_ethv1::TxDescriptor)); } +#endif bool STM32EthMACv1::TxDMA::descOwnedByDMA(size_t descIdx) { return txDescs[descIdx].dmaOwn; @@ -80,10 +81,30 @@ bool STM32EthMACv1::TxDMA::isDMAReadableBuffer(uint8_t const *start, size_t size } void STM32EthMACv1::TxDMA::giveToDMA(size_t descIdx, uint8_t const *buffer, size_t len, bool firstDesc, bool lastDesc) { + // Configure descriptor with buffer and size + txDescs[descIdx].buffer1 = buffer; + txDescs[descIdx].buffer1Size = len; + txDescs[descIdx].firstSegment = firstDesc; + txDescs[descIdx].lastSegment = lastDesc; + txDescs[descIdx].intrOnComplete = true; -} + // Return to DMA + txDescs[descIdx].dmaOwn = true; + + // Flush back to main memory +#ifdef __DCACHE_PRESENT + SCB_CleanDCache_by_Addr(&txDescs[descIdx], sizeof(stm32_ethv1::TxDescriptor)); +#else + __DMB(); // Make sure descriptor is written before the below lines #endif + // Clear buffer unavailable flag (I think this is for information only though) + base->DMASR = ETH_DMASR_TBUS_Msk; + + // Demand (good sir!) a Tx descriptor poll + base->DMATPDR = 1; +} + void STM32EthMACv1::RxDMA::startDMA() { // Rx buffer size must be a multiple of 4, per the descriptor definition @@ -437,4 +458,52 @@ void STM32EthMACv1::MACDriver::setPromiscuous(bool enable) { base->MACFFR &= ~ETH_MACFFR_PM_Msk; } } + +STM32EthMACv1::STM32EthMACv1(): +CompositeEMAC(txDMA, rxDMA, macDriver), +base(ETH), +txDMA(base), +rxDMA(base), +macDriver(base) +{ + instance = this; +} + +void STM32EthMACv1::irqHandler() { + const auto emacInst = instance; + uint32_t dma_flag = emacInst->base->DMASR; + + /* Packet received */ + if ((dma_flag & ETH_DMASR_RS_Msk) != 0U) + { + /* Clear the Eth DMA Rx IT pending bits */ + ETH->DMASR = ETH_DMASR_RS_Msk | ETH_DMASR_NIS_Msk; + + emacInst->rxISR(); + } + + /* Packet transmitted */ + if ((dma_flag & ETH_DMASR_TS_Msk) != 0U) + { + /* Clear the Eth DMA Tx IT pending bits */ + ETH->DMASR = ETH_DMASR_TS_Msk | ETH_DMASR_NIS_Msk; + + emacInst->txISR(); + } + + /* ETH DMA Error */ + if(dma_flag & ETH_DMASR_FBES_Msk) + { + MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_ETHERNET, EIO), \ + "STM32 EMAC v2: Hardware reports fatal DMA error\n"); + } +} + +// Provide default EMAC driver +MBED_WEAK EMAC &EMAC::get_default_instance() +{ + static mbed::STM32EthMACv1 emac; + return emac; +} + } diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h index d5e587fea7d..9012e8b9a7b 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h @@ -130,7 +130,20 @@ class STM32EthMACv1 : public CompositeEMAC void setPromiscuous(bool enable) override; }; + // Pointer to global instance, for ISR to use. + // TODO if we support more than 1 EMAC per MCU, this will need to be an array + static STM32EthMACv1 * instance; + + ETH_TypeDef * const base; // Base address of Ethernet peripheral + + // Components of the ethernet MAC + TxDMA txDMA; + RxDMA rxDMA; + MACDriver macDriver; + public: + STM32EthMACv1(); + // Interrupt callback static void irqHandler(); }; diff --git a/platform/mbed-trace/include/mbed-trace/mbed_trace.h b/platform/mbed-trace/include/mbed-trace/mbed_trace.h index 80666f91195..c6015e26d47 100644 --- a/platform/mbed-trace/include/mbed-trace/mbed_trace.h +++ b/platform/mbed-trace/include/mbed-trace/mbed_trace.h @@ -404,7 +404,7 @@ char *mbed_trace_array(const uint8_t *buf, uint16_t len); #elif !defined(MBED_TRACE_DUMMIES_DEFINED) // define dummies, hiding the real functions #define MBED_TRACE_DUMMIES_DEFINED -#define mbed_trace_init(...) ((int) 0) +#define mbed_trace_init(...) ((void) 0) #define mbed_trace_free(...) ((void) 0) #define mbed_trace_buffer_sizes(...) ((void) 0) #define mbed_trace_config_set(...) ((void) 0) From 56ad53a90a2fcbd95fde5c12d1b02a8c4bd8d0d7 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Mon, 24 Feb 2025 09:24:37 -0800 Subject: [PATCH 25/47] Fix some bugs, Tx DMA working but no packets are getting transmitted --- .../drivers/emac/TARGET_STM/STM32EthMACv1.cpp | 17 +++++++------ .../drivers/emac/include/GenericEthDMA.h | 24 ++++++++++++------- .../drivers/emac/sources/CompositeEMAC.cpp | 2 +- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp index 990d9b02086..bd86eb6f185 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp @@ -40,7 +40,7 @@ void STM32EthMACv1::TxDMA::startDMA() { txDescs[TX_NUM_DESCS - 1].endOfRing = true; // Set descriptor list address register - base->DMARDLAR = reinterpret_cast(&txDescs[0]); + base->DMATDLAR = reinterpret_cast(&txDescs[0]); // Start Tx DMA base->DMAOMR |= ETH_DMAOMR_ST_Msk; @@ -254,7 +254,7 @@ CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::init() { ETH_SetMDIOClockRange(base); // Configure MAC settings - base->MACCR |= ETH_MACCR_APCS_Msk; // Strip padding and CRC from frames + base->MACCR |= (1 << 25); // Strip CRC from frames. CSTF bit definition missing from CMSIS header for some reason? base->MACFFR = ETH_MACFFR_HPF_Msk | ETH_MACFFR_HM_Msk; // Use perfect and hash filters for multicast // Configure DMA settings. Default STM32CubeHAL settings used. @@ -278,6 +278,8 @@ CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::init() { // trigger if we run out of Rx descriptors, and we don't want to fatal error // in that case. base->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE | ETH_DMAIER_FBEIE | ETH_DMAIER_AISE; + + return CompositeEMAC::ErrCode::SUCCESS; } CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::deinit() { @@ -361,7 +363,7 @@ CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::mdioRead(uint8_t devAddr, uint8 // Get result result = base->MACMIIDR; - tr_info("MDIO read devAddr %" PRIu8 ", regAddr 0x%" PRIx8 " -> 0x%" PRIx16, devAddr, regAddr, result); + tr_debug("MDIO read devAddr %" PRIu8 ", regAddr 0x%" PRIx8 " -> 0x%" PRIx16, devAddr, regAddr, result); return ErrCode::SUCCESS; } @@ -459,6 +461,8 @@ void STM32EthMACv1::MACDriver::setPromiscuous(bool enable) { } } +STM32EthMACv1 * STM32EthMACv1::instance = nullptr; + STM32EthMACv1::STM32EthMACv1(): CompositeEMAC(txDMA, rxDMA, macDriver), base(ETH), @@ -495,15 +499,14 @@ void STM32EthMACv1::irqHandler() { if(dma_flag & ETH_DMASR_FBES_Msk) { MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_ETHERNET, EIO), \ - "STM32 EMAC v2: Hardware reports fatal DMA error\n"); + "STM32 EMAC v1: Hardware reports fatal DMA error\n"); } } +} // Provide default EMAC driver MBED_WEAK EMAC &EMAC::get_default_instance() { static mbed::STM32EthMACv1 emac; return emac; -} - -} +} \ No newline at end of file diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 35ef0c8671f..903cdaa5b0f 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -147,7 +147,7 @@ namespace mbed { txReclaimIndex = (txReclaimIndex + 1) % MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; ++txDescsOwnedByApplication; - tr_debug("Reclaimed descriptor %zu", txReclaimIndex); + tr_info("Reclaimed descriptor %zu", txReclaimIndex); returnedAnyDescriptors = true; } @@ -234,11 +234,11 @@ namespace mbed { SCB_CleanDCache_by_Addr(memory_manager->get_ptr(currBuf), memory_manager->get_len(currBuf)); #endif - // Move to next buffer - currBuf = memory_manager->get_next(currBuf); - if(currBuf == nullptr) + // Get next buffer + const auto nextBuf = memory_manager->get_next(currBuf); + if(nextBuf == nullptr) { - // Last descriptor, store buffer address for freeing + // Last descriptor, store head buffer address for freeing descStackBuffers[txSendIndex] = buf; } else @@ -246,19 +246,26 @@ namespace mbed { descStackBuffers[txSendIndex] = nullptr; } + // Get the pointer and length of the packet because this might not be doable in a critical section + const auto bufferPtr = static_cast(memory_manager->get_ptr(currBuf)); + const auto bufferLen = memory_manager->get_len(currBuf); + // Enter a critical section, because we could run into weird corner cases if the // interrupt executes while we are half done configuring this descriptor and updating // the counters. core_util_critical_section_enter(); // Configure settings. - giveToDMA(txSendIndex, static_cast(memory_manager->get_ptr(currBuf)), memory_manager->get_len(currBuf), descCount == 0, currBuf == nullptr); + giveToDMA(txSendIndex, bufferPtr, bufferLen, descCount == 0, nextBuf == nullptr); // Update descriptor count and index --txDescsOwnedByApplication; txSendIndex = (txSendIndex + 1) % MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; core_util_critical_section_exit(); + + // Move to next buffer + currBuf = nextBuf; } return CompositeEMAC::ErrCode::SUCCESS; @@ -558,9 +565,8 @@ namespace mbed { } #endif - tr_debug("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu (%p-%p)\n", - memory_manager->get_total_len(headBuffer), memory_manager->get_ptr(headBuffer), *firstDescIdx, *lastDescIdx, - &rxDescs[*firstDescIdx], &rxDescs[*lastDescIdx]); + tr_info("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu\n", + memory_manager->get_total_len(headBuffer), memory_manager->get_ptr(headBuffer), *firstDescIdx, *lastDescIdx); return headBuffer; } diff --git a/connectivity/drivers/emac/sources/CompositeEMAC.cpp b/connectivity/drivers/emac/sources/CompositeEMAC.cpp index 1f8fe67ffaf..2ecaf24c5e5 100644 --- a/connectivity/drivers/emac/sources/CompositeEMAC.cpp +++ b/connectivity/drivers/emac/sources/CompositeEMAC.cpp @@ -221,7 +221,7 @@ namespace mbed { return false; } - // Init DMA rungs + // Init DMA rings if(txDMA.init() != ErrCode::SUCCESS || rxDMA.init() != ErrCode::SUCCESS) { tr_err("power_up(): Failed to init DMA!"); return false; From baa5b767facb7500f70165d2a7bfe91d614e5236 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 25 Feb 2025 08:38:00 -0800 Subject: [PATCH 26/47] Tx and Rx working! --- .../TARGET_STM32F7/TARGET_NUCLEO_F767ZI/stm32f7_eth_init.c | 2 ++ connectivity/drivers/emac/include/GenericEthDMA.h | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F767ZI/stm32f7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F767ZI/stm32f7_eth_init.c index d31ab3187aa..57242048401 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F767ZI/stm32f7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F767ZI/stm32f7_eth_init.c @@ -53,6 +53,8 @@ void EthInitPinmappings(void) RMII_MII_TX_EN --------------------> PG11 RMII_MII_TXD0 ---------------------> PG13 RMII_MII_TXD1 ---------------------> PB13 + + NOTE: Must install JP6 and JP7 on board to use Ethernet. */ /* Configure PA1, PA2 and PA7 */ GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 903cdaa5b0f..40ca29deda2 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -147,7 +147,7 @@ namespace mbed { txReclaimIndex = (txReclaimIndex + 1) % MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; ++txDescsOwnedByApplication; - tr_info("Reclaimed descriptor %zu", txReclaimIndex); + tr_debug("Reclaimed descriptor %zu", txReclaimIndex); returnedAnyDescriptors = true; } @@ -192,7 +192,7 @@ namespace mbed { } } - tr_info("Transmitting packet of length %lu in %zu buffers and %zu descs\n", + tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", memory_manager->get_total_len(buf), memory_manager->count_buffers(buf), neededDescs); // Step 2: Copy packet if needed @@ -565,7 +565,7 @@ namespace mbed { } #endif - tr_info("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu\n", + tr_debug("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu\n", memory_manager->get_total_len(headBuffer), memory_manager->get_ptr(headBuffer), *firstDescIdx, *lastDescIdx); return headBuffer; From 42eb490dbfcc2da20545fcec5aa42c473c4551ea Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 25 Feb 2025 08:54:00 -0800 Subject: [PATCH 27/47] Update F2 and F4 eth inits --- .../TARGET_STM/TARGET_STM32F2/CMakeLists.txt | 12 +- .../TARGET_NUCLEO_F207ZG/stm32f2_eth_init.c | 143 ++++++++--------- .../TARGET_STM32F2/stm32f2_eth_conf.c | 61 -------- .../TARGET_STM/TARGET_STM32F4/CMakeLists.txt | 7 +- .../TARGET_NUCLEO_F429ZI/CMakeLists.txt | 1 - .../TARGET_NUCLEO_F429ZI/stm32f4_eth_conf.c | 61 -------- .../TARGET_NUCLEO_F429ZI/stm32f4_eth_init.c | 143 ++++++++--------- .../TARGET_NUCLEO_F439ZI/CMakeLists.txt | 1 - .../TARGET_NUCLEO_F439ZI/stm32f4_eth_conf.c | 61 -------- .../TARGET_NUCLEO_F439ZI/stm32f4_eth_init.c | 144 ++++++++---------- .../TARGET_STM32F7/stm32f7_eth_conf.c | 65 -------- 11 files changed, 200 insertions(+), 499 deletions(-) delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/stm32f2_eth_conf.c delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/stm32f4_eth_conf.c delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/stm32f4_eth_conf.c delete mode 100644 connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/stm32f7_eth_conf.c diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/CMakeLists.txt index 9a33dc74536..a161f155481 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/CMakeLists.txt @@ -3,14 +3,4 @@ if("NUCLEO_F207ZG" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_NUCLEO_F207ZG) -endif() - -target_include_directories(mbed-emac - PUBLIC - . -) - -target_sources(mbed-emac - PRIVATE - stm32f2_eth_conf.c -) +endif() \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/TARGET_NUCLEO_F207ZG/stm32f2_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/TARGET_NUCLEO_F207ZG/stm32f2_eth_init.c index 0e1c5776708..22f365067cd 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/TARGET_NUCLEO_F207ZG/stm32f2_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/TARGET_NUCLEO_F207ZG/stm32f2_eth_init.c @@ -29,98 +29,87 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT - #include "stm32f2xx_hal.h" +#include "PinNames.h" -/** - * Override HAL Eth Init function - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { GPIO_InitTypeDef GPIO_InitStructure; - if (heth->Instance == ETH) { - /* Enable GPIOs clocks */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); + /* Enable GPIOs clocks */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - /* Configure PA1, PA2 and PA7 */ - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; - GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; - GPIO_InitStructure.Pull = GPIO_NOPULL; - GPIO_InitStructure.Alternate = GPIO_AF11_ETH; - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + /* Configure PA1, PA2 and PA7 */ + GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Alternate = GPIO_AF11_ETH; + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Configure PB13 */ - GPIO_InitStructure.Pin = GPIO_PIN_13; - HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + /* Configure PB13 */ + GPIO_InitStructure.Pin = GPIO_PIN_13; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); - /* Configure PC1, PC4 and PC5 */ - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Configure PC1, PC4 and PC5 */ + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - /* Configure PG11 and PG13 */ - GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; - HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); + /* Configure PG11 and PG13 */ + GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; + HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); - /* Enable the Ethernet global Interrupt */ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); + /* Enable the Ethernet global Interrupt */ + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); - /* Enable ETHERNET clock */ - __HAL_RCC_ETH_CLK_ENABLE(); - } + /* Enable ETHERNET clock */ + __HAL_RCC_ETH_CLK_ENABLE(); } -/** - * Override HAL Eth DeInit function - */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH_CLK_DISABLE(); + /* Peripheral clock disable */ + __HAL_RCC_ETH_CLK_DISABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); - HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); - HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); - HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); + HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); + HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); - /* Disable the Ethernet global Interrupt */ - NVIC_DisableIRQ(ETH_IRQn); - } + /* Disable the Ethernet global Interrupt */ + NVIC_DisableIRQ(ETH_IRQn); } -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} \ No newline at end of file +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board +} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/stm32f2_eth_conf.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/stm32f2_eth_conf.c deleted file mode 100644 index 24b5fb7b7ce..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F2/stm32f2_eth_conf.c +++ /dev/null @@ -1,61 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stm32f2xx_hal.h" - -void _eth_config_mac(ETH_HandleTypeDef *heth) -{ - ETH_MACInitTypeDef macconf = { - .Watchdog = ETH_WATCHDOG_ENABLE, - .Jabber = ETH_JABBER_ENABLE, - .InterFrameGap = ETH_INTERFRAMEGAP_96BIT, - .CarrierSense = ETH_CARRIERSENCE_ENABLE, - .ReceiveOwn = ETH_RECEIVEOWN_ENABLE, - .LoopbackMode = ETH_LOOPBACKMODE_DISABLE, - .ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE, - .RetryTransmission = ETH_RETRYTRANSMISSION_DISABLE, - .AutomaticPadCRCStrip = ETH_AUTOMATICPADCRCSTRIP_DISABLE, - .BackOffLimit = ETH_BACKOFFLIMIT_10, - .DeferralCheck = ETH_DEFFERRALCHECK_DISABLE, - .ReceiveAll = ETH_RECEIVEAll_DISABLE, - .SourceAddrFilter = ETH_SOURCEADDRFILTER_DISABLE, - .PassControlFrames = ETH_PASSCONTROLFRAMES_BLOCKALL, - .BroadcastFramesReception = ETH_BROADCASTFRAMESRECEPTION_ENABLE, - .DestinationAddrFilter = ETH_DESTINATIONADDRFILTER_NORMAL, - .PromiscuousMode = ETH_PROMISCUOUS_MODE_DISABLE, - .MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE, - .UnicastFramesFilter = ETH_UNICASTFRAMESFILTER_PERFECT, - .HashTableHigh = 0x0U, - .HashTableLow = 0x0U, - .PauseTime = 0x0U, - .ZeroQuantaPause = ETH_ZEROQUANTAPAUSE_DISABLE, - .PauseLowThreshold = ETH_PAUSELOWTHRESHOLD_MINUS4, - .UnicastPauseFrameDetect = ETH_UNICASTPAUSEFRAMEDETECT_DISABLE, - .ReceiveFlowControl = ETH_RECEIVEFLOWCONTROL_DISABLE, - .TransmitFlowControl = ETH_TRANSMITFLOWCONTROL_DISABLE, - .VLANTagComparison = ETH_VLANTAGCOMPARISON_16BIT, - .VLANTagIdentifier = 0x0U - }; - - if (heth->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE) { - macconf.ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE; - } else { - macconf.ChecksumOffload = ETH_CHECKSUMOFFLAOD_DISABLE; - } - - (void) HAL_ETH_ConfigMAC(heth, &macconf); -} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/CMakeLists.txt index bf398c55be1..eb70c149ce4 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/CMakeLists.txt @@ -5,9 +5,4 @@ if("NUCLEO_F429ZI" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_NUCLEO_F429ZI) elseif("NUCLEO_F439ZI" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_NUCLEO_F439ZI) -endif() - -target_include_directories(mbed-emac - PUBLIC - . -) +endif() \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/CMakeLists.txt index 07d2f1d1b26..a550c5b5ada 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/CMakeLists.txt @@ -8,6 +8,5 @@ target_include_directories(mbed-emac target_sources(mbed-emac PRIVATE - stm32f4_eth_conf.c stm32f4_eth_init.c ) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/stm32f4_eth_conf.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/stm32f4_eth_conf.c deleted file mode 100644 index f41db94ac67..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/stm32f4_eth_conf.c +++ /dev/null @@ -1,61 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stm32f4xx_hal.h" - -void _eth_config_mac(ETH_HandleTypeDef *heth) -{ - ETH_MACInitTypeDef macconf = { - .Watchdog = ETH_WATCHDOG_ENABLE, - .Jabber = ETH_JABBER_ENABLE, - .InterFrameGap = ETH_INTERFRAMEGAP_96BIT, - .CarrierSense = ETH_CARRIERSENCE_ENABLE, - .ReceiveOwn = ETH_RECEIVEOWN_ENABLE, - .LoopbackMode = ETH_LOOPBACKMODE_DISABLE, - .ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE, - .RetryTransmission = ETH_RETRYTRANSMISSION_DISABLE, - .AutomaticPadCRCStrip = ETH_AUTOMATICPADCRCSTRIP_DISABLE, - .BackOffLimit = ETH_BACKOFFLIMIT_10, - .DeferralCheck = ETH_DEFFERRALCHECK_DISABLE, - .ReceiveAll = ETH_RECEIVEAll_DISABLE, - .SourceAddrFilter = ETH_SOURCEADDRFILTER_DISABLE, - .PassControlFrames = ETH_PASSCONTROLFRAMES_BLOCKALL, - .BroadcastFramesReception = ETH_BROADCASTFRAMESRECEPTION_ENABLE, - .DestinationAddrFilter = ETH_DESTINATIONADDRFILTER_NORMAL, - .PromiscuousMode = ETH_PROMISCUOUS_MODE_DISABLE, - .MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE, - .UnicastFramesFilter = ETH_UNICASTFRAMESFILTER_PERFECT, - .HashTableHigh = 0x0U, - .HashTableLow = 0x0U, - .PauseTime = 0x0U, - .ZeroQuantaPause = ETH_ZEROQUANTAPAUSE_DISABLE, - .PauseLowThreshold = ETH_PAUSELOWTHRESHOLD_MINUS4, - .UnicastPauseFrameDetect = ETH_UNICASTPAUSEFRAMEDETECT_DISABLE, - .ReceiveFlowControl = ETH_RECEIVEFLOWCONTROL_DISABLE, - .TransmitFlowControl = ETH_TRANSMITFLOWCONTROL_DISABLE, - .VLANTagComparison = ETH_VLANTAGCOMPARISON_16BIT, - .VLANTagIdentifier = 0x0U, - }; - - if (heth->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE) { - macconf.ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE; - } else { - macconf.ChecksumOffload = ETH_CHECKSUMOFFLAOD_DISABLE; - } - - (void) HAL_ETH_ConfigMAC(heth, &macconf); -} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/stm32f4_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/stm32f4_eth_init.c index 6423a3f92d2..68ad1aee8df 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/stm32f4_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F429ZI/stm32f4_eth_init.c @@ -29,98 +29,87 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT - #include "stm32f4xx_hal.h" +#include "PinNames.h" -/** - * Override HAL Eth Init function - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { GPIO_InitTypeDef GPIO_InitStructure; - if (heth->Instance == ETH) { - /* Enable GPIOs clocks */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); + /* Enable GPIOs clocks */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - /* Configure PA1, PA2 and PA7 */ - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; - GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; - GPIO_InitStructure.Pull = GPIO_NOPULL; - GPIO_InitStructure.Alternate = GPIO_AF11_ETH; - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + /* Configure PA1, PA2 and PA7 */ + GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Alternate = GPIO_AF11_ETH; + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Configure PB13 */ - GPIO_InitStructure.Pin = GPIO_PIN_13; - HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + /* Configure PB13 */ + GPIO_InitStructure.Pin = GPIO_PIN_13; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); - /* Configure PC1, PC4 and PC5 */ - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Configure PC1, PC4 and PC5 */ + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - /* Configure PG11 and PG13 */ - GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; - HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); + /* Configure PG11 and PG13 */ + GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; + HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); - /* Enable the Ethernet global Interrupt */ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); + /* Enable the Ethernet global Interrupt */ + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); - /* Enable ETHERNET clock */ - __HAL_RCC_ETH_CLK_ENABLE(); - } + /* Enable ETHERNET clock */ + __HAL_RCC_ETH_CLK_ENABLE(); } -/** - * Override HAL Eth DeInit function - */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH_CLK_DISABLE(); + /* Peripheral clock disable */ + __HAL_RCC_ETH_CLK_DISABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); - HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); - HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); - HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); + HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); + HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); - /* Disable the Ethernet global Interrupt */ - NVIC_DisableIRQ(ETH_IRQn); - } + /* Disable the Ethernet global Interrupt */ + NVIC_DisableIRQ(ETH_IRQn); } -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} \ No newline at end of file +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board +} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/CMakeLists.txt index 07d2f1d1b26..a550c5b5ada 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/CMakeLists.txt @@ -8,6 +8,5 @@ target_include_directories(mbed-emac target_sources(mbed-emac PRIVATE - stm32f4_eth_conf.c stm32f4_eth_init.c ) diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/stm32f4_eth_conf.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/stm32f4_eth_conf.c deleted file mode 100644 index f41db94ac67..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/stm32f4_eth_conf.c +++ /dev/null @@ -1,61 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stm32f4xx_hal.h" - -void _eth_config_mac(ETH_HandleTypeDef *heth) -{ - ETH_MACInitTypeDef macconf = { - .Watchdog = ETH_WATCHDOG_ENABLE, - .Jabber = ETH_JABBER_ENABLE, - .InterFrameGap = ETH_INTERFRAMEGAP_96BIT, - .CarrierSense = ETH_CARRIERSENCE_ENABLE, - .ReceiveOwn = ETH_RECEIVEOWN_ENABLE, - .LoopbackMode = ETH_LOOPBACKMODE_DISABLE, - .ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE, - .RetryTransmission = ETH_RETRYTRANSMISSION_DISABLE, - .AutomaticPadCRCStrip = ETH_AUTOMATICPADCRCSTRIP_DISABLE, - .BackOffLimit = ETH_BACKOFFLIMIT_10, - .DeferralCheck = ETH_DEFFERRALCHECK_DISABLE, - .ReceiveAll = ETH_RECEIVEAll_DISABLE, - .SourceAddrFilter = ETH_SOURCEADDRFILTER_DISABLE, - .PassControlFrames = ETH_PASSCONTROLFRAMES_BLOCKALL, - .BroadcastFramesReception = ETH_BROADCASTFRAMESRECEPTION_ENABLE, - .DestinationAddrFilter = ETH_DESTINATIONADDRFILTER_NORMAL, - .PromiscuousMode = ETH_PROMISCUOUS_MODE_DISABLE, - .MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE, - .UnicastFramesFilter = ETH_UNICASTFRAMESFILTER_PERFECT, - .HashTableHigh = 0x0U, - .HashTableLow = 0x0U, - .PauseTime = 0x0U, - .ZeroQuantaPause = ETH_ZEROQUANTAPAUSE_DISABLE, - .PauseLowThreshold = ETH_PAUSELOWTHRESHOLD_MINUS4, - .UnicastPauseFrameDetect = ETH_UNICASTPAUSEFRAMEDETECT_DISABLE, - .ReceiveFlowControl = ETH_RECEIVEFLOWCONTROL_DISABLE, - .TransmitFlowControl = ETH_TRANSMITFLOWCONTROL_DISABLE, - .VLANTagComparison = ETH_VLANTAGCOMPARISON_16BIT, - .VLANTagIdentifier = 0x0U, - }; - - if (heth->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE) { - macconf.ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE; - } else { - macconf.ChecksumOffload = ETH_CHECKSUMOFFLAOD_DISABLE; - } - - (void) HAL_ETH_ConfigMAC(heth, &macconf); -} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/stm32f4_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/stm32f4_eth_init.c index 6423a3f92d2..b8958960c51 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/stm32f4_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F439ZI/stm32f4_eth_init.c @@ -29,98 +29,86 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT #include "stm32f4xx_hal.h" +#include "PinNames.h" -/** - * Override HAL Eth Init function - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { - GPIO_InitTypeDef GPIO_InitStructure; - if (heth->Instance == ETH) { - - /* Enable GPIOs clocks */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); + /* Enable GPIOs clocks */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - /* Configure PA1, PA2 and PA7 */ - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; - GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; - GPIO_InitStructure.Pull = GPIO_NOPULL; - GPIO_InitStructure.Alternate = GPIO_AF11_ETH; - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + /* Configure PA1, PA2 and PA7 */ + GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Alternate = GPIO_AF11_ETH; + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Configure PB13 */ - GPIO_InitStructure.Pin = GPIO_PIN_13; - HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + /* Configure PB13 */ + GPIO_InitStructure.Pin = GPIO_PIN_13; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); - /* Configure PC1, PC4 and PC5 */ - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Configure PC1, PC4 and PC5 */ + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - /* Configure PG11 and PG13 */ - GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; - HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); + /* Configure PG11 and PG13 */ + GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; + HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); - /* Enable the Ethernet global Interrupt */ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); + /* Enable the Ethernet global Interrupt */ + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); - /* Enable ETHERNET clock */ - __HAL_RCC_ETH_CLK_ENABLE(); - } + /* Enable ETHERNET clock */ + __HAL_RCC_ETH_CLK_ENABLE(); } -/** - * Override HAL Eth DeInit function - */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH_CLK_DISABLE(); + /* Peripheral clock disable */ + __HAL_RCC_ETH_CLK_DISABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); - HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); - HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); - HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); + HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); + HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); - /* Disable the Ethernet global Interrupt */ - NVIC_DisableIRQ(ETH_IRQn); - } + /* Disable the Ethernet global Interrupt */ + NVIC_DisableIRQ(ETH_IRQn); } -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} \ No newline at end of file +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board +} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/stm32f7_eth_conf.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/stm32f7_eth_conf.c deleted file mode 100644 index bf725b6b6a1..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/stm32f7_eth_conf.c +++ /dev/null @@ -1,65 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stm32f7xx_hal.h" - -#if defined ETH - -void _eth_config_mac(ETH_HandleTypeDef *heth) -{ - ETH_MACInitTypeDef macconf = { - .Watchdog = ETH_WATCHDOG_ENABLE, - .Jabber = ETH_JABBER_ENABLE, - .InterFrameGap = ETH_INTERFRAMEGAP_96BIT, - .CarrierSense = ETH_CARRIERSENCE_ENABLE, - .ReceiveOwn = ETH_RECEIVEOWN_ENABLE, - .LoopbackMode = ETH_LOOPBACKMODE_DISABLE, - .ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE, - .RetryTransmission = ETH_RETRYTRANSMISSION_DISABLE, - .AutomaticPadCRCStrip = ETH_AUTOMATICPADCRCSTRIP_DISABLE, - .BackOffLimit = ETH_BACKOFFLIMIT_10, - .DeferralCheck = ETH_DEFFERRALCHECK_DISABLE, - .ReceiveAll = ETH_RECEIVEAll_DISABLE, - .SourceAddrFilter = ETH_SOURCEADDRFILTER_DISABLE, - .PassControlFrames = ETH_PASSCONTROLFRAMES_BLOCKALL, - .BroadcastFramesReception = ETH_BROADCASTFRAMESRECEPTION_ENABLE, - .DestinationAddrFilter = ETH_DESTINATIONADDRFILTER_NORMAL, - .PromiscuousMode = ETH_PROMISCUOUS_MODE_DISABLE, - .MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE, - .UnicastFramesFilter = ETH_UNICASTFRAMESFILTER_PERFECT, - .HashTableHigh = 0x0, - .HashTableLow = 0x0, - .PauseTime = 0x0, - .ZeroQuantaPause = ETH_ZEROQUANTAPAUSE_DISABLE, - .PauseLowThreshold = ETH_PAUSELOWTHRESHOLD_MINUS4, - .UnicastPauseFrameDetect = ETH_UNICASTPAUSEFRAMEDETECT_DISABLE, - .ReceiveFlowControl = ETH_RECEIVEFLOWCONTROL_DISABLE, - .TransmitFlowControl = ETH_TRANSMITFLOWCONTROL_DISABLE, - .VLANTagComparison = ETH_VLANTAGCOMPARISON_16BIT, - .VLANTagIdentifier = 0x0 - }; - - if (heth->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE) { - macconf.ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE; - } else { - macconf.ChecksumOffload = ETH_CHECKSUMOFFLAOD_DISABLE; - } - - (void) HAL_ETH_ConfigMAC(heth, &macconf); -} - -#endif // defined ETH From 5e27b230bbff841f3a19c1b984d191210eafdcba Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 25 Feb 2025 09:02:20 -0800 Subject: [PATCH 28/47] Try to fix bank test --- .../drivers/emac/TARGET_STM/STM32EthMACv1.cpp | 6 ++---- .../drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 10 +++------- .../TARGET_STM/TARGET_STM32F7/CMakeLists.txt | 10 ---------- .../drivers/emac/include/GenericEthDMA.h | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp index bd86eb6f185..683b8b74967 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp @@ -62,7 +62,7 @@ bool STM32EthMACv1::TxDMA::descOwnedByDMA(size_t descIdx) { bool STM32EthMACv1::TxDMA::isDMAReadableBuffer(uint8_t const *start, size_t size) const { #ifdef TARGET_STM32F7 - if(reinterpret_cast(start) < 1024*16) { + if(bufferTouchesMemoryBank(start, size, 0, 1024*16)) { // In ITCM memory, not accessible by DMA. Note that ITCM is not included in the CMSIS memory map (yet). return false; } @@ -70,9 +70,7 @@ bool STM32EthMACv1::TxDMA::isDMAReadableBuffer(uint8_t const *start, size_t size #if TARGET_STM32F2 || TARGET_STM32F4 // On STM32F2 and F2, ethernet DMA cannot access the flash memory. - if(reinterpret_cast(start) >= MBED_ROM_START || - reinterpret_cast(start + size) <= MBED_ROM_START + MBED_ROM_SIZE) - { + if(bufferTouchesMemoryBank(start, size, MBED_ROM_START, MBED_ROM_SIZE)) { return false; } #endif diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index c64bee16a11..9a1c2a65b74 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -66,18 +66,14 @@ namespace mbed { // On STM32H7, the Ethernet DMA cannot access data in DTCM. So, if someone sends // a packet with a data pointer in DTCM (e.g. a stack allocated payload), everything // will break if we don't copy it first. - if(reinterpret_cast(start) >= MBED_RAM_BANK_SRAM_DTC_START || - reinterpret_cast(start + size) <= MBED_RAM_BANK_SRAM_DTC_START + MBED_RAM_BANK_SRAM_DTC_SIZE) - { + if(bufferTouchesMemoryBank(start, size, MBED_RAM_BANK_SRAM_DTC_START, MBED_RAM_BANK_SRAM_DTC_SIZE)) { return false; } #endif #ifdef TARGET_STM32H5 - // On STM32H7, the Ethernet DMA cannot access data in backup SRAM. - if(reinterpret_cast(start) >= MBED_RAM_BANK_SRAM_BKUP_START || - reinterpret_cast(start + size) <= MBED_RAM_BANK_SRAM_BKUP_START + MBED_RAM_BANK_SRAM_BKUP_SIZE) - { + // On STM32H5, the Ethernet DMA cannot access data in backup SRAM. + if(bufferTouchesMemoryBank(start, size, MBED_RAM_BANK_SRAM_BKUP_START, MBED_RAM_BANK_SRAM_BKUP_SIZE)) { return false; } #endif diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/CMakeLists.txt b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/CMakeLists.txt index 46dc00c7ffb..bbb58b9239c 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/CMakeLists.txt @@ -12,13 +12,3 @@ elseif("NUCLEO_F756ZG" IN_LIST MBED_TARGET_LABELS) elseif("NUCLEO_F767ZI" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_NUCLEO_F767ZI) endif() - -target_include_directories(mbed-emac - PUBLIC - . -) - -target_sources(mbed-emac - PRIVATE - stm32f7_eth_conf.c -) diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 40ca29deda2..7543bbe69ef 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -83,6 +83,25 @@ namespace mbed { /// to tell it to start running again. virtual void giveToDMA(size_t descIdx, uint8_t const * buffer, size_t len, bool firstDesc, bool lastDesc) = 0; + // Utility function for implementing isDMAReadableBuffer(). + // 1D intersection test between a buffer and a memory bank. + static bool bufferTouchesMemoryBank(uint8_t const * start, size_t size, uint32_t bankStartAddr, uint32_t bankSize) { + const auto startAddrInt = reinterpret_cast(start); + + if(startAddrInt < bankStartAddr) { + // Case 1: buffer begins before bank + return (startAddrInt + size) > bankStartAddr; + } + else if(startAddrInt >= bankStartAddr && startAddrInt < (bankStartAddr + bankSize)) { + // Case 2: buffer begins inside bank + return true; + } + else { + // Case 3: buffer begins after bank + return false; + } + } + public: CompositeEMAC::ErrCode init() override { // At the start, we own all the descriptors From 8080467ef072289a4581426511e3bf247b8be5fb Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 25 Feb 2025 09:32:27 -0800 Subject: [PATCH 29/47] Disable Ethernet in HALs --- .../drivers/emac/TARGET_STM/STM32EthMACv1.cpp | 2 +- .../TARGET_DISCO_F746NG/stm32f7_eth_init.c | 140 +++++++--------- .../TARGET_DISCO_F769NI/stm32f7_eth_init.c | 138 +++++++--------- .../TARGET_NUCLEO_F746ZG/stm32f7_eth_init.c | 152 ++++++++---------- .../TARGET_NUCLEO_F756ZG/stm32f7_eth_init.c | 149 ++++++++--------- .../drivers/emac/include/GenericEthDMA.h | 4 +- .../emac_test_utils/EmacTestMemoryManager.cpp | 2 +- .../STM32Cube_FW/CMakeLists.txt | 1 - .../STM32Cube_FW/stm32f2xx_hal_conf.h | 5 +- .../STM32Cube_FW/CMakeLists.txt | 1 - .../STM32Cube_FW/stm32f4xx_hal_conf.h | 5 +- .../STM32Cube_FW/CMakeLists.txt | 1 - .../STM32Cube_FW/stm32f7xx_hal_conf.h | 5 +- 13 files changed, 274 insertions(+), 331 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp index 683b8b74967..c328514ea9a 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp @@ -177,7 +177,7 @@ void STM32EthMACv1::MACDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) { /* Get the ETHERNET MACMIIAR value */ uint32_t tempreg = base->MACMIIAR; /* Clear CSR Clock Range CR[2:0] bits */ - tempreg &= ETH_MACMIIAR_CR_MASK; + tempreg &= ETH_MACMIIAR_CR_Msk; /* Get hclk frequency value */ uint32_t hclk = HAL_RCC_GetHCLKFreq(); diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_DISCO_F746NG/stm32f7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_DISCO_F746NG/stm32f7_eth_init.c index 25f8bf61aeb..a4112a8a62f 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_DISCO_F746NG/stm32f7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_DISCO_F746NG/stm32f7_eth_init.c @@ -29,97 +29,79 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT - #include "stm32f7xx_hal.h" -#include "platform/mbed_critical.h" +#include "PinNames.h" -/** - * Override HAL Eth Init function - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { - GPIO_InitTypeDef GPIO_InitStructure; - if (heth->Instance == ETH) { - /* Disable DCache for STM32F7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); - - /* Enable GPIOs clocks */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); + /* Enable GPIOs clocks */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PG14 - */ - /* Configure PA1, PA2 and PA7 */ - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; - GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; - GPIO_InitStructure.Pull = GPIO_NOPULL; - GPIO_InitStructure.Alternate = GPIO_AF11_ETH; - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PG14 + */ + /* Configure PA1, PA2 and PA7 */ + GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Alternate = GPIO_AF11_ETH; + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Configure PC1, PC4 and PC5 */ - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Configure PC1, PC4 and PC5 */ + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - /* Configure PG11, PG13 and PG14 */ - GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14; - HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); + /* Configure PG11, PG13 and PG14 */ + GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14; + HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); - /* Enable the Ethernet global Interrupt */ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); + /* Enable the Ethernet global Interrupt */ + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); - /* Enable ETHERNET clock */ - __HAL_RCC_ETH_CLK_ENABLE(); - } + /* Enable ETHERNET clock */ + __HAL_RCC_ETH_CLK_ENABLE(); } -/** - * Override HAL Eth DeInit function - */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH_CLK_DISABLE(); + /* Peripheral clock disable */ + __HAL_RCC_ETH_CLK_DISABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PG14 - */ - HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); - HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); - HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PG14 + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); + HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14); - /* Disable the Ethernet global Interrupt */ - NVIC_DisableIRQ(ETH_IRQn); - } + /* Disable the Ethernet global Interrupt */ + NVIC_DisableIRQ(ETH_IRQn); } -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} \ No newline at end of file +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board +} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_DISCO_F769NI/stm32f7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_DISCO_F769NI/stm32f7_eth_init.c index b66ca2c60cc..b735f5e208b 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_DISCO_F769NI/stm32f7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_DISCO_F769NI/stm32f7_eth_init.c @@ -29,97 +29,81 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT - #include "stm32f7xx_hal.h" -#include "platform/mbed_critical.h" +#include "PinNames.h" -/** - * Override HAL Eth Init function - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { GPIO_InitTypeDef GPIO_InitStructure; - if (heth->Instance == ETH) { - /* Disable DCache for STM32F7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); - /* Enable GPIOs clocks */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); + /* Enable GPIOs clocks */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PG14 - */ - /* Configure PA1, PA2 and PA7 */ - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; - GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; - GPIO_InitStructure.Pull = GPIO_NOPULL; - GPIO_InitStructure.Alternate = GPIO_AF11_ETH; - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PG14 + */ + /* Configure PA1, PA2 and PA7 */ + GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Alternate = GPIO_AF11_ETH; + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Configure PC1, PC4 and PC5 */ - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Configure PC1, PC4 and PC5 */ + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - /* Configure PG11, PG13 and PG14 */ - GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14; - HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); + /* Configure PG11, PG13 and PG14 */ + GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14; + HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); - /* Enable the Ethernet global Interrupt */ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); + /* Enable the Ethernet global Interrupt */ + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); - /* Enable ETHERNET clock */ - __HAL_RCC_ETH_CLK_ENABLE(); - } + /* Enable ETHERNET clock */ + __HAL_RCC_ETH_CLK_ENABLE(); } -/** - * Override HAL Eth DeInit function - */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH_CLK_DISABLE(); + /* Peripheral clock disable */ + __HAL_RCC_ETH_CLK_DISABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PG14 - */ - HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); - HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); - HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PG14 + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); + HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14); - /* Disable the Ethernet global Interrupt */ - NVIC_DisableIRQ(ETH_IRQn); - } + /* Disable the Ethernet global Interrupt */ + NVIC_DisableIRQ(ETH_IRQn); } -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} \ No newline at end of file +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board +} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F746ZG/stm32f7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F746ZG/stm32f7_eth_init.c index ebc2d0448e8..f69c13b3147 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F746ZG/stm32f7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F746ZG/stm32f7_eth_init.c @@ -29,103 +29,91 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT #include "stm32f7xx_hal.h" -#include "platform/mbed_critical.h" +#include "PinNames.h" -/** - * Override HAL Eth Init function - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { - GPIO_InitTypeDef GPIO_InitStructure; - if (heth->Instance == ETH) { - /* Disable DCache for STM32F7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); + /* Disable DCache for STM32F7 family */ + core_util_critical_section_enter(); + SCB_DisableDCache(); + core_util_critical_section_exit(); - /* Enable GPIOs clocks */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); + /* Enable GPIOs clocks */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - /* Configure PA1, PA2 and PA7 */ - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; - GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; - GPIO_InitStructure.Pull = GPIO_NOPULL; - GPIO_InitStructure.Alternate = GPIO_AF11_ETH; - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + /* Configure PA1, PA2 and PA7 */ + GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Alternate = GPIO_AF11_ETH; + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Configure PB13 */ - GPIO_InitStructure.Pin = GPIO_PIN_13; - HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + /* Configure PB13 */ + GPIO_InitStructure.Pin = GPIO_PIN_13; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); - /* Configure PC1, PC4 and PC5 */ - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Configure PC1, PC4 and PC5 */ + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - /* Configure PG11 and PG13 */ - GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; - HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); + /* Configure PG11 and PG13 */ + GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; + HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); - /* Enable the Ethernet global Interrupt */ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); + /* Enable the Ethernet global Interrupt */ + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); - /* Enable ETHERNET clock */ - __HAL_RCC_ETH_CLK_ENABLE(); - } + /* Enable ETHERNET clock */ + __HAL_RCC_ETH_CLK_ENABLE(); } -/** - * Override HAL Eth DeInit function - */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH_CLK_DISABLE(); + /* Peripheral clock disable */ + __HAL_RCC_ETH_CLK_DISABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); - HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); - HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); - HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); + HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); + HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); - /* Disable the Ethernet global Interrupt */ - NVIC_DisableIRQ(ETH_IRQn); - } + /* Disable the Ethernet global Interrupt */ + NVIC_DisableIRQ(ETH_IRQn); } -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board +} diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F756ZG/stm32f7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F756ZG/stm32f7_eth_init.c index aa1287139d6..41a44fb8120 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F756ZG/stm32f7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F756ZG/stm32f7_eth_init.c @@ -29,103 +29,86 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USE_USER_DEFINED_HAL_ETH_MSPINIT #include "stm32f7xx_hal.h" -#include "platform/mbed_critical.h" +#include "PinNames.h" -/** - * Override HAL Eth Init function - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +void EthInitPinmappings(void) { - GPIO_InitTypeDef GPIO_InitStructure; - if (heth->Instance == ETH) { - /* Disable DCache for STM32F7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); - - /* Enable GPIOs clocks */ - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOG_CLK_ENABLE(); + /* Enable GPIOs clocks */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - /* Configure PA1, PA2 and PA7 */ - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; - GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; - GPIO_InitStructure.Pull = GPIO_NOPULL; - GPIO_InitStructure.Alternate = GPIO_AF11_ETH; - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + /* Configure PA1, PA2 and PA7 */ + GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Alternate = GPIO_AF11_ETH; + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Configure PB13 */ - GPIO_InitStructure.Pin = GPIO_PIN_13; - HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + /* Configure PB13 */ + GPIO_InitStructure.Pin = GPIO_PIN_13; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); - /* Configure PC1, PC4 and PC5 */ - GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Configure PC1, PC4 and PC5 */ + GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - /* Configure PG11 and PG13 */ - GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; - HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); + /* Configure PG11 and PG13 */ + GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13; + HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); - /* Enable the Ethernet global Interrupt */ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); + /* Enable the Ethernet global Interrupt */ + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); - /* Enable ETHERNET clock */ - __HAL_RCC_ETH_CLK_ENABLE(); - } + /* Enable ETHERNET clock */ + __HAL_RCC_ETH_CLK_ENABLE(); } -/** - * Override HAL Eth DeInit function - */ -void HAL_ETH_MspDeInit(ETH_HandleTypeDef *heth) +void EthDeinitPinmappings() { - if (heth->Instance == ETH) { - /* Peripheral clock disable */ - __HAL_RCC_ETH_CLK_DISABLE(); + /* Peripheral clock disable */ + __HAL_RCC_ETH_CLK_DISABLE(); - /** ETH GPIO Configuration - RMII_REF_CLK ----------------------> PA1 - RMII_MDIO -------------------------> PA2 - RMII_MDC --------------------------> PC1 - RMII_MII_CRS_DV -------------------> PA7 - RMII_MII_RXD0 ---------------------> PC4 - RMII_MII_RXD1 ---------------------> PC5 - RMII_MII_RXER ---------------------> none - RMII_MII_TX_EN --------------------> PG11 - RMII_MII_TXD0 ---------------------> PG13 - RMII_MII_TXD1 ---------------------> PB13 - */ - HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); - HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); - HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); - HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); + /** ETH GPIO Configuration + RMII_REF_CLK ----------------------> PA1 + RMII_MDIO -------------------------> PA2 + RMII_MDC --------------------------> PC1 + RMII_MII_CRS_DV -------------------> PA7 + RMII_MII_RXD0 ---------------------> PC4 + RMII_MII_RXD1 ---------------------> PC5 + RMII_MII_RXER ---------------------> none + RMII_MII_TX_EN --------------------> PG11 + RMII_MII_TXD0 ---------------------> PG13 + RMII_MII_TXD1 ---------------------> PB13 + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); + HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13); + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); + HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13); - /* Disable the Ethernet global Interrupt */ - NVIC_DisableIRQ(ETH_IRQn); - } + /* Disable the Ethernet global Interrupt */ + NVIC_DisableIRQ(ETH_IRQn); } -#endif /* USE_USER_DEFINED_HAL_ETH_MSPINIT */ - -// Blank, non-weak-override function to make sure the linker pulls in this file -void stm32_eth_init_weak_symbol_helper() -{} \ No newline at end of file +// Get Ethernet PHY reset pin +PinName EthGetPhyResetPin(void) +{ + return NC; // Not connected on this board +} diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 7543bbe69ef..43b708f4a3c 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -85,8 +85,8 @@ namespace mbed { // Utility function for implementing isDMAReadableBuffer(). // 1D intersection test between a buffer and a memory bank. - static bool bufferTouchesMemoryBank(uint8_t const * start, size_t size, uint32_t bankStartAddr, uint32_t bankSize) { - const auto startAddrInt = reinterpret_cast(start); + static bool bufferTouchesMemoryBank(uint8_t const * start, const size_t size, const uint32_t bankStartAddr, const uint32_t bankSize) { + const auto startAddrInt = reinterpret_cast(start); if(startAddrInt < bankStartAddr) { // Case 1: buffer begins before bank diff --git a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp index 236aa055304..3b6a0ce8b55 100644 --- a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp +++ b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp @@ -535,7 +535,7 @@ template void EmacTestMemoryManager::check_value(TYPE value, con va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); - assert(false); + MBED_ASSERT(false); va_end(ap); } } diff --git a/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/CMakeLists.txt b/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/CMakeLists.txt index 891564cf7e2..621a8cee239 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/CMakeLists.txt +++ b/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/CMakeLists.txt @@ -19,7 +19,6 @@ target_sources(mbed-stm32f2cube-fw STM32F2xx_HAL_Driver/stm32f2xx_hal_dcmi_ex.c STM32F2xx_HAL_Driver/stm32f2xx_hal_dma.c STM32F2xx_HAL_Driver/stm32f2xx_hal_dma_ex.c - STM32F2xx_HAL_Driver/stm32f2xx_hal_eth.c STM32F2xx_HAL_Driver/stm32f2xx_hal_exti.c STM32F2xx_HAL_Driver/stm32f2xx_hal_flash.c STM32F2xx_HAL_Driver/stm32f2xx_hal_flash_ex.c diff --git a/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/stm32f2xx_hal_conf.h b/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/stm32f2xx_hal_conf.h index 1f7a6e44ca2..e5678eb5aff 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/stm32f2xx_hal_conf.h +++ b/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/stm32f2xx_hal_conf.h @@ -43,7 +43,10 @@ #define HAL_DAC_MODULE_ENABLED #define HAL_DCMI_MODULE_ENABLED #define HAL_DMA_MODULE_ENABLED -#define HAL_ETH_MODULE_ENABLED + +// Mbed uses a handwritten driver for Ethernet +// #define HAL_ETH_MODULE_ENABLED + #define HAL_EXTI_MODULE_ENABLED #define HAL_FLASH_MODULE_ENABLED #define HAL_NAND_MODULE_ENABLED diff --git a/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/CMakeLists.txt b/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/CMakeLists.txt index ae0a4b831bd..c8f7524f42e 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/CMakeLists.txt +++ b/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/CMakeLists.txt @@ -24,7 +24,6 @@ target_sources(mbed-stm32f4cube-fw STM32F4xx_HAL_Driver/stm32f4xx_hal_dma2d.c STM32F4xx_HAL_Driver/stm32f4xx_hal_dma_ex.c STM32F4xx_HAL_Driver/stm32f4xx_hal_dsi.c - STM32F4xx_HAL_Driver/stm32f4xx_hal_eth.c STM32F4xx_HAL_Driver/stm32f4xx_hal_exti.c STM32F4xx_HAL_Driver/stm32f4xx_hal_flash.c STM32F4xx_HAL_Driver/stm32f4xx_hal_flash_ex.c diff --git a/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/stm32f4xx_hal_conf.h b/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/stm32f4xx_hal_conf.h index af2a9cc2e83..6e9354f719e 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/stm32f4xx_hal_conf.h +++ b/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/stm32f4xx_hal_conf.h @@ -43,7 +43,10 @@ #define HAL_DCMI_MODULE_ENABLED #define HAL_DMA_MODULE_ENABLED #define HAL_DMA2D_MODULE_ENABLED -#define HAL_ETH_MODULE_ENABLED + +// Mbed uses a handwritten driver for Ethernet +// #define HAL_ETH_MODULE_ENABLED + #define HAL_FLASH_MODULE_ENABLED #define HAL_NAND_MODULE_ENABLED #define HAL_NOR_MODULE_ENABLED diff --git a/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/CMakeLists.txt b/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/CMakeLists.txt index 95653610e25..cd708d41dd1 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/CMakeLists.txt +++ b/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/CMakeLists.txt @@ -26,7 +26,6 @@ target_sources(mbed-stm32f7cube-fw STM32F7xx_HAL_Driver/stm32f7xx_hal_dma2d.c STM32F7xx_HAL_Driver/stm32f7xx_hal_dma_ex.c STM32F7xx_HAL_Driver/stm32f7xx_hal_dsi.c - STM32F7xx_HAL_Driver/stm32f7xx_hal_eth.c STM32F7xx_HAL_Driver/stm32f7xx_hal_exti.c STM32F7xx_HAL_Driver/stm32f7xx_hal_flash.c STM32F7xx_HAL_Driver/stm32f7xx_hal_flash_ex.c diff --git a/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/stm32f7xx_hal_conf.h b/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/stm32f7xx_hal_conf.h index e4ef4b02ef8..154496c71c8 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/stm32f7xx_hal_conf.h +++ b/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/stm32f7xx_hal_conf.h @@ -44,7 +44,10 @@ #define HAL_DCMI_MODULE_ENABLED #define HAL_DMA_MODULE_ENABLED #define HAL_DMA2D_MODULE_ENABLED -#define HAL_ETH_MODULE_ENABLED + +// Mbed uses a handwritten driver for Ethernet +// #define HAL_ETH_MODULE_ENABLED + #define HAL_EXTI_MODULE_ENABLED #define HAL_FLASH_MODULE_ENABLED #define HAL_NAND_MODULE_ENABLED From b4e04f87f3f074c3223c5a15417c6fa8061e7611 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 25 Feb 2025 10:10:17 -0800 Subject: [PATCH 30/47] Apparently old GCC can't do attribute packed + alignas --- connectivity/drivers/emac/TARGET_STM/STM32EthV1Descriptors.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthV1Descriptors.h b/connectivity/drivers/emac/TARGET_STM/STM32EthV1Descriptors.h index 64a75dd7735..c715c5fc3ab 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthV1Descriptors.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthV1Descriptors.h @@ -24,7 +24,7 @@ namespace stm32_ethv1 { /// Struct for an enhanced Tx descriptor in the STM32 Eth IP v1. /// From STM32F7 Reference Manual, section 42.6.7. /// Note that even when there is no CPU cache, descriptors must be 32 bit aligned. - struct __attribute__ ((packed)) alignas(uint32_t) TxDescriptor + struct __attribute__((packed, aligned(4))) TxDescriptor { // TDES0 fields bool deferred : 1; @@ -83,7 +83,7 @@ namespace stm32_ethv1 { /// Struct for an enhanced Rx descriptor in the STM32 Eth IP v1. /// From STM32F7 Reference Manual, section 42.6.8 /// Note that even when there is no CPU cache, descriptors must be 32 bit aligned. - struct __attribute__ ((packed)) alignas(uint32_t) RxDescriptor + struct __attribute__((packed, aligned(4))) RxDescriptor { // RDES0 fields bool extStatusAvail : 1; From df8ebd725a70e232d7eb223ed3e27298374976b7 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Thu, 27 Feb 2025 09:34:52 -0800 Subject: [PATCH 31/47] Add RMII watchdog, add power test, fix unittests build --- .../drivers/emac/TARGET_STM/STM32EthMACv1.cpp | 41 + .../drivers/emac/TARGET_STM/STM32EthMACv1.h | 11 + .../drivers/emac/TARGET_STM/stm32xx_emac.cpp | 1067 ----------------- .../drivers/emac/TARGET_STM/stm32xx_emac.h | 201 ---- .../include/netsocket/NetStackMemoryManager.h | 4 + .../network/emac/emac_test_initialize.cpp | 9 + .../tests/TESTS/network/emac/emac_tests.h | 1 + .../tests/TESTS/network/emac/main.cpp | 2 + 8 files changed, 68 insertions(+), 1268 deletions(-) delete mode 100644 connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp delete mode 100644 connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp index c328514ea9a..45ac602972f 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp @@ -21,6 +21,8 @@ #include #include #include "mbed_error.h" +#include "mbed_events.h" +#include "CriticalSectionLock.h" #define TRACE_GROUP "STEMACv1" @@ -219,6 +221,29 @@ void STM32EthMACv1::MACDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) { base->MACMIIAR = (uint32_t)tempreg; } +#if ENABLE_ERRATA_2_21_6_WORKAROUND +void STM32EthMACv1::MACDriver::rmiiWatchdog() { + CriticalSectionLock lock; + + if(!rmiiWatchdogRunning) { + // Already canceled by main thread, bail + return; + } + + /* some good packets are received */ + if (base->MMCRGUFCR > 0) { + /* RMII Init is OK - cancel watchdog task */ + mbed_event_queue()->cancel(rmiiWatchdogRunning); + rmiiWatchdogRunning = false; + } else if (base->MMCRFCECR > 10) { + /* ETH received too many packets with CRC errors, resetting RMII */ + SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL; + SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; + base->MMCCR |= ETH_MMCCR_CR; + } +} +#endif + CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::init() { sleep_manager_lock_deep_sleep(); @@ -277,6 +302,13 @@ CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::init() { // in that case. base->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE | ETH_DMAIER_FBEIE | ETH_DMAIER_AISE; +#if ENABLE_ERRATA_2_21_6_WORKAROUND + // Start RMII watchdog task + rmiiWatchdogHandle = mbed_event_queue()->call_every(std::chrono::milliseconds(MBED_CONF_NSAPI_EMAC_PHY_POLL_PERIOD), + callback(this, &STM32EthMACv1::MACDriver::rmiiWatchdog)); + rmiiWatchdogRunning = true; +#endif + return CompositeEMAC::ErrCode::SUCCESS; } @@ -284,6 +316,15 @@ CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::deinit() { // Disable interrupt HAL_NVIC_DisableIRQ(ETH_IRQn); +#if ENABLE_ERRATA_2_21_6_WORKAROUND + // Disable RMII watchdog if still running + if(rmiiWatchdogRunning) { + CriticalSectionLock lock; + mbed_event_queue()->cancel(rmiiWatchdogRunning); + rmiiWatchdogRunning = false; + } +#endif + // Unlock deep sleep sleep_manager_unlock_deep_sleep(); diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h index 9012e8b9a7b..a3a9e95d446 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h @@ -21,6 +21,10 @@ #include "CacheAlignedBuffer.h" #include "GenericEthDMA.h" +#if TARGET_STM32F7 +#define ENABLE_ERRATA_2_21_6_WORKAROUND 1 +#endif + namespace mbed { @@ -100,6 +104,13 @@ class STM32EthMACv1 : public CompositeEMAC */ static void ETH_SetMDIOClockRange(ETH_TypeDef * const base); +#if ENABLE_ERRATA_2_21_6_WORKAROUND + // Workaround for ETH errata 2.21.6 from the STM32F7 errata sheet + int rmiiWatchdogHandle; + std::atomic rmiiWatchdogRunning = false; + void rmiiWatchdog(); +#endif + public: explicit MACDriver(ETH_TypeDef * const base): base(base) diff --git a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp deleted file mode 100644 index f90106d54de..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp +++ /dev/null @@ -1,1067 +0,0 @@ -/* Copyright (c) 2017-2019 ARM Limited - * Copyright (c) 2017-2019 STMicroelectronics - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if DEVICE_EMAC - -#include -#include - -#include "cmsis_os.h" - -#include "mbed_interface.h" -#include "mbed_assert.h" -#include "events/mbed_shared_queues.h" -#include "netsocket/nsapi_types.h" -#include "platform/mbed_power_mgmt.h" -#include "platform/mbed_error.h" -#include "CacheAlignedBuffer.h" -#include "MbedCRC.h" - -#include "stm32xx_emac_config.h" -#include "stm32xx_emac.h" - - -#include "mbed-trace/mbed_trace.h" - -#if defined(ETH_IP_VERSION_V2) -#define TRACE_GROUP "STE2" -#else -#define TRACE_GROUP "STE1" -#endif - -/* mbed trace feature is supported */ -/* ex in mbed_app.json */ -/* "mbed-trace.enable": "1" */ - -/* mbed_trace: debug traces (tr_debug) can be disabled here with no change in mbed_app.json */ -// #undef TRACE_LEVEL_DEBUG -// #define TRACE_LEVEL_DEBUG 0 - -/* To get trace from every packet, enable deep trace macro */ -// #define STM32xx_DEEP_TRACE -#ifdef STM32xx_DEEP_TRACE -#define tr_debug_deep(...) tr_debug(__VA_ARGS__) -#else -#define tr_debug_deep(...) -#endif - -#if defined(ETH_IP_VERSION_V2) -#include "lan8742/lan8742.h" -#endif - -using namespace std::chrono; - - - -/** \brief Driver thread priority */ -#define THREAD_PRIORITY (osPriorityHigh) - -#define PHY_TASK_PERIOD 200ms - -#define STM_HWADDR_SIZE (6) -#define STM_ETH_MTU_SIZE 1500 -#define STM_ETH_IF_NAME "st" - -#if defined(ETH_IP_VERSION_V2) - -static lan8742_Object_t LAN8742; - -static int32_t ETH_PHY_IO_Init(void); -static int32_t ETH_PHY_IO_DeInit(void); -static int32_t ETH_PHY_IO_ReadReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t *pRegVal); -static int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t RegVal); -static int32_t ETH_PHY_IO_GetTick(void); - -static lan8742_IOCtx_t LAN8742_IOCtx = { - ETH_PHY_IO_Init, - ETH_PHY_IO_DeInit, - ETH_PHY_IO_WriteReg, - ETH_PHY_IO_ReadReg, - ETH_PHY_IO_GetTick -}; - -#else - -// For IP v1, we do not do zero copy Rx, so we have to allocate all the Tx buffers ahead of time. -// Rx buffer addresses need to be aligned 4 bytes and to cache lines because we cache invalidate the buffers after receiving them. -mbed::StaticCacheAlignedBuffer Rx_Buff[ETH_RX_DESC_CNT] __attribute__((section(".EthBuffers"))); /* Ethernet Receive Buffers */ -#endif - -MBED_WEAK uint8_t mbed_otp_mac_address(char *mac); -void mbed_default_mac_address(char *mac); - -#ifdef __cplusplus -extern "C" { -#endif - -void _eth_config_mac(ETH_HandleTypeDef *heth); -void ETH_IRQHandler(void); - -// We need to give the linker a reason to pull in the stmxx_eth_init.c files, since they only contain -// weak symbol overrides and would otherwise be ignored. -void stm32_eth_init_weak_symbol_helper(); - -#ifdef __cplusplus -} -#endif - -#ifdef ETH_IP_VERSION_V2 -bool _phy_init() -{ - /* Set PHY IO functions */ - LAN8742_RegisterBusIO(&LAN8742, &LAN8742_IOCtx); - - /* Initialize the LAN8742 ETH PHY */ - return LAN8742_Init(&LAN8742) == LAN8742_STATUS_OK; -} - -int32_t _phy_get_state() -{ - return LAN8742_GetLinkState(&LAN8742); -} - -bool _phy_get_duplex_and_speed(int32_t phy_state, uint32_t *duplex, uint32_t *speed) -{ - switch (phy_state) { - case LAN8742_STATUS_100MBITS_FULLDUPLEX: - *duplex = ETH_FULLDUPLEX_MODE; - *speed = ETH_SPEED_100M; - break; - case LAN8742_STATUS_100MBITS_HALFDUPLEX: - *duplex = ETH_HALFDUPLEX_MODE; - *speed = ETH_SPEED_100M; - break; - case LAN8742_STATUS_10MBITS_FULLDUPLEX: - *duplex = ETH_FULLDUPLEX_MODE; - *speed = ETH_SPEED_10M; - break; - case LAN8742_STATUS_10MBITS_HALFDUPLEX: - *duplex = ETH_HALFDUPLEX_MODE; - *speed = ETH_SPEED_10M; - break; - default: - return false; - } - - return true; -} - -bool _phy_is_up(int32_t phy_state) -{ - return phy_state > LAN8742_STATUS_LINK_DOWN; -} - -#endif - -STM32_EMAC::STM32_EMAC(): -#ifdef ETH_IP_VERSION_V2 -phy_status(0) -#endif -{ -} - -static osThreadId_t create_new_thread(const char *threadName, void (*thread)(void *arg), void *arg, int stacksize, osPriority_t priority, mbed_rtos_storage_thread_t *thread_cb) -{ - osThreadAttr_t attr = {0}; - attr.name = threadName; - attr.stack_mem = malloc(stacksize); - attr.cb_mem = thread_cb; - attr.stack_size = stacksize; - attr.cb_size = sizeof(mbed_rtos_storage_thread_t); - attr.priority = priority; - return osThreadNew(thread, arg, &attr); -} - -/** - * In this function, the hardware should be initialized. - */ -bool STM32_EMAC::low_level_init_successful() -#ifndef ETH_IP_VERSION_V2 -{ - // Generate a reference to this empty function so the linker pulls it in. - stm32_eth_init_weak_symbol_helper(); - - uint32_t PHY_ID; - - /* Init ETH */ - uint8_t MACAddr[6]; - EthHandle.Instance = ETH; - EthHandle.Init.AutoNegotiation = MBED_CONF_STM32_EMAC_ETH_PHY_AUTONEGOTIATION; - EthHandle.Init.Speed = MBED_CONF_STM32_EMAC_ETH_PHY_SPEED; - EthHandle.Init.DuplexMode = MBED_CONF_STM32_EMAC_ETH_PHY_DUPLEXMODE; - EthHandle.Init.PhyAddress = MBED_CONF_STM32_EMAC_ETH_PHY_ADDRESS; -#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE) - MACAddr[0] = MBED_MAC_ADDR_0; - MACAddr[1] = MBED_MAC_ADDR_1; - MACAddr[2] = MBED_MAC_ADDR_2; - MACAddr[3] = MBED_MAC_ADDR_3; - MACAddr[4] = MBED_MAC_ADDR_4; - MACAddr[5] = MBED_MAC_ADDR_5; -#else - mbed_mac_address((char *)MACAddr); -#endif - EthHandle.Init.MACAddr = &MACAddr[0]; - EthHandle.Init.RxMode = ETH_RXINTERRUPT_MODE; - EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE; - EthHandle.Init.MediaInterface = MBED_CONF_STM32_EMAC_ETH_PHY_MEDIA_INTERFACE; - tr_info("power_up: PHY Addr %u AutoNeg %u", EthHandle.Init.PhyAddress, EthHandle.Init.AutoNegotiation); - tr_debug("MAC Addr %02x:%02x:%02x:%02x:%02x:%02x", MACAddr[0], MACAddr[1], MACAddr[2], MACAddr[3], MACAddr[4], MACAddr[5]); - tr_info("ETH buffers : %u Rx %u Tx", ETH_RXBUFNB, ETH_TXBUFNB); - - if (HAL_ETH_Init(&EthHandle) != HAL_OK) { - tr_error("HAL_ETH_Init issue"); - /* HAL_ETH_Init returns TIMEOUT when Ethernet cable is not plugged */; - } - - // Set MAC address - writeMACAddress(MACAddr, &EthHandle.Instance->MACA0HR, &EthHandle.Instance->MACA0LR); - - uint32_t TempRegisterValue; - if (HAL_ETH_ReadPHYRegister(&EthHandle, 2, &TempRegisterValue) != HAL_OK) { - tr_error("HAL_ETH_ReadPHYRegister 2 issue"); - } - PHY_ID = (TempRegisterValue << 16); - if (HAL_ETH_ReadPHYRegister(&EthHandle, 3, &TempRegisterValue) != HAL_OK) { - tr_error("HAL_ETH_ReadPHYRegister 3 issue"); - } - PHY_ID |= (TempRegisterValue & 0XFFF0); - tr_info("PHY ID %#X", PHY_ID); - - /* Initialize Tx Descriptors list: Chain Mode */ - if (HAL_ETH_DMATxDescListInit(&EthHandle, DMATxDscrTab, reinterpret_cast(&Tx_Buff[0][0]), ETH_TXBUFNB) != HAL_OK) { - tr_error("HAL_ETH_DMATxDescListInit issue"); - return false; - } - - /* Initialize Rx Descriptors list: Chain Mode */ - if (HAL_ETH_DMARxDescListInit(&EthHandle, DMARxDscrTab, reinterpret_cast(Rx_Buff[0].data()), ETH_RXBUFNB) != HAL_OK) { - tr_error("HAL_ETH_DMARxDescListInit issue"); - return false; - } - - /* Configure MAC */ - _eth_config_mac(&EthHandle); - - /* Enable MAC and DMA transmission and reception */ - if (HAL_ETH_Start(&EthHandle) != HAL_OK) { - tr_error("HAL_ETH_Start issue"); - return false; - } - - tr_info("low_level_init_successful"); - return true; -} -#else // ETH_IP_VERSION_V2 -{ - // Generate a reference to this empty function so the linker pulls it in. - stm32_eth_init_weak_symbol_helper(); - - /* Init ETH */ - uint8_t MACAddr[6]; - EthHandle.Instance = ETH; -#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE) - MACAddr[0] = MBED_MAC_ADDR_0; - MACAddr[1] = MBED_MAC_ADDR_1; - MACAddr[2] = MBED_MAC_ADDR_2; - MACAddr[3] = MBED_MAC_ADDR_3; - MACAddr[4] = MBED_MAC_ADDR_4; - MACAddr[5] = MBED_MAC_ADDR_5; -#else - mbed_mac_address((char *)MACAddr); -#endif - EthHandle.Init.MACAddr = &MACAddr[0]; - EthHandle.Init.MediaInterface = HAL_ETH_RMII_MODE; - - tr_debug("MAC Addr %02x:%02x:%02x:%02x:%02x:%02x", MACAddr[0], MACAddr[1], MACAddr[2], MACAddr[3], MACAddr[4], MACAddr[5]); - - if (HAL_ETH_Init(&EthHandle) != HAL_OK) { - return false; - } - - // Set MAC address - writeMACAddress(MACAddr, &EthHandle.Instance->MACA0HR, &EthHandle.Instance->MACA0LR); - - // Enable multicast hash and perfect filter - EthHandle.Instance->MACPFR = ETH_MACPFR_HMC | ETH_MACPFR_HPF; - - // Enable Tx, Rx, and fatal bus error interrupts. - // However, don't enable receive buffer unavailable interrupt, because that can - // trigger if we run out of Rx descriptors, and we don't want to fatal error - // in that case. - EthHandle.Instance->DMACIER = (ETH_DMACIER_NIE | ETH_DMACIER_RIE | ETH_DMACIER_TIE | - ETH_DMACIER_FBEE | ETH_DMACIER_AIE); - - // Disable checksum offload, this is enabled by HAL_ETH_Init - EthHandle.Instance->MACCR &= ~ETH_MACCR_IPC; - - // Init the DMA rings - dmaRings.emplace(*memory_manager, EthHandle, emac_link_input_cb); - if (dmaRings->startDMA() != HAL_OK) { - return false; - } - - // Enable the MAC transmission & reception - // TODO should only do this once the link goes up - EthHandle.Instance->MACCR |= ETH_MACCR_TE | ETH_MACCR_RE; - - tr_info("low_level_init_successful"); - return _phy_init(); -} -#endif // ETH_IP_VERSION_V2 - -/** - * This function should do the actual transmission of the packet. The packet is - * contained in the memory buffer chain that is passed to the function. - * - * @param buf the MAC packet to send (e.g. IP packet including MAC addresses and type) - * @return true if the packet could be sent - * false value if the packet couldn't be sent - * - * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to - * strange results. You might consider waiting for space in the DMA queue - * to become availale since the stack doesn't retry to send a packet - * dropped because of memory failure (except for the TCP timers). - */ -bool STM32_EMAC::link_out(emac_mem_buf_t *buf) -#ifndef ETH_IP_VERSION_V2 -{ - bool success = true; - emac_mem_buf_t *q; - uint8_t *buffer = reinterpret_cast(EthHandle.TxDesc->Buffer1Addr); - __IO ETH_DMADescTypeDef *DmaTxDesc; - uint32_t framelength = 0; - uint32_t bufferoffset = 0; - uint32_t byteslefttocopy = 0; - uint32_t payloadoffset = 0; - DmaTxDesc = EthHandle.TxDesc; - - /* Get exclusive access */ - TXLockMutex.lock(); - - /* copy frame from pbufs to driver buffers */ - for (q = buf; q != NULL; q = memory_manager->get_next(q)) { - /* Is this buffer available? If not, goto error */ - if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) { - success = false; - goto error; - } - - /* Get bytes in current lwIP buffer */ - byteslefttocopy = memory_manager->get_len(q); - payloadoffset = 0; - - /* Check if the length of data to copy is bigger than Tx buffer size*/ - while ((byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE) { - /* Copy data to Tx buffer*/ - memcpy(static_cast(buffer) + bufferoffset, static_cast(memory_manager->get_ptr(q)) + payloadoffset, (ETH_TX_BUF_SIZE - bufferoffset)); - - /* Point to next descriptor */ - DmaTxDesc = reinterpret_cast(DmaTxDesc->Buffer2NextDescAddr); - - /* Check if the buffer is available */ - if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) { - success = false; - goto error; - } - - buffer = reinterpret_cast(DmaTxDesc->Buffer1Addr); - - byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset); - payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset); - framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset); - bufferoffset = 0; - } - - /* Copy the remaining bytes */ - memcpy(static_cast(buffer) + bufferoffset, static_cast(memory_manager->get_ptr(q)) + payloadoffset, byteslefttocopy); - bufferoffset = bufferoffset + byteslefttocopy; - framelength = framelength + byteslefttocopy; - } - - /* Prepare transmit descriptors to give to DMA */ - if (HAL_ETH_TransmitFrame(&EthHandle, framelength) != HAL_OK) { - tr_error("HAL_ETH_TransmitFrame issue"); - success = false; - } - -error: - - /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */ - if ((EthHandle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET) { - /* Clear TUS ETHERNET DMA flag */ - EthHandle.Instance->DMASR = ETH_DMASR_TUS; - - /* Resume DMA transmission*/ - EthHandle.Instance->DMATPDR = 0; - } - - memory_manager->free(buf); - - /* Restore access */ - TXLockMutex.unlock(); - - return success; -} -#else // ETH_IP_VERSION_V2 -{ - /* Get exclusive access */ - TXLockMutex.lock(); - - bool success = dmaRings->txPacket(buf) == HAL_OK; - - /* Restore access */ - TXLockMutex.unlock(); - - return success; -} -#endif // ETH_IP_VERSION_V2 - -#ifndef ETH_IP_VERSION_V2 -/** - * Should allocate a contiguous memory buffer and transfer the bytes of the incoming - * packet to the buffer. - * - * @param buf If a frame was received and the memory buffer allocation was successful, a memory - * buffer filled with the received packet (including MAC header) - * @return negative value when no more frames, - * zero when frame is received - */ -int STM32_EMAC::low_level_input(emac_mem_buf_t **buf) -{ - uint32_t len = 0; - uint8_t *buffer; - __IO ETH_DMADescTypeDef *dmarxdesc; - uint32_t bufferoffset = 0; - uint32_t byteslefttocopy = 0; - emac_mem_buf_t *q; - uint32_t payloadoffset = 0; - - /* get received frame */ - if (HAL_ETH_GetReceivedFrame_IT(&EthHandle) != HAL_OK) { - tr_debug_deep("low_level_input no frame"); - return -1; - } - - /* Obtain the size of the packet and put it into the "len" variable. */ - len = EthHandle.RxFrameInfos.length; - buffer = reinterpret_cast(EthHandle.RxFrameInfos.buffer); - byteslefttocopy = len; - - dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; - - if (len > 0 && len <= ETH_RX_BUF_SIZE) { - tr_debug_deep("low_level_input len %u", len); - /* Allocate a memory buffer chain from buffer pool */ - *buf = memory_manager->alloc_pool(len, 0); - } - - if (*buf != NULL) { - dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; - bufferoffset = 0; - for (q = *buf; q != NULL; q = memory_manager->get_next(q)) { - byteslefttocopy = memory_manager->get_len(q); - payloadoffset = 0; - - /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/ - while ((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE) { - /* Copy data to pbuf */ - memcpy(static_cast(memory_manager->get_ptr(q)) + payloadoffset, static_cast(buffer) + bufferoffset, ETH_RX_BUF_SIZE - bufferoffset); - - /* Point to next descriptor */ - dmarxdesc = reinterpret_cast(dmarxdesc->Buffer2NextDescAddr); - buffer = reinterpret_cast(dmarxdesc->Buffer1Addr); - - byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset); - payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset); - bufferoffset = 0; - } - /* Copy remaining data in pbuf */ - memcpy(static_cast(memory_manager->get_ptr(q)) + payloadoffset, static_cast(buffer) + bufferoffset, byteslefttocopy); - bufferoffset = bufferoffset + byteslefttocopy; - } - } - - /* Release descriptors to DMA */ - /* Point to first descriptor */ - dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; - /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ - for (uint32_t i = 0; i < EthHandle.RxFrameInfos.SegCount; i++) { - dmarxdesc->Status |= ETH_DMARXDESC_OWN; - dmarxdesc = reinterpret_cast(dmarxdesc->Buffer2NextDescAddr); - } - - /* Clear Segment_Count */ - EthHandle.RxFrameInfos.SegCount = 0; - - /* When Rx Buffer unavailable flag is set: clear it and resume reception */ - if ((EthHandle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) { - /* Clear RBUS ETHERNET DMA flag */ - EthHandle.Instance->DMASR = ETH_DMASR_RBUS; - /* Resume DMA reception */ - EthHandle.Instance->DMARPDR = 0; - } - return 0; -} -#else // ETH_IP_VERSION_V2 -#endif // ETH_IP_VERSION_V2 - -/** - * This task checks phy link status and updates net status - */ -void STM32_EMAC::phy_task() -#ifndef ETH_IP_VERSION_V2 -{ - uint32_t status; - - if (HAL_ETH_ReadPHYRegister(&EthHandle, PHY_BSR, &status) == HAL_OK) { - if ((emac_link_state_cb) && (status != 0xFFFF)) { - if ((status & PHY_LINKED_STATUS) && !(phy_status & PHY_LINKED_STATUS)) { - tr_info("emac_link_state_cb set to true"); - emac_link_state_cb(true); - } else if (!(status & PHY_LINKED_STATUS) && (phy_status & PHY_LINKED_STATUS)) { - tr_info("emac_link_state_cb set to false"); - emac_link_state_cb(false); - } - } - phy_status = status; - } else { - tr_error("HAL_ETH_ReadPHYRegister issue"); - } - -} -#else // ETH_IP_VERSION_V2 -{ - const int32_t status = _phy_get_state(); - const int32_t old_status = (int32_t)phy_status; - const bool is_up = _phy_is_up(status); - const bool was_up = _phy_is_up(old_status); - - if (is_up && !was_up) { - uint32_t duplex, speed; - ETH_MACConfigTypeDef MACConf; - - if (!_phy_get_duplex_and_speed(status, &speed, &duplex)) { - // Default - duplex = ETH_FULLDUPLEX_MODE; - speed = ETH_SPEED_10M; - } - - /* Get MAC Config MAC */ - HAL_ETH_GetMACConfig(&EthHandle, &MACConf); - MACConf.DuplexMode = duplex; - MACConf.Speed = speed; - HAL_ETH_SetMACConfig(&EthHandle, &MACConf); - HAL_ETH_Start_IT(&EthHandle); - } else if (was_up && !is_up) { - // Stop ETH - disable_interrupts(); - HAL_ETH_Stop(&EthHandle); - enable_interrupts(); - } - - if (emac_link_state_cb) { - if (is_up && !was_up) { - emac_link_state_cb(true); - tr_info("emac_link_state_cb set to true"); - } else if (!is_up && was_up) { - emac_link_state_cb(false); - tr_info("emac_link_state_cb set to false"); - } - } - - phy_status = (uint32_t)status; -} -#endif // ETH_IP_VERSION_V2 - -#if defined (STM32F767xx) || defined (STM32F769xx) || defined (STM32F777xx)\ - || defined (STM32F779xx) -/** - * workaround for the ETH RMII bug in STM32F76x and STM32F77x revA - * - * \param[in] netif the lwip network interface structure - */ - -/** \brief Worker thread. - * - * Woken by thread flags to receive packets or clean up transmit - * - * \param[in] pvParameters pointer to the interface data - */ -void STM32_EMAC::rmii_watchdog_thread_function(void *pvParameters) -{ - struct STM32_EMAC *stm32_enet = static_cast(pvParameters); - - while (1) { - /* some good packets are received */ - if (stm32_enet->EthHandle.Instance->MMCRGUFCR > 0) { - /* RMII Init is OK - would need service to terminate or suspend - * the thread */ - while (1) { - /* don't do anything anymore */ - osDelay(0xFFFFFFFF); - } - } else if (stm32_enet->EthHandle.Instance->MMCRFCECR > 10) { - /* ETH received too many packets with CRC errors, resetting RMII */ - SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL; - SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; - stm32_enet->EthHandle.Instance->MMCCR |= ETH_MMCCR_CR; - } else { - osDelay(100); - } - } -} -#endif - -void STM32_EMAC::enable_interrupts(void) -{ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); -} - -void STM32_EMAC::disable_interrupts(void) -{ - NVIC_DisableIRQ(ETH_IRQn); -} - -/** This returns a unique 6-byte MAC address, based on the device UID -* This function overrides hal/common/mbed_interface.c function -* @param mac A 6-byte array to write the MAC address -*/ - -void mbed_mac_address(char *mac) -{ - if (mbed_otp_mac_address(mac)) { - return; - } else { - mbed_default_mac_address(mac); - } - return; -} - -MBED_WEAK uint8_t mbed_otp_mac_address(char *mac) -{ - return 0; -} - -void mbed_default_mac_address(char *mac) -{ - unsigned char ST_mac_addr[3] = {0x00, 0x80, 0xe1}; // default STMicro mac address - - // Read unic id -#if defined (TARGET_STM32F2) - uint32_t word0 = *(uint32_t *)0x1FFF7A10; -#elif defined (TARGET_STM32F4) - uint32_t word0 = *(uint32_t *)0x1FFF7A10; -#elif defined (TARGET_STM32F7) - uint32_t word0 = *(uint32_t *)0x1FF0F420; -#elif defined (TARGET_STM32H7) - uint32_t word0 = *(uint32_t *)0x1FF1E800; -#else -#error MAC address can not be derived from target unique Id -#endif - - mac[0] = ST_mac_addr[0]; - mac[1] = ST_mac_addr[1]; - mac[2] = ST_mac_addr[2]; - mac[3] = (word0 & 0x00ff0000) >> 16; - mac[4] = (word0 & 0x0000ff00) >> 8; - mac[5] = (word0 & 0x000000ff); - - return; -} - -bool STM32_EMAC::power_up() -{ - sleep_manager_lock_deep_sleep(); - - /* Initialize the hardware */ - if (!low_level_init_successful()) { - return false; - } - - /* Allow the PHY task to detect the initial link state and set up the proper flags */ - phy_task(); - - phy_task_handle = mbed::mbed_event_queue()->call_every(PHY_TASK_PERIOD, mbed::callback(this, &STM32_EMAC::phy_task)); - -#if defined (STM32F767xx) || defined (STM32F769xx) || defined (STM32F777xx)\ - || defined (STM32F779xx) - rmii_watchdog_thread = create_new_thread("stm32_rmii_watchdog", &STM32_EMAC::rmii_watchdog_thread_function, this, 128, THREAD_PRIORITY, &rmii_watchdog_thread_cb); -#endif - - // Set up interrupt handler - NVIC_SetVector(ETH_IRQn, reinterpret_cast(&STM32_EMAC::irqHandler)); - enable_interrupts(); - - /* Enable ETH DMA interrupts: - - Tx complete interrupt - - Rx complete interrupt - - Fatal bus interrupt - */ - ETH->DMACIER = ETH_DMACIER_NIE | ETH_DMACIER_RIE | ETH_DMACIER_TIE | ETH_DMACIER_FBEE | ETH_DMACIER_AIE; - - return true; -} - -uint32_t STM32_EMAC::get_mtu_size() const -{ - return STM_ETH_MTU_SIZE; -} - -uint32_t STM32_EMAC::get_align_preference() const -{ - return 0; -} - -void STM32_EMAC::get_ifname(char *name, uint8_t size) const -{ - memcpy(name, STM_ETH_IF_NAME, (size < sizeof(STM_ETH_IF_NAME)) ? size : sizeof(STM_ETH_IF_NAME)); -} - -uint8_t STM32_EMAC::get_hwaddr_size() const -{ - return STM_HWADDR_SIZE; -} - -bool STM32_EMAC::get_hwaddr(uint8_t *addr) const -{ - mbed_mac_address((char *)addr); - return true; -} - -void STM32_EMAC::set_hwaddr(const uint8_t *addr) -{ - /* No-op at this stage */ -} - -void STM32_EMAC::set_link_input_cb(emac_link_input_cb_t input_cb) -{ - emac_link_input_cb = input_cb; -} - -void STM32_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb) -{ - emac_link_state_cb = state_cb; -} - -void STM32_EMAC::add_multicast_group(const uint8_t *addr) -{ - if(numSubscribedMcastMacs >= MBED_CONF_STM32_EMAC_MAX_MCAST_SUBSCRIBES) - { - tr_error("Out of multicast group entries (currently have %d). Increase the 'stm32-emac.max-mcast-subscribes' JSON option!", MBED_CONF_STM32_EMAC_MAX_MCAST_SUBSCRIBES); - return; - } - - memcpy(mcastMacs[numSubscribedMcastMacs++].data(), addr, 6); - populateMcastFilterRegs(); -} - -void STM32_EMAC::remove_multicast_group(const uint8_t *addr) -{ - // Find MAC address in the subscription list - auto macsEndIter = std::begin(mcastMacs) + numSubscribedMcastMacs; - auto toRemoveIter = std::find_if(std::begin(mcastMacs), macsEndIter, [&](auto element) { - return memcmp(element.data(), addr, 6) == 0; - }); - - if(toRemoveIter == macsEndIter) - { - tr_warning("Tried to remove mcast group that was not added"); - return; - } - - // Swap the MAC addr to be removed to the end of the list, if it is not there already - auto lastElementIter = macsEndIter - 1; - if(toRemoveIter != std::begin(mcastMacs) && toRemoveIter != lastElementIter) - { - std::swap(*toRemoveIter, *lastElementIter); - } - - // 'remove' the last element by changing the length - numSubscribedMcastMacs--; - - // Rebuild the MAC registers with that MAC removed. - // Technically it would be more performance efficient to remove just this MAC address, but that gets complex - // once you throw the hash filter into the mix. Unless you are subscribed to insane numbers of mcast addrs, - // it's easier to just rebuild it all. - populateMcastFilterRegs(); -} - -void STM32_EMAC::set_all_multicast(bool all) -{ -#if defined(ETH_IP_VERSION_V2) - if(all) - { - EthHandle.Instance->MACPFR |= ETH_MACPFR_PM; - } - else - { - EthHandle.Instance->MACPFR &= ~ETH_MACPFR_PM; - } -#else - if(all) - { - EthHandle.Instance->MACFFR |= ETH_MACFFR_PM; - } - else - { - EthHandle.Instance->MACFFR &= ~ETH_MACFFR_PM; - } -#endif -} - -void STM32_EMAC::power_down() -{ - /* No-op at this stage */ - sleep_manager_unlock_deep_sleep(); -} - -void STM32_EMAC::set_memory_manager(EMACMemoryManager &mem_mngr) -{ - memory_manager = &mem_mngr; -} - -STM32_EMAC &STM32_EMAC::get_instance() -{ - static STM32_EMAC emac; - return emac; -} - -void STM32_EMAC::irqHandler() -{ - uint32_t dma_flag = ETH->DMACSR; - - /* Packet received */ - if ((dma_flag & ETH_DMACSR_RI) != 0U) - { - /* Clear the Eth DMA Rx IT pending bits */ - ETH->DMACSR = ETH_DMACSR_RI | ETH_DMACSR_NIS; - - get_instance().dmaRings->rxISR(); - } - - /* Packet transmitted */ - if ((dma_flag & ETH_DMACSR_TI) != 0U) - { - /* Clear the Eth DMA Tx IT pending bits */ - ETH->DMACSR = ETH_DMACSR_TI | ETH_DMACSR_NIS; - - get_instance().dmaRings->txISR(); - } - - /* ETH DMA Error */ - if(dma_flag & ETH_DMACSR_FBE) - { - MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_ETHERNET, EIO), \ - "STM32 EMAC: Hardware reports fatal DMA Error\n"); - } -} - -void STM32_EMAC::populateMcastFilterRegs() { - const size_t NUM_PERFECT_FILTER_REGS = 3; - - const size_t numPerfectFilterMacs = std::min(NUM_PERFECT_FILTER_REGS, numSubscribedMcastMacs); - const size_t numHashFilterMacs = numSubscribedMcastMacs - numPerfectFilterMacs; - - for(size_t perfFiltIdx = 0; perfFiltIdx < NUM_PERFECT_FILTER_REGS; ++perfFiltIdx) - { - // Find MAC addr registers (they aren't in an array :/) - uint32_t volatile * highReg; - uint32_t volatile * lowReg; - - if(perfFiltIdx == 0) - { - highReg = &EthHandle.Instance->MACA1HR; - lowReg = &EthHandle.Instance->MACA1LR; - } - else if(perfFiltIdx == 1) - { - highReg = &EthHandle.Instance->MACA2HR; - lowReg = &EthHandle.Instance->MACA2LR; - } - else - { - highReg = &EthHandle.Instance->MACA3HR; - lowReg = &EthHandle.Instance->MACA3LR; - } - - if(perfFiltIdx < numPerfectFilterMacs) - { - tr_debug("Using perfect filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, - mcastMacs[perfFiltIdx][0], mcastMacs[perfFiltIdx][1], mcastMacs[perfFiltIdx][2], - mcastMacs[perfFiltIdx][3], mcastMacs[perfFiltIdx][4], mcastMacs[perfFiltIdx][5]); - writeMACAddress(mcastMacs[perfFiltIdx].data(), highReg, lowReg); - } - else - { - // Write zeroes to disable this mac addr entry - *highReg = 0; - *lowReg = 0; - } - } - -#if defined(ETH_IP_VERSION_V2) - uint32_t volatile * hashRegs[] = { - &EthHandle.Instance->MACHT0R, - &EthHandle.Instance->MACHT1R - }; -#else - uint32_t volatile * hashRegs[] = { - &EthHandle.Instance->MACHTLR, - &EthHandle.Instance->MACHTHR - }; -#endif - - // Reset hash filter regs - *hashRegs[0] = 0; - *hashRegs[1] = 0; - - // Note: as always, the datasheet description of how to do this CRC was vague and slightly wrong. - // This forum thread figured it out: https://community.st.com/t5/stm32-mcus-security/calculating-ethernet-multicast-filter-hash-value/td-p/416984 - // What the datasheet SHOULD say is: - // Compute the Ethernet CRC-32 of the MAC address, with initial value of 1s, final XOR of ones, and input reflection on but output reflection off - // Then, take the upper 6 bits and use that to index the hash table. - - mbed::MbedCRC crcCalc(0xFFFFFFFF, 0xFFFFFFFF, true, false); - for(size_t hashFiltIdx = 0; hashFiltIdx < numHashFilterMacs; ++hashFiltIdx) - { - auto & currMacAddr = mcastMacs[hashFiltIdx + numPerfectFilterMacs]; - - tr_debug("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8, - currMacAddr[0], currMacAddr[1], currMacAddr[2], - currMacAddr[3], currMacAddr[4], currMacAddr[5]); - - // Compute Ethernet CRC-32 of the MAC address - uint32_t crc; - crcCalc.compute(currMacAddr.data(), currMacAddr.size(), &crc); - - // Take upper 6 bits - uint32_t hashVal = crc >> 26; - - // Set correct bit in hash filter - *hashRegs[hashVal >> 5] |= (1 << (hashVal & 0x1F)); - } -} - -void STM32_EMAC::writeMACAddress(const uint8_t *MAC, volatile uint32_t *addrHighReg, volatile uint32_t *addrLowReg) { - /* Set MAC addr bits 32 to 47 */ - *addrHighReg = (static_cast(MAC[5]) << 8) | static_cast(MAC[4]) | ETH_MACA1HR_AE_Msk; - /* Set MAC addr bits 0 to 31 */ - *addrLowReg = (static_cast(MAC[3]) << 24) | (static_cast(MAC[2]) << 16) | - (static_cast(MAC[1]) << 8) | static_cast(MAC[0]); -}// Weak so a module can override -MBED_WEAK EMAC &EMAC::get_default_instance() -{ - return STM32_EMAC::get_instance(); -} - -#if defined(ETH_IP_VERSION_V2) -/******************************************************************************* - PHI IO Functions -*******************************************************************************/ - -/** - * @brief Initializes the MDIO interface GPIO and clocks. - * @param None - * @retval 0 if OK, -1 if ERROR - */ -static int32_t ETH_PHY_IO_Init(void) -{ - /* We assume that MDIO GPIO configuration is already done - in the ETH_MspInit() else it should be done here - */ - STM32_EMAC &emac = STM32_EMAC::get_instance(); - - /* Configure the MDIO Clock */ - HAL_ETH_SetMDIOClockRange(&emac.EthHandle); - - return 0; -} - -/** - * @brief De-Initializes the MDIO interface . - * @param None - * @retval 0 if OK, -1 if ERROR - */ -static int32_t ETH_PHY_IO_DeInit(void) -{ - return 0; -} - -/** - * @brief Read a PHY register through the MDIO interface. - * @param DevAddr: PHY port address - * @param RegAddr: PHY register address - * @param pRegVal: pointer to hold the register value - * @retval 0 if OK -1 if Error - */ -static int32_t ETH_PHY_IO_ReadReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t *pRegVal) -{ - STM32_EMAC &emac = STM32_EMAC::get_instance(); - if (HAL_ETH_ReadPHYRegister(&emac.EthHandle, DevAddr, RegAddr, pRegVal) != HAL_OK) { - return -1; - } - - return 0; -} - -/** - * @brief Write a value to a PHY register through the MDIO interface. - * @param DevAddr: PHY port address - * @param RegAddr: PHY register address - * @param RegVal: Value to be written - * @retval 0 if OK -1 if Error - */ -static int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t RegVal) -{ - STM32_EMAC &emac = STM32_EMAC::get_instance(); - if (HAL_ETH_WritePHYRegister(&emac.EthHandle, DevAddr, RegAddr, RegVal) != HAL_OK) { - return -1; - } - - return 0; -} - -/** - * @brief Get the time in millisecons used for internal PHY driver process. - * @retval Time value - */ -static int32_t ETH_PHY_IO_GetTick(void) -{ - return HAL_GetTick(); -} - -/** - * Ethernet DMA transfer error callbacks - */ -void HAL_ETH_DMAErrorCallback(ETH_HandleTypeDef *heth) -{ - MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_ETHERNET, EIO), \ - "Error from ethernet HAL (HAL_ETH_DMAErrorCallback)\n"); -} - -/** - * Ethernet MAC transfer error callbacks - */ -void HAL_ETH_MACErrorCallback(ETH_HandleTypeDef *heth) -{ - MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_ETHERNET, EIO), \ - "Error from ethernet HAL (HAL_ETH_MACErrorCallback)\n"); -} -#endif // ETH_IP_VERSION_V2 - -#endif /* DEVICE_EMAC */ diff --git a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h deleted file mode 100644 index 6a6e7e11561..00000000000 --- a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h +++ /dev/null @@ -1,201 +0,0 @@ -/* Copyright (c) 2017 ARM Limited - * Copyright (c) 2017 STMicroelectronics - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef STM32_EMAC_H_ -#define STM32_EMAC_H_ - -#include "EMAC.h" -#include "rtos/Mutex.h" -#include "rtos/Thread.h" - -#include "STM32EthIPv2DMARings.h" -#include "GenericEthDMA.h" - -#include - -class STM32_EMAC : public EMAC { -public: - STM32_EMAC(); - - static STM32_EMAC &get_instance(); - - /** - * Return maximum transmission unit - * - * @return MTU in bytes - */ - virtual uint32_t get_mtu_size() const; - - /** - * Gets memory buffer alignment preference - * - * Gets preferred memory buffer alignment of the Emac device. IP stack may or may not - * align link out memory buffer chains using the alignment. - * - * @return Memory alignment requirement in bytes - */ - virtual uint32_t get_align_preference() const; - - /** - * Return interface name - * - * @param name Pointer to where the name should be written - * @param size Maximum number of character to copy - */ - virtual void get_ifname(char *name, uint8_t size) const; - - /** - * Returns size of the underlying interface HW address size. - * - * @return HW address size in bytes - */ - virtual uint8_t get_hwaddr_size() const; - - /** - * Return interface-supplied HW address - * - * Copies HW address to provided memory, @param addr has to be of correct size see @a get_hwaddr_size - * - * HW address need not be provided if this interface does not have its own HW - * address configuration; stack will choose address from central system - * configuration if the function returns false and does not write to addr. - * - * @param addr HW address for underlying interface - * @return true if HW address is available - */ - virtual bool get_hwaddr(uint8_t *addr) const; - - /** - * Set HW address for interface - * - * Provided address has to be of correct size, see @a get_hwaddr_size - * - * Called to set the MAC address to actually use - if @a get_hwaddr is provided - * the stack would normally use that, but it could be overridden, eg for test - * purposes. - * - * @param addr Address to be set - */ - virtual void set_hwaddr(const uint8_t *addr); - - /** - * Sends the packet over the link - * - * That can not be called from an interrupt context. - * - * @param buf Packet to be send - * @return True if the packet was send successfully, False otherwise - */ - virtual bool link_out(emac_mem_buf_t *buf); - - /** - * Initializes the HW - * - * @return True on success, False in case of an error. - */ - virtual bool power_up(); - - /** - * Deinitializes the HW - * - */ - virtual void power_down(); - - /** - * Sets a callback that needs to be called for packets received for that interface - * - * @param input_cb Function to be register as a callback - */ - virtual void set_link_input_cb(emac_link_input_cb_t input_cb); - - /** - * Sets a callback that needs to be called on link status changes for given interface - * - * @param state_cb Function to be register as a callback - */ - virtual void set_link_state_cb(emac_link_state_change_cb_t state_cb); - - /** Add device to a multicast group - * - * @param address A multicast group hardware address - */ - virtual void add_multicast_group(const uint8_t *address); - - /** Remove device from a multicast group - * - * @param address A multicast group hardware address - */ - virtual void remove_multicast_group(const uint8_t *address); - - /** Request reception of all multicast packets - * - * @param all True to receive all multicasts - * False to receive only multicasts addressed to specified groups - */ - virtual void set_all_multicast(bool all); - - /** Sets memory manager that is used to handle memory buffers - * - * @param mem_mngr Pointer to memory manager - */ - virtual void set_memory_manager(EMACMemoryManager &mem_mngr); - - // Called from driver functions - ETH_HandleTypeDef EthHandle; - - -private: - - bool low_level_init_successful(); - void packet_rx(); - int low_level_input(emac_mem_buf_t **buf); - void thread_function(); - static void rmii_watchdog_thread_function(void *pvParameters); - void phy_task(); - void enable_interrupts(); - void disable_interrupts(); - static void irqHandler(); - - // Populate multicast filter registers with the contents of mcastMacs. - // Uses the perfect filter registers first, then the hash filter. - void populateMcastFilterRegs(); - - // Write a MAC address into a high and low register pair on the MAC. - static void writeMACAddress(uint8_t const * MAC, uint32_t volatile * addrHighReg, uint32_t volatile * addrLowReg); - - mbed_rtos_storage_thread_t thread_cb; -#if defined (STM32F767xx) || defined (STM32F769xx) || defined (STM32F777xx)\ - || defined (STM32F779xx) - mbed_rtos_storage_thread_t rmii_watchdog_thread_cb; - osThreadId_t rmii_watchdog_thread; /**< Watchdog processing thread */ -#endif - rtos::Mutex TXLockMutex;/**< TX critical section mutex */ - emac_link_state_change_cb_t emac_link_state_cb; /**< Link state change callback */ - emac_link_input_cb_t emac_link_input_cb; /**< Callback for incoming packets */ - EMACMemoryManager *memory_manager; /**< Memory manager */ - - std::optional dmaRings; - - uint32_t phy_status; - int phy_task_handle; /**< Handle for phy task event */ - - // Multicast subscribe information - std::array mcastMacs[MBED_CONF_STM32_EMAC_MAX_MCAST_SUBSCRIBES]; - size_t numSubscribedMcastMacs; -}; - -#endif /* STM32_EMAC_H_ */ diff --git a/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h b/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h index 13f2fcd56ff..cc04f15976e 100644 --- a/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h +++ b/connectivity/netsocket/include/netsocket/NetStackMemoryManager.h @@ -104,7 +104,11 @@ class NetStackMemoryManager { */ uint32_t get_pool_size() { +#if UNITTEST + return 0; +#else return MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS; +#endif } /** diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp index 7e7f96ec1e0..48a4c7cb38d 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp @@ -44,4 +44,13 @@ void test_emac_initialize() worker_loop_link_up_wait(); } +/* + * Test which powers the EMAC down and then up again + */ +void test_emac_power_down_and_power_up() { + EmacTestNetworkStack::get_instance().get_emac()->power_down(); + + TEST_ASSERT_TRUE(EmacTestNetworkStack::get_instance().get_emac()->power_up()) +} + #endif // defined(MBED_CONF_RTOS_PRESENT) diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_tests.h b/connectivity/netsocket/tests/TESTS/network/emac/emac_tests.h index 6dbcd340a32..900f406f5cf 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_tests.h +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_tests.h @@ -19,6 +19,7 @@ #define EMAC_TESTS_H void test_emac_initialize(); +void test_emac_power_down_and_power_up(); void test_emac_broadcast(); void test_emac_unicast(); void test_emac_unicast_frame_len(); diff --git a/connectivity/netsocket/tests/TESTS/network/emac/main.cpp b/connectivity/netsocket/tests/TESTS/network/emac/main.cpp index b6bd7a5c3f2..024c5008147 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/main.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/main.cpp @@ -48,6 +48,8 @@ Case cases[] = { Case("EMAC initialize", test_emac_initialize), Case("EMAC broadcast", test_emac_broadcast), // note: this test case has the side effect of finding the CTP server MAC address and saving it Case("EMAC unicast", test_emac_unicast), + Case("EMAC power down and power up", test_emac_power_down_and_power_up), + Case("EMAC unicast again after power cycle", test_emac_unicast), Case("EMAC unicast frame length", test_emac_unicast_frame_len), Case("EMAC unicast burst", test_emac_unicast_burst), Case("EMAC unicast long", test_emac_unicast_long), From 126bfd5f8fe16c06e60bc2eadb4a6f4a152b853e Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 2 Mar 2025 00:04:04 -0800 Subject: [PATCH 32/47] Debugging EMAC reboot test --- connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp | 4 +--- connectivity/drivers/emac/include/GenericEthDMA.h | 9 +++++---- .../tests/TESTS/network/emac/emac_test_initialize.cpp | 9 +++++++-- connectivity/netsocket/tests/TESTS/network/emac/main.cpp | 4 ++-- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp index 9a1c2a65b74..3201ca3cff9 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp @@ -138,7 +138,7 @@ namespace mbed { void STM32EthMACv2::RxDMA::stopDMA() { // Disable Rx DMA - base->DMACTCR &= ~ETH_DMACRCR_SR; + base->DMACRCR &= ~ETH_DMACRCR_SR; } #if __DCACHE_PRESENT @@ -254,8 +254,6 @@ namespace mbed { // Note: Following code is based on HAL_Eth_Init() from the HAL /* Init the low level hardware : GPIO, CLOCK, NVIC. */ EthInitPinmappings(); - - #ifdef TARGET_STM32H7 // Use RMII HAL_SYSCFG_ETHInterfaceSelect(SYSCFG_ETH_RMII); diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 43b708f4a3c..189bf277fa7 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -160,13 +160,14 @@ namespace mbed { // Free any buffers associated with the descriptor if (descStackBuffers[txReclaimIndex] != nullptr) { memory_manager->free(descStackBuffers[txReclaimIndex]); + descStackBuffers[txReclaimIndex] = nullptr; } // Update counters txReclaimIndex = (txReclaimIndex + 1) % MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; ++txDescsOwnedByApplication; - tr_debug("Reclaimed descriptor %zu", txReclaimIndex); + tr_info("Reclaimed descriptor %zu", txReclaimIndex); returnedAnyDescriptors = true; } @@ -211,7 +212,7 @@ namespace mbed { } } - tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", + tr_info("Transmitting packet of length %lu in %zu buffers and %zu descs\n", memory_manager->get_total_len(buf), memory_manager->count_buffers(buf), neededDescs); // Step 2: Copy packet if needed @@ -431,7 +432,7 @@ namespace mbed { rxBuildIndex = (rxBuildIndex + 1) % RX_NUM_DESCS; } - tr_debug("buildRxDescriptors(): Returned %zu descriptors.", origRxDescsOwnedByApplication - rxDescsOwnedByApplication); + tr_info("buildRxDescriptors(): Returned %zu descriptors.", origRxDescsOwnedByApplication - rxDescsOwnedByApplication); } bool rxHasPackets_ISR() override { @@ -584,7 +585,7 @@ namespace mbed { } #endif - tr_debug("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu\n", + tr_info("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu\n", memory_manager->get_total_len(headBuffer), memory_manager->get_ptr(headBuffer), *firstDescIdx, *lastDescIdx); return headBuffer; diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp index 48a4c7cb38d..9105becc1b8 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp @@ -47,10 +47,15 @@ void test_emac_initialize() /* * Test which powers the EMAC down and then up again */ -void test_emac_power_down_and_power_up() { +void test_emac_power_down_and_power_up() +{ EmacTestNetworkStack::get_instance().get_emac()->power_down(); - TEST_ASSERT_TRUE(EmacTestNetworkStack::get_instance().get_emac()->power_up()) + // Note: Currently the EMAC does not deliver a link state change to down callback when powered down. + // Might change that in the future but for now we need to deliver the callback manually. + emac_if_link_state_change_cb(false); + + TEST_ASSERT_TRUE(EmacTestNetworkStack::get_instance().get_emac()->power_up()); } #endif // defined(MBED_CONF_RTOS_PRESENT) diff --git a/connectivity/netsocket/tests/TESTS/network/emac/main.cpp b/connectivity/netsocket/tests/TESTS/network/emac/main.cpp index 024c5008147..b624897d308 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/main.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/main.cpp @@ -47,9 +47,9 @@ utest::v1::status_t test_setup(const size_t number_of_cases) Case cases[] = { Case("EMAC initialize", test_emac_initialize), Case("EMAC broadcast", test_emac_broadcast), // note: this test case has the side effect of finding the CTP server MAC address and saving it - Case("EMAC unicast", test_emac_unicast), Case("EMAC power down and power up", test_emac_power_down_and_power_up), - Case("EMAC unicast again after power cycle", test_emac_unicast), + Case("EMAC broadcast again after power cycle", test_emac_broadcast), + Case("EMAC unicast", test_emac_unicast), Case("EMAC unicast frame length", test_emac_unicast_frame_len), Case("EMAC unicast burst", test_emac_unicast_burst), Case("EMAC unicast long", test_emac_unicast_long), From a0145e574d210e6115c98f6f2ea2782c39bd41b5 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 2 Mar 2025 00:19:45 -0800 Subject: [PATCH 33/47] Fix deinit crash on STM32F7 --- .../drivers/emac/TARGET_STM/STM32EthMACv1.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp index 45ac602972f..a2cc5580c5c 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.cpp @@ -223,6 +223,9 @@ void STM32EthMACv1::MACDriver::ETH_SetMDIOClockRange(ETH_TypeDef * const base) { #if ENABLE_ERRATA_2_21_6_WORKAROUND void STM32EthMACv1::MACDriver::rmiiWatchdog() { + // mbed_event_queue() is not ISR safe, so get the pointer before entering the critical section + auto * const equeue = mbed_event_queue(); + CriticalSectionLock lock; if(!rmiiWatchdogRunning) { @@ -233,7 +236,7 @@ void STM32EthMACv1::MACDriver::rmiiWatchdog() { /* some good packets are received */ if (base->MMCRGUFCR > 0) { /* RMII Init is OK - cancel watchdog task */ - mbed_event_queue()->cancel(rmiiWatchdogRunning); + equeue->cancel(rmiiWatchdogHandle); rmiiWatchdogRunning = false; } else if (base->MMCRFCECR > 10) { /* ETH received too many packets with CRC errors, resetting RMII */ @@ -319,9 +322,14 @@ CompositeEMAC::ErrCode STM32EthMACv1::MACDriver::deinit() { #if ENABLE_ERRATA_2_21_6_WORKAROUND // Disable RMII watchdog if still running if(rmiiWatchdogRunning) { + // mbed_event_queue() is not ISR safe, so get the pointer before entering the critical section + auto * const equeue = mbed_event_queue(); + CriticalSectionLock lock; - mbed_event_queue()->cancel(rmiiWatchdogRunning); - rmiiWatchdogRunning = false; + if(rmiiWatchdogRunning) { // Recheck flag inside critical section + equeue->cancel(rmiiWatchdogHandle); + rmiiWatchdogRunning = false; + } } #endif From 7deab9586993676d23c83f3828491dc73314718a Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 11 Mar 2025 21:11:08 -0700 Subject: [PATCH 34/47] Fix a couple issues with the power down test --- .../drivers/emac/include/GenericEthDMA.h | 10 +++++---- .../network/emac/emac_test_initialize.cpp | 6 +++++ .../TESTS/network/emac/emac_test_unicast.cpp | 22 ++++++++++++++----- .../tests/TESTS/network/emac/main.cpp | 6 ++--- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 189bf277fa7..f89e6757287 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -167,7 +167,7 @@ namespace mbed { txReclaimIndex = (txReclaimIndex + 1) % MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; ++txDescsOwnedByApplication; - tr_info("Reclaimed descriptor %zu", txReclaimIndex); + tr_debug("Reclaimed descriptor %zu", txReclaimIndex); returnedAnyDescriptors = true; } @@ -212,7 +212,7 @@ namespace mbed { } } - tr_info("Transmitting packet of length %lu in %zu buffers and %zu descs\n", + tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", memory_manager->get_total_len(buf), memory_manager->count_buffers(buf), neededDescs); // Step 2: Copy packet if needed @@ -382,6 +382,8 @@ namespace mbed { public: CompositeEMAC::ErrCode init() override { rxPoolPayloadSize = memory_manager->get_pool_alloc_unit(RX_BUFFER_ALIGN); + rxBuildIndex = 0; + rxNextIndex = 0; // At the start, we own all the descriptors rxDescsOwnedByApplication = RX_NUM_DESCS; @@ -432,7 +434,7 @@ namespace mbed { rxBuildIndex = (rxBuildIndex + 1) % RX_NUM_DESCS; } - tr_info("buildRxDescriptors(): Returned %zu descriptors.", origRxDescsOwnedByApplication - rxDescsOwnedByApplication); + tr_debug("buildRxDescriptors(): Returned %zu descriptors.", origRxDescsOwnedByApplication - rxDescsOwnedByApplication); } bool rxHasPackets_ISR() override { @@ -585,7 +587,7 @@ namespace mbed { } #endif - tr_info("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu\n", + tr_debug("Returning packet of length %lu, start %p from Rx descriptors %zu-%zu\n", memory_manager->get_total_len(headBuffer), memory_manager->get_ptr(headBuffer), *firstDescIdx, *lastDescIdx); return headBuffer; diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp index 9105becc1b8..1a271821067 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_initialize.cpp @@ -56,6 +56,12 @@ void test_emac_power_down_and_power_up() emac_if_link_state_change_cb(false); TEST_ASSERT_TRUE(EmacTestNetworkStack::get_instance().get_emac()->power_up()); + + // Currently EMACs may expect set_hwaddr() to be called after power up as this API is not well defined. + EmacTestNetworkStack::get_instance().get_emac()->set_hwaddr(EmacTestNetworkStack::get_instance().get_mac_addr()); + + // Wait for link to come back up before continuing + worker_loop_link_up_wait(); } #endif // defined(MBED_CONF_RTOS_PRESENT) diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast.cpp index b78e609789d..3a53e568570 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast.cpp @@ -27,13 +27,13 @@ using namespace utest::v1; +static bool send_request; +static int no_response_cnt; +static int retries; +static int test_step = 0; + void test_emac_unicast_cb(int opt) { - static bool send_request = true; - static int no_response_cnt = 0; - static int retries = 0; - static int test_step = 0; - // Timeout if (opt == TIMEOUT && send_request) { CTP_MSG_SEND(100, emac_if_get_echo_server_addr(0), emac_if_get_own_addr(), emac_if_get_own_addr(), 0); @@ -41,7 +41,7 @@ void test_emac_unicast_cb(int opt) no_response_cnt = 0; } else if (opt == TIMEOUT) { if (++no_response_cnt > 5) { - if (++retries > 3) { + if (++retries > 300) { printf("too many retries\r\n\r\n"); SET_ERROR_FLAGS(TEST_FAILED); END_TEST_LOOP; @@ -65,6 +65,16 @@ void test_emac_unicast_cb(int opt) void test_emac_unicast() { + if(test_step != 0) { + printf("Got here!\n"); + } + + // Reset flags + send_request = true; + no_response_cnt = 0; + retries = 0; + test_step = 0; + RESET_ALL_ERROR_FLAGS; SET_TRACE_LEVEL(TRACE_SEND | TRACE_ETH_FRAMES | TRACE_SUCCESS | TRACE_FAILURE); diff --git a/connectivity/netsocket/tests/TESTS/network/emac/main.cpp b/connectivity/netsocket/tests/TESTS/network/emac/main.cpp index b624897d308..6cea89d148b 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/main.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/main.cpp @@ -47,8 +47,6 @@ utest::v1::status_t test_setup(const size_t number_of_cases) Case cases[] = { Case("EMAC initialize", test_emac_initialize), Case("EMAC broadcast", test_emac_broadcast), // note: this test case has the side effect of finding the CTP server MAC address and saving it - Case("EMAC power down and power up", test_emac_power_down_and_power_up), - Case("EMAC broadcast again after power cycle", test_emac_broadcast), Case("EMAC unicast", test_emac_unicast), Case("EMAC unicast frame length", test_emac_unicast_frame_len), Case("EMAC unicast burst", test_emac_unicast_burst), @@ -57,7 +55,9 @@ Case cases[] = { (MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI)) Case("EMAC multicast filter", test_emac_multicast_filter), #endif // !(MBED_CONF_NETWORK_EMAC_NO_SUPPORT_FOR_MULTICAST_FILTER == 1) - Case("EMAC memory", test_emac_memory) + Case("EMAC memory", test_emac_memory), + Case("EMAC power down and power up", test_emac_power_down_and_power_up), + Case("EMAC unicast again after power cycle", test_emac_unicast), }; Specification specification(test_setup, cases); From 292d566a2bb28e97f3debafdfbd2ef1322fc74f3 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Wed, 12 Mar 2025 09:58:47 -0700 Subject: [PATCH 35/47] Start on docs --- connectivity/drivers/emac/CompositeEMAC.md | 11 +++++++++++ connectivity/drivers/emac/include/CompositeEMAC.h | 5 +++-- connectivity/netsocket/include/netsocket/EMAC.h | 4 ++-- .../tests/TESTS/network/emac/emac_test_unicast.cpp | 4 ---- 4 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 connectivity/drivers/emac/CompositeEMAC.md diff --git a/connectivity/drivers/emac/CompositeEMAC.md b/connectivity/drivers/emac/CompositeEMAC.md new file mode 100644 index 00000000000..4b175e30aa7 --- /dev/null +++ b/connectivity/drivers/emac/CompositeEMAC.md @@ -0,0 +1,11 @@ +# Composite EMAC + +The original Mbed OS EMAC API was added in Mbed OS 5.9. That API grouped all EMAC functionality into a single abstract class that targets/vendors had to implement, the `EMAC` class. Since then, dozens of Mbed targets have received EMAC drivers, and the strengths and weaknesses of this API have become clear. The general structure is good, and the idea of abstracting the memory manager and the network stack from the EMAC driver works well. + +However, the EMAC interface is difficult to implement, especially for people not intimately familiar with Mbed and its IP stacks. It requires EMAC implementations to implement the specifics of memory management, MAC address tracking, and DMA ring usage themselves, even though quite a bit of this logic is common to all MAC drivers. This has led to duplicated code, and quite often to half-assed code as well as chip vendors have struggled to conform to the (in some ways not very well defined) EMAC API. + +Couple that with inconsistent testing, and you have a recipe for inconsistent and buggy Ethernet drivers across the breadth of Mbed devices. For instance, Mbed supports zero-copy EMACs, where buffers can be passed directly to and from the Ethernet peripheral without being copied. This saves both memory and CPU time. However, this was only ever implemented for a few targets, because it's very difficult to get right. Even more egregiously, the EMAC driver implemented for STM32H7 for the past 6+ years has ignored the memory manager API and used LwIP directly, making it impossible to test with the EMAC tests (and hoo boy, were there a lot of things that would have failed). For extra fun, this driver also ignored the DMA functionality and sent packets _synchronously_, meaning the application is blocked while a packet is being transmitted! + +To address this situation, Mbed CE is implementing a new layer in the EMAC driver stack: CompositeEMAC. CompositeEMAC is a class which implements the `EMAC` API and breaks up the functionality into several subclasses. + +![Overview diagram](./doc/cemac-overview.svg) \ No newline at end of file diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index cc2d6f60d0d..fb57fa7dd2f 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -90,7 +90,7 @@ class CompositeEMAC : public EMAC /** * @brief Abstract interface for a driver for the low level ethernet MAC hardware. * - * Thread safety: CompositeEMAC will guarantee only one thread is utilizing this class at a time. + * Thread safety: CompositeEMAC will guarantee only one thread is interacting with this class at a time. */ class MACDriver : NonCopyable { @@ -339,7 +339,8 @@ class CompositeEMAC : public EMAC * @brief Dequeue a packet, if one is ready to be received. * * This function should also dequeue and dispose of any error or incomplete DMA descriptors. - * The MAC thread calls it over and over again until it returns nullptr. + * After rxHasPackets_ISR() returns true, the MAC thread will call this it over and over again + * until it returns nullptr. * * @return Packet pointer, or nullptr if there were no packets. */ diff --git a/connectivity/netsocket/include/netsocket/EMAC.h b/connectivity/netsocket/include/netsocket/EMAC.h index e7b5e5c4832..4bd384aa320 100644 --- a/connectivity/netsocket/include/netsocket/EMAC.h +++ b/connectivity/netsocket/include/netsocket/EMAC.h @@ -141,7 +141,7 @@ class EMAC { /** * Sets a callback that needs to be called for packets received for this interface. * - * Note that the callback function will contain appropriate locking such that may be called from any OS thread. + * Note that the callback function will contain appropriate locking such that it may be called from any OS thread. * However, it shall not be called from an interrupt. * * Also note that the callback will take ownership of the passed packet and must free it in all cases. @@ -153,7 +153,7 @@ class EMAC { /** * Sets a callback that needs to be called on link status changes for given interface * - * Note that the callback function will contain appropriate locking such that may be called from any OS thread. + * Note that the callback function will contain appropriate locking such that it may be called from any OS thread. * However, it shall not be called from an interrupt. * * @param state_cb Function to be registered as a callback diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast.cpp index 3a53e568570..a8f5cb9a877 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_unicast.cpp @@ -65,10 +65,6 @@ void test_emac_unicast_cb(int opt) void test_emac_unicast() { - if(test_step != 0) { - printf("Got here!\n"); - } - // Reset flags send_request = true; no_response_cnt = 0; From 0b769258e541f2637a043369afddfdb0ebf73cfb Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Wed, 12 Mar 2025 09:59:38 -0700 Subject: [PATCH 36/47] Oops missed diagram --- .../drivers/emac/doc/cemac-overview.svg | 448 ++++++++++++++++++ 1 file changed, 448 insertions(+) create mode 100644 connectivity/drivers/emac/doc/cemac-overview.svg diff --git a/connectivity/drivers/emac/doc/cemac-overview.svg b/connectivity/drivers/emac/doc/cemac-overview.svg new file mode 100644 index 00000000000..b38f851af48 --- /dev/null +++ b/connectivity/drivers/emac/doc/cemac-overview.svg @@ -0,0 +1,448 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Implements + Has References + EMAC API + CompositeEMAC +- Implements EMAC API +- Implements locking & the EMAC thread +- Delegates Ethernet functionality to subclasses + MACDriver +- Inits MAC hardware +- Sets up interrupts +- Does MDIO transactions +- Manages the MAC addr +- controls MAC filtering + PHYDriver +- Inits Eth PHY chip +- Configures Eth settings (duplex, speed, autonegotiation) +- Checks link status + TxDMARing +- Enqueues packets into the Ethernet transmit DMA +- Reclaims their memory once they're transmitted + TxDMARing +- Sets up Rx DMA with buffers to receive into +- Detects when packets have been received +- Dequeues pkts for EMAC + + + + From 64a973bdecb76fe0ff3cd189ed9e698235569e2e Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Wed, 12 Mar 2025 22:02:27 -0700 Subject: [PATCH 37/47] Fix typo --- .../drivers/emac/doc/cemac-overview.svg | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/connectivity/drivers/emac/doc/cemac-overview.svg b/connectivity/drivers/emac/doc/cemac-overview.svg index b38f851af48..caae85731ad 100644 --- a/connectivity/drivers/emac/doc/cemac-overview.svg +++ b/connectivity/drivers/emac/doc/cemac-overview.svg @@ -24,8 +24,8 @@ inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" inkscape:zoom="2.175463" - inkscape:cx="295.33943" - inkscape:cy="245.69483" + inkscape:cx="295.56927" + inkscape:cy="246.1545" inkscape:window-width="2560" inkscape:window-height="1417" inkscape:window-x="-8" @@ -260,27 +260,27 @@ style="white-space:pre;shape-inside:url(#rect3);display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.51181;stroke-dasharray:none;stroke-opacity:1">CompositeEMAC + id="tspan3">CompositeEMAC - Implements EMAC API + id="tspan5">- Implements EMAC API - Implements locking & the EMAC thread + id="tspan7">- Implements locking & the EMAC thread - Delegates Ethernet functionality to subclasses + id="tspan65">- Delegates Ethernet functionality to subclasses MACDriver + id="tspan67">MACDriver - Inits MAC hardware + id="tspan69">- Inits MAC hardware - Sets up interrupts + id="tspan71">- Sets up interrupts - Does MDIO transactions + id="tspan73">- Does MDIO transactions - Manages the MAC addr + id="tspan75">- Manages the MAC addr - controls MAC filtering + id="tspan77">- controls MAC filtering PHYDriver + id="tspan79">PHYDriver - Inits Eth PHY chip + id="tspan81">- Inits Eth PHY chip - Configures Eth settings - Configures Eth settings (duplex, speed, (duplex, speed, autonegotiation) + id="tspan87">autonegotiation) - Checks link status + id="tspan89">- Checks link status TxDMARing + id="tspan91">TxDMARing - Enqueues packets into - Enqueues packets into the Ethernet transmit the Ethernet transmit DMA + id="tspan97">DMA - Reclaims their memory - Reclaims their memory once they're transmitted + id="tspan101">once they're transmitted TxDMARing + id="tspan103">RxDMARing - Sets up Rx DMA with - Sets up Rx DMA with buffers to receive into + id="tspan107">buffers to receive into - Detects when packets - Detects when packets have been received + id="tspan111">have been received - Dequeues pkts for EMAC + id="tspan113">- Dequeues pkts for EMAC Date: Sun, 16 Mar 2025 10:40:07 -0700 Subject: [PATCH 38/47] Bugfix, improve docs --- connectivity/drivers/emac/CompositeEMAC.md | 32 +- .../TARGET_NUCLEO_F746ZG/stm32f7_eth_init.c | 5 - .../drivers/emac/doc/embedded-ethernet.svg | 1276 +++++++++++++++++ .../emac/doc/stm32f2-eth-dma-descriptors.png | Bin 0 -> 25619 bytes 4 files changed, 1305 insertions(+), 8 deletions(-) create mode 100644 connectivity/drivers/emac/doc/embedded-ethernet.svg create mode 100644 connectivity/drivers/emac/doc/stm32f2-eth-dma-descriptors.png diff --git a/connectivity/drivers/emac/CompositeEMAC.md b/connectivity/drivers/emac/CompositeEMAC.md index 4b175e30aa7..84bdac5923a 100644 --- a/connectivity/drivers/emac/CompositeEMAC.md +++ b/connectivity/drivers/emac/CompositeEMAC.md @@ -2,10 +2,36 @@ The original Mbed OS EMAC API was added in Mbed OS 5.9. That API grouped all EMAC functionality into a single abstract class that targets/vendors had to implement, the `EMAC` class. Since then, dozens of Mbed targets have received EMAC drivers, and the strengths and weaknesses of this API have become clear. The general structure is good, and the idea of abstracting the memory manager and the network stack from the EMAC driver works well. -However, the EMAC interface is difficult to implement, especially for people not intimately familiar with Mbed and its IP stacks. It requires EMAC implementations to implement the specifics of memory management, MAC address tracking, and DMA ring usage themselves, even though quite a bit of this logic is common to all MAC drivers. This has led to duplicated code, and quite often to half-assed code as well as chip vendors have struggled to conform to the (in some ways not very well defined) EMAC API. +However, the [EMAC interface](https://github.com/mbed-ce/mbed-os/blob/0553c111850997d847dc6a3189ac0b7048304e57/connectivity/netsocket/include/netsocket/EMAC.h#L33) is difficult to implement, especially for people not intimately familiar with Mbed and its IP stacks. It requires EMAC implementations to implement the specifics of memory management, MAC address tracking, and DMA ring usage themselves, even though quite a bit of this logic is common to all MAC drivers. This has led to duplicated code, and quite often to half-assed code as well as chip vendors have struggled to conform to the (in some ways not very well defined) EMAC API. -Couple that with inconsistent testing, and you have a recipe for inconsistent and buggy Ethernet drivers across the breadth of Mbed devices. For instance, Mbed supports zero-copy EMACs, where buffers can be passed directly to and from the Ethernet peripheral without being copied. This saves both memory and CPU time. However, this was only ever implemented for a few targets, because it's very difficult to get right. Even more egregiously, the EMAC driver implemented for STM32H7 for the past 6+ years has ignored the memory manager API and used LwIP directly, making it impossible to test with the EMAC tests (and hoo boy, were there a lot of things that would have failed). For extra fun, this driver also ignored the DMA functionality and sent packets _synchronously_, meaning the application is blocked while a packet is being transmitted! +Couple that with inconsistent testing, and you have a recipe for inconsistent and buggy Ethernet drivers across the breadth of Mbed devices. For instance, Mbed supports zero-copy EMACs, where buffers can be passed directly to and from the Ethernet peripheral without being copied. This saves both memory and CPU time. However, this was only ever implemented for a few targets, because it's very difficult to get right. Even more egregiously, the EMAC driver implemented for STM32H7 for the past 6+ years has ignored the memory manager API and used LwIP directly, making it impossible to even test it with the EMAC tests (and hoo boy, were there a lot of things that would have failed). For extra fun, this driver also ignored the DMA functionality and sent packets _synchronously_, meaning the application is blocked while a packet is being transmitted! To address this situation, Mbed CE is implementing a new layer in the EMAC driver stack: CompositeEMAC. CompositeEMAC is a class which implements the `EMAC` API and breaks up the functionality into several subclasses. -![Overview diagram](./doc/cemac-overview.svg) \ No newline at end of file +![Overview diagram](./doc/cemac-overview.svg) + +By implementing these four subclasses for each target MCU and board, high-performance Ethernet can be ported to any Mbed target much more easily than before. + +## Embedded Ethernet - An Overview + +Before we can get into the details of how CompositeEMAC works, we need to go over how embedded Ethernet works in general. + +![Embedded Ethernet diagram](./doc/embedded-ethernet.svg) + +To run an ethernet connection, two chips need to work together*: the microcontroller and an external Ethernet PHY. The microcontroller sends and receives logic level Ethernet packets, while the PHY transforms those into Ethernet signals, which are decidedly *not* logic level (and actually have a lot in common with radio signals). The Ethernet signals, called MDI (Media Dependent Interface) pairs, are sent through an isolation transformer, which removes common mode interference and provides electrical isolation (e.g. so that the two ends of the connection can have different ground voltage levels). + +The PHY and the MCU are connected via a standard called [Reduced Media Independent Interface](https://en.wikipedia.org/wiki/Media-independent_interface#RMII) (RMII), which transfers the Ethernet packets as serialized bytes. This is an 8-wire bus with a 50MHz clock, four receive lines, and three transmit lines. The clock is traditionally either supplied by the PHY or by a dedicated clock generator chip, though some MCUs support supplying this clock as well. In addition to RMII, there's also a two-wire command and control bus called [Management Data IO](https://en.wikipedia.org/wiki/Management_Data_Input/Output) (MDIO) (though it can also be referred to Station Management Interface (SMI) or even "MiiM"). MDIO is used for talking directly to the PHY, not for sending Ethernet packets. MDIO is an open-drain bus similar to I2C, but with 16-bit words instead of bytes and a specific frame format (referred to as "Clause 22"). Unlike RMII, MDIO is a multi-drop bus, so you can actually connect up to 15 PHYs or other devices to one set of MDIO lines as long as they have different addresses! + +Inside the microcontroller, the bridge between the CPU and Ethernet is a peripheral called the Ethernet MAC. MAC stands for "Media Access Control" and refers to the second layer of the Ethernet protocol stack, the logic which encodes Ethernet packets and decides when to send them across the wire. The MAC has a number of moving parts inside. The simplest is the block of configuration registers, which is accessible at a specific memory address and sets up operation of the MAC (e.g. what MAC addresses the hardware should accept and which checksums should be inserted/checked by the MAC). There is also an MDIO master interface, which controls the MDIO lines to talk to the PHY. + +Every Ethernet MAC I've seen also has DMA functionality. This means that the Ethernet peripheral can transmit and receive packets without direct CPU intervention. This is very important because it means your device can hit high network speeds without needing to have your CPU blocked for lots of time waiting on Ethernet packets to move through the hardware! For transmit, there will be a Tx DMA module which fetches data from the main RAM, and then enqueues the packet bytes plus control information into a FIFO (which is usually at least a couple thousand bytes long). Then, another block in the MAC, sometimes called the MTL (MAC Translation Layer) takes these bytes, applies any needed Ethernet framing, and shifts them out of the RMII Tx port. + +For reception, the process works the same but in reverse: the decoder and shifter block takes in packets and enqueues their bytes into the Rx FIFO. Then, the Rx DMA dequeues the packets and stores them into RAM at the right location. + +How does the DMA know where in RAM to read and write packets, though? On every embedded MAC I have seen so far, this is done through a ring of "DMA descriptors". Here's a diagram (that I stole from the STM32F2 datasheet): + +![DMA descriptor ring](doc/stm32f2-eth-dma-descriptors.png) + +A descriptor is a few-word-long structure in memory that contains control information and one or more pointers to memory buffers (which contain the actual packet data). For Tx, the DMA will fetch the descriptor, and then transmit the data in the buffers. For Rx, the DMA will fetch the descriptor, then write the packet to the descriptor's buffers. Either way, when done with the descriptor, the DMA will write status information (e.g. whether the checksum passed, or what timestamp the packet was sent at) back to the descriptor, set a flag, and then interrupt the CPU to tell it it has something to process. + +But we don't want the DMA to have to wait for the CPU, do we? To avoid this, each descriptor also specifies a "next descriptor", either via an offset or a pointer. The DMA can move to this next descriptor and start processing it right away to send or receive the next packet. The CPU will process the completed descriptor on its own time and give it back to the DMA. In this manner, as long as your ring of descriptors is big enough and your CPU can keep up with processing them, the CPU and MAC never have to wait for each other! \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F746ZG/stm32f7_eth_init.c b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F746ZG/stm32f7_eth_init.c index f69c13b3147..41a44fb8120 100644 --- a/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F746ZG/stm32f7_eth_init.c +++ b/connectivity/drivers/emac/TARGET_STM/TARGET_STM32F7/TARGET_NUCLEO_F746ZG/stm32f7_eth_init.c @@ -35,11 +35,6 @@ void EthInitPinmappings(void) { - /* Disable DCache for STM32F7 family */ - core_util_critical_section_enter(); - SCB_DisableDCache(); - core_util_critical_section_exit(); - /* Enable GPIOs clocks */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); diff --git a/connectivity/drivers/emac/doc/embedded-ethernet.svg b/connectivity/drivers/emac/doc/embedded-ethernet.svg new file mode 100644 index 00000000000..36bbcc750d7 --- /dev/null +++ b/connectivity/drivers/emac/doc/embedded-ethernet.svg @@ -0,0 +1,1276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Microcontroller + PHY + + + + + + + + + + + + RMII + MDIO + + Packet Encode and Shift + + + + Packet Shiftand Decode + MDIO MasterInterface + MAC ConfigRegisters + + Tx FIFO + + Tx DMA + + Rx FIFO + + Rx DMA + + + + + + + + + Ethernet MACPeripheral + + + + + + + System Bus + + Main RAM + + ARM CPU Core + EthernetJack + EthernetTransformer + + + + + + + + + MDI + Tx Pair + Rx Pair + + diff --git a/connectivity/drivers/emac/doc/stm32f2-eth-dma-descriptors.png b/connectivity/drivers/emac/doc/stm32f2-eth-dma-descriptors.png new file mode 100644 index 0000000000000000000000000000000000000000..3c61a868d206dd4d6ce930a988e9c9a74c01995d GIT binary patch literal 25619 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 oUk>nR-^L%r`# Date: Sun, 16 Mar 2025 10:49:03 -0700 Subject: [PATCH 39/47] Fix PNG --- .../emac/doc/stm32f2-eth-dma-descriptors.png | Bin 25619 -> 21612 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/connectivity/drivers/emac/doc/stm32f2-eth-dma-descriptors.png b/connectivity/drivers/emac/doc/stm32f2-eth-dma-descriptors.png index 3c61a868d206dd4d6ce930a988e9c9a74c01995d..f5a298dae730075cbf9dafd09495baef896cacfa 100644 GIT binary patch literal 21612 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 literal 25619 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 oUk>nR-^L%r`# Date: Sun, 16 Mar 2025 18:36:32 -0700 Subject: [PATCH 40/47] More docs, renames --- connectivity/drivers/emac/CompositeEMAC.md | 66 ++++++++++++++++++- .../drivers/emac/include/CompositeEMAC.h | 6 +- .../drivers/emac/include/GenericEthPhy.h | 2 +- .../drivers/emac/sources/CompositeEMAC.cpp | 6 +- .../drivers/emac/sources/PhyDrivers.cpp | 2 +- 5 files changed, 71 insertions(+), 11 deletions(-) diff --git a/connectivity/drivers/emac/CompositeEMAC.md b/connectivity/drivers/emac/CompositeEMAC.md index 84bdac5923a..172f3182b7a 100644 --- a/connectivity/drivers/emac/CompositeEMAC.md +++ b/connectivity/drivers/emac/CompositeEMAC.md @@ -32,6 +32,66 @@ How does the DMA know where in RAM to read and write packets, though? On every e ![DMA descriptor ring](doc/stm32f2-eth-dma-descriptors.png) -A descriptor is a few-word-long structure in memory that contains control information and one or more pointers to memory buffers (which contain the actual packet data). For Tx, the DMA will fetch the descriptor, and then transmit the data in the buffers. For Rx, the DMA will fetch the descriptor, then write the packet to the descriptor's buffers. Either way, when done with the descriptor, the DMA will write status information (e.g. whether the checksum passed, or what timestamp the packet was sent at) back to the descriptor, set a flag, and then interrupt the CPU to tell it it has something to process. - -But we don't want the DMA to have to wait for the CPU, do we? To avoid this, each descriptor also specifies a "next descriptor", either via an offset or a pointer. The DMA can move to this next descriptor and start processing it right away to send or receive the next packet. The CPU will process the completed descriptor on its own time and give it back to the DMA. In this manner, as long as your ring of descriptors is big enough and your CPU can keep up with processing them, the CPU and MAC never have to wait for each other! \ No newline at end of file +A descriptor is a structure in memory that contains control information and one or more pointers to memory buffers (which contain the actual packet data). For Tx, the DMA will fetch the descriptor, then transmit the data in the buffers. For Rx, the DMA will fetch the descriptor, then write the packet to the descriptor's buffers. Either way, when the MAC is done with the descriptor, the DMA will write back status information (e.g. whether the checksum passed, or what timestamp the packet was sent at) to the descriptor, set a "done" flag, and then interrupt the CPU to tell it it has something to process. + +But we don't want the DMA to have to wait for the CPU, do we? To avoid this, each descriptor also specifies a "next descriptor", either via an offset or a pointer. The DMA can move to this next descriptor and start processing it right away to send or receive the next packet. The CPU will process the completed descriptor on its own time and give it back to the DMA. In this manner, as long as your ring of descriptors is big enough and your CPU can keep up with the processing them, the CPU and MAC never have to wait for each other! + +## Components of the Composite EMAC +### MAC Driver + +The MAC driver (which must be implemented as a subclass of `CompositeEMAC::MACDriver`) is usually fairly simple. It provides an interface between Mbed and the MAC's configuration register block and MDIO master interface. Its responsibilities include: +- Initializing and muxing the RMII and MDIO pins +- Initializing all needed clocks +- Configuring all settings needed for MAC operation +- Configuring the unicast MAC address (as in, the MAC address that the device uses on the network) +- Adding and removing multicast subscriptions +- Configuring interrupts +- Talking to the PHY over MDIO + +### PHY Driver + +The PHY driver must be a subclass of `CompositeEMAC::PHYDriver`. It must: +- Confirm the existence of the PHY chip and initialize it +- Configure the selected Ethernet settings (autonegotiation, speed, duplex) into the PHY +- Check if link has been established and, if so, what kind + +Unlike the MAC driver and the DMA, the PHY driver does not need to be subclassed for each target device. Thankfully, the Ethernet standard imposes some order on the chaotic sea of PHY parts, and it mandates that the lower 16 registers are standardized and must work the same way on each part. Using this standard behavior, we have implemented the `mbed::GenericEthPhy` class, which should function as a driver for any 802.3u standard compliant PHY. All it needs is configuration, like the PHY's part number and its address on the MDIO bus. When porting to a new target, all you need to do is indicate the PHY model in `mbed-os/connectivity/netsocket/mbed_lib.json` like so: + +```json5 +"MY_TARGET": { + "nsapi.emac-phy-model": "LAN8742", + "nsapi.emac-phy-mdio-address": 0 +} +``` + +This will work out of the box, as long as `LAN8742` names a PHY driver defined in PhyDrivers.cpp. Individual PHY models will generally need their own drivers, since often PHYs have errata that need to be worked around or need other configuration that isn't defined in the standard. However, GenericEthPhy allows implementing the absolute minimum amount of logic per-phy as possible! + +Since user boards may want to use a different ethernet PHY, the driver can be customized in an application by overriding the `mbed::get_eth_phy_driver` weak function. This might look something like + +```c++ +namespace MY_PHY { +inline constexpr GenericEthPhy::Config Config = { + // These are found in the PHY datasheet. See GenericEthPhy::Config for documentation. + .OUI = 0x123, + .model = 0x45, + .address = 0, +}; + +class Driver : public GenericEthPhy { +public: + explicit Driver(GenericEthPhy::Config const & config = DefaultConfig): + GenericEthPhy(config) + {} + + // You may override/replace any functions of `GenericEthPhy` here +}; +} + +namespace mbed { +CompositeEMAC::PHYDriver * get_eth_phy_driver() +{ + static MY_PHY::Driver(MY_PHY::Config) phyDriver; + return &phyDriver; +} +} +``` \ No newline at end of file diff --git a/connectivity/drivers/emac/include/CompositeEMAC.h b/connectivity/drivers/emac/include/CompositeEMAC.h index fb57fa7dd2f..f1cb84a602d 100644 --- a/connectivity/drivers/emac/include/CompositeEMAC.h +++ b/connectivity/drivers/emac/include/CompositeEMAC.h @@ -225,14 +225,14 @@ class CompositeEMAC : public EMAC * * Thread safety: CompositeEMAC will guarantee only one thread is utilizing this class at a time. */ - class PhyDriver : NonCopyable + class PHYDriver : NonCopyable { protected: /// MAC driver. Shall be set in init(). MACDriver * mac = nullptr; public: - virtual ~PhyDriver() = default; + virtual ~PHYDriver() = default; /// Set the MAC driver of this PHY. Will be called by CompositeEMAC before init(). void setMAC(MACDriver * mac) { this->mac = mac; } @@ -365,7 +365,7 @@ class CompositeEMAC : public EMAC emac_link_input_cb_t linkInputCallback{}; // Instances of each of the 4 component classes - PhyDriver * phy = nullptr; + PHYDriver * phy = nullptr; RxDMA & rxDMA; TxDMA & txDMA; MACDriver & mac; diff --git a/connectivity/drivers/emac/include/GenericEthPhy.h b/connectivity/drivers/emac/include/GenericEthPhy.h index e04a38ab4be..a05aea62313 100644 --- a/connectivity/drivers/emac/include/GenericEthPhy.h +++ b/connectivity/drivers/emac/include/GenericEthPhy.h @@ -78,7 +78,7 @@ namespace GenPhyRegs { * Passed a configuration which sets most attributes of the phy. * May be extended to handle chip-specific quirks. */ -class GenericEthPhy : public mbed::CompositeEMAC::PhyDriver { +class GenericEthPhy : public mbed::CompositeEMAC::PHYDriver { public: /// Configuration structure for a generic Ethernet PHY struct Config { diff --git a/connectivity/drivers/emac/sources/CompositeEMAC.cpp b/connectivity/drivers/emac/sources/CompositeEMAC.cpp index 2ecaf24c5e5..ac723d04660 100644 --- a/connectivity/drivers/emac/sources/CompositeEMAC.cpp +++ b/connectivity/drivers/emac/sources/CompositeEMAC.cpp @@ -33,7 +33,7 @@ static uint32_t THREAD_FLAG_SHUTDOWN = 1 << 3; namespace mbed { // Defined in PhyDrivers.cpp - CompositeEMAC::PhyDriver * mbed_get_eth_phy_driver(); + CompositeEMAC::PHYDriver * get_eth_phy_driver(); void CompositeEMAC::rxISR() { // Note: Not locking mutex here as this is an ISR and should be able to run while the MAC thread is executing. @@ -198,9 +198,9 @@ namespace mbed { } // Get phy - phy = mbed_get_eth_phy_driver(); + phy = get_eth_phy_driver(); if(phy == nullptr) { - tr_err("power_up(): No Ethernet PHY driver configured! Either set nsapi.emac-phy-model or override mbed_get_eth_phy_driver()."); + tr_err("power_up(): No Ethernet PHY driver configured! Either set nsapi.emac-phy-model or override mbed::get_eth_phy_driver()."); return false; } diff --git a/connectivity/drivers/emac/sources/PhyDrivers.cpp b/connectivity/drivers/emac/sources/PhyDrivers.cpp index 878bb7e8e6e..75bc001b62f 100644 --- a/connectivity/drivers/emac/sources/PhyDrivers.cpp +++ b/connectivity/drivers/emac/sources/PhyDrivers.cpp @@ -53,7 +53,7 @@ class Driver : public GenericEthPhy { * * @return Phy driver class instance, or nullptr if none is configured. */ -MBED_WEAK CompositeEMAC::PhyDriver * mbed_get_eth_phy_driver() +MBED_WEAK CompositeEMAC::PHYDriver * get_eth_phy_driver() { #ifdef MBED_CONF_NSAPI_EMAC_PHY_MODEL static GenericEthPhy::Config driverConfig = MBED_CONF_NSAPI_EMAC_PHY_MODEL::DefaultConfig; From 491c06316efbdfd94ae0e869e6d8d5e75be9da9c Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sat, 22 Mar 2025 17:32:12 -0700 Subject: [PATCH 41/47] More renames, fix != --- connectivity/drivers/emac/CompositeEMAC.md | 10 ++++++++-- connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h | 2 +- connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h | 2 +- connectivity/drivers/emac/include/GenericEthDMA.h | 2 +- connectivity/drivers/emac/sources/GenericEthPhy.cpp | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/connectivity/drivers/emac/CompositeEMAC.md b/connectivity/drivers/emac/CompositeEMAC.md index 172f3182b7a..b45073e2296 100644 --- a/connectivity/drivers/emac/CompositeEMAC.md +++ b/connectivity/drivers/emac/CompositeEMAC.md @@ -37,6 +37,8 @@ A descriptor is a structure in memory that contains control information and one But we don't want the DMA to have to wait for the CPU, do we? To avoid this, each descriptor also specifies a "next descriptor", either via an offset or a pointer. The DMA can move to this next descriptor and start processing it right away to send or receive the next packet. The CPU will process the completed descriptor on its own time and give it back to the DMA. In this manner, as long as your ring of descriptors is big enough and your CPU can keep up with the processing them, the CPU and MAC never have to wait for each other! ## Components of the Composite EMAC +Now that we've covered how an EMAC works in hardware, we can talk through how CompositeEMAC works, and what needs to be implemented for each MCU target. + ### MAC Driver The MAC driver (which must be implemented as a subclass of `CompositeEMAC::MACDriver`) is usually fairly simple. It provides an interface between Mbed and the MAC's configuration register block and MDIO master interface. Its responsibilities include: @@ -66,7 +68,7 @@ Unlike the MAC driver and the DMA, the PHY driver does not need to be subclassed This will work out of the box, as long as `LAN8742` names a PHY driver defined in PhyDrivers.cpp. Individual PHY models will generally need their own drivers, since often PHYs have errata that need to be worked around or need other configuration that isn't defined in the standard. However, GenericEthPhy allows implementing the absolute minimum amount of logic per-phy as possible! -Since user boards may want to use a different ethernet PHY, the driver can be customized in an application by overriding the `mbed::get_eth_phy_driver` weak function. This might look something like +Since user boards may want to use a different ethernet PHY, the driver can be customized in an application by overriding the `mbed::get_eth_phy_driver` weak function to return a different driver class. This might look something like ```c++ namespace MY_PHY { @@ -94,4 +96,8 @@ CompositeEMAC::PHYDriver * get_eth_phy_driver() return &phyDriver; } } -``` \ No newline at end of file +``` + +### Tx DMA + +The Rx and Tx DMAs are implemented as their own driver classes, as they are somewhat complicated and generally don't interact with the other pieces of the EMAC very much. The Tx DMA must be implemented as a subclass of `CompositeEMAC::TxDMA`. However, since the large majority of microcontrollers implement Tx DMA in a very similar way, the `GenericTxDMARing` class has been provided which implements most of the needed functionality. \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h index a3a9e95d446..2b6bb52775d 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h @@ -33,7 +33,7 @@ namespace mbed */ class STM32EthMACv1 : public CompositeEMAC { - class TxDMA : public GenericTxDMALoop + class TxDMA : public GenericTxDMARing { protected: ETH_TypeDef * const base; // Base address of Ethernet peripheral diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h index 6fc0802738a..40a3d74aa47 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -27,7 +27,7 @@ namespace mbed { */ class STM32EthMACv2 : public CompositeEMAC { - class TxDMA : public GenericTxDMALoop + class TxDMA : public GenericTxDMARing { protected: ETH_TypeDef * const base; // Base address of Ethernet peripheral diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index f89e6757287..a301ec4008d 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -33,7 +33,7 @@ namespace mbed { * This implementation of Tx DMA should work for the large majority of embedded MCUs that use a DMA ring-based * ethernet MAC. */ - class GenericTxDMALoop : public CompositeEMAC::TxDMA + class GenericTxDMARing : public CompositeEMAC::TxDMA { protected: /// Number of entries in the Tx descriptor ring diff --git a/connectivity/drivers/emac/sources/GenericEthPhy.cpp b/connectivity/drivers/emac/sources/GenericEthPhy.cpp index 93608a1a303..b1873e0be13 100644 --- a/connectivity/drivers/emac/sources/GenericEthPhy.cpp +++ b/connectivity/drivers/emac/sources/GenericEthPhy.cpp @@ -72,7 +72,7 @@ CompositeEMAC::ErrCode GenericEthPhy::init() { } // Software reset, if we couldn't use the hardware reset line earlier - if(mac->getPhyResetPin() != NC) { + if(mac->getPhyResetPin() == NC) { uint16_t bmcrVal; FORWARD_ERR(mac->mdioRead(config.address, GenPhyRegs::BMCR, bmcrVal)); FORWARD_ERR(mac->mdioWrite(config.address, GenPhyRegs::BMCR, bmcrVal | GenPhyRegs::BMCR_SW_RST_Msk)); From 0776a75e264e9fbbfa37b5f21dde52bae6bfd398 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sat, 22 Mar 2025 19:05:45 -0700 Subject: [PATCH 42/47] Document Tx DMA --- connectivity/drivers/emac/CompositeEMAC.md | 66 ++- .../drivers/emac/doc/tx-ring-exhausted.svg | 465 ++++++++++++++++++ .../emac/doc/tx-ring-initial-state.svg | 445 +++++++++++++++++ .../drivers/emac/doc/tx-ring-step-1.svg | 465 ++++++++++++++++++ .../drivers/emac/doc/tx-ring-step-2.svg | 465 ++++++++++++++++++ .../drivers/emac/doc/tx-ring-step-3.svg | 465 ++++++++++++++++++ .../drivers/emac/doc/tx-ring-step-4.svg | 465 ++++++++++++++++++ .../drivers/emac/doc/tx-ring-step-5.svg | 449 +++++++++++++++++ .../drivers/emac/include/GenericEthDMA.h | 12 +- 9 files changed, 3293 insertions(+), 4 deletions(-) create mode 100644 connectivity/drivers/emac/doc/tx-ring-exhausted.svg create mode 100644 connectivity/drivers/emac/doc/tx-ring-initial-state.svg create mode 100644 connectivity/drivers/emac/doc/tx-ring-step-1.svg create mode 100644 connectivity/drivers/emac/doc/tx-ring-step-2.svg create mode 100644 connectivity/drivers/emac/doc/tx-ring-step-3.svg create mode 100644 connectivity/drivers/emac/doc/tx-ring-step-4.svg create mode 100644 connectivity/drivers/emac/doc/tx-ring-step-5.svg diff --git a/connectivity/drivers/emac/CompositeEMAC.md b/connectivity/drivers/emac/CompositeEMAC.md index b45073e2296..2120e1335ab 100644 --- a/connectivity/drivers/emac/CompositeEMAC.md +++ b/connectivity/drivers/emac/CompositeEMAC.md @@ -100,4 +100,68 @@ CompositeEMAC::PHYDriver * get_eth_phy_driver() ### Tx DMA -The Rx and Tx DMAs are implemented as their own driver classes, as they are somewhat complicated and generally don't interact with the other pieces of the EMAC very much. The Tx DMA must be implemented as a subclass of `CompositeEMAC::TxDMA`. However, since the large majority of microcontrollers implement Tx DMA in a very similar way, the `GenericTxDMARing` class has been provided which implements most of the needed functionality. \ No newline at end of file +The Rx and Tx DMAs are implemented as their own driver classes, instead of in the MAC driver, as they are somewhat complicated and generally don't interact with the other pieces of the EMAC very much. To work with CompositeEMAC, the Tx DMA must be implemented as a subclass of `CompositeEMAC::TxDMA`. However, since the large majority of microcontrollers implement Tx DMA in a very similar way, the `GenericTxDMARing` class has been provided which provides most of the needed functionality. + +#### Generic Tx DMA Ring Operation + +`GenericTxDMARing` assumes the basics: there is a ring of descriptors which are processed in sequence. The Ethernet DMA may transmit a packet once it is marked as "owned by DMA" in the ring (usually via a bitfield in the descriptor). Once the packet is done transmitting, the Ethernet DMA gives it back to the application by clearing the own flag and delivering an interrupt. + +Mbed's generic Tx DMA ring driver uses two indexes to manage the ring of descriptors: `txSendIndex`, the index of the next Tx descriptor that can be filled with data, and `txReclaimIndex`, the index of the next descriptor that can be reclaimed by the driver. Together, these indexes, plus a count variable, describe the state of the descriptor ring. + +To explain how this works, let's go through an example of transmitting some packets via the descriptors. + +##### Initial State +When the driver starts, both indexes point to descriptor 0. No data is in the DMA ring. + +![Initial DMA ring state](doc/tx-ring-initial-state.svg) + + +##### Packet 0 Enqueued +Now suppose we enqueue a packet to be transmitted into the ring. The packet will go into desc 0, since that is where `txSendIndex` is pointing. The driver will give desc 0 to the DMA, so it will start being transmitted out of the Ethernet port. + +![DMA ring with packet 0](doc/tx-ring-step-1.svg) + +##### Packet 1 Enqueued +Now, very soon after the first packet, we enqueue another packet to be transmitted. This packet is split between two buffers (common when using zero-copy transmission), so it takes up two descriptors. These two descriptors are then given to the DMA to send. + +![DMA ring with packet 1](doc/tx-ring-step-2.svg) + +##### Packet 0 Completes +Soon after, the MAC completes the transmission of packet 0. It sets the DMA own flag in the descriptor back to false, and delivers a Tx interrupt to the application. The below picture shows the state of the descriptors when the interrupt fires. + +![DMA ring after packet 0 completes](doc/tx-ring-step-3.svg) + +##### After Packet 0 Tx Interrupt +The Tx interrupt* will detect that the descriptor pointed to by `txReclaimIndex` has completed. It can then deallocate the memory for Packet 0, collect any needed status information (e.g. the timestamp) from the descriptor, and advance `txReclaimIndex` forward. Last but not least, it sets a thread flag to unblock any threads which are waiting for a Tx descriptor in order to send a packet. + +![DMA ring after packet 0 deallocated](doc/tx-ring-step-4.svg) + +*Actually, interrupts cannot deallocate memory in Mbed, so the Tx interrupt handler actually signals the MAC thread to wake up and process the completed descriptor. + +##### Final State +Eventually, after a couple hundred microseconds, the second packet will be transmitted, and its descriptors will also be reclaimed. The Tx descriptor ring is now empty, except that the descriptor pointers are now both pointing to Desc 3. + +![DMA ring after packet 1 deallocated](doc/tx-ring-step-5.svg) + +Note that in most cases, once the ring is empty like this, the Tx DMA in the hardware will simply sit and wait until dmaOwn is set on Desc 3. However, this depends a bit on the hardware -- some MACs need an additional "poke" command to make them recheck the descriptor, and yet others simply need to be told which descriptor address to transmit once a new one is made available. + +##### Descriptor Exhaustion +You might notice a slight problem with the send and reclaim indexes, as described so far. When the Tx descriptor ring is totally full, they both end up pointing to the same descriptor: + +![DMA ring with all descs allocated](doc/tx-ring-exhausted.svg) + +This is a problem because, earlier, we said that the initial state of the DMA ring, with no packets enqueued, also has both indices pointing to the same descriptor. This might cause us to naively calculate that the descriptor ring is empty! + +This is a classic problem with circular FIFOs, and to solve it, we go for the classic solution. An additional count variable, `txDescsOwnedByApplication`, counts how many descriptors are known to be NOT owned by DMA. It's decremented each time we give a descriptor to DMA, and incremented each time we reclaim one back. We can then use this count variable to dis-ambiguate the situation where `txSendIndex == txReclaimIndex`. + +#### Target-Specific Tx DMA Implementation + +For each target, the `GenericTxDMARing` class needs to be extended. The subclass must provide a couple functions: + +- Allocating the actual DMA descriptors (since this often has special requirements on alignment or memory banks) +- Initializing and deinitializing the Tx DMA +- Checking the dmaOwn flag on a descriptor +- Checking if a given buffer address is accessible by the DMA controller (since, on many MCUs, certain areas of memory are not OK and the buffer will need to be copied) +- Giving a descriptor to DMA after populating it with a given buffer + +Everything else, including the descriptor tracking and memory management, is done by the superclass. This should let target implementations focus only on the low level descriptor format while relying on common code for everything else. \ No newline at end of file diff --git a/connectivity/drivers/emac/doc/tx-ring-exhausted.svg b/connectivity/drivers/emac/doc/tx-ring-exhausted.svg new file mode 100644 index 00000000000..96b97e21c6a --- /dev/null +++ b/connectivity/drivers/emac/doc/tx-ring-exhausted.svg @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + + Desc 1 + + Desc 2 + + dmaOwn = true + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + dmaOwn = true + dmaOwn = true + dmaOwn = true + + + txReclaimIndex + + + txSendIndex + + Packet being Txed + Points to Packet 0 + Empty + Empty + Empty + Empty + Empty + dmaOwn = true + dmaOwn = true + + diff --git a/connectivity/drivers/emac/doc/tx-ring-initial-state.svg b/connectivity/drivers/emac/doc/tx-ring-initial-state.svg new file mode 100644 index 00000000000..def0f29f215 --- /dev/null +++ b/connectivity/drivers/emac/doc/tx-ring-initial-state.svg @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + dmaOwn = false + + Desc 1 + + Desc 2 + + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + + txReclaimIndex + + txSendIndex + Empty + Empty + Empty + Empty + Empty + Empty + + diff --git a/connectivity/drivers/emac/doc/tx-ring-step-1.svg b/connectivity/drivers/emac/doc/tx-ring-step-1.svg new file mode 100644 index 00000000000..2261f6b989a --- /dev/null +++ b/connectivity/drivers/emac/doc/tx-ring-step-1.svg @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + + Desc 1 + + Desc 2 + + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = true + + + txReclaimIndex + + + txSendIndex + + Packet being Txed + Points to Packet 0 + Empty + Empty + Empty + Empty + Empty + + diff --git a/connectivity/drivers/emac/doc/tx-ring-step-2.svg b/connectivity/drivers/emac/doc/tx-ring-step-2.svg new file mode 100644 index 00000000000..a36050d2009 --- /dev/null +++ b/connectivity/drivers/emac/doc/tx-ring-step-2.svg @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + + Desc 1 + + Desc 2 + + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = true + dmaOwn = true + dmaOwn = true + + + txReclaimIndex + + + txSendIndex + + Packet being Txed + Points to Packet 0 + Points to Packet 1 buf 0 + Points to Packet 1 buf 1 + Empty + Empty + Empty + + diff --git a/connectivity/drivers/emac/doc/tx-ring-step-3.svg b/connectivity/drivers/emac/doc/tx-ring-step-3.svg new file mode 100644 index 00000000000..225ca44e8cc --- /dev/null +++ b/connectivity/drivers/emac/doc/tx-ring-step-3.svg @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + + Desc 1 + + Desc 2 + + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = true + dmaOwn = true + + + txReclaimIndex + + + txSendIndex + + Packet being Txed + Points to Packet 0 + Points to Packet 1 buf 0 + Points to Packet 1 buf 1 + Empty + Empty + Empty + + diff --git a/connectivity/drivers/emac/doc/tx-ring-step-4.svg b/connectivity/drivers/emac/doc/tx-ring-step-4.svg new file mode 100644 index 00000000000..0da1b3f25cf --- /dev/null +++ b/connectivity/drivers/emac/doc/tx-ring-step-4.svg @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + + Desc 1 + + Desc 2 + + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = true + dmaOwn = true + + + txReclaimIndex + + + txSendIndex + + Packet being Txed + Empty + Points to Packet 1 buf 0 + Points to Packet 1 buf 1 + Empty + Empty + Empty + + diff --git a/connectivity/drivers/emac/doc/tx-ring-step-5.svg b/connectivity/drivers/emac/doc/tx-ring-step-5.svg new file mode 100644 index 00000000000..dde083b59cf --- /dev/null +++ b/connectivity/drivers/emac/doc/tx-ring-step-5.svg @@ -0,0 +1,449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + + Desc 1 + + Desc 2 + + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + + + txReclaimIndex + + + txSendIndex + Empty + Empty + Empty + Empty + Empty + Empty + + diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index a301ec4008d..21ccbdce2b2 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -105,7 +105,7 @@ namespace mbed { public: CompositeEMAC::ErrCode init() override { // At the start, we own all the descriptors - txDescsOwnedByApplication = MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; + txDescsOwnedByApplication = TX_NUM_DESCS; // Next descriptor will be descriptor 0 txSendIndex = 0; @@ -164,7 +164,7 @@ namespace mbed { } // Update counters - txReclaimIndex = (txReclaimIndex + 1) % MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; + txReclaimIndex = (txReclaimIndex + 1) % TX_NUM_DESCS; ++txDescsOwnedByApplication; tr_debug("Reclaimed descriptor %zu", txReclaimIndex); @@ -183,7 +183,7 @@ namespace mbed { // Step 1: Figure out if we can send this zero-copy, or if we need to copy it. size_t neededDescs = memory_manager->count_buffers(buf); bool needToCopy = false; - if(neededDescs >= MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS) + if(neededDescs >= TX_NUM_DESCS) { // Packet uses too many buffers, we have to copy it into a continuous buffer. // Note: Some Eth DMAs (e.g. STM32 v2) cannot enqueue all the descs in the ring at the same time @@ -191,6 +191,12 @@ namespace mbed { needToCopy = true; } + if(!needToCopy && (neededDescs < txDescsOwnedByApplication || txDescsOwnedByApplication > 0)) { + // Packet uses more buffers than we have descriptors, but we can send it immediately if we copy + // it into a single buffer. + needToCopy = true; + } + if(!needToCopy) { net_stack_mem_buf_t * currBuf = buf; while(currBuf != nullptr) { From 6bc2ef3fc7c407ec5fddd11bc5493673039a3d3c Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 23 Mar 2025 10:29:49 -0700 Subject: [PATCH 43/47] Fix some DMA bugs: - Broken check in Tx DMA meant it was always copying packets - Tx DMA would assert fail from trying to free nullptr if an allocation failed during packet copy - Rx DMA would not reset firstDescIdx after seeing an error descriptor, leading to an attempt to receive a 0 length packet about 20% of the time we ran the EMAC memory test --- .../drivers/emac/include/GenericEthDMA.h | 72 +++++++++++-------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 21ccbdce2b2..3f9c9177c11 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -191,7 +191,7 @@ namespace mbed { needToCopy = true; } - if(!needToCopy && (neededDescs < txDescsOwnedByApplication || txDescsOwnedByApplication > 0)) { + if(!needToCopy && (neededDescs > txDescsOwnedByApplication && txDescsOwnedByApplication > 0)) { // Packet uses more buffers than we have descriptors, but we can send it immediately if we copy // it into a single buffer. needToCopy = true; @@ -228,7 +228,6 @@ namespace mbed { if(newBuf == nullptr) { // No free memory, drop packet - memory_manager->free(newBuf); return CompositeEMAC::ErrCode::OUT_OF_MEMORY; } @@ -478,10 +477,31 @@ namespace mbed { return false; } + private: + + /// Helper function: Discard received Rx descriptors from a given start index (inclusive) to stop index (exclusive) + void discardRxDescs(size_t startIdx, size_t stopIdx) + { + for(size_t descToCleanIdx = startIdx; descToCleanIdx != stopIdx; descToCleanIdx = (descToCleanIdx + 1) % RX_NUM_DESCS) { + // Free Rx buffer attached to this desc + memory_manager->free(rxDescStackBufs[descToCleanIdx]); + rxDescStackBufs[descToCleanIdx] = nullptr; + + // Allow desc to be rebuilt + ++rxDescsOwnedByApplication; + ++rxNextIndex; + } + } + + public: + net_stack_mem_buf_t * dequeuePacket() override { // Indices of the first and last descriptors for the packet will be saved here std::optional firstDescIdx, lastDescIdx; + // Packet length is stored here once we check it + size_t pktLen; + // Prevent looping around into descriptors waiting for rebuild by limiting how many // we can process. const size_t maxDescsToProcess = RX_NUM_DESCS - rxDescsOwnedByApplication; @@ -508,39 +528,33 @@ namespace mbed { // Error or non-first-descriptor before a first descriptor // (could be caused by incomplete packets/junk in the DMA buffer). // Ignore, free associated memory, and schedule for rebuild. - memory_manager->free(rxDescStackBufs[descIdx]); - rxDescStackBufs[descIdx] = nullptr; - ++rxDescsOwnedByApplication; - ++rxNextIndex; - + discardRxDescs(descIdx, (descIdx + 1) % RX_NUM_DESCS); continue; } - else if(firstDescIdx.has_value() && (isError || isFirst)) + else if(firstDescIdx.has_value() && isError) { - // Already seen a first descriptor, but we have an error descriptor or another first descriptor. - // So, delete the in-progress packet up to this point. - - // Clean up the old first descriptor and any descriptors between there and here - const size_t endIdx = isFirst ? descIdx : (descIdx + 1) % RX_NUM_DESCS; - - for(size_t descToCleanIdx = *firstDescIdx; descToCleanIdx != endIdx; descToCleanIdx = (descToCleanIdx + 1) % RX_NUM_DESCS) { - memory_manager->free(rxDescStackBufs[descToCleanIdx]); - rxDescStackBufs[descToCleanIdx] = nullptr; - ++rxDescsOwnedByApplication; - ++rxNextIndex; - } - - if(!isError) - { - firstDescIdx = descIdx; - } + // Already seen a first descriptor, but we have an error descriptor. + // So, delete the in-progress packet up to this point. + discardRxDescs(*firstDescIdx, (descIdx + 1) % RX_NUM_DESCS); + firstDescIdx.reset(); + continue; + } + else if(firstDescIdx.has_value() && isFirst) + { + // Already seen a first descriptor, but we have another first descriptor. + // Some MACs do this if they run out of Rx descs when halfway through a packet. + // Delete the in-progress packet up to this point and start over from descIdx. + discardRxDescs(*firstDescIdx, descIdx); + firstDescIdx = descIdx; } else if(isFirst) { + // Normal first descriptor. firstDescIdx = descIdx; } - if (isLast) { + if(isLast) { + pktLen = getTotalLen(*firstDescIdx, descIdx); lastDescIdx = descIdx; } } @@ -559,9 +573,9 @@ namespace mbed { // Set length of first buffer net_stack_mem_buf_t *const headBuffer = rxDescStackBufs[*firstDescIdx]; - size_t lenRemaining = getTotalLen(*firstDescIdx, *lastDescIdx); - memory_manager->set_len(headBuffer, std::min(lenRemaining, rxPoolPayloadSize)); - lenRemaining -= std::min(lenRemaining, rxPoolPayloadSize); + + memory_manager->set_len(headBuffer, std::min(pktLen, rxPoolPayloadSize)); + size_t lenRemaining = pktLen - std::min(pktLen, rxPoolPayloadSize); // Iterate through the subsequent descriptors in this packet and link the buffers // Note that this also transfers ownership of subsequent buffers to the first buffer, From c8ae2e44ceffb496a420b47fd167a8003ff11c1a Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 23 Mar 2025 10:30:44 -0700 Subject: [PATCH 44/47] Make EMAC memory test smarter. It now disables pool allocations when disabling input memory, and errors out if the MAC was working when it shouldn't've been. --- .../TESTS/network/emac/emac_test_memory.cpp | 63 ++++++++++++++++--- .../emac_test_utils/EmacTestMemoryManager.cpp | 15 ++++- .../emac_test_utils/EmacTestMemoryManager.h | 12 +++- .../tests/emac_test_utils/emac_util.cpp | 16 +---- .../tests/emac_test_utils/emac_util.h | 9 ++- 5 files changed, 89 insertions(+), 26 deletions(-) diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_memory.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_memory.cpp index e093b581e48..592437309dc 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_memory.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_memory.cpp @@ -30,7 +30,8 @@ using namespace utest::v1; void test_emac_memory_cb(int opt) { static bool send_request = true; - static bool echo_should_work = true; + static bool echo_may_fail = false; + static bool echo_may_succeed = true; static int no_response_cnt = 0; static int retries = 0; static int msg_len = 0; @@ -38,57 +39,95 @@ void test_emac_memory_cb(int opt) static bool next_step = true; static int options = CTP_OPT_NON_ALIGNED; + static int successful_pkts_this_step = 0; + static int failed_pkts_this_step = 0; + // Defines test step if (next_step) { next_step = false; + + // Verify that, if this was a test step where allocations were supposed to fail, they actually did fail. + // We can accept a couple of successful packets due to buffers that had been allocated earlier, but the + // *majority* of the packets should have failed. + if(test_step > 0 && !echo_may_succeed) + { + if(successful_pkts_this_step >= failed_pkts_this_step) + { + printf("Too many successful packets (%d) vs failed packets (%d). This was supposed to be a failure test!\r\n", + successful_pkts_this_step, failed_pkts_this_step); + SET_ERROR_FLAGS(TEST_FAILED); + END_TEST_LOOP; + } + } + successful_pkts_this_step = 0; + failed_pkts_this_step = 0; + switch (test_step) { case 0: printf("STEP 0: memory available\r\n\r\n"); emac_if_set_output_memory(true); emac_if_set_input_memory(true); - echo_should_work = true; + emac_if_check_memory(false); + echo_may_fail = false; + echo_may_succeed = true; break; case 1: printf("STEP 1: no input memory buffer memory available\r\n\r\n"); emac_if_set_output_memory(true); emac_if_set_input_memory(false); - echo_should_work = false; + emac_if_check_memory(false); + echo_may_fail = true; + echo_may_succeed = false; break; case 2: printf("STEP 2: memory available\r\n\r\n"); emac_if_set_output_memory(true); emac_if_set_input_memory(true); - echo_should_work = true; + emac_if_check_memory(false); + echo_may_fail = false; + echo_may_succeed = true; break; case 3: printf("STEP 3: no output memory buffer memory available\r\n\r\n"); emac_if_set_output_memory(false); emac_if_set_input_memory(true); - echo_should_work = false; + emac_if_check_memory(false); + + // For this test, zero-copy implementations can still successfully work because + // they don't need to allocate anything to send a packet. + // So we allow either success or failure. + echo_may_fail = true; + echo_may_succeed = true; break; case 4: printf("STEP 4: memory available\r\n\r\n"); emac_if_set_output_memory(true); emac_if_set_input_memory(true); - echo_should_work = true; + emac_if_check_memory(false); + echo_may_fail = false; + echo_may_succeed = true; break; case 5: printf("STEP 5: no output or input memory buffer memory available\r\n\r\n"); emac_if_set_output_memory(false); emac_if_set_input_memory(false); - echo_should_work = false; + emac_if_check_memory(false); + echo_may_fail = true; + echo_may_succeed = false; break; case 6: printf("STEP 6: memory available\r\n\r\n"); emac_if_set_output_memory(true); emac_if_set_input_memory(true); - echo_should_work = true; + emac_if_check_memory(false); + echo_may_fail = false; + echo_may_succeed = true; break; case 7: @@ -107,8 +146,9 @@ void test_emac_memory_cb(int opt) } else if (opt == TIMEOUT) { if (++no_response_cnt > 3) { if (++retries > 1) { + failed_pkts_this_step += 1; // If echo replies should be received fails the test case - if (echo_should_work) { + if (!echo_may_fail) { printf("too many retries\r\n\r\n"); SET_ERROR_FLAGS(TEST_FAILED); END_TEST_LOOP; @@ -124,6 +164,11 @@ void test_emac_memory_cb(int opt) } } + if(opt == INPUT) + { + successful_pkts_this_step += 1; + } + // Echo response received or retry count exceeded for memory buffers are not available if (opt == INPUT || next_len) { if (msg_len == ETH_MAX_LEN) { diff --git a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp index 3b6a0ce8b55..b75355c8cb2 100644 --- a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp +++ b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp @@ -73,7 +73,8 @@ EmacTestMemoryManager::EmacTestMemoryManager() : m_mem_mutex(), m_mem_buffers(), m_alloc_unit(MBED_CONF_NSAPI_EMAC_RX_POOL_BUF_SIZE), - m_memory_available(true) + m_memory_available(true), + m_pool_memory_available(true) { #ifdef ETHMEM_SECTION static bool ns_heap_init = false; @@ -171,7 +172,7 @@ emac_mem_buf_t *EmacTestMemoryManager::alloc_pool(uint32_t size, uint32_t align, check_align(align); - if ((opt & MEM_CHECK) && !m_memory_available) { + if ((opt & MEM_CHECK) && (!m_memory_available || !m_pool_memory_available)) { return NULL; } @@ -507,6 +508,16 @@ void EmacTestMemoryManager::set_memory_available(bool memory) } } +void EmacTestMemoryManager::set_pool_memory_available(bool memory) +{ + m_pool_memory_available = memory; + + // Poke the EMAC in case it can allocate buffers + if (m_pool_memory_available && m_memory_available && onPoolSpaceAvailCallback) { + onPoolSpaceAvailCallback(); + } +} + void EmacTestMemoryManager::get_memory_statistics(int *buffers, int *memory) { if (!buffers || !memory) { diff --git a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h index 95cc27bb21d..70beb00c533 100644 --- a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h +++ b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.h @@ -108,7 +108,7 @@ class EmacTestMemoryManager : public EMACMemoryManager { virtual void set_alloc_unit(uint32_t alloc_unit); /** - * Sets whether memory is available + * Sets whether memory (heap or pool) is available * * Can be used to disable memory allocation request from emac. * @@ -116,6 +116,15 @@ class EmacTestMemoryManager : public EMACMemoryManager { */ void set_memory_available(bool memory); + /** + * Sets whether pool memory is available. + * + * Can be used to disable memory allocation request for the pool from emac. + * + * @param memory True if memory is available + */ + void set_pool_memory_available(bool memory); + /** * Gets memory statistics * @@ -136,6 +145,7 @@ class EmacTestMemoryManager : public EMACMemoryManager { unsigned int m_alloc_unit; size_t m_pool_bufs_used = 0; bool m_memory_available; + bool m_pool_memory_available; }; #endif /* EMAC_TEST_MEMORY_MANAGER_H */ diff --git a/connectivity/netsocket/tests/emac_test_utils/emac_util.cpp b/connectivity/netsocket/tests/emac_test_utils/emac_util.cpp index 2b071471219..62a1ffe8c0c 100644 --- a/connectivity/netsocket/tests/emac_test_utils/emac_util.cpp +++ b/connectivity/netsocket/tests/emac_test_utils/emac_util.cpp @@ -384,25 +384,15 @@ void emac_if_set_output_memory(bool memory) void emac_if_set_input_memory(bool memory) { allow_input_memory_allocs = memory; - - emac_if_set_memory(memory); + EmacTestMemoryManager::get_instance().set_pool_memory_available(allow_input_memory_allocs); } void emac_if_check_memory(bool output) { if (output) { - emac_if_set_memory(allow_output_memory_allocs); + EmacTestMemoryManager::get_instance().set_memory_available(allow_output_memory_allocs); } else { - emac_if_set_memory(allow_input_memory_allocs); - } -} - -void emac_if_set_memory(bool memory) -{ - static bool memory_value = true; - if (memory_value != memory) { - memory_value = memory; - EmacTestMemoryManager::get_instance().set_memory_available(memory); + EmacTestMemoryManager::get_instance().set_memory_available(allow_input_memory_allocs); } } diff --git a/connectivity/netsocket/tests/emac_test_utils/emac_util.h b/connectivity/netsocket/tests/emac_test_utils/emac_util.h index 82f0825fc76..b470c7977b9 100644 --- a/connectivity/netsocket/tests/emac_test_utils/emac_util.h +++ b/connectivity/netsocket/tests/emac_test_utils/emac_util.h @@ -113,10 +113,17 @@ void emac_if_set_all_multicast(bool all); void emac_if_add_multicast_group(uint8_t *address); void emac_if_remove_multicast_group(uint8_t *address); +/// If called with false as the arg, disables memory allocation +/// (a) from the pool at all times, and +/// (b) from the heap when not actively transmitting a packet void emac_if_set_output_memory(bool memory); + +/// If called with true as the arg, disables memory allocation from the pool and heap during packet Tx. void emac_if_set_input_memory(bool memory); + +/// Switches between input (false) and output (true) mode for the memory allocation disable. +/// Called by the CTP code when it's sending a packet. void emac_if_check_memory(bool output); -void emac_if_set_memory(bool memory); void emac_if_set_ctp_server_enabled(bool enabled); From 1dc339cf2d10efdf503ac4f14bf8aad8203377f6 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 23 Mar 2025 11:31:44 -0700 Subject: [PATCH 45/47] Document most of Rx DMA, do rename --- connectivity/drivers/emac/CompositeEMAC.md | 56 +- .../drivers/emac/TARGET_STM/STM32EthMACv1.h | 2 +- .../drivers/emac/TARGET_STM/STM32EthMACv2.h | 4 +- .../emac/doc/rx-ring-after-dequeue.svg | 515 +++++++++++++++++ .../emac/doc/rx-ring-after-rebuild.svg | 515 +++++++++++++++++ .../drivers/emac/doc/rx-ring-after-rx.svg | 519 ++++++++++++++++++ .../emac/doc/rx-ring-initial-state.svg | 515 +++++++++++++++++ .../drivers/emac/doc/tx-ring-exhausted.svg | 143 ++--- .../drivers/emac/include/GenericEthDMA.h | 6 +- 9 files changed, 2180 insertions(+), 95 deletions(-) create mode 100644 connectivity/drivers/emac/doc/rx-ring-after-dequeue.svg create mode 100644 connectivity/drivers/emac/doc/rx-ring-after-rebuild.svg create mode 100644 connectivity/drivers/emac/doc/rx-ring-after-rx.svg create mode 100644 connectivity/drivers/emac/doc/rx-ring-initial-state.svg diff --git a/connectivity/drivers/emac/CompositeEMAC.md b/connectivity/drivers/emac/CompositeEMAC.md index 2120e1335ab..611d210036e 100644 --- a/connectivity/drivers/emac/CompositeEMAC.md +++ b/connectivity/drivers/emac/CompositeEMAC.md @@ -18,11 +18,11 @@ Before we can get into the details of how CompositeEMAC works, we need to go ove ![Embedded Ethernet diagram](./doc/embedded-ethernet.svg) -To run an ethernet connection, two chips need to work together*: the microcontroller and an external Ethernet PHY. The microcontroller sends and receives logic level Ethernet packets, while the PHY transforms those into Ethernet signals, which are decidedly *not* logic level (and actually have a lot in common with radio signals). The Ethernet signals, called MDI (Media Dependent Interface) pairs, are sent through an isolation transformer, which removes common mode interference and provides electrical isolation (e.g. so that the two ends of the connection can have different ground voltage levels). +To run an ethernet connection, two chips need to work together*: the microcontroller and an external Ethernet PHY (PHYsical layer transceiver) chip. The microcontroller sends and receives logic level Ethernet packets, while the PHY transforms those into Ethernet signals, which are decidedly *not* logic level (and actually have a lot in common with radio signals). The Ethernet signals, called MDI (Media Dependent Interface) pairs, are sent through an isolation transformer, which removes common mode interference and provides electrical isolation (e.g. so that the two ends of the connection can have different ground voltage levels). Then, they go into the ethernet jack and across the ethernet cable to the link partner! -The PHY and the MCU are connected via a standard called [Reduced Media Independent Interface](https://en.wikipedia.org/wiki/Media-independent_interface#RMII) (RMII), which transfers the Ethernet packets as serialized bytes. This is an 8-wire bus with a 50MHz clock, four receive lines, and three transmit lines. The clock is traditionally either supplied by the PHY or by a dedicated clock generator chip, though some MCUs support supplying this clock as well. In addition to RMII, there's also a two-wire command and control bus called [Management Data IO](https://en.wikipedia.org/wiki/Management_Data_Input/Output) (MDIO) (though it can also be referred to Station Management Interface (SMI) or even "MiiM"). MDIO is used for talking directly to the PHY, not for sending Ethernet packets. MDIO is an open-drain bus similar to I2C, but with 16-bit words instead of bytes and a specific frame format (referred to as "Clause 22"). Unlike RMII, MDIO is a multi-drop bus, so you can actually connect up to 15 PHYs or other devices to one set of MDIO lines as long as they have different addresses! +The PHY and the MCU are connected via a standard called [Reduced Media Independent Interface](https://en.wikipedia.org/wiki/Media-independent_interface#RMII) (RMII), which transfers the Ethernet packets as serialized bytes. This is an 8-wire bus with a 50MHz clock, four receive lines, and three transmit lines. The clock is traditionally either supplied by the PHY or by a dedicated clock generator chip, though some MCUs support supplying this clock as well. In addition to RMII, there's also a two-wire command and control bus called [Management Data IO](https://en.wikipedia.org/wiki/Management_Data_Input/Output) (MDIO) (though it can also be referred to Station Management Interface (SMI) or even "MiiM" for some reason). MDIO is used for talking directly to the PHY, not for sending Ethernet packets. MDIO is an open-drain bus similar to I2C, but with 16-bit words instead of bytes and a specific frame format (referred to as "Clause 22"). Unlike RMII, MDIO is a multi-drop bus, so you can actually connect up to 15 PHYs or other devices to one set of MDIO lines as long as they have different addresses! -Inside the microcontroller, the bridge between the CPU and Ethernet is a peripheral called the Ethernet MAC. MAC stands for "Media Access Control" and refers to the second layer of the Ethernet protocol stack, the logic which encodes Ethernet packets and decides when to send them across the wire. The MAC has a number of moving parts inside. The simplest is the block of configuration registers, which is accessible at a specific memory address and sets up operation of the MAC (e.g. what MAC addresses the hardware should accept and which checksums should be inserted/checked by the MAC). There is also an MDIO master interface, which controls the MDIO lines to talk to the PHY. +Inside the microcontroller, the bridge between the CPU and Ethernet is a peripheral called the Ethernet MAC. MAC stands for "Media Access Control" and refers to the second layer of the Ethernet protocol stack, the logic which encodes Ethernet packets and decides when to send them across the wire. The MAC has a number of moving parts inside, which are shown in the diagram above. The simplest is the block of configuration registers, which is accessible at a specific memory address and sets up operation of the MAC (e.g. what MAC addresses the hardware should accept and which checksums should be inserted/checked by the MAC). There is also an MDIO master interface, which controls the MDIO lines to talk to the PHY. And then, we have the DMA. Every Ethernet MAC I've seen also has DMA functionality. This means that the Ethernet peripheral can transmit and receive packets without direct CPU intervention. This is very important because it means your device can hit high network speeds without needing to have your CPU blocked for lots of time waiting on Ethernet packets to move through the hardware! For transmit, there will be a Tx DMA module which fetches data from the main RAM, and then enqueues the packet bytes plus control information into a FIFO (which is usually at least a couple thousand bytes long). Then, another block in the MAC, sometimes called the MTL (MAC Translation Layer) takes these bytes, applies any needed Ethernet framing, and shifts them out of the RMII Tx port. @@ -164,4 +164,52 @@ For each target, the `GenericTxDMARing` class needs to be extended. The subclass - Checking if a given buffer address is accessible by the DMA controller (since, on many MCUs, certain areas of memory are not OK and the buffer will need to be copied) - Giving a descriptor to DMA after populating it with a given buffer -Everything else, including the descriptor tracking and memory management, is done by the superclass. This should let target implementations focus only on the low level descriptor format while relying on common code for everything else. \ No newline at end of file +Everything else, including the descriptor tracking and memory management, is done by the superclass. This should let target implementations focus only on the low level descriptor format while relying on common code for everything else. + +### Rx DMA + +The Rx DMA works similarly to the Tx DMA. To instantiate a `CompositeEMAC`, you must provide an instance of `CompositeEMAC::RxDMA`. Mbed provides a generic superclass (`GenericRxDMARing`) which implements most of TxDMA, but must be extended for each target to add the last pieces of functionality. + +#### Generic Rx DMA Ring Operation + +Like the Tx DMA, the Rx DMA works using a descriptor ring and two indexes that track where we are in the descriptor ring. `rxNextIndex` tracks the next descriptor index that we expect the Ethernet DMA to receive into, while `rxBuildIndex` tracks the next descriptor that we are going to give back to the DMA when possible. + +##### Initial State + +Unlike the Tx DMA, the initial state of the Rx DMA ring is for all the descriptors to be given to DMA and have their buffer pointers populated with blank network stack buffers. This makes them all available for the MAC to receive into. + +![Rx ring initial state](doc/rx-ring-initial-state.svg) + +Note that, for now, we always leave one un-filled descriptor in the ring. This is needed because certain target ethernet MACs (e.g. STM32 Eth IP v2) cannot have every Rx descriptor enqueued at the same time or they get confused and think there are *no* Rx descs enqueued! So, for now, we always need to have one extra Rx desc in the ring. + +##### After Packet Rx + +Let's now see what it looks like after we receive a large Ethernet packet (1500 bytes). With the default setting for `nsapi.emac-rx-pool-buf-size`, each of the blank buffers is 592 bytes, so packets <= 592 bytes only need one buffer, while a 1500 byte packet needs three buffers. + +Slight tangent: We could set `nsapi.emac-rx-pool-buf-size` to >=1500 bytes so that each packet always fits in one descriptor, and this would make receiving large packets more efficient (in terms of CPU and descriptors used). However, this would mean we would need ~3x the RAM to allocate our 6 descriptor ring, and that extra RAM is wasted when we receive a small packet that only needs a fraction of that 1500 byte buffer. So, this setting is a tradeoff, and may be customized if you know you are dealing with only small or only large packets. + +Anyway, when the packet is received, the MAC will write it into the next three available descriptors, then clear the own flags and mark which descriptors contain the start and end of the packet. Then, it delievers an Rx ISR to the driver. This is the state of the DMA ring when the Rx ISR fires: + +![Rx ring after Rx](doc/rx-ring-after-rx.svg) + +##### Packet Dequeued + +The Rx ISR checks the DMA ring to make sure we have at least one complete packet. Then, it signals the MAC thread to dequeue the packet and pass it off to the IP stack. + +When the packet is dequeued, the buffers will be removed from the descriptors and, for now, they stay in the "owned by application" region of the descriptor ring (between `rxNextIndex` and `rxBuildIndex`). + +![Rx ring after dequeue](doc/rx-ring-after-dequeue.svg) + +##### Descriptors Rebuilt + +The final step is to "rebuild" the descriptors, which means attaching a fresh buffer* to them and giving them back to the DMA. This is done by the `rebuildDescriptors()` function, and basically keeps rebuilding descriptors until `rxBuildIndex` is one behind `rxNextIndex`, or until we run out of memory in the Rx memory pool. + +Ideally, we will immediately rebuild all the descriptors right after dequeuing the packet. However, if we don't have enough memory, we will rebuild the descs later when we do. + +Once the descriptors have been rebuilt, we will basically be back in the initial state, except moved three descriptors along the Rx ring. + +![Rx ring after rebuild](doc/rx-ring-after-rebuild.svg) + +##### Memory Exhaustion + +#### Target-Specific Rx DMA Implementation \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h index 2b6bb52775d..fca6fef9405 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv1.h @@ -58,7 +58,7 @@ class STM32EthMACv1 : public CompositeEMAC {} }; - class RxDMA : public GenericRxDMALoop { + class RxDMA : public GenericRxDMARing { protected: ETH_TypeDef * const base; // Base address of Ethernet peripheral StaticCacheAlignedBuffer rxDescs; // Rx descriptors diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h index 40a3d74aa47..939b7cb9b0d 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -31,7 +31,7 @@ namespace mbed { { protected: ETH_TypeDef * const base; // Base address of Ethernet peripheral - StaticCacheAlignedBuffer txDescs; // Tx descriptors + StaticCacheAlignedBuffer txDescs; // Tx descriptors void startDMA() override; @@ -52,7 +52,7 @@ namespace mbed { {} }; - class RxDMA : public GenericRxDMALoop { + class RxDMA : public GenericRxDMARing { protected: ETH_TypeDef * const base; // Base address of Ethernet peripheral StaticCacheAlignedBuffer rxDescs; // Rx descriptors diff --git a/connectivity/drivers/emac/doc/rx-ring-after-dequeue.svg b/connectivity/drivers/emac/doc/rx-ring-after-dequeue.svg new file mode 100644 index 00000000000..b82fd62ade8 --- /dev/null +++ b/connectivity/drivers/emac/doc/rx-ring-after-dequeue.svg @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + + Desc 1 + + Desc 2 + + dmaOwn = true + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + flags = none + flags = none + flags = none + flags = none + + + rxBuildIndex + + + rxNextIndex + Empty + Empty + Empty + dmaOwn = true + Points to Blank Buffer + Points to Blank Buffer + Empty + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + flags = none + flags = none + + diff --git a/connectivity/drivers/emac/doc/rx-ring-after-rebuild.svg b/connectivity/drivers/emac/doc/rx-ring-after-rebuild.svg new file mode 100644 index 00000000000..00bc648e5e0 --- /dev/null +++ b/connectivity/drivers/emac/doc/rx-ring-after-rebuild.svg @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + + Desc 1 + + Desc 2 + + dmaOwn = true + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + flags = none + flags = none + flags = none + flags = none + + + rxBuildIndex + + + rxNextIndex + Empty + dmaOwn = true + Points to Blank Buffer + Points to Blank Buffer + dmaOwn = false + flags = none + flags = none + Points to Blank Buffer + Points to Blank Buffer + Points to Blank Buffer + dmaOwn = true + dmaOwn = true + dmaOwn = true + + diff --git a/connectivity/drivers/emac/doc/rx-ring-after-rx.svg b/connectivity/drivers/emac/doc/rx-ring-after-rx.svg new file mode 100644 index 00000000000..271916c0cc0 --- /dev/null +++ b/connectivity/drivers/emac/doc/rx-ring-after-rx.svg @@ -0,0 +1,519 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + + Desc 1 + + Desc 2 + + dmaOwn = true + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + flags = firstDesc + flags = none + flags = lastDesc + flags = none + flags = none + flags = none + + + rxBuildIndex + + + rxNextIndex + Points to Packet 0 Buf 0 + Points to Packet 0 Buf 1 + Points to Packet 0 Buf 2 + dmaOwn = true + Points to Blank Buffer + Points to Blank Buffer + Empty + dmaOwn = false + dmaOwn = false + dmaOwn = false + dmaOwn = false + + diff --git a/connectivity/drivers/emac/doc/rx-ring-initial-state.svg b/connectivity/drivers/emac/doc/rx-ring-initial-state.svg new file mode 100644 index 00000000000..241c1735ccb --- /dev/null +++ b/connectivity/drivers/emac/doc/rx-ring-initial-state.svg @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Desc 0 + + Desc 1 + + Desc 2 + + dmaOwn = true + Desc 3 + + Desc 4 + + Desc 5 + + + + + + + + dmaOwn = true + flags = none + flags = none + flags = none + flags = none + flags = none + flags = none + dmaOwn = true + dmaOwn = true + + + rxBuildIndex + + + rxNextIndex + Points to Blank Buffer + Points to Blank Buffer + dmaOwn = true + Points to Blank Buffer + Points to Blank Buffer + Points to Blank Buffer + Empty + dmaOwn = false + + diff --git a/connectivity/drivers/emac/doc/tx-ring-exhausted.svg b/connectivity/drivers/emac/doc/tx-ring-exhausted.svg index 96b97e21c6a..f5e2b1b9e46 100644 --- a/connectivity/drivers/emac/doc/tx-ring-exhausted.svg +++ b/connectivity/drivers/emac/doc/tx-ring-exhausted.svg @@ -8,7 +8,7 @@ version="1.1" id="svg1" inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)" - sodipodi:docname="tx-ring-template.svg" + sodipodi:docname="tx-ring-exhausted.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -24,8 +24,8 @@ inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" inkscape:zoom="3.0765692" - inkscape:cx="273.0314" - inkscape:cy="121.07642" + inkscape:cx="270.91866" + inkscape:cy="82.722014" inkscape:window-width="2560" inkscape:window-height="1417" inkscape:window-x="-8" @@ -323,7 +323,7 @@ style="stroke-width:1.50047">dmaOwn = true + transform="translate(-118.25292,0.91934455)"> txReclaimIndex + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Consolas;-inkscape-font-specification:Consolas;fill:#484848;fill-opacity:1">rxBuildIndex txSendIndex - - Packet being Txed + x="-63.108498" + y="81.938576" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Consolas;-inkscape-font-specification:Consolas;fill:#484848;fill-opacity:1">rxNextIndex Points to Packet 0 - Empty - Empty - Empty - Empty + x="68.823723" + y="150.44464" + style="stroke-width:1.50047">Points to Blank Buffer Empty + id="tspan11-6" + x="163.91342" + y="151.51054" + style="stroke-width:1.50047">Points to Blank Buffer dmaOwn = true + Points to Blank Buffer + Points to Blank Buffer + Points to Blank Buffer diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 3f9c9177c11..5d96958d187 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -42,7 +42,7 @@ namespace mbed { /// Pointer to first memory buffer in the chain associated with descriptor n. /// The buffer address shall only be set for the *last* descriptor, so that the entire chain is freed /// when the last descriptor is returned. - std::array descStackBuffers{}; + std::array descStackBuffers{}; /// EventFlag used to signal when a Tx descriptor becomes available rtos::EventFlags txDescAvailFlag; @@ -307,7 +307,7 @@ namespace mbed { * The subclass must allocate the DMA descriptors, and all access to them is done through virtual functions * that the subclass must override. */ - class GenericRxDMALoop : public CompositeEMAC::RxDMA { + class GenericRxDMARing : public CompositeEMAC::RxDMA { protected: /// How many extra buffers to leave in the Rx pool, relative to how many we keep assigned to Rx descriptors. /// We want to keep some amount of extra buffers because constantly hitting the network stack with failed pool @@ -340,7 +340,7 @@ namespace mbed { size_t rxPoolPayloadSize; /// Constructor. Subclass must allocate descriptor array of size RX_NUM_DESCS - GenericRxDMALoop() = default; + GenericRxDMARing() = default; /// Configure DMA registers to point to the DMA ring, /// and enable DMA. This is done before the MAC itself is enabled, and before any descriptors From 3a77ce045446d1986e0cd7a91fa32e2f67d46ba6 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 23 Mar 2025 19:53:43 -0700 Subject: [PATCH 46/47] Reformat, fix STM32 Eth v2 potential to have issues when the Tx ring gets full --- .../drivers/emac/TARGET_STM/STM32EthMACv2.h | 1 + .../drivers/emac/include/GenericEthDMA.h | 39 ++++++++++++++++--- .../TESTS/network/emac/emac_test_memory.cpp | 11 ++---- .../tests/emac_test_utils/emac_util.h | 2 +- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h index 939b7cb9b0d..75fda1823f4 100644 --- a/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h +++ b/connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.h @@ -48,6 +48,7 @@ namespace mbed { void giveToDMA(size_t descIdx, uint8_t const * buffer, size_t len, bool firstDesc, bool lastDesc) override; public: explicit TxDMA(ETH_TypeDef * const base): + GenericTxDMARing(1), // Request that 1 Tx descriptor is always left unfilled base(base) {} }; diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 5d96958d187..05cb0c08635 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -39,6 +39,10 @@ namespace mbed { /// Number of entries in the Tx descriptor ring static constexpr size_t TX_NUM_DESCS = MBED_CONF_NSAPI_EMAC_TX_NUM_DESCS; + /// Extra, unfilled Tx descs to leave in the DMA ring at all times. + /// This is used to support Eth MACs that don't allow enqueuing every single descriptor at a time. + const size_t extraTxDescsToLeave; + /// Pointer to first memory buffer in the chain associated with descriptor n. /// The buffer address shall only be set for the *last* descriptor, so that the entire chain is freed /// when the last descriptor is returned. @@ -55,6 +59,11 @@ namespace mbed { std::atomic txDescsOwnedByApplication; ///< Number of Tx descriptors owned by the application. Decremented by txPacket() and incremented by reclaimTxDescs() size_t txReclaimIndex; ///< Index of the next Tx descriptor that will be reclaimed by the mac thread calling reclaimTxDescs(). + /// Construct, passing a value for extraTxDescsToLeave + GenericTxDMARing(size_t extraTxDescsToLeave = 0): + extraTxDescsToLeave(extraTxDescsToLeave) + {} + /// Configure DMA registers to point to the DMA ring, /// and enable DMA. This is done before the MAC itself is enabled. virtual void startDMA() = 0; @@ -181,9 +190,10 @@ namespace mbed { CompositeEMAC::ErrCode txPacket(net_stack_mem_buf_t * buf) { // Step 1: Figure out if we can send this zero-copy, or if we need to copy it. - size_t neededDescs = memory_manager->count_buffers(buf); + size_t packetDescsUsed = memory_manager->count_buffers(buf); + size_t neededFreeDescs = packetDescsUsed + extraTxDescsToLeave; bool needToCopy = false; - if(neededDescs >= TX_NUM_DESCS) + if(neededFreeDescs >= TX_NUM_DESCS) { // Packet uses too many buffers, we have to copy it into a continuous buffer. // Note: Some Eth DMAs (e.g. STM32 v2) cannot enqueue all the descs in the ring at the same time @@ -191,7 +201,7 @@ namespace mbed { needToCopy = true; } - if(!needToCopy && (neededDescs > txDescsOwnedByApplication && txDescsOwnedByApplication > 0)) { + if(!needToCopy && (neededFreeDescs > txDescsOwnedByApplication && txDescsOwnedByApplication > extraTxDescsToLeave)) { // Packet uses more buffers than we have descriptors, but we can send it immediately if we copy // it into a single buffer. needToCopy = true; @@ -233,7 +243,8 @@ namespace mbed { // We should have gotten just one contiguous buffer MBED_ASSERT(memory_manager->get_next(newBuf) == nullptr); - neededDescs = 1; + packetDescsUsed = 1; + neededFreeDescs = packetDescsUsed + extraTxDescsToLeave; // Copy data over memory_manager->copy_from_buf(static_cast(memory_manager->get_ptr(newBuf)), memory_manager->get_len(newBuf), buf); @@ -245,14 +256,14 @@ namespace mbed { // Note that, in my experience, it's better to block here, as dropping the packet // due to not having enough buffers can create weird effects when the application sends // lots of packets at once. - while(txDescsOwnedByApplication < neededDescs) + while(txDescsOwnedByApplication < neededFreeDescs) { txDescAvailFlag.wait_any_for(1, rtos::Kernel::wait_for_u32_forever); } // Step 4: Load buffer into descriptors and send net_stack_mem_buf_t * currBuf = buf; - for(size_t descCount = 0; descCount < neededDescs; descCount++) + for(size_t descCount = 0; descCount < packetDescsUsed; descCount++) { #if __DCACHE_PRESENT // Write buffer back to main memory @@ -453,6 +464,8 @@ namespace mbed { // This could potentially produce a false positive if we do this in the middle of receiving // an existing packet, but that is unlikely and will not cause anything bad to happen if it does. + bool seenFirstDesc = false; + for(size_t descCount = 0; descCount < RX_NUM_DESCS; descCount++) { size_t descIdx = (rxNextIndex + descCount) % RX_NUM_DESCS; @@ -466,6 +479,20 @@ namespace mbed { // Descriptor owned by DMA. We are out of descriptors to process. return false; } + if(isFirstDesc(descIdx)) + { + if(seenFirstDesc) + { + // First desc seen after another first desc. + // Some MACs do this if they run out of Rx descs when halfway through a packet. + // dequeuePacket() can clean this up and reclaim the partial packet desc(s). + return true; + } + else + { + seenFirstDesc = true; + } + } if(isErrorDesc(descIdx) || isLastDesc(descIdx)) { // Reclaimable descriptor or complete packet detected. diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_memory.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_memory.cpp index 592437309dc..b6115392af9 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_test_memory.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_test_memory.cpp @@ -49,12 +49,10 @@ void test_emac_memory_cb(int opt) // Verify that, if this was a test step where allocations were supposed to fail, they actually did fail. // We can accept a couple of successful packets due to buffers that had been allocated earlier, but the // *majority* of the packets should have failed. - if(test_step > 0 && !echo_may_succeed) - { - if(successful_pkts_this_step >= failed_pkts_this_step) - { + if (test_step > 0 && !echo_may_succeed) { + if (successful_pkts_this_step >= failed_pkts_this_step) { printf("Too many successful packets (%d) vs failed packets (%d). This was supposed to be a failure test!\r\n", - successful_pkts_this_step, failed_pkts_this_step); + successful_pkts_this_step, failed_pkts_this_step); SET_ERROR_FLAGS(TEST_FAILED); END_TEST_LOOP; } @@ -164,8 +162,7 @@ void test_emac_memory_cb(int opt) } } - if(opt == INPUT) - { + if (opt == INPUT) { successful_pkts_this_step += 1; } diff --git a/connectivity/netsocket/tests/emac_test_utils/emac_util.h b/connectivity/netsocket/tests/emac_test_utils/emac_util.h index b470c7977b9..21a88654fed 100644 --- a/connectivity/netsocket/tests/emac_test_utils/emac_util.h +++ b/connectivity/netsocket/tests/emac_test_utils/emac_util.h @@ -114,7 +114,7 @@ void emac_if_add_multicast_group(uint8_t *address); void emac_if_remove_multicast_group(uint8_t *address); /// If called with false as the arg, disables memory allocation -/// (a) from the pool at all times, and +/// (a) from the pool at all times, and /// (b) from the heap when not actively transmitting a packet void emac_if_set_output_memory(bool memory); From d359996849e0ce97b70126d1bd57c2e0cf707da9 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Tue, 25 Mar 2025 09:12:16 -0700 Subject: [PATCH 47/47] Finish documenting Rx DMA --- connectivity/drivers/emac/CompositeEMAC.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/connectivity/drivers/emac/CompositeEMAC.md b/connectivity/drivers/emac/CompositeEMAC.md index 611d210036e..fe459ee9bd6 100644 --- a/connectivity/drivers/emac/CompositeEMAC.md +++ b/connectivity/drivers/emac/CompositeEMAC.md @@ -210,6 +210,25 @@ Once the descriptors have been rebuilt, we will basically be back in the initial ![Rx ring after rebuild](doc/rx-ring-after-rebuild.svg) +*In most EMAC drivers, you would just copy the Rx packet out of the DMA buffer, then re-enqueue the descriptor without changing the buffer. However, since this is a zero-copy EMAC, we give the original DMA buffer to the network stack, so we then need to get a fresh buffer before we can re-enqueue the descriptor. + ##### Memory Exhaustion -#### Target-Specific Rx DMA Implementation \ No newline at end of file +Running out of Rx pool memory is a problem for zero-copy Rx drivers in particular -- if the driver cannot rebuild an Rx descriptor when a packet is dequeued, it has to rebuild it at some later date. If this never happens, no reception can occur. + +Until CompositeEMAC was added, Mbed didn't have a good way to solve this problem. Some zero-copy drivers (e.g. LPC1768) would at least try and rebuild unbuilt descriptors the next time a packet was received, but none of them could handle the case where all Rx memory was exhausted -- there would be no built descriptors in the ring, so no packets could be received, and the MAC would be stuck forever. + +To solve this problem, the CompositeEMAC PR series added a new hook in both of the IP stacks for when a pool buffer is deallocated. This then triggers a callback to the memory manager, then to the MAC. This way, the MAC will know when Rx memory is available again and can unstick itself! + +#### Target-Specific Rx DMA Implementation + +For each target, the `GenericRxDMARing` class needs to be extended. The subclass must provide a couple functions: + +- Allocating the actual DMA descriptors (since this often has special requirements on alignment or memory banks) +- Initializing and deinitializing the Rx DMA +- Checking the dmaOwn flag on a descriptor +- Checking the flags (first, last, error) on a descriptor +- Getting the total length of a packet received into a series of descriptors +- Returning a rebuilt descriptor to the DMA controller, with a given buffer address + +Everything else, including the descriptor tracking and memory management, is done by the superclass. This should let target implementations focus only on the low level descriptor format while relying on common code for everything else.