diff --git a/.gitmodules b/.gitmodules index a33354bbff..2fcfc07fcc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -331,3 +331,7 @@ [submodule "ports/silabs/tools/slc_cli_linux"] path = ports/silabs/tools/slc_cli_linux url = https://github.com/SiliconLabs/circuitpython_slc_cli_linux +[submodule "ports/raspberrypi/lib/PicoDVI"] + path = ports/raspberrypi/lib/PicoDVI + url = https://github.com/circuitpython/PicoDVI.git + branch = circuitpython diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 16ff1b38cc..61fd0d0a6b 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -126,6 +126,7 @@ msgstr "" #: ports/espressif/common-hal/espulp/ULP.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/digitalio/DigitalInOut.c #: shared-bindings/microcontroller/Pin.c @@ -164,11 +165,11 @@ msgstr "" msgid "%q length must be >= %d" msgstr "" -#: py/argcheck.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c py/argcheck.c msgid "%q must be %d" msgstr "" -#: py/argcheck.c +#: py/argcheck.c shared-bindings/displayio/Bitmap.c msgid "%q must be %d-%d" msgstr "" @@ -468,6 +469,7 @@ msgstr "" msgid "All event channels in use" msgstr "" +#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "All state machines in use" msgstr "" @@ -476,6 +478,7 @@ msgstr "" msgid "All sync event channels in use" msgstr "" +#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c #: shared-bindings/pwmio/PWMOut.c msgid "All timers for this pin are in use" msgstr "" @@ -1222,7 +1225,9 @@ msgid "Interrupt error." msgstr "" #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c -#: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c py/argcheck.c +#: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c +#: ports/raspberrypi/bindings/picodvi/Framebuffer.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c #: shared-bindings/displayio/EPaperDisplay.c msgid "Invalid %q" diff --git a/main.c b/main.c index 6ff6202d6d..9351d90529 100644 --- a/main.c +++ b/main.c @@ -153,11 +153,15 @@ STATIC void start_mp(supervisor_allocation *heap, supervisor_allocation *pystack supervisor_workflow_reset(); // Stack limit should be less than real stack size, so we have a chance - // to recover from limit hit. (Limit is measured in bytes.) + // to recover from limit hit. (Limit is measured in bytes.) The top of the + // stack is set to our current state. Not the actual top. mp_stack_ctrl_init(); - if (stack_get_bottom() != NULL) { - mp_stack_set_limit(stack_get_length() - 1024); + uint32_t *stack_bottom = stack_get_bottom(); + if (stack_bottom != NULL) { + size_t stack_length = stack_get_length(); + mp_stack_set_top(stack_bottom + (stack_length / sizeof(uint32_t))); + mp_stack_set_limit(stack_length - 1024); } diff --git a/ports/broadcom/common-hal/videocore/Framebuffer.c b/ports/broadcom/common-hal/videocore/Framebuffer.c index 18b703b8b6..63c55324af 100644 --- a/ports/broadcom/common-hal/videocore/Framebuffer.c +++ b/ports/broadcom/common-hal/videocore/Framebuffer.c @@ -42,6 +42,7 @@ void common_hal_videocore_framebuffer_deinit(videocore_framebuffer_obj_t *self) if (vcmailbox_release_framebuffer()) { self->framebuffer = NULL; } + self->base.type = &mp_type_NoneType; } bool common_hal_videocore_framebuffer_deinited(videocore_framebuffer_obj_t *self) { diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile index fafc817ca2..f23da5a51d 100644 --- a/ports/raspberrypi/Makefile +++ b/ports/raspberrypi/Makefile @@ -33,11 +33,11 @@ INC_CYW43 := \ -isystem lib/cyw43-driver/firmware \ -isystem lib/cyw43-driver/src \ -isystem lib/lwip/src/include \ - -isystem sdk/src/rp2_common/pico_async_context/include/ \ + -isystem sdk/src/rp2_common/pico_async_context/include/ \ -isystem sdk/src/rp2_common/pico_cyw43_arch/include/ \ -isystem sdk/src/rp2_common/pico_cyw43_driver/include/ \ -isystem sdk/src/rp2_common/pico_lwip/include/ \ - -isystem sdk/src/rp2_common/pico_rand/include/ \ + -isystem sdk/src/rp2_common/pico_rand/include/ \ CFLAGS_CYW43 := -DCYW43_LWIP=1 -DPICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1 -DCYW43_USE_SPI -DIGNORE_GPIO25 -DIGNORE_GPIO23 -DIGNORE_GPIO24 -DCYW43_LOGIC_DEBUG=0 -DCYW43_NETUTILS=1 SRC_SDK_CYW43 := \ @@ -105,6 +105,7 @@ INC += \ -isystem sdk/src/common/pico_util/include/ \ -isystem sdk/src/rp2040/hardware_regs/include/ \ -isystem sdk/src/rp2040/hardware_structs/include/ \ + -isystem sdk/src/rp2_common/cmsis/ \ -isystem sdk/src/rp2_common/hardware_adc/include/ \ -isystem sdk/src/rp2_common/hardware_base/include/ \ -isystem sdk/src/rp2_common/hardware_claim/include/ \ @@ -113,16 +114,19 @@ INC += \ -isystem sdk/src/rp2_common/hardware_dma/include/ \ -isystem sdk/src/rp2_common/hardware_flash/include/ \ -isystem sdk/src/rp2_common/hardware_gpio/include/ \ + -isystem sdk/src/rp2_common/hardware_interp/include/ \ -isystem sdk/src/rp2_common/hardware_irq/include/ \ -isystem sdk/src/rp2_common/hardware_i2c/include/ \ -isystem sdk/src/rp2_common/hardware_pio/include/ \ -isystem sdk/src/rp2_common/hardware_pll/include/ \ + -isystem sdk/src/rp2_common/hardware_pwm/include/ \ -isystem sdk/src/rp2_common/hardware_resets/include/ \ -isystem sdk/src/rp2_common/hardware_rtc/include/ \ -isystem sdk/src/rp2_common/hardware_spi/include/ \ -isystem sdk/src/rp2_common/hardware_sync/include/ \ -isystem sdk/src/rp2_common/hardware_timer/include/ \ -isystem sdk/src/rp2_common/hardware_uart/include/ \ + -isystem sdk/src/rp2_common/hardware_vreg/include/ \ -isystem sdk/src/rp2_common/hardware_watchdog/include/ \ -isystem sdk/src/rp2_common/hardware_xosc/include/ \ -isystem sdk/src/rp2_common/pico_multicore/include/ \ @@ -131,7 +135,7 @@ INC += \ -isystem sdk/src/rp2_common/pico_printf/include/ \ -isystem sdk/src/rp2_common/pico_float/include/ \ -isystem sdk/src/rp2_common/pico_platform/include/ \ - -isystem sdk/src/rp2_common/pico_runtime/printf/include/ \ + -isystem sdk/src/rp2_common/pico_runtime/include/ \ -isystem sdk/src/rp2_common/pico_bootrom/include/ \ -isystem sdk/src/rp2_common/pico_unique_id/include/ \ $(INC_CYW43) \ @@ -141,7 +145,7 @@ INC += \ -I$(BUILD) # Pico specific configuration -CFLAGS += -DRASPBERRYPI -DPICO_ON_DEVICE=1 -DPICO_NO_BINARY_INFO=0 -DPICO_TIME_DEFAULT_ALARM_POOL_DISABLED=0 -DPICO_DIVIDER_CALL_IDIV0=0 -DPICO_DIVIDER_CALL_LDIV0=0 -DPICO_DIVIDER_HARDWARE=1 -DPICO_DOUBLE_ROM=1 -DPICO_FLOAT_ROM=1 -DPICO_MULTICORE=1 -DPICO_BITS_IN_RAM=0 -DPICO_DIVIDER_IN_RAM=0 -DPICO_DOUBLE_PROPAGATE_NANS=0 -DPICO_DOUBLE_IN_RAM=0 -DPICO_MEM_IN_RAM=0 -DPICO_FLOAT_IN_RAM=0 -DPICO_FLOAT_PROPAGATE_NANS=1 -DPICO_NO_FLASH=0 -DPICO_COPY_TO_RAM=0 -DPICO_DISABLE_SHARED_IRQ_HANDLERS=0 -DPICO_NO_BI_BOOTSEL_VIA_DOUBLE_RESET=0 +CFLAGS += -DRASPBERRYPI -DPICO_ON_DEVICE=1 -DPICO_NO_BINARY_INFO=0 -DPICO_TIME_DEFAULT_ALARM_POOL_DISABLED=0 -DPICO_DIVIDER_CALL_IDIV0=0 -DPICO_DIVIDER_CALL_LDIV0=0 -DPICO_DIVIDER_HARDWARE=1 -DPICO_DOUBLE_ROM=1 -DPICO_FLOAT_ROM=1 -DPICO_MULTICORE=1 -DPICO_BITS_IN_RAM=0 -DPICO_DIVIDER_IN_RAM=0 -DPICO_DOUBLE_PROPAGATE_NANS=0 -DPICO_DOUBLE_IN_RAM=0 -DPICO_MEM_IN_RAM=0 -DPICO_FLOAT_IN_RAM=0 -DPICO_FLOAT_PROPAGATE_NANS=1 -DPICO_NO_FLASH=0 -DPICO_COPY_TO_RAM=0 -DPICO_DISABLE_SHARED_IRQ_HANDLERS=0 -DPICO_NO_BI_BOOTSEL_VIA_DOUBLE_RESET=0 -DDVI_1BPP_BIT_REVERSE=0 OPTIMIZATION_FLAGS ?= -O3 # TinyUSB defines CFLAGS += -DTUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX=1 -DCFG_TUSB_MCU=OPT_MCU_RP2040 -DCFG_TUD_MIDI_RX_BUFSIZE=128 -DCFG_TUD_CDC_RX_BUFSIZE=256 -DCFG_TUD_MIDI_TX_BUFSIZE=128 -DCFG_TUD_CDC_TX_BUFSIZE=256 -DCFG_TUD_MSC_BUFSIZE=1024 @@ -172,8 +176,8 @@ DISABLE_WARNINGS = -Wno-stringop-overflow -Wno-cast-align CFLAGS += $(INC) -Wall -Werror -std=gnu11 -nostdlib -fshort-enums $(BASE_CFLAGS) $(CFLAGS_MOD) $(COPT) $(DISABLE_WARNINGS) -Werror=missing-prototypes CFLAGS += \ - -march=armv6-m \ - -mthumb \ + -march=armv6-m \ + -mthumb \ -mabi=aapcs-linux \ -mcpu=cortex-m0plus \ -msoft-float \ @@ -195,6 +199,7 @@ SRC_SDK := \ src/common/pico_time/time.c \ src/common/pico_time/timeout_helper.c \ src/common/pico_util/pheap.c \ + src/common/pico_util/queue.c \ src/rp2_common/hardware_adc/adc.c \ src/rp2_common/hardware_claim/claim.c \ src/rp2_common/hardware_clocks/clocks.c \ @@ -202,6 +207,7 @@ SRC_SDK := \ src/rp2_common/hardware_flash/flash.c \ src/rp2_common/hardware_gpio/gpio.c \ src/rp2_common/hardware_i2c/i2c.c \ + src/rp2_common/hardware_interp/interp.c \ src/rp2_common/hardware_irq/irq.c \ src/rp2_common/hardware_pio/pio.c \ src/rp2_common/hardware_pll/pll.c \ @@ -210,6 +216,7 @@ SRC_SDK := \ src/rp2_common/hardware_sync/sync.c \ src/rp2_common/hardware_timer/timer.c \ src/rp2_common/hardware_uart/uart.c \ + src/rp2_common/hardware_vreg/vreg.c \ src/rp2_common/hardware_watchdog/watchdog.c \ src/rp2_common/hardware_xosc/xosc.c \ src/rp2_common/pico_bootrom/bootrom.c \ @@ -223,6 +230,7 @@ SRC_SDK := \ src/rp2_common/pico_printf/printf.c \ src/rp2_common/pico_runtime/runtime.c \ src/rp2_common/pico_stdio/stdio.c \ + src/rp2_common/pico_stdlib/stdlib.c \ src/rp2_common/pico_unique_id/unique_id.c \ $(SRC_SDK_CYW43) \ @@ -240,12 +248,24 @@ SRC_C += \ background.c \ peripherals/pins.c \ lib/crypto-algorithms/sha256.c \ + lib/PicoDVI/software/libdvi/dvi.c \ + lib/PicoDVI/software/libdvi/dvi_serialiser.c \ + lib/PicoDVI/software/libdvi/dvi_timing.c \ + lib/PicoDVI/software/libdvi/tmds_encode.c \ lib/tinyusb/src/portable/raspberrypi/rp2040/dcd_rp2040.c \ lib/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.c \ mphalport.c \ $(SRC_CYW43) \ $(SRC_LWIP) \ +ifeq ($(CIRCUITPY_PICODVI),1) +SRC_C += \ + bindings/picodvi/__init__.c \ + bindings/picodvi/Framebuffer.c \ + common-hal/picodvi/Framebuffer.c \ + +endif + ifeq ($(CIRCUITPY_SSL),1) CFLAGS += -isystem $(TOP)/mbedtls/include SRC_MBEDTLS := $(addprefix lib/mbedtls/library/, \ @@ -359,6 +379,7 @@ SRC_S_UPPER = sdk/src/rp2_common/hardware_divider/divider.S \ sdk/src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S \ sdk/src/rp2_common/pico_mem_ops/mem_ops_aeabi.S \ sdk/src/rp2_common/pico_standard_link/crt0.S \ + lib/PicoDVI/software/libdvi/tmds_encode_asm.S \ OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_SDK:.c=.o)) @@ -405,13 +426,19 @@ SRC_QSTR += $(SRC_C) $(SRC_SUPERVISOR) $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_M all: $(BUILD)/firmware.uf2 -LINK_LD := $(firstword $(wildcard boards/$(BOARD)/link.ld link.ld)) -$(BUILD)/firmware.elf: $(OBJ) $(LINK_LD) +BOARD_LD := $(wildcard boards/$(BOARD)/link.ld) + +ifneq ($(BOARD_LD),) + LINKER_SCRIPTS = -Wl,-T,$(BOARD_LD) +endif + +LINKER_SCRIPTS += -Wl,-T,link.ld + +$(BUILD)/firmware.elf: $(OBJ) $(BOARD_LD) link.ld $(STEPECHO) "LINK $@" $(Q)echo $(OBJ) > $(BUILD)/firmware.objs $(Q)echo $(PICO_LDFLAGS) > $(BUILD)/firmware.ldflags - $(Q)$(CC) -o $@ $(CFLAGS) @$(BUILD)/firmware.ldflags -Wl,-T,$(LINK_LD) -Wl,-Map=$@.map -Wl,-cref -Wl,--gc-sections @$(BUILD)/firmware.objs -Wl,-lc - $(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(LINK_LD) $(BUILD) + $(Q)$(CC) -o $@ $(CFLAGS) @$(BUILD)/firmware.ldflags $(LINKER_SCRIPTS) -Wl,--print-memory-usage -Wl,-Map=$@.map -Wl,-cref -Wl,--gc-sections @$(BUILD)/firmware.objs -Wl,-lc $(BUILD)/firmware.bin: $(BUILD)/firmware.elf $(STEPECHO) "Create $@" diff --git a/ports/raspberrypi/audio_dma.c b/ports/raspberrypi/audio_dma.c index 7fef1a52e0..612fbbde4c 100644 --- a/ports/raspberrypi/audio_dma.c +++ b/ports/raspberrypi/audio_dma.c @@ -37,7 +37,7 @@ #include "src/rp2_common/hardware_irq/include/hardware/irq.h" -#if CIRCUITPY_AUDIOPWMIO || CIRCUITPY_AUDIOBUSIO +#if CIRCUITPY_AUDIOCORE void audio_dma_reset(void) { for (size_t channel = 0; channel < NUM_DMA_CHANNELS; channel++) { diff --git a/ports/raspberrypi/bindings/picodvi/Framebuffer.c b/ports/raspberrypi/bindings/picodvi/Framebuffer.c new file mode 100644 index 0000000000..12482c3baa --- /dev/null +++ b/ports/raspberrypi/bindings/picodvi/Framebuffer.c @@ -0,0 +1,264 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Scott Shawcroft for Adafruit Industries + * + * 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 "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/objarray.h" + +#include "bindings/picodvi/Framebuffer.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/framebufferio/__init__.h" +#include "shared-module/framebufferio/FramebufferDisplay.h" + +//| class Framebuffer: +//| """A PicoDVI managed frame buffer.""" +//| +//| def __init__( +//| self, +//| width: int, +//| height: int, +//| *, +//| clk_dp: microcontroller.Pin, +//| clk_dn: microcontroller.Pin, +//| red_dp: microcontroller.Pin, +//| red_dn: microcontroller.Pin, +//| green_dp: microcontroller.Pin, +//| green_dn: microcontroller.Pin, +//| blue_dp: microcontroller.Pin, +//| blue_dn: microcontroller.Pin, +//| color_depth: int = 8, +//| ) -> None: +//| """Create a Framebuffer object with the given dimensions (640x480 or 800x480). Memory is +//| allocated outside of onto the heap and then moved outside on VM +//| end. +//| +//| This will change the system clock speed to match the DVI signal. +//| Make sure to initialize other objects after this one so they account +//| for the changed clock. This also allocates a very large framebuffer +//| and is most likely to succeed the earlier it is attempted. +//| +//| Each dp and dn pair of pins must be neighboring, such as 19 and 20. +//| They must also be ordered the same way. In other words, dp must be +//| less than dn for all pairs or dp must be greater than dn for all pairs. +//| +//| The framebuffer pixel format varies depending on color_depth: +//| * 1 - Each bit is a pixel. Either white (1) or black (0). +//| * 2 - Each 2 bits is a pixels. Grayscale between white (0x3) and black (0x0). +//| * 8 - Each byte is a pixels in RGB332 format. +//| * 16 - Each two bytes are a pixel in RGB565 format. +//| +//| Monochrome framebuffers (color_depth=1 or 2) will be full resolution. +//| Color framebuffers will be half resolution and pixels will be +//| duplicated to create a signal with the target dimensions. +//| +//| A Framebuffer is often used in conjunction with a +//| `framebufferio.FramebufferDisplay`. +//| +//| :param int width: the width of the target display signal. It will be halved when +//| color_depth >= 8 when creating the framebuffer. Only 640 or 800 is currently supported. +//| :param int height: the height of the target display signal. It will be halved when +//| color_depth >= 8 when creating the framebuffer. Only 480 is currently supported. +//| :param ~microcontroller.Pin clk_dp: the positive clock signal pin +//| :param ~microcontroller.Pin clk_dn: the negative clock signal pin +//| :param ~microcontroller.Pin red_dp: the positive red signal pin +//| :param ~microcontroller.Pin red_dn: the negative red signal pin +//| :param ~microcontroller.Pin green_dp: the positive green signal pin +//| :param ~microcontroller.Pin green_dn: the negative green signal pin +//| :param ~microcontroller.Pin blue_dp: the positive blue signal pin +//| :param ~microcontroller.Pin blue_dn: the negative blue signal pin +//| :param int color_depth: the color depth of the framebuffer in bits. 1, 2 for grayscale +//| and 8 or 16 for color +//| """ + +STATIC mp_obj_t picodvi_framebuffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_width, ARG_height, ARG_clk_dp, ARG_clk_dn, ARG_red_dp, ARG_red_dn, ARG_green_dp, + ARG_green_dn, ARG_blue_dp, ARG_blue_dn, ARG_color_depth }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED }, + + { MP_QSTR_clk_dp, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_clk_dn, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_red_dp, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_red_dn, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_green_dp, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_green_dn, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_blue_dp, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_blue_dn, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + + { MP_QSTR_color_depth, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + picodvi_framebuffer_obj_t *self = &allocate_display_bus_or_raise()->picodvi; + self->base.type = &picodvi_framebuffer_type; + + mp_uint_t width = (mp_uint_t)mp_arg_validate_int_min(args[ARG_width].u_int, 0, MP_QSTR_width); + mp_uint_t height = (mp_uint_t)mp_arg_validate_int_min(args[ARG_height].u_int, 0, MP_QSTR_height); + mp_uint_t color_depth = args[ARG_color_depth].u_int; + if (color_depth != 1 && color_depth != 2 && color_depth != 8 && color_depth != 16) { + mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_color_depth); + } + common_hal_picodvi_framebuffer_construct(self, + width, height, + validate_obj_is_free_pin(args[ARG_clk_dp].u_obj, MP_QSTR_clk_dp), + validate_obj_is_free_pin(args[ARG_clk_dn].u_obj, MP_QSTR_clk_dn), + validate_obj_is_free_pin(args[ARG_red_dp].u_obj, MP_QSTR_red_dp), + validate_obj_is_free_pin(args[ARG_red_dn].u_obj, MP_QSTR_red_dn), + validate_obj_is_free_pin(args[ARG_green_dp].u_obj, MP_QSTR_green_dp), + validate_obj_is_free_pin(args[ARG_green_dn].u_obj, MP_QSTR_green_dn), + validate_obj_is_free_pin(args[ARG_blue_dp].u_obj, MP_QSTR_blue_dp), + validate_obj_is_free_pin(args[ARG_blue_dn].u_obj, MP_QSTR_blue_dn), + color_depth); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Free the resources (pins, timers, etc.) associated with this +//| rgbmatrix instance. After deinitialization, no further operations +//| may be performed.""" +//| ... +STATIC mp_obj_t picodvi_framebuffer_deinit(mp_obj_t self_in) { + picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in; + common_hal_picodvi_framebuffer_deinit(self); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_deinit_obj, picodvi_framebuffer_deinit); + +static void check_for_deinit(picodvi_framebuffer_obj_t *self) { + if (common_hal_picodvi_framebuffer_deinited(self)) { + raise_deinited_error(); + } +} + +//| width: int +//| """The width of the framebuffer, in pixels. It may be doubled for output (and half of what +//| width was given to __init__.)""" +STATIC mp_obj_t picodvi_framebuffer_get_width(mp_obj_t self_in) { + picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in; + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_picodvi_framebuffer_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_get_width_obj, picodvi_framebuffer_get_width); +MP_PROPERTY_GETTER(picodvi_framebuffer_width_obj, + (mp_obj_t)&picodvi_framebuffer_get_width_obj); + +//| height: int +//| """The width of the framebuffer, in pixels. It may be doubled for output (and half of what +//| width was given to __init__.)""" +//| +STATIC mp_obj_t picodvi_framebuffer_get_height(mp_obj_t self_in) { + picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in; + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_picodvi_framebuffer_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_get_height_obj, picodvi_framebuffer_get_height); + +MP_PROPERTY_GETTER(picodvi_framebuffer_height_obj, + (mp_obj_t)&picodvi_framebuffer_get_height_obj); + +STATIC const mp_rom_map_elem_t picodvi_framebuffer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&picodvi_framebuffer_deinit_obj) }, + + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&picodvi_framebuffer_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&picodvi_framebuffer_height_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(picodvi_framebuffer_locals_dict, picodvi_framebuffer_locals_dict_table); + +STATIC void picodvi_framebuffer_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) { + common_hal_picodvi_framebuffer_get_buffer(self_in, bufinfo, 0); +} + +// These versions exist so that the prototype matches the protocol, +// avoiding a type cast that can hide errors +STATIC void picodvi_framebuffer_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) { + (void)dirty_row_bitmap; + common_hal_picodvi_framebuffer_refresh(self_in); +} + +STATIC void picodvi_framebuffer_deinit_proto(mp_obj_t self_in) { + common_hal_picodvi_framebuffer_deinit(self_in); +} + +STATIC int picodvi_framebuffer_get_width_proto(mp_obj_t self_in) { + return common_hal_picodvi_framebuffer_get_width(self_in); +} + +STATIC int picodvi_framebuffer_get_height_proto(mp_obj_t self_in) { + return common_hal_picodvi_framebuffer_get_height(self_in); +} + +STATIC int picodvi_framebuffer_get_color_depth_proto(mp_obj_t self_in) { + return common_hal_picodvi_framebuffer_get_color_depth(self_in); + ; +} + +STATIC int picodvi_framebuffer_get_bytes_per_cell_proto(mp_obj_t self_in) { + return 1; +} + +STATIC int picodvi_framebuffer_get_native_frames_per_second_proto(mp_obj_t self_in) { + return 60; +} + +STATIC bool picodvi_framebuffer_get_pixels_in_byte_share_row_proto(mp_obj_t self_in) { + return true; +} + +STATIC int picodvi_framebuffer_get_row_stride_proto(mp_obj_t self_in) { + return common_hal_picodvi_framebuffer_get_row_stride(self_in); +} + +STATIC const framebuffer_p_t picodvi_framebuffer_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer) + .get_bufinfo = picodvi_framebuffer_get_bufinfo, + .get_width = picodvi_framebuffer_get_width_proto, + .get_height = picodvi_framebuffer_get_height_proto, + .get_color_depth = picodvi_framebuffer_get_color_depth_proto, + .get_row_stride = picodvi_framebuffer_get_row_stride_proto, + .get_bytes_per_cell = picodvi_framebuffer_get_bytes_per_cell_proto, + .get_native_frames_per_second = picodvi_framebuffer_get_native_frames_per_second_proto, + .get_pixels_in_byte_share_row = picodvi_framebuffer_get_pixels_in_byte_share_row_proto, + .swapbuffers = picodvi_framebuffer_swapbuffers, + .deinit = picodvi_framebuffer_deinit_proto, +}; + +const mp_obj_type_t picodvi_framebuffer_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Framebuffer, + .locals_dict = (mp_obj_dict_t *)&picodvi_framebuffer_locals_dict, + .make_new = picodvi_framebuffer_make_new, + MP_TYPE_EXTENDED_FIELDS( + .buffer_p = { .get_buffer = common_hal_picodvi_framebuffer_get_buffer, }, + .protocol = &picodvi_framebuffer_proto, + ), +}; diff --git a/ports/raspberrypi/bindings/picodvi/Framebuffer.h b/ports/raspberrypi/bindings/picodvi/Framebuffer.h new file mode 100644 index 0000000000..cfcab4af50 --- /dev/null +++ b/ports/raspberrypi/bindings/picodvi/Framebuffer.h @@ -0,0 +1,49 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "common-hal/picodvi/Framebuffer.h" + +#include "shared-bindings/microcontroller/Pin.h" + +extern const mp_obj_type_t picodvi_framebuffer_type; + +void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self, + mp_uint_t width, mp_uint_t height, + const mcu_pin_obj_t *clk_dp, const mcu_pin_obj_t *clk_dn, + const mcu_pin_obj_t *red_dp, const mcu_pin_obj_t *red_dn, + const mcu_pin_obj_t *green_dp, const mcu_pin_obj_t *green_dn, + const mcu_pin_obj_t *blue_dp, const mcu_pin_obj_t *blue_dn, + mp_uint_t color_depth); +void common_hal_picodvi_framebuffer_deinit(picodvi_framebuffer_obj_t *self); +bool common_hal_picodvi_framebuffer_deinited(picodvi_framebuffer_obj_t *self); +void common_hal_picodvi_framebuffer_refresh(picodvi_framebuffer_obj_t *self); +int common_hal_picodvi_framebuffer_get_width(picodvi_framebuffer_obj_t *self); +int common_hal_picodvi_framebuffer_get_height(picodvi_framebuffer_obj_t *self); +int common_hal_picodvi_framebuffer_get_row_stride(picodvi_framebuffer_obj_t *self); +int common_hal_picodvi_framebuffer_get_color_depth(picodvi_framebuffer_obj_t *self); +mp_int_t common_hal_picodvi_framebuffer_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); diff --git a/ports/raspberrypi/bindings/picodvi/__init__.c b/ports/raspberrypi/bindings/picodvi/__init__.c new file mode 100644 index 0000000000..edfe7759c8 --- /dev/null +++ b/ports/raspberrypi/bindings/picodvi/__init__.c @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries + * + * 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/obj.h" +#include "py/runtime.h" + +#include "bindings/picodvi/Framebuffer.h" + +//| """Low-level routines for interacting with PicoDVI Output""" + +STATIC const mp_rom_map_elem_t picodvi_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_picodvi) }, + { MP_ROM_QSTR(MP_QSTR_Framebuffer), MP_ROM_PTR(&picodvi_framebuffer_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(picodvi_module_globals, picodvi_module_globals_table); + +const mp_obj_module_t picodvi_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&picodvi_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_picodvi, picodvi_module, CIRCUITPY_PICODVI); diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/board.c b/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/board.c index 331653173e..bcbab5e4cd 100644 --- a/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/board.c +++ b/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/board.c @@ -26,4 +26,27 @@ #include "supervisor/board.h" +#include "bindings/picodvi/Framebuffer.h" +#include "shared-module/displayio/__init__.h" +#include "shared-bindings/framebufferio/FramebufferDisplay.h" + +void board_init(void) { + picodvi_framebuffer_obj_t *fb = &allocate_display_bus()->picodvi; + fb->base.type = &picodvi_framebuffer_type; + common_hal_picodvi_framebuffer_construct(fb, 640, 480, + &pin_GPIO17, &pin_GPIO16, + &pin_GPIO19, &pin_GPIO18, + &pin_GPIO21, &pin_GPIO20, + &pin_GPIO23, &pin_GPIO22, + 8); + + framebufferio_framebufferdisplay_obj_t *display = &displays[0].framebuffer_display; + display->base.type = &framebufferio_framebufferdisplay_type; + common_hal_framebufferio_framebufferdisplay_construct( + display, + MP_OBJ_FROM_PTR(fb), + 0, + true); +} + // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/mpconfigboard.mk index a60ff7b9a7..ca8a6fa396 100644 --- a/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/mpconfigboard.mk +++ b/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/mpconfigboard.mk @@ -7,3 +7,5 @@ CHIP_VARIANT = RP2040 CHIP_FAMILY = rp2 EXTERNAL_FLASH_DEVICES = "GD25Q64C,W25Q64JVxQ" + +CIRCUITPY_PICODVI = 1 diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/pins.c b/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/pins.c index c8b4e3474f..3dd0fe86bb 100644 --- a/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/pins.c +++ b/ports/raspberrypi/boards/adafruit_feather_rp2040_dvi/pins.c @@ -1,5 +1,7 @@ #include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + STATIC const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS @@ -49,6 +51,8 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_D2N), MP_ROM_PTR(&pin_GPIO22) }, { MP_ROM_QSTR(MP_QSTR_D2P), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].framebuffer_display)}, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, diff --git a/ports/raspberrypi/boards/raspberry_pi_pico/mpconfigboard.mk b/ports/raspberrypi/boards/raspberry_pi_pico/mpconfigboard.mk index 608ca280df..20607d50e4 100644 --- a/ports/raspberrypi/boards/raspberry_pi_pico/mpconfigboard.mk +++ b/ports/raspberrypi/boards/raspberry_pi_pico/mpconfigboard.mk @@ -9,3 +9,4 @@ CHIP_FAMILY = rp2 EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 +CIRCUITPY_PICODVI = 1 diff --git a/ports/raspberrypi/boards/raspberry_pi_pico_w/link.ld b/ports/raspberrypi/boards/raspberry_pi_pico_w/link.ld index 2777b41720..e814bead4c 100644 --- a/ports/raspberrypi/boards/raspberry_pi_pico_w/link.ld +++ b/ports/raspberrypi/boards/raspberry_pi_pico_w/link.ld @@ -1,294 +1 @@ -/* 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_FIRMWARE (rx) : ORIGIN = 0x10000000, LENGTH = 1532k - /* Followed by: 4kB of NVRAM and at least 512kB of CIRCUITPY */ - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256k - SCRATCH_X (rwx) : ORIGIN = 0x20040000, LENGTH = 4k - SCRATCH_Y (rwx) : ORIGIN = 0x20041000, LENGTH = 4k -} - -ENTRY(_entry_point) - -SECTIONS -{ - /* Second stage bootloader is prepended to the image. It must be 256 bytes big - and checksummed. It is usually built by the boot_stage2 target - in the Pico SDK - */ - - .flash_begin : { - __flash_binary_start = .; - } > FLASH_FIRMWARE - - .boot2 : { - __boot2_start__ = .; - KEEP (*(.boot2)) - __boot2_end__ = .; - } > FLASH_FIRMWARE - - ASSERT(__boot2_end__ - __boot2_start__ == 256, - "ERROR: Pico second stage bootloader must be 256 bytes in size") - - /* The second stage will always enter the image at the start of .text. - The debugger will use the ELF entry point, which is the _entry_point - symbol if present, otherwise defaults to start of .text. - This can be used to transfer control back to the bootrom on debugger - launches only, to perform proper flash setup. - */ - - .text : { - __logical_binary_start = .; - KEEP (*(.vectors)) - KEEP (*(.binary_info_header)) - __binary_info_header_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) - - __property_getter_start = .; - *(.property_getter) - __property_getter_end = .; - __property_getset_start = .; - *(.property_getset) - __property_getset_end = .; - - *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .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) - - *(.eh_frame*) - . = ALIGN(4); - } > FLASH_FIRMWARE - - .rodata : { - *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) - . = ALIGN(4); - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) - . = ALIGN(4); - } > FLASH_FIRMWARE - - .ARM.extab : - { - *(.ARM.extab* .gnu.linkonce.armextab.*) - } > FLASH_FIRMWARE - - __exidx_start = .; - .ARM.exidx : - { - *(.ARM.exidx* .gnu.linkonce.armexidx.*) - } > FLASH_FIRMWARE - __exidx_end = .; - - /* Machine inspectable binary information */ - . = ALIGN(4); - __binary_info_start = .; - .binary_info : - { - KEEP(*(.binary_info.keep.*)) - *(.binary_info.*) - } > FLASH_FIRMWARE - __binary_info_end = .; - . = ALIGN(4); - - /* End of .text-like segments */ - __etext = .; - - .ram_vector_table (COPY): { - *(.ram_vector_table) - } > 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*) - - . = 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 = .); - - . = 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 = .); - - *(.jcr) - . = ALIGN(4); - /* All data end */ - __data_end__ = .; - } > RAM AT> FLASH_FIRMWARE - - .itcm : - { - . = ALIGN(4); - *(.itcm.*) - - . = ALIGN(4); - } > RAM AT> FLASH_FIRMWARE - _ld_itcm_destination = ADDR(.itcm); - _ld_itcm_flash_copy = LOADADDR(.itcm); - _ld_itcm_size = SIZEOF(.itcm); - - .dtcm_data : - { - . = ALIGN(4); - - *(.dtcm_data.*) - - . = ALIGN(4); - } > RAM AT> FLASH_FIRMWARE - _ld_dtcm_data_destination = ADDR(.dtcm_data); - _ld_dtcm_data_flash_copy = LOADADDR(.dtcm_data); - _ld_dtcm_data_size = SIZEOF(.dtcm_data); - - .dtcm_bss : - { - . = ALIGN(4); - - *(.dtcm_bss.*) - - . = ALIGN(4); - } > RAM AT> RAM - _ld_dtcm_bss_start = ADDR(.dtcm_bss); - _ld_dtcm_bss_size = SIZEOF(.dtcm_bss); - - .uninitialized_data (COPY): { - . = ALIGN(4); - *(.uninitialized_data*) - } > 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_FIRMWARE - __scratch_x_source__ = LOADADDR(.scratch_x); - - .scratch_y : { - __scratch_y_start__ = .; - *(.scratch_y.*) - . = ALIGN(4); - __scratch_y_end__ = .; - } > SCRATCH_Y AT > FLASH_FIRMWARE - __scratch_y_source__ = LOADADDR(.scratch_y); - - .bss : { - . = ALIGN(4); - __bss_start__ = .; - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) - *(COMMON) - . = ALIGN(4); - __bss_end__ = .; - } > RAM - - .heap (COPY): - { - __end__ = .; - end = __end__; - *(.heap*) - __HeapLimit = .; - } > RAM - - /* .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 (COPY): - { - *(.stack1*) - } > SCRATCH_X - .stack_dummy (COPY): - { - *(.stack*) - } > SCRATCH_Y - - .flash_end : { - __flash_binary_end = .; - } > FLASH_FIRMWARE - - /* stack limit is poorly named, but historically is maximum heap ptr */ - __StackLimit = ORIGIN(RAM) + LENGTH(RAM); - __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); - __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); - __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); - __StackBottom = __StackTop - SIZEOF(.stack_dummy); - PROVIDE(__stack = __StackTop); - - /* Check if data + heap + stack exceeds RAM limit */ - ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") - - ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") - /* todo assert on extra code */ -} +firmware_size = 1532k; diff --git a/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.mk b/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.mk index a050391505..f36f98b7c5 100644 --- a/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.mk @@ -19,6 +19,8 @@ CIRCUITPY_MDNS = 1 CIRCUITPY_SOCKETPOOL = 1 CIRCUITPY_WIFI = 1 +CIRCUITPY_PICODVI = 1 + CFLAGS += -DCYW43_PIN_WL_HOST_WAKE=24 -DCYW43_PIN_WL_REG_ON=23 -DCYW43_WL_GPIO_COUNT=3 -DCYW43_WL_GPIO_LED_PIN=0 # Must be accompanied by a linker script change CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' diff --git a/ports/raspberrypi/common-hal/picodvi/Framebuffer.c b/ports/raspberrypi/common-hal/picodvi/Framebuffer.c new file mode 100644 index 0000000000..20f447a824 --- /dev/null +++ b/ports/raspberrypi/common-hal/picodvi/Framebuffer.c @@ -0,0 +1,419 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Scott Shawcroft for Adafruit Industries + * + * 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 "bindings/picodvi/Framebuffer.h" + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/time/__init__.h" +#include "common-hal/pwmio/PWMOut.h" +#include "common-hal/rp2pio/StateMachine.h" + +#include "src/common/pico_stdlib/include/pico/stdlib.h" +#include "src/rp2040/hardware_structs/include/hardware/structs/mpu.h" +#include "src/rp2_common/cmsis/stub/CMSIS/Device/RaspberryPi/RP2040/Include/RP2040.h" +#include "src/rp2_common/hardware_pwm/include/hardware/pwm.h" +#include "src/rp2_common/hardware_vreg/include/hardware/vreg.h" +#include "src/rp2_common/pico_multicore/include/pico/multicore.h" + +#include "lib/PicoDVI/software/libdvi/tmds_encode.h" + +picodvi_framebuffer_obj_t *active_picodvi = NULL; + +STATIC PIO pio_instances[2] = {pio0, pio1}; + +static void __not_in_flash_func(core1_main)(void) { + // The MPU is reset before this starts. + + picodvi_framebuffer_obj_t *self = active_picodvi; + dvi_register_irqs_this_core(&self->dvi, DMA_IRQ_1); + + while (queue_is_empty(&self->dvi.q_colour_valid)) { + __wfe(); + } + dvi_start(&self->dvi); + + // Turn off flash access. After this, it will hard fault. Better than messing + // up CIRCUITPY. + MPU->CTRL = MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk; + MPU->RNR = 6; // 7 is used by pico-sdk stack protection. + MPU->RBAR = XIP_MAIN_BASE | MPU_RBAR_VALID_Msk; + MPU->RASR = MPU_RASR_XN_Msk | // Set execute never and everything else is restricted. + MPU_RASR_ENABLE_Msk | + (0x1b << MPU_RASR_SIZE_Pos); // Size is 0x10000000 which masks up to SRAM region. + MPU->RNR = 7; + + uint y = 0; + while (1) { + uint32_t *scanbuf; + queue_remove_blocking_u32(&self->dvi.q_colour_valid, &scanbuf); + + uint32_t *tmdsbuf; + queue_remove_blocking_u32(&self->dvi.q_tmds_free, &tmdsbuf); + // Check to see if the tmds memory has moved and replace this tmdsbuf + // the corresponding on at a new location. + size_t old_fb = tmdsbuf[self->tmdsbuf_size - 1]; + if (old_fb != (uint32_t)self->framebuffer) { + size_t index = ((uint32_t)(tmdsbuf - old_fb)) / self->tmdsbuf_size; + // Check our index and hang if it is out of range. Hang is ok since this is core 1. + // Better than writing the wrong memory that is shared with CP. + while (index >= DVI_N_TMDS_BUFFERS) { + } + tmdsbuf = self->framebuffer + self->framebuffer_len + (self->tmdsbuf_size * index); + tmdsbuf[self->tmdsbuf_size - 1] = (uint32_t)self->framebuffer; + } + uint pixwidth = self->dvi.timing->h_active_pixels; + uint words_per_channel = pixwidth / DVI_SYMBOLS_PER_WORD; + if (self->color_depth == 8) { + tmds_encode_data_channel_8bpp(scanbuf, tmdsbuf + 0 * words_per_channel, pixwidth / 2, DVI_8BPP_BLUE_MSB, DVI_8BPP_BLUE_LSB); + tmds_encode_data_channel_8bpp(scanbuf, tmdsbuf + 1 * words_per_channel, pixwidth / 2, DVI_8BPP_GREEN_MSB, DVI_8BPP_GREEN_LSB); + tmds_encode_data_channel_8bpp(scanbuf, tmdsbuf + 2 * words_per_channel, pixwidth / 2, DVI_8BPP_RED_MSB, DVI_8BPP_RED_LSB); + } else if (self->color_depth == 16) { + tmds_encode_data_channel_16bpp(scanbuf, tmdsbuf + 0 * words_per_channel, pixwidth / 2, DVI_16BPP_BLUE_MSB, DVI_16BPP_BLUE_LSB); + tmds_encode_data_channel_16bpp(scanbuf, tmdsbuf + 1 * words_per_channel, pixwidth / 2, DVI_16BPP_GREEN_MSB, DVI_16BPP_GREEN_LSB); + tmds_encode_data_channel_16bpp(scanbuf, tmdsbuf + 2 * words_per_channel, pixwidth / 2, DVI_16BPP_RED_MSB, DVI_16BPP_RED_LSB); + } else if (self->color_depth == 1) { + tmds_encode_1bpp(scanbuf, tmdsbuf, pixwidth); + } else if (self->color_depth == 2) { + tmds_encode_2bpp(scanbuf, tmdsbuf, pixwidth); + } + queue_add_blocking_u32(&self->dvi.q_tmds_valid, &tmdsbuf); + + queue_add_blocking_u32(&self->dvi.q_colour_free, &scanbuf); + ++y; + if (y == self->dvi.timing->v_active_lines) { + y = 0; + } + } + __builtin_unreachable(); +} + +static void __not_in_flash_func(core1_scanline_callback)(void) { + picodvi_framebuffer_obj_t *self = active_picodvi; + uint32_t *next_scanline_buf; + next_scanline_buf = self->framebuffer + (self->pitch * self->next_scanline); + queue_add_blocking_u32(&self->dvi.q_colour_valid, &next_scanline_buf); + + // Remove any buffers that were sent back to us. + while (queue_try_remove_u32(&self->dvi.q_colour_free, &next_scanline_buf)) { + } + self->next_scanline += 1; + if (self->next_scanline >= self->height) { + self->next_scanline = 0; + // Update the framebuffer pointer in case it moved. + self->framebuffer = self->allocation->ptr; + } +} + +extern uint8_t dvi_vertical_repeat; +extern bool dvi_monochrome_tmds; + +void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self, + mp_uint_t width, mp_uint_t height, + const mcu_pin_obj_t *clk_dp, const mcu_pin_obj_t *clk_dn, + const mcu_pin_obj_t *red_dp, const mcu_pin_obj_t *red_dn, + const mcu_pin_obj_t *green_dp, const mcu_pin_obj_t *green_dn, + const mcu_pin_obj_t *blue_dp, const mcu_pin_obj_t *blue_dn, + mp_uint_t color_depth) { + + const struct dvi_timing *timing = NULL; + if (width == 640 && height == 480) { + timing = &dvi_timing_640x480p_60hz; + } else if (width == 800 && height == 480) { + timing = &dvi_timing_800x480p_60hz; + } else { + if (height == 480) { + mp_raise_ValueError_varg(translate("%q must be %d"), MP_QSTR_width, 480); + } + mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_height); + } + + if (active_picodvi != NULL) { + mp_raise_msg_varg(&mp_type_RuntimeError, translate("%q in use"), MP_QSTR_picodvi); + } + + bool invert_diffpairs = clk_dn->number < clk_dp->number; + int8_t other_pins[4]; + int8_t *a; + int8_t *b; + if (invert_diffpairs) { + a = other_pins; + b = self->pin_pair; + } else { + a = self->pin_pair; + b = other_pins; + } + a[0] = clk_dp->number; + a[1] = red_dp->number; + a[2] = green_dp->number; + a[3] = blue_dp->number; + b[0] = clk_dn->number; + b[1] = red_dn->number; + b[2] = green_dn->number; + b[3] = blue_dn->number; + qstr pin_names[4] = {MP_QSTR_clk_dp, MP_QSTR_red_dp, MP_QSTR_green_dp, MP_QSTR_blue_dp}; + for (size_t i = 0; i < 4; i++) { + if (other_pins[i] - self->pin_pair[i] != 1) { + raise_ValueError_invalid_pin_name(pin_names[i]); + } + } + + uint8_t slice = pwm_gpio_to_slice_num(self->pin_pair[0]); + + + pio_program_t program_struct = { + .instructions = NULL, + .length = 2, + .origin = -1 + }; + size_t pio_index = NUM_PIOS; + int free_state_machines[4]; // We may find all four free. We only use the first three. + for (size_t i = 0; i < NUM_PIOS; i++) { + PIO pio = pio_instances[i]; + uint8_t free_count = 0; + for (size_t sm = 0; sm < NUM_PIO_STATE_MACHINES; sm++) { + if (!pio_sm_is_claimed(pio, sm)) { + free_state_machines[free_count] = sm; + free_count++; + } + } + if (free_count >= 3 && pio_can_add_program(pio, &program_struct)) { + pio_index = i; + break; + } + } + + if (pio_index == NUM_PIOS) { + mp_raise_RuntimeError(translate("All state machines in use")); + } + + self->width = width; + self->height = height; + + size_t tmds_bufs_per_scanline; + if (color_depth >= 8) { + dvi_vertical_repeat = 2; + dvi_monochrome_tmds = false; + self->width /= 2; + self->height /= 2; + tmds_bufs_per_scanline = 3; + } else { + dvi_vertical_repeat = 1; + dvi_monochrome_tmds = true; + // One tmds buffer is used for all three color outputs. + tmds_bufs_per_scanline = 1; + } + self->pitch = (self->width * color_depth) / 8; + // Align each row to words. + if (self->pitch % sizeof(uint32_t) != 0) { + self->pitch += sizeof(uint32_t) - (self->pitch % sizeof(uint32_t)); + } + self->pitch /= sizeof(uint32_t); + size_t framebuffer_size = self->pitch * self->height; + // use width here because it hasn't been downsized for the frame buffer + self->tmdsbuf_size = tmds_bufs_per_scanline * width / DVI_SYMBOLS_PER_WORD + 1; + size_t total_allocation_size = sizeof(uint32_t) * (framebuffer_size + DVI_N_TMDS_BUFFERS * self->tmdsbuf_size); + self->allocation = allocate_memory(total_allocation_size, false, true); + if (self->allocation == NULL) { + m_malloc_fail(total_allocation_size); + return; + } + + // Do the pwmio check last because it claims the pwm slice. + if (!pwmio_claim_slice_ab_channels(slice)) { + mp_raise_ValueError(translate("All timers for this pin are in use")); + } + self->pwm_slice = slice; + + pwmout_never_reset(self->pwm_slice, 0); + pwmout_never_reset(self->pwm_slice, 1); + + for (size_t i = 0; i < 4; i++) { + never_reset_pin_number(self->pin_pair[i]); + never_reset_pin_number(self->pin_pair[i] + 1); + } + + for (size_t i = 0; i < 3; i++) { + rp2pio_statemachine_never_reset(pio_instances[pio_index], free_state_machines[i]); + } + + // For the output. + user_irq_claim(DMA_IRQ_1); + self->framebuffer_len = framebuffer_size; + self->framebuffer = self->allocation->ptr; + self->color_depth = color_depth; + + self->dvi.timing = timing; + self->dvi.ser_cfg.pio = pio_instances[pio_index]; + self->dvi.ser_cfg.sm_tmds[0] = free_state_machines[0]; + self->dvi.ser_cfg.sm_tmds[1] = free_state_machines[1]; + self->dvi.ser_cfg.sm_tmds[2] = free_state_machines[2]; + self->dvi.ser_cfg.pins_clk = self->pin_pair[0]; + self->dvi.ser_cfg.pins_tmds[0] = self->pin_pair[1]; + self->dvi.ser_cfg.pins_tmds[1] = self->pin_pair[2]; + self->dvi.ser_cfg.pins_tmds[2] = self->pin_pair[3]; + self->dvi.ser_cfg.invert_diffpairs = invert_diffpairs; + self->dvi.scanline_callback = core1_scanline_callback; + + vreg_set_voltage(VREG_VOLTAGE_1_20); + common_hal_time_delay_ms(10); + set_sys_clock_khz(timing->bit_clk_khz, true); // Run at TMDS bit clock + self->tmds_lock = next_striped_spin_lock_num(); + self->colour_lock = next_striped_spin_lock_num(); + dvi_init(&self->dvi, self->tmds_lock, self->colour_lock); + + // Load up the TMDS buffers. + for (int i = 0; i < DVI_N_TMDS_BUFFERS; ++i) { + uint32_t *tmdsbuf = self->framebuffer + (self->framebuffer_len + self->tmdsbuf_size * i); + // Use the last word in the buffer to track its original root. That way + // we can detect when framebuffer is moved. + tmdsbuf[self->tmdsbuf_size - 1] = (uint32_t)self->framebuffer; + queue_add_blocking_u32(&self->dvi.q_tmds_free, &tmdsbuf); + } + + active_picodvi = self; + + // Core 1 will wait until it sees the first colour buffer, then start up the + // DVI signalling. + multicore_launch_core1(core1_main); + + self->next_scanline = 0; + uint32_t *next_scanline_buf = self->framebuffer + (self->pitch * self->next_scanline); + queue_add_blocking_u32(&self->dvi.q_colour_valid, &next_scanline_buf); + self->next_scanline += 1; + next_scanline_buf = self->framebuffer + (self->pitch * self->next_scanline); + queue_add_blocking_u32(&self->dvi.q_colour_valid, &next_scanline_buf); + self->next_scanline += 1; + + // Wait for the second core to run dvi_start because it is in flash. Once it is done, + // it'll pull from this queue. Not waiting may lead to us reading flash when this core + // doesn't want us to. + while (queue_get_level(&self->dvi.q_colour_valid) == 2) { + } +} + +STATIC void _turn_off_dma(uint8_t channel) { + dma_channel_config c = dma_channel_get_default_config(channel); + channel_config_set_enable(&c, false); + dma_channel_set_config(channel, &c, false /* trigger */); + + if (dma_channel_is_busy(channel)) { + dma_channel_abort(channel); + } + dma_channel_set_irq1_enabled(channel, false); + dma_channel_unclaim(channel); +} + +void common_hal_picodvi_framebuffer_deinit(picodvi_framebuffer_obj_t *self) { + if (common_hal_picodvi_framebuffer_deinited(self)) { + return; + } + // Stop the other core and free resources. + + // Grab the locks before shutting down the other core so we don't leave the + // locks locked. + spin_lock_t *tmds_lock = spin_lock_instance(self->tmds_lock); + spin_lock_t *colour_lock = spin_lock_instance(self->colour_lock); + uint32_t tmds_save = spin_lock_blocking(tmds_lock); + uint32_t colour_save = spin_lock_blocking(colour_lock); + multicore_reset_core1(); + spin_unlock(colour_lock, colour_save); + spin_unlock(tmds_lock, tmds_save); + + for (size_t i = 0; i < 4; i++) { + reset_pin_number(self->pin_pair[i]); + reset_pin_number(self->pin_pair[i] + 1); + } + + for (int i = 0; i < N_TMDS_LANES; ++i) { + // Turn off data first because it chains to the ctrl DMA. + _turn_off_dma(self->dvi.dma_cfg[i].chan_data); + _turn_off_dma(self->dvi.dma_cfg[i].chan_ctrl); + } + + pwm_set_enabled(self->pwm_slice, false); + pwmout_free(self->pwm_slice, 0); + pwmout_free(self->pwm_slice, 1); + + pio_program_t program_struct = { + .length = 2 + }; + PIO pio = self->dvi.ser_cfg.pio; + for (size_t i = 0; i < 3; i++) { + int sm = self->dvi.ser_cfg.sm_tmds[i]; + pio_sm_set_enabled(pio, sm, false); + pio_sm_unclaim(pio, sm); + rp2pio_statemachine_reset_ok(pio, sm); + } + pio_remove_program(pio, &program_struct, self->dvi.ser_cfg.prog_offs); + + if (user_irq_is_claimed(DMA_IRQ_1)) { + user_irq_unclaim(DMA_IRQ_1); + } + + active_picodvi = NULL; + + free_memory(self->allocation); + self->framebuffer = NULL; + + self->base.type = &mp_type_NoneType; +} + +bool common_hal_picodvi_framebuffer_deinited(picodvi_framebuffer_obj_t *self) { + return self->framebuffer == NULL; +} + +void common_hal_picodvi_framebuffer_refresh(picodvi_framebuffer_obj_t *self) { +} + +int common_hal_picodvi_framebuffer_get_width(picodvi_framebuffer_obj_t *self) { + return self->width; +} + +int common_hal_picodvi_framebuffer_get_height(picodvi_framebuffer_obj_t *self) { + return self->height; +} + +int common_hal_picodvi_framebuffer_get_color_depth(picodvi_framebuffer_obj_t *self) { + return self->color_depth; +} + +mp_int_t common_hal_picodvi_framebuffer_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in; + bufinfo->buf = self->framebuffer; + char typecode = 'B'; + if (self->color_depth == 16) { + typecode = 'H'; + } + bufinfo->typecode = typecode; + bufinfo->len = self->framebuffer_len * sizeof(uint32_t); + return 0; +} + +int common_hal_picodvi_framebuffer_get_row_stride(picodvi_framebuffer_obj_t *self) { + // Pitch is in words but row stride is expected as bytes. + return self->pitch * sizeof(uint32_t); +} diff --git a/ports/raspberrypi/common-hal/picodvi/Framebuffer.h b/ports/raspberrypi/common-hal/picodvi/Framebuffer.h new file mode 100644 index 0000000000..f5d7df8f9a --- /dev/null +++ b/ports/raspberrypi/common-hal/picodvi/Framebuffer.h @@ -0,0 +1,51 @@ +#pragma once + +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Scott Shawcroft for Adafruit Industries + * + * 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 "py/obj.h" + +#include "supervisor/memory.h" + +#include "lib/PicoDVI/software/libdvi/dvi.h" + +typedef struct { + mp_obj_base_t base; + supervisor_allocation *allocation; + uint32_t *framebuffer; + size_t framebuffer_len; // in words + size_t tmdsbuf_size; // in words + struct dvi_inst dvi; + mp_uint_t width; + mp_uint_t height; + uint tmds_lock; + uint colour_lock; + uint16_t next_scanline; + uint16_t pitch; // Number of words between rows. (May be more than a width's worth.) + uint8_t color_depth; + uint8_t pwm_slice; + int8_t pin_pair[4]; +} picodvi_framebuffer_obj_t; diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c index e9cb3d34bb..9034b64242 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c @@ -218,9 +218,7 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, _current_program_len[i][j] == program_len) { program_offset = _current_program_offset[i][j]; } - int temp_claim = pio_claim_unused_sm(pio, false); - if (temp_claim >= 0) { - pio_sm_unclaim(pio, temp_claim); + if (!pio_sm_is_claimed(pio, j)) { free_count++; } } @@ -634,6 +632,16 @@ void common_hal_rp2pio_statemachine_set_frequency(rp2pio_statemachine_obj_t *sel pio_sm_clkdiv_restart(self->pio, self->state_machine); } +void rp2pio_statemachine_reset_ok(PIO pio, int sm) { + uint8_t pio_index = pio_get_index(pio); + _never_reset[pio_index][sm] = false; +} + +void rp2pio_statemachine_never_reset(PIO pio, int sm) { + uint8_t pio_index = pio_get_index(pio); + _never_reset[pio_index][sm] = true; +} + void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins) { common_hal_rp2pio_statemachine_stop(self); (void)common_hal_rp2pio_statemachine_stop_background_write(self); @@ -654,9 +662,7 @@ void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self) { } void common_hal_rp2pio_statemachine_never_reset(rp2pio_statemachine_obj_t *self) { - uint8_t sm = self->state_machine; - uint8_t pio_index = pio_get_index(self->pio); - _never_reset[pio_index][sm] = true; + rp2pio_statemachine_never_reset(self->pio, self->state_machine); // TODO: never reset all the pins } diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.h b/ports/raspberrypi/common-hal/rp2pio/StateMachine.h index 03dadc53b3..71f85b1f11 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.h +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.h @@ -95,6 +95,9 @@ uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self); void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins); void rp2pio_statemachine_dma_complete(rp2pio_statemachine_obj_t *self, int channel); +void rp2pio_statemachine_reset_ok(PIO pio, int sm); +void rp2pio_statemachine_never_reset(PIO pio, int sm); + extern const mp_obj_type_t rp2pio_statemachine_type; #endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_RP2PIO_STATEMACHINE_H diff --git a/ports/raspberrypi/lib/PicoDVI b/ports/raspberrypi/lib/PicoDVI new file mode 160000 index 0000000000..23a3a3bf18 --- /dev/null +++ b/ports/raspberrypi/lib/PicoDVI @@ -0,0 +1 @@ +Subproject commit 23a3a3bf18820f2abd78e8a9c05b45c01b5a3810 diff --git a/ports/raspberrypi/link.ld b/ports/raspberrypi/link.ld index 982c5e3a0c..1e758f61bf 100644 --- a/ports/raspberrypi/link.ld +++ b/ports/raspberrypi/link.ld @@ -21,13 +21,16 @@ __stack (== StackTop) */ +firmware_size = DEFINED(firmware_size) ? firmware_size : 1020K ; + MEMORY { - FLASH_FIRMWARE (rx) : ORIGIN = 0x10000000, LENGTH = 1020k + FLASH_FIRMWARE (rx) : ORIGIN = 0x10000000, LENGTH = firmware_size /* Followed by: 4kB of NVRAM and at least 1024kB of CIRCUITPY */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256k - SCRATCH_X (rwx) : ORIGIN = 0x20040000, LENGTH = 4k - SCRATCH_Y (rwx) : ORIGIN = 0x20041000, LENGTH = 4k + SCRATCH_Y (rwx) : ORIGIN = 0x20040000, LENGTH = 4k + /* X is used by core 1 so we put it last. */ + SCRATCH_X (rwx) : ORIGIN = 0x20041000, LENGTH = 4k } ENTRY(_entry_point) @@ -77,7 +80,7 @@ SECTIONS *(.property_getset) __property_getset_end = .; - *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *interp.o *divider.o) .text*) *(.fini) /* Pull all c'tors into .text */ *crtbegin.o(.ctors) @@ -137,7 +140,7 @@ SECTIONS __data_start__ = .; *(vtable) - *(.time_critical*) + *(EXCLUDE_FILE(*tmds_encode.o) .time_critical*) /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ *(.text*) @@ -222,23 +225,6 @@ SECTIONS *(.uninitialized_data*) } > 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_FIRMWARE - __scratch_x_source__ = LOADADDR(.scratch_x); - - .scratch_y : { - __scratch_y_start__ = .; - *(.scratch_y.*) - . = ALIGN(4); - __scratch_y_end__ = .; - } > SCRATCH_Y AT > FLASH_FIRMWARE - __scratch_y_source__ = LOADADDR(.scratch_y); - .bss : { . = ALIGN(4); __bss_start__ = .; @@ -252,10 +238,30 @@ SECTIONS { __end__ = .; end = __end__; + _ld_cp_dynamic_mem_start = .; *(.heap*) __HeapLimit = .; } > RAM + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + *tmds_encode.o (.time_critical*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH_FIRMWARE + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + /* Don't put anything into scratch y because CircuitPython manages it and uses it for core 0 stack. + /* *(.scratch_y.*) */ + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH_FIRMWARE + __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 @@ -269,6 +275,7 @@ SECTIONS { *(.stack1*) } > SCRATCH_X + .stack_dummy (COPY): { *(.stack*) @@ -282,6 +289,7 @@ SECTIONS __StackLimit = ORIGIN(RAM) + LENGTH(RAM); __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + _ld_cp_dynamic_mem_end = __StackTop; __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); __StackBottom = __StackTop - SIZEOF(.stack_dummy); PROVIDE(__stack = __StackTop); diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index 671d4669bf..ab60207b0d 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -39,7 +39,7 @@ CIRCUITPY_AUDIOBUSIO ?= 1 CIRCUITPY_AUDIOCORE ?= 1 CIRCUITPY_AUDIOPWMIO ?= 1 -CIRCUITPY_AUDIOMIXER = 1 +CIRCUITPY_AUDIOMIXER ?= 1 INTERNAL_LIBM = 1 diff --git a/ports/raspberrypi/sdk_config/pico/config_autogen.h b/ports/raspberrypi/sdk_config/pico/config_autogen.h index ee99b1c609..85a8cccb6f 100644 --- a/ports/raspberrypi/sdk_config/pico/config_autogen.h +++ b/ports/raspberrypi/sdk_config/pico/config_autogen.h @@ -3,6 +3,7 @@ #include "pico-sdk-configboard.h" // alphabetized +#define LIB_CMSIS_CORE (1) #define LIB_PICO_BINARY_INFO (0) #define LIB_PICO_PRINTF_NONE (0) #define LIB_PICO_PRINTF_PICO (0) @@ -23,4 +24,9 @@ #define PICO_STDIO_IGNORE_NESTED_STDOUT (0) #define PICO_USE_CRT_PRINTF (0) #define PICO_USE_OPTIMISTIC_SBRK (0) +// Stack guards cause a hard fault when 32 bytes around the stack bottom are +// accessed. These backtraces aren't always helpful and this conflicts with our +// own stack checking. #define PICO_USE_STACK_GUARDS (0) + +#include "include/cmsis/rename_exceptions.h" diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index f3da71cbd7..2ee3a1ee98 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -228,14 +228,14 @@ bool port_has_fixed_stack(void) { } // From the linker script -extern uint32_t __HeapLimit; -extern uint32_t __StackTop; +extern uint32_t _ld_cp_dynamic_mem_start; +extern uint32_t _ld_cp_dynamic_mem_end; uint32_t *port_stack_get_limit(void) { - return &__HeapLimit; + return &_ld_cp_dynamic_mem_start; } uint32_t *port_stack_get_top(void) { - return &__StackTop; + return &_ld_cp_dynamic_mem_end; } uint32_t *port_heap_get_bottom(void) { @@ -246,13 +246,14 @@ uint32_t *port_heap_get_top(void) { return port_stack_get_top(); } -extern uint32_t __scratch_x_start__; void port_set_saved_word(uint32_t value) { - __scratch_x_start__ = value; + // Store in a watchdog scratch register instead of RAM. 4-7 are used by the + // sdk. 0 is used by alarm. 1-3 are free. + watchdog_hw->scratch[1] = value; } uint32_t port_get_saved_word(void) { - return __scratch_x_start__; + return watchdog_hw->scratch[1]; } static volatile bool ticks_enabled; @@ -305,14 +306,12 @@ void port_idle_until_interrupt(void) { * \brief Default interrupt handler for unused IRQs. */ extern void HardFault_Handler(void); // provide a prototype to avoid a missing-prototypes diagnostic -__attribute__((used)) void HardFault_Handler(void) { - #ifdef ENABLE_MICRO_TRACE_BUFFER - // Turn off the micro trace buffer so we don't fill it up in the infinite - // loop below. - REG_MTB_MASTER = 0x00000000 + 6; - #endif - - reset_into_safe_mode(SAFE_MODE_HARD_FAULT); +__attribute__((used)) void __not_in_flash_func(HardFault_Handler)(void) { + // Only safe mode from core 0 which is running CircuitPython. Core 1 faulting + // should not be fatal to CP. (Fingers crossed.) + if (get_core_num() == 0) { + reset_into_safe_mode(SAFE_MODE_HARD_FAULT); + } while (true) { asm ("nop;"); } diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index fb5d745ebb..d9af56356a 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -278,14 +278,8 @@ endif ifeq ($(CIRCUITPY_PIXELMAP),1) SRC_PATTERNS += _pixelmap/% endif -ifeq ($(CIRCUITPY_QRIO),1) -SRC_PATTERNS += qrio/% -endif -ifeq ($(CIRCUITPY_RAINBOWIO),1) -SRC_PATTERNS += rainbowio/% -endif -ifeq ($(CIRCUITPY_RGBMATRIX),1) -SRC_PATTERNS += rgbmatrix/% +ifeq ($(CIRCUITPY_PICODVI),1) +SRC_PATTERNS += picodvi/% endif ifeq ($(CIRCUITPY_PS2IO),1) SRC_PATTERNS += ps2io/% @@ -296,9 +290,18 @@ endif ifeq ($(CIRCUITPY_PWMIO),1) SRC_PATTERNS += pwmio/% endif +ifeq ($(CIRCUITPY_QRIO),1) +SRC_PATTERNS += qrio/% +endif +ifeq ($(CIRCUITPY_RAINBOWIO),1) +SRC_PATTERNS += rainbowio/% +endif ifeq ($(CIRCUITPY_RANDOM),1) SRC_PATTERNS += random/% endif +ifeq ($(CIRCUITPY_RGBMATRIX),1) +SRC_PATTERNS += rgbmatrix/% +endif ifeq ($(CIRCUITPY_RP2PIO),1) SRC_PATTERNS += rp2pio/% endif diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 0aec2ebe74..639fae9bbc 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -339,6 +339,11 @@ CFLAGS += -DCIRCUITPY_OS=$(CIRCUITPY_OS) CIRCUITPY_PEW ?= 0 CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW) +# CIRCUITPY_PICODVI is handled in the raspberrypi tree. +# Only for RP2 chips. Assume not a raspberrypi build. +CIRCUITPY_PICODVI ?= 0 +CFLAGS += -DCIRCUITPY_PICODVI=$(CIRCUITPY_PICODVI) + CIRCUITPY_PIXELBUF ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_PIXELBUF=$(CIRCUITPY_PIXELBUF) diff --git a/shared-bindings/displayio/Bitmap.c b/shared-bindings/displayio/Bitmap.c index 12aee30b19..c4dc345cb8 100644 --- a/shared-bindings/displayio/Bitmap.c +++ b/shared-bindings/displayio/Bitmap.c @@ -151,15 +151,27 @@ STATIC mp_obj_t bitmap_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t val uint16_t x = 0; uint16_t y = 0; if (mp_obj_is_small_int(index_obj)) { - mp_int_t i = mp_arg_validate_int_min(MP_OBJ_SMALL_INT_VALUE(index_obj), 0, MP_QSTR_index); - uint16_t width = common_hal_displayio_bitmap_get_width(self); - x = i % width; - y = i / width; + mp_int_t i = MP_OBJ_SMALL_INT_VALUE(index_obj); + int total_length = self->width * self->height; + if (i < 0 || i >= total_length) { + mp_raise_IndexError_varg(translate("%q must be %d-%d"), MP_QSTR_index, 0, total_length - 1); + } + + x = i % self->width; + y = i / self->width; } else { mp_obj_t *items; mp_obj_get_array_fixed_n(index_obj, 2, &items); - x = mp_arg_validate_int_range(mp_obj_get_int(items[0]), 0, self->width - 1, MP_QSTR_x); - y = mp_arg_validate_int_range(mp_obj_get_int(items[1]), 0, self->height - 1, MP_QSTR_y); + mp_int_t x_in = mp_obj_get_int(items[0]); + if (x_in < 0 || x_in >= self->width) { + mp_raise_IndexError_varg(translate("%q must be %d-%d"), MP_QSTR_x, 0, self->width - 1); + } + mp_int_t y_in = mp_obj_get_int(items[1]); + if (y_in < 0 || y_in >= self->height) { + mp_raise_IndexError_varg(translate("%q must be %d-%d"), MP_QSTR_y, 0, self->height - 1); + } + x = x_in; + y = y_in; } if (value_obj == MP_OBJ_SENTINEL) { diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index 7064569e2d..112f7f9ad1 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -55,6 +55,13 @@ uint16_t displayio_colorconverter_compute_rgb565(uint32_t color_rgb888) { return r5 << 11 | g6 << 5 | b5; } +uint8_t displayio_colorconverter_compute_rgb332(uint32_t color_rgb888) { + uint32_t r3 = (color_rgb888 >> 21); + uint32_t g3 = (color_rgb888 >> 13) & 0x7; + uint32_t b2 = (color_rgb888 >> 6) & 0x3; + return r3 << 5 | g3 << 2 | b2; +} + uint8_t displayio_colorconverter_compute_rgbd(uint32_t color_rgb888) { uint32_t r1 = (color_rgb888 >> 23) & 0x1; uint32_t g1 = (color_rgb888 >> 15) & 0x1; @@ -309,6 +316,11 @@ void displayio_convert_color(const _displayio_colorspace_t *colorspace, bool dit output_color->pixel = pixel; output_color->opaque = true; return; + } else if (colorspace->depth == 8 && !colorspace->grayscale) { + uint8_t packed = displayio_colorconverter_compute_rgb332(pixel); + output_color->pixel = packed; + output_color->opaque = true; + return; } else if (colorspace->depth == 4) { uint8_t packed; if (colorspace->sevencolor) { diff --git a/shared-module/displayio/ColorConverter.h b/shared-module/displayio/ColorConverter.h index c1e46035ca..7ac5062ff3 100644 --- a/shared-module/displayio/ColorConverter.h +++ b/shared-module/displayio/ColorConverter.h @@ -57,6 +57,7 @@ uint32_t displayio_colorconverter_dither_noise_2(uint32_t x, uint32_t y); void displayio_convert_color(const _displayio_colorspace_t *colorspace, bool dither, const displayio_input_pixel_t *input_pixel, displayio_output_pixel_t *output_color); uint16_t displayio_colorconverter_compute_rgb565(uint32_t color_rgb888); +uint8_t displayio_colorconverter_compute_rgb332(uint32_t color_rgb888); uint8_t displayio_colorconverter_compute_rgbd(uint32_t color_rgb888); uint8_t displayio_colorconverter_compute_luma(uint32_t color_rgb888); uint8_t displayio_colorconverter_compute_chroma(uint32_t color_rgb888); diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 87962dfc60..2dd0cff8b8 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -152,6 +152,10 @@ void common_hal_displayio_release_displays(void) { } else if (displays[i].bus_base.type == &videocore_framebuffer_type) { common_hal_videocore_framebuffer_deinit(&displays[i].videocore); #endif + #if CIRCUITPY_PICODVI + } else if (displays[i].bus_base.type == &picodvi_framebuffer_type) { + common_hal_picodvi_framebuffer_deinit(&displays[i].picodvi); + #endif } displays[i].fourwire_bus.base.type = &mp_type_NoneType; } @@ -266,6 +270,13 @@ void reset_displays(void) { // The framebuffer is allocated outside of the heap so it doesn't // need to be moved. #endif + #if CIRCUITPY_PICODVI + } else if (displays[i].bus_base.type == &picodvi_framebuffer_type) { + picodvi_framebuffer_obj_t *vc = &displays[i].picodvi; + if (!any_display_uses_this_framebuffer(&vc->base)) { + common_hal_picodvi_framebuffer_deinit(vc); + } + #endif } else { // Not an active display bus. continue; diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index c1954b146c..b450cb711d 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -47,9 +47,13 @@ #if CIRCUITPY_SHARPDISPLAY #include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h" #endif +// Port unique frame buffers. #if CIRCUITPY_VIDEOCORE #include "bindings/videocore/Framebuffer.h" #endif +#if CIRCUITPY_PICODVI +#include "bindings/picodvi/Framebuffer.h" +#endif typedef struct { union { @@ -71,6 +75,9 @@ typedef struct { #if CIRCUITPY_VIDEOCORE videocore_framebuffer_obj_t videocore; #endif + #if CIRCUITPY_PICODVI + picodvi_framebuffer_obj_t picodvi; + #endif }; union { mp_obj_base_t display_base; diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c index 26585c54d5..5ab00d4f8a 100644 --- a/supervisor/shared/display.c +++ b/supervisor/shared/display.c @@ -103,37 +103,43 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { if (reset_tiles) { uint8_t *tiles = (uint8_t *)tilegrid_tiles->ptr; + // Adjust the display dimensions to account for scale of the outer group. + width_px /= scale; + height_px /= scale; + + // Number of tiles from the left edge to inset the status bar. + size_t min_left_padding = 0; #if CIRCUITPY_REPL_LOGO - status_bar->x = supervisor_blinka_sprite.pixel_width + 1; + // Blinka + 1 px padding minimum + min_left_padding = supervisor_blinka_sprite.pixel_width + 1; // Align the status bar to the bottom of the logo. status_bar->y = supervisor_blinka_sprite.pixel_height - status_bar->tile_height; #else - status_bar->x = 0; status_bar->y = 0; #endif - status_bar->top_left_y = 0; - status_bar->width_in_tiles = width_in_tiles; + status_bar->width_in_tiles = (width_px - min_left_padding) / status_bar->tile_width; status_bar->height_in_tiles = 1; - status_bar->pixel_width = width_in_tiles * status_bar->tile_width; + status_bar->pixel_width = status_bar->width_in_tiles * status_bar->tile_width; status_bar->pixel_height = status_bar->tile_height; + // Right align the status bar. + status_bar->x = width_px - status_bar->pixel_width; + status_bar->top_left_y = 0; status_bar->tiles = tiles; status_bar->full_change = true; - scroll_area->x = 0; - scroll_area->top_left_y = 0; scroll_area->width_in_tiles = width_in_tiles; - scroll_area->height_in_tiles = height_in_tiles - 1; - scroll_area->pixel_width = width_in_tiles * scroll_area->tile_width; - scroll_area->pixel_height = (height_in_tiles - 1) * scroll_area->tile_height; + scroll_area->height_in_tiles = height_in_tiles; #if CIRCUITPY_REPL_LOGO - scroll_area->y = blinka_bitmap.height; - #else - scroll_area->y = status_bar->tile_height; + scroll_area->height_in_tiles -= 1; #endif - int16_t extra_height = (scroll_area->pixel_height + scroll_area->y) - (height_px / scale); - // Subtract extra height so that the bottom line fully shows. The top line will be under the - // title bar and Blinka logo. - scroll_area->y -= extra_height; + scroll_area->pixel_width = scroll_area->width_in_tiles * scroll_area->tile_width; + scroll_area->pixel_height = scroll_area->height_in_tiles * scroll_area->tile_height; + // Right align the scroll area to give margin to the start of each line. + scroll_area->x = width_px - scroll_area->pixel_width; + scroll_area->top_left_y = 0; + // Align the scroll area to the bottom so that the newest line isn't cutoff. The top line + // may be clipped by the status bar and that's ok. + scroll_area->y = height_px - scroll_area->pixel_height; scroll_area->tiles = tiles + width_in_tiles; scroll_area->full_change = true; diff --git a/supervisor/shared/memory.c b/supervisor/shared/memory.c index a5f3296a28..ec0b27a9a6 100644 --- a/supervisor/shared/memory.c +++ b/supervisor/shared/memory.c @@ -77,6 +77,8 @@ enum { // Maximum needs of one display: max(4 if RGBMATRIX, 1 if SHARPDISPLAY, 0) #if CIRCUITPY_RGBMATRIX 4 + #elif CIRCUITPY_PICODVI + 2 #elif CIRCUITPY_SHARPDISPLAY 1 #else diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 1d600e1158..133a97a99d 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -123,7 +123,7 @@ void usb_set_defaults(void) { }; #if CIRCUITPY_USB_IDENTIFICATION -supervisor_allocation *usb_identification_allocation; +supervisor_allocation *usb_identification_allocation = NULL; #endif // Some dynamic USB data must be saved after boot.py. How much is needed? diff --git a/tools/ci_fetch_deps.py b/tools/ci_fetch_deps.py index b77bea72f9..15ef6a51cd 100644 --- a/tools/ci_fetch_deps.py +++ b/tools/ci_fetch_deps.py @@ -87,7 +87,11 @@ def main(): submodules = ["tools/"] # for huffman elif TARGET == "windows": # This builds one board from a number of ports so fill out a bunch of submodules - submodules = ["extmod/ulab", "lib/", "tools/", "ports/", "data/nvm.toml"] + for port in ("atmel-samd", "nrf", "raspberrypi", "stm"): + submodules.append(f"ports/{port}") + submodules.extend(PORT_DEPS[port]) + unique_submodules = set(submodules) + submodules = list(unique_submodules) elif TARGET == "website": submodules = ["tools/adabot/"] submodules_tags = ["frozen/"] diff --git a/tools/cortex-m-fault-gdb.py b/tools/cortex-m-fault-gdb.py index 31b76aa1d4..2ed2d13909 100644 --- a/tools/cortex-m-fault-gdb.py +++ b/tools/cortex-m-fault-gdb.py @@ -17,7 +17,7 @@ MMFAR = SCB + 0x034 # (R/W) MemManage Fault Address Register */ BFAR = SCB + 0x038 # (R/W) BusFault Address Register */ AFSR = SCB + 0x03C # (R/W) Auxiliary Fault Status Register */ -PARTS = {0xC27: "Cortex M7"} +PARTS = {0xC27: "Cortex M7", 0xC60: "Cortex M0+"} EXCEPTIONS = { 0: "Thread mode", @@ -40,18 +40,35 @@ class CortexMFault(gdb.Command): i = gdb.selected_inferior() return i.read_memory(address, 4).cast("I")[0] - def invoke(self, arg, from_tty): - cpuid = self._read(CPUID) - implementer = cpuid >> 24 - if implementer != 0x41: - raise RuntimeError() - variant = (cpuid >> 20) & 0xF - constant = (cpuid >> 16) & 0xF - if constant != 0xF: - raise RuntimeError() - revision = cpuid & 0xF - part_no = (cpuid >> 4) & 0xFFF - print(PARTS[part_no]) + def _armv6m_fault(self): + vtor = self._read(VTOR) + print("vtor", hex(vtor)) + + icsr = self._read(ICSR) + if (icsr & (1 << 23)) != 0: + print("No preempted exceptions") + else: + print("Another exception was preempted") + vectactive = icsr & 0x1FF + print(hex(icsr), vectactive) + if vectactive != 0: + if vectactive in EXCEPTIONS: + vectactive = EXCEPTIONS[vectactive] + else: + vectactive -= 16 + + print("Active interrupt:", vectactive) + + vectpending = (icsr >> 12) & 0x1FF + if vectpending != 0: + if vectpending in EXCEPTIONS: + vectpending = EXCEPTIONS[vectpending] + else: + vectpending -= 16 + + print("Pending interrupt:", vectpending) + + def _armv7m_fault(self): icsr = self._read(ICSR) if (icsr & (1 << 11)) != 0: print("No preempted exceptions") @@ -65,6 +82,7 @@ class CortexMFault(gdb.Command): print(vectactive - 16) vtor = self._read(VTOR) + print("vtor", hex(vtor)) # print(hex(self._read(SHCSR))) cfsr = self._read(CFSR) ufsr = cfsr >> 16 @@ -102,5 +120,23 @@ class CortexMFault(gdb.Command): print("Bus fault when reading vector table") print("VTOR", hex(vtor)) + def invoke(self, arg, from_tty): + cpuid = self._read(CPUID) + implementer = cpuid >> 24 + if implementer != 0x41: + raise RuntimeError() + variant = (cpuid >> 20) & 0xF + architecture = (cpuid >> 16) & 0xF + revision = cpuid & 0xF + part_no = (cpuid >> 4) & 0xFFF + print(PARTS[part_no]) + + if architecture == 0xF: + self._armv7m_fault() + elif architecture == 0xC: + self._armv6m_fault() + else: + raise RuntimeError(f"Unknown architecture {architecture:x}") + CortexMFault()