SE350 Lab
Taking notes for these labs because I actually want to understand what is going on, instead of just copying pasting the commands.
Our code https://git.uwaterloo.ca/ecese350/group27-lab/-/blob/deliverable2/Core/Src/k_task.c?ref_type=tags
The way you start things up…
Somewhere in your main, you should initialze your kernel
We implemented a preemptive scheduler, but the preemption is done inside the task itself by calling osYield()
.
osYield
makes a SVC call
This looks like the following:
- this consists of initializing your TCBs
- Then you create our tasks back in your main
This is what the task looks like:
So how does osYield
work?
- it makes a SVC call, because remember that switching between processes needs to be done in Kernel Mode, but we are generally in User Mode
Deliverable 3
I need some reminders for myself as to why we use the SVC. In theory, everything can be done at the user-level, and you don’t need to worry about making all these annoying system calls. But the reason we have system calls is because we want this level of separation between different tasks.
- Using system calls with the SVC provides a way to enforce rules and protect system resources
There are certain things you can only access through the SVC, like the stack pointer. You move the stack pointer to another address.
I need to understand what this does:
High Level
This code saves the context of the current task, calls a function to switch tasks, and then restores the context of the new task.
tst lr, #4
: This tests the 4th bit of the link register (LR). The result of this test determines whether the main stack pointer (MSP) or the process stack pointer (PSP) was used before the interrupt.ite eq
: This stands for “If Then Else”. It’s used to conditionally execute the following instructions based on the result of the previous test.mrseq r0, MSP
 andÂmrsne r0, PSP
: These instructions read the value of the MSP or PSP into register r0, depending on the result of the test.mov r1, r0
 andÂstmdb r1!, {r4-r11}
: These instructions save the current context (specifically, registers r4-r11) onto the stack.msreq MSP, r1
 andÂmsrne PSP, r1
: These instructions update the MSP or PSP with the new top of stack.blx SVC_Handler_Main
: This calls the main SVC handler function, which performs the actual task switch.MRS R0, PSP
,ÂLDMIA R0!, {r4-r11}
, andÂMSR PSP, R0
: These instructions restore the context of the new task from the stack.MOV LR, #0xFFFFFFFD
: This sets the link register to a special value that tells the processor to use the PSP when it returns from the interrupt.BX LR
: This returns from the interrupt, resuming execution of the new task.
I have some logic, because the function can call OsSleep, OsYield, and OsSetDeadline, but that is when the stack pointer is in PSP.
- However, we also need the logic to work in MSP. So we make SVC calls depending on the context, add a flag
Deliverable 2
Alright, I kind of slacked off working on this second part. Need to catch. Basically, need to figure out how to allocate memory for different processes.
The HM is initialized to 0x200083c0
- the pointer is
0x200084c0
(which is correct because HM is 16 bytes) NO - whatever, so this is the line (ptr + sizeof(HM), which points it to the data itself. If you substract sizeof(HM), you get the HM
- The pointer from two_task_1 should be
0x200083d0
The issue is caused by bad pointer arithmetic. This is our k_mem_init
So updated with correct numbers that you should expect:
- The HM is initialized to
0x20008a3c0
- The pointer should be
0x200083d0
(which is where HM points to + 16) - dealloc, and then alloc again should give the same pointer
The address space goes in the negative direction (up) as the stack grows
- Forget this, is just just for the stacks going up, but the numbers go down
Just think of the heap going down
- The pointer is 83d0
I’m confused, the new numbers are
- The HM is initialized to HEAP_START:
0x20008210
- The pointer should be
0x20008220
(which is where HM points to + 16) - dealloc, and then alloc again should give the same pointer
So for test case 3:
- First block:
0x20008420
I am now testing the fragmentation:
- checking the size of the stack seems to be 48624
Deliverable 1
The stack has a limit, we set that to 0x4000 bytes for now.
We are going to break it up and keep track of multiple stacks – one for the main stack and several for the thread stacks.
- So a stack of stacks
The main stack is going to be pointed by the MSP.
MSP_INIT_VAL
will store the location of the Main Stack Pointer
- This is initialized to
0x0
The addressing goes in the negative direction.
- The stack grows downwards
- The main stack size is to store general stack stuff
We now want to implement system calls, with the SVC.
Our OS will need to do most of its context switching via system calls, specifically using the SVC instruction and its sibling. PendSV. In particular, during a context switch we need to perform three tasks:
- Save the current thread’s context onto its stack
- Choose a new thread to run, and locate its stack
- Load the new thread’s context into the registers from its stack
So when we call __asm
, the SVC_Handler
automatically gets called (an interrupt gets called), and then we need to get into SVC_Handler_main