tiny AVR timer interrupt bug?

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

After a day of debugging a timer interrupt driven UART I'm writing, I've come to the conclusion that I've found a hardware bug with OCF0A.  According to the datasheet, "OCF0A is cleared by hardware when executing the corresponding interrupt handling
vector" (section 11.9.8, t85 datasheet).  I wrote a test program that sets up the timer, then loops waiting for OCF0A.  When set, it toggles an LED and enables interrupts.  The timer interrupt runs, which should also clear OCF0A.  On the next compare/match, the ISR should run again, clearing OCF0A at the same time.  That means the LED should not toggle again, but in many cases it does.  Here's the test program:

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

const int LED = 4;

// empty interrupt blink rate = 1/4 of ISR
//EMPTY_INTERRUPT(TIM0_COMPA_vect);
ISR(TIM0_COMPA_vect) {}

#ifndef TIMSK
#define TIMSK TIMSK0
#define TIFR TIFR0
#endif

int main()
{
    TCCR0B = 1<<CS02 | 1<<CS00;         // start timer0 /1024 prescaler
    DDRB |= 1<<LED;
    TIMSK = 1<<OCIE0A;                  // enable T0COMPA ISR
    while (1) {
        if (TIFR & 1<<OCF0A) {
            asm (
            "sei        \n"
            //"nop        \n"
            );
            PINB |= 1<<LED;
        }
    }
}

 

The behavior changes depending on whether EMPTY_INTERRUPT() or ISR() is used, and depending on the nop instruction after sei.  Testing on a t85 running on the internal 8Mhz RC oscillator here's my results:

With ISR(TIM0..), sei + nop, it works as expected: OCF0A gets set once, the LED turns on and stays on.  The ISR continues to run after every compare match, and main() never sees OCF0A set again.

When I comment out the nop (so the instruction after sei is sbi), the LED blinks at ~15.5Hz, which corresponds to toggling at the compare/match frequency.

When I use EMPTY_INTERRUPT, the blink rate is 4x slower, about 3.9Hz, and when I put the nop back in after sei, the LED blinks at ~5.2Hz.

 

I've also tested a t13 and get the same results but with a 10-20% faster blink rate due to the nominal 9.6Mhz clock rate.

 

I tried searching for any previous references to this problem, but all I could find are examples where someone had a bug in their timer code.

 

Is anyone with a t85 or t13 willing to run my test program and see if you get the same results?

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

What does the underlying .asm output look like?

 

My immediate thought was if this goes some way to explain things...

 

Quote:

When using the SEI instruction to enable interrupts, the instruction following SEI will be executed before any pending interrupts, as shown in this example.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "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."

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

What is the output compare reg set to? 

Try setting it non-zero.

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

When the AVR returns from interrupt, it will execute one instruction (from main) before it can respond to the interrupt again, so it is possible for main to catch the flag while set before the ISR() can clear it.

The timing has to be just right, but in a tight loop like that, it will happen often.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

ki0bk wrote:

When the AVR returns from interrupt, it will execute one instruction (from main) before it can respond to the interrupt again, so it is possible for main to catch the flag while set before the ISR() can clear it.

Don't think so.  The ISR doesn't clear it.  The hardware does.  And it does so before the ISR runs.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Although I think Brian is on the right track.  There is a possibility for a race condition whereby a reti instruction at the end of an ISR, which is subject to the same 'minimum-1-opcode' rule to which sei is subject (in truth, the rule applies to any 0->1 transition of the 'I' bit in SREG), occurs at the same time as a new interrupt condition is flagged by its interrupt flag bit.  If the '1-opcode' happens to be the 'in' instruction which fetches TIFR for testing, then it will observe the interrupt flag as '1' before the hardware can clear it.

 

