3
\$\begingroup\$

I am new to assembly and microcontrollers and am trying to figure out how they work.

Now, I have read a lot how in assembly text and data segments are for storing program code and initial data for it. Everywhere people say[1,2,3] (if they say something) data segment stores values preinitialized for the program.

But in AVR, as I understand, data means SRAM. And to have it initialized you need to instruct your microcontroller to put some actual values there. Only having instructions under .dseg somewhere in your code does not magically put values into SRAM. The microcontroller has to reach the initializing instructions by its pace, following the program as usual: starts at 0x0 of the program segment, which is flash in AVR, and follows the instructions.

For instance, in this question the guy examines the code, produced by avr-gcc from C, and finds the routines initializing SRAM from flash, i.e. the program segment of the code.

If it is so, then what is the purpose of having .dseg?

Maybe it is for different assemlers or other tools (like avr-gcc in the referenced question, or avrstudio in the link 1), which would add necessary initialization instructions when processing the code with .dseg?

But if you work with "bare bones" the microcontroller starts from 0x0 (or somewhere else, according to the datasheet), this is where your program starts and there is nothing more to it, is there? (Of course, you put a vector table there for the interupts, if you want to handle them. And 0x0 is the address where RESET brings the microcontroller to. But it is not essential.)

PS

I thought .dseg/cseg could imply different address spaces: when you write an address in .dseg it is for SRAM, in .cseg -- for flash. But it seams addressing depends solely on instruction definition: ADD a1, a2 -- a# are registers, RJMP a -- a is flash (i.e. program), the SRAM is accessed indirectly via registers -- LDS R1, 0x0060.

Here again, can it be so, that the code under .dseg label like in 1:

 .DSEG                        ; Start data segment
 var1:  .BYTE 1               ; reserve 1 byte to var1

substitutes the indirect access to SRAM via registers? Probably this information should be searched within the documentation for the particular assembler at hand? Again, "bare bone" AVR instruction set doesn't provide any of such?

\$\endgroup\$
  • \$\begingroup\$ I looked at avr-asm-tutorial.net/avr_en/beginner/index.html. It seems badly written, and may be the cause of confusion. \$\endgroup\$ – gbulmer Sep 17 '14 at 1:32
  • \$\begingroup\$ nongnu.org/avr-libc/user-manual/mem_sections.html \$\endgroup\$ – Ignacio Vazquez-Abrams Sep 17 '14 at 2:37
  • \$\begingroup\$ @IgnacioVazquez-Abrams - AFAICT, the OP is asking about assembler, and not avr-gcc, that is quite different. Further gnu's assembler directives, and Atmel's AVR assembler directives are sufficiently different that it doesn't simply map. It maybe that some of the confusion is due to reading those two or three different 'world views'. I believe that the OP is asking about AVR assembler, and not avr-gcc or avr-as/gas. \$\endgroup\$ – gbulmer Sep 17 '14 at 2:57
  • \$\begingroup\$ sorry guys, probably I indeed confuse different "world views", i.e. how the program is regarded in: what I called "bare bones" (by which I mean bare electronics, how the chip/hardware works itself, but not in terms of binary machine code -- in terms of instruction set of AVR, "bare" assembly if one can call it so), also there are some assemblers (AVR Assembler, AVRA, some else; AVR Assembler is what AVR Studio uses, for instance) and, of course, C (and gnu stuff is main here). I will think how to correct the question with not much mess. Again, thanks a lot for answers! \$\endgroup\$ – xealits Sep 17 '14 at 13:30
2
\$\begingroup\$

These segments are merely metadata - they have no meaning to the AVR CPU or to your code. They are instructions for the linker.

When you compile a C program with avr-gcc, it adds what is called a 'stub'. This code contains the interrupt vectors and reset vector. When the CPU starts up, it does not immediately execute your main method, instead it starts executing in the reset vector. The reset vector points to the startup code in the assember stub. This code initializes the stack pointer and the data segments. After this is finished, it jumps into your main method. If you write in pure assembly on bare metal, none of this is done for you unless you do it yourself.

The different segments are used primarily by the compiler, assembler, and linker to put the pieces of your program together intelligently and make sure each static variable has its own piece of address space. When the pieces are put together, address pointers and offsets are substituted into the program code in appropriate locations. After the program is compiled and linked, the segments no longer really exist in the output bin/hex file. Actually, none of the data segments end up in the hex file at all, only a copy of the initialization data for the initialized segment in a location that has been substituted into the startup code. For example, there is no difference from the microcontroller's standpoint between initialized and uninitialized data. However, the startup code would like all of the initialized data to be in one contiguous block so it can loop over it in one loop. Also, the startup code needs to know where the segment is located and how big it is. The linker combines the segments from the individual object files into one big segment, then it substitutes the addresses into the startup code after it has figured out where to put everything.

