Setting a flag in ASM

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

I want to set a simple global flag in ASM but I'm struggling to make it work.

 

In my S file I have

 

an include to my global var (_SOF_Flag)

 

I did this

 .extern _SOF_Flag

 

and later

sts     _SOF_Flag,1;set flag

 

but I my c app location that also includes the global definition  never sees this variable go to 1.

 

 

 

This topic has a solution.
Last Edited: Mon. Jul 16, 2018 - 01:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

STS takes a register number as the second parameter so you are storing R1 into the location. In GCC R1 usually hols 0x00
.
Do something like:
.
LDI R24, 0x37
STS _SOF_Flag, R24

Last Edited: Sun. Jul 15, 2018 - 09:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, curios though. Are there any tricks to do this in 1 clock cycle? I'm tiring to set a flag in my USB code and it's extremely time sensitive. The flag just need to be set to anything other then 0.

Last Edited: Sun. Jul 15, 2018 - 09:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you choose the active state of the flag to be zero instead of one, it will be faster, because r1 is already zero (you don't need to clear it if it's a function called from C), you can just move r1 to the flag.

The sts instruction itself takes 2 cycles at least, I think. To be faster, you can use some I/O register within the range of the out instruction (1 cycle) to store the flag (some I/O register you are not using and is safe to change).

Last Edited: Sun. Jul 15, 2018 - 09:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Duh, of course (Should have thought about that). I can do that. Is sts 2 clocks?

STS _SOF_Flag, R1 ;[2] ?

 

 

 

Last Edited: Sun. Jul 15, 2018 - 09:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, for classic AVRs memory access instructions usually take 2 cycles. Details here: http://ww1.microchip.com/downloa...

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

Remember that SBI and CBI also take 2 clk!

 

if it's only one flag and you are in control (no C) then use the T register. one clk to set and clear and easy to check with branches.

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

why not use:

 

ser reg#  ...set to 0xFF   ex:  ser myflag

clr reg#   ...set to 0x00  ex:   clr myflag

 

that's as fast as it gets.

Can you afford to allocate a whole register as your flag?  Probably, if you only have one (or few) flags.

When in the dark remember-the future looks brighter than ever.

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

because then you have to reserve one of the high registers. 

but if that is ok use ORI and ANDI and skip on bit. then only one bit is used. (an AVR don't really have either SER and CLR instructions thy are macros for LDI r,$ff and EOR r,r )

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

Yeah I tried that but it required me to use a register.

 

I decided to go with the 2 clock and just use the portb. This way I don't even have to fool with making a global (and for quick test). but where did I go wrong?

 

asm

sbi PORTB,0

 

 

 

code

PORTB &= ~1;
while(!PORTB & 1)

{

//I never hit this loop?

}

 

 

 

 

Last Edited: Mon. Jul 16, 2018 - 12:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Damn parenthesizes.

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

If there are many flags, General Purpose I/O Registers (GPIORx) registers within range of SBI/CBI and SBIS/SBIC can be used.

 

The tiny2313 has three such registers, but many AVRs have only one within range.

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

S_K_U_N_X wrote:
//I never hit this loop?
Which suggests the code where you later change the state of the bit is never executed. Can't help thinking you could benefit from an ICE here!

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

I should be clear just by single step the generated ASM code in the simulator. 

 

 

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

Sparrow - apparently it's V-USB he's trying to understand/change. The "SOF" mentioned above is a USB "Start of Frame". As it's software USB it might be quite difficult to setup the external conditions for true simulation with just the AS7 simulator - that's why I suggested ICE.

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

ok I was just looking at the problem code in #10

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

IS there any way possible for a xmega to set a PORT register in one clock?

 

 

I only have 1 nop .

 

 

; bit 7:    jump, eor
rxLoop:
    eor     x3, shift   ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others
    in      x1, USBIN   ;1 [1] <-- sample bit 0
    st      y+, x3      ;2 [3] store data
    ser     x3          ;1 [4]
    nop                 ;1 [5]
    eor     x2, x1      ;1 [6]
    bst     x2, USBMINUS;1 [7]
    bld     shift, 0    ;1 [8]
    in      x2, USBIN   ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed)
    andi    x2, USBMASK ;1 [10]
    breq    se0         ;1 [11] SE0 check for bit 1
    andi    shift, 0xf9 ;1 [12]

 

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

