Writing ISR in assembly. AVR-GCC 5.4.0

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

Greetings -

 

I want to write the entire ISR in assembly (rest of my program is in C) but having trouble with the static local variables inside ISR. For example looking at the following dummy test program.

 

#include <avr/io.h>
#include <avr/interrupt.h>

ISR(TIMER1_COMPA_vect)
{
    static uint8_t counter = 0; // Stored in SRAM

    static uint8_t data_1[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; // Stored in SRAM

    static const uint8_t data_2[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; // Stored in SRAM

    PORTA = data_1[counter];
    PORTB = data_2[counter];

    if (++counter == 8)
    {
        counter = 0;
    }
}

int main(void)
{
    DDRA = 0xff;
    DDRB = 0xff;

    TIMSK1 |= (1 << OCIE1A);
    OCR1A = 800;
    TCCR1B |= (1 << WGM12) | (1 << CS10) ;
    sei();

    while (1)
    {
        asm("nop");
    }
}

 

How/Where do I create counter/data_1 variable in assembly and also data_2 which is constant (I'd like it to be in SRAM, not in program memory) ?

 

I am writing assembly is separate .S file. Looking like this.

 

 #include <avr/io.h>

 .global TIMER1_COMPA_vect

    TIMER1_COMPA_vect:
        PUSH R16
        IN R16, 0x3F 

        // Writing ISR asm here.

        OUT 0x3F, R16
        POP R16
        reti

 

One sure way I know is if I make counter, data_1, data_2 global in C then I can access their address by using their name in assembly. ( Is this the optimal way? )

 

If not then I think in assembly I have to declare them first in some place in dseg, but then how C complier will know to not overwrite them with other variables in rest of the C program?

 

Thanks !!!

This topic has a solution.

“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?” - Brian W. Kernighan
“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.” - Antoine de Saint-Exupery

Last Edited: Thu. Apr 28, 2022 - 07:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Any ISR worth beans should 99% of the time save the sreg  (unless your ISR affects no carry flag;, Z flag, etc)

 

push r16

in r16, sreg

push r16

...

..

 

pop r16

out sreg, r16

pop r16

reti

 

 

if the reg is 100% dedicated to the isr sreg backup & not used by main, all the push/pop may not be needed. 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

Hi Heissen 

unfortunately I can’t answer your question. I have written many ISRs, however I have never needed to write one in avr assembler.

 

The reason for this is that the code generated by the compiler is very good. I’m sure it can be optimised if handcrafted but for almost all applications this optimisation isn’t needed.

 

Have you had a look at the codes generated by your C version of the ISR? You can see this in the listing file. (.lss in output files folder)  Are you certain that this is unacceptable for your application?

regards
Greg

Last Edited: Wed. Apr 27, 2022 - 10:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:
Any ISR worth beans should 99% of the time save the sreg

Yes, the address 0x3F in code is for SREG. Saved it in R16. Restoring it back on the way out.

 

gregd99 wrote:
Have you had a look at the codes generated by your C version of the ISR?

The ISR generated by C is great. I just want to make it faster and learn in the process, there is no special need for this, just want to explore and tinker with assembly.

“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?” - Brian W. Kernighan
“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.” - Antoine de Saint-Exupery

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

Just build the C with -save-temps and the .s file will reveal how to accomplish anything in the C from avr-as

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

 the address 0x3F in code is for SREG. 

Why not say so? 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

I can't explain in words.
However, the code I used for learning a long time ago was left, so I will upload it.
I don't know if this is the best, but it's working fine.

The variable get_ptr in this example must be initialized on the C code side.

 

 #include <avr/io.h>

;-----------------------------------------------------------
;Data RAM area (static variable initialized with significant value)
.section .data

;-----------------------------------------------------------
;Data RAM area (static variable initialized with zero)
.section .bss

;-----------------------------------------------------------
;Data RAM area (variable that is not initialized)
.section .noinit

.global get_ptr
get_ptr:    .ds.w   1

;-----------------------------------------------------------
;Program area
 .section .text

.global USART0_DRE_vect
USART0_DRE_vect:
    push    r0
    in      r0, (SREG)
    push    r24
    push    XL
    push    XH

    lds     XL, get_ptr
    lds     XH, get_ptr + 1
    ld      r24, X+
    sts     USART0_TXDATAL, r24
    sts     get_ptr, XL
    sts     get_ptr + 1, XH

    pop     XH
    pop     XL
    pop     r24
    out     (SREG), r0
    pop     r0
    reti

 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

The easiest solution is to make your local static variables be global variables instead.
"static local" variables are just a figment of C's imagination; they end up just being RAM locations, with certain limitations on who can see their names.

    uint8_t myisr_counter = 0; // Stored in SRAM
    const uint8_t myisr_data_1[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; // Stored in SRAM
    const uint8_t myisr_data_2[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; // Stored in SRAM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

sorry. I misunderstood the content of the question.
Declaration of static local variables in the assembler.

 

.section .data
data_2:	.dc.b	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08

 

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

westfw wrote:
The easiest solution is to make your local static variables be global variables instead.
"static local" variables are just a figment of C's imagination; they end up just being RAM locations, with certain limitations on who can see their names.

That's what I thought, just needed conformation.

 

Thanks everyone.

“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?” - Brian W. Kernighan
“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.” - Antoine de Saint-Exupery

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

One more question :-

 

Say I want to dedicate register r2 to act as a "counter" variable in the above code. So in ISR, I don't have to push pop the counter variable from RAM. Everything seems okay except, is there a way to tell the C compiler to not touch the register r2 for the C code.

 

In AVR libc documentation there is this part :-

 

How to permanently bind a variable to a register?

This can be done with register unsigned char counter asm("r3");

Typically, it should be safe to use r2 through r7 that way.

 

If I follow the above instruction and bind register r2 to a global variable named "counter"

 

register uint8_t counter asm("r2");

Now is it safe to assume that the compiler will leave the r2 register alone and won't use it in the rest of the program?

 

If not then

 

I remember reading on the forum, someone (I think clawson) was mentioning a argument passed to the compiler to avoid using a particular register for code generation. I can't seem to find it.

“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?” - Brian W. Kernighan
“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.” - Antoine de Saint-Exupery

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

Heisen wrote:
Say I want to dedicate register r2 to act as a "counter" variable in the above code. So in ISR, I don't have to push pop the counter variable from RAM. Everything seems okay except, is there a way to tell the C compiler to not touch the register r2 for the C code.
As you say, in one of your C files you can do something like:

uint16_t counter asm("r16");

which would locate counter in R17:R16 (I deliberately made it more than 8 bits wide just for the example).

 

Now you can access r17/r16 in the ISR and you are accessing what is globally known as "counter".

 

But two things:

 

1) your own code you write might happen to use r17/r16 somewhere too. You can stop that happening by compiling with:

-ffixed-16 -ffixed-17

As explained in the user manual:

 

https://gcc.gnu.org/onlinedocs/g...

 

this prevents any of the code built from your sources from using 16/17 (obviously the option has to be global and used for the compile of every .c file).

 

2) the above will prevent your code from using r16/r17 but say you call a complex function in one of the libs like printf() from libc.a or sin() from libm.a. It could well be that when they were built they may have had a lot of register pressure and needed to use pretty much all the AVR registers. So they could well have used r16/r17 too. So you will basically need to look at an Asm listing of the entire project (.lss file) and see if anything pulled in from any of the libraries has used 16/17. If so you will need to take steps in the vicinity of their calls to make sure r16/17 is stored and restored around the call.

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

Thanks, exactly what I was looking for.

 

Edit - I need control over every single clock cycle inside this ISR, the crazy part is that the interrupt is interrupting itself and that is by my design, I have to be careful, this is almost a setup for failure, but let's see, it only has to work once if my calculations are correct.

“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?” - Brian W. Kernighan
“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.” - Antoine de Saint-Exupery

Last Edited: Thu. Apr 28, 2022 - 11:02 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Out of interest, what interrupt rate are you planning?

regards
Greg

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

Is this something REALLY time sensitive like WS2812 LEDs perhaps. The net has plenty of existing examples showing the Asm to drive them (a bit like audio/video it's one of those things that almost has to be done in Asm)