;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Exempel på context switch i TSEA28 Datorteknik Y ;; ;; Require modified lab2 tm4c123gh6pm_startup_ccs.c ;; ;; 200208 K.Palmkvist ;; ;; Thumb mode code .thumb .text .align 2 ;; Supply following entry points .global main .global intgpiod .global intgpiof ;***************************************************** ;* ;* Constants that are not stored in pogram memory ;* ;* Used together with offset constants defined below ;* ;***************************************************** UART0_base .equ 0x4000c000 ; Start adress for UART GPIOA_base .equ 0x40004000 ; General Purpose IO port A start adress GPIOB_base .equ 0x40005000 ; General Purpose IO port B start adress GPIOC_base .equ 0x40006000 ; General Purpose IO port C start adress GPIOD_base .equ 0x40007000 ; General Purpose IO port D start adress GPIOE_base .equ 0x40024000 ; General Purpose IO port E start adress GPIOF_base .equ 0x40025000 ; General Purpose IO port F start adress GPIO_HBCTL .equ 0x400FE06C ; GPIO buss choise NVIC_base .equ 0xe000e000 ; Nested Vectored Interrupt Controller GPIO_KEY .equ 0x4c4f434b ; Key value to unlock configuration registers RCGCUART .equ 0x400FE618 ; Enable UART port RCGCGPIO .equ 0x400fe608 ; Enable GPIO port ;***************************************************** ; ; Use as offset together with base-definitions above ; ;***************************************************** UARTDR .equ 0x0000 ; Data register UARTFR .equ 0x0018 ; Flag register UARTIBRD .equ 0x0024 ; Baud rate control1 UARTFBRD .equ 0x0028 ; Baud rate control2 UARTLCRH .equ 0x002c ; UARTCTL .equ 0x0030 ; Control register GPIODATA .equ 0x03fc ; Data register GPIODIR .equ 0x0400 ; Direction select GPIOIS .equ 0x0404 ; interrupt sense GPIOIBE .equ 0x0408 ; interrupt both edges GPIOIEV .equ 0x040c ; interrupt event GPIOIM .equ 0x0410 ; interrupt mask GPIORIS .equ 0x0414 ; raw interrupt status GPIOMIS .equ 0x0418 ; masked interrupt status GPIOICR .equ 0x041c ; interrupt clear GPIOAFSEL .equ 0x0420 ; alternate function select GPIODR2R .equ 0x0500 ; 2 mA Drive select GPIODR4R .equ 0x0504 ; 4 mA Drive select GPIODR8R .equ 0x0508 ; 8 mA Drive select GPIOODR .equ 0x050c ; Open drain select GPIOPUR .equ 0x510 ; pull-up select GPIOPDR .equ 0x514 ; pull-down select GPIOSLR .equ 0x518 ; slew rate control select GPIODEN .equ 0x51c ; digital enable GPIOLOCK .equ 0x520 ; lock register GPIOCR .equ 0x524 ; commit GPIOAMSEL .equ 0x528 ; analog mode select GPIOPCTL .equ 0x52c ; port control NVIC_EN0 .equ 0x100 ; Enable interrupt 0-31 NVIC_PRI0 .equ 0x400 ; Select priority interrupts 0-3 NVIC_PRI1 .equ 0x404 ; Select priority interrupts 4-7 NVIC_PRI7 .equ 0x41c ; Select priority interrupts 28-31 NVIC_PRI12 .equ 0x430 ; Select priority interrupts 48-51 ;; Definitions for context switching ;; CONFIG_MAX_TASKS .equ 10 ;; ;; os_task_t 32-bit sp ;; 32-bit adress ;; 32-bit status ;; ;; m_task_table: os_task_t tasks[OS_CONFIG_MAX_TASKS] ;; 32-bit current_task ;; 32-bit next_free ;; Variable base address location M_TASK_TABLE_BASE .equ 0x20002000 ; start adress of task table ;; Offsets to variables relative to base WLEN_M_TASK_TABLE .equ (3*CONFIG_MAX_TASKS) ; total number of 32-bit words in table LEN_M_TASK_TABLE .equ (WLEN_M_TASK_TABLE << 2) ; (3*CONFIG_MAX_TASKS)*4 M_TASK_CURRENT_OFFSET .equ LEN_M_TASK_TABLE ; current active task index M_TASK_NEXT_FREE_OFFSET .equ M_TASK_CURRENT_OFFSET+4 ; next free task entry in table ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Main program: Initialize, add 3 tasks, start 1st task main: ; Start of program bl inituart ; turn on serial port bl initGPIOD ; turn on port D (required by initint) bl initGPIOF ; turn on port F bl initint ; setup and enable interrupt ; based also partly on https://www.adamh.cz/blog/2016/07/context-switch-on-the-arm-cortex-m0/ ; Clear table and variables mov r1,#(M_TASK_TABLE_BASE & 0xffff) movt r1,#(M_TASK_TABLE_BASE >> 16) mov r0,#0 mov r2,#(WLEN_M_TASK_TABLE+2) ; size of m_task_table + two additional variables clear_m_task_table: str r0,[r1],#4 ; clear m_task_table values and M_TASK_CURRENT and M_TASK_NEXT_FREE subs r2,r2,#1 bne clear_m_task_table ;; Create task 1 adr r0,task1 mov r1,#(0x20001400 & 0xffff) ; stack pointer movt r1,#(0x20001400 >> 16) bl create_task ;; Create task 2 adr r0,task2 mov r1,#(0x20001600 & 0xffff) ; stack pointer movt r1,#(0x20001600 >> 16) bl create_task ;; Create task 3 adr r0,task3 mov r1,#(0x20001800 & 0xffff) ; stack pointer movt r1,#(0x20001800 >> 16) bl create_task ;; set current task to 1st task mov r4,#(M_TASK_TABLE_BASE & 0xffff) movt r4,#(M_TASK_TABLE_BASE >> 16) ldr r5,[r4,#M_TASK_CURRENT_OFFSET] ; Load current task index mov r6,#3*4 ; calculate offset into task table mul r5,r5,r6 ; 12 byte for each slot ldr r0,[r4,r5] ;; fetch stack pointer add r0,r0,#64 ; include stack frame msr PSP,r0 ; initialize PSP stack pointer ;; set_CONTROL(0x03) switch to PSP, unprivileged mode mov r0,#3 msr CONTROL,r0 ;; ISB after change of CONTROL isb ;; jump to handler add r5,#4 ; point to current task ldr r0,[r4,r5] bx r0 .align 4 task1: adr r4,string1 task1_loop: ldrb r0,[r4],#1 ands r0,r0,r0 beq task1 ; If we are at the end of the string, restart! bl printchar b task1_loop .align 4 string1: .string "Task 1 is running",13,10,0 ;;; ---------------------------------------------------------------------- .align 4 task2: adr r4,string2 task2_loop: ldrb r0,[r4],#1 ands r0,r0,r0 beq task2 ; If we are at the end of the string, restart! bl printchar b task2_loop .align 4 string2: .string "Task 2 is running",13,10,0 ;;; ---------------------------------------------------------------------- .align 4 task3: mov r0,#32 task3_loop: bl printchar add r0,#1 cmp r0,#128 bne task3_loop b task3 ;;; ---------------------------------------------------------------------- ;;; Argument: r0: Contains the start address of the task ;;; r1: Contains initial user stack pointer value ;;; ---------------------------------------------------------------------- create_task: cpsid i ; Disable interrupts here as we don't ; want any interrupts while we are ; messing with this data structure! add r0,r0,#1 ; indikera att koden är thumb ;; Find location in m_task_table for current free slot mov r4,#(M_TASK_TABLE_BASE & 0xffff) ; point to M_TASK_TABLE_SIZE movt r4,#(M_TASK_TABLE_BASE >> 16) ldr r5,[r4,#M_TASK_NEXT_FREE_OFFSET] ; Find index to free slot mov r6,#3*4 mul r5,r5,r6 ; 12 byte for each slot add r5,r4,r5 ; point to slot str r0,[r5,#4] ; store task adress str r1,[r5] ; store stack pointer value mov r6,#1 str r6,[r5,#8] ; status idle ;; create stack frame mov r6,#0x01000000 ;; XPSR default value str r6,[r1,#(15<<2)] ;; PSR copy on stack str r0,[r1,#(14<<2)] ;; task adress to PC copy on stack mov r6,#0x30000000 ;; Generate fault if return from task str r6,[r1,#(13<<2)] ;; LR copy on stack ldr r5,[r4,#M_TASK_NEXT_FREE_OFFSET] ;; size++ add r5,r5,#1 str r5,[r4,#M_TASK_NEXT_FREE_OFFSET] cpsie i bx lr .align 0x100 ; Place interrupt routine for GPIO port D ; at an adress that ends with two zeros ;********************************************** ;* ;* Place your interrupt routine for GPIO port D here ;* Not used in the example, only left due to use of lab2 template file ;* intgpiod: bx lr ; Here is the interrupt routine triggered by port D .align 0x100 ; Place interrupt routine for GPIO port F at an adress that ends with two zeros ;*********************************************** ;* ;* Place your interrupt routine for GPIO port F here ;* intgpiof: ; Here is the interrupt routine triggered by port F ; If the interrupt is created by the pushbutton on the Launchpad board ; then should a short delay be added to remove the effect of contact ; bounces from the simple swith. This is not needed if a debounced ; switch is used. mov r0,#(GPIOF_base & 0xffff) ; point at GPIOF register set movt r0,#(GPIOF_base >> 16) mov r1,#0x10 str r1,[r0,#GPIOICR] ; Remove interrupt request from port F ;; context switching ;; &m_task_table.tasks[m_task_table.current_task]; mov r0,#(M_TASK_TABLE_BASE & 0xffff) movt r0,#(M_TASK_TABLE_BASE >> 16) ldr r1,[r0,#M_TASK_CURRENT_OFFSET] mov r2,#3*4 mul r1,r1,r2 add r2,r0,r1 ; point at adress of current task table entry ;;m_task_table.task[m_task_current]->status = OS_TASK_STATUS_IDLE; mov r3,#1 ; Show IDLE state for task str r3,[r2,#8] ; show that current task is stopped ;;/* Select next task: */ ;;m_task_current++; ldr r1,[r0,#M_TASK_CURRENT_OFFSET] add r1,r1,#1 ;;if (m_task_current >= m_task_next_free) ;; m_task_current = 0; ldr r3,[r0,#M_TASK_NEXT_FREE_OFFSET] cmp r1,r3 bne skip_current_idx_reset mov r1,#0 skip_current_idx_reset: str r1,[r0,#M_TASK_CURRENT_OFFSET] cpsid i ;:/* ;;Exception frame saved by the NVIC hardware onto stack: ;;+------+ ;;| | <- SP before interrupt (orig. SP) ;;| xPSR | ;;| PC | ;;| LR | ;;| R12 | ;;| R3 | ;;| R2 | ;;| R1 | ;;| R0 | <- SP after entering interrupt (orig. SP + 32 bytes) ;;+------+ ;;Registers saved by the software (intgpiof): ;;+------+ ;;| R7 | ;;| R6 | ;;| R5 | ;;| R4 | ;;| R11 | ;;| R10 | ;;| R9 | ;;| R8 | <- Saved SP (orig. SP + 64 bytes) ;;+------+ ;;*/ ;;/* Save registers R4-R11 (32 bytes) onto current PSP (process stack ;; pointer) and make the PSP point to the last stacked register (R8): ;; - The MRS/MSR instruction is for loading/saving a special registers. ;; - The STMIA inscruction can only save low registers (R0-R7), it is ;; therefore necesary to copy registers R8-R11 into R4-R7 and call ;; STMIA twice. */ mrs r1, psp ; get psp stack pointer value subs r1, #16 stmia r1!,{r4-r7} mov r4, r8 mov r5, r9 mov r6, r10 mov r7, r11 subs r1, #32 stmia r1!,{r4-r7} subs r1, #16 ;;/* Save current task's SP: */ str r1, [r2] ;;next_task = &m_task_table.tasks[m_task_table.current_task]; ldr r1, [r0,#M_TASK_CURRENT_OFFSET] mov r2,#3*4 mul r1,r1,r2 add r1,r1,r0 ;;next_task->status = OS_TASK_STATUS_ACTIVE; mov r0,#2 str r0, [r1,#8] ;;/* Load next task's SP: */ ldr r0, [r1] ; get stack of next task ;;/* Load registers R4-R11 (32 bytes) from the new PSP and make the PSP ;; point to the end of the exception stack frame. The NVIC hardware ;; will restore remaining registers after returning from exception): */ ldmia r0!,{r4-r7} mov r8, r4 mov r9, r5 mov r10, r6 mov r11, r7 ldmia r0!,{r4-r7} msr psp, r0 ;;/* EXC_RETURN - Thread mode with PSP: */ mov r0,#(0xfffffffd & 0xffff) movt r0,#(0xfffffffd >> 16) ;;/* Enable interrupts: */ cpsie i bx r0 .align 0x100 ; Next routine is started at an adress in the program memory that ends with two zeros ;******************************************************************************************************* ;* ;* Subrutines. Nothing of this needs to be changed in the lab. ;* .align 2 ;* DELAY: ;* r1 = number of ms, destroys r1 DELAY: push {r0} loop_millisecond: mov r0,#0x1300 loop_delay: subs r0,r0,#1 bne loop_delay subs r1,r1,#1 bne loop_millisecond pop {r0} bx lr ;* inituart: Initialize serial communiation (enable UART0, set baudrate 115200, 8N1 format) inituart: mov r1,#(RCGCUART & 0xffff) movt r1,#(RCGCUART >> 16) ldr r0,[r1] orr r0,#0x01 str r0,[r1] ; activate GPIO Port A mov r1,#(RCGCGPIO & 0xffff) movt r1,#(RCGCGPIO >> 16) ldr r0,[r1] orr r0,#0x01 str r0,[r1] nop nop nop ; Connect pin 0 and 1 on GPIO port A to the UART function (default for UART0) ; Allow alt function and enable digital I/O on port A pin 0 and 1 mov r1,#(GPIOA_base & 0xffff) movt r1,#(GPIOA_base >> 16) ldr r0,[r1,#GPIOAFSEL] orr r0,#0x03 str r0,[r1,#GPIOAFSEL] ldr r0,[r1,#GPIODEN] orr r0,#0x03 str r0,[r1,#GPIODEN] ; Set clockfrequency on the uart, calculated as BRD = 16 MHz / (16 * 115200) = 8.680556 ; => BRDI = 8, BRDF=0.6805556, DIVFRAC=(0.6805556*64+0.5)=44 ; Final settting of uart clock: ; 8 in UARTIBRD (bit 15 to 0 in UARTIBRD) mov r1,#(UART0_base & 0xffff) movt r1,#(UART0_base >> 16) mov r0,#0x08 str r0,[r1,#UARTIBRD] ; 44 in UARTFBRD (bit 5 to 0 in UARTFBRD) mov r0,#44 str r0,[r1,#UARTFBRD] ; initialize 8 bit, no FIFO buffert, 1 stop bit, no paritety bit (0x60 to bit 7 to 0 in UARTLCRH) mov r0,#0x60 str r0,[r1,#UARTLCRH] ; activate uart (0 to bits 15 and 14, 0 to bit 11, 0x6 to bits 9 to 7, 0x01 to bits 5 downto 0 in UARTCTL) mov r0,#0x0301 str r0,[r1,#UARTCTL] bx lr .align 0x10 ; initGPIOD, set pins 2,3,6,7 as inputs ; destroy r0, r1 initGPIOD: mov r1,#(RCGCGPIO & 0xffff) movt r1,#(RCGCGPIO >> 16) ldr r0,[r1] orr r0,#0x08 ; aktivera GPIO port D clocking str r0,[r1] nop ; 5 clock cycles before the port can be used nop nop mov r1,#(GPIO_HBCTL & 0xffff) ; do not use ahb for GPIOD movt r1,#(GPIO_HBCTL >> 16) ldr r0,[r1] bic r0,#0x08 ; use apb bus for GPIOD str r0,[r1] mov r1,#(GPIOD_base & 0xffff) movt r1,#(GPIOD_base >> 16) mov r0,#(GPIO_KEY & 0xffff) movt r0,#(GPIO_KEY >> 16) str r0,[r1,#GPIOLOCK] ; unlock port D configuration register mov r0,#0xcc ; Allow the 4 pins in the port to be configured str r0,[r1,#GPIOCR] mov r0,#0x0 ; all are inputs str r0,[r1,#GPIODIR] mov r0,#0 ; all pins are GPIO pins str r0,[r1,#GPIOAFSEL] mov r0,#0x00 ; disable analog function str r0,[r1,#GPIOAMSEL] mov r0,#0x00 ; Use port D as GPIO without special functions str r0,[r1,#GPIOPCTL] mov r0,#0x00 ; No pullup on port D str r0,[r1,#GPIOPUR] mov r0,#0xff ; all pins are digital I/O str r0,[r1,#GPIODEN] bx lr ; initGPIOF, set pin 0-3 as outputs, pin 4 as input with pullup ; destroys r0, r1 initGPIOF: mov r1,#(RCGCGPIO & 0xffff) movt r1,#(RCGCGPIO >> 16) ldr r0,[r1] orr r0,#0x20 ; activate GPIO port F str r0,[r1] nop ; 5 clock cycles before the port can be used nop nop mov r1,#(GPIO_HBCTL & 0xffff) ; Choose bus type for GPIOF movt r1,#(GPIO_HBCTL >> 16) ldr r0,[r1] bic r0,#0x20 ; Select GPIOF port connected to the apb bus str r0,[r1] mov r1,#(GPIOF_base & 0xffff) movt r1,#(GPIOF_base >> 16) mov r0,#(GPIO_KEY & 0xffff) movt r0,#(GPIO_KEY >> 16) str r0,[r1,#GPIOLOCK] ; unlock port F configuration registers mov r0,#0x1f ; allow all 5 pins to be configured str r0,[r1,#GPIOCR] mov r0,#0x00 ; disable analog function str r0,[r1,#GPIOAMSEL] mov r0,#0x00 ; use port F as GPIO str r0,[r1,#GPIOPCTL] mov r0,#0x0f ; use bit 0-3 as outputs (do NOT press the black buttons on Darma!) str r0,[r1,#GPIODIR] mov r0,#0 ; all pins is used by GPIO str r0,[r1,#GPIOAFSEL] mov r0,#0x10 ; weak pull-up for pin 4 str r0,[r1,#GPIOPUR] mov r0,#0xff ; all pins are digitala I/O str r0,[r1,#GPIODEN] bx lr ; initint, initialize interrupt management ; destroys r0,r1 ; Enable interrupts from pin 7 port D and pin 4 port F initint: ; disable interrupts while configuring cpsid i ; Generate interrupt from port D, GPIO port D is interrupt nr 3 (vector 19) ; positiv edge, high priority interrupt ; Generete interrupt from port F, GPIO port F is interrupt nr 30 (vector 46) ; positiv edge, low priority interrupt ; GPIO port F setup ; interrupt activated by input signal edge mov r1,#(GPIOF_base & 0xffff) movt r1,#(GPIOF_base >> 16) mov r0,#0x00 ; edge detection str r0,[r1,#GPIOIS] ; clear interrupts (unnecessary) mov r0,#0xff ; clear interrupts str r0,[r1,#GPIOICR] ; Enable positive edge (ignore falling edge) mov r0,#0x00 ; Use IEV to control str r0,[r1,#GPIOIBE] mov r0,#0x10 ; rising edge str r0,[r1,#GPIOIEV] ; clear interrupt mov r0,#0xff ; clear interrupts str r0,[r1,#GPIOICR] ; enable interrupts from bit 4 mov r0,#0x10 ; Send interrupt to controller str r0,[r1,#GPIOIM] ; NVIC setup to handle GPIO port F generated interrupt requests ; NVIC_priority interrupt 30 mov r1,#(NVIC_base & 0xffff) movt r1,#(NVIC_base >> 16) ldr r0,[r1,#NVIC_PRI7] ; Set priority 5 mvn r2,#0x00e00000 ; clear bits 23 downto 21 and r0,r2 orr r0,#0x00a00000 str r0,[r1,#NVIC_PRI7] ; NVIC_enable allow interrupt nr 30 (port F) ldr r0,[r1,#NVIC_EN0] orr r0,#0x40000000 str r0,[r1,#NVIC_EN0] ; GPIO Port D setup ; interrupt generated by positive edge mov r1,#(GPIOD_base & 0xffff) movt r1,#(GPIOD_base >> 16) mov r0,#0x00 ; edge detection str r0,[r1,#GPIOIS] ; clear interrupts (unnecessary) mov r0,#0xff ; clear interrupts str r0,[r1,#GPIOICR] ; ignorera fallande flank mov r0,#0x00 ; Use IEV to control str r0,[r1,#GPIOIBE] ; stigande flank edge mov r0,#0xcc ; rising edge str r0,[r1,#GPIOIEV] ;clear interrupts mov r0,#0xff ; clear interrupts str r0,[r1,#GPIOICR] ; enable interrupts from bit 7 mov r0,#0x80 ; Send interrupt to controller str r0,[r1,#GPIOIM] ; NVIC management of interrupts from GPIOport D ; NVIC_priority interrupt setup mov r1,#(NVIC_base & 0xffff) movt r1,#(NVIC_base >> 16) ldr r0,[r1,#NVIC_PRI0] ; Set priority 2 bic r0,r0,#0xe0000000 ; clear bits 31-29 orr r0,r0,#0x40000000 str r0,[r1,#NVIC_PRI0] ; NVIC_enable port D interrupt ldr r0,[r1,#NVIC_EN0] orr r0,#0x00000008 ; enable interrupt nr 3 str r0,[r1,#NVIC_EN0] ; enable interrupts cpsie i bx lr ; Start adress in r3, string terminated with the value 0 ; destroy r0, r1, r3 slowprintstring: push {lr} nextchar: ldrb r0,[r3],#1 cmp r0,#0 beq slowprintstringdone bl printchar mov r1,#40 bl DELAY b nextchar slowprintstringdone: pop {lr} bx lr printchar: ; Print character located in r0 (bit 7 - bit 0) ; Check bit 5 (TXFF) in UART0_FR, wait until it is 0 ; send bit 7-0 to UART0_DR push {r1} loop1: mov r1,#(UART0_base & 0xffff) movt r1,#(UART0_base >> 16) ldr r1,[r1,#UARTFR] ands r1,#0x20 ; Check if send buffer is full bne loop1 ; branch if full mov r1,#(UART0_base & 0xffff) movt r1,#(UART0_base >> 16) str r0,[r1,#UARTDR] ; send character pop {r1} bx lr