Jump to content
Sign in to follow this  
Richie T

Uneven Bit Durations

Recommended Posts

Hi,

 

I'm new here but I've been lurking for the last few weeks while I've been using SourceBoost - I must say that I find it to be excellent sofware, and it has helped me so much in my project. Anyway, I've come up against an issue, which isn't exactly a bug, but rather a consequence of the implementation of bit access. I've searched the board but I've found nothing related to what I'm describing below, so I've decided to produce the following bug report - sorry if this has been discussed before but I don't think it has. I don't think that my suggestion below should be implemented as the way to do bit access, but the post may come in handy for anyone experiencing this.

 

Bug description:

When writing each bit of a char variable to a bit variable (for example, when loading a byte of data serially to a single output pin), ones and zeroes appear to have a different duration. The code generated does execute in constant time, but the point where the value of the output is set is dependant on what is being written.

 

Steps to reproduce:

I've written the following program which should demonstrate the issue, although it can be seen more explicitly is the code is run and the output is monitored on an oscilloscope

 

void main ()
{
char packet = 0;
volatile bit data_out	@ PORTA.0;

// Configure the ports
trisa = 0;
trisc = 0b11111111;

// Assume that the byte 0b10101010 is on portc
// and you want to send that byte serially out on a pin
// on porta (data_out)

// Read in the byte
packet = portc;

// Write the bits out serially
data_out = packet.0;
data_out = packet.1;
data_out = packet.2;
data_out = packet.3;
data_out = packet.4;
data_out = packet.5;
data_out = packet.6;
data_out = packet.7;
}

 

I've taken a portion of the generated assembly and I've noted where the value of data_out is actually set:

 

// data_out = packet.0;
BTFSC main_1_packet,0; Test if bit 0 of packet is equal to zero
BSF main_1_data_out,0; It is, so replace this with NOP
BTFSS main_1_packet,0; Test if bit 0 of packet is equal to one
BCF main_1_data_out,0; It is not, so set data out equal to zero

// data_out = packet.1;
BTFSC main_1_packet,1; Test if bit 1 of packet is equal to zero
BSF main_1_data_out,0; It is not, so set data out equal to one
BTFSS main_1_packet,1; Test if bit 1 of packet is equal to one
BCF main_1_data_out,0; It is, so replace this with NOP

// data_out = packet.2;
BTFSC main_1_packet,2; Test if bit 2 of packet is equal to zero
BSF main_1_data_out,0; It is, so replace this with NOP
BTFSS main_1_packet,2; Test if bit 0 of packet is equal to one
BCF main_1_data_out,0; It is not, so set data out equal to zero

// data_out = packet.3;
BTFSC main_1_packet,3;Test if bit 3 of packet is equal to zero
BSF main_1_data_out,0; It is not, so set data out equal to one
BTFSS main_1_packet,3; Test if bit 3 of packet is equal to one
BCF main_1_data_out,0; It is, so replace this with NOP

// data_out = packet.4;
BTFSC main_1_packet,4; Test if bit 4 of packet is equal to zero
BSF main_1_data_out,0; It is, so replace this with NOP
BTFSS main_1_packet,4; Test if bit 4 of packet is equal to one
BCF main_1_data_out,0; It is not, so set data out equal to zero
.
.
.

 

As should be clear from the above, if a one is being written to data_out, then it is written in the second instruction involved in the sequence, whereas if a zero is written, it happens in the fourth instruction. If this code is tested and data_out is monitored using an oscilloscope, the ones appear to be longer in duration than the zeroes (by four instruction cycles, due to the different points where the value changes).

 

Expected behaviour:

As stated, the ones and zeroes should appear for equal duration - the following assembly should provide the behaviour i'm talking about:

 

// data_out = packet.0;
BTFSC main_1_packet, 0
GOTO false0
GOTO true0

false0
NOP; Some padding so the operation takes the same amount of time regardless of value written
BSF main_1_data_out,0
GOTO continue0

true0
BCF main_1_data_out,0
NOP; More padding
NOP

continue0

// data_out = packet.1;
BTFSC main_1_packet, 1
GOTO false1
GOTO true1

