/*
 * Author:  F. Poulain
 * Date:    05/06/2019
 *
 * The goal of this test is to see DDR3 behaviour in DSP one core writing.
 *
 */

#include <memory.h>

/* 1 MiB = 1024 KiB = 1048576  */
#define DDR_SIZE_TO_TEST_MAX_BYTES    (0x100000)

#define FLOAT_MB (1048576.0)

/* */
#define BUS_PRIORITY_MAX (0x0)

/* */
#define BUS_PRIORITY_MIN (0x7)

#define MEM_CTRL_BASE_ADDR  (0x21010000)
#define REG_SEL_CORE0   (0x00000000)
#define REG_SEL_CORE1   (0x01000000)
#define REG_SEL_CORE2   (0x02000000)
#define REG_SEL_CORE3   (0x03000000)
#define REG_SEL_CORE4   (0x04000000)
#define REG_SEL_CORE5   (0x05000000)
#define REG_SEL_CORE6   (0x06000000)
#define REG_SEL_CORE7   (0x07000000)
#define REG_SEL_LINUX   (0x08000000)

/*
 *
 */
void ddr_write_access(Uint32* p_tab, Uint32 size_bytes);
Uint32 verif(Uint32* p_tab, Uint32 size_bytes);
void init_cnt(void);

/* */
Uint32* ptab0 = NULL;

int* cnt_1_addr;
int* cnt_2_addr;


float calculate_throughput(Uint32 size_bytes, UInt64 nb_cycles_elapsed)
{
    float f32_speed_mb_per_sec = 0.0;
    double f64_speed_b_per_sec = 0.0;
    double f64_speed_mb_per_sec = 0.0;

    float execution_time_ns = 0.0;

    execution_time_ns = nb_cycles_elapsed * (1/1.2) ;

    f64_speed_b_per_sec = ( (double) 1000000000.0 * (double) size_bytes) / (double) execution_time_ns;

    f64_speed_mb_per_sec = f64_speed_b_per_sec / (double) FLOAT_MB;

    f32_speed_mb_per_sec = (float) (f64_speed_mb_per_sec);

    return f32_speed_mb_per_sec;
}


int main(void)
{
    // memory initialization
    memory_init();

    // Variable used for the test
    Uint32 start_1=0;
    Uint32 start_2=0;
    Uint32 end_1=0;
    Uint32 end_2=0;
    Uint32 duration_1=0;
    Uint32 duration_2=0;
    Uint32 err=0;

    // Performance counter initialization
    init_cnt();

    // set priority
    Uint32 priority = BUS_PRIORITY_MIN;
    CSL_XMC_setMDMAPriority(priority);

    Uint32 core_id = 0;

    Uint32 size_to_test_bytes = 4;

    core_id = DNUM;

    // pointer in DDR3A
    ptab0 = (Uint32*) DDR_TEST_START_ADDR;

    if (core_id == 0)
    {
        /* Only Core 0 do the test */

        do
        {
            start_1 = *cnt_1_addr; start_2 = *cnt_2_addr;
            ddr_write_access(&ptab0[0], size_to_test_bytes);
            // Writeback last data in cache
            //CACHE_wbL1d(&ptab0[0], size_to_test_bytes, CACHE_WAIT); // commented because L1D is not write-allocate & cache is free at beginning.
            end_1 = *cnt_1_addr; end_2 = *cnt_2_addr;

            // Invalidate cache line (security to ensure empty cache)
            CACHE_invL1d(&ptab0[0], size_to_test_bytes, CACHE_WAIT);

            // Verification
            err = verif(&ptab0[0], size_to_test_bytes);

            if (err != 0)
            {
                printf("Error: %d\n", err);
            }

            // reset all updated memory blocks in DDR3 memory
            memset(&ptab0[0], 0, size_to_test_bytes);

            // Writeback Invalidate cache (empty cache) due to verification
            CACHE_wbInvL1d(&ptab0[0], size_to_test_bytes, CACHE_WAIT);

            // Total read and write accesses by the DSP core #0
            duration_1 = end_1 - start_1; duration_2 = end_2 - start_2;

            printf("Data input: %dB, Read Acesses: %d, Write Acesses: %d\n", size_to_test_bytes, duration_1, duration_2);

            size_to_test_bytes *= 2;
        } while(size_to_test_bytes <= DDR_SIZE_TO_TEST_MAX_BYTES);

    }

    return 0;
}

void ddr_write_access(Uint32* p_tab, Uint32 size_bytes)
{
    Uint32 i_word = 0;
    Uint32 nb_words = size_bytes / 4;

    for (i_word = 0; i_word < nb_words; i_word++)
    {
        p_tab[i_word] = i_word;
    }
}

void init_cnt(void)
{
    // PERF_CNT_CFG
    int offset_cfg = 0x88;
    int *reg_cfg = (int*)(MEM_CTRL_BASE_ADDR + offset_cfg);
    *reg_cfg = 0x80038002; // CNT1: R, CNT2: W, DSP CORE0 MASTER ID

    // PERF_CNT_SEL
    int offset_sel = 0x8C;
    int *reg_sel = (int*)(MEM_CTRL_BASE_ADDR + offset_sel);
    *reg_sel = 0; // DSP CORE0 MASTER ID

    // PERF_CNT_1
    int offset_cnt_1 = 0x80;
    cnt_1_addr = (int*)(MEM_CTRL_BASE_ADDR + offset_cnt_1);

    // PERF_CNT_1
    int offset_cnt_2 = 0x84;
    cnt_2_addr = (int*)(MEM_CTRL_BASE_ADDR + offset_cnt_2);
}

Uint32 verif(Uint32* p_tab, Uint32 size_bytes)
{
    Uint32 i_word = 0;
    Uint32 err = 0;
    Uint32 nb_words = size_bytes / 4;

    for (i_word = 0; i_word < nb_words; i_word++)
    {
        if (p_tab[i_word] != i_word)
            err++;
    }

    return err;
}




