problem using FF 16 bit counter as watchdog

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.
PeSi_1583586
Level 3
Level 3
10 replies posted 10 questions asked 5 replies posted

I have a project using a PSoC5LP device compiled using PSoCCreater 4.2.

I am trying to use a fixed function Counter component as a watchdog timer. The counter generates an interrupt when the counter reaches its terminal count which generates a software reset.

I have named the counter 'Fido'. I call Fido_Start() at the start of my program, and periodically call Fido_WriteCounter(1200) to reset the counter to 1200 thus preventing it from reaching the terminal count and generating a reset unless the program crashes, in which case the counter will not be set to 1200, but will continue to count down to zero and generate an interrupt.

When I run the program, the first time Fido_WriteCounter(1200) is called, the program hangs.

Analysing the compiler-generated source file Fido.c

Fido_Start() calls Fido_Enable() which sets the enable flag by executing the statement

Fido_GLOBAL_ENABLE |= Fido_BLOCK_EN_MASK;

in Fido_WriteCounter(uint16 counter)         it executes the statement

CYASSERT (0u == (Fido_GLOBAL_ENABLE & Fido_BLOCK_EN_MASK));

       

As the enable flag in Fido_GLOBAL_ENABLE was set in Fido_Start() the if statement equates to zero, passing zero to  CYASSERT  which causes the cpu to be halted!

Should I disable the counter before calling Fido_WriteCounter() ?

The only high level API function which clears the enable bit in Fido_GLOBAL_ENABLE  is Fido_Stop()

Should I be calling Fido_Stop()?

Assuming the enable flag in Fido_GLOBAL_ENABLE  is clear when I called Fido_WriteCounter(),it would get past the statement

CYASSERT (0u == (Fido_GLOBAL_ENABLE & Fido_BLOCK_EN_MASK));

and immediately set the enable flag by executing  the statement

        Fido_GLOBAL_ENABLE |= Fido_BLOCK_EN_MASK;

Then writing data to the counter register before immediately clearing the enable flag by executing the statement

Fido_GLOBAL_ENABLE &= ((uint8)(~Fido_BLOCK_EN_MASK));

The counter would remain disabled until the next time I called Fido_WriteCounter();

This seems all wrong to me!

Am I misinterpreting the program?

Am I missing something?

or is there a bug in the compiler-generated Fido.c?

I have the following configuration settings in PSoC Creator

16bit Fixed Function

Period 1500

Run Mode: Continuous

Interrupt on TC

The version number for the Counter component is 3.0

The version number for PSoC Creator is 4.2

A copy of Fido.c is attached.

I originally tried using the built-in watchdog timer defined in CyLib.c

I started the Watchdog by calling

CyWdtStart(CYWDT_1024_TICKS  , CYWDT_LPMODE_NOCHANGE );

then periodically calling CyWdtClear();

When the program starts, I test the value of CyResetStatus, and if the CY_RESET_WD    bit is set, I take action to recover the error.

I modified my program so that it would deliberately get stuck in an infinite loop.

The Watchdog timer generated a reset 

but on testing the value returned by CyResetStatus,  the only bits that are set are

CY_RESET_SW                  and CY_RESET_GPIO1             

There is no sign of the supply voltage dipping during reset which could be interpreted as a normal power-on reset.

0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.

Peter,

It is solved!

Attached is a Bootloader/Bootloadable example project that proves the proof of concept.

It is VERY simple.

The Bootloader adds ONE line of code immediately entering main().

/* Save 'true' CyResetStatus to DMA config TD chan 22. Pass to Bootloadable code */

*(reg32 *)(CYREG_PHUB_CFGMEM22_CFG1) = CyResetStatus;

The Bootloadable adds ONE line of code immediately entering main().

/* Recover 'true' CyResetStatus from DMA config TD chan 22. Passed from Bootloader code */

CyResetStatus = *(reg32 *)(CYREG_PHUB_CFGMEM22_CFG1);

