longjmp() at the end of an ISR ? Or Goto+flag ?

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

Hi all,
I read on this forum this is not a good practice :

jmp_buf	Go_back_to_beginning;

int main (void){

	setjmp ( Go_back_to_beginning );
	
	delay_ms( 50 );  //// wait until 50ms free of ISR
	
	while(1){
		if( Something_else_is_High ){
			if( setjmp( Go_back_to_beginning ) )
				Do_SOMETHING_very_long_depending_on_updated_critical_value( critical_value );
		} else {
			if( setjmp( Go_back_to_beginning ) )
				Do_ANOTHER_THING_very_long_depending_on_updated_critical_value( critical_value );
		}
	}
}

ISR( INT0_vect ){
	Do_sometimes_something_critical_but_very_quick();
	critical_value++;

	longjmp ( Go_back_to_beginning, 1); 
}

But I don't understand why, because longjmp will return quickly 1, and then leave ISR ? No ?
It's too bad if it's not possible because, it would be cool to imagine very great structures, very flexible with that feature.

Should I use Goto instead, and then test a flag to :
- Do_SOMETHING_very_long_depending_on_updated_critical_value()
- or: Do_ANOTHER_THING_very_long_depending_on_updated_critical_value()

What would be the syntax of GOTO please (I never used it in C) ?

Thanks, all of you :wink:

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

NO!

In C, you do nothing at the end of an ISR. There is a built-in reti.

Also, every ISR has a "preamble" where a bunch of stuff is saved. If you try to exit before the natural end of the ISR, the stuff that was saved won't get restored and your program will likely fail.

Your setjmp and longjmp things make no sense.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Wed. Jun 6, 2012 - 03:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, the reti will not be called to leave the ISR : that's the problem with that structure ?

And about the GOTO instead ?

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

The GOTO violates the setup and restore operations at the beginning and end of the ISR. THese happen before the first line of written code and after the last line of written code.

My recommendation: if you want to do something like that, do not mess with c!
Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

I didn't understand your last point about GOTO : "restore operations at the beginning and end" ??
Perhaps it's because I'm french (sorry about that... ;))

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

I think setjmp and longjmp were invented to let K&R handle 64K pages in the PDP11 they were using to write unix in the early 70s. I agree it might make a good error/restart catchall. One of these old unix dudes could tell us where he has seen setjmp and longjmp used.

Imagecraft compiler user

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

There are a number of operations at the beginning of the ISR which you cannot see unless you look at the disassembled program. It saves a number of things from the main program which the ISR might accidently change. Like the status register.

Then, the ISR code runs.

Then, after the ISR code has finished, it restores the values that were saved at the start and re-enables interrupts. Again, you can see this only if you look at the disassembled program. Your strange structure would bypass the last step. Things would never be restored to the value they had before the ISR.

If you want to do what you want, do it with something other than C. There is a particular way that C is supposed to work, and you are trying to change that, C makes it very hard to do what you want.

For background on setjmp() and longjmp(), see:

http://en.wikipedia.org/wiki/Set...

What the OP proposes, IMHO, is quite different than the intended use.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Take a step back and say exactly what it is your are trying to achieve here. Why would you want an interrupt to ever do anything but simply return to the point where the interrupt occurred? What you are proposing is actually worse than simple goto code in that it's an interaction from a different thread of execution.

The implication of all this is that you have not flowcharted your design and you are trying to paper over some "hole" that has occurred because you haven't thought the design through in the first place.

I have a feeling that what you are saying is "if some condition happens in the interrupt then cancel some delay I'm part way through". If that's the case then instead of:

_delay_ms(3000);

do something like:

for (i=0; i < 3000; i++) {
  _delay_ms(1);
  if (interrupt_flag_set) break;
}

or perhaps:

i = 3000;
while(i--) {
  _delay_ms(1);
  if (interrupt_flag_set) i = 0;
}

or one of the many other ways you could arrange to break out early from a loop if some condition occurs. In this case your "latency" is only as much as 1ms.

Last Edited: Wed. Jun 6, 2012 - 03:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hummm hummm... You say "very hard". Perhaps not impossible, so ???
If i know what is not restored after called to longjmp(), I could evaluate if it's important for my application, and restore it manually if it's possible ?

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

I edited above while you typed - you may want to read my edit. Is it really something like breaking out of a long delay you are actually trying to achieve here?

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

Are you trying to implement some sort of RTOS? We get these strange inquiries, periodically, from people who are trying to do that. The usual response is a recommendation to look at how one of the "good" RTOS works.

What you are proposing is much like driving rules. Everywhere (pretty much except the UK and former colonies except Canada) folks drive one the right hand side of the road. You are asking to drive on the left hand side. Can you do that? Well, you can if the road has no other cars, otherwise BIG CRASH. C is like those driving rules. If you use C, you need to obey the rules.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Wed. Jun 6, 2012 - 03:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In fact I just want to stop the :
Do_SOMETHING_very_long_depending_on_updated_critical_value()
or
Do_ANOTHER_THING_very_long_depending_on_updated_critical_value()
...functions before their end, because they have to be reevaluated from the beginning, and they are long functions.

Of course I could test a flag regularly within those functions, and abort by return from them, or longjmp() to their beginning, but it's less efficient than direct jump from the end of the ISR.

What would be your recommandations ?

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

Quote:

Of course I could test a flag regularly within those functions, and abort by return from them, or longjmp() to their beginning, but it's less efficient than direct jump from the end of the ISR.

I don't agree. I guess this all comes back to the old argument about structured code? Where is Niklaus Wirth when you need him?

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

You are very reactive, thanks all of you !!
@Clawson : it's not only about the delay, but a general case for aborting long computing task on demand.
@Ka7ehk : Don't know much about RTOS. I believe my needings are not so critical. But I should really find a way to abort functions to do things well !

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

Why not just jump to the end of the ISR? That would solve your problem and the ISR could work the way it needs to.

