/*
   Baremetal main program with timer interrupt.
   SPDX-License-Identifier: Unlicense

   https://five-embeddev.com/

   Tested with sifive-hifive-revb, but should not have any
   dependencies to any particular implementation.
   
*/

// RISC-V CSR definitions and access classes
#include "riscv.h"
#include "riscv-interrupt.h"
#include "gpio.h"
#include "interrupt.h"
#include "gpio.h"
#include "timer.h"

/* #include "timer.h" */

#include "vector_table.h"

#define uint_xlen_t uint32_t

// Machine mode interrupt service routine

// Global to hold current timestamp, written in MTI handler.
static volatile uint64_t timestamp = 0;
// Expect this to increment one time per second - inside exception handler, after each return of MTI handler.
static volatile uint64_t ecall_count = 0;

#define RISCV_MTVEC_MODE_VECTORED 1
#define SYSTEM_CPU_INTR_FROM_CPU_0_REG (C3_SYSTEM+0x0028)
#define SYSTEM_PERIP_CLK_EN0_REG (C3_SYSTEM+0x0010)
#define SYSTEM_PERIP_CLK_EN1_REG (C3_SYSTEM+0x0014)


// #define INTERRUPT_CORE0_CPU_INTR_FROM_CPU_0_MAP_REG (C3_INTERRUPT+0x00C8)
// #define INTERRUPT_CORE0_INTR_STATUS_0_REG (C3_INTERRUPT+0x00F8)
// #define INTERRUPT_CORE0_INTR_STATUS_1_REG (C3_INTERRUPT+0x00FC)
// static int led_pin = 3;
// static int button_pin = 9;
//static int interrupt_num = 9;
#define SYSTEM_SYSTIMER_CLK_EN 29
int main(void) {
    // Goi cac ham khoi tao SoC o day
    // Global interrupt disable
    csr_clr_bits_mstatus(MSTATUS_MIE_BIT_MASK);
    //csr_write_mie(0);
    
    // Setup the IRQ handler entry point, set the mode to vectored
    csr_write_mtvec((uint_xlen_t) riscv_mtvec_table | RISCV_MTVEC_MODE_VECTORED);
    
    for (uint32_t i = 0; i < ETS_MAX_INTR_SOURCE; i++) {
        intr_matrix_set(0, i, ETS_INVALID_INUM);
    }
    isr_setup(ETS_GPIO_INUM, LEVEL_ISR, 5);
    isr_setup(ETS_SYSTEMTIMER_COMP0_INUM, LEVEL_ISR, 6);
    isr_setup(17, LEVEL_ISR, 7);
    *REG(INTERRUPT_CORE0_CLOCK_GATE_REG) = 1;
    *REG(SYSTEM_PERIP_CLK_EN0_REG) |= (1<<SYSTEM_SYSTIMER_CLK_EN);
    *REG(INTERRUPT_CORE0_CPU_INT_CLEAR_REG) = 0xFFFFFFFF;

    // Enable MIE.MTI
    csr_set_bits_mstatus(MIE_MTI_BIT_MASK);
    
    // Global interrupt enable 
    csr_set_bits_mstatus(MSTATUS_MIE_BIT_MASK);
    

    // Goi cac ham thiet lap ngat GPIO o day

    // isr_map(INTERRUPT_CORE0_GPIO_INTERRUPT_PRO_MAP_REG, interrupt_num);
    // intr_matrix_set(0, 16, 9);
    
    // thiet lap ngat timer 

    // Goi cac ham thiet lap GPIO o day
    // gpio_output(led_pin);
    // gpio_input(button_pin, PULL_UP);
    wdt_disable();
    // gpio_isr_type(button_pin, ANY_EDGE);
    // gpio_isr_enable(button_pin);
    // intr_matrix_set(0, ETS_GPIO_INTR_SOURCE, ETS_GPIO_INUM);
    setup_period_timer(0, 0, 16e6);
    map_timer_cpu_int(0, ETS_SYSTEMTIMER_COMP0_INUM);
    intr_matrix_set(0, ETS_FROM_CPU_INTR0_SOURCE, 17); // anh xa ngat mem do CPU sinh ra vao ngat so 17
    *REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG) = 1;
    printf("INT EN = %lx\n", *REG( INTERRUPT_CORE0_CPU_INT_ENABLE_REG));
    printf("INT TYPE = %lx\n", *REG(INTERRUPT_CORE0_CPU_INT_TYPE_REG));
    // printf("priority %d = %ld\n",9, *REG(INTERRUPT_CORE0_CPU_INT_PRI_REG(ETS_GPIO_INUM)));
    
    // Busy loop
    do {

        // Wait for timer interrupt
        //__asm__ volatile ("wfi");
        // __wfi();
        // Try a synchronous exception.
        //e__asm__ volatile ("ecall");
        //printf("mcause = %lx - status0 = %lx - status1 = %lx \n", csr_read_mcause(), *REG(INTERRUPT_CORE0_INTR_STATUS_0_REG), *REG(INTERRUPT_CORE0_INTR_STATUS_1_REG));
        //printf("cpu_int_eip_status = %lx\n", *REG(INTERRUPT_CORE0_CPU_INT_EIP_STATUS_REG));
        // printf("gpio_status_reg = %lx\n", *REG(GPIO_STATUS_REG));
        // printf("int status 0 = %lx, status 1 = %lx, pending = %lx, threshold = %lx\n", 
        //     *REG(INTERRUPT_CORE0_INTR_STATUS_0_REG), 
        //     *REG(INTERRUPT_CORE0_INTR_STATUS_1_REG), 
        //     *REG(INTERRUPT_CORE0_CPU_INT_EIP_STATUS_REG),
        //     *REG(INTERRUPT_CORE0_CPU_INT_THRESH_REG) );
        
    } while (1);
    
    // Will not reach here
    return 0;
}

#pragma GCC push_options
// Force the alignment for mtvec.BASE. A 'C' extension program could be aligned to to bytes.
#pragma GCC optimize ("align-functions=4")
// The 'riscv_mtvec_mti' function is added to the vector table by the vector_table.c
void riscv_mtvec_mti(void)  {
}

// void riscv_mtvec_platform_irq9() {
//     printf("Ham xu ly ngat so 9");
// }

// The 'riscv_mtvec_exception' function is added to the vector table by the vector_table.c
// This function looks at the cause of the exception, if it is an 'ecall' instruction then increment a global counter.
void riscv_mtvec_exception(void)  {
    uint_xlen_t this_cause = csr_read_mcause();
    uint_xlen_t this_pc    = csr_read_mepc();
    //uint_xlen_t this_value = csr_read_mtval();
    printf("Co ngat mem mcause = %lx, pc = %lx", this_cause, this_pc);
    switch (this_cause) {
        case RISCV_EXCP_ENVIRONMENT_CALL_FROM_M_MODE:
            ecall_count++;
            // Make sure the return address is the instruction AFTER ecall
            csr_write_mepc(this_pc+4);
            break;
    }
}
void riscv_mtvec_platform_irq(void) 
{
    uint_xlen_t this_cause = csr_read_mcause();
    uint_xlen_t this_pc    = csr_read_mepc();
    printf("Co ngat cung mcause = %lx - pc = %lx", this_cause, this_pc);
}
#pragma GCC pop_options

