Pi Pico cores seemingly not truly independent of one another

I have a program that runs on a Pi Pico, and I utilize the feature of running code in the second core of the CPU.

What it seems like I have discovered, is that when a delay() is performed in the main core, the code in the second core runs faster until that delay is done. This begs the question; is the second core truly independent of the first?

The code controls some stepper motors, where I don't utilize any libraries to control them, I work out the calculations for stepping and delays between steps and the second core has only one job which is that if the boolean - run is true, then it calls the step methods, along with the microsecond delays that happen between steps, and the delays, in this case, are non-blocking timers so that the steps can be performed only at the moments when that stepper motors time is up to do a step.

I'll spare you the details of the code, and only show what is relevant here:

#include "pico/multicore.h"
#include <BlockNot.h>

const int PEDAL  = 27; //GP27
const int STEP_1 = 14; //GP14
const int STEP_2 = 10; //GP10

volatile float rpm = 0;
volatile bool run = false;

BlockNot stepper1Delay(250, MICROSECONDS);
BlockNot stepper2Delay(250, MICROSECONDS);

#define READ_PEDAL analogRead(PEDAL)

void checkSerial() {
    String input = Serial.readString();
    if(input.startsWith("d")) {
        int ms = input.substring(1).toInt();
        delay(ms);
    }
}

void setSpeed() {
    int pot = READ_PEDAL;
    rpm = map(pot, 25, 1024, 0, 250);
    run = rpm > 0;
    setStepper1Delay(); //Changes the value of the stepper1Delay timer
    setStepper2Delay(); //Changes the value of the stepper2Delay timer
}

void stepStepper1() {
    digitalWrite(STEP_1, HIGH);
    delayMicroseconds(2);
    digitalWrite(STEP_1, LOW);
}

void stepStepper2() {
    digitalWrite(STEP_2, HIGH);
    delayMicroseconds(2);
    digitalWrite(STEP_2, LOW);
}

[[noreturn]] void core1Entry() {
    while (true) {
        if (run) {
            if(stepper1Delay.TRIGGERED)
                stepStepper1();
            if(stepper2Delay.TRIGGERED)
                stepStepper2();
        }
    }
}

void setup() {
    Serial.begin(115200);
    setPins(); //sets the pins to the stepper drivers as OUTPUT pins and sets the PEDAL pin as an INPUT pin.
    multicore_launch_core1(core1Entry);
}

void loop() {
    setSpeed();
    if(Serial.available())
        checkSerial();
}

Now, when I run this code, but then send, for example, d5000 to the serial port, while that delay is processing, the motors run a lot faster, then they slow down once the delay is over.

What, exactly am I seeing here? Do the two cores share a clock of some type? Clearly, the core1 code is running independently of the main cores code, or else the steppers wouldn't step at all when the delay command is blocking, but the fact that the steppers run faster while that delay is blocking is not what I would expect to see, so I can't really explain what is happening.

Any ideas?

the share the same memory(s); there is bound to be some contention there, especially if running from flash.

I believe that the two CPUs share the XIP cache, and I'm not sure it's smart enough to effectively cache the instruction streams from two different CPUs running at different addresses. Potentially, that means that running both CPUs from flash has a dramatic effect on performance!

Are there any other options than running from flash?

Can you make a test that we can try ? For example with a math calculation.
The millis() and delay() should still be the same. If those change depending on what is done on the other core, that would be a big problem. Is it only the delayMicroseconds() that changes ?

There are many ways to run code on the Raspberry Pi Pico. Are you using the official Arduino on top of Mbed ?

I see nothing special in its variant, so this Mbed code is used for millis(), micros(), delay() and delayMicroseconds(): https://github.com/arduino/ArduinoCore-mbed/blob/master/cores/arduino/wiring.cpp

OK, here is the actual code of the program that I'm running.

I use PlatformIO with CLion IDE. It handles compiling and uploading just fine. It's a far more luxurious interface than the stock Arduino IDE.

The program uses three DRV8825 drivers, but with two of them, I simply mirror the logic pins to the pico, and then I reverse the coils on the second driver. The motors face each other and when stepper1 is stepped, those motors spin identically but they both spin in the same direction (because they are facing each other).

The third DRV8825 turns a piece of allthread that I have a 3D printed object mounted on.

The project is used to make vape coils. While the two facing motors spin a wire (or a group of wires) in the same direction, I can have a spool of wire on the allthread that is feeding wire to the spinning wire and that allthread moves at a speed that is dictated by the wires gauge size to that it properly wraps the wire around the spinning wire.

This is why you might be puzzled about how I did certain calculations. I found it easier to keep everything in float units for all the calculations to prevent the need to convert anything until the number is actually needed.

With a serial terminal, I can set the wire gauge of the "feeding" wire, which has a direct impact on the speed of stepper2. I can also change the microstepping which I only included for testing to find optimal spinning for the motors.

BlockNot is a library that I wrote. I just re-wrote it so that it can do microsecond timing and I just published that update about 20 minutes ago so I'm not sure when it will hit the Arduino repository, but in your IDE, if you look it up and see that it is 2.0.0, then that's the version that will work with this code.

