please help w code to NOT initialize variables

Status
Not open for further replies.

analog&RFmodels

Well-known member
i am trying to write a pgm for Teensy LC that time slices tasks and that part is easy
via interval timer routine that increments the task number and main code that looks
to see what the task number is and do that task accordingly. that all works fine.

then i am trying to make the tasks hang proof without any elapsed time or loop
count checks inside the task. i have a second interval timer set for slightly longer
than the first interval timer. if any task takes too long the service routine for IT2
gets run.

i would like that event to cause a skip to the next task number in line. when i use
goto to jump out of the isr and back to main c gets lost.

so instead the isr for it2 sets a variable and does does a software restart. but
because the restart causes re-initialization of all of the variables to zero, it loses
the ability to know why it is starting up - power on or software restart.

i think i could use eeprom but trying to avoid going there for several reasons, and
am wondering if anyone knows how to tell the compiler that one or even all global
variables should NOT get initialized ? best in the c code itself but if elsewhere that
would be second best.

or is there a register that i could check to see why a start or re-start occurred ?

thanks
 
Interesting ... not sure how that can work:
> On restart there is no state to return to.

Any compiler controlled or stack objects will be initialized or random.

Maybe a direct addressed part of RAM would survive under power and not reset on startup? An area outside of heap/stack and all compiler data areas?

(some) T_3.x's have a few bytes of RAM in the RTC area that survive - though T_LC doesn't have a similar proper RTC and may not have any such bytes?

With a single line of execution down though the stack - only waiting for it to return or maybe taking control and overtly pushing through that func()'s return address call - but leaving the isr() would seem to want to return to the point of interruption as maintained somewhere in the MCU?
 
Interesting ... not sure how that can work:
> On restart there is no state to return to.

Any compiler controlled or stack objects will be initialized or random.

Maybe a direct addressed part of RAM would survive under power and not reset on startup? An area outside of heap/stack and all compiler data areas?

(some) T_3.x's have a few bytes of RAM in the RTC area that survive - though T_LC doesn't have a similar proper RTC and may not have any such bytes?

With a single line of execution down though the stack - only waiting for it to return or maybe taking control and overtly pushing through that func()'s return address call - but leaving the isr() would seem to want to return to the point of interruption as maintained somewhere in the MCU?


thanks for the feedback

i have done this before in a small pic. it does not matter what stack pointers or internal registers or hardware get reset/preset
- it only matters that you write your code knowing that everything could be gone except one or two variables in ram. at the
very beginning of main you test to see why you are there and act accordingly. in my case maybe just two variables - a flag
to know if someone ran too long and the task number that did it, so on restart do next task in line. and since power is never
removed you could know whatever you want if you could keep the compiler from doing its usual make everything zero gag.

i will look into the rtc and see how many bytes and can i get to them. - actually i think TLC has no rtc but it does have
a low power timer.
 
That part was in response to the initial 'non-restart' efforts with ... ' back to main c gets lost.'

For restart - I'd see if the 'void' area above code data and below stack survives restart. If there is a known good spot - you could keep a struct there and just hardcode a pointer to it. Not sure if 'names in the .ld' file might tell you were that is?
 
That part was in response to the initial 'non-restart' efforts with ... ' back to main c gets lost.'

For restart - I'd see if the 'void' area above code data and below stack survives restart. If there is a known good spot - you could keep a struct there and just hardcode a pointer to it. Not sure if 'names in the .ld' file might tell you were that is?

thanks again - yes now i get what you mean - it would almost have to either get lost or grow the stack to infinity.

i will look into lptmr and sort thru the ld file.
 
thanks again - yes now i get what you mean - it would almost have to either get lost or grow the stack to infinity.

i will look into lptmr and sort thru the ld file.

Never looked much at .ld files until the 1062 ... the one for T_LC very simple.

Maybe an edit to bump the stack down in : _estack = ORIGIN(RAM) + LENGTH(RAM);
Reserve 64 or 256 Bytes there may work with -64 or -256 to that?
Worth a quick test and where it is will be known - safe above stack. Build will perhaps fail - or maybe the area won't retain values not too hard to test.
Maybe put a SIG in there and the same in a known EEPROM spot. If the SIG matches it can be used - if not then it is a fresh power up or maybe after upload? It may be all zeroed on any startup ... but that would be extra effort somewhere.

Seems odd to write code expected to fail to run as well behaved and then punish it ? :)
 
Seems odd to write code expected to fail to run as well behaved and then punish it ? :)

