/*
 * Copyright (c) 2018 embedded brains GmbH
 *
 * Copyright (c) 2015 University of York.
 * Hesham ALmatary <hesham@alumni.york.ac.uk>
 *
 * COPYRIGHT (c) 1989-1999.
 * On-Line Applications Research Corporation (OAR).
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <rtems/score/cpuimpl.h>
#include <rtems/score/isr.h>
#include <rtems/score/riscv-utility.h>

#define RISCV_ASSERT_CONTEXT_OFFSET( field, off ) \
  RTEMS_STATIC_ASSERT( \
    offsetof( Context_Control, field) == RISCV_CONTEXT_ ## off, \
    riscv_context_offset_ ## field \
  )

RISCV_ASSERT_CONTEXT_OFFSET( isr_dispatch_disable, ISR_DISPATCH_DISABLE );
#ifdef RTEMS_SMP
RISCV_ASSERT_CONTEXT_OFFSET( is_executing, IS_EXECUTING );
#endif
RISCV_ASSERT_CONTEXT_OFFSET( ra, RA );
RISCV_ASSERT_CONTEXT_OFFSET( sp, SP );
RISCV_ASSERT_CONTEXT_OFFSET( tp, TP );
RISCV_ASSERT_CONTEXT_OFFSET( s0, S0 );
RISCV_ASSERT_CONTEXT_OFFSET( s1, S1 );
RISCV_ASSERT_CONTEXT_OFFSET( s2, S2 );
RISCV_ASSERT_CONTEXT_OFFSET( s3, S3 );
RISCV_ASSERT_CONTEXT_OFFSET( s4, S4 );
RISCV_ASSERT_CONTEXT_OFFSET( s5, S5 );
RISCV_ASSERT_CONTEXT_OFFSET( s6, S6 );
RISCV_ASSERT_CONTEXT_OFFSET( s7, S7 );
RISCV_ASSERT_CONTEXT_OFFSET( s8, S8 );
RISCV_ASSERT_CONTEXT_OFFSET( s9, S9 );
RISCV_ASSERT_CONTEXT_OFFSET( s10, S10 );
RISCV_ASSERT_CONTEXT_OFFSET( s11, S11 );

#if __riscv_flen > 0

RISCV_ASSERT_CONTEXT_OFFSET( fcsr, FCSR );
RISCV_ASSERT_CONTEXT_OFFSET( fs0, FS0 );
RISCV_ASSERT_CONTEXT_OFFSET( fs1, FS1 );
RISCV_ASSERT_CONTEXT_OFFSET( fs2, FS2 );
RISCV_ASSERT_CONTEXT_OFFSET( fs3, FS3 );
RISCV_ASSERT_CONTEXT_OFFSET( fs4, FS4 );
RISCV_ASSERT_CONTEXT_OFFSET( fs5, FS5 );
RISCV_ASSERT_CONTEXT_OFFSET( fs6, FS6 );
RISCV_ASSERT_CONTEXT_OFFSET( fs7, FS7 );
RISCV_ASSERT_CONTEXT_OFFSET( fs8, FS8 );
RISCV_ASSERT_CONTEXT_OFFSET( fs9, FS9 );
RISCV_ASSERT_CONTEXT_OFFSET( fs10, FS10 );
RISCV_ASSERT_CONTEXT_OFFSET( fs11, FS11 );

#endif /* __riscv_flen */

#define RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( field, off ) \
  RTEMS_STATIC_ASSERT( \
    offsetof( CPU_Interrupt_frame, field) == RISCV_INTERRUPT_FRAME_ ## off, \
    riscv_interrupt_frame_offset_ ## field \
  )

RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( mstatus, MSTATUS );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( mepc, MEPC );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( a2, A2 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( s0, S0 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( s1, S1 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ra, RA );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( a3, A3 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( a4, A4 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( a5, A5 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( a6, A6 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( a7, A7 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( t0, T0 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( t1, T1 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( t2, T2 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( t3, T3 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( t4, T4 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( t5, T5 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( t6, T6 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( a0, A0 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( a1, A1 );

#if __riscv_flen > 0

RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( fcsr, FCSR );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft0, FT0 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft1, FT1 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft2, FT2 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft3, FT3 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft4, FT4 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft5, FT5 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft6, FT6 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft7, FT7 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft8, FT8 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft9, FT9 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft10, FT10 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( ft11, FT11 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( fa0, FA0 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( fa1, FA1 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( fa2, FA2 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( fa3, FA3 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( fa4, FA4 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( fa5, FA5 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( fa6, FA6 );
RISCV_ASSERT_INTERRUPT_FRAME_OFFSET( fa7, FA7 );

#endif /* __riscv_flen */

#define RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( field, off ) \
  RTEMS_STATIC_ASSERT( \
    offsetof( CPU_Exception_frame, field) == RISCV_EXCEPTION_FRAME_ ## off, \
    riscv_context_offset_ ## field \
  )

RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( mcause, MCAUSE );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( sp, SP );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( gp, GP );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( tp, TP );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( s2, S2 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( s3, S3 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( s4, S4 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( s5, S5 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( s6, S6 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( s7, S7 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( s8, S8 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( s9, S9 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( s10, S10 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( s11, S11 );

#if __riscv_flen > 0

RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs0, FS0 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs1, FS1 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs2, FS2 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs3, FS3 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs4, FS4 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs5, FS5 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs6, FS6 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs7, FS7 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs8, FS8 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs9, FS9 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs10, FS10 );
RISCV_ASSERT_EXCEPTION_FRAME_OFFSET( fs11, FS11 );

#endif /* __riscv_flen */

RTEMS_STATIC_ASSERT(
  sizeof( CPU_Interrupt_frame ) % CPU_STACK_ALIGNMENT == 0,
  riscv_interrupt_frame_size
);

void _init(void);

void _fini(void);

void _init(void)
{
}

void _fini(void)
{
}

/**
 * @brief Performs processor dependent initialization.
 */
void _CPU_Initialize(void)
{
  /* Do nothing */
}

uint32_t _CPU_ISR_Get_level( void )
{
  if ( _CPU_ISR_Is_enabled( read_csr( mstatus ) ) ) {
    return 0;
  }

  return 1;
}

void *_CPU_Thread_Idle_body( uintptr_t ignored )
{
  while ( true ) {
    __asm__ volatile ( "wfi" );
  }

  return NULL;
}