Otherwise, you can download it and add it manually.

#include "pico/multicore.h"
#include <BlockNot.h>

short MICROSTEPS = 32;

const int M0_1 = 0; //GP0
const int M1_1 = 1; //GP1
const int M2_1 = 2; //GP2
const int SLEEP_1 = 3; //GP3
const int STEP_1 = 14; //GP14
const int DIRE_1 = 15; //GP15

const int M0_2 = 6; //GP6
const int M1_2 = 7; //GP7
const int M2_2 = 8; //GP8
const int SLEEP_2 = 9; //GP9
const int STEP_2 = 10; //GP10
const int DIRE_2 = 11; //GP11

const int PEDAL = 27;//GP27
const int POS = 26;//GP26
const int BUTTON1 = 19;//GP19
const int BUTTON2 = 21;//GP21
const int BUTTON3 = 22;//GP22

volatile float rpm = 0;
volatile bool run = false;
volatile float pot;
volatile float staggard = 1;
int selectedGauge = 33;

BlockNot dataTimer(1000);
BlockNot stepper1Delay(250, MICROSECONDS);
BlockNot stepper2Delay(250, MICROSECONDS);

unsigned long microStart1 = micros();
unsigned long microStart2 = micros();

#define READ_PEDAL analogRead(PEDAL)
#define REVERSE digitalRead(BUTTON1) == LOW
#define BUTTON2_PRESSED digitalRead(BUTTON2) == LOW
#define BUTTON3_PRESSED digitalRead(BUTTON3) == LOW

float reMap(float, float, float, float, float);
void showData();
uint delayCalc(float);
void setStepper1Delay();
void setStepper2Delay();
void microsteps();
void checkSerial();
void setSpeed();
void sleepStepper();
void wakeStepper();
void stepStepper1();
void stepStepper2();
void setPins();

const float gauge[] = {8.25146,7.34814,6.54371,5.82734,5.18940,4.62129,4.11538,3.66485,3.26364,2.90636,2.58819,2.30485,
                       2.05253,1.82783,1.62773,1.44953,1.29085,1.14953,1.02369,0.91162,0.81182,0.72295,0.64380,
                       0.57332,0.51056,0.45467,0.40489,0.36057,0.32109,0.28594,0.25464,0.22676,0.20194,0.17983,
                       0.16014,0.14261,0.12700,0.11310,0.10072,0.08969,0.07987,0.07113,0.06334,0.05641,0.05023,
                       0.04473};

float reMap(float sourceNumber, float fromRangeStart, float fromRangeEnd, float toRangeStart, float toRangeEnd) {
    float deltaA = fromRangeEnd - fromRangeStart;
    float deltaB = toRangeEnd - toRangeStart;
    float scale = deltaB / deltaA;
    float negA = -1 * fromRangeStart;
    float offset = (negA * scale) + toRangeStart;
    float finalNumber = (sourceNumber * scale) + offset;
    return finalNumber;
}

void showData() {
    String message =
            "D1: " + String(stepper1Delay.getDuration()) + "\n" +
            "D2: " + String(stepper2Delay.getDuration()) + "\n" +
            "Pot: " + String(pot) + "\n" +
            "Rev: " + (REVERSE ? "True" : "False") + "\n" +
            "RPM: " + String(rpm) + "\n" +
            "Staggard: " + ((staggard == 2) ? "YES" : "NO");
    Serial.println(message);
}

uint delayCalc(float RPM) {
    float numerator = RPM * 360 * (float) MICROSTEPS;
    float denominator = 60 * 1.8;
    float stepsPerSecond = numerator / denominator;
    float steppingMicrosecondsPerSecond = stepsPerSecond * 2.0F;
    float delta = 1000000 - steppingMicrosecondsPerSecond;
    return (uint) ((delta / stepsPerSecond)/2);
}

void setStepper1Delay() {
    stepper1Delay.setDuration((rpm == 0) ? 0 :  delayCalc(rpm));
}

void setStepper2Delay() {
    float mmToTravel = gauge[selectedGauge] * staggard;
    auto revPerGauge = (float) (mmToTravel * .75);
    float rpm2 = rpm * revPerGauge;
    stepper2Delay.setDuration((rpm == 0) ? 0 : delayCalc(rpm2));
}