However, with the OP's code, where there are 256 timer ticks (and 262,144 cpu cycles) between interrupts, I don't see how this race condition could ever occur.  On first blush it seems to me as though the interrupt flag should get caught by main exactly once, turning on the LED forever.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Well that while() loop will run that compare about every 3 to 5 clock cycles when false (which is most of the time) so you have about 1 in 3 or 5 chance of catching the flag when set.

I may be off by a cycle or two I did not count the loop cycles, it does not really matter,  it will catch it more then you think....  as evidenced by the blinking LED.

I don't think it is a bug, h/w or otherwise, its just the way it works and consistent with the DS description.

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

Last Edited: Mon. May 18, 2020 - 05:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian Fairchild wrote:

What does the underlying .asm output look like?

 

My immediate thought was if this goes some way to explain things...

 

Quote:

When using the SEI instruction to enable interrupts, the instruction following SEI will be executed before any pending interrupts, as shown in this example.

 

objdump -d is the first thing I check when I have a problem with C/C++ code.    Regarding the sei instruction, I thought of that, which is why I tried with and without the nop, even though, as I mentioned it's just an sbi instruction after the sei.

 

00000030 <main>:
  30:   85 e0           ldi     r24, 0x05       ; 5
  32:   83 bf           out     0x33, r24       ; 51
  34:   bc 9a           sbi     0x17, 4 ; 23
  36:   80 e1           ldi     r24, 0x10       ; 16
  38:   89 bf           out     0x39, r24       ; 57
  3a:   08 b6           in      r0, 0x38        ; 56
  3c:   04 fe           sbrs    r0, 4
  3e:   fd cf           rjmp    .-6             ; 0x3a <main+0xa>
  40:   78 94           sei
  42:   b4 9a           sbi     0x16, 4 ; 22
  44:   fa cf           rjmp    .-12            ; 0x3a <main+0xa>

00000046 <__vector_10>:
  46:   18 95           reti

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

ki0bk wrote:

Well that while() loop will run that compare about every 3 to 5 clock cycles when false (which is most of the time) so you have about 1 in 5 chance of catching the flag when set.

