Program execution during Flash operation?

Hi,

has anyone ever noticed that the Every Nano board runs the code already during flashing? So even before setup() is called.

I am currently working with a SPI FRAM and was wondering why the new values are already present on the first read.

With a logic analyzer I see activity on the SPI bus even before setup() is called.

I am preparing some more data ...

Don't forget that constructors, main and init run before setup - it may look like it's the first thing to execute, but it's not.

Hi,

But the actual program starts with setup(). The data transfer to the FRAM that my program does can't be executed before. :thinking:

Both readoutFRAM() calls in setup show the same values in the terminal. I wonder about that when I change the values before flashing again. Normally the old values should be read first and then the new ones.

/*
  FRAM
  Infineon (Cypress) FM25040B, SPI, 4kBit (512x8)
  https://www.cypress.com/file/136471/download
*/
struct Opcode
{
  const uint8_t WREN  {0x06}; // set write enable latch
  const uint8_t WRDI  {0x04}; // write disable
  const uint8_t RDSR  {0x05}; // read status register
  const uint8_t WRSR  {0x01}; // write status register
  const uint8_t READ  {0x03}; // read memory data + 3.Bit Adressbit
  const uint8_t WRITE {0x02}; // write memory data + 3.Bit Adressbit
};
constexpr Opcode opcode;

struct ServoData
{
  uint16_t limitMin {0};
  uint16_t limitMax {0};
  uint16_t lastPos  {0};
};
ServoData servoData;
ServoData servoDataReadTest;

// Sum of all struct data types, for serial read/write
constexpr uint8_t servoDataLength { (sizeof(servoData)) };  

#include <SPI.h>

struct FRAM
{
  const uint8_t CS   {10};
  //const uint8_t MOSI {11};
  //const uint8_t MISO {12};
  //const uint8_t CLK  {13};
};
constexpr FRAM fram;

SPISettings FRAMsettingSPI (1000000, MSBFIRST, SPI_MODE0);

void setup()
{
  pinMode(20, OUTPUT);
  // debug toggle - flash writing
  digitalWrite(20, HIGH); digitalWrite(20, LOW);
  digitalWrite(20, HIGH); digitalWrite(20, LOW);
  // digitalWrite(20, HIGH); digitalWrite(20, LOW);
    
  Serial.begin(250000);
  Serial.println("\n\nµC Reset ### ### ###");
  Serial.println(servoDataLength);
  SPI.begin();
  pinMode(fram.CS, OUTPUT);
  digitalWrite(fram.CS, HIGH);
  // new data to store
  servoData.limitMin = 35;
  servoData.limitMax = 45;
  servoData.lastPos = 55;
  
  readoutFRAM(fram.CS);
  writeBuffer(0, servoData, fram.CS);
  readoutFRAM(fram.CS);
}

void loop()
{
}

void readoutFRAM (const uint8_t pinCS)
{
  for (uint16_t i = 0; i < 16; i++) // i<x  0 ... 512
  {
    if (i % 8 == 0 && i != 0) {
      Serial.println();
    }

    if (i % 8 == 0) {
      Serial.print(i);
      Serial.print(".\t");
    }

    Serial.print(readOneByte(i, pinCS) );
    Serial.print('\t');
  }

  Serial.println('\n');
}


template <class T>
void readBuffer(const uint16_t addr, T &data, const uint8_t pinCS)
{
  uint8_t *ptr {reinterpret_cast<uint8_t*>(&data)};  // struct only ok
    
  for(size_t i = 0; i < sizeof(T); i++)
  {  
    *ptr = readOneByte(addr+i, pinCS);
    ptr++;
  }
}

template <class T>
void writeBuffer(const uint16_t addr, T &data, const uint8_t pinCS)
{
  const uint8_t *ptr {reinterpret_cast<uint8_t*>(&data)};  // struct only ok
  
  for(size_t i = 0; i < sizeof(T); i++)
  {  
    writeOneByte(addr+i, *ptr, pinCS);
    ptr++;
  }
}

uint8_t readOneByte(const uint16_t addr, const uint8_t pinCS)
{
  uint8_t data {0};           // incoming byte from the SPI

  // if address is greater than 8 bit, then insert the 9th address bit into the opcode byte, at 3rd position
  uint8_t bitNine {0x00};
  if (addr & 0x100) { bitNine = 0x04; }

  SPI.beginTransaction(FRAMsettingSPI);
  digitalWrite(pinCS, LOW);
  // Opcode inkl. MSB Address Byte, 9. Bit
  SPI.transfer( bitNine | opcode.READ );
  // LSB Address Byte
  SPI.transfer( static_cast<uint8_t>(addr & 0xFF) );
  data = SPI.transfer(0x00);
  digitalWrite(pinCS, HIGH);
  SPI.endTransaction();
  return (data);
}

