| /* Copyright (c) 2014 Authors |
| * |
| * Contributor Julius Baxter <julius.baxter@orsoc.se> |
| * Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de> |
| * |
| * 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 <stdint.h> |
| |
| #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__ */ |