/*
This code tests the interaction between PSRAM and SPI flash routines.
*/

#include <esp_types.h>
#include <stdio.h>
#include "rom/ets_sys.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/xtensa_api.h"
#include "unity.h"
#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "rom/ets_sys.h"
#include "esp_heap_caps.h"
#include "esp_spi_flash.h"
#include "esp_partition.h"
#include "test_utils.h"


#if CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MEMMAP

#define TSTSZ (16*1024)


volatile static int res[2], err[2];

void tstMem(void *arg) {
    volatile unsigned char *mem=(volatile unsigned char*)arg;
    int p=0;
    while(1) {
        for (int i=0; i<TSTSZ; i++) {
            mem[i]=(i^p);
        }
        for (int i=0; i<TSTSZ; i++) {
            if (mem[i]!=((i^p)&0xff)) {
                printf("Core %d mem err! Got %x espected %x at addr %p\n", xPortGetCoreID(), mem[i], (i^p)&0xff, &mem[i]);
                err[xPortGetCoreID()]++;
            }
        }
        p++;
        res[xPortGetCoreID()]++;
    }
}


TEST_CASE("Spiram cache flush on mmap", "[spiram][ignore]")
{
    void *mem[2];
    res[0]=0; res[1]=0;
#if CONFIG_SPIRAM_USE_CAPS_ALLOC
    mem[0]=pvPortMallocCaps(TSTSZ, MALLOC_CAP_SPIRAM);
    mem[1]=pvPortMallocCaps(TSTSZ, MALLOC_CAP_SPIRAM);
#else
    mem[0]=(void*)0x3f800000;
    mem[1]=(void*)0x3f800000+TSTSZ;
#endif
    TaskHandle_t th[2];
    err[0]=0; err[1]=0;
    printf("Creating tasks\n");
    xTaskCreatePinnedToCore(tstMem  , "tskone"  , 2048, mem[0], 3, &th[0], 0);
    xTaskCreatePinnedToCore(tstMem  , "tsktwo"  , 2048, mem[1], 3, &th[1], 1);

    const esp_partition_t* part = get_test_data_partition();
    for (int l=0; l<10; l++) {
        for (int p=0; p<4096*1024; p+=65536) {
            const void *out;
            spi_flash_mmap_handle_t h;
            spi_flash_mmap(p, 65536, SPI_FLASH_MMAP_DATA, &out, &h);
            spi_flash_munmap(h);
        }
        printf("%d/10\n", l);
    }

    printf("Checked memory %d and %d times. Errors: %d and %d\n", res[0], res[1], err[0], err[1]);

    vTaskDelete(th[0]);
    vTaskDelete(th[1]);
#if CONFIG_SPIRAM_USE_CAPS_ALLOC
    free(mem[0]);
    free(mem[1]);
#endif
    TEST_ASSERT(err[0]==0);
    TEST_ASSERT(err[1]==0);
}


#define CYCLES 1024

TEST_CASE("Spiram cache flush on write/read", "[spiram][ignore]")
{
    void *mem[2];
    res[0]=0; res[1]=0;
#if CONFIG_SPIRAM_USE_CAPS_ALLOC
    mem[0]=pvPortMallocCaps(TSTSZ, MALLOC_CAP_SPIRAM);
    mem[1]=pvPortMallocCaps(TSTSZ, MALLOC_CAP_SPIRAM);
#else
    mem[0]=(void*)0x3f800000;
    mem[1]=(void*)0x3f800000+TSTSZ;
#endif
    TaskHandle_t th[2];
    const esp_partition_t* part = get_test_data_partition();
    assert(part!=NULL);

    printf("Erasing sector...\n");
    esp_partition_erase_range(part, 0, 64*1024);
    printf("Erased.\n");
    printf("Creating tasks\n");
    xTaskCreatePinnedToCore(tstMem  , "tskone"  , 2048, mem[0], 3, &th[0], 0);
    xTaskCreatePinnedToCore(tstMem  , "tsktwo"  , 2048, mem[1], 3, &th[1], 1);
    char buf[512];

    const void *out;
    spi_flash_mmap_handle_t handle;
    esp_partition_mmap(part, 0, 512, SPI_FLASH_MMAP_DATA, &out, &handle);
    for (int i=0; i<CYCLES; i++) {
        printf("%d/%d\n", i, CYCLES);
        esp_partition_write(part, 0, buf, 512);
        esp_partition_read(part, 0, buf, 512);
        vTaskDelay(1);
    }
    spi_flash_munmap(handle);

    printf("Checked memory %d and %d times.\n", res[0], res[1]);

    vTaskDelete(th[0]);
    vTaskDelete(th[1]);
#if CONFIG_SPIRAM_USE_CAPS_ALLOC
    free(mem[0]);
    free(mem[1]);
#endif
}

#endif  //CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MEMMAP