Now CyResetStatus contains the 'real' reason for the reset that survives a Bootloader intervention.

Note:  In the example project.  The Bootloader will only wait 5 seconds with the LED on to allow Bootloader Host to load in a new Bootloadable app. (If there is no app it will wait forever).  Once the 5 seconds expires (with the LED ON), you will get either 8 to 10 long blinks (due to a Reset event) or 3 quick blinks (due to a watchdog event).

To cause the watchdog, make sure the WDT_dtct_reset app is loaded.  Wait for the RESET blinking to complete.  The LED should be off.  Press the SW1 (P2.2) for about 4 seconds.  Once the LED turns back ON release SW1.   The Bootloader will keep the LED for 5 seconds then blink 3 times quickly to indicate a watchdog event.

This method uses a currently unused PSoC register that preserves the installed value through a reset.  No EEPROM resource needed.

Len

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

View solution in original post

0 Likes
12 Replies
Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

PeSi,

I looked at your Fido.c.  It's a standard Counter component output from Cypress.  No new information here.

Should I disable the counter before calling Fido_WriteCounter() ?

The only high level API function which clears the enable bit in Fido_GLOBAL_ENABLE  is Fido_Stop()

Should I be calling Fido_Stop()?

Yes. You should Stop the Counter before loading in the new Count value with Fido_WriteCounter().  The API on this function says:

The Counter should be disabled before calling this function.

Based on the CY_ASSERT, "should" is not strong enough.  It should be "must".

As a curiosity:  You are aware of the "true" watchdog timer function available to the PSoC5?  It is a "true" watchdog because it CAN'T be disabled once turned on.

In past projects, I created a "fake" watchdog based off of a timer/counter.  I only used this when building a project in "Debug" mode.  This allowed me to place a breakpoint in the code if the "fake" watchdog timed out.  With this breakpoint, I could trace back in the stack where in the app code I was when it went off.  This was a good indication that either my watchdog interval was too short or my app code was too long.

In "Release" mode, the "true"  watchdog was used in place of the timer.

Len

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

Hi Len

It would appear, then, that I need to call Fido_Stop( ); immediately before calling

Fido_WriteCounter();

then to immediately call Fido_Start();

Is this correct?

This is not made clear in the documentation.

Could not the same thing be achieved if the Fido_WriteCounter function were to just clear the enable bit in the control register, write the data to the counter register, then set the enable bit in the control register?

I would very much prefer to use the built-in watchdog timer, thus eliminating all the overhead of calling

Fido_Stop()

Fido_WriteCounter(1200);

Fido_Start();

every time I needed to 'Feed the Dog'

but, as I stated in my previous message, the value returned in CyResetStatus after a watchdog reset was not as I expected.

I would have expected a value of 0x08 to be returned i.e. CY_RESET_WD indicating a watchdog reset

whereas a value of 0xA0 was returned indicating CY_RESET_SW and CY_RESET_GPIO1

Do you have any suggestions why this should be, and what I could try to fix it? Would it be safe to test for either or both of

CY_RESET_SW or CY_RESET_GPIO1 ?

Peter

0 Likes
lock attach
Attachments are accessible only for community members.

Peter,

This is not made clear in the documentation.

The API doc for the WriteCounter() states:

The Counter should be disabled before calling this function.

Should is not a string enough statement.  It must be MUST.

About having difficulties with the "true" watchdog, try these links:

Re: How to determine whether or not Watchdog has "bitten?"

Checking the source of reset in PSoC3/PSoC5

Len

Update:

I've included a PSoC Creator project for the PSoC5 that uses the WDT and checks if the WDT was the cause of the reset.

Len

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

The point I was trying to make regarding Fido_WriteCounter() is that the counter must be disabled with Fido_Stop() and must be re-enabled with either Fido_Start() or Fido_Enable(). It is not sufficient just to disable the counter by clearing the enable bit in the control register and re-enabling it by setting the enable bit in the control register, and it is this which is not made clear in the documentation.

