Chipcon/8051 sanity check

Hi,

I have a problem with a timer interrupt on a CC1110 (8051 with wireless interface) that I have been staring at for an hour with no success. 8051 is not a strength of mine so I would appreciate your comments -

Given the (very!) simple program:

char x; int main( void ) { char c = 0;

____IEN0 &= ~pdBIT_7; ____SetClockSource(); // Setup processor clock. ____vInitTimer3( 1000 );// Setup time to int every 1ms. ____P1DIR = 0xff; // P1 to output. ____IEN0 |= pdBIT_7;

____for( ;; ) ____{ ________x = c; // Do nowt but asign a char to a char. ____}

____return 0; }

__interrupt void T3_IRQ_C(void) { ____P1=~P1; // Toggle port 1 for viewing on a scope. ____TIMIF = 0; // Clear interrupt. }

the following assembler is generated for the for() loop.

000201 0E INC R6 x = c; 000202 EE MOV A,R6 000203 90 F2 00 MOV DPTR,#0xF200 000206 F0 MOVX @DPTR,A 000207 80 F8 SJMP 0x0201

The scope shows that P1 is toggling with the expected frequency with a nice clean square wave. So far so good.

Now, if I change the code as follows: (Note that c is now volatile and incremented rather than assigned. The interrupt function is unchanged.)

int main( void ) { volatile char c = 0;

____IEN0 &= ~pdBIT_7; ____SetClockSource(); // Setup processor clock. ____vInitTimer3( 1000 );// Setup time to int every 1ms. ____P1DIR = 0xff; // P1 to output. ____IEN0 |= pdBIT_7;

____for( ;; ) ____{ ________c++; // Do nowt but asign a char to a char. ____}

____return 0; }

the following asm is generated:

00020C 85 10 82 MOV DPL,XSP(L) 00020F 85 11 83 MOV DPH,XSP(H) 000212 E0 MOVX A,@DPTR 000213 24 01 ADD A,#0x01 000215 F0 MOVX @DPTR,A 000216 80 F4 SJMP 0x020C

With the processor sat in this tight loop the scope shows that lots of timer interrupts are being missed, and there is no longer a clean square wave being output on P1.

If I disable and enable interrupts around the line where c is incremented I find that the clean square wave returns and timer interrupts are no longer missed, so I was postulating that an interrupt occurring between the sequential access of the two XSPbytes might have been causing some corruption within the ISR, but the code generated for the interrupt is as follows:

T3_IRQ_C: 0003CB C0 E0 PUSH A P1=~P1; 0003CD E5 90 MOV A,P1 0003CF F4 CPL A 0003D0 F5 90 MOV P1,A TIMIF = 0; 0003D2 75 D8 00 MOV TIMIF,#0x00

0003D5 D0 E0 POP A 0003D7 32 RETI

so does not access the DPL, DPLH, XSP(L) or XSP(H) registers.

Either there is something seriously amiss or I have done something too dumb for me to see.

--
Regards,
Richard.

+ http://www.FreeRTOS.org
A free real time kernel for 8, 16 and 32bit systems.

+ http://www.SafeRTOS.com
An IEC 61508 compliant real time kernel for safety related systems.
Reply to
FreeRTOS.org
Loading thread data ...

I am puzzled. This assembly code does not do x = c; c is being incremented.

Ian

Reply to
Ian Bell

Sorry - my bad. It should be:

____for( ;; ) ____{ ________c++; ________x = c; // Do nowt but asign a char to a char. ____}

Both the working and non working code increment c. The working code then assigns c to x, otherwise it is optimised away and the loop does nothing. The non working code has c as a volatile so the assignment is not required.

--
Regards,
Richard.

+ http://www.FreeRTOS.org
A free real time kernel for 8, 16 and 32bit systems.

+ http://www.SafeRTOS.com
An IEC 61508 compliant real time kernel for safety related systems.
Reply to
FreeRTOS.org

Something is amiss. A 7 line, 1ms INT _should_ be competely 'dont care' to any 'outside' code, provided that code does not a) mess with the timer registers b) fire other interrupts, or disable interrupts

From a test viewpoint, I'd normally toggle a single pin, than a whole port, and choose a pin that is not interrupt allocated. (just in case you have another INT firing from P1 toggling, for example)

What has changed, is an absolute address 0xF200 is now in XSP(L) or XSP(H) - do you _know_ where they are pointing ?

The other change, is you added a XDATA read, where prior loop was only write. Of, course that _should_ not make any difference at all.... :)

-jg

Reply to
Jim Granville

I have tried a single pin also. The problem occurs even without a write to the port, hence I put in the test code to toggle the pin(s). This started as a larger program and I gradually stripped things out expecting to find the source of the problem, but having stripped out everything was left with an empty loop and still a problem :-(

I have tried using timer 1 in place of timer 3 - same problem.

The errata states that timer interrupts originating from the 'sleep' timer can be missed if the processor is in power mode 0 (which it is) but says nothing about any other timers.

Not being in the office now I cannot check - but I know they are constant within the loop (as would be expected from the assembly).

--
Regards,
Richard.

+ http://www.FreeRTOS.org
A free real time kernel for 8, 16 and 32bit systems.

+ http://www.SafeRTOS.com
An IEC 61508 compliant real time kernel for safety related systems.
Reply to
FreeRTOS.org

On Mar 12, 3:57 pm, "FreeRTOS.org" wrote: [...]

Grasping at straws here...

I'm not familiar with the part you mention, and indeed, it's been more than a half-dozen years since I've messed with an 8051. But does your part automatically disable interrupts about instructions that mess with DPTR? Do you have a good datasheet on the core?

Different chip, but I ran into a problem like that with an AVR a few years ago. I had a loop that looked something like

Disable_Interrupts(); while () { Enable_Interrupts(); Disable Interrupts(); } Enable_Interrupts();

The loop was structured such that the loop would not exit until a particular ISR had executed. In the above case, the ISR never had a chance to run, because the Enable/Disable sequence inside the loop was assembled as

SEI CLI

And it turns out that when SEI is used to enable interrupts, they remain disabled until the following instruction completes. Which, in this case, once again disables interrupts. Inserting a NOP between the instructions fixed it.

What does that code look like?

Regards, -=Dave

Reply to
Dave Hansen

Thanks for your suggestion. I have been clutching at straws on this one already!

No :-( I have good datasheets for standard 8051's, but this is a bit more fancy.

000366 C2 AF CLR EA c++; 000368 85 10 82 MOV DPL,XSP(L) 00036B 85 11 83 MOV DPH,XSP(H) 00036E E0 MOVX A,@DPTR 00036F 24 01 ADD A,#0x01 000371 F0 MOVX @DPTR,A 000372 D2 AF SETB EA asm("NOP"); 000374 00 NOP 000375 80 EF SJMP 0x0366

As you say - the NOP is required otherwise the interrupt does not fire at all. I think this is expected though.

I have tried replacing the increment of c within the loop with the following inline assembler:

1 asm( "MOVX A, @DPTR" ); 2 asm( "MOV DPL, SP" ); 3 asm( "NOP" ); 4 asm( "NOP" ); 5 asm( "NOP" );

With all lines in the problem occurs.

With line 1 commented out, the problem still occurs.

With line 2 commented out the problem goes away - even though there are 3 NOPs following.

Aaarg!

--
Regards,
Richard.

+ http://www.FreeRTOS.org
A free real time kernel for 8, 16 and 32bit systems.

+ http://www.SafeRTOS.com
An IEC 61508 compliant real time kernel for safety related systems.
Reply to
FreeRTOS.org

OK, that makes sense. What compiler are you using?

Ian

Reply to
Ian Bell

IAR, but note my reply to Dave Hansen post where I can isolate it seemingly to a line of assembler. Maybe the assembler is illegal, but it replicates code generated by the compiler.

--
Regards,
Richard.

+ http://www.FreeRTOS.org
A free real time kernel for 8, 16 and 32bit systems.

+ http://www.SafeRTOS.com
An IEC 61508 compliant real time kernel for safety related systems.
Reply to
FreeRTOS.org

That is very weird stuff. Are you sure that XSP points to the proper memory? Have you tried confirming that the code bytes that are getting loaded into the device are the same bytes in your ASM listing? Have you tried changing the MOVX @DPTR,A to a NOP? Or even the MOVX A,@DPTR (if it's pointing to the wrong address, could this read be clearing the INT before it gets recognized?) What happens if you hard-code the DPTR loading to a fixed known-safe address?

Just more straws for your growing collection. :-\

Reply to
Mike Silva

Going back to my C example:

volatile char c = 0;

{ c++; }

which generates:

000366 85 10 82 MOV DPL,XSP(L) 000369 85 11 83 MOV DPH,XSP(H) 00036C E0 MOVX A,@DPTR 00036D 24 01 ADD A,#0x01 00036F F0 MOVX @DPTR,A 000370 80 F4 SJMP 0x0366

SPX contains 0xF1FF, whereas the datasheet states that 0xF000 to 0xFEFF is XRAM. This would therefore seem to be ok.

XSP(L) and XSP(H) are in IDATA.

The original code - before I cut out everything usefull - has a checksum that is validated on powerup.

Makes no difference :-(

Thanks for the straws anyhow.

--
Regards,
Richard.

+ http://www.FreeRTOS.org
A free real time kernel for 8, 16 and 32bit systems.

+ http://www.SafeRTOS.com
An IEC 61508 compliant real time kernel for safety related systems.
Reply to
FreeRTOS.org

not quite, because there is a jump between the INT access opcodes, but you will have a somewhat impractical small % of time where INT is enabled.

Rather than remove the lines, I'd swap them with ones of equal size/time, but benign (like NOP) - that keeps as much the same as possible. ie you can determine if it is the smaller loop, or some acceess, that is the trigger.

There are bugs where the loop size affects things, as the opcode length changes, and you can get stability when the SW/INT 'phase lock', and problems when they do not.

Also check if the code memory location affects this, tho your listing show it moving around - ie pack a variable block of NOPs before the loop.

Can you get a handle on how many/how long, as that can give clues on the mechanism at work.

-jg

Reply to
Jim Granville

Good plan. The behaviour was the same however. Replacing line 2 with a NOP fixed the problem, whereas replacing line 1 with a nop had no effect.

It is always exactly one interrupt that is missed at a time, never two in a row and always with consistent frequcy when running the tight loop test (see below). With the original code (complete application, different execution paths, etc) it was more like one in 100. However the complete application did sit in a tight loop waiting for the start of a new control cyle, so maybe that is something to look at.

In the test program:

for( ;; ) { asm( "MOV DPL, sp" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); }

30 NOPs - every 41st interrupt is missed.

15 NOPs - problem goes away.

14 NOPs - every 13th interrupt is missed.

13 NOPs - every 18th interrupt is missed.

Change the code to: for( ;; ) { __disable_interrupt(); asm( "MOV DPL, sp" ); __enable_interrupt(); asm( "NOP" ); asm( "NOP" ); }

Now exactly every other interrupt is missed.

Too much information.

The original code used

TIMIF &= ~0x40;

which translated to

CLR TIMIF.6

Thanks for taking the time to come up with your suggestions.

--
Regards,
Richard.

+ http://www.FreeRTOS.org
A free real time kernel for 8, 16 and 32bit systems.

+ http://www.SafeRTOS.com
An IEC 61508 compliant real time kernel for safety related systems.
Reply to
FreeRTOS.org

FreeRTOS.org wrote: >> 2 asm( "MOV DPL, SP" );

note that 2 is not a one cycle line, and this does seem to be loop-cycle related.

Wow. Still, it is looking like an Int phase issue.

Did you try asm( "MOV DPL, B" ); - just in case the SP read has wrinkles.... (same time as MOV DPL, SP)

what about fewer NOPs, down to these SJMP $; LJMP $; AJMP $ ( ie no loop at all, try all 3 opcodes ) It's looking like perhaps a JMP cannot be properly interrupted ?.

Does that make any difference. I'd prefer the clr a single flag, as being safer. Does it say if the HW clears this flag, or not ?

- leave it out, and if the 1ms toggle changes drastically, yes, you need it :)

-jg

Reply to
Jim Granville

Using a different register other than SP has seemingly no impact on the problem, hence I was concluding (somewhat unconvincingly) that it was the DPL that was causing the issue. Can't quite believe that though.

But:

for(;;) { }

is fine...........a single line of asm being the branch SJMP 0x0366.

Just for kicks I tried this:

label1: goto label2; asm( "NOP" ); asm( "NOP" ); label2: goto label3; asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); label3: goto label4; asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); label4: goto label5; asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); label5: goto label6; asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); asm( "NOP" ); label6: // asm( "MOV DPL, b" ); goto label1;

