Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

w5500 no mem for receive buffer (IDFGH-7516) #9083

Closed
MadDogMayCry0 opened this issue Jun 1, 2022 · 17 comments
Closed

w5500 no mem for receive buffer (IDFGH-7516) #9083

MadDogMayCry0 opened this issue Jun 1, 2022 · 17 comments
Assignees
Labels
Resolution: Duplicate This issue or pull request already exists Status: Done Issue is done internally

Comments

@MadDogMayCry0
Copy link

MadDogMayCry0 commented Jun 1, 2022

vscode @ last esp-idf
w5500 module
WROOM32
AsyncTCP Server example
Putty as a tcp client

When i send 4027 bytes over tcp/ip i have "w5500.mac: no mem for receive buffer"
the problem is here.

static void emac_w5500_task(void *arg)
{
    emac_w5500_t *emac = (emac_w5500_t *)arg;
    uint8_t status = 0;
    uint8_t *buffer = NULL;
    uint32_t length = 0;
    while (1) {
        // check if the task receives any notification
        if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 &&    // if no notification ...
            gpio_get_level(emac->int_gpio_num) != 0) {               // ...and no interrupt asserted
            continue;                                                // -> just continue to check again
        }

        /* read interrupt status */
        w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
        /* packet received */
        if (status & W5500_SIR_RECV) {
            status = W5500_SIR_RECV;
            // clear interrupt status
            w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
            do {
                length = ETH_MAX_PACKET_SIZE;
                buffer = heap_caps_malloc(length, MALLOC_CAP_DMA);
                if (!buffer) {
                    ESP_LOGE(TAG, "no mem for receive buffer");
                    break;
                } else if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) {
                    /* pass the buffer to stack (e.g. TCP/IP layer) */
                    if (length) {
                        emac->eth->stack_input(emac->eth, buffer, length);
                    } else {
                        free(buffer);
                    }
                } else {
                    free(buffer);
                }
            } while (emac->packets_remain);
        }
    }
    vTaskDelete(NULL);
}
@espressif-bot espressif-bot added the Status: Opened Issue is new label Jun 1, 2022
@github-actions github-actions bot changed the title w5500 issue out of memory w5500 issue out of memory (IDFGH-7516) Jun 1, 2022
@MadDogMayCry0 MadDogMayCry0 changed the title w5500 issue out of memory (IDFGH-7516) w5500 no mem for receive buffer (IDFGH-7516) Jun 2, 2022
@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 2, 2022

UPD.
Any data block that includes a line break will allocate at least the amount of memory specified in the ETH_MAX_PACKET_SIZE directive.

...

What does it mean.
You have ETH_MAX_PACKET_SIZE = 4096
You send two line breaks (4 bytes) and this piece of code will allocate 4096 bytes of memory for each line break. That is, for 4 bytes of data, you will get 8192 bytes of allocated memory.

This is an absurd piece of code.
I see only one varioan, is to allocate memory based on RX_DATA_SIZE of w5500. But how to become this size before allocate buffer and write data to it?

UPD
So this is a putty problem.
He send packets separatly when it contain "\r\n" symbol.
I only habe to prevent this code from spoofing.

UPD
And one more. After "no mem for receive buffer" memory will newer free again.. So, need to do something to free allocated memory.

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 2, 2022

Ok, a crunch to prevent memory leaks when packets too intencive or data is too big.

static void emac_w5500_task(void *arg)
{
    emac_w5500_t *emac = (emac_w5500_t *)arg;
    uint8_t status = 0;
    uint8_t *buffer = NULL;
    uint32_t length = 0;
    while (1) {
        // check if the task receives any notification
        if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 &&    // if no notification ...
            gpio_get_level(emac->int_gpio_num) != 0) {               // ...and no interrupt asserted
            continue;                                                // -> just continue to check again
        }

        /* read interrupt status */
        w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
        /* packet received */
        if (status & W5500_SIR_RECV) {
            status = W5500_SIR_RECV;
            // clear interrupt status
            w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
            do {
                ESP_LOGE("toys.c","%d",heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
                length = ETH_MAX_PACKET_SIZE;
                
                //Prevent memory leak
                if(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)<120000){
                    buffer = heap_caps_malloc(length, MALLOC_CAP_DMA);
                    while (emac->packets_remain){
                        //flush buffer
                        if(emac->parent.receive(&emac->parent, buffer, &length)){
                            buffer = NULL;
                        }
                    }
                    free(buffer);
                }
                
                buffer = heap_caps_malloc(length, MALLOC_CAP_DMA);
                if(!buffer){
                    //Memory leak
                    w5500_reset(emac);
                    ESP_LOGE(TAG, "no mem for receive buffer");
                    break;
                } else if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) {
                    /* pass the buffer to stack (e.g. TCP/IP layer) */
                    if (length) {
                        emac->eth->stack_input(emac->eth, buffer, length);
                    } else {
                        free(buffer);
                    }
                } else {
                    free(buffer);
                }
            } 
            while (emac->packets_remain);
        }
    }
    vTaskDelete(NULL);
}

If you have a better way pls remind me!
To test LEAK, just send two hundert "\r\n" over TCP/IP in PUTTY.

