Skip to content

constexpr construction of SPI objects on Teensy 4 but not Teensy 3.6 #69

@cfeied

Description

@cfeied

I have longstanding projects running on T3.6 that have been stable but now crash silently after late_startup_hook() and before setup() if compiled under 1.58 or 1.59 beta3.

I tracked my issues to the fact that SPI construction is no longer happening as a constexpr, so the devices do not exist when I call them in the constructors of other objects.

I see that the problem has been recognized, understood, and solved for some libraries:
https://forum.pjrc.com/index.php?threads/problem-with-two-wire-build-between-11-3-1-and-5-4-1.73176/#post-328856
https://forum.pjrc.com/index.php?threads/teensy-4-1-does-not-boot-when-invoking-spi-from-external-class.73154/

Running a slightly modified version of the Teensyduino_Test_constinit_XXX code from one of those threads, I see Wire works correctly on both, but SPI works only on T4 and fails on T3.6.

= = = = = = = = = = = = = = = = = = = =
T4.1
Wire, SPI, and HWSerial all have been fixed:
= = = = = = = = = = = = = = = = = = = =

**startup_late_hook**
Wire: var #1 = 403F0000
Wire: var #2 = 60001F14
Wire1: var #1 = 403F8000
Wire1: var #2 = 60001E74
Wire2: var #1 = 403FC000
Wire2: var #2 = 60001E74
SPI: var #1 = 403A0000
SPI: var #2 = 200004B8
SPI1: var #1 = 4039C000
SPI1: var #2 = 20000448
SPI2: var #1 = 40394000
SPI2: var #2 = 200003D8
Serial: var #1 = 40198000
Serial: var #2 = 20000378
Serial2: var #1 = 40190000
Serial2: var #2 = 20001028
Serial3: var #1 = 40188000
Serial3: var #2 = 20000F68
Serial4: var #1 = 4018C000
Serial4: var #2 = 20000FC8
Serial5: var #1 = 401A0000
Serial5: var #2 = 20001148
Serial6: var #1 = 40184000
Serial6: var #2 = 20000F08
Serial7: var #1 = 4019C000
Serial7: var #2 = 200010E8
Serial8: var #1 = 40194000
Serial8: var #2 = 20001088

**setup**
Wire: var #1 = 403F0000
Wire: var #2 = 60001F14
Wire1: var #1 = 403F8000
Wire1: var #2 = 60001E74
Wire2: var #1 = 403FC000
Wire2: var #2 = 60001E74
SPI: var #1 = 403A0000
SPI: var #2 = 200004B8
SPI1: var #1 = 4039C000
SPI1: var #2 = 20000448
SPI2: var #1 = 40394000
SPI2: var #2 = 200003D8
Serial: var #1 = 40198000
Serial: var #2 = 20000378
Serial2: var #1 = 40190000
Serial2: var #2 = 20001028
Serial3: var #1 = 40188000
Serial3: var #2 = 20000F68
Serial4: var #1 = 4018C000
Serial4: var #2 = 20000FC8
Serial5: var #1 = 401A0000
Serial5: var #2 = 20001148
Serial6: var #1 = 40184000
Serial6: var #2 = 20000F08
Serial7: var #1 = 4019C000
Serial7: var #2 = 200010E8
Serial8: var #1 = 40194000
Serial8: var #2 = 20001088

= = = = = = = = = = = = = = = = =
T3.6
Wire has been fixed, but SPI has not:
= = = == = = = = = = = = = = = =

**startup_late_hook**
Wire: var #1 = 40066000
Wire: var #2 = 2700
Wire1: var #1 = 40067000
Wire1: var #2 = 26C0
Wire2: var #1 = 400E6000
Wire2: var #2 = 26C0
SPI: var #1 = 0
SPI: var #2 = 0
SPI1: var #1 = 0
SPI1: var #2 = 0
SPI2: var #1 = 0
SPI2: var #2 = 0

**setup**
Wire: var #1 = 40066000
Wire: var #2 = 2700
Wire1: var #1 = 40067000
Wire1: var #2 = 26C0
Wire2: var #1 = 400E6000
Wire2: var #2 = 26C0
SPI: var #1 = 4002C000
SPI: var #2 = 2744
SPI1: var #1 = 4002D000
SPI1: var #2 = 27D8
SPI2: var #1 = 400AC000
SPI2: var #2 = 286C

= = = = = = = = = = = = = = = = = = = =
Here is the code that produces this output:

#include <Wire.h>
#include <SPI.h>

