Bare metal C from scratch (Cortex M0+ Kinetis from Freescale) - Page 1
Forum section: electronics / general

author: RoGeorge
author_rank: Super Contributor
timestamp: February 25, 2022, 03:24:22 pm
content: I'm trying to have some C fun, mostly for learning, with an ARM Cortex M0+ from Freescale. This devboard,Kinetis FRDM-KL25Z, has a small MCU (128K flash, 16K SRAM) almost like an AVR.The Freescale toolchain is based on Eclipse, and their configuration tool called Processor Expert is a plugin for Eclipse. Looks cumbersome and heavy, I don't need/want to learn their RTOS and/or code boilerplates, so I would like to use the command line and GNU ARM compiler instead (which is possible for this board/MCU).However, when I try to write from scratch, even the most basic tasks like configuring the clocks and the ports require way too much work and sometimes there are configuration quirks and certain steps had to be done in certain order, etc., so it looks close to impossible to write from scratch, without relying on their code templates.It all looks overly complicated and I'm not sure why.Is bare metal C (from scratch) the wrong way with ARM, or it just happened this particular MCU to be a nasty one?

author: PlainName
author_rank: Super Contributor
timestamp: February 25, 2022, 04:52:26 pm
content: Sounds normal to me. Typically the C runtime will set up memory, copy stuff from ROM to is, clear some of it, then dump you at main() and it's all yours from then on. If anything needs configuring then you have to configure it, and if it's got foibles then you have to work around them. Always been that way at this level.Which is why you get stuff like Arduino and manufacturer IDEs/libs to smooth it all out. If you look under the bonnet at those then it should be obvious that not using them means you'll have to replicate much of it. For some that's good and for others a giant PITA, but such is life.

author: ataradov
author_rank: Super Contributor
timestamp: February 25, 2022, 05:15:29 pm
content: With ARM devices you typically have more options and more rich and complex peripheral, so there is some increase of configuration difficulty.But it just comes from the established architecture. And it is more or less the same for all ARM devices. Once you figure out what is going on, you can apply that knowledge to every device.Libraries will hide this from you to some extent, and it makes it easier to start. But it is very bad in a long run, since you essentially don't fully understand how your system works.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 25, 2022, 06:27:51 pm
content: There are two things to consider IMO: setting up the startup code, the linker script and compiler options, and then configuring the MCU and its peripherals directly through registers instead of relying on some vendor library.The startup code and linker scripts are usually provided by vendors, and you can get inspiration from those to write your own, or use them as is. Same for compiler options: most often, vendor tools rely on makefiles, so just read the makefiles which will contain the information you need.As to avoiding vendor libraries, reading datasheets and reference manuals should be all that's needed. But here again, looking at the vendor libraries' source code should help you get started for writing your own stuff. To sum it up, do not hesitate to look at the vendors' source code! The good thing is that all vendors (at least that I've worked with) provide source code for their libraries and example projects, so that's certainly some help to get you started - you don't need to use those libraries blindly. Source code is there!But sure, modern 32-bit MCUs are complex beasts, so don't expect this to be a complete picnic. Though, starting with a M0+-based one sounds like a good idea as it's going to be simpler to setup than a typical M7, for instance.