E (381734) toys.c: 295276
E (381734) toys.c: 293712
E (381744) toys.c: 292148
E (381744) toys.c: 290584
E (381744) toys.c: 289020
E (381754) toys.c: 287456
E (381754) toys.c: 285892
E (381754) toys.c: 284328
E (381764) toys.c: 284312
E (381764) toys.c: 282748
E (381764) toys.c: 281184
E (381774) toys.c: 279620
E (381774) toys.c: 278056
E (381774) toys.c: 276492
E (381784) toys.c: 274928
E (381784) toys.c: 273352
E (381784) toys.c: 271788
E (381794) toys.c: 270224
E (381794) toys.c: 268660
E (381794) toys.c: 267096
E (381804) toys.c: 265532
E (381804) toys.c: 263968
E (381804) toys.c: 262404
E (381814) toys.c: 260840
E (381814) toys.c: 259276
E (381814) toys.c: 257712
E (381824) toys.c: 256148
E (381824) toys.c: 254584
E (381824) toys.c: 253020
E (381834) toys.c: 251456
E (381834) toys.c: 249892
E (381834) toys.c: 248328
E (381844) toys.c: 246764
E (381844) toys.c: 245200
E (381844) toys.c: 243636
E (381854) toys.c: 242072
E (381854) toys.c: 240508
E (381854) toys.c: 238944
E (381864) toys.c: 237380
E (381864) toys.c: 235816
E (381864) toys.c: 234252
E (381874) toys.c: 232688
E (381874) toys.c: 231124
E (381874) toys.c: 229560
E (381884) toys.c: 227996
E (381884) toys.c: 226432
E (381884) toys.c: 224868
E (381894) toys.c: 223292
E (381894) toys.c: 221728
E (381894) toys.c: 220164
E (381904) toys.c: 218600
E (381904) toys.c: 217036
E (381904) toys.c: 215472
E (381914) toys.c: 213908
E (381914) toys.c: 212344
E (381914) toys.c: 210780
E (381924) toys.c: 209216
E (381924) toys.c: 207652
E (381924) toys.c: 206088
E (381934) toys.c: 204524
E (381934) toys.c: 202960
E (381934) toys.c: 201396
E (381944) toys.c: 199832
E (381944) toys.c: 198268
E (381944) toys.c: 196704
E (381954) toys.c: 195140
E (381954) toys.c: 193576
E (381954) toys.c: 192012
E (381954) toys.c: 190448
E (381964) toys.c: 188884
E (381964) toys.c: 187320
E (381964) toys.c: 185756
E (381974) toys.c: 184192
E (381974) toys.c: 182628
E (381974) toys.c: 181064
E (381984) toys.c: 179500
E (381984) toys.c: 177936
E (381984) toys.c: 176372
E (381994) toys.c: 174808
E (381994) toys.c: 173244
E (381994) toys.c: 171680
E (382004) toys.c: 170112
E (382004) toys.c: 168548
E (382004) toys.c: 166984
E (382014) toys.c: 165420
E (382014) toys.c: 163856
E (382014) toys.c: 162292
E (382024) toys.c: 160728
E (382024) toys.c: 159164
E (382024) toys.c: 157600
E (382034) toys.c: 156036
E (382034) toys.c: 154472
E (382034) toys.c: 152908
E (382044) toys.c: 151344
E (382044) toys.c: 149780
E (382044) toys.c: 148216
E (382054) toys.c: 146652
E (382054) toys.c: 145088
E (382054) toys.c: 143524
E (382064) toys.c: 141960
E (382064) toys.c: 140396
E (382064) toys.c: 138832
E (382074) toys.c: 137268
E (382074) toys.c: 135704
E (382074) toys.c: 134140
E (382084) toys.c: 132576
E (382084) toys.c: 131012
E (382084) toys.c: 129448
E (382094) toys.c: 127884
E (382094) toys.c: 126320
E (382094) toys.c: 124756
E (382104) toys.c: 123192
E (382104) toys.c: 121628
E (382104) toys.c: 120064
E (382114) toys.c: 118500
E (382164) toys.c: 118500
E (382164) toys.c: 118500
--->>> FLUSH BUFFER
E (381734) toys.c: 295276

@KaeLL
Copy link
Contributor

KaeLL commented Jun 2, 2022

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 2, 2022

@suda-morris any particular reason to allocate max ethernet frame sized buffers? Querying the packet's size to allocate the buffer accordingly seems to work fine.

I think becuse of this peace (wtf???)

