Author Topic: Cortex-M startup files without assembler  (Read 14893 times)

0 Members and 1 Guest are viewing this topic.

Offline technixTopic starter

  • Super Contributor
  • ***
  • Posts: 3507
  • Country: cn
  • From Shanghai With Love
    • My Untitled Blog
Cortex-M startup files without assembler
« on: March 07, 2017, 04:01:28 pm »
I have figured out a way to use a combination of linker scripts and C language extensions to create Cortex-M startup files without touching any assembler. Is it worth the effort? Will it be problematic down the road?

Here is the general gist:
  • Linker scripts can place symbols at critical locations like the beginning and the end of different sections for you. This includes code (which I usually don't put symbols to,) data (important,) BSS (maybe not too important,) and stack (crucial.)
  • Library routines memcpy and memset can be used to move data in place and clear BSS, if you have marked data sections and BSS with symbols.
  • Through the use of linker scripts, sections can be places accurately (important for the ISR)
  • Most C compiler supports putting things, like functions, structs and arrays in specific sections.

I have created assembler-free startup code for STM32F103 and STM32F303, all in this fashion. I use the free and open source GNU toolchain for this. LLVM/clang should work too if you can set up the cross compiling toolchain.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11248
  • Country: us
    • Personal site
Re: Cortex-M startup files without assembler
« Reply #1 on: March 07, 2017, 06:08:30 pm »
What do you mean " figured out"? This feature was the explicit design goal of the core.

Here are my starter projects with no assembly in them at all https://github.com/ataradov/mcu-starter-projects/ , but that's nothing special really, that's the expected behavior.
« Last Edit: March 07, 2017, 06:10:04 pm by ataradov »
Alex
 

Offline mark03

  • Frequent Contributor
  • **
  • Posts: 711
  • Country: us
Re: Cortex-M startup files without assembler
« Reply #2 on: March 07, 2017, 06:17:42 pm »
I have noticed vendors differ in their use of assembler or C in their CMSIS packages, e.g. some define the interrupt handlers in assembler, others in C.  It doesn't make a huge difference, but assembler syntax is more uniform across assemblers than low-level compiler directives are across compilers.  E.g. it's actually easier to do everything in C from gcc than it is from IAR, because gcc supports more low-level meddling from the C source than IAR does.

IMO many embedded SW people are unjustifiably allergic to assembly code.  Personally, I would rather just have an assembly file than "do assembly in C" for the sake of being 100% pure.  AFAIK every C toolchain has an assembler; it's good to use it when appropriate.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11248
  • Country: us
    • Personal site
Re: Cortex-M startup files without assembler
« Reply #3 on: March 07, 2017, 06:21:08 pm »
The only thing you need, is ability to place a symbol at a fixed offset. You don't really need any non-standard C for this, but linker must support such placement. And this will apply for assembler as well, anyway.
Alex
 

Offline Sal Ammoniac

  • Super Contributor
  • ***
  • Posts: 1670
  • Country: us
Re: Cortex-M startup files without assembler
« Reply #4 on: March 07, 2017, 06:24:17 pm »
I don't like using in-line assembly in a C file. I prefer to just do it purely in assembler in a separate file.

But I like writing assembly code, which is getting more and more rare these days. I've been in the embedded SW game since assembly was the only option. Good C compilers didn't exist back then, and even when they did, resources were so tight that sometimes it only made sense to write hand optimized assembler. As a result, I have no aversion to writing in assembly when needed.
Complexity is the number-one enemy of high-quality code.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11248
  • Country: us
    • Personal site
Re: Cortex-M startup files without assembler
« Reply #5 on: March 07, 2017, 06:29:40 pm »
But that's the thing. Assembly is not needed for Cortex-M devices. It is not a hack, it is a thing that was a bullet point in the design documents.
Alex
 

Offline mark03

  • Frequent Contributor
  • **
  • Posts: 711
  • Country: us
Re: Cortex-M startup files without assembler
« Reply #6 on: March 07, 2017, 08:25:39 pm »
The only thing you need, is ability to place a symbol at a fixed offset. You don't really need any non-standard C for this, but linker must support such placement. And this will apply for assembler as well, anyway.

I think you are oversimplifying.  What about weak symbols, to pick just one example?  Sure, they're not strictly required, but pretty much standard in any CMSIS-compliant project.  Can't do that in standard C.

Anyway, how do you place a symbol at a fixed offset?  I guess you could delegate this entirely to the linker script, i.e. in the linker config you say "place symbol foo at offset bar", but it's cleaner to have a user-defined section and place the section in the linker config.  Then you are back to vendor-specific pragmas for telling the compiler which objects belong in which section.  Not that there is anything wrong with this, but again, it's not standard C.  So intelligent compromise is necessary.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11248
  • Country: us
    • Personal site
Re: Cortex-M startup files without assembler
« Reply #7 on: March 07, 2017, 09:06:32 pm »
Can't do that in standard C.
Weak symbols are also not a given in assembly. But you don't really need them.

in the linker config you say "place symbol foo at offset bar", but it's cleaner to have a user-defined section and place the section in the linker config.
It is cleaner, but there is no standard assembly syntax either, so if you are worried about portability between the toolchains, then you are not solving anything. And going between IAR assembler and GCC assembler is way harder than going between IAR C and GCC C extensions.
Alex
 
The following users thanked this post: newbrain

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: Cortex-M startup files without assembler
« Reply #8 on: March 08, 2017, 06:27:26 am »
I don't like using in-line assembly in a C file. I prefer to just do it purely in assembler in a separate file.

I haven't written a lot of assembly (lately), but if there's just a small amount needed--usually to manipulate weird registers in the core--I'd much rather do it as asm volatile (). Putting the assembly in C/C++ form means I can use the same constants as in the main program, and also make sure the whole thing is type safe.

With your assembly in a separate file, your point of integration is at the linker stage. I.e., you can write functions in assembly and call them from C. With inline functions defined with inline assembly, you can mix it up a bit more and essentially insert snippets of assembly within other functions.

Plus, I can never remember what the difference between .s and .S is.  :o
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: Cortex-M startup files without assembler
« Reply #9 on: March 08, 2017, 11:19:03 am »
Plus, I can never remember what the difference between .s and .S is.
That's still simple, what really gets people is the difference between .c and .C.

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: Cortex-M startup files without assembler
« Reply #10 on: March 08, 2017, 02:56:05 pm »
That's still simple, what really gets people is the difference between .c and .C.
.C ?
 

Offline janoc

  • Super Contributor
  • ***
  • Posts: 3785
  • Country: de
Re: Cortex-M startup files without assembler
« Reply #11 on: March 08, 2017, 03:33:02 pm »
That's still simple, what really gets people is the difference between .c and .C.
.C ?

.c = C code
.C = C++ code - original extension for C++ still used by some compilers on case-sensitive platforms (mostly Unix). Such file will be compiled as C++ with common compilers (gcc, for ex). This could seriously trip you up if you are careless with your filenames. I haven't tried what will gcc make out of a file with .C extension in Windows (case-insensitive but case-preserving system) - it is possible it will understand it as C++ as well.

In fact, the reason why we have .cpp today instead of .C is because case insensitive platforms have proliferated since C++ has appeared and there it is not possible/practical to make difference between .c and .C extensions in filenames. You could also find .cc extension for C++ code.

.s and .S is the same thing, assembler source. HOWEVER, the .S will get preprocessor run on it first, .s will not. Unless you are Apple user, apparently there the preprocessor will be used on both types of files. Yay ...
« Last Edit: March 08, 2017, 03:41:37 pm by janoc »
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26906
  • Country: nl
    • NCT Developments
Re: Cortex-M startup files without assembler
« Reply #12 on: March 08, 2017, 03:59:22 pm »
What do you mean " figured out"? This feature was the explicit design goal of the core.
I agree. All it takes is a memset and a memcpy to initialise the memory (bonus points for clearing the stack space as well). I have been doing that on ARM Cortex since day one. It is all in the linker script and I don't see how that can be any different on a non-GCC compiler unless it has a really crappy linker which doesn't allow to create symbols in the linker script.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: Cortex-M startup files without assembler
« Reply #13 on: March 08, 2017, 04:05:08 pm »
.c = C code
.C = C++ code - original extension for C++ still used by some compilers on case-sensitive platforms (mostly Unix). Such file will be compiled as C++ with common compilers (gcc, for ex). This could seriously trip you up if you are careless with your filenames. I haven't tried what will gcc make out of a file with .C extension in Windows (case-insensitive but case-preserving system) - it is possible it will understand it as C++ as well.

In fact, the reason why we have .cpp today instead of .C is because case insensitive platforms have proliferated since C++ has appeared and there it is not possible/practical to make difference between .c and .C extensions in filenames. You could also find .cc extension for C++ code.

.s and .S is the same thing, assembler source. HOWEVER, the .S will get preprocessor run on it first, .s will not. Unless you are Apple user, apparently there the preprocessor will be used on both types of files. Yay ...
Yep, I write all my code on a mac and have been bitten by the case insensitive filesystem before. I was even writing C++ back in the cfront days (also on a mac), but have forgotten what file extensions we used.

I prefer .cc to .cpp. Actually, .cpp isn't that bad, but .hpp just seems wrong (and they like to go together). So I use .cc and .h

Oh, and back on the original topic... I've been using X-Macros to define the interrupt vector on my Cortex-M builds:

Code: [Select]
// Create a forward declaration for each interrupt handler
#define TM4C_INTERRUPT(num,bit,name) extern "C" void name ## _Handler(void) __attribute__ ((weak, alias("Unused_Handler")));
  TM4C_INTERRUPTS
#undef TM4C_INTERRUPT

typedef void (*handler_t)(void);

// Create a structure definition to hold all the handlers in order
struct cortex_interrupt_vectors_t {
  void *initial_msp;
#define TM4C_INTERRUPT(num,bit,name) handler_t name ## _handler;
  TM4C_INTERRUPTS
#undef TM4C_INTERRUPT
} const cortex_vectors __attribute__ ((section(".vectors"), used)) = {
  &__main_stack_end__,
#define TM4C_INTERRUPT(num,bit,name) .name ## _handler = name##_Handler,
  TM4C_INTERRUPTS
#undef TM4C_INTERRUPT

The interrupts are defined like this:
Code: [Select]
#pragma once

/*
 * This macro defines the names and order of the interrupt handlers for the
 * TM4C1232H6PM mcu. The macro is defined once and expanded multiple times as an
 * X Macro (ref http://en.wikipedia.org/wiki/X_Macro).
 *
 * These interrupts are defined in tm4c1232h6pm.pdf, Section 2.5.2
 */

//                 +------------------- Vector Number
//                 |                                 
//                 |    +-------------- Interrupt Bit Number
//                 |    |                             
//                 |    |    +--------  Vector Name
//                 |    |    |
//                 v    v    v
#define TM4C_INTERRUPTS                   \
  TM4C_INTERRUPT(  1,  -1,  Reset)        \
  TM4C_INTERRUPT(  2,  -1,  NMI)          \
  TM4C_INTERRUPT(  3,  -1,  HardFault)    \
  TM4C_INTERRUPT(  4,  -1,  MemManage)    \
  TM4C_INTERRUPT(  5,  -1,  BusFault)     \
  TM4C_INTERRUPT(  6,  -1,  UsageFault)   \
  TM4C_INTERRUPT(  7,  -1,  Reserved7)    \
  TM4C_INTERRUPT(  8,  -1,  Reserved8)    \
  TM4C_INTERRUPT(  9,  -1,  Reserved9)    \
  TM4C_INTERRUPT( 10,  -1,  Reserved10)   \
  TM4C_INTERRUPT( 11,  -1,  SVC)          \
  TM4C_INTERRUPT( 12,  -1,  DebugMonitor) \
  TM4C_INTERRUPT( 13,  -1,  Reserved13)   \
  TM4C_INTERRUPT( 14,  -1,  PendSV)       \
  TM4C_INTERRUPT( 15,  -1,  SysTick)      \
  TM4C_INTERRUPT( 16,   0,  GPIOA)        \
  TM4C_INTERRUPT( 17,   1,  GPIOB)        \
  TM4C_INTERRUPT( 18,   2,  GPIOC)        \
  TM4C_INTERRUPT( 19,   3,  GPIOD)        \
  TM4C_INTERRUPT( 20,   4,  GPIOE)        \
  TM4C_INTERRUPT( 21,   5,  UART0)        \
  TM4C_INTERRUPT( 22,   6,  UART1)        \
  TM4C_INTERRUPT( 23,   7,  SSI0)         \
  TM4C_INTERRUPT( 24,   8,  I2C0)         \
  TM4C_INTERRUPT( 25,   9,  Reserved25)   \
  TM4C_INTERRUPT( 26,  10,  Reserved26)   \
  TM4C_INTERRUPT( 27,  11,  Reserved27)   \
  TM4C_INTERRUPT( 28,  12,  Reserved28)   \
  TM4C_INTERRUPT( 29,  13,  Reserved)     \
  TM4C_INTERRUPT( 30,  14,  ADC0SS0)      \
  TM4C_INTERRUPT( 31,  15,  ADC0SS1)      \
  TM4C_INTERRUPT( 32,  16,  ADC0SS2)      \
  TM4C_INTERRUPT( 33,  17,  ADC0SS3)      \
  TM4C_INTERRUPT( 34,  18,  WATCHDOG)     \
  TM4C_INTERRUPT( 35,  19,  TIMER0A)      \
  TM4C_INTERRUPT( 36,  20,  TIMER0B)      \
  TM4C_INTERRUPT( 37,  21,  TIMER1A)      \
  TM4C_INTERRUPT( 38,  22,  TIMER1B)      \
  TM4C_INTERRUPT( 39,  23,  TIMER2A)      \
  TM4C_INTERRUPT( 40,  24,  TIMER2B)      \
  TM4C_INTERRUPT( 41,  25,  COMP0)        \
  TM4C_INTERRUPT( 42,  26,  COMP1)        \
  TM4C_INTERRUPT( 43,  27,  Reserved43)   \
  TM4C_INTERRUPT( 44,  28,  SYSCTL)       \
  TM4C_INTERRUPT( 45,  29,  FLASH)        \
  TM4C_INTERRUPT( 46,  30,  GPIOF)        \
  TM4C_INTERRUPT( 47,  31,  GPIOG)        \
  TM4C_INTERRUPT( 48,  32,  Reserved48)   \
  TM4C_INTERRUPT( 49,  33,  UART2)        \
  TM4C_INTERRUPT( 50,  34,  SSI1)         \
  TM4C_INTERRUPT( 51,  35,  TIMER3A)      \
  TM4C_INTERRUPT( 52,  36,  TIMER3B)      \
  TM4C_INTERRUPT( 53,  37,  I2C1)         \
  TM4C_INTERRUPT( 54,  38,  Reserved54)   \
  TM4C_INTERRUPT( 55,  39,  CAN0)         \
  TM4C_INTERRUPT( 56,  40,  Reserved56)   \
  TM4C_INTERRUPT( 57,  41,  Reserved57)   \
  TM4C_INTERRUPT( 58,  42,  Reserved58)   \
  TM4C_INTERRUPT( 59,  43,  Reserved59)   \
  TM4C_INTERRUPT( 60,  44,  USB0)         \
  TM4C_INTERRUPT( 61,  45,  Reserved61)   \
  TM4C_INTERRUPT( 62,  46,  UDMA)         \
  TM4C_INTERRUPT( 63,  47,  UDMAERR)      \
  TM4C_INTERRUPT( 64,  48,  ADC1SS0)      \
  TM4C_INTERRUPT( 65,  49,  ADC1SS1)      \
  TM4C_INTERRUPT( 66,  50,  ADC1SS2)      \
  TM4C_INTERRUPT( 67,  51,  ADC1SS3)      \
  TM4C_INTERRUPT( 68,  52,  Reserved68)   \
  TM4C_INTERRUPT( 69,  53,  Reserved69)   \
  TM4C_INTERRUPT( 70,  54,  Reserved70)   \
  TM4C_INTERRUPT( 71,  55,  Reserved71)   \
  TM4C_INTERRUPT( 72,  56,  Reserved72)   \
  TM4C_INTERRUPT( 73,  57,  SSI2)         \
  TM4C_INTERRUPT( 74,  58,  SSI3)         \
  TM4C_INTERRUPT( 75,  59,  UART3)        \
  TM4C_INTERRUPT( 76,  60,  UART4)        \
  TM4C_INTERRUPT( 77,  61,  UART5)        \
  TM4C_INTERRUPT( 78,  62,  UART6)        \
  TM4C_INTERRUPT( 79,  63,  UART7)        \
  TM4C_INTERRUPT( 80,  64,  Reserved80)   \
  TM4C_INTERRUPT( 81,  65,  Reserved81)   \
  TM4C_INTERRUPT( 82,  66,  Reserved82)   \
  TM4C_INTERRUPT( 83,  67,  Reserved83)   \
  TM4C_INTERRUPT( 84,  68,  I2C2)         \
  TM4C_INTERRUPT( 85,  69,  I2C3)         \
  TM4C_INTERRUPT( 86,  70,  TIMER4A)      \
  TM4C_INTERRUPT( 87,  71,  TIMER4B)      \
  TM4C_INTERRUPT( 88,  72,  Reserved88)   \
  TM4C_INTERRUPT( 89,  73,  Reserved89)   \
  TM4C_INTERRUPT( 90,  74,  Reserved90)   \
  TM4C_INTERRUPT( 91,  75,  Reserved91)   \
  TM4C_INTERRUPT( 92,  76,  Reserved92)   \
  TM4C_INTERRUPT( 93,  77,  Reserved93)   \
  TM4C_INTERRUPT( 94,  78,  Reserved94)   \
  TM4C_INTERRUPT( 95,  79,  Reserved95)   \
  TM4C_INTERRUPT( 96,  80,  Reserved96)   \
  TM4C_INTERRUPT( 97,  81,  Reserved97)   \
  TM4C_INTERRUPT( 98,  82,  Reserved98)   \
  TM4C_INTERRUPT( 99,  83,  Reserved99)   \
  TM4C_INTERRUPT(100,  84,  Reserved100)  \
  TM4C_INTERRUPT(101,  85,  Reserved101)  \
  TM4C_INTERRUPT(102,  86,  Reserved102)  \
  TM4C_INTERRUPT(103,  87,  Reserved103)  \
  TM4C_INTERRUPT(104,  88,  Reserved104)  \
  TM4C_INTERRUPT(105,  89,  Reserved105)  \
  TM4C_INTERRUPT(106,  90,  Reserved106)  \
  TM4C_INTERRUPT(107,  91,  Reserved107)  \
  TM4C_INTERRUPT(108,  92,  TIMER5A)      \
  TM4C_INTERRUPT(109,  93,  TIMER5B)      \
  TM4C_INTERRUPT(110,  94,  WTIMER0A)     \
  TM4C_INTERRUPT(111,  95,  WTIMER0B)     \
  TM4C_INTERRUPT(112,  96,  WTIMER1A)     \
  TM4C_INTERRUPT(113,  97,  WTIMER1B)     \
  TM4C_INTERRUPT(114,  98,  WTIMER2A)     \
  TM4C_INTERRUPT(115,  99,  WTIMER2B)     \
  TM4C_INTERRUPT(116, 100,  WTIMER3A)     \
  TM4C_INTERRUPT(117, 101,  WTIMER3B)     \
  TM4C_INTERRUPT(118, 102,  WTIMER4A)     \
  TM4C_INTERRUPT(119, 103,  WTIMER4B)     \
  TM4C_INTERRUPT(120, 104,  WTIMER5A)     \
  TM4C_INTERRUPT(121, 105,  WTIMER5B)     \
  TM4C_INTERRUPT(122, 106,  SYSEXC)       \
  TM4C_INTERRUPT(123, 107,  Reserved123)  \
  TM4C_INTERRUPT(124, 108,  Reserved124)  \
  TM4C_INTERRUPT(125, 109,  I2C4)         \
  TM4C_INTERRUPT(126, 110,  I2C5)         \
  TM4C_INTERRUPT(127, 111,  Reserved127)  \
  TM4C_INTERRUPT(128, 112,  Reserved128)  \
  TM4C_INTERRUPT(129, 113,  Reserved129)  \
  TM4C_INTERRUPT(130, 114,  Reserved130)  \
  TM4C_INTERRUPT(131, 115,  Reserved131)  \
  TM4C_INTERRUPT(132, 116,  Reserved132)  \
  TM4C_INTERRUPT(133, 117,  Reserved133)  \
  TM4C_INTERRUPT(134, 118,  Reserved134)  \
  TM4C_INTERRUPT(135, 119,  Reserved135)  \
  TM4C_INTERRUPT(136, 120,  Reserved136)  \
  TM4C_INTERRUPT(137, 121,  Reserved137)  \
  TM4C_INTERRUPT(138, 122,  Reserved138)  \
  TM4C_INTERRUPT(139, 123,  Reserved139)  \
  TM4C_INTERRUPT(140, 124,  Reserved140)  \
  TM4C_INTERRUPT(141, 125,  Reserved141)  \
  TM4C_INTERRUPT(142, 126,  Reserved142)  \
  TM4C_INTERRUPT(143, 127,  Reserved143)  \
  TM4C_INTERRUPT(144, 128,  Reserved144)  \
  TM4C_INTERRUPT(145, 129,  Reserved145)  \
  TM4C_INTERRUPT(146, 130,  Reserved146)  \
  TM4C_INTERRUPT(147, 131,  Reserved147)  \
  TM4C_INTERRUPT(148, 132,  Reserved148)  \
  TM4C_INTERRUPT(149, 133,  Reserved149)  \
  TM4C_INTERRUPT(150, 134,  Reserved150)  \
  TM4C_INTERRUPT(151, 135,  Reserved151)  \
  TM4C_INTERRUPT(152, 136,  Reserved152)  \
  TM4C_INTERRUPT(153, 137,  Reserved153)  \
  TM4C_INTERRUPT(154, 138,  Reserved154)
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: Cortex-M startup files without assembler
« Reply #14 on: March 08, 2017, 04:11:50 pm »
I agree. All it takes is a memset and a memcpy to initialise the memory (bonus points for clearing the stack space as well). I have been doing that on ARM Cortex since day one. It is all in the linker script and I don't see how that can be any different on a non-GCC compiler unless it has a really crappy linker which doesn't allow to create symbols in the linker script.
Seems like the IDE vendors like to do their own magic startup implementation, putting as much of it in their own assembler syntax to make it complicated and proprietary. That's enough to scare away most embedded coders and help lock projects into using that particular IDE.

It probably made more sense when proprietary compilers were the norm, but today with GCC and ARM Cortex being nearly everywhere, the proprietary stuff doesn't add nearly as much value (they do have nice debuggers though).

PS. When you "clear" the stack space... are you clearing it to 0? Or to some sentinel value for detecting overflow?
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26906
  • Country: nl
    • NCT Developments
Re: Cortex-M startup files without assembler
« Reply #15 on: March 08, 2017, 04:42:51 pm »
I just clear it to zero but during development I keep track on how much stack space I can use. I keep things simple so it is pretty straightforward.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline janoc

  • Super Contributor
  • ***
  • Posts: 3785
  • Country: de
Re: Cortex-M startup files without assembler
« Reply #16 on: March 08, 2017, 04:50:19 pm »
I prefer .cc to .cpp. Actually, .cpp isn't that bad, but .hpp just seems wrong (and they like to go together). So I use .cc and .h

.hpp emerged mostly because IDEs like Visual Studio or Borland/Turbo C didn't know how to figure out whether the header is C or C++ from the content. That messes up things like syntax highlighting. In fact, the standard C++ headers are without any extension, .h headers are supposed to be for C, but few projects actually follow this - especially with braindead IDEs like Visual Studio which are utterly lost if the extension is not present.

 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: Cortex-M startup files without assembler
« Reply #17 on: March 08, 2017, 05:23:59 pm »
Code: [Select]
// -*- Mode:C++ -*-
 

Offline technixTopic starter

  • Super Contributor
  • ***
  • Posts: 3507
  • Country: cn
  • From Shanghai With Love
    • My Untitled Blog
Re: Cortex-M startup files without assembler
« Reply #18 on: March 08, 2017, 11:51:26 pm »
My default file extension for C++ is .cc - to keep some symmetry with another language I often use on the desktop: .m and .mm for Objective-C and Objective-C++. Also I don't use .hpp or .hh but C++ headers have #ifdef __cplusplus outside #include guard.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Re: Cortex-M startup files without assembler
« Reply #19 on: March 09, 2017, 12:19:38 am »
>
Quote
Is it worth the effort?
No, I dont' think so.  Even in processors that are much less C-friendly than ARM, there is usually so little assembler startup code that it's not a big deal.  As others have pointed out, the assembler is probably more standardized and better documented than the (relatively minor) C compiler-dependent commands to accomplish the bits that need to happen, and it's usually the linker commands that confuse people (sadly needed for both assembler and C-based start-up code.)

Like various others, I had read the "no assembler needed" boilerplate from ARM ("as long as you implement our recommended intrinsics."), and I was surprised to see various compilers that were shipping with assembly startup code anyway (Keil, now owned by ARM, for instance.)
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11248
  • Country: us
    • Personal site
Re: Cortex-M startup files without assembler
« Reply #20 on: March 09, 2017, 12:23:05 am »
assembler is probably more standardized and better documented
I would 100% disagree with this statement. While actual assembly instructions are standard (UAL syntax defined by ARM), service commands are totally different among major compilers.

and I was surprised to see various compilers that were shipping with assembly startup code anyway (Keil, now owned by ARM, for instance.)
This is a very annoying practice of carrying legacy code and legacy thinking.

Hardware was designed to do something - take advantage of it.
Alex
 
The following users thanked this post: newbrain

Offline technixTopic starter

  • Super Contributor
  • ***
  • Posts: 3507
  • Country: cn
  • From Shanghai With Love
    • My Untitled Blog
Re: Cortex-M startup files without assembler
« Reply #21 on: March 09, 2017, 02:47:15 pm »
assembler is probably more standardized and better documented
I would 100% disagree with this statement. While actual assembly instructions are standard (UAL syntax defined by ARM), service commands are totally different among major compilers.

and I was surprised to see various compilers that were shipping with assembly startup code anyway (Keil, now owned by ARM, for instance.)
This is a very annoying practice of carrying legacy code and legacy thinking.

Hardware was designed to do something - take advantage of it.
I have a few dev boards based on ARM7TDMI, ARM926EJ-S and ARM1176JZF-S. For those boards there is always only one assembler file for the interrupt vector, and all remaining code is C with GCC extensions.
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26906
  • Country: nl
    • NCT Developments
Re: Cortex-M startup files without assembler
« Reply #22 on: March 09, 2017, 03:12:50 pm »
Those are older architectures. On those you need to setup various stack pointers using assembler. Also pushing & popping the variables on stack is not done automatically and there may be extra code needed to jump to thumb code (for example on the ARM7TDMI which executes interrupts in ARM mode).
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline janoc

  • Super Contributor
  • ***
  • Posts: 3785
  • Country: de
Re: Cortex-M startup files without assembler
« Reply #23 on: March 09, 2017, 07:47:49 pm »
Code: [Select]
// -*- Mode:C++ -*-

And that works elsewhere than in Emacs?

 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Re: Cortex-M startup files without assembler
« Reply #24 on: March 10, 2017, 10:04:36 am »
Quote
Quote
I was surprised to see various compilers that were shipping with assembly startup code
This is a very annoying practice of carrying legacy code and legacy thinking.
Hardware was designed to do something - take advantage of it.

I don't know.  Having the interrupt controller behavior match the C abi, so you don't need special compiler behavior for ISRs, is really nice.   But eliminating a half page of assembly start-up code by allowing the same thing in C seems a bit gimmicky.  Especially since most of that is just a big table of ISR vectors, which is equally opaque (or not) in either C or ASM.

FWIW, it looks like the startup code for ATMEL ARM chips is written entirely in C.

(Hmm.  I think a big factor might be that it's pretty important for startup code to be published in a form that users can use without having to buy an expensive compiler.  Most vendors have some sort of free assembler, but not all of them distribute a free C compiler, so having the startup code be in ASM is a bit of an advantage...) (?)

Here's the entire Atmel SAMD10 startup code, not including most of the vector table, or the magic commands that make symbols weak and otherwise easy to manipulate:

Code: [Select]
* Copyright (c) 2014 Atmel Corporation. All rights reserved.
/* Exception Table */
__attribute__ ((section(".vectors")))
const DeviceVectors exception_table = {

        /* Configure Initial Stack Pointer, using linker-generated symbols */
        (void*) (&_estack),

        (void*) Reset_Handler,
        (void*) NMI_Handler,
            :   // Bunch of vector omitted.
        (void*) ADC_Handler,            /* 15 Analog Digital Converter */
        (void*) AC_Handler,             /* 16 Analog Comparators */
        (void*) DAC_Handler,            /* 17 Digital Analog Converter */
        (void*) PTC_Handler             /* 18 Peripheral Touch Controller */
};

/**
 * \brief This is the code that gets called on processor reset.
 * To initialize the device, and call the main() routine.
 */
void Reset_Handler(void)
{
        uint32_t *pSrc, *pDest;

        /* Initialize the relocate segment */
        pSrc = &_etext;
        pDest = &_srelocate;

        if (pSrc != pDest) {
                for (; pDest < &_erelocate;) {
                        *pDest++ = *pSrc++;
                }
        }

        /* Clear the zero segment */
        for (pDest = &_szero; pDest < &_ezero;) {
                *pDest++ = 0;
        }

        /* Set the vector table base address */
        pSrc = (uint32_t *) & _sfixed;
        SCB->VTOR = ((uint32_t) pSrc & SCB_VTOR_TBLOFF_Msk);

        /* Initialize the C library */
        __libc_init_array();

        /* Branch to main function */
        main();

        /* Infinite loop */
        while (1);
}

void Dummy_Handler(void)
{
        while (1) {
        }
}
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf