|
|
View previous topic :: View next topic |
Author |
Message |
Futterama
Joined: 17 Oct 2005 Posts: 98
|
CCS handling of int16 in if statement |
Posted: Sun Jul 17, 2016 12:48 pm |
|
|
Hi forum,
I'm building a time critical application in a PIC10F222, where each loop of code needs to take an equal amount of time/instructions to execute. I found a pretty good way of doing this, but I face a problem when CCS is evaluating a line like this:
Code: | if(value1 == value2) |
when values are int16 variables. I'm looking at the ASM output in the list file, and I know each ASM line equals one instruction, and I know what the GOTO statement does. It seems to me that the ASM code evaluates one byte first, maybe the MSB, and if these don't match, then there is no reason to check the LSB, so the code makes a GOTO. This is causing me problems, because the execution time of this statement then relies on the ever changing content of the int16 values.
The problem does not exist when evaluating int8, but I need the range of int16.
Is there a clever way to compare two int16 where the execution time remains the same regardless of variable content? It would be OK if the evaluation took a bit more code, and hence time, because my application is only sensitive to a change in looping time, not actual loop time.
Another solution I have been working on, is letting a timer run while my code executes, and then in the end of the code, check the timer value this way:
It worked OK in the beginning, but there are still fluctuations in execution time (because the statement above in itself is taking a few instructions to execute and is thereby not instantaneous when the timer reaches the value of 1) and that is apparently enough to disturb my application in a negative way.
Last resort is to split the most time accuracy critical tasks between several PIC10F222 and just use a pin high/low to communicate between them (reading a pin status is as fast as it gets). |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9228 Location: Greensville,Ontario
|
|
Posted: Sun Jul 17, 2016 4:31 pm |
|
|
OK, I'm SURE Mr. T posted a reply earlier(....make8()...) but it doesn't show up on my PC !
Did someone let the gremlins out ??
Jay |
|
|
guy
Joined: 21 Oct 2005 Posts: 297
|
|
Posted: Sun Jul 17, 2016 5:59 pm |
|
|
One approach would be to get your hands dirty with ASM and rewrite the IF instruction so it takes up a constant amount of time.
Another approach is to use the timer together with an interrupt routine. This would improve the timing jitter but not solve it completely (plus it will take up more CPU time).
And a third possible solution is to see if your timing-critical mission could be accomplished by one of the peripherals of a more capable PIC. Higher clock rate will also improve the jitter problem (a 2-instruction jitter with a 64MHz internal clock is merely 125ns !) |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jul 17, 2016 7:05 pm |
|
|
Quote: | Is there a clever way to compare two int16 where the execution
time remains the same regardless of variable content? |
If you XOR the 16-bit values, the compiler will first XOR the LSB's then
XOR the MSB's and then OR the results of each test. Use this line:
Code: | if(value1 ^ value2) |
If the two values are not equal, the body of the if() statement will be
executed. That's because if you XOR two equal bit values, you get 0.
If they are not equal, you get one or more of the bits set to 1, and
that's non-zero, which is seen as "True" by the compiler.
It gives this ASM code:
Code: | .................... if(value1 ^ value2)
0019: MOVF 16,W
001A: XORWF 18,W // XOR LSB's
001B: MOVWF 10
001C: MOVF 17,W
001D: XORWF 19,W // XOR MSB's
001E: MOVWF 13
001F: MOVF 10,W
0020: IORWF 13,W // OR together the two previous results
0021: BTFSC 03.2 // Branch if the result is non-zero
0022: GOTO 026 // Jump if zero (both values are equal)
|
Partial test program:
Code: |
void main()
{
int16 value1 = 0x1235;
int16 value2 = 0x1234;
int8 equal;
if(value1 ^ value2)
equal = FALSE;
else
equal = TRUE;
}
|
If you want to make it operate with positive logic, which is more
reasonable, you can negate the XOR result:
Code: |
if(!(value1 ^ value2))
equal = TRUE;
else
equal = FALSE;
if(equal == TRUE)
printf("Equal\r");
else
printf("Not equal\r"); |
|
|
|
guy
Joined: 21 Oct 2005 Posts: 297
|
|
Posted: Mon Jul 18, 2016 2:10 am |
|
|
PCM programmer - Like! |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Mon Jul 18, 2016 8:47 am |
|
|
There are potentially big problems with ths sort of approach. There is no guarantee that code produced by later versions of the compiler, or even the same compiler with different options or with altered/enlarged code, will work the same, so you are going to have to at least check every time you do a compilation.
High level code, or even medium level code such as C, is NOT guaranteed to be implemented in any particular way, and is not intended and should not be expected to produce machine code that has any particular timing characteristic. In short, using high level code to produce strictly controlled execution times is not a good thing. In fact it's very bad programming practice. It might work for a one-off, experimental hobby project, but it's not going to fly for professional use, and such techniques certainly will not be allowed to fly, as in be acceptable for avionics use, at all.
If you really want repeatable, accurate timing, then use hardware. Software is not the tool for that sort of job.
What is the application? Why do you need precise, repeatable timing? Why are you not using hardware? |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9228 Location: Greensville,Ontario
|
|
Posted: Mon Jul 18, 2016 9:14 am |
|
|
Man , I have to ask WHY the 10F222 ? 512 word of mem ain't much UNLESS you code in ASM.
As pointed out C doesn't always compile the same ASM twice.
Jay |
|
|
Futterama
Joined: 17 Oct 2005 Posts: 98
|
|
Posted: Mon Jul 18, 2016 11:03 am |
|
|
Thanks for all the answers, especially to PCM programmer, I'll try that tonight.
RF_Developer: End application is a special hobby RC related device that takes a servo signal, and generates pulses if the signal is long enough (like when the switch is flipped on the transmitter) and the pulse frequency depends on the signal length from the RC receiver. I got this working very reliably, because I only need to read the servo signal length once. But the challenge is that I need to generate an accurate signal for an attached servo (not the same signal as the one from the RC receiver), and if the loop time in my code changes, so does the output signal to the servo and hence the servo will be moving when it shouldn't. I don't have the hardware available in the PIC10F222.
temtronic: I'm using the PIC10F222 basically because I have more than 100 of them in stock from another project, and for this limited code project, it was suited just fine and the pin count is just perfect. Also, I find it very educating to have limited code and memory space, my code gets more and more optimized, which it wouldn't have been if I had a lot more RAM and ROM available. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9228 Location: Greensville,Ontario
|
|
Posted: Mon Jul 18, 2016 11:17 am |
|
|
Yup.. that makes sense ! Use what you have in inventory and get educated at the same time. BTDT.
Jay |
|
|
Futterama
Joined: 17 Oct 2005 Posts: 98
|
|
Posted: Mon Jul 18, 2016 11:33 am |
|
|
PCM programmer wrote: | Quote: | Is there a clever way to compare two int16 where the execution
time remains the same regardless of variable content? |
If you XOR the 16-bit values, the compiler will first XOR the LSB's then
XOR the MSB's and then OR the results of each test. Use this line:
Code: | if(value1 ^ value2) |
If the two values are not equal, the body of the if() statement will be
executed. That's because if you XOR two equal bit values, you get 0.
If they are not equal, you get one or more of the bits set to 1, and
that's non-zero, which is seen as "True" by the compiler.
It gives this ASM code:
Code: | .................... if(value1 ^ value2)
0019: MOVF 16,W
001A: XORWF 18,W // XOR LSB's
001B: MOVWF 10
001C: MOVF 17,W
001D: XORWF 19,W // XOR MSB's
001E: MOVWF 13
001F: MOVF 10,W
0020: IORWF 13,W // OR together the two previous results
0021: BTFSC 03.2 // Branch if the result is non-zero
0022: GOTO 026 // Jump if zero (both values are equal)
|
Partial test program:
Code: |
void main()
{
int16 value1 = 0x1235;
int16 value2 = 0x1234;
int8 equal;
if(value1 ^ value2)
equal = FALSE;
else
equal = TRUE;
}
|
If you want to make it operate with positive logic, which is more
reasonable, you can negate the XOR result:
Code: |
if(!(value1 ^ value2))
equal = TRUE;
else
equal = FALSE;
if(equal == TRUE)
printf("Equal\r");
else
printf("Not equal\r"); |
|
Thank you sir, this works flawlessly!
If you need some PIC10F222 for a project, just give me a shout, and I will supply |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19515
|
|
Posted: Mon Jul 18, 2016 12:04 pm |
|
|
As a couple of 'add on' comments to this. When you have finished, and have it working as you want, store the source files, and the compiler used. Put a note in the source file saying 'using compiler x.xx', and noting the reason. Then if you come back to it, you can carry on from where you are.
Generally, the use of the logic test here is pretty certain to ensure the operation remains '16bit', since done like this, any bit set has the same priority, which is not the case for an arithmetic test. |
|
|
Futterama
Joined: 17 Oct 2005 Posts: 98
|
|
Posted: Mon Jul 18, 2016 12:27 pm |
|
|
Ttelmah, thanks, I will do that.
Now, can we do something similar with the following statement?
Code: | if((InputPin) && (Some_int8_value > 0)) |
I think the ASM code says if InputPin is not high, GOTO the else statement, so here it doesn't check the second statement if the first evaluates as false:
Code: | 0040: BTFSS 06.3
0041: GOTO 053
0042: MOVF 0F,F
0043: BTFSC 03.2
0044: GOTO 053 |
|
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Tue Jul 19, 2016 2:02 am |
|
|
Futterama wrote: | Now, can we do something similar with the following statement?
Code: | if((InputPin) && (Some_int8_value > 0)) |
I think the ASM code says if InputPin is not high, GOTO the else statement, so here it doesn't check the second statement if the first evaluates as false.
|
Yes, that's what happens. It's called "lazy evaluation" and is intended as a speed optimisation. Many implimentations of many languages do it, and it generally cannot be turned off. You are asking the compiler to waste time and run slowly. The obvious way round it would be to split the condition into two if statements. Be aware that optimisation may affect the code generated. Try something like:
Code: | if(InputPin)
{
if (Some_int8_value > 0)
{
// Do something.
}
else
{
// Do another thing.
}
}
else
{
// Do another thing copy. Must be the same as above to balance the code paths.
} |
|
|
|
Futterama
Joined: 17 Oct 2005 Posts: 98
|
|
Posted: Tue Jul 19, 2016 4:23 am |
|
|
RF_Developer, thanks, I actually already did that, and it will actually fit my code flow better that way |
|
|
Futterama
Joined: 17 Oct 2005 Posts: 98
|
|
Posted: Sat Jul 23, 2016 9:01 am |
|
|
Followup on this thread. I succeeded in making my code do the loop in exactly the same amount of time +/- 100ns according to my scope.
The way I did it was to clear Timer0 at the very beginning of my mail while-loop. The prescale is set so that the TMR0 value changes from 0 to 1 in 32µs. Since the polling of TMR0 takes 3 instructions:
Code: | .................... while(TMR0 != 1); // Wait for Timer0 to get predictive duration for each loop
0175: DECFSZ 01,W
0176: GOTO 175
0177: GOTO 0C0 |
all I had to do was to make sure all the different ways through the code wound take a number of instructions that can be divided by 3. E.g. 3-6-9-12 and so on. This way, when the code reaches the TMR0 polling, TMR0 would change to 1 at just the right moment.
This would then also mean that some of my code looks like this:
Code: | case 3: // 6µs or 7,5µs
if(!(ShotDelayCount ^ SparkDuration)) // ShotDelayCount == SparkDuration
{
HV2 = 0; // Deactivate spark on output 2
ShootMode = 4; // Jump to wait mode
Skip;
}else{Skip;Skip;}
Skip;Skip;
break; |
"Skip" is a define:
Code: | #define Skip{#asm NOP #endasm } |
A lot of "Skips" are used throughout my code, and it gets a bit confusing at times, but it works really well now. I couldn't have done it without my 4-channel digital scope, or maybe an ASM book
Thanks to you all! |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|