URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [or1ksim/] [testsuite/] [test-code-or1k/] [tick/] [tick.c] - Rev 352
Go to most recent revision | Compare with Previous | Blame | View Log
/* tick.c. Test of Or1ksim tick timer Copyright (C) 2005 György `nog' Jeney <nog@sdf.lonestar.org> Copyright (C) 2010 Embecosm Limited Contributor György `nog' Jeney <nog@sdf.lonestar.org> Contributor Jeremy Bennett <jeremy.bennett@embecosm.com> This file is part of OpenRISC 1000 Architectural Simulator. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http: www.gnu.org/licenses/>. */ /* ---------------------------------------------------------------------------- This code is commented throughout for use with Doxygen. --------------------------------------------------------------------------*/ #include "spr-defs.h" #include "support.h" /*! Number of spurious interrupts we'll allow before properly disabling them */ #define MAX_SPURIOUS 5 /*! Convenience macro to check that a test has passed */ #define ASSERT(x, msg) ((x) ? printf ("Test succeeded %s\n", msg) : fail (msg)) /*! Convenience macro to check that a test has passed, but only print a message on failure. */ #define SILENT_ASSERT(x, msg) ((x) ? : fail (msg)) /*! Convenience macros for accessing SPRs */ #define GET_TTMR() (mfspr (SPR_TTMR)) #define SET_TTMR(x) (mtspr (SPR_TTMR, x)) #define GET_TTCR() (mfspr (SPR_TTCR)) #define SET_TTCR(x) (mtspr (SPR_TTCR, x)) /*! Global count of number of times interrupt handler has been called */ static volatile int tick_cnt = 0; /*! Global flag to indicate if the TTMR_IP flag should be cleared */ static int clear_ip = 1; /* --------------------------------------------------------------------------*/ /*!Report failure Terminate execution. @param[in] msg Description of test which failed. */ /* --------------------------------------------------------------------------*/ static void fail (char *msg) { printf ("Test failed: %s\n", msg); report (0xeeeeeeee); exit (1); } /* fail () */ /* --------------------------------------------------------------------------*/ /*!Set a tick timer mode. All other bits and the period are masked @param[in] ttmr The entire TTMR value. @param[in] mode The new mode @return The new TTMR with the mode set */ /* --------------------------------------------------------------------------*/ static unsigned long int set_mode (unsigned long int ttmr, unsigned long int mode) { ttmr &= ~SPR_TTMR_M; ttmr |= mode & SPR_TTMR_M; return ttmr; } /* set_mode () */ /* --------------------------------------------------------------------------*/ /*!Set a tick timer period. All other bits and the period are masked @param[in] ttmr The entire TTMR value. @param[in] period The new period @return The new TTMR with the period set */ /* --------------------------------------------------------------------------*/ static unsigned long int set_period (unsigned long int ttmr, unsigned long int period) { ttmr &= ~SPR_TTMR_PERIOD; ttmr |= period & SPR_TTMR_PERIOD; return ttmr; } /* set_period () */ /* --------------------------------------------------------------------------*/ /*!Clear the mode register Period is zeroed, interrupt pending and enabled flags are cleared, disabled mode is set. @note This function exists to allow for the disabled mode to be explicit, rather than assumed to be zero. @return The new TTMR */ /* --------------------------------------------------------------------------*/ static unsigned long int clear_ttmr () { unsigned long int ttmr = SPR_TTMR_DI; SET_TTMR (ttmr); return ttmr; } /* clear_ttmr () */ /* --------------------------------------------------------------------------*/ /*!Clear the count register Count is zeroed @return The new TTCR */ /* --------------------------------------------------------------------------*/ static unsigned long int clear_ttcr () { unsigned long int ttcr = 0; SET_TTCR (ttcr); return ttcr; } /* clear_ttcr () */ /* --------------------------------------------------------------------------*/ /*!Set a new timer. Convenience function for a common sequence @param[in] period The period of the timer @param[in] mode The timer mode @param[in] flags Any falgs to set (usually IE) @return The new value of TTMR */ /* --------------------------------------------------------------------------*/ static unsigned long int new_timer (unsigned long int period, unsigned long int mode, unsigned long int flags) { unsigned long int ttmr; ttmr = 0; ttmr = set_period (ttmr, period); ttmr = set_mode (ttmr, mode); ttmr |= flags; SET_TTMR (ttmr); return ttmr; } /* new_timer () */ /* --------------------------------------------------------------------------*/ /*!Standard tick interrupt handler Up the count and clear the interrupt appropriately */ /* --------------------------------------------------------------------------*/ static void tick_int (void) { unsigned long int ttmr = mfspr (SPR_TTMR); /* Make sure that the tick timer interrupt pending bit is set */ SILENT_ASSERT (0 != (ttmr & SPR_TTMR_IP), "IP bit not set in normal interrupt handler"); /* One more interrupt handled */ tick_cnt++; /* Clear interrupt (Write a 0 to SPR_TTMR_IP) If we programmed a one-shot timer, make sure to disable the interrupts, else we'd get a spurious interrupt */ if (SPR_TTMR_SR == (ttmr & SPR_TTMR_M)) { ttmr &= ~(SPR_TTMR_IP | SPR_TTMR_IE); } else { ttmr &= ~SPR_TTMR_IP; } mtspr (SPR_TTMR, ttmr); } /* tick_count () */ /* --------------------------------------------------------------------------*/ /*!Tick interrupt handler generting spurious interrupts If we have a one-shot timer set, then when we clear the interrupt (if a global flag allows), but leave the interrupt enabled we should get a spurious interrupt. Allow this to happen MAX_SPURIOUS times before disabling the interrupt. Up the count and clear the interrupt appropriately */ /* --------------------------------------------------------------------------*/ static void tick_int_spurious (void) { unsigned long int ttmr = mfspr(SPR_TTMR); /* Make sure that the tick timer interrupt pending bit is set */ SILENT_ASSERT (0 != (ttmr & SPR_TTMR_IP), "IP bit not set in spurious interrupt handler"); /* Clear interrupt if global flag allows it (Write a 0 to SPR_TTMR_IP) */ if (clear_ip) { ttmr &= ~SPR_TTMR_IP; mtspr (SPR_TTMR, ttmr); } /* Allow MAX_SPURIOUS spurious spurious interrupt */ if (++tick_cnt == MAX_SPURIOUS) { ttmr &= ~SPR_TTMR_IE; mtspr (SPR_TTMR, ttmr); } } /* tick_int_spurious () */ /* --------------------------------------------------------------------------*/ /*!Waste a little time We are waiting for TTCR to increment @note This is an entirely arbitrary period. In particular excessive use of printf in an interrupt handler can tie up cycles and cause TTCR not to increment in time. */ /* --------------------------------------------------------------------------*/ static void waste_time (void) { int i; volatile int x; for (i = 0; i < 50; i++) { x = i; } } /* waste_time () */ /* --------------------------------------------------------------------------*/ /*!Wait for a tick timer exception This occurs when the tick count goes up. Reset the count once the exception is received. */ /* --------------------------------------------------------------------------*/ /* Waits for a tick timer exception */ static void wait_match (void) { while (!tick_cnt) { } tick_cnt = 0; } /* wait_match () */ /* --------------------------------------------------------------------------*/ /*!Main program testing tick timer Tests all aspecst of tick timer behavior. @return Return code from the program (always zero). */ /* --------------------------------------------------------------------------*/ int main() { unsigned long int ttmr; unsigned long int ttcr; /* Use normal interrupt handler */ excpt_tick = (unsigned long)tick_int; /* Enable tick interrupt */ mtspr (SPR_SR, mfspr (SPR_SR) | SPR_SR_TEE); /* Clear interrupt pending and interrupt enabled, zero the period, set disabled mode and set count to zero. */ ttmr = clear_ttmr (); ttcr = clear_ttcr (); /* Waste some time to check if the timer is really disabled */ waste_time (); /* Timer is disabled and shouldn't count, TTCR should still be 0 */ ttcr = GET_TTCR (); ASSERT (0 == ttcr, "Tick timer not counting while disabled"); /* Start timer in continous running mode. Enable timer interrupt and set * the period to 0x100 */ ttmr = new_timer (0x100, SPR_TTMR_CR, SPR_TTMR_IE); /* Wait for the timer to count up to the match value, get the count register value. Then waste some time and check that couting has continued. */ wait_match (); ttcr = GET_TTCR (); waste_time(); /* The timer should have kept counting and our saved TTCR should not be the * same as the current ttcr */ ASSERT (ttcr < GET_TTCR (), "Tick timer kept counting during continuous mode"); /* Clear the mode register flags, zero the period and set disabled mode. Then get the count register which should be unaffected by this. */ ttmr = clear_ttmr (); ttcr = GET_TTCR (); /* Restart the timer in continous run mode and the counter will keep going. There should be no interrupts, since we haven't enabled them. Waste some time to allow the counter to advance. */ ttmr = set_mode (ttmr, SPR_TTMR_CR); SET_TTMR (ttmr); waste_time (); /* The timer should have carried on from what was SPR_TTCR when we started it. */ ASSERT (ttcr < GET_TTCR (), "Tick timer continued counting after restart"); /* Disable the timer and waste some time to check that the count does not advance */ ttmr = clear_ttmr (); ttcr = GET_TTCR (); waste_time (); /* Timer should be disabled and should not have counted */ ASSERT(ttcr == GET_TTCR (), "Tick timer counter stops when disabled"); /* Start in single run mode with a count of 0x100. Run until the match is hit, then check that the counter does not advance further while wasting time. */ ttcr = clear_ttcr (); ttmr = new_timer (0x100, SPR_TTMR_SR, SPR_TTMR_IE); wait_match(); ttcr = GET_TTCR (); waste_time(); /* The timer should have stoped and the counter advanced no further. */ ASSERT (ttcr == GET_TTCR (), "Timer stopped after match"); /* The counter should still indicate single run mode */ ttmr = GET_TTMR (); ASSERT ((ttmr & SPR_TTMR_SR) == SPR_TTMR_SR, "Timer still indicating one-shot mode after match"); /* Disable the timer, then start auto-restarting timer every 0x1000 ticks. */ ttmr = clear_ttmr (); ttcr = clear_ttcr (); ttmr = new_timer (0x1000, SPR_TTMR_RT, SPR_TTMR_IE); /* Wait for two matches, then disable the timer. If this doesn't work the test will time out, so no ASSERT here. */ wait_match(); ttcr = GET_TTCR (); wait_match(); /* Start a one-shot counter but keep interrupts disabled */ ttmr = clear_ttmr (); ttcr = clear_ttcr (); ttmr = new_timer (0x100, SPR_TTMR_SR, 0); /* Wait for the counter to stop */ while (GET_TTCR () != 0x100) { } /* Make sure the counter has actually stopped and there have been no more interrupts (neither tick count nor pending flag. */ waste_time(); ttmr = GET_TTMR (); ASSERT (GET_TTCR () == 0x100, "One-shot timer stopped"); ASSERT (tick_cnt == 0, "No more interrupts after one-shot timer"); ASSERT (SPR_TTMR_IP != (ttmr & SPR_TTMR_IP), "IP flag not set after one-shot timer"); /* Start a perpetual counter but with no interrupts enabled while it is still counting. */ ttmr = clear_ttmr (); ttcr = clear_ttcr (); ttmr = new_timer (0x100, SPR_TTMR_CR, 0); /* Wait until it reaches its count. */ while(GET_TTCR () < 0x100) { } /* Waste some time and check the counter has carried on past its count and that there have bee no more interrupts nor interrupts pending. */ waste_time(); ttmr = GET_TTMR (); ttcr = GET_TTCR (); ASSERT (ttcr > 0x100, "Perptual timer kept counting"); ASSERT (tick_cnt == 0, "No more interrupts during perpetual timer count"); ASSERT (SPR_TTMR_IP != (ttmr & SPR_TTMR_IP), "IP flag not set during perpetual timer count"); /* Disable the timer interrupt */ mtspr (SPR_SR, mfspr (SPR_SR) & ~SPR_SR_TEE); /* Set a one-shot timer, with the counter started at zero. */ ttmr = clear_ttmr (); ttcr = clear_ttcr (); ttmr = new_timer (0x100, SPR_TTMR_SR, SPR_TTMR_IE); /* Wait for the interrupt pending bit to be set. */ do { ttmr = GET_TTMR (); } while (0 == (ttmr & SPR_TTMR_IP)); /* Give some time for a potential interrupt to occur */ waste_time(); /* No interrupt should have occured */ ASSERT (tick_cnt == 0, "No interrupt when tick timer disabled"); /* Enable tick interrupt */ mtspr (SPR_SR, mfspr (SPR_SR) | SPR_SR_TEE); /* Test Setting TTCR while counting. Set a period of 0x3000 but do not enable interrupts. */ ttmr = clear_ttmr (); ttcr = clear_ttcr (); ttmr = new_timer (0x3000, SPR_TTMR_CR, 0); /* Wait for the count to reach 10 times our period. */ while (GET_TTCR () < 0x30000) { } /* Waste some time and then reset the count to 0x50 */ waste_time(); SET_TTCR (0x50); /* Verify that after a short wait we have counted to more than 0x50, but less than 0x30000 */ waste_time(); ttcr = GET_TTCR (); ASSERT ((0x50 < ttcr) && (ttcr < 0x30000), "TTCR reset while counting"); /* Disable the timer. Set the counter to a high value and start a single run timer with a low timer period. Demonstrate the counter wraps round and then triggers at the period. Need to reset the tick counter, since there may have been an interrupt during the previous period. */ ttmr = clear_ttmr (); ttcr = 0x20000; SET_TTCR (ttcr); ttmr = new_timer (0x100, SPR_TTMR_SR, SPR_TTMR_IE); /* The counter should start counting from 0x20000 and wrap around to 0x100 * causeing an interrupt. Check we keep on counting. */ waste_time(); ASSERT (GET_TTCR () > 0x20000, "Timer started counting from high value"); /* If TTCR is greater than TTMR_PERIOD then the interrupt gets delivered after TTCR wraps around to 0 and counts to SPR_TTMR_PERIOD. Set up an auto-restart timer to wrap around. Reset the tick count, because it may have incremented since the last match. */ ttmr = clear_ttmr (); ttcr = 0xffffc00; SET_TTCR (ttcr); tick_cnt = 0; ttmr = new_timer (0x10000, SPR_TTMR_RT, SPR_TTMR_IE); /* Wait for the trigger, then check that we have restarted. */ wait_match(); ttcr = GET_TTCR (); ASSERT (ttcr < 0x10000, "Auto-restart wrapped round"); /* Test wrap around in continuous mode. Reset the tick count in case we have had another interrupt. */ ttmr = clear_ttmr (); ttcr = 0xffffc00; SET_TTCR (ttcr); tick_cnt = 0; ttmr = new_timer (0x10000, SPR_TTMR_CR, SPR_TTMR_IE); /* Wait for trigger, then check that we have carried on counting. */ wait_match(); ttcr = GET_TTCR () & SPR_TTCR_PERIOD; ASSERT ((0x10000 < ttcr) && (ttcr < 0xffffc00), "Continuous mode wrapped round"); /* Disable the timer and set up the special spurious interrupt handler, to check spurious interrupts occur as expected. */ ttmr = clear_ttmr (); excpt_tick = (unsigned long)tick_int_spurious; /* Set up a disabled timer with a period of 0x100. */ clear_ttcr (); ttmr = new_timer (0x100, SPR_TTMR_DI, SPR_TTMR_IE); /* Set up the count to match the period, and check that spurious interrupts are generated, even though the timer is disabled. */ ttcr = 0x100; SET_TTCR (ttcr); while(tick_cnt != MAX_SPURIOUS) { } /* Check the count has not changed */ ttcr = GET_TTCR (); ASSERT (0x100 == ttcr, "Spurious interrupts handled with matching period"); /* Reset the tick count, then test setting TTCR first then TTMR */ tick_cnt = 0; ttcr = 0x101; SET_TTCR (ttcr); ttmr = new_timer (0x101, SPR_TTMR_DI, SPR_TTMR_IE); while(tick_cnt != MAX_SPURIOUS) { } ttcr = GET_TTCR (); ASSERT (0x101 == ttcr, "Spurious interrupts handled after TTCR and TTMR"); /* Set countinous counter, but make sure we never clear the TTMR_IP bit */ tick_cnt = 0; clear_ip = 0; ttcr = clear_ttcr (); ttmr = new_timer (0x100, SPR_TTMR_CR, SPR_TTMR_IE); while(tick_cnt != MAX_SPURIOUS) { } /* If we get here everything worked. */ report(0xdeaddead); return 0; } /* main () */
Go to most recent revision | Compare with Previous | Blame | View Log