Skip to Content

Addressing Modes

Addressing Modes

We have load and store instructions to get data to and from memory. To make certain types of programming easier, such as accessing an array of data, the AVR supports several different addressing modes, or ways of accessing memory locations.

  1. Immediate: The actual data is located in memory immediately following the instruction's opcode: only useful for a constant
  2. Direct: The 16-bit address of the data is located immediately following the opcode: used for accessing single global variables
  3. Indirect: The value in an index register (X, Y, or Z) is as the address of the data, potentially in addition to a constant offset.
  4. Relative (or PC-Relative): This is used for branches and relative jumps/calls, not for accessing data. The 16-bit address of the location in the program to branch or jump to is the address of the current instruction (contained in the PC), plus 1, plus a 2's complement offset store in the machine code. This offset is only 7 bits for branch instructions (+64 / -65 locations), but is 12 bits for the relative jump/call instructions (+2047 / -2048 locations)

Time

We need to understand how long our program, or a piece of our program, takes to execute. This is important for such things as, say, turning the robot for 1/2 of a circle. We would take the speed of the motor and figure out how long to keep the motor on, then write a program to handle that.

In a digital synchronous system, which a CPU is, time is strictly controlled by a clock signal. During each clock cycle, something happens.

On the AVR, most instructions take 1 or 2 clock cycles, with a few taking 3 or 4 cycles. If you recall the fetch/decode/execute explanation of program execution, it is hard to imagine that those three steps can all happen in one cycle!

Well, they don't!

The AVR uses the idea of pipelining, which is overlapping the execution of sequential instructions. Modern complex processors like the ones in your PC use very complex pipelining schemes, but the AVR uses a simple one: the fetch of the next instruction is overlapped with the execution of the current instruction. This allows most of the simple instructions to take only one cycle of execution time, even though their total fetch+execute time is two cycles.

Branches cause problems with pipelining because the processor doesn't know what instruction to fetch next until the branch decides which way to go! The AVR does a small bit of speculation: it goes ahead and fetches the next instruction in sequence while the branch instruction is executing; if the branch falls through then the instruction fetched is ready to execute. If the branch needs to be taken, then the instruction that was fetched is thrown away and the instruction at the branch target is fetched. This is why in the instruction table branches are shown as taking either 1 or 2 cycles: they take 1 if they fall through, but take 2 if they actually branch.

Endianess

Although the AVR uses mostly 1-byte data values, it does sometimes operate on multi-byte values, and in the program memory all machine code is viewed as 2-byte values. All CPUs must choose a byte ordering when dealing with multi-byte values. The AVR chooses to store the lowermost byte first, and we call this choice little endian.

Other CPUs choose to store the highest byte first, and we call this big endian. Highest byte first seems natural in one way, because if we look at memory as increasing in address from left to right, the number reads in the same direction we would write it down. But what if you had a 16-bit result where you knew the upper byte was zero, and you just wanted to access the lower byte. This actually happens alot in C programming, because many of the C library functions return an integer, but actually the result is a single-byte character. In this case, then, you need to access the memory location at 1 plus the base address of the value on a big endian CPU, but on a little endian CPU the value is directly at the address.

In computer science, we call this choice "endianess". If the most significant byte is first, then that CPU is "big-endian", and if the least significant byte is first, then the CPU is "little-endian". Our AVR processor is little endian.

Intel CPU's (and thus the resulting PCs) are little endian, while most Motorola CPU's are big endian. If you sent a 16 or 32 bit number from a PC to a Motorola CPU without modification, they would not agree on what that number is!!!