static void emac_w5500_task(void *arg)
{
    emac_w5500_t *emac = (emac_w5500_t *)arg;
    uint8_t status = 0;
    uint8_t *buffer = NULL;
    uint32_t length = 0;
    
    //rxBuffer length
    uint16_t rxLen = 0;

    while (1) {
        // check if the task receives any notification
        if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 &&    // if no notification ...
            gpio_get_level(emac->int_gpio_num) != 0) {               // ...and no interrupt asserted
            continue;                                                // -> just continue to check again
        }

        /* read interrupt status */
        w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
        /* packet received */
        if (status & W5500_SIR_RECV) {
            status = W5500_SIR_RECV;

            //get rx length
            w5500_get_rx_received_size(emac, &rxLen);
            printf("rxLen:%d\r\n",rxLen);
            int iter = 0;
            // clear interrupt status
            w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
            do {
                
                //Wtf???????????
                printf("iter:%d rxLen:%d\r\n",iter,rxLen);
                iter++;
                
                // ESP_LOGE("toys.c","%d",heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
                length = ETH_MAX_PACKET_SIZE;
                
                //Prevent memory leak
                // if(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)<120000){
                //     buffer = heap_caps_malloc(length, MALLOC_CAP_DMA);
                //     while (emac->packets_remain){
                //         //flush buffer
                //         if(emac->parent.receive(&emac->parent, buffer, &length)){
                //             buffer = NULL;
                //         }
                //     }
                //     free(buffer);
                // }
                
                buffer = heap_caps_malloc(length, MALLOC_CAP_DMA);
                if(!buffer){
                    //Memory leak - reboot module
                    w5500_reset(emac);
                    ESP_LOGE(TAG, "no mem for receive buffer");
                    break;
                } else if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) {
                    /* pass the buffer to stack (e.g. TCP/IP layer) */
                    if (length) {
                        emac->eth->stack_input(emac->eth, buffer, length);
                    } else {
                        free(buffer);
                    }
                } else {
                    free(buffer);
                }
            } 
            while (emac->packets_remain);
        }
    }
    vTaskDelete(NULL);
}

50 peaces of "\r\n" with 98 bytes of length was sended

iter:0 rxLen:156
iter:1 rxLen:156
iter:2 rxLen:156
iter:3 rxLen:156
iter:4 rxLen:156
iter:5 rxLen:156
iter:6 rxLen:156
iter:7 rxLen:156
iter:8 rxLen:156
iter:9 rxLen:156
iter:10 rxLen:156
iter:11 rxLen:156
iter:12 rxLen:156
iter:13 rxLen:156
iter:14 rxLen:156
iter:15 rxLen:156
iter:16 rxLen:156
iter:17 rxLen:156
iter:18 rxLen:156
iter:19 rxLen:156
iter:20 rxLen:156
iter:21 rxLen:156
iter:22 rxLen:156
iter:23 rxLen:156
iter:24 rxLen:156
iter:25 rxLen:156
iter:26 rxLen:156
iter:27 rxLen:156
iter:28 rxLen:156
iter:29 rxLen:156
iter:30 rxLen:156
iter:31 rxLen:156
iter:32 rxLen:156
iter:33 rxLen:156
iter:34 rxLen:156
iter:35 rxLen:156
iter:36 rxLen:156
iter:37 rxLen:156
iter:38 rxLen:156
iter:39 rxLen:156
iter:40 rxLen:156
iter:41 rxLen:156
iter:42 rxLen:156
iter:43 rxLen:156
iter:44 rxLen:156
iter:45 rxLen:156
iter:46 rxLen:156
iter:47 rxLen:156
iter:48 rxLen:156
iter:49 rxLen:156
iter:50 rxLen:156
iter:51 rxLen:156
iter:52 rxLen:156
iter:53 rxLen:156
iter:54 rxLen:156
iter:55 rxLen:156
iter:56 rxLen:156
iter:57 rxLen:156
iter:58 rxLen:156
iter:59 rxLen:156
iter:60 rxLen:156
iter:61 rxLen:156
iter:62 rxLen:156
iter:63 rxLen:156
iter:64 rxLen:156
iter:65 rxLen:156
iter:66 rxLen:156
iter:67 rxLen:156
iter:68 rxLen:156
iter:69 rxLen:156
iter:70 rxLen:156
iter:71 rxLen:156
iter:72 rxLen:156
iter:73 rxLen:156
iter:74 rxLen:156
iter:75 rxLen:156
iter:76 rxLen:156
iter:77 rxLen:156
iter:78 rxLen:156
iter:79 rxLen:156
iter:80 rxLen:156
iter:81 rxLen:156
iter:82 rxLen:156
iter:83 rxLen:156
iter:84 rxLen:156
iter:85 rxLen:156
iter:86 rxLen:156
iter:87 rxLen:156
iter:88 rxLen:156
iter:89 rxLen:156
iter:90 rxLen:156
iter:91 rxLen:156
iter:92 rxLen:156
iter:93 rxLen:156
iter:94 rxLen:156
iter:95 rxLen:156
iter:96 rxLen:156
iter:97 rxLen:156
iter:98 rxLen:156
iter:99 rxLen:156
iter:100 rxLen:156
iter:101 rxLen:156
iter:102 rxLen:156
iter:103 rxLen:156
iter:104 rxLen:156
iter:105 rxLen:156
iter:106 rxLen:156
iter:107 rxLen:156
iter:108 rxLen:156
iter:109 rxLen:156
iter:110 rxLen:156
iter:111 rxLen:156
iter:112 rxLen:156
iter:113 rxLen:156
iter:114 rxLen:156
iter:115 rxLen:156
iter:116 rxLen:156
iter:117 rxLen:156
iter:118 rxLen:156
iter:119 rxLen:156
iter:120 rxLen:156
iter:121 rxLen:156
iter:122 rxLen:156
iter:123 rxLen:156
iter:124 rxLen:156
iter:125 rxLen:156
iter:126 rxLen:156
iter:127 rxLen:156
iter:128 rxLen:156
iter:129 rxLen:156
iter:130 rxLen:156
iter:131 rxLen:156
iter:132 rxLen:156
iter:133 rxLen:156
iter:134 rxLen:156
iter:135 rxLen:156
iter:136 rxLen:156
iter:137 rxLen:156
iter:138 rxLen:156
iter:139 rxLen:156

