3

I've spent most of my time developing for microcontrollers using tools and IDEs such as Atmel Studio, and abstracted from as to what goes on behind the scenes exactly.

Let's say in this case we are executing the code directly from flash which can be the case in embedded systems.

When we develop an application and flash it to the microcontroller using provided tools from the chip vendor, is it the loader / flasher that determines at what physical address in memory will be flashed?

I understand that the linker script defines memory offsets as to where the different sections such as .data and .txt should be placed, so is it actually this that ultimately determines at what address in the mcu flash everything should go?

Lets's say now that I discover a part of flash that is not used at all and I wish to put something else there (another application even), do I modify the linker script, create a new one, or how would I write to this particular location? I haven't fully grasped this yet.

Engineer999
  • 3,683
  • 6
  • 33
  • 71
  • normally you use command line options or some form of linker script to tell the linker to make the program match your desire. then the binary format used needs to contain this information then the loader uses that information to place the bytes in that location. – old_timer May 25 '19 at 01:29
  • some tools/targets support position independent code. where either at load time or runtime you can place these items in a place not matching what you told the linker, within the rules of what you can do. – old_timer May 25 '19 at 01:31

2 Answers2

4

Your idea is generally correct. A linker script, which is nothing more than a file with its specialized language that conveys certain information regarding memory and how they should be used, determines the layout of your program in the memory. Some linkers would accept such memory specifications via command line options as well.

Regarding your specific question, yes, you can write another firmware that occupies different memory addresses (than the first one) by modifying the second one's linker script, in particular, the starting address of the program image. This of course does not say anything about how these two pieces of firmware will run or communicate. Those are separate issues. You will also need to know the MCU's flash erasable page boundary so you can place the 2nd image in a different erasable page boundary.

In some firmware downloader / programmer, you may also be able to specify a different starting offset than what's specified in the firmware image. This is separate from the linker script process. There are a few uses for this. For example, your firmware maybe built to run from SRAM instead of directly from flash. Therefore at firmware download, it will need to be moved to a flash location, and at runtime, some kind of mechanism will be used to copy the code from the flash to SRAM where the program actually will run. There are other scenarios as well.

  • @Engineer999 I should also mention that to do what you say, the flash downloader must allow the option of "not mass erase chip". Mass erase is generally the default, since erasing a few pages may take as long as "erase entire chip" (different commands to the flash controller). In your case, if you want to preserve that content of the first firmware image, you would want this option. – Richard at ImageCraft May 27 '19 at 19:18
1

You asked if you should modify or create a new linker script. I would requirement that you modify the exciting linker script. I find them from time to time quite extensive. Why throw away what is already there while modifying it is only a few lines.

Some very common cases for micro controllers to change the linker script are:

  1. Adding a bootloader in the first flash sector while the actual program starts in a higher sector.
  2. Adding a sector dedicated to storing data generated by the application.
  3. Using a dedicated sector for configuration parameters.

Lets use the first as an example on how to write to firmware to different flash sectors. Important to know is that for this example the bootloader and the actual application are two separate projects each with their own compilation configuration or makefile. Each project also has its own linker script. Below part of both files is listed:

The bootloader:

/* Specify the memory areas */
MEMORY
{
  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 16K
  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
  CCMRAM (rw)     : ORIGIN = 0x10000000, LENGTH = 64K
}

The application:

/* Specify the memory areas */
MEMORY
{
  FLASH (rx)      : ORIGIN = 0x0800C000, LENGTH = (1M - 16K)
  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
  CCMRAM (rw)     : ORIGIN = 0x10000000, LENGTH = 64K
}

Please not the differences in the definition of the FLASH. The bootloader flash start at the default. The size allocated for the bootloader is 16K. Important to note is that the total flash size of the example chip is 1M. This is seen in the size of the flash of the application project. But we do not allow the full 1M for the application as of course we need to reduce it by the size allocated for the bootloader. You can also see it does not start at the same address. It has an offset of 0xC000 which amounts for the 16k.

Before you can run and debug the application code you have to do two more things:

  1. Define the vector table offset of your code. This is required for the compiler and linker to know where the code is located in memory. This is where my knowledge of this part is lacking a bit so anyone who knows please elaborate.
  2. If you would like to debug your application you also have to tell your debugger where to start. Atollic does this via a linker script. From what I remember from a few years back Atmel Studio has an option in the programming window where you can set the offset.

One final note: when you do something link this it is best to stick to the flash sectors of the chip. Often you can only delete a whole sector. Placing your next part of the code at the start of the next sector makes live easier.

The given examples are based on a linker file generated by Atollic TrueSTUDIO for STM32 for a STM32F4xx chip. Compilation is done using ARM gcc toolchain.

Bart
  • 1,405
  • 6
  • 32
  • Thanks for the info Bart. When we define the separate memory areas in the linker script in your example, how does the loader know to load the bootloader at 0x08000000 and the application at 0x0800C000? When we have the two different object/executable files, how do we link them to the information in the linker script addresses? Also, the RAM addresses etc. are the same for each ? – Engineer999 May 28 '19 at 15:52
  • Engineer999 as I mentioned above, if linked according to what @Bart wrote, the generated .ELF and .bin files will have the correct addresses baked in. There is no "loader" per se with the MCU. That's a concept inherent in a system running an OS. In the MCU's "bare metal" case that you are looking at, when you download your image to the flash, it will see the addresses in the .elf file and burn the image at that address. This is why I mentioned "disable mass erase" above. As for SRAM space, this is why I said, "the communications etc. between the two programs are up to you"... (more) – Richard at ImageCraft May 28 '19 at 21:45
  • So if your two "programs" are truly independent and somehow you want to run one, and then maybe under some conditions that you would reset the chip and get the other running, then using the same SRAM space is appropriate. This is the scenario with Bart's example - he is envisioning that one is a small bootloader and the other is a real app. Other scenarios are possible. If somehow you want the programs to actually talk to each other (for example, one could be a static library with known entry point addresses), then they cannot use the same SRAM space. – Richard at ImageCraft May 28 '19 at 21:47
  • In the case of most microcontroller when they startup they will always start executing the command at the first address of the flash. In my example you would have to tell the process to "jump" out of the bootloader into the application. This is done by setting the start address of the application in the vector table of the processor. The vector table is in short the memory address of the processor to execute. From the top of my head I believe that ram is reset when jumping. – Bart May 29 '19 at 06:56