I think sbi only takes 1 cycle on the xmega.

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

I kept reading 1 on tiny, 2 on xmega on the forums. but stuffing sbi in the NOP causes it to fail.

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

Is the moment when the port changes important? Maybe placing the instruction before the "ser x3" would help, it's possible the instruction takes one cycle but the effect is not immediate due to some kind of pipeline.

Or maybe it really takes 2 cycles. Try on the Atmel Studio simulator, it usually has correct timings.

Last Edited: Wed. Jul 18, 2018 - 11:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No it can flip later, that's ok.  Best I can tell it is taking 2. Any way of preparing data or a register and using load immediate? Does not matter what is loaded as long as it is always loaded the same way. I can check any condition.

Last Edited: Thu. Jul 19, 2018 - 01:24 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:
IS there any way possible for a xmega to set a PORT register in one clock?
The Xmega has the ability to map four PORTx_OUT/PORTx_DIR/PORTx_IN registers to low IO space using "VPORT" and then you can use things like SBI/CBI on them but if you are talking about the "USBIN" mentioned in the code above then if that is a real USB register in the USB peripheral then, no almost all the other SFRs in the XMega are not mappable to low addresses to benefit from IN/OUT/SBI/CBI/etc

 

Or is "USBIN" just a synonym for some PORTx_IN? If so, then yes, look to map "x" to one of the four VPORT positions.

 

PS this post is in the tiny/mega forum. Are you saying it's actually about Xmega?

Last Edited: Thu. Jul 19, 2018 - 09:02 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry I meant atmega.

 

Was looking to set any register or variable in one clock. Does not have to be a PORT register. 

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

The only 1 clk IO instruction on "normal" AVR's are the IN and OUT instructions.

 

 

Add:

Forget this I did not read the "not"

Last Edited: Thu. Jul 19, 2018 - 04:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok so 

 

out $18,r1 ;

 

Should write a zero to PORTB  (PORTB  =0) based on the memory map since r1 is initially 0.  

 

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