I think all of this code have to be redisigned.

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 2, 2022

And this variant show something strange

static void emac_w5500_task(void *arg)
{
    emac_w5500_t *emac = (emac_w5500_t *)arg;
    uint8_t status = 0;
    uint8_t *buffer = NULL;
    uint32_t length = 0;
    
    //rxBuffer length
    uint16_t rxLen = 0;

    while (1) {
        // check if the task receives any notification
        if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 &&    // if no notification ...
            gpio_get_level(emac->int_gpio_num) != 0) {               // ...and no interrupt asserted
            continue;                                                // -> just continue to check again
        }

        /* read interrupt status */
        w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
        /* packet received */
        if (status & W5500_SIR_RECV) {
            status = W5500_SIR_RECV;

            //get rx length
            int iter = 0;
            // clear interrupt status
            w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
            do {
                w5500_get_rx_received_size(emac, &rxLen);
                //Wtf???????????
                printf("iter:%d rxLen:%d\r\n",iter,rxLen);
                iter++;
                
                // ESP_LOGE("toys.c","%d",heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
                length = ETH_MAX_PACKET_SIZE;
                
                //Prevent memory leak
                // if(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)<120000){
                //     buffer = heap_caps_malloc(length, MALLOC_CAP_DMA);
                //     while (emac->packets_remain){
                //         //flush buffer
                //         if(emac->parent.receive(&emac->parent, buffer, &length)){
                //             buffer = NULL;
                //         }
                //     }
                //     free(buffer);
                // }
                
                buffer = heap_caps_malloc(length, MALLOC_CAP_DMA);
                if(!buffer){
                    //Memory leak - reboot module
                    w5500_reset(emac);
                    ESP_LOGE(TAG, "no mem for receive buffer");
                    break;
                } else if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) {
                    /* pass the buffer to stack (e.g. TCP/IP layer) */
                    if (length) {
                        emac->eth->stack_input(emac->eth, buffer, length);
                    } else {
                        free(buffer);
                    }
                } else {
                    free(buffer);
                }
            } 
            while (emac->packets_remain);
        }
    }
    vTaskDelete(NULL);
}

50 peaces of "\r\n" with 98 bytes of length was sended

iter:0 rxLen:868
iter:1 rxLen:806
iter:2 rxLen:2170
iter:3 rxLen:2852
iter:4 rxLen:2790
iter:5 rxLen:2728
iter:6 rxLen:2666
iter:7 rxLen:2604
iter:8 rxLen:2542
iter:9 rxLen:2480
iter:10 rxLen:2418
iter:11 rxLen:2356
iter:12 rxLen:2294
iter:13 rxLen:2232
iter:14 rxLen:2170
iter:15 rxLen:2108
iter:16 rxLen:2046
iter:17 rxLen:1984
iter:18 rxLen:1922
iter:19 rxLen:1860
iter:20 rxLen:1798
iter:21 rxLen:1736
iter:22 rxLen:1674
iter:23 rxLen:1612
iter:24 rxLen:1550
iter:25 rxLen:1488
iter:26 rxLen:1426
iter:27 rxLen:1364
iter:28 rxLen:1302
iter:29 rxLen:1240
iter:30 rxLen:1178
iter:31 rxLen:1116
iter:32 rxLen:1054
iter:33 rxLen:992
iter:34 rxLen:930
iter:35 rxLen:868
iter:36 rxLen:806
iter:37 rxLen:744
iter:38 rxLen:682
iter:39 rxLen:620
iter:40 rxLen:558
iter:41 rxLen:496
iter:42 rxLen:434
iter:43 rxLen:372
iter:44 rxLen:310
iter:45 rxLen:248
iter:46 rxLen:186
iter:47 rxLen:124
iter:48 rxLen:62

@MadDogMayCry0
Copy link
Author

UPD

printf("iter:%d rxLen:%d\r\n",iter,__builtin_bswap16( rxLen ) - 2);

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 2, 2022

So, this works as i see. But bug is not desapears. Have to flush it when leak. This variant can recive more data btw spending much less memory and effort... I think I've proven the block of code wrong. It requires a complete redesign and rethinking.
To prevent leak i added part described above, but i think we can get more effecient way.

