HCS08C: Software watchdog and detecting last routine

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

HCS08C: Software watchdog and detecting last routine

3,531 Views
BasePointer
Contributor II
Hi,
 
I'm using software watchdog method and internal COP unit.
I want to detect when the reset was occured (which line of my main routine) without using BDM.
I'm not sure this is possible for COP but I thing it is possible for software watchdog.
 
 
Code:
void main(void){  ..  ..  while(1) // main loop  {    SOFTWARE_WATCHDOG = 0; // clear soft watchdog    func1();    func2();    func3();    func4();    func5();  }}interrupt VectorNumber_Vtpm1ovf void intTPM1OVF(void){  TPM1SC_TOF = 0;   // software watchdog for 5sec  if(++SOFTWARE_WATCHDOG > 200) asm("dcb 0x8D"); // illegal opcode  ...}

Can I save interrupt return address to a register before executing illegal opcode? How can I obtain this address in interrupt routine?
 
Is there any way to detect similar address that COP reset was occured?
 
Thanks,
BP.
 
Labels (1)
0 Kudos
26 Replies

915 Views
bigmac
Specialist III
Hello BP,
 
I assume that you wish to store the previous PC address reached prior to the ISR being entered.  This is directly available from the stack.
 
word PC_location @0x0100;  // Non-initialised global variable
 
#pragma NO_ENTRY
#pragma NO_EXIT
#pragma NO_FRAME
interrupt VectorNumber_Vtpm1ovf void intTPM1OVF(void)
{
  __asm pshh;
  TPM1SC_TOF = 0;

  // software watchdog for 5sec
  if(++SOFTWARE_WATCHDOG > 200) {
    __asm ldhx 5,sp;     // Return address on stack
    __asm sthx PC_location;
    __asm ("dcb 0x8D" ); // Illegal opcode reset
  }
  __asm pulh;
}
 
Note that the global variable must not undergo ANSI initialisation.
 
This method relies on the TPM module remaining operational.  If this were to cease for any reason, the COP timeout would fail.  Perhaps you might additionally use the normal COP module to protect against this event, and add a COP reset within the ISR.
 
Regards,
Mac
 


Message Edited by bigmac on 2008-03-24 08:55 PM
0 Kudos

915 Views
BasePointer
Contributor II
Hi Mac,
 
That is exactly what I want to do.
I will store PC_Counter in NO_INIT area to prevent ansi-c initialization at start-up. This is OK.
Actually, my timer1 overflow interrupt routine is a little bit more complex than I implemented before.
If I declare a new local variables in the ISR routine, will your implementation change?
I didn't understand assembler codes that you used at all. Isn't there a C way?
I also wonder about your pragma directives NO_ENTRY, NO_EXIT, NO_FRAME. They may be a problem for my routine?
 
 
Code:
interrupt VectorNumber_Vtpm1ovf void intTPM1OVF(void){  char tmp_button_menu, tmp_button;    TPM1SC_TOF = 0;    ++counter_25ms;  ++seq_timeout;    if(++SOFTWARE_WATCHDOG > 200) asm("dcb 0x8D"); // illegal opcode  ...  ...  func1();  func5(&ptr, func2());  ...  ...}

 
I'm also using COP module as additional to soft method. But not using it like you suggested.
It seems as if it was more functional than my method. I will think it.
 
10x,
BP.
 
0 Kudos

915 Views
Denn
Contributor I
Hi BP,
 
Code:
interrupt VectorNumber_Vtpm1ovf void intTPM1OVF(void){  __asm pshh;  TPM1SC_TOF = 0;  // software watchdog for 5sec  if(++SOFTWARE_WATCHDOG > 200) {    __asm ldhx 5,sp;     // Return address on stack    __asm sthx PC_location;    __asm ("dcb 0x8D" ); // Illegal opcode reset  }  __asm pulh;}

 
Macs ISR is right. Once the ISR is entered Stack Pointer is decremented by 5 counts(5 bytes automatically pushed by MCU in the order of PCL,PCH,Index Reg- X,Accumulator and finally Condition Code Register CCR. Refer figure "Interrupt stack frame" in datasheet
 
You can see he is pushing Index Reg- H. Its necessary because the CPU register H contents will be lost if not saved after it starts executing the code after returning from ISR.Normally compilers take care of pushing H register .If its not doing then programmer has to do it.
 
SP(afetr interrupt)--> SP( before interrupt)+6 after this push H
 
Since you wanted the address of the code in main function to be stored before the reset he is taking the contents of PC from the stack.It is located in location SP( before interrupt)-1 location 
 
    __asm ldhx 5,sp;     // Return address on stack
here he is loading H:X register with the contents of location pointed by [SP+5] which is nothing but   PCH : PCL in the stack.
 
 
    __asm sthx PC_location;
Next he is storing the contents of H:X to the locationPC_location.Now PC_location contains the PC value(16 bit program memory address executed just before the interrupt has come)


BasePointer wrote:
If I declare a new local variables in the ISR routine, will your implementation change?

Yes it will change in terms of stack usage. You need to change line    __asm ldhx 5,sp;     // Return address on stack
If you are using 2 local variables then offset in the above line is incremented by 2
i.e. __asm ldhx 7,sp;
 
Thats all.
 
I really doubt you can implement these on C because here one needs to touch CPU registers and that could be done only by assembly language.
 
Regards,
Denn.
0 Kudos

915 Views
JimDon
Senior Contributor III
You should have no troubles doing this in "C".
Make the local variable static, so it is just in ram, not on the stack.
This is not a re-entrant routine so this should not be a problem.

0 Kudos

915 Views
Denn
Contributor I
Hello Jim,

 

JimDon wrote:
You should have no troubles doing this in "C".


 
How will you read the contents of stack (we are interested in Program counter -PC values )in "C" ?
 
 
 

JimDon wrote:
Make the local variable static, so it is just in ram, not on the stack.
This is not a re-entrant routine so this should not be a problem.



When one can acheive the objective by using local variables, why try  unnessarily put variables in RAM by making it as static.
 
All one needs to do is increment the offset by the number of local variables used in the ISR.
__asm ldhx offset,sp; // offset=5+(number of local variables used in ISR.)
 
In our case Offset=5+2=7
 
 
Hello Mac,
 
Isn't storing the contents of PC to non-volatile memory location(flash or any other nonvolatile memory) necessary rather than RAM (0x100)before reset ?
 
 
Regards,
Denn.

 


Edit- Bold font size

Message Edited by Denn*** on 2008-03-25 05:48 AM
0 Kudos

915 Views
JimDon
Senior Contributor III
Denn,
"How will you read the contents of stack (we are interested in Program counter -PC values )in "C" ?"

I was referring to the local variable. Actually, it is possible to get the address of the PC, if you do have a local, non static variable. Since that variable is on the stack, the PC will be a certain offset from it. It is done all the time, bit it is machine dependent, which is this case does not matter.

 "When one can acheive the objective by using local variables, why try  unnessarily put variables in RAM by making it as static."

A local variable is just scoping by the compiler. All variables are in RAM, it's just if you want to see it later.
If the variable is not static, and is on the stack, you really have no way to determine is address after the fact.

local variable, not static = on stack.
local variable, static  = not on stack

Also, id you are going to play stack game and you are worried about the compiler optimizing away variable, declare them volatile.

Sorry if I seemed to have annoyed you. You need to not take this so emotionally. It just some ideas to think about. I don't come here to get 5 star  ratings, so rate me by how you feel, rather that what I say. You can't always be right, and if I am not the rate me 1. But at least take the time to try and understand what I say,.

In flash would probably be better, and since you are crashing the machine with an illegal opcode anyway, you could try that.



0 Kudos

915 Views
Denn
Contributor I
Hello BP,
 
Can you try with local declaration with "volatile char val1, val2" and try 
(&val1 + 5)
 
Please update me on the results.
 
 
 
 
Hello Jim,
 
"I was referring to the local variable."
THe question BP put was whether he can replace below assembly with C code
 "   __asm ldhx 5,sp;     // Return address on stack
    __asm sthx PC_location; "
 
Nothing else . The question of converting the local to static didn't arise itself.To the BP's question I replied that assembly was necessary, to which you gave some answer which was no where near the answer expected.Seeing your answer why should I go mad or disturbed ?emotional ??!!??? No way!!!
 
"it is possible to get the address of the PC, if you do have a local, non static variable"
You never said the answer when it was really required and when someone gives them, claiming with confidence doesn't sound nice.I agree I am not right most of the time and so are you.
 
"A local variable is just scoping by the compiler. All variables are in RAM, it's just if you want to see it later.
If the variable is not static, and is on the stack, you really have no way to determine is address after the fact.

local variable, not static = on stack.
local variable, static  = not on stack
 "
Yes . You are right. All variables are physically on RAM, but segment wise locals on STACK segment while static and global on DATA segment(variables alive till the power is off).
 
But the benifits of going for stack space is that once you come out of that function those variables are removed (dereferenced) and the same memory can be used for other local variables.BUT in global/static(RAM) variables the memory is simply allocated permanently even if you use it just for a single time.So its better to go for locals and global/static used only when it is really required aka resuability concept.
 
 
To stop compiler optimization on a variable  "volatile" keyword could be used.You are very much right.
 
 
" Sorry if I seemed to have annoyed you. You need to not take this so emotionally. It just some ideas to think about. I don't come here to get 5 star  ratings, so rate me by how you feel, rather that what I say. You can't always be right, and if I am not the rate me 1. But at least take the time to try and understand what I say,."
 
You didn't annoy me.About trying  to understand what you say, I assure you from today especially for your postings I try to do that sincerely:smileyhappy:.But you should try to understand what others are asking rather than giving some wayward answers!!!:smileywink:
 
Really I was surprised to see 5 rating for myself.Just like you even I don't come here for 5 rating :smileyhappy:
 
 
 
Your friend,
Denn.
0 Kudos

915 Views
JimDon
Senior Contributor III
Denn,
Glad to your  hear not upset.

Well, since you asked, this is general how you get the return address in "C" off the stack.
Stack forensics is quite interesting, and can be used to good effect after a crash.

You may have to adjust the offset for an interrupt stack.



Code:
word ReturnAddress;#define OFFSET   1
void GetMyCaller(void) {
    byte marker;
    ReturnAddress = *((word*) (&marker +OFFSET));
}


 

0 Kudos

915 Views
CompilerGuru
NXP Employee
NXP Employee
That works fine, the only problem is that the OFFSET value depends on what exactly the GetMyCaller
function contains. So any added C code will affect the OFFSET and the changed value needed can also not be easily anticipated.
So in order to avoid any assumptions of how the compiler allocates locals or generates certain code, I would code it like in the sample below, basically by moving the C code into its own function so that the code accessing the stack is in a HLI only routine.
Note that, to keep it simple, I did not use the COP/Timer interrupt, just the SWI. And I did not protect the variable from being overwritten at reset either.

Daniel


Code:
#include <hidef.h> /* for EnableInterrupts macro */#include "derivative.h" /* include peripheral declarations */int ReturnAddress;struct InterruptStackFrame {  unsigned int PC;  unsigned char X;  unsigned char A;  unsigned char CCR;  unsigned char H;  // as stored by PSHH, not part of automatic stack frame};void UseReturnAddress(struct InterruptStackFrame* frm){  ReturnAddress= frm->PC;}#pragma NO_ENTRY#pragma NO_EXIT#pragma NO_FRAME// HLI only implementation to make sure stackframe is exactly how we expect it to be.interrupt VectorNumber_Vswi void swi_handler() {  __asm PSHH  __asm TSX#if defined(__HC08__) && !defined(__HCS08__)  __asm PSHH ; // S08: passes 16 bit in H:X, HC08 in A:X,  __asm PULA ; // have to move H to A for HC08.#endif    __asm JSR UseReturnAddress   __asm PULH  __asm RTI}void main(void) {  EnableInterrupts; /* enable interrupts */  /* include your code here */  for(;;) {    __asm swi;    __RESET_WATCHDOG(); /* feeds the dog */  } /* loop forever */  /* please make sure that you never leave main */}

 

0 Kudos

915 Views
bigmac
Specialist III
Hello Daniel,
 
A minor point - if my understanding is correct, I did not expect to see the inline RTI instruction included within the interrupt function.  Alternatively, with explicit inclusion of the inline RTI instruction, I might have also expected to see the NO_RETURN pragma used. 
 
But perhaps it doesn't really matter, since the final RTI instruction generated by the compiler would never be reached.
 
Regards,
Mac
 
0 Kudos

915 Views
JimDon
Senior Contributor III
Well, you do have to adjust the OFFSET for the circumstances, but once properly adjusted it is an invariant after you determine the setting for a given set of conditions.

Each solution has it pros and cons, and I am loathe to declare any workable solution absolutely correct or wrong.

While your solution is indeed an elegant one, certainly befitting of a Guru, mine (well,.not mine - it's been in use for probably 30 years) is usable on virtually any programming system, and while the offset will need adjustment, it is far simpler and more transportable. While requiring adjustments, it is not compiler dependent as far building on any system. Your solution only works on exactly one compiler, and I would like to know how to implement yours on GNU. I'm sure it can be done, if you are will to spend the time to figure it out.

Also note that for a given compiler you will quick see exactly how to adjust the offset for a given set of conditions.

(BTW I know this is a long and chatty thread, and I don't blame you for not carefully reading the entire thread, but Mac presented and similar solution that seemed fine - this solution was present as an all "C" usable anywhere solution as an alternative)



Message Edited by JimDon on 2008-03-27 05:21 PM
0 Kudos

915 Views
CompilerGuru
NXP Employee
NXP Employee
For me, the main thing about that version is not that it's using a struct to show all the stack context or that it is using HLI to define the access. It is to separate all the code which could cause that the compiler generates a stack frame which could interfere with the PC retrieval.
For both the C and Macs version, one of the "setting for a given set of conditions" is all the C code in the same function. And as the original poster already found out, all the other C code in the same function is part of those conditions, I would add the exact compiler version and the options used to compile it too (-or and more locals end up in registers). So the main proposal was to move all the "custom" code into its own function and to let little (?) left for the compiler which could cause the code not to work as expected on a S08.
About the compiler dependency, to my understanding every version to get the PC is compiler dependent.
I agree that the OFFSET version easier to port to other architectures, but given the nature of the problem I would protect this function with some #ifdef's not to compile it on architectures/compilers I did not test/adapt the OFFSET for. And for a gcc version of it, I would actually use the gcc things:

http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Return-Address.html

The initial thought why I used the struct was to explicitly show where the offset to the PC comes from.

Daniel

PS2: Ups, the RTI is indeed not necessary, can be dropped or a #pragma NO_RETURN could be added just as bigmac noticed. I did somehow forget that #pragma NO_EXIT does not drop the return on its own.
0 Kudos

915 Views
CompilerGuru
NXP Employee
NXP Employee
I would just use Macs routine as it is, with all the #pragmas ahead and add a single function call to a C-only routine after the return address has been assigned in inline assembly.
Probably counting the locals and adding that as offset works too, but you can never be really sure, as the compiler will eventually not allocate some locals if they are not actually accessed on the stack, and the compiler may also allocate more space for temporaries. So placing the C code into a function of their own avoids all such problems. Using statics instead of locals causes that the memory of those variables is not shared, also I prefer to use stack space even for such special functions (or main, or functions which do not return, or..).
Anyway, what is best probably also depends on how much else there is to do in C and if you can do that in assembly too.
If you are storing the found PC in RAM or in flash depends on what you do with it. Storing it in a variable allocated in a NO_INIT segment is good enough to have it available after the next reset. If it has to be  available even after unplugging the power, then  store it in flash, but I doubt that this is really necessary.

Daniel

0 Kudos

915 Views
Denn
Contributor I
Hello Mac,
 
Sorry !!! The RAM data is lost only after POR/LVD resets. We are looking for PC values stored(in RAM) after an  illegal opcode reset.
 
Datasheet says "At power-on, the contents of RAM are uninitialized. RAM data is unaffected by any reset provided that the supply voltage does not drop below the minimum value for RAM retention."
 
 
Hello Daniel,
 
You are right when you say unused local variables doesnot get stored in stack.They are simply ignored by the compiler.
 
 
 


CompilerGuru wrote:
the compiler may also allocate more space for temporaries.


But why do compiler assign more memory in stack in case of local variable ? I think the memory allocated for locals is deteministic.
 
Best Regards,
Denn
0 Kudos

915 Views
CompilerGuru
NXP Employee
NXP Employee
Even used variables may not get allocated on the stack if the compiler only needs them in a register ever. Not very often, but this happens.
Also for normal C code the compiler does sometimes need some storage to store intermediate values, he usually allocates them by just decreasing the SP, but sometimes the intermediate has to live a bit longer and then he can also use "static spills", the space for those is allocated like the space for locals in the entry code. In the end, if you are only using simple C code snippets, then you probably do not see this often, but it does exist.

Daniel
0 Kudos

915 Views
bigmac
Specialist III
Hello BP,
 
With the method that I proposed, this requires that the address be read directly from the contents of the stack.  Therefore the exact position of the address on the stack needs to be totally predictable.  The purposed of the pragmas is to prevent the compiler from manipulating the stack of its own accord, which would affect the stack position read if it were to do so.  I don't know of any alternative to the use of inline assembly.
 
Any variables referenced by the ISR should be either global or static so that they will not utilize the stack.  Even if local variables were to be declared after the address value was read, the presence of the pragmas would mean that the stack pointer would require to be manually adjusted at the conclusion of the ISR.  To avoid this complication is is better to use variables that do not use the stack.
 
I would probably do the timeout test first, when the ISR was first entered, and then continue with the other requirements of the ISR.
 
Regards,
Mac
 
 
0 Kudos

915 Views
BasePointer
Contributor II
Hi,
 
I found the PC return address in stack by debuging it. If I add or remove a local variable to the routine, place of the address is changing. I have to take care that, before changing the routine.
 
Code:
interrupt VectorNumber_Vtpm1ovf void intTPM1OVF(void){  // timer1 overflow interrupt routine  char val1, val2;    TPM1SC_TOF = 0;    ++counter_25ms;  ++seq_timeout;    if(++SOFTWARE_WATCHDOG > 200)   {    NV.Statistic.LAST_PROGRAM_COUNTER = *(unsigned int*)(&val2 + 6);    asm("dcb 0x8D"); // illegal opcode  }...

 
Thank you so much for the helps!
BP.
0 Kudos

915 Views
Denn
Contributor I
Hello BP,
 
Glad to see that you fixed the problem.
 
But make sure that Index register is pushed to the stack at the entry of ISR(similarly popped from stack at exit of ISR) by the compiler.If itsnot doing then you need to do that manually as in Mac's ISR.
 
I feel the compiler is pushing [H] register by its own. Now the stack is something like this in increasing address order
 
Val2
Val1
[H]
[CCR]
[Accumulator]
[X]
PCH
PCL
 
 
(&val2 + 6) =location where PCH is stored.     
Then you read two bytes since typecasted to int and hence entire PC value (16 bits)
 
 
 
Regards,
Denn.
 
 
0 Kudos

915 Views
Denn
Contributor I
Hello BP,
 
Instead of reading address of 2nd local variable, I would suggest you to read address of 1st local variable and hence the adding/ removing local variables has no effect on your routine.The only necessity is that you shouldn't remove the 1st local (i.e. routine without locals) and it should be used somewhere in the routine. 
 
(&val1 + 5) =location where PCH is stored.   // here 1st local is used
 
 
 
Regards,
Denn.


Message Edited by Denn*** on 2008-03-25 09:35 AM
0 Kudos

915 Views
BasePointer
Contributor II
Hi Denn,
 
If I use val2, here is the stack list:
[val2]
[val1]
[H]
[CCR]
[Accumulator]
[X]
[PCH]
[PCL]
--------------------------------------------
If I use val1 instead of val2 and, (&val1+5), I think the compiler optimizing the code and changes stack list: :smileyindifferent:
[val1]
[val2]
[H]
[CCR]
[Accumulator]
[X]
[PCH]
[PCL]
So, (&val1+6) and (&val2+6) are working, others not.
 
10x,
BP.
 
0 Kudos