### Syntacore SCR* infra
###
### @copyright (C) Syntacore 2015-2017. All rights reserved.
###
### @brief SCR* infra startup code

#if __riscv_xlen == 64
# define STORE    sd
# define LOAD     ld
# define REGBYTES 8
#else
# define STORE    sw
# define LOAD     lw
# define REGBYTES 4
#endif

#ifndef __riscv_32e
#define TRAP_REGS_SPACE (REGBYTES*32)
#else // !__riscv_32e
#define TRAP_REGS_SPACE (4*16)
#endif // !__riscv_32e


    ## some useful macros
    .altmacro

    .macro save_reg_offs reg, offs, save_mem_base=zero
        STORE \reg, \offs*REGBYTES(\save_mem_base)
    .endm

    .macro save_reg regn, save_mem_base=zero
        STORE x\regn, \regn*REGBYTES(\save_mem_base)
    .endm

    .macro load_reg_offs reg, offs, load_mem_base=zero
        LOAD \reg, \offs*REGBYTES(\load_mem_base)
    .endm

    .macro load_reg regn, load_mem_base=zero
        LOAD x\regn, \regn*REGBYTES(\load_mem_base)
    .endm

    .macro load_gp addr=__global_pointer$
        LOCAL rel_addr
        .option push
        .option norelax
rel_addr:
        auipc gp, %pcrel_hi(\addr)
        addi  gp, gp, %pcrel_lo(rel_addr)
        .option pop
    .endm

    .macro save_regs reg_first, reg_last, save_mem_base=zero
        LOCAL regn
        regn = \reg_first
        .rept \reg_last - \reg_first + 1
        save_reg %(regn), \save_mem_base
        regn = regn+1
        .endr
    .endm

    .macro load_regs reg_first, reg_last, load_mem_base=zero
        LOCAL regn
        regn = \reg_first
        .rept \reg_last - \reg_first + 1
        load_reg %(regn), \load_mem_base
        regn = regn+1
        .endr
    .endm

    .macro memcpy src_beg, src_end, dst, tmp_reg
        LOCAL memcpy_1, memcpy_2
        j    memcpy_2
    memcpy_1:
        LOAD   \tmp_reg, (\src_beg)
        STORE   \tmp_reg, (\dst)
        add  \src_beg, \src_beg, 4
        add  \dst, \dst, 4
    memcpy_2:
        bltu \src_beg, \src_end, memcpy_1
    .endm

    .macro memset dst_beg, dst_end, val_reg
        LOCAL memset_1, memset_2
        j    memset_2
    memset_1:
        STORE   \val_reg, (\dst_beg)
        add  \dst_beg, \dst_beg, 4
    memset_2:
        bltu \dst_beg, \dst_end, memset_1
    .endm

    .macro context_save
        addi sp, sp, -TRAP_REGS_SPACE
        save_reg 1, sp
        save_regs 3, 15, sp
#ifndef __riscv_32e
        save_regs 16, 31, sp
#endif //  __riscv_32e
        addi a0, sp, TRAP_REGS_SPACE
        save_reg_offs a0, 2, sp ## save original sp
    .endm ## context_save

    .macro context_restore
        load_reg 1, sp
        load_regs 3, 15, sp
#ifndef __riscv_32e
        load_regs 16, 31, sp
#endif //  __riscv_32e
        load_reg 2, sp
    .endm ## context_restore

### #########################
### startup code

    .globl _start, main, trap_handler, trap_entry, _hart_halt, copy_data, scr_rtc_init, trim_code_init, TIMER_CMP_INT, drv_chip_ctrl_init
    .weak trap_entry, trap_handler, scr_rtc_init, TIMER_CMP_INT

    .option norvc

    ## .text
    .section ".text.crt","ax",@progbits

    ## Entry point
_start:
    ## reset mstatus: MPP=3@Bit-11, MPIE=0@Bit-7, MIE=0@Bit-3
    li t0, 0x1800
    csrw  mstatus, t0

    ## setup gp
    load_gp

    ## setup MTVEC
    la    t0, trap_entry
    csrw  mtvec, t0

    ## setup MIE, MIP
    csrw  mie, zero
    csrw  mip, zero

    ## park SMP additional cores
    csrr  a0, mhartid
    bnez  a0, _hart_halt

    ## boot hart 0

    ## init bss
    la    t0, __BSS_START__
    la    t1, __BSS_END__
    memset t0, t1, zero

    ## write stack pattern
    la    t0, __STACK_START__
    la    t1, __STACK_END__
    li    t2, 0xABABABAB
    memset t0, t1, t2

    /* set stack pointer */
    la	sp, __STACK_END__
    csrw  mscratch, sp