Unless interrupts are pre-empted by the '1-opcode' rule following a change in the I bit from 0 to 1, my understanding was that hardware is supposed to  handle a pending interrupt before the flag bit is available to software via an 'in' (or any other) instruction.  After the first catch (by which, in the OP's code, interrupts are enabled), software should never again see OCF0A set.

 

I can confirm the behaviour in a t45, but also in an m328p.  So it seems that my understanding is incomplete.  I shall have to revisit my own codebase for code which relies on this incorrect understanding.

 

I don't think this is a bug so much as it is a shortcoming of documentation, albeit (possibly) across the entire line of AVR.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

So I think that shows a 1 in 3 chance of the interrupt flag being set at just the right time....

in          <-- this happens upon return from interrupt with flag set by timer while in ISR()

sbrs

rjmp (back to the in)

LED blinks <---- upon return from next interrupt

 

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

Last Edited: Mon. May 18, 2020 - 05:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:

Although I think Brian is on the right track.  There is a possibility for a race condition whereby a reti instruction at the end of an ISR, which is subject to the same 'minimum-1-opcode' rule to which sei is subject (in truth, the rule applies to any 0->1 transition of the 'I' bit in SREG), occurs at the same time as a new interrupt condition is flagged by its interrupt flag bit.  If the '1-opcode' happens to be the 'in' instruction which fetches TIFR for testing, then it will observe the interrupt flag as '1' before the hardware can clear it.

 

However, with the OP's code, where there are 256 timer ticks (and 262,144 cpu cycles) between interrupts, I don't see how this race condition could ever occur.  On first blush it seems to me as though the interrupt flag should get caught by main exactly once, turning on the LED forever.

 

Agreed.   However the length of the interrupt has an impact on the behavior.  EMPTY_INTERRUPT() is just a reti, while ISR() with no instructions has the minimal push/pop r0, r1, & SREG.

Another thing I noticed is if i enable interrupts before the loop, main never sees OCF0A set; it always gets cleared before the interrupt returns.  What I'm seeing only happens when the ISR is delayed due to the global interrupt flag being cleared when OCF0A triggers the first time.

 

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

Regarding the nop, I'd guess that it serves to ensure the correct alignment for the 'in' instruction which samples OCF0A, such that it never sees the flag set.  Any other combination of instructions which results in the same alignment would suffice.

 

For example, if you move the nop out of the while loop and place it just before the while loop, you effectively offset the 'in' which samples OCF0A by one cycle, and the behaviour disappears.

 

The sampling loop looks like this:

    while (1) {
        if (TIFR & 1<<OCF0A) {
  52:	08 b6       	in	r0, 0x38	; 56
  54:	04 fe       	sbrs	r0, 4
  56:	fd cf       	rjmp	.-6      	; 0x52 <main+0xe>

So it is 4 cycles long, which fits nicely into the timer period an integral number of times.  That is, the 'in' will occur at the same point in the timer period every time.  By putting a nop before the while loop, you change the 'in' sample point, thereby denying it any chance to see OCF0A as set.

 

If I add 1, 2, or 3 nops before the while loop, I similarly deny the 'in'.  But adding 4 (or any multiple of 4) nops, I see the same behaviour you reported.

 

I'd chalk this up to your (and my) misunderstanding of how interrupt flag bits are available to software.

 

ralphd wrote:
However the length of the interrupt has an impact on the behavior
The length of the interrupt will alter the number of cycles available to the main thread during each timer period.  Try adding a single nop to the ISR and see what happens.  Hint:  you'll see the same behaviour as with an empty interrupt.  In essence, you are seeing a 'beating' of the two cycle-counts:  that of the cycles consumed by the sampling loop in main, and that of the cycles per timer period >>available<< to the loop in main.  The factor-of-four difference you see is the result of the 1:4 ratio of a single nop to the length of the sampling loop.  The 'beat' frequency falls out of that.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

ralphd wrote:
Another thing I noticed is if i enable interrupts before the loop, main never sees OCF0A set; it always gets cleared before the interrupt returns
This 'works' only because you're moving the placement of the sampling 'in' to where it will only ever see a '0'.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

joeymorin wrote:

ki0bk wrote:

Well that while() loop will run that compare about every 3 to 5 clock cycles when false (which is most of the time) so you have about 1 in 5 chance of catching the flag when set.

Unless interrupts are pre-empted by the '1-opcode' rule following a change in the I bit from 0 to 1, my understanding was that hardware is supposed to  handle a pending interrupt before the flag bit is available to software via an 'in' (or any other) instruction.  After the first catch (by which, in the OP's code, interrupts are enabled), software should never again see OCF0A set.

 

I can confirm the behaviour in a t45, but also in an m328p.  So it seems that my understanding is incomplete.  I shall have to revisit my own codebase for code which relies on this incorrect understanding.

 

I don't think this is a bug so much as it is a shortcoming of documentation, albeit (possibly) across the entire line of AVR.

 

Thanks for verifying, and for testing on the m328p.  I have a few other AVRs that I could test as well, but for now I'd just like to figure out WTF is going on.

Here's the version that doesn't blink (sei before the loop in main).

00000030 <main>:
  30:   85 e0           ldi     r24, 0x05       ; 5
  32:   83 bf           out     0x33, r24       ; 51
  34:   bc 9a           sbi     0x17, 4 ; 23
  36:   80 e1           ldi     r24, 0x10       ; 16
  38:   89 bf           out     0x39, r24       ; 57
  3a:   78 94           sei
  3c:   08 b6           in      r0, 0x38        ; 56
  3e:   04 fe           sbrs    r0, 4
  40:   fd cf           rjmp    .-6             ; 0x3c <main+0xc>
  42:   b4 9a           sbi     0x16, 4 ; 22
  44:   fb cf           rjmp    .-10            ; 0x3c <main+0xc>

00000046 <__vector_10>:
  46:   1f 92           push    r1
  48:   0f 92           push    r0
  4a:   0f b6           in      r0, 0x3f        ; 63
  4c:   0f 92           push    r0
  4e:   11 24           eor     r1, r1
  50:   0f 90           pop     r0
  52:   0f be           out     0x3f, r0        ; 63
  54:   0f 90           pop     r0
  56:   1f 90           pop     r1
  58:   18 95           reti

What I'm beginning to suspect is that the datasheet is wrong when it says, "OCF0A is cleared by hardware when executing the corresponding interrupt".   If OCF0A is cleared several cycles AFTER the interrupt runs, that might explain what is going on.

Changing the above code to use EMPTY_INTERRUPT results in the LED flashing at ~15.5Hz.

 

Starting from the rjmp __vector10, that's two cycles, then 4 cycles for the reti.  The loop to check TIFR is 4 cycles (in, sbrs, rjmp).  Since the loop always catches OCF0A before it is cleared, the latency has to be > 10 cycles. 

The "long" version of the ISR takes 15 cycles before the reti, and OCF0A is always cleared in the main loop.  That puts the latency for clearing OCF0A somewhere between 10 and 25 cycles.

 

 

 

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

ralphd wrote:
but for now I'd just like to figure out WTF is going on.
What appears to be going on is that an interrupt is executing on the cycle following the cycle in which its flag is set*.  In your test code, cycles just happened to align in such a manner that the 'in' which sampled OCF0A always did so on the very cycle during which OCF0A was set, thereby eliciting your observed behaviour.

 

* admittedly, this is not what I have always assumed.  As I mentioned, I had always assumed that an interrupt would occur in the >>same<< cycles as that during which its interrupt flag was set.  I can find no documentation to support this assumption, however.  In hindsight, it does make sense that the servicing of the interrupt couldn't happen until after the event which triggers the flag.  I now believe the behaviour you and I are seeing is entirely expected.

 

ralphd wrote:
What I'm beginning to suspect is that the datasheet is wrong when it says, "OCF0A is cleared by hardware when executing the corresponding interrupt".
Not wrong so much as incomplete.  The flag >>is<< cleared, and by hardware, when the interrupt is serviced.  Although it's not possible to confirm exactly when, I expect it occurs in the same cycle(s) the initial vector occurs, i.e. when the PC is loaded with the address of the vector table entry associated with that interrupt source.

 

The omission is that there is a a risk of a race condition, whereby the interrupt flag associated with any given interrupt is available to software for the one instruction/cycle before the instruction/cycle which marks the beginning of the hardware servicing of that interrupt.  This is the race condition which your code 'accidentally' stumbled upon.

 

EDIT: typo

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Mon. May 18, 2020 - 06:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:

Regarding the nop, I'd guess that it serves to ensure the correct alignment for the 'in' instruction which samples OCF0A, such that it never sees the flag set.  Any other combination of instructions which results in the same alignment would suffice.

 

For example, if you move the nop out of the while loop and place it just before the while loop, you effectively offset the 'in' which samples OCF0A by one cycle, and the behaviour disappears.

 

The sampling loop looks like this:

    while (1) {
        if (TIFR & 1<<OCF0A) {
  52:	08 b6       	in	r0, 0x38	; 56
  54:	04 fe       	sbrs	r0, 4
  56:	fd cf       	rjmp	.-6      	; 0x52 <main+0xe>

So it is 4 cycles long, which fits nicely into the timer period an integral number of times.  That is, the 'in' will occur at the same point in the timer period every time.  By putting a nop before the while loop, you change the 'in' sample point, thereby denying it any chance to see OCF0A as set.

 

If I add 1, 2, or 3 nops before the while loop, I similarly deny the 'in'.  But adding 4 (or any multiple of 4) nops, I see the same behaviour you reported.

 

I'd chalk this up to your (and my) misunderstanding of how interrupt flag bits are available to software.

 

ralphd wrote:
However the length of the interrupt has an impact on the behavior
The length of the interrupt will alter the number of cycles available to the main thread during each timer period.  Try adding a single nop to the ISR and see what happens.  Hint:  you'll see the same behaviour as with an empty interrupt.  In essence, you are seeing a 'beating' of the two cycle-counts:  that of the cycles consumed by the sampling loop in main, and that of the cycles per timer period >>available<< to the loop in main.  The factor-of-four difference you see is the result of the 1:4 ratio of a single nop to the length of the sampling loop.  The 'beat' frequency falls out of that.

 

This sounds more plausible than my theory regarding the long latency to clear.  The datasheet says the following about interrupt response time:

" If an interrupt occurs during execution of a multi-cycle instruction, this instruction is completed before the interrupt is served"

It neglects to say the same thing about a single-cycle instruction, which I think explains everything when one considers your observation about the sampling of TIFR.

From my previous analysis of timer interrupts, I determined that the compare/match happens on a clock transition.  So OCF0A is getting set on the transition from the 2nd cycle of the "rjmp" to the 1st (and only) cycle of the 'in"instruction.  That doesn't immediately trigger the interrupt, as the next instruction will get executed BEFORE the interrupt runs.  Therefore the 'in' sees OCF0A set, the interrupt runs, and returns to the sbrs instruction.

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

ralphd wrote:
f OCF0A is cleared several cycles AFTER the interrupt runs, that might explain what is going on.
No, and tests for the flag within the ISR can confirm this.
ralphd wrote:

Changing the above code to use EMPTY_INTERRUPT results in the LED flashing at ~15.5Hz.

See my post #12 re 'beating'.

00000046 <__vector_10>:
  46:   1f 92           push    r1
  48:   0f 92           push    r0
  4a:   0f b6           in      r0, 0x3f        ; 63
  4c:   0f 92           push    r0
  4e:   11 24           eor     r1, r1
  50:   0f 90           pop     r0
  52:   0f be           out     0x3f, r0        ; 63
  54:   0f 90           pop     r0
  56:   1f 90           pop     r1
  58:   18 95           reti

That's 19 cycles, plus 3 for loading PC with the vector table entry, plus 2 for the rjmp at the vector, is 24 cycles.  You'll note that this is a multiple of 4, so it doesn't disrupt the relationship between the 4-cycle sampling loop in main and the multiple-of-4 timer period of 262,144 cycles.

 

The empty interrupt consists only of the 3-cycle PC load, the 2-cycle rjmp, and 4-cycle reti, for a total of 9 cycles.  Not a multiple of 4, so you'll find that the alignment 'drifts' by one cycle every timer period.  Thus you see the 1/4 speed behaviour.

 

EDIT:  typo

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Mon. May 18, 2020 - 06:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:

ralphd wrote:
but for now I'd just like to figure out WTF is going on.
What appears to be going on is that an interrupt is executing on the cycle following the cycle in which its flag is set*.  In your test code, cycles just happened to align in such a manner that the 'in' which sampled OCF0A always did so on the very cycle during which OCF0A was set, thereby eliciting your observed behaviour.

 

Yes.  See my last post which I wrote while you were posting yours.

 

Despite the pain in the ass this was to figure out, I'll have to agree with it being a documentation problem.  I'd use a stronger word than "shortcoming" though...

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

ralphd wrote:

It neglects to say the same thing about a single-cycle instruction, which I think explains everything when one considers your observation about the sampling of TIFR.

The only new piece of information required to explain the observed behaviour is a 1-cycle (well, instruction, really, since you can't have a partial instruction) window between the time an interrupt's flag is set and the time hardware services that interrupt, during which the set state of the flag is visible to software.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

ralphd wrote:
shortcoming
Agreed.
ralphd wrote:
See my last post which I wrote while you were posting yours
Happens a lot around here ;-)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

why don't you test it in the simulator ?

 

The model is based on the same logic that compile into the chip.