and this is fine too, until I uncomment the line immediately below label6.

I agree with respect to only clearing the single bit. I changed it as part of the test only. The docs are (to my mind) very ambiguous as to whether the line is needed or not, it seems so for some interrupts and not others. As it happens it makes no difference in this case, but the sample code that comes with the kit has it, so I thought it best to include it.

--
Regards,
Richard.

+ http://www.FreeRTOS.org
A free real time kernel for 8, 16 and 32bit systems.

+ http://www.SafeRTOS.com
An IEC 61508 compliant real time kernel for safety related systems.
Reply to
FreeRTOS.org

What you have enabled is a 3 byte, 2 cycle opcode, of the general form MOV Dir,Dir

If Jumps are ok, then it's starting to look like the 3 byte/2 cycle opcode has problems. [and one that certainly belongs in the errata :) ]

Other 3 byte 2 cy MOV opcodes are MOV Dir.#Immed, and ANL/ORL/XRL Dir,#Immed

so I'd try all forms, and also see if the SFR or DATA memory has any effect.

[There are also LJMP/LCALL, DJNZ Dir,RelAdr, CJNE, and Bit opcodes that are 3 byte, 2 cycle ones.]

Seems they were unsure as well ! :)

-jg

Reply to
-jg

Richard,

Did you nail this down in the end ?

-jg

Reply to
Jim Granville

Hmm. I eventually got a response from TI. Snippets include "the behaviour is expected" (huh?). "The device is not suitable for volume production or software development".

Paraphrasing the rest: "the device is no longer available, customers ordering a dev kit we will now be shipped an ABC dev kit in its place, which has a compatible interface, and then the real part later this year".

I also note a waiver form dated January this year saying that the part will only be shipped to customers who sign the waiver to indicate that they know it doesn't work.

Red faces all round I think, including us when we talk about our new budget and timescales with our customer :o( (I did not select the part in the first place but we are left with the bill).

--
Regards,
Richard.

+ http://www.FreeRTOS.org
A free real time kernel for 8, 16 and 32bit systems.

+ http://www.SafeRTOS.com
An IEC 61508 compliant real time kernel for safety related systems.
Reply to
FreeRTOS.org

Yes, it was sounding very like an errata item :) So there was no practical work-around on this ? How different is the replcement part ?

-jg

Reply to
Jim Granville

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.