Closed stefanrueger closed 1 year ago
I think the datasheet should be able to tell the info.
1) Section 8.1 of the following datasheet says The ATmega640/1280/1281/2560/2561 Program Counter (PC) is 15/16/17 bits wide, thus addressing the 32K/64K/128K program memory locations
. And as per the datasheet Section 7.6.2, they have EIND
register.
I think that means ATmega640/1280/1281 have 16bit PC and have EIND.
2) ATmega16U4/32U4 have 16bit PC and no EIND. https://ww1.microchip.com/downloads/en/devicedoc/atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf
3) ATmega8U2/16U2/32U2 have 16bit PC and no EIND. https://ww1.microchip.com/downloads/en/DeviceDoc/doc7799.pdf
4) AT90USB82/162 have 16bit PC and no EIND. http://ww1.microchip.com/downloads/en/devicedoc/doc7707.pdf
5) AT90USB646/647/1286/1287 -- 16bit PC but ?? on EIND I can not find official datasheet from Microchip website. The following datasheet is not clear whether they have EIND register or not. Section 33 (register summary) does not metion EIND but Section 34 mentions EIND. https://www.mouser.sg/datasheet/2/268/doc7593-1369068.pdf
I think that means ATmega640/1280/1281 have 16bit PC and have EIND
Thanks, @mcuee. I don't interpret the data sheet in the same sure-footed way. There is no need to implement EIND and eicall
/eijmp
if the silicon implementation of the logical 15/16 bit PC is actually a two-byte PC. It could still be that the silicon implementation of rcall
is still the same between m1280
and m2560
(push 3 bytes on stack and call the subroutine) just that the third byte is always 0.
I would feel much more comfortable if there was an experimental verification. Do you have one of the parts (eg, m1280
, m1281
, m640
) and can run the function above?
The PC is internally indeed "capped" correctly. Regardless of whether the variants are created by the same die, or are actually different silicon, there are internal OTP fuses that trigger certain decisions in the digital code. The actual size of the PC is one of these decisions. You can easily see this even in the compiler: for PCs that are larger than 16 bits, the stack uses one more byte for each CALL (to store the return PC), and the compiler has to cope with that when performing stack parameter offset calculations. You can see there that it properly relies that only devices with more than 128 KiB use three bytes on the stack. ;-) Compile this:
#include <stdarg.h>
extern void doit(int, va_list);
void dosomething(const char *cmd, ...) {
va_list ap;
va_start(ap, cmd);
switch (cmd[0]) {
case 'A':
doit(0, ap);
break;
default:
doit(42, ap);
break;
}
va_end(ap);
}
once for an ATmega1281, and once for an ATmega2561, and you'll see the difference.
stack uses one more byte for each CALL (to store the return PC)
Yes, that's what my little function spwidth()
utilises to return 2 for a 16-bit PC and 3 for a 22-bit PC. Lacking an ATmega1281 I cannot run that function, though!
@dl8dtl Neat idea to ask the compiler as a proxy whether it thinks the PC is two or three bytes, but your example compiles the same with an error message undefined reference to 'doit'
. It does not (as I hoped) give me a compile-time answer of my question. Needs to be compile-time as I don't have the part.
Just compile it to assembly source code only (-Os -S -mmcu=xxx
).
Here's the respective diff output between both generated assembly files:
% diff -u m1281.s m2561.s
--- m1281.s 2023-04-11 16:08:15.037631000 +0200
+++ m2561.s 2023-04-11 16:08:08.125990000 +0200
@@ -18,7 +18,7 @@
/* stack size = 2 */
.L__stack_usage = 2
movw r30,r28
- adiw r30,5
+ adiw r30,6
ld r26,Z+
ld r27,Z+
ld r24,X
@dl8dtl OK, very good. I see, the compiler uses the actual flash size to determine whether a part implements a 2-byte or 3-byte PC in silicon. If the compiler were to get this wrong, then function calls would get stack frames wrong for passing parameters on the stack, and someone would have noticed in the last 20 years or so.
What about the EIND business? Is it cool for avr-libc or the ATDF files to pretend there is an EIND register when in fact there isn't any? User code might do unnecessary/wrong things thinking they might need to manage EIND or thinking EIND is synonymous with FLASHEND > 0x20000
I would feel much more comfortable if there was an experimental verification. Do you have one of the parts (eg, m1280, m1281, m640) and can run the function above?
@stefanrueger
I do not have these parts. I am not sure if @MCUdude has them or not.
But I guess this is not necessary any more, right?
I have an ATmega1281 I can test with
Even though I do not have the parts, but I use Simulator inside Microchip Studio and here are the results.
ATmega1281 --> r =2
ATmega2561 --> r =3
What about the EIND business? Is it cool for avr-libc or the ATDF files to pretend there is an EIND register when in fact there isn't any? User code might do unnecessary/wrong things thinking they might need to manage EIND or thinking EIND is synonymous with FLASHEND > 0x20000
@stefanrueger
Do you have a code to test the existence of EIND?
@mcuee What a cool way to find out! And thanks for the confirmation. I am now convinced by @dl8dtl's compilation trick and by your simulator outcome that smaller parts don't use unnecessarily wide PCs.
code to test the existence of EIND
By convention it's a preprocessor "#define", so it would be #ifdef EIND
. That's how I generated the table entries in the first post. It's the memory address of the EIND register. The eijmp/eicall opcodes use it, but for small parts the effect wouldn't be visible even if eijmp/eicall was implemented b/c AVR flash that has a power-of-2 size wraps round.
Just released version u7.7 of urboot - thanks for helping out for this issue @mcuee @MCUdude @dl8dtl
It is clear that parts with flash above 128 kiBi need more than 16 bits for their PC. What isn't clear is whether lower-spec'd parts of the same architecture also have a 22-bit PC: for example, a m1280 might well be a m2560 reject with flash errors in the upper half where an undocumented fuse bit limits flash to 128 kiBi.
Why is this relevant? Urboot's SWIO routine can produce bit delays matching a given number of CPU cycles (within reason) but it needs to know whether the part has a 22-bit PC or a 16-bit PC as the respective timings of the
rcall
andret
opcodes differ.Microchip's AVR instruction set manual does not say which parts have 22-bit PCs, but it asserts that only parts with 22-bit PC implement the
eicall
that uses theEIND
register to extend the PC. Now, ATDF and avr-libc differ in their opinion whether a part has theEIND
register, see the table below. That's the closest allusion I found in the documentation as to which parts might have a 22-bit PC.If you have one of the parts below, @mcuee @MCUdude @SpenceKonde @dl8dtl, please could you run the following function and edit the table below whether it returns 3 (ie, 22-bit PC) or 2 (ie, 16-bit PC).
Colums 2-4 show whether the part has an
EIND
register. [Edited after discussion]