Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

Hi,

I'm having an issue with creating a very simple program on the PSoC3.   If you're interested in finding the issue, I'd be thankful.  I've spent a few hours with multiple experiments and I'm ready to bang my head against a wall.

I'm an experienced embedded HW and SW designer.  However, I've spent most of my time with the PSoC5LP which uses the ARM GCC compiler.

I'm currently trying to create a 'simple' version of the CySysTick functions for the PSoC3.   The DP8051 MCU on the PSoC3 doesn't not natively have SysTick timer resources which are available on the ARM MCUs.   I'm creating a simplified version of the CySysTick API calls for  PSoC3 that ONLY uses a single clock.  No UDB blocks are used.

In creating this CySysTick-compatible interface, I can create a CySysTick callback to a user isr.   The CySysTick ticks at 1ms.  I enter the user isr (via callback) once every 1ms as designed.  In the ISR it increments a variable.

However, when I use the incremented variable and perform a simple operation (toggle a pin) every 300ms (=VDAC_STEP_TIME), it occurs every 5ms.

 

		if((systick - systick_prev) >= VDAC_STEP_TIME)
		{
		static toggle = 0x00;
			*(reg8 *)CYREG_PRT3_DR = toggle;
			systick_prev = systick;
			toggle = ~toggle;
		}
//		CyDelay(1);

 

 If I uncomment the CyDelay(1), my toggling of the pin occurs every 300ms as intended.

I have an identical CySysTick routine in main.c on the PSoC5LP.   There is no issue and no need for the CyDelay(1).

I imagine I'm missing some subtle issue with the Keil DP8051 compiler and some missing directive.

Attached is the project for your review.

Thanks for advanced for the help.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.

moto-an and BiBi,

Thank you for your input.   Due to your input and my experiments I have determined the reason for the issue.  It helped to disassemble the code to come to the solution.

Mea Culpa!  Mea Culpa!, Mea maxima Culpa!

 It was my coding error that caused the issue.

Here is a quick outline to my code issue with the relevant code.

The following is the outline of the faulting code:

 