all good deeds must be punished. i had to do this once before w pic w application such that it
needed to be robust as possible and if a task runs long skip to next one - if the fault heals so
be it - if not at least run everything else - and do this without having to put overhead inside
of each task to check if you have waited too long for input or how much time has elapsed.
this was not so bad for T36 because it can run the TeensyThreads lib but TLC cannot.
 
@analog&RFmodels - would be interesting to see a skeleton of it working. Wondering what these tasks are doing in turn.

Seems an _isr_timer could set a flag to casually check and bail without much overhead ... depending on how the task workload is cycling.
 
@analog&RFmodels - would be interesting to see a skeleton of it working. Wondering what these tasks are doing in turn.

Seems an _isr_timer could set a flag to casually check and bail without much overhead ... depending on how the task workload is cycling.

here is what i have since you suggested trying to use timer register - see comments middle of deck - reading timer does not work ?
keep in mind that as a programmer i am a duffer

Code:
// teensy lc no hang time slice skeleton
// calls tasks this order 01020103
//                        ABACABAD
// 8 slices 4 tasks
// this case 1 s ea task
// 8 s = whole pgm
// task 0 4 times task 1 2 times  task 2 and 3 1 times
// aborts slices that run too long
// compile debug for now
// of course normally tasks would be much shoter
// time but slow allows debug using only serial
// monitor 

const int led = 13;

// set time per slice
volatile int us = 1000000;
// set slice adder to trigger isr2 = abort long task 
volatile int lg =  100000;

//__no_init_ volatile int tc;

volatile int tc;
volatile int tr;
volatile int tx;
volatile int n3;
volatile int rf;
volatile int xx;

  IntervalTimer it1;
  IntervalTimer it2;

// long detect
void isr2() {
    cli();
    tx=99;
    ++tc;
    if(tc > 7) tc = 0;
    Serial.end();
    rf=1;
//  this is a software restart
    SCB_AIRCR=0x05FA0004;
    return;
}

// increment task count via timer
void isr() {
    cli();
    ++tc;
    if(tc > 7) tc = 0;
    sei();
    return;
}

void task0(int data) {
Serial.print ("0");
delay(500);
}

void task1(int data) {
Serial.print ("1");
delay(500);
}

void task2(int data) {
Serial.print ("2");
// make this delay 5000 to see response
// to long task
delay(500);
}

void task3(int data) {
Serial.print ("3");
delay(500);
  }

int main() {
mainx:
  Serial.begin(9600);
  delay(500);
  Serial.println("tr tc tx rf");
  tr=SCB_AIRCR;
  Serial.printf("%X",tr);
  Serial.println("*");
// line below is where i am trying to
// read and print one of the low power
// timer registers (there are only 16
// bits that are both read and write)
// LPTMR0_CMR is at 4004_0008 and altho
// it is 32 bits only 16 are r/w
// if you uncomment  this line it still
// compiles but freezes on that line
// when you run it
//  xx = LPTMR0_CMR;
  Serial.println("**");  
  Serial.printf("lptc  ");
  Serial.printf("%X",xx);
  Serial.println(" "); 
  Serial.println(tc);
  Serial.println(tx);
  Serial.println(rf);
  if(rf==1) {rf=0; goto cont;}
  tc=0;
  tx=0;
  rf=0;
  
  pinMode(led, OUTPUT);
  Serial.println("*");
  
cont:

  it1.begin (isr,us);
  it2.begin (isr2,us+lg);

loopm:
    it2.begin (isr2,us+lg);

    if (tc==0)  task0(1);
    it2.begin (isr2,us+lg);
w0:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==0) goto w0;

    if (tc==1)  task1(1);
    it2.begin (isr2,us+lg);
w1:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==1) goto w1;

    if (tc==2)  task0(1);
    it2.begin (isr2,us+lg);
w2:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==2) goto w2;

    if (tc==3)  task2(1);
    it2.begin (isr2,us+lg);
w3:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==3) goto w3;

    if (tc==4)  task0(1);
    it2.begin (isr2,us+lg);
w4:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==4) goto w4;

    if (tc==5)  task1(1);
    it2.begin (isr2,us+lg);
w5:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==5) goto w5;

    if (tc==6)  task0(1);
    it2.begin (isr2,us+lg);
w6:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==6) goto w6;
    
    if (tc==7)  task3(1);
    it2.begin (isr2,us+lg);

    Serial.println (" ");
w7:
    if (tx==99) {tx=0; goto loopm;}
    if (tc==7) goto w7;

  goto loopm;

}
 