1:
    ## load data section
    jal copy_data
    ## platform init
    jal trim_code_init
    jal drv_chip_ctrl_init
    ## start main
    li a0, 0
    li a1, 0
    jal  main

    .globl exit, abort
    .weak exit, abort
exit:
    nop

trap_handler:
halt_loop:
    ## halt hart
_hart_halt:
    ## disable interrupts
    csrci mstatus, 0xf
    csrw  mie, zero
1:  wfi
    j     1b
abort:
    j     _hart_halt


/* When trap is an interrupt, this function is called */
trap_interrupt:
    slli    t0, t0, 1
    srli    t0, t0, 1
    addi    t0, t0, -3
    beq     t0, x0, trap_int_msw
    addi    t0, t0, -8
    beq     t0, x0, trap_int_mext
    LOAD    t0, 0x0(sp)
    addi    sp, sp, REGBYTES

    /* Interupt is timer interrupt */
    j       TIMER_CMP_INT
    mret

trap_int_mext:
    /* Interupt is external interrupt */
    LOAD  t0, 0x0(sp)
    addi  sp, sp, REGBYTES
    j trap_int_mext_entry
    mret

trap_int_msw:
    /* Interupt is software interrupt */
    LOAD  t0, 0x0(sp)
    addi  sp, sp, REGBYTES
    j trap_int_msw_entry
    mret

### #########################
### trap handler
    .section ".text.crt.trap_entry","ax",@progbits
    .org (0xC0)
    .align 7
trap_entry:
    /* Check for interrupt */
    addi    sp, sp, -REGBYTES
    STORE   t0, 0x0(sp)
    csrr    t0, mcause
    blt     t0, x0, trap_interrupt
    LOAD    t0, 0x0(sp)
    addi    sp, sp, REGBYTES

    ## save context
    context_save
    ## save mstatus priv stack
    csrr s0, mstatus
    ## restore TP
    csrr tp, mscratch
    ## load trap handler args
    csrr a0, mcause
    csrr a1, mepc
    mv   a2, sp
    ## setup gp
    load_gp
    ## call m-mode timer interrupt handler
    jal  trap_handler
    ## restore mstatus priv stack
    csrw mstatus, s0
    ## restore context
    context_restore
    mret

### Vectored interrupt handers

    .globl trap_int_msw_entry, trap_int_mtimer_entry, trap_int_mext_entry
    .weak trap_int_msw_entry, trap_int_mtimer_entry, trap_int_mext_entry
    .globl trap_int_msw_handler, trap_int_mtimer_handler, trap_int_mext_handler

    .section ".text.crt.trap_int_msw_entry","ax",@progbits
    .align 2
trap_int_msw_entry:
    ## save context
    context_save
    ## save mstatus priv stack
    csrr s0, mstatus
    ## restore TP
    csrr tp, mscratch
    ## load trap handler args
    csrr a0, mcause
    csrr a1, mepc
    mv   a2, sp
    ## setup gp
    load_gp
    ## call m-mode timer interrupt handler
    jal  trap_int_msw_handler
    ## restore mstatus priv stack
    csrw mstatus, s0
    ## restore context
    context_restore
    mret

    .section ".text.crt.trap_int_mtimer_entry","ax",@progbits
    .align 2
trap_int_mtimer_entry:
    ## save context
    context_save
    ## save mstatus priv stack
    csrr s0, mstatus
    ## restore TP
    csrr tp, mscratch
    ## load trap handler args
    csrr a0, mcause
    csrr a1, mepc
    mv   a2, sp
    ## setup gp
    load_gp
    ## call m-mode timer interrupt handler
    jal  trap_int_mtimer_handler
    ## restore mstatus priv stack
    csrw mstatus, s0
    ## restore context
    context_restore
    mret

    .section ".text.crt.trap_int_mext_entry","ax",@progbits
    .align 2
trap_int_mext_entry:
    ## save context
    context_save
    ## save mstatus priv stack
    csrr s0, mstatus
    ## restore TP
    csrr tp, mscratch
    ## load trap handler args
    csrr a0, mcause
    csrr a1, mepc
    mv   a2, sp
    ## setup gp
    load_gp
    ## call m-mode external interrupt handler
    jal  trap_int_mext_handler
    ## restore mstatus priv stack
    csrw mstatus, s0
    ## restore context
    context_restore
    mret
