/* Copyright (c) 2014 Authors * * Contributor Julius Baxter * Contributor Stefan Wallentowitz * * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this * notice is included verbatim in any distributions. No written agreement, * license, or royalty fee is required for any of the authorized uses. * Modifications to this software may be copyrighted by their authors * and need not follow the licensing terms described here, provided that * the new terms are clearly indicated on the first page of each file where * they apply. */ /* -------------------------------------------------------------------------- */ /* This program is commented throughout in a fashion suitable for processing with Doxygen. */ /* -------------------------------------------------------------------------- */ #include #ifndef __OR1K_SUPPORT_H__ #define __OR1K_SUPPORT_H__ /*! * \defgroup or1k_macros OR1K macros * @{ */ /*! * Access byte-sized memory mapped register * * Used to access a byte-sized memory mapped register. It avoids usage errors * when not defining register addresses volatile and handles casting correctly. * * Example for both read and write: * * \code * uint8_t status = REG8(IPBLOCK_STATUS_REG_ADDR); * REG8(IPBLOCK_ENABLE) = 1; * \endcode * * \param add Register address */ #define REG8(add) *((volatile unsigned char *) (add)) /*! * Access halfword-sized memory mapped register * * Used to access a 16 byte-sized memory mapped register. It avoids usage errors * when not defining register addresses volatile and handles casting correctly. * * See REG8() for an example. * * \param add Register address */ #define REG16(add) *((volatile unsigned short *) (add)) /*! * Access word-sized memory mapped register * * Used to access a word-sized memory mapped register. It avoids usage errors * when not defining register addresses volatile and handles casting correctly. * * See REG8() for an example. * * \param add Register address */ #define REG32(add) *((volatile unsigned long *) (add)) /*! * @} */ /*! * \defgroup or1k_interrupts OR1K interrupt control * * Interrupt control function prototypes * * @{ */ /*! Function pointer to interrupt handler functions */ typedef void (*or1k_interrupt_handler_fptr)(void* data); /*! * Add interrupt handler for interrupt line * * Registers a callback function for a certain interrupt line. * * \param line Interrupt line/id to register a handler for * \param handler Handler to register * \param data Data value passed to the handler */ void or1k_interrupt_handler_add(uint32_t line, or1k_interrupt_handler_fptr handler, void* data); /*! * Enable interrupts from a given line * * Unmask the given interrupt line. It is also important to enable interrupts * in general, e.g., using or1k_interrupts_enable(). * * \param line Interrupt line to enable */ void or1k_interrupt_enable(int line); /*! * Disable interrupts from a given line * * Mask given interrupt line. It can be unmasked using or1k_interrupt_enable(). * * \param line Interrupt line to disable */ void or1k_interrupt_disable(int line); /*! * Disable interrupts * * This disables the interrupt exception. This is sufficient to disable all * interrupts. It does not change the mask register (which is modified using * or1k_interrupt_enable() and or1k_interrupt_disable()). * * The interrupt exception can be enabled using or1k_interrupts_enable(). * * Finally, the status of the interrupt exception enable flag is returned by * this function. That allows to call this function even if interrupts are * already disabled. To restore the value of the interrupt exception enable * flag, use the or1k_interrupts_restore() function. That way you avoid to * accidentally enable interrupts. Example: * * \code * void f() { * uint32_t interrupt_status = or1k_interrupts_disable(); * // do something * or1k_interrupts_restore(status); * } * \endcode * * This code will preserve the original status of the interrupt enable flag. * * \return Interrupt exception enable flag before call */ uint32_t or1k_interrupts_disable(void); /*! * Enable interrupt exception * * Enable the interrupt exception. Beside the interrupt exception, it is also * necessary to enable the individual interrupt lines using * or1k_interrupt_enable(). * * You should avoid using this function together with or1k_interrupts_disable() * to guard atomic blocks as it unconditionally enables the interrupt * exception (see documentation of or1k_interrupts_disable()). */ void or1k_interrupts_enable(void); /*! * Restore interrupt exception enable flag * * This function restores the given status to the processor. * or1k_interrupts_restore(0) is identical to or1k_interrupts_disable() and * or1k_interrupts_restore(SPR_SR_IEE) is identical to or1k_interrupts_enable(). * * It is for example used to guard an atomic block and restore the original * status of the interrupt exception enable flag as returned by * or1k_interrupts_disable(). See the documentation of or1k_interrupts_disable() * for a usage example. * * \param status Status of the flag to restore */ void or1k_interrupts_restore(uint32_t status); /*! * Disable timer and interrupt exception * * This function disables the timer and interrupt exception to guard critical * sections. It returns the status of the enable bits before the critical * section, that is restored with or1k_critical_end(). * * Example: * \code * ... * uint32_t status = or1k_critical_start(); * // critical part * or1k_critical_end(status); * ... * \endcode * * \return Status of timer and interrupt exception at time of call */ uint32_t or1k_critical_begin(); /*! * Enable timer and interrupt exception * * Restore the timer and interrupt exception enable. The restore value is the * return value from or1k_critical_start(). * * \param restore Interrupt and timer exception enable restore value */ void or1k_critical_end(uint32_t restore); /*! * @} */ /*! * \defgroup or1k_exception Exception handling * @{ */ /*! Function pointer to an exception handler function */ typedef void (*or1k_exception_handler_fptr)(void); /*! * Register exception handler * * Register an exception handler for the given exception id. This handler is * in the following then called when the exception occurs. You can thereby * individually handle those exceptions. * * \param id Exception id * \param handler Handler callback */ void or1k_exception_handler_add(int id, or1k_exception_handler_fptr handler); /*! * @} */ /*! * \defgroup or1k_spr SPR access * @{ */ /*! * Move value to special purpose register * * Move data value to a special purpose register * * \param spr SPR identifier, see spr-defs.h for macros * \param value value to move to SPR */ static inline void or1k_mtspr (uint32_t spr, uint32_t value) { __asm__ __volatile__ ("l.mtspr\t\t%0,%1,0": : "r" (spr), "r" (value)); } /*! * Copy value from special purpose register * * Copy a data value from the given special purpose register. * * \param spr SPR identifier, see spr-defs.h for macros * \return SPR data value */ static inline uint32_t or1k_mfspr (uint32_t spr) { uint32_t value; __asm__ __volatile__ ("l.mfspr\t\t%0,%1,0" : "=r" (value) : "r" (spr)); return value; } /*! * @} */ /*! * \defgroup or1k_util Miscellaneous utility functions * * @{ */ /*! * Report value to simulator * * Uses the built-in simulator functionality. * * \param value Value to report */ void or1k_report (unsigned long int value); /*! * Get (pseudo) random number * * This should return pseudo-random numbers, based on a Galois LFSR. * * \return (Pseudo) Random number */ unsigned long int or1k_rand(void); /*! * Register UART callback * * This function sets a callback function that is called when a character is * received via UART. The callback function has no return and a gets the * character as parameter. When a character is received, the function is called. * * Example (UART echo): * \code * void uart_in(char c) { * printf("%c", c); // Echo * } * * int main() { * or1k_uart_set_read_cb(&uart_in); * or1k_interrupts_enable(); * * while (1) {} * } * \endcode */ void or1k_uart_set_read_cb(void (*cb)(char c)); /*! * @} */ /*! * \defgroup or1k_cache Cache control * * @{ */ /*! * Enable instruction cache */ void or1k_icache_enable(void); /*! * Disable instruction cache */ void or1k_icache_disable(void); /*! * Flush instruction cache * * Invalidate instruction cache entry * * \param entry Entry to invalidate */ void or1k_icache_flush(uint32_t entry); /*! * Enable data cache */ void or1k_dcache_enable(void); /*! * Disable data cache */ void or1k_dcache_disable(void); /*! * Flush data cache * * Invalidate data cache entry * * \param entry Entry to invalidate */ void or1k_dcache_flush(unsigned long entry); /*! * @} */ /*! * \defgroup or1k_mmu MMU control * @{ */ /*! * Enable instruction MMU */ void or1k_immu_enable(void); /*! * Disable instruction MMU */ void or1k_immu_disable(void); /*! * Enable data MMU */ void or1k_dmmu_enable(void); /*! * Disable data MMU */ void or1k_dmmu_disable(void); /*! * @} */ /*! * \defgroup or1k_timer Timer control * * The tick timer can be used for time measurement, operating system scheduling * etc. By default it is initialized to continuously count the ticks of a * certain period after calling or1k_timer_init(). The period can later be * changed using or1k_timer_set_period(). * * The timer is controlled using or1k_timer_enable(), or1k_timer_disable(), * or1k_timer_restore(), or1k_timer_pause(). After initialization it is required * to enable the timer the first time using or1k_timer_enable(). * or1k_timer_disable() only disables the tick timer interrupts, it does not * disable the timer counting. If you plan to use a pair of or1k_timer_disable() * and or1k_timer_enable() to protect sections of your code against interrupts * you should use or1k_timer_disable() and or1k_timer_restore(), as it may be * possible that the timer interrupt was not enabled before disabling it, * enable would then start it unconditionally. or1k_timer_pause() pauses the * counting. * * In the default mode you can get the tick value using or1k_timer_get_ticks() * and reset this value using or1k_timer_reset_ticks(). * * Example for using the default mode: * * \code * int main() { * uint32_t ticks = 0; * uint32_t timerstate; * or1k_timer_init(100); * or1k_timer_enable(); * while (1) { * while (ticks == or1k_timer_get_ticks()) { } * timerstate = or1k_timer_disable(); * // do something atomar * or1k_timer_restore(timerstate); * if (ticks == 100) { * printf("A second elapsed\n"); * or1k_timer_reset_ticks(); * ticks = 0; * } * } * } * \endcode * * It is possible to change the mode of the tick timer using * or1k_timer_set_mode(). Allowed values are the correct bit pattern (including * the bit positions) for the TTMR register, it is recommended to use the macros * defined in spr-defs.h. For example, implementing an operating system with * scheduling decisions of varying duration favors the implementation of single * run tick timer. Here, each quantum is started before leaving the operating * system kernel. The counter can be restarted with or1k_timer_reset(). * Example: * * \code * void tick_handler(void) { * // Make schedule decision * // and set new thread * or1k_timer_reset(); * // End of exception, new thread will run * } * * int main() { * // Configure operating system and start threads.. * * // Configure timer * or1k_timer_init(50); * or1k_timer_set_handler(&tick_handler); * or1k_timer_set_mode(SPR_TTMR_SR); * or1k_timer_enable(); * * // Schedule first thread and die.. * } * \endcode * * @{ */ /*! * Initialize tick timer * * This initializes the tick timer in default mode (see \ref or1k_timer for * details). * * \param hz Initial period of the tick timer * \return 0 if successful, -1 if timer not present */ int or1k_timer_init(unsigned int hz); /*! * Set period of timer * * Set the period of the timer to a value in Hz. The frequency from the board * support package is used to determine the match value. */ void or1k_timer_set_period(uint32_t hz); /*! * Replace the timer interrupt handler * * By default the tick timer is used to handle timer ticks. The user can replace * this with an own handler for example when implementing an operating system. * * \param handler The callback function pointer to the handler */ void or1k_timer_set_handler(void (*handler)(void)); /*! * Set timer mode * * The timer has different modes (see architecture manual). The default is to * automatically restart counting (SPR_TTMR_RT), others are single run * (SPR_TTMR_SR) and continuous run (SPR_TTMR_CR). * * \param mode a valid mode (use definitions from spr-defs.h as it is important * that those are also at the correct position in the bit field!) */ void or1k_timer_set_mode(uint32_t mode); /*! * Enable timer interrupt * * Enable the timer interrupt exception, independent of the status before. * If you want to enable the timer conditionally, for example to implement a * non-interruptible sequence of code, you should use or1k_timer_restore(). See * the description of or1k_timer_disable() for more details. * * The enable will also restore the mode if the timer was paused previously. */ void or1k_timer_enable(void); /*! * Disable timer interrupt * * This disables the timer interrupt exception and returns the state of the * interrupt exception enable flag before the call. This can be used with * or1k_timer_restore() to implement sequences of code that are not allowed to * be interrupted. Using or1k_timer_enable() will unconditionally enable the * interrupt independent of the state before calling or1k_timer_disable(). * For an example see \ref or1k_timer. * * \return Status of timer interrupt before call */ uint32_t or1k_timer_disable(void); /*! * Restore timer interrupt exception flag * * Restores the timer interrupt exception flag as returned by * or1k_timer_disable(). See the description of or1k_timer_disable() and \ref * or1k_timer for details and an example. * * \param sr_tee Status of timer interrupt */ void or1k_timer_restore(uint32_t sr_tee); /*! * Pause timer counter * * Pauses the counter of the tick timer. The counter will hold its current value * and it can be started again with or1k_timer_enable() which will restore the * configured mode. */ void or1k_timer_pause(void); /*! * Reset timer counter */ void or1k_timer_reset(void); /*! * Get timer ticks * * Get the global ticks of the default configuration. This will increment the * tick counter according to the preconfigured period. * * \return Current value of ticks */ unsigned long or1k_timer_get_ticks(void); /*! * Reset timer ticks * * Resets the timer ticks in default configuration to 0. */ void or1k_timer_reset_ticks(void); /*! * @} */ /*! * \defgroup or1k_multicore Multicore and Synchronization Support * * @{ */ /*! * Compiled with multicore support * * \return 1 if compiled with multicore support, 0 otherwise */ uint32_t or1k_has_multicore_support(void); /*! * Read core identifier * * \return Core identifier */ uint32_t or1k_coreid(void); /*! * Read number of cores * * \return Total number of cores */ uint32_t or1k_numcores(void); /*! * Load linked * * Load a value from the given address and link it. If the following * or1k_sync_sc() goes to the same address and there was no conflicting access * between loading and storing, the value is written back, else the write fails. * * \param address Address to load value from * \return Value read from the address */ uint32_t or1k_sync_ll(void *address); /** * Store conditional * * Conditionally store a value to the address. The address must have been read * before using or1k_sync_ll() and there must be no other load link after that, * otherwise this will always fail. In case there was no other write to the same * address in between the load link and the store conditional, the store is * successful, otherwise it will also fail. * * \param address Address to conditionally store to * \param value Value to write to address * \return 1 if success, 0 if fail */ int or1k_sync_sc(void *address, uint32_t value); /*! * Compare and Swap * * Loads a data item from the memory and compares a given value to it. If the * values match, a new value is written to the memory, if they mismatch, the * operation is aborted. The whole operation is atomic, i.e., it is guaranteed * that no other core changes the value between the read and the write. * * \param address Address to operate on * \param compare Compare value * \param swap New value to write * \return The value read from memory (can be used to check for success) */ uint32_t or1k_sync_cas(void *address, uint32_t compare, uint32_t swap); /*! * Test and Set Lock * * Check for a lock on an address. This means, if there is 0 at an address it * will overwrite it with 1 and return 0. If the lock was already set (value * 1 read from address), the function returns 1. The operation is atomic. * * \param address Address of the lock * \return 0 if success, 1 if failed */ int or1k_sync_tsl(void *address); /*! * @} */ #endif /* __NEWLIB_OR1K_SUPPORT_H__ */