Forums

Chipcon/8051 sanity check

Started by FreeRTOS.org March 12, 2007
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.




FreeRTOS.org wrote:
 
> > 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 > >
I am puzzled. This assembly code does not do x = c; c is being incremented. Ian
"Ian Bell" <ruffrecords@yahoo.co.uk> wrote in message 
news:45f5d11a.0@entanet...
> FreeRTOS.org wrote: > >> >> 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 >> >> > > I am puzzled. This assembly code does not do x = c; c is being > incremented.
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.
FreeRTOS.org wrote:

<snips>

> 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 > > > 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 > > > > 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 > > Either there is something seriously amiss or I have done something too dumb > for me to see.
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
"Jim Granville" <no.spam@designtools.maps.co.nz> wrote in message 
news:45f5d480$1@clear.net.nz...
> FreeRTOS.org wrote: > > <snips> > >> 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 >> >> >> 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 >> >> >> >> 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 >> >> Either there is something seriously amiss or I have done something too >> dumb for me to see. > > 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)
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.
> What has changed, is an absolute address 0xF200 is now in XSP(L) or > XSP(H) - do you _know_ where they are pointing ?
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.
On Mar 12, 3:57 pm, "FreeRTOS.org" <noem...@noaddress.com> wrote:
[...]
> 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.
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 (<loop conditions>) { <protected code> Enable_Interrupts(); Disable Interrupts(); <protected code> } 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.
> > 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
What does that code look like? Regards, -=Dave
> 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?
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.
> 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 (<loop conditions>) > { > <protected code> > Enable_Interrupts(); > Disable Interrupts(); > <protected code> > } > 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. > >> >> 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 > > What does that code look like?
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.
FreeRTOS.org wrote:

> > "Ian Bell" <ruffrecords@yahoo.co.uk> wrote in message > >> >> I am puzzled. This assembly code does not do x = c; c is being >> incremented. > > > 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. >
OK, that makes sense. What compiler are you using? Ian
> OK, that makes sense. What compiler are you using?
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.
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. :-\