Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


tonyp12 last won the day on August 13 2017

tonyp12 had the most liked content!

About tonyp12

  • Rank

Profile Information

  • Gender
    Not Telling
  • Location

Recent Profile Visitors

492 profile views
  1. As all gio registers start with port and then OUT, IN, IE etc, example: P1OUT, with some macro word scrambling you can do this: #define read & // words for common gpio interaction #define clear &= ~ #define set |= #define toggle ^= #define Button1(y,x) (P1 ##x y BIT0) // PORT and PIN (ignore the ##x y) #define BlueLED(y,x) (P2 ##x y BIT5) To use them: BlueLED(set,DIR); BlueLED(clear,OUT); Button1(clear,DIR); // a input Button1(set,OUT); // pull-up resistor Button1(set,REN); Button1(set,IES); // falling edge button1(clear,IFG); Button1(set,IE);
  2. #include "msp430.h" #define UART_RX BIT1 #define UART_TX BIT2 #define UART_PORT(x) (P1 ##x) #define softintvec 0xC0004210 // start of flash + opcode for BR void bootloader_reset(void); void TXdata(char* pnt, int len); void bootloader_USCIAB0TX(void); void bootloader_flasherase(void); __interrupt void bootloaderRX_ISR(void); //-------------------- define structures -------------------- struct jumpstruct { const unsigned long branch[16]; const unsigned int bsl; const unsigned long* vector[16]; }; //-------------------- declare strings ---------------------- #pragma location = "BOOTSTRINGS" const char uartstring1[] = "run main, password anytime erases\r\n"; #pragma location = "BOOTSTRINGS" const char uartstring2[] = "Ready for Firmware\r\n"; //-------------------- reserve a fixed ram location --------- #pragma location = 0x200 __no_init char* passwordpnt; //-------------------- entry on reset ----------------------- #pragma location = "BOOTLOADER" void bootloader_reset(void) { asm(" MOV #SFE(CSTACK), SP"); // set stack pointer WDTCTL = WDTPW+WDTHOLD; if (CALBC1_1MHZ != 0xff){ // erased by mistake? BCSCTL1 = CALBC1_1MHZ; // Set DCO to factory calibrate 1MHz DCOCTL = CALDCO_1MHZ; } UCA0CTL1 = UCSWRST; // Set UCSWRST (hold USCI in Reset state) UCA0CTL1 |= UCSSEL_2; // Use SMCLK UCA0CTL0 = 0; // UART N,8,1 UCA0BR0 = 104; // upper register (9600) UCA0BR1 = 0; // the lower rate register UCA0MCTL = UCBRS1; // don't use modulation as it jitters UART_PORT(SEL) |= UART_TX + UART_RX; UART_PORT(SEL2) |= UART_TX + UART_RX; UCA0CTL1 &= ~UCSWRST; // relase reset, Initialize USCI state machine IE2 |= UCA0RXIE; // enable USCI_A0 RX interrupt if (*(int*)0xC01E != 0xffff){ // is reset vector filled in? TXdata((char*)&uartstring1, sizeof(uartstring1)-1); passwordpnt = (char*) &uartstring1; // use string1 as password __bis_SR_register(GIE); // so our RX_ISR can get data asm (" BR &0xC01E"); // indirect jump } TXdata((char*)&uartstring2, sizeof(uartstring2)-1); BCSCTL3 |= LFXT1S_2; // aclk = vloclk BCSCTL1 |= DIVA_3; // aclk divided by 8 WDTCTL = WDTPW+WDTCNTCL+WDTSSEL; // aclk sourced ~28sec __bis_SR_register(LPM3_bits); // trap it } //-------------------- uart byte tx routine ----------------- #pragma location = "BOOTLOADER" void TXdata(char* pnt, int len) { while(len--){ UCA0TXBUF = *pnt++; while (!(IFG2 & UCA0TXIFG)); } } //-------------------- UartRX sniffer ISR ------------------- #pragma location = "BOOTLOADER" __interrupt void bootloaderRX_ISR(void){ if (IFG2 & UCA0RXIFG){ // our USCI_A0 Recive Interrupt? if (UCA0RXBUF == *passwordpnt){ // a match? if (++passwordpnt == uartstring1+sizeof(uartstring1)-3){ bootloader_flasherase(); WDTCTL = WDTPW+WDTCNTCL+WDTSSEL; // reset the system __bis_SR_register_on_exit(LPM3_bits); } } else passwordpnt = (char*) &uartstring1; // reset it to the start if (*(int*)0xC00E != 0xffff){ // is USCIAB0RX vector filled in? IFG2 |= UCA0RXIFG; // if real routine needs it asm (" BR &0xC00E"); // indirect jump to user isr } } } //-------------------- flash erase blocks ------------------- #pragma location = "BOOTLOADER" void bootloader_flasherase(void){ int* pnt = (int*) 0xC000; // start of flash unsigned int i = 16*2-1; // erase 16K -512B WDTCTL = WDTPW+WDTHOLD; // no WDT __bic_SR_register(GIE); // so no interupt FCTL2 = FWKEY + FSSEL0 + FN1; // MCLK/3 for Flash Timing Generator FCTL3 = FWKEY; // Clear Lock bit while (i--){ FCTL1 = FWKEY + ERASE; // Set Erase bit *pnt = 0; // Dummy write to erase Flash block pnt += 256; // 256 ints up (e.g 512 bytes) } FCTL3 = FWKEY + LOCK; // Set LOCK bit } //-------------------- const rejumping real intvec ---------- #pragma location = "JUMPTABLE" const struct jumpstruct jumptable = { .branch[0] = softintvec + 0 * 0x20000u, .branch[1] = softintvec + 1 * 0x20000u, .branch[2] = softintvec + 2 * 0x20000u, .branch[3] = softintvec + 3 * 0x20000u, .branch[4] = softintvec + 4 * 0x20000u, .branch[5] = softintvec + 5 * 0x20000u, .branch[6] = softintvec + 6 * 0x20000u, .branch[7] = softintvec + 7 * 0x20000u, .branch[8] = softintvec + 8 * 0x20000u, .branch[9] = softintvec + 9 * 0x20000u, .branch[10] = softintvec + 10 * 0x20000u, .branch[11] = softintvec + 10 * 0x20000u, .branch[12] = softintvec + 12 * 0x20000u, .branch[13] = softintvec + 13 * 0x20000u, .branch[14] = softintvec + 14 * 0x20000u, .branch[15] = softintvec + 15 * 0x20000u, .bsl = 0x0000, .vector[0] = &jumptable.branch[0], .vector[1] = &jumptable.branch[1], .vector[2] = &jumptable.branch[2], .vector[3] = &jumptable.branch[3], .vector[4] = &jumptable.branch[4], .vector[5] = &jumptable.branch[5], .vector[6] = &jumptable.branch[6], .vector[7] = (unsigned long*) &bootloaderRX_ISR, .vector[8] = &jumptable.branch[8], .vector[9] = &jumptable.branch[9], .vector[10] = &jumptable.branch[10], .vector[11] = &jumptable.branch[11], .vector[12] = &jumptable.branch[12], .vector[13] = &jumptable.branch[13], .vector[14] = &jumptable.branch[14], .vector[15] = (unsigned long*) &bootloader_reset }; #pragma required=jumptable // tell compiler it's not a dead var Override linker location, copy it and edit the last sections so it looks like thisit moves intvec to just before code so firmware is just now one block including the vectors -Z(CONST)DATA16_C,DATA16_ID,TLS16_ID,DIFUNCT,CHECKSUM=C020-FDFF // normally C000-FFDD // ------------------------------------- // Code // -Z(CODE)CSTART,ISR_CODE,CODE_ID=C020-FDFF // normally C000-FFDD -P(CODE)CODE=C020-FDFF // normally C000-FFDD // ------------------------------------- // Signature memory and interrupt vectors // -Z(CONST)SIGNATURE=FFDE-FFDF -Z(CODE)INTVEC=C000-C01F // normally FFE0-FFFF -Z(CODE)RESET=C01E-C01F // normally FFFE-FFFF -Z(CODE)BOOTLOADER=FE00-FF5F // new addition -Z(CONST)BOOTSTRINGS=FF60-FF9D // new addition -Z(CONST)JUMPTABLE=FF9E-FFFF // new addition ?Done with IAR Workbench for G2553 The bootloaders Uart RX routine sniffs all incoming data so user can share uart for its intended purpose, so if you have a Cellular GSM module and you want remote firmware update capability ?this may be best near un-brickable way, as soon it detects the password it erase the Flash (except the boot loader block) and now it will go in to a waiting sequence for new firmware, It will send a text string ~28second to remind you. ?The firmware Flash Loader itself is not done yet, probably need to move up to a 1K size block to fit everything
  3. The idea is that any new firmware does not include the upper 512bytes of Flash, but as IRQ vectors will change with new firmware but is now part of the firmware-block as compiler Intvec have been moved -512 bytes down. If you put your custom Loader in the upper 512bytes too and as you never erase this block, much less chance of bricking. I guess opcode for BR could change from msp family? but this does work on a G2553. ?CCS could use pragma location if it can not handle @ Not all vectors are available or used, so you should fill them with random value instead for the BSL password if you want some security. But at least the BSL password will now stay the same from reversions. static const unsigned int jumptable[49] @0xFF9E = { 0x4210,0xFDE0, // indirect BR &0x---- 0x4210,0xFDE2, 0x4210,0xFDE4, 0x4210,0xFDE6, 0x4210,0xFDE8, 0x4210,0xFDEA, 0x4210,0xFDEC, 0x4210,0xFDEE, 0x4210,0xFDF0, 0x4210,0xFDF2, 0x4210,0xFDF4, 0x4210,0xFDF6, 0x4210,0xFDF8, 0x4210,0xFDFA, 0x4210,0xFDFC, 0x4210,0xFDFE, 0x0000, // BSL signature 0xFF9E,0xFFA2, // real intvec 0xFFA6,0xFFAA, 0xFFAE,0xFFB2, 0xFFB6,0xFFBA, 0xFFBE,0xFFC2, 0xFFC6,0xFFCA, 0xFFCE,0xFFD2, 0xFFD6,0xFFDA }; int main( void ) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer if (jumptable[0] == 0xffff) {while(1){};}; // need to use array for something Override default location of linker file and make a copy of it and move it to local project folder and edit: // ------------------------------------- // Signature memory and interrupt vectors // -Z(CONST)SIGNATURE=FFDE-FFDF // used by BSL -Z(CODE)INTVEC=FDE0-FDFF // normally FFE0-FFFF -Z(CODE)RESET=FDFE-FDFF // normally FFFE-FFFF
  4. 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
  5. >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 off but probably only happens with function-calls that pass a couple of longs.
  6. ... 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)
  7. 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 words I 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;
  8. preemptive means that the code (e.g.each task that is pretty much its own main.c) does not know it is sharing a single mcu core. it does not need to say "i take a pause now so next task can go on", you can even run two instances of the same task (though not really useful) A higher power takes over and switch tasks. Sure there is stuff to add like system_sleep and priority and maybe even new task and end task etc. I plan to show how to use a 512Hz mems osc to NMI pin, so task switching can not be overridden by (maybe mistakenly) disabling GIE. ?wiki: Preemptive multitasking wiki: Cooperative multitasking P.S I also have a Cooperative multitasking system that uses a Event Machine in main.c and each (up to 16) tasks are all State Machines (switch/case) as that is a good time slicer. ?What I like about that is that I know I have exclusivity while I'm doing my thing so no race conditions. ?And very little overhead so better for battery-operation as task that needs to use a time-constant can sleep from 1ms to 64sec independently keeping in LPM3 95% of the time
  9. ?{28}; is the same thing as {28,0,0} and 28 was just random number to show something so if you forget to fill in all stack sizes for each task I will give you 24 words (48bytes) A minimum should be 14 words if your task don't use the stack at all. -26, as I cast the address to (int) it's now a byte space referenced If I wrapped it first (int) (multistack-13); it will be word referenced as it is a int pointer, probably should wrap it for next time, ?So I'm just adjusting the stack pointer to include the PC,SR and the 12 pop's it will see on first entry (only done for task2+) That is 14 words but Pop is post increment, it works if I do -13 words the registers will have random data first time, but as task have not started doing anything yet it should not care. a POP R15 is actual a: MOV.W @R1+,R15
  10. I may replace the c code in isr with asm if I can not make changes to get it optimized so it only uses 8 words, now it uses 19 words, but making stuff (int*) instead of array[char value] I think that will get it down to 12 at least. If all asm of course there is no need for __raw if you compiler don't support it. btw the asm ("br &taskpnt") can be done in C by: taskpnt[0](); but it does a call and that wastes a stackword for return address. So unless you want task1 to have an option to exit and return to that spot in main, a exit in task1 now would use the return address from the original call to main
  11. You're right, that is room for 16 multi-stacks!! Run that msp at 16Mhz, intervals will now be 2ms and you have something powerful. ?IRQ's can handle the real-time stuff and just make sure you always code with the thoughts that code can get on hold at any time (though temporary disable WDTIE will give you a lock) ?I just selected 1K+ and did not see that some had even more. http://www.ti.com/lsds/ti/microcontrollers_16-bit_32-bit/msp/ultra-low_power/msp430g2x_i2x/products.page#p1219=1;4
  12. a ISR always have way to trick the RETI by modifying the SR values on stack. I trick the RETI with a different stack location , task 1 have a stack as normal as first entry in to ISR the PC,SR and R15-R4 are push'ed ? But for the other tasks I have to preset the PC and SR in the "fake" stack for its first entry in and also offset the stack pointer as it will see 12 pop's the first time without seeing the 12 push'es yet. I plan to add system_delay() so task can asked to be put on hold, though it will be hard to calculate a actual time if other tasks also do that at the same time. maybe even enter LPM0 if all task are on timeout for this 4ms interval.
  13. Tested on G2553 Launchpad with IAR, I recommend G2955 with 1K RAM if you want more than 3 task #include "msp430.h" #include "common.h" //=========================(C) Tony Philipsson 2016 ======================= funcpnt const taskpnt[]={ task1, task2, task3, // <- PUT YOUR TASKS HERE }; const int stacksize[tasks] = {28}; // a blank value defaults to 24 stack words //========================================================================= int taskstackpnt[tasks]; unsigned int taskdelay[tasks]; char taskrun; int main( void ) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer if (CALBC1_8MHZ != 0xff){ // erased by mistake? BCSCTL1 = CALBC1_8MHZ; // Set DCO to factory calibrate 1MHz DCOCTL = CALDCO_8MHZ; } 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 } WDTCTL = WDTPW+WDTTMSEL+WDTCNTCL; // 4ms interval at 8MHz smclk IE1 |= WDTIE; __bis_SR_register(GIE); asm ("br &taskpnt"); // indirect jmp to first task } //============= TASK SWITCHER ISR ============= #pragma vector = WDT_VECTOR __raw __interrupt void taskswitcher(void) { asm ("push R15\n push R14\n push R13\n push R12\n" "push R11\n push R10\n push R9\n push R8\n" "push R7\n push R6\n push R5\n push R4"); taskstackpnt[taskrun] = __get_SP_register(); if (++taskrun == tasks) taskrun = 0; __set_SP_register(taskstackpnt[taskrun]); asm ("pop R4\n pop R5\n pop R6\n pop R7\n" "pop R8\n pop R9\n pop R10\n pop R11\n" "pop R12\n pop R13\n pop R14\n pop R15"); } #include "msp430.h" #include "common.h" __task void task1(void){ P1DIR |= BIT0; while(1){ __delay_cycles(800000); P1OUT |= BIT0; __delay_cycles(800000); P1OUT &=~BIT0; } } #include "msp430.h" #include "common.h" __task void task2(void){ P1DIR |= BIT6; while(1){ __delay_cycles(1200000); P1OUT |= BIT6; __delay_cycles(1200000); P1OUT &=~BIT6; } } #include "msp430.h" #include "common.h" unsigned int fibo(int); __task void task3(void){ int temp = 0; while(1){ fibo(++temp); } } unsigned int fibo(int n){ if (n < 2) return n; else return (fibo(n-1) + fibo(n-2)); } #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
  14. If you look at the disassembly, you can see that is set R1 to either 1 or 0 every time before it writes to the bitband region. That must waste double the time, strange that ARM4 don't have built in BME, so you can use XOR etc on the bitband region. (eg Decorated store operations) Just a quick dirty test shows that if you can store 1 to R1 and 0 to R2 you double the rate, if the hardware can keep up that is. I tried to declare two uint8_t inside the function so compiler reserved the values in R1 and R2 but no go, it used the stack. So a BITBAND_PERI Toggle Function needs to be created that does use two registers. You can trick it to use Registers by call to a function, but it's depending on optimization so I force medium And when you take this in account, it should go even faster (if it applies to this type of STR) Neighboring load and store single instructions can pipeline their address and data phases. This enables these instructions to complete in a single execution cycle. while(1) { BITBAND_PERI(P1OUT,0)=1; // Set P1.0 (sets R0 to bitband address, sets R1 to 1) asm(" MOVS R2, #0"); asm(" STRB R2, [R0]"); asm(" STRB R1, [R0]"); // un-rolled toggle loop asm(" STRB R2, [R0]"); asm(" STRB R1, [R0]"); asm(" STRB R2, [R0]"); asm(" STRB R1, [R0]"); asm(" STRB R2, [R0]"); } while(1) { toggle(1,0); } } #pragma optimize = medium void toggle(char one, char zero) { BITBAND_PERI(P1OUT,0)=one; BITBAND_PERI(P1OUT,0)=zero; BITBAND_PERI(P1OUT,0)=one; BITBAND_PERI(P1OUT,0)=zero; BITBAND_PERI(P1OUT,0)=one; BITBAND_PERI(P1OUT,0)=zero; BITBAND_PERI(P1OUT,0)=one; BITBAND_PERI(P1OUT,0)=zero; }
  • Create New...