c++arduinoraspberry-pi-picoplatformiopimoroni-pico-plus-2

How to access PSRAM - Pimoroni Pico Plus 2 (RP2350) | PlatformIO


I'm trying to access the PSRAM on a Pimoroni pico plus 2 but im not very skilled in C++.

I'm using platform io.

platformio.ini:

[env:rpipico2]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
build_flags = -fexceptions
board = rpipico2
board_build.core = earlephilhower
framework = arduino
lib_deps = 
    SPI

Things I've tried

1 - AndrewCapon's library

This library: https://github.com/AndrewCapon/PicoPlusPsram, however the board would freeze when I called getInstance.

2 - Using lwmem directly

I was trying to do a simple routine of adding numbers to an array then printing them:

// ChatGPT slop:

#include <Arduino.h>
#include <lwmem/lwmem.h>

//---------------------------------------------------------------------------
// 1) Configure PSRAM region
//    (Addresses/size may differ on your board)
//---------------------------------------------------------------------------
#define PSRAM_LOCATION (0x11000000)  // Common base address on some Pico-like boards
#define PSRAM_SIZE (8 * 1024 * 1024) // Example: 8 MB PSRAM

static lwmem_region_t psram_regions[] = {
    {(void *)PSRAM_LOCATION, PSRAM_SIZE},
    {NULL, 0} // Terminator
};

//---------------------------------------------------------------------------
// 2) Global variables
//---------------------------------------------------------------------------
static int *myArray = nullptr;  // Pointer to array in PSRAM
static size_t arraySize = 10;   // How many elements in our array
static size_t currentIndex = 0; // Tracks where we write next

//---------------------------------------------------------------------------
// 3) Setup
//---------------------------------------------------------------------------
void setup()
{
    Serial.begin(115200);
    while (!Serial)
    {
        // Wait for Serial on some boards
    }
    delay(1000);

    // Let lwmem know it can use our PSRAM region
    lwmem_assignmem(psram_regions);
    Serial.println("Assigned PSRAM region to lwmem.");

    // Use calloc so the array is zero-initialized
    myArray = (int *)lwmem_calloc(arraySize, sizeof(int));
    if (!myArray)
    {
        Serial.println("PSRAM allocation failed!");
        while (true)
        { /* halt */
        }
    }
    Serial.println("Allocated zero-initialized array in PSRAM.");

    // Print initial contents (should all be zero)
    Serial.println("Initial array contents:");
    for (size_t i = 0; i < arraySize; i++)
    {
        Serial.print(myArray[i]);
        if (i < arraySize - 1)
        {
            Serial.print(", ");
        }
    }
    Serial.println();
}

//---------------------------------------------------------------------------
// 4) Loop
//---------------------------------------------------------------------------
void loop()
{
    static unsigned long lastPrint = 0;
    if (millis() - lastPrint >= 5000)
    {
        lastPrint = millis();

        // Store a random value in the array
        int value = random(0, 1000); // Range: [0 .. 999]
        myArray[currentIndex] = value;

        Serial.print("Added ");
        Serial.print(value);
        Serial.print(" at index ");
        Serial.println(currentIndex);

        // Print entire array
        Serial.print("Current array contents: ");
        for (size_t i = 0; i < arraySize; i++)
        {
            Serial.print(myArray[i]);
            if (i < arraySize - 1)
            {
                Serial.print(", ");
            }
        }
        Serial.println();

        // Move to next index, wrap around at the end
        currentIndex = (currentIndex + 1) % arraySize;
    }
}

Output was this so I figure the ram hasnt been mapped?

-initialized array in PSRAM.
Initial array contents:
0, -858993524, 0, -858993524, 0, -858993524, 0, -858993524, 0, -858993524
Added 933 at index 0
Current array contents: 0, -858993524, 0, -858993524, 0, -858993524, 0, -858993524, 0, -858993524
Added 743 at index 1
Current array contents: 0, -858993524, 0, -858993524, 0, -858993524, 0, -858993524, 0, -858993524

3 - Attempt to map the PSRAM

I asked ChatGPT to configure the PSRAM before running the same demo. It gave the following and when I ran it, I would get the same freezing behaviour as the first attempt.

// main.cpp
#include <Arduino.h>

// ============== Attempt to pull in Pico SDK hardware headers ==============
extern "C" {
#include <lwmem/lwmem.h>
}
#include "pico/stdlib.h"
#include "hardware/structs/ioqspi.h"
#include "hardware/structs/qmi.h"
#include "hardware/structs/xip_ctrl.h"
#include "hardware/sync.h"
#include "hardware/clocks.h"

// ------------- Config for your external PSRAM -------------
#define PIMORONI_PICO_PLUS2_PSRAM_CS_PIN 29
#define PSRAM_BASE_ADDR 0x11000000
#define PSRAM_SIZE_BYTES (8 * 1024 * 1024) // 8 MB example