static void emac_w5500_task(void *arg){
    emac_w5500_t *emac = (emac_w5500_t *)arg;
    uint8_t status = 0;
    uint8_t *buffer = NULL;
    uint32_t length = 0;    
    //rxBuffer length
    uint16_t rxLen = 0;
    // int iter = 0;
    while (1) {
        // check if the task receives any notification
        if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 &&    // if no notification ...
            gpio_get_level(emac->int_gpio_num) != 0) {               // ...and no interrupt asserted
            continue;                                                // -> just continue to check again
        }
        /* read interrupt status */
        w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
        /* packet received */
        if (status & W5500_SIR_RECV) {
            status = W5500_SIR_RECV;

            // clear interrupt status
            w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
            //get rx length
            w5500_get_rx_received_size(emac, &rxLen);
            if(rxLen<1){
                continue;
            }
            
            buffer = heap_caps_malloc(rxLen, MALLOC_CAP_DMA);
            // printf("rxLen:%d heap:%d\r\n",rxLen,heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
            
            //flush buffer
            if(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)<120000){
                while (emac->packets_remain){
                    emac->parent.receive(&emac->parent, buffer, &length);
                }
            }

            if(!buffer){
                ESP_LOGE(TAG, "no mem for receive buffer");
                w5500_reset(emac);
                continue;
            }
            if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) {
                /* pass the buffer to stack (e.g. TCP/IP layer) */
                emac->eth->stack_input(emac->eth, buffer, length);
            }
        }
    }
    vTaskDelete(NULL);
}

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 2, 2022

The problem as I see it is some strange behavior of the buffer. Either the buffer is incorrectly calculated, or the buffer itself is somehow difficult to distribute frames within itself.
If we send 50 peaces of "\r\n" separatly step by step intencively, we become

rxLen:124 heap:304032
rxLen:1736 heap:302268
rxLen:3162 heap:299080
rxLen:4588 heap:294468
rxLen:5704 heap:288740
rxLen:5642 heap:283072
rxLen:5580 heap:277468
rxLen:5518 heap:271924
rxLen:5456 heap:271988
rxLen:5394 heap:272048
rxLen:5332 heap:272112
rxLen:5270 heap:272172
rxLen:5208 heap:272236
rxLen:5146 heap:272296
rxLen:5084 heap:272360
rxLen:5022 heap:272420
rxLen:4960 heap:272484
rxLen:4898 heap:272544
rxLen:4836 heap:272608
rxLen:4774 heap:293776
rxLen:4712 heap:289024
rxLen:4650 heap:284332
rxLen:4588 heap:279700
rxLen:4526 heap:275132
rxLen:4698 heap:270392
rxLen:4636 heap:265708
rxLen:4574 heap:261092
rxLen:4512 heap:256540
rxLen:4450 heap:252048
rxLen:4388 heap:247620
rxLen:4326 heap:243252
rxLen:4264 heap:238948
rxLen:4202 heap:234704
rxLen:4140 heap:230524
rxLen:4078 heap:226404
rxLen:4016 heap:222348
rxLen:3954 heap:218352
rxLen:3892 heap:214420
rxLen:3830 heap:210548
rxLen:3768 heap:206740
rxLen:3706 heap:202992
rxLen:3644 heap:199308
rxLen:3582 heap:195684
rxLen:3520 heap:192124
rxLen:3458 heap:194228
rxLen:3396 heap:190792
rxLen:3334 heap:187416
rxLen:3272 heap:184104
rxLen:3210 heap:180848
rxLen:3148 heap:177660
rxLen:3086 heap:174520
rxLen:3024 heap:171456
rxLen:2962 heap:168452
rxLen:2900 heap:165512
rxLen:2838 heap:162632
rxLen:2776 heap:159816
rxLen:2714 heap:157060
rxLen:2652 heap:154368
rxLen:2590 heap:151736
rxLen:2528 heap:149168
rxLen:2466 heap:146660
rxLen:2404 heap:144216
rxLen:2342 heap:141832
rxLen:2280 heap:139512
rxLen:2218 heap:137252
rxLen:2156 heap:135056
rxLen:2094 heap:132920
rxLen:2032 heap:130848
rxLen:1970 heap:128836
rxLen:1908 heap:126888
rxLen:1846 heap:125000
rxLen:1784 heap:123176
rxLen:1722 heap:121412
rxLen:1660 heap:119712
rxLen:1598 heap:118072
rxLen:1536 heap:116496
rxLen:1474 heap:114980
rxLen:1412 heap:113528
rxLen:1350 heap:112136
rxLen:1288 heap:110808
rxLen:1226 heap:109540
rxLen:1164 heap:108336
rxLen:1102 heap:107192
rxLen:1040 heap:106112
rxLen:978 heap:105092
rxLen:916 heap:104136
rxLen:854 heap:103240
rxLen:792 heap:102408
rxLen:730 heap:101636
rxLen:668 heap:100928
rxLen:606 heap:100280
rxLen:544 heap:99696
rxLen:234 heap:100004

But it's not possible to have 5642 bytes for 5st iteration and for only 10 bytes data that was sended before.

And as expected, if we manually allocate something like 256 bytes per line break, we see that all 50 iterations will work very well. It turns out that the value of the incoming buffer is incorrectly calculated.

Or it is calculated correctly, but something does not have time to reset the previous values ​​before a new calculation, and as a result, under certain conditions, the result is incorrect.

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 3, 2022

Ok, size function must be something like so

