I would have expected these to produce the same code, but having just changed a few PORTx.OUTSETs to PORTX_OUTSETs I seem to have saved memory. New to Atmel Studio so not sure how to look at the compiled code yet, but have others seen this effect ?
PORTx.OUTSET vs PORTx_OUTSET
Post real code. There are no official macros called PORTx or PORTx_OUTSET
Defining PORTx as a specific SFR e.g. PORTA can make your code easier to maintain.
.
I like using MACROs when appropriate. You must write them VERY carefully. They are a case of Write Once Use Many times.
Cliff Lawson does not like them. There is always a risk of obfuscating your code.
.
After all. PORTA.OUTSET = value is clearer than PORTx.OUTSET = value
.
David.
Post real code.
They weren't macros, I was just highlighting it as a generic issue. The code has four instances of
PORTB_OUTSET = PIN1_bm;
This is reported by Atmel Studio as using 12 less bytes than
PORTB.OUTSET = PIN1_bm;
which seems both excessive and very odd.
I'm obviously not going to publish the whole program as it's both irrelevant and commercially confidential.
In which case you should explain what you meant.
.
Both of your statements do exactly the same thing. I would expect an optimising Compiler to generate similar ASM code.
Examine the LSS file. Compare the operation in the AS7 Simulator.
.
Yes, the first statement gives more clues to the Compiler. You can alter the -O arguments to see which end up with identical output from the second statement.
.
Personally, I would advise you to use the style that you feel most comfortable.
If speed and efficiency is important, VPORTs offer dramatic performance.
.
David.
Yes I would have expected the compiler to produce identical code as well. I'll play with the -O arguments as you suggest. And I only found this problem as I'd ran out of VPORTs.
Mike
You can change VPORT on the fly. A maiintenance nightmare unless you document it carefully.
.
Are you running out of Flash?
There are probably better ways to save Flash than changing all your PORT code.
.
It is well worth spending time and effort on the 1% of code that affects the execution time by 50%.
Wasting effort on saving Flash when you have several kilobytes spare is pointless.
.
David.
The reason for GROUP.REG and GROUP_REG is that the former uses C structs while the latter is a direct definition. For writing Asm code the assembler does not understand structs which is why the GROUP_REG version has to exist. In C:
POTRB.OUTSET = 0x55; PORTB_OUTSET = 0x55;
will ultimately have the same result. In Asm you would have no choice:
ldi R16, 0x55 sts PORTB_OUTSET, r16
Personally, I would advise you to use the style that you feel most comfortable.
Having said that you would need to take a look at the generated code but if you were to write:
PORTB.DIR = 0xF0; PORTB.OUTSET = 0xC0; if (PORTB.IN & 0x0F != 0) { PORTB.OUTSET = 0x30; }
then because C knows this is a struct and that all these registers are "close" the way it's likely to access this is to load an index register with the base address of "PORTB" and then use indexed offsets to do the writes. If you had written this with the "_" define macro form then it's more likely to treat each location as completely independent and may use STS rather than ST access, having to specify full addresses every time (ie larger/slower code). So there *might* be an advantage to using the struct style access.
This is the version using C style struct:
PORTB.DIR = 0xF0; 220: e0 e2 ldi r30, 0x20 ; 32 222: f6 e0 ldi r31, 0x06 ; 6 224: 20 ef ldi r18, 0xF0 ; 240 PORTB.OUTSET = 0xC0; 226: 90 ec ldi r25, 0xC0 ; 192 if (PORTB.IN & 0x0F != 0) { PORTB.OUTSET = 0x30; 228: 30 e3 ldi r19, 0x30 ; 48 int main(void) { while (1) { PORTB.DIR = 0xF0; 22a: 20 83 st Z, r18 PORTB.OUTSET = 0xC0; 22c: 95 83 std Z+5, r25 ; 0x05 if (PORTB.IN & 0x0F != 0) { 22e: 80 85 ldd r24, Z+8 ; 0x08 230: 80 ff sbrs r24, 0 232: fb cf rjmp .-10 ; 0x22a <main+0xa> PORTB.OUTSET = 0x30; 234: 35 83 std Z+5, r19 ; 0x05
It has put the base in Z then uses things like ST Z, ST Z+5, ST Z+8
This is the version using the Asm macros:
PORTB_DIR = 0xF0; 220: c0 e2 ldi r28, 0x20 ; 32 222: d6 e0 ldi r29, 0x06 ; 6 PORTB_OUTSET = 0xC0; 224: e5 e2 ldi r30, 0x25 ; 37 226: f6 e0 ldi r31, 0x06 ; 6 if (PORTB_IN & 0x0F != 0) { 228: a8 e2 ldi r26, 0x28 ; 40 22a: b6 e0 ldi r27, 0x06 ; 6 int main(void) { while (1) { PORTB_DIR = 0xF0; 22c: 20 ef ldi r18, 0xF0 ; 240 PORTB_OUTSET = 0xC0; 22e: 90 ec ldi r25, 0xC0 ; 192 if (PORTB_IN & 0x0F != 0) { PORTB_OUTSET = 0x30; 230: 30 e3 ldi r19, 0x30 ; 48 int main(void) { while (1) { PORTB_DIR = 0xF0; 232: 28 83 st Y, r18 PORTB_OUTSET = 0xC0; 234: 90 83 st Z, r25 if (PORTB_IN & 0x0F != 0) { 236: 8c 91 ld r24, X 238: 80 ff sbrs r24, 0 23a: fb cf rjmp .-10 ; 0x232 <main+0x12> PORTB_OUTSET = 0x30; 23c: 30 83 st Z, r19
It's slightly longer in that for the three locations it has put their addresses separately in X, Y and Z rather than doing everything as an offset from the single Z.
I guess to see this in action I really need an example involving more than 3 registers because in the latter case it will shortly run out of X/Y/Z
I would expect an orphan PORTB_OUTSET statement to use STS
And a sequence of PORTB_xxx accesses to possibly calculate address and use STD
An orphan PORTB.OUTSET would probably load PORTB address into Z
Pure speculation on my part. The Optimiser will be making cost-benefit analyses at every stage. The end results may vary for different apps and even for the same app.
Hey-ho. Compiler writers are pretty clever. I really do not care how they do it. Just as long as they obey my statements.
Ok, I would if these statements lie in the 1% of code that ruins the application's performance.
Otherwise, I attempt to write straightforward code in a HLL and never worry about the binary.
David.
Personally I'd choose the C struct style definitions anyway as it is simply more "C like"
Grouping registers in structs is probably the first step towards object oriented code ;-)