jayaura 0 Posted April 2, 2014 Share Posted April 2, 2014 Hello everyone, I disassembled the following code: #include <msp430.h> #define RED BIT0 #define GREEN BIT6 int main(int argc, const char *argv[]) { WDTCTL = WDTPW + WDTHOLD; P1DIR = BIT6 | BIT0; while (1) { P1OUT = BIT6; __delay_cycles(500000); P1OUT ^= (BIT6 | BIT0); __delay_cycles(500000); } return 0; } On running msp430-objdump -D hello_world.elf, I got: Disassembly of section .text: 0000c000 <__watchdog_support>: c000: 55 42 20 01 mov.b &0x0120,r5 //0x120 is WDTCTL c004: 35 d0 08 5a bis #23048, r5 ;#0x5a08 //Supposed to be WDTPW + WDTHOLD c008: 82 45 00 02 mov r5, &0x0200 // copying to RAM 0000c00c <__init_stack>: c00c: 31 40 00 04 mov #1024, r1 ;#0x0400 0000c010 <__do_copy_data>: c010: 3f 40 00 00 mov #0, r15 ;#0x0000 c014: 0f 93 tst r15 c016: 08 24 jz $+18 ;abs 0xc028 c018: 92 42 00 02 mov &0x0200,&0x0120 c01c: 20 01 c01e: 2f 83 decd r15 c020: 9f 4f 88 c0 mov -16248(r15),512(r15);0xc088(r15), 0x0200(r15) c024: 00 02 c026: f8 23 jnz $-14 ;abs 0xc018 0000c028 <__do_clear_bss>: c028: 3f 40 00 00 mov #0, r15 ;#0x0000 c02c: 0f 93 tst r15 c02e: 07 24 jz $+16 ;abs 0xc03e c030: 92 42 00 02 mov &0x0200,&0x0120 c034: 20 01 c036: 1f 83 dec r15 c038: cf 43 00 02 mov.b #0, 512(r15);r3 As==00, 0x0200(r15) c03c: f9 23 jnz $-12 ;abs 0xc030 0000c03e <main>: c03e: b2 40 80 5a mov #23168, &0x0120 ;#0x5a80 c042: 20 01 c044: f2 40 41 00 mov.b #65, &0x0022 ;#0x0041 c048: 22 00 c04a: f2 40 40 00 mov.b #64, &0x0021 ;#0x0040 c04e: 21 00 c050: 3e 40 03 00 mov #3, r14 ;#0x0003 c054: 3f 40 06 8b mov #-29946,r15 ;#0x8b06 c058: 1f 83 dec r15 c05a: fe 23 jnz $-2 ;abs 0xc058 c05c: 1e 83 dec r14 c05e: fc 23 jnz $-6 ;abs 0xc058 c060: 03 43 nop c062: f2 e0 41 00 xor.b #65, &0x0021 ;#0x0041 c066: 21 00 c068: 3e 40 03 00 mov #3, r14 ;#0x0003 c06c: 3f 40 06 8b mov #-29946,r15 ;#0x8b06 c070: 1f 83 dec r15 c072: fe 23 jnz $-2 ;abs 0xc070 c074: 1e 83 dec r14 c076: fc 23 jnz $-6 ;abs 0xc070 c078: 03 43 nop c07a: e7 3f jmp $-48 ;abs 0xc04a 0000c07c <__stop_progExec__>: c07c: 32 d0 f0 00 bis #240, r2 ;#0x00f0 c080: fd 3f jmp $-4 ;abs 0xc07c 0000c082 <__ctors_end>: c082: 30 40 86 c0 br #0xc086 0000c086 <_unexpected_>: c086: 00 13 reti Disassembly of section .noinit: 00000200 <__wdt_clear_value>: ... Disassembly of section .vectors: 0000ffe0 <__ivtbl_16>: ffe0: 82 c0 82 c0 bic r0, &0xc082 ffe4: 82 c0 82 c0 bic r0, &0xc082 ffe8: 82 c0 82 c0 bic r0, &0xc082 ffec: 82 c0 82 c0 bic r0, &0xc082 fff0: 82 c0 82 c0 bic r0, &0xc082 fff4: 82 c0 82 c0 bic r0, &0xc082 fff8: 82 c0 82 c0 bic r0, &0xc082 fffc: 82 c0 00 c0 bic r0, &0xc000 My questions are: 1) In <__watchdog_support> c000: 55 42 20 01 mov.b &0x0120,r5 //0x120 is WDTCTL c004: 35 d0 08 5a bis #23048, r5 ;#0x5a08 //Supposed to be WDTPW + WDTHOLD c008: 82 45 00 02 mov r5, &0x0200 // copying to RAM The second line - WDTHOLD is the 8th bit in WDTCTL. So shouldnt it be 0x5a80 ? Why is it 0x5a08 ? Why copy the modified content to RAM ? Why not directly write to WDTCTL register? But later in main: c03e: b2 40 80 5a mov #23168, &0x0120 ;#0x5a80 Now this seems consistent. So why is that "watchdog support" section even there? 2) In section <__do_copy_data> c010: 3f 40 00 00 mov #0, r15 ;#0x0000 c014: 0f 93 tst r15 c016: 08 24 jz $+18 ;abs 0xc028 When #0 is explicitly moved to r15, after tst, its pretty obvious that jz will be evaluate to true. Then why not unconditionally jump to 0xc028 ? If thats the case, what about the code just below jz? that is *dead* code, right? Quote Link to post Share on other sites
spirilis 1,265 Posted April 2, 2014 Share Posted April 2, 2014 How did you compile it? What optimization options did you use? Try -Os if you haven't already. Sent from my Galaxy Note II with Tapatalk 4 Quote Link to post Share on other sites
pabigot 355 Posted April 2, 2014 Share Posted April 2, 2014 All your questions relate to mspgcc's runtime support infrastructure and are independent of the program you've compiled. My questions are: 1) In <__watchdog_support> c000: 55 42 20 01 mov.b &0x0120,r5 //0x120 is WDTCTL c004: 35 d0 08 5a bis #23048, r5 ;#0x5a08 //Supposed to be WDTPW + WDTHOLD c008: 82 45 00 02 mov r5, &0x0200 // copying to RAMThe second line - WDTHOLD is the 8th bit in WDTCTL. So shouldnt it be 0x5a80 ? Why is it 0x5a08 ?Why copy the modified content to RAM ? Why not directly write to WDTCTL register? Because the value that needs to get written to the watchdog timer to reset it varies depending on MCU, and the expression computed by __watchdog_support reads the current setting then changes it to be a valid reset that preserves that setting. (Specifically, in some MCU families the bits that produce the power-up default timeout interval are different because of clock differences, so having a constant won't work across all platforms. I didn't want yet another chip-specific object file, so I found a general solution.) It's copied into RAM so that subsequent uses of the intrinsic __watchdog_clear() know what value to write. mspgcc also supports intrinsics __set_watchdog_clear_value() and __get_watchdog_clear_value(). It is the only toolchain that does this, so since it's not supported anymore people will have to create a new solution to the problem. But later in main: c03e: b2 40 80 5a mov #23168, &0x0120 ;#0x5a80Now this seems consistent. So why is that "watchdog support" section even there? Because your example doesn't use the built-in watchdog support, but the runtime system doesn't know that so it leaves it present. 2) In section <__do_copy_data> c010: 3f 40 00 00 mov #0, r15 ;#0x0000 c014: 0f 93 tst r15 c016: 08 24 jz $+18 ;abs 0xc028 When #0 is explicitly moved to r15, after tst, its pretty obvious that jz will be evaluate to true. Then why not unconditionally jump to 0xc028 ? If thats the case, what about the code just below jz? that is *dead* code, right? The value copied to r15 is defined at link time, and is not zero when you have global variables that must be initialized before main() gets invoked. jayaura, bluehash and spirilis 3 Quote Link to post Share on other sites
pabigot 355 Posted April 2, 2014 Share Posted April 2, 2014 BTW: The value in __watchdog_support() is not supposed to be WDTHOLD. If you choose to turn off the watchdog in main() (as your program does), that's fine, but the toolchain should not make that policy decision for you. Instead it preserves the power-up setting, and uses the value it saved on startup to ensure the watchdog does not fire during the startup code. jayaura and tripwire 2 Quote Link to post Share on other sites
wasson65 16 Posted April 2, 2014 Share Posted April 2, 2014 In a nutshell: Programming in C: Now why did that stupid compiler do that in a wasteful and ultra-careful way? Programming in Assembly: Now why didn't that stupid programmer spend 4 extra cycles to make that routine generally safer? ----- Once upon a time, there were people who coded PC applications in assembly because it made for faster programs. Then the machines got faster, compliers got a little better, and assembly really wasn't worth it any more on PC's. HOWEVER: Because libraries have to work (generally) on multiple architectures, and programs (generally) have to co-exist in memory and systems (generally) are multi-tasking, and interrupt routines (generally) must leave the environment exactly as they found them, there are things that the compliers are going to do, just to be 'good citizens'. A good part of this 'good citizenship' can be dropped if you have intimate knowledge about the environment that the code is going to have to run in, which we (generally) have. And when the amount of application logic is large compared to the amount of hardware specific logic, these tradeoffs make a lot of sense. BUT: On our little '430s the hardware specific logic is probably 80% of our entire codebase, so the generalizations can begin to consume a non-trivial amount of space and cycles, which, as you know, we never have quite enough of.... Viva la Assemblia! - lol tripwire and jayaura 2 Quote Link to post Share on other sites
jayaura 0 Posted April 2, 2014 Author Share Posted April 2, 2014 All your questions relate to mspgcc's runtime support infrastructure and are independent of the program you've compiled. Because the value that needs to get written to the watchdog timer to reset it varies depending on MCU, and the expression computed by __watchdog_support reads the current setting then changes it to be a valid reset that preserves that setting. (Specifically, in some MCU families the bits that produce the power-up default timeout interval are different because of clock differences, so having a constant won't work across all platforms. I didn't want yet another chip-specific object file, so I found a general solution.) It's copied into RAM so that subsequent uses of the intrinsic __watchdog_clear() know what value to write. mspgcc also supports intrinsics __set_watchdog_clear_value() and __get_watchdog_clear_value(). It is the only toolchain that does this, so since it's not supported anymore people will have to create a new solution to the problem. Because your example doesn't use the built-in watchdog support, but the runtime system doesn't know that so it leaves it present. The value copied to r15 is defined at link time, and is not zero when you have global variables that must be initialized before main() gets invoked. Dear pabigot Thank you for that informative response. I tried adding __set_watchdog_clear_value(WDTPW+WDTHOLD); ed at the start of main() and checked the disassembled output, but I get the same watchdog_support section as before. What is the right way to use the inbuilt watchdog ? It might be an overkill for me right now to use these advanced stuff, but I would also like to know about all the other internal features that mspgcc has. Is there a documentation about them anywhere? I googled, but couldnt find anything regarding the watchdog functions you mentioned. The best I could find was http://mspgcc.sourceforge.net/manual/book1.html. How did you compile it? What optimization options did you use? Try -Os if you haven't already. Sent from my Galaxy Note II with Tapatalk 4 I already had -Os in the build. Quote Link to post Share on other sites
jayaura 0 Posted April 2, 2014 Author Share Posted April 2, 2014 Viva la Assemblia! - lol Indeed! Quote Link to post Share on other sites
Rickta59 589 Posted April 2, 2014 Share Posted April 2, 2014 When using 'C', the compiler always gives you enough rope to hang yourself. When I was writing the msp430 fabooh framework, I also wanted to get rid of this extra bloat. When I compile this: https://github.com/RickKimball/fabooh/blob/master/examples/basic/blink/blink.cpp the output is this: msp430g2231in14_release/blink.elf: file format elf32-msp430 Disassembly of section .text: 0000f800 <__watchdog_support>: f800: b2 40 80 5a mov #23168, &0x0120 ;#0x5a80 f804: 20 01 0000f806 <__init_stack>: f806: 31 40 80 02 mov #640, r1 ;#0x0280 0000f80a <__do_clear_bss>: BCSCTL1 = CALBC1_8MHZ; DCOCTL = CALDCO_8MHZ; #endif } else if (MCLK_FREQ == 1000000) { BCSCTL1 = CALBC1_1MHZ; f80a: d2 42 ff 10 mov.b &0x10ff,&0x0057 f80e: 57 00 DCOCTL = CALDCO_1MHZ; f810: d2 42 fe 10 mov.b &0x10fe,&0x0056 f814: 56 00 clear(pins_mask); pren |= pins_mask; } static ALWAYS_INLINE void setmode_output(const uint8_t pins_mask) { pdir |= pins_mask; f816: d2 d3 22 00 bis.b #1, &0x0022 ;r3 As==01 static ALWAYS_INLINE void clear(const uint8_t pins_mask) { pout &= ~pins_mask; } static ALWAYS_INLINE void toggle(const uint8_t pins_mask) { pout ^= pins_mask; f81a: d2 e3 21 00 xor.b #1, &0x0021 ;r3 As==01 void loop(void) { while (1) { RED_LED::toggle(); delay(msec_delay); f81e: 2e 43 mov #2, r14 ;r3 As==10 f820: 3f 40 82 45 mov #17794, r15 ;#0x4582 f824: 1f 83 dec r15 f826: fe 23 jnz $-2 ;abs 0xf824 f828: 1e 83 dec r14 f82a: fc 23 jnz $-6 ;abs 0xf824 f82c: 03 43 nop f82e: f5 3f jmp $-20 ;abs 0xf81a 0000f830 <__stop_progExec__>: f830: 32 d0 f0 00 bis #240, r2 ;#0x00f0 f834: fd 3f jmp $-4 ;abs 0xf830 0000f836 <__ctors_end>: f836: 30 40 3a f8 br #0xf83a 0000f83a <_unexpected_>: f83a: 00 13 reti Disassembly of section .vectors: 0000ffe0 <__ivtbl_16>: ffe0: 36 f8 36 f8 36 f8 36 f8 36 f8 36 f8 36 f8 36 f8 6.6.6.6.6.6.6.6. fff0: 36 f8 36 f8 36 f8 36 f8 36 f8 36 f8 36 f8 00 f8 6.6.6.6.6.6.6... I compiled the code above like this: $ make Compiling file: blink.cpp msp430-g++ -c -mmcu=msp430g2231 -Os -Wall -Wextra -DFIXMATH_NO_CACHE -DFIXMATH_NO_64BIT -DFIXMATH_NO_ROUNDING -fwrapv -fmessage-length=0 -ffunction-sections -fdata-sections -std=gnu++98 -g -ggdb3 -fno-rtti -fno-exceptions -fverbose-asm -DFABOOH -DF_CPU=1000000 -MD -MP -MF msp430g2231in14_release/blink.d -I../../../ -I../../../board/msp430g2231in14 -I../../../include/msp430/core -I../../../include/3rdparty blink.cpp -o msp430g2231in14_release/blink.o Linking target: msp430g2231in14_release/blink.elf msp430-gcc -mmcu=msp430g2231 -DFIXMATH_NO_CACHE -DFIXMATH_NO_64BIT -DFIXMATH_NO_ROUNDING -fwrapv -fmessage-length=0 -ffunction-sections -fdata-sections -mmcu=msp430g2231 -mdisable-watchdog -Wl,--gc-sections,-Map=msp430g2231in14_release/blink.map,-umain -L../../../board/msp430g2231in14 msp430g2231in14_release/blink.o -o msp430g2231in14_release/blink.elf If you look at the main.h header file, you can see how I replaced the system functions with my own versions that do nothing: https://github.com/RickKimball/fabooh/blob/master/include/msp430/core/main.h#L116 Another thing to reducing the size is to use the -mdisable-watchdog flag. That gets rid of all extra WDT code. Of course when all this fails to work, you can at least claim your code is small -rick jayaura 1 Quote Link to post Share on other sites
pabigot 355 Posted April 2, 2014 Share Posted April 2, 2014 I tried adding __set_watchdog_clear_value( WDTPW+WDTHOLD); ed at the start of main() and checked the disassembled output, but I get the same watchdog_support section as before. What is the right way to use the inbuilt watchdog ? Nothing short of editing the linker script or providing an alternative definition will affect __watchdog_support. It belongs there. Do not mess with it. To use the built-in watchdog, simply don't add WDTCTL = WDTPW + WDTHOLD; anywhere. That turns the watchdog off. Instead, make sure you invoke __watchdog_clear() every 32ms. Use __set_watchdog_clear_value() to assign a new value (WDTPW + something) that configures the interval you want; even if you do, still invoke __watchdog_clear() at the required frequency. Consult the family user's guide for details on how to determine alternative configuration values. It might be an overkill for me right now to use these advanced stuff, but I would also like to know about all the other internal features that mspgcc has. Is there a documentation about them anywhere? I googled, but couldnt find anything regarding the watchdog functions you mentioned. The best I could find was http://mspgcc.sourceforge.net/manual/book1.html.No; that manual is completely out of date and does not mention these functions. Look at intrinsics.h that was installed with the compiler, and experiment. Probably nobody but me ever used those functions. jayaura 1 Quote Link to post Share on other sites
pabigot 355 Posted April 2, 2014 Share Posted April 2, 2014 Another thing to reducing the size is to use the -mdisable-watchdog flag. That gets rid of all extra WDT code. Yes, that's the best solution if you want to pretend the MCU doesn't have a watchdog timer. It'll only work on mspgcc (not msp430-elf-gcc), and you'll save a whole 18 bytes of code and 2 bytes of RAM. ("Bloat"?) Of course when all this fails to work, you can at least claim your code is small Um, yes. de gustibus non est disputandum and all that. Quote Link to post Share on other sites
Rickta59 589 Posted April 2, 2014 Share Posted April 2, 2014 I actually think the msp430-gcc 4.6.3 version produces pretty reasonable sized output. You can always throw inline asm at any code you really don't like. There are some other tricks you can use, like reserving a fixed register for use with the mainline code and some timing critical ISR to avoid register pushing and popping overhead. However, when you get to that point, you come to the realization that you are probably using the wrong chip. I'm saddened that the new msp430-elf-gcc stuff truly does have bloat. (BTW: the bloat comment was tongue in cheek in the posting above) pabigot and spirilis 2 Quote Link to post Share on other sites
pabigot 355 Posted April 2, 2014 Share Posted April 2, 2014 Yeah, msp430-elf-gcc didn't turn out the way I'd hoped it would when I endorsed that initiative. The compiler is actually pretty good; the problems with expectations and device support aren't Red Hat's fault. I'm really enjoying Cortex-M development, though again TI's software approach for Tiva comes up short relative to my quality expectations. Silicon Labs, though, is getting an A- for the EFM32 line. (BTW: the bloat comment was tongue in cheek in the posting above) I mostly figured it was, but I'm finding I'm a bit sensitive since I did put a lot of effort into making sure the CRT stuff in particular (which is in assembly) was both space/time efficient and conformed to the requirements of the language. (For years Code Composer Studio didn't initialize global variables to zero because of a fear that the watchdog would fire while they were doing so, thus making their implementation of C completely non-conforming and requiring the application to explicitly initialize globals and statics---assuming it could somehow figure out that it hadn't already done so.) 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.