tonyp12 26 Posted March 18, 2016 Author Share Posted March 18, 2016 Code is free for non-commercial use, for commerical use a negotiated donation amount.?Though commercial use will be after I added more features.?I was able to trim it down to 13words, though as SP is volatile it wastes two words moving SP to R15 twice that is never used (in high optimization) = 15 wordsI have not tested this yet *taskrun = __get_SP_register(); if (++taskrun == taskstackpnt+tasks) taskrun = taskstackpnt; __set_SP_register(*taskrun); and in main.c change to int* taskrun = taskstackpnt; Quote Link to post Share on other sites
Rickta59 589 Posted March 18, 2016 Share Posted March 18, 2016 It would be interesting to the see what code your IAR compiler is producing. msp430-gcc is more like 48 words for the taskswitch routine. Quote Link to post Share on other sites
Rickta59 589 Posted March 18, 2016 Share Posted March 18, 2016 I ported the code over to msp430-gcc 4.6.3. I did change some things around: Most ints are changed to unsigned int, otherwise msp430-gcc will generate sign extension asm instructions each time you touch them. I made the taskrun a volatile so it actually works with gcc __task is replaced with the equivalent gcc __attribute__() directives I moved some of the stack variables in main to ".bss" so you get the whole stack ... * otherwise you lose a couple of words gcc generates better code for ints than char so those are replaced I changed the WDT to tick at 1ms interval to make a systick easier added a sys.c that contains "a sort of accurate" sys_delay(msec) added support for both msp430g2553 and msp430fr5969 both run at 8MHz the init code doesn't bother with checking the size of the stack words for a default, you must supply a size for each task. init code changed magic # to new magic # : ) to try and make it more obvious how the multi stack is being used added a ".bss" count variable to task1 and task2 that can be checked added a makefile, targets are all, clean, install, debug. (I assume you are running linux .. sorry windows people ) debug uses mspdebug and xterms to create 2 windows for debugging from the command line using msp430-gdb probably other changes ... note: msp430-elf- target doesn't really work yet. You can always find the latest version here: https://gist.github.com/RickKimball/3173f5ca73bc6dcdb7bc -rick Quote Link to post Share on other sites
tonyp12 26 Posted March 18, 2016 Author Share Posted March 18, 2016 ... 1204 push.w R4 ; last push 410F mov.w SP,R15 ; it does not remove it due to volatile 421E 0200 mov.w &taskrun,R14 418E 0000 mov.w SP,0x0(R14) 532E incd.w R14 903E 0208 cmp.w #0x208,R14 2002 jne 0xC0CE 403E 0202 mov.w #0x202,R14 4E82 0200 mov.w R14,&taskrun 4E0F mov.w R14,R15 ; another wasted due to volatile 4E21 mov.w @R14,SP 4134 pop.w R4 ; first pop ... IAR is the King when it comes to high optimization, you can get twice the work done making it possible to use a mcu that cost half as much. ?So in other words it could pay for itself (though commercial use baseline EW430-BL cost $1299) Quote Link to post Share on other sites
Rickta59 589 Posted March 18, 2016 Share Posted March 18, 2016 ah .. you are only talking about the code after and before the push .. ok well gcc isn't that bad then Quote Link to post Share on other sites
Rickta59 589 Posted March 18, 2016 Share Posted March 18, 2016 IAR is the King when it comes to high optimization, you can get twice the work done making it possible to use a mcu that cost half as much. ?So in other words it could pay for itself (though commercial use baseline EW430-BL cost $1299) msp430-gcc can do ok too : ) . I used the -ffix-reg feature to generate some optimized switcher code: //============= TASK SWITCHER ISR ============= __attribute__((interrupt(WDT_VECTOR), naked, used)) void taskswitcher(void); void taskswitcher(void) { 4576: 04 41 mov r1, r4 4578: 34 52 add #8, r4 ;r2 As==11 #ifdef __MSP430_HAS_MSP430XV2_CPU__ __asm__ volatile ("pushm #9,r15"); 457a: 8f 15 pushm #9, r15 "push R11\n push R10\n push R9\n push R8\n" "push R7\n" ); #endif ++systick; 457c: 92 53 18 1c inc &0x1c18 *taskrun = (unsigned)__read_stack_pointer(); 4580: 0f 45 mov r5, r15 4582: 85 41 00 00 mov r1, 0(r5) ;0x0000(r5) if (++taskrun == lasttask ) taskrun = taskstackpnt; 4586: 2f 53 incd r15 4588: 05 4f mov r15, r5 458a: 05 96 cmp r6, r5 458c: 02 20 jnz $+6 ;abs 0x4592 458e: 35 40 0e 1c mov #7182, r5 ;#0x1c0e __write_stack_pointer((void *)*taskrun); 4592: 21 45 mov @r5, r1 #ifdef __MSP430_HAS_MSP430XV2_CPU__ __asm__ volatile ("popm #9,r15"); 4594: 87 17 popm #9, r15 Quote Link to post Share on other sites
tonyp12 26 Posted March 18, 2016 Author Share Posted March 18, 2016 >cmp r6, r5?I don't see where R6 is set? I could reserve R4 as a __regvar and use it as the taskstackpnt, compiler probably could then do the switching in 6 words plus one less push/pull?. But if tasks run out of regs it will use the stack, so that is a trade offbut probably only happens with function-calls that pass a couple of longs. Quote Link to post Share on other sites
Rickta59 589 Posted March 18, 2016 Share Posted March 18, 2016 I just pushed the changes .. take another look at the gist repo. https://gist.github.com/RickKimball/3173f5ca73bc6dcdb7bc#file-main-c-L27 -rick Quote Link to post Share on other sites
tonyp12 26 Posted March 20, 2016 Author Share Posted March 20, 2016 I think this is the smallest I can get it if you always use 2,4 or 8 task.?I did a second fibo, that task sure need a larger stack, it crashes if I don't give it 70 words each __regvar __no_init unsigned int taskrun @ __R4; ?... stackpnt[i] = (unsigned int) (multistack-12); // PC+SR+11 dummy push? R4 is no longer pushed ?... taskrun = 0; ... asm (" mov SP,stackpnt(R4)"); asm (" incd R4"); asm (" bic #-8,R4"); asm (" mov stackpnt(R4),SP"); RESULT 7 WORDS 1205 push.w R5 ; last push 4184 0200 mov.w SP,0x200(R4) 5324 incd.w R4 C034 FFF8 bic.w #0xFFF8,R4 4411 0200 mov.w 0x200(R4),SP 4135 pop.w R5 ; first pop taskrun = (int) stackpnt; ? ... asm (" mov SP,0x00(R4)"); asm (" incd R4"); asm (" cmp #stackpnt+3*2,R4"); // I can not get tasks define to work here asm (" jne $+6;"); asm (" mov #stackpnt,R4"); asm (" mov @R4,SP"); ?RESULT 9 WORDS but work on any number tasks Quote Link to post Share on other sites
Rickta59 589 Posted March 20, 2016 Share Posted March 20, 2016 I've been spending way too much time looking at this thing. So thanks for that. I did some timing testing with the msp430g2553 running at 8MHz, it seems like it takes about 8 to 10 microseconds to do the context switch. There is some overhead, however I was able to compensate for extra cycles by over clocking the DCO. On the msp430g2553, I made the slight change to increase the DCO clock: BCSCTL1 = CALBC1_8MHZ; // Set DCO to factory calibrated 8MHz DCOCTL = CALDCO_8MHZ; DCOCTL += 13; // overclock 103% or 8.22MHzSpeeding it up this much seems to account for the context switch overhead. Any thoughts? BTW: This was with 2 tasks. Splitting the 8MHz clock evenly between the 2 task I used 4MHz to calculate my delay cycle calls: #include "common.h" void task1(void) { volatile unsigned count=0; // 4 bytes on stack P1DIR |= BIT0; P1OUT &= ~BIT0; while(1){ P1OUT ^= BIT0; #if 1 __delay_cycles(50*(4000000/1000)); #else sys_delay(50); #endif P1OUT ^= BIT0; #if 1 __delay_cycles(450*(4000000/1000)); #else sys_delay(450); // 4+2 bytes of stack used #endif ++count; } -rick spirilis 1 Quote Link to post Share on other sites
Rickta59 589 Posted March 22, 2016 Share Posted March 22, 2016 ... Unfortunately, I've not been successful getting this to work with msp430-elf-gcc. ...I've got the msp430-elf-gcc version working properly now in the latest checkin https://gist.github.com/RickKimball/3173f5ca73bc6dcdb7bc Turns out that I got lucky with msp430-gcc. I had forgot to add a "RETI" instruction in the WDT ISR. The only reason it worked with msp430-gcc was the default exception handler had ended up in flash right after the WDT ISR. The default exception handler just does a "RETI" so it just happened to work. I also added a msp430fr5969 specific version that uses the FRAM for stack space. This frees you of the limits of RAM and allows for larger stacks and more tasks. It should be easy to adapt this to any of the newer chips that are only supported by msp430-elf-gcc. (RH/TI 4.9 gcc) -rick Quote Link to post Share on other sites
MadMayonnaise 0 Posted November 28, 2018 Share Posted November 28, 2018 @tonyp12 and other folks, can anybody help me better understand the following lines? #ifndef COMMON_H_ #define COMMON_H_ #define tasks (sizeof(taskpnt)/2) __task void task1(void); __task void task2(void); __task void task3(void); typedef __task void (*funcpnt)(void); #endif and funcpnt const taskpnt[]={ task1, task2, task3, // <- PUT YOUR TASKS HERE }; Have a good one.. Quote Link to post Share on other sites
zeke 693 Posted December 17, 2018 Share Posted December 17, 2018 @MadMayonnaise They are called Function Pointer Arrays. I love them. I have been using them extensively to create my CLI - Command Line Interpreter. I can add a new command just by defining a new entry into an array. They are an alternative to the Gigantic Switch Statement style of coding. To get you started, here is an article from 1999 by Nigel Jones:How to Create Jump Tables via Function Pointer Arrays in C and C++ I also found the book Programming Embedded Systems in C and C++ by Michael Barr to be exceptionally helpful. Chapter nine specifically. veryalive 1 Quote Link to post Share on other sites
MadMayonnaise 0 Posted December 18, 2018 Share Posted December 18, 2018 Hi @zeke, Thanks for the reply. I've already checked the links. Can you also roughly explain the following code snippet? I know the concept of context switching and what needs to be done prior to it, but the following steps confused me a little bit. int* multistack = (int*) __get_SP_register(); int i=0; while(i<tasks-1){ int j = stacksize[i]; if (!j) j = 24; multistack -= j; *(multistack) = (int) taskpnt[++i]; // prefill in PC *(multistack-1) = GIE; // prefill in SR taskstackpnt[i] = (int) multistack-26; // needs 12 dummy push words Thanks.. Quote Link to post Share on other sites
zeke 693 Posted December 19, 2018 Share Posted December 19, 2018 First thing, the snippet you posted seems to be incomplete. I cannot see the closing parenthesis. Next, the code is initializing a data structure (called multistack) that will serve as The Stack. When this code runs, the author does several things with The Stack: Saves the SP register value (which is the present location/address of The Stack Pointer) Checks to make sure that there are no more than "tasks" numbers of tasks. Initializes the index of The Stack so it knows where it is in The Stack Stores a task pointer into the stack (he calls it the PC - probably Program Counter) Stores the state of the GIE register (Global Interupt Enable register) Then makes space for 26 bytes of information in The Stack (he calls them 16 bit Words + 2 for wastage[see his post above]) This is the kind of operations that an operating system will do when it wants to stop doing one thing, switch context, and start doing another thing, complete that, then come back and continue doing what it was doing before the interruption. Does that make sense? Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.