uint16_t offset = 0;
uint16_t len = 0;
w5500_read(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset));
offset = __builtin_bswap16(offset);
w5500_read_buffer(emac, &len, sizeof(len), offset);
rxLen = __builtin_bswap16(len) - 2;

but i think better way is to have static sized uint8_t arr[16384] and after we recive buffer and know about length in this arr, we can then allocate memory to put this data in stack.
I didn't test it, but i think we need more time for SPI requests to get data header, instead of just keeping extra 16kb that will work as an incoming buffer and we will in any time know about recived data size.

static void emac_w5500_task(void *arg){
    emac_w5500_t *emac = (emac_w5500_t *)arg;
    uint8_t status = 0;
    uint8_t *buffer = NULL;
    uint32_t length = 0;    
    //rxBuffer length
    uint16_t rxLen = 0;
    while (1) {
        // check if the task receives any notification
        if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 &&    // if no notification ...
            gpio_get_level(emac->int_gpio_num) != 0) {               // ...and no interrupt asserted
            continue;                                                // -> just continue to check again
        }
        /* read interrupt status */
        w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
        /* packet received */
        if (status & W5500_SIR_RECV) {
            status = W5500_SIR_RECV;

            // clear interrupt status
            w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
            
            //get rx length
            uint16_t offset = 0;
            uint16_t len = 0;
            w5500_read(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset));
            offset = __builtin_bswap16(offset);
            w5500_read_buffer(emac, &len, sizeof(len), offset);
            rxLen = __builtin_bswap16(len) - 2;
            if(rxLen < 1){
                continue;
            }
            
            //mem allocation
            buffer = heap_caps_malloc(rxLen, MALLOC_CAP_DMA);
            printf("rxLen:%d heap:%d\r\n",rxLen,heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
            
           if(!buffer){
                ESP_LOGE(TAG, "no mem for receive buffer");
                w5500_reset(emac);
                continue;
           }
           if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) {
                /* pass the buffer to stack (e.g. TCP/IP layer) */
                emac->eth->stack_input(emac->eth, buffer, length);
               continue;
           }
           free(buffer);
        }
    }
    vTaskDelete(NULL);
}

UPD
ok about SPI request. it's something like 85uS to get buffer size. So, i think this method is much better as described before.

and about this function.

static esp_err_t w5500_get_rx_received_size(emac_w5500_t *emac, uint16_t *size)
{
    esp_err_t ret = ESP_OK;
    uint16_t received0, received1 = 0;
    do {
        ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_RX_RSR(0), &received0, sizeof(received0)), err, TAG, "read RX RSR failed");
        ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_RX_RSR(0), &received1, sizeof(received1)), err, TAG, "read RX RSR failed");
    } while (received0 != received1);
    *size = __builtin_bswap16(received0);

err:
    return ret;
}

i think it's not possible to have some data inbeetween requests, and even so, you become this data later in another frame.

static esp_err_t w5500_get_rx_received_size(emac_w5500_t *emac, uint16_t *size){
    esp_err_t ret = ESP_OK;
    uint16_t res = 0;
    ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_RX_RSR(0), &res, sizeof(res)), err, TAG, "read RX RSR failed");
    *size = __builtin_bswap16(res);

err:
    return ret;
}

tests
sending 560 peace of "\r\n" through putty

rxLen:60 heap:304096
rxLen:60 heap:304000
rxLen:60 heap:303916
rxLen:60 heap:303832
rxLen:60 heap:303748
rxLen:60 heap:303664
rxLen:60 heap:303580
rxLen:60 heap:303496
...
rxLen:60 heap:276972
rxLen:60 heap:276872
rxLen:60 heap:276772
rxLen:60 heap:276672
rxLen:60 heap:276572
rxLen:60 heap:276472
rxLen:1160 heap:275272
free: 304156

Happy end? :)

@KaeLL
Copy link
Contributor

KaeLL commented Jun 3, 2022

but i think better way is to have static sized uint8_t arr[16384] and after we recive buffer and know about length in this arr, we can then allocate memory to put this data in stack.

I think LwIP assumes the buffer is dynamically allocated. This means it attempts to free it, which will crash your app. I can't check that right now, but it's probably under lwip/port.

I didn't test it, but i think we need more time for SPI requests to get data header

Up to your application. In my driver, by default, I chose a 1 tick delay.

i think it's not possible to have some data inbeetween requests

Then you should read the datasheet.

and even so, you become this data later in another frame.

The benefit of polling RX_RSR for a stable value is reading the entire ring buffer in one go, which is more efficient, at least on its own.

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 3, 2022

but i think better way is to have static sized uint8_t arr[16384] and after we recive buffer and know about length in this arr, we can then allocate memory to put this data in stack.

I think LwIP assumes the buffer is dynamically allocated. This means it attempts to free it, which will crash your app. I can't check that right now, but it's probably under lwip/port.

Thats right, thats why i told about allocation after we get length of data in buffer

"..and know about length in this arr, we can then allocate memory to put this data in stack."

So, then you have to copy data from static array to dynamic block of memory and then put it to stack. But anyway this is not good way. Faster as any time request for buffer size, but bad. I don't like this way.

I didn't test it, but i think we need more time for SPI requests to get data header

Up to your application. In my driver, by default, I chose a 1 tick delay.

