From 9095b037aaed983b84c3d7e2113031b4dfe9759b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=20Lepp=C3=A4nen?= Date: Fri, 1 Jun 2018 12:58:27 +0300 Subject: [PATCH 1/2] Updated EMAC test environment for LPCxx boards Updated EMAC memory manager to use libservice nsdynmemlib for EMAC memory buffers. Located the nsdynmemlib buffer heap to DMA safe memory bank on LPCxx boards. Optimized placement of static variables on EMAC test environment for LPCxx boards to maximize available memory. --- TESTS/network/emac/emac_TestMemoryManager.cpp | 59 +++++++++++++++++++ TESTS/network/emac/emac_TestNetworkStack.cpp | 12 ++++ TESTS/network/emac/emac_TestNetworkStack.h | 32 ++++++++++ TESTS/network/emac/emac_util.cpp | 39 +++++++++++- TESTS/network/emac/emac_util.h | 2 +- 5 files changed, 141 insertions(+), 3 deletions(-) diff --git a/TESTS/network/emac/emac_TestMemoryManager.cpp b/TESTS/network/emac/emac_TestMemoryManager.cpp index 6a47454034..05a0a62663 100644 --- a/TESTS/network/emac/emac_TestMemoryManager.cpp +++ b/TESTS/network/emac/emac_TestMemoryManager.cpp @@ -27,6 +27,11 @@ #include "rtos/Mutex.h" +extern "C" { +#include "arm_hal_interrupt_private.h" +} +#include "nsdynmemLIB.h" + #include "EMACMemoryManager.h" #include "emac_TestMemoryManager.h" @@ -43,12 +48,57 @@ char s_trace_buffer[100] = MEM_MNGR_TRACE; +/* For LPC boards define the heap memory bank ourselves to give us section placement + control */ +#ifndef ETHMEM_SECTION +#if defined(TARGET_LPC4088) || defined(TARGET_LPC4088_DM) +# if defined (__ICCARM__) +# define ETHMEM_SECTION +# elif defined(TOOLCHAIN_GCC_CR) +# define ETHMEM_SECTION __attribute__((section(".data.$RamPeriph32"))) +# else +# define ETHMEM_SECTION __attribute__((section("AHBSRAM1"),aligned)) +# endif +#elif defined(TARGET_LPC1768) || defined(TARGET_LPC1769) +# if defined (__ICCARM__) +# define ETHMEM_SECTION +# elif defined(TOOLCHAIN_GCC_CR) +# define ETHMEM_SECTION __attribute__((section(".data.$RamPeriph32"))) +# else +# define ETHMEM_SECTION __attribute__((section("AHBSRAM0"),aligned)) +# endif +#endif +#endif + +#ifdef ETHMEM_SECTION +// Use nanostack libservice dynamic memory library +#define EMAC_HEAP_SIZE 16300 + +#if defined (__ICCARM__) +#pragma location = ".ethusbram" +#endif +ETHMEM_SECTION static unsigned char ns_heap[EMAC_HEAP_SIZE]; + +void emac_heap_error_handler(heap_fail_t event) +{ + MBED_ASSERT(0); +} +#endif + EmacTestMemoryManager::EmacTestMemoryManager() : m_mem_mutex(), m_mem_buffers(), m_alloc_unit(BUF_POOL_SIZE), m_memory_available(true) { +#ifdef ETHMEM_SECTION + static bool ns_heap_init = false; + if (!ns_heap_init) { + platform_critical_init(); // Create mutex for dynamic memory library + ns_dyn_mem_init(ns_heap, EMAC_HEAP_SIZE, emac_heap_error_handler, NULL); + ns_heap_init = true; + } +#endif } emac_mem_buf_t *EmacTestMemoryManager::alloc_heap(uint32_t size, uint32_t align) @@ -74,7 +124,11 @@ emac_mem_buf_t *EmacTestMemoryManager::alloc_heap(uint32_t size, uint32_t align, CHECK_ASSERT(buf, "alloc_heap() no memory"); +#ifdef ETHMEM_SECTION + buf->buffer = ns_dyn_mem_alloc(BUF_HEAD_SIZE + size + align + BUF_TAIL_SIZE); +#else buf->buffer = std::malloc(BUF_HEAD_SIZE + size + align + BUF_TAIL_SIZE); +#endif CHECK_ASSERT(buf->buffer, "alloc_heap() no memory"); @@ -218,7 +272,12 @@ void EmacTestMemoryManager::free(emac_mem_buf_t *buf) emac_memory_t *next = mem_buf->next; m_mem_buffers.erase(mem_buf_entry); + +#ifdef ETHMEM_SECTION + ns_dyn_mem_free(mem_buf->buffer); +#else std::free(mem_buf->buffer); +#endif delete mem_buf; mem_buf = next; diff --git a/TESTS/network/emac/emac_TestNetworkStack.cpp b/TESTS/network/emac/emac_TestNetworkStack.cpp index 9209f79492..cb5a2d2a2c 100644 --- a/TESTS/network/emac/emac_TestNetworkStack.cpp +++ b/TESTS/network/emac/emac_TestNetworkStack.cpp @@ -39,6 +39,18 @@ nsapi_error_t EmacTestNetworkStack::add_dns_server(const SocketAddress &address) return NSAPI_ERROR_OK; } +nsapi_error_t EmacTestNetworkStack::call_in(int delay, mbed::Callback func) +{ + // Implemented as empty to save memory + return NSAPI_ERROR_DEVICE_ERROR; +} + +EmacTestNetworkStack::call_in_callback_cb_t EmacTestNetworkStack::get_call_in_callback() +{ + call_in_callback_cb_t cb(this, &EmacTestNetworkStack::call_in); + return cb; +} + nsapi_error_t EmacTestNetworkStack::socket_open(nsapi_socket_t *handle, nsapi_protocol_t proto) { return NSAPI_ERROR_OK; diff --git a/TESTS/network/emac/emac_TestNetworkStack.h b/TESTS/network/emac/emac_TestNetworkStack.h index 8d325c6f89..d781d15190 100644 --- a/TESTS/network/emac/emac_TestNetworkStack.h +++ b/TESTS/network/emac/emac_TestNetworkStack.h @@ -356,6 +356,38 @@ protected: int optname, void *optval, unsigned *optlen); private: + + /** Call in callback + * + * Callback is used to call the call in method of the network stack. + */ + typedef mbed::Callback user_cb)> call_in_callback_cb_t; + + /** Get a call in callback + * + * Get a call in callback from the network stack context. + * + * Callback should not take more than 10ms to execute, otherwise it might + * prevent underlying thread processing. A portable user of the callback + * should not make calls to network operations due to stack size limitations. + * The callback should not perform expensive operations such as socket recv/send + * calls or blocking operations. + * + * @return Call in callback + */ + virtual call_in_callback_cb_t get_call_in_callback(); + + /** Call a callback after a delay + * + * Call a callback from the network stack context after a delay. If function + * returns error callback will not be called. + * + * @param delay Delay in milliseconds + * @param func Callback to be called + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t call_in(int delay, mbed::Callback func); + Interface *m_interface; }; diff --git a/TESTS/network/emac/emac_util.cpp b/TESTS/network/emac/emac_util.cpp index 9bffd9cf65..23ece204ce 100644 --- a/TESTS/network/emac/emac_util.cpp +++ b/TESTS/network/emac/emac_util.cpp @@ -36,6 +36,32 @@ using namespace utest::v1; +/* For LPC boards define the memory bank ourselves to give us section placement + control */ +#ifndef ETHMEM_SECTION +#if defined(TARGET_LPC4088) || defined(TARGET_LPC4088_DM) +# if defined (__ICCARM__) +# define ETHMEM_SECTION +# elif defined(TOOLCHAIN_GCC_CR) +# define ETHMEM_SECTION __attribute__((section(".data.$RamPeriph32"))) +# else +# define ETHMEM_SECTION __attribute__((section("AHBSRAM0"),aligned)) +# endif +#elif defined(TARGET_LPC1768) || defined(TARGET_LPC1769) +# if defined (__ICCARM__) +# define ETHMEM_SECTION +# elif defined(TOOLCHAIN_GCC_CR) +# define ETHMEM_SECTION __attribute__((section(".data.$RamPeriph32"))) +# else +# define ETHMEM_SECTION __attribute__((section("AHBSRAM1"),aligned)) +# endif +#endif +#endif + +#ifndef ETHMEM_SECTION +#define ETHMEM_SECTION +#endif + typedef struct { int length; int receipt_number; @@ -63,7 +89,12 @@ static int eth_mtu_size = 0; // Event queue static rtos::Semaphore worker_loop_semaphore; static rtos::Semaphore link_status_semaphore; -static EventQueue worker_loop_event_queue(20 * EVENTS_EVENT_SIZE); + +#if defined (__ICCARM__) +#pragma location = ".ethusbram" +#endif +ETHMEM_SECTION static EventQueue worker_loop_event_queue(20 * EVENTS_EVENT_SIZE); + static void worker_loop_event_cb(int event); static Event worker_loop_event(&worker_loop_event_queue, worker_loop_event_cb); static void link_input_event_cb(void *buf); @@ -460,7 +491,11 @@ static void link_input_event_cb(void *buf) } } -static unsigned char thread_stack[2048]; + +#if defined (__ICCARM__) +#pragma location = ".ethusbram" +#endif +ETHMEM_SECTION static unsigned char thread_stack[2048]; void worker_loop(void); diff --git a/TESTS/network/emac/emac_util.h b/TESTS/network/emac/emac_util.h index 62b72fa022..08570341a3 100644 --- a/TESTS/network/emac/emac_util.h +++ b/TESTS/network/emac/emac_util.h @@ -58,7 +58,7 @@ extern const unsigned char eth_mac_broadcast_addr[]; #define RESET_ERROR_FLAGS(flags) emac_if_reset_error_flags(flags) #define ETH_FRAME_HEADER_LEN 28 -#define ETH_FRAME_MIN_LEN 60 +#define ETH_FRAME_MIN_LEN 60 + 4 #define ETH_MAC_ADDR_LEN 6 #define TIMEOUT 1 From 35f064fc696e6da100a7f46edacf6280831eae06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=20Lepp=C3=A4nen?= Date: Fri, 1 Jun 2018 12:37:45 +0300 Subject: [PATCH 2/2] Changed TCP socket test to use shared buffers Changed RX and TX buffers used in TCP socket tests to global variables to conserve memory. --- TESTS/netsocket/tcp/main.cpp | 3 +++ TESTS/netsocket/tcp/tcp_tests.h | 11 +++++++++ TESTS/netsocket/tcp/tcpsocket_echotest.cpp | 23 ++++++++----------- .../tcp/tcpsocket_echotest_burst.cpp | 18 +++++++-------- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/TESTS/netsocket/tcp/main.cpp b/TESTS/netsocket/tcp/main.cpp index 2eaa4d0cd7..45157b87d9 100644 --- a/TESTS/netsocket/tcp/main.cpp +++ b/TESTS/netsocket/tcp/main.cpp @@ -34,6 +34,9 @@ namespace NetworkInterface* net; } +char tcp_global::rx_buffer[RX_BUFF_SIZE]; +char tcp_global::tx_buffer[TX_BUFF_SIZE]; + NetworkInterface* get_interface() { return net; diff --git a/TESTS/netsocket/tcp/tcp_tests.h b/TESTS/netsocket/tcp/tcp_tests.h index f392ef7446..50e815d70b 100644 --- a/TESTS/netsocket/tcp/tcp_tests.h +++ b/TESTS/netsocket/tcp/tcp_tests.h @@ -24,6 +24,17 @@ void fill_tx_buffer_ascii(char *buff, size_t len); void tcpsocket_connect_to_echo_srv(TCPSocket& sock); void tcpsocket_connect_to_discard_srv(TCPSocket& sock); +namespace tcp_global +{ +static const int TCP_OS_STACK_SIZE = 1024; + +static const int RX_BUFF_SIZE = 1220; +static const int TX_BUFF_SIZE = 1220; + +extern char rx_buffer[RX_BUFF_SIZE]; +extern char tx_buffer[TX_BUFF_SIZE]; +} + /* * Test cases */ diff --git a/TESTS/netsocket/tcp/tcpsocket_echotest.cpp b/TESTS/netsocket/tcp/tcpsocket_echotest.cpp index bcd83035f7..013fd7172b 100644 --- a/TESTS/netsocket/tcp/tcpsocket_echotest.cpp +++ b/TESTS/netsocket/tcp/tcpsocket_echotest.cpp @@ -31,9 +31,6 @@ namespace static const int SIGIO_TIMEOUT = 5000; //[ms] static const int BUFF_SIZE = 1200; - char rx_buffer[BUFF_SIZE] = {0}; - char tx_buffer[BUFF_SIZE] = {0}; - static const int PKTS = 22; static const int pkt_sizes[PKTS] = {1,2,3,4,5,6,7,8,9,10, \ 100,200,300,400,500,600,700,800,900,1000,\ @@ -54,9 +51,9 @@ void TCPSOCKET_ECHOTEST() int sent; int x = 0; for (int pkt_s = pkt_sizes[x]; x < PKTS; pkt_s = pkt_sizes[x++]) { - fill_tx_buffer_ascii(tx_buffer, BUFF_SIZE); + fill_tx_buffer_ascii(tcp_global::tx_buffer, BUFF_SIZE); - sent = sock.send(tx_buffer, pkt_s); + sent = sock.send(tcp_global::tx_buffer, pkt_s); if (sent < 0) { printf("[Round#%02d] network error %d\n", x, sent); TEST_FAIL(); @@ -64,14 +61,14 @@ void TCPSOCKET_ECHOTEST() int bytes2recv = sent; while (bytes2recv) { - recvd = sock.recv(&(rx_buffer[sent-bytes2recv]), bytes2recv); + recvd = sock.recv(&(tcp_global::rx_buffer[sent-bytes2recv]), bytes2recv); if (recvd < 0) { printf("[Round#%02d] network error %d\n", x, recvd); TEST_FAIL(); } bytes2recv -= recvd; } - TEST_ASSERT_EQUAL(0, memcmp(tx_buffer, rx_buffer, sent)); + TEST_ASSERT_EQUAL(0, memcmp(tcp_global::tx_buffer, tcp_global::rx_buffer, sent)); } TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close()); } @@ -81,7 +78,7 @@ void tcpsocket_echotest_nonblock_receiver(void *receive_bytes) int bytes2recv = *(int*)receive_bytes; int recvd; while (bytes2recv) { - recvd = sock.recv(&(rx_buffer[*(int*)receive_bytes-bytes2recv]), bytes2recv); + recvd = sock.recv(&(tcp_global::rx_buffer[*(int*)receive_bytes-bytes2recv]), bytes2recv); if (recvd == NSAPI_ERROR_WOULD_BLOCK) { wait(1); continue; @@ -91,7 +88,7 @@ void tcpsocket_echotest_nonblock_receiver(void *receive_bytes) bytes2recv -= recvd; } - TEST_ASSERT_EQUAL(0, memcmp(tx_buffer, rx_buffer, *(int*)receive_bytes)); + TEST_ASSERT_EQUAL(0, memcmp(tcp_global::tx_buffer, tcp_global::rx_buffer, *(int*)receive_bytes)); static int round = 0; printf("[Recevr#%02d] bytes received: %d\n", round++, *(int*)receive_bytes); @@ -110,22 +107,22 @@ void TCPSOCKET_ECHOTEST_NONBLOCK() int sent; int s_idx = 0; Thread *thread; - unsigned char *stack_mem = (unsigned char *)malloc(OS_STACK_SIZE); + unsigned char *stack_mem = (unsigned char *)malloc(tcp_global::TCP_OS_STACK_SIZE); TEST_ASSERT_NOT_NULL(stack_mem); for (int pkt_s = pkt_sizes[s_idx]; s_idx < PKTS; ++s_idx) { pkt_s = pkt_sizes[s_idx]; thread = new Thread(osPriorityNormal, - OS_STACK_SIZE, + tcp_global::TCP_OS_STACK_SIZE, stack_mem, "receiver"); TEST_ASSERT_EQUAL(osOK, thread->start(callback(tcpsocket_echotest_nonblock_receiver, &pkt_s))); - fill_tx_buffer_ascii(tx_buffer, pkt_s); + fill_tx_buffer_ascii(tcp_global::tx_buffer, pkt_s); bytes2send = pkt_s; while (bytes2send > 0) { - sent = sock.send(&(tx_buffer[pkt_s-bytes2send]), bytes2send); + sent = sock.send(&(tcp_global::tx_buffer[pkt_s-bytes2send]), bytes2send); if (sent == NSAPI_ERROR_WOULD_BLOCK) { TEST_ASSERT_NOT_EQUAL(osEventTimeout, osSignalWait(SIGNAL_SIGIO, SIGIO_TIMEOUT).status); continue; diff --git a/TESTS/netsocket/tcp/tcpsocket_echotest_burst.cpp b/TESTS/netsocket/tcp/tcpsocket_echotest_burst.cpp index adc3cdd0e8..12b8fdf403 100644 --- a/TESTS/netsocket/tcp/tcpsocket_echotest_burst.cpp +++ b/TESTS/netsocket/tcp/tcpsocket_echotest_burst.cpp @@ -32,8 +32,6 @@ namespace static const int BURST_CNT = 100; static const int BURST_SIZE = 1220; - char rx_buffer[BURST_SIZE] = {0}; - char tx_buffer[BURST_SIZE] = {0}; } static void _sigio_handler(osThreadId id) { @@ -47,7 +45,7 @@ void TCPSOCKET_ECHOTEST_BURST() sock.sigio(callback(_sigio_handler, Thread::gettid())); // TX buffer to be preserved for comparison - fill_tx_buffer_ascii(tx_buffer, BURST_SIZE); + fill_tx_buffer_ascii(tcp_global::tx_buffer, BURST_SIZE); int recvd; int bt_left; @@ -55,7 +53,7 @@ void TCPSOCKET_ECHOTEST_BURST() for (int i = 0; i < BURST_CNT; i++) { bt_left = BURST_SIZE; while (bt_left > 0) { - sent = sock.send(&(tx_buffer[BURST_SIZE-bt_left]), bt_left); + sent = sock.send(&(tcp_global::tx_buffer[BURST_SIZE-bt_left]), bt_left); if (sent == NSAPI_ERROR_WOULD_BLOCK) { if(osSignalWait(SIGNAL_SIGIO, SIGIO_TIMEOUT).status == osEventTimeout) { TEST_FAIL(); @@ -70,7 +68,7 @@ void TCPSOCKET_ECHOTEST_BURST() bt_left = BURST_SIZE; while (bt_left > 0) { - recvd = sock.recv(&(rx_buffer[BURST_SIZE-bt_left]), BURST_SIZE); + recvd = sock.recv(&(tcp_global::rx_buffer[BURST_SIZE-bt_left]), BURST_SIZE); if (recvd < 0) { printf("[%02d] network error %d\n", i, recvd); break; @@ -83,7 +81,7 @@ void TCPSOCKET_ECHOTEST_BURST() TEST_FAIL(); } - TEST_ASSERT_EQUAL(0, memcmp(tx_buffer, rx_buffer, BURST_SIZE)); + TEST_ASSERT_EQUAL(0, memcmp(tcp_global::tx_buffer, tcp_global::rx_buffer, BURST_SIZE)); } TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close()); } @@ -96,7 +94,7 @@ void TCPSOCKET_ECHOTEST_BURST_NONBLOCK() sock.sigio(callback(_sigio_handler, Thread::gettid())); // TX buffer to be preserved for comparison - fill_tx_buffer_ascii(tx_buffer, BURST_SIZE); + fill_tx_buffer_ascii(tcp_global::tx_buffer, BURST_SIZE); int sent; int recvd; @@ -104,7 +102,7 @@ void TCPSOCKET_ECHOTEST_BURST_NONBLOCK() for (int i = 0; i < BURST_CNT; i++) { bt_left = BURST_SIZE; while (bt_left > 0) { - sent = sock.send(&(tx_buffer[BURST_SIZE-bt_left]), bt_left); + sent = sock.send(&(tcp_global::tx_buffer[BURST_SIZE-bt_left]), bt_left); if (sent == NSAPI_ERROR_WOULD_BLOCK) { if(osSignalWait(SIGNAL_SIGIO, SIGIO_TIMEOUT).status == osEventTimeout) { TEST_FAIL(); @@ -120,7 +118,7 @@ void TCPSOCKET_ECHOTEST_BURST_NONBLOCK() bt_left = BURST_SIZE; while (bt_left > 0) { - recvd = sock.recv(&(rx_buffer[BURST_SIZE-bt_left]), BURST_SIZE); + recvd = sock.recv(&(tcp_global::rx_buffer[BURST_SIZE-bt_left]), BURST_SIZE); if (recvd == NSAPI_ERROR_WOULD_BLOCK) { if(osSignalWait(SIGNAL_SIGIO, SIGIO_TIMEOUT).status == osEventTimeout) { printf("[bt#%02d] packet timeout...", i); @@ -140,7 +138,7 @@ void TCPSOCKET_ECHOTEST_BURST_NONBLOCK() TEST_FAIL(); } - TEST_ASSERT_EQUAL(0, memcmp(tx_buffer, rx_buffer, BURST_SIZE)); + TEST_ASSERT_EQUAL(0, memcmp(tcp_global::tx_buffer, tcp_global::rx_buffer, BURST_SIZE)); } TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close()); }