The big issue is jumping OUT of the ISR.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

You can do longjmp() and setjmp() in the foreground code. I have done it myself.

I definitely would not longjmp() out of an ISR().

There are better ways of doing things. You can abort a long function with a simple return().

You can raise an interrupt when you want to signal that you have finished something. However it is just as easy to set a flag.

And there is no problem with using goto() within a function. Sometimes it is tidy. You can always invent spurious loops to satisfy the purists.

David.

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

Quote:
Why not just jump to the end of the ISR? That would solve your problem and the ISR could work the way it needs to.
Huuuu ???
Something I missed perhaps ?
You mean :
- jumping from ISR to beginning of main function,
- then, one line just after that, jump from the main function to the end of the ISR to end things properly ?
- and of course go back to the wright place...

If it's possible, it's probably the best thing to do, but is it what you mean ?

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

No, either put the jump label at the end of the ISR and to a jump to that.

Or, use David's suggestion of a return() which, behind the scenes, will do the same thing.

Never jump out of the ISR. You will have very big problems. Why make problems when the solution is so easy?

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Quote:
There are better ways of doing things. You can abort a long function with a simple return().

Yes, Clawson encouraged me on that way.
But I will have some kind of for(){float*float/float*sin(float/float...you know...} within this long function.
So it will be too bad I stop for ISR just at the beginning of this for() statement...

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

This line take 1 clock when false ?

if( Return_from_ISR_flag ) goto beginning;
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One clock in assembler. Many more in C.

In C, you generally do not worry about clock cycles. Optimize it for speed and let the compiler do what it needs to do.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Dare I even mention it but another option is to use an RTOS. That does allow you to effectively run several programs at once so the more reactive stuff (like servicing the user interface) can still run even if some heavy maths is churning away in the background (and you already decided to ignore its result when it finished anyway). However this is only marginally less pretty than jumping about!

It's certainly true that your minimal "atomic unit" is going to be something like a call to sin() and even if you abort when it returns it may have just started and could do several hundred, even thousand cycles, before control gets back to a point where you could abort.

I wonder if an OS would let you completely "delete" then restart the "heavy math" thread once you decided that what it was doing was pointless - that might get you back quicker than waiting for the longest atomic step to complete.

Last Edited: Wed. Jun 6, 2012 - 04:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And with ISR_NAKED ?

Quote:
Another solution is to still implement the ISR in C language but take over the compiler's job of generating the prologue and epilogue. This can be done using the ISR_NAKED attribute to the ISR() macro. Note that the compiler does not generate anything as prologue or epilogue, so the final reti() must be provided by the actual implementation.

It seems I would have to execute reti() anyway, so it's not a solution too ?

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

Then do not use C!

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

This is the first time C is not enough for me... But Assembly is perhaps too much for me, Jim.

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

What do you think about ISR_NAKED ?
- Entering ISR_NAKED
- Disable all other interrupt in SREG with cli()
- leaving ISR with a longjmp(), and without reti(),
- I could restore SREG just after jump at a "good beginning value" just before to begin my function again... with something like 0b10000000 ?

Could it be possible ?

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

Quote:

I could restore SREG just after jump

How? I'd suggest you restore it before but watch for a race condition - you have ONE opcode after setting I before the interrupt system actually becomes active again.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
jmp_buf   Go_back_to_beginning;

int main (void){

   setjmp ( Go_back_to_beginning );
   
   delay_ms( 50 );  //// wait until 50ms free of ISR
   
   while(1){
      if( Something_else_is_High ){
         if( setjmp( Go_back_to_beginning ) )
            Do_SOMETHING_very_long_depending_on_updated_critical_value( critical_value );
      } else {
         if( setjmp( Go_back_to_beginning ) )
            Do_ANOTHER_THING_very_long_depending_on_updated_critical_value( critical_value );
      }
   }
}

void Do_SOMETHING_very_long_depending_on_updated_critical_value(int critical_value )
{
   while (lots) {
      if (bad) longjmp(Go_back_to_beginning);
      ...
   }
}

The bad flag could be set by an interrupt.
In all honesty, the slow function could just return early to main(). The advantage of longjmp() is when you want to abort a function that is deeply nested.

David.

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

You mean I have to wait several clocks before my interrupts are globally enable again, time to write 0b10000000 in SREG ?
But this is probably the same delay than with reti() executed by classic ISR(), you don't think so ?

And I save a bit of time at the beginning of the ISR, because it's not saving the SREG before executing ISR code.

Perhaps I make a mistake, tell me ??

EDIT: @David: yes this is possible but I have to deal with horrible kind of line of code like sin(float*float/float...etc). And if I have to test flag before each multiplication of float, it may not be so efficient.

Last Edited: Wed. Jun 6, 2012 - 05:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

David,

But if you were willing to act on the interrupt flag why bother with jumping all over the place. Just:

while(lots) [
  if (bad) return;
}

but I think OP is saying the problem is:

while(lots) [
  f = sin(theta);
  f = cos (f);
  f = tan(f);
  if (bad) return;
}

and even if he breaks that into:

while(lots) [
  f = sin(theta);
  if (bad) return;
  f = cos (f);
  if (bad) return;
  f = tan(f);
  if (bad) return;
}

then it could have disappeared off down the rabbit hole for many hundreds of cycles because while(lots) really is "lots".

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

Cross response to David... But that is the point, yes.

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

Put some numbers on the table. Exactly how many cycles do you have between "doing A" and switching to "doing B" in the mainline code when something occurs in the ISR to force the switch.

You talk about naked ISRs, no RETI and other ill advised actions to save 1 machine cycle but it could be 10's of cycles before you are back to main() and 100's before a current action can be cancelled. So exactly how many cycles do you have? If a sin() took another 500 cycles to complete would it be the end of the world?

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

The advantage is when the function may appear via different routes. For example, you might putchar() or strcmp() in many places in the average app.

If the slow_function() only appears via one route, a simple return is easier.

Yes. Your second example shows that you need to test the bad flag in many places.

I dislike the idea of longjmp() from an ISR(). Mind you, setjmp() should know how to restore SREG, PC, SP etc. Before I would even consider it, I should read the setjmp() source code.

In answer to the OP, you can use goto() in main() but need longjmp() in other functions. If you want to do a LOT of research, interfering with an ISR() might be possible. The thought terrifies me.

David.

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

Could you please tell me what do you think of this :

- Entering ISR_NAKED on any kind of vector
- If needed, saving SREG to a local uint8_t
- Disable all other interrupt in SREG with cli(): but it seems it's not necessary, because ISR_NAKED already do that when entering

And then :
- If needed, restore SREG manually and reti() to leave clean
or
- leaving ISR with a longjmp() to abort long execution function, and so without reti(),
- and just after that setjmp() point, restore SREG to a standard "good beginning value" just before to begin any new line of code... with something like 0b10000000 ?

Is it possible ? I don't know very well SREG ?

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

Does GCC use the two-stack model? ie the hardware one to stack call/returns and a software one for function data frames?

Is there not a danger that at least one of them will 'burst'.

What happens when the ISR causes the long slow code to abort in the middle of a really complex section? Does the compiler put intermediate variable on the data stack? What's going to happen to the stack-frame pointer? And what about aborting inside a function call? Compilers often use internal calls for particular operations.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

Quote:
Put some numbers on the table.

Ok, it's not that long : 200 - 250µs for one, 800µs for another in pure math, but the ISR could reach 5000 Hz, so I'd like to favore good ISR counting, and depending on last measured ISR frequency, ask for a compute I know I have enough time to finish before next ISR.

In one word, compute some accurate values instead of finishing compute on older values, or beginning some kind of compute I know I can't finish on time.

The structure is not finished of course, but it could become a huge headaque to fit on 8bit MCU !

EDIT : forgot to say : on an atmega328p 16MHz

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

Does anyone understand what is done there ?

volatile register byte upcount asm("r3");  // dedicated a register to ISR var

ISR(PCINT0_vect, ISR_NAKED) {
  asm( // Standard ISR entry, minus "known zero" setup
  "push r0\n"
    "in r0, 0x3F\n"  // Save status
  "push r0" // Might not be needed
  );

  if (PORTB & 4) {
    upcount++;
  }

  asm( // low-overhead ISR exit...
  "pop r0\n"  // matches "might not be needed"
    "out 0x3f, r0\n"  // restore status
  "pop r0\n"  // restore r0
  "reti\n");

} 

Perhaps I could add asm() just after jump to main, to restore what is needed ? But what is needed ?

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

Do you really need the foreground to calculate very fast?

Most apps can process the foreground at a leisurely pace.
Your interrupts need to be serviced promptly, but quite honestly do you need to calculate on every cycle ?

You can often use the result of your calculation some 5 or 10 periods later. Few realtime systems would notice.

Of course creating audio or some fast varying signal need prompt processing.

I would avoid gobbledygook if I can. Get the algorithm designed first. Only 'speed it up' if necessary.

David.

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

clawson wrote:
but I think OP is saying the problem is:

while(lots) [
  f = sin(theta);
  f = cos (f);
  f = tan(f);
  if (bad) return;
}

and even if he breaks that into:

while(lots) [
  f = sin(theta);
  if (bad) return;
  f = cos (f);
  if (bad) return;
  f = tan(f);
  if (bad) return;
}

then it could have disappeared off down the rabbit hole for many hundreds of cycles because while(lots) really is "lots".


while(1) {
  partoflots;
  if (bad) return;
  anotherpart;
  if (bad) return;
  finishlots;
  if (!lots) break;
  if (bad) return;
  f = sin(theta);
  if (bad) return;
  f = cos (f);
  if (bad) return;
  f = tan(f);
  if (bad) return;
}

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

@David : in fact I need to study dynamic of the signal (fast varying that's true) for the better result possible. And high latency in acquisition give poor results : the lower it is, better are the results.
That's why I'm looking for the better schem before another try...

Actually, I'm looking for all registers I'll need to restore after my jump from an interrupt, and from what initial state ? Because setjmp() already register some state values... Which are restored when longjmp().

So in addidtion to that already restored state : what would be necessary to restore after an interruption ?

About ISR, would it be different kind of registers to restore when exiting from different kind of ISR (ADC, INT, PCINT, TIMER, TWI...), or I can fixe a general restore procedure, valid after every type of interrupt ?

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

No, I doubt if there is much else to restore.

Most AVR interrupts clear the flag that caused them automatically. TWINT and PCINT need manual reset. So when longjmp() restores SREG, SP, R0-R31 you should be pretty straight.

But it is up to you to check what setjmp() has saved. And it is your risk if something is not managed properly.

David.

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

Quote:
Quote:
This line take 1 clock when false ?

if( Return_from_ISR_flag ) goto beginning; 


One clock in assembler. Many more in C.
I disagree on both counts. The flag has to be tested first (at least one clock), then the branch (one clock if the branch is within range of a brne, and more if you need to use rjmp or jmp). In C, there will likely be a load from memory which may be as little as 2 clocks, and the rest would likely be the same as assembler.

Regards,
Steve A.

The Board helps those that help themselves.

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

I have strange behaviour with this simple test on arduino Nano :
With :
TCCR2A = 0b00000000;
TCCR2B = 0b00000110;

	jmp_buf	Go_back_to_beginning;
	jmp_buf	Go_back_to_end;
	
	volatile int cpteur_ISR = 0;
	int cpteur_jump_from_ISR = 0;
	volatile unsigned long increment_out_of_ISR = 0;
	volatile uint8_t oldSREG;
	volatile unsigned long timer_debut_ISR = 0;
	
	volatile boolean FLAG_out_of_ISR = 0;
	
	
	void loop()
{

	boolean INIT_jump_point = 0;
	
	#define		WITH_JUMP			1
	#define		NAKED				0
	
	#if			WITH_JUMP	==		0
		TIMSK2 = 0b00000001; ////////// enable ISR 
	#endif
	
	while(1){
	
		#if		WITH_JUMP		==		1
			if( setjmp ( Go_back_to_beginning ) ){
				Serial.println(  micros() - timer_debut_ISR  );
				
					// while( FLAG_out_of_ISR ){ ///// you should test this too, it's funny behaviour too
					while( 1 ){
						increment_out_of_ISR++;
					}
					
				} else {
				
					if( !INIT_jump_point ){
						INIT_jump_point = 1;
						TIMSK2 = 0b00000001; ////////// enable ISR after the setjmp() is set
					}
				}
			
			#else
					
					while( !FLAG_out_of_ISR ){
						increment_out_of_ISR++;
					}
					
					Serial.println(  micros() - timer_debut_ISR  );
					FLAG_out_of_ISR = 0;
			
		#endif

	}
}


ISR( 	TIMER2_OVF_vect
		#if	NAKED			==		1
		, ISR_NAKED
		#endif
		) { 
		
	#if	NAKED			==		1
		cli();
	#endif
	
	timer_debut_ISR = micros();
	Serial.println(increment_out_of_ISR);
	increment_out_of_ISR = 0;
	
	FLAG_out_of_ISR = 1;
	TCNT2 = 0;
	
	#if		WITH_JUMP		==		1
		longjmp ( Go_back_to_beginning, 1);
	#elif	NAKED			==		1
		reti();
	#endif
}

It's not representative of my actual needings, but you are right : results should be very near between jump or flag read solution ?

But it's not the case : when Jump is enabled, it works like this, without restoring anything (probably no SREG change in this simple test), and both timer and increment are very stable reproductive.
And without the jump : time inside the ISR is quite the same, but the increment outside the ISR is very unstable (even if in mean it may be the same value).

Naked doesn't change a lot the execution time of the ISR.

Don't know what you think about, but to drive all your code execution time based on ISR, including the main() part, it seems to be a not so bad way to code, no ?

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

This result is quite impressive :

	jmp_buf	Go_back_to_beginning;
	jmp_buf	Go_back_to_end;
	
	volatile int cpteur_ISR = 0;
	int cpteur_jump_from_ISR = 0;
	volatile unsigned long increment_out_of_ISR = 0;
	volatile unsigned long increment_end_compute = 0;
	volatile uint8_t oldSREG;
	volatile unsigned long timer_debut_ISR = 0;
	volatile unsigned long timer_debut_OUTSIDE_ISR = 0;
	
	volatile boolean FLAG_out_of_ISR = 0;
	volatile unsigned long total_pr_moyenne_OUTSIDE_ISR = 0;
	volatile unsigned long total_pr_moyenne_INSIDE_ISR = 0;
	volatile unsigned int denominateur_inside = 0;
	volatile unsigned int denominateur_outside = 0;
	
	volatile unsigned long ECART_entre_ISR = 0;
	
	float global_1 = 1.0;
	float global_2 = 2.0;
	float global_3 = 3.0;
	float global_4 = 4.0;
	
	void loop()
	{
		
		Serial.println( SREG, BIN );
		boolean INIT_jump_point = 0;

		#define		WITH_JUMP			0
		#define		NAKED				0 
		
		#if			WITH_JUMP	==		0
			TIMSK2 = 0b00000001; ////////// enable ISR 
		#endif
		
		while(1){
		
			#if		WITH_JUMP		==		1
				if( setjmp ( Go_back_to_beginning ) ){
					// Serial.println(  micros() - timer_debut_ISR  );
					timer_debut_OUTSIDE_ISR = micros();
					total_pr_moyenne_INSIDE_ISR += timer_debut_OUTSIDE_ISR - timer_debut_ISR;
					denominateur_inside++;
					
						if( denominateur_outside >= 128 ){
							Serial.println( ECART_entre_ISR );
							Serial.println( total_pr_moyenne_OUTSIDE_ISR / denominateur_outside );
							Serial.println( total_pr_moyenne_INSIDE_ISR / denominateur_inside );
							Serial.println( increment_out_of_ISR );
							Serial.println( increment_end_compute );
							
							increment_end_compute = 0;
							increment_out_of_ISR =0;
							total_pr_moyenne_OUTSIDE_ISR = 0;
							denominateur_outside = 0;
							total_pr_moyenne_INSIDE_ISR = 0;
							denominateur_inside = 0;
							
						}
						
					while( 1 ){
						increment_out_of_ISR++;
						
						global_1 /= global_2;
						global_4 = sin( (float)global_1 * (float)global_3);
						
						increment_end_compute++;
					}
					
				} else {
				
					if( !INIT_jump_point ){
						INIT_jump_point = 1;
						TIMSK2 = 0b00000001; ////////// enable ISR after the setjmp() is set
					}
				}
			
			#else
					
					while( !FLAG_out_of_ISR ){
						increment_out_of_ISR++;
						
						global_1 /= global_2;
						global_4 = sin( (float)global_1 * (float)global_3);
						
						increment_end_compute++;
					}
					
					// Serial.println(  micros() - timer_debut_ISR  );
					timer_debut_OUTSIDE_ISR = micros();
					total_pr_moyenne_INSIDE_ISR += timer_debut_OUTSIDE_ISR - timer_debut_ISR;
					denominateur_inside++;
					
						if( denominateur_outside >= 128 ){
							Serial.println( ECART_entre_ISR );
							Serial.println( total_pr_moyenne_OUTSIDE_ISR / denominateur_outside );
							Serial.println( total_pr_moyenne_INSIDE_ISR / denominateur_inside );
							Serial.println( increment_out_of_ISR );
							Serial.println( increment_end_compute );
							
							increment_end_compute = 0;
							increment_out_of_ISR =0;
							total_pr_moyenne_OUTSIDE_ISR = 0;
							denominateur_outside = 0;
							total_pr_moyenne_INSIDE_ISR = 0;
							denominateur_inside = 0;
							
						}
					
					FLAG_out_of_ISR = 0;
					
				
			#endif
			
			
			
			
		}
		
	
	
ISR( 	TIMER2_OVF_vect
		#if	NAKED			==		1
		, ISR_NAKED
		#endif
		) { 
	// TCNT2 = TCNT2_valeur;
	#if	NAKED			==		1
		// cli();
	#endif
	unsigned long temp_timer = micros();
	ECART_entre_ISR = temp_timer - timer_debut_ISR;
	
	timer_debut_ISR = temp_timer;
	
	total_pr_moyenne_OUTSIDE_ISR += timer_debut_ISR - timer_debut_OUTSIDE_ISR;
	denominateur_outside++;
					
	// Serial.println(increment_out_of_ISR);
	// increment_out_of_ISR = 0;
	
	FLAG_out_of_ISR = 1;
	
	
	#if		WITH_JUMP		==		1
		longjmp ( Go_back_to_beginning, 1);
	#elif	NAKED			==		1
		reti();
	#endif
}

By doing mean of timers to control integrity (which is ok in this example), and concerning the ability to execute the sin(...) part the more it can (increment_out_of_ISR is number of began but not finished executions, increment_end_compute is number of finished compute (sometimes "false" without the jump, cause ISR was done befor it ends):

With JUMP :
increment_out_of_ISR 196106
increment_end_compute 195979

Without JUMP (normal ISR) :
increment_out_of_ISR 6600
increment_end_compute 6600

What do you think about this ? Is there something I missed there ?

EDIT : ok, understood : change for :

		while( FLAG_out_of_ISR ){
		// while(1){

And then it give :
increment_out_of_ISR 3818
increment_end_compute 3690

So checking flag set inside of ISR may produce some kind of ASM optimization, which in this case is not exactly good for performance !

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

Ok, don't read upon results, they were false of course... To strange !
It appears the global floats have to be volatile (if you jump from ISR) in order to execute compute and store good values.
Then the execution time become coherent again : quite the same : a bit less increment_end_compute when jump is activated.

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

I'm looking for the way I can save then restore register after jump, but without success.
Is it something like :
asm( "push r2\n" );
then
asm( "pop r2\n" );
?

Could you please tell me what is the ASM syntax please, cause I tried many things, but it doesn't work at all...

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

ka7ehk wrote:
NO!

In C, you do nothing at the end of an ISR. There is a built-in reti.

Also, every ISR has a "preamble" where a bunch of stuff is saved. If you try to exit before the natural end of the ISR, the stuff that was saved won't get restored and your program will likely fail.

Your setjmp and longjmp things make no sense.

Jim

There is no requirement to to restore what was saved in the ISR preamble if it doesn't return. longjmp will restore what needs to be restored for execution to continue after setjmp, that includes the callee saved registers, the stack pointer and SREG.

The avr-libc manual implies the setjmp/longjmp act like UNIX, and POSIX states:

Quote:
As it bypasses the usual function call and return mechanisms, longjmp() shall execute correctly in contexts of interrupts, signals, and any of their associated functions. However, if longjmp() is invoked from a nested signal handler (that is, from a function invoked as a result of a signal raised during the handling of another signal), the behavior is undefined.
Therefore the use of longjmp here is well defined; AVR Libc acts as described.

The gotcha here is that some code may not cope with being interrupted like this, the issues are similar to calling code in interrupts and the main program.

Also note that the non-volatile local variables in the function that called setjmp that are altered after calling setjmp have undefined values after longjmp.

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

neoirto wrote:
It appears the global floats have to be volatile (if you jump from ISR) in order to execute compute and store good values.

No, volatile is not sufficient as interrupts may occur halfway though updating a variable.

neoirto wrote:
I'm looking for the way I can save then restore register after jump, but without success.
Is it something like :
asm( "push r2\n" );
then
asm( "pop r2\n" );
?

Could you please tell me what is the ASM syntax please, cause I tried many things, but it doesn't work at all...

You can't mix assembler and C like that, CPU registers are used by the compiler and altering them in assembler except where permitted will result in undefined behaviour. What are you trying to do?

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

Well,  I wanted to do the exact same thing, to use an interrupt in order to jump to the very beginning of a while loop, regardless of where the interrupt occurred, in a very long while loop (~50 ms).  Using an ATtiny84, I set up the MCUCR and GIMSK registers, then sei() to activate the INT0 interrupt (high or low trigger, but I chose a low going pin), but didn't include an ISR loop for it to go to.

 

It worked - it hit the beginning of the while loop, every time, at a frequency of around 4 Hz.  If I added a blank ISR loop, it didn't work.  So I can only assume this is peculiar to the ATTiny84, or, without a ISR destination, the system choses to use the while loop as the only option it has, in which case, it can only go to the beginning of it.  I don't know if this is the case, I only know that it worked.

 

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

neoirto wrote:
But I don't understand why, because longjmp will return quickly 1, and then leave ISR ? No

 

No.

 

Standard `setjmp/longjmp` functionality is designed to work in a very specific environment: a chain/stack of nested function calls `A -> B -> C -> D`. Somewhere at the bottom of that chain (in `A`) you called `setjmp`, and then at the top of that chain (in D) you can call `longjmp` to quickly return back to `A`. "Quickly" means jumping back through several layers of nested calls.

 

The critical point here is that at the moment when you call `longjmp` your target - `A` - is still sitting in the callstack, waiting for you to return. And that is what you do with `longjmp` - you return to `A`. Not in normal by in step-by-step fashion (through `B` and `C`), but jump over `B` and `C` directly to `A`. This is why the function is called `longjmp`. Any other use of `setjmp/longjmp` is illegal and will not do anything meaningful.

 

In your case interrupt handler is not called from `main`. `main` is a not a member of ISR's callstack. So, trying to jump to `main` from an ISR handler is not going to work.

Last Edited: Sun. May 12, 2019 - 02:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:
I think setjmp and longjmp were invented to let K&R handle 64K pages in the PDP11 they were using to write unix in the early 70s. I agree it might make a good error/restart catchall.

 

No, this has absolutely nothing to do with it.

 

bobgardner wrote:
One of these old unix dudes could tell us where he has seen setjmp and longjmp used.

 

They are actively used to this day by those who know how to use them. These functions definitely belong to "use them only when you really have to" category, but they are not in any way obsolete. This is how exception-like functionality is implemented in C. This is the underlying mechanism in C co-routines.

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

ISR+longjmp works as expected, in particular SREG and SP are saved in jmpbuf. And longjmp is designed to jump right out of a call tree (including any level of IRQs).
.
You are trying to implement timeout like in Java where an exception is thrown when a timer triggers.
.
The alternative would be to clutter up your funcs' code with tests an returns if the condition is true, which is not very nice either.
.
Advantage of longjmp is that it keeps your funcs clean and in addition is zero overhead until actually performed, and even then it's just around 40 ticks or so.
.
Main disadvantage of longjmp is that you need cleanup of your funcs internal state which you don't know, as you don't know when exactly the IRQ triggers.
.
And longjmp is not very common, hence there might be some bias against it.

avrfreaks does not support Opera. Profile inactive.

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

setjmp/longjmp are often used in interactive programs to catch the interrupt key-sequence (e.g., Control-c): inside the REPL (read-eval-print-loop) one performs a setjmp(); inside the Control-c signal handler one performs a longjmp().  (By the way, the "modern" version of setjmp/longjmp is setcontext().)     This mechanism can also be used to implement co-routines, exception handlers, etc.

 

So I can see how the OP may have come to the solution in the first post.  And as odd as the code looks, I don't see why it would not work here.   But this style is not usual for embedded / real-time systems.

 

I did look at the implementation of setjmp in avr-libc (see attached).   The implementations is quite simple.  It saves the stack pointer and status register.  longjmp () restores the stack pointer and jmps to the setjmp() address.  It looks like any other registers will get trashed.

 

Looking at the original post -- not having read all the follow-ups -- it seems the OP wants to do a specific computation based on the value of critical_value and abort that computation when critical_value changes.  He has the choice of polling for critical value in the loop or using setjmp/longjmp.

 

 

Attachment(s): 

Last Edited: Sun. May 12, 2019 - 04:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The avr-libc routines also save/restore the call-saved registers, cf. the .irp code. setjmp behaves like a function, i.e. clobbers the call-used regs so that any local whose lifetime crosses setjmp will survive.

avrfreaks does not support Opera. Profile inactive.

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

20 years ago I used an OS with preemptable tasks that used setjmp and longjmp to save and restore contexts.

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

SprinterSB wrote:
The avr-libc routines also save/restore the call-saved registers, cf. the .irp code. setjmp behaves like a function, i.e. clobbers the call-used regs so that any local whose lifetime crosses setjmp will survive.

 

Thanks.  I missed that. 

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

SprinterSB wrote:
ISR+longjmp works as expected, in particular SREG and SP are saved in jmpbuf. And longjmp is designed to jump right out of a call tree (including any level of IRQs).

 

It might be true for the given platform, no argument here. For another example, on x86 the `setjmp/longjmp` mechanism is sometimes successfully used to implement coroutines, i.e. to pass control between two different branches of call tree (basically, it is a side-jump, not a backwards jump). If done properly, it "works", even though it is not formally legal.

 

However, in general, as far as C standard library specification is concerned, it is illegal to `longjmp` out of signal handlers (or interrupt handlers). The behavior is undefined. There are several underlying reasonings behind this restriction, including (but not limited to) 1. the fact that the call tree for the handler might include several platform-dependent routines that cannot be safely jumped over, 2. `longjmp` is not an "async safe" function.

Last Edited: Mon. May 13, 2019 - 02:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No.

 

Stop.

 

You don't know where your code was when your interrupt fired. If you leave the ISR via longjmp, you are not going back where you were before it was called. You're going to where setjmp was called.

 

Your problem isn't some subtlety of restoring things, it's that your idea makes no sense whatsoever. The idea of an interrupt is that your code is currently doing something, and then the interrupt happens, and when it's done, it resumes what it was doing previously. If you try to use a goto, or a longjmp, you are no longer resuming what you were doing previously.

 

 

Circumstances under which this would be reasonable: Nothing your program does matters to you, at all, and you don't care whether any of the code you wrote runs.

 

If it is possible that you care whether your program runs, then this is an idea which is completely, totally, wrong in every way.

 

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

I guess the real solution here is to break up the long (50ms) work in main() to smaller steps that are also then conditional on some kind of "abandon because of interrupt" flag that is then set in the ISR.

 

(I often make this point but shouldn't all this have been captured in the design stages before implementation?)

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

the_real_seebs wrote:
If you leave the ISR via longjmp, you are not going back where you were before it was called. You're going to where setjmp was called.

 

It can get more complicated when you have preemptible tasks.  The OS gets control after interrupts.  It runs the highest priority task that is ready to run.

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

AndreyT wrote:

However, in general, as far as C standard library specification is concerned, it is illegal to `longjmp` out of signal handlers (or interrupt handlers). The behavior is undefined. There are several underlying reasonings behind this restriction, including (but not limited to) 1. the fact that the call tree for the handler might include several platform-dependent routines that cannot be safely jumped over, 2. `longjmp` is not an "async safe" function.

 

Are you sure about this?   I thought the issue was repeated signal handlers.  That is, if longjmp is used in signal handlers then signals must be blocked in the handlers.  Extending to interrupt handlers, interrupts must be disabled.

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

MattRW wrote:

AndreyT wrote:

However, in general, as far as C standard library specification is concerned, it is illegal to `longjmp` out of signal handlers (or interrupt handlers). The behavior is undefined. There are several underlying reasonings behind this restriction, including (but not limited to) 1. the fact that the call tree for the handler might include several platform-dependent routines that cannot be safely jumped over, 2. `longjmp` is not an "async safe" function.

 

Are you sure about this?   I thought the issue was repeated signal handlers.  That is, if longjmp is used in signal handlers then signals must be blocked in the handlers.  Extending to interrupt handlers, interrupts must be disabled.

 

See the WG14 response to this matter:  http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1318.htm

 

What you are saying is similar to what the original C standard (C89/90) was saying (and might also rely on additional guarantees made by POSIX at that time)

 

"As it bypasses the usual function call and return mechanisms, the longjmp function shall execute correctly in contexts of interrupts, signals and any of their associated functions. However, if the longjmp function is invoked from a nested signal handler (that is, from a function invoked as a result of a signal raised during the handling of another signal), the behavior is undefined."

 

However, even this spec was challenged by Defect Report 152: http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_152.html

To which WG14 responded with

 

"The C Standard is clear enough as is. The longjmp function shall execute correctly when called from a non-nested signal handler invoked through calls to the raise or abort functions; if longjmp is called from a signal handler invoked by other means, or from a nested signal handler, the behavior is undefined."

 

(and the original wording was quietly removed from C standard).

 

So, according to this clarification from the very beginning using `longjmp` has only been allowed in signal handlers that were invoked manually, through `raise` or `abort` functions, not by "real" signals. 

 

The current C standard covers this issue with a more generic rule

 

"7.14.1.1 The signal function

5 If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler."

 

As you can see, calling `longjmp` from asynchronous signal handlers remains illegal. Naturally, all this also applies to interrupt handlers invoked by actual interrupts.

Last Edited: Mon. May 13, 2019 - 10:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Dare to be naïve." - Buckminster Fuller

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

With AVRs and avr-gcc, I'd be surprised if longjump did not work almost as expected.

The lack of a RETI could be a problem, but since SREG gets restored, all should be ok.

We are not in the realm of C-standard signal-handlers.

We are closer to the realm of PINB |= 4 .

In any case, avr-gcc's ISRs are not C-standard things.

That said, ISR+longjump is not a documented avr-gcc thing either.

If ISR+longjump works for you,

my suggestion is to get the source for it.

Use said source instead of the library version.

That way you might not be affected by updates.

BTW my recollection is that the way to handle register-trashing

by longjmp is to call setjmp from a function that does nothing else

but return the value was wrong.

 

Edit: corrected BTW ....

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

Last Edited: Wed. May 15, 2019 - 11:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT, Many thanks for the detailed explanation!

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

Just going to chime in and say...

 

Even if it works, calling longjmp is probably a bad idea. Not because it won't work correctly, but because it makes it harder for the maintenance programmer to understand.

 

In general, ISRs should just set state and return. Now, sometimes setting that state is a complicated business and makes the ISR take a bit longer than we'd hope, but it will still return to the main program.

 

If the main program can't handle things quickly enough (before another ISR arrives), then you can put more processing in the ISR- do the calculations etc, in there, and store the results in volatile variables, + ready flags etc. Be careful not to create race conditions, but the main code can always disable interrupts while it reads the volatile variables (copy them into locals, then re-enable).

 

For example, if reading serial data, you might create a buffer in ram and have the ISR just stash the byte (attiny1 USART does not have a hardware buffer of more than one byte) and move the counter on, the main program can then stay busy doing whatever it's doing until it's ready to read the serial data.

 

There might also be cases where calling longjmp from a ISR leaves the hardware in an unexpected state- but knowing that a ISR might do that, you can always disable interrupts while doing some hardware operations.

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

I found some of the RTOS code we used in 1996.  I think this is how some of it worked.  The maximum stack size for each task had to be specified.  At startup, space was reserved on the stack for each task.  Setjmp and longjmp were used to run each task in its own environment.   Our code ran on a TI DSP.  We could also run it on a PC for testing.

 

It was based on an RTOS developed by TI.  If you google for TI-RTOS you will get some hits.

Last Edited: Wed. May 15, 2019 - 05:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

TI-RTOS is one of the RTOS for an event processing framework.

QP/C: TI-RTOS Kernel (SYS/BIOS)

 

"Dare to be naïve." - Buckminster Fuller

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

skeeve wrote:

BTW my recollection is that the way to handle register-trashing

by longjmp is to call setjmp from a function that does nothing else

but return the value.

 

That wouldn't work because setjmp saves its calling environment, which includes the stack frame. If the function that calls setjmp returns, the calling environment is no longer valid. (The old vfork system call had the same constraint for similar reasons.)

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

christop wrote:

 

That wouldn't work because setjmp saves its calling environment, which includes the stack frame. If the function that calls setjmp returns, the calling environment is no longer valid.

But the setjmp caller can call longjmp instead of return.

 

Here is the Scheduler we used.

 

/*f*****************************************************************************
*
*          void Scheduler(Process_s *NextProcess)
* DESCRIPTION: Scheduler() uses ANSI setjmp macro to preserve process context.
*              If NextProcess argument is NULL, it searches for the next higher
*              priority ready-to-run process and switch to it Or simply returns
*              to NullProcess if no ready-to-run process is found.  If not NULL,
*              it simply take the NextProcess agrument and switch to such
*              process. Scheduler() uses ANSI longjmp macro to restore context
*              and switch to scheduled process.
* RETURNS:       void.
* ASSUMES: NextProcess must be a valid process or NULL.
*          Interrupts are disabled.  They will be enabled in Longjmp when returning
*          to the next ready task.
* HISTORY: Created 03/28/96 by David Leung
*     03/28/96 DKL: Initial Start Coding.
*****************************************************************************f*/

void Scheduler(Process_s *next_process_p)
{
Process_s *process_p;

#ifndef _TMS320C5XX
  if (setjmp(currentProcess_p->context_a)==0)
#else
  if (rtmkSetjmp(currentProcess_p->context_a)==0)
#endif /* _TMS320C5XX */

  {
    if (next_process_p)
      currentProcess_p = next_process_p;
    else  {           /* determine the NextProcess */
      for(process_p = process_a; process_p->awaitingSig; process_p++)
         ;
      if(process_p->awaitingResource_p)   {
         if(process_p->awaitingResource_p->ownerProcess_p->awaitingSig  ||
            process_p->awaitingResource_p->ownerProcess_p->awaitingResource_p) {
            for(; process_p->awaitingSig  ||  process_p->awaitingResource_p;
               process_p++)
               ;
            }
         else
            process_p = process_p->awaitingResource_p->ownerProcess_p;
         }
      currentProcess_p = process_p;
      }
#ifndef _TMS320C5XX
  longjmp(currentProcess_p->context_a, 1);
#else
  rtmkLongjmp(currentProcess_p->context_a, 1);
#endif /* _TMS320C5XX */
  }
}

This brings back memories.  The scheduler searches the processes from highest to lowest priority.  Notice the "if(process_p->awaitingResource_p" stuff.  I added that code to eliminate priority inversion.  If a process is waiting for a resource, the scheduler now runs the process that has the resource locked.  

 

I never heard of "priority inversion" but I knew the original code didn't make sense.  When I told the guy who downloaded this RTOS from Dr. Dobbs journal of what I did, he knew the term "priority inversion".  He said that is what caused several spacecraft to be lost that NASA tried to send to mars.  NASA should have hired me.  Maybe NASA gets their code from Dr. Dobbs journal too.

 

Here are RTMK.c and RTMK.h.   I also zipped up a lot of stuff that has headers that RTMK uses.  7zip creates a file that is one third the size of Microsoft's zip, but this forum won't allow the filename extension .7z.   (500k vs. 1500k)  If anyone wants it, should I add a .txt to the .7z extension or upload the .zip file or put it on OneDrive?
 

Attachment(s): 

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

steve17 wrote:
He said that is what caused several spacecraft to be lost that NASA tried to send to mars.
or go wonky after landing on Mars.

steve17 wrote:
NASA should have hired me.
May one be aware of what one wishes for.

 


What really happened to the software on the Mars Pathfinder spacecraft? | Rapita Systems

fyi

Because VxWorks contains a C language interpreter intended to allow developers to type in C expressions and functions to be executed during system debugging, it was possible to upload a short C program to the spacecraft, which when interpreted, changed the values of these variables [enable priority inheritance for 3 mutexes] from FALSE to TRUE. 

More than VxWorks have a C or C++ interpreter :

Solutions for Embedded Scripting in C/C++

...

  • Add a C/C++ interpreter into your application by compiling and linking with the library of our Embedded Ch.

...

though the OOTB RTOS is QNX or likely the configurable real-time part of a somewhat recent Linux kernel :

Embedded and embedding Ch - System Requirements

...

  • QNX 6.3.0 or above

...

The minimum disk space requirement for Embedded Ch with complete ISO C standard functions:

  • Less than 3 Mb.

 

"Dare to be naïve." - Buckminster Fuller

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

So what you (the OP) have, in effect is a process/task/thread that is running, and when an interrupt occurs, you want to abort that process and then restart it.

 

If I had to do this, I would implement it AS SUCH (in assembly language), saving the full context needed for the process involved, and restoring that context to restart the process.  This is "known science", used by various RTOS code. - no need to re-invent the wheel.    I would suspect that longjump() might miss some of the needed context, since it (probably?) operates at the "C" context level.  For example I wouldn't count on it restoring the "known zero" in R1, which will get trashed during a multi-byte multiply operation.

 

(On some architectures, it may be that longjump() does restore all necessary context to do this.  But from the quoted parts of the C specifications, it certainly seems that you can't count on it.)

(I'm not sure why people are so inclined to try to twist a HLL into doing work that a few relatively obvious lines of assembly language would accomplish.)

 

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

westfw wrote:

I would suspect that longjump() might miss some of the needed context, since it (probably?) operates at the "C" context level.  For example I wouldn't count on it restoring the "known zero" in R1, which will get trashed during a multi-byte multiply operation.

 

Easy enough to test. Just compile this and see what the .LST file looks like.

 

jmp_buf cpu_state;


void foo(void)
{
    longjmp(cpu_state,1);
}

void main(void)
{

	volatile int i;
	int j = 0;

    while (1)
    {
        if (setjmp(cpu_state) == 0)
        {
            foo();
        } else {
            //jump here from foo()
            i=j;
        }
    }
}

 

 

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

Restoring R1 is not needed because it can only be non-zero during a multi-instruction pattern. You cannot call longjmp (or setjmp for that matter) from within a multi-instruction pattern.
.
If you ever have R1 non-zero between multi-instruction patterns then your program ([inline] asm) is trash.
.
The only thing that does not work as expected are global registers: they are restored but should rather behave like static storage, i.e. retain their values across longjmp.
.
Restoring RAMPx is not needed because GCC assumes they are all 0 and ISR prologue inits them with 0 (except in the case when there is RAMPZ and no RAMPD, content of RAMPZ is of no concern then). (There are some GCC issues with RAMPx handling though, but that's not anything longjmp is to be blamed for.)
.
EIND is also not restored, but that's no issue because GCC assumes that EIND never changes during program execution anyway.

avrfreaks does not support Opera. Profile inactive.

Last Edited: Fri. May 17, 2019 - 11:06 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fascinating discussion, but did no one notice that this is a seven-year necromancy started by @renkitch 25 posts ago?

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

I did but folks seemed to be having so much fun ;-)

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

The use of longjmp etc. is timeless.  20 years ago I used an RTOS with preemptive tasks that used it.  I'm still wondering if it might be a good idea.

 

More difficult to explain is people posting C code when C was rendered obsolete when the C++ compiler was made available.  I continued using C back in 1985 although my OS9 OS came with a C++ compiler.