void microsteps() {
    switch(MICROSTEPS) {
        case 1:
            digitalWrite(M0_1, LOW);
            digitalWrite(M0_2, LOW);
            digitalWrite(M1_1, LOW);
            digitalWrite(M1_2, LOW);
            digitalWrite(M2_1, LOW);
            digitalWrite(M2_2, LOW);
            break;

        case 2:
            digitalWrite(M0_1, LOW);
            digitalWrite(M0_2, LOW);
            digitalWrite(M1_1, LOW);
            digitalWrite(M1_2, LOW);
            digitalWrite(M2_1, HIGH);
            digitalWrite(M2_2, HIGH);
            break;

        case 4:
            digitalWrite(M0_1, LOW);
            digitalWrite(M0_2, LOW);
            digitalWrite(M1_1, HIGH);
            digitalWrite(M1_2, HIGH);
            digitalWrite(M2_1, LOW);
            digitalWrite(M2_2, LOW);
            break;

        case 8:
            digitalWrite(M0_1, LOW);
            digitalWrite(M0_2, LOW);
            digitalWrite(M1_1, HIGH);
            digitalWrite(M1_2, HIGH);
            digitalWrite(M2_1, HIGH);
            digitalWrite(M2_2, HIGH);
            break;

        case 16:
            digitalWrite(M0_1, HIGH);
            digitalWrite(M0_2, HIGH);
            digitalWrite(M1_1, LOW);
            digitalWrite(M1_2, LOW);
            digitalWrite(M2_1, LOW);
            digitalWrite(M2_2, LOW);
            break;

        case 32:
            digitalWrite(M0_1, HIGH);
            digitalWrite(M0_2, HIGH);
            digitalWrite(M1_1, HIGH);
            digitalWrite(M1_2, HIGH);
            digitalWrite(M2_1, HIGH);
            digitalWrite(M2_2, HIGH);
            break;
        default:
            break;
    }
    Serial.print(F("Microsteps: "));
    Serial.println(MICROSTEPS);
}

void checkSerial() {
    String input = Serial.readString();
    if(input.startsWith("m")) {
        MICROSTEPS = (short) input.substring(1).toInt();
        microsteps();
        Serial.print(F("Microsteps: "));
        Serial.println(MICROSTEPS);
    }
    else if(input.startsWith("g")) {
        selectedGauge = input.substring(1).toInt();
        Serial.print(F("Selected Gauge: "));
        Serial.println(selectedGauge);
    }
    else if(input.startsWith("d")) {
        int ms = input.substring(1).toInt();
        delay(ms);
    }
}

void setSpeed() {
    static bool lastRun = false;
    pot = READ_PEDAL;
    rpm = reMap(pot, 25, 1024, 0, 200);
    run = rpm > 0;
    if (rpm < 0) rpm = 0;
    bool changingState = lastRun != run;
    if (changingState && run) {
        wakeStepper();
    }
    if (changingState && !run) {
        sleepStepper();
    }
    lastRun = run;
    setStepper1Delay();
    setStepper2Delay();
}

void sleepStepper() {
    run = false;
    digitalWrite(SLEEP_1, LOW);
    digitalWrite(SLEEP_2, LOW);
}

void wakeStepper() {
    run = true;
    digitalWrite(SLEEP_1, HIGH);
    digitalWrite(SLEEP_2, HIGH);
    delay(2);
}

void stepStepper1() {
    digitalWrite(STEP_1, HIGH);
    delayMicroseconds(3);
    digitalWrite(STEP_1, LOW);
}

void stepStepper2() {
    digitalWrite(STEP_2, HIGH);
    delayMicroseconds(3);
    digitalWrite(STEP_2, LOW);
}

void setPins() {
    pinMode(SLEEP_1, OUTPUT);
    pinMode(STEP_1, OUTPUT);
    pinMode(DIRE_1, OUTPUT);
    pinMode(M2_1, OUTPUT);
    pinMode(M0_1, OUTPUT);
    pinMode(M1_1, OUTPUT);
    pinMode(SLEEP_2, OUTPUT);
    pinMode(STEP_2, OUTPUT);
    pinMode(DIRE_2, OUTPUT);
    pinMode(M2_2, OUTPUT);
    pinMode(M0_2, OUTPUT);
    pinMode(M1_2, OUTPUT);
    pinMode(POS, OUTPUT);
    pinMode(PEDAL, INPUT);
    pinMode(BUTTON1, INPUT_PULLUP);
    pinMode(BUTTON2, INPUT_PULLUP);
    pinMode(BUTTON3, INPUT_PULLUP);
    digitalWrite(POS, HIGH); //Set high for BUTTON1 so button can sit directly on the MC pins - used for reversing the motors.
}

[[noreturn]] void core1Entry() {
    while (true) {
        if (run) {
            if (REVERSE) {
                digitalWrite(DIRE_1, HIGH);
                digitalWrite(DIRE_2, LOW);
            } else {
                digitalWrite(DIRE_1, LOW);
                digitalWrite(DIRE_2, HIGH);
            }
            if (stepper1Delay.TRIGGERED)
                stepStepper1();
            if (stepper2Delay.TRIGGERED)
                stepStepper2();
        }
    }
}

void setup() {
    Serial.begin(115200);
    setPins();
    microsteps();
    sleepStepper();
    multicore_launch_core1(core1Entry);
}

void loop() {
    setSpeed();
    if(BUTTON2_PRESSED) {
        delay(300);
        if(staggard == 1) staggard = 2;
        else staggard = 1;
    }
    if(BUTTON3_PRESSED) {
        if(dataTimer.TRIGGERED)
            showData();
    }
    if(Serial.available())
        checkSerial();
}