false1
NOP; Some padding so the operation takes the same amount of time regardless of value written
BSF main_1_data_out,0
GOTO continue1

true1
BCF main_1_data_out,0
NOP; More padding
NOP

continue1
.
.
.

 

In the above, the value on data_out is set to the necessary value by the fifth instruction executed in this sequence.

 

Is the problem 100% reproduceable:

Yes, but is probably only of consequence when writing to an output pin, and where the duration of the value is of critical importance

 

IDE version: 6.84

Compiler: BoostC PIC16

Compiler version: 6.84

Target device: PIC16F876A

OS: Windows XP Home Edition

 

While I'm discussing it, an idea for a possible extension might be a feature that analyses the branches in the program execution and pads the code with blank instructions so that each possible fork in the program takes the same to execute. This may sound unusual, but for my current application I have to perform a number of operations (mainly interfacing to other devices) and stream data out from my PIC at a constant rate, and as a result I have to carefully study the execution paths involved and pad the code accordingly - I imagine that there may be other applications which would require the same. Obviously C is not the best choice for this type of thing but I'm new with PICs, so it's nice to be able to write some simple C to express the functionality I need, generate efficient code, and tweak until I get what I need! Just some food for thought - thanks again for some fine software!

Share this post


Link to post
Share on other sites

Not a bug. It's out of scope of C language to guarantee execution time of an expression. On a side note your code does not compile because include of system.h is missing and as resuld many identifiers in your code (line portc or PORTA) are undefined. And even when compiled your code will fall into infinite loop as you let main exit.

 

Regards,

Pavel

Share this post


Link to post
Share on other sites
Not a bug. It's out of scope of C language to guarantee execution time of an expression. On a side note your code does not compile because include of system.h is missing and as resuld many identifiers in your code (line portc or PORTA) are undefined. And even when compiled your code will fall into infinite loop as you let main exit.

 

Regards,

Pavel

 

 

Yes I am aware of that, I just thought I'd raise the issue as it may be of interest to others - I apologise if it was inappropriate to post it in the bug forum - as for the code not running, I only typed up the heart of the code in question to illustrate my point - I guess I should have inserted a working version - thanks for the reply though.

 

Rich

Share this post


Link to post
Share on other sites
Not a bug. It's out of scope of C language to guarantee execution time of an expression.

 

I couldn't agree more.

 

In addition, to do what was suggested would penalise those who require the fastest overall execution speed.

 

Not to mention for a non volatile destination, there's nothing wrong with the compiler implementing the assignment as:

 

dest = 0;
if ( src.0 )
dest = 1;

// and so on

 

You could spend hours tinkering with the C to get the compiler to produce the code you want and the timings you want only to have the next version of the compiler do something slightly differently and break your code. So, if you want precise timings, you absolutely have to use assembly. No big deal, the compiler supports inline assembly.

 

Orin.

Share this post


Link to post
Share on other sites
While I'm discussing it, an idea for a possible extension might be a feature that analyses the branches in the program execution and pads the code with blank instructions so that each possible fork in the program takes the same to execute. This may sound unusual, but for my current application I have to perform a number of operations (mainly interfacing to other devices) and stream data out from my PIC at a constant rate, and as a result I have to carefully study the execution paths involved and pad the code accordingly - I imagine that there may be other applications which would require the same. Obviously C is not the best choice for this type of thing but I'm new with PICs, so it's nice to be able to write some simple C to express the functionality I need, generate efficient code, and tweak until I get what I need! Just some food for thought - thanks again for some fine software!

 

Try the following:

 

char shadowA;

shadowA = porta;

shadowA.0 = 0;
if ( packet.0 )
shadowA.0 = 1;
porta = shadowA;

 

Or even:

 

asm {
movf _porta, W

// repeat per bit
andlw 0xFE
btfsc _packet, 0
iorlw 0x01
movwf _porta
//
}

 

BTW, all bets are off if the variable 'packet' isn't in the same bank as porta as the compiler will insert bank switching code. Yes, the assembly code is a slow read - modify - write of porta. Remember bsf and bcf on porta are implemented in the PIC hardware as read-modify-write so there's no real difference.

 

Orin.

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...