Not building with IDE? Are you on Windows?

Accessing the timer unit if not enabled it will fault.

Was the persistence of 'unused' RAM tested?
 
Not building with IDE? Are you on Windows?

Accessing the timer unit if not enabled it will fault.

Was the persistence of 'unused' RAM tested?

good point - i will enable lptmr first and see if solves, but worry that once enabled it might start incrementing
every millisecond and destroy my data.
have not tried unused ram yet - need to figure out how to read and write to an absolute memory address first.
yes - ide is arduino 1.8.5 under windows

meanwhile, here is eeprom version - works fine - i will have to look into how many eeprom writes to kill a location
to see if it really practical - no good if erratic situation requiring many restarts writes the eeprom to death in two
days. if the time slices are short that could happen often - maybe a scheme that would count writes and spread
the damage over all 128 locations would make it practical.

Code:
// teensy lc no hang time slice skeleton
// calls tasks this order 01020103
//                        ABACABAD
// 8 slices 4 tasks
// this case 1 s ea task
// 8 s = whole pgm
// task 0 4 times task 1 2 times  task 2 and 3 1 times
// aborts slices that run too long
// compile debug for now
// of course normally tasks would be much shoter
// time but slow allows debug using only serial
// monitor
// uses eeprom addr 50 for 0/1 soft restart flag
// uses eeprom addr 51 for save tc

#include <EEPROM.h>

const int led = 13;

// set time per slice
volatile int us = 1000000;
// set slice adder to trigger isr2 = abort long task 
volatile int lg =  100000;

//__no_init_ volatile int tc;

volatile int tc;
volatile int tr;
volatile int tx;
volatile int n3;
volatile int rf;
volatile int xx;

  IntervalTimer it1;
  IntervalTimer it2;

// long detect
void isr2() {
    cli();
    tx=99;
    ++tc;
    if(tc > 7) tc = 0;
    Serial.end();
    EEPROM.write(50,1);
    EEPROM.write(51,tc);
    Serial.println("eeprom write");    
//  this is a software restart
    SCB_AIRCR=0x05FA0004;
    return;
}

// increment task count via timer
void isr() {
    cli();
    ++tc;
    if(tc > 7) tc = 0;
    sei();
    return;
}

void task0(int data) {
Serial.print ("0");
delay(500);
}

void task1(int data) {
Serial.print ("1");
delay(500);
}

void task2(int data) {
Serial.print ("2");
// make this delay 5000 to see response
// to long task
delay(500);
}

void task3(int data) {
Serial.print ("3");
delay(500);
  }

int main() {
  Serial.begin(9600);
  delay(500);
  Serial.println("*");
  rf=EEPROM.read(50);
//  if(rf==1) {rf=0; goto cont;}
  if(rf==1) {
            EEPROM.write(50,0);
            Serial.println("eeprom write");
            tc=EEPROM.read(51);
            goto cont;
            }
  tc=0;
  tx=0;
  rf=0;
  
  pinMode(led, OUTPUT);

cont:

  it1.begin (isr,us);
  it2.begin (isr2,us+lg);

loopm:
    it2.begin (isr2,us+lg);

    if (tc==0)  task0(1);
    it2.begin (isr2,us+lg);
w0:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==0) goto w0;

    if (tc==1)  task1(1);
    it2.begin (isr2,us+lg);
w1:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==1) goto w1;

    if (tc==2)  task0(1);
    it2.begin (isr2,us+lg);
w2:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==2) goto w2;

    if (tc==3)  task2(1);
    it2.begin (isr2,us+lg);
w3:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==3) goto w3;

    if (tc==4)  task0(1);
    it2.begin (isr2,us+lg);
w4:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==4) goto w4;

    if (tc==5)  task1(1);
    it2.begin (isr2,us+lg);
w5:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==5) goto w5;

    if (tc==6)  task0(1);
    it2.begin (isr2,us+lg);
w6:
    if (tx>98) {tx=0; goto loopm;}
    if (tc==6) goto w6;
    
    if (tc==7)  task3(1);
    it2.begin (isr2,us+lg);

    Serial.println (" ");
w7:
    if (tx==99) {tx=0; goto loopm;}
    if (tc==7) goto w7;

  goto loopm;

}
 
Haven't tried the direct address - but the .ld shows this :: RAM (rwx) : ORIGIN = 0x1FFFF800, LENGTH = 8K

Assume that is helpful? Make a static int and a stack int and print their addresses - should align with that mathwise?

