Steve
Steve

Reputation: 5005

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.

Upvotes: 3

Views: 108

Answers (2)

Maks
Maks

Reputation: 7935

For those that may want to access the psram directly instead of using an allocator with just the plain Pico C SDK, turns out all you need to do is initialise the correct pin and mode. So for the Pimoroni PGA2350 you do:

#include "hardware/gpio.h"
#include "hardware/structs/io_bank0.h"
#include "hardware/structs/xip.h"

//...

void psram_init() {
  gpio_set_function(47, GPIO_FUNC_XIP_CS1); // CS for PSRAM
  xip_ctrl_hw->ctrl|=XIP_CTRL_WRITABLE_M1_BITS;
}

reference

Upvotes: 0

Steve
Steve

Reputation: 5005

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()
{
}

Upvotes: 0

Related Questions