/* | |
* FreeRTOS Kernel <DEVELOPMENT BRANCH> | |
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
* | |
* SPDX-License-Identifier: MIT | |
* | |
* 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. | |
* | |
* https://www.FreeRTOS.org | |
* https://github.com/FreeRTOS | |
* | |
*/ | |
/* | |
Changes from V3.0.0 | |
Changes from V3.0.1 | |
*/ | |
#ifndef PORTMACRO_H | |
#define PORTMACRO_H | |
#if !defined(_SERIES) || _SERIES != 18 | |
#error "WizC supports FreeRTOS on the Microchip PIC18-series only" | |
#endif | |
#if !defined(QUICKCALL) || QUICKCALL != 1 | |
#error "QuickCall must be enabled (see ProjectOptions/Optimisations)" | |
#endif | |
#include <stddef.h> | |
#include <pic.h> | |
#define portCHAR char | |
#define portFLOAT float | |
#define portDOUBLE portFLOAT | |
#define portLONG long | |
#define portSHORT short | |
#define portSTACK_TYPE uint8_t | |
#define portBASE_TYPE char | |
typedef portSTACK_TYPE StackType_t; | |
typedef signed char BaseType_t; | |
typedef unsigned char UBaseType_t; | |
#if( configUSE_16_BIT_TICKS == 1 ) | |
typedef uint16_t TickType_t; | |
#define portMAX_DELAY ( TickType_t ) ( 0xFFFF ) | |
#else | |
typedef uint32_t TickType_t; | |
#define portMAX_DELAY ( TickType_t ) ( 0xFFFFFFFF ) | |
#endif | |
#define portBYTE_ALIGNMENT 1 | |
/*-----------------------------------------------------------*/ | |
/* | |
* Constant used for context switch macro when we require the interrupt | |
* enable state to be forced when the interrupted task is switched back in. | |
*/ | |
#define portINTERRUPTS_FORCED (0x01) | |
/* | |
* Constant used for context switch macro when we require the interrupt | |
* enable state to be unchanged when the interrupted task is switched back in. | |
*/ | |
#define portINTERRUPTS_UNCHANGED (0x00) | |
/* Initial interrupt enable state for newly created tasks. This value is | |
* used when a task switches in for the first time. | |
*/ | |
#define portINTERRUPTS_INITIAL_STATE (portINTERRUPTS_FORCED) | |
/* | |
* Macros to modify the global interrupt enable bit in INTCON. | |
*/ | |
#define portDISABLE_INTERRUPTS() \ | |
do \ | |
{ \ | |
bGIE=0; \ | |
} while(bGIE) // MicroChip recommends this check! | |
#define portENABLE_INTERRUPTS() \ | |
do \ | |
{ \ | |
bGIE=1; \ | |
} while(0) | |
/*-----------------------------------------------------------*/ | |
/* | |
* Critical section macros. | |
*/ | |
extern uint8_t ucCriticalNesting; | |
#define portNO_CRITICAL_SECTION_NESTING ( ( uint8_t ) 0 ) | |
#define portENTER_CRITICAL() \ | |
do \ | |
{ \ | |
portDISABLE_INTERRUPTS(); \ | |
\ | |
/* \ | |
* Now interrupts are disabled ucCriticalNesting \ | |
* can be accessed directly. Increment \ | |
* ucCriticalNesting to keep a count of how \ | |
* many times portENTER_CRITICAL() has been called. \ | |
*/ \ | |
ucCriticalNesting++; \ | |
} while(0) | |
#define portEXIT_CRITICAL() \ | |
do \ | |
{ \ | |
if(ucCriticalNesting > portNO_CRITICAL_SECTION_NESTING) \ | |
{ \ | |
/* \ | |
* Decrement the nesting count as we are leaving a \ | |
* critical section. \ | |
*/ \ | |
ucCriticalNesting--; \ | |
} \ | |
\ | |
/* \ | |
* If the nesting level has reached zero then \ | |
* interrupts should be re-enabled. \ | |
*/ \ | |
if( ucCriticalNesting == portNO_CRITICAL_SECTION_NESTING ) \ | |
{ \ | |
portENABLE_INTERRUPTS(); \ | |
} \ | |
} while(0) | |
/*-----------------------------------------------------------*/ | |
/* | |
* The minimal stacksize is calculated on the first reference of | |
* portMINIMAL_STACK_SIZE. Some input to this calculation is | |
* compiletime determined, other input is port-defined (see port.c) | |
*/ | |
extern uint16_t usPortCALCULATE_MINIMAL_STACK_SIZE( void ); | |
extern uint16_t usCalcMinStackSize; | |
#define portMINIMAL_STACK_SIZE \ | |
((usCalcMinStackSize == 0) \ | |
? usPortCALCULATE_MINIMAL_STACK_SIZE() \ | |
: usCalcMinStackSize ) | |
/* | |
* WizC uses a downgrowing stack | |
*/ | |
#define portSTACK_GROWTH ( -1 ) | |
/*-----------------------------------------------------------*/ | |
/* | |
* Macro's that pushes all the registers that make up the context of a task onto | |
* the stack, then saves the new top of stack into the TCB. TOSU and TBLPTRU | |
* are only saved/restored on devices with more than 64kB (32k Words) ROM. | |
* | |
* The stackpointer is helt by WizC in FSR2 and points to the first free byte. | |
* WizC uses a "downgrowing" stack. There is no framepointer. | |
* | |
* We keep track of the interruptstatus using ucCriticalNesting. When this | |
* value equals zero, interrupts have to be enabled upon exit from the | |
* portRESTORE_CONTEXT macro. | |
* | |
* If this is called from an ISR then the interrupt enable bits must have been | |
* set for the ISR to ever get called. Therefore we want to save | |
* ucCriticalNesting with value zero. This means the interrupts will again be | |
* re-enabled when the interrupted task is switched back in. | |
* | |
* If this is called from a manual context switch (i.e. from a call to yield), | |
* then we want to keep the current value of ucCritialNesting so it is restored | |
* with its current value. This allows a yield from within a critical section. | |
* | |
* The compiler uses some locations at the bottom of RAM for temporary | |
* storage. The compiler may also have been instructed to optimize | |
* function-parameters and local variables to global storage. The compiler | |
* uses an area called LocOpt for this wizC feature. | |
* The total overheadstorage has to be saved in it's entirety as part of | |
* a task context. These macro's store/restore from data address 0x0000 to | |
* (OVERHEADPAGE0-LOCOPTSIZE+MAXLOCOPTSIZE - 1). | |
* OVERHEADPAGE0, LOCOPTSIZE and MAXLOCOPTSIZE are compiler-generated | |
* assembler definitions. | |
*/ | |
#define portSAVE_CONTEXT( ucInterruptForced ) \ | |
do \ | |
{ \ | |
portDISABLE_INTERRUPTS(); \ | |
\ | |
_Pragma("asm") \ | |
; \ | |
; Push the relevant SFR's onto the task's stack \ | |
; \ | |
movff STATUS,POSTDEC2 \ | |
movff WREG,POSTDEC2 \ | |
movff BSR,POSTDEC2 \ | |
movff PRODH,POSTDEC2 \ | |
movff PRODL,POSTDEC2 \ | |
movff FSR0H,POSTDEC2 \ | |
movff FSR0L,POSTDEC2 \ | |
movff FSR1H,POSTDEC2 \ | |
movff FSR1L,POSTDEC2 \ | |
movff TABLAT,POSTDEC2 \ | |
if __ROMSIZE > 0x8000 \ | |
movff TBLPTRU,POSTDEC2 \ | |
endif \ | |
movff TBLPTRH,POSTDEC2 \ | |
movff TBLPTRL,POSTDEC2 \ | |
if __ROMSIZE > 0x8000 \ | |
movff PCLATU,POSTDEC2 \ | |
endif \ | |
movff PCLATH,POSTDEC2 \ | |
; \ | |
; Store the compiler-scratch-area as described above. \ | |
; \ | |
movlw OVERHEADPAGE0-LOCOPTSIZE+MAXLOCOPTSIZE \ | |
clrf FSR0L,ACCESS \ | |
clrf FSR0H,ACCESS \ | |
_rtos_S1: \ | |
movff POSTINC0,POSTDEC2 \ | |
decfsz WREG,W,ACCESS \ | |
SMARTJUMP _rtos_S1 \ | |
; \ | |
; Save the pic call/return-stack belonging to the \ | |
; current task by copying it to the task's software- \ | |
; stack. We save the hardware stack pointer (which \ | |
; is the number of addresses on the stack) in the \ | |
; W-register first because we need it later and it \ | |
; is modified in the save-loop by executing pop's. \ | |
; After the loop the W-register is stored on the \ | |
; stack, too. \ | |
; \ | |
movf STKPTR,W,ACCESS \ | |
bz _rtos_s3 \ | |
_rtos_S2: \ | |
if __ROMSIZE > 0x8000 \ | |
movff TOSU,POSTDEC2 \ | |
endif \ | |
movff TOSH,POSTDEC2 \ | |
movff TOSL,POSTDEC2 \ | |
pop \ | |
tstfsz STKPTR,ACCESS \ | |
SMARTJUMP _rtos_S2 \ | |
_rtos_s3: \ | |
movwf POSTDEC2,ACCESS \ | |
; \ | |
; Next the value for ucCriticalNesting used by the \ | |
; task is stored on the stack. When \ | |
; (ucInterruptForced == portINTERRUPTS_FORCED), we save \ | |
; it as 0 (portNO_CRITICAL_SECTION_NESTING). \ | |
; \ | |
if ucInterruptForced == portINTERRUPTS_FORCED \ | |
clrf POSTDEC2,ACCESS \ | |
else \ | |
movff ucCriticalNesting,POSTDEC2 \ | |
endif \ | |
; \ | |
; Save the new top of the software stack in the TCB. \ | |
; \ | |
movff pxCurrentTCB,FSR0L \ | |
movff pxCurrentTCB+1,FSR0H \ | |
movff FSR2L,POSTINC0 \ | |
movff FSR2H,POSTINC0 \ | |
_Pragma("asmend") \ | |
} while(0) | |
/************************************************************/ | |
/* | |
* This is the reverse of portSAVE_CONTEXT. | |
*/ | |
#define portRESTORE_CONTEXT() \ | |
do \ | |
{ \ | |
_Pragma("asm") \ | |
; \ | |
; Set FSR0 to point to pxCurrentTCB->pxTopOfStack. \ | |
; \ | |
movff pxCurrentTCB,FSR0L \ | |
movff pxCurrentTCB+1,FSR0H \ | |
; \ | |
; De-reference FSR0 to set the address it holds into \ | |
; FSR2 (i.e. *( pxCurrentTCB->pxTopOfStack ) ). FSR2 \ | |
; is used by wizC as stackpointer. \ | |
; \ | |
movff POSTINC0,FSR2L \ | |
movff POSTINC0,FSR2H \ | |
; \ | |
; Next, the value for ucCriticalNesting used by the \ | |
; task is retrieved from the stack. \ | |
; \ | |
movff PREINC2,ucCriticalNesting \ | |
; \ | |
; Rebuild the pic call/return-stack. The number of \ | |
; return addresses is the next item on the task stack. \ | |
; Save this number in PRODL. Then fetch the addresses \ | |
; and store them on the hardwarestack. \ | |
; The datasheets say we can't use movff here... \ | |
; \ | |
movff PREINC2,PRODL // Use PRODL as tempregister \ | |
clrf STKPTR,ACCESS \ | |
_rtos_R1: \ | |
push \ | |
movf PREINC2,W,ACCESS \ | |
movwf TOSL,ACCESS \ | |
movf PREINC2,W,ACCESS \ | |
movwf TOSH,ACCESS \ | |
if __ROMSIZE > 0x8000 \ | |
movf PREINC2,W,ACCESS \ | |
movwf TOSU,ACCESS \ | |
else \ | |
clrf TOSU,ACCESS \ | |
endif \ | |
decfsz PRODL,F,ACCESS \ | |
SMARTJUMP _rtos_R1 \ | |
; \ | |
; Restore the compiler's working storage area to page 0 \ | |
; \ | |
movlw OVERHEADPAGE0-LOCOPTSIZE+MAXLOCOPTSIZE \ | |
movwf FSR0L,ACCESS \ | |
clrf FSR0H,ACCESS \ | |
_rtos_R2: \ | |
decf FSR0L,F,ACCESS \ | |
movff PREINC2,INDF0 \ | |
tstfsz FSR0L,ACCESS \ | |
SMARTJUMP _rtos_R2 \ | |
; \ | |
; Restore the sfr's forming the tasks context. \ | |
; We cannot yet restore bsr, w and status because \ | |
; we need these registers for a final test. \ | |
; \ | |
movff PREINC2,PCLATH \ | |
if __ROMSIZE > 0x8000 \ | |
movff PREINC2,PCLATU \ | |
else \ | |
clrf PCLATU,ACCESS \ | |
endif \ | |
movff PREINC2,TBLPTRL \ | |
movff PREINC2,TBLPTRH \ | |
if __ROMSIZE > 0x8000 \ | |
movff PREINC2,TBLPTRU \ | |
else \ | |
clrf TBLPTRU,ACCESS \ | |
endif \ | |
movff PREINC2,TABLAT \ | |
movff PREINC2,FSR1L \ | |
movff PREINC2,FSR1H \ | |
movff PREINC2,FSR0L \ | |
movff PREINC2,FSR0H \ | |
movff PREINC2,PRODL \ | |
movff PREINC2,PRODH \ | |
; \ | |
; The return from portRESTORE_CONTEXT() depends on \ | |
; the value of ucCriticalNesting. When it is zero, \ | |
; interrupts need to be enabled. This is done via a \ | |
; retfie instruction because we need the \ | |
; interrupt-enabling and the return to the restored \ | |
; task to be uninterruptable. \ | |
; Because bsr, status and W are affected by the test \ | |
; they are restored after the test. \ | |
; \ | |
movlb ucCriticalNesting>>8 \ | |
tstfsz ucCriticalNesting,BANKED \ | |
SMARTJUMP _rtos_R4 \ | |
_rtos_R3: \ | |
movff PREINC2,BSR \ | |
movff PREINC2,WREG \ | |
movff PREINC2,STATUS \ | |
retfie 0 ; Return enabling interrupts \ | |
_rtos_R4: \ | |
movff PREINC2,BSR \ | |
movff PREINC2,WREG \ | |
movff PREINC2,STATUS \ | |
return 0 ; Return without affecting interrupts \ | |
_Pragma("asmend") \ | |
} while(0) | |
/*-----------------------------------------------------------*/ | |
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) | |
/*-----------------------------------------------------------*/ | |
extern void vPortYield( void ); | |
#define portYIELD() vPortYield() | |
#define portNOP() _Pragma("asm") \ | |
nop \ | |
_Pragma("asmend") | |
/*-----------------------------------------------------------*/ | |
#define portTASK_FUNCTION( xFunction, pvParameters ) \ | |
void pointed xFunction( void *pvParameters ) \ | |
_Pragma(asmfunc xFunction) | |
#define portTASK_FUNCTION_PROTO portTASK_FUNCTION | |
/*-----------------------------------------------------------*/ | |
#define volatile | |
#define register | |
#endif /* PORTMACRO_H */ | |