/* * FreeRTOS Kernel V10.3.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * http://www.FreeRTOS.org * http://aws.amazon.com/freertos * */ /* * Changes from V1.00: * + Call to taskYIELD() from within tick ISR has been replaced by the more + efficient portSWITCH_CONTEXT(). + ISR function definitions renamed to include the prv prefix. + + Changes from V1.2.0: + + portRESET_PIC() is now called last thing before the end of the preemptive + tick routine. + + Changes from V2.6.1 + + Replaced the sUsingPreemption variable with the configUSE_PREEMPTION + macro to be consistent with the later ports. */ /*----------------------------------------------------------- * Implementation of functions defined in portable.h for the Flashlite 186 * port. *----------------------------------------------------------*/ #include #include #include #include #include "FreeRTOS.h" #include "task.h" #include "portasm.h" /*lint -e950 Non ANSI reserved words okay in this file only. */ #define portTIMER_EOI_TYPE ( 8 ) #define portRESET_PIC() portOUTPUT_WORD( ( uint16_t ) 0xff22, portTIMER_EOI_TYPE ) #define portTIMER_INT_NUMBER 0x12 #define portTIMER_1_CONTROL_REGISTER ( ( uint16_t ) 0xff5e ) #define portTIMER_0_CONTROL_REGISTER ( ( uint16_t ) 0xff56 ) #define portTIMER_INTERRUPT_ENABLE ( ( uint16_t ) 0x2000 ) /* Setup the hardware to generate the required tick frequency. */ static void prvSetTickFrequency( uint32_t ulTickRateHz ); /* Set the hardware back to the state as per before the scheduler started. */ static void prvExitFunction( void ); #if configUSE_PREEMPTION == 1 /* Tick service routine used by the scheduler when preemptive scheduling is * being used. */ static void __interrupt __far prvPreemptiveTick( void ); #else /* Tick service routine used by the scheduler when cooperative scheduling is * being used. */ static void __interrupt __far prvNonPreemptiveTick( void ); #endif /* Trap routine used by taskYIELD() to manually cause a context switch. */ static void __interrupt __far prvYieldProcessor( void ); /*lint -e956 File scopes necessary here. */ /* Set true when the vectors are set so the scheduler will service the tick. */ static int16_t sSchedulerRunning = pdFALSE; /* Points to the original routine installed on the vector we use for manual context switches. This is then used to restore the original routine during prvExitFunction(). */ static void( __interrupt __far * pxOldSwitchISR )(); /* Used to restore the original DOS context when the scheduler is ended. */ static jmp_buf xJumpBuf; /*lint +e956 */ /*-----------------------------------------------------------*/ BaseType_t xPortStartScheduler( void ) { /* This is called with interrupts already disabled. */ /* Remember what was on the interrupts we are going to use * so we can put them back later if required. */ pxOldSwitchISR = _dos_getvect( portSWITCH_INT_NUMBER ); /* Put our manual switch (yield) function on a known * vector. */ _dos_setvect( portSWITCH_INT_NUMBER, prvYieldProcessor ); #if configUSE_PREEMPTION == 1 { /* Put our tick switch function on the timer interrupt. */ _dos_setvect( portTIMER_INT_NUMBER, prvPreemptiveTick ); } #else { /* We want the timer interrupt to just increment the tick count. */ _dos_setvect( portTIMER_INT_NUMBER, prvNonPreemptiveTick ); } #endif prvSetTickFrequency( configTICK_RATE_HZ ); /* Clean up function if we want to return to DOS. */ if( setjmp( xJumpBuf ) != 0 ) { prvExitFunction(); sSchedulerRunning = pdFALSE; } else { sSchedulerRunning = pdTRUE; /* Kick off the scheduler by setting up the context of the first task. */ portFIRST_CONTEXT(); } return sSchedulerRunning; } /*-----------------------------------------------------------*/ /* The tick ISR used depend on whether or not the preemptive or cooperative * kernel is being used. */ #if configUSE_PREEMPTION == 1 static void __interrupt __far prvPreemptiveTick( void ) { /* Get the scheduler to update the task states following the tick. */ if( xTaskIncrementTick() != pdFALSE ) { /* Switch in the context of the next task to be run. */ portSWITCH_CONTEXT(); } /* Reset the PIC ready for the next time. */ portRESET_PIC(); } #else /* if configUSE_PREEMPTION == 1 */ static void __interrupt __far prvNonPreemptiveTick( void ) { /* Same as preemptive tick, but the cooperative scheduler is being used * so we don't have to switch in the context of the next task. */ xTaskIncrementTick(); portRESET_PIC(); } #endif /* if configUSE_PREEMPTION == 1 */ /*-----------------------------------------------------------*/ static void __interrupt __far prvYieldProcessor( void ) { /* Switch in the context of the next task to be run. */ portSWITCH_CONTEXT(); } /*-----------------------------------------------------------*/ void vPortEndScheduler( void ) { /* Jump back to the processor state prior to starting the * scheduler. This means we are not going to be using a * task stack frame so the task can be deleted. */ longjmp( xJumpBuf, 1 ); } /*-----------------------------------------------------------*/ static void prvExitFunction( void ) { const uint16_t usTimerDisable = 0x0000; uint16_t usTimer0Control; /* Interrupts should be disabled here anyway - but no * harm in making sure. */ portDISABLE_INTERRUPTS(); if( sSchedulerRunning == pdTRUE ) { /* Put back the switch interrupt routines that was in place * before the scheduler started. */ _dos_setvect( portSWITCH_INT_NUMBER, pxOldSwitchISR ); } /* Disable the timer used for the tick to ensure the scheduler is * not called before restoring interrupts. There was previously nothing * on this timer so there is no old ISR to restore. */ portOUTPUT_WORD( portTIMER_1_CONTROL_REGISTER, usTimerDisable ); /* Restart the DOS tick. */ usTimer0Control = portINPUT_WORD( portTIMER_0_CONTROL_REGISTER ); usTimer0Control |= portTIMER_INTERRUPT_ENABLE; portOUTPUT_WORD( portTIMER_0_CONTROL_REGISTER, usTimer0Control ); portENABLE_INTERRUPTS(); } /*-----------------------------------------------------------*/ static void prvSetTickFrequency( uint32_t ulTickRateHz ) { const uint16_t usMaxCountRegister = 0xff5a; const uint16_t usTimerPriorityRegister = 0xff32; const uint16_t usTimerEnable = 0xC000; const uint16_t usRetrigger = 0x0001; const uint16_t usTimerHighPriority = 0x0000; uint16_t usTimer0Control; /* ( CPU frequency / 4 ) / clock 2 max count [inpw( 0xff62 ) = 7] */ const uint32_t ulClockFrequency = 0x7f31a0; uint32_t ulTimerCount = ulClockFrequency / ulTickRateHz; portOUTPUT_WORD( portTIMER_1_CONTROL_REGISTER, usTimerEnable | portTIMER_INTERRUPT_ENABLE | usRetrigger ); portOUTPUT_WORD( usMaxCountRegister, ( uint16_t ) ulTimerCount ); portOUTPUT_WORD( usTimerPriorityRegister, usTimerHighPriority ); /* Stop the DOS tick - don't do this if you want to maintain a TOD clock. */ usTimer0Control = portINPUT_WORD( portTIMER_0_CONTROL_REGISTER ); usTimer0Control &= ~portTIMER_INTERRUPT_ENABLE; portOUTPUT_WORD( portTIMER_0_CONTROL_REGISTER, usTimer0Control ); } /*lint +e950 */