i think it's not possible to have some data inbeetween requests

Then you should read the datasheet.

If you are ready to demonstrate that such a situation is practically possible, then I would like to reproduce it.
The speed of the module is so fast that even a continuous stream of data sent one after another will not be dropped into one area of ​​the module. It works so fast that it is able to catch the smallest pauses between data. In this case, it is necessary to completely move away from the concept of interruption and work with the buffer directly.

and even so, you become this data later in another frame.

The benefit of polling RX_RSR for a stable value is reading the entire ring buffer in one go, which is more efficient, at least on its own.

There is an extra loop in the code, which does not lead to the desired result, but only creates an unnecessary load. But even under such conditions, you would not get intermediate data into the buffer. Without this cycle, receiving intermediate data is excluded.

The code described in the library does not achieve the expected behavior. There is too much theory that is not reproduced in practice. Banal tests instantly reveal all the flaws in the logic that were implemented by its author.

By the way, this test demonstrates that frames are not able to stick together even if it is sent one after another. The module never sees more than one block at a time there, and the next block cannot in any way wedge into the previous one. We would need a delay between these two probes before we see another block. But taking into account this delay, we would quickly consider the block in the next frame and would have time to connect significantly more blocks already in the stack, and not in the procedure for checking glued frames in this function.
sending 560 peace of "\r\n" through putty

rxLen:60 heap:304096
rxLen:60 heap:304000
rxLen:60 heap:303916
rxLen:60 heap:303832
rxLen:60 heap:303748
rxLen:60 heap:303664
rxLen:60 heap:303580
rxLen:60 heap:303496
...
rxLen:60 heap:276972
rxLen:60 heap:276872
rxLen:60 heap:276772
rxLen:60 heap:276672
rxLen:60 heap:276572
rxLen:60 heap:276472
rxLen:1160 heap:275272
free: 304156

UPD
Ok, not proofs, need time and i check it.

Yes, you are right. But..

uint16_t res_a, res_b = 0;
            int trg = 0;
            do{
                w5500_read(emac, W5500_REG_SOCK_RX_RSR(0), &res_a, sizeof(res_a));
                w5500_read(emac, W5500_REG_SOCK_RX_RSR(0), &res_b, sizeof(res_b));
                trg++;
            }
            while(res_a != res_b);
            printf("- %d -\r\n",trg);
            uint16_t size = __builtin_bswap16(res_a);
            
            buffer = heap_caps_malloc(4096, MALLOC_CAP_DMA);
            printf("res:%d heap:%d\r\n",size,heap_caps_get_free_size(MALLOC_CAP_INTERNAL));

sending data

[part_of_data_1]
[part_of_data_2]
[part_of_data_3]
[part_of_data_4]

1
2

result

- 0 -
res:137 heap:300060
- 0 -
res:62 heap:295928
- 0 -
res:62 heap:300060
- 0 -
res:124 heap:300060
- 0 -
res:62 heap:295928
- 0 -
- 1 -
res:384 heap:291808
- 0 -

but as i said it's cost much more time, and we need a delay between probe to get it to work correclty.
UPD!
Nope, my fault!
I set printf inside of cycle. Without printf inside of it i can't catch any data inbeetween probe anymore!
So, i think i'm right, we need something like 200uS delay between probe to get result!

In any case, this code have to be redisigned! All or huge part of it.

UPD :)
No, I still can! But this is very rare and just not worth it without delay.

@MadDogMayCry0
Copy link
Author

UPD
When we become interrupt the buffer has all of data! So, no need to check any other. We can get whole buffer at once. I will try to implement this.

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 3, 2022

UPD
Yes, you are right. For a big data, for example 4096 bytes, this probes can detect increasing buffer. So, i'm a noob from the street! Don't judge me :)
Not so big diffirent, but yes, it's does something... ahhah

2x probe
test:2992
test:2992
test:1496
test:3623
test:2127
test:631

1x probe
test:2992
test:1496
test:1496
test:3623
test:2127
test:631

Ok, then.
If we will allocate the memory in a cycle, we going to big memory alocation problem through scenario i described above.
Need thinking, but on this moment i have only one idea - get buffer size. Get one time memory allocation and then glue the necessary parts of the date in a loop, taking them out of the module's buffer.

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 4, 2022

UPD.
So i think this is something like this