// ------------- lwmem region for PSRAM -------------
static lwmem_region_t psram_regions[] = {
    { (void*)PSRAM_BASE_ADDR, PSRAM_SIZE_BYTES },
    { NULL, 0 }
};

// ------------- Mark function to (try to) place in ramfunc -------------
#define PSRAM_INIT_FN __attribute__((section(".ramfunc")))

// ------------- Minimal PSRAM init function -------------
PSRAM_INIT_FN bool psram_init_minimal(uint cs_pin) {
    // 1) Setup CS pin for XIP
    gpio_set_function(cs_pin, GPIO_FUNC_XIP_CS1);

    // Disable interrupts
    uint32_t save = save_and_disable_interrupts();

    // Enter direct mode with safe divider
    qmi_hw->direct_csr = (30 << QMI_DIRECT_CSR_CLKDIV_LSB) | QMI_DIRECT_CSR_EN_BITS;
    while (qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) {
        tight_loop_contents();
    }

    // Example: Send "QPI enable" command (0x35)
    qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS;
    qmi_hw->direct_tx = 0x35;
    // Wait for TX empty
    while (!(qmi_hw->direct_csr & QMI_DIRECT_CSR_TXEMPTY_BITS)) {
        tight_loop_contents();
    }
    // Wait for not busy
    while (qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) {
        tight_loop_contents();
    }
    qmi_hw->direct_csr &= ~QMI_DIRECT_CSR_ASSERT_CS1N_BITS;

    // Setup M1 region
    int clk_sys_hz = clock_get_hz(clk_sys);
    int desired_psram_freq = 133000000;
    int divisor = (clk_sys_hz + desired_psram_freq - 1) / desired_psram_freq;
    if (divisor < 2) {
        divisor = 2;
    }
    int rxdelay = divisor;
    int max_select = 10;
    int min_deselect = 2;

    qmi_hw->m[1].timing =
          (1 << QMI_M1_TIMING_COOLDOWN_LSB)
        | (QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB)
        | (max_select << QMI_M1_TIMING_MAX_SELECT_LSB)
        | (min_deselect << QMI_M1_TIMING_MIN_DESELECT_LSB)
        | (rxdelay << QMI_M1_TIMING_RXDELAY_LSB)
        | (divisor << QMI_M1_TIMING_CLKDIV_LSB);

    // QPI read: 0xEB
    qmi_hw->m[1].rfmt =
          (QMI_M0_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M0_RFMT_PREFIX_WIDTH_LSB)
        | (QMI_M0_RFMT_ADDR_WIDTH_VALUE_Q   << QMI_M0_RFMT_ADDR_WIDTH_LSB)
        | (QMI_M0_RFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M0_RFMT_SUFFIX_WIDTH_LSB)
        | (QMI_M0_RFMT_DUMMY_WIDTH_VALUE_Q  << QMI_M0_RFMT_DUMMY_WIDTH_LSB)
        | (QMI_M0_RFMT_DATA_WIDTH_VALUE_Q   << QMI_M0_RFMT_DATA_WIDTH_LSB)
        | (QMI_M0_RFMT_PREFIX_LEN_VALUE_8   << QMI_M0_RFMT_PREFIX_LEN_LSB)
        | (6 << QMI_M0_RFMT_DUMMY_LEN_LSB);
    qmi_hw->m[1].rcmd = 0xEB;

    // QPI write: 0x38
    qmi_hw->m[1].wfmt =
          (QMI_M0_WFMT_PREFIX_WIDTH_VALUE_Q << QMI_M0_WFMT_PREFIX_WIDTH_LSB)
        | (QMI_M0_WFMT_ADDR_WIDTH_VALUE_Q   << QMI_M0_WFMT_ADDR_WIDTH_LSB)
        | (QMI_M0_WFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M0_WFMT_SUFFIX_WIDTH_LSB)
        | (QMI_M0_WFMT_DUMMY_WIDTH_VALUE_Q  << QMI_M0_WFMT_DUMMY_WIDTH_LSB)
        | (QMI_M0_WFMT_DATA_WIDTH_VALUE_Q   << QMI_M0_WFMT_DATA_WIDTH_LSB)
        | (QMI_M0_WFMT_PREFIX_LEN_VALUE_8   << QMI_M0_WFMT_PREFIX_LEN_LSB);
    qmi_hw->m[1].wcmd = 0x38;

    // Exit direct mode
    qmi_hw->direct_csr = 0;

    // Enable writes to M1
    hw_set_bits(&xip_ctrl_hw->ctrl, XIP_CTRL_WRITABLE_M1_BITS);

    restore_interrupts(save);
    return true;
}

// ------------- Demo array -------------
static int*    myArray      = nullptr;
static size_t  arraySize    = 10;
static size_t  currentIndex = 0;

