/**
 * \file    csr.h
 * \brief   Architecture specific CSR's defs and inlines
 * 
 * \par     Copyright (c) 2018 UWE Technologies Corp., Ltd. All Rights Reserved.
 * \author  James
 * \date    2018-03-12
 * \version 
 * 
 * \history 
 *  Date        Note
 *  2018-03-12  Original Creation
 * 
 */
 
#ifndef __CSR_H_
#define __CSR_H_


#include <stdint.h>
#include <stdbool.h>


#define MSTATUS_MIE             0x00000008
#define MSTATUS_MPIE            0x00000080
#define MSTATUS_MPP             0x00001800

#define IRQ_M_SOFT              3
#define IRQ_M_TIMER             7
#define IRQ_M_EXT               11

#define MIP_MSIP                (1 << IRQ_M_SOFT)
#define MIP_MTIP                (1 << IRQ_M_TIMER)
#define MIP_MEIP                (1 << IRQ_M_EXT)

#define PRV_U                   0
#define PRV_S                   1
#define PRV_H                   2
#define PRV_M                   3     //only machine mode is supported

#define MCAUSE_INSTRUCTION_ADDR_MISALIGNED 0x0
#define MCAUSE_INSTRUCTION_ACCESS_FAULT 0x1
#define MCAUSE_ILLEGAL_INSTRUCTION 0x2
#define MCAUSE_BREAKPOINT 0x3
#define MCAUSE_LOAD_ADDR_MISALIGNED 0x4
#define MCAUSE_LOAD_ADDR_FAULT 0x5
#define MCAUSE_STORE_ADDR_MISALIGNED 0x6
#define MCAUSE_STORE_ADDR_FAULT 0x7
#define MCAUSE_MACHINE_ECALL 0xb

#define SCR_MTIMER_BASE_ADDR    (0x00490000)

#define NON_STD_SCR_IPIC_BASE_ADDR  (0xBF0)

#define _TOSTR(s) __TOSTR(s)
#define __TOSTR(s) #s

#define _IMM_5BIT_CONST(val) \
    (__builtin_constant_p(val) && (unsigned long)(val) < 32)
#define _IMM_ZERO_CONST(val) \
    (__builtin_constant_p(val) && (val) == 0)

#define read_csr(reg)                           \
    ({                                          \
        unsigned long __tmp;                    \
        asm volatile ("csrr %0, " _TOSTR(reg)   \
                      : "=r"(__tmp));           \
        __tmp;                                  \
    })

#define write_csr(reg, val)                                 \
    do {                                                    \
        if (_IMM_ZERO_CONST(val)) {                         \
            asm volatile ("csrw " _TOSTR(reg) ", zero" ::); \
        } else if (_IMM_5BIT_CONST(val)) {                  \
            asm volatile ("csrw " _TOSTR(reg) ", %0"        \
                          :: "i"(val));                     \
        } else {                                            \
            asm volatile ("csrw " _TOSTR(reg) ", %0"        \
                          :: "r"(val));                     \
        }                                                   \
    } while (0)

#define swap_csr(reg, val)                                  \
    ({                                                      \
        unsigned long __tmp;                                \
        if (_IMM_ZERO_CONST(val)) {                         \
            asm volatile ("csrrw %0, " _TOSTR(reg) ", zero" \
                          :  "=r"(__tmp) :);                \
        } else if (_IMM_5BIT_CONST(val)) {                  \
            asm volatile ("csrrw %0, " _TOSTR(reg) ", %1"   \
                          : "=r"(__tmp) : "i"(val));        \
        } else {                                            \
            asm volatile ("csrrw %0, " _TOSTR(reg) ", %1"   \
                          : "=r"(__tmp) : "r"(val));        \
        }                                                   \
        __tmp;                                              \
    })

#define set_csr(reg, bit)                                   \
    ({                                                      \
        unsigned long __tmp;                                \
        if (_IMM_5BIT_CONST(bit)) {                         \
            asm volatile ("csrrs %0, " _TOSTR(reg) ", %1"   \
                          : "=r"(__tmp) : "i"(bit));        \
        } else {                                            \
            asm volatile ("csrrs %0, " _TOSTR(reg) ", %1"   \
                          : "=r"(__tmp) : "r"(bit));        \
        }                                                   \
        __tmp;                                              \
    })

#define clear_csr(reg, bit)                                 \
    ({                                                      \
        unsigned long __tmp;                                \
        if (_IMM_5BIT_CONST(bit)) {                         \
            asm volatile ("csrrc %0, " _TOSTR(reg) ", %1"   \
                          : "=r"(__tmp) : "i"(bit));        \
        } else {                                            \
            asm volatile ("csrrc %0, " _TOSTR(reg) ", %1"   \
                          : "=r"(__tmp) : "r"(bit));        \
        }                                                   \
        __tmp;                                              \
    })

#define rdtime() read_csr(time)
#define rdcycle() read_csr(cycle)
#define rdinstret() read_csr(instret)


#endif /* #ifndef __CSR_H_ */