Any register?  [and you gave the OUT example with a whole register >>and<< I/O address.

 

You have already been given the T register suggestion.  There are many op codes on AVR8 that will affect e.g. the low bit of a register in one cycle. [apparently the xmega mention was a red herring but we still don't know the purpose of the snip hunt]  CLR COM INC DEC ADDI ...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

 I'm tiring to set a flag in my USB code and it's extremely time sensitive

If its that super-critical, why not just pick a register for that purpose & be done with it?  The AVR has plenty available.  

When in the dark remember-the future looks brighter than ever.

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

That's a good point and I can always CLR or INC it in one clock. A bit more elegant than using a predefined register like PORTx anyways. Is there  away in the AVRstudio 6 debugger to see what has not been used? I'm thinking r31 is a safe bet want want to be sure.

Last Edited: Fri. Jul 20, 2018 - 12:53 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is there a way in GCC to allocate a variable to a fixed location in a register? You can certainly do it in other compilers.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

Do you mean GCC 'C', or the GNU Assembler ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

GCC does have a mechanism to reserve registers. The command line option -ffixed-13 would tell it not to generate code that contains R13 (0..31 obviously available) and in C the syntax:

register uint8_t foo asm("r13");

could locate a variable called "foo" into register 13.

 

However, the libc that comes with the compiler (strcpy, printf, memset, etc) comes pre-compiled and on the day that it was built no one told it to avoid R13. So if this a program in which any libc function is invoked (either from Asm or C or both) there's a possibility the reserved register may be clobbered. It may be enough simply to study the LSS to verify that nothing but the author's code touches R13 but if the program is later built with next month's issue of GCC+libc it may be clobbered because of usage in the next issue.

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

r31 is for sure bad because it's a part of Z.

In general with GCC you can use r2-r10, but it depends of the libs you use.

read the doc. just to declare a variable to a register don't tell compiler not to use it for other things, but there is a way to tell not to use that register.

And remember that all instructions with a constant don't work with the low registers!

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

clawson wrote:
So if this a program in which any libc function is invoked (either from Asm or C or both) there's a possibility the reserved register may be clobbered.
Not only then. There are more sources for pre-compiled code than libc (libgcc, libm). You don't even need to call a function explicitly, simple usage of operators can pull in pre-compiled code from a library. 

Stefan Ernst

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

Aside from choosing the right register how can I use this?

in my h i did

register unsigned char CT_BUSY_FLAG asm("r3");

 

 

in asm I did

 .extern CT_BUSY_FLAG

inc CT_BUSY_FLAG

 

but I get constant value required.

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

I fear you misunderstand. The point is with -ffixed-3 and the:

register unsigned char CT_BUSY_FLAG asm("r3");

in your C you have reserved register R3 so in the Asm you don't need an "extern" reference to it. It is simply R3. So if you do something like:

ldir R24, 0x55
mov r3, r24

then back over in the C when you access the "CT_BUSY_FLAG" you should find it now contains 0x55.

 

Two things:

 

1) I wouldn't use all upper case for C symbols. Most programmers on planet earth expect any symbol in upper case to be a pre-pro macro so it is very confusing!

 

2) I suppose in the Asm you could use define or similar to give R3 a more obvious name. Like:

#define CT_busy_flag r3

 ...
 
   ldi r24, 0x55
   mov CT_busy_flag, r24

and there I go breaking the rule in #1. For consistency I think you'd want to use the exact same symbol name in C and Asm but because of (1) I'm suggesting it's not all upper case so now in the Asm you have a #define that breaks rule (1) because it's a macro with a name that is no longer all upper case. Guess you can't win!

 

(perhaps avr-as has another way to assign a name to a register that does not involve the pre-pro or #define?)

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

Sure that all makes sense. Curios though what the difference here? Or I should say, why is mov needed?

 

  ldi r24, 0x55
   mov CT_busy_flag, r24
  ldi CT_busy_flag,0x55
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:
Or I should say, why is mov needed?
LDI works only with R16-R31.

Stefan Ernst

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

clawson wrote:
Most programmers on planet earth expect any symbol in upper case to be a pre-pro macro

Does that really apply globally, or is it really just a 'C' thing?

 

If it's just a 'C' thing, then that gets rid of (or, at least, reduces) your "problem" of breaking rule #1 (which is, then, just a 'C' rule) when writing in Assembler ...

 

cool

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
(perhaps avr-as has another way to assign a name to a register that does not involve the pre-pro or #define?)

.equiv CT_busy_flag, 3

(no, the '3' instead of 'r3' is not a typo)

 

Stefan Ernst

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

the answer to #35 is given but I will add:

 

in1 clk on low registers:

low registers can easy be cleared (eor r2,r2, or sub r2,r2) and that will work regardless of the value of r2 before.

 

but there is no simple way to put a value even just non zero into a low register (without knowing something else).

if you know it's zero just inc r2. (or com , neg ) (if there can come more than 0 this can fail)

if you know T==1 bst r2,n

if C(carry)==1 subc r2,r2  (this sounds odd to use but often that is actually the reason why you are setting the flag.)

have an other register that for sure are none zero mov r2,rn

 

If speed really matter I would go for the last and the most "clean", I have had ASM programs where r14=0xff and R15=0x00 all the time.

 

Ok there are also the ugly version:

in r2,$nn

(it should be possible to find something that is non zero, like if there is a output on a port that PORT register is non zero)