If you are programming AVR microcontrollers in C, usually don’t think about how the compiled program is stored in microcontrollers’ flash memory. The compiler organizes data in the way it looks optimal. But sometimes, you are working with programs where you need to code chunks located in specific program memory locations. For instance, I faced this problem while developing an AVR controlled signal generator. I wanted to make an efficient and compiler independent main loop where the signal has to be read from flash memory and transferred to port. I managed to use the inline ASM function, which does the job:
//……………………………………………………..
void static inline signalOUT(const uint8_t *signal, uint8_t ad2, uint8_t ad1, uint8_t ad0)
{
asm volatile( “eor r18, r18 ;r18<-0" "\n\t"
“eor r19, r19 ;r19<-0" "\n\t"
“1:” “\n\t”
“add r18, %0 ;1 cycle” “\n\t”
“adc r19, %1 ;1 cycle” “\n\t”
“adc %A3, %2 ;1 cycle” “\n\t”
“lpm ;3 cycles” “\n\t”
“out %4, __tmp_reg__ ;1 cycle” “\n\t”
“rjmp 1b ;2 cycles. Total 9 cycles” “\n\t”
:
:”r” (ad0),”r” (ad1),”r” (ad2),”e” (signal),”I” (_SFR_IO_ADDR(PORTD))
:”r18″, “r19”
);
}
//……………………………………………………..
The linker produces ASM code like this:
eor r18, r18 ;r18<-0
eor r19, r19 ;r19<-0
1:
add r18, r11 ;1 cycle
adc r19, r16 ;1 cycle
adc r30, r17 ;1 cycle
lpm ;3 cycles
out 18, __tmp_reg__ ;1 cycle
rjmp 1b ;2 cycles. Total 9 cycles
I will not deep into this algorithm, but this generates a DDS signal where the frequency can be controlled by varying phase accumulator. I managed to send one sample in nine clock cycles. Using inline ASM, this part doesn’t depend on compiler optimization – it always will be the same.
I needed to pass signal table pointer value to this function:
//……………………………………………………..
signalOUT(sinewave,tfreq3, tfreq2, tfreq1);
//……………………………………………………..
My signal tables was declared like this:
//……………………………………………………..
//signals saved in flash memory
const uint8_t sinewave[] PROGMEM= //256 values
{0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95….
//……………………………………………………..
And my algorithm wasn’t working correctly. Because signal tables were not located at the 256-byte boundary, my algorithm requires the address to start at this boundary; otherwise, it reads part of the signal table and then continues reading other flash memory data. In other words, I needed to store at addresses like .org 0x100, .org 0x200…
Of course, AVR-GCC doesn’t support such commands, but there is another way – Use memory sections. Using them is not very hard.
The memory space “seen” by AVRGCC runs from addresses:
0x00000000 up to 0x0007FFFF for Flash memory,
0x00800000 up to 0x0080FFFF for RAM, and
0x00810000 up to 0x0081FFFF for EEPROM.
This means if you need your variable to be stored at a specific RAM address, you need to add offset 0x00800000 to your desired RAM address, like if you want the .data section to start at 0x1100, pass 0x801100 at the address to the linker.
The same is with EEPROM. Just offset is 0x00810000. For flash memory, you don’t need to add offsets.
Practically speaking, you may ask where to define these sections? Returning to my example, I defined my signal table this way:
//……………………………………………………..
//signals saved in flash memory
const uint8_t sinewave[] __attribute__ ((section (“.MySection1”)))= //256 values
{0x80,0x83,0x86,0x89,0x8c,0x8f,0x92…
//……………………………………………………..
you see I placed __attribute__ ((section (“.MySection1”))) instead of PROGMEM. This means that instead of letting the compiler decide where to store this table, I will define this place by myself.
Now I need to define Mysection1. Let’s say I want to put this table at flash address 0x1F00. Then I need to tel linker to put this section thereby using the command:
………………………………………………….
-Wl,--section-start=.MySection1=0x1F00
…………………………………………
If you work With the WinAVR tool-set,
just open Makefile and find where is
……………………………………………………..
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += $(CSTANDARD) located.
…………………………………………………….
At the end write additional line:
CFLAGS += -Wl,-section-start=.MySection5=0x1F00
This is it now you can compile the program.
Now my table is located at Address 0x1F00:
:101F000000020406080A0C0E10121416181A1C1EE1
:101F100020222426282A2C2E30323436383A3C3ED1
:101F200040424446484A4C4E50525456585A5C5EC1
:101F300060626466686A6C6E70727476787A7C7EB1
…
Note: To avoid any potential stack collisions or conflicts with other existing data in Flash memory, it would probably be best to locate such a memory section at the very end of program memory.
Simplier example of table, located in flash:
#include
uint8_t sinetable[] PROGMEM = { … };
…
level = pgm_read_byte_near(sinetable + phase);
But section address control information is very useful, thanks a lot!