Put something large on the stack int foo[500] and print the first and last address of foo? if that is more stack than ever used try the &foo[499] for a pointer value and store something there in a different sketch then check the value after powerup, upload, restart between runs.

If that works then edit the .ld and use bytes above the stack and repeat the test?

T_LC EEPROM not as robust as other Teensy given the MCU that makes it LC - good for general usage (some many K writes IIRC) and it will wear level as it can for given elements with the 128 bytes across 4K. But indeed a code spasm could eat that life up.
 
too much work for a duffer - when i set up a pointer to the ram area - either 25 bytes from bottom or
25 bytes from top, compiles fine, hang on run - must be somebody trying to keep me out. decided
against the lptmr register because then you would never have lptmr if you wanted it. eeprom does
not have decent write life. robust apps will have to have something that can run threads unless i
can somehow force a jump out of the long detect isr back to main, because then there need be
no soft restart and therefore no initialization free memory.
 
The 3x and 4x have some bytes Battery backed up RAM.
Or use external static ram with battery backup.
 
@FrankB > post#2 noted battery backed RAM in other Teensy - but this project seems T_LC specific.

It looks like T_LC flushes ALL RAM in all the restarts I tried:
Code:
int bar = 0;

void setup() {
  // put your setup code here, to run once:
  while (!Serial && millis() < 2000) ; // wait
  Serial.println("\n USB SERIAL " __FILE__ " " __DATE__ " " __TIME__);
  //  SerialUSB1.println("\n SerialUSB1 " __FILE__ " " __DATE__ " " __TIME__);
  //  SerialUSB2.println("\n SerialUSB2 " __FILE__ " " __DATE__ " " __TIME__);
  int barA = 0;
  int foo[100];
  int barB = 0;
  Serial.printf("STATIC >> addr &bar %lu \t0x%lX \n", &bar, &bar );
  Serial.printf("addr &barA %lu \t0x%lX \n", &barA, &barA );
  Serial.printf("addr &foo[0] %lu \t0x%lX \n", &foo[0], &foo[0] );
  Serial.printf("addr &foo[99] %lu \t0x%lX \n", &foo[99], &foo[99] );
  Serial.printf("addr &barB %lu \t0x%lX \n", &barB, &barB );

  pinMode( 13, OUTPUT );
  digitalWriteFast( 13, !digitalReadFast( 13 ));
  uint32_t *iSurvive = (uint32_t *)0x20001000;
  Serial.printf("addr iSurvive %lu \t0x%lX \n", iSurvive, iSurvive );

  Serial.printf("VALUE iSurvive %lu \t0x%lX \n", iSurvive[0], iSurvive[0] );
  if ( iSurvive[0] != 0x12345678 ) {
    iSurvive[0] = 0x12345678;
    Serial.printf("SET VALUE iSurvive %lu \t0x%lX \n", iSurvive[0], iSurvive[0] );
  }

}
elapsedMillis iBlink;
void loop() {
  if ( iBlink > 300 ) {
    iBlink = 0;
    digitalWriteFast( 13, !digitalReadFast( 13 ));
  }
  if ( Serial.available() ) {
    _reboot_Teensyduino_();
  }

}

Even with .ld edit :: _estack = ORIGIN(RAM) + LENGTH(RAM)-256;
and this :: uint32_t *iSurvive = (uint32_t *)0x200016A0;

The stack moved down - it compiled - but it doesn't survive a reset.
 
Thinking outside the box, what about writing the task number to 4-bits (or however many bits you need to cover all possible tasks) of a 75HC595 & then just read those outputs from the 74HC595 as inputs whenever a reset (not a fresh power-up) is activated to see which task was last executing. Let a fresh power-up activate the reset on the 74HC595 (via an RC circuit or some other means that does not depend upon the Teensy).

Mark J Culross
KD5RXT
 
Thinking outside the box, what about writing the task number to 4-bits (or however many bits you need to cover all possible tasks) of a 75HC595 & then just read those outputs from the 74HC595 as inputs whenever a reset (not a fresh power-up) is activated to see which task was last executing. Let a fresh power-up activate the reset on the 74HC595 (via an RC circuit or some other means that does not depend upon the Teensy).

Mark J Culross
KD5RXT

...a static spi-ram has more space and may be even easier to use..

But i'd just a T3x anyway.
 
I agree with Frank, the best solution is to use Teensy 3.2 with a coin cell on VBAT. Then you can store this data in the battery supported RTC memory.

