/*
 * FSL_Wait.h
 *
 *  Created on: Mar 11, 2015
 *      Author: B52414
 */

/**
 * 1. variant (most precise)
 * uint32_t cycles = ...;
 * WAIT_FOR_MULTIPLY_OF_(3|4|5)_CYCLES(cycles); // you have to know ahead what are the needed cycles multiple of
 *
 * 2. variant
 * WaitForMultipleOf(2|3|5)Cycles(cycles); // you can pass variable argument
 *
 * 3. variant
 * cycles = GET_CYCLES_FOR_(MS|US|NS)[_PRECISE](count, CPU_CORE_CLK_HZ);
 * WaitCycles(cycles);
 *
 * 4. variant
 * you can use macro version of defined functions, they are just slightly faster, because functions are defined as inline
 * WAIT_SEC(sec);
 * WAIT_MS(ms);
 * WAIT_US(us);
 * WAIT_NS(ns);
 * WAIT_CYCLES(cycles);
 *
 * 5. variant (less precise)
 * you can use defined functions
 * WaitSec(sec); // precise enough
 * WaitMS(ms);   // precise enough
 * WaitUS(us);   // better to use first or second variant for the lower range of parameter
 * WaitNS(ns);   // crucial to use first variant or predefined WAIT_X_CYCLES macros for the lower range of parameter
 */

#ifndef FSL_WAIT_H_
#define FSL_WAIT_H_

#include "PE_Types.h"
#include "CPU.h"

#define GET_CYCLES_FOR_MS(ms, freq) ((ms)*(((float)freq)/1000))               /* calculates the needed cycles based on core clock frequency */
#define GET_CYCLES_FOR_US(us, freq) ((us)*((((float)freq)/1000)/1000))        /* calculates the needed cycles based on core clock frequency */
#define GET_CYCLES_FOR_NS(ns, freq) (((ns)*((((float)freq)/1000)/1000))/1000) /* calculates the needed cycles based on core clock frequency */

#define WAIT_1_CYCLE asm("nop \n\t")
#define WAIT_2_CYCLES asm("nop \n\t" "nop \n\t")
#define WAIT_3_CYCLES asm("nop \n\t" "nop \n\t" "nop \n\t")
#define WAIT_4_CYCLES asm("nop \n\t" "nop \n\t" "nop \n\t")
#define WAIT_5_CYCLES asm("nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t")
#define WAIT_6_CYCLES asm("nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t")
#define WAIT_7_CYCLES asm("nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t")
#define WAIT_8_CYCLES asm("nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t")
#define WAIT_9_CYCLES asm("nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t")
#define WAIT_10_CYCLES asm("nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t" "nop \n\t")

/* Optimized code which calculates remainder for given dividend and divisor. Applies only for divisors which are power-of-two. */
#define GET_REMAINDER_OPTIMIZED(dividend, divisor) (dividend & (--divisor)) /* 2 cycles (1 for SUB and 1 for AND) */

#if defined(__thumb__) && !defined(__thumb2__) /* Thumb instruction set only */

/**
 * Waits for exact number of cycles which can be expressed as multiple of 3.
 *
 * MOV - 1 cycle
 * SUB - 1 cycle
 * BNE - 1 cycle or 2 cycles if jump is realized
 *
 * output list (empty) - which registers are output and how to map them to C code
 * input list (cycles) - which registers are input and how to map them to C code
 * clobber list (r0, r1, cc) - which registers might have changed during execution of asm code (compiler will have to reload them)
 */
#define WAIT_FOR_MUL3_CYCLES(cycles) \
  __asm( \
    "mov r0, %[cycles] \n\t" \
    "0: \n\t" \
      "sub r0, #3 \n\t" \
    "bne 0b \n\t" \
     : \
     : [cycles] "r" (cycles) \
     : "r0", "r1", "cc" \
  ) \

#else /* Thumb2 or A32 instruction set */

/**
 * Waits for exact number of cycles which can be expressed as multiple of 3.
 */
#define WAIT_FOR_MUL3_CYCLES(cycles) \
  __asm( \
    "movs r0, %[cycles] \n"  \
    "0: \n"                  \
      "subs r0, r0, #3 \n"   \
    "bne 0b \n"              \
     :                       \
     : [cycles] "r" (cycles) \
     : "r0", "r1", "cc"      \
  ) \

#endif

void WaitCycles(uint32_t cycles);
void WaitSec(uint16_t sec);
void WaitMS(uint16_t ms);
void WaitUS(uint16_t us);
void WaitNS(uint16_t ns);

#endif /* FSL_WAIT_H_ */