void writeOneByte(const uint16_t addr, const uint8_t data, const uint8_t pinCS)
{
  // if address is greater than 8 bit, then insert the 9th address bit into the opcode byte, at 3rd position
  uint8_t bitNine {0x00};
  if (addr & 0x100) { bitNine = 0x04; }

  SPI.beginTransaction(FRAMsettingSPI);
  enableWriting(pinCS);
  digitalWrite(pinCS, LOW);
  // Opcode inkl. MSB Address Byte, 9. Bit
  SPI.transfer( bitNine | opcode.WRITE );
  // LSB Address Byte
  SPI.transfer( static_cast<uint8_t>(addr & 0xFF) );
  SPI.transfer(data);
  digitalWrite(pinCS, HIGH);
  disableWriting(pinCS);
  SPI.endTransaction();
}

void enableWriting(const uint8_t pinCS)
{
  SPI.beginTransaction(FRAMsettingSPI);
  digitalWrite(pinCS, LOW);
  SPI.transfer(opcode.WREN);
  digitalWrite(pinCS, HIGH);
  SPI.endTransaction();
}

void disableWriting(const uint8_t pinCS)
{
  SPI.beginTransaction(FRAMsettingSPI);
  digitalWrite(pinCS, LOW);
  SPI.transfer(opcode.WRDI);
  digitalWrite(pinCS, HIGH);
  SPI.endTransaction();
}

uint8_t readStatus(const uint8_t pinCS)
{
  SPI.beginTransaction(FRAMsettingSPI);
  digitalWrite(pinCS, LOW);
  SPI.transfer(opcode.RDSR);
  uint8_t data = SPI.transfer(0x00);
  digitalWrite(pinCS, HIGH);
  SPI.endTransaction();
  return (data);
}

With the previous program, I had pin D20 toggle 3x. After that I changed pin D20 to only 2x switching directly in setup().

In the Logic Analyzer I see on the left side yellow bars (not visible because of wide angle zoom) that pin D20 switches 3x. That means somehow the old program must come 3x consecutively to the setup. But this is not visible in the IDE terminal.

After that there is a long pause, the new program is flashed and called 4x. The 4 yellow bars on the right show only 2x switching of pin D20.

Only after the last 2x toggle you see something in the IDE terminal.

Your global objects execute their constructors before setup. One of them likely touched the SPI pins too.

Hi,

But how can the "debug pin" D20 be switched before setup? That cannot be at all.This is not logical for me. What has a constructor to do with the program execution? Decisive is the call and not the building. Not that you misunderstand me. I'm not interested in the logic levels of the SPI pins wobbling. That's not my point either. The point is that setup is called incomplete several times before the final reset. Because data are already transferred before what one does not see in the terminal.

To me it looks like the ATmega4809 is reset several times before it is allowed to real start. D20 switches already but it doesn't get to the serial yet.

If I add a 5s delay directly at the beginning of setup(), everything works as expected. After the long pause for the flash/verify there is only one D20 toggle sequence and in the IDE terminal I see the old and then the new values.

So it really looks like the controller is reset multiple times and only the first lines of code in setup that are processed fast enough can be processed until the next reset. Serial.begin() seems to take too long for this.

If you know that, you can help yourself if this is important. I was just surprised by it.

Practically this looks then in such a way if one flashed its program with a 1s delay starting from the 2.times that setup cannot be processed yet. At the beginning of the flash there is no D20 toggle on the left and only one toggle sequence on the right.

void setup()
{
  delay(1000);
  pinMode(20, OUTPUT);
  // debug toggle - flash writing
  digitalWrite(20, HIGH); digitalWrite(20, LOW);
  digitalWrite(20, HIGH); digitalWrite(20, LOW);
     
  Serial.begin(250000);
  Serial.println("\n\nµC Reset ### ### ###");
  Serial.println(servoDataLength);
  SPI.begin();
  pinMode(fram.CS, OUTPUT);
  digitalWrite(fram.CS, HIGH);
  // new data to store
  servoData.limitMin = 32;
  servoData.limitMax = 42;
  servoData.lastPos = 52;
  
  readoutFRAM(fram.CS);
  writeBuffer(0, servoData, fram.CS);
  readoutFRAM(fram.CS);
}