Another good approach would be to add a FRAM chip. While those technically do have a limit, the write endurance of FRAM is so high and the bandwidth is limited by SPI or I2C speed enough that it would take a lifetime to reach the endurance limit, even if stuck in an infinite loop writing at the maximum possible speed. If a battery is unacceptable and you absolutely must have a highly robust non-volatile memory which can withstand rapid writing, FRAM is the solution.
 
@FrankB > post#2 noted battery backed RAM in other Teensy - but this project seems T_LC specific.
...

If using a T_3.2 or better ... this whole experiment could use TeensyThreads (p#7 ) - for RTC RAM - seems the desire was to use T_LC and make something work there with this prior scheme.

So yes external storage seems needed - if not using EEPROM for storage - as the T_LC doesn't seem to have any space that survives.
 
defragster, Frank, Mark, Paul - thanks for all the good feedback. i will look at all of the relative cost size etc but i would
not be surprised if TLC + shift register wins -- one external chip, no coin battery, and if the shift register has a reset
as the 595 and many do then one rc to the reset pin for POR, 3 wires to TLC (clk, dtaout, datain). also Frank, your
arm_reset function works well T4 for those cases where time is the only wake up criteria you need and cold start ok,
only draws 220 ua while it is "gone".

i even tried Duff's excellent Zilch library (V0.3 that will play w TLC) and it is great, will handle with grace a task that is
slow, but grinds to a halt with a hung task. and when i look at all the code he wrote to get Zilch going it makes me
think this is a non trivial pursuit.

but since the EEPROM version works well it just needs slight mods to use a shift register instead - and right now there
are only 8 tasks so that is only 3 bits and maybe a forth bit for start-up status.

thanks again everyone for all of the feedback and i will post here what ends up being the solution used.

i think maybe i am biased toward TLC because it has such low hibernate current and also because at
this point it has become personal between me and the TLC.
 
at $0.57 singles and 0.49 10's the TLC + HC595 wins the cost battle - had a 595 in junk box - works fine - using as serial in serial out
remember that you need to re-write a shift register as you read it or the read is destructive. also - put a pull down on whatever TLC
pin is used to clock the shift register as all of the io pins go tri-state for a short time during the software restart. i used a 15k pullup
on the 595 reset pin with a 0.01u to gnd for POR and a 0.1u bypass - whole thing on 2 inch 5 cond ribbon. so with 1 flag bit and 3
bits for task number there are 4 bits left over for addtl info. this all works out for TLC because you do not have to have anti hang
code (like preventing death with a hung peripheral or sensor) in any of the tasks which saves much needed code space and makes
code memory go further.

thanks again everyone for helping out.
 
Glad everything worked out so well & that this approach satisfies the need !! Quite frankly, my thinking when I proposed the "outside-the-box" idea was to read the outputs from the 75HC595 using more input pins on the Teensy, but your seriial in/serial out approach is ingenious, as it requires only one additional pin !! Very well done, indeed !!

Mark J Culross
KD5RXT
 
Glad everything worked out so well & that this approach satisfies the need !! Quite frankly, my thinking when I proposed the "outside-the-box" idea was to read the outputs from the 75HC595 using more input pins on the Teensy, but your seriial in/serial out approach is ingenious, as it requires only one additional pin !! Very well done, indeed !!

Mark J Culross
KD5RXT

Mark,
you can't have too many unused pins !! gotta admit i went cross-eyed debugging my code for care and feeding of
serial in/serial out (haven't used a "circular memory" since the 70's). i hate that there is no flag to tell the compiler
to leave the user ram alone at startup. thanks again for the good feedback - i was stuck "inside the box".
Bill
 
it turns out there is some register storage in Teensy LC that will live thru a software restart
- 16 bits of the lptmr compare register. at one point defragster told me not to try to use it
unless i had enabled it - i only thought i had - see below

Code:
// Teensy LC retain data after software restart

// early on you have to enable BOTH the module and the timer
  SIM_SCGC5 = SIM_SCGC5 | SIM_SCGC5_LPTIMER;
  LPTMR0_CSR = LPTMR0_CSR | LPTMR_CSR_TEN;
// then store data before the restart
  LPTMR0_CMR = srdataw;
// or read data after after the restart
  srdatar = LPTMR0_CMR;
// have only tested 8 bit store
// but should be good for 16
// here is the software restart
  SCB_AIRCR=0x05FA0004;
 
I haven't looked at it, but this might be relevant.

Code:
int x __attribute__((noinit));
 
Status
Not open for further replies.
Back
Top