author: westfw
author_rank: Super Contributor
timestamp: February 27, 2022, 01:43:24 am
content: QuoteThe clock setup complexity on most ARM chips is unfortunate, since it typically stands in the way of getting to less-complex and less "scary" peripherals, and freaks out potential bare metal programmers. :-)Frequently, you can get by with using the default startup clock. Code that is much simpler than the usual SDK clock initialization COULD be derived if you want the usual "set clock to the max allowed", but coming up with the simpler code may involved much stressing over the datasheet, and probably figuring out the SDK functionality :-( Data sheets rarely document things in a sensible order :-(Once you have the clocks set up (possibly including flash wait-states and such), and the various peripherals turned "on" WRT clock and/or "power manager" configuration, the peripherals themselves shouldn't be nearly as difficult.I am afraid that I haven't gotten around to playing with my NXP/Freescale boards yet. :-(

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 27, 2022, 03:25:10 am
content: Yep, the setup of clocks is sometimes a hard bit to tackle on those MCUs.Another one - the MMU, for the MCUs that have one. Yes, on some, you *need* to configure it to be able to use some peripherals/DMA/enable cache/whatever/... and that's usually even "trickier" than setting up the clocks. This part of course is rarely for the simpler M0+ MCUs, but can be seen on the more complex ones, such as some M7-based MCUs. Going bare metal, while using "advanced" features of the NXP iMXRT MCUs is no picnic.

author: westfw
author_rank: Super Contributor
timestamp: February 27, 2022, 08:57:53 am
content: I dragged out my KL25Z board, installed MCUXpresso, and fired up the frdmkl25z_driver_examples_gpio_led_output ("blink") example.I'm relatively impressed - once I cut out some of the obvious fluff ("printf("This is blink");" and the uart drivers and "file IO" from "REDLIB", the binary was down to about 2k of code. That's not too bad.As expected, the main culprits are clock setup:Code:00000434 00000030 T main000006c0 00000044 T GPIO_PinInit00000678 00000048 T CLOCK_BootToPeeMode00000490 0000004c T CLOCK_SetSimConfig000000d4 0000004c T ResetISR00000550 00000050 T CLOCK_EnablePll000000774 00000060 T BOARD_InitPins000005a0 00000068 T CLOCK_InitOsc000000704 00000070 T BOARD_BootClockRUN00000608 00000070 T CLOCK_SetPbeMode000004dc 00000074 T CLOCK_SetInternalRefClkConfigOne of the nice things about the "simpler" ARM chips is that there isn't any OS "protecting" the hardware.You are absolutely free to import the SDK's clock initialization code, and then implement the rest of your code in as bare-metal a fashion as you might want. You can always go back to the clocks, later.

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 27, 2022, 09:41:03 am
content: Quote from: westfw on February 27, 2022, 08:57:53 amI wonder if whoever wrote that function realized the double entendre in the name. Or is it just me and my dirty, juvenile mind?

author: dietert1
author_rank: Super Contributor
timestamp: February 27, 2022, 12:15:56 pm
content: Most of the cortex devices have a built in, default clock generator. So if you don't care about the clock rate, you can run a simple C main() without clock initialization. Next thing is PIO, which is simple once you find the proper reference manual. Then you just need to understand how port configuration registers are named in your framework. Sometimes the names are identical or similar to the ones used in the reference manual. Also there are some energy saving bits, i mean you need to set a bit to route the clock generator to the port logic in order to make it work. Same with interrupt vectors, one can do it step by step. The C framework has some extention to define interrupt procedures.Using libraries and configuration tools saves a lot of time, though.Regards, Dieter

author: RoGeorge
author_rank: Super Contributor
timestamp: February 27, 2022, 12:56:22 pm
content: Quote from: westfw on February 27, 2022, 08:57:53 amI didn't know about MCUXpresso, thank you, downloaded, will give it a try. So far I've only used their "CodeWarrior" IDE which was left on a Windows laptop from about 5-10 years ago, when I first tried the FRDM-KL25Z devboard.Recently (couple of weeks ago) I've tried the GCC ARM compiler on Linux, and updated a git demo to compile with current version (arm-none-eabi-gcc --versionreturns "arm-none-eabi-gcc (15:9-2019-q4-0ubuntu1) 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599] Copyright (C) 2019 Free Software Foundation, Inc.")To install the GCC ARM compiler, I did:Code:sudo apt install gcc-arm-none-eabiand to grant user access to the devboard (add the current user to the dialout group, so to have access to the serial ports, /dev/ttyACMx is USB serial port used by the devboard):Code:sudo usermod -a -G dialout my_linux_usernameThe GCC ARM does not have any specific files for the Freescale devboard, so I forked from github two projects meant for this FRDM-KL25Z devboard I happen to have (projects payne92/bare-metal-arm and 1nv1/OpenKL25Z). These are projects for the FRDM-KL25Z devboard, just that they are from many years ago. The last one even has ADC readings implemented, which was particularly tempting (the ADC has 16 bits instead of the usual 10 bits in other MCUs).However, the second example project (the one with the ADC) also has some multitasking features, or an incipient RTOS, which made it hard to understand (for me, not a software dev).And such came the idea of starting from scratch, where after a couple of days of reading and tries I failed gloriously, so thought of asking in the hope somebody would say "it's not you, it's the MCU".The two git projs I'm talking (updated to compile with current GCC) arehttps://github.com/RoGeorge/bare-metal-armhttps://github.com/RoGeorge/OpenKL25ZTo compile and run the second one (with ADC):QuoteI haven't uploaded on github any of my attempts of starting from scratch (instead of starting from one of the above already working git examples), because I don't have yet any that works. Made a couple of failed attempts, then got stuck/overwhelmed by the docs. Kilo-pages of docs like these:https://spivey.oriel.ox.ac.uk/dswiki/images-digisys/5/56/KL25-refman.pdfhttps://www.nxp.com/docs/en/reference-manual/KSDK20APIRM.pdf

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 27, 2022, 06:05:15 pm
content: Quote from: Nominal Animal on February 27, 2022, 09:41:03 amI admit this is an unfortunate naming, but yes, you seem to be prone to picking this kind of stuff up lately.

author: westfw
author_rank: Super Contributor
timestamp: February 28, 2022, 06:53:04 pm
content: QuoteCodeWarrior was the Freescale IDE (Motorola bought CW a long time ago.) MCUXpresso is the NXP IDE (NXP Bought the MCUX folk ("Code Red") somewhat more recently (2013, actually.)Xpresso seems to follow the model of "into each project, copy all of the files that might be relevant." This includes both the .h files that reflect the hardware, AND the .h and .c files for the "sdk." That's one valid way to do source control (and it's catching on a disks get bigger), but it's annoying from the perspective of someone trying to do bare-metal development. I *want* the avr-gcc model that says "put one include file at the top of your source, and it will be in the default include path, and will suck in all the hardware definitions that you need. Libraries are separate; you didn't want to use libraries, anyway, did you?"Worse, the basic HW definitions for the CPU seem to be buried in some .zip file somewhere in the IDE. So setting up a project without the IDE's "helpfullness" is difficult. It might be best to start with an existing example project, and extract files from there...

author: RoGeorge
author_rank: Super Contributor
timestamp: February 28, 2022, 09:56:30 pm
content: Yesterday I've made an account (can not download the MCUXpresso IDE without having an NXP account, grrr!) then somehow arrived to a page that can build a custom SDK that includes only the specified board/compiler/OS.I didn't understand yesterday very well what that does, so requested a few combinations of OS/compiler for the FRDM-KL25Z board only. Looking inside those downloaded SDK today, there is a very clean tree structure with all the .c and .h files, including the very startup init code loaded before any compiled main.cI didn't try it yet, but the SDK alone seems enough to build a C project without any IDE. That would be close enough from what I was trying to do, if I lose the "from scratch" part (which for now I'm ready to give up without regrets), and keep only the "bare metal C" request from the title.Seems like the missing of the SDK files was the main reason of frustration.Found all the missing components now, thank you, will tinker with the FRDM-KL25Z board in the next days.I'll probably still use their MCUXpresso at least to quickly configure peripherals/pinouts for the MCU (they also seem to have an online configurator tool for this (https://mcuxpresso.nxp.com/en/pinsnot yet sure how that works), will see.

author: mac.6
author_rank: Regular Contributor
timestamp: March 01, 2022, 09:41:48 pm
content: All the NXP SDKs share the same structure, because underlying it's the same code base.There is support for GCC+CMake under linux, not great imho, but it's here.