static void emac_w5500_task(void *arg){
    emac_w5500_t *emac = (emac_w5500_t *)arg;
    uint8_t status = 0;
    uint8_t *buffer = NULL;
    while (1) {
        // check if the task receives any notification
        if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 &&    // if no notification ...
            gpio_get_level(emac->int_gpio_num) != 0) {               // ...and no interrupt asserted
            continue;                                                // -> just continue to check again
        }
        /* read interrupt status */
        w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
        /* packet received */
        if (status & W5500_SIR_RECV) {
            status = W5500_SIR_RECV;

            // clear interrupt status
            w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));

            uint16_t offset = 0;
            uint16_t rx_len = 0;
            uint16_t remain_bytes = 0;
            w5500_get_rx_received_size(emac, &remain_bytes);
            
            printf("rx_size:%d heap:%d\r\n",remain_bytes,heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
            
            while(remain_bytes){
                    // get current read pointer
                    w5500_read(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset));
                    offset = __builtin_bswap16(offset);
                    
                    // read head first
                    w5500_read_buffer(emac, &rx_len, sizeof(rx_len), offset);
                    rx_len = __builtin_bswap16(rx_len) - 2; // data size includes 2 bytes of header
                    offset += 2;
                    
                    //Memory allocation
                    buffer = heap_caps_malloc(rx_len, MALLOC_CAP_DMA);
                    if(!buffer){
                        break;
                    }
                    // read the payload
                    w5500_read_buffer(emac, buffer, rx_len, offset);
                    
                    // update read pointer
                    offset += rx_len;
                    offset = __builtin_bswap16(offset);
                    w5500_write(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset));
                    /* issue RECV command */
                    w5500_send_command(emac, W5500_SCR_RECV, 100);
                    remain_bytes -= rx_len + 2;
                    // stacking
                    emac->eth->stack_input(emac->eth, buffer, rx_len);
            }
        }
    }
    vTaskDelete(NULL);
}

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 4, 2022

UPD
Strange variant, and get a bit more ram to work, but much logical and crappy :)

static void emac_w5500_task(void *arg){
    emac_w5500_t *emac = (emac_w5500_t *)arg;
    uint8_t status = 0;
    uint8_t *buffer = NULL;
    while (1) {
        // check if the task receives any notification
        if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 &&    // if no notification ...
            gpio_get_level(emac->int_gpio_num) != 0) {               // ...and no interrupt asserted
            continue;                                                // -> just continue to check again
        }
        /* read interrupt status */
        w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
        /* packet received */
        if (status & W5500_SIR_RECV) {
            status = W5500_SIR_RECV;

            // clear interrupt status
            w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));

            uint16_t offset = 0;
            uint16_t rx_len = 0;
            uint16_t remain_bytes = 0;
            
            w5500_get_rx_received_size(emac, &remain_bytes);
            printf("size:%d heap:%d\r\n",remain_bytes,heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
            //Memory allocation
            buffer = heap_caps_malloc(remain_bytes, MALLOC_CAP_DMA);
            if(!buffer){
                continue;
            }
            
            uint16_t res = 0;
            if(remain_bytes){
                while(remain_bytes){
                    // get current read pointer
                    w5500_read(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset));
                    offset = __builtin_bswap16(offset);
                    
                    // read head first
                    w5500_read_buffer(emac, &rx_len, sizeof(rx_len), offset);
                    rx_len = __builtin_bswap16(rx_len) - 2; // data size includes 2 bytes of header
                    offset += 2;
                    
                    // read the payload
                    w5500_read_buffer(emac, buffer+res, rx_len, offset);
                    // update read pointer
                    offset += rx_len;
                    offset = __builtin_bswap16(offset);
                    w5500_write(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset));
                    /* issue RECV command */
                    w5500_send_command(emac, W5500_SCR_RECV, 100);
                    remain_bytes -= rx_len + 2;
                    res += rx_len;
                }
                printf("stacking\r\n");
                emac->eth->stack_input(emac->eth, buffer, res);
            }
        }
    }
    vTaskDelete(NULL);
}

works bad, but shows how stack works, ans why we cant just get a whole buffer from module.

@KaeLL
Copy link
Contributor

KaeLL commented Jun 6, 2022

You now know there 2 ways from getting the size of the data to be read from the chip. Personally, I went with reading the 2 byte header of each packet because I found it to be simpler. I actually got to write and test the RX_RSR polling method, but gave up on it because in order to be superior to the other method, it had to be paired up up with passing managed buffers to LwIP. Since it's not possible due to ESP-NETIF demanding ownership of the buffer by the fact that it frees it up by itself, it didn't make much sense to go with this method.
On the upside, though, ESP-NETIF taking ownership of the buffer avoids memcpys from link layer to network layer.

@MadDogMayCry0
Copy link
Author

MadDogMayCry0 commented Jun 7, 2022

You now know there 2 ways from getting the size of the data to be read from the chip. Personally, I went with reading the 2 byte header of each packet because I found it to be simpler. I actually got to write and test the RX_RSR polling method, but gave up on it because in order to be superior to the other method, it had to be paired up up with passing managed buffers to LwIP. Since it's not possible due to ESP-NETIF demanding ownership of the buffer by the fact that it frees it up by itself, it didn't make much sense to go with this method. On the upside, though, ESP-NETIF taking ownership of the buffer avoids memcpys from link layer to network layer.

Clear! So, i do use this one #9083 (comment) last days, and it's seems to works stable. Need add some additionals checks to get it to work in final. This on is sux #9083 (comment) and i don't know why it's not works as expected. Something strange here. :)

@espressif-bot espressif-bot removed the Status: Opened Issue is new label Jul 13, 2022
@espressif-bot espressif-bot added Resolution: Duplicate This issue or pull request already exists Status: Done Issue is done internally and removed Status: Selected for Development Issue is selected for development labels Aug 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: Duplicate This issue or pull request already exists Status: Done Issue is done internally
Projects
None yet
Development

No branches or pull requests

6 participants