Skip to content

Commit 43a2714

Browse files
committed
make things work on the pimoroni2w plus
1 parent 102f7f8 commit 43a2714

File tree

5 files changed

+258
-25
lines changed

5 files changed

+258
-25
lines changed

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CHIP=pico2w
1+
CHIP=pimoroni2w
22

33
check:
44
cargo +nightly check --features $(CHIP)
@@ -11,6 +11,10 @@ image:
1111
PATH=$(PWD)/picotool/build:${PATH} RUST_LOG=info RUSTC_LOG=rustc_codegen_ssa::back::link=info cargo +nightly build --release --features $(CHIP)
1212
PATH=$(PWD)/picotool/build:${PATH} picotool uf2 convert -t elf target/thumbv8m.main-none-eabihf/release/picocalc-wezterm wezterm-$(CHIP)-`git -c core.abbrev=8 show -s --format=%cd-%h --date=format:%Y%m%d`.uf2
1313

14+
flash-ocd: image
15+
PATH=$(PWD)/picotool/build:${PATH} RUST_LOG=info RUSTC_LOG=rustc_codegen_ssa::back::link=info cargo +nightly build --release --features $(CHIP)
16+
openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" -c "program target/thumbv8m.main-none-eabihf/release/picocalc-wezterm verify reset exit"
17+
1418
flash:
1519
PATH=$(PWD)/picotool/build:${PATH} RUST_LOG=info RUSTC_LOG=rustc_codegen_ssa::back::link=info cargo +nightly run --release --features $(CHIP)
1620
#PATH=$(PWD)/picotool/build:${PATH} cargo +nightly run --release

pimoroni2w.x

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,21 @@ MEMORY {
1010
* This is usually good for performance, as it distributes load on
1111
* those banks evenly.
1212
*/
13-
RAM : ORIGIN = 0x20000000, LENGTH = 512K
13+
RAM : ORIGIN = 0x20000000, LENGTH = 516K
1414
/*
1515
* RAM banks 8 and 9 use a direct mapping. They can be used to have
1616
* memory areas dedicated for some specific job, improving predictability
1717
* of access times.
1818
* Example: Separate stacks for core0 and core1.
1919
*/
20+
/*
2021
SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K
22+
*/
2123
SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K
2224
}
2325

24-
_panic_dump_start = ORIGIN(SRAM4);
25-
_panic_dump_end = ORIGIN(SRAM4) + LENGTH(SRAM4);
26+
_panic_dump_start = ORIGIN(SRAM5);
27+
_panic_dump_end = ORIGIN(SRAM5) + LENGTH(SRAM5);
2628

2729
SECTIONS {
2830
/* ### Boot ROM info

src/heap.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use embedded_alloc::LlffHeap as Heap;
77
pub static HEAP: DualHeap = DualHeap::empty();
88
const HEAP_SIZE: usize = 64 * 1024;
99
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
10-
static mut HEAP_TWO: [MaybeUninit<u8>; 1024] = [MaybeUninit::uninit(); 1024];
1110

1211
struct Region {
1312
start: AtomicUsize,
@@ -113,12 +112,10 @@ unsafe impl GlobalAlloc for DualHeap {
113112
pub fn init_heap() {
114113
let primary_start = &raw mut HEAP_MEM as usize;
115114
unsafe { HEAP.add_primary(Region::new(primary_start, HEAP_SIZE)) }
115+
}
116116

117-
// The idea is that internal PSRAM would get added as the secondary.
118-
// This is just a proof of concept that we can make an aggregating
119-
// heap allocator
120-
let secondary_start = &raw mut HEAP_TWO as usize;
121-
unsafe { HEAP.add_secondary(Region::new(secondary_start, 1024)) }
117+
pub fn init_qmi_psram_heap(size: u32) {
118+
unsafe { HEAP.add_secondary(Region::new(0x11000000, size as usize)) }
122119
}
123120

124121
pub async fn free_command(_args: &[&str]) {
@@ -135,11 +132,11 @@ pub async fn free_command(_args: &[&str]) {
135132
"RAM"
136133
);
137134

138-
let xip_used = HEAP.secondary.used();
139-
let xip_free = HEAP.secondary.free();
140-
let xip_total = xip_used + xip_free;
135+
let qmi_used = HEAP.secondary.used();
136+
let qmi_free = HEAP.secondary.free();
137+
let qmi_total = qmi_used + qmi_free;
141138
print!(
142-
"{:<10} {xip_total:>10} {xip_used:>10} {xip_free:>10}\r\n",
143-
"XIP"
139+
"{:<10} {qmi_total:>10} {qmi_used:>10} {qmi_free:>10}\r\n",
140+
"PSRAM (QMI)"
144141
);
145142
}

src/main.rs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
#![no_main]
44

55
use crate::config::{CONFIG, Flash};
6-
use crate::heap::HEAP;
7-
use crate::psram::init_psram;
6+
use crate::heap::{HEAP, init_qmi_psram_heap};
7+
use crate::psram::{init_psram, init_psram_qmi};
88
use crate::screen::SCREEN;
99
use crate::storage::init_storage;
1010
use core::cell::RefCell;
@@ -155,13 +155,17 @@ async fn main(spawner: Spawner) {
155155
)
156156
.await;
157157

158-
print!("\u{1b}[35mWezTerm {} ({})\u{1b}[0m\r\n", env!("WEZTERM_CI_TAG"), if cfg!(feature="pico2w") {
159-
"pico2w"
160-
} else if cfg!(feature="pimoroni2w") {
161-
"pimoroni2w"
162-
} else {
163-
""
164-
});
158+
print!(
159+
"\u{1b}[35mWezTerm {} ({})\u{1b}[0m\r\n",
160+
env!("WEZTERM_CI_TAG"),
161+
if cfg!(feature = "pico2w") {
162+
"pico2w"
163+
} else if cfg!(feature = "pimoroni2w") {
164+
"pimoroni2w"
165+
} else {
166+
""
167+
}
168+
);
165169

166170
if let Some(msg) = panic_persist::get_panic_message_utf8() {
167171
// Give serial a chance to be ready to capture this info
@@ -240,11 +244,20 @@ async fn main(spawner: Spawner) {
240244
)
241245
.await;
242246

247+
let psram_qmi_size = init_psram_qmi(&embassy_rp::pac::QMI, &embassy_rp::pac::XIP_CTRL);
248+
if psram_qmi_size > 0 {
249+
init_qmi_psram_heap(psram_qmi_size);
250+
}
251+
243252
{
244253
print!(
245-
"RAM {} avail of 512KiB. PSRAM {}\r\n",
254+
"RAM {} avail of 520KiB\r\n",
246255
byte_size(get_max_usable_stack()),
256+
);
257+
print!(
258+
"PSRAM: {} (SLOW), {} (QMI)\r\n",
247259
byte_size(psram.size),
260+
byte_size(psram_qmi_size),
248261
);
249262
if psram.size == 0 {
250263
// This can happen if you power on the pico without first

src/psram.rs

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::Irqs;
22
use embassy_futures::yield_now;
33
use embassy_rp::PeripheralRef;
4+
use embassy_rp::clocks::clk_peri_freq;
45
use embassy_rp::gpio::Drive;
56
use embassy_rp::peripherals::{DMA_CH1, DMA_CH2, PIN_2, PIN_3, PIN_20, PIN_21, PIO1};
67
use embassy_rp::pio::program::pio_asm;
@@ -434,3 +435,219 @@ async fn test_psram(psram: &mut PsRam) -> bool {
434435

435436
bad_count == 0
436437
}
438+
439+
// The origin of the code in this file is:
440+
// <https://github.com/Altaflux/rp2350-psram-test/blob/ae50a819fef96486f6d962a609984cde4b4dd4cc/src/psram.rs#L1>
441+
// which is MIT/Apache-2 licensed.
442+
#[unsafe(link_section = ".data")]
443+
#[inline(never)]
444+
pub fn detect_psram_qmi(qmi: &embassy_rp::pac::qmi::Qmi) -> u32 {
445+
const GPIO_FUNC_XIP_CS1: u8 = 9;
446+
const XIP_CS_PIN: usize = 47;
447+
embassy_rp::pac::PADS_BANK0.gpio(XIP_CS_PIN).modify(|w| {
448+
w.set_iso(true);
449+
});
450+
embassy_rp::pac::PADS_BANK0.gpio(XIP_CS_PIN).modify(|w| {
451+
w.set_ie(true);
452+
w.set_od(false);
453+
});
454+
embassy_rp::pac::IO_BANK0
455+
.gpio(XIP_CS_PIN)
456+
.ctrl()
457+
.write(|w| w.set_funcsel(GPIO_FUNC_XIP_CS1));
458+
embassy_rp::pac::PADS_BANK0.gpio(XIP_CS_PIN).modify(|w| {
459+
w.set_iso(false);
460+
});
461+
462+
critical_section::with(|_cs| {
463+
// Try and read the PSRAM ID via direct_csr.
464+
qmi.direct_csr().write(|w| {
465+
w.set_clkdiv(30);
466+
w.set_en(true);
467+
});
468+
469+
// Need to poll for the cooldown on the last XIP transfer to expire
470+
// (via direct-mode BUSY flag) before it is safe to perform the first
471+
// direct-mode operation
472+
while qmi.direct_csr().read().busy() {
473+
// rp235x_hal::arch::nop();
474+
}
475+
476+
// Exit out of QMI in case we've inited already
477+
qmi.direct_csr().modify(|w| w.set_assert_cs1n(true));
478+
479+
// Transmit the command to exit QPI quad mode - read ID as standard SPI
480+
// Transmit as quad.
481+
qmi.direct_tx().write(|w| {
482+
w.set_oe(true);
483+
w.set_iwidth(embassy_rp::pac::qmi::vals::Iwidth::Q);
484+
w.set_data(PSRAM_CMD_QUAD_END.into());
485+
});
486+
487+
while qmi.direct_csr().read().busy() {
488+
// rp235x_hal::arch::nop();
489+
}
490+
491+
let _ = qmi.direct_rx().read();
492+
493+
qmi.direct_csr().modify(|w| {
494+
w.set_assert_cs1n(false);
495+
});
496+
497+
// Read the id
498+
qmi.direct_csr().modify(|w| {
499+
w.set_assert_cs1n(true);
500+
});
501+
502+
// kgd is "known good die"
503+
let mut kgd: u16 = 0;
504+
let mut eid: u16 = 0;
505+
for i in 0usize..7 {
506+
qmi.direct_tx().write(|w| {
507+
w.set_data(if i == 0 {
508+
PSRAM_CMD_READ_ID.into()
509+
} else {
510+
PSRAM_CMD_NOOP.into()
511+
})
512+
});
513+
514+
while !qmi.direct_csr().read().txempty() {
515+
// rp235x_hal::arch::nop();
516+
}
517+
518+
while qmi.direct_csr().read().busy() {
519+
// rp235x_hal::arch::nop();
520+
}
521+
522+
let value = qmi.direct_rx().read().direct_rx();
523+
match i {
524+
5 => {
525+
kgd = value;
526+
}
527+
6 => {
528+
eid = value;
529+
}
530+
_ => {}
531+
}
532+
}
533+
534+
qmi.direct_csr().modify(|w| {
535+
w.set_assert_cs1n(false);
536+
w.set_en(false);
537+
});
538+
let mut param_size: u32 = 0;
539+
if kgd == PSRAM_KNOWN_GOOD_DIE_PASS as u16 {
540+
param_size = 1024 * 1024;
541+
let size_id = eid >> 5;
542+
if eid == 0x26 || size_id == 2 {
543+
param_size *= 8;
544+
} else if size_id == 0 {
545+
param_size *= 2;
546+
} else if size_id == 1 {
547+
param_size *= 4;
548+
}
549+
}
550+
param_size
551+
})
552+
}
553+
554+
#[unsafe(link_section = ".data")]
555+
#[inline(never)]
556+
pub fn init_psram_qmi(
557+
qmi: &embassy_rp::pac::qmi::Qmi,
558+
xip: &embassy_rp::pac::xip_ctrl::XipCtrl,
559+
) -> u32 {
560+
let psram_size = detect_psram_qmi(qmi);
561+
562+
if psram_size == 0 {
563+
return 0;
564+
}
565+
566+
// Set PSRAM timing for APS6404
567+
//
568+
// Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133MHz.
569+
// So: don't allow running at divisor 1 above 100MHz (because delay of 2 would be too late),
570+
// and add an extra 1 to the rxdelay if the divided clock is > 100MHz (i.e. sys clock > 200MHz).
571+
const MAX_PSRAM_FREQ: u32 = 133_000_000;
572+
573+
let clock_hz = clk_peri_freq();
574+
575+
let mut divisor: u32 = (clock_hz + MAX_PSRAM_FREQ - 1) / MAX_PSRAM_FREQ;
576+
if divisor == 1 && clock_hz > 100_000_000 {
577+
divisor = 2;
578+
}
579+
let mut rxdelay: u32 = divisor;
580+
if clock_hz / divisor > 100_000_000 {
581+
rxdelay += 1;
582+
}
583+
584+
// - Max select must be <= 8us. The value is given in multiples of 64 system clocks.
585+
// - Min deselect must be >= 18ns. The value is given in system clock cycles - ceil(divisor / 2).
586+
let clock_period_fs: u64 = 1_000_000_000_000_000_u64 / u64::from(clock_hz);
587+
let max_select: u8 = ((125 * 1_000_000) / clock_period_fs) as u8;
588+
let min_deselect: u32 = ((18 * 1_000_000 + (clock_period_fs - 1)) / clock_period_fs
589+
- u64::from(divisor + 1) / 2) as u32;
590+
591+
log::info!(
592+
"clock_period_fs={clock_period_fs} max_select={max_select} min_deselect={min_deselect}"
593+
);
594+
595+
qmi.direct_csr().write(|w| {
596+
w.set_clkdiv(10);
597+
w.set_en(true);
598+
w.set_auto_cs1n(true);
599+
});
600+
601+
while qmi.direct_csr().read().busy() {
602+
// rp235x_hal::arch::nop();
603+
}
604+
605+
qmi.direct_tx().write(|w| {
606+
w.set_nopush(true);
607+
w.0 = 0x35;
608+
});
609+
610+
while qmi.direct_csr().read().busy() {
611+
// rp235x_hal::arch::nop();
612+
}
613+
614+
qmi.mem(1).timing().write(|w| {
615+
w.set_cooldown(1);
616+
w.set_pagebreak(embassy_rp::pac::qmi::vals::Pagebreak::_1024);
617+
w.set_max_select(max_select as u8);
618+
w.set_min_deselect(min_deselect as u8);
619+
w.set_rxdelay(rxdelay as u8);
620+
w.set_clkdiv(divisor as u8);
621+
});
622+
623+
// // Set PSRAM commands and formats
624+
qmi.mem(1).rfmt().write(|w| {
625+
w.set_prefix_width(embassy_rp::pac::qmi::vals::PrefixWidth::Q);
626+
w.set_addr_width(embassy_rp::pac::qmi::vals::AddrWidth::Q);
627+
w.set_suffix_width(embassy_rp::pac::qmi::vals::SuffixWidth::Q);
628+
w.set_dummy_width(embassy_rp::pac::qmi::vals::DummyWidth::Q);
629+
w.set_data_width(embassy_rp::pac::qmi::vals::DataWidth::Q);
630+
w.set_prefix_len(embassy_rp::pac::qmi::vals::PrefixLen::_8);
631+
w.set_dummy_len(embassy_rp::pac::qmi::vals::DummyLen::_24);
632+
});
633+
634+
qmi.mem(1).rcmd().write(|w| w.0 = 0xEB);
635+
636+
qmi.mem(1).wfmt().write(|w| {
637+
w.set_prefix_width(embassy_rp::pac::qmi::vals::PrefixWidth::Q);
638+
w.set_addr_width(embassy_rp::pac::qmi::vals::AddrWidth::Q);
639+
w.set_suffix_width(embassy_rp::pac::qmi::vals::SuffixWidth::Q);
640+
w.set_dummy_width(embassy_rp::pac::qmi::vals::DummyWidth::Q);
641+
w.set_data_width(embassy_rp::pac::qmi::vals::DataWidth::Q);
642+
w.set_prefix_len(embassy_rp::pac::qmi::vals::PrefixLen::_8);
643+
});
644+
645+
qmi.mem(1).wcmd().write(|w| w.0 = 0x38);
646+
647+
// Disable direct mode
648+
qmi.direct_csr().write(|w| w.0 = 0);
649+
650+
// Enable writes to PSRAM
651+
xip.ctrl().modify(|w| w.set_writable_m1(true));
652+
psram_size
653+
}

0 commit comments

Comments
 (0)