Home > Net >  Using same memory address ,but code taking 8 bytes extra in program memory?
Using same memory address ,but code taking 8 bytes extra in program memory?

Time:05-14

I am using Atmel Studio for the atmega8 microcontroller. Here I have two options for accessing it's io ports.

  1. I can use DDRB, PORTB, and PINB Macro


    MCU std ports macros

    #define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
    #define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr)   __SFR_OFFSET)
    
    /* Port B */
    #define PINB     _SFR_IO8(0x16)
    #define DDRB     _SFR_IO8(0x17)
    #define PORTB    _SFR_IO8(0x18)
    

this is my simple test code

   #include <avr/io.h>

   #define F_CPU 1000000UL

   #include <util/delay.h>

   int main(void)
   {
    DDRB = 0x01;
    while (1)
    {
        PORTB = 0x01;
        _delay_ms(1000);
        PORTB = 0x00;
        _delay_ms(1000);
    }
   }

after successful compilation

Program Memory Usage : 108 bytes 1.3 % Full
Data Memory Usage : 0 bytes 0.0 % Full

  1. Or I can use my own version

    gpio.h\

#ifndef GPIO_H_
#define GPIO_H_

#include <avr/io.h>

typedef union {
    struct  
    {
        uint8_t pin0:1;
        uint8_t pin1:1;
        uint8_t pin2:1;
        uint8_t pin3:1;
        uint8_t pin4:1;
        uint8_t pin5:1;
        uint8_t pin6:1;
        uint8_t pin7:1;
    };
    struct {
        uint8_t lsb4:4;
        uint8_t msb4:4;
        };
        uint8_t pins;
}port_reg_t;

typedef struct  
{
    port_reg_t r;
    port_reg_t d;
    port_reg_t p;
}port_t;

#define bio    (*(volatile port_t *) (0x16   __SFR_OFFSET))

#endif /* GPIO_H_ */

this is sample code

/*

  • led.cpp

  • Created: 12-05-2022 14:59:53

  • Author : HP */

     #include <avr/io.h>
     #define F_CPU 1000000UL
     #include <util/delay.h>
    
     #include "gpio.h"
    
     int main(void)
     {
    
         //DDRB = 0x01;
         //
         //
         //while (1)
         //{
         //PORTB = 0x01;
         //_delay_ms(1000);
         //PORTB = 0x00;
         //_delay_ms(1000);
         //}
    
     bio.d.pin0 = 1;
    
     while (1) 
     {
         bio.p.pin0 = 1;
         _delay_ms(1000);
         bio.p.pin0 = 0;
        _delay_ms(1000);
    
     }
    }
    

after successful compilation

Program Memory Usage : 116 bytes 1.4 % Full
Data Memory Usage : 0 bytes 0.0 % Full

my question is, why it's taking 8 bytes extra in program memory?

CodePudding user response:

You assume that both program do the same, but they do not. So this is the reason for the different memory usages.

The first program writes a complete byte to port B:

    PORTB = 0x01;

The second program sets (resets) just one bit, leaving the other seven bits alone:

    bio.p.pin0 = 1;

This is what happens "under the hood":

    bio.p.pins = bio.r.pins | 0x01;

This is the comparable version with your header:

    bio.p.pins = 0x01;

CodePudding user response:

From the code you show, I can demonstrate your structs take 6 bytes more (in program space). I don't know why your compiler needs 8 bytes more instead of 6, but you can investigate the generated assembler (maybe you has to ask to generate it).

An assignation in C to an I/O register, like:

PORTB = 0x01;

is carried out by an AVR MCU in this way:

LDI   R1, 1      ; 16 bit instruction, 2 bytes
STS   PORTB, R1  ; again 2 bytes

For a total of 4 bytes. Instead of STS, sometimes it is possible to use OUT (see later), but it is an instruction anyway.

In your first program, you have 3 assignation like this.

In the second program, the compiler know it should operate on single bits, not whole bytes. To set a single bit in a I/O register, because the C program states:

bio.p.pin0 = 1;

the compiler has to generate:

LDS   R1, bio.p
ORI   R1, 1       ; <-- added instruction, 2 bytes
STS   bio.p, R1

That added instruction is the one that sets a bit leaving the other untouched. One instruction more is needed.

Now, in both programs there are 3 assignations like this and, in the second version, each one takes one more MCU instruction, for a total of 6 bytes.

The AVR MCU has instruction to manipulate single bits - in a register or in the I/O space. So the same effect to set a bit vould have been made with a single:

SBI   PORTB, 0   ; <-- set bit 0 of PORTB

This single instruction does the same as the 3 previous LDS/ORI/STS! And it is to be noted that, if those instructions were used, the second program would be shorter than the former, not longer.

The problem is that those instructions (SBI/CBI) can be applied only to the first 32 addresses in their space. Now, depending also on the precise model of MCU, not all I/O registers reside in the first 32 addresses, so this instruction can not always be used, it depends on the target.

Probably your compiler chose to not use SBI/CBI because it is not able to understand they are safe in this case. Maybe if you turn on full optimization they will be used, or maybe you have to instruct the compiler to use them in this particular case.

About STS and OUT: STS can do everything OUT does, but they use different addresses. I don't understand why OUT (and its companion IN) exists, but surely there is a reason.

  • Related