Regarding the true watchdog timer, the links provided just told me what I already knew.

I have studied the code which is executed before main() in Cm3Start.h and it would appear that data is read from the register CYREG_RESET_SR0 where the reset flags are stored, and written to register CYREG_PHUB_CFGMEM23_CFG1 then later read out of CYREG_PHUB_CFGMEM23_CFG1 and written to the variable CyResetStatus.

I have reduced my application to essentially

int main()

{

UART_1_Start();

char Buffer[32];

sprintf(Buffer, "%02X %02X %02X\r\n", CY_GET_REG8(CYREG_RESET_SR0), CY_GET_REG8(CYREG_PHUB_CFGMEM23_CFG1), CyResetStatus);

UART_1_PutString(Buffer);

CyWdtStart(CYWDT_1024_TICKS, CYWDT_LPMODE_NOCHANGE );

while(1);

}

which reads the two registers and the variable CyResetStatus and prints their values to the serial port, then performs a watchdog reset.

The values printed out each time is 00 A0 A0

indicating the cause of the reset as CY_RESET_SW and CY_RESET_GPIO1

My project has two components, a BootLoader and my main application which has a BootLoadable component.

On studying the code in Bootloadable_1.c, I see the function Bootloadable_1_Load() executes a SoftwareReset() and I guess it is this which is setting the reset status bits I am seeing.

This is born out by the fact that if I compile my project without the bootloader and bootloadable component in the main application, the values printed out by the above program is 00 08 08indicating a watchdog reset.

The fact that CYREG_RESET_SR0 is zero , indicates that the register has already been read causing it to be cleared to 0.

How can I pass the value read from CYREG_RESET_SR0 in the BootLoader to my main application?

0 Likes

Peter,

Have you tried my attached project?  It's VERY simple code and takes care of detecting the cause of the reset.

Note:  Don't use CYREG_RESET_SR0.  Before you enter main(), CYREG_RESET_SR0 is read and the value is stored in a non-initialized variable called "CyResetStatus".  Therefore CYREG_RESET_SR0 is cleared before entering main().

When you enter main(), CyResetStatus now contains what was in CYREG_RESET_SR0.

CyResetStatus can now be tested for the cause of the reset.

Len

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

I have now studied your watchdog project, and it raises a number of questions.

I have opened the project in pSoC Creator, and am able to see the schematic and the code in main.c, but I am unable to compile the project because it cannot find the file project.h and attempting to compile it without the #include project.h produces an error 'Unable to find component "cy_boot_v5_90"'. Neither can I find a hex file to program directly into the device. Should I be able to compile and run the program?

The file main.c includes 2 function prototypes

void InitializeWDT(void);

void WdtInterruptHandler(void);

neither of these functions appear to be defined or used. Are they defined/used elsewhere?

In the comments just before the definition of main() it states

" Summary: This is the main function for CM4 CPU. It configures the WDT in either reset mode or periodic interrupt mode."

As I understood it, the watchdog timer in the pSoC5 devices was only capable of generating a reset. Am I mistaken? There appears to be nothing in the definition of main to select which of these modes it will operate in.

After starting the watchdog timer, you enable global interrupts by calling __enable_irq();

Does the watchdog timer need interrupts to be enabled?

Looking at the definition of enable_irq() it states it 'Can only be executed in Privileged modes.' Should I use CyGlobalIntEnable; instead?

Your project does not address the question raised in my last message, namely that my test program returned CyResetStatus indicating a watchdog reset as would be expected when my project did not include a boot loader and bootloadable component, but when I include a bootloader and a bootloadable component, the CyResetStatus indicates a software reset, which I assume to be due to Bootloadable_Load executing a software reset.

I need some means of preserving the reset status in the bootloader so that I can read it in my main application to determine the actual cause of the reset. How can I do that.

0 Likes
lock attach
Attachments are accessible only for community members.

Peter,

To answer your questions:

