4

I have a loop with several conditional branches in it. The loop is supposed to always run the same amount of cycles no matter what branches are taken.

To achieve this I filled the shorter branches with NOPs (using asm("nop")) until they were of equal length to the longer branches. This achieved what I wanted.

Now I have a lot more branches and want to automatically balance the branches. I am compiling with avr-gcc.

Is there a way to do this?

As requested: I am using an ATmega 1284p and avr-gcc for my implementation.

tuzojil
  • 43
  • 4
  • 1
    What are you trying to achieve by doing this? – fuz Jun 29 '16 at 09:41
  • The loop polls a pin of a mico controller. To ensure that the pin is polled in regular intervals the runtime of the loop should be the same no matter what branches are taken. – tuzojil Jun 29 '16 at 09:44
  • I don't think the compiler can do what you want. It might be a better idea to use an interrupt-driven approach or to rewrite your code so it is branch-free. If you show us your code, I can try to help you rewrite it so that it doesn't branch. – fuz Jun 29 '16 at 09:46
  • Is there not always a branch when I use if ? – tuzojil Jun 29 '16 at 09:50
  • Yes. But often it is possible to get rid of most if statements using bit-fiddling and other tricks. This depends on your code though. – fuz Jun 29 '16 at 09:51
  • That sounds like it would make the code hard to understand. Which I can not afford in this project. – tuzojil Jun 29 '16 at 09:54
  • If this is about writing code that is easy to understand, consider using an interrupt-driven approach for the polling. Set a timer to interrupt you at a fixed rate and do the polling in the interrupt handler. Adding random nops is both complicated and fragile as other compilers/future gcc versions may optimize your code differently causing the nops to be in all the wrong places. – fuz Jun 29 '16 at 09:55
  • 2
    Also, different instructions take different time to execute so you need to account for that when adding your nops. – fuz Jun 29 '16 at 09:56
  • Ok I will try that – tuzojil Jun 29 '16 at 09:59
  • 4
    no reason to assume with each compiler or each version of gcc that you will get the same compiled results, so you will be hand tuning this on a regular basis if you are trying to use number of instructions and time per instruction to make each path the same length. easier to just use a timer at the end to keep them the same number of clocks or a timer based interrupt. So long as no compiled path is too long that will give you the best results. dont understand why each path would need to be the same length, my guess is you are making something harder than it needs to be. – old_timer Jun 29 '16 at 10:13
  • If this would be security measure (to make harder brute forcing secret by measuring response time, so your code response is stable for both valid and invalid key bits), you should not use plain `nop` to sync the branch timing. That would still affect EMF emitted by CPU and the timing of `nop` is something I don't believe into much. It's better to write the code without if-s (something like ternary operator in C/Java), so it's basically doing all the same *ops* always, just some `MOVcc` will make the resulting *value* different. Showing the code would make it easier to decide if that's possible – Ped7g Jun 29 '16 at 10:26
  • But you shouldn't be so much afraid of code written in that way - being less understandable. Actually for human following all possible branch paths is not easy either, if a bit more complex ternary operator line can save you whole big branch, it's often the opposite, and easier to read (especially if you get used to it). – Ped7g Jun 29 '16 at 10:30
  • 2
    @Ped7g [Ternary operator is just an handy if](https://godbolt.org/g/Azqam6). Also the OP is working on AVR (though not tagged as it yet). – Margaret Bloom Jun 29 '16 at 11:06
  • if the code inside the if-block is only doing calculations, you can simply replace them by "abusing" the int values of boolean expression, to get expressions with equal run-time in either case. `if (a>b) c+=8;` e.g. could be replaced by 'c+=(a>b)*8' , or `if (a>b) c=3; else c=7; ` would be `c=7-4*(a>b)` or `c=3+4*(a – Tommylee2k Jul 01 '16 at 13:39
  • The interrupt takes 5 cycles to jump into the service routine has to save several registes and then return which is again 5 cycles. Unfortunately that is too much – tuzojil Jul 05 '16 at 07:40

1 Answers1

3

Well you didn't specify whether you are coding in asm or in c, since you use branches but you call "asm()"... If using C, you can call millis() at the beginning of your loop, and call it at the end too. You have to calculate the maximum duration of the loop. So subtract the two millis values and compare the difference with the maximum duration of the loop. Yea lilbit confusing, here the code:

#define MAX_DURATION 1000 //let's say 1 second, but you should calculate it
while(yourcondition) {
  temp1 = millis();
  //do your branches

  temp2 = millis();
  delay(MAX_DURATION-(temp2-temp1));
}

While if you are coding in asm, you have at first to disable interrupts in order not to have longer loops. Then you can setup a 16bit timer, if your processor has any, with the greatest prescaler and then checking the timer value instead of millis and make a delay function, easily done as:

delay: ;put the millisecond to wait in r17:r16
  ldi r18, 200
  ldi r19, 26 ;200*26* (3 cicles each little loop) = 1 millisecond of delay with a 16MHz oscillator
  mov r20, r17
  delay_loop:
       dec r19
       brne delay_loop
      ldi r19, 26
      dec r18
      brne delay_loop
     ldi r18, 200
     dec r17
     brne delay_loop
    mov r17, r20
    dec r16
    brne delay_loop
  ret

Hoping that your instrunction set is similar to mine. Next time specify what code are you using and What processor are you targeting

Frazzo
  • 371
  • 2
  • 10