OpenCores
URL https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk

Subversion Repositories open8_urisc

[/] [open8_urisc/] [trunk/] [taskmgr/] [taskmgr_const.s] - Rev 302

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
 
; TASK_SETUP 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 TASK_SETUP
 
              ; Setup a pointer in R3:R2 to the beginning of the parameter
              ;  table to load task information from.
              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 paramenter region
 
              ADD  R2
              T0X  R2
              TX0  R1
              ADC  R3
              T0X  R3
 
              ; 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
              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
              PSH  R0                  ; R0
              PSH  R0                  ; R1
              PSH  R0                  ; R2
              PSH  R0                  ; R3
              PSH  R0                  ; R4
              PSH  R0                  ; R5
              PSH  R0                  ; R6
              PSH  R0                  ; R7
 
              ; Once the task's stack has been initialized, retrieve the new
              ;  stack pointer from the SP and store it in the task's backup
              ;  variable.
 
              LDI  R0, #\@
              LDI  R1, #STACK_TABLE_OFFSET
              MUL  R1
 
              LDA  R2, STACK_MEMORY_PTR + 0
              LDA  R3, STACK_MEMORY_PTR + 1
 
              ADD  R2
              T0X  R2
              TX0  R1
              ADC  R3
              T0X  R3
 
              RETRIEVE_SP
              STX  R2
              TX0  R1
              STO  R2,1
.ENDM
 
; 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
              TASK_SETUP
          .ENDR
.ENDM
 
; REINIT_STACK_BUFFER_PTR uses the This_Task variable to configure R3:R2 as a
;  pointer into the system memory where stack pointer backups are stored.
.MACRO REINIT_STACK_BUFFER_PTR
              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
.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
 
; 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
 
; SUSPEND_CURRENT_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_CURRENT_TASK
              BACKUP_FULL_CONTEXT
 
              REINIT_STACK_BUFFER_PTR
 
              RETRIEVE_SP              ; Copy CPU SP -> R1:R0
              STX  R2                  ; Push R1:R0 -> [R3:R2]*
              TX0  R1
              STO  R2,1
.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
              LDA  R1, TaskMgr.Next_Task ; Copy Next_Task -> This_Task
              STA  R1, TaskMgr.This_Task
 
; This is 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.
 
              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
 
              REINIT_STACK_BUFFER_PTR  ; Lookup the new task's table pointer
 
              LDO  R2, PARAM_MAIN_ADDR_HIGH ; Push [R3:R2]* -> R1:R0
              T0X  R1
              LDO  R2, PARAM_MAIN_ADDR_LOW
              RELOCATE_SP              ; Update R1:R0 -> CPU SP
 
              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
 
              RESET_PREEMPTION_TIMER   ; Reset the PIT timer
 
              RESTORE_FULL_CONTEXT     ; Restore the new task's register state
              RTI
.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.
;------------------------------------------------------------------------------
 
; INIT_NEXT_TASK forces the Next_Task variable to 0 (Task 0)
.MACRO INIT_NEXT_TASK
              CLR  R0
              STA  R0, TaskMgr.Next_Task
.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
              AWAKEN_NEXT_TASK
 
; Create all of the task loops here.
              INSTANCE_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_CURRENT_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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.