// Separate version for T4 vs T3 because T4 has portAddr while T3 has port_addr
#if defined(__IMXRT1062__)   // T4
uintptr_t Teensyduino_Test_constinit_Wire(int instance, int index) {
    if (instance == 0) {
        if (index == -1) return (uintptr_t)("Wire");
        if (index == 0) return 2;
        if (index == 1) return Wire.portAddr;   // probably should decide whether it is portAddr or port_addr and use the same thing everywhere
        if (index == 2) return (uintptr_t)&Wire.hardware;
    }
    if (instance == 1) {
        if (index == -1) return (uintptr_t)("Wire1");
        if (index == 0) return 2;
        if (index == 1) return Wire1.portAddr;
        if (index == 2) return (uintptr_t)&Wire2.hardware;
    }
    if (instance == 2) {
        if (index == -1) return (uintptr_t)("Wire2");
        if (index == 0) return 2;
        if (index == 1) return Wire2.portAddr;
        if (index == 2) return (uintptr_t)&Wire2.hardware;
    }
#if defined(ARDUINO_TEENSY_MICROMOD)
    if (instance == 3) {
        if (index == -1) return (uintptr_t)("Wire3");
        if (index == 0) return 2;
        if (index == 1) return Wire3.port_addr;
        if (index == 2) return (uintptr_t)&Wire3.hardware;
    }
#endif
    return 0;
}
#else   // Not T4
uintptr_t Teensyduino_Test_constinit_Wire(int instance, int index) {
    if (instance == 0) {
        if (index == -1) return (uintptr_t)("Wire");
        if (index == 0) return 2;
        if (index == 1) return Wire.port_addr;
        if (index == 2) return (uintptr_t)&Wire.hardware;
    }
    if (instance == 1) {
        if (index == -1) return (uintptr_t)("Wire1");
        if (index == 0) return 2;
        if (index == 1) return Wire1.port_addr;
        if (index == 2) return (uintptr_t)&Wire2.hardware;
    }
    if (instance == 2) {
        if (index == -1) return (uintptr_t)("Wire2");
        if (index == 0) return 2;
        if (index == 1) return Wire2.port_addr;
        if (index == 2) return (uintptr_t)&Wire2.hardware;
    }
#if defined(ARDUINO_TEENSY_MICROMOD)
    if (instance == 3) {
        if (index == -1) return (uintptr_t)("Wire3");
        if (index == 0) return 2;
        if (index == 1) return Wire3.port_addr;
        if (index == 2) return (uintptr_t)&Wire3.hardware;
    }
#endif
    return 0;
}
#endif

// constexpr construction of SPI objects on Teensy 4 but not Teensy 3
uintptr_t Teensyduino_Test_constinit_SPI(int instance, int index) {
    if (instance == 0) {
        if (index == -1) return (uintptr_t)("SPI");
        if (index == 0) return 2;
        if (index == 1) return SPI.port_addr;
        if (index == 2) return SPI.hardware_addr;
    }
    if (instance == 1) {
        if (index == -1) return (uintptr_t)("SPI1");
        if (index == 0) return 2;
        if (index == 1) return SPI1.port_addr;
        if (index == 2) return SPI1.hardware_addr;
    }
    if (instance == 2) {
        if (index == -1) return (uintptr_t)("SPI2");
        if (index == 0) return 2;
        if (index == 1) return SPI2.port_addr;
        if (index == 2) return SPI2.hardware_addr;
    }
    return 0;
}

// Can't run this section on teensy 3 because T3 HardwareSerial does not have .port_addr or portAddress or .hardware 
#if defined(__IMXRT1062__)
uintptr_t Teensyduino_Test_constinit_HardwareSerial(int instance, int index) {
    if (instance == 0) {
        if (index == -1) return (uintptr_t)("Serial");
        if (index == 0) return 2;
        if (index == 1) return Serial1.port_addr;
        if (index == 2) return (uintptr_t)Serial1.hardware;
    }
    if (instance == 1) {
        if (index == -1) return (uintptr_t)("Serial2");
        if (index == 0) return 2;
        if (index == 1) return Serial2.port_addr;
        if (index == 2) return (uintptr_t)Serial2.hardware;
    }
    if (instance == 2) {
        if (index == -1) return (uintptr_t)("Serial3");
        if (index == 0) return 2;
        if (index == 1) return Serial3.port_addr;
        if (index == 2) return (uintptr_t)Serial3.hardware;
    }
    if (instance == 3) {
        if (index == -1) return (uintptr_t)("Serial4");
        if (index == 0) return 2;
        if (index == 1) return Serial4.port_addr;
        if (index == 2) return (uintptr_t)Serial4.hardware;
    }
    if (instance == 4) {
        if (index == -1) return (uintptr_t)("Serial5");
        if (index == 0) return 2;
        if (index == 1) return Serial5.port_addr;
        if (index == 2) return (uintptr_t)Serial5.hardware;
    }
    if (instance == 5) {
        if (index == -1) return (uintptr_t)("Serial6");
        if (index == 0) return 2;
        if (index == 1) return Serial6.port_addr;
        if (index == 2) return (uintptr_t)Serial6.hardware;
    }
    if (instance == 6) {
        if (index == -1) return (uintptr_t)("Serial7");
        if (index == 0) return 2;
        if (index == 1) return Serial7.port_addr;
        if (index == 2) return (uintptr_t)Serial7.hardware;
    }
#if defined(ARDUINO_TEENSY41)
    if (instance == 7) {
        if (index == -1) return (uintptr_t)("Serial8");
        if (index == 0) return 2;
        if (index == 1) return Serial8.port_addr;
        if (index == 2) return (uintptr_t)Serial8.hardware;
    }
#endif
    return 0;
}
#endif

void print_info(uintptr_t(*f)(int, int)) {
    int instance = 0;
    while (1) {
        const char* name = (char*)f(instance, -1);
        if (!name) break;
        int count = f(instance, 0);
        for (int i = 1; i <= count; i++) {
            uintptr_t n = f(instance, i);
            Serial.print(name);
            Serial.print(": var #");
            Serial.print(i);
            Serial.print(" = ");
            Serial.println(n, HEX);
        }
        instance++;
    }
}

// must appear separately before the function definition else ld complains about multiple definitions
extern "C" void startup_late_hook();    

void startup_late_hook() {
    while (!Serial); // wait for serial monitor
    delay(15);
    Serial.println("startup_late_hook");
    print_info(Teensyduino_Test_constinit_Wire);
    print_info(Teensyduino_Test_constinit_SPI);
#if defined(__IMXRT1062__)
    print_info(Teensyduino_Test_constinit_HardwareSerial);
#endif
}

void setup() {
    Serial.println("\nsetup");
    print_info(Teensyduino_Test_constinit_Wire);
    print_info(Teensyduino_Test_constinit_SPI);
#if defined(__IMXRT1062__)
    print_info(Teensyduino_Test_constinit_HardwareSerial);
#endif
}

void loop() {
    delay(1);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions