URL
https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk
Subversion Repositories open8_urisc
[/] [open8_urisc/] [trunk/] [taskmgr/] [taskmgr_const.s] - Rev 305
Go to most recent revision | Compare with Previous | Blame | View Log
; Copyright (c)2022 Jeremy Seth Henry ; All rights reserved. ; ; Redistribution and use in source and binary forms, with or without ; modification, are permitted provided that the following conditions are met: ; * Redistributions of source code must retain the above copyright ; notice, this list of conditions and the following disclaimer. ; * Redistributions in binary form must reproduce the above copyright ; notice, this list of conditions and the following disclaimer in the ; documentation and/or other materials provided with the distribution, ; where applicable (as part of a user interface, debugging port, etc.) ; ; THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``AS IS'' AND ANY ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ; DISCLAIMED. IN NO EVENT SHALL JEREMY SETH HENRY BE LIABLE FOR ANY ; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ; ;------------------------------------------------------------------------------ ; taskmgr_const.s ; ; Task Manager constants, tables, and macros ; ; Revision History ; Author Date Change ;---------------- -------- ---------------------------------------------------- ; Seth Henry 7/15/22 Initial Release ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; Semaphore Value - should always be non-zero .DEFINE SEMAPHORE_VAL $FF ; Standard value used to set semaphores ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; I/O Mapping (HDL -> ASSY) ;------------------------------------------------------------------------------ ; System RAM Write-Protect Register .DEFINE WPR_MASK_REG WPR_Address ; Write Protect Mask .DEFINE WPR_MASK_0 WPR_MASK_REG + 0 .DEFINE WPR_MASK_1 WPR_MASK_REG + 1 .DEFINE WPR_MASK_2 WPR_MASK_REG + 2 .DEFINE WPR_MASK_3 WPR_MASK_REG + 3 ; WP Register Map: ; Offset Bitfield Description Read/Write ; 0x00 AAAAAAAA Region Enables 7:0 (RW) ; 0x01 AAAAAAAA Region Enables 15:8 (RW) ; 0x02 AAAAAAAA Region Enables 23:16 (RW) ; 0x03 AAAAAAAA Region Enables 31:24 (RW) ; I/O Write Qualification Register .DEFINE IO_WRITE_QUAL WQL_Address ; External Interrupt Manager / Task Timer .DEFINE INT_MGR_IF INT_Address ; Shared with the main .DEFINE TASK_TIMER_PRD INT_MGR_IF + 0 ; Defines for the 8-bit interrupt manager .DEFINE EXT_INT_MASK INT_MGR_IF + 1 .DEFINE EXT_INT_PEND INT_MGR_IF + 2 .DEFINE EXT_INT_ACK INT_MGR_IF + 3 ; Defines for the 16-bit interrupt manager .DEFINE EXT_INT16_MASK_L INT_MGR_IF + 2 .DEFINE EXT_INT16_MASK_H INT_MGR_IF + 3 .DEFINE EXT_INT16_PEND_L INT_MGR_IF + 4 .DEFINE EXT_INT16_PEND_H INT_MGR_IF + 5 .DEFINE EXT_INT16_ACK INT_MGR_IF + 7 ; Register Map: ; Offset Bitfield Description Read/Write ; 0x00 AAAAAAAA PIT Timer Interval (0 = disabled) (RW) ; 0x01 AAAAAAAA External Interrupt Mask (RW) ; 0x02 AAAAAAAA Pending External Ints* (RW) ; 0x02 A------- Interrupt Requested (write to clear) (RW) ; External Interrupt bit & mask definitions .DEFINE EXT_INT0_BIT 2^0 .DEFINE EXT_INT1_BIT 2^1 .DEFINE EXT_INT2_BIT 2^2 .DEFINE EXT_INT3_BIT 2^3 .DEFINE EXT_INT4_BIT 2^4 .DEFINE EXT_INT5_BIT 2^5 .DEFINE EXT_INT6_BIT 2^6 .DEFINE EXT_INT7_BIT 2^7 .DEFINE EXT_INT8_BIT 2^0 .DEFINE EXT_INT9_BIT 2^1 .DEFINE EXT_INT10_BIT 2^2 .DEFINE EXT_INT11_BIT 2^3 .DEFINE EXT_INT12_BIT 2^4 .DEFINE EXT_INT13_BIT 2^5 .DEFINE EXT_INT14_BIT 2^6 .DEFINE EXT_INT15_BIT 2^7 ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; Register the interrupt service routines and form the vector address table at ; the end of the ROM ;------------------------------------------------------------------------------ .ORG ISR_Start_Addr .DW RAM_FAULT ; (0xFFF1:0xFFF0) ISR 0 .DW TASK_SW_INT ; (0xFFF3:0xFFF2) ISR 1 .DW EXT_INT_MGR ; (0xFFF5:0xFFF4) ISR 2 .DW EXEC_SUPV0 ; (0xFFF7:0xFFF6) ISR 3 .DW EXEC_SUPV1 ; (0xFFF9:0xFFF8) ISR 4 .DW EXEC_SUPV2 ; (0xFFFB:0xFFFA) ISR 5 .DW EXEC_SUPV3 ; (0xFFFD:0xFFFC) ISR 6 .DW EXEC_SUPV4 ; (0xFFFF:0xFFFE) ISR 7 .DEFINE CPU_INT_ENABLES $FF ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; Pointer Defines and Table Organization ;------------------------------------------------------------------------------ ; Create defined constants to pointer table ; Note that these are offset by 4 to account for the bootstrap JMP and NOP .DEFINE STACK_MEMORY_PTR BOOT_BLOCK + 4 .DEFINE FREE_SYSMEM_PTR BOOT_BLOCK + 6 .DEFINE TASK_PARAMS_PTR BOOT_BLOCK + 8 .DEFINE TASK_PARAM_TABLE BOOT_BLOCK + 10 .MACRO INSTANCE_TASK_POINTERS .DW TaskMgr.Task_Stacks ; ( + 4) First available RAM location for stack data .DW TaskMgr.Free_Mem ; ( + 6) First available free RAM location in sysmem .DW TASK_PARAM_TABLE ; ( + 8) Start of task parameter table .ENDM ;------------------------------------------------------------------------------ ; INSTANCE_TASK_EXPORTS / CREATE_TASK_EXPORTS setup a ROM table for the task ; switcher with critical entry points and memory & I/O write access ; permissions. The entry points are created automatically by ; INSTANCE_MAIN_LOOPS, while the other constants are setup in taskmgr_config.s ; based on its task configuration and memory needs ;------------------------------------------------------------------------------ .DEFINE STACK_TABLE_OFFSET 2 .DEFINE PARAM_TABLE_OFFSET 20 ; Define table offsets to allow quick use of LDO to access individual records ; for each task .DEFINE PARAM_MAIN_ADDR_LOW 0 .DEFINE PARAM_MAIN_ADDR_HIGH 1 .DEFINE PARAM_STACK_ADDR_LOW 2 .DEFINE PARAM_STACK_ADDR_HIGH 3 .DEFINE PARAM_WPR_BYTE0 4 .DEFINE PARAM_WPR_BYTE1 5 .DEFINE PARAM_WPR_BYTE2 6 .DEFINE PARAM_WPR_BYTE3 7 .DEFINE PARAM_WQL_LOW 8 .DEFINE PARAM_WQL_HIGH 9 .DEFINE SUPV_FN0_ENTRY_LOW 10 .DEFINE SUPV_FN0_ENTRY_HIGH 11 .DEFINE SUPV_FN1_ENTRY_LOW 12 .DEFINE SUPV_FN1_ENTRY_HIGH 13 .DEFINE SUPV_FN2_ENTRY_LOW 14 .DEFINE SUPV_FN2_ENTRY_HIGH 15 .DEFINE SUPV_FN3_ENTRY_LOW 16 .DEFINE SUPV_FN3_ENTRY_HIGH 17 .DEFINE SUPV_FN4_ENTRY_LOW 18 .DEFINE SUPV_FN4_ENTRY_HIGH 19 ; CREATE_TASK_EXPORTS creates an entry in the TASK_PARAM_TABLE with critical ; task information. Note that the WPR_LOW, WPR_HIGH, and WQL must be defined ; in taskmgr_config.s. Everything else is automatically created here, or by ; the assembler/linker. .MACRO CREATE_TASK_EXPORTS .DEFINE TASK\@_STACK_END RAM_Address + (TASK\@_STACK_RGN * WP_Rgn_Size) .DEFINE TASK\@_STACK_START TASK\@_STACK_END + WP_Rgn_Size - 1 .DW TASK\@_MAIN ; 1:0 Entry point created by INSTANCE_MAIN_LOOPS .DW TASK\@_STACK_START ; 3:2 Pointer to top of the task stack .DW TASK\@_WPR_LOW ; 5:4 Mask for enabling task mem writes for vars .DW TASK\@_WPR_HIGH ; 7:6 Mask for enabling task mem writes for stack .DW TASK\@_WQL ; 9:8 Mask for enabling task I/O write access .DW _F0_EXE_S\@ ; 11:10 Entry point for supervisory function 0 .DW _F1_EXE_S\@ ; 13:12 Entry point for supervisory function 1 .DW _F2_EXE_S\@ ; 15:14 Entry point for supervisory function 2 .DW _F3_EXE_S\@ ; 17:16 Entry point for supervisory function 3 .DW _F4_EXE_S\@ ; 19:18 Entry point for supervisory function 4 .ENDM .MACRO INSTANCE_TASK_EXPORTS .REPEAT TASK_COUNT CREATE_TASK_EXPORTS .ENDR .ENDM ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; Memory Structures and Variable Organization ;------------------------------------------------------------------------------ ; Variable Size Description ; -------- ---- ------------------------------------------------------- .STRUCT str_task_label Label DW ; 2-byte "header" to allow task RAM to be easily found .ENDST .STRUCT str_isr_flag Flag DB ; Single-byte flag variable for returning semaphores .ENDST .DEFINE TSB_SIZE 2 * TASK_COUNT .DEFINE FREE_SYS WP_Rgn_Size - 8 - TSB_SIZE .STRUCT str_taskman This_Task DB ; Holds the task number of the current task Next_Task DB ; Specifieds which task to be executed next Fault_Flag DB ; Memory write fault flag Fault_Task DB ; Task active/responsible for write fault Temp_R0 DB ; Temp storage for R0 Temp_R1 DB ; Temp storage for R1 Temp_R2 DB ; Temp storage for R2 Temp_R3 DB ; Temp storage for R3 Task_Stacks DSB TSB_SIZE ; First location for stack data* Free_Mem DSB FREE_SYS ; Unused memory in system region .ENDST ; * Note that this location is used to setup a pointer, which the task manager ; will use to assign each task a backup location for its current stack pointer ; Thus, this area grows up to 2x the number tasks. The type is intentionally ; set as DB as a reminder. If attempting to reuse memory in this region, this ; should be noted .ENUM SYSTEM_VARMEM TaskMgr INSTANCEOF str_taskman .ENDE ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; Utility macros for booting the system and providing access to internal vars ;------------------------------------------------------------------------------ ; The Open8 supports an optional mode for the RSP instruction that ; converts it from Reset Stack Pointer to Relocate Stack Pointer. ; The new form of the instruction takes one of the CPU flags as a ; direction/mode bit. Setting PSR_S will cause the instruction ; to write R1:R0 -> SP, while clearing it will cause the instruction ; to copy the SP -> R1:R0. .MACRO RETRIEVE_SP CLP PSR_S ; Affirmatively clear PSR_S flag first RSP ; Then execute the RSP instruction .ENDM .MACRO RELOCATE_SP STP PSR_S ; Affirmatively set PSR_S flag first RSP ; Execute the RSP instruction CLP PSR_S ; Reset PSR_GP4 afterward .ENDM ; Task initialization, switching time, and init/exec macros .MACRO DISABLE_PREEMPTION_TIMER CLR R0 ; Make sure the PIT is disabled STA R0, TASK_TIMER_PRD ; by writing 0 to the PIT period .ENDM .MACRO RESET_PREEMPTION_TIMER LDI R0, #MAX_TASK_TIMESLICE; Turn on the PIT timer at this point STA R0, TASK_TIMER_PRD ; to kick off the task switcher .ENDM ; Fault flag macros .MACRO INITIALIZE_FAULT_FLAGS CLR R0 STA R0, TaskMgr.Fault_Flag STA R0, TaskMgr.Fault_Task .ENDM .MACRO SET_FAULT_FLAGS LDI R0, #SEMAPHORE_VAL STA R0, TaskMgr.Fault_Flag LDA R0, TaskMgr.This_Task STA R0, TaskMgr.Fault_Task; .ENDM ; BACKUP_FULL_CONTEXT pushes all 8 registers to the stack .MACRO BACKUP_FULL_CONTEXT PSH R0 PSH R1 PSH R2 PSH R3 PSH R4 PSH R5 PSH R6 PSH R7 .ENDM ; RESTORE_FULL_CONTEXT pops all 8 registers off of the stack .MACRO RESTORE_FULL_CONTEXT POP R7 POP R6 POP R5 POP R4 POP R3 POP R2 POP R1 POP R0 .ENDM ; REINIT_TASK_TABLE_PTR uses the This_Task variable to configure R3:R2 as a ; pointer into the TASK_PARAMS table. .MACRO REINIT_TASK_TABLE_PTR LDA R0, TaskMgr.This_Task LDI R1, #PARAM_TABLE_OFFSET MUL R1 LDA R2, TASK_PARAMS_PTR + 0 ; Load R3:R2 with the start of the LDA R3, TASK_PARAMS_PTR + 1 ; task parameter region ADD R2 ; Add [R3:R2] + [R1:R0] -> [R3:R2] T0X R2 TX0 R1 ADC R3 T0X R3 .ENDM ; INITIALIZE_TASK_STACK relocates the CPU stack pointer during setup, then ; simulates an RTI by loading an initial flag and return address, then resets ; all registers to 0 .MACRO INITIALIZE_TASK_STACK ; Get the task's starting stack address from the table and load ; it into the CPU SP LDO R2, PARAM_STACK_ADDR_HIGH T0X R1 LDO R2, PARAM_STACK_ADDR_LOW RELOCATE_SP ; R1:R0 -> CPU SP ; From here on out, the CPU SP is pointing to the target task's ; stack region, so we can initialize its stack memory ; Write initial flag value to simulate entering code as an ISR CLR R0 PSH R0 ; Initialize the return address to point to the "initialize" ; portion of the task, so that the task can do its one-time ; startup code. LDO R2, PARAM_MAIN_ADDR_HIGH ; Write return PC MSB PSH R0 LDO R2, PARAM_MAIN_ADDR_LOW ; Write return PC LSB PSH R0 ; Setup the initial reg values CLR R0 ; Initialize all registers to 0 T0X R1 T0X R2 T0X R3 T0X R4 T0X R5 T0X R6 T0X R7 .ENDM ; BACKUP_STACK_POINTER uses the This_Task variable to configure R3:R2 as a ; pointer into the system memory where stack pointer backups are stored, then ; obtains the current stack address and pushes it to the task's backup ; SP variable .MACRO BACKUP_STACK_POINTER LDA R0, TaskMgr.This_Task ; Get the task number LDI R1, #STACK_TABLE_OFFSET ; Multiply it by 2 MUL R1 LDA R2, STACK_MEMORY_PTR + 0 ; Load R3:R2 with the start of the LDA R3, STACK_MEMORY_PTR + 1 ; stack memory region ADD R2 ; Add [R3:R2] + [R1:R0] -> [R3:R2] T0X R2 TX0 R1 ADC R3 T0X R3 RETRIEVE_SP STX R2 TX0 R1 STO R2,1 .ENDM ; SUSPEND_THIS_TASK pushes all of the registers to a task's stack, then ; backups up the stack pointer to the system memory backup location for that ; task. .MACRO SUSPEND_THIS_TASK BACKUP_FULL_CONTEXT BACKUP_STACK_POINTER .ENDM ; SETUP_TASK is a template used to generate code that sets up a single task's ; stack and stack pointer for each a task. This macros will temporarily ; relocate the stack pointer, push the task's initial state to the task's ; stack. Note that it is important to also ensure that the stack has data to ; not only restore the return address, but also R7:R0, as well as the flag ; state. .MACRO SETUP_TASK LDI R0, #\@ STA R0, TaskMgr.This_Task ; Write This_Task REINIT_TASK_TABLE_PTR ; Use This_Task to initialize R3:R2 INITIALIZE_TASK_STACK ; Setup the new task's stack area SUSPEND_THIS_TASK ; Suspend the task to store setup .ENDM ; RESTORE_STACK_POINTER uses the This_Task variable to configure R3:R2 as a ; pointer into the system memory where stack pointer backups are stored. It ; then looks up the task's SP backup variable and pushes it back to the CPU SP .MACRO RESTORE_STACK_POINTER LDA R0, TaskMgr.This_Task ; Get the task number LDI R1, #STACK_TABLE_OFFSET ; Multiply it by 2 MUL R1 LDA R2, STACK_MEMORY_PTR + 0 ; Load R3:R2 with the start of the LDA R3, STACK_MEMORY_PTR + 1 ; stack memory region ADD R2 ; Add [R3:R2] + [R1:R0] -> [R3:R2] T0X R2 TX0 R1 ADC R3 T0X R3 LDO R2,1 ; Copy [R3:R2]* -> R1:R0 T0X R1 LDX R2 RELOCATE_SP ; Update R1:R0 -> CPU SP .ENDM ; RESTORE_TASK_PERMISSIONS looks up the current (new) task's RAM & I/O write ; permissions (masks) and reconfigures the hardware to allow access. .MACRO RESTORE_TASK_PERMISSIONS REINIT_TASK_TABLE_PTR ; Setup R3:R2 to point to the task table ; Rewrite the RAM WPR register for this task. Note that the WPR ; is a 32-bit register. LDO R2, PARAM_WPR_BYTE0 STA R0, WPR_MASK_0 LDO R2, PARAM_WPR_BYTE1 STA R0, WPR_MASK_1 LDO R2, PARAM_WPR_BYTE2 STA R0, WPR_MASK_2 LDO R2, PARAM_WPR_BYTE3 STA R0, WPR_MASK_3 LDO R2, PARAM_WQL_LOW ; Update the new task's WQL STA R0, IO_WRITE_QUAL .ENDM ; INIT_NEXT_TASK forces the Next_Task variable to 0 (Task 0) .MACRO INIT_NEXT_TASK CLR R0 STA R0, TaskMgr.Next_Task .ENDM ; ADVANCE_NEXT_TASK implements the simple scheduler. By default, the task ; manager uses a simple round-robin scheduler, unless an ISR alters the ; Next_Task variable, so increment the task count, check it against the ; total task count, and reset it to task 0 if necessary. .MACRO ADVANCE_NEXT_TASK LDA R1, TaskMgr.Next_Task ; Copy Next_Task -> This_Task STA R1, TaskMgr.This_Task INC R1 ; Increment the task count LDI R0, #TASK_COUNT ; Compare it with the task count XOR R1 BNZ _TS_ADV_TN_\@ ; If it matches the task count LDI R1, #$00 ; reset the counter to zero _TS_ADV_TN_\@:STA R1, TaskMgr.Next_Task ; Store the new value into Next_Task .ENDM ; AWAKEN_NEXT_TASK is responsible for updating the Next_Task variable, then ; using it to restore the stack pointer from the system memory copy. It then ; updates the memory write protection parameters and I/O write qualification ; register for the new task. Finally, it resets the pre-emption timer and ; restores the register state from the task's stack. .MACRO AWAKEN_NEXT_TASK ADVANCE_NEXT_TASK ; Advance the task positions RESTORE_STACK_POINTER ; Lookup the new task's table pointer RESTORE_TASK_PERMISSIONS ; Restore the task's write access masks RESET_PREEMPTION_TIMER ; Reset the PIT timer RESTORE_FULL_CONTEXT ; Restore the new task's register state RTI ; Return to the new task .ENDM ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; System Start ; ; System initialization requires that the CPU start in supervisor mode, as it ; writes to every active region in memory. Thus, if the CPU is NOT in ; in supervisor mode, something has gone horribly wrong. This branch locks the ; main loop if the I bit is not set. This is probably unnecessary now that the ; panic ISR is in place. ;------------------------------------------------------------------------------ ; INSTANCE_TASK_SETUP expands to create setup code for all of the tasks based ; on the template above. There should be one setup per task, hence the repeat ; based on TASK_COUNT .MACRO INSTANCE_TASK_SETUP .REPT TASK_COUNT SETUP_TASK .ENDR .ENDM .MACRO BOOT_SYSTEM ; Before beginning, turn off all of the CPU interrupts (The NMI will still be ; active, but memory write protection is effectively disabled with the I-bit ; set) Note that this code is running in interrupt context (I-bit is set) ; so it is "uninterruptible" due to the HDL generic being set. However, this ; doesn't prevent pending interrupts from being latched by the CPU. ; Note that this macro is defined in "isr_const.s" DISABLE_CPU_INTS ; Even though interrupts are off, disable the task timer so that there isn't ; a pending task timer interrupt waiting when interrupts are re-enabled. This ; avoids the first task missing out on initialization. DISABLE_PREEMPTION_TIMER ; Initialize the system fault flags INITIALIZE_FAULT_FLAGS ; Setup the stack and stack pointer for each of the tasks. (see above) INSTANCE_TASK_SETUP ; Before starting, make sure the external interrupt controller has been ; initialized. Initialization of the external interrupt controller involves ; clearing any pending interrupts and setting up the interrupt mask. Finally, ; the CPU interrupts should be enabled at this point, as the interrupt core is ; ready. .IFDEF INTMGR16 INITIALIZE_IO_INTMGR16 ; Setup the 16-bit IF .ELSE INITIALIZE_IO_INTMGR ; Setup the 8-bit IF .ENDIF ; Once the external interrupt manager is configured, enable the CPU interrupts ENABLE_CPU_INTS ; Enable the CPU interrupt inputs ; Like the task switcher ISR, restore the full CPU state for the first task INIT_NEXT_TASK ; Reset the next task to task 0 AWAKEN_NEXT_TASK ; Load task 0's context to system ; Create all of the task loops here. INSTANCE_TASK_LOOPS ; Instantiate the main task loops .ENDM ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; Task Loops ;------------------------------------------------------------------------------ ; INSTANCE_MAIN_LOOP: ; These loops represent each task's "main()", and should never "exit". ; Each task's func.s file should declare two required functions, TASKn_INIT and ; TASKn_EXEC ; ; Note that the call to the task switch ISR occurs prior to the task's exec ; function to allow every task to finish its setup BEFORE any task begins ; processing. ; ; Last, the final branch is only taken if the I bit is still NOT set. If it ; somehow gets set, the task will trigger a system panic (which never returns) ; There should be one loop per task. Note that the labels are used in to create ; the table of entry points .MACRO INSTANCE_TASK_LOOP TASK\@_MAIN: JSR TASK\@_INIT _TASK\@_LOOP: CALL_TASK_SW JSR TASK\@_EXEC BNI _TASK\@_LOOP CALL_PANIC .ENDM ; INSTANCE_MAIN_LOOPS expands to create the stub main loops for all of the ; tasks based on the template above. There should be one setup per task, hence ; the repeat based on TASK_COUNT. Note that the output labels will be used to ; populate fields in the task parameter table. .MACRO INSTANCE_TASK_LOOPS .REPEAT TASK_COUNT INSTANCE_TASK_LOOP .ENDR .ENDM ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; Software-callable Interrupts ;------------------------------------------------------------------------------ ; CALL_PANIC executes the same ISR as a memory fault and triggers the shutdown ; sequence. This requires a hard-reset to recover .MACRO CALL_PANIC INT 0 .ENDM ; CALL_TASK_SW executes the same ISR as the pre-emption timer, and allows tasks ; to give up any remaining time .MACRO CALL_TASK_SW INT 1 .ENDM ; Interrupt 2 is reserved for the external interrupt manager. Calling it would ; likely be harmless, but do no real work. ; CALL_SUPV_FNn allow tasks to execute code in an interrupt/supervisor context ; and should be used with caution. .MACRO CALL_SUPV_FN0 INT 3 .ENDM .MACRO CALL_SUPV_FN1 INT 4 .ENDM .MACRO CALL_SUPV_FN2 INT 5 .ENDM .MACRO CALL_SUPV_FN3 INT 6 .ENDM .MACRO CALL_SUPV_FN4 INT 7 .ENDM ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; CPU Interrupt Control Macros ;------------------------------------------------------------------------------ .MACRO DISABLE_CPU_INTS CLR R0 SMSK .ENDM .MACRO ENABLE_CPU_INTS LDI R0, #CPU_INT_ENABLES SMSK .ENDM ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; RAM Access Fault Shutdown (CALL_PANIC) ; If a task manages to cause a memory fault, sets default conditions and ; then soft-halts the CPU. ; Note that DISABLE_PREEMPTION_TIMER, SET_FAULT_FLAGS are defined in ; "main_const.s", while PANIC_HALT macro calls each task's PANICn_HALT macro ;------------------------------------------------------------------------------ .MACRO PROCESS_RAM_FAULT DISABLE_CPU_INTS ; Disable interrupts DISABLE_PREEMPTION_TIMER ; Disable the task timer SET_FAULT_FLAGS ; Copy the faulting task info EXEC_PANIC_HALT ; Run any necessary shutdown code _RFLT_HALT: WAI ; Soft-Halt the CPU JMP _RFLT_HALT ; This shouldn't happen, but if it does .ENDM .MACRO CREATE_PANIC_TASK_BLOCK LDI R0, #\@ LDI R1, #PARAM_TABLE_OFFSET MUL R1 LDA R2, TASK_PARAMS_PTR + 0 ; Load R3:R2 with the start of the LDA R3, TASK_PARAMS_PTR + 1 ; task parameter region ADD R2 ; Add [R3:R2] + [R1:R0] -> [R3:R2] T0X R2 TX0 R1 ADC R3 T0X R3 ; Update the WQL for the task's panic code so each task isn't ; doing this on its own LDO R2, PARAM_WQL_LOW STA R0, IO_WRITE_QUAL TASK\@_PANIC .ENDM .MACRO EXEC_PANIC_HALT .REPEAT TASK_COUNT CREATE_PANIC_TASK_BLOCK .ENDR .ENDM ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; Task Switcher ISR (CALL_TASK_SW) ; ; Handles switching context between tasks when called. ;------------------------------------------------------------------------------ .MACRO SWITCH_TASKS SUSPEND_THIS_TASK AWAKEN_NEXT_TASK .ENDM ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; External System Interrupt Map & ISR Macros ; This code configures the external I/O interrupt manager at startup ;------------------------------------------------------------------------------ ; Setup the external I/O Interrupt manager by writing the mask registers and ; clearing any pending interrupts. Note that the interrupt enable constants ; are defined in ext_isr_config.s .MACRO INITIALIZE_IO_INTMGR CLR R0 ; Disable all external interrupts STA R0, EXT_INT_MASK LDI R0, #SEMAPHORE_VAL ; Clear any pending interrupts STA R0, EXT_INT_PEND ; and do a master acknowledge STA R0, EXT_INT_ACK LDI R0, #EXT_INTERRUPT_EN_L; Re-enable the external interrupts STA R0, EXT_INT_MASK .ENDM .MACRO INITIALIZE_IO_INTMGR16 CLR R0 ; Disable all external interrupts STA R0, EXT_INT16_MASK_L STA R0, EXT_INT16_MASK_H LDI R0, #SEMAPHORE_VAL ; Clear any pending interrupts STA R0, EXT_INT16_PEND_L ; and do a master acknowledge STA R0, EXT_INT16_PEND_H STA R0, EXT_INT16_ACK LDI R0, #EXT_INTERRUPT_EN_L; Re-enable the external interrupts STA R0, EXT_INT16_MASK_L LDI R0, #EXT_INTERRUPT_EN_H; Re-enable the external interrupts STA R0, EXT_INT16_MASK_H .ENDM ; Sets up an individual external interrupt macro. Note that these refer to ; macros defined in ext_isr_config.s .MACRO PROCESS_EXT_ISR SET_INT\@_FLAGS TX0 R3 ; When done with the flags, update the LDI R1, #EXT_INT\@_BIT ; mask clear register with the ext bit OR R1 ; and restore it to R3. Then, before T0X R3 ; falling into the next check, make TX0 R2 ; sure to restore the current ints .ENDM ; This code checks the external I/O interrupt manager status and processes the ; flag code for each interrupt source. .MACRO CHECK_EXTERNAL_IO_INTS _EXT_INT_STRT:PSH R0 PSH R1 PSH R2 PSH R3 _EXT_INT_LO: CLR R0 ; R3 will be our pending mask clear reg, T0X R3 ; so clear it here LDA R0, EXT_INT_PEND ; R2 will be our current pending reg, so T0X R2 ; load it from the hardware here _EXT_INT_0: BTT 0 BRZ _EXT_INT_1 PROCESS_EXT_ISR _EXT_INT_1: BTT 1 BRZ _EXT_INT_2 PROCESS_EXT_ISR _EXT_INT_2: BTT 2 BRZ _EXT_INT_3 PROCESS_EXT_ISR _EXT_INT_3: BTT 3 BRZ _EXT_INT_4 PROCESS_EXT_ISR _EXT_INT_4: BTT 4 BRZ _EXT_INT_5 PROCESS_EXT_ISR _EXT_INT_5: BTT 5 BRZ _EXT_INT_6 PROCESS_EXT_ISR _EXT_INT_6: BTT 6 BRZ _EXT_INT_7 PROCESS_EXT_ISR _EXT_INT_7: BTT 7 BRZ _EXT_INT_CLR PROCESS_EXT_ISR _EXT_INT_CLR: STA R3, EXT_INT_PEND STA R3, EXT_INT_ACK ; pending ints and ack the HW POP R3 POP R2 POP R1 POP R0 RTI .ENDM .MACRO CHECK_EXTERNAL_IO_INTS16 _EXT_INT_STRT:PSH R0 PSH R1 PSH R2 PSH R3 _EXT_INT_LO: CLR R0 ; R3 will be our pending mask clear reg, T0X R3 ; so clear it here LDA R0, EXT_INT16_PEND_L; R2 will be our current pending reg, so T0X R2 ; load it from the hardware here _EXT_INT_0: BTT 0 BRZ _EXT_INT_1 PROCESS_EXT_ISR _EXT_INT_1: BTT 1 BRZ _EXT_INT_2 PROCESS_EXT_ISR _EXT_INT_2: BTT 2 BRZ _EXT_INT_3 PROCESS_EXT_ISR _EXT_INT_3: BTT 3 BRZ _EXT_INT_4 PROCESS_EXT_ISR _EXT_INT_4: BTT 4 BRZ _EXT_INT_5 PROCESS_EXT_ISR _EXT_INT_5: BTT 5 BRZ _EXT_INT_6 PROCESS_EXT_ISR _EXT_INT_6: BTT 6 BRZ _EXT_INT_7 PROCESS_EXT_ISR _EXT_INT_7: BTT 7 BRZ _EXT_INT_CLRL PROCESS_EXT_ISR _EXT_INT_CLRL:STA R3, EXT_INT16_PEND_L _EXT_INT_HI: CLR R0 T0X R3 LDA R0, EXT_INT16_PEND_H T0X R2 _EXT_INT_8: BTT 0 BRZ _EXT_INT_9 PROCESS_EXT_ISR _EXT_INT_9: BTT 1 BRZ _EXT_INT_10 PROCESS_EXT_ISR _EXT_INT_10: BTT 2 BRZ _EXT_INT_11 PROCESS_EXT_ISR _EXT_INT_11: BTT 3 BRZ _EXT_INT_12 PROCESS_EXT_ISR _EXT_INT_12: BTT 4 BRZ _EXT_INT_13 PROCESS_EXT_ISR _EXT_INT_13: BTT 5 BRZ _EXT_INT_14 PROCESS_EXT_ISR _EXT_INT_14: BTT 6 BRZ _EXT_INT_15 PROCESS_EXT_ISR _EXT_INT_15: BTT 7 BRZ _EXT_INT_CLRH PROCESS_EXT_ISR _EXT_INT_CLRH:STA R3, EXT_INT16_PEND_H _EXT_ACK_HW: STA R3, EXT_INT16_ACK ; pending ints and ack the HW POP R3 POP R2 POP R1 POP R0 RTI .ENDM ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; ISR/Supervisory Mode Functions - Allows tasks to define up to 5 functions ; that will operate in supervisor mode by calling a soft-int. ; ; Note 1: that using these functions requires at least 5 free bytes of stack ; ; Note 2: Due to assembler limitations, it is actually necessary for each task ; to define all 5 sets of these macros, even if not used. These are defined ; in the pattern of TASKx_SPV_FUNCy, where x is the task number and ; y is the function 0-4. ; ; Note 3: Task code for these functions should NOT use RTS or RTI, as this WILL ; corrupt their stacks and likely crash the whole system. These are intended ; for operations that are atomic in nature, or that require supervisor perms ; due to write access (writing flags/messages to other tasks) Registers will ; be preserved in local system memory and restored prior to entering the stub ; effectively meaning that registers will retain their state through the ; function call. The state of system flags will NOT be retained, however. ; ; Note 4: These functions will run to completion (or hang) regardless of the ; task timer, as interrupts can't pre-empt each other, so caution should be ; used with them. However, both the internal and external interrupt managers ; will latch incoming interrupts while these are running. ;------------------------------------------------------------------------------ ; CREATE_SUPV_FUNC creates an individual supervisory task function, which is ; referenced in the ISR table. (EXEC_SUPVn is used in the interrupt table) .MACRO CREATE_SUPV_FUNC EXEC_SUPV\@: STA R0, TaskMgr.Temp_R0 ; Copy R0-R3 to local RAM, not stack STA R1, TaskMgr.Temp_R1 STA R2, TaskMgr.Temp_R2 STA R3, TaskMgr.Temp_R3 REINIT_TASK_TABLE_PTR ; Setup R3:R2 to point to the task table LDO R2, SUPV_FN\@_ENTRY_HIGH PSH R0 LDO R2, SUPV_FN\@_ENTRY_LOW PSH R0 LDA R3, TaskMgr.Temp_R3 ; Replace R0-R3 from local RAM so that LDA R2, TaskMgr.Temp_R2 ; the stub has the same register space LDA R1, TaskMgr.Temp_R1 ; as a function call - less the flag LDA R0, TaskMgr.Temp_R0 ; state, which isn't preserved RTS ; Use RTS to "return" to our jump addr .REPEAT TASK_COUNT ; Create a stub for each task. The final CREATE_F\@_FUNCTION_STUB ; RTI will be handled in the stubs .ENDR .ENDM ; CREATE_Fn_FUNCTION_STUB creates an entry point that is referenced in the ; TASK_PARAM_TABLE, and is "RTS JMP'ed" to by the code from CREATE_SUPV_FUNC. ; Because RTS was used to reach the code generated in these blocks, a final ; JMP instruction will return to the calling supervisory function without ; disrupting the stack. (These aren't technically subroutines) .MACRO CREATE_F0_FUNCTION_STUB _F0_EXE_S\@: TASK\@_SUPV_FN0 ; Run the stub code from the task RTI ; Return from the interrupt .ENDM .MACRO CREATE_F1_FUNCTION_STUB _F1_EXE_S\@: TASK\@_SUPV_FN1 ; Run the stub code from the task RTI ; Return from the interrupt .ENDM .MACRO CREATE_F2_FUNCTION_STUB _F2_EXE_S\@: TASK\@_SUPV_FN2 ; Run the stub code from the task RTI ; Return from the interrupt .ENDM .MACRO CREATE_F3_FUNCTION_STUB _F3_EXE_S\@: TASK\@_SUPV_FN3 ; Run the stub code from the task RTI ; Return from the interrupt .ENDM .MACRO CREATE_F4_FUNCTION_STUB _F4_EXE_S\@: TASK\@_SUPV_FN4 ; Run the stub code from the task RTI ; Return from the interrupt .ENDM ; INSTANCE_SUPV_FUNCS creates all 5 supervisory function entry points and a set ; of stub functions for each task and is used to place everything in ROM .MACRO INSTANCE_SUPV_FUNCS .REPEAT 5 CREATE_SUPV_FUNC .ENDR .ENDM ;------------------------------------------------------------------------------
Go to most recent revision | Compare with Previous | Blame | View Log