In CycSysTick.c:
static void CySysTickServiceCallbacks(void);
/******/
void CySysTickInit(void)
{
...
// Replace with ISR method
    (void) CySysTick_isr_StartEx(&CySysTickServiceCallbacks);	/* init the 	
...
}

/****/
static void CySysTickServiceCallbacks(void)
{...}

In main.c:
/******/
CY_ISR(systick_isr)
{...}

/*****/
int main(void)
{
...
    CySysTickSetCallback(0, &systick_isr);
...
}

 

The following is the outline of the 'fixed' code:

 

In CycSysTick.c
CY_ISR_PROTO( CySysTickServiceCallbacks);

/******/
void CySysTickInit(void)
{
...
// Replace with ISR method
    (void) CySysTick_isr_StartEx(&CySysTickServiceCallbacks);	/* init the 	
...
}

/****/
CY_ISR ( CySysTickServiceCallbacks)
{...}

/******/
void systick_isr(void)
{...}

In main.c:
/*****/
int main(void)
{
...
    CySysTickSetCallback(0, &systick_isr);
...
}

 

 

There are only two real differences.   The systick_isr() was converted from an CY_ISR() to a function.  The CySysTickServiceCallBacks()  was converted from a function to an CY_ISR().

In short, when the CySysTck_isr causes the interrupt event,  the CySysTickServiceCallBacks() was not treated as an interrupt and therefore CPU registers were not pushed or pulled entering the function.   This is the reason that the 'C' flag register was not saved at the time of entering the ISR.

Changing CySysTickServiceCallBacks() to an CY_ISR() fixes this issue. (Note: the isr_systick() converted to a function instead of an ISR.)

With this change, the CyGlobalIntDisable()/CyGlobalIntEnable() framing, CyDelay() or CyDelayUs() or other 'fixes' are not needed.

It would be also correct that it is a "race condition" in that when the ISR does get processed will determine the change to the 'C' flag that affects the if() conditional.

Now ALL makes sense in the world.  The sky is NOT falling!

I've attached a fixed version of the project for those who are interested.

Len
"Engineering is an Art. The Art of Compromise."

View solution in original post

21 Replies
BiBi_1928986
Level 7
Level 7
First comment on blog 500 replies posted 250 replies posted

Hi Len.

An interesting problem.
I don't have a PSoC3 to test with.  I run Creator 4.2 and 3.1.

Just a thought, I'm wondering if systick, when used in the toggle routine, needs to be in a 'critical region'.  i.e., disable/enable interrupts around it while it's being used.

The fact that 5LP version works, is also interesting.
Could processor speed be contributing to 5LP success (avoiding some un-identified race condition)?
What happens if you put CyDelay(1) into 5LP code?  Do you still see 300ms toggle?  I suspect yes.

Or, what happens if PSoC3 CyDelay(10)?  I haven't looked at the code for CyDelay() in a long time, but I think interrupts are still enabled.

 

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

I'm just curious, but I wonder if you change the part of code like below, does it have same effect?

 

    uint32_t ms_passed = 0 ;
// ...
    ms_passed = systick - systick_prev ;

		if(ms_passed >= VDAC_STEP_TIME)
		{
		static toggle = 0x00;
			*(reg8 *)CYREG_PRT3_DR = toggle;
			systick_prev = systick;
			toggle = ~toggle;
		}

 

moto

0 Likes

Hi Moto.

You could be on the right trail.
I had that exact issue as you pointed out.  I solved it the same way you did.  I had tried all sorts of nested brackets (parentheses), but nothing would fix the issue until I took the arithmetic outside of the 'if'.

When I looked at the compiled code (assembler), the 'if' only tested the (systick - systick_prev) while ignoring the >=VDAC_STEP_TIME.  It drove me nuts trying to figure it out (a state of mind Len is experiencing!).

A little thought experiment:
Assume systick is only uint8 and the 'if' statement is faulty by only testing (systick - systick_prev).
Initialize systick=0, systick_prev=0.
The 'if' will execute 'true' all the way to systick=ff and set systick_prev=ff in that iteration.

Now, when systick rolls over to 0x00, systick_prev is  >systick and the toggle does not occur and systick_prev does not get updated.  When systick reaches 0xff again, the 'if' is true and the toggle occurs.  Since systick_prev is still 0xff, it isn't actually updated, but the code thinks it is.

And again, systick rolls over to 0x00, and the cycle repeats itself while systick_prev=ff constantly.

So, when the CyDelay(1) is added, we see the toggle occur after 256ms.  Just coincidence with the value of 300ms.
Without CyDelay(1), we see the execution speed of PSoC3 running through the 'if' statement (not true) 256 times before it toggles the GPIO.

Len, you can prove this by setting VDAC_STEP_TIME to 100ms and you'll still see toggle rate of 256ms.

I saw this issue with either PSoC5LP or PSoC4.  I'd have to go back and check some old projects but I suspect it was both processors where I had the issue.

Let us know what you find Len.  I think moto has got the solution.

0 Likes

moto-san,

I removed the math [(systick - systick_prev)] to before the if() as you suggested.  It didn't fix it.  However it did change the period to an asymmetric 6ms then 2ms.

Again, by adding a delay of 1ms before going back to the top of the for() loop, it works fine.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

Or even more sickly

 

    uint32_t ms_passed = 0 ;
//...
    ms_passed = systick ;
    ms_passed -= systick_prev ;

		if(ms_passed >= VDAC_STEP_TIME)
		{
		static toggle = 0x00;
			*(reg8 *)CYREG_PRT3_DR = toggle;
			systick_prev = systick;
			toggle = ~toggle;
		}

 

 

moto

0 Likes

moto-san,

Here's something even stranger:

If I turn off optimization, I get the following results:

The period is 1ms then 7 ms then 1ms then 300ms ... repeat.

If I turn optimization back on, with Opt Lvl = 2 and Opt Type = Speed I get:

The period is 1ms then 7 ms then 1ms then 300ms ... repeat.

Just like with Optimization off.

When I return the optimization to the default settings Opt Lvl = 2 and Opt Type = Size I get:

period is 2ms then 6ms ... repeat.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi Len-san,

Merry Christmas  🎉

I believe that the root cause is as Bibi-san suggested the race condition.

So my suggestion was kind of fooling the critical path by lowering the chance of the race.

Or I was hoping that mere assignment may not conflict with the increment of the volatile counter.

Anyway, let me know when you find the answer 😉

moto

0 Likes

moto-san,

Thank you and Merry Christmas to you also.

It would be peculiar if it were a race condition.

if((systick - systick_prev) >= VDAC_STEP_TIME)
{
static toggle = 0x00;
	*(reg8 *)CYREG_PRT3_DR = toggle;
	systick_prev = systick;   /* This stores the current systick to the previous.  This should yield (systick-systick_prev) = 0 in the next iteration of the for() loop */
	toggle = ~toggle;
}
//		CyDelay(1);

Personally unless I want to go through and debug the disassembly, I'm suspecting this is a compiler bug.

I'll post to this thread if I find the answer.

Len
"Engineering is an Art. The Art of Compromise."

Hi Len.

Can you expand your scope time base to see if you are getting micro-second GPIO toggles.  I'm wondering if the "if" true condition is getting executed all the time.

That's a pretty bizarre outcome when you changed optimization options.

0 Likes

BiBi,

I've time-zoomed in and I'm not getting us or ns extra pulses.

BiBi and moto-san,

New finding:

When I change my code to:

if((systick - systick_prev) == VDAC_STEP_TIME)
{
static toggle = 0x00;
	*(reg8 *)CYREG_PRT3_DR = toggle;
	systick_prev = systick;   /* This stores the current systick to the previous.  This should yield (systick-systick_prev) = 0 in the next iteration of the for() loop */
	toggle = ~toggle;
}

where I change the if() conditional to '==', then it works as expected and I get a consistent 300ms pulse.

The trouble with using the '==' conditional is that if the main() doesn't process this conditional in less than 1ms then systick could increment to be greater than systick_prev + VDAC_STEP_TIME.   This could occur depending on the other virtual tasks being executed.  For example, if I use the CyDelay(1), it is possible that under the right conditions, I could get two systicks before I process the conditional.

If I miss the conditional because systick has advanced from systick_prev + VDAC_STEP_TIME then it would never get executed UNTIL the systick wrap-around occurs.  Since systick is a 32-bit value, the next time the conditional would be true would be in 4,294,967.596 secs = 49.71 days.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

New finding:

If I change my code to:

 

if((systick - systick_prev) >= VDAC_STEP_TIME)
{
static toggle = 0x00;
	*(reg8 *)CYREG_PRT3_DR = toggle;
	systick_prev = systick;   /* This stores the current systick to the previous.  This should yield (systick-systick_prev) = 0 in the next iteration of the for() loop */
	toggle = ~toggle;
}
CyDelayUs(5)  /* delay in usecs */

 

Where I use the '>=' conditional AND add a CyDelayUs(5) of 5 usecs, then I get the 300ms period as desired.   If I lower the delay to 4 usecs, I get an erratic period of 1 to 5 ms.

This is appearing more as a race-condition as BiBi suggested.  The only thing is it doesn't make sense as a timing race-condition in main().  The only two things that comes to mind as a potential race-condition is if the DP8051 MPU uses an instruction cache or one of the variables is being optimized in a register.

I didn't think the DP8051 has an instruction cache. 

Also, in my previous experiment of turning off optimization, which also failed, variables should not be stored in registers.  Additional I put the 'volatile' keyword in front of systick and systick_prev and no improvements.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Ahhhhhh Len, you got my brain thinking.
When I had the problem before with >=, my solution was to get the result ahead of the "if" test.  I had tried breaking up the >= into an OR condition, but that also failed.  It's as though the "if" can't test complex conditions.

So, between what you found and what Moto suggested, try this.
uint32 temp1 = systick - systick_prev;
if (temp1==VDAC_STEP_TIME)
{
  static toggle = 0x00;
  *(reg8 *)CYREG_PTR3_DR=toggle;
  systick_prev = systick;
  toggle=~toggle;
}

else if (temp1>VDAC_STEP_TIME)
{
  static toggle = 0x00;
*(reg8 *)CYREG_PTR3_DR=toggle;
  systick_prev = systick;
  toggle=~toggle;
}

Something like that.  I'm sure you'll figure it out.

You could also try putting the conditions, >, ==, into an OR conditional, within a single "if", but I couldn't get that to work.  You might have better luck.  You should be able to eliminate the CyDelay(1).

0 Likes

BiBi,

I've tried some additional experiments.

I replaced the CyDelayUs(5) with three i++ lines.  That worked also.  Two i++ did not work.

I tried your suggestion about splitting the '>=' conditional to two separate if(), else if() tests.  It still didn't work.

I should note one thing that was very consistent.   When the if() statement would be true, it would always be at 1ms (systick interrupt) intervals.  No early triggers even when the for(;;) loop returns for the next iteration before the next systick event.

Next:  I'm going to try to disassemble the code from the main.lst file to see if I can find the reason for the bizarre non-intuitive effect I'm seeing.  This can get laborious so it might take a while.

 

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

Len-san's report about adding additional commands after the branch sounds like "Branch Penalty".

So I downloaded the TRM_PSoC3 and there is a description

1.3.1 Processor
PSoC 3 CPU subsystem is built around an 8-bit single cycle
pipelined 8051 processor, running up to 67 MHz. 

This may explain the symptom.

But as Len-san stated, a "C Compiler" should have taken care of  this.

So it might (must?) be a bug of the compiler.

 

Last night suddenly I remembered that "a 8051 is a 8bit processor".

I wonder how many machine cycles are required to assign/compare uint32_t ?

(In the era of 8bit CPUs, a 32bit data was a precious resource...)

 

So if Len-san can afford time for testing... 

I'd like to ask if changing the data size of these systick related variable to

uint8_t or uint16_t and see if it affects the behavior of the branch symptom.

I'm suspecting that the poor compiler can handle 8/16 bit correctly.

 

Meantime, if Len-san needs 24~32 bit timer, may be UDB implementation is required.

Or limit the size of timer to 16bit to fit in the available 8/16-bit timer/counter/PWM.

 

moto

 

0 Likes

Hi Len.

Now that you have a minimum delay in micro-seconds, how about trying to enable/disable interrupts around the entire "if", including the braces.

CyGlobalIntDisable;

if(...)
{...
}

CyGlobalIntEnable;
/* CyDelayUs(5); */

If this works, I suspect the 300ms toggle may not be accurate since time is lost (systick does not get incremented) during execution of the "if".  And, if it works, then just as Moto has suggested, 8051 may take many-many CPU cycles to perform 32-bit math.

I'm intrigued by the insertion of multiple i++ and its outcome.  Why does additional s/w delay (also shown with CyDelayUs(5)) make things better?  Is interrupt rate simply too fast for PSoC3 to keep up with house keeping?  The INT disable/enable may shed some light on this.

edit:
To minimize duration when interrupts are disabled, use a temp variable to hold systick.

CyGlobalIntDisable;
uint32 temp=systick;
CyGlobalIntEnable;

/* substitute temp for systick in "if" */

if(...)
{...
}
/* CyDelayUs(5); */

 

0 Likes

moto-an and BiBi,

Thank you for your input.  I think we're closer to an answer.

I changed the main() code to include two 'framing' toggles.

  • When the for(;;) is first executed (P0.0)
  • When the if(systick - systick_prev) >= VDAC_STEP_TIME is true (P3.0)

 

int main(void)
{
	CySysTickStart();
	CySysTickSetCallback(0, &systick_isr);
    CyGlobalIntEnable; /* Enable global interrupts. */
	
    for(;;)
    {
	static tog = 0;
		*(reg8 *)CYREG_PRT0_DR = tog;
		tog = ~tog;		
/* process virtual Tasks */
		{
		if((systick - systick_prev) >= VDAC_STEP_TIME)
		{
		static toggle = 0x00;
			systick_prev = systick;
			*(reg8 *)CYREG_PRT3_DR = toggle;
			toggle = ~toggle;
		}
		}
    }
}

 

As you can see from the scope plots, the if() toggles at 3 ms and then 7 ms ... repeat.

The for() toggle occurs every ~7us.  So the (systick - systick_prev) >= VDAC_STEP_TIME uint32 processing is a fraction of 7us.   The longer delay to the 7us toggle is due to the systick processing which occurs every 1 ms.

Len_CONSULTRON_2-1640616075546.png

 

Len_CONSULTRON_1-1640615815554.png

I'm confident that although it takes longer to process uint32 data for the compare, it's less than 7us.

When I take BiBi's suggestion of framing the CyGlobalIntDisable()/CyGlobalIntEnable() around the if() conditional, I consistently get the 300 ms toggle of the if() as intended!!!

/*************************************************************************************/
int main(void)
{
	CySysTickStart();
	CySysTickSetCallback(0, &systick_isr);
    CyGlobalIntEnable; /* Enable global interrupts. */
	
    for(;;)
    {
	static tog = 0;
		*(reg8 *)CYREG_PRT0_DR = tog;
		tog = ~tog;		
/* process virtual Tasks */
		{
CyGlobalIntDisable; /* Enable global interrupts. */
		if((systick - systick_prev) >= VDAC_STEP_TIME)
		{
		static toggle = 0x00;
			systick_prev = systick;
			*(reg8 *)CYREG_PRT3_DR = toggle;
			toggle = ~toggle;
		}
CyGlobalIntEnable; /* Enable global interrupts. */
		}
    }
}

 

Why is the CyGlobalIntDisable()/CyGlobalIntEnable() framing working?

I disassembled the code and found a routine called ?C?ULCMP

It 

0x000001A0 mov a, r3      ; VDAC_STEP_TIME[3] -> a
0x000001A1 subb a, r7     ; (systick[3]-systick_prev[3]) - a -> a
0x000001A2 mov b, a        ; a -> b
0x000001A4 mov a, r2      ; VDAC_STEP_TIME[2] -> a
0x000001A5 subb a, r6     ; (systick[2]-systick_prev[2]) - a -> a
0x000001A6 orl b, a           ; (a | b) -> b
0x000001A8 mov a, r1      ; VDAC_STEP_TIME[1] -> a
0x000001A9 subb a, r5     ; (systick[1]-systick_prev[1]) - a -> a
0x000001AA orl b, a           ; (a | b) -> b
0x000001AC mov a, r0      ; VDAC_STEP_TIME[0] -> a
0x000001AD subb a, r4     ; (systick[0]-systick_prev[0]) - a -> a
0x000001AE orl a, b            ; (a | b) -> b
0x000001B0 ret

I discerned: 

if b == 0 then (systick-systick_prev) == VDAC_STEP_TIME
if C == 1 then (systick-systick_prev) > VDAC_STEP_TIME

Here is the disassembled code for the if() conditional:

                                        ; SOURCE LINE # 51
004D 900000 R MOV DPTR,#systick_prev    ; move the ptr to systick_prev
0050 120000 E LCALL ?C?LLDXDATA0  ; long load of systick_prev into 4 registers
0053 900000 R MOV DPTR,#systick    ; move the ptr to systick
0056 120000 E LCALL ?C?LLDXDATA  ; long load of systick into 4 registers
0059 120000 E LCALL ?C?LSUB   ; long subtract (systick - systick_prev)
005C 7B2C MOV R3,#02CH            ; moving 300 (ms) into registers
005E 7A01 MOV R2,#01H
0060 7900 MOV R1,#00H
0062 7800 MOV R0,#00H
0064 D3 SETB C                                       ; set the 'C' flag
0065 120000 E LCALL ?C?ULCMP     ; Here is the long compare routine
0068 502F JNC ?C0004                         ; This is the jump around if the carry flag 'C' is not set
                                       ; SOURCE LINE # 52

You can see that the 'C' flag is the only flag being checked to determine that the if() condition is true or not.

I suspect since the CyGlobalIntDisable()/CyGlobalIntEnable() works, that without this interrupt disabling the systick processing changes the 'C' flag to the WRONG state which causes the if() to be true at the wrong times.

It is my opinion that there is something wrong with the compiler or worse the CPU microcode.  Normally during interrupt processing, the CPU registers are pushed on the stack entering the interrupt and pulled from the stack when exiting.  This includes the CPU flag registers.

Here is the disassembled code of the systick isr:

; FUNCTION systick_isr (BEGIN)
0000 C0E0 PUSH ACC
0002 C0F0 PUSH B
0004 C083 PUSH DPH
0006 C082 PUSH DPL
0008 C085 PUSH DPH1
000A C084 PUSH DPL1
000C C086 PUSH DPS
000E 758600 MOV DPS,#00H
0011 C000 E PUSH ?C?XPAGE1SFR
0013 750000 E MOV ?C?XPAGE1SFR,#?C?XPAGE1RST
0016 C0D0 PUSH PSW
0018 75D000 MOV PSW,#00H
001B C000 PUSH AR0
001D C001 PUSH AR1
001F C002 PUSH AR2
0021 C003 PUSH AR3
0023 C004 PUSH AR4
0025 C005 PUSH AR5
0027 C006 PUSH AR6
0029 C007 PUSH AR7
; SOURCE LINE # 28
; SOURCE LINE # 30
002B 900000 R MOV DPTR,#systick
002E 120000 E LCALL ?C?LLDXDATA
0031 7B01 MOV R3,#01H
0033 7A00 MOV R2,#00H
0035 7900 MOV R1,#00H
0037 7800 MOV R0,#00H
0039 120000 E LCALL ?C?LADD
003C 900000 R MOV DPTR,#systick
003F 120000 E LCALL ?C?LSTXDATA
; SOURCE LINE # 31
0042 D007 POP AR7
0044 D006 POP AR6
0046 D005 POP AR5
0048 D004 POP AR4
004A D003 POP AR3
004C D002 POP AR2
004E D001 POP AR1
0050 D000 POP AR0
0052 D0D0 POP PSW
0054 D000 E POP ?C?XPAGE1SFR
0056 D086 POP DPS
0058 D084 POP DPL1
005A D085 POP DPH1
005C D082 POP DPL
005E D083 POP DPH
0060 D0F0 POP B
0062 D0E0 POP ACC
0064 32 RETI
; FUNCTION systick_isr (END)

I don't recognize the CPU flag register in the PUSHs or PULLs.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
lock attach
Attachments are accessible only for community members.

moto-an and BiBi,

Thank you for your input.   Due to your input and my experiments I have determined the reason for the issue.  It helped to disassemble the code to come to the solution.

Mea Culpa!  Mea Culpa!, Mea maxima Culpa!

 It was my coding error that caused the issue.

Here is a quick outline to my code issue with the relevant code.

The following is the outline of the faulting code:

 

In CycSysTick.c:
static void CySysTickServiceCallbacks(void);
/******/
void CySysTickInit(void)
{
...
// Replace with ISR method
    (void) CySysTick_isr_StartEx(&CySysTickServiceCallbacks);	/* init the 	
...
}

/****/
static void CySysTickServiceCallbacks(void)
{...}

In main.c:
/******/
CY_ISR(systick_isr)
{...}

/*****/
int main(void)
{
...
    CySysTickSetCallback(0, &systick_isr);
...
}

 

The following is the outline of the 'fixed' code:

 

In CycSysTick.c
CY_ISR_PROTO( CySysTickServiceCallbacks);

/******/
void CySysTickInit(void)
{
...
// Replace with ISR method
    (void) CySysTick_isr_StartEx(&CySysTickServiceCallbacks);	/* init the 	
...
}

/****/
CY_ISR ( CySysTickServiceCallbacks)
{...}

/******/
void systick_isr(void)
{...}

In main.c:
/*****/
int main(void)
{
...
    CySysTickSetCallback(0, &systick_isr);
...
}

 

 

There are only two real differences.   The systick_isr() was converted from an CY_ISR() to a function.  The CySysTickServiceCallBacks()  was converted from a function to an CY_ISR().

In short, when the CySysTck_isr causes the interrupt event,  the CySysTickServiceCallBacks() was not treated as an interrupt and therefore CPU registers were not pushed or pulled entering the function.   This is the reason that the 'C' flag register was not saved at the time of entering the ISR.

Changing CySysTickServiceCallBacks() to an CY_ISR() fixes this issue. (Note: the isr_systick() converted to a function instead of an ISR.)

With this change, the CyGlobalIntDisable()/CyGlobalIntEnable() framing, CyDelay() or CyDelayUs() or other 'fixes' are not needed.

It would be also correct that it is a "race condition" in that when the ISR does get processed will determine the change to the 'C' flag that affects the if() conditional.

Now ALL makes sense in the world.  The sky is NOT falling!

I've attached a fixed version of the project for those who are interested.

Len
"Engineering is an Art. The Art of Compromise."

Bravo Len!

I understand your explanation.  And the solution.

BTW, 8051 register name Program Status Word (PSW) contains the Carry bit (bit-7).

Now the question is, does the same issue exist implemented for 5LP or 4xxx?

Great detective work from all.

BiBi,

Thanks for the PSW info.

I don't this issue exists for the PSoC5xxx, PSoC4xxx, PSoC6xxx.

The ARM architecture includes SysTick as a standard resource.  Therefore the CyLib.c (cy_boot) code is tested and mature.

I borrowed the CyLib.c code from the PSoC5 to create my simulated CySysTick minimally functional.  However, I had to make some changes (ie.  There is no  CyIntSetSysVector() call that would apply.)  Therefore my modified version needed to point to an CY_ISR() called function to perform an RETI when it exits.

Len
"Engineering is an Art. The Art of Compromise."
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Len-san,

 

Congratulations!

I'm glad that you could come up with the solution during this year.

 

So my understanding is that your "SysTick Call" calls an ISR function (or ISR functions),

but itself was not treated as ISR so it affected the flag when it is called.

 

My slow brain has not been able to digest why adding the "branch slot" like commands (seemed to) fix the problem, above reason was enough to convince me that the "if" was not safe in this context.

 

It has been a fun discussion for "thinking"

Thank you for your providing us the chance to participate,

and have a happy new year 😉

 

Best Regards,

28-Dec-2021

Motoo Tanaka

 

 

 

 

0 Likes

moto-san,

Thank you for the kind holiday wishes.

The issue I caused because of the differences in how the Keil DP8051 compiler treats interrupts compared to the GCC ARM compiler.

In my implementation, I used a 1KHz clock with a period of 1ms to drive an ISR.

Len_CONSULTRON_0-1640656757803.png

My error is that when I initialized the CySysTick_isr with the pointer to CySysTickServiceCallbacks() I didn't assign it to CY_ISR().  

 

 

    (void) CySysTick_isr_StartEx(&CySysTickServiceCallbacks);	/* init the 	
...
}

 

 

 On the GCC ARM compiler, you don't need to.   

However, on the Keil compiler you need to add the 'interrupt' keyword at the end of the function declaration.  This is done by using the CY_ISR() assignment to the function.

Here's the CY_ISR() declaration:

#define CY_ISR(FuncName) void FuncName (void) interrupt 0

With the 'interrupt' keyword, the beginning of the function is coded to push the current PC registers to the stack.  On return from the function, the registers are pulled back into the registers and finally ends with a RETI instead of an RET.

When I didn't use CY_ISR(CySysTickServiceCallbacks) it didn't push and pull registers or perform an RETI.  Therefore the previous CPU state was not preserved on the CycSysTick_isr event losing the state of the 'C' flag for the if() conditional.

I also changed the systick_isr() from an CY_ISR() to just a function declaration for use with the CySysTickSetCallback(0, &systick_isr) call.

I see from your previous posts, you like challenges.

I have a couple more PSoC3 build issues if you're interested:

PSoC3-reentrant-issue-I-get-a-build-error 

Keil-Issue-Receiving-L15-warning-from-Linker 

I think the if() statement is relatively safe.  As safe as any statement that performs a comparison. 

I've found in my long engineering career that once you think you've conquered all the 'bugs' in a design, 90% of the remaining problems are asynchronous in nature where race conditions are the biggest culprits.   

Len
"Engineering is an Art. The Art of Compromise."