Also, there is a rather important difference between an AVR microcontroller and a standard PC: in the AVR, instructions and data have completely separate address spaces. This means that you need to know which address space you're referring to when you perform a memory access. The assembler may be able to help you with this if it is intelligent enough.

\$\endgroup\$
  • \$\begingroup\$ I may have mislead you by including some C in my answer. The OP's question is all about AVR assembler. \$\endgroup\$ – gbulmer Sep 17 '14 at 3:03
  • 1
    \$\begingroup\$ It's really only useful to the linker. Which certainly doesn't mean it's useless if you're in writing directly in assembly. If you're mixing assembly and C, then I believe the C startup code will help you. If you're working in bare assembly, then I believe you can leverage the linker to organize the data and pass the addresses to your custom startup code. The point is that it is metadata that can be powerful if you know how to leverage it. However, the segments mean nothing to the CPU, only to the toolchain and possibly startup code. \$\endgroup\$ – alex.forencich Sep 17 '14 at 3:10
  • \$\begingroup\$ I'm not disagreeing that segments are useful. I am simply pointing out that the question is about the AVR assembler, and not avr-gcc. Also, what is the 'it' in the statement "It's really only useful to the linker."? \$\endgroup\$ – gbulmer Sep 17 '14 at 3:25
  • 1
    \$\begingroup\$ The 'it' was referring to segments. It seems like the OP is primarily asking about segments and what they do. My point is they're just metadata to instruct the linker, but this can be really useful if you know how the linker works. \$\endgroup\$ – alex.forencich Sep 17 '14 at 3:29
  • \$\begingroup\$ Segments can be used by a linker, but they are not only for the linker. The AVR assembler relies on segment directives to distinguish code and data values (in Flash) from variables (labeled space in SRAM). AFAIK, if it assembled one source file (all other parts being INCLUDE'd), then there would be a single code segment and single data segment. Atmels documentation says .dseg and .code segments are coalesced by the assembler into one .dseg and one .code segment. I believe Atmel's AVR assembler can generate 'PROMable' code, and does not need a linker. AFAIK, that is what the OP is asking about. \$\endgroup\$ – gbulmer Sep 17 '14 at 3:41
3
\$\begingroup\$

It doesn't matter what instructions are going to be used, the processor can get at all parts of SRAM and Flash memory. So ignore registers and instructions for now.

Initialised variables must be placed in SRAM, or they can't be used as variables, i.e. the program can't update their value.

Their initial values must be stored in flash, otherwise the initial values would be lost when power is removed.

The processor copies values from Flash to SRAM to initialise the values of all the initialised variables (they are normally stored next to each other in SRAM, and before the uninitialised variables in SRAM). The processor executes a loop which copies an area of Flash to SRAM. That loop is an early part of the program that initialises the program's state. In C, all that initialisation is run before main() is called.

The variables are stored in SRAM when the program is running, and the space is reserved by the assembler reading the program's .dseg directives containing .BYTE to reserve the space. Their initial values are stored in Flash, in a .CSEG, and the literal values are stored within the .CSEG using .DB or .DW.

If all of the initialised variables are next to each other, then the program needs to know the start address of the SRAM variables to initialise, and the start address of the Flash that contains their initial values. It also needs the length of the initialised variables area.

In almost-C it would be:

unsigned char* flash_values = flash_address_of_initial_values_of_initialised_variables;
unsigned char* SRAM_initialised_vars = SRAM_address_of_initialised_variables;
for (int i=0; i<length_of_initialised_variables; ++i) {
    SRAM_initialised_vars[i] = PROGRAM_MEMORY(flash_values[i]);
}

Edit: PROGRAM_MEMORY(...) retrieves the value from Flash memory.

\$\endgroup\$
  • \$\begingroup\$ very nice answer, useful stuff, but I accept the other one as a more to the subject. Thanks you both, you clarified the situation greatly. \$\endgroup\$ – xealits Sep 17 '14 at 13:21
  • 1
    \$\begingroup\$ @xealits - Okay. Just to be sure though, you do understand there is small problem with the other question? For the AVR assembler, which is what you asked about, the other one is misleading. There is no need for the linker. The AVR assembler uses the .dseg and .cseg directives. Without them it can't generate output. With them it can do the entire job. That is what I am discussing the answer with alex.forencich. \$\endgroup\$ – gbulmer Sep 17 '14 at 13:29
  • \$\begingroup\$ As I understand, I called "AVR assembler" 2 different things in my question: AVR instruction set and the program AVR Assembler. By "bare bones", "AVR assembly" and "AVR instruction set" I mean the code which is written into the chip and run by it, i.e. the machine code, but expressed in assembly instead of bytes (like in avr-asm.tripod.com they say "ASSEMBLY LANGUAGE: The Mnemonic Representation of Machine Code"). So, the main issue was how these things relate: the chip, instructions run by it, higher-level tools for programming (AVR Assembler, avr-gcc, etc.) And you cleared it. \$\endgroup\$ – xealits Sep 17 '14 at 13:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.