I have opened the project in pSoC Creator, and am able to see the schematic and the code in main.c, but I am unable to compile the project because it cannot find the file project.h and attempting to compile it without the #include project.h produces an error 'Unable to find component "cy_boot_v5_90"'. Neither can I find  a hex  file to program directly into the device.  Should I be able to compile and run the program?

I generally use PSoC Creator 4.3.  It uses cy_boot_v5_90 by default.  I've attached a revised version of my project that uses the cy_boot_v5_80 available in Creator 4.2.

The file main.c includes 2 function prototypes

void InitializeWDT(void);

void WdtInterruptHandler(void);

neither of these functions appear to be defined or used. Are they defined/used elsewhere?

My project was based off of a PSoC6 project that used the WDT.  My new project eliminates these functions that I eliminated to make the project simple.

In the comments just before the definition of main() it states

" Summary: This is the main function for CM4 CPU. It configures the WDT in either reset mode or periodic interrupt mode."

As I understood it, the watchdog timer in the pSoC5 devices was only capable of generating a reset. Am I mistaken? There appears to be nothing in the definition of main to select which of these modes it will operate in.

My project was based off of a PSoC6 project that used the WDT  Ignore.

After starting the watchdog timer, you enable global interrupts by calling __enable_irq();

Does the watchdog timer need interrupts to be enabled?

Looking at the definition of enable_irq() it states it 'Can only be executed in Privileged modes.' Should I use CyGlobalIntEnable;  instead?

My project was based off of a PSoC6 project that used the WDT.  Yes.  New project changed to CyGlobalIntEnable although for now no interrupts are being used.

Your project does not address the question raised in my last message, namely that my test program returned CyResetStatus indicating a watchdog reset as would be expected when my project did not include a boot loader and bootloadable component,  but when I include a bootloader and a bootloadable component, the CyResetStatus indicates a software reset, which I assume to be due to Bootloadable_Load executing a software reset.

I need some means of preserving the reset status in the bootloader so that I can read it in my main application to determine the actual cause of the reset. How can I do that.

I apologize for not understanding the crux of your problem earlier.  I now see that the Bootloader you are using needs to pass the CyResetStatus to the Bootloadable code.

Some thoughts:

In effect what you need is a shared variable between the Bootloader and the Bootloadable code.  This shared variable can be in RAM or EEPROM but the address of this variable MUST be common between both codes.

Check with Cypress if they already have a shared variable similar to CyResetStatus where the Bootloader reads CYREG_RESET_SR0 and places it in a place where the Bootloadable code can get to it.

If this doesn't exist, you may have to modify the Bootloader and Bootloadable component code to provide this support.

Len

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

Peter,

Here is some further research on your issue.

In the Bootloader component BootLoader.h lists:

#define Bootloader_RESET_SR0_REG           (* (reg8 *) CYREG_RESET_SR0)

#define Bootloader_SCHEDULE_MASK            (0xC0u)        // RESET_SR0 bits(gpsw_s[1:0]) => General purpose status for user software.

#define Bootloader_GET_RUN_TYPE       (Bootloader_RESET_SR0_REG & Bootloader_SCHEDULE_MASK)  // Mask the RESET_SR0 for gpsw_s[1:0] bits only.

It appears the Bootloader which gets executed first at reset does not preserve the cause of the reset as you are witnessing.

Once the Bootloader determines it is to launch into the Bootload application it causes a SW reset which clears the previous setting of the CYREG_RESET_SR0 and only the gpsw_s[1:0] bits are set.

It is possible for you to copy the Bootloader component and modify the code to add the preservation of the original CYREG_RESET_SR0 that caused the reset.  If I'm correct, the change needs to be placed early in Bootloader_Start()  located in Bootloader.c

As indicated in the earlier post you need a variable in a CPU resource that is preserved after a reset (with the exception of a POR).  This resource location/address needs to be understood between the Bootloader and Bootable application.

