Skip to content

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

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);
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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