How to view converted assembly code in avrstudio5? [RTOS]

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

I want to see the converted (from C) assembly code in avrstudio5. How do I do it? Cant find any options in the menu... :(

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

EITHER go into the project settings and make sure a .lss file is generated at build time, and then open that file,

OR (while debugging, e.g. in Simulator) go into the Debug menu, submenu Windows (or some such) and select the Disassembly window.

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

One day I really should make this a tutorial:

https://www.avrfreaks.net/index.p...

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

Thanks guys! You guys are legends like always.

Ok so the thing is I am thinking of writing my own OS (micro stuff). And I am studying the context switching etc. I was hoping to find out the code that is created from a simple ISR routine. I am surprised to see that the assembly code for the ISR routine doesnt save (context) all the 32 registers (?).

I have the following in C:

ISR(TCC1_OVF_vect)
{
    TaskTick = 1;
}

then assembly gives me:

ISR(TCC1_OVF_vect)
{
 e24:	1f 92       	push	r1
 e26:	0f 92       	push	r0
 e28:	0f b6       	in	r0, 0x3f	; 63
 e2a:	0f 92       	push	r0
 e2c:	11 24       	eor	r1, r1
 e2e:	8f 93       	push	r24
	TaskTick = 1;
 e30:	81 e0       	ldi	r24, 0x01	; 1
 e32:	80 93 4e 20 	sts	0x204E, r24
}
 e36:	8f 91       	pop	r24
 e38:	0f 90       	pop	r0
 e3a:	0f be       	out	0x3f, r0	; 63
 e3c:	0f 90       	pop	r0
 e3e:	1f 90       	pop	r1
 e40:	18 95       	reti

why is only r0 and r1 pushed? Was hoping to find the rest of the 32 registers...And what is this 3F doing? Also why are those pops outside the bracket?

I dont know much about assembly as you can tell...:(

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

Quote:

I am surprised to see that the assembly code for the ISR routine doesnt save (context) all the 32 registers (?).

Why would it? The compiler knows it's only using R24 in the body of that function so saves that and the rest around it are just "housekeeping".

There's actually a missed optimisation in the code you showed. The fact is that some functions in C rely on R1 holding 0 (internally it's called "__zero_reg__" by the compiler) so it saves r1 and then does "eor r1,r1" to be sure it holds 0. Only thing is that in this very simple code nothing relies on R1=0 so it might as well not have bothered. Similarly it saves R0 so it can use it to read a copy of SREG (IO address 0x3F) into it and push that, then restores this at the end. But again the actual body of the function:

   TaskTick = 1;
 e30:   81 e0          ldi   r24, 0x01   ; 1
 e32:   80 93 4e 20    sts   0x204E, r24 

does not change SREG. So in this case it's another missed optimisation as it might as well not have bothered saving SREG and using R0 to do this (which also therefore needed to be saved/restored). The code could have been:

ISR(TCC1_OVF_vect)
{
 e2e:   8f 93          push   r24
   TaskTick = 1;
 e30:   81 e0          ldi   r24, 0x01   ; 1
 e32:   80 93 4e 20    sts   0x204E, r24
}
 e36:   8f 91          pop   r24
 e40:   18 95          reti 

Now see what happens if the ISR calls a function that is not visible in the current source file:

#include 

volatile uint8_t TaskTick;

ISR(TCC1_OVF_vect)
{
    TaskTick = 1;
	foo();
}
ISR(TCC1_OVF_vect)
{
   0:   1f 92           push    r1
   2:   0f 92           push    r0
   4:   0f b6           in      r0, 0x3f        ; 63
   6:   0f 92           push    r0
   8:   08 b6           in      r0, 0x38        ; 56
   a:   0f 92           push    r0
   c:   09 b6           in      r0, 0x39        ; 57
   e:   0f 92           push    r0
  10:   0b b6           in      r0, 0x3b        ; 59
  12:   0f 92           push    r0
  14:   11 24           eor     r1, r1
  16:   18 be           out     0x38, r1        ; 56
  18:   19 be           out     0x39, r1        ; 57
  1a:   1b be           out     0x3b, r1        ; 59
  1c:   2f 93           push    r18
  1e:   3f 93           push    r19
  20:   4f 93           push    r20
  22:   5f 93           push    r21
  24:   6f 93           push    r22
  26:   7f 93           push    r23
  28:   8f 93           push    r24
  2a:   9f 93           push    r25
  2c:   af 93           push    r26
  2e:   bf 93           push    r27
  30:   ef 93           push    r30
  32:   ff 93           push    r31
    TaskTick = 1;
  34:   81 e0           ldi     r24, 0x01       ; 1
  36:   80 93 00 00     sts     0x0000, r24
        foo();
  3a:   0e 94 00 00     call    0       ; 0x0 <__vector_20>
}
  3e:   ff 91           pop     r31
  40:   ef 91           pop     r30
  42:   bf 91           pop     r27
  44:   af 91           pop     r26
  46:   9f 91           pop     r25
  48:   8f 91           pop     r24
  4a:   7f 91           pop     r23
  4c:   6f 91           pop     r22
  4e:   5f 91           pop     r21
  50:   4f 91           pop     r20
  52:   3f 91           pop     r19
  54:   2f 91           pop     r18
  56:   0f 90           pop     r0
  58:   0b be           out     0x3b, r0        ; 59
  5a:   0f 90           pop     r0
  5c:   09 be           out     0x39, r0        ; 57
  5e:   0f 90           pop     r0
  60:   08 be           out     0x38, r0        ; 56
  62:   0f 90           pop     r0
  64:   0f be           out     0x3f, r0        ; 63
  66:   0f 90           pop     r0
  68:   1f 90           pop     r1
  6a:   18 95           reti

It's still not all the registers but it's the ones the called function are likely to use and that cannot be corrupted. As the compiler cannot "see" foo() it cannot know which registers it actually uses (it might just be R24 again) so it saves the lot.

As for RTOS and context saving. I don't know of a better explanation than this which just happens to be written about avr-gcc on an AVR chip:

http://www.freertos.org/implemen...

Try to forgive the poor website design but simply read through by following the "Next:" link given at the bottom of each page.

By the end you'll know exactly how to do context saving in a timer ISR at which point you will probably think "blimey this FreeRTOS does a bloody good job - why am I bothering to reinvent the wheel because what I'm working towards here is going to turn out the same in the end anyway"

Oh and the "signal attribute" is the "old fashioned" way of doing what's now simply provided by ISR(vector_name).

Note that these days you can use:

ISR(TIMER_vect, ISR_NAKED) {

to do what they are showing there. Also

reti();

can be used in place of their:

asm volatile ( "reti" );

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

hhm.. thanks for that. that was an interesting read...some of it were common with what I had been reading so far...

I still find reading through the FreeRTOS code is a big job...eats my brain very quick...so I want to progress slowly by learning the steps myself. I think I understand the context switching so far. I might just copy the context switching macro and use it in my code...

Ok here is an issue...currently I have my BLDC motor code using a simple task scheduler with linked list etc...the task scheduler is only used for printing debug messages or ramping up the motor (every 1ms changing the commutation interval). Its not using any context switching. I intend to implement context switching with a new variant of task scheduler.

The thing is the core motor driving code is using timer interrupts. And there is no way I would want to bring that part of the code inside a task scheduler. So if I ran my misc stuff using a context switched task scheduler, could I still keep my existing motor driving ISR routines the way they are? I dont want to change those ADC interrupt/commutation ISR etc.

If an ISR fires they automatically save the context anyway. So I dont see why I need to worry about them when I implement my task scheduler. My task scheduler will context switch when necessary, sure and when one of those motor driving ISR fires, it automatically context switch my task related contexts and then switch it back at the end its run. Do you see any issues?

What about those motor driving (ADC interrupt/commutation ISRs) having a function call? their context switch wont be saving all those registers used inside the function call (as you demonstrate above), could this not be a problem, if the function modifies my registers that were being used in the task contexts?

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

Quote:

I intend to implement context switching with a new variant of task scheduler.

Adding more code overhead to slow things down? Why? What possible advantage could it have? A controller program generally consists of a "slow" main() loop doing the background work that is not time constrained then one or more fast acting interrupt handlers that do the work that must happen promptly.

Throwing an RTOS into the mix is almost certainly not the solution to a bad overall design - how long, for example, does it take a pre-emptive OS to switch context? 50 cycles? 100 cycles? How often? Every 1ms? Every 10ms? What a complete waste of CPU cycles if you have something to be done in a timely fashion.

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

well as I said the motor driving stuff I dont want to change and want to keep them with the ISR code they r running already. main loop code does not interract with this stuff.

I am only thinking of having a context switched task scheduler for doing other things like printing message via uart etc and other misc things. I expect this task timer to run every 1ms?

Without a context switched task scheduler (which is currently a simple linked list of timer expire routine), for example my task scheduler is stuck until lets say it finishes printing a 100 char line.

I am thinking the above context switches can be fairly short? 32 registers?Stack pointer, program counter? no more than 40 cycles?

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

Quote:

I am only thinking of having a context switched task scheduler for doing other things like printing message via uart etc and other misc things. I expect this task timer to run every 1ms?


But why? Why not just:

int main(void) {
  init_stuff();
  while(1) {
    // some stuff
    if (something_to_print) {
      print_it();
    }
  }
}

If the printing is for the benefit of humans they don't care (in fact they cannot even tell) if it happens to be 50ms or even 100ms "late".

If the problem is waiting for 100 to print then:

void print_it() {
 switch(state) {
   case FIRST_BIT: print_10(); state=SECOND_BIT; break;
   case SECOND_BIT: print_10(); state=THIRD_BIT; break;
etc.
}

then call it 10 times.

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

lol...I know. I am quite happy the way it is currently. I hate wasting CPU myself. But I thought it would be a good learning experience.

There has to be a better way of managing it all without having to spend CPU copying and storing context registers... :(

So you think i should just abandon the idea of taking up a RTOS solution? I do have a project where I have implemented TCP/IP with sockets using simple state based solution rather than RTOS.

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

Quote:

There has to be a better way of managing it all without having to spend CPU copying and storing context registers...

Explore Adam Dunkel's "protothreads".

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

hhm...that just went over my head lol...whooaaa... there was a time when I wrote C only for assignments. Now I do it for work/fun/personal projects! But now its starting to hurt my brain...

Think I might stick with my simple linked list based task scheduler (without context switch)...

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

I reckoner avr micros should have a facility to perform hardware context switching :P

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

Quote:

I reckoner avr micros should have a facility to perform hardware context switching

Why? These are MICROcontrollers - if you want a processor designed for an OS then look at anything with an MMU such as ARM9 which allows the address space of any particular task to be virtualised so the context switch is a question of which one is paged into context.

Last Edited: Tue. Nov 1, 2011 - 11:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hhm...thats a big step for me right now :P Still struggling with UC3A devices and too many registers and pins to deal with for now :P
Perhaps soon.... :)

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

Better to use a single stack scheduler rather than a full RTOS, especially with preemption. Far simpler and compatible with small-RAM micros.

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

i like the preemption idea... but u cant do that wth single stack scheduler, can u?

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

I've written a very primitive and fast scheduler which does no context save. In the ISR it enqueues an event (event number 0-31/priority 0-7 in one byte) The dispatcher runs the handler for the highest priority event to completion - no preemption. It needs a about 4 microsec/event overhead at 18MHz clock, and is good enough for the particular application.
Jerry

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

Quote:

The dispatcher runs the handler for the highest priority event to completion - no preemption.

If there's no preemption (and no context saves) then presumably the dispatcher simply relies on the previous task yielding after some amount of work? What if it chooses to work on for 3000ms?

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

^ yeah thats the trouble with the linked list version of scheduler I have got too lol...context switching is still the way to go...I hope to do those routines setup in my code sometime soon. For now normal simple scheduler is adequatish I guess... :|

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

hhm... I needed to revive this old thread rather than write a new one. I was just reading the datasheet for ATSAM4S, the ARM atmel chip... and there it claims in section 11.1 on page 41 that it has "Hardware stacking of registers" during an interrupt!

Does that mean what we have been talking above with explitely stacking the registers and program counters etc will not apply with this ARM micro?

This is a significant gain is it now? I mean it would be possible to then just have a 1ms timer interrupt which calls the task scheduler function from within the interrupt?

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

Suggest you read this:

http://infocenter.arm.com/help/t...

and this:

http://infocenter.arm.com/help/t...

As such it is clear that the automatic 8 word stack frame is not an entire machine context (as an RTOS requires) but merely a subset of the registers. So it may help but it's not the complete solution.

Anyway, on an M4 wouldn't you be using Linux so why would you need to care?

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

erm...thanks for those links clawson... but where do u get 8 word stack frame? I would have thought it stores all the registers? :|
The SAM4 micro goes up to 1MB flash size....can you fit linux in it? Besides it doesnt have memory management...so I dont think you can...

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

Quote:

I would have thought it stores all the registers?

So you didn't bother to read the links then? Ho hum.

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

clawson wrote:
Quote:

I would have thought it stores all the registers?

So you didn't bother to read the links then? Ho hum.

I did! ok i will read again... :|

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

You are able to see the embedded images I take it (My "No Script" blocked them but they appeared when I allowed access). Fig 2.3 in the first link (assuming CM4 without FP) shows that it stacks R0..R3, R12, LR, PC and xPSR. Meanwhile the first link show the M4 to also have R4..R11 and various control and exception mask registers.

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

Ok if I understand correctly only the R0, R1, R2, R3 and R12 is saved in stack?

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

Quote:

Ok if I understand correctly only the R0, R1, R2, R3 and R12 is saved in stack?

Snap!

(well LR and PC are really R14 and R15 too).

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

lol you posted before me :P
hhm... why on earth would they not make the hardware such that it pushes all the registers in stack? :( Please it seems only 16 such stacks can be pushed in? I am guess this is what those S registers pointing to?

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

Quote:

hhm... why on earth would they not make the hardware such that it pushes all the registers in stack?

Speculative, to some point, since it's been a while since I looked at ARM architectures:

Because not all of them needs to be saved all of the time. And the information on which that actually needs to be saved is not known by the hardware but by the compiler. So it is largely up to the compiler to push what needs to be pushed. All this IIRC.

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

You do know the ARM has LDMIA and STMIA don't you? Why would you impose a full register save on those who don't actually need it? Presumably the same reason the AVR doesn't PUSH/POP all 32 registers around an ISR either?

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

lol ok I dont know much about ARM...I am barely playing with UC3A 32bits still...what is this LDMIA and STMIA?

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

Lots of examples of context switching scheduler and RTOSes. One simple, fairly easy to understand, for AVR, is "seos". It's either in the projects section here or can be found on the 'net. I have it in my stash, as an AVRstudio 4 project. I've done a couple of sample task switching programs with it, including one on the Teensy using USB.

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

Quote:

what is this LDMIA and STMIA?

Oh come on - has Google been banned in Aus?

http://infocenter.arm.com/help/t...
http://www.rz.uni-karlsruhe.de/r...
http://www.rz.uni-karlsruhe.de/r...

You can basically stack or pop a whole bunch of registers in one go. ARM is RISC and is optimized for implementing a language such as C so it has instructions such as this which are typically going to be used a lot by code that makes a lot of stack usage.

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

ARM7 (and newer?) pushes/pops a list of register numbers given in one instruction. Neat.

Another thing to challenge compiler writers for ARM.. is that most any instruction can have an "if condition" attached, like jump if (normal), but add-if, subtract-if, call-if, and so on. Done right, that can save time and memory. But I wonder if the compilers really exploit that? I use the IAR compiler for ARM and have seen some amazing optimization done, but haven't looked at these conditionals on common instructions.

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

Quote:

But I wonder if the compilers really exploit that?

Of course they do - this is one of the main reasons ARm has such a following - it's a brilliantly designed instruction set and it's particularly good for implementing high level languages. The AVR in the 8 bit field kind of mimics that - as we know it was designed in collaboration with IAR to make a processor that was particularly suited to implement C (hence the large register file).

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

Does GCC for ARM7 and/or Cortex do a good job of exploiting these instruction sets? I wonder.. as IAR and Keil spend $$$ to compete in this space. I evaluated both and chose IAR several years ago and have been very pleased with them + Segger.

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

Quote:

Does GCC for ARM7 and/or Cortex do a good job of exploiting these instruction sets? I wonder..

Yes it's brilliant. In fact a lot of the focus of ongoing GCC development is targetted at ARM (unlike AVR which is just on the side lines).

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

clawson thanks for that....man so much reading to be done in the world micro controllers. ok we know the arm does automatic stacking of R0-R3 during an interrupt...and now you say the LDMIA which does from R0-R7....what if I want to store the full context? I will have to do it manually... hhm...

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

In thumb mode as the register diagram I linked to above shows there aren't that many more registers to be saved and, as I say, STMIA helps a lot.

Probably worth looking at Freertos to see how their Cortex port works.