// ------------- Setup -------------
void setup() {
    Serial.begin(115200);
    delay(1000);
    Serial.println("Starting Arduino + PSRAM + lwmem demo...");

    // Attempt to init PSRAM
    Serial.println("Initializing external PSRAM...");
    if (!psram_init_minimal(PIMORONI_PICO_PLUS2_PSRAM_CS_PIN)) {
        Serial.println("PSRAM init failed!");
        while (true) { }
    }
    Serial.println("PSRAM init success (hopefully)!");

    // Assign lwmem region
    lwmem_assignmem(psram_regions);
    Serial.println("Assigned lwmem to use PSRAM region.");

    // Allocate array in PSRAM
    myArray = (int*) lwmem_calloc(arraySize, sizeof(int));
    if (!myArray) {
        Serial.println("PSRAM allocation failed!");
        while (true) { }
    }
    Serial.print("Allocated an array of ");
    Serial.print(arraySize);
    Serial.println(" integers in PSRAM.");

    // Print initial contents
    Serial.println("Initial array contents:");
    for (size_t i = 0; i < arraySize; i++) {
        Serial.print(myArray[i]);
        if (i < arraySize - 1) Serial.print(", ");
    }
    Serial.println();
}

// ------------- Loop -------------
void loop() {
    static unsigned long lastPrint = 0;
    if (millis() - lastPrint >= 5000) {
        lastPrint = millis();

        // Store a random value
        int val = random(0, 1000);
        myArray[currentIndex] = val;

        Serial.print("Wrote ");
        Serial.print(val);
        Serial.print(" at index ");
        Serial.println(currentIndex);

        // Print the whole array
        Serial.print("Array: ");
        for (size_t i = 0; i < arraySize; i++) {
            Serial.print(myArray[i]);
            if (i < arraySize - 1) Serial.print(", ");
        }
        Serial.println();

        currentIndex = (currentIndex + 1) % arraySize;
    }
}

ChatGPT suggested that using the Arduino framework is my issue because it interrupts the reading of the code from flash.

Unfortunately this is not my wheelhouse so im really struggling. A minimal working demo similar to the above would be so helpful but I can't find anything online.


Solution

  • The answer was in the config 💀

    platformio.ini

    
    [env:rpipico2]
    platform = https://github.com/maxgerhardt/platform-raspberrypi.git
    build_flags = -fexceptions
                 -DRP2350_PSRAM_CS=47
    board = rpipico2
    board_build.core = earlephilhower
    board_build.pico_boot2_name = boot2_psram8.S
    board_build.pico_psram_size = 8
    board_upload.psram_length = 8388608
    framework = arduino
    lib_deps = 
        SPI
    
    

    Then I just made a helper:

    PsramAllocator.h

    #ifndef PSRAM_ALLOCATOR_H
    #define PSRAM_ALLOCATOR_H
    
    #include <Arduino.h>
    
    extern "C"
    {
        void *pmalloc(size_t size);
    }
    
    template <class T>
    struct PsramAllocator
    {
        using value_type = T;
    
        PsramAllocator() noexcept {}
        template <class U>
        PsramAllocator(const PsramAllocator<U> &) noexcept {}
    
        T *allocate(std::size_t n)
        {
            if (auto p = static_cast<T *>(pmalloc(n * sizeof(T))))
            {
                return p;
            }
            throw std::bad_alloc();
        }
    
        void deallocate(T *p, std::size_t /*n*/) noexcept
        {
            if (p)
            {
                free(p);
            }
        }
    };
    
    #endif // PSRAM_ALLOCATOR_H
    

    And here is a demo script:

    main.cpp

    #include <Arduino.h>
    #include <vector>
    #include "PsramAllocator.h"
    
    // A vector that uses your PsramAllocator, so its buffer is in PSRAM.
    static std::vector<int, PsramAllocator<int>> psramVector;
    
    void setup()
    {
        Serial.begin(115200);
        delay(5000);
    
        Serial.println("PSRAM Allocator Demo Start");
    
        // Show how much PSRAM was detected
        Serial.print("Detected PSRAM size: ");
        Serial.println(rp2040.getPSRAMSize());
    
        // Reserve space in the PSRAM vector
        psramVector.resize(100);
        
        // Write some values
        for (size_t i = 0; i < psramVector.size(); i++)
        {
            psramVector[i] = i * 2; // e.g. fill with even numbers
        }
    
        Serial.println("Data written to psramVector in external PSRAM.");
    
        delay(1000);
    
        // Read them back
        Serial.println("Reading back the first 10 elements:");
        for (size_t i = 0; i < 10 && i < psramVector.size(); i++)
        {
            Serial.print("psramVector[");
            Serial.print(i);
            Serial.print("] = ");
            Serial.println(psramVector[i]);
        }
    }
    
    void loop()
    {
    }