There are potentially 3 methods to pass the original reason for the reset to the application:

  • Store the contents of CYREG_RESET_SR0 in RAM and the Bootable application MUST access this BEFORE the standard RAM initialization phase.  PRO: RAM is cheap and quick.  CON: Unless caution is taken in the app's startup code, RAM normally get "reset" to 0.
  • Store the contents of CYREG_RESET_SR0 in a non-used CPU Register that doesn't get reset to default values after a SW reset.  PRO: Cheap and quick.  CON: Finding a CPU register that survives a SW reset without modification may be a challenge.  Additionally Cypress use of CPU registers may not be consistent across devices or upgrades in builds.
  • Store the contents of CYREG_RESET_SR0 in a EEPROM location.  PRO: EEPROM is designed to survive a reset as well as a POR.  The Bootable app can look at this variable in main() rather than in the startup code before main().  CON:  Once the reset reason is determined, this EEPROM should be cleared.  This is an extra write to EEPROM.  If there are MANY EEPROM writes (to set and clear), then you might have a problem with write endurance of the EEPROM.  (1M cycles @ 25C).  If the resets are few, there should be no problem.  A EEPROM write operation will take about 20msec.  In the Bootloader, it will take this additional time to write the reset cause.  If this is not an issue of the additional latency, then no problem.  (Note:  It should be possible to start the EEPROM write, let the Bootloader validation and Bootable application validation finish then wait for the completion of the EEPROM write.  This should minimize the latency).

I personally think the EEPROM method might be your best bet.  I'd think that placing the share EEPROM var at the top of EEPROM memory would be best.  This is because some bootloaders might be using the low EEPROM memory for other purposes.

Len

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

Peter,

I might have some more useful information.  Here some information about the unused Cypress register option.

Look in the file Cm3Start.c

Look for the function Reset().  It should have a line:

*(reg32 *)(CYREG_PHUB_CFGMEM23_CFG1) = *(reg32 *)(CYREG_RESET_SR0);

Hopefully it isn't conditionally compiled out.

This is how the normal app (non-bootload) perserves the CYREG_RESET_SR0 register.

In the function initialize_psoc() there is a line:

CyResetStatus = CY_GET_REG8(CYREG_PHUB_CFGMEM23_CFG1);

This is how the preserved RESET cause is pushed into CyResetStatus for use in main().

What might work is that you need the Bootloader to perform the line in Reset() however you need to prevent this same line in Reset() in the Bootloadable code.

If this works, you can pass the true reset reason from the Bootloader to the Bootload app using the same variable CyResetStatus without needing a EEPROM write.

Easy-Peasy!?

Len

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

That looks like a rather neat solution, however, if I comment out the line

*(reg32 *)(CYREG_PHUB_CFGMEM23_CFG1) = *(reg32 *)(CYREG_RESET_SR0);

in the definition of Reset() in Cm3Start.c then when I recompile the project, that line in Cm5Start.c has reverted to what it was originally, i.e. not commented out.

Presumably the file is re-created every time the project is compiled.

Is there any way I can prevent that from happening?

0 Likes
*(reg32 *)(CYREG_PHUB_CFGMEM23_CFG1) = *(reg32 *)(CYREG_RESET_SR0); // Push reset reason into TD chan #23

Peter,

The issue of the Cm3Start.c re-creation occurs when you Clean the project or make any changes to TopDesign.  Cm3Start.c is a part of cy_boot component.  When you make ANY changes to TopDesign, it automatically re-builds ALL components including cy_boot.

You can place a "read-only" on Cm3Start.c after the modifications but Creator doesn't like it and will stop the re-build.

You can copy the modified Cm3Start.c to your root of the project.  But a clean and rebuild will complain about two versions of the same file being present for the build.

There might be another choice.

When Cm3Start.c pushes the CYREG_RESET_SR0 into CYREG_PHUB_CFGMEM23_CFG1

