Need to write function address behind the stack pointer before RETI-ing to that function

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm trying to context switch the main program so that when my Monitor Program says break, the USARTC0_RXC interrupt ends and the stack pointer comes back to the function MP_deadlock.

	if(MP_ContextRequested&&MP_CurrentContext==0)
	{
		CPU_SPH=0x2F;
		CPU_SPL=0xFD;
		MP_tmphand=(char*)0x2FFD; // This is a temporary char-paster
		*MP_tmphand=0x00; // Put 0 on 0x2FFD because that's the extended byte of the MP_deadlock address
		MP_tmpptr=(uintptr_t*)(0x2FFE); // This is a temporary 16-bit pointer-paster
		*MP_tmpptr=(uintptr_t)MP_deadlock/2; // Paste MP_deadlock address on stack so that when we...
		MP_CurrentContext=1;
		asm("sei");
		reti(); // ...RETI, that we get on that function!
	}

The problem that I have constantly is that CPU suddenly enters bootloader. Could it be because of stack underflow or because the value on stack was so big that the program counter just sprinted away to the bootloader?

Last Edited: Thu. Feb 19, 2015 - 05:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What CPU is this?

 

I'm sort of assuming AVR from the "sei" and reti(). Which raises the next question. Why would you ever sei() and reti() as well? A reti() is a RET with an SEI built in.

 

Can you take a step back and say what it is you are tying to achieve here? Is this in your WDT_vect? Is the idea that because the user program has "locked up" that you just want to abandon the execution of their code and, instead, continue at your "MP_Deadlock" function?

 

If so why all this stack jiggery-pokery? What is wrong with a simple:

SP = back_to_start_value
asm("jmp MP_dealock");

or if you don't want to use asm() just:

SP = back_to_start_value;
MP_deadlock();

(the only downside of which is that the function is CALL'd so a return address to it is stacked).

 

Have the MP_deadlock() function just sei() or cli() as you require. (I'm assuming if the user code crashed you probably don't want to potentially jump back into any of his interrupt handlers that might be active because of interrupts he might have enabled - so I'd stick with I clear until all interrupt sources are turned off.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You don't get it!

The RXC interrupt MUST RETI!!! Otherwise I won't be able to write characters into USART!

However, when it RETI-s, it has to jump on NOT the address of the main program, but to the MP_deadlock so that it can be frozen until I input a command called "continue".

 

What I want to know is how do I put a function pointer on that place

CPU_SP    VV
OLD STACK 00 00 0A 00 XX XX XX

CPU_SP    VV
NEW STACK 00 00 09 87 XX XX XX
             ^^-^^-^^--- How do I put the function pointer there!?

After RETI, this happens
CPU_SP             VV
NEW STACK 00 00 09 87 XX XX XX
CPU_PC    0x000987

void MP_deadlock() at 0x000987
{
    while(1);
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
uint8_t * pStk8 = (uint8_t *)SP;
uint16_t * pStk16 = (uint16_t *)SP;

pStk8[3] = 0x55; // 0x55 to 3 bytes above current SP
pStk16[7] = 12345; // 12345 to 14 and 15 bytes above current SP
etc.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't understand

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi!

The RXC interrupt MUST RETI!!! Otherwise I won't be able to write characters into USART! However, when it RETI-s, it has to jump on NOT the address of the main program, but to the MP_deadlock

If there is atMega then "sei()" is enough.

 

If you use atXmega, then you need use assembler (or I do not konw how to write ISR without "reti" in C): use helper function with "reti" only:

 

ISR....

 .... save regs ....

 ....

 call RetI_Helper

 call MP_deadlock

 .... restore regs...

 ret  /* not reti !!! */

 

RetI_Helper: reti

 

 

Ilya

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't understand

Sorry, which bit don't you understand. Apart from "SP" this is "plain C".

 

The AVR-LibC defines "SP" as a 16 bit registers located at the I/O address of the Stack Pointer in an AVR:

include/avr$ grep SP common.h 
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
and no option for external RAM), thus SPH is officially "reserved"
#  ifndef SPL
#    define SPL _SFR_MEM8(0x3D)
#  ifndef SPH
#    define SPH _SFR_MEM8(0x3E)
#  ifndef SP
#    define SP _SFR_MEM16(0x3D)
#  ifndef SPL
#    define SPL _SFR_IO8(0x3D)
#    ifndef SP
#      define SP  _SFR_IO8(0x3D)
#    ifndef SP
#      define SP  _SFR_IO16(0x3D)
#    ifndef SPH
#      define SPH _SFR_IO8(0x3E)
#if defined(SP)
#  define AVR_STACK_POINTER_REG   SP
#    define AVR_STACK_POINTER_ADDR  _SFR_MEM_ADDR(SP)
#    define AVR_STACK_POINTER_ADDR  _SFR_IO_ADDR(SP)
#if defined(SPH)
#  define AVR_STACK_POINTER_HI_REG   SPH
#    define AVR_STACK_POINTER_HI_ADDR  _SFR_MEM_ADDR(SPH)
#    define AVR_STACK_POINTER_HI_ADDR  _SFR_IO_ADDR(SPH)
#if defined(SPL)
#  define AVR_STACK_POINTER_LO_REG   SPL
#    define AVR_STACK_POINTER_LO_ADDR  _SFR_MEM_ADDR(SPL)
#    define AVR_STACK_POINTER_LO_ADDR  _SFR_IO_ADDR(SPL)

the key line in that being:

#    define SP _SFR_MEM16(0x3D)

So when I type:

uint8_t * pStk8 = (uint8_t *)SP;

I'm saying go and read the 16bit value from IO address 0x3D/0x3E and assign it as a pointer to uint8_t to a variable called "pStk8" - I hoped the variable name was meaningful. The "p" says "pointer", "Stk" is just a shortened form of "Stack" and the "8" indicates it's pointing to 8 bit wide data (ie bytes). So this is an 8 bit pointer to wherever the Stack Pointer was pointing to. Then when I use:

pStk8[3] = 0x55;

The array notation is just (IMAO) an easier to understand way to say the same thing as:

*(pStk8 + 3) = 0x55;

which means take that pStk8 memory address, add 3 bytes to it (so 3 above where SP was pointing) and then write 0x55 through to that location.

 

My other access was to also take SP but interpret it as a pointer to 16 bit words rather than 8 bit bytes. Then:

pStk16[7] = 12345;

which is the same as:

*(pStk16 + 7) = 12345;

Says take the address SP is pointing to. Add 7 16 bit words (14 bytes) to the value and then write 12345 through to whatever that location is.

 

This is just pretty standard stuff to write to known locations with the known base address being taken from the SP register.

 

You asked about writing a function pointer to the stack. The same would hold:

typedef void (*fptr_t)(void);

fptr_t pStkFn = (fptr_t) SP;

void hello(void) {
}

   pStkFn[4] = hello;

That example writes the address of the hello function 4 function pointer widths above the current SP.