NOTE: The source code, as presented in the video, might cause compilation errors with the newer MDK-ARM / uVision toolsets. This is because the underlying compiler in MDK-ARM has been changed to "Compiler-6", while the older "Compiler-5" is considered now obsolete. The updated code that compiles cleanly with "Compiler-6" is available from the companion website at: www.state-machine.com/video-course and from GitHub: github.com/QuantumLeaps/modern-embedded-programming-course
This took me several iterations back and forth previous lessons to grasp what is going on! So each minute in the videos is so succinct. And of course this is the best embedded programming course I ever came across! Thanks for these fantastic lessons. I'm lucky to come across this right in my early searches.
Notes: In the earlier example, we were changing the value of PC to switch to different led functions. That basically means when an entry into an exception such as Systick Handler is made, the stack frame (which comprises R0, R1, R2, R3, R12, LR, PC and xPSR) are saved on the stack. Then the address of the exception is fetched from the vector table by NVIC, and the processor starts executing the exception handler function. So when we changed the value of PC in the stack frame, we were able to switch to another function. However in the last example, we essentially created two stack frames for each LED function. And just when the exception handler was about to return, we changed the value of stack frame to one of our stack frame. Our personal stack frame had the context for our particular blinky function saved (i.e. R0-R3,R12,LR,PC,xPSR), so the processor was tricked into thinking that it saved this particular stack frame and popped the value of PC and LR from this stack frame. And thus we made the switch to another function lets say green led. After we executed it few times, we again put a breakpoint on the Systick handler, And this time we retrieve the value of stack frame of the green led function. However this time the top of green led stack frame is legitimate and has the correct value of return instruction. Now we put the value of blue led fabricated stack frame, and again trick the processor into thinking that this is the stack frame which was pushed on it, upon entry to the exception handler. And the blue led function runs now. If we break at systick handler now, we will have a legitimate stack frame for blue led as well. Thus we have two legitimate stack frames for context switch, and now upon context switching we would return to the original address, where the exception entry was made.
if you need to monetize to create more content in less time, I would definitely pay for this quality of instruction. Better than any class I had at university by far.
If you really wish to help, please spread the word about this video course! You could make posts or comments on: stackoverflow, reddit, quora, linkedin, facebook, or wherever you see fit. People constantly ask about specific subjects, about fundamental concepts, and about learning resources. This course is such a resource, but it is still relatively unknown. Please help others to find it and make this course more popular in the process. --Miro
I have been going back and forth in your all videos, especially RTOS ones. All of them are extremely valuable! I really appreciate your efforts in making these wonderful tutorials and hope see more of them in future!
This is really insightful and easy to follow at the same time. Your lessons are awesome! As I'm self-taught at embedded programming these deeper-level topics are really interesting, because digging all of this up on my own would have taken loads of time. Thanks!
Note-2: Also when we make entry to exception handler, register R0-R3,R12,LR,PC and xPSR are saved. But R4-R11 are not. Therefore if the ISR does use any of the registers R4-R11, it will restore the original value before returning to the original functions. Thus no harm done. However if we are returning to some other function, than this will create a problem. Since we would be returning the value of register meant for the previous function, and not to the function which we are jumping into. Thus we have two situtations, one is the automatic saving of registers (R0-R3,R12,LR,PC and xPSR) upon entry to the exception handler as guaranteed by ARM procedure call. But to ensure integrity we have to additionally save register R4-R11 as well. But the second thing needs to be done manually !! (or by coding, :-p) ,, --> we also have to decrement the stack pointer by 0x20 (32) --> so the 8 registers(R4-R11), each of 4 bytes could be accounted for in the stack frame.
Hello Miro, I've got a question, isnt global data(i.e the array) placed somewhere else other than the stack(.bss since this isnt initialized), how did we assume this to be a stack frame and poping worked on it? Thanks.
Global or static data is not placed on the C stack, of course. But in this lesson, we *made* the stack out of a global array by forcing the CPU stack pointer register to point into that array.
Thank you so much for the amazing video! One question though, at 21:11, the solution is to save the additional registers R4-R11 to the thread stack. The timing diagram shows they are saved after the ISR returns. Can we save them before the ISR preempts (i.e. save them at the same time as other registers like R0-R3, etc.)?
In order to save registers before an ISR is not possible, because you don't know when the interrupt will occur. Once it occurs and your ISR starts running, the interrupt stack frame is already saved on the stack. --MMS
Hello Doc, First of all thank you for your work on this. Its good stuff.I have a couple of questions for you. At around the 16:50 mark: You mention that we would have to save the current top of stack for the blink1 thread before attempting to preempt with with the blinky2 thread. Is this because points of preempting of at the blinky1 thread would generate the corresponding exception frame? And the reason we do this is so that we can save the context to return to where we left off , once blink2 has been preempted by blinky1? I think if didn't do that and go with initial values that we populated the stack frame for blink1 with, we would simply resume from the beginning which I suppose is okay for simple blinking examples but not so for more complex examples where more info is saved before a context switch. Is this understanding correct? Thank you for your time in advance!
I've read your comment carefully, but I don't understand yor question. However, the most important thing I want you to remember is that all of this context switching is really utilizing the *interrupt* handling mechanism of the ARM Cortex-M CPU (hardware mechanism). Everything that transpires during a context switch follows from the understanding of that mechanism. In particular, the preemption you see at 16:50 has already happened and was caused by the SysTick interrupt. At 16:50 you merely change the stack pointer (SP) to control the *return* from interrupt. So, I think your problem might be that you think incorrectly about the "preemption". The context switching code does not cause preemption, it merely uses it. The preemption has to happen in hardware, so in case you need to create it at will, you need to trigger an interrupt. You'll see in the later lessons how to do just that and utilize the special PendSV interrupt for it. --MMS
@@StateMachineCOM I'm sorry if I wasnt clear. I was referring to the fact that stack pointer value that you had to save manually. It is different from the one that was initial value that we set in order to begin execution. If we were to use this initial value, we would begin execution from the beginning of the theard, which is okay if we are doing it for the first time , so as it get things going. As I understand it, Context switching is to be done in such a way as to resume from the point of preemptiion, and not to start over at the beginning. That is to say if we are to supply the incorrect context, the thread may not resume from where it left off. In this particular example, after the thread is preempted, you save the new stackpointer value (to correctly record this context) such that when control is restored to this thread , it picks up from where it left off when it was preempted as opposed it starting from the beginning if we had supplied it the initial sp value. So when a thread is preempted at different stages of its execution, the corresponding exeption stack that is generated with with a sp value is to be used to get back and to the point from where it was preempted. I apologise if this sounded a bit repetitive. I just wanted to get my point access. and yes I did believe that I've misunderstood preemptiion and context switching . It is interrupt premeptuin that allows us to switch away from the normal sequential execution. It is during this premeptuin that were are able ti change the context by altering the values if the sp so that after premption the flow of execution is able to return to point from what the sp points to. I hope this is clear
@@robbieschultz3559 Yes, of course! Every preempted thread needs to continue from the point of preemption. Starting over from the beginning every time would make no sense, would it? In fact, you can think of the private stack of a thread as a "bookmark", which remembers precisely where a thread was preempted, so that it can be resumed from that exact point. The stack "bookmark" is very sophisticated because it can remember very complex patterns of thread execution. For example, a thread can call a function, that calls another function, which gets preempted. The stack "bookmark" remembers all this, so that it can resume from the nested function call and correctly continue by returning from the second function, then returning from the first function, etc. I hope you can see how complicated things can get and how powerful the concept of the stack is. --MMS
Hi Sir, Thanks for the series of great videos. I have an query here as my newbie in the embedded world. Could you please tell me that why didn't you save R12 on the stack while you stored R11 down to R4 on the stack (Last of this video : 21:53 time line)? I am seeing that SP is set as 0x200000088 on the stack and above SP , we must have R12 then followed by R11-R4 while you showed a red block (R11-R4) copied just above 0x200000088 which i felt , it must be two addresses up from SP (R13) and R12 then it would be aligned. Regards, Manish
The complete stack frame for a thread's context consists of two parts: (1) the registers saved in hardware upon the interrupt entry and (2) registers saved on top of that by the software. The choice of the software-saved registers is dictated by what the hardware already saved, because you don't want to repeatedly save the same registers and you don't want to miss any either. Also, you might want to watch lesson 9 about ARM Application Procedure Call Standard, which explains the registers saved during a function call. --MMS
It is to account for the 8 values that had to be manually copied from registers R4-R11 onto the top of the current ISR stack frame at 21:44 (8 values * 4 bytes each = 32 bytes = 0x20 bytes in hex).
hey DR Miro when i breakpoint at the first time at the of systick_hnadler unlike your call stack , which contains both SysTick_Handler and main mine only contains SysTick_Handler at addreess 0x00000000 which seems weird . additionaly after having the right adresses of sp_blinky1\2 stack , i hit run and again unlike your call stack i have calls ti BSP_tickCtr twice and call to SysTick_hnadler . when i run free only blinky1 main loop executes .i think the two issues are connected and somehow the one of BSP_tickCtr (maybie belongs to the callstack of the blue led main loop) returns to blinky_1 instead to blinky2 (but again in your call stack it does not even appear). do u have any idea?
In situations like that, I highly recommend that you download the official project for this lesson (lesson-22.zip) from the companion web page ( www.state-machine.com/video-course ). Back up your current project, and unzip the official project. Try the official project, which should work. Then compare the original to your own (a good free tool is WinMerge). The problem must be in the difference. I hope this strategy makes sense to you. --MMS
hey Dr Miro @@StateMachineCOM thanks for the fast response , this is exactly what i did . should i have all the directories & files under the project ? i use keil ide but i did not quite undersood the readme file . my project builds sucssesfuly and run (besides the context ) , maybie i had to remove a file from the project ? do we use arm or gcc in this ide ? maybie its related somehow
Hi Miro, thanks a lot for the RTOS series of videos. I have a silly question, specifically on this example. Where is the interrupt used in this code, What is the ISR doing in this example? Is it used for the delay? Particularly from the plot /illustration of 10:53, why is ISR happening non-periodically and why are they of different duration?
"Where is the interrupt used in this code?" -> This code uses the periodic SysTick interrupt, which is configured in BSP_init() inside bsp.c file. "What is the ISR doing in this example" -> The SysTick ISR is only incrementing a counter variable. But it is also used to manually simulate context switching, by setting a breakpoint inside the ISR and then changing things manually inside the CPU using the debugger. "Why is ISR happening non-periodically and why are they of different duration?" -> The ISR used for context switching does not need to be periodic and it does not need to have the same duration each time. These things simply don't matter, and therefore the timing diagram at 10:53 shows a general case. --MMS
hi Miro. Can you talk about firts setting with Debug in Keil-C. I do everything the same as you but the result is different. BX lr don't jump to main_blinky1 and display in Call Stack + Local also not the same you. Thank you so much about 2 previous response. Hope to receive your reply soon.
It's difficult for me to talk you through setting up the KEIL uVision debugger in this RUclips comment. You just need to watch the video and try it on your host computer. But regarding the BX LR instruction that returns from the SysTick interrupt, it will NEVER go to main_blinky1 or main_blinky2 unless you manually change something in the CPU. For example, at 7:09 you change the stack entry corresponding to the PC register, and only after that change you execute the BX LR instruciton. This change causes the BX LR to return to your main_blinky1. Such "manual context switching" is the main point of this lesson. --MMS
@@StateMachineCOM i understand returns from the SysTick interrupt. I had change the value of SP same to main_blinky1 or main_blinky2 but it not go back what i want :
@@thucngoxuan9011 I think that you must be doing something else than what is shown in the video. Please note that the video progresses in a few steps and at each step you need to do something different. For example, at 7:09 you need to change a value in the stack memory. Later, at 15:55 you change the SP register in the CPU. So, please pay attention to what needs to happen at wich stage. Finally, if you cannot get your code to do what's shown in the video, you should download the project accompanying this lesson (from the companion web-page at www.state-machine.com/video-course ) and use that project. --MMS
The interrupt stack frame is larger when the FPU is used. But there is more to it. An excellent description of the issues is published in the ARM Application Note 298 (just google for it) "Cortex-M4(F) Lazy Stacking and Context Switching". --MMS
Why should the Blue-LED go off when a preemption happens? I mean, if the preemption happens while the Blue-LED is on, it *should* stay on as long as the thread main_blinky1 is not running. This is because only main_blinky1 turns the Blue-LED off. --MMS
Hello miro , hope your doin good . I recently noticed something when I debugged this lesson , when u switch context , say u want switch to blinkey1 , u put sp_blinky1 in sp CPU register , but before this u save the OLD sp CPU value in sp_blinkey2 . in the video , every time u do this the sp CPU must be less than the old sp_blinkey2 by 2 words sp_blinkey1 is either (0x20000090) or (0x20000088) , sp_blinkey2 is either (0x20000130) or (0x20000128), , but with me there is no difference, sp_blinkey1 is always (0x20000090), sp_blinkey2 is always (0x20000130), as u notice : the sp CPU is exactly the same previous sp_blinky when i hit the breakpoint . is it normal ?
hello mr miro i just want to understand __just in brief definition__ what do we mean by word (kernel) in our course ....and thanks a lot . i ve already googled but i am little confused !
First thanks for great lessons, I am going through RTOS lesson again after a year. I have one doubt. At 12:50, you are explaining that we don't need extra aligned unit for task specific stack as its already 8 byte boundary aligned. I am not able to understand reason. I watched that portion multiple times. Can you please tell the reason in comment box ?
Yes, you are right that the alignment of the arrays `stack_blink1[]` and `stack_blinky2[]` at 8-byte boundary is not guaranteed at this point. The ARM-KEIL compiler happens to align the arrays that way, but in principle, another, standard-compliant compiler (or the linker for that matter) could align them differently (only on 4-byte boundary.) To guarantee the 8-byte-boundary alignment, you could add `__attribute__ ((aligned (8)))` before or after the declarations of the stack arrays, but this is a non-portable extension to the C language. In the next lesson about the RTOS, you would see how this will be implemented in the "MiROS" RTOS, in the OSThread_start() function. Specifically, you'll see that the top of stack will be aligned explicitly. --MMS
@@StateMachineCOM Thanks for reply. Yes in next lecture we are taking care of it. Also would like to put a thank you note for helping and motivating me & many other embedded engineers through your work.
Hello Sir, thanks for the video. I have a problem with this. After changing my stack address in debugger with my created stack, the stacks on the debugger doesn't change values. But the functions are working. Hope you can help me solve.!!
Miro, here you mention that "an ISR must necessarily run to completion before returning to the preempted code". And I'm wondering what that means in terms of ISR preempted by an ISR of higher priority. If I were to use a while loop as the idle thread and have 2 or 3 ISRs running at different rates and each with a different priority level, also each one more computationally burdensome on the CPU than simple blinky ( I am thinking motor control applications) would it still be necessary to store registers R4-to-R11 for meeting hard and soft real time deadlines? Because it almost seems like a higher priority would preempt a low priority ISR, and all ISRs would eventually run to completion before returning to the idle thread.
Yes, this lesson should be applicable to Arduino boards based on the ARM Cortex-M CPU, such as Arduino DUE, Arduino Zero, or Arduino MKR, and perhaps others. (At the same time, the code presented here will NOT work on AVR-based Arduinos, such as Arduino UNO.) Also, if you are interested in Arduino, you might want to check out QP-arduino adaptation on GitHub github.com/QuantumLeaps/qp-arduino
Thank you very much for this wonderful course! BTW, the transcript for lesson 16 and 21 are missing on your official website. When click on the txt file it brings you to a page saying file is missing.
question: could you please more clearly why the aligner stack entry is not necessary? ( my platforme is cc2640R2 Launchpad from Ti with CCS toolset) .. thanks in advanced
Stack aligner is necessary only when the stack before the interrupt is misaligned. Misaligned stack means that the value in the SP register is not divisible by 8. Normally a stack misalignment should not happen, because the compiler avoids pushing an odd number of registers. But in case there is some assembly code (not generated by the compiler), a misalignment could happen. In the following lessons on RTOS, you will see some assembly code to perform context switch, but this assembly code will be careful to avoid stack misalignment. Regarding your CC2460R2 LaunchPad board, it is not appropriate for this course. To really follow along, you need the TivaC123 LaunchPad. --MMS
Yes it could! The risk of stack overflow is multiplied by the number of threads in an RTOS, because each and every private stack of a thread can overflow. So, you need to be extremely careful and you need to size every stack properly. This is one of the biggest problems of using a traditional RTOS.
Hello Dr Miro. Thanks for the wonderful tutorial. I am trying to create same application on NXP Kl25z freedom board, however when I try to switch stack entry from CPU stack to blinky1/2 stack, by loading SP register with value of blinky 1/2 stack pointer variable at sys tick interrupt exit (at BX lr instruction), the hard fault interrupt is triggered. When I try to swtch from main thred to blinky thread by changing PC address in main CPU stack (at sys tick interrupt exit) it works properly. I am using Keil uvision 5 IDE. I am facing similar issue in IAR but there hard fault interrupt occurs after running blinky thread for few msec. Appreciate your help on this!
To use the code discussed in this video course directly, you really need the same board as used in this course (TivaC LaunchPad). Other boards, even if based on ARM Cortex-M CPU, differ just enough so that the code will NOT work without some changes. For example, your KL25Z Freedom board is based on Cortex-M0+, which is significantly different from the Cortex-M4 in the TivaC LaunchPad. Also, the GPIO structure (used to blink the LEDs) is very different in the NXP processors compared to the TI processors. But TivaC LauchPad costs only $12.99, so perhaps you could afford it to benefit from this course. I really hope this makes sense to you. --MMS
Thanks for the reply. I could try this on Tiva board. Actually I have followed all of your earlier tutorials and created all applications on NXP KL 25z including led blinking and also found that the exception entries (stack frame) for Cortex M0 and Cortex M4 are also similar. Also, the code that I have written for Kl25z based on this tutorial is also same as that you have written for Tiva board, so I was hoping to have it working on kl25z. Is there any generic way that MIROS will work independent of arm architecture/board? Thank you again for the help!
Looks like manually changing SP register in IDE causing change in the debug instructions and thus triggering hard fault interrupt. Finally I could able to run multiple threads on KL25Z freedom board with automatic thread switching as mentioned in your next tutorial with little modifications. Really helpful tutorials and lot to learn. Thanks!!
"But in your case, the ISR does NOT return to the preempted code, but rather to another thread: blinky2. This other thread can also use the R7 register and as any function is also obligated by the AAPCS to restore the R7 upon its return. But you are not executing the whole thread function but rather just a piece of it. This piece of code doesn't need to comply with the AAPCS and it can change the value in R7." What exactly is meant by not executing the whole thread function, but rather a piece of it?
When you call a C function, you execute the whole function from the beginning to the end. This is important, because at the beginning of a function the compiler generates special code known as function prologue, which saves the required registers on the stack. Also, the right before returning from the function, the compiler generates special code known as function epilogue to restore the previously saved registers. However, when you return from an interrupt to a preempted thread function, you specifically DON'T execute the function from the beginning but rather only a piece of it from the point of preemption. This means that you generally don't execute the function prologue, so the registers required by the AAPCS are not saved. This is what is meant by saying the "the piece of code does not need to comply with AAPCS". --MMS
For all such problems, please visit the companion web-page to this video course advertised in every video and listed in the notes under every video: www.state-machine.com/quickstart/ . Specifically to driver issues, you might need to (re)install the Tiva/Stellaris USB Drivers on your Windows PC. The companion web-page provides a download link for these drivers as well as the USB drivers installation instructions (Tiva/Stellaris In-Circuit ICDI Manual). --MMS
Great video Miro. Did you know that according to our very complex video scoring system (sounds fancy but we simply do (likes-dislikes)/views), this video has the highest score of all the videos posted over the course of the last 12 months (for all vendors we monitor). See: www.embeddedrelated.com/vendors#tabs1-popyt and click on the 'one year' button. Great job!
I'm really glad to hear this. New video lessons are coming. The next lesson 27, RTOS part-6 will be about inter-thread synchronization with semaphores. Stay tuned! --Miro
Hi, just want to make sure I understand this correctly: why initialize the stack pointer variable for each thread to (length of stack + 1) instead of just the bottom of the stack ? Because in your code, you pre-decrement the stack pointer address before you start assigning values to the stack (so you're starting from the bottom of the stack regardless).
@ClassyJohn: As explained at 13:00, the ARM stack is the so-called *full stack*, which means that the SP points to the last used slot (as opposed to the last empty slot). Therefore, in the video, the stack pointer variable has been initialized to simulate a situation right before adding a new interrupt stack frame. For each stack entry, the ARM hardware would first decrement the SP and then would fill the stack memory. Please note that code efficiency is not the objective here. It is more important to faithfully simulate the hardware. I hope that this makes more sense now. --MMS
@ClassyJohn: The stack pointer variable is NOT initialized to (length-of-stack + 1). The initialization is sp = &stack_blinky1[40], which corresponds to the length-of-stack (without +1). But you need to remember that arrays in C start with the index 0, so &stack_blinky1[40] points one word beyond the end of stack_blinky1[40] array (see video at 11:45). This is an important aspect to understand about C arrays. Otherwise, you'll be making a lot of off-by-one errors in your code. --MMS
Hello Dr. Miro, I feel so lucky to find your tutorial series. I follow your tutorial with an STM32-L432KC board and the STM Cube IDE software. I managed to get the context switching done, but I had to fabricate a different stack. The STM32-L432KC MCU also uses an ARM Cortex-M4 CPU so I'm not sure why I had to change the stack pattern for blinky sp's. Here is what I had to come up after a lot of trial and error. // Fabricate Cortex-M ISR stack frame for blinky1 *(--sp_blinky1) = (1U
Hi Amila. I assume that you're talking about the ISR stack frame discussed at 14:35. I don't understand why you had to add 2 extra registers to that stack frame because it should be the same for all Cortex-M CPUs. I would check your brake point at the end of SysTick in case you still have some code that changes the SP (such as popping two additional registers). The point where you manually change the SP is critical and the video assumes that there are no instructions that would mess up with the SP. I hope you see what I mean. --MMS
I'm not sure why your second comment is not showing up here. But the GCC compiler apparently pops 2 registers at the end of SysTick_Handler (at the optimization level you chose). This is completely normal. To use the ISR stack frame shown in the video, you simply need to move your break point to the very last machine instruction of your SysTick_Handler in disassembly. --MMS
@@StateMachineCOM Thank you so much Miro. I managed to figure out the issue with the help of your response. Earlier, I had a function inside SysTick_Handler() that made some changes to the disassembly, popping two registers as you mentioned. I removed that function and used the stack fabrication as in the video, and it worked! Although there are some additional assembly instructions before the "bx lr" instruction that I'm not aware of. Anyways, I copied the blinky1 sp to the sp register before executing the "bx lr" instruction and it worked as expected. Here are the assembly instructions: SysTick_Handler: push {r7} add r7, sp, #0 } nop mov sp, r7 ldr.w r7, [sp], #4 bx lr Also, another interesting thing I found is that earlier, I didn't had the "bx lr" instruction. I'm not familiar with assembly, so debugging is a bit challenging for me. I will try to figure things out from here, thank you again Miro. You are a wonderful teacher!
Actually, I was very careful NOT to use the term "process", because it has a different meaning than "task" or "thread. On the other hand, the terms "task" and "thread" seem to be synonymous in the RTOS community. Virtually all RTOS manuals/books (VxWorks, ThreadX, MicroC/OS, FreeRTOS, Wikipedia RTOS article, etc.) use predominantly the word "task", but note that "thread" is also an alternative term for the same thing. The clarification of the terms "thread"/"process"/"task" can be found on StackOverflow at: stackoverflow.com/questions/3042717/what-is-the-difference-between-a-thread-process-task
I agree that _unfortunately_ 'task' and 'thread' uses simultaneously. I am quote old person and in my mind 'process', 'task' and 'thread' are different. With embedded systems these words are more 'synonymous' then it was with the big engines and this is why in my case I usually start lessons of RTOS with 'adjustment of therms'. From the classical point, the 'task' resources were created when a task was created with calls like 'task_create()', 'execv()', 'posix_spawn()', etc, derivate from general 'fork()'. From the other hand, 'fork()' has a strong correlation with a process. This is why in some OS there is also a 'task group'. In the link you've provided, from StackOverflow, task and thread are not the same as well. All these does not make your course bad! :)
Dear Miro, why not to put a link for donation for this channel and for your effort? This level of information can not be achieved with courses of thousands of dollars. Therefore I want to donate for this effort.
Thank you for the suggestion. It is very generous. But for anybody who would like to help, it would be actually more valuable if you could simply subscribe and spread the word about this video channel (share the links on forums, Reddit, Quora, LinkedIn, etc.). --MMS
Why is there overly loud music playing whenever you pause on talking. I would recommend for future videos just keep it silent so its not distracting, over-wise great video and thank you!
Note that the music is completely suppressed during narration and is mostly used to fill out the silence. Also, you can always use the Closed Captions by pressing the CC button at the bottom-right of the video. --MMS
The qassert.h file is located in the qpc/include directory. This is a very *useful* file that can be used standalone without the rest of the qpc framework. Also, the latest lesson #47 and #48 are precisely about assertions and Design by Contract in embedded systems. --MMS
NOTE: The source code, as presented in the video, might cause compilation errors with the newer MDK-ARM / uVision toolsets. This is because the underlying compiler in MDK-ARM has been changed to "Compiler-6", while the older "Compiler-5" is considered now obsolete. The updated code that compiles cleanly with "Compiler-6" is available from the companion website at:
www.state-machine.com/video-course
and from GitHub:
github.com/QuantumLeaps/modern-embedded-programming-course
This took me several iterations back and forth previous lessons to grasp what is going on! So each minute in the videos is so succinct. And of course this is the best embedded programming course I ever came across! Thanks for these fantastic lessons. I'm lucky to come across this right in my early searches.
Notes:
In the earlier example, we were changing the value of PC to switch to different led functions. That basically means when an entry into an exception such as Systick Handler is made, the stack frame (which comprises R0, R1, R2, R3, R12, LR, PC and xPSR) are saved on the stack. Then the address of the exception is fetched from the vector table by NVIC, and the processor starts executing the exception handler function.
So when we changed the value of PC in the stack frame, we were able to switch to another function.
However in the last example, we essentially created two stack frames for each LED function. And just when the exception handler was about to return, we changed the value of stack frame to one of our stack frame.
Our personal stack frame had the context for our particular blinky function saved (i.e. R0-R3,R12,LR,PC,xPSR), so the processor was tricked into thinking that it saved this particular stack frame and popped the value of PC and LR from this stack frame. And thus we made the switch to another function lets say green led.
After we executed it few times, we again put a breakpoint on the Systick handler, And this time we retrieve the value of stack frame of the green led function. However this time the top of green led stack frame is legitimate and has the correct value of return instruction.
Now we put the value of blue led fabricated stack frame, and again trick the processor into thinking that this is the stack frame which was pushed on it, upon entry to the exception handler. And the blue led function runs now. If we break at systick handler now, we will have a legitimate stack frame for blue led as well.
Thus we have two legitimate stack frames for context switch, and now upon context switching we would return to the original address, where the exception entry was made.
if you need to monetize to create more content in less time, I would definitely pay for this quality of instruction. Better than any class I had at university by far.
If you really wish to help, please spread the word about this video course! You could make posts or comments on: stackoverflow, reddit, quora, linkedin, facebook, or wherever you see fit. People constantly ask about specific subjects, about fundamental concepts, and about learning resources. This course is such a resource, but it is still relatively unknown. Please help others to find it and make this course more popular in the process. --Miro
@@StateMachineCOM absolutely!
There is nothing more to say than... i love you!
I have been going back and forth in your all videos, especially RTOS ones. All of them are extremely valuable! I really appreciate your efforts in making these wonderful tutorials and hope see more of them in future!
This series continues to get better and better. Keep up the good work!
Can't wait for next video. Thank you so much Miro for creating these videos.
The best explanation I've watched, thanks!
Excellent teacher and skilled firmware designer.
Thanks for your video.
This is really insightful and easy to follow at the same time. Your lessons are awesome! As I'm self-taught at embedded programming these deeper-level topics are really interesting, because digging all of this up on my own would have taken loads of time. Thanks!
the best 25 minutes of my day... :)
Best lesson of my whole year!
Quality content! Thanks for your uploads. I watched your videos before taking a course at school and I am well ahead of the class!
Note-2:
Also when we make entry to exception handler, register R0-R3,R12,LR,PC and xPSR are saved. But R4-R11 are not.
Therefore if the ISR does use any of the registers R4-R11, it will restore the original value before returning to the original functions. Thus no harm done.
However if we are returning to some other function, than this will create a problem. Since we would be returning the value of register meant for the previous function, and not to the function which we are jumping into.
Thus we have two situtations, one is the automatic saving of registers (R0-R3,R12,LR,PC and xPSR) upon entry to the exception handler as guaranteed by ARM procedure call. But to ensure integrity we have to additionally save register R4-R11 as well. But the second thing needs to be done manually !! (or by coding, :-p) ,, --> we also have to decrement the stack pointer by 0x20 (32) --> so the 8 registers(R4-R11), each of 4 bytes could be accounted for in the stack frame.
You are such a great teacher! Thanks for clarifying all these things
I am on the edge of my seat for the next video :)
Fantastic video Miro. It's even helpful for experienced engineers to refresh the basics. Waiting for next one. Keep up the good work.
I will share your channel !!!! Good job like this should not go unnoticed!!!
ONe of The MOST awaited videos RTOS!!! Great Thanks
so far the best video i have watched explaining rtos thank you so much
Excellent lessons (this and others)! Absolute brilliance! Top quality!
Really a great representation of context switching with programming and highlighted details....Thanks it will be a part of my vdo library on YT
Thank you sir. Your videos are great. You do not need to entertain the public by music. It interferes focusing on material.
thank you for taking time in making these videos
Welcome back Miro, I always waiting for you.
Very deep understanding to RTOS!
Hello Miro,
I've got a question, isnt global data(i.e the array) placed somewhere else other than the stack(.bss since this isnt initialized), how did we assume this to be a stack frame and poping worked on it?
Thanks.
Global or static data is not placed on the C stack, of course. But in this lesson, we *made* the stack out of a global array by forcing the CPU stack pointer register to point into that array.
Thank you so much for the amazing video! One question though, at 21:11, the solution is to save the additional registers R4-R11 to the thread stack. The timing diagram shows they are saved after the ISR returns. Can we save them before the ISR preempts (i.e. save them at the same time as other registers like R0-R3, etc.)?
In order to save registers before an ISR is not possible, because you don't know when the interrupt will occur. Once it occurs and your ISR starts running, the interrupt stack frame is already saved on the stack. --MMS
Thank you very much for excellent lesson. Can’t to see part 2.
Hello Doc,
First of all thank you for your work on this. Its good stuff.I have a couple of questions for you. At around the 16:50 mark:
You mention that we would have to save the current top of stack for the blink1 thread before attempting to preempt with with the blinky2 thread.
Is this because points of preempting of at the blinky1 thread would generate the corresponding exception frame?
And the reason we do this is so that we can save the context to return to where we left off , once blink2 has been preempted by blinky1?
I think if didn't do that and go with initial values that we populated the stack frame for blink1 with, we would simply resume from the beginning which I suppose is okay for simple blinking examples but not so for more complex examples where more info is saved before a context switch. Is this understanding correct?
Thank you for your time in advance!
I've read your comment carefully, but I don't understand yor question. However, the most important thing I want you to remember is that all of this context switching is really utilizing the *interrupt* handling mechanism of the ARM Cortex-M CPU (hardware mechanism). Everything that transpires during a context switch follows from the understanding of that mechanism. In particular, the preemption you see at 16:50 has already happened and was caused by the SysTick interrupt. At 16:50 you merely change the stack pointer (SP) to control the *return* from interrupt. So, I think your problem might be that you think incorrectly about the "preemption". The context switching code does not cause preemption, it merely uses it. The preemption has to happen in hardware, so in case you need to create it at will, you need to trigger an interrupt. You'll see in the later lessons how to do just that and utilize the special PendSV interrupt for it. --MMS
@@StateMachineCOM I'm sorry if I wasnt clear.
I was referring to the fact that stack pointer value that you had to save manually. It is different from the one that was initial value that we set in order to begin execution.
If we were to use this initial value, we would begin execution from the beginning of the theard, which is okay if we are doing it for the first time , so as it get things going.
As I understand it, Context switching is to be done in such a way as to resume from the point of preemptiion, and not to start over at the beginning.
That is to say if we are to supply the incorrect context, the thread may not resume from where it left off.
In this particular example, after the thread is preempted, you save the new stackpointer value (to correctly record this context) such that when control is restored to this thread , it picks up from where it left off when it was preempted as opposed it starting from the beginning if we had supplied it the initial sp value.
So when a thread is preempted at different stages of its execution, the corresponding exeption stack that is generated with with a sp value is to be used to get back and to the point from where it was preempted.
I apologise if this sounded a bit repetitive. I just wanted to get my point access. and yes I did believe that I've misunderstood preemptiion and context switching . It is interrupt premeptuin that allows us to switch away from the normal sequential execution. It is during this premeptuin that were are able ti change the context by altering the values if the sp so that after premption the flow of execution is able to return to point from what the sp points to.
I hope this is clear
@@robbieschultz3559 Yes, of course! Every preempted thread needs to continue from the point of preemption. Starting over from the beginning every time would make no sense, would it? In fact, you can think of the private stack of a thread as a "bookmark", which remembers precisely where a thread was preempted, so that it can be resumed from that exact point. The stack "bookmark" is very sophisticated because it can remember very complex patterns of thread execution. For example, a thread can call a function, that calls another function, which gets preempted. The stack "bookmark" remembers all this, so that it can resume from the nested function call and correctly continue by returning from the second function, then returning from the first function, etc. I hope you can see how complicated things can get and how powerful the concept of the stack is. --MMS
really under the hood RTOS operation :) keep up!
Hi Sir,
Thanks for the series of great videos.
I have an query here as my newbie in the embedded world. Could you please tell me that why didn't you save R12 on the stack while you stored R11 down to R4 on the stack (Last of this video : 21:53 time line)? I am seeing that SP is set as 0x200000088 on the stack and above SP , we must have R12 then followed by R11-R4 while you showed a red block (R11-R4) copied just above 0x200000088 which i felt , it must be two addresses up from SP (R13) and R12 then it would be aligned.
Regards,
Manish
The complete stack frame for a thread's context consists of two parts: (1) the registers saved in hardware upon the interrupt entry and (2) registers saved on top of that by the software. The choice of the software-saved registers is dictated by what the hardware already saved, because you don't want to repeatedly save the same registers and you don't want to miss any either. Also, you might want to watch lesson 9 about ARM Application Procedure Call Standard, which explains the registers saved during a function call. --MMS
Hi Miro, I did not understand the offset of 20 bytes regarding the stack pointer at 22:04
It is to account for the 8 values that had to be manually copied from registers R4-R11 onto the top of the current ISR stack frame at 21:44 (8 values * 4 bytes each = 32 bytes = 0x20 bytes in hex).
macOS?
manOS!
-- this man just made manual OS entirely by hand.
hey DR Miro
when i breakpoint at the first time at the of systick_hnadler unlike your call stack , which contains both SysTick_Handler and main mine only contains SysTick_Handler at addreess 0x00000000 which seems weird . additionaly after having the right adresses of sp_blinky1\2 stack , i hit run and again unlike your call stack i have calls ti BSP_tickCtr twice and call to SysTick_hnadler . when i run free only blinky1 main loop executes .i think the two issues are connected and somehow the one of BSP_tickCtr (maybie belongs to the callstack of the blue led main loop) returns to blinky_1 instead to blinky2 (but again in your call stack it does not even appear). do u have any idea?
In situations like that, I highly recommend that you download the official project for this lesson (lesson-22.zip) from the companion web page ( www.state-machine.com/video-course ). Back up your current project, and unzip the official project. Try the official project, which should work. Then compare the original to your own (a good free tool is WinMerge). The problem must be in the difference. I hope this strategy makes sense to you. --MMS
hey Dr Miro @@StateMachineCOM
thanks for the fast response , this is exactly what i did . should i have all the directories & files under the project ? i use keil ide but i did not quite undersood the readme file . my project builds sucssesfuly and run (besides the context ) , maybie i had to remove a file from the project ? do we use arm or gcc in this ide ? maybie its related somehow
Hi Miro, thanks a lot for the RTOS series of videos. I have a silly question, specifically on this example. Where is the interrupt used in this code, What is the ISR doing in this example? Is it used for the delay? Particularly from the plot /illustration of 10:53, why is ISR happening non-periodically and why are they of different duration?
"Where is the interrupt used in this code?" -> This code uses the periodic SysTick interrupt, which is configured in BSP_init() inside bsp.c file.
"What is the ISR doing in this example" -> The SysTick ISR is only incrementing a counter variable. But it is also used to manually simulate context switching, by setting a breakpoint inside the ISR and then changing things manually inside the CPU using the debugger.
"Why is ISR happening non-periodically and why are they of different duration?" -> The ISR used for context switching does not need to be periodic and it does not need to have the same duration each time. These things simply don't matter, and therefore the timing diagram at 10:53 shows a general case. --MMS
hi Miro. Can you talk about firts setting with Debug in Keil-C. I do everything the same as you but the result is different. BX lr don't jump to main_blinky1 and display in Call Stack + Local also not the same you. Thank you so much about 2 previous response. Hope to receive your reply soon.
It's difficult for me to talk you through setting up the KEIL uVision debugger in this RUclips comment. You just need to watch the video and try it on your host computer. But regarding the BX LR instruction that returns from the SysTick interrupt, it will NEVER go to main_blinky1 or main_blinky2 unless you manually change something in the CPU. For example, at 7:09 you change the stack entry corresponding to the PC register, and only after that change you execute the BX LR instruciton. This change causes the BX LR to return to your main_blinky1. Such "manual context switching" is the main point of this lesson. --MMS
@@StateMachineCOM i understand returns from the SysTick interrupt. I had change the value of SP same to main_blinky1 or main_blinky2 but it not go back what i want :
@@thucngoxuan9011 I think that you must be doing something else than what is shown in the video. Please note that the video progresses in a few steps and at each step you need to do something different. For example, at 7:09 you need to change a value in the stack memory. Later, at 15:55 you change the SP register in the CPU. So, please pay attention to what needs to happen at wich stage. Finally, if you cannot get your code to do what's shown in the video, you should download the project accompanying this lesson (from the companion web-page at www.state-machine.com/video-course ) and use that project. --MMS
15:56 I was even holding my breath, with sweat on my forehead!
Thanx for the videos.
one of the best content on the web.
thanx for all the videos.
keep up the good work
fantastic classes...waiting for next video
If you are using a FPU, your stack for each Task has to be increased to 26 additional Bytes, right?
The interrupt stack frame is larger when the FPU is used. But there is more to it. An excellent description of the issues is published in the ARM Application Note 298 (just google for it) "Cortex-M4(F) Lazy Stacking and Context Switching". --MMS
any guesses why my blue led doesn't go OFF when the green one comes ON ? The blue led turns off when I run the code without the breakpoint
Why should the Blue-LED go off when a preemption happens? I mean, if the preemption happens while the Blue-LED is on, it *should* stay on as long as the thread main_blinky1 is not running. This is because only main_blinky1 turns the Blue-LED off. --MMS
great video! I think i will make some time to watch all your lessons!. Thank you!
Hello miro , hope your doin good .
I recently noticed something when I debugged this lesson , when u switch context , say u want switch to blinkey1 , u put sp_blinky1 in sp CPU register , but before this u save the OLD sp CPU value in sp_blinkey2 .
in the video , every time u do this the sp CPU must be less than the old sp_blinkey2 by 2 words
sp_blinkey1 is either (0x20000090) or (0x20000088) ,
sp_blinkey2 is either (0x20000130) or (0x20000128),
, but with me there is no difference,
sp_blinkey1 is always (0x20000090),
sp_blinkey2 is always (0x20000130),
as u notice : the sp CPU is exactly the same previous sp_blinky when i hit the breakpoint . is it normal ?
hello mr miro
i just want to understand __just in brief definition__ what do we mean by word (kernel) in our course ....and thanks a lot .
i ve already googled but i am little confused !
Truly very profound!!
what a fabulous :) .. thank you very much Miro ,
Impresionante!! great Mr! thanks a lot!
First thanks for great lessons, I am going through RTOS lesson again after a year. I have one doubt. At 12:50, you are explaining that we don't need extra aligned unit for task specific stack as its already 8 byte boundary aligned. I am not able to understand reason. I watched that portion multiple times. Can you please tell the reason in comment box ?
Yes, you are right that the alignment of the arrays `stack_blink1[]` and `stack_blinky2[]` at 8-byte boundary is not guaranteed at this point. The ARM-KEIL compiler happens to align the arrays that way, but in principle, another, standard-compliant compiler (or the linker for that matter) could align them differently (only on 4-byte boundary.) To guarantee the 8-byte-boundary alignment, you could add `__attribute__ ((aligned (8)))` before or after the declarations of the stack arrays, but this is a non-portable extension to the C language. In the next lesson about the RTOS, you would see how this will be implemented in the "MiROS" RTOS, in the OSThread_start() function. Specifically, you'll see that the top of stack will be aligned explicitly. --MMS
@@StateMachineCOM Thanks for reply. Yes in next lecture we are taking care of it.
Also would like to put a thank you note for helping and motivating me & many other embedded engineers through your work.
Hello Sir, thanks for the video. I have a problem with this. After changing my stack address in debugger with my created stack, the stacks on the debugger doesn't change values. But the functions are working. Hope you can help me solve.!!
Miro, here you mention that "an ISR must necessarily run to completion before returning to the preempted code". And I'm wondering what that means in terms of ISR preempted by an ISR of higher priority. If I were to use a while loop as the idle thread and have 2 or 3 ISRs running at different rates and each with a different priority level, also each one more computationally burdensome on the CPU than simple blinky ( I am thinking motor control applications) would it still be necessary to store registers R4-to-R11 for meeting hard and soft real time deadlines? Because it almost seems like a higher priority would preempt a low priority ISR, and all ISRs would eventually run to completion before returning to the idle thread.
Is it possible to apply this concept on an Arduino board or with an Atmel chip??
Yes, this lesson should be applicable to Arduino boards based on the ARM Cortex-M CPU, such as Arduino DUE, Arduino Zero, or Arduino MKR, and perhaps others. (At the same time, the code presented here will NOT work on AVR-based Arduinos, such as Arduino UNO.) Also, if you are interested in Arduino, you might want to check out QP-arduino adaptation on GitHub github.com/QuantumLeaps/qp-arduino
@@StateMachineCOM Thank you
Waiting for the next video. These video of your is very helpful.
Thank you very much for this wonderful course! BTW, the transcript for lesson 16 and 21 are missing on your official website. When click on the txt file it brings you to a page saying file is missing.
The companion website at www.state-machine.com/video-course has been corrected and the transcipts shouild be available. Thank you for reporting! --MMS
question:
could you please more clearly why the aligner stack entry is not necessary? ( my platforme is cc2640R2 Launchpad from Ti with CCS toolset) .. thanks in advanced
Stack aligner is necessary only when the stack before the interrupt is misaligned. Misaligned stack means that the value in the SP register is not divisible by 8. Normally a stack misalignment should not happen, because the compiler avoids pushing an odd number of registers. But in case there is some assembly code (not generated by the compiler), a misalignment could happen. In the following lessons on RTOS, you will see some assembly code to perform context switch, but this assembly code will be careful to avoid stack misalignment. Regarding your CC2460R2 LaunchPad board, it is not appropriate for this course. To really follow along, you need the TivaC123 LaunchPad. --MMS
Dear Mr. Miro, if the main_blinky2 use a lot of stack memory, could it overwrite the private stack memory of main_blinky1's exception frame?
Yes it could! The risk of stack overflow is multiplied by the number of threads in an RTOS, because each and every private stack of a thread can overflow. So, you need to be extremely careful and you need to size every stack properly. This is one of the biggest problems of using a traditional RTOS.
@@StateMachineCOM Dear Mr. Miro, thank you very much for your conscientious answers and awesome videos.
Can you do a video on interrupt latency and how to deal with it?
Hello Dr Miro. Thanks for the wonderful tutorial. I am trying to create same application on NXP Kl25z freedom board, however when I try to switch stack entry from CPU stack to blinky1/2 stack, by loading SP register with value of blinky 1/2 stack pointer variable at sys tick interrupt exit (at BX lr instruction), the hard fault interrupt is triggered. When I try to swtch from main thred to blinky thread by changing PC address in main CPU stack (at sys tick interrupt exit) it works properly. I am using Keil uvision 5 IDE. I am facing similar issue in IAR but there hard fault interrupt occurs after running blinky thread for few msec. Appreciate your help on this!
To use the code discussed in this video course directly, you really need the same board as used in this course (TivaC LaunchPad). Other boards, even if based on ARM Cortex-M CPU, differ just enough so that the code will NOT work without some changes. For example, your KL25Z Freedom board is based on Cortex-M0+, which is significantly different from the Cortex-M4 in the TivaC LaunchPad. Also, the GPIO structure (used to blink the LEDs) is very different in the NXP processors compared to the TI processors. But TivaC LauchPad costs only $12.99, so perhaps you could afford it to benefit from this course. I really hope this makes sense to you. --MMS
Thanks for the reply. I could try this on Tiva board. Actually I have followed all of your earlier tutorials and created all applications on NXP KL 25z including led blinking and also found that the exception entries (stack frame) for Cortex M0 and Cortex M4 are also similar. Also, the code that I have written for Kl25z based on this tutorial is also same as that you have written for Tiva board, so I was hoping to have it working on kl25z. Is there any generic way that MIROS will work independent of arm architecture/board? Thank you again for the help!
Looks like manually changing SP register in IDE causing change in the debug instructions and thus triggering hard fault interrupt. Finally I could able to run multiple threads on KL25Z freedom board with automatic thread switching as mentioned in your next tutorial with little modifications.
Really helpful tutorials and lot to learn. Thanks!!
been waiting for so long for the rest of the videos :(
Great explanation! Thank you very much!
"But in your case, the ISR does NOT return to the preempted code, but
rather to another thread: blinky2. This other thread can also use the R7 register and as any function is also obligated by the AAPCS to restore the R7 upon its return. But you are not executing the whole thread function but rather just a piece of it. This piece of code doesn't need to comply with the AAPCS and it can change the value in R7."
What exactly is meant by not executing the whole thread function, but rather a piece of it?
When you call a C function, you execute the whole function from the beginning to the end. This is important, because at the beginning of a function the compiler generates special code known as function prologue, which saves the required registers on the stack. Also, the right before returning from the function, the compiler generates special code known as function epilogue to restore the previously saved registers.
However, when you return from an interrupt to a preempted thread function, you specifically DON'T execute the function from the beginning but rather only a piece of it from the point of preemption. This means that you generally don't execute the function prologue, so the registers required by the AAPCS are not saved. This is what is meant by saying the "the piece of code does not need to comply with AAPCS". --MMS
@@StateMachineCOM Thanks for the detailed explanation
While debugging i got error driver DLL could not be found in specified path please help
For all such problems, please visit the companion web-page to this video course advertised in every video and listed in the notes under every video: www.state-machine.com/quickstart/ . Specifically to driver issues, you might need to (re)install the Tiva/Stellaris USB Drivers on your Windows PC. The companion web-page provides a download link for these drivers as well as the USB drivers installation instructions (Tiva/Stellaris In-Circuit ICDI Manual). --MMS
excellent video ..Thanking u
Great video Miro. Did you know that according to our very complex video scoring system (sounds fancy but we simply do (likes-dislikes)/views), this video has the highest score of all the videos posted over the course of the last 12 months (for all vendors we monitor). See: www.embeddedrelated.com/vendors#tabs1-popyt and click on the 'one year' button. Great job!
I'm really glad to hear this. New video lessons are coming. The next lesson 27, RTOS part-6 will be about inter-thread synchronization with semaphores. Stay tuned! --Miro
Hi, just want to make sure I understand this correctly: why initialize the stack pointer variable for each thread to (length of stack + 1) instead of just the bottom of the stack ?
Because in your code, you pre-decrement the stack pointer address before you start assigning values to the stack (so you're starting from the bottom of the stack regardless).
@ClassyJohn: As explained at 13:00, the ARM stack is the so-called *full stack*, which means that the SP points to the last used slot (as opposed to the last empty slot). Therefore, in the video, the stack pointer variable has been initialized to simulate a situation right before adding a new interrupt stack frame. For each stack entry, the ARM hardware would first decrement the SP and then would fill the stack memory. Please note that code efficiency is not the objective here. It is more important to faithfully simulate the hardware. I hope that this makes more sense now. --MMS
@ClassyJohn: The stack pointer variable is NOT initialized to (length-of-stack + 1). The initialization is sp = &stack_blinky1[40], which corresponds to the length-of-stack (without +1). But you need to remember that arrays in C start with the index 0, so &stack_blinky1[40] points one word beyond the end of stack_blinky1[40] array (see video at 11:45). This is an important aspect to understand about C arrays. Otherwise, you'll be making a lot of off-by-one errors in your code. --MMS
one of best channel
Wish I had this video 10 years ago
Hi sir, i knew how to create a RTOS, pls make a video how to create mutex, semaphore. Thank you.
waiting for next video
Excellent Explanation and course! But I used almost 2-3 hour to really understand this course. Content is really difficult.
No pain, no gain! 2-3 hours is not long to learn, is it...
Hello Dr. Miro, I feel so lucky to find your tutorial series. I follow your tutorial with an STM32-L432KC board and the STM Cube IDE software. I managed to get the context switching done, but I had to fabricate a different stack.
The STM32-L432KC MCU also uses an ARM Cortex-M4 CPU so I'm not sure why I had to change the stack pattern for blinky sp's.
Here is what I had to come up after a lot of trial and error.
// Fabricate Cortex-M ISR stack frame for blinky1
*(--sp_blinky1) = (1U
Hi Amila. I assume that you're talking about the ISR stack frame discussed at 14:35. I don't understand why you had to add 2 extra registers to that stack frame because it should be the same for all Cortex-M CPUs. I would check your brake point at the end of SysTick in case you still have some code that changes the SP (such as popping two additional registers). The point where you manually change the SP is critical and the video assumes that there are no instructions that would mess up with the SP. I hope you see what I mean. --MMS
I'm not sure why your second comment is not showing up here. But the GCC compiler apparently pops 2 registers at the end of SysTick_Handler (at the optimization level you chose). This is completely normal. To use the ISR stack frame shown in the video, you simply need to move your break point to the very last machine instruction of your SysTick_Handler in disassembly. --MMS
@@StateMachineCOM Thank you so much Miro. I managed to figure out the issue with the help of your response. Earlier, I had a function inside SysTick_Handler() that made some changes to the disassembly, popping two registers as you mentioned. I removed that function and used the stack fabrication as in the video, and it worked!
Although there are some additional assembly instructions before the "bx lr" instruction that I'm not aware of. Anyways, I copied the blinky1 sp to the sp register before executing the "bx lr" instruction and it worked as expected.
Here are the assembly instructions:
SysTick_Handler:
push {r7}
add r7, sp, #0
}
nop
mov sp, r7
ldr.w r7, [sp], #4
bx lr
Also, another interesting thing I found is that earlier, I didn't had the "bx lr" instruction. I'm not familiar with assembly, so debugging is a bit challenging for me. I will try to figure things out from here, thank you again Miro. You are a wonderful teacher!
@@StateMachineCOM Sorry I deleted a comment. I actually did what you said just now and also made a comment. Many thanks Miro!
excuse me sir ,,how to understand your codes 😔😔😔😔
Very nice tutorial, but... I do not like when the word 'thread' is used as a synonym of the word 'task' or 'process'... These things are different.
Actually, I was very careful NOT to use the term "process", because it has a different meaning than "task" or "thread. On the other hand, the terms "task" and "thread" seem to be synonymous in the RTOS community. Virtually all RTOS manuals/books (VxWorks, ThreadX, MicroC/OS, FreeRTOS, Wikipedia RTOS article, etc.) use predominantly the word "task", but note that "thread" is also an alternative term for the same thing. The clarification of the terms "thread"/"process"/"task" can be found on StackOverflow at: stackoverflow.com/questions/3042717/what-is-the-difference-between-a-thread-process-task
I agree that _unfortunately_ 'task' and 'thread' uses simultaneously. I am quote old person and in my mind 'process', 'task' and 'thread' are different. With embedded systems these words are more 'synonymous' then it was with the big engines and this is why in my case I usually start lessons of RTOS with 'adjustment of therms'. From the classical point, the 'task' resources were created when a task was created with calls like 'task_create()', 'execv()', 'posix_spawn()', etc, derivate from general 'fork()'. From the other hand, 'fork()' has a strong correlation with a process. This is why in some OS there is also a 'task group'. In the link you've provided, from StackOverflow, task and thread are not the same as well.
All these does not make your course bad! :)
I am very much disappointed, That I can give only one like for this Video 😅
Dear Miro, why not to put a link for donation for this channel and for your effort? This level of information can not be achieved with courses of thousands of dollars. Therefore I want to donate for this effort.
Thank you for the suggestion. It is very generous. But for anybody who would like to help, it would be actually more valuable if you could simply subscribe and spread the word about this video channel (share the links on forums, Reddit, Quora, LinkedIn, etc.). --MMS
I agree as well.
very good
Why is there overly loud music playing whenever you pause on talking. I would recommend for future videos just keep it silent so its not distracting, over-wise great video and thank you!
Check course online to learn embedded C programming on arm cortex-m processor: www.udemy.com/course/embedded-arm-cortex-m3-programming-guide/
sssssuuuuuppppppeeeer
The music is a terrible idea :(
Note that the music is completely suppressed during narration and is mostly used to fill out the silence. Also, you can always use the Closed Captions by pressing the CC button at the bottom-right of the video. --MMS
could someone tell me where I can found out the 'qassert.h' file? I would be interested in looking up Q_* macros. Thanks
The qassert.h file is located in the qpc/include directory. This is a very *useful* file that can be used standalone without the rest of the qpc framework. Also, the latest lesson #47 and #48 are precisely about assertions and Design by Contract in embedded systems. --MMS