Assembler newbie: Can't understand IO address offset

Go To Last Post
48 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Hi all,

I'm trying to learn a bit of assembler out of curiosity. I just spent a fair amount of time wondering why I couldn't light some LEDs using this code:

	ldi mp,0xFF
	out DDRB,mp
	out PORTB,mp

Finally ran across some posts that talk about special function registers and point to this instructions page: http://www.nongnu.org/avr-libc/u...

So I fixed my code up and it now works:

	ldi mp,0xFF
	out _SFR_IO_ADDR(DDRB),mp
	out _SFR_IO_ADDR(PORTB),mp

But I'm having trouble understanding why the include file is offsetting those addresses by 0x20 in the first place. Can someone give me an example of where this offset is necessary and useful?

Thanks.

(if it matters, I'm using an ATtiny13 and obviously AVR-GCC as the compiler)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

But I'm having trouble understanding why the include file is offsetting those addresses by 0x20 in the first place.

Because they are really written for the C compiler and the C compiler initially generates LSD/STS for all addresses so they need to have the 0x20 offset in the IO 0x00..0x3F, RAM 0x20..0x5F region. If the optimiser is switched on it will then say "ah ha, that is between 0x20 and 0x5F so I'll switch to using IN/OUT and sutract 0x20".

For example see this:

int main(void) {
  DDRB = 0xFF;
  PORTB = 0xFF;
}

built without optimisation:

main:
.LFB2:
.LM1:
	push r29	 ; 
	push r28	 ; 
	in r28,__SP_L__	 ; 
	in r29,__SP_H__	 ; 
/* prologue: function */
/* frame size = 0 */
.LM2:
	ldi r30,lo8(36)	 ;  D.1216,
	ldi r31,hi8(36)	 ;  D.1216,
	ldi r24,lo8(-1)	 ;  tmp45,
	st Z,r24	 ; * D.1216, tmp45
.LM3:
	ldi r30,lo8(37)	 ;  D.1217,
	ldi r31,hi8(37)	 ;  D.1217,
	ldi r24,lo8(-1)	 ;  tmp46,
	st Z,r24	 ; * D.1217, tmp46

36/37 in that are the RAM addresses of DDRB and PORTB in decimal (for the mega168 I built for). Now with optimisation:

.LM2:
	ldi r24,lo8(-1)	 ;  tmp43,
	out 36-32,r24	 ; ,, tmp43
.LM3:
	out 37-32,r24	 ; ,, tmp43

Notice now the -32's in that (that is -0x20)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Barney download and learn to use AVR001: Conditional Assembly and portability macros. This will automatically use the correct instruction depending on where the I/O port is located.

Remember that the original AVRs were pretty small, as they started to grow in size they run out of port addresses so some of the newer peripherals end up in memory space, therefore the offset.

ldi mp,0xFF 
out DDRB,mp 
out PORTB,mp 
.
.
becomes
.
.
ldi mp,0xFF 
STORE DDRB,mp 
STORE PORTB,mp 

using the macro. These are the macros from AVR001 to use instead of in and out

;*********************************************************
;*	Byte access anywhere in IO or lower $FF of data space
;* 	STORE - Store register in IO or data space
;* 	LOAD  - Load register from IO or data space
;*********************************************************

.MACRO STORE 		;Arguments: Address, Register
	.if	@0>0x3F
		sts	@0, @1
	.else
		out	@0, @1
	.endif
.ENDMACRO

.MACRO LOAD 		;Arguments: Register, Address
	.if	@1>0x3F
		lds	@0, @1
	.else
		in	@0, @1
	.endif
.ENDMACRO

edit oops I see that you are not using the Atmel assembler, they may need to be modified to work with GAS.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Thu. Jul 7, 2011 - 07:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

John,

Those macros won't work in the avr-as he's using,

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah...just saw that he is using the evil GAS assembler... :wink:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

js wrote:
Remember that the original AVRs were pretty small, as they started to grow in size they run out of port addresses so some of the newer peripherals end up in memory space, therefore the offset.

I think I'm having trouble conceptualizing what these addresses actually are.

From what I've read it seems like there are two addresses for the ports depending on what you want to do with them. Specifially, this paragraph from the ATtiny13 confuses me:

ATtiny13 Datasheet Page 20 wrote:
All ATtiny13 I/Os and peripherals are placed in the I/O space. All I/O locations may be
accessed by the LD/LDS/LDD and ST/STS/STD instructions, transferring data between
the 32 general purpose working registers and the I/O space. I/O Registers within the
address range 0x00 - 0x1F are directly bit-accessible using the SBI and CBI instruc-
tions. In these registers, the value of single bits can be checked by using the SBIS and
SBIC instructions. Refer to the instruction set section for more details. When using the
I/O specific commands IN and OUT, the I/O addresses 0x00 - 0x3F must be used.
When addressing I/O Registers as data space using LD and ST instructions, 0x20 must
be added to these addresses.

Why is there a different address for the OUT command than there is for the ST command? Or is the ST command inappropriate when working with PORTx registers?

I get even more confused when looking that the data memory map on page 14. If the first 32 addresses are for general purpose registers, and the next 64 addresses are for I/O registers, why the heck is PORTB located at 0x18? Shouldn't it be addressed between 0x20 and 0x5F since it's an I/O port? Ack!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are two "parallel" address spaces in AVRs. Each special function register has two addresses, one in each address space, the memory address space and the I/O address space. So you can access an SFR either through memory operations or through IO operations. In the RAM address space the general purpose registers (R0..R31) come first so the SFRs starts at addres 0x20. The GPRs are not mapped into the IO address space at all so in that address space the first SFR is at address 0.

This is what Cliff is talking about above. The compiler starts with emitting accesses to all registers through memory address space. But LDS/STS are two-clock instructions, so when the optimizer kicks in it detects what register accesses are to SFRs and replace those accesses with IN/OUT which are one-clock instructions.

If you look at the end of your data sheet you will see a table summarizing all SFRs and there are two addresses to the left. The IO address and the memory address.

Did that make sense?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Thu. Jul 7, 2011 - 10:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aarrghhh the wolrd stopped for an hour.....

Quote:
why the heck is PORTB located at 0x18? Shouldn't it be addressed between 0x20 and 0x5F since it's an I/O port?
The tiny13 is NOT a good chip to understand what's going on. And by the way your "issue" would have not arisen if you were to use the Atmel assembler. :) what you see it's a quirk of GAS.

If you get the data sheet for say the tiny2313 and look at page 197 Register Summary you see that the ports have 2 sets of adresses, one starts at 0x00 and finishes at 0x3f (standard I/O addressing where IN/OUT works) the other (in brackets) starts at 0x20 and finishes at 0x5f. Can you see the 0x20 offset? In other words each of those registers have 2 ways of being addressed.

Now if you look at say the Mega48 datasheet under Register Summary you will see the same set up for registers 0x00-0x3f HOWEVER everything above that can only be addressed with LDS/STS as the maximum allowed register addresses provided with the older chips was 0x3f.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OT:

Quote:
aarrghhh the wolrd stopped for an hour.....

Yes, we've had the local midnight stop (the server is at GMT+1) for a long time, and for the latest half year or so a shorter stop at 23:00 local. This time the stop lasted for that full hour span. It broke down exactly when I posted my latest rant above, and I was glad I have the habit of ALWAYS copying longish posts before I hit submit. Had to be careful with my paste buffer for the hour, though... :D

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks to both Johan and js for your responses. This certainly does help to clear things up. It becomes much more clear when looking at the datasheet for a more full-featured chip like the tiny2313 as suggested.

As for the Atmel compiler: I'd love to give it a try. But when I go to look at Atmel's tools for Linux, it appears their toolchain is GCC and binutils. Isn't that what I'm already using with AVR-GCC?

Is the compiler that js is talking about available for Linux?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maybe another attack might help.

There is a group of registers that can be addressed two ways. One is as part of "ordinary memory" and one is as part of "IO register" memory. But, why?

On MOST AVRs (excepting some tiny chips, but not the tiny13, getting an arbitrary memory location takes two address bytes. The logic unit required two memory fetches to get the two address bytes, so it takes THREE cpu cycles (for a "direct" access: one to get the low address byte out of the instruction, one to get the high address byte out of the instruction, and one to actually read the data at that address).

But, I/O addresses are single byte addresses So, those operations can be done in TWO cycles. That lets the program run faster. And it takes fewer code bytes (2 instead of 4)

Now, you ask - why do such a silly thing in a Tiny13? Well, first thing is that it has 64K of SRAM, and that takes two address bytes to access. And, even more important, with such small flash (code) memory, you need to do everything possible to minimize use of code space. This is one way.

Which brings us to a short explanation of "op codes" For the instructions that read or write to any memory location, the instructions take up 4 bytes of memory (2 words). Two hold the address and two describe the operation. For IO instructions, only one word (2 bytes) is needed. One byte is the IO address and one is the instruction. Thus, the flash space used is HALF as much, and the time taken for the instruction is 2/3 as long.

OK, with the IO instructions being so much "better", why not use them for all the memory? Simple fact is that one byte can only address 256 address locations. That is all you get from 8 bits.

Hope this helps
Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are two "distributions" for Windows of the avr-gcc tool chain: The Atmel AVR Toolchain and the WinAVR package. And one could build the toolchain oneself from the sources, but few if any do this with those two distributions available. It is a major operation to say the least (ask the builder of the WinAVR package, fellow AVRFreak 'EW' (Eric Weddington) about the ins and outs, or read in a description of the complete process in the avrlibc documentation. Part of the problem is keeping track of all patches that has to be applied to get something that really works.

Partly because of the patches, the process is similarly "heavy" for GNU/Linux also. But there are true heroes in this world..

For GNU/Linux, our fellow AVRfreaks member 'bingo600' has posted scripts to build the avr-gcc tool chain. Locate the sticky post in this forum to read all about it. If you can handle Debian packages (e.g. you are running *buntu) then there are such ready-built from Bingo's scripts. They are hosted by fellow 'freak 'clawson' - link can be found in same thread.

What you definitively should do is to stay away from avr-gcc packages supplied in the repositories for GNU/Linux distributions. They don't get all that patch work right (it seems they think it's just OK to pull some sources that 'looks right', build and release. The AVR community is small in the GCC world, and we are "far out from the trunk on a thin branch", and patches and knowledge don't trickle very well towards "the trunk". Stay away from GNU/Linux distros repositories. Use Bingos script or the ready-built stuff from those hosted by 'clawson'.

Re asemblers, there is of-course one coming with the avr-gcc tool chain (avr-gas). But Atmel also has it's own AVR Assembler 2 (and there has been an AVR Assembler (with implied '1')), which has it's own syntax re directives etc. Atmels assembler comes with both AVR Studio 4 and AVR Studio 5.

HTH and good night!

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

barney_1 wrote:
Is the compiler that js is talking about available for Linux?

Well the assembler ( not compiler ) comes with AVR Studio so... no. You can run Studio under Wine ( AS4 not AS5 ) or under Windows in a virtual machine.

For just learning a bit of asm out of curiosity, I don't think I would download and install the assembler. Also, if you are planning on continuing most of your work in C with avr-gcc, I would stick with GAS, as then you will be able to combine asm with your C code - the Atmel assembler will not combine easily. As js noted, the LOAD/STORE macros could likely be modified for GAS syntax and used should you wish it ( not a bad idea if you are planning to do a lot of asm ); but what you found is not a quirk of GAS - it is a result of how the header files are written ( with C language programming - where you have an optimizer - in mind ).

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
why do such a silly thing in a Tiny13? Well, first thing is that it has 64K of SRAM, and that takes two address bytes to access.

Holy cow! I never noticed all that SRAM before.

Wait... Looks like you and I both are looking at old datasheets with mistakes in them. I had been using 2535H–AVR–10/07, after seeing that it says 64K bytes Internal SRAM in the features I figured that had to be an error. I just downloaded the lastest (2535J–AVR–08/10) and it lists Internal SRAM as 64 bytes (which is more sensible).

I'm still digesting what you said in your reply. I have questions but I want some more time to understand before I ask them. Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Amazingly enough, I think I now understand. But that understanding raises another question of why (it makes me feel like a child saying: why? why? why?).

If I have this correct, there are different ways of accessing I/O registers

    1. Using IN and OUT which is accessing the I/O space so the first 32 registers ARE NOT mapped, resulting in I/O registers that start at 0x00. 2. Using LDS and STS which load and store in SRAM, where the first 32 registers are used for general purpose, pushing the I/O register addresses back to start with 0x20
Does that sound right?

Now the question that this raises for me is when do you use the commands that access the SRAM instead of directly accessing the I/O space?

Is the answer because you can store or write from SRAM registers to I/O registers without using a general purpose register as an intermediary? That is to say, I could set output levels directly like this:

LDS PORTB,0x04FF

In this case I'd be reading the last byte of SRAM on an ATmega168 (a chip large enough to have more than 256 bytes of memory space). And PORTB would need to be defined as 0x25 because that's the m168's address for it in memory space (it is 0x05 in I/O space).

Whew... did I get it right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
There are two "parallel" address spaces in AVRs. Each special function register has two addresses, one in each address space, the memory address space and the I/O address space.

This information seems to apply to the ATmega328 and similar chips but the term "Special Function Register" does not show up anywhere in their data sheet.

It seems that the corresponding term for these processors would be 'Standard I/O Registers' (0x0020 - 0x005F). It doesn't look like the 'Extended I/O Registers' (0x0060 - 0x00FF) qualify since they cannot be accessed via the I/O Address Space. Am I correct in this analysis?

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I could set output levels directly like this:
No you can't only a register can put stuff on the I/O (or ram) locations.

I believe AVRA (very close to Atmel assembler) can be compiled for platforms other than windows, maybe there is some for linux already compiled.

"Special Function Register" maybe just a way for GCC to handle the Harvard architecture but don't really know. Unless you are using GAS one does not have to worry about _SFR_IO_ADDR.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Now the question that this raises for me is when do you use the commands that access the SRAM instead of directly accessing the I/O space?

Wind back and think how Atmel designed the original AVRs (that had just 64 SFRs). So they laid out the memory and said. We'll make the 32 machine registers visible at 0x0000 to 0x001F. We'll then place the 64 SFRs next from 0x0020 to 0x005F and then the SRAM can start from 0x0060 onwards. All of this can be accessed by LDS and STS.

But wait a minute, the program is going to spend a lot of it's life accessing those SFRs so let's also provide some optimised access instructions that can hold their address within the single 16 bit opcode (using 6 bits to address 64 bytes). As there'll only be one 16bit fetch for these instructions they can execute in one cycle less. Only thing is that we've got the machine registers at 0x0000 so we need to make the base address for this I/O addressing offset by 0x20 so that the 6 bits in the opcode can reach all the 64 SFRs.

That's why there's a 32 byte offset, because there are 32 machine registers. It might have worked better if they'd put the machine registers AFTER the 64 SFRs in which case the addressing for IN/OUT and LDS/STS would use exactly the same addresses. But I guess Atmel was already looking to the future when there'd be AVRs with more SFRs. If the machine registers were at 0x0040 future SFRs would be split beyond this (maybe that doesn't really matter). In modern AVRs there are 224 SFRs and they end at 0x00FF with RAM starting at 0x0100. The Xmega have even more SFRs and RAM starts at 0x0200.

In a typical modern AVR you probably realise that the first 64 SFRs can be accessed using the fast IN/OUT opcodes but the remaining 220 have to be accessed with the slower LDS/STS opcodes.

Oh and just wait until you stumble upon SBI/CBI - your head will explode. (the limit there is because there was only room for 5 bits of addressing in the opcode, not 6)

Quote:
Unless you are using GAS one does not have to worry about _SFR_IO_ADDR.

You don't have to use it in avr-as either. I do:

#define __SFR_OFFSET 0
#include 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Cliff, that was the missing piece of the puzzle. I had too small of scope since the diminutive chips I'm looking at (even the mega168) don't have enough SFRs to make sense of multiple addresses.

I also appreciate the tip about redefining the offset to zero. I'd seen that on the libc reference pages, but didn't know if it was appropriate. As long as I know I'll need to add 0x20 if accessing I/O in the memory space I should be fine. That makes more sense to me anyway.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

(even the mega168) don't have enough SFRs to make sense of multiple addresses.

Err wrongo pongo I'm afraid. Look at the table in chapter 30 of the datasheet ("Register summary"). It has SFRs all the way up to 0xFF with the last used one being UDR0 at 0x00C6

Now go a few pages forward until you get to the page with ADMUX on the second line of the table. Note that it is at 0x007C. That, too, is a RAM address and can only be accessed with LDS and STS. IN/OUT cannot be used. Now look half way down the page and a thick black line across the middle. Above it is WDTCSR at 0x0060 but look at SREG immediately below the line. Rather than just showing this as 0x005F it has two addresses shown "0x3F (0x5f)". That's telling you that it's in range on IN/OUT (it is the 64th SFR) and when accessed with IN/OUT you use 0x3F but it's still also accessible using RAM addresing at location 0x005F. So:

LDS R16, 0x005F
ORI R16, 0x80
STS 0x005F, R16

and

IN R16, 0x3F
ORI R16, 0x80
OUT 0x3F, R16

achieve the same thing (set the I bit in SREG) but the former takes longer to do it than the latter (of course the AVR has the SEI opcode that does this particular thing quicker and atomically compared to either of these two).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Part of the why answer is simple economics.

Atmel has an architecture that includes dual access to a limited part of the memory. Now, along comes really small devices. They cannot justify ripping up the guts of a standard layout for a device that provides a small dollar/unit return. So, they keep it, even though the justification for the two-byte SRAM addresses may have totally disappeared. Atmel's "advantage" (for that matter, any micro-maker's advatage) is having a core architecture that a wide variety of peripherals can be hung on, without significant core changes.

That is the why. They cannot afford to redesign the core for every chip!

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I am sorry for waking up this 5 years sleeping monster, but today I touch a putrid scar in AVR addressing that needs to be addressed, literally, and no pun intended.... ;)

 

Okay, to be easy to explain, the following is a part of the DEF of Mega168:

 

.equ	PCMSK0	= 0x6b	; MEMORY MAPPED
.equ	EICRA	= 0x69	; MEMORY MAPPED
.equ	PCICR	= 0x68	; MEMORY MAPPED
.equ	OSCCAL	= 0x66	; MEMORY MAPPED
.equ	PRR	= 0x64	; MEMORY MAPPED
.equ	CLKPR	= 0x61	; MEMORY MAPPED
.equ	WDTCSR	= 0x60	; MEMORY MAPPED
.equ	SREG	= 0x3f
.equ	SPL	= 0x3d
.equ	SPH	= 0x3e
.equ	SPMCSR	= 0x37
.equ	MCUCR	= 0x35
.equ	MCUSR	= 0x34
.equ	SMCR	= 0x33
.equ	ACSR	= 0x30
.equ	SPDR	= 0x2e
.equ	SPSR	= 0x2d
.equ	SPCR	= 0x2c
.equ	GPIOR2	= 0x2b
.equ	GPIOR1	= 0x2a
.equ	OCR0B	= 0x28

Pay attention to WDTCSR (0x60), memory mapped.

Its physical MEMORY address is really 0x60.  If the IN/OUT instruction could touch it (can't), its I/O address would be 0x40.

Up to now, everything is fine.

 

So, when you include the m168pdef.inc file, the "WDTCSR" name is  equated to be a number 0x60.

 

If you try to use the instruction   IN R16,WDTCSR, the AVRASM2 will read "WDTCSR" as 0x60, and will flick the IN/OUT ADDRESS EXCEPTION ALERT light, since 0x60 is larger than 0x3F, the maximum address that can be dealt by IN or OUT instruction.   The EXCEPTION ALERT will end the AVRASM2 failing, reporting "error: Operand 1 out of range".

 

Then, as a good programmer you remember the issue of I/O addresses above the range of IN/OUT instructions, blah, blah, and replace the IN or OUT with a fantastic LDS/STS instruction.   LDS R16,WDTCSR  will work perfectly, since its Equate value of 0x60 it is the real MEMORY MAPPED address for such I/O, then it will work fine.

 

Again, everything is nice and everyone understand that.

 

Now, here comes the nasty flaming dragon.

 

Working with SPI, you have few registers that you access:

 

I/O ADDRESS

SPDR = 0x2E   (Data In/Out)

SPSR = 0x2D   (Flags)

SPCR = 0x2C

 

Special attention for bit 7 of SPSR.  This is the flag (SPIF - SPIFlag) that becomes "1" whenever the SPI circuit finished transmitting.

So, whenever you want to write another byte at SPDR to be transmitted, it is VERY wise to check bit 7 of SPSR, if it is zero, wait until it becomes 1, then you can write the byte.

 

Okay, the dragon smokes now.

What you do in Atmega8 if you want to send two bytes, 0x55 and 0x56?

SBIS  SPSR,SPIF
RJMP  PC-1
LDI   R16,0x55
OUT   SPDR,R16
SBIS  SPSR,SPIF
RJMP  PC-1
LDI   R16,0X56
OUT   SPDR,R16

SBIS SPSR,SPIF = Skip Next Instruction, IF bit SPIF (7) of SPSR is SET

 

Nice, works, since on AtMEGA8, SPSR is at I/O address 0x0D, MEMORY MAPPED 0x2D

SBIS will work specifically in the I/O ADDRESS, not MEMORY MAPPED.

SBIS has a range of I/O address from 0x00 to 0x1F, that looking through the MEMORY MAPPED hole, it is from 0x20 to 0x3F.

That's okay, it means SPSR as I/O=0x0D is inside the range of the SBIS, and it works.

 

Here comes the smelling dragon.

 

On AtMega168, remember:

I/O ADDRESS

SPDR = 0x2E   (Data In/Out)

SPSR = 0x2D   (Flags)

SPCR = 0x2C

 

If using AtMega168, the  SBIS SPDR,SPIF will NOT WORK, since SBIS use I/O address, and SPSR is 0x2D, higher than the range (0x00-0x1F) used by SBIS.

So, the assembler yell "Failure!!! your stupid moron!!!"

So, as a fast and jumping programmer, you go there and fix it in a blink of the eye;

 

From this

Wait_SPI_Ready:

      SBIS  SPSR,SPIF

      RJMP Wait_SPI_Ready

 

To this:

Wait_SPI_Ready:

      LDS   R16,SPSR

      SBRS  R16,7

      RJMP  Wait_SPI_Ready

 

And the dragon BURN you alive. Only ashes.

Did you see the dragon?

SBRS use the same address range from 0x00-0x1F as SBIS

 

See, on the m168def.inc SPSR is equated as 0x2D, since it is an I/O Address equate, to be used by I/O instructions, such as IN / OUT / SBIC / SBIS.

The LDS instruction will deal with MEMORY MAPPED addresses.

So,  LDS  R16,SPSR  will execute  LDS R16,0x2D,  and 0x2D in MEMORY MAPPED address on Mega168 is not SPSR, in fact it is I/O 0x0D, that doesn't exist on Mega168 !!!

MEMORY MAPPED address 0x2C-0x34 is "reserved" for Mega168... and the AVRASM2 can't realize the huge mistake it is doing. Well, it is my mistake, but burns.

Ahhh, fire, smoke, ashes.

 

So, Atmel thought nicely to use MEMORY MAPPED address for I/Os above 0x3F, becoming 0x60 and up, because YOU will use MEMORY MAPPED instructions to access them, like LDS/STS.

But, the dragon fire still for instructions that can not reach I/Os to 0x3F, such SBIS/SBIC, those can access only up to 0x1F I/Os, and if you use LDS to read the I/O from 0x20-03F to check for a bit, you are doing a HUGE mistake.  From I/Os in this range, 0x20-0x3F, using LDS, you MUST add 0x20 to the I/O address, since that I/O address in the MEMORY MAPPED address is 0x20 higher.

 

Of course, being smart and intelligent, you will use  IN instead of LDS for that range 0x20-0x3F, since IN will use the I/O address, and 0x2D is correct.

 

But the dragon still burning.

DO YOU HAVE ALL THE I/O ADDRESSES READILY AVAILABLE IN YOUR MIND to know what I/Os are in this range of 0x20-0x3F for all the different AVRs ??? and not use LDS and use IN to replace SBIC/SBIS?

The answer is simple, NO, YOU DO NOT!!!

 

Well, the trick is, if the assembler complains the SBIS or SBIC is out of range, do not replace directly for a LDS, first replace by an IN instruction, if the assembler still complaining, than yes, replace with a LDS, because in this case, the I/O address is correctly MEMORY MAPPED in the AVR def.inc file.    There is no easy solution, this is a pain in the neck we need to live with.

 

Wait_SPI_Ready:

      SBIS  SPSR,7    ;<<< doesn't work

      LDS   R16,SPSR  ;<<<< fails drastically

      IN    R16,SPSR  ;<<< works

      SBRS  R16,7

      RJMP  Wait_SPI_Ready

      SBIC  ADCL,1    ; <<< doesn't work

      IN    R16,ADCL  ; <<< doesn't work

      LDS   R16,ADCL  ; <<< works 

 

Remember, if "SBIC/SBIS" fails, use "IN" to read the I/O,  then If "IN" fails, then, and only then use "LDS"        

 

And using John's idea for the macro:

 

; Wait for Bit SET in I/O in SRAM MEMORY
;
;	wbsram	address, bit

.macro	wbsram

Wait_For_Bit:
  .if @0 < 0x40
     in   r16, @0
  .else
     lds  r16, @0
  .endif
	
     sbrs    r16,@1
     rjmp    Wait_For_Bit
.endm

 

Cancel the fire fighters, please. 

 

Wagner Lipnharski

Orlando Florida USA

 

 

 

 

 

Wagner Lipnharski
Orlando Florida USA

Last Edited: Thu. Aug 11, 2016 - 05:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I/O ADDRESS

SPDR = 0x2E   (Data In/Out)

SPSR = 0x2D   (Flags)

SPCR = 0x2C

What you do in Atmega8 if you want to send two bytes, 0x55 and 0x56?

Those are the MEMORY MAPPED addresses for SP*R.  The I/O MAPPED addresses are:

I/O ADDRESS

SPDR = 0x0E   (Data In/Out)

SPSR = 0x0D   (Flags)

SPCR = 0x0C

That's why you can use the SBIS/C and C/SBI instructions for those register on the m8.

 

You cannot do this on the m168, because the addresses are above 0x1F (I/O space).

 

This is why g-d invented AVR001.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The main problem as I see it is the lack of information in the new layout of the datasheets.

 

Look nice but very very bad and make you run into this kind of problems.

 

Try to get hold of an old version of the datasheet, it has both addr. in the IO table. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wagnerlip wrote:
There is no easy solution, this is a pain in the neck we need to live with.
joeymorin wrote:
This is why g-d invented AVR001.

joey beat me to it.

 

I don't know if it covers all cases, e.g. to make a "portable" ASM SPI driver. 

wagnerlip wrote:
LDS R16,SPSR ;<<<< fails drastically

And whether it covers this case.  But IME LOAD/STORE works well in vanilla programs that aren't cycle or word counting.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try to get hold of an old version of the datasheet, it has both addr. in the IO table. 

I've noticed that, as well.  Very annoying that the new sheets don't put the addresses next to the register descriptions in each chapter.  Although they are still visible in the register summary towards the end of the datasheet.  Nevermind :)

 

Also lacking are fine-grained chapter titles.  In the older datasheets I can use the chapter search facility of my PDF viewer to rapidly (<1s) find the page for, say, SPCR.  With the newer datasheets, you cannot.  For example, in the datasheet for the 48/88/168PB, although there is a named sub-chapter for SPCR, it does not contain the actual name of that register.  It is 24.5.1 "SPI Control Register 0".  On the older datasheets, the name of the sub-chapter is "SPCR - SPI Control Register".

 

With the new datasheets, I must use the slower full-document text search, which of course hits every mention of the register.

 

I would like to have a conversation with Atmel's head of technical writing.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Thu. Aug 11, 2016 - 02:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

is the lack of information in the new layout of the datasheets.

I just picked a new style (ie blue) datasheet at random...

 

 

Surely "0x0C (0x2C)" etc is what one would be looking for ?

 

(actually just looked at an even newer one - tiny1634 - and that lists both addresses for the bottom 64 registers too)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's not a recent datasheet.  It's from 2013.

 

Here's one from 2016 (Atmel-42176G-ATmega48PB/88PB/168PB_Datasheet_Complete-03/2016):

 

Note the order is reversed w.r.t. previous datasheets.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Thu. Aug 11, 2016 - 03:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
Note the order is reversed w.r.t. previous datasheets.

IMO that part isn't all bad.

 

But indeed -- both addresses not shown.  And no footnote or lead-in note on the double-addresses.  E.g.:

 

To be fair, I guess:

 

11.5. I/O Memory
The I/O space definition of the device is shown in the Register Summary.
All device I/Os and peripherals are placed in the I/O space. All I/O locations may be accessed by the
LD/LDS/LDD and ST/STS/STD instructions, transferring data between the 32 general purpose working
registers and the I/O space. I/O Registers within the address range 0x00-0x1F are directly bit-accessible
using the SBI and CBI instructions. In these registers, the value of single bits can be checked by using
the SBIS and SBIC instructions.
When using the I/O specific commands IN and OUT, the I/O addresses 0x00-0x3F must be used. When
addressing I/O Registers as data space using LD and ST instructions, 0x20 must be added to these
addresses. The device is a complex microcontroller with more ...
 

...which is similar to the text in the older datasheets.  Editor decided twice is too much?

 

And no "units" on the Offset column -- offset from what?  And apparently PORTA is empty -- would it be that hard to put the block of three gray lines for 0x20/21/22?

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Thu. Aug 11, 2016 - 03:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello guys, I have the ATmega 328p and, studying the datasheet, I had a lot of doubts about those addressing modes (direct and indirect).

I would like to say that this topic helped me a lot!! Thank you very much!

 

My post here is about a figure shown in ATmega328p datasheet that I can't understand.. Actually, I think it is wrong and I would like your opinion! The figure is in page 35 of the datasheet (http://www.atmel.com/pt/br/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf).

 

 

It says that, in RAM space, the addresses of the 64 I/O registers go from 0x0020 to 0x005F. To go to the I/O space, we need to subtract 0x0020, hence going from 0x0000 to 0x003F. In the figure, it says that, in I/O space, the addresses go from 0x0000 to 0x001F, but to me they should go up to 0x003F.

What am I missing?

 

Thank you very much!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JABBA_JOE wrote:
0x001F, but to me they should go up to 0x003F. What am I missing?
You are quite right, that is an error. Or rather it is "half an error".

 

The I/O space (64 locations) is, itself, split in two. All 64 locations (0x00..0x3F) are accessible to IN/OUT but only the first 32 locations (0x00..0x1F) are available to CBI/SBI (and similar). That's because one set of opcodes (IN/OUT) has 6 bits in the opcode available for addressing and the other opcodes (CBI/SBI/etc) only have 5 bits available for the address.

 

So I bet the author who typed 0x0000..0x0001F on that was thinking about CBI/SBI/etc at the time ;-)

 

For completeness they might even have included:

 

IN/OUT (0x00..0x3F)

CBI/SBI/SBIC/SBIS (0x00..0x1F)

 

(not the first error in an Atmel datasheet, though on the whole they aren't too bad!)

Last Edited: Wed. Nov 16, 2016 - 04:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for confirming!! I was getting mad trying to figure out what I was missing!! 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Get hold on an old datasheet.

 

There are way to many errors in the new datasheet setup.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

So I'll try to summarize what I've learned.

 

In AVR architecture, the first 64 I/O registers are mapped in two "different" spaces: the RAM space and the I/O space.

 

In I/O space, all 64 registers are mapped from address 0x0000 to 0x003F and the instructions IN/OUT are available. The first 32 registers within those 64 (from address 0x0000 to 0x001F) are directly bit-accessible, therefore the instructions CBI/SBI/SBIC/SBIS are available

We can address only 64 registers in I/O space due to the instructions limitation. IN/OUT instructions are limited by 6 bits (hence 64 positions) and CBI/SBI/SBIC/SBIS instructions are limited by 5 bits (32 positions).

 

In RAM space, the 64 registers have an offset of 0x0020, going from address 0x0020 to 0x005F. The instructions available are ST/LD (IN/OUT/CBI/SBI/SBIC/SBIS are not available in this case).

When addressing I/O registers within RAM space, 160 extra I/O registers are available (address from 0x0060 to 0x00FF).

 

Addressing an I/O register in I/O space is called Direct Addressing. In direct addressing mode, the binary value of the operand is an address and the content of that address is a data.

 

Addressing an I/O register in RAM space is called Indirect Addressing. In indirect addressing mode, the binary value of the operand is an address and the content of that address is another address in memory, in which the data resides.

 

Compiling C code with avr-gcc will always generate an indirect address in first place. When the optimization runs, it will, when possible, shift to direct address.

 

The direct address has the advantage of being fast and generate reduced code, and have the disadvantage of being limited to 6 bits (64 peripherals)

 

The indirect address has the advantage of having more capacity (ATmega 328p has 356 registers), hence, more peripherals. The disadvantage of this mode is that it consumes more cicles and the resulting code is bigger.

 

 

Is this right? Any comments or corrections?

 

Thanks!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I see a problem with this wording :

Addressing an I/O register in RAM space is called Indirect Addressing. In indirect addressing mode, the binary value of the operand is an address and the content of that address is another address in memory, in which the data resides.

If you use X Y or Z as a pointer (LD, ST LDD, STD) it's Indirect Addressing, but if you use STS or LDS that would be a direct addressing. 

 

 

And I have to add that this is not for all AVR's where this is correct correct :(

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
And I have to add that this is not for all AVR's where this is correct correct :(
JABBA_JOE wrote:
When addressing I/O registers within RAM space, 160 extra I/O registers are available (address from 0x0060 to 0x00FF).

That is model-dependent.  IIRC, so is the size of the "base" I/O space.  But no matter in practice...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I found an old datasheet and it had this info :

Note: 1. For compatibility with future devices, reserved bits should be written to zero if accessed. Reserved I/O memory addresses should never be written.

2. I/O Registers within the address range 0x00 - 0x1F are directly bit-accessible using the SBI and CBI instructions. In these registers, the value of single bits can be checked by using the SBIS and SBIC instructions.

3. Some of the Status Flags are cleared by writing a logical one to them. Note that, unlike most other AVRs, the CBI and SBI instructions will only operate on the specified bit, and can therefore be used on registers containing such Status Flags. The CBI and SBI instructions work with registers 0x00 to 0x1F only.

4. When using the I/O specific commands IN and OUT, the I/O addresses 0x00 - 0x3F must be used. When addressing I/O Registers as data space using LD and ST instructions, 0x20 must be added to these addresses. The ATmega48A/PA/88A/PA/168A/PA/328/P is a complex microcontroller with more peripheral units than can be supported within the 64 location reserved in Opcode for the IN and OUT instructions. For the Extended I/O space from 0x60 - 0xFF in SRAM, only the ST/STS/STD and LD/LDS/LDD instructions can be used.

5. Only valid for ATmega88A/88PA/168A/168PA/328/328P. 6. BODS and BODSE only available for picoPower devices ATmega48PA/88PA/168PA/328P

Add

I better correct it before somebody tell me:

The info is still in the new datasheet sorry  

But the table don't show it.

Last Edited: Thu. Nov 17, 2016 - 12:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A nice complete footnote.  hard to show that in a block diagram.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes and no.

The old IO table have fat lines showing where the limit are.

And because the addresses of interest are showed just on top of the footnote it make sense.

 

And for people that want to ASM the instructions manual is so bad about what which instructions the different AVR's have and how many clk cycles they take. 

 

 

Again I stay corrected they have made a new version this month that actually try to get the different cores sorted out.

Much better than before, but I see some errors ;)

 

A bit sad they don't reference the different cores to the same version name as the GCC.

 

 

 

Last Edited: Thu. Nov 17, 2016 - 05:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One thing I miss deeply is the easily searchable index.  Notably, register names are missing from section names.  In the old datasheets I could get directly to, say, section 9.12.2 CLKPR - Clock Prescale Register simply by typing CLKPR into the page number field (evince does on-the-fly index searches that way).  This doesn't work in the new datasheets because the register names aren't a part of the section names.  I can still type "clock prescale register", which works because that >>is<< in the section name, but that's far less convenient and intuitive.  It would be great if we could get back to including the register names in the section headings which describe them.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Thu. Nov 17, 2016 - 07:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
One thing I miss deeply is the easily searchable index. Notably, register names are missing from section names. In the old datasheets ...

 

I guess "old" and "new" are in the eye of the beholder.  I pulled up a couple AVR8 datasheets dated late last year, in the "newer" formate, and the register names are in the section headers in the table of contents pane of my PDF viewer.

 

Now, are we talking of the new beasties e.g. Tiny817?  I guess with that Xmega-type of structure it might have a different master layout.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess "old" and "new" are in the eye of the beholder.  I pulled up a couple AVR8 datasheets dated late last year, in the "newer" formate, and the register names are in the section headers in the table of contents pane of my PDF viewer.

I was referring to the datasheet for the 328P, linked to in #30, compared to the one from the most recent Techlib DVD (admittedly quite far back now, from 08/2012).

 

The older one:

 

Note how CLKPR is right in the section name, both in the body of the document and when searching.

 

Now the new one:

 

CLKPR is in the text appearing >>below<< the section name, but not in the section name itself, so searching for clkpr by section name yields nothing.

 

The same is revealed via the Index view:

 

Old: