diff --git a/docs/library/rp2.PIO.rst b/docs/library/rp2.PIO.rst index e0675af1e9a2c..f922456c8c483 100644 --- a/docs/library/rp2.PIO.rst +++ b/docs/library/rp2.PIO.rst @@ -27,6 +27,17 @@ Constructors Methods ------- +.. method:: PIO.gpio_base([base]) + + Query and optionally set the current GPIO base for this PIO instance. + + If an argument is given then it must be a pin (or integer corresponding to a pin + number), restricted to either GPIO0 or GPIO16. The GPIO base will then be set to + that pin. Setting the GPIO base must be done before any programs are added or state + machines created. + + Returns the current GPIO base pin. + .. method:: PIO.add_program(program) Add the *program* to the instruction memory of this PIO instance. diff --git a/lib/pico-sdk b/lib/pico-sdk index 6a7db34ff6334..efe2103f9b284 160000 --- a/lib/pico-sdk +++ b/lib/pico-sdk @@ -1 +1 @@ -Subproject commit 6a7db34ff63345a7badec79ebea3aaef1712f374 +Subproject commit efe2103f9b28458a1615ff096054479743ade236 diff --git a/lib/tinyusb b/lib/tinyusb index d10b65ada4be7..4232642899362 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit d10b65ada4be7d5754b3128e80a9b4db72bdb23f +Subproject commit 4232642899362fa5e9cf0dc59bad6f1f6d32c563 diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 904925ae3f5e5..9dc5dbc948ea7 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -93,6 +93,14 @@ include(${MICROPY_DIR}/py/usermod.cmake) add_executable(${MICROPY_TARGET}) +# Provide a C-level definitions of PICO_ARM. +# (The pico-sdk already defines PICO_RISCV when it's enabled.) +if(PICO_ARM) + target_compile_definitions(pico_platform_headers INTERFACE + PICO_ARM=1 + ) +endif() + set(MICROPY_QSTRDEFS_PORT ${MICROPY_PORT_DIR}/qstrdefsport.h ) @@ -108,7 +116,6 @@ set(MICROPY_SOURCE_LIB ${MICROPY_DIR}/shared/netutils/netutils.c ${MICROPY_DIR}/shared/netutils/trace.c ${MICROPY_DIR}/shared/readline/readline.c - ${MICROPY_DIR}/shared/runtime/gchelper_thumb1.s ${MICROPY_DIR}/shared/runtime/gchelper_native.c ${MICROPY_DIR}/shared/runtime/interrupt_char.c ${MICROPY_DIR}/shared/runtime/mpirq.c @@ -123,6 +130,16 @@ set(MICROPY_SOURCE_LIB ${MICROPY_DIR}/shared/tinyusb/mp_usbd_runtime.c ) +if(PICO_ARM) + list(APPEND MICROPY_SOURCE_LIB + ${MICROPY_DIR}/shared/runtime/gchelper_thumb1.s + ) +elseif(PICO_RISCV) + list(APPEND MICROPY_SOURCE_LIB + ${MICROPY_DIR}/shared/runtime/gchelper_rv32i.s + ) +endif() + set(MICROPY_SOURCE_DRIVERS ${MICROPY_DIR}/drivers/bus/softspi.c ${MICROPY_DIR}/drivers/dht/dht.c @@ -130,6 +147,7 @@ set(MICROPY_SOURCE_DRIVERS set(MICROPY_SOURCE_PORT clocks_extra.c + datetime_patch.c fatfs_port.c help.c machine_bitstream.c @@ -155,6 +173,12 @@ set(MICROPY_SOURCE_PORT ${CMAKE_BINARY_DIR}/pins_${MICROPY_BOARD}.c ) +if(PICO_RP2350) + list(APPEND MICROPY_SOURCE_PORT + rp2_psram.c + ) +endif() + set(MICROPY_SOURCE_QSTR ${MICROPY_SOURCE_PY} ${MICROPY_DIR}/shared/readline/readline.c @@ -178,9 +202,9 @@ set(MICROPY_SOURCE_QSTR ) set(PICO_SDK_COMPONENTS - cmsis_core hardware_adc hardware_base + hardware_boot_lock hardware_clocks hardware_dma hardware_flash @@ -191,19 +215,26 @@ set(PICO_SDK_COMPONENTS hardware_pll hardware_pwm hardware_regs - hardware_rtc + hardware_resets hardware_spi hardware_structs hardware_sync + hardware_sync_spin_lock hardware_timer hardware_uart hardware_watchdog hardware_xosc + pico_aon_timer pico_base_headers pico_binary_info pico_bootrom pico_multicore pico_platform + pico_platform_compiler + pico_platform_sections + pico_platform_panic + pico_runtime + pico_runtime_init pico_stdio pico_stdlib pico_sync @@ -214,6 +245,17 @@ set(PICO_SDK_COMPONENTS tinyusb_device ) +if(PICO_ARM) + list(APPEND PICO_SDK_COMPONENTS + cmsis_core + ) +elseif(PICO_RISCV) + list(APPEND PICO_SDK_COMPONENTS + hardware_hazard3 + hardware_riscv + ) +endif() + # Use our custom pico_float_micropython float implementation. This is needed for two reasons: # - to fix inf handling in pico-sdk's __wrap___aeabi_fadd(); # - so we can use our own libm functions, to fix inaccuracies in the pico-sdk versions. @@ -224,14 +266,24 @@ pico_add_library(pico_float_micropython) # pico_float_micropython: add pico-sdk float and our libm source files. target_sources(pico_float_micropython INTERFACE - ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_aeabi.S - ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_init_rom.c - ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_v1_rom_shim.S ${MICROPY_SOURCE_LIB_LIBM} ${MICROPY_SOURCE_LIB_LIBM_SQRT_SW} ${MICROPY_PORT_DIR}/libm_extra.c ) +if(PICO_RP2040) + target_sources(pico_float_micropython INTERFACE + ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_v1_rom_shim_rp2040.S + ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_aeabi_rp2040.S + ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_init_rom_rp2040.c + ) +elseif(PICO_RP2350 AND PICO_ARM) + target_sources(pico_float_micropython INTERFACE + ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_aeabi_dcp.S + ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_conv_m33.S + ) +endif() + # pico_float_micropython: wrap low-level floating-point ops, to call the pico-sdk versions. pico_wrap_function(pico_float_micropython __aeabi_fdiv) pico_wrap_function(pico_float_micropython __aeabi_fmul) @@ -253,7 +305,9 @@ pico_wrap_function(pico_float_micropython __aeabi_ul2f) pico_wrap_function(pico_float_micropython __aeabi_f2iz) pico_wrap_function(pico_float_micropython __aeabi_f2lz) pico_wrap_function(pico_float_micropython __aeabi_f2uiz) -pico_wrap_function(pico_float_micropython __aeabi_f2ulz) +if (PICO_RP2040) + pico_wrap_function(pico_float_micropython __aeabi_f2ulz) +endif() pico_wrap_function(pico_float_micropython __aeabi_f2d) if (MICROPY_PY_LWIP) @@ -466,9 +520,15 @@ target_compile_options(${MICROPY_TARGET} PRIVATE target_link_options(${MICROPY_TARGET} PRIVATE -Wl,--defsym=__micropy_c_heap_size__=${MICROPY_C_HEAP_SIZE} -Wl,--wrap=dcd_event_handler - -Wl,--wrap=clocks_init + -Wl,--wrap=runtime_init_clocks ) +if(PICO_RP2350) + target_link_options(${MICROPY_TARGET} PRIVATE + -Wl,--defsym=__micropy_extra_stack__=4096 + ) +endif() + # Apply optimisations to performance-critical source code. set_source_files_properties( ${MICROPY_PY_DIR}/map.c @@ -478,6 +538,12 @@ set_source_files_properties( COMPILE_OPTIONS "-O2" ) +set_source_files_properties( + rp2_pio.c + PROPERTIES + COMPILE_OPTIONS "-Wno-error=discarded-qualifiers" +) + set_source_files_properties( ${PICO_SDK_PATH}/src/rp2_common/pico_double/double_math.c ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_math.c @@ -505,9 +571,14 @@ target_compile_definitions(${MICROPY_TARGET} PRIVATE PICO_NO_PROGRAM_VERSION_STRING=1 # do it ourselves in main.c MICROPY_BUILD_TYPE="${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION} ${CMAKE_BUILD_TYPE}" PICO_NO_BI_STDIO_UART=1 # we call it UART REPL - PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1 ) +if (PICO_RP2040) + target_compile_definitions(${MICROPY_TARGET} PRIVATE + PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1 + ) +endif() + target_link_libraries(${MICROPY_TARGET} ${PICO_SDK_COMPONENTS} ) @@ -523,12 +594,16 @@ endif() # a linker script modification) until we explicitly add macro calls around the function # defs to move them into RAM. if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM) - pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp.ld) + if(PICO_RP2040) + pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld) + elseif(PICO_RP2350) + pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld) + endif() endif() pico_add_extra_outputs(${MICROPY_TARGET}) -pico_find_compiler(PICO_COMPILER_SIZE ${PICO_GCC_TRIPLE}-size) +pico_find_compiler_with_triples(PICO_COMPILER_SIZE "${PICO_GCC_TRIPLE}" size) add_custom_command(TARGET ${MICROPY_TARGET} POST_BUILD @@ -542,6 +617,9 @@ foreach(comp ${PICO_SDK_COMPONENTS}) micropy_gather_target_properties(${comp}_headers) endforeach() +set(MICROPY_CPP_FLAGS_EXTRA ${PICO_COMMON_LANG_FLAGS}) +separate_arguments(MICROPY_CPP_FLAGS_EXTRA) + # Include the main MicroPython cmake rules. include(${MICROPY_DIR}/py/mkrules.cmake) @@ -551,6 +629,14 @@ set(GEN_PINS_MKPINS "${MICROPY_PORT_DIR}/boards/make-pins.py") set(GEN_PINS_SRC "${CMAKE_BINARY_DIR}/pins_${MICROPY_BOARD}.c") set(GEN_PINS_HDR "${MICROPY_GENHDR_DIR}/pins.h") +if(NOT PICO_NUM_GPIOS) + set(PICO_NUM_GPIOS 30) +endif() + +if(NOT PICO_NUM_EXT_GPIOS) + set(PICO_NUM_EXT_GPIOS 10) +endif() + if(EXISTS "${MICROPY_BOARD_DIR}/pins.csv") set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_DIR}/pins.csv") set(GEN_PINS_CSV_ARG --board-csv "${GEN_PINS_BOARD_CSV}") @@ -563,7 +649,7 @@ target_sources(${MICROPY_TARGET} PRIVATE # Generate pins add_custom_command( OUTPUT ${GEN_PINS_HDR} ${GEN_PINS_SRC} - COMMAND ${Python3_EXECUTABLE} ${GEN_PINS_MKPINS} ${GEN_PINS_CSV_ARG} --af-csv ${GEN_PINS_AF_CSV} --prefix ${GEN_PINS_PREFIX} --output-source ${GEN_PINS_SRC} --output-header ${GEN_PINS_HDR} + COMMAND ${Python3_EXECUTABLE} ${GEN_PINS_MKPINS} ${GEN_PINS_CSV_ARG} --af-csv ${GEN_PINS_AF_CSV} --prefix ${GEN_PINS_PREFIX} --output-source ${GEN_PINS_SRC} --num-gpios ${PICO_NUM_GPIOS} --num-ext-gpios ${PICO_NUM_EXT_GPIOS} --output-header ${GEN_PINS_HDR} DEPENDS ${GEN_PINS_AF_CSV} ${GEN_PINS_BOARD_CSV} diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile index b6e8852169217..f950bc7b90898 100644 --- a/ports/rp2/Makefile +++ b/ports/rp2/Makefile @@ -30,7 +30,7 @@ endif $(VERBOSE)MAKESILENT = -s -CMAKE_ARGS = -DMICROPY_BOARD=$(BOARD) -DMICROPY_BOARD_DIR="$(abspath $(BOARD_DIR))" +CMAKE_ARGS += -DMICROPY_BOARD=$(BOARD) -DMICROPY_BOARD_DIR="$(abspath $(BOARD_DIR))" ifdef USER_C_MODULES CMAKE_ARGS += -DUSER_C_MODULES=${USER_C_MODULES} diff --git a/ports/rp2/boards/RPI_PICO/mpconfigboard.cmake b/ports/rp2/boards/RPI_PICO/mpconfigboard.cmake index 13269e81e52e5..386bd33285890 100644 --- a/ports/rp2/boards/RPI_PICO/mpconfigboard.cmake +++ b/ports/rp2/boards/RPI_PICO/mpconfigboard.cmake @@ -1,2 +1,3 @@ # cmake file for Raspberry Pi Pico set(PICO_BOARD "pico") +set(PICO_PLATFORM "rp2040") diff --git a/ports/rp2/boards/RPI_PICO2/board.json b/ports/rp2/boards/RPI_PICO2/board.json new file mode 100644 index 0000000000000..8f3e4bde71a2e --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2/board.json @@ -0,0 +1,22 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Dual-core", + "External Flash", + "USB" + ], + "images": [ + "rp2-pico2.jpg" + ], + "mcu": "rp2350", + "product": "Pico 2", + "thumbnail": "", + "url": "https://www.raspberrypi.com/products/raspberry-pi-pico-2/", + "variants": { + "RISCV": "RISC-V CPU mode" + }, + "vendor": "Raspberry Pi" +} diff --git a/ports/rp2/boards/RPI_PICO2/mpconfigboard.cmake b/ports/rp2/boards/RPI_PICO2/mpconfigboard.cmake new file mode 100644 index 0000000000000..48b6545aa3428 --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2/mpconfigboard.cmake @@ -0,0 +1,5 @@ +# cmake file for Raspberry Pi Pico2 +set(PICO_BOARD "pico2") + +# To change the gpio count for QFN-80 +# set(PICO_NUM_GPIOS 48) diff --git a/ports/rp2/boards/RPI_PICO2/mpconfigboard.h b/ports/rp2/boards/RPI_PICO2/mpconfigboard.h new file mode 100644 index 0000000000000..4b5eac6eb3ae3 --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2/mpconfigboard.h @@ -0,0 +1,3 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico2" +#define MICROPY_HW_FLASH_STORAGE_BYTES (PICO_FLASH_SIZE_BYTES - 1024 * 1024) diff --git a/ports/rp2/boards/RPI_PICO2/mpconfigvariant.cmake b/ports/rp2/boards/RPI_PICO2/mpconfigvariant.cmake new file mode 100644 index 0000000000000..6fe039ba51bba --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2/mpconfigvariant.cmake @@ -0,0 +1 @@ +set(PICO_PLATFORM "rp2350") diff --git a/ports/rp2/boards/RPI_PICO2/mpconfigvariant_RISCV.cmake b/ports/rp2/boards/RPI_PICO2/mpconfigvariant_RISCV.cmake new file mode 100644 index 0000000000000..65a97fc3350d1 --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2/mpconfigvariant_RISCV.cmake @@ -0,0 +1 @@ +set(PICO_PLATFORM "rp2350-riscv") diff --git a/ports/rp2/boards/RPI_PICO2/pins.csv b/ports/rp2/boards/RPI_PICO2/pins.csv new file mode 100644 index 0000000000000..16e334026424f --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2/pins.csv @@ -0,0 +1,28 @@ +GP0,GPIO0 +GP1,GPIO1 +GP2,GPIO2 +GP3,GPIO3 +GP4,GPIO4 +GP5,GPIO5 +GP6,GPIO6 +GP7,GPIO7 +GP8,GPIO8 +GP9,GPIO9 +GP10,GPIO10 +GP11,GPIO11 +GP12,GPIO12 +GP13,GPIO13 +GP14,GPIO14 +GP15,GPIO15 +GP16,GPIO16 +GP17,GPIO17 +GP18,GPIO18 +GP19,GPIO19 +GP20,GPIO20 +GP21,GPIO21 +GP22,GPIO22 +GP25,GPIO25 +GP26,GPIO26 +GP27,GPIO27 +GP28,GPIO28 +LED,GPIO25 diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json new file mode 100755 index 0000000000000..15630b39e99b4 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json @@ -0,0 +1,21 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Dual-core", + "External Flash", + "JST-SH", + "RGB LED", + "USB-C" + ], + "images": [ + "17745-SparkFun_Thing_Plus_-_RP2040-01a.jpg" + ], + "mcu": "rp2350", + "product": "Pro Micro RP2350", + "thumbnail": "", + "url": "https://www.sparkfun.com/products/24870", + "vendor": "Sparkfun" +} \ No newline at end of file diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.cmake b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.cmake new file mode 100755 index 0000000000000..b1682bdd01fa0 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.cmake @@ -0,0 +1,5 @@ +# cmake file for SparkFun Pro Micro RP2350 +set(PICO_BOARD "sparkfun_promicro_rp2350") + +# To change the gpio count for QFN-80 +# set(PICO_NUM_GPIOS 48) \ No newline at end of file diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.h new file mode 100755 index 0000000000000..85192612cf501 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.h @@ -0,0 +1,26 @@ +// https://www.sparkfun.com/products/17717 + +#define MICROPY_HW_BOARD_NAME "SparkFun Pro Micro RP2350" +#define MICROPY_HW_FLASH_STORAGE_BYTES (PICO_FLASH_SIZE_BYTES - 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x1B4F) +#define MICROPY_HW_USB_PID (0x0039) + +#define MICROPY_HW_UART1_TX (8) +#define MICROPY_HW_UART1_RX (9) +#define MICROPY_HW_UART1_CTS (10) +#define MICROPY_HW_UART1_RTS (11) + + +// PSRAM things + +// Enable PSRAM +#define MICROPY_HW_ENABLE_PSRAM (1) +// CS Pin for the boards PSRAM +#define MICROPY_HW_PSRAM_CS_PIN (19) +// PSRAM max frequency is based on VDD (see datasheet) - SparkFun Pro Micro RP2350 uses 3.3V => 109MHz +#define MICROPY_HW_PSRAM_MAX_SCK_HZ (109*1000*1000) + +// Enable the heap split - heap is split between sram and psram memory +#define MICROPY_GC_SPLIT_HEAP (1) +// NeoPixel data GPIO25, power not toggleable diff --git a/ports/rp2/boards/make-pins.py b/ports/rp2/boards/make-pins.py index cbc3424705def..06d6847540e0a 100755 --- a/ports/rp2/boards/make-pins.py +++ b/ports/rp2/boards/make-pins.py @@ -9,9 +9,9 @@ import boardgen # This is NUM_BANK0_GPIOS. Pin indices are 0 to 29 (inclusive). -NUM_GPIOS = 30 +NUM_GPIOS = None # Up to 10 additional extended pins (e.g. via the wifi chip). -NUM_EXT_GPIOS = 10 +NUM_EXT_GPIOS = None class Rp2Pin(boardgen.Pin): @@ -108,12 +108,6 @@ def __init__(self): enable_af=True, ) - # Pre-define the pins (i.e. don't require them to be listed in pins.csv). - for i in range(NUM_GPIOS): - self.add_cpu_pin("GPIO{}".format(i)) - for i in range(NUM_EXT_GPIOS): - self.add_cpu_pin("EXT_GPIO{}".format(i)) - # Provided by pico-sdk. def cpu_table_size(self): return "NUM_BANK0_GPIOS" @@ -128,6 +122,31 @@ def print_source(self, out_source): super().print_source(out_source) self.print_cpu_locals_dict(out_source) + def extra_args(self, parser): + parser.add_argument("--num-gpios", type=int) + parser.add_argument("--num-ext-gpios", type=int) + + def load_inputs(self, out_source): + global NUM_GPIOS + global NUM_EXT_GPIOS + + # Needed by validate_cpu_pin_name + NUM_GPIOS = self.args.num_gpios + NUM_EXT_GPIOS = self.args.num_ext_gpios + + if NUM_GPIOS is None: + raise boardgen.PinGeneratorError("Please pass num-gpios") + + if NUM_EXT_GPIOS is None: + NUM_EXT_GPIOS = 0 + # Pre-define the pins (i.e. don't require them to be listed in pins.csv). + for i in range(NUM_GPIOS): + self.add_cpu_pin("GPIO{}".format(i)) + for i in range(NUM_EXT_GPIOS): + self.add_cpu_pin("EXT_GPIO{}".format(i)) + + super().load_inputs(out_source) + if __name__ == "__main__": Rp2PinGenerator().main() diff --git a/ports/rp2/clocks_extra.c b/ports/rp2/clocks_extra.c index e2c97962cb43d..73def24b80455 100644 --- a/ports/rp2/clocks_extra.c +++ b/ports/rp2/clocks_extra.c @@ -13,22 +13,36 @@ #include "hardware/xosc.h" #include "hardware/irq.h" #include "hardware/gpio.h" +#include "hardware/ticks.h" +#if PICO_RP2040 +// The RTC clock frequency is 48MHz divided by power of 2 (to ensure an integer +// division ratio will be used in the clocks block). A divisor of 1024 generates +// an RTC clock tick of 46875Hz. This frequency is relatively close to the +// customary 32 or 32.768kHz 'slow clock' crystals and provides good timing resolution. #define RTC_CLOCK_FREQ_HZ (USB_CLK_KHZ * KHZ / 1024) +#endif + +static void start_all_ticks(void) { + uint32_t cycles = clock_get_hz(clk_ref) / MHZ; + // Note RP2040 has a single tick generator in the watchdog which serves + // watchdog, system timer and M0+ SysTick; The tick generator is clocked from clk_ref + // but is now adapted by the hardware_ticks library for compatibility with RP2350 + // npte: hardware_ticks library now provides an adapter for RP2040 + + for (int i = 0; i < (int)TICK_COUNT; ++i) { + tick_start((tick_gen_num_t)i, cycles); + } +} // Wrap the SDK's clocks_init() function to save code size -void __wrap_clocks_init(void) { - clocks_init_optional_usb(true); +void __wrap_runtime_init_clocks(void) { + runtime_init_clocks_optional_usb(true); } -// Copy of clocks_init() from pico-sdk, with USB +// Copy of runtime_init_clocks() from pico-sdk, with USB // PLL and clock init made optional (for light sleep wakeup). -void clocks_init_optional_usb(bool init_usb) { - // Start tick in watchdog, the argument is in 'cycles per microsecond' i.e. MHz - watchdog_start_tick(XOSC_KHZ / KHZ); - - // Modification: removed FPGA check here - +void runtime_init_clocks_optional_usb(bool init_usb) { // Disable resus that may be enabled from previous software clocks_hw->resus.ctrl = 0; @@ -46,14 +60,26 @@ void clocks_init_optional_usb(bool init_usb) { } /// \tag::pll_init[] - pll_init(pll_sys, PLL_COMMON_REFDIV, PLL_SYS_VCO_FREQ_KHZ * KHZ, PLL_SYS_POSTDIV1, PLL_SYS_POSTDIV2); + pll_init(pll_sys, PLL_COMMON_REFDIV, PLL_SYS_VCO_FREQ_HZ, PLL_SYS_POSTDIV1, PLL_SYS_POSTDIV2); if (init_usb) { - pll_init(pll_usb, PLL_COMMON_REFDIV, PLL_USB_VCO_FREQ_KHZ * KHZ, PLL_USB_POSTDIV1, PLL_USB_POSTDIV2); + pll_init(pll_usb, PLL_COMMON_REFDIV, PLL_USB_VCO_FREQ_HZ, PLL_USB_POSTDIV1, PLL_USB_POSTDIV2); } /// \end::pll_init[] // Configure clocks - // CLK_REF = XOSC (usually) 12MHz / 1 = 12MHz + + // todo amy, what is this N1,2,4 meant to mean? + // RP2040 CLK_REF = XOSC (usually) 12MHz / 1 = 12MHz + // RP2350 CLK_REF = XOSC (XOSC_MHZ) / N (1,2,4) = 12MHz + + // clk_ref aux select is 0 because: + // + // - RP2040: no aux mux on clk_ref, so this field is don't-care. + // + // - RP2350: there is an aux mux, but we are selecting one of the + // non-aux inputs to the glitchless mux, so the aux select doesn't + // matter. The value of 0 here happens to be the sys PLL. + clock_configure(clk_ref, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, 0, // No aux mux @@ -85,18 +111,32 @@ void clocks_init_optional_usb(bool init_usb) { USB_CLK_KHZ * KHZ, USB_CLK_KHZ * KHZ); + #if HAS_RP2040_RTC // CLK RTC = PLL USB 48MHz / 1024 = 46875Hz clock_configure(clk_rtc, 0, // No GLMUX CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, USB_CLK_KHZ * KHZ, RTC_CLOCK_FREQ_HZ); + #endif - // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable - // Normally choose clk_sys or clk_usb + // CLK PERI = clk_sys. Used as reference clock for UART and SPI serial. clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, SYS_CLK_KHZ * KHZ, SYS_CLK_KHZ * KHZ); + + #if PICO_RP2350 + // CLK_HSTX = clk_sys. Transmit bit clock for the HSTX peripheral. + clock_configure(clk_hstx, + 0, + CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLK_SYS, + SYS_CLK_KHZ * KHZ, + SYS_CLK_KHZ * KHZ); + #endif + + // Finally, all clocks are configured so start the ticks + // The ticks use clk_ref so now that is configured we can start them + start_all_ticks(); } diff --git a/ports/rp2/clocks_extra.h b/ports/rp2/clocks_extra.h index 40f77f53bd850..7d630e5088f5c 100644 --- a/ports/rp2/clocks_extra.h +++ b/ports/rp2/clocks_extra.h @@ -28,6 +28,6 @@ #include "hardware/clocks.h" -void clocks_init_optional_usb(bool init_usb); +void runtime_init_clocks_optional_usb(bool init_usb); #endif // MICROPY_INCLUDED_RP2_CLOCKS_EXTRA_H diff --git a/ports/rp2/datetime_patch.c b/ports/rp2/datetime_patch.c new file mode 100644 index 0000000000000..810af4cf1650d --- /dev/null +++ b/ports/rp2/datetime_patch.c @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Angus Gratton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "py/mpconfig.h" +#include "shared/timeutils/timeutils.h" + +// This is a workaround for the issue that pico-sdk datetime.c will otherwise +// pull in a lot of libc code for time zone support. +// +// Upstream issue is https://github.com/raspberrypi/pico-sdk/issues/1810 + +struct tm *localtime_r(const time_t *__restrict time, struct tm *__restrict local_time) { + return gmtime_r(time, local_time); +} + +time_t mktime(struct tm *__restrict tm) { + return timeutils_mktime(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); +} diff --git a/ports/rp2/fatfs_port.c b/ports/rp2/fatfs_port.c index 9706b6fe7dc70..1ce505dd69e3f 100644 --- a/ports/rp2/fatfs_port.c +++ b/ports/rp2/fatfs_port.c @@ -25,10 +25,13 @@ */ #include "lib/oofatfs/ff.h" -#include "hardware/rtc.h" +#include "pico/aon_timer.h" +#include "shared/timeutils/timeutils.h" MP_WEAK DWORD get_fattime(void) { - datetime_t t; - rtc_get_datetime(&t); - return ((t.year - 1980) << 25) | ((t.month) << 21) | ((t.day) << 16) | ((t.hour) << 11) | ((t.min) << 5) | (t.sec / 2); + struct timespec ts; + timeutils_struct_time_t tm; + aon_timer_get_time(&ts); + timeutils_seconds_since_epoch_to_struct_time(ts.tv_sec, &tm); + return ((tm.tm_year - 1980) << 25) | ((tm.tm_mon) << 21) | ((tm.tm_mday) << 16) | ((tm.tm_hour) << 11) | ((tm.tm_min) << 5) | (tm.tm_sec / 2); } diff --git a/ports/rp2/machine_adc.c b/ports/rp2/machine_adc.c index 3594f4b124022..40c216d59bc8d 100644 --- a/ports/rp2/machine_adc.c +++ b/ports/rp2/machine_adc.c @@ -31,9 +31,8 @@ #include "hardware/adc.h" #include "machine_pin.h" -#define ADC_IS_VALID_GPIO(gpio) ((gpio) >= 26 && (gpio) <= 29) -#define ADC_CHANNEL_FROM_GPIO(gpio) ((gpio) - 26) -#define ADC_CHANNEL_TEMPSENSOR (4) +#define ADC_IS_VALID_GPIO(gpio) ((gpio) >= ADC_BASE_PIN && (gpio) < (ADC_BASE_PIN + NUM_ADC_CHANNELS)) +#define ADC_CHANNEL_FROM_GPIO(gpio) ((gpio) - ADC_BASE_PIN) static uint16_t adc_config_and_read_u16(uint32_t channel) { adc_select_input(channel); @@ -47,7 +46,7 @@ static uint16_t adc_config_and_read_u16(uint32_t channel) { // MicroPython bindings for machine.ADC #define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS \ - { MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMPSENSOR) }, \ + { MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_TEMPERATURE_CHANNEL_NUM) }, \ typedef struct _machine_adc_obj_t { mp_obj_base_t base; @@ -75,7 +74,7 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args if (mp_obj_is_int(source)) { channel = mp_obj_get_int(source); - if (!(channel >= 0 && channel <= ADC_CHANNEL_TEMPSENSOR)) { + if (!(channel >= 0 && channel < NUM_ADC_CHANNELS)) { // Not a valid ADC channel, fallback to searching for a pin. channel = -1; } @@ -116,7 +115,7 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args adc_gpio_init(pin->id); channel = ADC_CHANNEL_FROM_GPIO(pin->id); } - } else if (channel == ADC_CHANNEL_TEMPSENSOR) { + } else if (channel == ADC_TEMPERATURE_CHANNEL_NUM) { // Enable temperature sensor. adc_set_temp_sensor_enabled(1); } diff --git a/ports/rp2/machine_bitstream.c b/ports/rp2/machine_bitstream.c index b65ec02147b40..ec21beed35c56 100644 --- a/ports/rp2/machine_bitstream.c +++ b/ports/rp2/machine_bitstream.c @@ -34,6 +34,25 @@ #define MP_HAL_BITSTREAM_NS_OVERHEAD (9) +#if PICO_RISCV + +__attribute__((naked)) void mcycle_init(void) { + __asm volatile ( + "li a0, 4\n" + "csrw mcountinhibit, a0\n" + "ret\n" + ); +} + +__attribute__((naked)) uint32_t mcycle_get(void) { + __asm volatile ( + "csrr a0, mcycle\n" + "ret\n" + ); +} + +#endif + void __time_critical_func(machine_bitstream_high_low)(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) { uint32_t fcpu_mhz = mp_hal_get_cpu_freq() / 1000000; // Convert ns to clock ticks [high_time_0, period_0, high_time_1, period_1]. @@ -48,11 +67,17 @@ void __time_critical_func(machine_bitstream_high_low)(mp_hal_pin_obj_t pin, uint } } mp_hal_pin_output(pin); - // Enable the systick counter, source CPU clock. - systick_hw->csr = 5; uint32_t irq_state = mp_hal_quiet_timing_enter(); + #if PICO_ARM + + // Set systick reset value + systick_hw->rvr = 0x00FFFFFF; + + // Enable the systick counter, source CPU clock. + systick_hw->csr = 5; + for (size_t i = 0; i < len; ++i) { uint8_t b = buf[i]; for (size_t j = 0; j < 8; ++j) { @@ -68,6 +93,27 @@ void __time_critical_func(machine_bitstream_high_low)(mp_hal_pin_obj_t pin, uint } } + #elif PICO_RISCV + + mcycle_init(); + + for (size_t i = 0; i < len; ++i) { + uint8_t b = buf[i]; + for (size_t j = 0; j < 8; ++j) { + uint32_t *t = &timing_ns[b >> 6 & 2]; + uint32_t start_ticks = mcycle_get(); + mp_hal_pin_high(pin); + while ((mcycle_get() - start_ticks) < t[0]) { + } + b <<= 1; + mp_hal_pin_low(pin); + while ((mcycle_get() - start_ticks) < t[1]) { + } + } + } + + #endif + mp_hal_quiet_timing_exit(irq_state); } diff --git a/ports/rp2/machine_pin.c b/ports/rp2/machine_pin.c index de217785690c2..f11f4190f48c6 100644 --- a/ports/rp2/machine_pin.c +++ b/ports/rp2/machine_pin.c @@ -87,9 +87,10 @@ typedef struct _machine_pin_irq_obj_t { static const mp_irq_methods_t machine_pin_irq_methods; extern const machine_pin_obj_t machine_pin_obj_table[NUM_BANK0_GPIOS]; +static const int num_intr_regs = sizeof(iobank0_hw->intr) / sizeof(iobank0_hw->intr[0]); // Mask with "1" indicating that the corresponding pin is in simulated open-drain mode. -uint32_t machine_pin_open_drain_mask; +uint64_t machine_pin_open_drain_mask; #if MICROPY_HW_PIN_EXT_COUNT static inline bool is_ext_pin(__unused const machine_pin_obj_t *self) { @@ -100,7 +101,7 @@ static inline bool is_ext_pin(__unused const machine_pin_obj_t *self) { #endif static void gpio_irq(void) { - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < num_intr_regs; ++i) { uint32_t intr = iobank0_hw->intr[i]; if (intr) { for (int j = 0; j < 8; ++j) { @@ -298,7 +299,7 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin af: %d"), af); } gpio_set_function(self->id, af); - machine_pin_open_drain_mask &= ~(1 << self->id); + machine_pin_open_drain_mask &= ~(1ULL << self->id); } } @@ -380,7 +381,7 @@ static mp_obj_t machine_pin_low(mp_obj_t self_in) { } else if (GPIO_IS_OPEN_DRAIN(self->id)) { gpio_set_dir(self->id, GPIO_OUT); } else { - gpio_clr_mask(1u << self->id); + gpio_clr_mask64(1ULL << self->id); } return mp_const_none; } @@ -396,7 +397,7 @@ static mp_obj_t machine_pin_high(mp_obj_t self_in) { } else if (GPIO_IS_OPEN_DRAIN(self->id)) { gpio_set_dir(self->id, GPIO_IN); } else { - gpio_set_mask(1u << self->id); + gpio_set_mask64(1ULL << self->id); } return mp_const_none; } @@ -417,7 +418,7 @@ static mp_obj_t machine_pin_toggle(mp_obj_t self_in) { gpio_set_dir(self->id, GPIO_OUT); } } else { - gpio_xor_mask(1u << self->id); + gpio_xor_mask64(1ULL << self->id); } return mp_const_none; } diff --git a/ports/rp2/machine_pin.h b/ports/rp2/machine_pin.h index b3349188e8ba1..196132019e609 100644 --- a/ports/rp2/machine_pin.h +++ b/ports/rp2/machine_pin.h @@ -49,7 +49,7 @@ typedef struct _machine_pin_af_obj_t { typedef struct _machine_pin_obj_t { mp_obj_base_t base; qstr name; - uint8_t id : 5; + uint8_t id : 6; #if MICROPY_HW_PIN_EXT_COUNT uint8_t is_ext : 1; uint8_t is_output : 1; diff --git a/ports/rp2/machine_pwm.c b/ports/rp2/machine_pwm.c index 4c0bc2c103243..fd20be5ec2f6f 100644 --- a/ports/rp2/machine_pwm.c +++ b/ports/rp2/machine_pwm.c @@ -67,10 +67,20 @@ static machine_pwm_obj_t machine_pwm_obj[] = { {{&machine_pwm_type}, 6, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, {{&machine_pwm_type}, 7, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, {{&machine_pwm_type}, 7, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + #if NUM_PWM_SLICES == 12 + {{&machine_pwm_type}, 8, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 8, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 9, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 9, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 10, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 10, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 11, PWM_CHAN_A, 0, DUTY_NOT_SET, 0 }, + {{&machine_pwm_type}, 11, PWM_CHAN_B, 0, DUTY_NOT_SET, 0 }, + #endif }; static bool defer_start; -static bool slice_freq_set[8]; +static bool slice_freq_set[NUM_PWM_SLICES]; static void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq); static void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u16); @@ -155,7 +165,7 @@ static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args // Stop all active slices. void machine_pwm_deinit_all(void) { - for (int i = 0; i < 8; i++) { + for (int i = 0; i < NUM_PWM_SLICES; i++) { slice_freq_set[i] = false; pwm_set_enabled(machine_pwm_obj[i].slice, false); } diff --git a/ports/rp2/machine_rtc.c b/ports/rp2/machine_rtc.c index ce224be711b89..0d8fdef30f322 100644 --- a/ports/rp2/machine_rtc.c +++ b/ports/rp2/machine_rtc.c @@ -30,6 +30,8 @@ #include #include +#include "pico/aon_timer.h" + #include "py/nlr.h" #include "py/obj.h" #include "py/runtime.h" @@ -37,8 +39,6 @@ #include "py/mperrno.h" #include "extmod/modmachine.h" #include "shared/timeutils/timeutils.h" -#include "hardware/rtc.h" -#include "pico/util/datetime.h" typedef struct _machine_rtc_obj_t { mp_obj_base_t base; @@ -50,14 +50,12 @@ static const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}}; static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); - bool r = rtc_running(); + bool r = aon_timer_is_running(); if (!r) { - // This shouldn't happen as rtc_init() is already called in main so - // it's here just in case - rtc_init(); - datetime_t t = { .month = 1, .day = 1 }; - rtc_set_datetime(&t); + // This shouldn't happen. it's here just in case + struct timespec ts = { 0, 0 }; + aon_timer_start(&ts); mp_hal_time_ns_set_from_rtc(); } // return constant object @@ -66,47 +64,36 @@ static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s static mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { if (n_args == 1) { - bool ret; - datetime_t t; - - ret = rtc_get_datetime(&t); - if (!ret) { - mp_raise_OSError(MP_EIO); - } - + struct timespec ts; + timeutils_struct_time_t tm; + aon_timer_get_time(&ts); + timeutils_seconds_since_epoch_to_struct_time(ts.tv_sec, &tm); mp_obj_t tuple[8] = { - mp_obj_new_int(t.year), - mp_obj_new_int(t.month), - mp_obj_new_int(t.day), - mp_obj_new_int(t.dotw), - mp_obj_new_int(t.hour), - mp_obj_new_int(t.min), - mp_obj_new_int(t.sec), - mp_obj_new_int(0) + mp_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_wday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int(0), }; - return mp_obj_new_tuple(8, tuple); } else { mp_obj_t *items; - mp_obj_get_array_fixed_n(args[1], 8, &items); - - datetime_t t = { - .year = mp_obj_get_int(items[0]), - .month = mp_obj_get_int(items[1]), - .day = mp_obj_get_int(items[2]), - .hour = mp_obj_get_int(items[4]), - .min = mp_obj_get_int(items[5]), - .sec = mp_obj_get_int(items[6]), + timeutils_struct_time_t tm = { + .tm_year = mp_obj_get_int(items[0]), + .tm_mon = mp_obj_get_int(items[1]), + .tm_mday = mp_obj_get_int(items[2]), + .tm_hour = mp_obj_get_int(items[4]), + .tm_min = mp_obj_get_int(items[5]), + .tm_sec = mp_obj_get_int(items[6]), }; - // Deliberately ignore the weekday argument and compute the proper value - t.dotw = timeutils_calc_weekday(t.year, t.month, t.day); - - if (!rtc_set_datetime(&t)) { - mp_raise_OSError(MP_EINVAL); - } + struct timespec ts = { 0, 0 }; + ts.tv_sec = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + aon_timer_set_time(&ts); mp_hal_time_ns_set_from_rtc(); - } return mp_const_none; } diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index 689906003e7dc..57981363c97b2 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -69,8 +69,13 @@ #define MAX_BUFFER_SIZE (32766) #define IS_VALID_PERIPH(uart, pin) (((((pin) + 4) & 8) >> 3) == (uart)) +#if PICO_RP2350 +#define IS_VALID_TX(uart, pin) (((pin) & 1) == 0 && IS_VALID_PERIPH(uart, pin)) +#define IS_VALID_RX(uart, pin) (((pin) & 1) == 1 && IS_VALID_PERIPH(uart, pin)) +#else #define IS_VALID_TX(uart, pin) (((pin) & 3) == 0 && IS_VALID_PERIPH(uart, pin)) #define IS_VALID_RX(uart, pin) (((pin) & 3) == 1 && IS_VALID_PERIPH(uart, pin)) +#endif #define IS_VALID_CTS(uart, pin) (((pin) & 3) == 2 && IS_VALID_PERIPH(uart, pin)) #define IS_VALID_RTS(uart, pin) (((pin) & 3) == 3 && IS_VALID_PERIPH(uart, pin)) @@ -398,8 +403,8 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, __dsb(); // make sure UARTLCR_H register is written to uart_set_fifo_enabled(self->uart, true); __dsb(); // make sure UARTLCR_H register is written to - gpio_set_function(self->tx, GPIO_FUNC_UART); - gpio_set_function(self->rx, GPIO_FUNC_UART); + gpio_set_function(self->tx, UART_FUNCSEL_NUM(self->uart, self->tx)); + gpio_set_function(self->rx, UART_FUNCSEL_NUM(self->uart, self->rx)); if (self->invert & UART_INVERT_RX) { gpio_set_inover(self->rx, GPIO_OVERRIDE_INVERT); } diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 98417e8da6b0b..f2005c706575d 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -26,6 +26,8 @@ #include +#include "rp2_psram.h" +#include "rp2_flash.h" #include "py/compile.h" #include "py/cstack.h" #include "py/runtime.h" @@ -47,11 +49,9 @@ #include "genhdr/mpversion.h" #include "mp_usbd.h" -#include "RP2040.h" // cmsis, for PendSV_IRQn and SCB/SCB_SCR_SEVONPEND_Msk #include "pico/stdlib.h" #include "pico/binary_info.h" #include "pico/unique_id.h" -#include "hardware/rtc.h" #include "hardware/structs/rosc.h" #if MICROPY_PY_LWIP #include "lwip/init.h" @@ -60,6 +60,13 @@ #if MICROPY_PY_NETWORK_CYW43 #include "lib/cyw43-driver/src/cyw43.h" #endif +#if PICO_RP2040 +#include "RP2040.h" // cmsis, for PendSV_IRQn and SCB/SCB_SCR_SEVONPEND_Msk +#elif PICO_RP2350 && PICO_ARM +#include "RP2350.h" // cmsis, for PendSV_IRQn and SCB/SCB_SCR_SEVONPEND_Msk +#endif +#include "pico/aon_timer.h" +#include "shared/timeutils/timeutils.h" extern uint8_t __StackTop, __StackBottom; extern uint8_t __GcHeapStart, __GcHeapEnd; @@ -75,7 +82,9 @@ bi_decl(bi_program_feature_group_with_flags(BINARY_INFO_TAG_MICROPYTHON, int main(int argc, char **argv) { // This is a tickless port, interrupts should always trigger SEV. + #if PICO_ARM SCB->SCR |= SCB_SCR_SEVONPEND_Msk; + #endif pendsv_init(); soft_timer_init(); @@ -83,6 +92,9 @@ int main(int argc, char **argv) { // Set the MCU frequency and as a side effect the peripheral clock to 48 MHz. set_sys_clock_khz(125000, false); + // Set the flash divisor to an appropriate value + rp2_flash_set_timing(); + #if MICROPY_HW_ENABLE_UART_REPL bi_decl(bi_program_feature("UART REPL")) setup_default_uart(); @@ -103,22 +115,29 @@ int main(int argc, char **argv) { #endif // Start and initialise the RTC - datetime_t t = { - .year = 2021, - .month = 1, - .day = 1, - .dotw = 4, // 0 is Monday, so 4 is Friday - .hour = 0, - .min = 0, - .sec = 0, - }; - rtc_init(); - rtc_set_datetime(&t); + struct timespec ts = { 0, 0 }; + ts.tv_sec = timeutils_seconds_since_epoch(2021, 1, 1, 0, 0, 0); + aon_timer_start(&ts); mp_hal_time_ns_set_from_rtc(); // Initialise stack extents and GC heap. mp_cstack_init_with_top(&__StackTop, &__StackTop - &__StackBottom); + + #if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM && defined(MICROPY_HW_PSRAM_MAX_SCK_HZ) + size_t psram_size = rp2_psram_init(MICROPY_HW_PSRAM_CS_PIN, MICROPY_HW_PSRAM_MAX_SCK_HZ); + if (psram_size) { + #if MICROPY_GC_SPLIT_HEAP + gc_init(&__GcHeapStart, &__GcHeapEnd); + gc_add((void *)PSRAM_LOCATION, (void *)(PSRAM_LOCATION + psram_size)); + #else + gc_init((void *)PSRAM_LOCATION, (void *)(PSRAM_LOCATION + psram_size)); + #endif + } else { + gc_init(&__GcHeapStart, &__GcHeapEnd); + } + #else gc_init(&__GcHeapStart, &__GcHeapEnd); + #endif #if MICROPY_PY_LWIP // lwIP doesn't allow to reinitialise itself by subsequent calls to this function diff --git a/ports/rp2/mbedtls/mbedtls_port.c b/ports/rp2/mbedtls/mbedtls_port.c index 9b1e0d20e1700..521d815d2bedd 100644 --- a/ports/rp2/mbedtls/mbedtls_port.c +++ b/ports/rp2/mbedtls/mbedtls_port.c @@ -29,9 +29,9 @@ #include "mbedtls_config_port.h" -#include "hardware/rtc.h" #include "shared/timeutils/timeutils.h" #include "mbedtls/platform_time.h" +#include "pico/aon_timer.h" extern uint8_t rosc_random_u8(size_t cycles); @@ -44,9 +44,9 @@ int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t } time_t rp2_rtctime_seconds(time_t *timer) { - datetime_t t; - rtc_get_datetime(&t); - return timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.min, t.sec); + struct timespec ts; + aon_timer_get_time(&ts); + return ts.tv_sec; } mbedtls_ms_time_t mbedtls_ms_time(void) { diff --git a/ports/rp2/memmap_mp.ld b/ports/rp2/memmap_mp_rp2040.ld similarity index 100% rename from ports/rp2/memmap_mp.ld rename to ports/rp2/memmap_mp_rp2040.ld diff --git a/ports/rp2/memmap_mp_rp2350.ld b/ports/rp2/memmap_mp_rp2350.ld new file mode 100644 index 0000000000000..3d57117a8198e --- /dev/null +++ b/ports/rp2/memmap_mp_rp2350.ld @@ -0,0 +1,313 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 4096k + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k + SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k +} + +ENTRY(_entry_point) + +SECTIONS +{ + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + /* The bootrom will enter the image at the point indicated in your + IMAGE_DEF, which is usually the reset handler of your vector table. + + The debugger will use the ELF entry point, which is the _entry_point + symbol, and in our case is *different from the bootrom's entry point.* + This is used to go back through the bootrom on debugger launches only, + to perform the same initial flash setup that would be performed on a + cold boot. + */ + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + KEEP (*(.reset)) + /* TODO revisit this now memset/memcpy/float in ROM */ + /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from + * FLASH ... we will include any thing excluded here in .data below by default */ + *(.init) + *libgcc.a:cmse_nonsecure_call.o + /* Change for MicroPython... exclude gc.c, parse.c, vm.c from flash */ + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *gc.c.obj *vm.c.obj *parse.c.obj) .text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + *(.eh_frame*) + . = ALIGN(4); + } > FLASH + + /* Note the boot2 section is optional, and should be discarded if there is + no reference to it *inside* the binary, as it is not called by the + bootrom. (The bootrom performs a simple best-effort XIP setup and + leaves it to the binary to do anything more sophisticated.) However + there is still a size limit of 256 bytes, to ensure the boot2 can be + stored in boot RAM. + + Really this is a "XIP setup function" -- the name boot2 is historic and + refers to its dual-purpose on RP2040, where it also handled vectoring + from the bootrom into the user image. + */ + + .boot2 : { + __boot2_start__ = .; + *(.boot2) + __boot2_end__ = .; + } > FLASH + + ASSERT(__boot2_end__ - __boot2_start__ <= 256, + "ERROR: Pico second stage bootloader must be no more than 256 bytes in size") + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + *(.srodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + .ram_vector_table (NOLOAD): { + *(.ram_vector_table) + } > RAM + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + *(.sdata*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + *(.jcr) + . = ALIGN(4); + } > RAM AT> FLASH + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM AT> FLASH + PROVIDE(__data_end__ = .); + + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + PROVIDE(__global_pointer$ = . + 2K); + *(.sbss*) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + /* Change for MicroPython: don't include this, it increases reported firmware size. + /* . = ORIGIN(RAM) + LENGTH(RAM); */ + __HeapLimit = .; + } > RAM + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + .flash_end : { + KEEP(*(.embedded_end_block*)) + PROVIDE(__flash_binary_end = .); + } > FLASH =0xaa + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = __bss_end__ + __micropy_c_heap_size__; + + /* Define start and end of GC heap */ + __GcHeapStart = __StackLimit; /* after the C heap (sbrk limit) */ + __GcHeapEnd = ORIGIN(RAM) + LENGTH(RAM) - __micropy_extra_stack__; + + /* Define start and end of C stack */ + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackBottom = __GcHeapEnd; + PROVIDE(__stack = __StackTop); + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check GC heap is at least 64 KB */ + /* This is half the minimum RAM suggested for full-featured MicroPython. + * This value accounts for large static buffers included in user C or C++ + * modules, which might significantly reduce the available heap but also + * lower demand for memory at runtime. + */ + ASSERT((__GcHeapEnd - __GcHeapStart) > 64*1024, "GcHeap is too small") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary") + ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") + + /* todo assert on extra code */ +} + diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 372c17bcf9d6b..117ba6193ae25 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -31,6 +31,8 @@ #include "mp_usbd.h" #include "modmachine.h" #include "uart.h" +#include "rp2_psram.h" +#include "rp2_flash.h" #include "clocks_extra.h" #include "hardware/pll.h" #include "hardware/structs/rosc.h" @@ -41,6 +43,7 @@ #include "pico/bootrom.h" #include "pico/stdlib.h" #include "pico/unique_id.h" +#include "pico/runtime_init.h" #if MICROPY_PY_NETWORK_CYW43 #include "lib/cyw43-driver/src/cyw43.h" #endif @@ -93,6 +96,11 @@ static mp_obj_t mp_machine_get_freq(void) { static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { mp_int_t freq = mp_obj_get_int(args[0]); + + // If necessary, increase the flash divider before increasing the clock speed + const int old_freq = clock_get_hz(clk_sys); + rp2_flash_set_timing_for_freq(MAX(freq, old_freq)); + if (!set_sys_clock_khz(freq / 1000, false)) { mp_raise_ValueError(MP_ERROR_TEXT("cannot change frequency")); } @@ -110,10 +118,19 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { } } } + + // If clock speed was reduced, maybe we can reduce the flash divider + if (freq < old_freq) { + rp2_flash_set_timing_for_freq(freq); + } + #if MICROPY_HW_ENABLE_UART_REPL setup_default_uart(); mp_uart_init(); #endif + #if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM && defined(MICROPY_HW_PSRAM_MAX_SCK_HZ) + rp2_psram_set_timing(MICROPY_HW_PSRAM_MAX_SCK_HZ); + #endif } static void mp_machine_idle(void) { @@ -162,6 +179,9 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { } clock_stop(clk_adc); + #if PICO_RP2350 + clock_stop(clk_hstx); + #endif // CLK_REF = XOSC clock_configure(clk_ref, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, 0, xosc_hz, xosc_hz); @@ -170,7 +190,9 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF, 0, xosc_hz, xosc_hz); // CLK_RTC = XOSC / 256 + #if PICO_RP2040 clock_configure(clk_rtc, 0, CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC, xosc_hz, xosc_hz / 256); + #endif // CLK_PERI = CLK_SYS clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, xosc_hz, xosc_hz); @@ -190,45 +212,70 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { #endif xosc_dormant(); } else { - uint32_t sleep_en0 = clocks_hw->sleep_en0; - uint32_t sleep_en1 = clocks_hw->sleep_en1; bool timer3_enabled = irq_is_enabled(3); - clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS; + const uint32_t alarm_num = 3; + const uint32_t irq_num = TIMER_ALARM_IRQ_NUM(timer_hw, alarm_num); if (use_timer_alarm) { // Make sure ALARM3/IRQ3 is enabled on _this_ core - timer_hw->inte |= 1 << 3; if (!timer3_enabled) { - irq_set_enabled(3, true); + irq_set_enabled(irq_num, true); } + hw_set_bits(&timer_hw->inte, 1u << alarm_num); // Use timer alarm to wake. + clocks_hw->sleep_en0 = 0x0; + #if PICO_RP2040 clocks_hw->sleep_en1 = CLOCKS_SLEEP_EN1_CLK_SYS_TIMER_BITS; - timer_hw->alarm[3] = timer_hw->timerawl + delay_ms * 1000; + #elif PICO_RP2350 + clocks_hw->sleep_en1 = CLOCKS_SLEEP_EN1_CLK_REF_TICKS_BITS | CLOCKS_SLEEP_EN1_CLK_SYS_TIMER0_BITS; + #else + #error Unknown processor + #endif + timer_hw->intr = 1u << alarm_num; // clear any IRQ + timer_hw->alarm[alarm_num] = timer_hw->timerawl + delay_ms * 1000; } else { // TODO: Use RTC alarm to wake. - clocks_hw->sleep_en1 = 0; + clocks_hw->sleep_en0 = 0x0; + clocks_hw->sleep_en1 = 0x0; } if (!disable_usb) { clocks_hw->sleep_en0 |= CLOCKS_SLEEP_EN0_CLK_SYS_PLL_USB_BITS; + #if PICO_RP2040 clocks_hw->sleep_en1 |= CLOCKS_SLEEP_EN1_CLK_USB_USBCTRL_BITS; + #elif PICO_RP2350 + clocks_hw->sleep_en1 |= CLOCKS_SLEEP_EN1_CLK_SYS_USBCTRL_BITS; + #else + #error Unknown processor + #endif } + #if PICO_ARM + // Configure SLEEPDEEP bits on Cortex-M CPUs. + #if PICO_RP2040 scb_hw->scr |= M0PLUS_SCR_SLEEPDEEP_BITS; + #elif PICO_RP2350 + scb_hw->scr |= M33_SCR_SLEEPDEEP_BITS; + #else + #error Unknown processor + #endif + #endif + + // Go into low-power mode. __wfi(); - scb_hw->scr &= ~M0PLUS_SCR_SLEEPDEEP_BITS; + if (!timer3_enabled) { - irq_set_enabled(3, false); + irq_set_enabled(irq_num, false); } - clocks_hw->sleep_en0 = sleep_en0; - clocks_hw->sleep_en1 = sleep_en1; + clocks_hw->sleep_en0 |= ~(0u); + clocks_hw->sleep_en1 |= ~(0u); } // Enable ROSC. rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB; // Bring back all clocks. - clocks_init_optional_usb(disable_usb); + runtime_init_clocks_optional_usb(disable_usb); MICROPY_END_ATOMIC_SECTION(my_interrupts); } diff --git a/ports/rp2/modtime.c b/ports/rp2/modtime.c index 21e5cb459b283..7d6dd8fd97112 100644 --- a/ports/rp2/modtime.c +++ b/ports/rp2/modtime.c @@ -26,28 +26,30 @@ #include "py/obj.h" #include "shared/timeutils/timeutils.h" -#include "hardware/rtc.h" +#include "pico/aon_timer.h" // Return the localtime as an 8-tuple. static mp_obj_t mp_time_localtime_get(void) { - datetime_t t; - rtc_get_datetime(&t); + struct timespec ts; + aon_timer_get_time(&ts); + timeutils_struct_time_t tm; + timeutils_seconds_since_epoch_to_struct_time(ts.tv_sec, &tm); mp_obj_t tuple[8] = { - mp_obj_new_int(t.year), - mp_obj_new_int(t.month), - mp_obj_new_int(t.day), - mp_obj_new_int(t.hour), - mp_obj_new_int(t.min), - mp_obj_new_int(t.sec), - mp_obj_new_int(t.dotw), - mp_obj_new_int(timeutils_year_day(t.year, t.month, t.day)), + mp_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int(tm.tm_wday), + mp_obj_new_int(tm.tm_yday), }; return mp_obj_new_tuple(8, tuple); } // Return the number of seconds since the Epoch. static mp_obj_t mp_time_time_get(void) { - datetime_t t; - rtc_get_datetime(&t); - return mp_obj_new_int_from_ull(timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.min, t.sec)); + struct timespec ts; + aon_timer_get_time(&ts); + return mp_obj_new_int_from_ull(ts.tv_sec); } diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 49acafe239b9d..23be3240cc13a 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -35,7 +35,16 @@ #include "mpconfigboard.h" // Board and hardware specific configuration +#if PICO_RP2040 #define MICROPY_HW_MCU_NAME "RP2040" +#elif PICO_RP2350 && PICO_ARM +#define MICROPY_HW_MCU_NAME "RP2350" +#elif PICO_RP2350 && PICO_RISCV +#define MICROPY_HW_MCU_NAME "RP2350-RISCV" +#else +#error Unknown MCU +#endif + #ifndef MICROPY_HW_ENABLE_UART_REPL #define MICROPY_HW_ENABLE_UART_REPL (0) // useful if there is no USB #endif @@ -62,17 +71,34 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) #endif +#ifndef MICROPY_HW_ENABLE_PSRAM +#define MICROPY_HW_ENABLE_PSRAM (0) +#endif + // Memory allocation policies +#if MICROPY_HW_ENABLE_PSRAM +#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t +#else #define MICROPY_GC_STACK_ENTRY_TYPE uint16_t +#endif +#ifndef MICROPY_GC_SPLIT_HEAP +#define MICROPY_GC_SPLIT_HEAP (0) // whether PSRAM is added to or replaces the heap +#endif #define MICROPY_ALLOC_PATH_MAX (128) #define MICROPY_QSTR_BYTES_IN_HASH (1) // MicroPython emitters #define MICROPY_PERSISTENT_CODE_LOAD (1) +#if PICO_ARM #define MICROPY_EMIT_THUMB (1) -#define MICROPY_EMIT_THUMB_ARMV7M (0) #define MICROPY_EMIT_INLINE_THUMB (1) +#if PICO_RP2040 +#define MICROPY_EMIT_THUMB_ARMV7M (0) #define MICROPY_EMIT_INLINE_THUMB_FLOAT (0) +#endif +#elif PICO_RISCV +#define MICROPY_EMIT_RV32 (1) +#endif // Optimisations #define MICROPY_OPT_COMPUTED_GOTO (1) diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index aa5415d6c842d..3f50151620a02 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -37,16 +37,18 @@ #include "tusb.h" #include "uart.h" #include "hardware/irq.h" -#include "hardware/rtc.h" #include "pico/unique_id.h" +#include "pico/aon_timer.h" #if MICROPY_PY_NETWORK_CYW43 #include "lib/cyw43-driver/src/cyw43.h" #endif +#if PICO_RP2040 // This needs to be added to the result of time_us_64() to get the number of // microseconds since the Epoch. static uint64_t time_us_64_offset_from_epoch; +#endif #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC @@ -123,6 +125,17 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { return did_write ? ret : 0; } +#if PICO_RISCV +__attribute__((naked)) mp_uint_t mp_hal_ticks_cpu(void) { + __asm volatile ( + "li a0, 4\n" // mask value to uninhibit mcycle counter + "csrw mcountinhibit, a0\n" // uninhibit mcycle counter + "csrr a0, mcycle\n" // get mcycle counter + "ret\n" + ); +} +#endif + void mp_hal_delay_us(mp_uint_t us) { // Avoid calling sleep_us() and invoking the alarm pool by splitting long // sleeps into an optional longer sleep and a shorter busy-wait @@ -145,27 +158,35 @@ void mp_hal_delay_ms(mp_uint_t ms) { } void mp_hal_time_ns_set_from_rtc(void) { + #if PICO_RP2040 // Outstanding RTC register writes need at least two RTC clock cycles to // update. (See RP2040 datasheet section 4.8.4 "Reference clock"). mp_hal_delay_us(44); // Sample RTC and time_us_64() as close together as possible, so the offset // calculated for the latter can be as accurate as possible. - datetime_t t; - rtc_get_datetime(&t); + struct timespec ts; + aon_timer_get_time(&ts); uint64_t us = time_us_64(); - // Calculate the difference between the RTC Epoch seconds and time_us_64(). - uint64_t s = timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.min, t.sec); - time_us_64_offset_from_epoch = (uint64_t)s * 1000000ULL - us; + // Calculate the difference between the RTC Epoch and time_us_64(). + time_us_64_offset_from_epoch = ((uint64_t)ts.tv_sec * 1000000ULL) + ((uint64_t)ts.tv_nsec / 1000ULL) - us; + #endif } uint64_t mp_hal_time_ns(void) { - // The RTC only has seconds resolution, so instead use time_us_64() to get a more + #if PICO_RP2040 + // The RTC probably has limited resolution, so instead use time_us_64() to get a more // precise measure of Epoch time. Both these "clocks" are clocked from the same // source so they remain synchronised, and only differ by a fixed offset (calculated // in mp_hal_time_ns_set_from_rtc). return (time_us_64_offset_from_epoch + time_us_64()) * 1000ULL; + #else + // aon timer has ms resolution + struct timespec ts; + aon_timer_get_time(&ts); + return ((uint64_t)ts.tv_sec * 1000000000ULL) + (uint64_t)ts.tv_nsec; + #endif } // Generate a random locally administered MAC address (LAA) diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h index 16ac4259a6feb..25a472dfbdda4 100644 --- a/ports/rp2/mphalport.h +++ b/ports/rp2/mphalport.h @@ -92,11 +92,15 @@ static inline mp_uint_t mp_hal_ticks_ms(void) { return to_ms_since_boot(get_absolute_time()); } +#if PICO_ARM static inline mp_uint_t mp_hal_ticks_cpu(void) { // ticks_cpu() is defined as using the highest-resolution timing source // in the system. This is usually a CPU clock, but doesn't have to be. return time_us_32(); } +#elif PICO_RISCV +mp_uint_t mp_hal_ticks_cpu(void); +#endif static inline mp_uint_t mp_hal_get_cpu_freq(void) { return clock_get_hz(clk_sys); @@ -115,7 +119,7 @@ static inline mp_uint_t mp_hal_get_cpu_freq(void) { #define MP_HAL_PIN_PULL_UP (1) #define MP_HAL_PIN_PULL_DOWN (2) -extern uint32_t machine_pin_open_drain_mask; +extern uint64_t machine_pin_open_drain_mask; mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index e58adb3ba3595..fcc60b3fe5796 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -43,7 +43,14 @@ static soft_timer_entry_t mp_network_soft_timer; #include "lib/cyw43-driver/src/cyw43.h" #include "lib/cyw43-driver/src/cyw43_stats.h" #include "hardware/irq.h" + +#if PICO_RP2040 #include "RP2040.h" // cmsis, for NVIC_SetPriority and PendSV_IRQn +#elif PICO_RP2350 +#include "RP2350.h" // cmsis, for NVIC_SetPriority and PendSV_IRQn +#else +#error Unknown processor +#endif #define CYW43_IRQ_LEVEL GPIO_IRQ_LEVEL_HIGH #define CYW43_SHARED_IRQ_HANDLER_PRIORITY PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY diff --git a/ports/rp2/pendsv.c b/ports/rp2/pendsv.c index 6cfe624c3037f..905a5aa162ec5 100644 --- a/ports/rp2/pendsv.c +++ b/ports/rp2/pendsv.c @@ -28,7 +28,14 @@ #include "py/mpconfig.h" #include "mutex_extra.h" #include "pendsv.h" + +#if PICO_RP2040 #include "RP2040.h" +#elif PICO_RP2350 && PICO_ARM +#include "RP2350.h" +#elif PICO_RISCV +#include "pico/aon_timer.h" +#endif #if MICROPY_PY_NETWORK_CYW43 #include "lib/cyw43-driver/src/cyw43_stats.h" @@ -36,6 +43,8 @@ static pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS]; +void PendSV_Handler(void); + // Using the nowait variant here as softtimer updates PendSV from the loop of mp_wfe_or_timeout(), // where we don't want the CPU event bit to be set. static recursive_mutex_nowait_t pendsv_mutex; @@ -68,10 +77,16 @@ void pendsv_resume(void) { void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { pendsv_dispatch_table[slot] = f; if (pendsv_mutex.mutex.enter_count == 0) { + #if PICO_ARM // There is a race here where other core calls pendsv_suspend() before // ISR can execute, but dispatch will happen later when other core // calls pendsv_resume(). SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; + #elif PICO_RISCV + struct timespec ts; + aon_timer_get_time(&ts); + aon_timer_enable_alarm(&ts, PendSV_Handler, false); + #endif } else { #if MICROPY_PY_NETWORK_CYW43 CYW43_STAT_INC(PENDSV_DISABLED_COUNT); diff --git a/ports/rp2/rp2_dma.c b/ports/rp2/rp2_dma.c index 3afcf8b5d63af..78f69e6452758 100644 --- a/ports/rp2/rp2_dma.c +++ b/ports/rp2/rp2_dma.c @@ -57,23 +57,28 @@ typedef struct _rp2_dma_ctrl_field_t { } rp2_dma_ctrl_field_t; static rp2_dma_ctrl_field_t rp2_dma_ctrl_fields_table[] = { - { MP_QSTR_enable, 0, 1, 0 }, - { MP_QSTR_high_pri, 1, 1, 0 }, - { MP_QSTR_size, 2, 2, 0 }, - { MP_QSTR_inc_read, 4, 1, 0 }, - { MP_QSTR_inc_write, 5, 1, 0 }, - { MP_QSTR_ring_size, 6, 4, 0 }, - { MP_QSTR_ring_sel, 10, 1, 0 }, - { MP_QSTR_chain_to, 11, 4, 0 }, - { MP_QSTR_treq_sel, 15, 6, 0 }, - { MP_QSTR_irq_quiet, 21, 1, 0 }, - { MP_QSTR_bswap, 22, 1, 0 }, - { MP_QSTR_sniff_en, 23, 1, 0 }, - { MP_QSTR_busy, 24, 1, 1 }, - // bits 25 through 28 are reserved - { MP_QSTR_write_err, 29, 1, 0 }, - { MP_QSTR_read_err, 30, 1, 0 }, - { MP_QSTR_ahb_err, 31, 1, 1 }, + { MP_QSTR_enable, DMA_CH0_CTRL_TRIG_EN_LSB, 1, 0 }, + { MP_QSTR_high_pri, DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_LSB, 1, 0 }, + { MP_QSTR_size, DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB, 2, 0 }, + { MP_QSTR_inc_read, DMA_CH0_CTRL_TRIG_INCR_READ_LSB, 1, 0 }, + #if PICO_RP2350 + { MP_QSTR_inc_read_rev, DMA_CH0_CTRL_TRIG_INCR_READ_REV_LSB, 1, 0 }, + #endif + { MP_QSTR_inc_write, DMA_CH0_CTRL_TRIG_INCR_WRITE_LSB, 1, 0 }, + #if PICO_RP2350 + { MP_QSTR_inc_write_rev, DMA_CH0_CTRL_TRIG_INCR_WRITE_REV_LSB, 1, 0 }, + #endif + { MP_QSTR_ring_size, DMA_CH0_CTRL_TRIG_RING_SIZE_LSB, 4, 0 }, + { MP_QSTR_ring_sel, DMA_CH0_CTRL_TRIG_RING_SEL_LSB, 1, 0 }, + { MP_QSTR_chain_to, DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB, 4, 0 }, + { MP_QSTR_treq_sel, DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB, 6, 0 }, + { MP_QSTR_irq_quiet, DMA_CH0_CTRL_TRIG_IRQ_QUIET_LSB, 1, 0 }, + { MP_QSTR_bswap, DMA_CH0_CTRL_TRIG_BSWAP_LSB, 1, 0 }, + { MP_QSTR_sniff_en, DMA_CH0_CTRL_TRIG_SNIFF_EN_LSB, 1, 0 }, + { MP_QSTR_busy, DMA_CH0_CTRL_TRIG_BUSY_LSB, 1, 1 }, + { MP_QSTR_write_err, DMA_CH0_CTRL_TRIG_WRITE_ERROR_LSB, 1, 0 }, + { MP_QSTR_read_err, DMA_CH0_CTRL_TRIG_READ_ERROR_LSB, 1, 0 }, + { MP_QSTR_ahb_err, DMA_CH0_CTRL_TRIG_AHB_ERROR_LSB, 1, 1 }, }; static const uint32_t rp2_dma_ctrl_field_count = MP_ARRAY_SIZE(rp2_dma_ctrl_fields_table); @@ -298,7 +303,12 @@ static mp_obj_t rp2_dma_active(size_t n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_dma_active_obj, 1, 2, rp2_dma_active); // Default is quiet, unpaced, read and write incrementing, word transfers, enabled -#define DEFAULT_DMA_CONFIG (1 << 21) | (0x3f << 15) | (1 << 5) | (1 << 4) | (2 << 2) | (1 << 0) +#define DEFAULT_DMA_CONFIG (1 << DMA_CH0_CTRL_TRIG_IRQ_QUIET_LSB) | \ + (DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_PERMANENT << DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB) | \ + (1 << DMA_CH0_CTRL_TRIG_INCR_WRITE_LSB) | \ + (1 << DMA_CH0_CTRL_TRIG_INCR_READ_LSB) | \ + (2 << DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB) | \ + (1 << DMA_CH0_CTRL_TRIG_EN_LSB) // DMA.pack_ctrl(...) static mp_obj_t rp2_dma_pack_ctrl(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c index c1acb54e75748..9280dc5f8ecc2 100644 --- a/ports/rp2/rp2_flash.c +++ b/ports/rp2/rp2_flash.c @@ -26,12 +26,19 @@ #include +#include "rp2_psram.h" #include "py/mphal.h" #include "py/runtime.h" #include "extmod/vfs.h" #include "modrp2.h" #include "hardware/flash.h" #include "pico/binary_info.h" +#ifdef PICO_RP2350 +#include "hardware/structs/ioqspi.h" +#include "hardware/structs/qmi.h" +#else +#include "hardware/structs/ssi.h" +#endif #define BLOCK_SIZE_BYTES (FLASH_SECTOR_SIZE) @@ -70,16 +77,73 @@ bi_decl(bi_block_device( BINARY_INFO_BLOCK_DEV_FLAG_WRITE | BINARY_INFO_BLOCK_DEV_FLAG_PT_UNKNOWN)); +// Function to set the flash divisor to the correct divisor, assumes interrupts disabled +// and core1 locked out if relevant. +static void __no_inline_not_in_flash_func(rp2_flash_set_timing_internal)(int clock_hz) { + + // Use the minimum divisor assuming a 133MHz flash. + const int max_flash_freq = 133000000; + int divisor = (clock_hz + max_flash_freq - 1) / max_flash_freq; + + #if PICO_RP2350 + // Make sure flash is deselected - QMI doesn't appear to have a busy flag(!) + while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) { + ; + } + + // RX delay equal to the divisor means sampling at the same time as the next falling edge of SCK after the + // falling edge that generated the data. This is pretty tight at 133MHz but seems to work with the Winbond flash chips. + const int rxdelay = divisor; + qmi_hw->m[0].timing = (1 << QMI_M0_TIMING_COOLDOWN_LSB) | + rxdelay << QMI_M1_TIMING_RXDELAY_LSB | + divisor << QMI_M1_TIMING_CLKDIV_LSB; + + // Force a read through XIP to ensure the timing is applied + volatile uint32_t *ptr = (volatile uint32_t *)0x14000000; + (void)*ptr; + #else + // RP2040 SSI hardware only supports even divisors + if (divisor & 1) { + divisor += 1; + } + + // Wait for SSI not busy + while (ssi_hw->sr & SSI_SR_BUSY_BITS) { + ; + } + + // Disable, set the new divisor, and re-enable + hw_clear_bits(&ssi_hw->ssienr, SSI_SSIENR_SSI_EN_BITS); + ssi_hw->baudr = divisor; + hw_set_bits(&ssi_hw->ssienr, SSI_SSIENR_SSI_EN_BITS); + #endif +} + // Flash erase and write must run with interrupts disabled and the other core suspended, // because the XIP bit gets disabled. static uint32_t begin_critical_flash_section(void) { if (multicore_lockout_victim_is_initialized(1 - get_core_num())) { multicore_lockout_start_blocking(); } - return save_and_disable_interrupts(); + uint32_t state = save_and_disable_interrupts(); + + #if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM + // We're about to invalidate the XIP cache, clean it first to commit any dirty writes to PSRAM + uint8_t *maintenance_ptr = (uint8_t *)XIP_MAINTENANCE_BASE; + for (int i = 1; i < 16 * 1024; i += 8) { + maintenance_ptr[i] = 0; + } + #endif + + return state; } static void end_critical_flash_section(uint32_t state) { + rp2_flash_set_timing_internal(clock_get_hz(clk_sys)); + #if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM && defined(MICROPY_HW_PSRAM_MAX_SCK_HZ) + // update timing for PSRAM + rp2_psram_set_timing(MICROPY_HW_PSRAM_MAX_SCK_HZ); + #endif restore_interrupts(state); if (multicore_lockout_victim_is_initialized(1 - get_core_num())) { multicore_lockout_end_blocking(); @@ -145,11 +209,16 @@ static mp_obj_t rp2_flash_readblocks(size_t n_args, const mp_obj_t *args) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_flash_readblocks_obj, 3, 4, rp2_flash_readblocks); +static inline size_t min_size(size_t a, size_t b) { + return a < b ? a : b; +} + static mp_obj_t rp2_flash_writeblocks(size_t n_args, const mp_obj_t *args) { rp2_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); uint32_t offset = mp_obj_get_int(args[1]) * BLOCK_SIZE_BYTES; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + if (n_args == 3) { mp_uint_t atomic_state = begin_critical_flash_section(); flash_range_erase(self->flash_base + offset, bufinfo.len); @@ -159,10 +228,31 @@ static mp_obj_t rp2_flash_writeblocks(size_t n_args, const mp_obj_t *args) { } else { offset += mp_obj_get_int(args[3]); } - mp_uint_t atomic_state = begin_critical_flash_section(); - flash_range_program(self->flash_base + offset, bufinfo.buf, bufinfo.len); - end_critical_flash_section(atomic_state); - mp_event_handle_nowait(); + + if ((uintptr_t)bufinfo.buf >= SRAM_BASE) { + mp_uint_t atomic_state = begin_critical_flash_section(); + flash_range_program(self->flash_base + offset, bufinfo.buf, bufinfo.len); + end_critical_flash_section(atomic_state); + mp_event_handle_nowait(); + } else { + size_t bytes_left = bufinfo.len; + size_t bytes_offset = 0; + static uint8_t copy_buffer[BLOCK_SIZE_BYTES] = {0}; + + while (bytes_left) { + memcpy(copy_buffer, bufinfo.buf + bytes_offset, min_size(bytes_left, BLOCK_SIZE_BYTES)); + mp_uint_t atomic_state = begin_critical_flash_section(); + flash_range_program(self->flash_base + offset + bytes_offset, copy_buffer, min_size(bytes_left, BLOCK_SIZE_BYTES)); + end_critical_flash_section(atomic_state); + bytes_offset += BLOCK_SIZE_BYTES; + if (bytes_left <= BLOCK_SIZE_BYTES) { + break; + } + bytes_left -= BLOCK_SIZE_BYTES; + mp_event_handle_nowait(); + } + } + // TODO check return value return mp_const_none; } @@ -210,3 +300,23 @@ MP_DEFINE_CONST_OBJ_TYPE( make_new, rp2_flash_make_new, locals_dict, &rp2_flash_locals_dict ); + +// Modify the flash timing. Ensure flash access is suspended while +// the timings are altered. +void rp2_flash_set_timing_for_freq(int clock_hz) { + if (multicore_lockout_victim_is_initialized(1 - get_core_num())) { + multicore_lockout_start_blocking(); + } + uint32_t state = save_and_disable_interrupts(); + + rp2_flash_set_timing_internal(clock_hz); + + restore_interrupts(state); + if (multicore_lockout_victim_is_initialized(1 - get_core_num())) { + multicore_lockout_end_blocking(); + } +} + +void rp2_flash_set_timing() { + rp2_flash_set_timing_for_freq(clock_get_hz(clk_sys)); +} diff --git a/ports/rp2/rp2_flash.h b/ports/rp2/rp2_flash.h new file mode 100644 index 0000000000000..d5cf3ba2acac0 --- /dev/null +++ b/ports/rp2/rp2_flash.h @@ -0,0 +1,7 @@ +#ifndef MICROPY_INCLUDED_RP2_MACHINE_FLASH_H +#define MICROPY_INCLUDED_RP2_MACHINE_FLASH_H + +extern void rp2_flash_set_timing_for_freq(int clock_hz); +extern void rp2_flash_set_timing(); + +#endif diff --git a/ports/rp2/rp2_pio.c b/ports/rp2/rp2_pio.c index 0ca91656f84ef..0d885471538dc 100644 --- a/ports/rp2/rp2_pio.c +++ b/ports/rp2/rp2_pio.c @@ -32,11 +32,15 @@ #include "py/mphal.h" #include "shared/runtime/mpirq.h" #include "modrp2.h" +#include "machine_pin.h" +#include "genhdr/pins.h" #include "hardware/clocks.h" #include "hardware/irq.h" #include "hardware/pio.h" +extern const machine_pin_obj_t machine_pin_obj_table[NUM_BANK0_GPIOS]; + typedef struct _rp2_pio_obj_t { mp_obj_base_t base; PIO pio; @@ -54,7 +58,7 @@ typedef struct _rp2_state_machine_obj_t { PIO pio; uint8_t irq; uint8_t sm; // 0-3 - uint8_t id; // 0-7 + uint8_t id; // 0-7 on RP2040, or 0-11 on RP2350 } rp2_state_machine_obj_t; typedef struct _rp2_state_machine_irq_obj_t { @@ -63,11 +67,11 @@ typedef struct _rp2_state_machine_irq_obj_t { uint8_t trigger; } rp2_state_machine_irq_obj_t; -static const rp2_state_machine_obj_t rp2_state_machine_obj[8]; -static uint8_t rp2_state_machine_initial_pc[8]; +static const rp2_state_machine_obj_t rp2_state_machine_obj[NUM_PIOS * 4]; +static uint8_t rp2_state_machine_initial_pc[NUM_PIOS * 4]; // These masks keep track of PIO instruction memory used by this module. -static uint32_t rp2_pio_instruction_memory_usage_mask[2]; +static uint32_t rp2_pio_instruction_memory_usage_mask[NUM_PIOS]; static const rp2_state_machine_obj_t *rp2_state_machine_get_object(mp_int_t sm_id); static void rp2_state_machine_reset_all(void); @@ -104,8 +108,19 @@ static void pio1_irq0(void) { pio_irq0(pio1); } +#if NUM_PIOS >= 3 +static void pio2_irq0(void) { + pio_irq0(pio2); +} +#endif + // Returns the correct irq0 handler wrapper for a given pio static inline irq_handler_t rp2_pio_get_irq_handler(PIO pio) { + #if NUM_PIOS >= 3 + if (pio == pio2) { + return pio2_irq0; + } + #endif return pio == pio0 ? pio0_irq0 : pio1_irq0; } @@ -172,6 +187,12 @@ void rp2_pio_deinit(void) { irq_set_enabled(PIO1_IRQ_0, false); irq_remove_handler(PIO1_IRQ_0, pio1_irq0); } + #if NUM_PIOS >= 3 + if (irq_get_exclusive_handler(PIO2_IRQ_0) == pio2_irq0) { + irq_set_enabled(PIO2_IRQ_0, false); + irq_remove_handler(PIO2_IRQ_0, pio2_irq0); + } + #endif rp2_state_machine_reset_all(); @@ -180,6 +201,9 @@ void rp2_pio_deinit(void) { // and their PIO programs should remain intact. rp2_pio_remove_all_managed_programs(pio0); rp2_pio_remove_all_managed_programs(pio1); + #if NUM_PIOS >= 3 + rp2_pio_remove_all_managed_programs(pio2); + #endif } /******************************************************************************/ @@ -212,7 +236,7 @@ static void asm_pio_override_shiftctrl(mp_obj_t arg, uint32_t bits, uint32_t lsb } } -static void asm_pio_get_pins(const char *type, mp_obj_t prog_pins, mp_obj_t arg_base, asm_pio_config_t *config) { +static void asm_pio_get_pins(PIO pio, const char *type, mp_obj_t prog_pins, mp_obj_t arg_base, asm_pio_config_t *config) { if (prog_pins != mp_const_none) { // The PIO program specified pins for initialisation on out/set/sideset. if (mp_obj_is_integer(prog_pins)) { @@ -238,13 +262,21 @@ static void asm_pio_get_pins(const char *type, mp_obj_t prog_pins, mp_obj_t arg_ if (arg_base != mp_const_none) { // The instantiation of the PIO program specified a base pin. config->base = mp_hal_get_pin_obj(arg_base); + + #if PICO_PIO_USE_GPIO_BASE + // Check the base is within range of the configured gpio_base. + uint gpio_base = pio_get_gpio_base(pio); + if ((gpio_base == 0 && config->base >= 32) || (gpio_base == 16 && config->base < 16)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("%s_base not within gpio_base range"), type); + } + #endif } } static void asm_pio_init_gpio(PIO pio, uint32_t sm, asm_pio_config_t *config) { - uint32_t pinmask = ((1 << config->count) - 1) << config->base; - pio_sm_set_pins_with_mask(pio, sm, config->pinvals << config->base, pinmask); - pio_sm_set_pindirs_with_mask(pio, sm, config->pindirs << config->base, pinmask); + uint32_t pinmask = ((1 << config->count) - 1) << (config->base - pio_get_gpio_base(pio)); + pio_sm_set_pins_with_mask(pio, sm, config->pinvals << (config->base - pio_get_gpio_base(pio)), pinmask); + pio_sm_set_pindirs_with_mask(pio, sm, config->pindirs << (config->base - pio_get_gpio_base(pio)), pinmask); for (size_t i = 0; i < config->count; ++i) { gpio_set_function(config->base + i, GPIO_FUNC_PIO0 + pio_get_index(pio)); } @@ -258,6 +290,9 @@ static const mp_irq_methods_t rp2_pio_irq_methods; static rp2_pio_obj_t rp2_pio_obj[] = { { { &rp2_pio_type }, pio0, PIO0_IRQ_0 }, { { &rp2_pio_type }, pio1, PIO1_IRQ_0 }, + #if NUM_PIOS >= 3 + { { &rp2_pio_type }, pio2, PIO2_IRQ_0 }, + #endif }; static void rp2_pio_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -357,6 +392,31 @@ static mp_obj_t rp2_pio_state_machine(size_t n_args, const mp_obj_t *pos_args, m } MP_DEFINE_CONST_FUN_OBJ_KW(rp2_pio_state_machine_obj, 2, rp2_pio_state_machine); +#if PICO_PIO_USE_GPIO_BASE +// PIO.gpio_base([base]) +static mp_obj_t rp2_pio_gpio_base(size_t n_args, const mp_obj_t *args) { + rp2_pio_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (n_args > 1) { + // Set gpio_base value. + uint gpio_base = mp_hal_get_pin_obj(args[1]); + + // Must be 0 for GPIOs 0 to 31 inclusive, or 16 for GPIOs 16 to 48 inclusive. + if (!(gpio_base == 0 || gpio_base == 16)) { + mp_raise_ValueError("invalid GPIO base"); + } + + if (pio_set_gpio_base(self->pio, gpio_base) != PICO_OK) { + mp_raise_OSError(MP_EINVAL); + } + } + + // Return current gpio_base value. + return pio_get_gpio_base(self->pio) == 0 ? pin_GPIO0 : pin_GPIO16; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_pio_gpio_base_obj, 1, 2, rp2_pio_gpio_base); +#endif + // PIO.irq(handler=None, trigger=IRQ_SM0|IRQ_SM1|IRQ_SM2|IRQ_SM3, hard=False) static mp_obj_t rp2_pio_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_handler, ARG_trigger, ARG_hard }; @@ -410,6 +470,9 @@ static mp_obj_t rp2_pio_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k static MP_DEFINE_CONST_FUN_OBJ_KW(rp2_pio_irq_obj, 1, rp2_pio_irq); static const mp_rom_map_elem_t rp2_pio_locals_dict_table[] = { + #if PICO_PIO_USE_GPIO_BASE + { MP_ROM_QSTR(MP_QSTR_gpio_base), MP_ROM_PTR(&rp2_pio_gpio_base_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_add_program), MP_ROM_PTR(&rp2_pio_add_program_obj) }, { MP_ROM_QSTR(MP_QSTR_remove_program), MP_ROM_PTR(&rp2_pio_remove_program_obj) }, { MP_ROM_QSTR(MP_QSTR_state_machine), MP_ROM_PTR(&rp2_pio_state_machine_obj) }, @@ -486,6 +549,12 @@ static const rp2_state_machine_obj_t rp2_state_machine_obj[] = { { { &rp2_state_machine_type }, pio1, PIO1_IRQ_0, 1, 5 }, { { &rp2_state_machine_type }, pio1, PIO1_IRQ_0, 2, 6 }, { { &rp2_state_machine_type }, pio1, PIO1_IRQ_0, 3, 7 }, + #if NUM_PIOS >= 3 + { { &rp2_state_machine_type }, pio2, PIO2_IRQ_0, 0, 8 }, + { { &rp2_state_machine_type }, pio2, PIO2_IRQ_0, 1, 9 }, + { { &rp2_state_machine_type }, pio2, PIO2_IRQ_0, 2, 10 }, + { { &rp2_state_machine_type }, pio2, PIO2_IRQ_0, 3, 11 }, + #endif }; static const rp2_state_machine_obj_t *rp2_state_machine_get_object(mp_int_t sm_id) { @@ -604,14 +673,14 @@ static mp_obj_t rp2_state_machine_init_helper(const rp2_state_machine_obj_t *sel // Configure out pins, if needed. asm_pio_config_t out_config = ASM_PIO_CONFIG_DEFAULT; - asm_pio_get_pins("out", prog[PROG_OUT_PINS], args[ARG_out_base].u_obj, &out_config); + asm_pio_get_pins(self->pio, "out", prog[PROG_OUT_PINS], args[ARG_out_base].u_obj, &out_config); if (out_config.base >= 0) { sm_config_set_out_pins(&config, out_config.base, out_config.count); } // Configure set pin, if needed. asm_pio_config_t set_config = ASM_PIO_CONFIG_DEFAULT; - asm_pio_get_pins("set", prog[PROG_SET_PINS], args[ARG_set_base].u_obj, &set_config); + asm_pio_get_pins(self->pio, "set", prog[PROG_SET_PINS], args[ARG_set_base].u_obj, &set_config); if (set_config.base >= 0) { sm_config_set_set_pins(&config, set_config.base, set_config.count); } @@ -623,7 +692,7 @@ static mp_obj_t rp2_state_machine_init_helper(const rp2_state_machine_obj_t *sel // Configure sideset pin, if needed. asm_pio_config_t sideset_config = ASM_PIO_CONFIG_DEFAULT; - asm_pio_get_pins("sideset", prog[PROG_SIDESET_PINS], args[ARG_sideset_base].u_obj, &sideset_config); + asm_pio_get_pins(self->pio, "sideset", prog[PROG_SIDESET_PINS], args[ARG_sideset_base].u_obj, &sideset_config); if (sideset_config.base >= 0) { uint32_t count = sideset_config.count; if (config.execctrl & (1 << PIO_SM0_EXECCTRL_SIDE_EN_LSB)) { @@ -951,5 +1020,5 @@ static const mp_irq_methods_t rp2_state_machine_irq_methods = { .info = rp2_state_machine_irq_info, }; -MP_REGISTER_ROOT_POINTER(void *rp2_pio_irq_obj[2]); -MP_REGISTER_ROOT_POINTER(void *rp2_state_machine_irq_obj[8]); +MP_REGISTER_ROOT_POINTER(void *rp2_pio_irq_obj[NUM_PIOS]); +MP_REGISTER_ROOT_POINTER(void *rp2_state_machine_irq_obj[NUM_PIOS * 4]); diff --git a/ports/rp2/rp2_psram.c b/ports/rp2/rp2_psram.c new file mode 100644 index 0000000000000..a3b7ad2b009bb --- /dev/null +++ b/ports/rp2/rp2_psram.c @@ -0,0 +1,280 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hardware/structs/ioqspi.h" +#include "hardware/structs/qmi.h" +#include "hardware/structs/xip_ctrl.h" +#include "hardware/clocks.h" +#include "hardware/sync.h" +#include "rp2_psram.h" + +// Notes on the PSRAM IC supported +// +// Most boards seem to support a variant of the following PSRAM IC: +// +// apmemory APS6404L-3SQR-ZR +// https://www.mouser.com/ProductDetail/AP-Memory/APS6404L-3SQR-ZR?qs=IS%252B4QmGtzzpDOdsCIglviw%3D%3D +// +// The origin of this logic is from the Circuit Python code that was downloaded from: +// https://github.com/raspberrypi/pico-sdk-rp2350/issues/12#issuecomment-2055274428 +// +// The returned ID value for the above IC +const uint8_t PSRAM_ID = 0x5D; + +// From the above datasheet, PSRAM SPI command codes + +const uint8_t PSRAM_CMD_QUAD_END = 0xF5; +const uint8_t PSRAM_CMD_QUAD_ENABLE = 0x35; +const uint8_t PSRAM_CMD_READ_ID = 0x9F; +const uint8_t PSRAM_CMD_RSTEN = 0x66; +const uint8_t PSRAM_CMD_RST = 0x99; +const uint8_t PSRAM_CMD_QUAD_READ = 0xEB; +const uint8_t PSRAM_CMD_QUAD_WRITE = 0x38; +const uint8_t PSRAM_CMD_NOOP = 0xFF; + + +// For PSRAM timing calculations - to use int math, we work in femto seconds (fs) (1e-15), +// NOTE: This idea is from micro python work on psram support. + +#define SEC_TO_FS 1000000000000000ll + +// max select pulse width = 8us => 8e6 ns => 8000 ns => 8000 * 1e6 fs => 8000e6 fs +// Additionally, the MAX select is in units of 64 clock cycles - will use a constant that +// takes this into account - so 8000e6 fs / 64 = 125e6 fs +const uint32_t PSRAM_MAX_SELECT_FS64 = 125000000; + +// min deselect pulse width = 18ns => 18 * 1e6 fs => 18e6 fs +const uint32_t PSRAM_MIN_DESELECT_FS = 18000000; + +// Define the divisor limit for at 100MHz +#define PSRAM_DIVISOR_LIMIT 100000000 + +size_t __no_inline_not_in_flash_func(psram_detect)() { + + int psram_size = 0; + + uint32_t intr_stash = save_and_disable_interrupts(); + + // Try and read the PSRAM ID via direct_csr. + qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | QMI_DIRECT_CSR_EN_BITS; + + // Need to poll for the cooldown on the last XIP transfer to expire + // (via direct-mode BUSY flag) before it is safe to perform the first + // direct-mode operation + while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { + } + + // Exit out of QMI in case we've inited already + qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; + + // Transmit the command to exit QPI quad mode - read ID as standard SPI + qmi_hw->direct_tx = QMI_DIRECT_TX_OE_BITS | QMI_DIRECT_TX_IWIDTH_VALUE_Q << QMI_DIRECT_TX_IWIDTH_LSB | PSRAM_CMD_QUAD_END; + + while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { + } + + (void)qmi_hw->direct_rx; + + qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS); + + // Read the id + qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; + uint8_t kgd = 0; // known good die + uint8_t eid = 0; // density/size + + for (size_t i = 0; i < 7; i++) + { + if (i == 0) { + qmi_hw->direct_tx = PSRAM_CMD_READ_ID; + } else { + qmi_hw->direct_tx = PSRAM_CMD_NOOP; + } + + while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_TXEMPTY_BITS) == 0) { + } + + while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { + } + + if (i == 5) { + kgd = qmi_hw->direct_rx; + } else if (i == 6) { + eid = qmi_hw->direct_rx; + } else { + (void)qmi_hw->direct_rx; + } + } + + // Disable direct csr. + qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS); + + if (kgd == PSRAM_ID) { + psram_size = 1024 * 1024; // 1 MiB + uint8_t size_id = eid >> 5; + if (eid == 0x26 || size_id == 2) { + psram_size *= 8; // 8 MiB + } else if (size_id == 0) { + psram_size *= 2; // 2 MiB + } else if (size_id == 1) { + psram_size *= 4; // 4 MiB + } + } + + restore_interrupts(intr_stash); + return psram_size; +} + + +//----------------------------------------------------------------------------- +/// @brief Update the PSRAM timing configuration based on system clock +/// +/// @param max_psram_freq The maximum frequency the PSRAM can operate at. +// +// Note: Made this into a fuction incase it's needed to be called if the system clock changes. +// + void __no_inline_not_in_flash_func(rp2_psram_set_timing)(const uint32_t max_psram_freq) +{ + // Set PSRAM timing for APS6404 + // + // Note: The max SCK frequency of the APS6404 depends on the VDD voltage supplied. + // The max frequency is 133MHz for VDD = 3.0V, 109MHz for VDD = 3.3V + // See the pages 1 of the above datasheet for more details. Since this depends on + // the board design, this is a parameter passed to this function. + // + // Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133MHz. + // So: don't allow running at divisor 1 above 100MHz (because delay of 2 would be too late), + // and add an extra 1 to the rxdelay if the divided clock is > 100MHz (i.e. sys clock > 200MHz). + + const uint32_t clock_hz = clock_get_hz(clk_sys); + uint8_t divisor = (clock_hz + max_psram_freq - 1) / max_psram_freq; + if (divisor == 1 && clock_hz > PSRAM_DIVISOR_LIMIT) { + divisor = 2; + } + uint8_t rxdelay = divisor; + if (clock_hz / divisor > PSRAM_DIVISOR_LIMIT) { + rxdelay += 1; + } + + // clock period in femto seconds + const uint32_t clock_period_fs = SEC_TO_FS / clock_hz; + + // - Max select must be <= 8us. The value is given in multiples of 64 system clocks. + // So maxFS / (64 * clock_period_fs) = max_select = PSRAM_MAX_SELECT_FS64/clock_period_fs + const uint8_t max_select = PSRAM_MAX_SELECT_FS64 / clock_period_fs; // 125 = 8000ns / 64 + + // - Min deselect must be >= 18ns. The value is given in system clock cycles - ceil(divisor / 2). + const uint8_t min_deselect = (PSRAM_MIN_DESELECT_FS + (clock_period_fs - 1)) / clock_period_fs - (divisor + 1) / 2; + + qmi_hw->m[1].timing = 1 << QMI_M1_TIMING_COOLDOWN_LSB | + QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB | // Break between pages. + 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; +} + +size_t __no_inline_not_in_flash_func(rp2_psram_init)(uint cs_pin, const uint32_t max_psram_freq) { + + gpio_set_function(cs_pin, GPIO_FUNC_XIP_CS1); + + size_t psram_size = psram_detect(); + + if (!psram_size) { + return 0; + } + + // After the PSRAM detect operation, and this is startup, do a RESETEN, RESET and quad enable + + // Enable QPI mode on the PSRAM - use "direct mode" to send the command + // From the flash boot ASM code (in the SDK): + // Need to use direct serial mode to send SR commands. Choose a + // conservative direct-mode divisor (5 MHz at 150 MHz clk_sys) + // since the XIP-mode divisor may be unsafe without an RX delay. + qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | QMI_DIRECT_CSR_EN_BITS; + + // Need to poll for the cooldown on the last XIP transfer to expire + // (via direct-mode BUSY flag) before it is safe to perform the first + // direct-mode operation + while (qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) { + ; + } + // RESETEN, RESET and quad enable + for (uint8_t i = 0; i < 3; i++) + { + qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; + if (i == 0) + qmi_hw->direct_tx = PSRAM_CMD_RSTEN; + else if (i == 1) + qmi_hw->direct_tx = PSRAM_CMD_RST; + else + qmi_hw->direct_tx = PSRAM_CMD_QUAD_ENABLE; + + while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) + { + } + qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS); + for (size_t j = 0; j < 20; j++) + asm("nop"); + + (void)qmi_hw->direct_rx; + } + + // Disable direct csr. + qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS); + + // setup the PSRAM timing + rp2_psram_set_timing(max_psram_freq); + + // Set PSRAM commands and formats + qmi_hw->m[1].rfmt = + (QMI_M1_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_RFMT_PREFIX_WIDTH_LSB | + QMI_M1_RFMT_ADDR_WIDTH_VALUE_Q << QMI_M1_RFMT_ADDR_WIDTH_LSB | + QMI_M1_RFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M1_RFMT_SUFFIX_WIDTH_LSB | + QMI_M1_RFMT_DUMMY_WIDTH_VALUE_Q << QMI_M1_RFMT_DUMMY_WIDTH_LSB | + QMI_M1_RFMT_DATA_WIDTH_VALUE_Q << QMI_M1_RFMT_DATA_WIDTH_LSB | + QMI_M1_RFMT_PREFIX_LEN_VALUE_8 << QMI_M1_RFMT_PREFIX_LEN_LSB | + QMI_M1_RFMT_DUMMY_LEN_VALUE_24 << QMI_M1_RFMT_DUMMY_LEN_LSB | + QMI_M1_RFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_RFMT_SUFFIX_LEN_LSB ); + + qmi_hw->m[1].rcmd = PSRAM_CMD_QUAD_READ << QMI_M1_RCMD_PREFIX_LSB; + + qmi_hw->m[1].wfmt = + ( QMI_M1_WFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_WFMT_PREFIX_WIDTH_LSB | + QMI_M1_WFMT_ADDR_WIDTH_VALUE_Q << QMI_M1_WFMT_ADDR_WIDTH_LSB | + QMI_M1_WFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M1_WFMT_SUFFIX_WIDTH_LSB | + QMI_M1_WFMT_DUMMY_WIDTH_VALUE_Q << QMI_M1_WFMT_DUMMY_WIDTH_LSB | + QMI_M1_WFMT_DUMMY_LEN_VALUE_NONE << QMI_M1_WFMT_DUMMY_LEN_LSB | + QMI_M1_WFMT_DATA_WIDTH_VALUE_Q << QMI_M1_WFMT_DATA_WIDTH_LSB | + QMI_M1_WFMT_PREFIX_LEN_VALUE_8 << QMI_M1_WFMT_PREFIX_LEN_LSB | + QMI_M1_WFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_WFMT_SUFFIX_LEN_LSB ); + + qmi_hw->m[1].wcmd = PSRAM_CMD_QUAD_WRITE << QMI_M1_WCMD_PREFIX_LSB; + + // Enable writes to PSRAM + hw_set_bits(&xip_ctrl_hw->ctrl, XIP_CTRL_WRITABLE_M1_BITS); + + return psram_size; +} diff --git a/ports/rp2/rp2_psram.h b/ports/rp2/rp2_psram.h new file mode 100644 index 0000000000000..dd558bcbe04a7 --- /dev/null +++ b/ports/rp2/rp2_psram.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "pico/stdlib.h" + +#ifndef MICROPY_INCLUDED_RP2_MACHINE_PSRAM_H +#define MICROPY_INCLUDED_RP2_MACHINE_PSRAM_H + +#define PSRAM_LOCATION _u(0x11000000) + +extern size_t rp2_psram_init(uint cs_pin, const uint32_t max_psram_freq); +extern void rp2_psram_set_timing(const uint32_t max_psram_freq); + +#endif diff --git a/py/usermod.cmake b/py/usermod.cmake index 853276283746d..c814369a4865e 100644 --- a/py/usermod.cmake +++ b/py/usermod.cmake @@ -5,6 +5,10 @@ function(usermod_gather_sources SOURCES_VARNAME INCLUDE_DIRECTORIES_VARNAME INCL if (NOT ${LIB} IN_LIST ${INCLUDED_VARNAME}) list(APPEND ${INCLUDED_VARNAME} ${LIB}) + if (NOT TARGET ${LIB}) + return() + endif() + # Gather library sources get_target_property(lib_sources ${LIB} INTERFACE_SOURCES) if (lib_sources) diff --git a/shared/tinyusb/mp_usbd.h b/shared/tinyusb/mp_usbd.h index 31234566b6ca4..5c8f2a6095f30 100644 --- a/shared/tinyusb/mp_usbd.h +++ b/shared/tinyusb/mp_usbd.h @@ -40,6 +40,13 @@ #include "device/dcd.h" #endif +// Initialise TinyUSB device. +static inline void mp_usbd_init_tud(void) { + tusb_init(); + tud_cdc_configure_fifo_t cfg = { .rx_persistent = 0, .tx_persistent = 1 }; + tud_cdc_configure_fifo(&cfg); +} + // Run the TinyUSB device task void mp_usbd_task(void); @@ -125,7 +132,8 @@ inline static bool mp_usb_device_builtin_enabled(const mp_obj_usb_device_t *usbd static inline void mp_usbd_init(void) { // Without runtime USB support, this can be a thin wrapper wrapper around tusb_init() - tusb_init(); + // which is called in the below helper function. + mp_usbd_init_tud(); } #endif diff --git a/shared/tinyusb/mp_usbd_runtime.c b/shared/tinyusb/mp_usbd_runtime.c index fe28a4a727791..608aa74d244f6 100644 --- a/shared/tinyusb/mp_usbd_runtime.c +++ b/shared/tinyusb/mp_usbd_runtime.c @@ -428,8 +428,10 @@ void mp_usbd_init(void) { } if (need_usb) { - tusb_init(); // Safe to call redundantly - tud_connect(); // Reconnect if mp_usbd_deinit() has disconnected + // The following will call tusb_init(), which is safe to call redundantly. + mp_usbd_init_tud(); + // Reconnect if mp_usbd_deinit() has disconnected. + tud_connect(); } } diff --git a/shared/tinyusb/tusb_config.h b/shared/tinyusb/tusb_config.h index d0c9f89ec600c..0cc5ef03985ef 100644 --- a/shared/tinyusb/tusb_config.h +++ b/shared/tinyusb/tusb_config.h @@ -83,7 +83,6 @@ #ifndef CFG_TUD_CDC_TX_BUFSIZE #define CFG_TUD_CDC_TX_BUFSIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 256) #endif -#define CFG_TUD_CDC_PERSISTENT_TX_BUFF (1) #endif // MSC Configuration diff --git a/tests/ports/rp2/rp2_dma.py b/tests/ports/rp2/rp2_dma.py index 2459213f4c014..f9bd1c5fbf9ae 100644 --- a/tests/ports/rp2/rp2_dma.py +++ b/tests/ports/rp2/rp2_dma.py @@ -62,7 +62,8 @@ def run_and_time_dma(dma): dma.count = len(dest) // 4 dma.ctrl = dma.pack_ctrl() dt = run_and_time_dma(dma) -print(70 <= dt <= 110) +expected_dt = 90 * 120000000 // machine.freq() +print(abs(dt - expected_dt) <= 20) print(dest[:8], dest[-8:]) dma.close() diff --git a/tests/ports/rp2/rp2_uart.py b/tests/ports/rp2/rp2_uart.py new file mode 100644 index 0000000000000..da88599344795 --- /dev/null +++ b/tests/ports/rp2/rp2_uart.py @@ -0,0 +1,10 @@ +# Test construction of machine.UART objects. + +import sys +from machine import UART + +print(UART(0, tx=0, rx=1)) + +if "RP2350" in sys.implementation._machine: + # Test that UART can be constructed using other tx/rx pins. + UART(0, tx=2, rx=3) diff --git a/tests/ports/rp2/rp2_uart.py.exp b/tests/ports/rp2/rp2_uart.py.exp new file mode 100644 index 0000000000000..4594de5dc78f2 --- /dev/null +++ b/tests/ports/rp2/rp2_uart.py.exp @@ -0,0 +1 @@ +UART(0, baudrate=115200, bits=8, parity=None, stop=1, tx=0, rx=1, txbuf=256, rxbuf=256, timeout=0, timeout_char=1, invert=None) diff --git a/tools/ci.sh b/tools/ci.sh index 175d7f57622fb..9774895faf28b 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -314,6 +314,8 @@ function ci_rp2_build { make ${MAKEOPTS} -C ports/rp2 make ${MAKEOPTS} -C ports/rp2 BOARD=RPI_PICO_W submodules make ${MAKEOPTS} -C ports/rp2 BOARD=RPI_PICO_W USER_C_MODULES=../../examples/usercmodule/micropython.cmake + make ${MAKEOPTS} -C ports/rp2 BOARD=RPI_PICO2 submodules + make ${MAKEOPTS} -C ports/rp2 BOARD=RPI_PICO2 make ${MAKEOPTS} -C ports/rp2 BOARD=W5100S_EVB_PICO submodules make ${MAKEOPTS} -C ports/rp2 BOARD=W5100S_EVB_PICO