*(reg32 *)(CYREG_PHUB_CFGMEM23_CFG1) = *(reg32 *)(CYREG_RESET_SR0);

it is pushing the reset reason into a currently unused register that happens to be the 24th DMA TD channel.  Since you're not using DMA this early in the boot process, no harm, no foul.

Let me propose the following sequence:

/* Start of the Bootloader phase */

void Reset(void)

{

...

*(reg32 *)(CYREG_PHUB_CFGMEM23_CFG1) = *(reg32 *)(CYREG_RESET_SR0);      // Push reset reason into TD chan #23

...

void initialize_psoc(void)

{

...

/* Was stored in CFGMEM to avoid being cleared while SRAM gets cleared */

CyResetStatus = CY_GET_REG8(CYREG_PHUB_CFGMEM23_CFG1);     // push the reset reason into RAM var

...

void main(void)

{

*(reg32 *)(CYREG_PHUB_CFGMEM22_CFG1) = CyResetStatus;   // Push reset reason into TD chan #22

/* The goal here is to push the reset reason into another TD channel reg not being used. 

I believe this 'push' will survive a SW reset into the bootloadable app. */

...     // wait for the bootloadable app to take over.

/*  This is the end of the Bootloader phase */

/* Start of the Bootloadable app phase */

void Reset(void)

{

...

*(reg32 *)(CYREG_PHUB_CFGMEM23_CFG1) = *(reg32 *)(CYREG_RESET_SR0);      // Push reset reason into TD chan #23

// This is not needed since the reset reason will always be SW reset.  Don't change it.

...

void initialize_psoc(void)

{

...

/* Was stored in CFGMEM to avoid being cleared while SRAM gets cleared */

CyResetStatus = CY_GET_REG8(CYREG_PHUB_CFGMEM23_CFG1);     // push the reset reason into RAM var

// This is not needed since the reset reason will always be SW reset.  Don't change it.

...

void main(void)

{

CyResetStatus = *(reg32 *)(CYREG_PHUB_CFGMEM22_CFG1);  

/* Push reset reason from TD chan #22 into CyResetStatus */

/* Now CyResetStatus should contain the reset reason seen by the Bootloader. */

if (CY_RESET_WD == CyResetStatus)

{ ... }

...     // Continue with Bootloadable app code

/*  This is the end of the Bootloader phase */

If I am correct, the above code sequence should allow you to avoid making changes to Cm3Start.c and the changes occur in main.c which is under your control.

Len

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

Peter,

It is solved!

Attached is a Bootloader/Bootloadable example project that proves the proof of concept.

It is VERY simple.

The Bootloader adds ONE line of code immediately entering main().

/* Save 'true' CyResetStatus to DMA config TD chan 22. Pass to Bootloadable code */

*(reg32 *)(CYREG_PHUB_CFGMEM22_CFG1) = CyResetStatus;

The Bootloadable adds ONE line of code immediately entering main().

/* Recover 'true' CyResetStatus from DMA config TD chan 22. Passed from Bootloader code */

CyResetStatus = *(reg32 *)(CYREG_PHUB_CFGMEM22_CFG1);

Now CyResetStatus contains the 'real' reason for the reset that survives a Bootloader intervention.

Note:  In the example project.  The Bootloader will only wait 5 seconds with the LED on to allow Bootloader Host to load in a new Bootloadable app. (If there is no app it will wait forever).  Once the 5 seconds expires (with the LED ON), you will get either 8 to 10 long blinks (due to a Reset event) or 3 quick blinks (due to a watchdog event).

To cause the watchdog, make sure the WDT_dtct_reset app is loaded.  Wait for the RESET blinking to complete.  The LED should be off.  Press the SW1 (P2.2) for about 4 seconds.  Once the LED turns back ON release SW1.   The Bootloader will keep the LED for 5 seconds then blink 3 times quickly to indicate a watchdog event.

This method uses a currently unused PSoC register that preserves the installed value through a reset.  No EEPROM resource needed.

Len

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