From 0ee160e7c06ddc6641257d5ebb88d006e731a3aa Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 18 Dec 2024 14:49:56 +1100 Subject: [PATCH 01/77] extmod/extmod.mk: Add cyw43_spi.c to list of sources. This file is part of the updated cyw43-driver. It will only be used if `CYW43_USE_SPI` is enabled. Signed-off-by: Damien George --- extmod/extmod.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/extmod/extmod.mk b/extmod/extmod.mk index a510f3c54c25c..d2fe2acbad95c 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -458,6 +458,7 @@ SRC_THIRDPARTY_C += $(addprefix $(CYW43_DIR)/src/,\ cyw43_lwip.c \ cyw43_ll.c \ cyw43_sdio.c \ + cyw43_spi.c \ cyw43_stats.c \ ) ifeq ($(MICROPY_PY_BLUETOOTH),1) From a9384c71c50a7bc4aaae1f88ddee5ebacc56ba81 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 28 Feb 2025 23:19:31 +1100 Subject: [PATCH 02/77] extmod/extmod.mk: Switch from drivers/cyw43/cywbt to lib/cyw43-drivers. The cyw43-driver now provides the Bluetooth initialisation code, making `drivers/cyw43/cywbt.c` obsolete. To use the new code a port must enable the `CYW43_ENABLE_BLUETOOTH_OVER_UART` option. Some ports have yet to migrate to the new code, so in the meantime they can explicitly add the old source to their source list and continue to use it without change. Signed-off-by: Damien George --- extmod/extmod.mk | 4 +--- ports/mimxrt/Makefile | 1 + ports/stm32/Makefile | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/extmod.mk b/extmod/extmod.mk index d2fe2acbad95c..859f610c90ac1 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -454,6 +454,7 @@ CYW43_DIR = lib/cyw43-driver GIT_SUBMODULES += $(CYW43_DIR) CFLAGS_EXTMOD += -DMICROPY_PY_NETWORK_CYW43=1 SRC_THIRDPARTY_C += $(addprefix $(CYW43_DIR)/src/,\ + cyw43_bthci_uart.c \ cyw43_ctrl.c \ cyw43_lwip.c \ cyw43_ll.c \ @@ -461,9 +462,6 @@ SRC_THIRDPARTY_C += $(addprefix $(CYW43_DIR)/src/,\ cyw43_spi.c \ cyw43_stats.c \ ) -ifeq ($(MICROPY_PY_BLUETOOTH),1) -DRIVERS_SRC_C += drivers/cyw43/cywbt.c -endif $(BUILD)/$(CYW43_DIR)/src/cyw43_%.o: CFLAGS += -std=c11 endif # MICROPY_PY_NETWORK_CYW43 diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 8106ab4f3ff03..5c54295028083 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -280,6 +280,7 @@ endif ifeq ($(MICROPY_PY_BLUETOOTH),1) SRC_C += mpbthciport.c +DRIVERS_SRC_C += drivers/cyw43/cywbt.c endif # MICROPY_PY_BLUETOOTH ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 8ac9a8af03d1a..c938dcbda7d6d 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -432,6 +432,7 @@ endif ifeq ($(MICROPY_PY_BLUETOOTH),1) SRC_C += mpbthciport.c +DRIVERS_SRC_C += drivers/cyw43/cywbt.c ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) SRC_C += mpnimbleport.c From 7268034d56d353e90992f0d688eb4a35ae86e09a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2025 23:35:07 +1100 Subject: [PATCH 03/77] top: Add "ser" to codespell exclusion list. This word appears in the upcoming alif port. Signed-off-by: Damien George --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8053df24dd797..263b120d3fea4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.codespell] count = "" ignore-regex = '\b[A-Z]{3}\b' -ignore-words-list = "ans,asend,deques,dout,emac,extint,hsi,iput,mis,notin,numer,shft,synopsys,technic,ure,curren" +ignore-words-list = "ans,asend,deques,dout,emac,extint,hsi,iput,mis,notin,numer,ser,shft,synopsys,technic,ure,curren" quiet-level = 3 skip = """ */build*,\ From c5102a7858e9e23aaaa99e70bb0d196bd7e25ba4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Dec 2023 00:11:32 +1100 Subject: [PATCH 04/77] lib/alif_ensemble-cmsis-dfp: Add new submodule for Alif SDK v1.3.2. Signed-off-by: Damien George --- .gitmodules | 3 +++ lib/alif_ensemble-cmsis-dfp | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/alif_ensemble-cmsis-dfp diff --git a/.gitmodules b/.gitmodules index 6338f0e66d63d..7c5a517259269 100644 --- a/.gitmodules +++ b/.gitmodules @@ -68,3 +68,6 @@ [submodule "lib/arduino-lib"] path = lib/arduino-lib url = https://github.com/arduino/arduino-lib-mpy.git +[submodule "lib/alif_ensemble-cmsis-dfp"] + path = lib/alif_ensemble-cmsis-dfp + url = https://github.com/alifsemi/alif_ensemble-cmsis-dfp.git diff --git a/lib/alif_ensemble-cmsis-dfp b/lib/alif_ensemble-cmsis-dfp new file mode 160000 index 0000000000000..04b3176a0031c --- /dev/null +++ b/lib/alif_ensemble-cmsis-dfp @@ -0,0 +1 @@ +Subproject commit 04b3176a0031c945d4306db00663379e9b431e08 From 1356860e2258cf2ecef43a65f40d83ddcbf98d0b Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Mar 2025 16:47:39 +1100 Subject: [PATCH 05/77] lib/alif-security-toolkit: Add new submodule for Alif Security Toolkit. Signed-off-by: Damien George --- .gitmodules | 3 +++ lib/alif-security-toolkit | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/alif-security-toolkit diff --git a/.gitmodules b/.gitmodules index 7c5a517259269..02849ec9bdd11 100644 --- a/.gitmodules +++ b/.gitmodules @@ -71,3 +71,6 @@ [submodule "lib/alif_ensemble-cmsis-dfp"] path = lib/alif_ensemble-cmsis-dfp url = https://github.com/alifsemi/alif_ensemble-cmsis-dfp.git +[submodule "lib/alif-security-toolkit"] + path = lib/alif-security-toolkit + url = https://github.com/micropython/alif-security-toolkit.git diff --git a/lib/alif-security-toolkit b/lib/alif-security-toolkit new file mode 160000 index 0000000000000..63698efe8567e --- /dev/null +++ b/lib/alif-security-toolkit @@ -0,0 +1 @@ +Subproject commit 63698efe8567eed115fe620d5e5de75f460310d7 From 092d85557cc901a2840ac4abce3d12eb520c9765 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Dec 2023 00:11:41 +1100 Subject: [PATCH 06/77] alif/tinyusb_port: Add Alif TinyUSB DCD driver. From https://github.com/alifsemi/alif_vscode-tinyusb.git, commit c79f39361d334ee44f44fed30c56e70dbb368649 Signed-off-by: Damien George Signed-off-by: iabdalkader --- ports/alif/tinyusb_port/alif_dcd_reg.h | 691 ++++++++++++++++++++++++ ports/alif/tinyusb_port/tusb_alif_dcd.c | 688 +++++++++++++++++++++++ 2 files changed, 1379 insertions(+) create mode 100644 ports/alif/tinyusb_port/alif_dcd_reg.h create mode 100644 ports/alif/tinyusb_port/tusb_alif_dcd.c diff --git a/ports/alif/tinyusb_port/alif_dcd_reg.h b/ports/alif/tinyusb_port/alif_dcd_reg.h new file mode 100644 index 0000000000000..84220f6be4ce4 --- /dev/null +++ b/ports/alif/tinyusb_port/alif_dcd_reg.h @@ -0,0 +1,691 @@ +// *FORMAT-OFF* +///------------------------------------------------------------------------------------------------- +/// @file alif_dcd_reg.h +/// @author karol.saja@alifsemi.com +/// @version 0.0.1 +/// @date 2023-09-08 +/// @brief Low Level SPI driver +///------------------------------------------------------------------------------------------------- + +#ifndef __ALIF_DCD_REG_H__ +#define __ALIF_DCD_REG_H__ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +//-------------------------------------------------------------------------------------------------- + +#define _rw volatile uint32_t +#define __w volatile uint32_t +#define __r volatile const uint32_t + +volatile struct { + union { + _rw gsbuscfg0; + struct { + _rw incrbrstena : 1; + _rw incr4brstena : 1; + _rw incr8brstena : 1; + _rw incr16brstena : 1; + _rw incr32brstena : 1; + _rw incr64brstena : 1; + _rw incr128brstena : 1; + _rw incr256brstena : 1; + __r : 2; + _rw desbigend : 1; + _rw datbigend : 1; + __r : 4; + _rw deswrreqinfo : 4; + _rw datwrreqinfo : 4; + _rw desrdreqinfo : 4; + _rw datrdreqinfo : 4; + } gsbuscfg0_b; + }; + + union { + _rw gsbuscfg1; + struct { + __r : 8; + _rw pipetranslimit : 4; + _rw en1kpage : 1; + __r : 19; + } gsbuscfg1_b; + }; + + __r : 32; __r : 32; + + union { + _rw gctl; + struct { + _rw dsblclkgtng : 1; + _rw gblhibernationen : 1; + __r : 1; + _rw disscramble : 1; + _rw scaledown : 2; + _rw ramclksel : 2; + __r : 2; + _rw sofitpsync : 1; + _rw coresoftreset : 1; + _rw prtcapdir : 2; + _rw frmscldwn : 2; + __r : 1; + _rw bypssetaddr : 1; + __r : 14; + } gctl_b; + }; + + __r : 32; + + union { + _rw gsts; + struct { + __r curmod : 2; + __r : 2; + _rw buserraddrvld : 1; + _rw csrtimeout : 1; + __r device_ip : 1; + __r host_ip : 1; + __r adp_ip : 1; + __r bc_ip : 1; + __r otg_ip : 1; + __r ssic_ip : 1; + __r : 8; + __r cbelt : 12; + } gsts_b; + }; + + union { + _rw guctl1; + struct { + _rw loa_filter_en : 1; + _rw ovrld_l1_susp_com : 1; + _rw hc_parchk_disable : 1; + _rw hc_errata_enable : 1; + _rw l1_susp_thrld_for_host : 4; + _rw l1_susp_thrld_en_for_host : 1; + _rw dev_hs_nyet_bulk_spr : 1; + _rw resume_opmode_hs_host : 1; + __r : 1; + _rw disusb2refclkgtng : 1; + __r : 2; + _rw parkmode_disable_fsls : 1; + _rw parkmode_disable_hs : 1; + __r : 1; + _rw nak_per_enh_hs : 1; + _rw nak_per_enh_fs : 1; + _rw dev_lsp_tail_lock_dis : 1; + _rw ip_gap_add_on : 2; + _rw dev_l1_exit_by_hw : 1; + __r : 2; + _rw dev_trb_out_spr_ind : 1; + _rw tx_ipgap_linecheck_dis : 1; + _rw filter_se0_fsls_eop : 1; + __r : 1; + _rw dev_decouple_l1l2_evt : 1; + } guctl1_b; + }; + + union { + _rw gsnpsid; + }; + + __r : 32; + + union { + _rw guid; + struct { + _rw userid : 32; + } guid_b; + }; + + union { // <- host only, leaving unimplemented for now [FIXME] + _rw guctl; + struct { + } guctl_b; + }; + + union { + _rw gbuserraddrlo; + struct { + _rw buserraddr : 32; + } gbuserraddrlo_b; + }; + + union { + _rw gbuserraddrhi; + struct { + _rw buserraddr : 32; + } gbuserraddrhi_b; + }; + + __r : 32; __r : 32; + + __r ghwparams[8]; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; + + // base + 0x9C here + + union { + _rw guctl2; + struct { + __r : 11; + _rw disablecfc : 1; + _rw enableepcacheevict : 1; + __r : 1; + _rw rst_actbitlater : 1; + __r : 4; + _rw en_hp_pm_timer : 7; + __r : 6; + } guctl2_b; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x100 here + + union { + _rw gusb2phycfg0; + struct { + _rw toutcal : 3; + _rw phyif : 1; + __r ulpi_utmi_sel : 1; + __r fsintf : 1; + _rw suspendusb20 : 1; + _rw physel : 1; + _rw enblslpm : 1; + _rw xcvrdly : 1; + _rw usbtrdtim : 4; + __r : 5; + _rw lsipd : 3; + _rw lstrd : 3; + _rw ovrd_fsls_disc_time : 1; + __r inv_sel_hsic : 1; + __r hsic_con_width_adj : 2; + _rw ulpi_lpm_with_opmode_chk : 1; + _rw u2_freeclk_exists : 1; + _rw physoftrst : 1; + } gusb2phycfg0_b; + }; + + __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x200 here + + union { + _rw gtxfifosiz[4]; + struct { + _rw txfdep : 16; + _rw txfstaddr : 16; + } gtxfifosiz_b[4]; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x280 here + + union { + _rw grxfifosiz[4]; + struct { + _rw rxfdep : 16; + _rw rxfstaddr : 16; + } grxfifosiz_b[4]; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x300 here + + union { + _rw gevntadrlo0; + struct { + _rw evntadrlo : 32; + } gevntadrlo0_b; + }; + + union { + _rw gevntadrhi0; + struct { + _rw evntadrhi : 32; + } gevntadrhi0_b; + }; + + union { + _rw gevntsiz0; + struct { + _rw eventsiz : 16; + __r : 15; + _rw evntintrptmask : 1; + } gevntsiz0_b; + }; + + union { + _rw gevntcount0; + struct { + _rw evntcount : 16; + __r : 15; + _rw evnt_handler_busy : 1; + } gevntcount0_b; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x500 here + + __r ghwparams8; + + __r : 32; __r : 32; __r : 32; + + // base + 0x510 here + + union { + _rw gtxfifopridev; + struct { + _rw gtxfifopridev : 4; + __r : 28; + } gtxfifopridev_b; + }; + + __r : 32; + + union { + _rw gtxfifoprihst; + struct { + _rw gtxfifoprihst : 2; + __r : 30; + } gtxfifoprihst_b; + }; + + union { + _rw grxfifoprihst; + struct { + _rw grxfifoprihst : 2; + __r : 30; + } grxfifoprihst_b; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x530 here + + union { + _rw gfladj; + struct { + _rw gfladj_30mhz : 6; + __r : 1; + _rw gfladj_30mhz_sdbnd_sel : 1; + _rw gfladj_refclk_fladj : 14; + __r : 1; + _rw gfladj_refclk_lpm_sel : 1; + _rw gfladj_refclk_240mhz_decr : 7; + _rw gfladj_refclk_240mhzdecr_pls1 : 1; + } gfladj_b; + }; + + __r : 32; __r : 32; __r : 32; + + // base + 0x540 here + + union { + _rw gusb2rhbctl0; + struct { + _rw ovrd_l1timeout : 4; + __r : 28; + } gusb2rhbctl0_b; + }; + +} *ugbl = (void *) (USB_BASE + 0xC100); + +volatile struct { + union { + _rw dcfg; + struct { + _rw devspd : 3; + _rw devaddr : 7; + __r : 2; + _rw intrnum : 5; + _rw nump : 5; + _rw lpmcap : 1; + _rw ignstrmpp : 1; + __r : 8; + } dcfg_b; + }; + + union { + _rw dctl; + struct { + __r : 1; + _rw tstctl : 4; + __w ulstchngreq : 4; + __r : 7; + _rw css : 1; + _rw crs : 1; + _rw l1hibernationen : 1; + _rw keepconnect : 1; + _rw lpm_nyet_thres : 4; + _rw hirdthres : 5; + __r : 1; + _rw csftrst : 1; + _rw run_stop : 1; + } dctl_b; + }; + + union { + _rw devten; + struct { + _rw dissconnevten : 1; + _rw usbrstevten : 1; + _rw connectdoneevten : 1; + _rw ulstcngen : 1; + _rw wkupevten : 1; + _rw hibernationreqevten : 1; + _rw u3l2l1suspen : 1; + _rw softevten : 1; + _rw l1suspen : 1; + _rw errticerrevten : 1; + __r : 2; + _rw vendevtstrcvden : 1; + __r : 1; + _rw l1wkupevten : 1; + __r : 1; + _rw eccerren : 1; + __r : 15; + } devten_b; + }; + + union { + _rw dsts; + struct { + __r connectspd : 3; + __r soffn : 14; + __r rxfifoempty : 1; + __r usblnkst : 4; + __r devctrlhlt : 1; + __r coreidle : 1; + __r sss : 1; + __r rss : 1; + __r : 2; + _rw sre : 1; + __r dcnrd : 1; + __r : 2; + } dsts_b; + }; + + union { + _rw dgcmdpar; + struct { + _rw parameter : 32; + } dgcmdpar_b; + }; + + union { + _rw dgcmd; + struct { + _rw cmdtyp : 8; + _rw cmdioc : 1; + __r : 1; + _rw cmdact : 1; + __r : 1; + __r cmdstatus : 4; + __r : 16; + } dgcmd_b; + }; + + __r : 32; __r : 32; + + union { + _rw dalepena; + struct { + _rw usbactep : 32; + } dalepena_b; + }; + + __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x100 here + + struct { + union { + _rw par2; + struct { + _rw parameter : 32; + } par2_b; + }; + + union { + _rw par1; + struct { + _rw parameter : 32; + } par1_b; + }; + + union { + _rw par0; + struct { + _rw parameter : 32; + } par0_b; + }; + + union { + _rw depcmd; + struct { + _rw cmdtyp : 4; + __r : 4; + _rw cmdioc : 1; + __r : 1; + _rw cmdact : 1; + _rw hipri_forcerm : 1; + _rw cmdstatus : 4; + _rw commandparam : 16; + } depcmd_b; + }; + } depcmd[8]; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x300 here + + union { + _rw dev_imod0; + struct { + _rw device_imodi : 16; + _rw device_imocd : 16; + } dev_imod0_b; + }; +} *udev = (void *) (USB_BASE + 0xC700); + +//-------------------------------------------------------------------------------------------------- + +typedef union { + _rw val : 32; + struct { + _rw sig : 8; + _rw evt : 5; + __r : 3; + _rw info : 9; + __r : 7; + } devt; + struct { + _rw sig : 1; + _rw ep : 5; + _rw evt : 4; + __r : 2; + _rw sts : 4; + _rw par : 16; + } depevt; +} evt_t; + +//-------------------------------------------------------------------------------------------------- + +enum { + CMDTYP_RESERVED = 0, + CMDTYP_DEPCFG, + CMDTYP_DEPXFERCFG, + CMDTYP_DEPGETSTATE, + CMDTYP_DEPSSTALL, + CMDTYP_DEPCSTALL, + CMDTYP_DEPSTRTXFER, + CMDTYP_DEPUPDXFER, + CMDTYP_DEPENDXFER, + CMDTYP_DEPSTARTCFG +} DEPCMD_CMDTYP; + +enum { + DEVT_DISCONNEVT = 0, + DEVT_USBRST, + DEVT_CONNECTDONE, + DEVT_ULSTCHNG, + DEVT_WKUPEVT, + // DEVT_HIBRQ not implemented + DEVT_USBSUSP = 6, + DEVT_SOF, + DEVT_L1SUSP, + DEVT_ERRTICERR, + DEVT_CMDCMPLT, + DEVT_EVNTOVERFLOW, + DEVT_VNDDEVTSTRCVED, + // reserved + DEVT_L1RESM = 14, + // reserved + DEVT_ECCERR = 16 +} DEVT; + +enum { + // reserved + DEPEVT_XFERCOMPLETE = 1, + DEPEVT_XFERINPROGRESS, + DEPEVT_XFERNOTREADY, + // not implemented + DEPEVT_STREAMEVT = 6, + DEPEVT_EPCMDCMPLT +} DEPEVT; + +enum { + // reserved + TRBCTL_NORMAL = 1, + TRBCTL_CTL_SETUP, + TRBCTL_CTL_STAT2, + TRBCTL_CTL_STAT3, + TRBCTL_CTL_DATA, + TRBCTL_ISO_FIRST, + TRBCTL_ISO, + TRBCTL_LINK, + TRBCTL_NORMAL_ZLP +} TRBCTL; + +//-------------------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------------------- + +#ifdef __cplusplus +} +#endif // __cplusplus + + +#endif // __ALIF_DCD_REG_H__ diff --git a/ports/alif/tinyusb_port/tusb_alif_dcd.c b/ports/alif/tinyusb_port/tusb_alif_dcd.c new file mode 100644 index 0000000000000..510f0e9c13a9b --- /dev/null +++ b/ports/alif/tinyusb_port/tusb_alif_dcd.c @@ -0,0 +1,688 @@ +// *FORMAT-OFF* +/* Copyright (C) 2024 Alif Semiconductor - All Rights Reserved. + * Use, distribution and modification of this code is permitted under the + * terms stated in the Alif Semiconductor Software License Agreement + * + * You should have received a copy of the Alif Semiconductor Software + * License Agreement with this file. If not, please write to: + * contact@alifsemi.com, or visit: https://alifsemi.com/license + * + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED + +#if defined(CORE_M55_HE) +#include "M55_HE.h" +#elif defined(CORE_M55_HP) +#include "M55_HP.h" +#else +#error "Unsupported core!" +#endif + +#include "device/dcd.h" + +#include "alif_dcd_reg.h" + +#include "clk.h" +#include "power.h" + +// #define TUSB_ALIF_DEBUG +// #define TUSB_ALIF_DEBUG_DEPTH (2048) + +#if defined(TUSB_ALIF_DEBUG) +#if (1 < TUSB_ALIF_DEBUG_DEPTH) +#define LOG(...) memset(logbuf[bi % TUSB_ALIF_DEBUG_DEPTH], ' ', 48);\ + snprintf(logbuf[(bi++) % TUSB_ALIF_DEBUG_DEPTH], 48, __VA_ARGS__); +char logbuf[TUSB_ALIF_DEBUG_DEPTH][48]; +int bi = 0; +#else +#define LOG(...) memset(logbuf, ' ', 48);\ + snprintf(logbuf, 48, __VA_ARGS__) +char logbuf[48]; +#endif +#else +#define LOG(...) +#endif + +/// Structs and Buffers -------------------------------------------------------- + +static uint32_t _evnt_buf[1024] CFG_TUSB_MEM_SECTION __attribute__((aligned(4096))); // [TODO] runtime alloc +static volatile uint32_t* _evnt_tail; + +static uint8_t _ctrl_buf[64] CFG_TUSB_MEM_SECTION __attribute__((aligned(32))); // [TODO] runtime alloc +static uint32_t _xfer_trb[8][4] CFG_TUSB_MEM_SECTION __attribute__((aligned(32))); // [TODO] runtime alloc +static uint16_t _xfer_bytes[8]; +static bool _ctrl_long_data = false; +static bool _xfer_cfgd = false; +// static bool _addr_req = false; +static uint32_t _sts_stage = 0; + +/// Private Functions ---------------------------------------------------------- + +static uint8_t _dcd_cmd_wait(uint8_t ep, uint8_t typ, uint16_t param); +static uint8_t _dcd_start_xfer(uint8_t ep, void* buf, uint32_t size, uint8_t type); + +static void _dcd_handle_depevt(uint8_t ep, uint8_t evt, uint8_t sts, uint16_t par); +static void _dcd_handle_devt(uint8_t evt, uint16_t info); + +/// API Extension -------------------------------------------------------------- + +void dcd_uninit(void); + +/// Device Setup --------------------------------------------------------------- + +// Initializes the USB peripheral for device mode and enables it. +// This function should enable internal D+/D- pull-up for enumeration. +void dcd_init(uint8_t rhport) +{ + // enable 20mhz clock + enable_cgu_clk20m(); + // enable usb peripheral clock + enable_usb_periph_clk(); + // power up usb phy + enable_usb_phy_power(); + // disable usb phy isolation + disable_usb_phy_isolation(); + // clear usb phy power-on-reset signal + CLKCTL_PER_MST->USB_CTRL2 &= ~(1 << 8); + + // force stop/disconnect + udev->dctl_b.run_stop = 0; + + // dctl set csftrst to 1 and wait for 0 + udev->dctl_b.csftrst = 1; + while(0 != udev->dctl_b.csftrst); + + sys_busy_loop_us(50000); + ugbl->gctl_b.coresoftreset = 1; + ugbl->gusb2phycfg0_b.physoftrst = 1; + sys_busy_loop_us(50000); + ugbl->gusb2phycfg0_b.physoftrst = 0; + sys_busy_loop_us(50000); + ugbl->gctl_b.coresoftreset = 0; + sys_busy_loop_us(50000); + + ugbl->gsbuscfg0 = 0x00000009; + // ugbl->gusb2phycfg0 = 0x4000154F; // [TODO] document as bits + uint32_t reg = ugbl->gusb2phycfg0; + reg &= ~((1 << 3) | (1 << 4) | (0xF << 10)); // clear phyif, ulpi_utmi_sel and usbtrdtim + reg |= ((1 << 3) | (5 << 10)); + ugbl->gusb2phycfg0 = reg; + + // set device speed (USBHS only) + udev->dcfg_b.devspd = 0x0; // HS, this will need #if condition [TODO] + + // allocate ring buffer for events + memset(_evnt_buf, 0, sizeof(_evnt_buf)); + RTSS_CleanDCache_by_Addr(_evnt_buf, sizeof(_evnt_buf)); + _evnt_tail = _evnt_buf; + ugbl->gevntadrlo0 = (uint32_t) LocalToGlobal(_evnt_buf); + ugbl->gevntsiz0_b.eventsiz = (uint32_t) sizeof(_evnt_buf); + ugbl->gevntcount0_b.evntcount = 0; + + // write devten to enable usb reset, conn done, link state change... + udev->devten_b.dissconnevten = 1; + udev->devten_b.usbrstevten = 1; + udev->devten_b.connectdoneevten = 1; + udev->devten_b.ulstcngen = 1; + + // begin endpoint setup + _dcd_cmd_wait(0, CMDTYP_DEPSTARTCFG, 0); + + // configure CONTROL IN and OUT eps + udev->depcmd[0].par1 = (0 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[0].par0 = (0 << 22) | (0 << 17) | (512 << 3) | (0 << 1); + _dcd_cmd_wait(0, CMDTYP_DEPCFG, 0); + + udev->depcmd[1].par1 = (1 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[1].par0 = (0 << 22) | (0 << 17) | (512 << 3) | (0 << 1); + _dcd_cmd_wait(1, CMDTYP_DEPCFG, 0); + + // set initial xfer configuration for CONTROL eps + udev->depcmd[0].par0 = 1; + _dcd_cmd_wait(0, CMDTYP_DEPXFERCFG, 0); + + udev->depcmd[1].par0 = 1; + _dcd_cmd_wait(1, CMDTYP_DEPXFERCFG, 0); + + // enable pull-ups + dcd_connect(rhport); + + // prepare trb for the first setup packet + memset(_ctrl_buf, 0, sizeof(_ctrl_buf)); + _xfer_trb[0][0] = (uint32_t) LocalToGlobal(_ctrl_buf); + _xfer_trb[0][1] = 0; + _xfer_trb[0][2] = 8; + _xfer_trb[0][3] = (1 << 11) | (1 << 10) | (TRBCTL_CTL_SETUP << 4) | (1 << 1) | (1 << 0); + RTSS_CleanDCache_by_Addr(_xfer_trb[0], sizeof(_xfer_trb[0])); + + // send trb to the usb dma + udev->depcmd[0].par1 = (uint32_t) LocalToGlobal(_xfer_trb[0]); + udev->depcmd[0].par0 = 0; + _dcd_cmd_wait(0, CMDTYP_DEPSTRTXFER, 0); + + // enable ep event interrupts for CONTROL OUT and IN + udev->dalepena_b.usbactep = (1 << 1) | (1 << 0); + + // enable interrupts in the NVIC +#if !defined(TUSB_ALIF_NO_IRQ_CFG) + NVIC_ClearPendingIRQ(USB_IRQ_IRQn); + NVIC_SetPriority(USB_IRQ_IRQn, 5); +#endif + dcd_int_enable(rhport); +} + + +// Processes all the hardware generated events e.g bus reset, new data packet +// from host... It will be called by application in the MCU USB interrupt handler. +void dcd_int_handler(uint8_t rhport) +{ + LOG("%010u IRQ enter, evntcount %u", DWT->CYCCNT, ugbl->gevntcount0_b.evntcount); + + // process failures first + if (ugbl->gsts_b.device_ip) { + if (ugbl->gsts_b.csrtimeout || ugbl->gsts_b.buserraddrvld) { + // buserraddrvld is usually set when USB tries to write to protected + // memory region. Check linker script to ensure USB event buffer and + // TRBs reside in bulk memory or other NS-allowed region + __BKPT(0); + } + } + + // cycle through event queue + // evaluate on every iteration to prevent unnecessary isr exit/reentry + while (0 < ugbl->gevntcount0_b.evntcount) { + RTSS_InvalidateDCache_by_Addr(_evnt_buf, sizeof(_evnt_buf)); + volatile evt_t e = {.val = *_evnt_tail++}; + + LOG("%010u IRQ loop, evntcount %u evnt %08x", DWT->CYCCNT, + ugbl->gevntcount0_b.evntcount, e.val); + + // wrap around + if (_evnt_tail >= (_evnt_buf + 1024)) _evnt_tail = _evnt_buf; + + // dispatch the right handler for the event type + if (0 == e.depevt.sig) { // DEPEVT + _dcd_handle_depevt(e.depevt.ep, e.depevt.evt, e.depevt.sts, e.depevt.par); + } else if (1 == e.devt.sig) { // DEVT + _dcd_handle_devt(e.devt.evt, e.devt.info); + } else { + // bad event?? + LOG("Unknown event %u", e.val); + __BKPT(0); + } + + // consume one event + ugbl->gevntcount0 = 4; + } + + LOG("%010u IRQ exit, evntcount %u", DWT->CYCCNT, ugbl->gevntcount0_b.evntcount); +} + + +// Enables the USB device interrupt. +// May be used to prevent concurrency issues when mutating data structures +// shared between main code and the interrupt handler. +void dcd_int_enable (uint8_t rhport) +{ + NVIC_EnableIRQ(USB_IRQ_IRQn); + + (void) rhport; +} + +// Disables the USB device interrupt. +// May be used to prevent concurrency issues when mutating data structures +// shared between main code and the interrupt handler. +void dcd_int_disable(uint8_t rhport) +{ + NVIC_DisableIRQ(USB_IRQ_IRQn); + + (void) rhport; +} + +// Receive Set Address request, mcu port must also include status IN response. +// If your peripheral automatically changes address during enumeration you may +// leave this empty and also no queue an event for the corresponding SETUP packet. +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); + + udev->dcfg_b.devaddr = dev_addr; + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); +} + +// Called to remote wake up host when suspended (e.g hid keyboard) +void dcd_remote_wakeup(uint8_t rhport) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); +} + +// Connect by enabling internal pull-up resistor on D+/D- +void dcd_connect(uint8_t rhport) +{ + udev->dctl_b.run_stop = 1; + + (void) rhport; +} + +// Disconnect by disabling internal pull-up resistor on D+/D- +void dcd_disconnect(uint8_t rhport) +{ + // [TODO] clear all xfers and eps first + + udev->dctl_b.run_stop = 0; + + (void) rhport; +} + +// Enable/Disable Start-of-frame interrupt. Default is disabled +void dcd_sof_enable(uint8_t rhport, bool en) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); +} + + +/// Endpoint Management -------------------------------------------------------- + +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); + + _ctrl_long_data = false; + _dcd_start_xfer(TUSB_DIR_OUT, _ctrl_buf, 8, TRBCTL_CTL_SETUP); +} + +// Opening an endpoint is done for all non-control endpoints once the host picks +// a configuration that the device should use. +// At this point, the endpoint should be enabled in the peripheral and +// configured to match the endpoint descriptor. +// Pay special attention to the direction of the endpoint you can get from the +// helper methods above. It will likely change what registers you are setting. +// Also make sure to enable endpoint specific interrupts. +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) +{ + LOG("%010u >%s %u %s %u %u", DWT->CYCCNT, __func__, desc_ep->bEndpointAddress, + desc_ep->bmAttributes.xfer == TUSB_XFER_BULK ? "bulk" : "int", + desc_ep->wMaxPacketSize, desc_ep->bInterval); + + if (TUSB_XFER_ISOCHRONOUS == desc_ep->bmAttributes.xfer) + return false; + + uint8_t ep = (tu_edpt_number(desc_ep->bEndpointAddress) << 1) | + tu_edpt_dir(desc_ep->bEndpointAddress); + + // [TODO] verify that the num doesn't exceed hw max + + if (false == _xfer_cfgd) { + _dcd_cmd_wait(0, CMDTYP_DEPSTARTCFG, 2); + _xfer_cfgd = true; + } + + uint8_t fifo_num = TUSB_DIR_IN == tu_edpt_dir(desc_ep->bEndpointAddress) ? + tu_edpt_number(desc_ep->bEndpointAddress) : 0; + uint8_t interval = 0 < desc_ep->bInterval ? (desc_ep->bInterval - 1) : 0; + + udev->depcmd[ep].par1 = (ep << 25) | (interval << 16) | + (1 << 10) | (1 << 8); + udev->depcmd[ep].par0 = (0 << 30) | (0 << 22) | (fifo_num << 17) | + ((desc_ep->wMaxPacketSize & 0x7FF) << 3) | + (desc_ep->bmAttributes.xfer << 1); + + _dcd_cmd_wait(ep, CMDTYP_DEPCFG, 0); + udev->depcmd[ep].par0 = 1; + _dcd_cmd_wait(ep, CMDTYP_DEPXFERCFG, 0); + + udev->dalepena_b.usbactep |= (1 << ep); + + return true; +} + +// Close all non-control endpoints, cancel all pending transfers if any. +// Invoked when switching from a non-zero Configuration by SET_CONFIGURE therefore +// required for multiple configuration support. +void dcd_edpt_close_all(uint8_t rhport) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); +} + +// Close an endpoint. his function is used for implementing alternate settings. +// After calling this, the device should not respond to any packets directed +// towards this endpoint. When called, this function must abort any transfers in +// progress through this endpoint, before returning. +// Implementation is optional. Must be called from the USB task. +// Interrupts could be disabled or enabled during the call. +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK; + +// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to +// notify the stack +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + // DEPSTRTXFER command + LOG("%010u >%s %u %x %u", DWT->CYCCNT, __func__, ep_addr, (uint32_t) buffer, total_bytes); + + uint8_t ep = (tu_edpt_number(ep_addr) << 1) | tu_edpt_dir(ep_addr); + + switch (ep) { + case 0: { // CONTROL OUT + if (0 < total_bytes) { + // DATA OUT request + _xfer_bytes[0] = total_bytes; + _dcd_start_xfer(0, buffer, total_bytes, TRBCTL_CTL_STAT3); + } else { + // TinyUSB explicitly requests STATUS OUT fetch after DATA IN + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_OUT), + 0, XFER_RESULT_SUCCESS, true); + } + } + } break; + case 1: { // CONTROL IN + _xfer_bytes[1] = total_bytes; + + if (0 < total_bytes) { + RTSS_CleanDCache_by_Addr(buffer, total_bytes); + uint8_t type = _ctrl_long_data ? TRBCTL_NORMAL : TRBCTL_CTL_DATA; + if (64 == total_bytes) { + _ctrl_long_data = true; + } + _dcd_start_xfer(1, buffer, total_bytes, type); + } else { + // status events are handled directly from the ISR when USB + // controller triggers XferNotReady event for status stage + + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_IN), + 0, XFER_RESULT_SUCCESS, true); + } + // dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN), + // 0, XFER_RESULT_SUCCESS, false); + } + } break; + default: { // DATA EPs (BULK & INTERRUPT only) + _xfer_bytes[ep] = total_bytes; + if (TUSB_DIR_IN == tu_edpt_dir(ep_addr)) { + RTSS_CleanDCache_by_Addr(buffer, total_bytes); + } else { + total_bytes = 512; // temporary hack, controller requires max + // size requests on OUT endpoints [FIXME] + } + uint8_t ret = _dcd_start_xfer(ep, buffer, total_bytes, + total_bytes ? TRBCTL_NORMAL : TRBCTL_NORMAL_ZLP); + LOG("start xfer sts %u", ret); + } + } + + return true; +} + +// Submit a transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack +// This API is optional, may be useful for register-based for transferring data. +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK; + +// Stall endpoint, any queuing transfer should be removed from endpoint +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + // DEPSSTALL command + LOG(">%s", __func__); + + uint8_t ep = (tu_edpt_number(ep_addr) << 1) | tu_edpt_dir(ep_addr); + + _dcd_cmd_wait(ep, CMDTYP_DEPSSTALL, 0); + + if (0 == tu_edpt_number(ep_addr)) { + _ctrl_long_data = false; + _dcd_start_xfer(TUSB_DIR_OUT, _ctrl_buf, 8, TRBCTL_CTL_SETUP); + } +} + +// clear stall, data toggle is also reset to DATA0 +// This API never calls with control endpoints, since it is auto cleared when +// receiving setup packet +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + // DEPCSTALL command + LOG(">%s", __func__); + + uint8_t ep = (tu_edpt_number(ep_addr) << 1) | tu_edpt_dir(ep_addr); + + _dcd_cmd_wait(ep, CMDTYP_DEPCSTALL, 0); +} + +void dcd_uninit(void) +{ + CLKCTL_PER_MST->USB_CTRL2 |= 1 << 8; // set usb phy power-on-reset signal + enable_usb_phy_isolation(); // enable usb phy isolation + disable_usb_phy_power(); // power down usb phy + disable_usb_periph_clk(); // disable usb peripheral clock +} + +void USB_IRQHandler(void) +{ + dcd_int_handler(TUD_OPT_RHPORT); +} + + +static uint8_t _dcd_cmd_wait(uint8_t ep, uint8_t typ, uint16_t param) +{ + // capture phy state and disable lpm and suspend + uint32_t phycfg = ugbl->gusb2phycfg0; + ugbl->gusb2phycfg0_b.enblslpm = 0; + ugbl->gusb2phycfg0_b.suspendusb20 = 0; + + // set up command in depcmd register + udev->depcmd[ep].depcmd_b.cmdtyp = typ; + udev->depcmd[ep].depcmd_b.cmdioc = 0; + udev->depcmd[ep].depcmd_b.commandparam = param; + + // dispatch command and wait for completion + udev->depcmd[ep].depcmd_b.cmdact = 1; + while(0 != udev->depcmd[ep].depcmd_b.cmdact); + + // restore phy state + ugbl->gusb2phycfg0 = phycfg; + + return udev->depcmd[ep].depcmd_b.cmdstatus; +} + + +static void _dcd_handle_depevt(uint8_t ep, uint8_t evt, uint8_t sts, uint16_t par) +{ + LOG("%010u DEPEVT ep%u evt%u sts%u par%u", DWT->CYCCNT, ep, evt, sts, par); + + switch (evt) { + case DEPEVT_XFERCOMPLETE: { + LOG("Transfer complete"); + RTSS_InvalidateDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[0])); + if (0 == ep) { + uint8_t trbctl = (_xfer_trb[0][3] >> 4) & 0x3F; + LOG("ep0 xfer trb3 = %08x", _xfer_trb[0][3]); + if (TRBCTL_CTL_SETUP == trbctl) { + RTSS_InvalidateDCache_by_Addr(_ctrl_buf, sizeof(_ctrl_buf)); + LOG("%02x %02x %02x %02x %02x %02x %02x %02x", + _ctrl_buf[0], _ctrl_buf[1], _ctrl_buf[2], _ctrl_buf[3], + _ctrl_buf[4], _ctrl_buf[5], _ctrl_buf[6], _ctrl_buf[7]); + dcd_event_setup_received(TUD_OPT_RHPORT, _ctrl_buf, true); + } else if (TRBCTL_CTL_STAT3 == trbctl) { + if (0 < _xfer_bytes[0]) { + RTSS_InvalidateDCache_by_Addr((void*) _xfer_trb[0][0], _xfer_bytes[0]); + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_OUT), + _xfer_bytes[0] - (_xfer_trb[0][2] & 0xFFFFFF), + XFER_RESULT_SUCCESS, true); + } else { + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_OUT), + 0, XFER_RESULT_SUCCESS, true); + + // *(volatile uint32_t*) 0x4900C000 ^= 8; // [TEMP] + } + } + } else { + // invalid TRBCTL value + __BKPT(0); + } + } else if (1 == ep) { + uint8_t trbctl = (_xfer_trb[1][3] >> 4) & 0x3F; + LOG("ep1 xfer trb3 = %08x trb2 = %08x", _xfer_trb[1][3], _xfer_trb[1][2]); + if (TRBCTL_CTL_STAT2 != trbctl) { // STATUS IN notification is done at xfer request + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_IN), + _xfer_bytes[1] - (_xfer_trb[1][2] & 0xFFFFFF), + XFER_RESULT_SUCCESS, true); + } else { + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_IN), + 0, XFER_RESULT_SUCCESS, true); + + // *(volatile uint32_t*) 0x4900C000 ^= 8; // [TEMP] + } + } + } else { + // [TODO] check if ep is open + LOG("ep%u xfer trb3 = %08x trb2 = %08x", ep, _xfer_trb[ep][3], _xfer_trb[ep][2]); + if (TUSB_DIR_OUT == tu_edpt_dir(tu_edpt_addr(ep >> 1, ep & 1))) { + RTSS_InvalidateDCache_by_Addr((void*) _xfer_trb[ep][0], + 512 - _xfer_trb[ep][2]); + // _xfer_bytes[ep] - _xfer_trb[ep][2]); + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(ep >> 1, ep & 1), + 512 - _xfer_trb[ep][2], + XFER_RESULT_SUCCESS, true); + } else + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(ep >> 1, ep & 1), + _xfer_bytes[ep] - _xfer_trb[ep][2], + XFER_RESULT_SUCCESS, true); + } + } break; + case DEPEVT_XFERINPROGRESS: { + LOG("Transfer in progress"); + } break; + case DEPEVT_XFERNOTREADY: { + LOG("Transfer not ready: %s", sts & 8 ? "no TRB" : "no XFER"); + + // XferNotReady NotActive for status stage + if ((1 == ep) && (0b0010 == (sts & 0b1011))) { + _dcd_start_xfer(1, NULL, 0, TRBCTL_CTL_STAT2); + break; + } + + if ((0 == ep) && (0b0010 == (sts & 0b1011))) { + _xfer_bytes[0] = 0; + _dcd_start_xfer(0, _ctrl_buf, 64, TRBCTL_CTL_STAT3); + break; + } + + if ((1 > ep) && (sts & (1 << 3))) { + if (_xfer_trb[ep][3] & (1 << 0)) { // transfer was configured + // dependxfer can only block when actbitlater is set + ugbl->guctl2_b.rst_actbitlater = 1; + _dcd_cmd_wait(ep, CMDTYP_DEPENDXFER, 0); + ugbl->guctl2_b.rst_actbitlater = 0; + + // reset the trb byte count and clean the cache + RTSS_InvalidateDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[0])); + _xfer_trb[ep][2] = _xfer_bytes[ep]; + RTSS_CleanDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[ep])); + + // prepare ep command + udev->depcmd[ep].par1 = (uint32_t) LocalToGlobal(_xfer_trb[ep]); + udev->depcmd[ep].par0 = 0; + + // issue the block command and pass the status + _dcd_cmd_wait(ep, CMDTYP_DEPSTRTXFER, 0); + + *(volatile uint32_t*) 0x49007000 ^= 16; // [TEMP] + } + } + } break; + case DEPEVT_EPCMDCMPLT: { + // redundant, currently no commands are issued with IOC bit set + } break; + } +} + + +static void _dcd_handle_devt(uint8_t evt, uint16_t info) +{ + LOG("%010u DEVT evt%u info%u", DWT->CYCCNT, evt, info); + switch (evt) { + case DEVT_USBRST: { + _xfer_cfgd = false; + + // [TODO] issue depcstall for any ep in stall mode + udev->dcfg_b.devaddr = 0; + LOG("USB reset"); + dcd_event_bus_reset(TUD_OPT_RHPORT, TUSB_SPEED_HIGH, true); // [TODO] actual speed + } break; + case DEVT_CONNECTDONE: { + // read conn speed from dsts + // program ramclksel in gctl if needed + LOG("Connect done"); + + udev->depcmd[0].par1 = (0 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[0].par0 = (2 << 30) | (0 << 22) | (0 << 17) | (64 << 3) | (0 << 1); + _dcd_cmd_wait(0, CMDTYP_DEPCFG, 0); + + udev->depcmd[1].par1 = (1 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[1].par0 = (2 << 30) | (0 << 22) | (0 << 17) | (64 << 3) | (0 << 1); + _dcd_cmd_wait(1, CMDTYP_DEPCFG, 0); + } break; + case DEVT_ULSTCHNG: { + LOG("Link status change"); + switch (info) { + case 0x3: { // suspend (L2) + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_SUSPEND, true); + } break; + case 0x4: { // disconnected + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_UNPLUGGED, true); + } break; + case 0xF: { // resume + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_RESUME, true); + } break; + default: {} + } + // 0x0: ON state + // 0x2: L1 state (sleep) + // 0x3: L2 state (suspend) + // 0x4: disconnected state + // 0x5: early suspend + // 0xE: reset + // 0xF: resume + } break; + case DEVT_ERRTICERR: { + __BKPT(0); + } break; + default: { + LOG("Unknown DEVT event"); + } + } +} + + +static uint8_t _dcd_start_xfer(uint8_t ep, void* buf, uint32_t size, uint8_t type) +{ + dcd_int_disable(TUD_OPT_RHPORT); // prevent race conditions + + // program the trb and clean the cache + _xfer_trb[ep][0] = buf ? (uint32_t) LocalToGlobal(buf) : 0; + _xfer_trb[ep][1] = 0; + _xfer_trb[ep][2] = size; + _xfer_trb[ep][3] = (1 << 11) | (1 << 10) | (type << 4) | (1 << 1) | (1 << 0); + RTSS_CleanDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[ep])); + + // prepare ep command + udev->depcmd[ep].par1 = (uint32_t) LocalToGlobal(_xfer_trb[ep]); + udev->depcmd[ep].par0 = 0; + + dcd_int_enable(TUD_OPT_RHPORT); + + // issue the block command and pass the status + return _dcd_cmd_wait(ep, CMDTYP_DEPSTRTXFER, 0); +} + +#endif // CFG_TUD_ENABLED From ebecd6d101f0ca73baa8a7452ae946aac8b42817 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 22 Oct 2024 08:48:25 +0200 Subject: [PATCH 07/77] alif/tinyusb_port: Disable USB IRQ on deinit. Signed-off-by: iabdalkader --- ports/alif/tinyusb_port/tusb_alif_dcd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/alif/tinyusb_port/tusb_alif_dcd.c b/ports/alif/tinyusb_port/tusb_alif_dcd.c index 510f0e9c13a9b..5ede591478645 100644 --- a/ports/alif/tinyusb_port/tusb_alif_dcd.c +++ b/ports/alif/tinyusb_port/tusb_alif_dcd.c @@ -460,6 +460,8 @@ void dcd_uninit(void) enable_usb_phy_isolation(); // enable usb phy isolation disable_usb_phy_power(); // power down usb phy disable_usb_periph_clk(); // disable usb peripheral clock + dcd_int_disable(TUD_OPT_RHPORT); + NVIC_ClearPendingIRQ(USB_IRQ_IRQn); } void USB_IRQHandler(void) From b8a9cdf0673916457ca2abf8a69ad8e22afaea11 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 28 Mar 2025 00:29:06 +1100 Subject: [PATCH 08/77] alif/tinyusb_port: Implement SOF event. Signed-off-by: Damien George --- ports/alif/tinyusb_port/tusb_alif_dcd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/alif/tinyusb_port/tusb_alif_dcd.c b/ports/alif/tinyusb_port/tusb_alif_dcd.c index 5ede591478645..9a990cedbe387 100644 --- a/ports/alif/tinyusb_port/tusb_alif_dcd.c +++ b/ports/alif/tinyusb_port/tusb_alif_dcd.c @@ -281,6 +281,8 @@ void dcd_disconnect(uint8_t rhport) void dcd_sof_enable(uint8_t rhport, bool en) { LOG("%010u >%s", DWT->CYCCNT, __func__); + + udev->devten_b.softevten = en; } @@ -656,6 +658,9 @@ static void _dcd_handle_devt(uint8_t evt, uint16_t info) // 0xE: reset // 0xF: resume } break; + case DEVT_SOF: { + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_SOF, true); + } break; case DEVT_ERRTICERR: { __BKPT(0); } break; From ccc5935234bbef026db776ebeb83a94f353e7d6b Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Dec 2023 00:11:46 +1100 Subject: [PATCH 09/77] alif: Add initial port to Alif Ensemble MCUs. This commit adds the beginning of a new alif port with support for Alif Ensemble MCUs. See https://alifsemi.com/ Supported features of this port added by this commit: - UART REPL. - TinyUSB support, for REPL and MSC. - Octal SPI flash support, for filesystem. - machine.Pin support. General notes about the port: - It uses make, similar to other bare-metal ports here. - The toolchain is the standard arm-none-eabi- toolchain. - Flashing a board can be done using either the built-in serial bootloader, or JLink (both supported here). - There are two required submodules (one for drivers/SDK, one for security tools), both of which are open source and on GitHub. - No special hardware or software is needed for development, just a board connected over USB. OpenMV have generously sponsored the development of this port. Signed-off-by: Damien George Signed-off-by: iabdalkader --- ports/alif/Makefile | 261 ++++++++++++++++++++++ ports/alif/README.md | 21 ++ ports/alif/alif_flash.c | 160 ++++++++++++++ ports/alif/boards/manifest.py | 5 + ports/alif/fatfs_port.c | 38 ++++ ports/alif/irq.h | 84 ++++++++ ports/alif/machine_pin.c | 298 ++++++++++++++++++++++++++ ports/alif/main.c | 145 +++++++++++++ ports/alif/mcu/M55_HP_cfg.json | 14 ++ ports/alif/mcu/ensemble.ld.S | 165 ++++++++++++++ ports/alif/mcu/make-pins.py | 64 ++++++ ports/alif/mcu/pins_prefix.c | 2 + ports/alif/modalif.c | 46 ++++ ports/alif/modalif.h | 33 +++ ports/alif/modmachine.c | 79 +++++++ ports/alif/modules/_boot.py | 23 ++ ports/alif/mpconfigport.h | 155 ++++++++++++++ ports/alif/mpconfigport.mk | 12 ++ ports/alif/mphalport.c | 152 +++++++++++++ ports/alif/mphalport.h | 152 +++++++++++++ ports/alif/mpuart.c | 113 ++++++++++ ports/alif/mpuart.h | 32 +++ ports/alif/msc_disk.c | 151 +++++++++++++ ports/alif/ospi_flash.c | 265 +++++++++++++++++++++++ ports/alif/ospi_flash.h | 52 +++++ ports/alif/ospi_xip_user.h | 5 + ports/alif/pendsv.c | 49 +++++ ports/alif/pendsv.h | 48 +++++ ports/alif/qstrdefsport.h | 2 + ports/alif/tinyusb_port/tusb_config.h | 73 +++++++ ports/alif/usbd.c | 41 ++++ 31 files changed, 2740 insertions(+) create mode 100644 ports/alif/Makefile create mode 100644 ports/alif/README.md create mode 100644 ports/alif/alif_flash.c create mode 100644 ports/alif/boards/manifest.py create mode 100644 ports/alif/fatfs_port.c create mode 100644 ports/alif/irq.h create mode 100644 ports/alif/machine_pin.c create mode 100644 ports/alif/main.c create mode 100644 ports/alif/mcu/M55_HP_cfg.json create mode 100644 ports/alif/mcu/ensemble.ld.S create mode 100755 ports/alif/mcu/make-pins.py create mode 100644 ports/alif/mcu/pins_prefix.c create mode 100644 ports/alif/modalif.c create mode 100644 ports/alif/modalif.h create mode 100644 ports/alif/modmachine.c create mode 100644 ports/alif/modules/_boot.py create mode 100644 ports/alif/mpconfigport.h create mode 100644 ports/alif/mpconfigport.mk create mode 100644 ports/alif/mphalport.c create mode 100644 ports/alif/mphalport.h create mode 100644 ports/alif/mpuart.c create mode 100644 ports/alif/mpuart.h create mode 100644 ports/alif/msc_disk.c create mode 100644 ports/alif/ospi_flash.c create mode 100644 ports/alif/ospi_flash.h create mode 100644 ports/alif/ospi_xip_user.h create mode 100644 ports/alif/pendsv.c create mode 100644 ports/alif/pendsv.h create mode 100644 ports/alif/qstrdefsport.h create mode 100644 ports/alif/tinyusb_port/tusb_config.h create mode 100644 ports/alif/usbd.c diff --git a/ports/alif/Makefile b/ports/alif/Makefile new file mode 100644 index 0000000000000..2863ebe2c4d61 --- /dev/null +++ b/ports/alif/Makefile @@ -0,0 +1,261 @@ +################################################################################ +# Initial setup of Makefile environment + +BOARD ?= ALIF_ENSEMBLE +BOARD_DIR ?= boards/$(BOARD) +BUILD ?= build-$(BOARD) + +ifeq ($(wildcard $(BOARD_DIR)/.),) +$(error Invalid BOARD specified: $(BOARD_DIR)) +endif + +include ../../py/mkenv.mk +include mpconfigport.mk +include $(BOARD_DIR)/mpconfigboard.mk + +# qstr definitions (must come before including py.mk) +QSTR_DEFS += qstrdefsport.h + +# include py core make definitions +include $(TOP)/py/py.mk +include $(TOP)/extmod/extmod.mk + +################################################################################ +# Project specific settings and compiler/linker flags + +CROSS_COMPILE ?= arm-none-eabi- +GIT_SUBMODULES += lib/tinyusb lib/alif_ensemble-cmsis-dfp lib/alif-security-toolkit +PORT ?= /dev/ttyACM0 + +ALIF_TOOLS ?= ../../lib/alif-security-toolkit/toolkit +ALIF_DFP_REL_TOP ?= lib/alif_ensemble-cmsis-dfp +ALIF_DFP_REL_HERE ?= $(TOP)/lib/alif_ensemble-cmsis-dfp +CMSIS_DIR ?= $(TOP)/lib/cmsis/inc + +MCU_CORE ?= M55_HP +ALIF_CONFIG ?= mcu/$(MCU_CORE)_cfg.json +LD_FILE ?= mcu/ensemble.ld.S + +INC += -I. +INC += -I$(TOP) +INC += -I$(BUILD) +INC += -I$(BOARD_DIR) +INC += -I$(CMSIS_DIR) +INC += -I$(ALIF_DFP_REL_HERE)/drivers/include/ +INC += -I$(ALIF_DFP_REL_HERE)/ospi_xip/source/ospi +INC += -I$(ALIF_DFP_REL_HERE)/Device/common/config/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/common/include/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/config/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/include/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/$(MCU_SERIES)/$(MCU_VARIANT)/ +INC += -I$(TOP)/lib/tinyusb/src +INC += -Itinyusb_port + +GEN_PIN_MKPINS = mcu/make-pins.py +GEN_PIN_PREFIX = mcu/pins_prefix.c +GEN_PINS_BOARD_CSV = $(BOARD_DIR)/pins.csv +GEN_PINS_SRC = $(BUILD)/pins_board.c +GEN_PINS_HDR = $(HEADER_BUILD)/pins_board.h + +CFLAGS_FPU += -mfloat-abi=hard -mfpu=fpv5-d16 +CFLAGS_CORTEX_M55 += -mthumb -mcpu=cortex-m55 -mtune=cortex-m55 $(CFLAGS_FPU) + +CFLAGS += $(INC) -Wall -Werror -std=c99 $(CFLAGS_CORTEX_M55) -nostdlib +CFLAGS += -Wdouble-promotion -Wfloat-conversion +CFLAGS += -fdata-sections -ffunction-sections +CFLAGS += -D$(MCU_CORE) -DCORE_$(MCU_CORE) -DALIF_CMSIS_H="\"$(MCU_CORE).h\"" + +ifeq ($(MICROPY_FLOAT_IMPL),float) +CFLAGS += -fsingle-precision-constant +CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT +else +CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_DOUBLE +endif + +AFLAGS = -mthumb -march=armv8.1-m.main $(CFLAGS_FPU) + +LDFLAGS += -nostdlib +LDFLAGS += -T$(BUILD)/ensemble.ld -Map=$@.map --cref --gc-sections +LDFLAGS += --wrap=dcd_event_handler + +# Tune for Debugging or Optimization +ifeq ($(DEBUG), 1) +CFLAGS += -Og -ggdb3 +# Disable text compression in debug builds +MICROPY_ROM_TEXT_COMPRESSION = 0 +else +CFLAGS += -O2 -DNDEBUG +endif + +LIBS += "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)" + +JLINK_CMD = '\ +ExitOnError 1\n\ +Device $(JLINK_DEV)\n\ +SelectInterface SWD\n\ +Speed auto\n\ +Connect\n\ +Reset\n\ +ShowHWStatus\n\ +LoadFile "$(BUILD)/firmware_toc.bin",0x8057f1c0\n\ +LoadFile "$(BUILD)/firmware.bin",0x80000000\n\ +Reset\n\ +Exit' + +################################################################################ +# Source files and libraries + +SRC_O += \ + shared/runtime/gchelper_thumb2.o + +SRC_C = \ + alif_flash.c \ + fatfs_port.c \ + machine_pin.c \ + main.c \ + modalif.c \ + mphalport.c \ + mpuart.c \ + msc_disk.c \ + ospi_flash.c \ + pendsv.c \ + usbd.c \ + $(wildcard $(BOARD_DIR)/*.c) + +ifeq ($(MICROPY_FLOAT_IMPL),float) +LIBM_SRC_C += $(SRC_LIB_LIBM_C) +LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) +$(BUILD)/lib/libm/%.o: CFLAGS += -Wno-maybe-uninitialized +else +LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_C) +LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_HW_C) +$(BUILD)/lib/libm_dbl/%.o: CFLAGS += -Wno-maybe-uninitialized +endif + +SHARED_SRC_C += $(addprefix shared/,\ + libc/string0.c \ + netutils/dhcpserver.c \ + netutils/netutils.c \ + netutils/trace.c \ + readline/readline.c \ + runtime/gchelper_native.c \ + runtime/interrupt_char.c \ + runtime/mpirq.c \ + runtime/pyexec.c \ + runtime/softtimer.c \ + runtime/stdout_helpers.c \ + runtime/sys_stdio_mphal.c \ + timeutils/timeutils.c \ + tinyusb/mp_usbd.c \ + tinyusb/mp_usbd_cdc.c \ + tinyusb/mp_usbd_descriptor.c \ + ) + +DRIVERS_SRC_C += $(addprefix drivers/,\ + bus/softspi.c \ + bus/softqspi.c \ + memory/spiflash.c \ + dht/dht.c \ + ) + +TINYUSB_SRC_C += \ + lib/tinyusb/src/tusb.c \ + lib/tinyusb/src/class/cdc/cdc_device.c \ + lib/tinyusb/src/class/msc/msc_device.c \ + lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/usbd.c \ + lib/tinyusb/src/device/usbd_control.c \ + tinyusb_port/tusb_alif_dcd.c \ + +ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ + Device/common/source/clk.c \ + Device/common/source/mpu_M55.c \ + Device/common/source/system_M55.c \ + Device/common/source/system_utils.c \ + Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ + drivers/source/pinconf.c \ + drivers/source/uart.c \ + ospi_xip/source/ospi/ospi_drv.c \ + ) + +$(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 + +# List of sources for qstr extraction +SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC) + +OBJ += $(PY_O) +OBJ += $(addprefix $(BUILD)/, $(SRC_O)) +OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(LIBM_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(TINYUSB_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(ALIF_SRC_C:.c=.o)) +OBJ += $(GEN_PINS_SRC:.c=.o) + +################################################################################ +# Main targets + +.DELETE_ON_ERROR: + +.PHONY: all +all: $(BUILD)/firmware_toc.bin + +$(BUILD)/ensemble.ld: $(LD_FILE) + $(ECHO) "Preprocess linker script $@" + $(Q)$(CPP) -P -E $(CFLAGS) $^ > $@ + +$(BUILD)/firmware.elf: $(OBJ) $(BUILD)/ensemble.ld + $(ECHO) "Link $@" + $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + $(Q)$(SIZE) $@ + +$(BUILD)/firmware.bin: $(BUILD)/firmware.elf + $(Q)$(OBJCOPY) -Obinary $^ $(BUILD)/firmware.bin + +$(BUILD)/firmware_toc.bin: $(BUILD)/firmware.bin + $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ + --filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \ + --output-dir $(BUILD) \ + --firmware-dir $(BUILD) \ + --output $@ + +.PHONY: deploy +deploy: $(BUILD)/firmware_toc.bin + $(ECHO) "Writing $< to the board" + $(Q)python $(ALIF_TOOLS)/app-write-mram.py \ + --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ + --port $(PORT) \ + --pad \ + --images file:$(BUILD)/application_package.ds + +.PHONY: deploy-jlink +deploy-jlink: $(BUILD)/firmware_toc.bin + $(Q)echo -e $(JLINK_CMD) | $(JLINK_EXE) + +.PHONY: maintenance +maintenance: + $(Q)python $(ALIF_TOOLS)/maintenance.py \ + --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ + --port $(PORT) + +.PHONY: update-system-package +update-system-package: + $(Q)python $(ALIF_TOOLS)/updateSystemPackage.py \ + --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ + --port $(PORT) + +################################################################################ +# Remaining make rules + +# Use a pattern rule here so that make will only call make-pins.py once to make +# both pins_board.c and pins_board.h +$(BUILD)/%_board.c $(HEADER_BUILD)/%_board.h: $(BOARD_DIR)/%.csv $(GEN_PIN_MKPINS) $(GEN_PIN_PREFIX) | $(HEADER_BUILD) + $(ECHO) "GEN $@" + $(Q)$(PYTHON) $(GEN_PIN_MKPINS) \ + --board-csv $(GEN_PINS_BOARD_CSV) \ + --prefix $(GEN_PIN_PREFIX) \ + --output-source $(GEN_PINS_SRC) \ + --output-header $(GEN_PINS_HDR) + +include $(TOP)/py/mkrules.mk diff --git a/ports/alif/README.md b/ports/alif/README.md new file mode 100644 index 0000000000000..824a63da186d2 --- /dev/null +++ b/ports/alif/README.md @@ -0,0 +1,21 @@ +MicroPython port to Alif Ensemble MCUs +====================================== + +This is a port of MicroPython to the Alif Ensemble series of microcontrollers. + +Initial development of this Alif port was sponsored by OpenMV LLC. + +Features currently supported: +- UART REPL. +- TinyUSB with CDC and MSC device support. +- Octal SPI flash with XIP mode. +- machine.Pin support with named pins. +- machine.UART, machine.SPI, machine.I2C, machine.RTC peripherals. +- WiFi and Bluetooth using cyw43. +- Dual core support of the HE and HP cores using Open-AMP. +- Low power modes. + +The following more advanced features will follow later: +- Ethernet support. +- SDRAM support. +- Other machine modules. diff --git a/ports/alif/alif_flash.c b/ports/alif/alif_flash.c new file mode 100644 index 0000000000000..e3b2125800ea2 --- /dev/null +++ b/ports/alif/alif_flash.c @@ -0,0 +1,160 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mphal.h" +#include "py/runtime.h" +#include "extmod/vfs.h" +#include "modalif.h" +#include "ospi_flash.h" + +typedef struct _alif_flash_obj_t { + mp_obj_base_t base; + uint32_t flash_base_addr; + uint32_t flash_size; +} alif_flash_obj_t; + +static alif_flash_obj_t alif_flash_obj = { + .base = { &alif_flash_type }, + .flash_base_addr = MICROPY_HW_FLASH_STORAGE_BASE_ADDR, + .flash_size = MICROPY_HW_FLASH_STORAGE_BYTES, +}; + +static mp_obj_t alif_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Parse arguments + enum { ARG_start, ARG_len }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + 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); + + if (args[ARG_start].u_int == -1 && args[ARG_len].u_int == -1) { + // Default singleton object that accesses entire flash + return MP_OBJ_FROM_PTR(&alif_flash_obj); + } + + alif_flash_obj_t *self = mp_obj_malloc(alif_flash_obj_t, &alif_flash_type); + + mp_int_t start = args[ARG_start].u_int; + if (start == -1) { + start = 0; + } else if (!(0 <= start && start < MICROPY_HW_FLASH_STORAGE_BYTES && start % MICROPY_HW_FLASH_BLOCK_SIZE_BYTES == 0)) { + mp_raise_ValueError(NULL); + } + + mp_int_t len = args[ARG_len].u_int; + if (len == -1) { + len = MICROPY_HW_FLASH_STORAGE_BYTES - start; + } else if (!(0 < len && start + len <= MICROPY_HW_FLASH_STORAGE_BYTES && len % MICROPY_HW_FLASH_BLOCK_SIZE_BYTES == 0)) { + mp_raise_ValueError(NULL); + } + + self->flash_base_addr = MICROPY_HW_FLASH_STORAGE_BASE_ADDR + start; + self->flash_size = len; + + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t alif_flash_readblocks(size_t n_args, const mp_obj_t *args) { + alif_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = mp_obj_get_int(args[1]) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); + if (n_args == 4) { + offset += mp_obj_get_int(args[3]); + } + int ret = ospi_flash_read(self->flash_base_addr + offset, bufinfo.len, bufinfo.buf); + mp_event_handle_nowait(); + return MP_OBJ_NEW_SMALL_INT(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alif_flash_readblocks_obj, 3, 4, alif_flash_readblocks); + +static mp_obj_t alif_flash_writeblocks(size_t n_args, const mp_obj_t *args) { + alif_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = mp_obj_get_int(args[1]) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + if (n_args == 3) { + uint32_t addr = self->flash_base_addr + offset; + int32_t len = bufinfo.len; + while (len > 0) { + int ret = ospi_flash_erase_sector(addr); + mp_event_handle_nowait(); + if (ret < 0) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + addr += MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + len -= MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + } + } else { + offset += mp_obj_get_int(args[3]); + } + int ret = ospi_flash_write(self->flash_base_addr + offset, bufinfo.len, bufinfo.buf); + mp_event_handle_nowait(); + return MP_OBJ_NEW_SMALL_INT(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alif_flash_writeblocks_obj, 3, 4, alif_flash_writeblocks); + +static mp_obj_t alif_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) { + alif_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t cmd = mp_obj_get_int(cmd_in); + switch (cmd) { + case MP_BLOCKDEV_IOCTL_INIT: + return MP_OBJ_NEW_SMALL_INT(0); + case MP_BLOCKDEV_IOCTL_DEINIT: + return MP_OBJ_NEW_SMALL_INT(0); + case MP_BLOCKDEV_IOCTL_SYNC: + return MP_OBJ_NEW_SMALL_INT(0); + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + return MP_OBJ_NEW_SMALL_INT(self->flash_size / MICROPY_HW_FLASH_BLOCK_SIZE_BYTES); + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + return MP_OBJ_NEW_SMALL_INT(MICROPY_HW_FLASH_BLOCK_SIZE_BYTES); + case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: { + uint32_t offset = mp_obj_get_int(arg_in) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + int ret = ospi_flash_erase_sector(self->flash_base_addr + offset); + return MP_OBJ_NEW_SMALL_INT(ret); + } + default: + return mp_const_none; + } +} +static MP_DEFINE_CONST_FUN_OBJ_3(alif_flash_ioctl_obj, alif_flash_ioctl); + +static const mp_rom_map_elem_t alif_flash_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&alif_flash_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&alif_flash_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&alif_flash_ioctl_obj) }, +}; +static MP_DEFINE_CONST_DICT(alif_flash_locals_dict, alif_flash_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + alif_flash_type, + MP_QSTR_Flash, + MP_TYPE_FLAG_NONE, + make_new, alif_flash_make_new, + locals_dict, &alif_flash_locals_dict + ); diff --git a/ports/alif/boards/manifest.py b/ports/alif/boards/manifest.py new file mode 100644 index 0000000000000..148f1d6a6eb5e --- /dev/null +++ b/ports/alif/boards/manifest.py @@ -0,0 +1,5 @@ +freeze("$(PORT_DIR)/modules") +include("$(MPY_DIR)/extmod/asyncio") +require("dht") +require("neopixel") +require("onewire") diff --git a/ports/alif/fatfs_port.c b/ports/alif/fatfs_port.c new file mode 100644 index 0000000000000..5883c9f3b9447 --- /dev/null +++ b/ports/alif/fatfs_port.c @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "lib/oofatfs/ff.h" + +DWORD get_fattime(void) { + // TODO + int year = 2024; + int month = 1; + int day = 1; + int hour = 0; + int min = 0; + int sec = 0; + return ((year - 1980) << 25) | (month << 21) | (day << 16) | (hour << 11) | (min << 5) | (sec / 2); +} diff --git a/ports/alif/irq.h b/ports/alif/irq.h new file mode 100644 index 0000000000000..723f89f1d907e --- /dev/null +++ b/ports/alif/irq.h @@ -0,0 +1,84 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2023 Damien P. George + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_IRQ_H +#define MICROPY_INCLUDED_ALIF_IRQ_H + +#include +#include ALIF_CMSIS_H + +// IRQ priority definitions. +// +// The M55-HP CPU has __NVIC_PRIO_BITS==8 bits for setting the IRQ priority. +// It uses NVIC_SetPriorityGrouping(0) which is 7 bits for preempt priority +// and 1 bit for the sub-priority. +// +// Lower number implies higher interrupt priority. + +#define NVIC_PRIORITYGROUP_7 ((uint32_t)0x00000000U) +#define IRQ_PRI_SYSTEM_TICK NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 0, 0) +#define IRQ_PRI_QUIET_TIMING NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) +#define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) +#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) +#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) + +// these states correspond to values from query_irq, enable_irq and disable_irq +#define IRQ_STATE_DISABLED (0x00000001) +#define IRQ_STATE_ENABLED (0x00000000) + +static inline uint32_t query_irq(void) { + return __get_PRIMASK(); +} + +static inline void enable_irq(uint32_t state) { + __set_PRIMASK(state); +} + +static inline uint32_t disable_irq(void) { + uint32_t state = __get_PRIMASK(); + __disable_irq(); + return state; +} + +// irqs with a priority value greater or equal to "pri" will be disabled +static inline uint32_t raise_irq_pri(uint32_t pri) { + uint32_t basepri = __get_BASEPRI(); + // If non-zero, the processor does not process any exception with a + // priority value greater than or equal to BASEPRI. + // When writing to BASEPRI_MAX the write goes to BASEPRI only if either: + // - Rn is non-zero and the current BASEPRI value is 0 + // - Rn is non-zero and less than the current BASEPRI value + pri <<= (8 - __NVIC_PRIO_BITS); + __ASM volatile ("msr basepri_max, %0" : : "r" (pri) : "memory"); + return basepri; +} + +// "basepri" should be the value returned from raise_irq_pri +static inline void restore_irq_pri(uint32_t basepri) { + __set_BASEPRI(basepri); +} + +#endif // MICROPY_INCLUDED_ALIF_IRQ_H diff --git a/ports/alif/machine_pin.c b/ports/alif/machine_pin.c new file mode 100644 index 0000000000000..02c5e482adb89 --- /dev/null +++ b/ports/alif/machine_pin.c @@ -0,0 +1,298 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/runtime.h" +#include "py/mphal.h" +#include "extmod/modmachine.h" +#include "extmod/virtpin.h" +#include "shared/runtime/mpirq.h" + +extern const mp_obj_dict_t machine_pin_cpu_pins_locals_dict; +extern const mp_obj_dict_t machine_pin_board_pins_locals_dict; + +static const machine_pin_obj_t *machine_pin_find_named(const mp_obj_dict_t *named_pins, mp_obj_t name) { + const mp_map_t *named_map = &named_pins->map; + mp_map_elem_t *named_elem = mp_map_lookup((mp_map_t *)named_map, name, MP_MAP_LOOKUP); + if (named_elem != NULL && named_elem->value != NULL) { + return MP_OBJ_TO_PTR(named_elem->value); + } + return NULL; +} + +const machine_pin_obj_t *machine_pin_find(mp_obj_t pin) { + // Is already a object of the proper type + if (mp_obj_is_type(pin, &machine_pin_type)) { + return MP_OBJ_TO_PTR(pin); + } + if (mp_obj_is_str(pin)) { + // Try to find the pin in the board pins first. + const machine_pin_obj_t *self = machine_pin_find_named(&machine_pin_board_pins_locals_dict, pin); + if (self != NULL) { + return self; + } + + // If not found, try to find the pin in the cpu pins. + self = machine_pin_find_named(&machine_pin_cpu_pins_locals_dict, pin); + if (self != NULL) { + return self; + } + + // Pin name not found. + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unknown named pin \"%s\""), mp_obj_str_get_str(pin)); + } + mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); +} + +static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_pin_obj_t *self = self_in; + + uint8_t alt_func, pad_ctrl; + pinconf_get(self->port, self->pin, &alt_func, &pad_ctrl); + + qstr mode_qst; + if (gpio_get_direction(self->gpio, self->pin) == GPIO_PIN_DIR_INPUT) { + mode_qst = MP_QSTR_IN; + } else { + if (pad_ctrl & PADCTRL_DRIVER_OPEN_DRAIN) { + mode_qst = MP_QSTR_OPEN_DRAIN; + } else { + mode_qst = MP_QSTR_OUT; + } + } + mp_printf(print, "Pin(%q, mode=%q", self->name, mode_qst); + uint8_t pad_ctrl_pull = pad_ctrl & (PADCTRL_DRIVER_DISABLED_PULL_DOWN | PADCTRL_DRIVER_DISABLED_PULL_UP); + if (pad_ctrl_pull == PADCTRL_DRIVER_DISABLED_PULL_UP) { + mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP); + } else if (pad_ctrl_pull == PADCTRL_DRIVER_DISABLED_PULL_DOWN) { + mp_printf(print, ", pull=%q", MP_QSTR_PULL_DOWN); + } + if (alt_func != PINMUX_ALTERNATE_FUNCTION_0) { + mp_printf(print, ", alt=%u", alt_func); + } + mp_printf(print, ")"); +} + +enum { + ARG_mode, ARG_pull, ARG_value, ARG_alt +}; +static const mp_arg_t allowed_args[] = { + {MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + {MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + {MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + {MP_QSTR_alt, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0}}, +}; + +static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get initial value of pin (only valid for OUT and OPEN_DRAIN modes) + int value = -1; + if (args[ARG_value].u_obj != mp_const_none) { + value = mp_obj_is_true(args[ARG_value].u_obj); + } + + // configure mode + if (args[ARG_mode].u_obj != mp_const_none) { + mp_int_t mode = mp_obj_get_int(args[ARG_mode].u_obj); + if (mode == MP_HAL_PIN_MODE_INPUT) { + mp_hal_pin_input(self); + } else if (mode == MP_HAL_PIN_MODE_OUTPUT) { + if (value != -1) { + // set initial output value before configuring mode + mp_hal_pin_write(self, value); + } + mp_hal_pin_output(self); + } else if (mode == MP_HAL_PIN_MODE_OPEN_DRAIN) { + if (value != -1) { + // set initial output value before configuring mode + mp_hal_pin_write(self, value); + } + mp_hal_pin_open_drain(self); + } + } + + // Configure pull (unconditionally because None means no-pull). + uint32_t pull = 0; + if (args[ARG_pull].u_obj != mp_const_none) { + pull = mp_obj_get_int(args[ARG_pull].u_obj); + } + uint8_t alt_func; + uint8_t pad_ctrl; + pinconf_get(self->port, self->pin, &alt_func, &pad_ctrl); + alt_func = PINMUX_ALTERNATE_FUNCTION_0; + pad_ctrl |= PADCTRL_READ_ENABLE; + pad_ctrl &= ~(PADCTRL_DRIVER_DISABLED_PULL_DOWN | PADCTRL_DRIVER_DISABLED_PULL_UP); + if (pull & MP_HAL_PIN_PULL_UP) { + pad_ctrl |= PADCTRL_DRIVER_DISABLED_PULL_UP; + } + if (pull & MP_HAL_PIN_PULL_DOWN) { + pad_ctrl |= PADCTRL_DRIVER_DISABLED_PULL_DOWN; + } + pinconf_set(self->port, self->pin, alt_func, pad_ctrl); + + return mp_const_none; +} + +// constructor(id, ...) +mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + const machine_pin_obj_t *self = machine_pin_find(args[0]); + + if (n_args > 1 || n_kw > 0) { + // pin mode given, so configure this GPIO + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args); + } + return MP_OBJ_FROM_PTR(self); +} + +// fast method for getting/setting pin value +static mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + machine_pin_obj_t *self = self_in; + if (n_args == 0) { + // get pin + return MP_OBJ_NEW_SMALL_INT(mp_hal_pin_read(self)); + } else { + // set pin + bool value = mp_obj_is_true(args[0]); + mp_hal_pin_write(self, value); + return mp_const_none; + } +} + +// pin.init(mode, pull) +static mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init); + +// pin.value([value]) +static mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) { + return machine_pin_call(args[0], n_args - 1, 0, args + 1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value); + +// pin.low() +static mp_obj_t machine_pin_low(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_hal_pin_low(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_low_obj, machine_pin_low); + +// pin.high() +static mp_obj_t machine_pin_high(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_hal_pin_high(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_high_obj, machine_pin_high); + +// pin.toggle() +static mp_obj_t machine_pin_toggle(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + gpio_toggle_value(self->gpio, self->pin); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle); + +static MP_DEFINE_CONST_OBJ_TYPE( + pin_cpu_pins_obj_type, + MP_QSTR_cpu, + MP_TYPE_FLAG_NONE, + locals_dict, &machine_pin_cpu_pins_locals_dict + ); + +static MP_DEFINE_CONST_OBJ_TYPE( + pin_board_pins_obj_type, + MP_QSTR_board, + MP_TYPE_FLAG_NONE, + locals_dict, &machine_pin_board_pins_locals_dict + ); + +static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&machine_pin_low_obj) }, + { MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&machine_pin_high_obj) }, + { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_low_obj) }, + { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_high_obj) }, + { MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) }, + + // class attributes + { MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&pin_board_pins_obj_type) }, + { MP_ROM_QSTR(MP_QSTR_cpu), MP_ROM_PTR(&pin_cpu_pins_obj_type) }, + + // class constants + { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(MP_HAL_PIN_MODE_INPUT) }, + { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(MP_HAL_PIN_MODE_OUTPUT) }, + { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(MP_HAL_PIN_MODE_OPEN_DRAIN) }, + { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(MP_HAL_PIN_PULL_UP) }, + { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(MP_HAL_PIN_PULL_DOWN) }, +}; +static MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table); + +static mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + (void)errcode; + machine_pin_obj_t *self = self_in; + + switch (request) { + case MP_PIN_READ: { + return mp_hal_pin_read(self); + } + case MP_PIN_WRITE: { + mp_hal_pin_write(self, arg); + return 0; + } + } + return -1; +} + +static const mp_pin_p_t pin_pin_p = { + .ioctl = pin_ioctl, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_pin_type, + MP_QSTR_Pin, + MP_TYPE_FLAG_NONE, + make_new, mp_pin_make_new, + print, machine_pin_print, + call, machine_pin_call, + protocol, &pin_pin_p, + locals_dict, &machine_pin_locals_dict + ); + +mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) { + return machine_pin_find(obj); +} diff --git a/ports/alif/main.c b/ports/alif/main.c new file mode 100644 index 0000000000000..0eb43a38268a6 --- /dev/null +++ b/ports/alif/main.c @@ -0,0 +1,145 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/compile.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/stackctrl.h" +#include "shared/readline/readline.h" +#include "shared/runtime/gchelper.h" +#include "shared/runtime/pyexec.h" +#include "shared/tinyusb/mp_usbd.h" +#include "tusb.h" +#include "mpuart.h" +#include "ospi_flash.h" +#include "pendsv.h" + +extern uint8_t __StackTop, __StackLimit; +extern uint8_t __GcHeapStart, __GcHeapEnd; + +NORETURN void panic(const char *msg) { + mp_hal_stdout_tx_strn("\nFATAL ERROR:\n", 14); + mp_hal_stdout_tx_strn(msg, strlen(msg)); + for (;;) { + *(volatile uint32_t *)0x4900C000 ^= 9; + for (volatile uint delay = 0; delay < 10000000; delay++) { + } + } +} + +void _start(void) { + SysTick_Config(SystemCoreClock / 1000); + + MICROPY_BOARD_STARTUP(); + + pendsv_init(); + + MICROPY_BOARD_EARLY_INIT(); + + #if MICROPY_HW_ENABLE_UART_REPL + mp_uart_init(); + #endif + + if (ospi_flash_init() != 0) { + MICROPY_BOARD_FATAL_ERROR("ospi_init failed"); + } + + #if MICROPY_HW_ENABLE_USBDEV + NVIC_ClearPendingIRQ(USB_IRQ_IRQn); + NVIC_SetPriority(USB_IRQ_IRQn, IRQ_PRI_USB); + #endif + + // Initialise stack extents and GC heap. + mp_stack_set_top(&__StackTop); + mp_stack_set_limit(&__StackTop - &__StackLimit - 1024); + gc_init(&__GcHeapStart, &__GcHeapEnd); + + for (;;) { + // Initialise MicroPython runtime. + mp_init(); + + // Initialise sub-systems. + readline_init0(); + + // Execute _boot.py to set up the filesystem. + pyexec_frozen_module("_boot.py", false); + + // Execute user scripts. + int ret = pyexec_file_if_exists("boot.py"); + #if MICROPY_HW_ENABLE_USBDEV + mp_usbd_init(); + #endif + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) { + ret = pyexec_file_if_exists("main.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + } + + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } + + soft_reset_exit: + mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + gc_sweep_all(); + mp_deinit(); + } +} + +void gc_collect(void) { + gc_collect_start(); + gc_helper_collect_regs_and_stack(); + gc_collect_end(); +} + +void nlr_jump_fail(void *val) { + mp_printf(&mp_plat_print, "FATAL: uncaught exception %p\n", val); + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(val)); + for (;;) { + __WFE(); + } +} + +#ifndef NDEBUG +void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { + printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); + panic("Assertion failed"); +} +#endif diff --git a/ports/alif/mcu/M55_HP_cfg.json b/ports/alif/mcu/M55_HP_cfg.json new file mode 100644 index 0000000000000..228ddbf6c9464 --- /dev/null +++ b/ports/alif/mcu/M55_HP_cfg.json @@ -0,0 +1,14 @@ +{ + "USER_APP": { + "binary": "firmware.bin", + "mramAddress": "0x80000000", + "version": "1.0.0", + "cpu_id": "M55_HP", + "flags": ["boot"], + "signed": false + }, + "DEVICE": { + "binary": "app-device-config.json", + "version" : "0.5.00" + } +} diff --git a/ports/alif/mcu/ensemble.ld.S b/ports/alif/mcu/ensemble.ld.S new file mode 100644 index 0000000000000..198641ba3dcba --- /dev/null +++ b/ports/alif/mcu/ensemble.ld.S @@ -0,0 +1,165 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 OpenMV LLC. + * + * 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. + */ + +// Entry Point +ENTRY(Reset_Handler) + +MEMORY +{ + ROM (rx) : ORIGIN = 0x80000000, LENGTH = 0x0057F000 + ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 + #ifdef CORE_M55_HP + DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00100000 + #else + DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00040000 + #endif + SRAM0 (rwx) : ORIGIN = 0x02000000, LENGTH = 0x00400000 + SRAM1 (rwx) : ORIGIN = 0x08000000, LENGTH = 0x00280000 +} + +__STACK_SIZE = 0x00004000; +__HEAP_SIZE = 0x00004000; +__MP_HEAP_SIZE = 0x00040000; + +SECTIONS +{ + .text : ALIGN(16) + { + KEEP(*(.vectors)) + . = ALIGN(4); + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(16); + } > ROM + + .copy.table : ALIGN(4) + { + __copy_table_start__ = .; + LONG ( LOADADDR(.data) ) + LONG ( ADDR(.data) ) + LONG ( SIZEOF(.data)/4 ) + __copy_table_end__ = .; + . = ALIGN(16); + } > ROM + + .zero.table : ALIGN(4) + { + __zero_table_start__ = .; + LONG (ADDR(.bss)) + LONG (SIZEOF(.bss)/4) + LONG (ADDR(.bss.sram0)) + LONG (SIZEOF(.bss.sram0)/4) + __zero_table_end__ = .; + . = ALIGN(16); + } > ROM + + .data : ALIGN(8) + { + *(.data) + . = ALIGN(8); + *(.data.*) + . = ALIGN(16); + } > DTCM AT > ROM + + /* Peripherals in expansion master 0 (USB, Ethernet, SD/MMC) + are by default configured as non-secure, so they don't + have access to DTCMs. This can be fixed in the ToC by allowing + access to DTCMs to all bus masters, for now these peripherals + should place buffers in regular SRAM */ + .bss.sram0 (NOLOAD) : ALIGN(4) + { + * (.bss.sram0*) + } > SRAM0 + + .bss : ALIGN(4) + { + __bss_start__ = .; + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > DTCM + + .heap (NOLOAD) : ALIGN(4) + { + __end__ = .; + PROVIDE(end = .); + . = . + __HEAP_SIZE; + . = ALIGN(4); + __HeapLimit = .; + + /* MicroPython GC heap */ + . = ALIGN(16); + __GcHeapStart = .; + . = . + __MP_HEAP_SIZE; + __GcHeapEnd = .; + } > DTCM + + .stack (NOLOAD) : ALIGN(4) + { + __StackLimit = .; + . = . + __STACK_SIZE; + . = ALIGN(4); + __StackTop = .; + } > DTCM + PROVIDE(__stack = __StackTop); + + .init_fini_arrays : ALIGN(16) + { + KEEP(*(.init)) + KEEP(*(.fini)) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + 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 = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.eh_frame*)) + . = ALIGN(16); + } > ROM + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/ports/alif/mcu/make-pins.py b/ports/alif/mcu/make-pins.py new file mode 100755 index 0000000000000..b49db4cfe49bb --- /dev/null +++ b/ports/alif/mcu/make-pins.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +import os +import re +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../../tools")) +import boardgen + +NUM_PORTS = 16 +NUM_PINS_PER_PORT = 8 + + +class AlifPin(boardgen.Pin): + # Emit the struct which contains the pin instance. + def definition(self): + port, pin = self.name()[1:].split("_") + base = "LPGPIO_BASE" if port == "15" else "GPIO{}_BASE".format(port) + return ( + "{{ " + ".base = {{ .type = &machine_pin_type }}, " + ".gpio = (GPIO_Type *){base}, " + ".port = PORT_{port}, " + ".pin = PIN_{pin}, " + ".name = MP_QSTR_P{port}_{pin} " + "}}".format(port=port, pin=pin, base=base) + ) + + # Alif cpu names must be "Pn_m". + @staticmethod + def validate_cpu_pin_name(cpu_pin_name): + boardgen.Pin.validate_cpu_pin_name(cpu_pin_name) + + if not (m := re.match("P([0-9]){1,2}_([0-9])", cpu_pin_name)): + raise boardgen.PinGeneratorError( + "Invalid cpu pin name '{}', must be 'Pn_m'".format(cpu_pin_name) + ) + + port = int(m.group(1)) + pin = int(m.group(2)) + if not (0 <= port < NUM_PORTS and 0 <= pin < NUM_PINS_PER_PORT): + raise boardgen.PinGeneratorError("Unknown cpu pin '{}'".format(cpu_pin_name)) + + +class AlifPinGenerator(boardgen.PinGenerator): + def __init__(self): + # Use custom pin type above. + super().__init__(pin_type=AlifPin) + + # Pre-define the pins (i.e. don't require them to be listed in pins.csv). + for i in range(NUM_PORTS): + for j in range(NUM_PINS_PER_PORT): + self.add_cpu_pin("P{}_{}".format(i, j)) + + # Only use pre-defined cpu pins (do not let board.csv create them). + def find_pin_by_cpu_pin_name(self, cpu_pin_name, create=True): + return super().find_pin_by_cpu_pin_name(cpu_pin_name, create=False) + + def cpu_table_size(self): + return "{} * {}".format(NUM_PORTS, NUM_PINS_PER_PORT) + + +if __name__ == "__main__": + AlifPinGenerator().main() diff --git a/ports/alif/mcu/pins_prefix.c b/ports/alif/mcu/pins_prefix.c new file mode 100644 index 0000000000000..99fef5939a0ce --- /dev/null +++ b/ports/alif/mcu/pins_prefix.c @@ -0,0 +1,2 @@ +#include "py/mphal.h" +#include "extmod/modmachine.h" diff --git a/ports/alif/modalif.c b/ports/alif/modalif.c new file mode 100644 index 0000000000000..69b7e6c5c8e87 --- /dev/null +++ b/ports/alif/modalif.c @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mphal.h" +#include "py/runtime.h" +#include "modalif.h" + +static const mp_rom_map_elem_t alif_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alif) }, + { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&alif_flash_type) }, + #if MICROPY_HW_USB_MSC + // Attribute to indicate USB MSC is enabled. + { MP_ROM_QSTR(MP_QSTR_usb_msc), MP_ROM_TRUE }, + #endif +}; +static MP_DEFINE_CONST_DICT(alif_module_globals, alif_module_globals_table); + +const mp_obj_module_t mp_module_alif = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&alif_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_alif, mp_module_alif); diff --git a/ports/alif/modalif.h b/ports/alif/modalif.h new file mode 100644 index 0000000000000..5812d6ba54597 --- /dev/null +++ b/ports/alif/modalif.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_MODALIF_H +#define MICROPY_INCLUDED_ALIF_MODALIF_H + +#include "py/obj.h" + +extern const mp_obj_type_t alif_flash_type; + +#endif // MICROPY_INCLUDED_ALIF_MODALIF_H diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c new file mode 100644 index 0000000000000..c4102225dabfc --- /dev/null +++ b/ports/alif/modmachine.c @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ + +// This file is never compiled standalone, it's included directly from +// extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. + +#define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ + { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ + +static void mp_machine_idle(void) { + mp_event_wait_indefinite(); +} + +static mp_obj_t mp_machine_unique_id(void) { + return mp_obj_new_bytes((const uint8_t *)"ABCD", 4); +} + +NORETURN static void mp_machine_reset(void) { + NVIC_SystemReset(); +} + +NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { + __disable_irq(); + + MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); + + while (1) { + ; + } +} + +static mp_int_t mp_machine_reset_cause(void) { + // TODO + return 0; +} + +static mp_obj_t mp_machine_get_freq(void) { + return MP_OBJ_NEW_SMALL_INT(SystemCoreClock); +} + +static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { + mp_raise_NotImplementedError(NULL); +} + +static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { + mp_int_t delay = -1; + if (n_args == 1) { + delay = mp_obj_get_int(args[0]); + } + mp_hal_delay_ms(delay); +} + +NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { + mp_machine_lightsleep(n_args, args); + mp_machine_reset(); +} diff --git a/ports/alif/modules/_boot.py b/ports/alif/modules/_boot.py new file mode 100644 index 0000000000000..36044b461e662 --- /dev/null +++ b/ports/alif/modules/_boot.py @@ -0,0 +1,23 @@ +import sys, os, alif + + +bdev = alif.Flash() +if hasattr(alif, "usb_msc"): + try: + # This may fail on VfsFat construction, or mount. + os.mount(os.VfsFat(bdev), "/flash") + except: + os.VfsFat.mkfs(bdev) + os.mount(os.VfsFat(bdev), "/flash") +else: + try: + os.mount(os.VfsLfs2(bdev, progsize=256), "/flash") + except: + os.VfsLfs2.mkfs(bdev, progsize=256) + os.mount(os.VfsLfs2(bdev, progsize=256), "/flash") + +sys.path.append("/flash") +sys.path.append("/flash/lib") +os.chdir("/flash") + +del sys, os, alif, bdev diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h new file mode 100644 index 0000000000000..2d8e3b2a09dbe --- /dev/null +++ b/ports/alif/mpconfigport.h @@ -0,0 +1,155 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ + +// Options controlling how MicroPython is built, overriding defaults in py/mpconfig.h + +#include +#include // for alloca() + +// board specific definitions +#include "mpconfigboard.h" + +#ifndef MICROPY_CONFIG_ROM_LEVEL +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) +#endif + +#define MICROPY_HW_ENABLE_UART_REPL (1) // useful if there is no USB +#define MICROPY_HW_ENABLE_USBDEV (1) + +#ifndef MICROPY_HW_USB_PRODUCT_FS_STRING +#define MICROPY_HW_USB_PRODUCT_FS_STRING "Board in HS mode" +#endif +#define MICROPY_HW_USB_CDC (1) +#define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) +#ifndef MICROPY_HW_USB_MSC +#define MICROPY_HW_USB_MSC (0) +#endif +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0xf055) +#endif +#ifndef MICROPY_HW_USB_PID +#define MICROPY_HW_USB_PID (0x9802) // interface has CDC only +#endif + +#define MICROPY_HW_FLASH_BLOCK_SIZE_BYTES (4096) + +// Memory allocation policies +#ifndef MICROPY_ALLOC_GC_STACK_SIZE +#define MICROPY_ALLOC_GC_STACK_SIZE (128) +#endif +#define MICROPY_ALLOC_PATH_MAX (128) +#define MICROPY_QSTR_BYTES_IN_HASH (1) + +// MicroPython emitters +#define MICROPY_PERSISTENT_CODE_LOAD (1) +#define MICROPY_EMIT_THUMB (1) +#define MICROPY_EMIT_THUMB_ARMV7M (1) +#define MICROPY_EMIT_INLINE_THUMB (1) +#define MICROPY_EMIT_INLINE_THUMB_FLOAT (1) + +// Optimisations +#define MICROPY_OPT_COMPUTED_GOTO (1) + +// Python internal features +#define MICROPY_READER_VFS (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_SCHEDULER_DEPTH (8) +#define MICROPY_SCHEDULER_STATIC_NODES (1) +#define MICROPY_USE_INTERNAL_ERRNO (1) + +// Fine control over Python builtins, classes, modules, etc +#define MICROPY_PY_SYS_PLATFORM "alif" + +// Extended modules +#define MICROPY_EPOCH_IS_1970 (1) +#define MICROPY_PY_OS_DUPTERM (1) +#define MICROPY_PY_OS_SEP (1) +#define MICROPY_PY_OS_SYNC (1) +#define MICROPY_PY_OS_UNAME (1) +#define MICROPY_PY_TIME (1) +#define MICROPY_PY_MACHINE (1) +#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/alif/modmachine.c" +#define MICROPY_PY_MACHINE_RESET (1) +#define MICROPY_PY_MACHINE_BOOTLOADER (1) +#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1) +#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1) +#define MICROPY_PY_MACHINE_DHT_READINTO (1) +#define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_SOFTI2C (1) +#define MICROPY_PY_MACHINE_SOFTSPI (1) +#define MICROPY_PY_MACHINE_TIMER (1) +#define MICROPY_VFS (1) + +// fatfs configuration +#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ +#define MICROPY_FATFS_RPATH (2) +#if MICROPY_HW_USB_MSC +#define MICROPY_FATFS_USE_LABEL (1) +#define MICROPY_FATFS_MULTI_PARTITION (1) +// Set FatFS block size to flash sector size to avoid caching +// the flash sector in memory to support smaller block sizes. +#define MICROPY_FATFS_MAX_SS (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES) +#endif + +#define MP_STATE_PORT MP_STATE_VM + +// Miscellaneous settings + +// We need an implementation of the log2 function which is not a macro +#define MP_NEED_LOG2 (1) + +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) + +#define MP_SSIZE_MAX (0x7fffffff) + +// Assume that if we already defined the obj repr then we also defined these items +#ifndef MICROPY_OBJ_REPR +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size +typedef intptr_t mp_off_t; +#endif + +// Board configuration settings. + +#ifndef MICROPY_BOARD_STARTUP +#define MICROPY_BOARD_STARTUP() +#endif + +#ifndef MICROPY_BOARD_EARLY_INIT +#define MICROPY_BOARD_EARLY_INIT() +#endif + +#ifndef MICROPY_BOARD_FATAL_ERROR +extern void panic(const char *); +#define MICROPY_BOARD_FATAL_ERROR panic +#endif + +#ifndef MICROPY_BOARD_ENTER_BOOTLOADER +#define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) +#endif diff --git a/ports/alif/mpconfigport.mk b/ports/alif/mpconfigport.mk new file mode 100644 index 0000000000000..3d04450a60fa7 --- /dev/null +++ b/ports/alif/mpconfigport.mk @@ -0,0 +1,12 @@ +# Enable/disable extra modules and features + +# MicroPython feature configurations +MICROPY_ROM_TEXT_COMPRESSION ?= 1 +MICROPY_FLOAT_IMPL ?= double + +# VFS support. +MICROPY_VFS_FAT ?= 1 +MICROPY_VFS_LFS2 ?= 1 + +# File containing description of content to be frozen into firmware. +FROZEN_MANIFEST ?= boards/manifest.py diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c new file mode 100644 index 0000000000000..b433aa5e155d2 --- /dev/null +++ b/ports/alif/mphalport.c @@ -0,0 +1,152 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Damien P. George + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mphal.h" +#include "py/ringbuf.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "extmod/misc.h" +#include "shared/runtime/interrupt_char.h" +#include "shared/timeutils/timeutils.h" +#include "shared/tinyusb/mp_usbd.h" +#include "shared/tinyusb/mp_usbd_cdc.h" +#include "tusb.h" +#include "mpuart.h" + +#ifndef MICROPY_HW_STDIN_BUFFER_LEN +#define MICROPY_HW_STDIN_BUFFER_LEN 512 +#endif + +static uint8_t stdin_ringbuf_array[MICROPY_HW_STDIN_BUFFER_LEN]; +ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array) }; + +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { + uintptr_t ret = 0; + #if MICROPY_HW_USB_CDC + ret |= mp_usbd_cdc_poll_interfaces(poll_flags); + #endif + #if MICROPY_HW_ENABLE_UART_REPL + if (poll_flags & MP_STREAM_POLL_WR) { + ret |= MP_STREAM_POLL_WR; + } + #endif + #if MICROPY_PY_OS_DUPTERM + ret |= mp_os_dupterm_poll(poll_flags); + #endif + return ret; +} + +// Receive single character +int mp_hal_stdin_rx_chr(void) { + for (;;) { + #if MICROPY_HW_USB_CDC + mp_usbd_cdc_poll_interfaces(0); + #endif + int c = ringbuf_get(&stdin_ringbuf); + if (c != -1) { + return c; + } + #if MICROPY_PY_OS_DUPTERM + int dupterm_c = mp_os_dupterm_rx_chr(); + if (dupterm_c >= 0) { + return dupterm_c; + } + #endif + mp_event_wait_indefinite(); + } +} + +// Send string of given length +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { + mp_uint_t ret = len; + bool did_write = false; + + #if MICROPY_HW_ENABLE_UART_REPL + mp_uart_write_strn(str, len); + did_write = true; + #endif + + #if MICROPY_HW_USB_CDC + mp_uint_t cdc_res = mp_usbd_cdc_tx_strn(str, len); + if (cdc_res > 0) { + did_write = true; + ret = MIN(cdc_res, ret); + } + #endif + + #if MICROPY_PY_OS_DUPTERM + int dupterm_res = mp_os_dupterm_tx_strn(str, len); + if (dupterm_res >= 0) { + did_write = true; + ret = MIN((mp_uint_t)dupterm_res, ret); + } + #endif + + return did_write ? ret : 0; +} + +static uint32_t volatile ticks_ms; + +void SysTick_Handler(void) { + ++ticks_ms; +} + +mp_uint_t mp_hal_ticks_cpu(void) { + if (!(DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk)) { + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + #if defined(__CORTEX_M) && __CORTEX_M == 7 + // on Cortex-M7 we must unlock the DWT before writing to its registers + DWT->LAR = 0xc5acce55; + #endif + DWT->CYCCNT = 0; + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + } + return DWT->CYCCNT; +} + +mp_uint_t mp_hal_ticks_us(void) { + return ticks_ms / 1000; +} + +mp_uint_t mp_hal_ticks_ms(void) { + return ticks_ms; +} + +void mp_hal_delay_us(mp_uint_t us) { + mp_hal_delay_ms(us / 1000); +} + +void mp_hal_delay_ms(mp_uint_t ms) { + uint32_t t0 = mp_hal_ticks_ms(); + while ((mp_hal_ticks_ms() - t0) < ms) { + __WFI(); + } +} + +uint64_t mp_hal_time_ns(void) { + return 0; +} diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h new file mode 100644 index 0000000000000..1b8c854fd0b95 --- /dev/null +++ b/ports/alif/mphalport.h @@ -0,0 +1,152 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/ringbuf.h" +#include "shared/runtime/interrupt_char.h" +#include "irq.h" +#include ALIF_CMSIS_H + +#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq() +#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state) + +// For regular code that wants to prevent "background tasks" from running. +// These background tasks (LWIP, Bluetooth) run in PENDSV context. +#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state); + +// Port level Wait-for-Event macro +// +// Do not use this macro directly, include py/runtime.h and +// call mp_event_wait_indefinite() or mp_event_wait_ms(timeout) +#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) \ + do { \ + if ((TIMEOUT_MS) < 0) { \ + __WFE(); \ + } else { \ + /* TODO */ \ + __WFE(); \ + } \ + } while (0) + +// TODO requires mods to py/emitglue.c for this to be picked up +#define MP_HAL_CLEAN_DCACHE(addr, size) \ + (SCB_CleanDCache_by_Addr((uint32_t *)((uint32_t)addr & ~0x1f), \ + ((uint32_t)((uint8_t *)addr + size + 0x1f) & ~0x1f) - ((uint32_t)addr & ~0x1f))) + +extern ringbuf_t stdin_ringbuf; + +// TODO +#define mp_hal_quiet_timing_enter() 0 +#define mp_hal_quiet_timing_exit(x) (void)x +#define mp_hal_delay_us_fast mp_hal_delay_us + +/******************************************************************************/ +// C-level pin HAL + +#include "py/obj.h" +#include "gpio.h" +#include "pinconf.h" + +#define MP_HAL_PIN_FMT "%q" +#define MP_HAL_PIN_MODE_INPUT (0) +#define MP_HAL_PIN_MODE_OUTPUT (1) +#define MP_HAL_PIN_MODE_OPEN_DRAIN (2) +#define MP_HAL_PIN_PULL_NONE (0) +#define MP_HAL_PIN_PULL_UP (1) +#define MP_HAL_PIN_PULL_DOWN (2) + +#define mp_hal_pin_obj_t const machine_pin_obj_t * + +typedef struct _machine_pin_obj_t { + mp_obj_base_t base; + GPIO_Type *gpio; + uint8_t port; + uint8_t pin; + qstr name; +} machine_pin_obj_t; + +mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); + +static inline qstr mp_hal_pin_name(mp_hal_pin_obj_t pin) { + return pin->name; +} + +static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) { + uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0; + uint8_t pad_ctrl = PADCTRL_READ_ENABLE; + pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl); + gpio_set_direction_input(pin->gpio, pin->pin); +} + +static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) { + uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0; + uint8_t pad_ctrl = PADCTRL_READ_ENABLE; + pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl); + gpio_set_direction_output(pin->gpio, pin->pin); +} + +static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) { + uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0; + uint8_t pad_ctrl = PADCTRL_DRIVER_OPEN_DRAIN | PADCTRL_READ_ENABLE; + pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl); + gpio_set_direction_output(pin->gpio, pin->pin); +} + +static inline void mp_hal_pin_low(mp_hal_pin_obj_t pin) { + gpio_set_value_low(pin->gpio, pin->pin); +} + +static inline void mp_hal_pin_high(mp_hal_pin_obj_t pin) { + gpio_set_value_high(pin->gpio, pin->pin); +} + +static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) { + return gpio_get_value(pin->gpio, pin->pin); +} + +static inline void mp_hal_pin_write(mp_hal_pin_obj_t pin, int v) { + if (v) { + mp_hal_pin_high(pin); + } else { + mp_hal_pin_low(pin); + } +} + +static inline void mp_hal_pin_od_low(mp_hal_pin_obj_t pin) { + mp_hal_pin_low(pin); +} + +static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) { + mp_hal_pin_high(pin); +} + +static inline void mp_hal_wake_main_task_from_isr(void) { + // Defined for tinyusb support, nothing needs to be done here. +} + +// Include all the pin definitions. +#include "genhdr/pins_board.h" diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c new file mode 100644 index 0000000000000..e9cfcf34fed53 --- /dev/null +++ b/ports/alif/mpuart.c @@ -0,0 +1,113 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/runtime.h" +#include "py/mphal.h" +#include "mpuart.h" + +#if MICROPY_HW_ENABLE_UART_REPL + +#include "pinconf.h" +#include "sys_ctrl_uart.h" +#include "uart.h" + +#define TX_PORT PORT_12 +#define TX_PIN PIN_2 +#define RX_PORT PORT_12 +#define RX_PIN PIN_1 +#define UART_ID 4 +#define UART_IRQN UART4_IRQ_IRQn +#define UART_PTR ((UART_Type *)UART4_BASE) +#define BAUDRATE 115200 +#define SYST_PCLK 100000000 + +static UART_TRANSFER transfer; + +void mp_uart_init(void) { + pinconf_set(TX_PORT, TX_PIN, PINMUX_ALTERNATE_FUNCTION_2, 0); + pinconf_set(RX_PORT, RX_PIN, PINMUX_ALTERNATE_FUNCTION_2, PADCTRL_READ_ENABLE); + select_uart_clock_syst_pclk(UART_ID); + enable_uart_clock(UART_ID); + uart_software_reset(UART_PTR); + uart_enable_fifo(UART_PTR); + uart_disable_tx_irq(UART_PTR); + uart_disable_rx_irq(UART_PTR); + uart_set_baudrate(UART_PTR, SYST_PCLK, BAUDRATE); + uart_set_data_parity_stop_bits(UART_PTR, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1); + uart_set_flow_control(UART_PTR, UART_FLOW_CONTROL_NONE); + NVIC_ClearPendingIRQ(UART_IRQN); + NVIC_SetPriority(UART_IRQN, IRQ_PRI_UART_REPL); + NVIC_EnableIRQ(UART_IRQN); + uart_set_tx_trigger(UART_PTR, UART_TX_FIFO_EMPTY); + uart_set_rx_trigger(UART_PTR, UART_RX_ONE_CHAR_IN_FIFO); + uart_enable_rx_irq(UART_PTR); +} + +void mp_uart_write_strn(const char *str, size_t len) { + memset(&transfer, 0, sizeof(transfer)); + transfer.tx_buf = (uint8_t *)str; + transfer.tx_total_num = len; + transfer.tx_curr_cnt = 0U; + transfer.status = UART_TRANSFER_STATUS_NONE; + + uart_enable_tx_irq(UART_PTR); + + uint32_t start = mp_hal_ticks_ms(); + while (transfer.status == UART_TRANSFER_STATUS_NONE) { + if (mp_hal_ticks_ms() - start > 10 * len) { + break; + } + __WFE(); + } + uart_disable_tx_irq(UART_PTR); +} + +void UART4_IRQHandler(void) { + if (UART_PTR->UART_RFL) { + for (;;) { + uint32_t rx_fifo_available_cnt = UART_PTR->UART_RFL; + if (rx_fifo_available_cnt == 0) { + break; + } + for (uint32_t i = 0; i < rx_fifo_available_cnt; ++i) { + int c = UART_PTR->UART_RBR; + #if MICROPY_KBD_EXCEPTION + if (c == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + continue; + } + #endif + ringbuf_put(&stdin_ringbuf, c); + } + } + } else { + uart_irq_handler(UART_PTR, &transfer); + } + __SEV(); +} + +#endif diff --git a/ports/alif/mpuart.h b/ports/alif/mpuart.h new file mode 100644 index 0000000000000..0c2e510a94e7c --- /dev/null +++ b/ports/alif/mpuart.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ALIF2_UART_H +#define MICROPY_INCLUDED_ALIF2_UART_H + +void mp_uart_init(void); +void mp_uart_write_strn(const char *str, size_t len); + +#endif // MICROPY_INCLUDED_ALIF2_UART_H diff --git a/ports/alif/msc_disk.c b/ports/alif/msc_disk.c new file mode 100644 index 0000000000000..a01494d20f0e4 --- /dev/null +++ b/ports/alif/msc_disk.c @@ -0,0 +1,151 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mpconfig.h" +#include "py/misc.h" +#include "ospi_flash.h" +#include "tusb.h" + +#if CFG_TUD_MSC + +#if MICROPY_FATFS_MAX_SS != MICROPY_HW_FLASH_BLOCK_SIZE_BYTES +#error MICROPY_FATFS_MAX_SS must be the same size as MICROPY_HW_FLASH_BLOCK_SIZE_BYTES +#endif + +#define BLOCK_SIZE (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES) +#define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_BYTES / BLOCK_SIZE) +#define FLASH_BASE_ADDR (MICROPY_HW_FLASH_STORAGE_BASE_ADDR) + +static bool ejected = false; + +// Invoked on SCSI_CMD_INQUIRY. +// Fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively. +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { + memcpy(vendor_id, MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING), 8)); + memcpy(product_id, MICROPY_HW_USB_MSC_INQUIRY_PRODUCT_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_PRODUCT_STRING), 16)); + memcpy(product_rev, MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING), 4)); +} + +// Invoked on Test-Unit-Ready command. +// Return true allowing host to read/write this LUN (e.g SD card inserted). +bool tud_msc_test_unit_ready_cb(uint8_t lun) { + if (ejected) { + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + return true; +} + +// Invoked on SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size. +void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) { + *block_size = BLOCK_SIZE; + *block_count = BLOCK_COUNT; +} + +// Invoked on Start-Stop-Unit command: +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { + if (load_eject) { + if (start) { + // load disk storage + ejected = false; + } else { + // unload disk storage + ejected = true; + } + } + return true; +} + +// Callback invoked on READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of read bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) { + uint32_t addr = FLASH_BASE_ADDR + lba * BLOCK_SIZE; + uint32_t block_count = bufsize / BLOCK_SIZE; + int ret = ospi_flash_read(addr, block_count * BLOCK_SIZE, buffer); + if (ret < 0) { + return ret; + } + return block_count * BLOCK_SIZE; +} + +// Callback invoked on WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes. +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) { + if (bufsize < BLOCK_SIZE) { + // Workaround for issue with TinyUSB passing in a small buffer. + uint32_t addr = FLASH_BASE_ADDR + lba * BLOCK_SIZE + offset; + if (offset == 0) { + int ret = ospi_flash_erase_sector(addr); + if (ret < 0) { + return ret; + } + } + int ret = ospi_flash_write(addr, bufsize, buffer); + if (ret < 0) { + return ret; + } + return bufsize; + } + + uint32_t addr = FLASH_BASE_ADDR + lba * BLOCK_SIZE; + uint32_t block_count = bufsize / BLOCK_SIZE; + for (uint32_t block = 0; block < block_count; ++block) { + int ret = ospi_flash_erase_sector(addr); + if (ret < 0) { + return ret; + } + ret = ospi_flash_write(addr, BLOCK_SIZE, buffer); + if (ret < 0) { + return ret; + } + addr += BLOCK_SIZE; + buffer += BLOCK_SIZE; + } + return block_count * BLOCK_SIZE; +} + +// Callback invoked on a SCSI command that's not handled by TinyUSB. +int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) { + int32_t resplen = 0; + switch (scsi_cmd[0]) { + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Sync the logical unit if needed. + break; + + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + return resplen; +} + +#endif diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c new file mode 100644 index 0000000000000..fa96053b36579 --- /dev/null +++ b/ports/alif/ospi_flash.c @@ -0,0 +1,265 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mperrno.h" +#include "py/mphal.h" +#include "ospi_flash.h" + +#include "ospi_drv.h" +#include "pinconf.h" + +#define CMD_RDSR (0x05) +#define CMD_WREN (0x06) +#define CMD_SEC_ERASE_32ADDR (0x21) // 4kiB sector erase with 32-bit address +#define CMD_WRVOL (0x81) +#define CMD_RD_DEVID (0x9f) + +#define WAIT_SR_TIMEOUT (1000000) + +// maximum bytes we can write in one SPI transfer +// limited by 256 byte FIFO buffer (can't go up to 256) +// need to use DMA to make this 256 +#define PAGE_SIZE (128) + +#define ISSI_MODE_OCTAL_DDR_DQS (0xe7) + +// All OSPI1 pins use the same alternate function. +#define OSPI1_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1 + +typedef struct _mp_spiflash_t { + ospi_flash_cfg_t cfg; +} mp_spiflash_t; + +static mp_spiflash_t global_flash; + +// Alif version of this function can overwrite the destination buffer. +static void ospi_recv_blocking2(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer) { + uint32_t val; + + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return; + } + } + val = ospi_readl(ospi_cfg, data_reg); + *buffer++ = (uint8_t)val; + ospi_cfg->rx_cnt++; + } +} + +static int mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len, uint8_t *dest) { + ospi_setup_read(&self->cfg, 0, len, 8); + ospi_recv_blocking2(&self->cfg, cmd, dest); + return 0; +} + +static int mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) { + ospi_setup_write(&self->cfg, 0); + ospi_send_blocking(&self->cfg, cmd); + return 0; +} + +static int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) { + do { + uint8_t sr; + int ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr); + if (ret != 0) { + return ret; + } + if ((sr & mask) == val) { + return 0; // success + } + } while (timeout--); + + return -MP_ETIMEDOUT; +} + +static int mp_spiflash_wait_wel1(mp_spiflash_t *self) { + return mp_spiflash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT); +} + +static int mp_spiflash_wait_wip0(mp_spiflash_t *self) { + return mp_spiflash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT); +} + +static uint32_t ospi_flash_read_id(mp_spiflash_t *self) { + uint8_t buf[8]; + ospi_setup_read(&self->cfg, 0, 3, ospi_flash_settings.read_id_dummy_cycles); + ospi_recv_blocking2(&self->cfg, CMD_RD_DEVID, buf); + return buf[0] | buf[1] << 8 | buf[2] << 16; +} + +static void ospi_flash_write_reg_sdr(mp_spiflash_t *self, uint8_t cmd, uint8_t addr, uint8_t value) { + mp_spiflash_write_cmd(self, CMD_WREN); + ospi_setup_write_sdr(&self->cfg, 6); + ospi_push(&self->cfg, cmd); + ospi_push(&self->cfg, 0x00); + ospi_push(&self->cfg, 0x00); + ospi_push(&self->cfg, addr); + ospi_send_blocking(&self->cfg, value); +} + +int ospi_flash_init(void) { + mp_spiflash_t *self = &global_flash; + + uint32_t pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; + + pinconf_set(pin_OSPI1_CS->port, pin_OSPI1_CS->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); + pinconf_set(pin_OSPI1_SCLK->port, pin_OSPI1_SCLK->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST); + pinconf_set(pin_OSPI1_D0->port, pin_OSPI1_D0->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D1->port, pin_OSPI1_D1->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D2->port, pin_OSPI1_D2->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D3->port, pin_OSPI1_D3->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + #if defined(pin_OSPI1_D4) + pinconf_set(pin_OSPI1_D4->port, pin_OSPI1_D4->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D5->port, pin_OSPI1_D5->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D6->port, pin_OSPI1_D6->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D7->port, pin_OSPI1_D7->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + #endif + #if defined(pin_OSPI1_RXDS) + pinconf_set(pin_OSPI1_RXDS->port, pin_OSPI1_RXDS->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + #endif + + #if defined(pin_OSPI1_RXDS) + if (pin_OSPI1_RXDS->port == PORT_10 && pin_OSPI1_RXDS->pin == PIN_7) { + // Alif: P5_6 is needed to support proper alt function selection of P10_7. + pinconf_set(PORT_5, PIN_6, OSPI1_PIN_FUNCTION, pad_ctrl); + } + #endif + + // Reset the SPI flash. + mp_hal_pin_output(pin_OSPI1_RESET); + mp_hal_pin_low(pin_OSPI1_RESET); + mp_hal_delay_us(30); + mp_hal_pin_high(pin_OSPI1_RESET); + + // Configure the OSPI peripheral. + self->cfg.regs = (ssi_regs_t *)OSPI1_BASE; + self->cfg.aes_regs = (aes_regs_t *)AES1_BASE; + self->cfg.xip_base = (volatile void *)OSPI1_XIP_BASE; + self->cfg.ser = 1; + self->cfg.addrlen = 8; // 32-bit address length + self->cfg.ospi_clock = ospi_flash_settings.freq_mhz; + self->cfg.ddr_en = 0; + self->cfg.wait_cycles = 0; // used only for ospi_xip_exit + ospi_init(&self->cfg); + + if (ospi_flash_settings.is_oct && ospi_flash_settings.is_ddr) { + // Switch SPI flash to Octal DDR mode. + ospi_flash_write_reg_sdr(self, CMD_WRVOL, 0x00, ISSI_MODE_OCTAL_DDR_DQS); + self->cfg.ddr_en = 1; + } + + // Check the device ID. + if (ospi_flash_read_id(self) != ospi_flash_settings.jedec_id) { + return -1; + } + + return 0; +} + +int ospi_flash_erase_sector(uint32_t addr) { + mp_spiflash_t *self = &global_flash; + + mp_spiflash_write_cmd(self, CMD_WREN); + int ret = mp_spiflash_wait_wel1(self); + if (ret < 0) { + return ret; + } + + ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/); + ospi_push(&self->cfg, CMD_SEC_ERASE_32ADDR); + ospi_send_blocking(&self->cfg, addr); + + return mp_spiflash_wait_wip0(self); +} + +int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { + // OSPI FIFO is limited to 256 bytes. Need DMA to get a longer read. + mp_spiflash_t *self = &global_flash; + + while (len) { + uint32_t l = len; + if (l > 256) { + l = 256; + } + ospi_setup_read(&self->cfg, 8 /* 32-bit addr len*/, l, ospi_flash_settings.read_dummy_cycles); + ospi_push(&self->cfg, ospi_flash_settings.read_command); + ospi_recv_blocking2(&self->cfg, addr, dest); + addr += l; + len -= l; + dest += l; + } + + return 0; +} + +static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src) { + mp_spiflash_t *self = &global_flash; + + mp_spiflash_write_cmd(self, CMD_WREN); + int ret = mp_spiflash_wait_wel1(self); + if (ret < 0) { + return ret; + } + + ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/); + ospi_push(&self->cfg, ospi_flash_settings.write_command); + ospi_push(&self->cfg, addr); + while (--len) { + ospi_push(&self->cfg, *src++); + } + ospi_send_blocking(&self->cfg, *src); + + return mp_spiflash_wait_wip0(self); +} + +int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { + int ret = 0; + uint32_t offset = addr & (PAGE_SIZE - 1); + while (len) { + size_t rest = PAGE_SIZE - offset; + if (rest > len) { + rest = len; + } + ret = ospi_flash_write_page(addr, rest, src); + if (ret != 0) { + break; + } + len -= rest; + addr += rest; + src += rest; + offset = 0; + } + return ret; +} diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h new file mode 100644 index 0000000000000..945dc4bb07ba4 --- /dev/null +++ b/ports/alif/ospi_flash.h @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_OSPI_FLASH_H +#define MICROPY_INCLUDED_ALIF_OSPI_FLASH_H + +#include +#include + +typedef struct _ospi_flash_settings_t { + uint32_t jedec_id; + uint32_t freq_mhz; + bool is_quad : 1; + bool is_oct : 1; + bool is_ddr : 1; + uint8_t read_id_dummy_cycles; + uint8_t read_dummy_cycles; + uint8_t read_command; + uint8_t write_command; +} ospi_flash_settings_t; + +// Provided by the board when it enables OSPI1. +extern const ospi_flash_settings_t ospi_flash_settings; + +int ospi_flash_init(void); +int ospi_flash_erase_sector(uint32_t addr); +int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest); +int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src); + +#endif // MICROPY_INCLUDED_ALIF_OSPI_FLASH_H diff --git a/ports/alif/ospi_xip_user.h b/ports/alif/ospi_xip_user.h new file mode 100644 index 0000000000000..a15baff2a8ba4 --- /dev/null +++ b/ports/alif/ospi_xip_user.h @@ -0,0 +1,5 @@ +// This file is needed by ospi_xip/source/ospi/ospi_drv.c. +#define OSPI_XIP_ENABLE_AES_DECRYPTION (0) +#define OSPI_XIP_RX_SAMPLE_DELAY (4) +#define OSPI_XIP_DDR_DRIVE_EDGE (1) +#define OSPI_XIP_RXDS_DELAY (12) diff --git a/ports/alif/pendsv.c b/ports/alif/pendsv.c new file mode 100644 index 0000000000000..965053e6c595a --- /dev/null +++ b/ports/alif/pendsv.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "pendsv.h" +#include "irq.h" + +static pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS]; + +void pendsv_init(void) { + NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV); +} + +void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { + pendsv_dispatch_table[slot] = f; + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; +} + +void PendSV_Handler(void) { + for (size_t i = 0; i < PENDSV_DISPATCH_NUM_SLOTS; ++i) { + if (pendsv_dispatch_table[i] != NULL) { + pendsv_dispatch_t f = pendsv_dispatch_table[i]; + pendsv_dispatch_table[i] = NULL; + f(); + } + } +} diff --git a/ports/alif/pendsv.h b/ports/alif/pendsv.h new file mode 100644 index 0000000000000..43a4b7a853846 --- /dev/null +++ b/ports/alif/pendsv.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_PENDSV_H +#define MICROPY_INCLUDED_ALIF_PENDSV_H + +#include "py/mpconfig.h" + +#ifndef MICROPY_BOARD_PENDSV_ENTRIES +#define MICROPY_BOARD_PENDSV_ENTRIES +#endif + +enum { + PENDSV_DISPATCH_SOFT_TIMER, + MICROPY_BOARD_PENDSV_ENTRIES + PENDSV_DISPATCH_MAX +}; + +#define PENDSV_DISPATCH_NUM_SLOTS PENDSV_DISPATCH_MAX + +typedef void (*pendsv_dispatch_t)(void); + +void pendsv_init(void); +void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f); + +#endif // MICROPY_INCLUDED_ALIF_PENDSV_H diff --git a/ports/alif/qstrdefsport.h b/ports/alif/qstrdefsport.h new file mode 100644 index 0000000000000..00d3e2ae3c555 --- /dev/null +++ b/ports/alif/qstrdefsport.h @@ -0,0 +1,2 @@ +// qstrs specific to this port +// *FORMAT-OFF* diff --git a/ports/alif/tinyusb_port/tusb_config.h b/ports/alif/tinyusb_port/tusb_config.h new file mode 100644 index 0000000000000..d244b4c241e6b --- /dev/null +++ b/ports/alif/tinyusb_port/tusb_config.h @@ -0,0 +1,73 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + */ + +#ifndef _ALIF_TUSB_CONFIG_H_ +#define _ALIF_TUSB_CONFIG_H_ + +// --------------------------------------------------------------------+ +// Board Specific Configuration +// --------------------------------------------------------------------+ + +#define CFG_TUSB_MCU OPT_MCU_NONE +// #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +#define TUP_DCD_ENDPOINT_MAX 8 +#define TUD_OPT_RHPORT 0 + +// -------------------------------------------------------------------- +// COMMON CONFIGURATION +// -------------------------------------------------------------------- + +#define CFG_TUSB_OS OPT_OS_NONE +#define CFG_TUSB_DEBUG 0 + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED OPT_MODE_HIGH_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + */ +#define CFG_TUSB_MEM_SECTION __attribute__((section(".bss.sram0"))) + +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(32))) + +// -------------------------------------------------------------------- +// DEVICE CONFIGURATION +// -------------------------------------------------------------------- + +#define CFG_TUD_ENDPOINT0_SIZE 64 + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE (4096) +#define CFG_TUD_CDC_RX_BUFSIZE (4096) +#define CFG_TUD_CDC_TX_BUFSIZE (4096) + +#include "shared/tinyusb/tusb_config.h" + +#endif /* _ALIF_TUSB_CONFIG_H_ */ diff --git a/ports/alif/usbd.c b/ports/alif/usbd.c new file mode 100644 index 0000000000000..9df6610683507 --- /dev/null +++ b/ports/alif/usbd.c @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mpconfig.h" + +#if MICROPY_HW_ENABLE_USBDEV + +#include "shared/tinyusb/mp_usbd.h" +#include "tusb.h" + +void mp_usbd_port_get_serial_number(char *serial_buf) { + // TODO + uint8_t id[8] = "ABCDEFGH"; + MP_STATIC_ASSERT(sizeof(id) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); + mp_usbd_hex_str(serial_buf, id, sizeof(id)); +} + +#endif From 40ff0c2f276883ea325ce8977453bd16a2f2029a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Jan 2024 14:37:52 +1100 Subject: [PATCH 10/77] alif/system_tick: Use a UTIMER for system ticks and timing. Includes an implementation of `system_tick_wfe_with_timeout_us()`. Signed-off-by: Damien George --- ports/alif/Makefile | 2 + ports/alif/main.c | 3 +- ports/alif/mphalport.c | 36 +++++------ ports/alif/system_tick.c | 128 +++++++++++++++++++++++++++++++++++++++ ports/alif/system_tick.h | 38 ++++++++++++ 5 files changed, 184 insertions(+), 23 deletions(-) create mode 100644 ports/alif/system_tick.c create mode 100644 ports/alif/system_tick.h diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 2863ebe2c4d61..cd02f4ac2d61c 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -119,6 +119,7 @@ SRC_C = \ msc_disk.c \ ospi_flash.c \ pendsv.c \ + system_tick.c \ usbd.c \ $(wildcard $(BOARD_DIR)/*.c) @@ -175,6 +176,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/pinconf.c \ drivers/source/uart.c \ + drivers/source/utimer.c \ ospi_xip/source/ospi/ospi_drv.c \ ) diff --git a/ports/alif/main.c b/ports/alif/main.c index 0eb43a38268a6..9293d74c0b80d 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -38,6 +38,7 @@ #include "mpuart.h" #include "ospi_flash.h" #include "pendsv.h" +#include "system_tick.h" extern uint8_t __StackTop, __StackLimit; extern uint8_t __GcHeapStart, __GcHeapEnd; @@ -53,7 +54,7 @@ NORETURN void panic(const char *msg) { } void _start(void) { - SysTick_Config(SystemCoreClock / 1000); + system_tick_init(); MICROPY_BOARD_STARTUP(); diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index b433aa5e155d2..abb71aae69624 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -36,6 +36,7 @@ #include "shared/tinyusb/mp_usbd_cdc.h" #include "tusb.h" #include "mpuart.h" +#include "system_tick.h" #ifndef MICROPY_HW_STDIN_BUFFER_LEN #define MICROPY_HW_STDIN_BUFFER_LEN 512 @@ -109,42 +110,33 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { return did_write ? ret : 0; } -static uint32_t volatile ticks_ms; - -void SysTick_Handler(void) { - ++ticks_ms; -} - mp_uint_t mp_hal_ticks_cpu(void) { - if (!(DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk)) { - CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; - #if defined(__CORTEX_M) && __CORTEX_M == 7 - // on Cortex-M7 we must unlock the DWT before writing to its registers - DWT->LAR = 0xc5acce55; - #endif - DWT->CYCCNT = 0; - DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; - } - return DWT->CYCCNT; + return system_tick_get_u32(); } mp_uint_t mp_hal_ticks_us(void) { - return ticks_ms / 1000; + // Convert system tick to microsecond counter. + return system_tick_get_u64() / system_core_clock_mhz; } mp_uint_t mp_hal_ticks_ms(void) { - return ticks_ms; + // Convert system tick to millisecond counter. + return system_tick_get_u64() / (SystemCoreClock / 1000); } void mp_hal_delay_us(mp_uint_t us) { - mp_hal_delay_ms(us / 1000); + uint64_t ticks_delay = (uint64_t)us * system_core_clock_mhz; + uint64_t start = system_tick_get_u64(); + while (system_tick_get_u64() - start < ticks_delay) { + } } void mp_hal_delay_ms(mp_uint_t ms) { uint32_t t0 = mp_hal_ticks_ms(); - while ((mp_hal_ticks_ms() - t0) < ms) { - __WFI(); - } + do { + // TODO: power saving wait + mp_event_handle_nowait(); + } while (mp_hal_ticks_ms() - t0 < ms); } uint64_t mp_hal_time_ns(void) { diff --git a/ports/alif/system_tick.c b/ports/alif/system_tick.c new file mode 100644 index 0000000000000..95d5391c03855 --- /dev/null +++ b/ports/alif/system_tick.c @@ -0,0 +1,128 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "irq.h" +#include "system_tick.h" + +#include "utimer.h" + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define UTIMER ((UTIMER_Type *)UTIMER_BASE) +#define UTIMER_CHANNEL (11) + +uint64_t system_core_clock_mhz; +static volatile uint32_t system_tick_hi; + +static void system_tick_nvic_config(unsigned int index) { + NVIC_ClearPendingIRQ(UTIMER_IRQ0_IRQn + UTIMER_CHANNEL * 8 + index); + NVIC_SetPriority(UTIMER_IRQ0_IRQn + UTIMER_CHANNEL * 8 + index, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(UTIMER_IRQ0_IRQn + UTIMER_CHANNEL * 8 + index); +} + +void system_tick_init(void) { + system_tick_hi = 0; + system_core_clock_mhz = SystemCoreClock / 1000000; + + // Configure NVIC OVER_FLOW interrupt. + system_tick_nvic_config(7); + + utimer_clock_enable(UTIMER, UTIMER_CHANNEL); + utimer_channel_config cfg = { 0 }; + cfg.fixed_buffer = false; + utimer_config_direction(UTIMER, UTIMER_CHANNEL, UTIMER_COUNTER_UP, &cfg); + utimer_set_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR_PTR, 0xffffffff); + utimer_unmask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_OVER_FLOW_MASK); + utimer_control_enable(UTIMER, UTIMER_CHANNEL); + utimer_counter_start(UTIMER, UTIMER_CHANNEL); + + // Set up the UTIMER compare interrupt, to be used later. + system_tick_nvic_config(2); + UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_COMPARE_CTRL_A |= COMPARE_CTRL_DRV_COMPARE_EN; +} + +// COMPARE_A_BUF1 +void UTIMER_IRQ90Handler(void) { + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + if (chan_interrupt & CHAN_INTERRUPT_COMPARE_A_BUF1_MASK) { + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); + __SEV(); + } +} + +// OVER_FLOW +void UTIMER_IRQ95Handler(void) { + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + if (chan_interrupt & CHAN_INTERRUPT_OVER_FLOW_MASK) { + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_OVER_FLOW_MASK); + ++system_tick_hi; + } +} + +uint32_t system_tick_get_u32(void) { + return utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); +} + +uint64_t system_tick_get_u64(void) { + uint32_t irq_state = disable_irq(); + uint32_t ticks_lo = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + uint32_t ticks_hi = system_tick_hi; + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + enable_irq(irq_state); + + if (chan_interrupt & CHAN_INTERRUPT_OVER_FLOW_MASK) { + // The timer had an overflow while interrupts were disabled. + if (ticks_lo < 0x80000000) { + // The timer had an overflow just before we sampled it. + ++ticks_hi; + } + } + + // This ticks runs at SystemCoreClock. + return (uint64_t)ticks_hi << 32 | ticks_lo; +} + +void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { + // Maximum 10 second timeout, to not overflow signed 32-bit ticks when + // system_core_clock_mhz==400. + uint32_t timeout_ticks = MIN(timeout_us, 10000000) * system_core_clock_mhz; + + // Set up the UTIMER compare interrupt to fire after the given timeout. + uint32_t cntr = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + utimer_set_count(UTIMER, UTIMER_CHANNEL, UTIMER_COMPARE_A_BUF1, cntr + timeout_ticks); + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); + utimer_unmask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); + + // Wait for an event (or compare timeout event) if the timeout hasn't expired yet + // (this check handles the case of short timeouts). + uint32_t cntr2 = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + if ((uint32_t)(cntr2 - cntr) < timeout_ticks) { + __WFE(); + } + + // Disable the UTIMER compare interrupt. + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); +} diff --git a/ports/alif/system_tick.h b/ports/alif/system_tick.h new file mode 100644 index 0000000000000..307c87a345fb8 --- /dev/null +++ b/ports/alif/system_tick.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H +#define MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H + +#include + +extern uint64_t system_core_clock_mhz; + +void system_tick_init(void); +uint32_t system_tick_get_u32(void); +uint64_t system_tick_get_u64(void); +void system_tick_wfe_with_timeout_us(uint32_t timeout_us); + +#endif // MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H From 975f84f2adc1f89b4c9349b6a6e83f6bc4ec2fd6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Jan 2024 16:08:45 +1100 Subject: [PATCH 11/77] alif/mphalport: Enable efficient events and implement quiet timing. Signed-off-by: Damien George --- ports/alif/mphalport.c | 12 ++++++++---- ports/alif/mphalport.h | 13 ++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index abb71aae69624..8d0fa1b92ba4a 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -133,10 +133,14 @@ void mp_hal_delay_us(mp_uint_t us) { void mp_hal_delay_ms(mp_uint_t ms) { uint32_t t0 = mp_hal_ticks_ms(); - do { - // TODO: power saving wait - mp_event_handle_nowait(); - } while (mp_hal_ticks_ms() - t0 < ms); + mp_event_handle_nowait(); + for (;;) { + uint32_t t1 = mp_hal_ticks_ms(); + if (t1 - t0 >= ms) { + break; + } + mp_event_wait_ms(ms - (t1 - t0)); + } } uint64_t mp_hal_time_ns(void) { diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 1b8c854fd0b95..8098c2650df01 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -27,6 +27,7 @@ #include "py/ringbuf.h" #include "shared/runtime/interrupt_char.h" #include "irq.h" +#include "system_tick.h" #include ALIF_CMSIS_H #define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq() @@ -47,8 +48,7 @@ if ((TIMEOUT_MS) < 0) { \ __WFE(); \ } else { \ - /* TODO */ \ - __WFE(); \ + system_tick_wfe_with_timeout_us(TIMEOUT_MS * 1000); \ } \ } while (0) @@ -57,13 +57,12 @@ (SCB_CleanDCache_by_Addr((uint32_t *)((uint32_t)addr & ~0x1f), \ ((uint32_t)((uint8_t *)addr + size + 0x1f) & ~0x1f) - ((uint32_t)addr & ~0x1f))) -extern ringbuf_t stdin_ringbuf; - -// TODO -#define mp_hal_quiet_timing_enter() 0 -#define mp_hal_quiet_timing_exit(x) (void)x +#define mp_hal_quiet_timing_enter() raise_irq_pri(IRQ_PRI_QUIET_TIMING) +#define mp_hal_quiet_timing_exit(irq_state) restore_irq_pri(irq_state) #define mp_hal_delay_us_fast mp_hal_delay_us +extern ringbuf_t stdin_ringbuf; + /******************************************************************************/ // C-level pin HAL From ada0939c5f474e7204b6d3468bd27603daea676b Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Jan 2024 23:41:00 +1100 Subject: [PATCH 12/77] alif/system_tick: Integrate soft timer. Signed-off-by: Damien George --- ports/alif/main.c | 2 ++ ports/alif/mphalport.c | 17 ++++++++++++++++ ports/alif/system_tick.c | 44 ++++++++++++++++++++++++++++++++++++++-- ports/alif/system_tick.h | 2 ++ 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/ports/alif/main.c b/ports/alif/main.c index 9293d74c0b80d..b4e2434399970 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -33,6 +33,7 @@ #include "shared/readline/readline.h" #include "shared/runtime/gchelper.h" #include "shared/runtime/pyexec.h" +#include "shared/runtime/softtimer.h" #include "shared/tinyusb/mp_usbd.h" #include "tusb.h" #include "mpuart.h" @@ -119,6 +120,7 @@ void _start(void) { soft_reset_exit: mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + soft_timer_deinit(); gc_sweep_all(); mp_deinit(); } diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 8d0fa1b92ba4a..ba58923f2fe2a 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -31,11 +31,13 @@ #include "py/stream.h" #include "extmod/misc.h" #include "shared/runtime/interrupt_char.h" +#include "shared/runtime/softtimer.h" #include "shared/timeutils/timeutils.h" #include "shared/tinyusb/mp_usbd.h" #include "shared/tinyusb/mp_usbd_cdc.h" #include "tusb.h" #include "mpuart.h" +#include "pendsv.h" #include "system_tick.h" #ifndef MICROPY_HW_STDIN_BUFFER_LEN @@ -146,3 +148,18 @@ void mp_hal_delay_ms(mp_uint_t ms) { uint64_t mp_hal_time_ns(void) { return 0; } + +void system_tick_schedule_callback(void) { + pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); +} + +uint32_t soft_timer_get_ms(void) { + return mp_hal_ticks_ms(); +} + +void soft_timer_schedule_at_ms(uint32_t ticks_ms) { + int32_t ms = soft_timer_ticks_diff(ticks_ms, mp_hal_ticks_ms()); + ms = MAX(0, ms); + ms = MIN(ms, 4000000); // ensure ms * 1000 doesn't overflow + system_tick_schedule_after_us(ms * 1000); +} diff --git a/ports/alif/system_tick.c b/ports/alif/system_tick.c index 95d5391c03855..5cdf45cba0126 100644 --- a/ports/alif/system_tick.c +++ b/ports/alif/system_tick.c @@ -59,9 +59,13 @@ void system_tick_init(void) { utimer_control_enable(UTIMER, UTIMER_CHANNEL); utimer_counter_start(UTIMER, UTIMER_CHANNEL); - // Set up the UTIMER compare interrupt, to be used later. + // Set up the UTIMER compare A interrupt, to be used by system_tick_wfe_with_timeout_us. system_tick_nvic_config(2); UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_COMPARE_CTRL_A |= COMPARE_CTRL_DRV_COMPARE_EN; + + // Set up the UTIMER compare B interrupt, to be used by soft-timer. + system_tick_nvic_config(4); + UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_COMPARE_CTRL_B |= COMPARE_CTRL_DRV_COMPARE_EN; } // COMPARE_A_BUF1 @@ -73,6 +77,17 @@ void UTIMER_IRQ90Handler(void) { } } +// COMPARE_B_BUF1 +void UTIMER_IRQ92Handler(void) { + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + if (chan_interrupt & CHAN_INTERRUPT_COMPARE_B_BUF1_MASK) { + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + system_tick_schedule_callback(); + __SEV(); + } +} + // OVER_FLOW void UTIMER_IRQ95Handler(void) { uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; @@ -106,7 +121,7 @@ uint64_t system_tick_get_u64(void) { } void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { - // Maximum 10 second timeout, to not overflow signed 32-bit ticks when + // Maximum 10 second timeout, to not overflow 32-bit ticks when // system_core_clock_mhz==400. uint32_t timeout_ticks = MIN(timeout_us, 10000000) * system_core_clock_mhz; @@ -126,3 +141,28 @@ void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { // Disable the UTIMER compare interrupt. utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); } + +void system_tick_schedule_after_us(uint32_t ticks_us) { + // Disable the interrupt in case it's still active. + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + + // Maximum 10 second timeout, to not overflow 32-bit ticks when + // system_core_clock_mhz==400. + uint32_t timeout_ticks = MIN(ticks_us, 10000000) * system_core_clock_mhz; + + // Set up the UTIMER compare interrupt to fire after the given timeout. + uint32_t cntr = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + utimer_set_count(UTIMER, UTIMER_CHANNEL, UTIMER_COMPARE_B_BUF1, cntr + timeout_ticks); + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + utimer_unmask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + + // Handle the case of short timeouts. + uint32_t cntr2 = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + if ((uint32_t)(cntr2 - cntr) >= timeout_ticks) { + if (!(UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT_MASK & CHAN_INTERRUPT_COMPARE_B_BUF1_MASK)) { + // Interrupt is still enabled, so disable it and manually call the callback. + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + system_tick_schedule_callback(); + } + } +} diff --git a/ports/alif/system_tick.h b/ports/alif/system_tick.h index 307c87a345fb8..2373f9421de7b 100644 --- a/ports/alif/system_tick.h +++ b/ports/alif/system_tick.h @@ -34,5 +34,7 @@ void system_tick_init(void); uint32_t system_tick_get_u32(void); uint64_t system_tick_get_u64(void); void system_tick_wfe_with_timeout_us(uint32_t timeout_us); +void system_tick_schedule_after_us(uint32_t ticks_us); +void system_tick_schedule_callback(void); #endif // MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H From 53b7c14836b00a432855439513721dcf07392041 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Jan 2024 23:41:10 +1100 Subject: [PATCH 13/77] alif/modmachine: Enable machine.Timer. Signed-off-by: Damien George --- ports/alif/modmachine.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index c4102225dabfc..e19150e010d0b 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -29,6 +29,7 @@ #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ + { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, \ static void mp_machine_idle(void) { mp_event_wait_indefinite(); From 4e62ade4421337944a12a53c05ddd4498ba3be8f Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jan 2024 12:00:18 +1100 Subject: [PATCH 14/77] alif/se_services: Add SE services interface. Includes services to get random numbers, reset SoC, get unique-id, dump SoC info, and CPU control services. Signed-off-by: Damien George Signed-off-by: iabdalkader --- ports/alif/Makefile | 12 ++ ports/alif/irq.h | 1 + ports/alif/main.c | 2 + ports/alif/se_services.c | 247 +++++++++++++++++++++++++++++++++++++++ ports/alif/se_services.h | 47 ++++++++ 5 files changed, 309 insertions(+) create mode 100644 ports/alif/se_services.c create mode 100644 ports/alif/se_services.h diff --git a/ports/alif/Makefile b/ports/alif/Makefile index cd02f4ac2d61c..656cdc0e4cd44 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -42,6 +42,7 @@ INC += -I$(BUILD) INC += -I$(BOARD_DIR) INC += -I$(CMSIS_DIR) INC += -I$(ALIF_DFP_REL_HERE)/drivers/include/ +INC += -I$(ALIF_DFP_REL_HERE)/se_services/include/ INC += -I$(ALIF_DFP_REL_HERE)/ospi_xip/source/ospi INC += -I$(ALIF_DFP_REL_HERE)/Device/common/config/ INC += -I$(ALIF_DFP_REL_HERE)/Device/common/include/ @@ -120,6 +121,7 @@ SRC_C = \ ospi_flash.c \ pendsv.c \ system_tick.c \ + se_services.c \ usbd.c \ $(wildcard $(BOARD_DIR)/*.c) @@ -174,13 +176,23 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/common/source/system_M55.c \ Device/common/source/system_utils.c \ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ + drivers/source/mhu_driver.c \ + drivers/source/mhu_receiver.c \ + drivers/source/mhu_sender.c \ drivers/source/pinconf.c \ drivers/source/uart.c \ drivers/source/utimer.c \ ospi_xip/source/ospi/ospi_drv.c \ + se_services/source/services_host_application.c \ + se_services/source/services_host_boot.c \ + se_services/source/services_host_clocks.c \ + se_services/source/services_host_cryptocell.c \ + se_services/source/services_host_handler.c \ + se_services/source/services_host_system.c \ ) $(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 +$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation # List of sources for qstr extraction SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC) diff --git a/ports/alif/irq.h b/ports/alif/irq.h index 723f89f1d907e..e2e0685c679a9 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -40,6 +40,7 @@ #define NVIC_PRIORITYGROUP_7 ((uint32_t)0x00000000U) #define IRQ_PRI_SYSTEM_TICK NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 0, 0) +#define IRQ_PRI_MHU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 0, 0) #define IRQ_PRI_QUIET_TIMING NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) diff --git a/ports/alif/main.c b/ports/alif/main.c index b4e2434399970..2d3134176d32e 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -39,6 +39,7 @@ #include "mpuart.h" #include "ospi_flash.h" #include "pendsv.h" +#include "se_services.h" #include "system_tick.h" extern uint8_t __StackTop, __StackLimit; @@ -60,6 +61,7 @@ void _start(void) { MICROPY_BOARD_STARTUP(); pendsv_init(); + se_services_init(); MICROPY_BOARD_EARLY_INIT(); diff --git a/ports/alif/se_services.c b/ports/alif/se_services.c new file mode 100644 index 0000000000000..cbf66bfac6ccd --- /dev/null +++ b/ports/alif/se_services.c @@ -0,0 +1,247 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 + +#include "irq.h" +#include "se_services.h" + +#include "mhu.h" +#include "services_lib_bare_metal.h" +#include "services_lib_protocol.h" + +#include "py/mphal.h" + +// MHU indices. +#define MHU_M55_SE_MHU0 0 +#define MAX_MHU 1 + +// The following timeout is implemented in se_services_handle.c as a +// simple loop busy polling on a variable set from an IRQ. +#define TIMEOUT 10000000 + +typedef struct { + volatile unsigned int RST_CTRL; // 0x1A010318 + volatile unsigned int RST_STAT; // 0x1A01031C +} CPU_Type; + +// HE CPU register flags +#define RST_CTRL_CPUWAIT_MASK (1 << 0) +#define RST_CTRL_RST_REQ_MASK (1 << 1) +#define RST_STAT_RST_ACK_MASK (3 << 1) +#define HE_CPU ((CPU_Type *)0x1A010318) + +static const uint32_t mhu_sender_base_address_list[MAX_MHU] = { + MHU_SESS_S_TX_BASE, +}; + +static const uint32_t mhu_receiver_base_address_list[MAX_MHU] = { + MHU_SESS_S_RX_BASE, +}; + +// Must be aligned as a uint32_t. +static uint32_t packet_buffer[SERVICES_MAX_PACKET_BUFFER_SIZE / sizeof(uint32_t)]; + +static mhu_driver_out_t mhu_driver_out; +static uint32_t se_services_handle; + +void MHU_SESS_S_TX_IRQHandler(void) { + mhu_driver_out.sender_irq_handler(MHU_M55_SE_MHU0); +} + +void MHU_SESS_S_RX_IRQHandler(void) { + mhu_driver_out.receiver_irq_handler(MHU_M55_SE_MHU0); +} + +int dummy_printf(const char *fmt, ...) { + (void)fmt; + return 0; +} + +void se_services_init(void) { + // Initialize MHU. + mhu_driver_in_t mhu_driver_in; + mhu_driver_in.sender_base_address_list = (uint32_t *)mhu_sender_base_address_list; + mhu_driver_in.receiver_base_address_list = (uint32_t *)mhu_receiver_base_address_list; + mhu_driver_in.mhu_count = MAX_MHU; + mhu_driver_in.send_msg_acked_callback = SERVICES_send_msg_acked_callback; + mhu_driver_in.rx_msg_callback = SERVICES_rx_msg_callback; + mhu_driver_in.debug_print = NULL; // not currently used by MHU_driver_initialize + MHU_driver_initialize(&mhu_driver_in, &mhu_driver_out); + + // Initialize SE services. + services_lib_t services_init_params = { + .packet_buffer_address = (uint32_t)packet_buffer, + .fn_send_mhu_message = mhu_driver_out.send_message, + .fn_wait_ms = NULL, // not currently used by services_host_handler.c + .wait_timeout = TIMEOUT, + .fn_print_msg = dummy_printf, + }; + SERVICES_initialize(&services_init_params); + + // Create SE services channel for sending requests. + se_services_handle = SERVICES_register_channel(MHU_M55_SE_MHU0, 0); + + // Enable MHU interrupts. + NVIC_ClearPendingIRQ(MHU_SESS_S_RX_IRQ_IRQn); + NVIC_SetPriority(MHU_SESS_S_RX_IRQ_IRQn, IRQ_PRI_MHU); + NVIC_EnableIRQ(MHU_SESS_S_RX_IRQ_IRQn); + NVIC_ClearPendingIRQ(MHU_SESS_S_TX_IRQ_IRQn); + NVIC_SetPriority(MHU_SESS_S_TX_IRQ_IRQn, IRQ_PRI_MHU); + NVIC_EnableIRQ(MHU_SESS_S_TX_IRQ_IRQn); + + // Send heartbeat services requests until one succeeds. + SERVICES_synchronize_with_se(se_services_handle); +} + +void se_services_dump_device_data(void) { + uint32_t error_code; + + uint8_t revision[80]; + SERVICES_get_se_revision(se_services_handle, revision, &error_code); + + SERVICES_version_data_t data; + SERVICES_system_get_device_data(se_services_handle, &data, &error_code); + + printf("SE revision: %s\n", revision); + printf("ALIF_PN: %s\n", data.ALIF_PN); + printf("Raw device data:\n"); + for (int i = 0; i < sizeof(data); ++i) { + printf(" %02x", ((uint8_t *)&data)[i]); + if (i % 16 == 15) { + printf("\n"); + } + } + printf("\n"); +} + +void se_services_get_unique_id(uint8_t id[5]) { + uint32_t error_code; + SERVICES_version_data_t data; + SERVICES_system_get_device_data(se_services_handle, &data, &error_code); + // The MfgData has 5 bytes of valid data, at least on REV_B2. + memcpy(id, data.MfgData, 5); +} + +__attribute__((noreturn)) void se_services_reset_soc(void) { + SERVICES_boot_reset_soc(se_services_handle); + NVIC_SystemReset(); +} + +uint64_t se_services_rand64(void) { + // If the SE core is not ready then the return value can be + // SERVICES_REQ_NOT_ACKNOWLEDGE. So retry a few times. + for (int retry = 0; retry < 100; ++retry) { + uint64_t value; + int32_t error_code; + uint32_t ret = SERVICES_cryptocell_get_rnd(se_services_handle, sizeof(uint64_t), &value, &error_code); + if (ret == SERVICES_REQ_SUCCESS) { + return value; + } + } + + // No random number available. + return 0; +} + +uint32_t se_services_enable_clock(clock_enable_t clock, bool enable) { + uint32_t error_code; + SERVICES_clocks_enable_clock(se_services_handle, clock, enable, &error_code); + return error_code; +} + +uint32_t se_services_select_pll_source(pll_source_t source, pll_target_t target) { + uint32_t error_code; + SERVICES_clocks_select_pll_source(se_services_handle, source, target, &error_code); + return error_code; +} + +uint32_t se_services_get_run_profile(run_profile_t *profile) { + uint32_t error_code; + SERVICES_get_run_cfg(se_services_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_set_run_profile(run_profile_t *profile) { + uint32_t error_code; + SERVICES_set_run_cfg(se_services_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_get_off_profile(off_profile_t *profile) { + uint32_t error_code; + SERVICES_get_off_cfg(se_services_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_set_off_profile(off_profile_t *profile) { + uint32_t error_code; + SERVICES_set_off_cfg(se_services_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_boot_process_toc_entry(const uint8_t *image_id) { + uint32_t error_code; + SERVICES_boot_process_toc_entry(se_services_handle, image_id, &error_code); + return error_code; +} + +uint32_t se_services_boot_cpu(uint32_t cpu_id, uint32_t address) { + uint32_t error_code; + SERVICES_boot_cpu(se_services_handle, cpu_id, address, &error_code); + return error_code; +} + +uint32_t se_services_boot_reset_cpu(uint32_t cpu_id) { + uint32_t error_code; + if (HE_CPU->RST_CTRL & RST_CTRL_CPUWAIT_MASK) { + // CPU held in reset + return SERVICES_REQ_SUCCESS; + } + + for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(1)) { + uint32_t ret = SERVICES_boot_reset_cpu(se_services_handle, cpu_id, &error_code); + if (ret != SERVICES_REQ_SUCCESS) { + return error_code; + } + + if ((HE_CPU->RST_STAT & RST_STAT_RST_ACK_MASK) == 0x4) { + return SERVICES_REQ_SUCCESS; + } + + if ((mp_hal_ticks_ms() - start) >= 100) { + return SERVICES_REQ_TIMEOUT; + } + } + return error_code; +} + +uint32_t se_services_boot_release_cpu(uint32_t cpu_id) { + uint32_t error_code; + SERVICES_boot_release_cpu(se_services_handle, cpu_id, &error_code); + return error_code; +} diff --git a/ports/alif/se_services.h b/ports/alif/se_services.h new file mode 100644 index 0000000000000..b1079130c1635 --- /dev/null +++ b/ports/alif/se_services.h @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_SE_SERVICES_H +#define MICROPY_INCLUDED_ALIF_SE_SERVICES_H + +#include "services_lib_api.h" + +void se_services_init(void); +void se_services_dump_device_data(void); +void se_services_get_unique_id(uint8_t id[5]); +__attribute__((noreturn)) void se_services_reset_soc(void); +uint64_t se_services_rand64(void); +uint32_t se_services_enable_clock(clock_enable_t clock, bool enable); +uint32_t se_services_select_pll_source(pll_source_t source, pll_target_t target); +uint32_t se_services_get_run_profile(run_profile_t *profile); +uint32_t se_services_set_run_profile(run_profile_t *profile); +uint32_t se_services_get_off_profile(off_profile_t *profile); +uint32_t se_services_set_off_profile(off_profile_t *profile); + +uint32_t se_services_boot_process_toc_entry(const uint8_t *image_id); +uint32_t se_services_boot_cpu(uint32_t cpu_id, uint32_t address); +uint32_t se_services_boot_reset_cpu(uint32_t cpu_id); +uint32_t se_services_boot_release_cpu(uint32_t cpu_id); +#endif // MICROPY_INCLUDED_ALIF_SE_SERVICES_H From c6cb082ed1716396f436ae4983131be2dcbf0447 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jan 2024 12:00:37 +1100 Subject: [PATCH 15/77] alif/mpconfigport: Enable os.urandom(). Uses the SE services to provide random numbers. Signed-off-by: Damien George --- ports/alif/modos.c | 49 +++++++++++++++++++++++++++++++++++++++ ports/alif/mpconfigport.h | 2 ++ 2 files changed, 51 insertions(+) create mode 100644 ports/alif/modos.c diff --git a/ports/alif/modos.c b/ports/alif/modos.c new file mode 100644 index 0000000000000..9c8c84268ca16 --- /dev/null +++ b/ports/alif/modos.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/runtime.h" +#include "se_services.h" + +#if MICROPY_PY_OS_URANDOM +static mp_obj_t mp_os_urandom(mp_obj_t num) { + mp_int_t n = mp_obj_get_int(num); + vstr_t vstr; + vstr_init_len(&vstr, n); + uint64_t rnd = 0; + size_t rnd_bits = 0; + for (int i = 0; i < n; i++) { + if (rnd_bits == 0) { + rnd = se_services_rand64(); + rnd_bits = 64; + } + vstr.buf[i] = rnd; + rnd >>= 8; + rnd_bits -= 8; + } + return mp_obj_new_bytes_from_vstr(&vstr); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_os_urandom_obj, mp_os_urandom); +#endif diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 2d8e3b2a09dbe..74cfe8edaab74 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -87,10 +87,12 @@ // Extended modules #define MICROPY_EPOCH_IS_1970 (1) +#define MICROPY_PY_OS_INCLUDEFILE "ports/alif/modos.c" #define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_PY_OS_SEP (1) #define MICROPY_PY_OS_SYNC (1) #define MICROPY_PY_OS_UNAME (1) +#define MICROPY_PY_OS_URANDOM (1) #define MICROPY_PY_TIME (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/alif/modmachine.c" From 64af93e74e9de36fd6106e6bea4923fc300afb49 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jan 2024 12:03:46 +1100 Subject: [PATCH 16/77] alif/mpconfigport: Enable MICROPY_PY_RANDOM_SEED_INIT_FUNC. Uses the SE services to provide a random seed. Signed-off-by: Damien George --- ports/alif/mpconfigport.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 74cfe8edaab74..b01eb1908abb0 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -93,6 +93,7 @@ #define MICROPY_PY_OS_SYNC (1) #define MICROPY_PY_OS_UNAME (1) #define MICROPY_PY_OS_URANDOM (1) +#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (se_services_rand64()) #define MICROPY_PY_TIME (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/alif/modmachine.c" @@ -155,3 +156,6 @@ extern void panic(const char *); #ifndef MICROPY_BOARD_ENTER_BOOTLOADER #define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) #endif + +// Needed for MICROPY_PY_RANDOM_SEED_INIT_FUNC. +uint64_t se_services_rand64(void); From 2a580b05ada2ce6c87d6e94196178c511ef7acdf Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Feb 2024 17:50:15 +1100 Subject: [PATCH 17/77] alif/modalif: Add alif.info() function. Calls the SE services to print information about the SoC. Signed-off-by: Damien George --- ports/alif/modalif.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/alif/modalif.c b/ports/alif/modalif.c index 69b7e6c5c8e87..5577b53eaca85 100644 --- a/ports/alif/modalif.c +++ b/ports/alif/modalif.c @@ -27,10 +27,18 @@ #include "py/mphal.h" #include "py/runtime.h" #include "modalif.h" +#include "se_services.h" + +static mp_obj_t alif_info(void) { + se_services_dump_device_data(); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(alif_info_obj, alif_info); static const mp_rom_map_elem_t alif_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alif) }, { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&alif_flash_type) }, + { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&alif_info_obj) }, #if MICROPY_HW_USB_MSC // Attribute to indicate USB MSC is enabled. { MP_ROM_QSTR(MP_QSTR_usb_msc), MP_ROM_TRUE }, From 2f85a19d7d7c342aefa6e3d628c1d398f53d3adf Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Feb 2024 17:50:34 +1100 Subject: [PATCH 18/77] alif/modmachine: Implement machine.unique_id(), fix machine.reset(). They both use SE services. Signed-off-by: Damien George --- ports/alif/modmachine.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index e19150e010d0b..29a70305dd516 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -27,6 +27,8 @@ // This file is never compiled standalone, it's included directly from // extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. +#include "se_services.h" + #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, \ @@ -36,11 +38,13 @@ static void mp_machine_idle(void) { } static mp_obj_t mp_machine_unique_id(void) { - return mp_obj_new_bytes((const uint8_t *)"ABCD", 4); + uint8_t id[5]; + se_services_get_unique_id(id); + return mp_obj_new_bytes(id, sizeof(id)); } NORETURN static void mp_machine_reset(void) { - NVIC_SystemReset(); + se_services_reset_soc(); } NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { From 31d18c5885fd53c6aacc80428bdfb8b0bbdcd7cb Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Feb 2024 17:51:03 +1100 Subject: [PATCH 19/77] alif/usbd: Implement proper USB serial number. Using SE services to get the SoC unique id. Signed-off-by: Damien George --- ports/alif/usbd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/alif/usbd.c b/ports/alif/usbd.c index 9df6610683507..da16905d43cd9 100644 --- a/ports/alif/usbd.c +++ b/ports/alif/usbd.c @@ -30,10 +30,11 @@ #include "shared/tinyusb/mp_usbd.h" #include "tusb.h" +#include "se_services.h" void mp_usbd_port_get_serial_number(char *serial_buf) { - // TODO - uint8_t id[8] = "ABCDEFGH"; + uint8_t id[5]; + se_services_get_unique_id(id); MP_STATIC_ASSERT(sizeof(id) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); mp_usbd_hex_str(serial_buf, id, sizeof(id)); } From 62beb541e76e7776cb402fabc10a6116da8f2dcf Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 13 Feb 2024 18:05:05 +1100 Subject: [PATCH 20/77] alif/machine_adc: Add basic ADC support. ADC12 information has been added to pin struct. Signed-off-by: Damien George --- ports/alif/Makefile | 1 + ports/alif/irq.h | 1 + ports/alif/machine_adc.c | 225 ++++++++++++++++++++++++++++++++++++ ports/alif/mcu/make-pins.py | 33 +++++- ports/alif/mpconfigport.h | 2 + ports/alif/mphalport.h | 2 + 6 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 ports/alif/machine_adc.c diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 656cdc0e4cd44..068e34575a143 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -176,6 +176,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/common/source/system_M55.c \ Device/common/source/system_utils.c \ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ + drivers/source/adc.c \ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ diff --git a/ports/alif/irq.h b/ports/alif/irq.h index e2e0685c679a9..a20ff7aebdef1 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -43,6 +43,7 @@ #define IRQ_PRI_MHU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 0, 0) #define IRQ_PRI_QUIET_TIMING NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) +#define IRQ_PRI_ADC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 3, 0) #define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) diff --git a/ports/alif/machine_adc.c b/ports/alif/machine_adc.c new file mode 100644 index 0000000000000..91eb95b5ba002 --- /dev/null +++ b/ports/alif/machine_adc.c @@ -0,0 +1,225 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ + +// This file is never compiled standalone, it's included directly from +// extmod/machine_adc.c via MICROPY_PY_MACHINE_ADC_INCLUDEFILE. + +#include "py/mphal.h" + +#include "analog_config.h" +#include "adc.h" +#include "sys_ctrl_adc.h" + +#define ADC_CHANNEL_TEMP_SENSOR (6) +#define ADC_CHANNEL_INT_VREF (7) + +static const uint8_t adc_instance_table[4] = { + ADC_INSTANCE_ADC12_0, + ADC_INSTANCE_ADC12_1, + ADC_INSTANCE_ADC12_2, + ADC_INSTANCE_ADC24_0, +}; + +static ADC_Type *const adc_regs_table[4] = { + (ADC_Type *)ADC120_BASE, + (ADC_Type *)ADC121_BASE, + (ADC_Type *)ADC122_BASE, + (ADC_Type *)ADC24_BASE, +}; + +static bool adc_inited[4]; +static conv_info_t adc_conv_info_table[4]; + +static void adc_nvic_config(unsigned int irq) { + NVIC_ClearPendingIRQ(irq); + NVIC_SetPriority(irq, IRQ_PRI_ADC); + NVIC_EnableIRQ(irq); +} + +static void adc_init(uint8_t adc_periph) { + if (adc_inited[adc_periph]) { + return; + } + + ADC_Type *regs = adc_regs_table[adc_periph]; + uint8_t adc_instance = adc_instance_table[adc_periph]; + conv_info_t *conv_info = &adc_conv_info_table[adc_periph]; + + adc_set_clk_control(adc_instance, true); + enable_cmp_periph_clk(); + analog_config_vbat_reg2(); + analog_config_cmp_reg2(); + adc_set_differential_ctrl(adc_instance, false); + adc_set_comparator_ctrl(adc_instance, true, 2); + + if (adc_instance == ADC_INSTANCE_ADC24_0) { + enable_adc24(); + set_adc24_output_rate(0); + set_adc24_bias(0); + } else { + adc_set_sample_width(regs, 32); + } + + adc_set_clk_div(regs, 4); + adc_set_avg_sample(regs, 32); + adc_set_n_shift_bit(regs, 1, 1); + adc_set_single_ch_scan_mode(regs, conv_info); + adc_unmask_interrupt(regs); + + adc_nvic_config(ADC120_DONE0_IRQ_IRQn); + adc_nvic_config(ADC120_DONE1_IRQ_IRQn); + adc_nvic_config(ADC121_DONE0_IRQ_IRQn); + adc_nvic_config(ADC121_DONE1_IRQ_IRQn); + adc_nvic_config(ADC122_DONE0_IRQ_IRQn); + adc_nvic_config(ADC122_DONE1_IRQ_IRQn); +} + +static uint16_t adc_config_and_read_u16(unsigned int adc_periph, uint32_t channel) { + ADC_Type *adc_regs = adc_regs_table[adc_periph]; + conv_info_t *conv_info = &adc_conv_info_table[adc_periph]; + + conv_info->status = ADC_CONV_STAT_NONE; + conv_info->mode = ADC_CONV_MODE_SINGLE_SHOT; + + // Select channel and start conversion. + adc_init_channel_select(adc_regs, channel); + adc_set_single_ch_scan_mode(adc_regs, conv_info); + adc_enable_single_shot_conv(adc_regs); + + // Wait for conversion to complete. + while (!(conv_info->status & ADC_CONV_STAT_COMPLETE)) { + __WFE(); + } + + return conv_info->sampled_value; +} + +static void adc_done0_irq_handler_helper(unsigned int adc_periph) { + adc_done0_irq_handler(adc_regs_table[adc_periph], &adc_conv_info_table[adc_periph]); +} + +static void adc_done1_irq_handler_helper(unsigned int adc_periph) { + adc_done1_irq_handler(adc_regs_table[adc_periph], &adc_conv_info_table[adc_periph]); +} + +void ADC120_DONE0_IRQHandler(void) { + adc_done0_irq_handler_helper(0); + __SEV(); +} + +void ADC120_DONE1_IRQHandler(void) { + adc_done1_irq_handler_helper(0); + __SEV(); +} + +void ADC121_DONE0_IRQHandler(void) { + adc_done0_irq_handler_helper(1); + __SEV(); +} + +void ADC121_DONE1_IRQHandler(void) { + adc_done1_irq_handler_helper(1); + __SEV(); +} + +void ADC122_DONE0_IRQHandler(void) { + adc_done0_irq_handler_helper(2); + __SEV(); +} + +void ADC122_DONE1_IRQHandler(void) { + adc_done1_irq_handler_helper(2); + __SEV(); +} + +/******************************************************************************/ +// MicroPython bindings for machine.ADC + +#define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMP_SENSOR) }, \ + { MP_ROM_QSTR(MP_QSTR_CORE_VREF), MP_ROM_INT(ADC_CHANNEL_INT_VREF) }, \ + +typedef struct _machine_adc_obj_t { + mp_obj_base_t base; + uint8_t adc_periph; + uint8_t adc_channel; +} machine_adc_obj_t; + +static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "", self->adc_periph, self->adc_channel); +} + +// ADC(id) +static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Check number of arguments + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_t source = all_args[0]; + + uint8_t adc_periph; + uint8_t adc_channel; + const machine_pin_obj_t *pin = NULL; + + if (mp_obj_is_int(source)) { + // Get and validate channel number. + adc_channel = mp_obj_get_int(source); + if (adc_channel < ADC_CHANNEL_TEMP_SENSOR) { + adc_periph = 0; + } else if (adc_channel == ADC_CHANNEL_TEMP_SENSOR || adc_channel == ADC_CHANNEL_INT_VREF) { + adc_periph = 2; + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid channel")); + } + } else { + // Get GPIO and check it has ADC capabilities. + pin = mp_hal_get_pin_obj(source); + if (pin->adc12_periph <= 2 && pin->adc12_channel <= 5) { + // Select the ADC12 peripheral and channel. + adc_periph = pin->adc12_periph; + adc_channel = pin->adc12_channel; + // Configure the pad for analog input. + pinconf_set(pin->port, pin->pin, PINMUX_ALTERNATE_FUNCTION_7, PADCTRL_READ_ENABLE); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("Pin doesn't have ADC capabilities")); + } + } + + // Initialise the ADC peripheral. + adc_init(adc_periph); + + // Create ADC object. + machine_adc_obj_t *o = mp_obj_malloc(machine_adc_obj_t, &machine_adc_type); + o->adc_periph = adc_periph; + o->adc_channel = adc_channel; + + return MP_OBJ_FROM_PTR(o); +} + +// read_u16() +static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { + return adc_config_and_read_u16(self->adc_periph, self->adc_channel); +} diff --git a/ports/alif/mcu/make-pins.py b/ports/alif/mcu/make-pins.py index b49db4cfe49bb..d4fbf5d8b8d9a 100755 --- a/ports/alif/mcu/make-pins.py +++ b/ports/alif/mcu/make-pins.py @@ -10,11 +10,34 @@ NUM_PORTS = 16 NUM_PINS_PER_PORT = 8 +# Maps pad name to (adc12_periph, adc12_channel). +ADC12_ANA_MAP = { + "P0_0": (0, 0), + "P0_1": (0, 1), + "P0_2": (0, 2), + "P0_3": (0, 3), + "P0_4": (0, 4), + "P0_5": (0, 5), + "P0_6": (1, 0), + "P0_7": (1, 1), + "P1_0": (1, 2), + "P1_1": (1, 3), + "P1_2": (1, 4), + "P1_3": (1, 5), + "P1_4": (2, 0), + "P1_5": (2, 1), + "P1_6": (2, 2), + "P1_7": (2, 3), + "P2_0": (2, 4), + "P2_1": (2, 5), +} + class AlifPin(boardgen.Pin): # Emit the struct which contains the pin instance. def definition(self): port, pin = self.name()[1:].split("_") + adc12_periph, adc12_channel = ADC12_ANA_MAP.get(self.name(), (3, 7)) base = "LPGPIO_BASE" if port == "15" else "GPIO{}_BASE".format(port) return ( "{{ " @@ -22,8 +45,16 @@ def definition(self): ".gpio = (GPIO_Type *){base}, " ".port = PORT_{port}, " ".pin = PIN_{pin}, " + ".adc12_periph = {adc12_periph}, " + ".adc12_channel = {adc12_channel}, " ".name = MP_QSTR_P{port}_{pin} " - "}}".format(port=port, pin=pin, base=base) + "}}".format( + port=port, + pin=pin, + base=base, + adc12_periph=adc12_periph, + adc12_channel=adc12_channel, + ) ) # Alif cpu names must be "Pn_m". diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index b01eb1908abb0..95b5e3ca9fa24 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -101,6 +101,8 @@ #define MICROPY_PY_MACHINE_BOOTLOADER (1) #define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1) #define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1) +#define MICROPY_PY_MACHINE_ADC (1) +#define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/alif/machine_adc.c" #define MICROPY_PY_MACHINE_DHT_READINTO (1) #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 8098c2650df01..7b054b671bcd6 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -85,6 +85,8 @@ typedef struct _machine_pin_obj_t { GPIO_Type *gpio; uint8_t port; uint8_t pin; + uint8_t adc12_periph : 2; + uint8_t adc12_channel : 3; qstr name; } machine_pin_obj_t; From b7df5aa86a6f150ab3282f0525fe9c3538ceaef7 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 10 Jul 2024 20:30:02 +0300 Subject: [PATCH 21/77] alif/mcu: Add ToC config for dual images. Note that 128K at the beginning of MRAM is reserved for future bootloaders. Signed-off-by: iabdalkader --- ports/alif/mcu/M55_DUAL_cfg.json | 26 ++++++++++++++++++++++++++ ports/alif/mcu/M55_HE_cfg.json | 17 +++++++++++++++++ ports/alif/mcu/M55_HP_cfg.json | 17 ++++++++++------- 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 ports/alif/mcu/M55_DUAL_cfg.json create mode 100644 ports/alif/mcu/M55_HE_cfg.json diff --git a/ports/alif/mcu/M55_DUAL_cfg.json b/ports/alif/mcu/M55_DUAL_cfg.json new file mode 100644 index 0000000000000..81beef5f03614 --- /dev/null +++ b/ports/alif/mcu/M55_DUAL_cfg.json @@ -0,0 +1,26 @@ +{ + "DEVICE": { + "disabled" : false, + "binary": "app-device-config.json", + "version" : "0.5.00", + "signed": false + }, + "HP_APP": { + "disabled" : false, + "binary": "M55_HP/firmware.bin", + "mramAddress": "0x80020000", + "version": "1.0.0", + "cpu_id": "M55_HP", + "flags": ["boot"], + "signed": false + }, + "HE_APP": { + "disabled" : false, + "binary": "M55_HE/firmware.bin", + "mramAddress": "0x80320000", + "version": "1.0.0", + "cpu_id": "M55_HE", + "flags": ["deferred"], + "signed": false + } +} diff --git a/ports/alif/mcu/M55_HE_cfg.json b/ports/alif/mcu/M55_HE_cfg.json new file mode 100644 index 0000000000000..83898418043ec --- /dev/null +++ b/ports/alif/mcu/M55_HE_cfg.json @@ -0,0 +1,17 @@ +{ + "DEVICE": { + "disabled" : false, + "binary": "app-device-config.json", + "version" : "0.5.00", + "signed": false + }, + "HE_APP": { + "disabled" : false, + "binary": "M55_HE/firmware.bin", + "mramAddress": "0x80320000", + "version": "1.0.0", + "cpu_id": "M55_HE", + "flags": ["boot"], + "signed": false + } +} diff --git a/ports/alif/mcu/M55_HP_cfg.json b/ports/alif/mcu/M55_HP_cfg.json index 228ddbf6c9464..d6710f69c77b6 100644 --- a/ports/alif/mcu/M55_HP_cfg.json +++ b/ports/alif/mcu/M55_HP_cfg.json @@ -1,14 +1,17 @@ { - "USER_APP": { - "binary": "firmware.bin", - "mramAddress": "0x80000000", + "DEVICE": { + "disabled" : false, + "binary": "app-device-config.json", + "version" : "0.5.00", + "signed": false + }, + "HP_APP": { + "disabled" : false, + "binary": "M55_HP/firmware.bin", + "mramAddress": "0x80020000", "version": "1.0.0", "cpu_id": "M55_HP", "flags": ["boot"], "signed": false - }, - "DEVICE": { - "binary": "app-device-config.json", - "version" : "0.5.00" } } From 8f82089bd051eb154e7c55951dd29220bde94611 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 10 Jul 2024 20:27:18 +0300 Subject: [PATCH 22/77] alif: Support building the port for HE or HP or both cores. With this new Makefile you can build the following: make BOARD=MY_BOARD MCU_CORE=M55_HP # build HP firmware/ToC. make BOARD=MY_BOARD MCU_CORE=M55_HE # build HE firmware/ToC. make BOARD=MY_BOARD MCU_CORE=M55_DUAL # build HE+HP firmware + ToC. Signed-off-by: iabdalkader --- ports/alif/Makefile | 269 +++++++---------------------------- ports/alif/alif.mk | 257 +++++++++++++++++++++++++++++++++ ports/alif/mcu/ensemble.ld.S | 43 ++++-- 3 files changed, 343 insertions(+), 226 deletions(-) create mode 100644 ports/alif/alif.mk diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 068e34575a143..5cc334a7cf9f9 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -1,242 +1,96 @@ -################################################################################ -# Initial setup of Makefile environment - BOARD ?= ALIF_ENSEMBLE BOARD_DIR ?= boards/$(BOARD) BUILD ?= build-$(BOARD) - -ifeq ($(wildcard $(BOARD_DIR)/.),) -$(error Invalid BOARD specified: $(BOARD_DIR)) -endif - -include ../../py/mkenv.mk -include mpconfigport.mk -include $(BOARD_DIR)/mpconfigboard.mk - -# qstr definitions (must come before including py.mk) -QSTR_DEFS += qstrdefsport.h - -# include py core make definitions -include $(TOP)/py/py.mk -include $(TOP)/extmod/extmod.mk - -################################################################################ -# Project specific settings and compiler/linker flags - -CROSS_COMPILE ?= arm-none-eabi- +MCU_CORE ?= M55_HP GIT_SUBMODULES += lib/tinyusb lib/alif_ensemble-cmsis-dfp lib/alif-security-toolkit PORT ?= /dev/ttyACM0 ALIF_TOOLS ?= ../../lib/alif-security-toolkit/toolkit -ALIF_DFP_REL_TOP ?= lib/alif_ensemble-cmsis-dfp -ALIF_DFP_REL_HERE ?= $(TOP)/lib/alif_ensemble-cmsis-dfp -CMSIS_DIR ?= $(TOP)/lib/cmsis/inc - -MCU_CORE ?= M55_HP -ALIF_CONFIG ?= mcu/$(MCU_CORE)_cfg.json -LD_FILE ?= mcu/ensemble.ld.S - -INC += -I. -INC += -I$(TOP) -INC += -I$(BUILD) -INC += -I$(BOARD_DIR) -INC += -I$(CMSIS_DIR) -INC += -I$(ALIF_DFP_REL_HERE)/drivers/include/ -INC += -I$(ALIF_DFP_REL_HERE)/se_services/include/ -INC += -I$(ALIF_DFP_REL_HERE)/ospi_xip/source/ospi -INC += -I$(ALIF_DFP_REL_HERE)/Device/common/config/ -INC += -I$(ALIF_DFP_REL_HERE)/Device/common/include/ -INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/config/ -INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/include/ -INC += -I$(ALIF_DFP_REL_HERE)/Device/$(MCU_SERIES)/$(MCU_VARIANT)/ -INC += -I$(TOP)/lib/tinyusb/src -INC += -Itinyusb_port - -GEN_PIN_MKPINS = mcu/make-pins.py -GEN_PIN_PREFIX = mcu/pins_prefix.c -GEN_PINS_BOARD_CSV = $(BOARD_DIR)/pins.csv -GEN_PINS_SRC = $(BUILD)/pins_board.c -GEN_PINS_HDR = $(HEADER_BUILD)/pins_board.h - -CFLAGS_FPU += -mfloat-abi=hard -mfpu=fpv5-d16 -CFLAGS_CORTEX_M55 += -mthumb -mcpu=cortex-m55 -mtune=cortex-m55 $(CFLAGS_FPU) - -CFLAGS += $(INC) -Wall -Werror -std=c99 $(CFLAGS_CORTEX_M55) -nostdlib -CFLAGS += -Wdouble-promotion -Wfloat-conversion -CFLAGS += -fdata-sections -ffunction-sections -CFLAGS += -D$(MCU_CORE) -DCORE_$(MCU_CORE) -DALIF_CMSIS_H="\"$(MCU_CORE).h\"" - -ifeq ($(MICROPY_FLOAT_IMPL),float) -CFLAGS += -fsingle-precision-constant -CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT -else -CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_DOUBLE -endif - -AFLAGS = -mthumb -march=armv8.1-m.main $(CFLAGS_FPU) - -LDFLAGS += -nostdlib -LDFLAGS += -T$(BUILD)/ensemble.ld -Map=$@.map --cref --gc-sections -LDFLAGS += --wrap=dcd_event_handler -# Tune for Debugging or Optimization -ifeq ($(DEBUG), 1) -CFLAGS += -Og -ggdb3 -# Disable text compression in debug builds -MICROPY_ROM_TEXT_COMPRESSION = 0 -else -CFLAGS += -O2 -DNDEBUG -endif - -LIBS += "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)" - -JLINK_CMD = '\ +JLINK_CMD_PREFIX = \ ExitOnError 1\n\ Device $(JLINK_DEV)\n\ SelectInterface SWD\n\ Speed auto\n\ Connect\n\ Reset\n\ -ShowHWStatus\n\ -LoadFile "$(BUILD)/firmware_toc.bin",0x8057f1c0\n\ -LoadFile "$(BUILD)/firmware.bin",0x80000000\n\ -Reset\n\ -Exit' - -################################################################################ -# Source files and libraries +ShowHWStatus\n -SRC_O += \ - shared/runtime/gchelper_thumb2.o +JLINK_CMD_SUFFIX = \ +Reset\n\ +Exit -SRC_C = \ - alif_flash.c \ - fatfs_port.c \ - machine_pin.c \ - main.c \ - modalif.c \ - mphalport.c \ - mpuart.c \ - msc_disk.c \ - ospi_flash.c \ - pendsv.c \ - system_tick.c \ - se_services.c \ - usbd.c \ - $(wildcard $(BOARD_DIR)/*.c) +ifeq ($(MCU_CORE),M55_HP) -ifeq ($(MICROPY_FLOAT_IMPL),float) -LIBM_SRC_C += $(SRC_LIB_LIBM_C) -LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) -$(BUILD)/lib/libm/%.o: CFLAGS += -Wno-maybe-uninitialized -else -LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_C) -LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_HW_C) -$(BUILD)/lib/libm_dbl/%.o: CFLAGS += -Wno-maybe-uninitialized -endif +ALIF_TOC_CONFIG = mcu/M55_HP_cfg.json +ALIF_TOC_APPS = $(BUILD)/M55_HP/firmware.bin +JLINK_CMD = '\ +$(JLINK_CMD_PREFIX)\ +LoadFile "$(BUILD)/M55_HP/firmware.bin",0x80020000\n\ +$(JLINK_CMD_SUFFIX)' -SHARED_SRC_C += $(addprefix shared/,\ - libc/string0.c \ - netutils/dhcpserver.c \ - netutils/netutils.c \ - netutils/trace.c \ - readline/readline.c \ - runtime/gchelper_native.c \ - runtime/interrupt_char.c \ - runtime/mpirq.c \ - runtime/pyexec.c \ - runtime/softtimer.c \ - runtime/stdout_helpers.c \ - runtime/sys_stdio_mphal.c \ - timeutils/timeutils.c \ - tinyusb/mp_usbd.c \ - tinyusb/mp_usbd_cdc.c \ - tinyusb/mp_usbd_descriptor.c \ - ) +else ifeq ($(MCU_CORE),M55_HE) -DRIVERS_SRC_C += $(addprefix drivers/,\ - bus/softspi.c \ - bus/softqspi.c \ - memory/spiflash.c \ - dht/dht.c \ - ) +ALIF_TOC_CONFIG = mcu/M55_HE_cfg.json +ALIF_TOC_APPS = $(BUILD)/M55_HE/firmware.bin +JLINK_CMD = '\ +$(JLINK_CMD_PREFIX)\ +LoadFile "$(BUILD)/M55_HE/firmware.bin",0x80320000\n\ +$(JLINK_CMD_SUFFIX)' -TINYUSB_SRC_C += \ - lib/tinyusb/src/tusb.c \ - lib/tinyusb/src/class/cdc/cdc_device.c \ - lib/tinyusb/src/class/msc/msc_device.c \ - lib/tinyusb/src/common/tusb_fifo.c \ - lib/tinyusb/src/device/usbd.c \ - lib/tinyusb/src/device/usbd_control.c \ - tinyusb_port/tusb_alif_dcd.c \ +else ifeq ($(MCU_CORE),M55_DUAL) -ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ - Device/common/source/clk.c \ - Device/common/source/mpu_M55.c \ - Device/common/source/system_M55.c \ - Device/common/source/system_utils.c \ - Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ - drivers/source/adc.c \ - drivers/source/mhu_driver.c \ - drivers/source/mhu_receiver.c \ - drivers/source/mhu_sender.c \ - drivers/source/pinconf.c \ - drivers/source/uart.c \ - drivers/source/utimer.c \ - ospi_xip/source/ospi/ospi_drv.c \ - se_services/source/services_host_application.c \ - se_services/source/services_host_boot.c \ - se_services/source/services_host_clocks.c \ - se_services/source/services_host_cryptocell.c \ - se_services/source/services_host_handler.c \ - se_services/source/services_host_system.c \ - ) +ALIF_TOC_CONFIG = mcu/M55_DUAL_cfg.json +ALIF_TOC_APPS = $(BUILD)/M55_HP/firmware.bin $(BUILD)/M55_HE/firmware.bin +JLINK_CMD = '\ +$(JLINK_CMD_PREFIX)\ +LoadFile "$(BUILD)/M55_HP/firmware.bin",0x80020000\n\ +LoadFile "$(BUILD)/M55_HE/firmware.bin",0x80320000\n\ +$(JLINK_CMD_SUFFIX)' -$(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 -$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation +else +$(error Invalid MCU core specified)) +endif -# List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC) +include ../../py/mkenv.mk +include mpconfigport.mk +include $(BOARD_DIR)/mpconfigboard.mk -OBJ += $(PY_O) -OBJ += $(addprefix $(BUILD)/, $(SRC_O)) -OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(LIBM_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(TINYUSB_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(ALIF_SRC_C:.c=.o)) -OBJ += $(GEN_PINS_SRC:.c=.o) +# include py core make definitions +include $(TOP)/py/py.mk +include $(TOP)/extmod/extmod.mk ################################################################################ # Main targets -.DELETE_ON_ERROR: - .PHONY: all -all: $(BUILD)/firmware_toc.bin +all: $(BUILD)/firmware.toc.bin + +# Force make commands to run the targets every time +# regardless of whether firmware.toc.bin already exists +# to detect changes in the source files and rebuild. +.PHONY: $(BUILD)/M55_HE/firmware.bin +.PHONY: $(BUILD)/M55_HP/firmware.bin -$(BUILD)/ensemble.ld: $(LD_FILE) - $(ECHO) "Preprocess linker script $@" - $(Q)$(CPP) -P -E $(CFLAGS) $^ > $@ +$(BUILD): + $(MKDIR) -p $@ -$(BUILD)/firmware.elf: $(OBJ) $(BUILD)/ensemble.ld - $(ECHO) "Link $@" - $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) - $(Q)$(SIZE) $@ +$(BUILD)/M55_HP/firmware.bin: + make -f alif.mk MCU_CORE=M55_HP -$(BUILD)/firmware.bin: $(BUILD)/firmware.elf - $(Q)$(OBJCOPY) -Obinary $^ $(BUILD)/firmware.bin +$(BUILD)/M55_HE/firmware.bin: + make -f alif.mk MCU_CORE=M55_HE -$(BUILD)/firmware_toc.bin: $(BUILD)/firmware.bin +$(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS) $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ - --filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \ + --filename $(abspath $(ALIF_TOC_CONFIG)) \ + --config-dir $(BOARD_DIR) \ --output-dir $(BUILD) \ --firmware-dir $(BUILD) \ --output $@ .PHONY: deploy -deploy: $(BUILD)/firmware_toc.bin +deploy: $(BUILD)/firmware.toc.bin $(ECHO) "Writing $< to the board" $(Q)python $(ALIF_TOOLS)/app-write-mram.py \ --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ @@ -245,7 +99,7 @@ deploy: $(BUILD)/firmware_toc.bin --images file:$(BUILD)/application_package.ds .PHONY: deploy-jlink -deploy-jlink: $(BUILD)/firmware_toc.bin +deploy-jlink: $(ALIF_TOC_APPS) $(Q)echo -e $(JLINK_CMD) | $(JLINK_EXE) .PHONY: maintenance @@ -260,17 +114,4 @@ update-system-package: --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ --port $(PORT) -################################################################################ -# Remaining make rules - -# Use a pattern rule here so that make will only call make-pins.py once to make -# both pins_board.c and pins_board.h -$(BUILD)/%_board.c $(HEADER_BUILD)/%_board.h: $(BOARD_DIR)/%.csv $(GEN_PIN_MKPINS) $(GEN_PIN_PREFIX) | $(HEADER_BUILD) - $(ECHO) "GEN $@" - $(Q)$(PYTHON) $(GEN_PIN_MKPINS) \ - --board-csv $(GEN_PINS_BOARD_CSV) \ - --prefix $(GEN_PIN_PREFIX) \ - --output-source $(GEN_PINS_SRC) \ - --output-header $(GEN_PINS_HDR) - include $(TOP)/py/mkrules.mk diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk new file mode 100644 index 0000000000000..ad69499eccfa8 --- /dev/null +++ b/ports/alif/alif.mk @@ -0,0 +1,257 @@ +################################################################################ +# Initial setup of Makefile environment + +BOARD ?= ALIF_ENSEMBLE +BOARD_DIR ?= boards/$(BOARD) +BUILD ?= build-$(BOARD)/$(MCU_CORE) + +ifeq ($(wildcard $(BOARD_DIR)/.),) +$(error Invalid BOARD specified: $(BOARD_DIR)) +endif + +include ../../py/mkenv.mk +include mpconfigport.mk +include $(BOARD_DIR)/mpconfigboard.mk + +# qstr definitions (must come before including py.mk) +QSTR_DEFS += qstrdefsport.h + +# include py core make definitions +include $(TOP)/py/py.mk +include $(TOP)/extmod/extmod.mk + +################################################################################ +# Project specific settings and compiler/linker flags + +CROSS_COMPILE ?= arm-none-eabi- +ALIF_DFP_REL_TOP ?= lib/alif_ensemble-cmsis-dfp +ALIF_DFP_REL_HERE ?= $(TOP)/lib/alif_ensemble-cmsis-dfp +CMSIS_DIR ?= $(TOP)/lib/cmsis/inc + +MCU_CORE ?= M55_HP +ALIF_CONFIG ?= mcu/$(MCU_CORE)_cfg.json +LD_FILE ?= mcu/ensemble.ld.S + +INC += -I. +INC += -I$(TOP) +INC += -I$(BUILD) +INC += -I$(BOARD_DIR) +INC += -I$(CMSIS_DIR) +INC += -I$(ALIF_DFP_REL_HERE)/drivers/include/ +INC += -I$(ALIF_DFP_REL_HERE)/se_services/include +INC += -I$(ALIF_DFP_REL_HERE)/ospi_xip/source/ospi +INC += -I$(ALIF_DFP_REL_HERE)/Device/common/config/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/common/include/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/config/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/include/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/$(MCU_SERIES)/$(MCU_VARIANT)/ +INC += -I$(TOP)/lib/tinyusb/src +INC += -Itinyusb_port + +GEN_PIN_MKPINS = mcu/make-pins.py +GEN_PIN_PREFIX = mcu/pins_prefix.c +GEN_PINS_BOARD_CSV = $(BOARD_DIR)/pins.csv +GEN_PINS_SRC = $(BUILD)/pins_board.c +GEN_PINS_HDR = $(HEADER_BUILD)/pins_board.h + +CFLAGS_FPU += -mfloat-abi=hard -mfpu=fpv5-d16 + +CFLAGS += $(INC) \ + -std=c99 \ + -Wall \ + -Werror \ + -Wdouble-promotion \ + -Wfloat-conversion \ + -mthumb \ + -mcpu=cortex-m55 \ + -mtune=cortex-m55 \ + $(CFLAGS_FPU) \ + -march=armv8.1-m.main+fp+mve.fp \ + -nostdlib \ + -fdata-sections \ + -ffunction-sections \ + -D$(MCU_CORE)=1 \ + -DCORE_$(MCU_CORE) \ + -DALIF_CMSIS_H="\"$(MCU_CORE).h\"" + +ifeq ($(MICROPY_FLOAT_IMPL),float) +CFLAGS += -fsingle-precision-constant +CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT +else +CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_DOUBLE +endif + +# Tune for Debugging or Optimization +ifeq ($(DEBUG), 1) +CFLAGS += -Og -ggdb3 +# Disable text compression in debug builds +MICROPY_ROM_TEXT_COMPRESSION = 0 +else +CFLAGS += -O2 -DNDEBUG +endif + +CFLAGS += $(CFLAGS_EXTRA) + +AFLAGS = -mthumb -march=armv8.1-m.main+fp+mve.fp $(CFLAGS_FPU) + +LDFLAGS += -nostdlib \ + -T$(BUILD)/ensemble.ld \ + -Map=$@.map \ + --cref \ + --gc-sections \ + --print-memory-usage +ifeq ($(MCU_CORE),M55_HP) +LDFLAGS += --wrap=dcd_event_handler +endif + +LIBS += "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)" + +################################################################################ +# Source files and libraries + +SRC_O += \ + shared/runtime/gchelper_thumb2.o + +SRC_C = \ + alif_flash.c \ + fatfs_port.c \ + machine_pin.c \ + main.c \ + modalif.c \ + mphalport.c \ + mpuart.c \ + msc_disk.c \ + ospi_flash.c \ + pendsv.c \ + system_tick.c \ + se_services.c \ + usbd.c \ + $(wildcard $(BOARD_DIR)/*.c) + +ifeq ($(MICROPY_FLOAT_IMPL),float) +LIBM_SRC_C += $(SRC_LIB_LIBM_C) +LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) +$(BUILD)/lib/libm/%.o: CFLAGS += -Wno-maybe-uninitialized +else +LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_C) +LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_HW_C) +$(BUILD)/lib/libm_dbl/%.o: CFLAGS += -Wno-maybe-uninitialized +endif + +SHARED_SRC_C += $(addprefix shared/,\ + libc/string0.c \ + netutils/dhcpserver.c \ + netutils/netutils.c \ + netutils/trace.c \ + readline/readline.c \ + runtime/gchelper_native.c \ + runtime/interrupt_char.c \ + runtime/mpirq.c \ + runtime/pyexec.c \ + runtime/softtimer.c \ + runtime/stdout_helpers.c \ + runtime/sys_stdio_mphal.c \ + timeutils/timeutils.c \ + tinyusb/mp_usbd.c \ + tinyusb/mp_usbd_cdc.c \ + tinyusb/mp_usbd_descriptor.c \ + ) + +DRIVERS_SRC_C += $(addprefix drivers/,\ + bus/softspi.c \ + bus/softqspi.c \ + memory/spiflash.c \ + dht/dht.c \ + ) + +TINYUSB_SRC_C += \ + lib/tinyusb/src/tusb.c \ + lib/tinyusb/src/class/cdc/cdc_device.c \ + lib/tinyusb/src/class/msc/msc_device.c \ + lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/usbd.c \ + lib/tinyusb/src/device/usbd_control.c \ + tinyusb_port/tusb_alif_dcd.c \ + +ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ + Device/common/source/clk.c \ + Device/common/source/mpu_M55.c \ + Device/common/source/system_M55.c \ + Device/common/source/system_utils.c \ + Device/common/source/tcm_partition.c \ + Device/common/source/tgu_M55.c \ + Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ + drivers/source/adc.c \ + drivers/source/mhu_driver.c \ + drivers/source/mhu_receiver.c \ + drivers/source/mhu_sender.c \ + drivers/source/pinconf.c \ + drivers/source/uart.c \ + drivers/source/utimer.c \ + ospi_xip/source/ospi/ospi_drv.c \ + se_services/source/services_host_application.c \ + se_services/source/services_host_boot.c \ + se_services/source/services_host_clocks.c \ + se_services/source/services_host_cryptocell.c \ + se_services/source/services_host_handler.c \ + se_services/source/services_host_system.c \ + se_services/source/services_host_power.c \ + se_services/source/services_host_maintenance.c \ + ) + +$(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 +$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation +$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized + +# List of sources for qstr extraction +SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC) + +OBJ += $(PY_O) +OBJ += $(addprefix $(BUILD)/, $(SRC_O)) +OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(LIBM_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(TINYUSB_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(ALIF_SRC_C:.c=.o)) +OBJ += $(GEN_PINS_SRC:.c=.o) + +################################################################################ +# Main targets + +.PHONY: all erase deploy gdb objdump release + +.DELETE_ON_ERROR: + +obj: $(OBJ) +all: $(BUILD)/firmware.bin + +$(BUILD): + $(MKDIR) -p $@ + +$(BUILD)/ensemble.ld: $(LD_FILE) + $(ECHO) "Preprocess linker script $@" + $(Q)$(CPP) -P -E $(CFLAGS) $^ > $@ + +$(BUILD)/firmware.elf: $(OBJ) $(BUILD)/ensemble.ld + $(ECHO) "Link $@" + $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + $(Q)$(SIZE) $@ + +$(BUILD)/firmware.bin: $(BUILD)/firmware.elf + $(Q)$(OBJCOPY) -Obinary $^ $(BUILD)/firmware.bin + +################################################################################ +# Remaining make rules + +# Use a pattern rule here so that make will only call make-pins.py once to make +# both pins_board.c and pins_board.h +$(BUILD)/%_board.c $(HEADER_BUILD)/%_board.h: $(BOARD_DIR)/%.csv $(GEN_PIN_MKPINS) $(GEN_PIN_PREFIX) | $(HEADER_BUILD) + $(ECHO) "GEN $@" + $(Q)$(PYTHON) $(GEN_PIN_MKPINS) \ + --board-csv $(GEN_PINS_BOARD_CSV) \ + --prefix $(GEN_PIN_PREFIX) \ + --output-source $(GEN_PINS_SRC) \ + --output-header $(GEN_PINS_HDR) + +include $(TOP)/py/mkrules.mk diff --git a/ports/alif/mcu/ensemble.ld.S b/ports/alif/mcu/ensemble.ld.S index 198641ba3dcba..8129a749eb22b 100644 --- a/ports/alif/mcu/ensemble.ld.S +++ b/ports/alif/mcu/ensemble.ld.S @@ -24,25 +24,44 @@ * THE SOFTWARE. */ +#ifdef CORE_M55_HP +__DTCM_SIZE = 1024K; +#else +__DTCM_SIZE = 256K; +#endif + // Entry Point ENTRY(Reset_Handler) MEMORY { - ROM (rx) : ORIGIN = 0x80000000, LENGTH = 0x0057F000 - ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 - #ifdef CORE_M55_HP - DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00100000 - #else - DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00040000 - #endif - SRAM0 (rwx) : ORIGIN = 0x02000000, LENGTH = 0x00400000 - SRAM1 (rwx) : ORIGIN = 0x08000000, LENGTH = 0x00280000 + MRAM_BL (rx) : ORIGIN = 0x80000000, LENGTH = 128K + MRAM_HP (rx) : ORIGIN = 0x80020000, LENGTH = 3072K + MRAM_HE (rx) : ORIGIN = 0x80320000, LENGTH = 1400K + MRAM_FS (rx) : ORIGIN = 0x8047e000, LENGTH = 1024K + MRAM_TOC (rx): ORIGIN = 0x8057e000, LENGTH = 8K + ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 256K + DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = __DTCM_SIZE + SRAM0 (rwx) : ORIGIN = 0x02000000, LENGTH = 4096K + SRAM1 (rwx) : ORIGIN = 0x08000000, LENGTH = 2560K + SRAM6_A (rw) : ORIGIN = 0x62000000, LENGTH = 1024K + SRAM6_B (rw) : ORIGIN = 0x62400000, LENGTH = 1024K + SRAM7 (rw) : ORIGIN = 0x63000000, LENGTH = 512K + SRAM8 (rw) : ORIGIN = 0x63200000, LENGTH = 2048K + SRAM9_A (rw) : ORIGIN = 0x60000000, LENGTH = 256K + SRAM9_B (rw) : ORIGIN = 0x60040000, LENGTH = 512K } -__STACK_SIZE = 0x00004000; -__HEAP_SIZE = 0x00004000; -__MP_HEAP_SIZE = 0x00040000; +#ifdef CORE_M55_HP +REGION_ALIAS("ROM", MRAM_HP); +__MP_HEAP_SIZE = 256K; +#else +REGION_ALIAS("ROM", MRAM_HE); +__MP_HEAP_SIZE = 128K; +#endif + +__STACK_SIZE = 16K; +__HEAP_SIZE = 16K; SECTIONS { From 6b4d46569bf87673521645b3340e25a3336c8d40 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 17 Jul 2024 17:43:38 +0300 Subject: [PATCH 23/77] alif: Support running the port on the HE core. The same MicroPython firmware is built for the HE but with slightly different options, for example no USB. Signed-off-by: iabdalkader --- ports/alif/alif_flash.c | 2 ++ ports/alif/boards/manifest.py | 2 +- ports/alif/main.c | 4 ++-- ports/alif/modalif.c | 2 ++ ports/alif/modules/he/_boot.py | 21 +++++++++++++++++++++ ports/alif/modules/{ => hp}/_boot.py | 0 ports/alif/mpconfigport.h | 20 +++++++++++++++----- ports/alif/mpconfigport.mk | 1 + ports/alif/mphalport.c | 2 ++ ports/alif/ospi_flash.c | 4 +++- ports/alif/tinyusb_port/tusb_config.h | 2 +- 11 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 ports/alif/modules/he/_boot.py rename ports/alif/modules/{ => hp}/_boot.py (100%) diff --git a/ports/alif/alif_flash.c b/ports/alif/alif_flash.c index e3b2125800ea2..f2ea5ac85a88e 100644 --- a/ports/alif/alif_flash.c +++ b/ports/alif/alif_flash.c @@ -30,6 +30,7 @@ #include "modalif.h" #include "ospi_flash.h" +#if MICROPY_HW_ENABLE_OSPI typedef struct _alif_flash_obj_t { mp_obj_base_t base; uint32_t flash_base_addr; @@ -158,3 +159,4 @@ MP_DEFINE_CONST_OBJ_TYPE( make_new, alif_flash_make_new, locals_dict, &alif_flash_locals_dict ); +#endif // MICROPY_HW_ENABLE_OSPI diff --git a/ports/alif/boards/manifest.py b/ports/alif/boards/manifest.py index 148f1d6a6eb5e..1dc9e179acc14 100644 --- a/ports/alif/boards/manifest.py +++ b/ports/alif/boards/manifest.py @@ -1,4 +1,4 @@ -freeze("$(PORT_DIR)/modules") +freeze("$(PORT_DIR)/modules/$(MCU_CORE)") include("$(MPY_DIR)/extmod/asyncio") require("dht") require("neopixel") diff --git a/ports/alif/main.c b/ports/alif/main.c index 2d3134176d32e..975ee7ed2d328 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -68,11 +68,11 @@ void _start(void) { #if MICROPY_HW_ENABLE_UART_REPL mp_uart_init(); #endif - + #if MICROPY_HW_ENABLE_OSPI if (ospi_flash_init() != 0) { MICROPY_BOARD_FATAL_ERROR("ospi_init failed"); } - + #endif #if MICROPY_HW_ENABLE_USBDEV NVIC_ClearPendingIRQ(USB_IRQ_IRQn); NVIC_SetPriority(USB_IRQ_IRQn, IRQ_PRI_USB); diff --git a/ports/alif/modalif.c b/ports/alif/modalif.c index 5577b53eaca85..1c3c34394cd11 100644 --- a/ports/alif/modalif.c +++ b/ports/alif/modalif.c @@ -37,7 +37,9 @@ static MP_DEFINE_CONST_FUN_OBJ_0(alif_info_obj, alif_info); static const mp_rom_map_elem_t alif_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alif) }, + #if MICROPY_HW_ENABLE_OSPI { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&alif_flash_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&alif_info_obj) }, #if MICROPY_HW_USB_MSC // Attribute to indicate USB MSC is enabled. diff --git a/ports/alif/modules/he/_boot.py b/ports/alif/modules/he/_boot.py new file mode 100644 index 0000000000000..bc1320f190694 --- /dev/null +++ b/ports/alif/modules/he/_boot.py @@ -0,0 +1,21 @@ +import openamp +import time +from machine import Pin + + +def ept_recv_callback(src_addr, data): + print("Received message on endpoint", data) + + +# Create a new RPMsg endpoint to communicate with main core. +ept = openamp.Endpoint("vuart-channel", callback=ept_recv_callback) + +pin = Pin("LED_BLUE", Pin.OUT) + +count = 0 +while True: + if ept.is_ready(): + ept.send("Hello from HE %d" % count, timeout=1000) + count += 1 + time.sleep_ms(100) + pin(not pin()) diff --git a/ports/alif/modules/_boot.py b/ports/alif/modules/hp/_boot.py similarity index 100% rename from ports/alif/modules/_boot.py rename to ports/alif/modules/hp/_boot.py diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 95b5e3ca9fa24..df33b92202431 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -36,13 +36,18 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) #endif -#define MICROPY_HW_ENABLE_UART_REPL (1) // useful if there is no USB -#define MICROPY_HW_ENABLE_USBDEV (1) - +#ifndef MICROPY_HW_ENABLE_OSPI +#define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) +#endif +#ifndef MICROPY_HW_ENABLE_USBDEV +#define MICROPY_HW_ENABLE_USBDEV (CORE_M55_HP) +#endif #ifndef MICROPY_HW_USB_PRODUCT_FS_STRING #define MICROPY_HW_USB_PRODUCT_FS_STRING "Board in HS mode" #endif -#define MICROPY_HW_USB_CDC (1) +#ifndef MICROPY_HW_USB_CDC +#define MICROPY_HW_USB_CDC (CORE_M55_HP) +#endif #define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) #ifndef MICROPY_HW_USB_MSC #define MICROPY_HW_USB_MSC (0) @@ -53,7 +58,9 @@ #ifndef MICROPY_HW_USB_PID #define MICROPY_HW_USB_PID (0x9802) // interface has CDC only #endif - +#ifndef MICROPY_HW_ENABLE_UART_REPL +#define MICROPY_HW_ENABLE_UART_REPL (CORE_M55_HP) // useful if there is no USB +#endif #define MICROPY_HW_FLASH_BLOCK_SIZE_BYTES (4096) // Memory allocation policies @@ -78,6 +85,9 @@ #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#ifndef MICROPY_FLOAT_IMPL +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#endif #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_USE_INTERNAL_ERRNO (1) diff --git a/ports/alif/mpconfigport.mk b/ports/alif/mpconfigport.mk index 3d04450a60fa7..92d9a5db8faf0 100644 --- a/ports/alif/mpconfigport.mk +++ b/ports/alif/mpconfigport.mk @@ -10,3 +10,4 @@ MICROPY_VFS_LFS2 ?= 1 # File containing description of content to be frozen into firmware. FROZEN_MANIFEST ?= boards/manifest.py +MICROPY_MANIFEST_MCU_CORE := $(shell echo $(MCU_CORE) | awk -F'_' '{print tolower($$2)}') diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index ba58923f2fe2a..9769ddeaeb61e 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -85,8 +85,10 @@ int mp_hal_stdin_rx_chr(void) { // Send string of given length mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { + #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC || MICROPY_PY_OS_DUPTERM mp_uint_t ret = len; bool did_write = false; + #endif #if MICROPY_HW_ENABLE_UART_REPL mp_uart_write_strn(str, len); diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index fa96053b36579..f84761366b3f0 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -26,8 +26,9 @@ #include "py/mperrno.h" #include "py/mphal.h" -#include "ospi_flash.h" +#if MICROPY_HW_ENABLE_OSPI +#include "ospi_flash.h" #include "ospi_drv.h" #include "pinconf.h" @@ -263,3 +264,4 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { } return ret; } +#endif // MICROPY_HW_ENABLE_OSPI diff --git a/ports/alif/tinyusb_port/tusb_config.h b/ports/alif/tinyusb_port/tusb_config.h index d244b4c241e6b..6c4ead1128846 100644 --- a/ports/alif/tinyusb_port/tusb_config.h +++ b/ports/alif/tinyusb_port/tusb_config.h @@ -44,7 +44,7 @@ #define CFG_TUSB_DEBUG 0 // Enable Device stack -#define CFG_TUD_ENABLED 1 +#define CFG_TUD_ENABLED (CORE_M55_HP) // Default is max speed that hardware controller could support with on-chip PHY #define CFG_TUD_MAX_SPEED OPT_MODE_HIGH_SPEED From 4f6f283abbdcbdec5cb2144a74fdd2937540ac71 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 17 Jul 2024 17:45:08 +0300 Subject: [PATCH 24/77] alif: Implement Open-AMP port backend. Signed-off-by: iabdalkader --- ports/alif/Makefile | 4 +- ports/alif/alif.mk | 11 +++ ports/alif/irq.h | 1 + ports/alif/mcu/ensemble.ld.S | 8 ++ ports/alif/mpconfigport.h | 1 + ports/alif/mpmetalport.c | 119 +++++++++++++++++++++++ ports/alif/mpmetalport.h | 89 ++++++++++++++++++ ports/alif/mpremoteprocport.c | 172 ++++++++++++++++++++++++++++++++++ 8 files changed, 403 insertions(+), 2 deletions(-) create mode 100644 ports/alif/mpmetalport.c create mode 100644 ports/alif/mpmetalport.h create mode 100644 ports/alif/mpremoteprocport.c diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 5cc334a7cf9f9..7d6731dacf200 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -76,10 +76,10 @@ $(BUILD): $(MKDIR) -p $@ $(BUILD)/M55_HP/firmware.bin: - make -f alif.mk MCU_CORE=M55_HP + make -f alif.mk MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0 $(BUILD)/M55_HE/firmware.bin: - make -f alif.mk MCU_CORE=M55_HE + make -f alif.mk MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 $(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS) $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index ad69499eccfa8..c68fc66d44cc6 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -203,6 +203,17 @@ $(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALI $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized +# Add Alif-specific implementation of libmetal (and optionally OpenAMP's rproc). +# Note: libmetal code is generated via a pre-processor so ensure that runs first. +ifeq ($(MICROPY_PY_OPENAMP),1) +SRC_C += mpmetalport.c +$(BUILD)/mpmetalport.o: $(BUILD)/openamp/metal/config.h +ifeq ($(MICROPY_PY_OPENAMP_REMOTEPROC),1) +SRC_C += mpremoteprocport.c +$(BUILD)/mpremoteprocport.o: $(BUILD)/openamp/metal/config.h +endif +endif + # List of sources for qstr extraction SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC) diff --git a/ports/alif/irq.h b/ports/alif/irq.h index a20ff7aebdef1..de6d07ab6d32c 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -45,6 +45,7 @@ #define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_ADC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 3, 0) #define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) +#define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) // these states correspond to values from query_irq, enable_irq and disable_irq diff --git a/ports/alif/mcu/ensemble.ld.S b/ports/alif/mcu/ensemble.ld.S index 8129a749eb22b..aeefdb7eacb78 100644 --- a/ports/alif/mcu/ensemble.ld.S +++ b/ports/alif/mcu/ensemble.ld.S @@ -114,6 +114,14 @@ SECTIONS * (.bss.sram0*) } > SRAM0 + /* Open-AMP Shared Memory Region */ + .openamp_memory (NOLOAD) : ALIGN(32) + { + _openamp_shm_region_start = .; + . = . + 64K; + _openamp_shm_region_end = .; + } >SRAM6_A + .bss : ALIGN(4) { __bss_start__ = .; diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index df33b92202431..dfe1ec2561380 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -91,6 +91,7 @@ #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_USE_INTERNAL_ERRNO (1) +#define MICROPY_TRACKED_ALLOC (MICROPY_PY_OPENAMP) // Fine control over Python builtins, classes, modules, etc #define MICROPY_PY_SYS_PLATFORM "alif" diff --git a/ports/alif/mpmetalport.c b/ports/alif/mpmetalport.c new file mode 100644 index 0000000000000..f80d039c26d23 --- /dev/null +++ b/ports/alif/mpmetalport.c @@ -0,0 +1,119 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + * + * libmetal Alif port. + */ + +#include ALIF_CMSIS_H +#include "hwsem.h" + +#include "py/mperrno.h" +#include "py/mphal.h" + +#include "metal/sys.h" +#include "metal/utilities.h" +#include "metal/device.h" + +struct metal_state _metal; +static mp_sched_node_t rproc_notify_node; + +int metal_sys_init(const struct metal_init_params *params) { + metal_unused(params); + + // Reset the hardware semaphore. + hwsem_reset(METAL_HSEM_DEVICE); + #if MICROPY_PY_OPENAMP_HOST + hwsem_reset(METAL_HSEM_REMOTE); + #endif + + // Enable the hardware semaphore IRQ. + NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); + NVIC_SetPriority(METAL_HSEM_IRQn, IRQ_PRI_HWSEM); + NVIC_EnableIRQ(METAL_HSEM_IRQn); + + // If cache management is not enabled, configure the MPU to disable + // caching for the entire Open-AMP shared memory region. + #ifndef VIRTIO_USE_DCACHE + ARM_MPU_Disable(); + // NOTE: The startup code uses the first 4 attributes. + #define MEMATTR_IDX_NORMAL_NON_CACHEABLE 4 + ARM_MPU_SetMemAttr(MEMATTR_IDX_NORMAL_NON_CACHEABLE, ARM_MPU_ATTR( + ARM_MPU_ATTR_NON_CACHEABLE, + ARM_MPU_ATTR_NON_CACHEABLE)); + MPU->RNR = METAL_MPU_REGION_ID; + MPU->RBAR = ARM_MPU_RBAR(METAL_MPU_REGION_BASE, ARM_MPU_SH_NON, 0, 1, 0); // RO-0, NP-1, XN-0 + MPU->RLAR = ARM_MPU_RLAR(METAL_MPU_REGION_BASE + METAL_MPU_REGION_SIZE - 1, MEMATTR_IDX_NORMAL_NON_CACHEABLE); + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); + #endif + + metal_bus_register(&metal_generic_bus); + + return 0; +} + +void metal_sys_finish(void) { + NVIC_DisableIRQ(METAL_HSEM_IRQn); + NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); + hwsem_reset(METAL_HSEM_DEVICE); + metal_bus_unregister(&metal_generic_bus); +} + +unsigned int sys_irq_save_disable(void) { + return disable_irq(); +} + +void sys_irq_restore_enable(unsigned int state) { + enable_irq(state); +} + +void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa, + size_t size, unsigned int flags) { + metal_unused(pa); + metal_unused(size); + metal_unused(flags); + return va; +} + +void metal_machine_cache_flush(void *addr, unsigned int len) { + SCB_CleanDCache_by_Addr(addr, len); +} + +void metal_machine_cache_invalidate(void *addr, unsigned int len) { + SCB_InvalidateDCache_by_Addr(addr, len); +} + +int metal_rproc_notify(void *priv, uint32_t id) { + // Release the HW semaphore to notify the other core. + hwsem_release(METAL_HSEM_REMOTE, HWSEM_MASTERID); + return 0; +} + +void METAL_HSEM_IRQ_HANDLER(void) { + // Schedule the node only if the other core released the Semaphore. + if (METAL_HSEM_DEVICE->HWSEM_REL_REG == 0) { + mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified); + } + hwsem_request(METAL_HSEM_DEVICE, METAL_HSEM_REMOTE_ID); +} diff --git a/ports/alif/mpmetalport.h b/ports/alif/mpmetalport.h new file mode 100644 index 0000000000000..5cd697c0e9c7b --- /dev/null +++ b/ports/alif/mpmetalport.h @@ -0,0 +1,89 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + * + * libmetal alif port. + */ +#ifndef MICROPY_INCLUDED_ALIF_MPMETALPORT_H +#define MICROPY_INCLUDED_ALIF_MPMETALPORT_H + +#include +#include "py/mphal.h" +#include "py/runtime.h" + +#define METAL_HAVE_STDATOMIC_H 0 +#define METAL_HAVE_FUTEX_H 0 + +#define METAL_MAX_DEVICE_REGIONS 2 + +#if MICROPY_PY_OPENAMP_HOST +#define METAL_HSEM_DEVICE ((HWSEM_Type *)HWSEM14_BASE) +#define METAL_HSEM_REMOTE ((HWSEM_Type *)HWSEM15_BASE) +#define METAL_HSEM_REMOTE_ID (0x410FD222U) +#define METAL_HSEM_IRQn HWSEM_IRQ14_IRQn +#define METAL_HSEM_IRQ_HANDLER HWSEM_IRQ14Handler +#else +#define METAL_HSEM_DEVICE ((HWSEM_Type *)HWSEM15_BASE) +#define METAL_HSEM_REMOTE ((HWSEM_Type *)HWSEM14_BASE) +#define METAL_HSEM_REMOTE_ID (0x410FD221U) +#define METAL_HSEM_IRQn HWSEM_IRQ15_IRQn +#define METAL_HSEM_IRQ_HANDLER HWSEM_IRQ15Handler +#endif + +// Set to 1 to enable log output. +#define METAL_LOG_HANDLER_ENABLE 0 + +#define metal_cpu_yield() + +// Shared memory config +#define METAL_SHM_NAME "OPENAMP_SHM" +// Note 1K must be reserved at the start of the openamp +// shared memory region, for the shared resource table. +#define METAL_RSC_ADDR ((void *)_openamp_shm_region_start) +#define METAL_RSC_SIZE (1024) + +#define METAL_SHM_ADDR ((metal_phys_addr_t)(_openamp_shm_region_start + METAL_RSC_SIZE)) +#define METAL_SHM_SIZE ((size_t)(_openamp_shm_region_end - _openamp_shm_region_start - METAL_RSC_SIZE)) + +#define METAL_MPU_REGION_ID (9) // NOTE: The startup code uses the first 9 regions. +#define METAL_MPU_REGION_BASE ((uint32_t)_openamp_shm_region_start) +#define METAL_MPU_REGION_SIZE (0x00010000U) + +extern const char _openamp_shm_region_start[]; +extern const char _openamp_shm_region_end[]; + +int metal_rproc_notify(void *priv, uint32_t id); +extern void openamp_remoteproc_notified(mp_sched_node_t *node); + +static inline int __metal_sleep_usec(unsigned int usec) { + mp_hal_delay_us(usec); + return 0; +} + +static inline void metal_generic_default_poll(void) { + mp_event_handle_nowait(); + __WFI(); +} + +#endif // MICROPY_INCLUDED_ALIF_METAL_PORT_H diff --git a/ports/alif/mpremoteprocport.c b/ports/alif/mpremoteprocport.c new file mode 100644 index 0000000000000..27210a824057e --- /dev/null +++ b/ports/alif/mpremoteprocport.c @@ -0,0 +1,172 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + * + * modremoteproc alif port. + */ + +#include +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "metal/alloc.h" +#include "metal/errno.h" +#include "metal/io.h" +#include "metal/sys.h" +#include "metal/device.h" +#include "metal/utilities.h" +#include "extmod/modopenamp_remoteproc.h" +#include "se_services.h" + +typedef struct mmap { + uintptr_t base; + uintptr_t limit; +} mmap_t; + +static const mmap_t mmap_nocache[] = { + { .base = ITCM_BASE, .limit = ITCM_BASE + ITCM_SIZE }, + { .base = DTCM_BASE, .limit = DTCM_BASE + ITCM_SIZE }, + { .base = MRAM_BASE, .limit = MRAM_BASE + MRAM_SIZE } +}; + +static bool is_cacheable(const void *p, size_t bytes) { + uintptr_t base = (uintptr_t)p; + if (bytes == 0) { + return false; + } + uintptr_t limit = base + bytes; + for (unsigned int i = 0; i < sizeof(mmap_nocache) / sizeof(mmap_nocache[0]); i++) { + if (base >= mmap_nocache[i].base && limit < mmap_nocache[i].limit) { + return false; + } + } + return true; +} + +struct remoteproc *mp_openamp_remoteproc_init(struct remoteproc *rproc, + const struct remoteproc_ops *ops, void *arg) { + metal_log(METAL_LOG_DEBUG, "rproc_init()\n"); + + rproc->ops = ops; + rproc->state = RPROC_OFFLINE; + // Allocate the image store and save it in private data. + rproc->priv = mp_openamp_remoteproc_store_alloc(); + + // Reset the remote core. + se_services_boot_reset_cpu(EXTSYS_1); + return rproc; +} + +void *mp_openamp_remoteproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, unsigned int attribute, + struct metal_io_region **io) { + metal_log(METAL_LOG_DEBUG, "rproc_mmap(): pa 0x%p da 0x%p io 0x%p size %u\n", *pa, *da, *io, size); + + struct remoteproc_mem *mem; + metal_phys_addr_t lpa = *pa; + metal_phys_addr_t lda = *da; + + if (lda == METAL_BAD_PHYS) { + return NULL; + } + + if (lpa == METAL_BAD_PHYS) { + lpa = lda; + } + + // Currently this port doesn't support loading firmware to flash, + // only SD/SRAM images are supported. Check of load address is in + // the flash region, and if so return NULL. + if (lda >= MRAM_BASE && lda < (MRAM_BASE + MRAM_SIZE)) { + return NULL; + } + + mem = metal_allocate_memory(sizeof(*mem)); + if (!mem) { + return NULL; + } + + *io = metal_allocate_memory(sizeof(struct metal_io_region)); + if (!*io) { + metal_free_memory(mem); + return NULL; + } + + remoteproc_init_mem(mem, NULL, lpa, lda, size, *io); + + metal_io_init(*io, (void *)mem->da, &mem->pa, size, + sizeof(metal_phys_addr_t) << 3, attribute, NULL); + + remoteproc_add_mem(rproc, mem); + *pa = lpa; + *da = lda; + return metal_io_phys_to_virt(*io, mem->pa); +} + +int mp_openamp_remoteproc_start(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_start()\n"); + + // Flush cached areas for the remote core. + struct metal_list *node; + metal_list_for_each(&rproc->mems, node) { + struct remoteproc_mem *mem; + mem = metal_container_of(node, struct remoteproc_mem, node); + if (is_cacheable((uint32_t *)mem->pa, mem->size)) { + SCB_CleanDCache_by_Addr((uint32_t *)mem->pa, mem->size); + } + } + + se_services_boot_reset_cpu(EXTSYS_1); + se_services_boot_cpu(EXTSYS_1, (uint32_t)rproc->bootaddr); // GlobalToLocal + return 0; +} + +int mp_openamp_remoteproc_stop(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_stop()\n"); + if (rproc->state == RPROC_RUNNING) { + se_services_boot_reset_cpu(EXTSYS_1); + } + return 0; +} + +int mp_openamp_remoteproc_config(struct remoteproc *rproc, void *data) { + metal_log(METAL_LOG_DEBUG, "rproc_config()\n"); + (void)rproc; + return 0; +} + +void mp_openamp_remoteproc_remove(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_remove()\n"); + (void)rproc; +} + +int mp_openamp_remoteproc_shutdown(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_shutdown()\n"); + if (rproc->state == RPROC_RUNNING) { + se_services_boot_reset_cpu(EXTSYS_1); + } + return 0; +} From cee8e111cbaedc877fcffeb7d8166b60317f7377 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 28 Jul 2024 21:51:29 +0300 Subject: [PATCH 25/77] alif/irq: Define more IRQ priorities. Signed-off-by: Damien George --- ports/alif/irq.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/alif/irq.h b/ports/alif/irq.h index de6d07ab6d32c..59dbce9705947 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -44,8 +44,10 @@ #define IRQ_PRI_QUIET_TIMING NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_ADC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 3, 0) -#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) +#define IRQ_PRI_CSI NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) +#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 7, 0) #define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) +#define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) // these states correspond to values from query_irq, enable_irq and disable_irq From bbb8fd77fde3400cc1b9902e94f05889a7c1f49a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 14 Aug 2024 16:50:28 +1000 Subject: [PATCH 26/77] alif/system_tick: Implement optional LPTIMER support for systick. Signed-off-by: Damien George --- ports/alif/mpconfigport.h | 6 ++ ports/alif/mphalport.c | 12 ++++ ports/alif/system_tick.c | 137 +++++++++++++++++++++++++++++++++++++- ports/alif/system_tick.h | 6 +- 4 files changed, 158 insertions(+), 3 deletions(-) diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index dfe1ec2561380..b1a01910628bb 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -36,6 +36,12 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) #endif +// Select the low-level system tick implementation. +#if !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \ + && !defined(MICROPY_HW_SYSTEM_TICK_USE_UTIMER) +#define MICROPY_HW_SYSTEM_TICK_USE_UTIMER (1) +#endif + #ifndef MICROPY_HW_ENABLE_OSPI #define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) #endif diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 9769ddeaeb61e..becac8f3b4a0a 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -120,16 +120,28 @@ mp_uint_t mp_hal_ticks_cpu(void) { mp_uint_t mp_hal_ticks_us(void) { // Convert system tick to microsecond counter. + #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + return system_tick_get_u64() * 1000000 / system_tick_source_hz; + #else return system_tick_get_u64() / system_core_clock_mhz; + #endif } mp_uint_t mp_hal_ticks_ms(void) { // Convert system tick to millisecond counter. + #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + return system_tick_get_u64() * 1000ULL / system_tick_source_hz; + #else return system_tick_get_u64() / (SystemCoreClock / 1000); + #endif } void mp_hal_delay_us(mp_uint_t us) { + #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + uint64_t ticks_delay = (uint64_t)us * system_tick_source_hz / 1000000; + #else uint64_t ticks_delay = (uint64_t)us * system_core_clock_mhz; + #endif uint64_t start = system_tick_get_u64(); while (system_tick_get_u64() - start < ticks_delay) { } diff --git a/ports/alif/system_tick.c b/ports/alif/system_tick.c index 5cdf45cba0126..72b8d021c39c0 100644 --- a/ports/alif/system_tick.c +++ b/ports/alif/system_tick.c @@ -27,10 +27,141 @@ #include "irq.h" #include "system_tick.h" -#include "utimer.h" - #define MIN(x, y) ((x) < (y) ? (x) : (y)) +#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + +#include "lptimer.h" +#include "sys_ctrl_lptimer.h" + +// Channel 0 and 1 are cascaded to make a 64-bit counter. +// Channel 2 is used for system_tick_wfe_with_timeout_us. +// Channel 3 is used for system_tick_schedule_after_us. +#define LPTIMER ((LPTIMER_Type *)LPTIMER_BASE) +#define LPTIMER_CH_A (0) +#define LPTIMER_CH_B (1) +#define LPTIMER_CH_C (2) +#define LPTIMER_CH_D (3) + +uint64_t system_tick_source_hz; + +void system_tick_init(void) { + lptimer_disable_counter(LPTIMER, LPTIMER_CH_A); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_B); + + ANA_REG->MISC_CTRL |= 1 << 0; // SEL_32K, select LXFO + + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_A); + select_lptimer_clk(LPTIMER_CLK_SOURCE_CASCADE, LPTIMER_CH_B); + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_C); + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_D); + + lptimer_load_max_count(LPTIMER, LPTIMER_CH_A); + lptimer_set_mode_freerunning(LPTIMER, LPTIMER_CH_A); + + lptimer_load_max_count(LPTIMER, LPTIMER_CH_B); + lptimer_set_mode_freerunning(LPTIMER, LPTIMER_CH_B); + + lptimer_enable_counter(LPTIMER, LPTIMER_CH_B); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_A); + + system_tick_source_hz = 32768; + + NVIC_ClearPendingIRQ(LPTIMER2_IRQ_IRQn); + NVIC_SetPriority(LPTIMER2_IRQ_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(LPTIMER2_IRQ_IRQn); + + NVIC_ClearPendingIRQ(LPTIMER3_IRQ_IRQn); + NVIC_SetPriority(LPTIMER3_IRQ_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(LPTIMER3_IRQ_IRQn); +} + +void LPTIMER2_IRQHandler(void) { + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_C); + __SEV(); +} + +void LPTIMER3_IRQHandler(void) { + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_D); + system_tick_schedule_callback(); + __SEV(); +} + +uint32_t system_tick_get_u32(void) { + return 0xffffffff - lptimer_get_count(LPTIMER, LPTIMER_CH_A); +} + +uint64_t system_tick_get_u64(void) { + // Get 64-bit counter value from the hardware timer. + // Sample it twice in case the low counter wraps around while sampling. + uint32_t irq_state = disable_irq(); + uint32_t lo0 = lptimer_get_count(LPTIMER, LPTIMER_CH_A); + uint32_t hi0 = lptimer_get_count(LPTIMER, LPTIMER_CH_B); + uint32_t lo1 = lptimer_get_count(LPTIMER, LPTIMER_CH_A); + uint32_t hi1 = lptimer_get_count(LPTIMER, LPTIMER_CH_B); + enable_irq(irq_state); + + if (hi0 == hi1) { + // Low counter may have wrapped around between sampling of lo0 and hi0, so prefer second sampling. + lo0 = lo1; + hi0 = hi1; + } else { + // Low counter wrapped around either between sampling of hi0 and lo1, or sampling of lo1 and hi1. + // In either case use the first sampling. + } + + // Convert from descending count to ascending. + lo0 = 0xffffffff - lo0; + hi0 = 0xffffffff - hi0; + + // Return a 64-bit value. + return ((uint64_t)hi0 << 32) | (uint64_t)lo0; +} + +void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { + // Maximum 131 second timeout, to not overflow 32-bit ticks when + // LPTIMER is clocked at 32768Hz. + uint32_t timeout_ticks = (uint64_t)MIN(timeout_us, 131000000) * system_tick_source_hz / 1000000; + + // Set up the LPTIMER interrupt to fire after the given timeout. + lptimer_disable_counter(LPTIMER, LPTIMER_CH_C); + lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_C); + lptimer_load_count(LPTIMER, LPTIMER_CH_C, &timeout_ticks); + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_C); + + // Wait for an event. + __WFE(); + + // Disable the LPTIMER interrupt (in case a different interrupt woke the WFE). + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_C); +} + +void system_tick_schedule_after_us(uint32_t ticks_us) { + // Disable the interrupt in case it's still active. + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_D); + + // Maximum 131 second timeout, to not overflow 32-bit ticks when + // LPTIMER is clocked at 32768Hz. + uint32_t timeout_ticks = (uint64_t)MIN(ticks_us, 131000000) * system_tick_source_hz / 1000000; + + // Set up the LPTIMER interrupt to fire after the given timeout. + lptimer_disable_counter(LPTIMER, LPTIMER_CH_D); + lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_D); + lptimer_load_count(LPTIMER, LPTIMER_CH_D, &timeout_ticks); + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_D); +} + +#elif MICROPY_HW_SYSTEM_TICK_USE_UTIMER + +#include "utimer.h" + #define UTIMER ((UTIMER_Type *)UTIMER_BASE) #define UTIMER_CHANNEL (11) @@ -166,3 +297,5 @@ void system_tick_schedule_after_us(uint32_t ticks_us) { } } } + +#endif diff --git a/ports/alif/system_tick.h b/ports/alif/system_tick.h index 2373f9421de7b..d808b76322e7d 100644 --- a/ports/alif/system_tick.h +++ b/ports/alif/system_tick.h @@ -26,9 +26,13 @@ #ifndef MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H #define MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H -#include +#include "py/mpconfig.h" +#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER +extern uint64_t system_tick_source_hz; +#else extern uint64_t system_core_clock_mhz; +#endif void system_tick_init(void); uint32_t system_tick_get_u32(void); From c6ebecc4c3dbeeecbb0eecabd4ecb6162b8f14bb Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Aug 2024 12:59:47 +1000 Subject: [PATCH 27/77] alif/system_tick: Implement optional ARM SysTick support for systick. Signed-off-by: Damien George --- ports/alif/mpconfigport.h | 6 +++- ports/alif/mphalport.c | 16 ++++++++-- ports/alif/system_tick.c | 64 ++++++++++++++++++++++++++++++++++++++- ports/alif/system_tick.h | 2 +- 4 files changed, 82 insertions(+), 6 deletions(-) diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index b1a01910628bb..0fee2c379a3bd 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -37,10 +37,14 @@ #endif // Select the low-level system tick implementation. -#if !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \ +#if !defined(MICROPY_HW_SYSTEM_TICK_USE_SYSTICK) \ + && !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \ && !defined(MICROPY_HW_SYSTEM_TICK_USE_UTIMER) #define MICROPY_HW_SYSTEM_TICK_USE_UTIMER (1) #endif +#if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK +#define MICROPY_SOFT_TIMER_TICKS_MS system_tick_ms_counter +#endif #ifndef MICROPY_HW_ENABLE_OSPI #define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index becac8f3b4a0a..5e3dad141908e 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -120,7 +120,9 @@ mp_uint_t mp_hal_ticks_cpu(void) { mp_uint_t mp_hal_ticks_us(void) { // Convert system tick to microsecond counter. - #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + return system_tick_get_u64(); + #elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER return system_tick_get_u64() * 1000000 / system_tick_source_hz; #else return system_tick_get_u64() / system_core_clock_mhz; @@ -129,7 +131,9 @@ mp_uint_t mp_hal_ticks_us(void) { mp_uint_t mp_hal_ticks_ms(void) { // Convert system tick to millisecond counter. - #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + return system_tick_get_u64() / 1000ULL; + #elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER return system_tick_get_u64() * 1000ULL / system_tick_source_hz; #else return system_tick_get_u64() / (SystemCoreClock / 1000); @@ -137,7 +141,9 @@ mp_uint_t mp_hal_ticks_ms(void) { } void mp_hal_delay_us(mp_uint_t us) { - #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + uint64_t ticks_delay = (uint64_t)us; + #elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER uint64_t ticks_delay = (uint64_t)us * system_tick_source_hz / 1000000; #else uint64_t ticks_delay = (uint64_t)us * system_core_clock_mhz; @@ -167,6 +173,8 @@ void system_tick_schedule_callback(void) { pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); } +#if !defined(MICROPY_SOFT_TIMER_TICKS_MS) + uint32_t soft_timer_get_ms(void) { return mp_hal_ticks_ms(); } @@ -177,3 +185,5 @@ void soft_timer_schedule_at_ms(uint32_t ticks_ms) { ms = MIN(ms, 4000000); // ensure ms * 1000 doesn't overflow system_tick_schedule_after_us(ms * 1000); } + +#endif diff --git a/ports/alif/system_tick.c b/ports/alif/system_tick.c index 72b8d021c39c0..4f161e6709e4b 100644 --- a/ports/alif/system_tick.c +++ b/ports/alif/system_tick.c @@ -29,7 +29,69 @@ #define MIN(x, y) ((x) < (y) ? (x) : (y)) -#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER +#if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + +#include "shared/runtime/softtimer.h" +#include "pendsv.h" + +volatile uint32_t system_tick_ms_counter; + +void system_tick_init(void) { + // Configure SysTick to run at 1kHz (1ms interval) + SysTick_Config(SystemCoreClock / 1000); + NVIC_SetPriority(SysTick_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(SysTick_IRQn); +} + +void SysTick_Handler(void) { + uint32_t uw_tick = system_tick_ms_counter + 1; + system_tick_ms_counter = uw_tick; + + // Read the systick control register to clear the COUNTFLAG bit. + SysTick->CTRL; + + if (soft_timer_next == uw_tick) { + pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); + } +} + +uint32_t system_tick_get_u32(void) { + return system_tick_get_u64(); +} + +uint64_t system_tick_get_u64(void) { + mp_uint_t irq_state = disable_irq(); + uint32_t counter = SysTick->VAL; + uint32_t milliseconds = system_tick_ms_counter; + uint32_t status = SysTick->CTRL; + enable_irq(irq_state); + + // It's still possible for the COUNTFLAG bit to get set if the counter was + // reloaded between reading VAL and reading CTRL. With interrupts disabled + // it definitely takes less than 50 cycles between reading VAL and + // reading CTRL, so the test (counter > 50) is to cover the case where VAL + // is +ve and very close to zero, and the COUNTFLAG bit is also set. + if ((status & SysTick_CTRL_COUNTFLAG_Msk) && counter > 50) { + // This means that the HW reloaded VAL between the time we read VAL and the + // time we read CTRL, which implies that there is an interrupt pending + // to increment the tick counter. + milliseconds++; + } + uint32_t load = SysTick->LOAD; + counter = load - counter; // Convert from decrementing to incrementing + + // Calculate 64-bit microsecond counter. + return (uint64_t)milliseconds * 1000ULL + (uint64_t)((counter * 1000) / (load + 1)); +} + +void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { + if (timeout_us > 1000) { + // SysTick will wake us in at most 1ms. + __WFI(); + } +} + +#elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER #include "lptimer.h" #include "sys_ctrl_lptimer.h" diff --git a/ports/alif/system_tick.h b/ports/alif/system_tick.h index d808b76322e7d..a380808b2e043 100644 --- a/ports/alif/system_tick.h +++ b/ports/alif/system_tick.h @@ -30,7 +30,7 @@ #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER extern uint64_t system_tick_source_hz; -#else +#elif MICROPY_HW_SYSTEM_TICK_USE_UTIMER extern uint64_t system_core_clock_mhz; #endif From 58d6fe236b60daa92810abc81c8462e362a344dc Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 3 Sep 2024 17:04:56 +1000 Subject: [PATCH 28/77] alif/mpconfigport: Select SysTick on HE core. UTIMER is used by the HP. Signed-off-by: Damien George --- ports/alif/mpconfigport.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 0fee2c379a3bd..ae3ad7a30d352 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -40,7 +40,11 @@ #if !defined(MICROPY_HW_SYSTEM_TICK_USE_SYSTICK) \ && !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \ && !defined(MICROPY_HW_SYSTEM_TICK_USE_UTIMER) +#if CORE_M55_HP #define MICROPY_HW_SYSTEM_TICK_USE_UTIMER (1) +#else +#define MICROPY_HW_SYSTEM_TICK_USE_SYSTICK (1) +#endif #endif #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK #define MICROPY_SOFT_TIMER_TICKS_MS system_tick_ms_counter From 4c4b4844df7746aa2ed4b124663e2472cb908b1a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Oct 2024 18:20:06 +1100 Subject: [PATCH 29/77] alif/mpu: Add custom MPU_Load_Regions function. Signed-off-by: Damien George --- ports/alif/alif.mk | 1 + ports/alif/mpu.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++ ports/alif/mpu.h | 31 ++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 ports/alif/mpu.c create mode 100644 ports/alif/mpu.h diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index c68fc66d44cc6..210b06c65f74e 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -119,6 +119,7 @@ SRC_C = \ main.c \ modalif.c \ mphalport.c \ + mpu.c \ mpuart.c \ msc_disk.c \ ospi_flash.c \ diff --git a/ports/alif/mpu.c b/ports/alif/mpu.c new file mode 100644 index 0000000000000..11c6273fcb345 --- /dev/null +++ b/ports/alif/mpu.c @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mpconfig.h" +#include "mpu.h" +#include ALIF_CMSIS_H + +static const ARM_MPU_Region_t mpu_table[] __STARTUP_RO_DATA_ATTRIBUTE = { + { /* SRAM0 - 4MB : RO-0, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x02000000, ARM_MPU_SH_NON, 0, 1, 0), + .RLAR = ARM_MPU_RLAR(0x023FFFFF, MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT) + }, + { /* SRAM1 - 2.5MB : RO-0, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x08000000, ARM_MPU_SH_NON, 0, 1, 0), + .RLAR = ARM_MPU_RLAR(0x0827FFFF, MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA) + }, + { /* Host Peripherals - 16MB : RO-0, NP-1, XN-1 */ + .RBAR = ARM_MPU_RBAR(0x1A000000, ARM_MPU_SH_NON, 0, 1, 1), + .RLAR = ARM_MPU_RLAR(0x1AFFFFFF, MP_MPU_ATTR_INDEX_DEVICE_nGnRE) + }, + { /* MRAM - 5.5MB : RO-1, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x80000000, ARM_MPU_SH_NON, 1, 1, 0), + .RLAR = ARM_MPU_RLAR(0x8057FFFF, MP_MPU_ATTR_INDEX_NORMAL_WT_RA) + }, + { /* OSPI Regs - 16MB : RO-0, NP-1, XN-1 */ + .RBAR = ARM_MPU_RBAR(0x83000000, ARM_MPU_SH_NON, 0, 1, 1), + .RLAR = ARM_MPU_RLAR(0x83FFFFFF, MP_MPU_ATTR_INDEX_DEVICE_nGnRE) + }, + { /* OSPI0 XIP flash - 512MB : RO-1, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0xA0000000, ARM_MPU_SH_NON, 1, 1, 0), + .RLAR = ARM_MPU_RLAR(0xBFFFFFFF, MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE) + }, +}; + +void MPU_Load_Regions(void) { + // Configure memory attributes. + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT, + ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0), ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0))); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_DEVICE_nGnRE, + ARM_MPU_ATTR(ARM_MPU_ATTR_DEVICE, ARM_MPU_ATTR_DEVICE_nGnRE)); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA, + ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1), ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1))); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WT_RA, + ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0), ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0))); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE, + ARM_MPU_ATTR(ARM_MPU_ATTR_NON_CACHEABLE, ARM_MPU_ATTR_NON_CACHEABLE)); + + // Load the MPU regions from the table. + ARM_MPU_Load(0, mpu_table, sizeof(mpu_table) / sizeof(ARM_MPU_Region_t)); +} diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h new file mode 100644 index 0000000000000..87a7518a126be --- /dev/null +++ b/ports/alif/mpu.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ + +#define MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT (0) +#define MP_MPU_ATTR_INDEX_DEVICE_nGnRE (1) +#define MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA (2) +#define MP_MPU_ATTR_INDEX_NORMAL_WT_RA (3) +#define MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE (4) From 84effb386ac23257e9a399bf52dc23f8f50387fe Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 7 Oct 2024 16:06:50 +1100 Subject: [PATCH 30/77] alif/ospi_flash: Generalise flash driver to support MX chips. Signed-off-by: Damien George --- ports/alif/alif.mk | 1 + ports/alif/ospi_ext.c | 292 ++++++++++++++++++++++++++++++ ports/alif/ospi_ext.h | 57 ++++++ ports/alif/ospi_flash.c | 362 ++++++++++++++++++++++++------------- ports/alif/ospi_flash.h | 61 ++++++- ports/alif/ospi_xip_user.h | 5 - 6 files changed, 643 insertions(+), 135 deletions(-) create mode 100644 ports/alif/ospi_ext.c create mode 100644 ports/alif/ospi_ext.h delete mode 100644 ports/alif/ospi_xip_user.h diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 210b06c65f74e..1581452195840 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -122,6 +122,7 @@ SRC_C = \ mpu.c \ mpuart.c \ msc_disk.c \ + ospi_ext.c \ ospi_flash.c \ pendsv.c \ system_tick.c \ diff --git a/ports/alif/ospi_ext.c b/ports/alif/ospi_ext.c new file mode 100644 index 0000000000000..48446d0622d48 --- /dev/null +++ b/ports/alif/ospi_ext.c @@ -0,0 +1,292 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 ALIF_CMSIS_H +#include "ospi_ext.h" +#include "ospi_xip_user.h" + +#define INST_L16bit (3) + +static void ospi_xip_disable(ospi_flash_cfg_t *ospi_cfg) { + ospi_cfg->aes_regs->aes_control &= ~AES_CONTROL_XIP_EN; +} + +static void ospi_xip_enable(ospi_flash_cfg_t *ospi_cfg) { + ospi_cfg->aes_regs->aes_control |= AES_CONTROL_XIP_EN; + #if OSPI_XIP_ENABLE_AES_DECRYPTION + ospi_cfg->aes_regs->aes_control |= (AES_CONTROL_LD_KEY | AES_CONTROL_DECRYPT_EN); + #endif +} + +// Standard SPI transfer. +void ospi_spi_transfer(ospi_flash_cfg_t *ospi_cfg, size_t len, const uint8_t *buf_out, uint8_t *buf_in) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t ctrlr0 = CTRLR0_IS_MST + | (SINGLE << CTRLR0_SPI_FRF_OFFSET) + | (SPI_TMOD_TR << CTRLR0_TMOD_OFFSET) + | (CTRLR0_DFS_8bit << CTRLR0_DFS_OFFSET) + ; + + uint32_t spi_ctrlr0 = TRANS_TYPE_STANDARD; + + ospi_writel(ospi_cfg, ctrlr0, ctrlr0); + ospi_writel(ospi_cfg, ctrlr1, len - 1); + ospi_writel(ospi_cfg, spi_ctrlr0, spi_ctrlr0); + spi_enable(ospi_cfg); + + // Buffer output data in SPI FIFO. + for (int i = 0; i < len; ++i) { + ospi_writel(ospi_cfg, data_reg, buf_out[i]); + } + + // Enable the SPI peripheral. + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + // Read in data. + for (int i = 0; i < len; ++i) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return; + } + } + buf_in[i] = ospi_readl(ospi_cfg, data_reg); + } +} + +void ospi_setup_read_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len, uint32_t read_len, uint32_t wait_cycles) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + ospi_writel(ospi_cfg, ctrlr1, read_len - 1); + + if (ospi_cfg->ddr_en) { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (1 << CTRLR0_SPI_DDR_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << CTRLR0_INST_DDR_EN_OFFSET; + } + } else { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + } + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + ospi_cfg->rx_req = read_len; + spi_enable(ospi_cfg); +} + +int ospi_recv_blocking_8bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer) { + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return -OSPI_ETIMEDOUT; + } + } + *buffer++ = ospi_readl(ospi_cfg, data_reg); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +int ospi_recv_blocking_16bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint16_t *buffer) { + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return -OSPI_ETIMEDOUT; + } + } + *buffer++ = ospi_readl(ospi_cfg, data_reg); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +int ospi_recv_blocking_32bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint32_t *buffer) { + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return -OSPI_ETIMEDOUT; + } + } + *buffer++ = __ROR(ospi_readl(ospi_cfg, data_reg), 16); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (TMOD_TO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + ospi_writel(ospi_cfg, ctrlr1, 0); + + if (ospi_cfg->ddr_en) { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (1 << CTRLR0_SPI_DDR_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (0 << CTRLR0_WAIT_CYCLES_OFFSET); + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << CTRLR0_INST_DDR_EN_OFFSET; + } + } else { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (0 << CTRLR0_WAIT_CYCLES_OFFSET); + } + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + spi_enable(ospi_cfg); +} + +void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles) { + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (0 << CTRLR0_SCPOL_OFFSET) + | (0 << CTRLR0_SCPH_OFFSET) + | (0 << CTRLR0_SSTE_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + + val = (OCTAL << XIP_CTRL_FRF_OFFSET) + | (0x2 << XIP_CTRL_TRANS_TYPE_OFFSET) + | (ADDR_L32bit << XIP_CTRL_ADDR_L_OFFSET) + | (INST_L16bit << XIP_CTRL_INST_L_OFFSET) + | (0x0 << XIP_CTRL_MD_BITS_EN_OFFSET) + | (read_dummy_cycles << XIP_CTRL_WAIT_CYCLES_OFFSET) + | (0x1 << XIP_CTRL_DFC_HC_OFFSET) + | (ospi_cfg->ddr_en << XIP_CTRL_DDR_EN_OFFSET) + | (ospi_cfg->ddr_en << XIP_CTRL_INST_DDR_EN_OFFSET) + | (0x1 << XIP_CTRL_RXDS_EN_OFFSET) + | (0x1 << XIP_CTRL_INST_EN_OFFSET) + | (0x0 << XIP_CTRL_CONT_XFER_EN_OFFSET) + | (0x0 << XIP_CTRL_HYPERBUS_EN_OFFSET) + | (0x1 << XIP_CTRL_RXDS_SIG_EN) + | (0x0 << XIP_CTRL_XIP_MBL_OFFSET) + | (0x0 << XIP_PREFETCH_EN_OFFSET) + | (0x0 << XIP_CTRL_RXDS_VL_EN_OFFSET); + + ospi_writel(ospi_cfg, xip_ctrl, val); + + ospi_writel(ospi_cfg, rx_sample_dly, 4); + ospi_writel(ospi_cfg, txd_drive_edge, 1); + ospi_cfg->aes_regs->aes_rxds_delay = OSPI_XIP_RXDS_DELAY; + + ospi_writel(ospi_cfg, xip_mode_bits, 0x0); + ospi_writel(ospi_cfg, xip_incr_inst, incr_command); + ospi_writel(ospi_cfg, xip_wrap_inst, wrap_command); + ospi_writel(ospi_cfg, xip_ser, ospi_cfg->ser); + + spi_enable(ospi_cfg); + ospi_xip_enable(ospi_cfg); +} + +void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, uint16_t wrap_command) { + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (0 << CTRLR0_SCPOL_OFFSET) + | (0 << CTRLR0_SCPH_OFFSET) + | (0 << CTRLR0_SSTE_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (CTRLR0_DFS_32bit << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + + val = TRANS_TYPE_FRF_DEFINED + | ((ospi_cfg->ddr_en) << CTRLR0_SPI_DDR_EN_OFFSET) + | (2 << CTRLR0_XIP_MBL_OFFSET) + | (1 << CTRLR0_XIP_DFS_HC_OFFSET) + | (1 << CTRLR0_XIP_INST_EN_OFFSET) + | (CTRLR0_INST_L_16bit << CTRLR0_INST_L_OFFSET) + | (ospi_cfg->addrlen) << (CTRLR0_ADDR_L_OFFSET) + | (ospi_cfg->wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + + ospi_writel(ospi_cfg, xip_mode_bits, 0x1); + ospi_writel(ospi_cfg, xip_incr_inst, incr_command); + ospi_writel(ospi_cfg, xip_wrap_inst, wrap_command); + ospi_writel(ospi_cfg, xip_ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, xip_cnt_time_out, 100); + + spi_enable(ospi_cfg); + + ospi_xip_enable(ospi_cfg); + ospi_xip_disable(ospi_cfg); +} diff --git a/ports/alif/ospi_ext.h b/ports/alif/ospi_ext.h new file mode 100644 index 0000000000000..e467772d15670 --- /dev/null +++ b/ports/alif/ospi_ext.h @@ -0,0 +1,57 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_OSPI_EXT_H +#define MICROPY_INCLUDED_ALIF_OSPI_EXT_H + +#include +#include "ospi_drv.h" + +#define OSPI_ETIMEDOUT (110) + +#define OSPI_INST_L_8bit (CTRLR0_INST_L_8bit) +#define OSPI_INST_L_16bit (CTRLR0_INST_L_16bit) + +#define OSPI_ADDR_L_0bit UINT32_C(0x0) +#define OSPI_ADDR_L_24bit UINT32_C(0x6) +#define OSPI_ADDR_L_32bit UINT32_C(0x8) + +#define OSPI_DATA_L_8bit (CTRLR0_DFS_8bit) +#define OSPI_DATA_L_16bit (CTRLR0_DFS_16bit) +#define OSPI_DATA_L_32bit (CTRLR0_DFS_32bit) + +void ospi_spi_transfer(ospi_flash_cfg_t *ospi_cfg, size_t len, const uint8_t *buf_out, uint8_t *buf_in); + +void ospi_setup_read_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len, uint32_t read_len, uint32_t wait_cycles); +int ospi_recv_blocking_8bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer); +int ospi_recv_blocking_16bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint16_t *buffer); +int ospi_recv_blocking_32bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint32_t *buffer); + +void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len); + +void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles); +void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, uint16_t wrap_command); + +#endif // MICROPY_INCLUDED_ALIF_OSPI_EXT_H diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index f84761366b3f0..6f083de630aa9 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -28,72 +28,84 @@ #include "py/mphal.h" #if MICROPY_HW_ENABLE_OSPI + +#include "ospi_ext.h" #include "ospi_flash.h" #include "ospi_drv.h" +#include "ospi_xip_user.h" #include "pinconf.h" -#define CMD_RDSR (0x05) -#define CMD_WREN (0x06) -#define CMD_SEC_ERASE_32ADDR (0x21) // 4kiB sector erase with 32-bit address -#define CMD_WRVOL (0x81) -#define CMD_RD_DEVID (0x9f) - -#define WAIT_SR_TIMEOUT (1000000) +#define WAIT_SR_TIMEOUT (1000000) -// maximum bytes we can write in one SPI transfer -// limited by 256 byte FIFO buffer (can't go up to 256) -// need to use DMA to make this 256 -#define PAGE_SIZE (128) +// Generic SPI flash commands. +#define CMD_SPI_WREN (0x06) -#define ISSI_MODE_OCTAL_DDR_DQS (0xe7) +// This is the maximum number of bytes that can be written to SPI flash at once. +#define PAGE_SIZE (256) -// All OSPI1 pins use the same alternate function. -#define OSPI1_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1 +// All OSP0/OSPI1 pins use the same alternate function. +#define OSPI_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1 -typedef struct _mp_spiflash_t { +typedef struct _ospi_flash_t { + const ospi_pin_settings_t *pin; + const ospi_flash_settings_t *set; ospi_flash_cfg_t cfg; -} mp_spiflash_t; +} ospi_flash_t; -static mp_spiflash_t global_flash; +static ospi_flash_t global_flash; -// Alif version of this function can overwrite the destination buffer. -static void ospi_recv_blocking2(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer) { - uint32_t val; +/******************************************************************************/ +// Generic SPI-flash helper functions. - ospi_writel(ospi_cfg, data_reg, command); - ospi_writel(ospi_cfg, ser, ospi_cfg->ser); - - ospi_cfg->rx_cnt = 0; +static void ospi_flash_wren_spi(ospi_flash_t *self) { + uint8_t buf[1] = {CMD_SPI_WREN}; + ospi_spi_transfer(&self->cfg, 1, buf, buf); +} - while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { - unsigned int timeout = 100000; - while (ospi_readl(ospi_cfg, rxflr) == 0) { - if (--timeout == 0) { - return; - } - } - val = ospi_readl(ospi_cfg, data_reg); - *buffer++ = (uint8_t)val; - ospi_cfg->rx_cnt++; +static int ospi_flash_read_cmd(ospi_flash_t *self, uint32_t cmd, uint8_t cmd_dummy_cycles, size_t len, uint8_t *dest) { + int ret = 0; + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_0bit, OSPI_DATA_L_8bit, len, cmd_dummy_cycles); + ret = ospi_recv_blocking_8bit_data(&self->cfg, cmd, dest); + } else { + uint16_t dest16 = 0; + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, len, cmd_dummy_cycles); + ospi_push(&self->cfg, cmd); + ret = ospi_recv_blocking_16bit_data(&self->cfg, 0 /* addr */, &dest16); + *dest = dest16; } + return ret; } -static int mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len, uint8_t *dest) { - ospi_setup_read(&self->cfg, 0, len, 8); - ospi_recv_blocking2(&self->cfg, cmd, dest); +static int ospi_flash_write_cmd_addr(ospi_flash_t *self, uint32_t cmd, uint32_t addr_len, uint32_t addr) { + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_write_ext(&self->cfg, false, self->set->inst_len, addr_len, OSPI_DATA_L_8bit); + } else { + ospi_setup_write_ext(&self->cfg, self->set->rxds, self->set->inst_len, addr_len, OSPI_DATA_L_16bit); + } + if (addr_len == OSPI_ADDR_L_0bit) { + ospi_send_blocking(&self->cfg, cmd); + } else { + ospi_push(&self->cfg, cmd); + ospi_send_blocking(&self->cfg, addr); + } return 0; } -static int mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) { - ospi_setup_write(&self->cfg, 0); - ospi_send_blocking(&self->cfg, cmd); - return 0; +static int ospi_flash_write_cmd(ospi_flash_t *self, uint32_t cmd) { + return ospi_flash_write_cmd_addr(self, cmd, OSPI_ADDR_L_0bit, 0); } -static int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) { +static uint32_t ospi_flash_read_id_spi(ospi_flash_t *self) { + uint8_t buf[4] = {0x9f, 0, 0, 0}; + ospi_spi_transfer(&self->cfg, 4, buf, buf); + return buf[1] | buf[2] << 8 | buf[3] << 16; +} + +static int ospi_flash_wait_sr(ospi_flash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) { do { - uint8_t sr; - int ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr); + uint8_t sr = 0; + int ret = ospi_flash_read_cmd(self, self->set->read_sr, self->set->read_sr_dummy_cycles, 1, &sr); if (ret != 0) { return ret; } @@ -105,144 +117,251 @@ static int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, u return -MP_ETIMEDOUT; } -static int mp_spiflash_wait_wel1(mp_spiflash_t *self) { - return mp_spiflash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT); +static int ospi_flash_wait_wel1(ospi_flash_t *self) { + return ospi_flash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT); } -static int mp_spiflash_wait_wip0(mp_spiflash_t *self) { - return mp_spiflash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT); +static int ospi_flash_wait_wip0(ospi_flash_t *self) { + return ospi_flash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT); } -static uint32_t ospi_flash_read_id(mp_spiflash_t *self) { - uint8_t buf[8]; - ospi_setup_read(&self->cfg, 0, 3, ospi_flash_settings.read_id_dummy_cycles); - ospi_recv_blocking2(&self->cfg, CMD_RD_DEVID, buf); +static uint32_t ospi_flash_read_id(ospi_flash_t *self) { + uint8_t buf[4] = {0}; + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_0bit, OSPI_DATA_L_8bit, 3, self->set->read_id_dummy_cycles); + ospi_recv_blocking_8bit_data(&self->cfg, self->set->read_id, buf); + } else { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, 4, self->set->read_id_dummy_cycles); + ospi_push(&self->cfg, self->set->read_id); + // Read 8-bit values because data is in SDR mode for read id. + ospi_recv_blocking_8bit_data(&self->cfg, 0, buf); + } return buf[0] | buf[1] << 8 | buf[2] << 16; } -static void ospi_flash_write_reg_sdr(mp_spiflash_t *self, uint8_t cmd, uint8_t addr, uint8_t value) { - mp_spiflash_write_cmd(self, CMD_WREN); - ospi_setup_write_sdr(&self->cfg, 6); - ospi_push(&self->cfg, cmd); - ospi_push(&self->cfg, 0x00); - ospi_push(&self->cfg, 0x00); +/******************************************************************************/ +// Functions specific to ISSI flash chips. + +int ospi_flash_issi_octal_switch(ospi_flash_t *self) { + // Switch SPI flash to Octal DDR mode. + const uint8_t cmd_wrvol = 0x81; + const uint8_t issi_mode_octal_ddr_dqs = 0xe7; + ospi_flash_wren_spi(self); + uint8_t buf[5] = {cmd_wrvol, 0, 0, 0, issi_mode_octal_ddr_dqs}; + ospi_spi_transfer(&self->cfg, sizeof(buf), buf, buf); + self->cfg.ddr_en = 1; + return 0; +} + +/******************************************************************************/ +// Functions specific to MX flash chips. + +int ospi_flash_mx_octal_switch(ospi_flash_t *self) { + // Switch SPI flash to Octal SDR or DDR mode (SOPI or DOPI) by writing to CR2. + const uint8_t cmd_wrcr2 = 0x72; + const uint8_t mx_mode_enable_sopi = 0x01; + const uint8_t mx_mode_enable_dopi = 0x02; + uint8_t mx_mode; + if (self->set->octal_mode == OSPI_FLASH_OCTAL_MODE_DDD) { + mx_mode = mx_mode_enable_dopi; + } else { + mx_mode = mx_mode_enable_sopi; + } + ospi_flash_wren_spi(self); + uint8_t buf[6] = {cmd_wrcr2, 0, 0, 0, 0, mx_mode}; + ospi_spi_transfer(&self->cfg, sizeof(buf), buf, buf); + if (self->set->octal_mode == OSPI_FLASH_OCTAL_MODE_DDD) { + self->cfg.ddr_en = 1; + } else { + self->cfg.ddr_en = 0; + } + return 0; +} + +static uint8_t ospi_flash_mx_read_cr_helper(ospi_flash_t *self, uint16_t command, uint32_t addr) { + // TODO: currently only works in DDR mode + + uint16_t buf[1] = {0}; + ospi_setup_read_ext(&self->cfg, self->set->rxds, OSPI_INST_L_16bit, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, 1, 4); + ospi_push(&self->cfg, command); + ospi_recv_blocking_16bit_data(&self->cfg, addr, buf); + return buf[0] & 0xff; +} + +uint8_t ospi_flash_mx_read_cr(ospi_flash_t *self) { + return ospi_flash_mx_read_cr_helper(self, 0x15ea, 0); +} + +uint8_t ospi_flash_mx_read_cr2(ospi_flash_t *self, uint32_t addr) { + return ospi_flash_mx_read_cr_helper(self, 0x718e, addr); +} + +static int ospi_flash_mx_write_cr_helper(ospi_flash_t *self, uint16_t command, uint32_t addr, uint8_t value) { + // TODO: currently only works in DDR mode + + // Enable writes so that the register can be modified. + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); + if (ret < 0) { + return ret; + } + + // Do the write. + ospi_setup_write_ext(&self->cfg, self->set->rxds, OSPI_INST_L_16bit, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit); + ospi_push(&self->cfg, command); ospi_push(&self->cfg, addr); - ospi_send_blocking(&self->cfg, value); + ospi_push(&self->cfg, value << 8); // in DDR mode, MSByte contains the register value to write + ospi_writel((&self->cfg), ser, self->cfg.ser); + while ((ospi_readl((&self->cfg), sr) & (SR_TF_EMPTY | SR_BUSY)) != SR_TF_EMPTY) { + } + + // Wait for the write to finish. + return ospi_flash_wait_wip0(self); +} + +int ospi_flash_mx_write_cr(ospi_flash_t *self, uint8_t value) { + return ospi_flash_mx_write_cr_helper(self, 0x01fe, 1, value); } +int ospi_flash_mx_write_cr2(ospi_flash_t *self, uint32_t addr, uint8_t value) { + return ospi_flash_mx_write_cr_helper(self, 0x728d, addr, value); +} + +/******************************************************************************/ +// SPI flash initialisation. + int ospi_flash_init(void) { - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; + + const ospi_pin_settings_t *pin = &ospi_pin_settings; + const ospi_flash_settings_t *set = &ospi_flash_settings; + self->pin = pin; + self->set = set; uint32_t pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; - pinconf_set(pin_OSPI1_CS->port, pin_OSPI1_CS->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); - pinconf_set(pin_OSPI1_SCLK->port, pin_OSPI1_SCLK->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST); - pinconf_set(pin_OSPI1_D0->port, pin_OSPI1_D0->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D1->port, pin_OSPI1_D1->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D2->port, pin_OSPI1_D2->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D3->port, pin_OSPI1_D3->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - #if defined(pin_OSPI1_D4) - pinconf_set(pin_OSPI1_D4->port, pin_OSPI1_D4->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D5->port, pin_OSPI1_D5->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D6->port, pin_OSPI1_D6->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D7->port, pin_OSPI1_D7->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - #endif - #if defined(pin_OSPI1_RXDS) - pinconf_set(pin_OSPI1_RXDS->port, pin_OSPI1_RXDS->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - #endif - - #if defined(pin_OSPI1_RXDS) - if (pin_OSPI1_RXDS->port == PORT_10 && pin_OSPI1_RXDS->pin == PIN_7) { - // Alif: P5_6 is needed to support proper alt function selection of P10_7. - pinconf_set(PORT_5, PIN_6, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_cs->port, pin->pin_cs->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); + pinconf_set(pin->pin_clk->port, pin->pin_clk->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST); + if (pin->pin_rwds != NULL) { + pinconf_set(pin->pin_rwds->port, pin->pin_rwds->pin, OSPI_PIN_FUNCTION, pad_ctrl); + if (pin->pin_rwds->port == PORT_10 && pin->pin_rwds->pin == PIN_7) { + // Alif: P5_6 is needed to support proper alt function selection of P10_7. + pinconf_set(PORT_5, PIN_6, OSPI_PIN_FUNCTION, pad_ctrl); + } + } + pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, pad_ctrl); + if (pin->pin_d4 != NULL) { + pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d5->port, pin->pin_d5->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d6->port, pin->pin_d6->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d7->port, pin->pin_d7->pin, OSPI_PIN_FUNCTION, pad_ctrl); } - #endif // Reset the SPI flash. - mp_hal_pin_output(pin_OSPI1_RESET); - mp_hal_pin_low(pin_OSPI1_RESET); - mp_hal_delay_us(30); - mp_hal_pin_high(pin_OSPI1_RESET); + mp_hal_pin_output(pin->pin_reset); + mp_hal_pin_low(pin->pin_reset); + mp_hal_delay_us(100); + mp_hal_pin_high(pin->pin_reset); + mp_hal_delay_us(1000); // Configure the OSPI peripheral. - self->cfg.regs = (ssi_regs_t *)OSPI1_BASE; - self->cfg.aes_regs = (aes_regs_t *)AES1_BASE; - self->cfg.xip_base = (volatile void *)OSPI1_XIP_BASE; - self->cfg.ser = 1; + if (pin->peripheral_number == 0) { + self->cfg.regs = (ssi_regs_t *)OSPI0_BASE; + self->cfg.aes_regs = (aes_regs_t *)AES0_BASE; + self->cfg.xip_base = (volatile void *)OSPI0_XIP_BASE; + } else { + self->cfg.regs = (ssi_regs_t *)OSPI1_BASE; + self->cfg.aes_regs = (aes_regs_t *)AES1_BASE; + self->cfg.xip_base = (volatile void *)OSPI1_XIP_BASE; + } + self->cfg.ser = 1; // enable slave select self->cfg.addrlen = 8; // 32-bit address length - self->cfg.ospi_clock = ospi_flash_settings.freq_mhz; + self->cfg.ospi_clock = set->freq_hz; self->cfg.ddr_en = 0; self->cfg.wait_cycles = 0; // used only for ospi_xip_exit ospi_init(&self->cfg); - if (ospi_flash_settings.is_oct && ospi_flash_settings.is_ddr) { - // Switch SPI flash to Octal DDR mode. - ospi_flash_write_reg_sdr(self, CMD_WRVOL, 0x00, ISSI_MODE_OCTAL_DDR_DQS); - self->cfg.ddr_en = 1; + // Check the device ID before attempting to switch to octal mode (if needed). + if (ospi_flash_read_id_spi(self) != set->jedec_id) { + return -1; } - // Check the device ID. - if (ospi_flash_read_id(self) != ospi_flash_settings.jedec_id) { - return -1; + // Switch to octal mode if needed. + if (set->octal_switch != NULL) { + set->octal_switch(self); + + // Check the device ID after switching mode. + if (ospi_flash_read_id(self) != set->jedec_id) { + return -1; + } } return 0; } +/******************************************************************************/ +// Top-level read/erase/write functions. + int ospi_flash_erase_sector(uint32_t addr) { - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; - mp_spiflash_write_cmd(self, CMD_WREN); - int ret = mp_spiflash_wait_wel1(self); + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); if (ret < 0) { return ret; } - ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/); - ospi_push(&self->cfg, CMD_SEC_ERASE_32ADDR); - ospi_send_blocking(&self->cfg, addr); + ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); - return mp_spiflash_wait_wip0(self); + return ospi_flash_wait_wip0(self); } int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { // OSPI FIFO is limited to 256 bytes. Need DMA to get a longer read. - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; while (len) { - uint32_t l = len; + uint32_t l = len / 4; if (l > 256) { l = 256; } - ospi_setup_read(&self->cfg, 8 /* 32-bit addr len*/, l, ospi_flash_settings.read_dummy_cycles); - ospi_push(&self->cfg, ospi_flash_settings.read_command); - ospi_recv_blocking2(&self->cfg, addr, dest); - addr += l; - len -= l; - dest += l; + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_32bit, l, self->set->read_dummy_cycles); + ospi_push(&self->cfg, self->set->read_command); + ospi_recv_blocking_32bit_data(&self->cfg, addr, (uint32_t *)dest); + addr += l * 4; + len -= l * 4; + dest += l * 4; } return 0; } static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src) { - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; - mp_spiflash_write_cmd(self, CMD_WREN); - int ret = mp_spiflash_wait_wel1(self); + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); if (ret < 0) { return ret; } - ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/); - ospi_push(&self->cfg, ospi_flash_settings.write_command); + ospi_setup_write_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_32bit); + ospi_push(&self->cfg, self->set->write_command); ospi_push(&self->cfg, addr); - while (--len) { - ospi_push(&self->cfg, *src++); + + const uint32_t *src32 = (const uint32_t *)src; + for (; len; len -= 4) { + ospi_push(&self->cfg, __ROR(*src32++, 16)); } - ospi_send_blocking(&self->cfg, *src); - return mp_spiflash_wait_wip0(self); + ospi_writel((&self->cfg), ser, self->cfg.ser); + while ((ospi_readl((&self->cfg), sr) & (SR_TF_EMPTY | SR_BUSY)) != SR_TF_EMPTY) { + } + + return ospi_flash_wait_wip0(self); } int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { @@ -264,4 +383,5 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { } return ret; } + #endif // MICROPY_HW_ENABLE_OSPI diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 945dc4bb07ba4..6f5d8327b416d 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -26,24 +26,67 @@ #ifndef MICROPY_INCLUDED_ALIF_OSPI_FLASH_H #define MICROPY_INCLUDED_ALIF_OSPI_FLASH_H -#include -#include +#include "py/mphal.h" + +// Format of command, address and data phases. +enum { + OSPI_FLASH_OCTAL_MODE_SSS, + OSPI_FLASH_OCTAL_MODE_SDD, + OSPI_FLASH_OCTAL_MODE_DDD, +}; + +struct _ospi_flash_t; + +typedef struct _ospi_pin_settings_t { + uint32_t peripheral_number; + const mp_hal_pin_obj_t pin_reset; + const mp_hal_pin_obj_t pin_cs; + const mp_hal_pin_obj_t pin_clk; + const mp_hal_pin_obj_t pin_rwds; + const mp_hal_pin_obj_t pin_d0; + const mp_hal_pin_obj_t pin_d1; + const mp_hal_pin_obj_t pin_d2; + const mp_hal_pin_obj_t pin_d3; + const mp_hal_pin_obj_t pin_d4; + const mp_hal_pin_obj_t pin_d5; + const mp_hal_pin_obj_t pin_d6; + const mp_hal_pin_obj_t pin_d7; +} ospi_pin_settings_t; typedef struct _ospi_flash_settings_t { uint32_t jedec_id; - uint32_t freq_mhz; - bool is_quad : 1; - bool is_oct : 1; - bool is_ddr : 1; + uint32_t freq_hz; + int (*octal_switch)(struct _ospi_flash_t *); + uint8_t octal_mode; + bool rxds; + uint8_t inst_len; + uint8_t xip_data_len; + uint16_t read_sr; + uint8_t read_sr_dummy_cycles; + uint16_t read_id; uint8_t read_id_dummy_cycles; + uint16_t write_en; + uint16_t read_command; uint8_t read_dummy_cycles; - uint8_t read_command; - uint8_t write_command; + uint16_t write_command; + uint16_t erase_command; } ospi_flash_settings_t; -// Provided by the board when it enables OSPI1. +// Provided by the board when it enables OSPI. +extern const ospi_pin_settings_t ospi_pin_settings; extern const ospi_flash_settings_t ospi_flash_settings; +// Functions specific to ISSI flash chips. +int ospi_flash_issi_octal_switch(struct _ospi_flash_t *self); + +// Functions specific to MX flash chips. +int ospi_flash_mx_octal_switch(struct _ospi_flash_t *self); +uint8_t ospi_flash_mx_read_cr(struct _ospi_flash_t *self); +uint8_t ospi_flash_mx_read_cr2(struct _ospi_flash_t *self, uint32_t addr); +int ospi_flash_mx_write_cr(struct _ospi_flash_t *self, uint8_t value); +int ospi_flash_mx_write_cr2(struct _ospi_flash_t *self, uint32_t addr, uint8_t value); + +// SPI flash interface. int ospi_flash_init(void); int ospi_flash_erase_sector(uint32_t addr); int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest); diff --git a/ports/alif/ospi_xip_user.h b/ports/alif/ospi_xip_user.h deleted file mode 100644 index a15baff2a8ba4..0000000000000 --- a/ports/alif/ospi_xip_user.h +++ /dev/null @@ -1,5 +0,0 @@ -// This file is needed by ospi_xip/source/ospi/ospi_drv.c. -#define OSPI_XIP_ENABLE_AES_DECRYPTION (0) -#define OSPI_XIP_RX_SAMPLE_DELAY (4) -#define OSPI_XIP_DDR_DRIVE_EDGE (1) -#define OSPI_XIP_RXDS_DELAY (12) From 41e16886b1bec870b601d8eb14cd4b05426d8015 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Oct 2024 23:07:35 +1100 Subject: [PATCH 31/77] alif/ospi_flash: Enter XIP mode when flash is idle. Signed-off-by: Damien George --- ports/alif/ospi_flash.c | 47 ++++++++++++++++++++++++++++++++++++++++- ports/alif/ospi_flash.h | 1 + 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 6f083de630aa9..be575d9ed772d 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -50,10 +50,14 @@ typedef struct _ospi_flash_t { const ospi_pin_settings_t *pin; const ospi_flash_settings_t *set; ospi_flash_cfg_t cfg; + bool xip_active; } ospi_flash_t; static ospi_flash_t global_flash; +static int ospi_flash_xip_enter(ospi_flash_t *self); +static int ospi_flash_xip_exit(ospi_flash_t *self); + /******************************************************************************/ // Generic SPI-flash helper functions. @@ -299,6 +303,34 @@ int ospi_flash_init(void) { } } + // Enter XIP mode. It will be disabled during flash read/erase/write. + ospi_flash_xip_enter(self); + + return 0; +} + +uintptr_t ospi_flash_get_xip_base(void) { + ospi_flash_t *self = &global_flash; + return (uintptr_t)self->cfg.xip_base; +} + +static int ospi_flash_xip_enter(ospi_flash_t *self) { + if (!self->xip_active) { + uint32_t irq_state = disable_irq(); + self->xip_active = true; + ospi_xip_enter_16bit_cmd(&self->cfg, self->set->xip_data_len, self->set->read_command, self->set->read_command, self->set->read_dummy_cycles); + enable_irq(irq_state); + } + return 0; +} + +static int ospi_flash_xip_exit(ospi_flash_t *self) { + if (self->xip_active) { + uint32_t irq_state = disable_irq(); + ospi_xip_exit_16bit_cmd(&self->cfg, self->set->read_command, self->set->read_command); + self->xip_active = false; + enable_irq(irq_state); + } return 0; } @@ -308,21 +340,29 @@ int ospi_flash_init(void) { int ospi_flash_erase_sector(uint32_t addr) { ospi_flash_t *self = &global_flash; + ospi_flash_xip_exit(self); + ospi_flash_write_cmd(self, self->set->write_en); int ret = ospi_flash_wait_wel1(self); if (ret < 0) { + ospi_flash_xip_enter(self); return ret; } ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); + ret = ospi_flash_wait_wip0(self); - return ospi_flash_wait_wip0(self); + ospi_flash_xip_enter(self); + return ret; } int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { // OSPI FIFO is limited to 256 bytes. Need DMA to get a longer read. + // Note that direct reading is much faster than using XIP memory-mapped read. ospi_flash_t *self = &global_flash; + ospi_flash_xip_exit(self); + while (len) { uint32_t l = len / 4; if (l > 256) { @@ -336,6 +376,7 @@ int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { dest += l * 4; } + ospi_flash_xip_enter(self); return 0; } @@ -365,6 +406,8 @@ static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src } int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { + ospi_flash_xip_exit(&global_flash); + int ret = 0; uint32_t offset = addr & (PAGE_SIZE - 1); while (len) { @@ -381,6 +424,8 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { src += rest; offset = 0; } + + ospi_flash_xip_enter(&global_flash); return ret; } diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 6f5d8327b416d..8fe69764a3777 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -88,6 +88,7 @@ int ospi_flash_mx_write_cr2(struct _ospi_flash_t *self, uint32_t addr, uint8_t v // SPI flash interface. int ospi_flash_init(void); +uintptr_t ospi_flash_get_xip_base(void); int ospi_flash_erase_sector(uint32_t addr); int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest); int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src); From 3d17f634785f4e151d883b1d1110db3a66673efd Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 21 Nov 2024 07:55:10 +0100 Subject: [PATCH 32/77] alif/mpu: Define constants for MPU regions. Signed-off-by: iabdalkader --- ports/alif/mpu.c | 34 +++++++++++++++++----------------- ports/alif/mpu.h | 17 ++++++++++++----- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/ports/alif/mpu.c b/ports/alif/mpu.c index 11c6273fcb345..471eff20a2c5b 100644 --- a/ports/alif/mpu.c +++ b/ports/alif/mpu.c @@ -29,48 +29,48 @@ #include ALIF_CMSIS_H static const ARM_MPU_Region_t mpu_table[] __STARTUP_RO_DATA_ATTRIBUTE = { - { /* SRAM0 - 4MB : RO-0, NP-1, XN-0 */ + [MP_MPU_REGION_SRAM0] = { /* SRAM0 - 4MB : RO-0, NP-1, XN-0 */ .RBAR = ARM_MPU_RBAR(0x02000000, ARM_MPU_SH_NON, 0, 1, 0), - .RLAR = ARM_MPU_RLAR(0x023FFFFF, MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT) + .RLAR = ARM_MPU_RLAR(0x023FFFFF, MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT) }, - { /* SRAM1 - 2.5MB : RO-0, NP-1, XN-0 */ + [MP_MPU_REGION_SRAM1] = { /* SRAM1 - 2.5MB : RO-0, NP-1, XN-0 */ .RBAR = ARM_MPU_RBAR(0x08000000, ARM_MPU_SH_NON, 0, 1, 0), - .RLAR = ARM_MPU_RLAR(0x0827FFFF, MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA) + .RLAR = ARM_MPU_RLAR(0x0827FFFF, MP_MPU_ATTR_NORMAL_WB_RA_WA) }, - { /* Host Peripherals - 16MB : RO-0, NP-1, XN-1 */ + [MP_MPU_REGION_HOST_PERIPHERALS] = { /* Host Peripherals - 16MB : RO-0, NP-1, XN-1 */ .RBAR = ARM_MPU_RBAR(0x1A000000, ARM_MPU_SH_NON, 0, 1, 1), - .RLAR = ARM_MPU_RLAR(0x1AFFFFFF, MP_MPU_ATTR_INDEX_DEVICE_nGnRE) + .RLAR = ARM_MPU_RLAR(0x1AFFFFFF, MP_MPU_ATTR_DEVICE_nGnRE) }, - { /* MRAM - 5.5MB : RO-1, NP-1, XN-0 */ + [MP_MPU_REGION_MRAM] = { /* MRAM - 5.5MB : RO-1, NP-1, XN-0 */ .RBAR = ARM_MPU_RBAR(0x80000000, ARM_MPU_SH_NON, 1, 1, 0), - .RLAR = ARM_MPU_RLAR(0x8057FFFF, MP_MPU_ATTR_INDEX_NORMAL_WT_RA) + .RLAR = ARM_MPU_RLAR(0x8057FFFF, MP_MPU_ATTR_NORMAL_WT_RA) }, - { /* OSPI Regs - 16MB : RO-0, NP-1, XN-1 */ + [MP_MPU_REGION_OSPI_REGISTERS] = { /* OSPI Regs - 16MB : RO-0, NP-1, XN-1 */ .RBAR = ARM_MPU_RBAR(0x83000000, ARM_MPU_SH_NON, 0, 1, 1), - .RLAR = ARM_MPU_RLAR(0x83FFFFFF, MP_MPU_ATTR_INDEX_DEVICE_nGnRE) + .RLAR = ARM_MPU_RLAR(0x83FFFFFF, MP_MPU_ATTR_DEVICE_nGnRE) }, - { /* OSPI0 XIP flash - 512MB : RO-1, NP-1, XN-0 */ + [MP_MPU_REGION_OSPI0_XIP] = { /* OSPI0 XIP flash - 512MB : RO-1, NP-1, XN-0 */ .RBAR = ARM_MPU_RBAR(0xA0000000, ARM_MPU_SH_NON, 1, 1, 0), - .RLAR = ARM_MPU_RLAR(0xBFFFFFFF, MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE) + .RLAR = ARM_MPU_RLAR(0xBFFFFFFF, MP_MPU_ATTR_NORMAL_NON_CACHEABLE) }, }; void MPU_Load_Regions(void) { // Configure memory attributes. - ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT, + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT, ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0), ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0))); - ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_DEVICE_nGnRE, + ARM_MPU_SetMemAttr(MP_MPU_ATTR_DEVICE_nGnRE, ARM_MPU_ATTR(ARM_MPU_ATTR_DEVICE, ARM_MPU_ATTR_DEVICE_nGnRE)); - ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA, + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_WB_RA_WA, ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1), ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1))); - ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WT_RA, + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_WT_RA, ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0), ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0))); - ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE, + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_NON_CACHEABLE, ARM_MPU_ATTR(ARM_MPU_ATTR_NON_CACHEABLE, ARM_MPU_ATTR_NON_CACHEABLE)); // Load the MPU regions from the table. diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h index 87a7518a126be..c259c8585adfd 100644 --- a/ports/alif/mpu.h +++ b/ports/alif/mpu.h @@ -24,8 +24,15 @@ * THE SOFTWARE. */ -#define MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT (0) -#define MP_MPU_ATTR_INDEX_DEVICE_nGnRE (1) -#define MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA (2) -#define MP_MPU_ATTR_INDEX_NORMAL_WT_RA (3) -#define MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE (4) +#define MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT (0) +#define MP_MPU_ATTR_DEVICE_nGnRE (1) +#define MP_MPU_ATTR_NORMAL_WB_RA_WA (2) +#define MP_MPU_ATTR_NORMAL_WT_RA (3) +#define MP_MPU_ATTR_NORMAL_NON_CACHEABLE (4) + +#define MP_MPU_REGION_SRAM0 (0) +#define MP_MPU_REGION_SRAM1 (1) +#define MP_MPU_REGION_HOST_PERIPHERALS (2) +#define MP_MPU_REGION_MRAM (3) +#define MP_MPU_REGION_OSPI_REGISTERS (4) +#define MP_MPU_REGION_OSPI0_XIP (5) From 5152a1f04da3699bc35419bc40bf204760eac66c Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 21 Nov 2024 07:56:02 +0100 Subject: [PATCH 33/77] alif/mpmetalport: Add Open-AMP MPU region. Define an MPU region for Open-AMP and remove hard-coded attribute. Signed-off-by: iabdalkader --- ports/alif/mpmetalport.c | 18 ++++++------------ ports/alif/mpmetalport.h | 3 ++- ports/alif/mpu.h | 1 + 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/ports/alif/mpmetalport.c b/ports/alif/mpmetalport.c index f80d039c26d23..a7d4025468a16 100644 --- a/ports/alif/mpmetalport.c +++ b/ports/alif/mpmetalport.c @@ -48,28 +48,22 @@ int metal_sys_init(const struct metal_init_params *params) { hwsem_reset(METAL_HSEM_REMOTE); #endif - // Enable the hardware semaphore IRQ. - NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); - NVIC_SetPriority(METAL_HSEM_IRQn, IRQ_PRI_HWSEM); - NVIC_EnableIRQ(METAL_HSEM_IRQn); - // If cache management is not enabled, configure the MPU to disable // caching for the entire Open-AMP shared memory region. #ifndef VIRTIO_USE_DCACHE ARM_MPU_Disable(); - // NOTE: The startup code uses the first 4 attributes. - #define MEMATTR_IDX_NORMAL_NON_CACHEABLE 4 - ARM_MPU_SetMemAttr(MEMATTR_IDX_NORMAL_NON_CACHEABLE, ARM_MPU_ATTR( - ARM_MPU_ATTR_NON_CACHEABLE, - ARM_MPU_ATTR_NON_CACHEABLE)); MPU->RNR = METAL_MPU_REGION_ID; - MPU->RBAR = ARM_MPU_RBAR(METAL_MPU_REGION_BASE, ARM_MPU_SH_NON, 0, 1, 0); // RO-0, NP-1, XN-0 - MPU->RLAR = ARM_MPU_RLAR(METAL_MPU_REGION_BASE + METAL_MPU_REGION_SIZE - 1, MEMATTR_IDX_NORMAL_NON_CACHEABLE); + MPU->RBAR = ARM_MPU_RBAR(METAL_MPU_REGION_BASE, ARM_MPU_SH_NON, 0, 1, 1); // RO-0, NP-1, XN-1 + MPU->RLAR = ARM_MPU_RLAR(METAL_MPU_REGION_BASE + METAL_MPU_REGION_SIZE - 1, MP_MPU_ATTR_NORMAL_NON_CACHEABLE); ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); #endif metal_bus_register(&metal_generic_bus); + // Enable the hardware semaphore IRQ. + NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); + NVIC_SetPriority(METAL_HSEM_IRQn, IRQ_PRI_HWSEM); + NVIC_EnableIRQ(METAL_HSEM_IRQn); return 0; } diff --git a/ports/alif/mpmetalport.h b/ports/alif/mpmetalport.h index 5cd697c0e9c7b..7a428dbc6c62a 100644 --- a/ports/alif/mpmetalport.h +++ b/ports/alif/mpmetalport.h @@ -31,6 +31,7 @@ #include #include "py/mphal.h" #include "py/runtime.h" +#include "mpu.h" #define METAL_HAVE_STDATOMIC_H 0 #define METAL_HAVE_FUTEX_H 0 @@ -66,7 +67,7 @@ #define METAL_SHM_ADDR ((metal_phys_addr_t)(_openamp_shm_region_start + METAL_RSC_SIZE)) #define METAL_SHM_SIZE ((size_t)(_openamp_shm_region_end - _openamp_shm_region_start - METAL_RSC_SIZE)) -#define METAL_MPU_REGION_ID (9) // NOTE: The startup code uses the first 9 regions. +#define METAL_MPU_REGION_ID (MP_MPU_REGION_OPENAMP) #define METAL_MPU_REGION_BASE ((uint32_t)_openamp_shm_region_start) #define METAL_MPU_REGION_SIZE (0x00010000U) diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h index c259c8585adfd..88fbe011206ed 100644 --- a/ports/alif/mpu.h +++ b/ports/alif/mpu.h @@ -36,3 +36,4 @@ #define MP_MPU_REGION_MRAM (3) #define MP_MPU_REGION_OSPI_REGISTERS (4) #define MP_MPU_REGION_OSPI0_XIP (5) +#define MP_MPU_REGION_OPENAMP (6) From 1585080ff0ee3075e49fce01effddfc51644b5ed Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 14 Dec 2024 15:47:59 +0100 Subject: [PATCH 34/77] alif/ospi_flash: Fix XIP for 8-bit instructions (ISSI). Disable XIP instruction DDR for 8-bit instructions. Signed-off-by: iabdalkader --- ports/alif/ospi_ext.c | 15 ++++++++------- ports/alif/ospi_ext.h | 4 ++-- ports/alif/ospi_flash.c | 5 +++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/ports/alif/ospi_ext.c b/ports/alif/ospi_ext.c index 48446d0622d48..3226c244697b8 100644 --- a/ports/alif/ospi_ext.c +++ b/ports/alif/ospi_ext.c @@ -28,8 +28,6 @@ #include "ospi_ext.h" #include "ospi_xip_user.h" -#define INST_L16bit (3) - static void ospi_xip_disable(ospi_flash_cfg_t *ospi_cfg) { ospi_cfg->aes_regs->aes_control &= ~AES_CONTROL_XIP_EN; } @@ -208,7 +206,7 @@ void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_l spi_enable(ospi_cfg); } -void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles) { +void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles) { spi_disable(ospi_cfg); uint32_t val = CTRLR0_IS_MST @@ -224,12 +222,11 @@ void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uin val = (OCTAL << XIP_CTRL_FRF_OFFSET) | (0x2 << XIP_CTRL_TRANS_TYPE_OFFSET) | (ADDR_L32bit << XIP_CTRL_ADDR_L_OFFSET) - | (INST_L16bit << XIP_CTRL_INST_L_OFFSET) + | (inst_len << XIP_CTRL_INST_L_OFFSET) | (0x0 << XIP_CTRL_MD_BITS_EN_OFFSET) | (read_dummy_cycles << XIP_CTRL_WAIT_CYCLES_OFFSET) | (0x1 << XIP_CTRL_DFC_HC_OFFSET) | (ospi_cfg->ddr_en << XIP_CTRL_DDR_EN_OFFSET) - | (ospi_cfg->ddr_en << XIP_CTRL_INST_DDR_EN_OFFSET) | (0x1 << XIP_CTRL_RXDS_EN_OFFSET) | (0x1 << XIP_CTRL_INST_EN_OFFSET) | (0x0 << XIP_CTRL_CONT_XFER_EN_OFFSET) @@ -239,6 +236,10 @@ void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uin | (0x0 << XIP_PREFETCH_EN_OFFSET) | (0x0 << XIP_CTRL_RXDS_VL_EN_OFFSET); + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << XIP_CTRL_INST_DDR_EN_OFFSET; + } + ospi_writel(ospi_cfg, xip_ctrl, val); ospi_writel(ospi_cfg, rx_sample_dly, 4); @@ -254,7 +255,7 @@ void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uin ospi_xip_enable(ospi_cfg); } -void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, uint16_t wrap_command) { +void ospi_xip_exit_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint16_t incr_command, uint16_t wrap_command) { spi_disable(ospi_cfg); uint32_t val = CTRLR0_IS_MST @@ -272,7 +273,7 @@ void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, | (2 << CTRLR0_XIP_MBL_OFFSET) | (1 << CTRLR0_XIP_DFS_HC_OFFSET) | (1 << CTRLR0_XIP_INST_EN_OFFSET) - | (CTRLR0_INST_L_16bit << CTRLR0_INST_L_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) | (ospi_cfg->addrlen) << (CTRLR0_ADDR_L_OFFSET) | (ospi_cfg->wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); diff --git a/ports/alif/ospi_ext.h b/ports/alif/ospi_ext.h index e467772d15670..e5a2b50beb693 100644 --- a/ports/alif/ospi_ext.h +++ b/ports/alif/ospi_ext.h @@ -51,7 +51,7 @@ int ospi_recv_blocking_32bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len); -void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles); -void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, uint16_t wrap_command); +void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles); +void ospi_xip_exit_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint16_t incr_command, uint16_t wrap_command); #endif // MICROPY_INCLUDED_ALIF_OSPI_EXT_H diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index be575d9ed772d..c3ae50a9ee4ef 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -318,7 +318,8 @@ static int ospi_flash_xip_enter(ospi_flash_t *self) { if (!self->xip_active) { uint32_t irq_state = disable_irq(); self->xip_active = true; - ospi_xip_enter_16bit_cmd(&self->cfg, self->set->xip_data_len, self->set->read_command, self->set->read_command, self->set->read_dummy_cycles); + ospi_xip_enter_ext(&self->cfg, self->set->inst_len, self->set->xip_data_len, + self->set->read_command, self->set->read_command, self->set->read_dummy_cycles); enable_irq(irq_state); } return 0; @@ -327,7 +328,7 @@ static int ospi_flash_xip_enter(ospi_flash_t *self) { static int ospi_flash_xip_exit(ospi_flash_t *self) { if (self->xip_active) { uint32_t irq_state = disable_irq(); - ospi_xip_exit_16bit_cmd(&self->cfg, self->set->read_command, self->set->read_command); + ospi_xip_exit_ext(&self->cfg, self->set->inst_len, self->set->read_command, self->set->read_command); self->xip_active = false; enable_irq(irq_state); } From aec030004f019155334f56f61cf230fcf63ad759 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 16 Dec 2024 15:52:09 +0100 Subject: [PATCH 35/77] alif/ospi_flash: Support flash device auto-detection in runtime. This commit enables detecting the flash device in runtime, and uses the settings of the detected device instead of board-defined flash settings. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 26 +++++++++-- ports/alif/ospi_flash.h | 4 +- ports/alif/ospi_flash_settings.h | 79 ++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 ports/alif/ospi_flash_settings.h diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index c3ae50a9ee4ef..8bffd69805c9f 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -238,9 +238,9 @@ int ospi_flash_init(void) { ospi_flash_t *self = &global_flash; const ospi_pin_settings_t *pin = &ospi_pin_settings; - const ospi_flash_settings_t *set = &ospi_flash_settings; + const ospi_flash_settings_t *set = NULL; + self->pin = pin; - self->set = set; uint32_t pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; @@ -283,16 +283,32 @@ int ospi_flash_init(void) { } self->cfg.ser = 1; // enable slave select self->cfg.addrlen = 8; // 32-bit address length - self->cfg.ospi_clock = set->freq_hz; self->cfg.ddr_en = 0; self->cfg.wait_cycles = 0; // used only for ospi_xip_exit + self->cfg.ospi_clock = 100000; // use 100KHz for detection. ospi_init(&self->cfg); - // Check the device ID before attempting to switch to octal mode (if needed). - if (ospi_flash_read_id_spi(self) != set->jedec_id) { + // Read the device ID. + uint32_t jedec_id = ospi_flash_read_id_spi(self); + + // Auto-detect the flash. + for (size_t i = 0; i < ospi_flash_settings_len; i++) { + set = &ospi_flash_settings[i]; + if (jedec_id == set->jedec_id) { + self->set = set; + break; + } + } + + if (self->set == NULL) { + // Flash part is not supported. return -1; } + // Switch to the higher frequency. + self->cfg.ospi_clock = set->freq_hz; + ospi_init(&self->cfg); + // Switch to octal mode if needed. if (set->octal_switch != NULL) { set->octal_switch(self); diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 8fe69764a3777..20846d4471315 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_ALIF_OSPI_FLASH_H #include "py/mphal.h" +#include "ospi_flash_settings.h" // Format of command, address and data phases. enum { @@ -74,7 +75,8 @@ typedef struct _ospi_flash_settings_t { // Provided by the board when it enables OSPI. extern const ospi_pin_settings_t ospi_pin_settings; -extern const ospi_flash_settings_t ospi_flash_settings; +extern const ospi_flash_settings_t ospi_flash_settings[]; +extern const size_t ospi_flash_settings_len; // Functions specific to ISSI flash chips. int ospi_flash_issi_octal_switch(struct _ospi_flash_t *self); diff --git a/ports/alif/ospi_flash_settings.h b/ports/alif/ospi_flash_settings.h new file mode 100644 index 0000000000000..c409f856dfa1a --- /dev/null +++ b/ports/alif/ospi_flash_settings.h @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ + +#ifndef __OSPI_FLASH_SETTINGS_H__ +#define __OSPI_FLASH_SETTINGS_H__ +#include +#include "ospi_flash.h" + +// Macronix MX25 +#define OSPI_FLASH_SETTINGS_MX25 \ + .octal_switch = ospi_flash_mx_octal_switch, \ + .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ + .rxds = true, \ + .inst_len = OSPI_INST_L_16bit, \ + .xip_data_len = OSPI_DATA_L_16bit, \ + .read_sr = 0x05fa, \ + .read_sr_dummy_cycles = 4, \ + .write_en = 0x06f9, \ + .read_id = 0x9f60, \ + .read_id_dummy_cycles = 4, \ + .read_command = 0xee11, \ + .write_command = 0x12ed, \ + .erase_command = 0x21de + +// Everspin EM. +#define OSPI_FLASH_SETTINGS_EM \ + .octal_switch = ospi_flash_issi_octal_switch, \ + .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ + .rxds = false, \ + .inst_len = OSPI_INST_L_8bit, \ + .xip_data_len = OSPI_DATA_L_16bit, \ + .read_sr = 0x05, \ + .read_sr_dummy_cycles = 8, \ + .write_en = 0x06, \ + .read_id = 0x9f, \ + .read_id_dummy_cycles = 8, \ + .read_command = 0xfd, \ + .write_command = 0xc2, \ + .erase_command = 0x21 + +// ISSI IS25. +#define OSPI_FLASH_SETTINGS_IS25 \ + .octal_switch = ospi_flash_issi_octal_switch, \ + .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ + .rxds = true, \ + .inst_len = OSPI_INST_L_8bit, \ + .xip_data_len = OSPI_DATA_L_16bit, \ + .read_sr = 0x05, \ + .read_sr_dummy_cycles = 8, \ + .write_en = 0x06, \ + .read_id = 0x9f, \ + .read_id_dummy_cycles = 8, \ + .read_command = 0xfd, \ + .write_command = 0xc2, \ + .erase_command = 0x21 +#endif // __OSPI_FLASH_SETTINGS_H__ From 8807f8d01bc29d7c09a71a329877a1e8ad617fa8 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 16 Dec 2024 15:52:51 +0100 Subject: [PATCH 36/77] alif/ospi_flash: Configure dummy cycles. The default dummy cycles may not match the actual flash frequency supported by a certain board. For example, the MX chip uses 20 dummy cycles by default which supports up to 200MHz DDR, but the maximum frequency supported by the AE3 board is 50MHz DDR. So the dummy cycles for this board can be as low as 6. It's important to set the correct dummy cycles, as it results in doubling the XIP read speed, in the case of the AE3 board. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 43 ++++++++++++++++++++++++-------- ports/alif/ospi_flash.h | 6 ++--- ports/alif/ospi_flash_settings.h | 6 ++--- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 8bffd69805c9f..279227aa36d06 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -146,13 +146,19 @@ static uint32_t ospi_flash_read_id(ospi_flash_t *self) { /******************************************************************************/ // Functions specific to ISSI flash chips. -int ospi_flash_issi_octal_switch(ospi_flash_t *self) { - // Switch SPI flash to Octal DDR mode. +int ospi_flash_issi_init(ospi_flash_t *self) { const uint8_t cmd_wrvol = 0x81; + + // Configure dummy cycles. + ospi_flash_wren_spi(self); + uint8_t buf0[5] = {cmd_wrvol, 0, 0, 1, self->set->read_dummy_cycles}; + ospi_spi_transfer(&self->cfg, sizeof(buf0), buf0, buf0); + + // Switch SPI flash to Octal DDR mode. const uint8_t issi_mode_octal_ddr_dqs = 0xe7; ospi_flash_wren_spi(self); - uint8_t buf[5] = {cmd_wrvol, 0, 0, 0, issi_mode_octal_ddr_dqs}; - ospi_spi_transfer(&self->cfg, sizeof(buf), buf, buf); + uint8_t buf1[5] = {cmd_wrvol, 0, 0, 0, issi_mode_octal_ddr_dqs}; + ospi_spi_transfer(&self->cfg, sizeof(buf1), buf1, buf1); self->cfg.ddr_en = 1; return 0; } @@ -160,8 +166,7 @@ int ospi_flash_issi_octal_switch(ospi_flash_t *self) { /******************************************************************************/ // Functions specific to MX flash chips. -int ospi_flash_mx_octal_switch(ospi_flash_t *self) { - // Switch SPI flash to Octal SDR or DDR mode (SOPI or DOPI) by writing to CR2. +int ospi_flash_mx_init(ospi_flash_t *self) { const uint8_t cmd_wrcr2 = 0x72; const uint8_t mx_mode_enable_sopi = 0x01; const uint8_t mx_mode_enable_dopi = 0x02; @@ -171,9 +176,26 @@ int ospi_flash_mx_octal_switch(ospi_flash_t *self) { } else { mx_mode = mx_mode_enable_sopi; } + + // Configure dummy cycles. + uint8_t ddc_value = 0; + const uint8_t ospi_flash_mx_ddc[][2] = { + {20, 0}, {18, 1}, {16, 2}, {14, 3}, {12, 4}, {10, 5}, {8, 6}, {6, 7} + }; + for (size_t i = 0; i < MP_ARRAY_SIZE(ospi_flash_mx_ddc); i++) { + if (self->set->read_dummy_cycles == ospi_flash_mx_ddc[i][0]) { + ddc_value = ospi_flash_mx_ddc[i][1]; + break; + } + } ospi_flash_wren_spi(self); - uint8_t buf[6] = {cmd_wrcr2, 0, 0, 0, 0, mx_mode}; - ospi_spi_transfer(&self->cfg, sizeof(buf), buf, buf); + uint8_t buf0[6] = {cmd_wrcr2, 0, 0, 3, 0, ddc_value}; + ospi_spi_transfer(&self->cfg, sizeof(buf0), buf0, buf0); + + // Switch SPI flash to Octal SDR or DDR mode. + ospi_flash_wren_spi(self); + uint8_t buf1[6] = {cmd_wrcr2, 0, 0, 0, 0, mx_mode}; + ospi_spi_transfer(&self->cfg, sizeof(buf1), buf1, buf1); if (self->set->octal_mode == OSPI_FLASH_OCTAL_MODE_DDD) { self->cfg.ddr_en = 1; } else { @@ -310,8 +332,8 @@ int ospi_flash_init(void) { ospi_init(&self->cfg); // Switch to octal mode if needed. - if (set->octal_switch != NULL) { - set->octal_switch(self); + if (set->flash_init != NULL) { + set->flash_init(self); // Check the device ID after switching mode. if (ospi_flash_read_id(self) != set->jedec_id) { @@ -321,7 +343,6 @@ int ospi_flash_init(void) { // Enter XIP mode. It will be disabled during flash read/erase/write. ospi_flash_xip_enter(self); - return 0; } diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 20846d4471315..39d270d7d5465 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -57,7 +57,7 @@ typedef struct _ospi_pin_settings_t { typedef struct _ospi_flash_settings_t { uint32_t jedec_id; uint32_t freq_hz; - int (*octal_switch)(struct _ospi_flash_t *); + int (*flash_init)(struct _ospi_flash_t *); uint8_t octal_mode; bool rxds; uint8_t inst_len; @@ -79,10 +79,10 @@ extern const ospi_flash_settings_t ospi_flash_settings[]; extern const size_t ospi_flash_settings_len; // Functions specific to ISSI flash chips. -int ospi_flash_issi_octal_switch(struct _ospi_flash_t *self); +int ospi_flash_issi_init(struct _ospi_flash_t *self); // Functions specific to MX flash chips. -int ospi_flash_mx_octal_switch(struct _ospi_flash_t *self); +int ospi_flash_mx_init(struct _ospi_flash_t *self); uint8_t ospi_flash_mx_read_cr(struct _ospi_flash_t *self); uint8_t ospi_flash_mx_read_cr2(struct _ospi_flash_t *self, uint32_t addr); int ospi_flash_mx_write_cr(struct _ospi_flash_t *self, uint8_t value); diff --git a/ports/alif/ospi_flash_settings.h b/ports/alif/ospi_flash_settings.h index c409f856dfa1a..c8605275cdcc0 100644 --- a/ports/alif/ospi_flash_settings.h +++ b/ports/alif/ospi_flash_settings.h @@ -31,7 +31,7 @@ // Macronix MX25 #define OSPI_FLASH_SETTINGS_MX25 \ - .octal_switch = ospi_flash_mx_octal_switch, \ + .flash_init = ospi_flash_mx_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ .rxds = true, \ .inst_len = OSPI_INST_L_16bit, \ @@ -47,7 +47,7 @@ // Everspin EM. #define OSPI_FLASH_SETTINGS_EM \ - .octal_switch = ospi_flash_issi_octal_switch, \ + .flash_init = ospi_flash_issi_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ .rxds = false, \ .inst_len = OSPI_INST_L_8bit, \ @@ -63,7 +63,7 @@ // ISSI IS25. #define OSPI_FLASH_SETTINGS_IS25 \ - .octal_switch = ospi_flash_issi_octal_switch, \ + .flash_init = ospi_flash_issi_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ .rxds = true, \ .inst_len = OSPI_INST_L_8bit, \ From 872f3d70d37d189525783137e53a50878314d37d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 14 Dec 2024 18:19:42 +0100 Subject: [PATCH 37/77] alif/ospi_flash: Add negative clock pin. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 28 ++++++++++++++++------------ ports/alif/ospi_flash.h | 3 ++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 279227aa36d06..8eaf923550141 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -264,26 +264,30 @@ int ospi_flash_init(void) { self->pin = pin; - uint32_t pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; + uint32_t ck_pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST; + uint32_t io_pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; pinconf_set(pin->pin_cs->port, pin->pin_cs->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); - pinconf_set(pin->pin_clk->port, pin->pin_clk->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST); + pinconf_set(pin->pin_clk_p->port, pin->pin_clk_p->pin, OSPI_PIN_FUNCTION, ck_pad_ctrl); + if (pin->pin_clk_n != NULL) { + pinconf_set(pin->pin_clk_n->port, pin->pin_clk_n->pin, OSPI_PIN_FUNCTION, ck_pad_ctrl); + } if (pin->pin_rwds != NULL) { - pinconf_set(pin->pin_rwds->port, pin->pin_rwds->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_rwds->port, pin->pin_rwds->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); if (pin->pin_rwds->port == PORT_10 && pin->pin_rwds->pin == PIN_7) { // Alif: P5_6 is needed to support proper alt function selection of P10_7. - pinconf_set(PORT_5, PIN_6, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(PORT_5, PIN_6, OSPI_PIN_FUNCTION, io_pad_ctrl); } } - pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); if (pin->pin_d4 != NULL) { - pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d5->port, pin->pin_d5->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d6->port, pin->pin_d6->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d7->port, pin->pin_d7->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d5->port, pin->pin_d5->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d6->port, pin->pin_d6->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d7->port, pin->pin_d7->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); } // Reset the SPI flash. diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 39d270d7d5465..affa4e6102215 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -42,7 +42,8 @@ typedef struct _ospi_pin_settings_t { uint32_t peripheral_number; const mp_hal_pin_obj_t pin_reset; const mp_hal_pin_obj_t pin_cs; - const mp_hal_pin_obj_t pin_clk; + const mp_hal_pin_obj_t pin_clk_p; + const mp_hal_pin_obj_t pin_clk_n; const mp_hal_pin_obj_t pin_rwds; const mp_hal_pin_obj_t pin_d0; const mp_hal_pin_obj_t pin_d1; From 0709936653895e0de499e0a7019afb80eda7425c Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 19 Dec 2024 09:47:04 +0100 Subject: [PATCH 38/77] alif/ospi_flash: Enable pull-up IO2/WP. Leaving this pin low in combination with the default EM settings enables flash protection for the EM flash. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 8eaf923550141..d5aa99f2b493f 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -281,7 +281,7 @@ int ospi_flash_init(void) { } pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, io_pad_ctrl | PADCTRL_DRIVER_DISABLED_PULL_UP); pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); if (pin->pin_d4 != NULL) { pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); From df06bf91a5a78c89eaa7b6b7d157df83c576553f Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 20 Dec 2024 08:57:08 +0100 Subject: [PATCH 39/77] alif/ospi_ext: Optimize XIP speed. This change increases XIP read speed to ~30Mbytes/s at 50MHz DDR: - Enable continuous mode. - Remove hard-coded settings. - Set XIP continuous mode timeout. The prefetch remains disabled. Although enabling the prefetch gives the best performance for the CPU in XIP mode, it must be disabled when the NPU accesses the OSPI flash. Signed-off-by: iabdalkader --- ports/alif/ospi_ext.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/alif/ospi_ext.c b/ports/alif/ospi_ext.c index 3226c244697b8..e3389d8bd54da 100644 --- a/ports/alif/ospi_ext.c +++ b/ports/alif/ospi_ext.c @@ -229,7 +229,7 @@ void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t | (ospi_cfg->ddr_en << XIP_CTRL_DDR_EN_OFFSET) | (0x1 << XIP_CTRL_RXDS_EN_OFFSET) | (0x1 << XIP_CTRL_INST_EN_OFFSET) - | (0x0 << XIP_CTRL_CONT_XFER_EN_OFFSET) + | (0x1 << XIP_CTRL_CONT_XFER_EN_OFFSET) | (0x0 << XIP_CTRL_HYPERBUS_EN_OFFSET) | (0x1 << XIP_CTRL_RXDS_SIG_EN) | (0x0 << XIP_CTRL_XIP_MBL_OFFSET) @@ -242,14 +242,15 @@ void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t ospi_writel(ospi_cfg, xip_ctrl, val); - ospi_writel(ospi_cfg, rx_sample_dly, 4); - ospi_writel(ospi_cfg, txd_drive_edge, 1); + ospi_writel(ospi_cfg, rx_sample_dly, OSPI_XIP_RX_SAMPLE_DELAY); + ospi_writel(ospi_cfg, txd_drive_edge, OSPI_XIP_DDR_DRIVE_EDGE); ospi_cfg->aes_regs->aes_rxds_delay = OSPI_XIP_RXDS_DELAY; ospi_writel(ospi_cfg, xip_mode_bits, 0x0); ospi_writel(ospi_cfg, xip_incr_inst, incr_command); ospi_writel(ospi_cfg, xip_wrap_inst, wrap_command); ospi_writel(ospi_cfg, xip_ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, xip_cnt_time_out, 100); spi_enable(ospi_cfg); ospi_xip_enable(ospi_cfg); From 602bc86b6d8ebd200029568c060b878146050da1 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 8 Jan 2025 15:12:18 +0100 Subject: [PATCH 40/77] alif/ospi_flash: Use OSPI in XIP mode only. The OSPI controller supports concurrent direct/XIP accesses, there's no need to disable XIP on direct access. In addition to improving the performance, this change lays the groundwork for supporting access by the HP and HE cores simultaneously. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 39 ++++++++------------------------------- ports/alif/ospi_flash.h | 4 ++++ 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index d5aa99f2b493f..7400a678b226a 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -55,9 +55,6 @@ typedef struct _ospi_flash_t { static ospi_flash_t global_flash; -static int ospi_flash_xip_enter(ospi_flash_t *self); -static int ospi_flash_xip_exit(ospi_flash_t *self); - /******************************************************************************/ // Generic SPI-flash helper functions. @@ -188,6 +185,7 @@ int ospi_flash_mx_init(ospi_flash_t *self) { break; } } + ospi_flash_wren_spi(self); uint8_t buf0[6] = {cmd_wrcr2, 0, 0, 3, 0, ddc_value}; ospi_spi_transfer(&self->cfg, sizeof(buf0), buf0, buf0); @@ -355,7 +353,7 @@ uintptr_t ospi_flash_get_xip_base(void) { return (uintptr_t)self->cfg.xip_base; } -static int ospi_flash_xip_enter(ospi_flash_t *self) { +int ospi_flash_xip_enter(ospi_flash_t *self) { if (!self->xip_active) { uint32_t irq_state = disable_irq(); self->xip_active = true; @@ -366,7 +364,7 @@ static int ospi_flash_xip_enter(ospi_flash_t *self) { return 0; } -static int ospi_flash_xip_exit(ospi_flash_t *self) { +int ospi_flash_xip_exit(ospi_flash_t *self) { if (self->xip_active) { uint32_t irq_state = disable_irq(); ospi_xip_exit_ext(&self->cfg, self->set->inst_len, self->set->read_command, self->set->read_command); @@ -382,43 +380,23 @@ static int ospi_flash_xip_exit(ospi_flash_t *self) { int ospi_flash_erase_sector(uint32_t addr) { ospi_flash_t *self = &global_flash; - ospi_flash_xip_exit(self); - ospi_flash_write_cmd(self, self->set->write_en); int ret = ospi_flash_wait_wel1(self); if (ret < 0) { - ospi_flash_xip_enter(self); return ret; } ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); ret = ospi_flash_wait_wip0(self); - ospi_flash_xip_enter(self); + SCB_InvalidateDCache_by_Addr(global_flash.cfg.xip_base + addr, MICROPY_HW_FLASH_BLOCK_SIZE_BYTES); return ret; } int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { - // OSPI FIFO is limited to 256 bytes. Need DMA to get a longer read. - // Note that direct reading is much faster than using XIP memory-mapped read. ospi_flash_t *self = &global_flash; - - ospi_flash_xip_exit(self); - - while (len) { - uint32_t l = len / 4; - if (l > 256) { - l = 256; - } - ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_32bit, l, self->set->read_dummy_cycles); - ospi_push(&self->cfg, self->set->read_command); - ospi_recv_blocking_32bit_data(&self->cfg, addr, (uint32_t *)dest); - addr += l * 4; - len -= l * 4; - dest += l * 4; - } - - ospi_flash_xip_enter(self); + // Perform an XIP read (memcpy) + memcpy(dest, (void *)self->cfg.xip_base + addr, len); return 0; } @@ -448,10 +426,9 @@ static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src } int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { - ospi_flash_xip_exit(&global_flash); - int ret = 0; uint32_t offset = addr & (PAGE_SIZE - 1); + while (len) { size_t rest = PAGE_SIZE - offset; if (rest > len) { @@ -467,7 +444,7 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { offset = 0; } - ospi_flash_xip_enter(&global_flash); + SCB_InvalidateDCache_by_Addr(global_flash.cfg.xip_base + addr, len); return ret; } diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index affa4e6102215..0095ee2d4280b 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -89,6 +89,10 @@ uint8_t ospi_flash_mx_read_cr2(struct _ospi_flash_t *self, uint32_t addr); int ospi_flash_mx_write_cr(struct _ospi_flash_t *self, uint8_t value); int ospi_flash_mx_write_cr2(struct _ospi_flash_t *self, uint32_t addr, uint8_t value); +// XIP control +int ospi_flash_xip_enter(struct _ospi_flash_t *self); +int ospi_flash_xip_exit(struct _ospi_flash_t *self); + // SPI flash interface. int ospi_flash_init(void); uintptr_t ospi_flash_get_xip_base(void); From 92f056d58f74cfd3986aaef1eea08f4f948fe03a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 6 Feb 2025 10:14:34 +0100 Subject: [PATCH 41/77] alif/ospi_flash: Add 16-bit words swap flash setting. The byte order (endianness) seems to be swapped when read in 8D-8D-8D in XIP mode, for most flashes, with the exception of MX which seems to swap half-words. This commit adds a flash setting to allow parts to enable half-word swap when data is written, to fix this issue. By default, only endianness is fixed. Tested with both MX and ISSI parts on AE3, flash test and simple file write/read. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 12 ++++++++++-- ports/alif/ospi_flash.h | 1 + ports/alif/ospi_flash_settings.h | 5 ++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 7400a678b226a..f2f95a04cf241 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -414,8 +414,16 @@ static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src ospi_push(&self->cfg, addr); const uint32_t *src32 = (const uint32_t *)src; - for (; len; len -= 4) { - ospi_push(&self->cfg, __ROR(*src32++, 16)); + if (self->set->bswap16) { + // MX flashes swap 16-bit words when read in 8D-8D-8D. + for (; len; len -= 4) { + ospi_push(&self->cfg, __ROR(*src32++, 16)); + } + } else { + // For the rest of the flashes, we just correct the endianness. + for (; len; len -= 4) { + ospi_push(&self->cfg, __REV(*src32++)); + } } ospi_writel((&self->cfg), ser, self->cfg.ser); diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 0095ee2d4280b..f909f4a43c889 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -61,6 +61,7 @@ typedef struct _ospi_flash_settings_t { int (*flash_init)(struct _ospi_flash_t *); uint8_t octal_mode; bool rxds; + bool bswap16; uint8_t inst_len; uint8_t xip_data_len; uint16_t read_sr; diff --git a/ports/alif/ospi_flash_settings.h b/ports/alif/ospi_flash_settings.h index c8605275cdcc0..72403c26b67b5 100644 --- a/ports/alif/ospi_flash_settings.h +++ b/ports/alif/ospi_flash_settings.h @@ -34,6 +34,7 @@ .flash_init = ospi_flash_mx_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ .rxds = true, \ + .bswap16 = true, \ .inst_len = OSPI_INST_L_16bit, \ .xip_data_len = OSPI_DATA_L_16bit, \ .read_sr = 0x05fa, \ @@ -50,6 +51,7 @@ .flash_init = ospi_flash_issi_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ .rxds = false, \ + .bswap16 = false, \ .inst_len = OSPI_INST_L_8bit, \ .xip_data_len = OSPI_DATA_L_16bit, \ .read_sr = 0x05, \ @@ -65,7 +67,8 @@ #define OSPI_FLASH_SETTINGS_IS25 \ .flash_init = ospi_flash_issi_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ - .rxds = true, \ + .rxds = false, \ + .bswap16 = false, \ .inst_len = OSPI_INST_L_8bit, \ .xip_data_len = OSPI_DATA_L_16bit, \ .read_sr = 0x05, \ From ff6ed730c534a809ea32648b0b30832b2993aaae Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 5 Feb 2025 19:57:40 +0100 Subject: [PATCH 42/77] alif/se_services: Use EUI extension for unique id. The right service call to get UID is SERVICES_system_get_eui_extension which returns an 8 bytes UID. Signed-off-by: iabdalkader --- ports/alif/modmachine.c | 2 +- ports/alif/se_services.c | 7 ++----- ports/alif/se_services.h | 2 +- ports/alif/usbd.c | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index 29a70305dd516..b77a207fe2cf7 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -38,7 +38,7 @@ static void mp_machine_idle(void) { } static mp_obj_t mp_machine_unique_id(void) { - uint8_t id[5]; + uint8_t id[8] = {0}; se_services_get_unique_id(id); return mp_obj_new_bytes(id, sizeof(id)); } diff --git a/ports/alif/se_services.c b/ports/alif/se_services.c index cbf66bfac6ccd..e79a5b97c8944 100644 --- a/ports/alif/se_services.c +++ b/ports/alif/se_services.c @@ -139,12 +139,9 @@ void se_services_dump_device_data(void) { printf("\n"); } -void se_services_get_unique_id(uint8_t id[5]) { +void se_services_get_unique_id(uint8_t id[8]) { uint32_t error_code; - SERVICES_version_data_t data; - SERVICES_system_get_device_data(se_services_handle, &data, &error_code); - // The MfgData has 5 bytes of valid data, at least on REV_B2. - memcpy(id, data.MfgData, 5); + SERVICES_system_get_eui_extension(se_services_handle, false, id, &error_code); } __attribute__((noreturn)) void se_services_reset_soc(void) { diff --git a/ports/alif/se_services.h b/ports/alif/se_services.h index b1079130c1635..87deb05592990 100644 --- a/ports/alif/se_services.h +++ b/ports/alif/se_services.h @@ -30,7 +30,7 @@ void se_services_init(void); void se_services_dump_device_data(void); -void se_services_get_unique_id(uint8_t id[5]); +void se_services_get_unique_id(uint8_t id[8]); __attribute__((noreturn)) void se_services_reset_soc(void); uint64_t se_services_rand64(void); uint32_t se_services_enable_clock(clock_enable_t clock, bool enable); diff --git a/ports/alif/usbd.c b/ports/alif/usbd.c index da16905d43cd9..8d841d188c333 100644 --- a/ports/alif/usbd.c +++ b/ports/alif/usbd.c @@ -33,7 +33,7 @@ #include "se_services.h" void mp_usbd_port_get_serial_number(char *serial_buf) { - uint8_t id[5]; + uint8_t id[8] = {0}; se_services_get_unique_id(id); MP_STATIC_ASSERT(sizeof(id) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); mp_usbd_hex_str(serial_buf, id, sizeof(id)); From 039df0c884ef0169257b3ab31255be8cd45caacf Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 3 Feb 2025 06:54:37 +0100 Subject: [PATCH 43/77] alif/modmachine: Implement proper low-power modes. Lightsleep current is around 23mA. Deepsleep current is sub 50uA. Signed-off-by: iabdalkader --- ports/alif/alif.mk | 1 + ports/alif/modmachine.c | 56 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 1581452195840..35f316cae0edd 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -182,6 +182,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/common/source/system_utils.c \ Device/common/source/tcm_partition.c \ Device/common/source/tgu_M55.c \ + Device/common/source/pm.c \ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/adc.c \ drivers/source/mhu_driver.c \ diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index b77a207fe2cf7..98d7bffbad932 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -28,6 +28,9 @@ // extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. #include "se_services.h" +#include "tusb.h" + +extern void dcd_uninit(void); #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ @@ -70,15 +73,56 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { mp_raise_NotImplementedError(NULL); } -static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { - mp_int_t delay = -1; - if (n_args == 1) { - delay = mp_obj_get_int(args[0]); +#if MICROPY_HW_ENABLE_USBDEV +static void mp_machine_enable_usb(bool enable) { + if (enable) { + // Initialize TinyUSB and DCD. + tusb_init(); + } else { + // Disconnect USB device. + tud_disconnect(); + // Deinitialize TinyUSB. + tud_deinit(TUD_OPT_RHPORT); + // Deinitialize DCD (disables IRQs). + dcd_uninit(); } - mp_hal_delay_ms(delay); +} +#endif + +static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { + #if MICROPY_HW_ENABLE_USBDEV + mp_machine_enable_usb(false); + #endif + + #ifdef MICROPY_BOARD_ENTER_STANDBY + MICROPY_BOARD_ENTER_STANDBY(); + #endif + + // This enters the deepest possible CPU sleep state, without + // losing CPU state. CPU and subsystem power will remain on. + pm_core_enter_deep_sleep(); + + #ifdef MICROPY_BOARD_EXIT_STANDBY + MICROPY_BOARD_EXIT_STANDBY(); + #endif + + #if MICROPY_HW_ENABLE_USBDEV + mp_machine_enable_usb(true); + #endif } NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { - mp_machine_lightsleep(n_args, args); + #if MICROPY_HW_ENABLE_USBDEV + mp_machine_enable_usb(false); + #endif + + #ifdef MICROPY_BOARD_ENTER_STOP + MICROPY_BOARD_ENTER_STOP(); + #endif + + // If power is removed from the subsystem, the function does + // not return, and the CPU will reboot when/if the subsystem + // is next powered up. + pm_core_enter_deep_sleep_request_subsys_off(); mp_machine_reset(); } From 82bae652eb43ac3ce8226d08c1ce0047ffa76191 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 9 Jan 2025 19:58:25 +0100 Subject: [PATCH 44/77] alif: Add support for pin alternate function selection. Signed-off-by: iabdalkader --- ports/alif/alif.mk | 2 + ports/alif/mcu/ensemble_pin_alt.csv | 130 ++++++++++++++++++++++++++++ ports/alif/mcu/make-pins.py | 28 ++++-- ports/alif/mphalport.c | 50 +++++++++++ ports/alif/mphalport.h | 41 +++++++++ 5 files changed, 242 insertions(+), 9 deletions(-) create mode 100644 ports/alif/mcu/ensemble_pin_alt.csv diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 35f316cae0edd..b338e67c66c9a 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -51,6 +51,7 @@ INC += -Itinyusb_port GEN_PIN_MKPINS = mcu/make-pins.py GEN_PIN_PREFIX = mcu/pins_prefix.c GEN_PINS_BOARD_CSV = $(BOARD_DIR)/pins.csv +GEN_PINS_MCU_CSV = mcu/ensemble_pin_alt.csv GEN_PINS_SRC = $(BUILD)/pins_board.c GEN_PINS_HDR = $(HEADER_BUILD)/pins_board.h @@ -263,6 +264,7 @@ $(BUILD)/firmware.bin: $(BUILD)/firmware.elf $(BUILD)/%_board.c $(HEADER_BUILD)/%_board.h: $(BOARD_DIR)/%.csv $(GEN_PIN_MKPINS) $(GEN_PIN_PREFIX) | $(HEADER_BUILD) $(ECHO) "GEN $@" $(Q)$(PYTHON) $(GEN_PIN_MKPINS) \ + --af-csv $(GEN_PINS_MCU_CSV) \ --board-csv $(GEN_PINS_BOARD_CSV) \ --prefix $(GEN_PIN_PREFIX) \ --output-source $(GEN_PINS_SRC) \ diff --git a/ports/alif/mcu/ensemble_pin_alt.csv b/ports/alif/mcu/ensemble_pin_alt.csv new file mode 100644 index 0000000000000..10bfedc9a1d68 --- /dev/null +++ b/ports/alif/mcu/ensemble_pin_alt.csv @@ -0,0 +1,130 @@ +Pin,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7 +P0_0,,OSPI,UART,I3C,UT,LPCAM,CAM,ANA +P0_1,,OSPI,UART,I3C,UT,LPCAM,CAM,ANA +P0_2,,OSPI,UART,I2C,UT,LPCAM,CAM,ANA +P0_3,,OSPI,UART,I2C,UT,LPCAM,CAM,ANA +P0_4,,OSPI,UART,PDM,I2C,UT,,ANA +P0_5,,OSPI,UART,PDM,I2C,UT,,ANA +P0_6,,OSPI,UART,PDM,I2C,UT,,ANA +P0_7,,OSPI,UART,PDM,I2C,UT,CDC,ANA +P1_0,,UART,SPI,I2C,UT,LPCAM,ETH,ANA +P1_1,,UART,SPI,I2C,UT,LPCAM,ETH,ANA +P1_2,,UART,SPI,I3C,UT,LPCAM,ETH,ANA +P1_3,,UART,SPI,I3C,UT,LPCAM,ETH,ANA +P1_4,,OSPI,UART,SPI,UT,LPCAM,ETH,ANA +P1_5,,OSPI,UART,SPI,UT,LPCAM,ETH,ANA +P1_6,,OSPI,UART,I2S,UT,LPCAM,ETH,ANA +P1_7,,OSPI,UART,I2S,UT,LPCAM,ETH,ANA +P2_0,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA +P2_1,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA +P2_2,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA +P2_3,,OSPI,UART,LPPDM,UT,LPCAM,CDC,ANA +P2_4,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA +P2_5,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA +P2_6,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA +P2_7,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA +P3_0,,OSPI,UART,PDM,I2S,QEC,LPCAM,CAM +P3_1,,OSPI,UART,PDM,I2S,QEC,LPCAM,CAM +P3_2,,OSPI,PDM,I2S,I3C,QEC,LPCAM,CAM +P3_3,,OSPI,PDM,I2S,I3C,QEC,LPCAM,CAM +P3_4,,OSPI,UART,LPPDM,I2S,I2C,QEC,CAM +P3_5,,OSPI,UART,LPPDM,SPI,I2C,QEC,CAM +P3_6,,HFXO,LPUART,LPPDM,SPI,I2C,QEC,CAM +P3_7,,JTAG,LPUART,LPPDM,SPI,I2C,QEC,CAM +P4_0,,JTAG,,I2S,SPI,QEC,CDC,CAM +P4_1,,JTAG,I2S,SPI,QEC,SD,CDC,CAM +P4_2,,JTAG,,I2S,SPI,QEC,SD,CAM +P4_3,,JTAG,,I2S,SPI,QEC,SD,CAM +P4_4,,JTAG,I2S,SPI,FAULT,,, +P4_5,,JTAG,SPI,FAULT,,,, +P4_6,,JTAG,SPI,FAULT,,,, +P4_7,,JTAG,SPI,FAULT,,,, +P5_0,,OSPI,UART,PDM,SPI,I2C,UT,SD +P5_1,,OSPI,UART,PDM,SPI,I2C,UT,SD +P5_2,,OSPI,UART,PDM,SPI,LPI2C,UT,SD +P5_3,,OSPI,UART,SPI,LPI2C,UT,SD,CDC +P5_4,,OSPI,UART,PDM,SPI,UT,SD,CDC +P5_5,,OSPI,UART,PDM,UT,SD,ETH,CDC +# P5_6 doesn't really have OSPI on AF1 but it's needed for P10_7 to be in OSPI mode +P5_6,,OSPI,UART,I2C,UT,SD,ETH,CDC +P5_7,,OSPI,UART,I2C,UT,SD,ETH, +P6_0,,OSPI,UART,PDM,UT,SD,ETH, +P6_1,,OSPI,UART,PDM,UT,SD,ETH, +P6_2,,OSPI,UART,,PDM,UT,SD,ETH +P6_3,,OSPI,UART,,PDM,UT,SD,ETH +P6_4,,OSPI,UART,,SPI,UT,SD,ETH +P6_5,,OSPI,UART,,SPI,UT,SD,ETH +P6_6,,OSPI,UART,,SPI,UT,SD,ETH +P6_7,,OSPI,UART,PDM,SPI,UT,SD,ETH +P7_0,,,CMP,SPI,I2C,UT,SD, +P7_1,,,CMP,SPI,I2C,UT,SD, +P7_2,,,UART,CMP,SPI,I2C,UT,SD +P7_3,,,UART,CMP,SPI,I2C,UT, +P7_4,,,LPUART,LPPDM,LPSPI,LPI2C,UT, +P7_5,,,LPUART,,LPPDM,LPSPI,LPI2C,UT +P7_6,,,LPUART,,LPPDM,LPSPI,I3C,UT +P7_7,,,LPUART,,LPPDM,LPSPI,I3C,UT +P8_0,,OSPI,AUDIO,FAULT,LPCAM,SD,CDC,CAM +P8_1,,I2S,FAULT,LPCAM,SD,CDC,CAM, +P8_2,,I2S,SPI,FAULT,LPCAM,SD,CDC,CAM +P8_3,,I2S,SPI,FAULT,LPCAM,SD,CDC,CAM +P8_4,,I2S,SPI,QEC,LPCAM,SD,CDC,CAM +P8_5,,,SPI,QEC,LPCAM,SD,CDC,CAM +P8_6,,,I2S,QEC,LPCAM,SD,CDC,CAM +P8_7,,,I2S,QEC,LPCAM,SD,CDC,CAM +P9_0,,,I2S,QEC,SD,CDC,CAM, +P9_1,,LPUART,I2S,QEC,SD,CDC,CAM, +P9_2,,LPUART,I2S,SPI,QEC,SD,CDC,CAM +P9_3,,HFXO,UART,I2S,SPI,QEC,CDC,CAM +P9_4,,UART,I2S,SPI,I2C,QEC,CDC,CAM +P9_5,,OSPI,I2S,SPI,I2C,QEC,CDC,CAM +P9_6,,OSPI,AUDIO,SPI,I2C,QEC,CDC,CAM +P9_7,,OSPI,UART,SPI,I2C,QEC,CDC,CAM +P10_0,,OSPI,UART,SPI,UT,LPCAM,CDC,CAM +P10_1,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM +P10_2,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM +P10_3,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM +P10_4,,OSPI,,LPI2S,I2C,UT,ETH,CDC +P10_5,,UART,I2S,SPI,I2C,UT,ETH,CDC +P10_6,,UART,I2S,SPI,I2C,UT,ETH,CDC +P10_7,,UART,I2S,SPI,I2C,UT,CDC,OSPI +P11_0,,OSPI,UART,I2S,SPI,UT,ETH,CDC +P11_1,,OSPI,UART,SPI,UT,ETH,CDC, +P11_2,,OSPI,UART,LPPDM,SPI,UT,ETH,CDC +P11_3,,OSPI,UART,LPPDM,SPI,UT,ETH,CDC +P11_4,,OSPI,UART,PDM,LPSPI,UT,ETH,CDC +P11_5,,OSPI,UART,PDM,LPSPI,UT,ETH,CDC +P11_6,,OSPI,UART,LPPDM,LPSPI,UT,ETH,CDC +P11_7,,OSPI,UART,LPPDM,LPSPI,UT,ETH,CDC +P12_0,,OSPI,AUDIO,I2S,UT,CDC,, +P12_1,,OSPI,UART,I2S,UT,CDC,, +P12_2,,OSPI,UART,I2S,UT,CDC,, +P12_3,,OSPI,UART,I2S,UT,CDC,, +P12_4,,OSPI,SPI,UT,,CDC,, +P12_5,,,SPI,UT,,CDC,, +P12_6,,,SPI,UT,,CDC,, +P12_7,,OSPI,,SPI,UT,CDC,, +P13_0,,OSPI,,SPI,QEC,SD,CDC, +P13_1,,OSPI,SPI,QEC,SD,CDC,, +P13_2,,OSPI,SPI,QEC,SD,CDC,, +P13_3,,OSPI,SPI,QEC,SD,CDC,, +P13_4,,OSPI,LPI2S,QEC,SD,CDC,, +P13_5,,OSPI,LPI2S,QEC,SD,CDC,, +P13_6,,OSPI,LPI2S,QEC,SD,CDC,, +P13_7,,OSPI,LPI2S,QEC,SD,CDC,, +P14_0,,OSPI,UART,QEC,SD,,, +P14_1,,OSPI,UART,,QEC,SD,, +P14_2,,OSPI,UART,,QEC,SD,, +P14_3,,OSPI,UART,,QEC,,, +P14_4,,CMP,SPI,FAULT,,,, +P14_5,,CMP,SPI,FAULT,,,, +P14_6,,CMP,SPI,FAULT,,,, +P14_7,,CMP,SPI,FAULT,,,, +P15_0,,LPTMR,,,,,, +P15_1,,LPTMR,,,,,, +P15_2,,LPTMR,,,,,, +P15_3,,LPTMR,,,,,, +P15_4,,,,,,,, +P15_5,,,,,,,, +P15_6,,,,,,,, +P15_7,,,,,,,, diff --git a/ports/alif/mcu/make-pins.py b/ports/alif/mcu/make-pins.py index d4fbf5d8b8d9a..fb000082642bc 100755 --- a/ports/alif/mcu/make-pins.py +++ b/ports/alif/mcu/make-pins.py @@ -34,26 +34,36 @@ class AlifPin(boardgen.Pin): + def __init__(self, cpu_pin_name): + super().__init__(cpu_pin_name) + self._afs = ["MP_HAL_PIN_ALT_NONE"] * 8 + + # Called for each AF defined in the csv file for this pin. + def add_af(self, af_idx, af_name, af): + self._afs[af_idx] = f"MP_HAL_PIN_ALT_{af}" + # Emit the struct which contains the pin instance. def definition(self): port, pin = self.name()[1:].split("_") adc12_periph, adc12_channel = ADC12_ANA_MAP.get(self.name(), (3, 7)) base = "LPGPIO_BASE" if port == "15" else "GPIO{}_BASE".format(port) return ( - "{{ " - ".base = {{ .type = &machine_pin_type }}, " - ".gpio = (GPIO_Type *){base}, " - ".port = PORT_{port}, " - ".pin = PIN_{pin}, " - ".adc12_periph = {adc12_periph}, " - ".adc12_channel = {adc12_channel}, " - ".name = MP_QSTR_P{port}_{pin} " + "{{\n" + " .name = MP_QSTR_P{port}_{pin},\n" + " .base = {{ .type = &machine_pin_type }},\n" + " .gpio = (GPIO_Type *){base},\n" + " .port = PORT_{port},\n" + " .pin = PIN_{pin},\n" + " .adc12_periph = {adc12_periph},\n" + " .adc12_channel = {adc12_channel},\n" + " .alt = {{{alt}}},\n" "}}".format( port=port, pin=pin, base=base, adc12_periph=adc12_periph, adc12_channel=adc12_channel, + alt=", ".join([f"{af}" for af in self._afs]), ) ) @@ -76,7 +86,7 @@ def validate_cpu_pin_name(cpu_pin_name): class AlifPinGenerator(boardgen.PinGenerator): def __init__(self): # Use custom pin type above. - super().__init__(pin_type=AlifPin) + super().__init__(pin_type=AlifPin, enable_af=True) # Pre-define the pins (i.e. don't require them to be listed in pins.csv). for i in range(NUM_PORTS): diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 5e3dad141908e..1a6136c31abeb 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -169,6 +169,56 @@ uint64_t mp_hal_time_ns(void) { return 0; } +void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode, + uint32_t pull, uint32_t speed, uint32_t drive, uint32_t alt, bool ren) { + uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0; + uint8_t pad_ctrl = drive | speed | (ren ? PADCTRL_READ_ENABLE : 0); + + // Configure pull-up or pull-down. + if (pull & MP_HAL_PIN_PULL_UP) { + pad_ctrl |= PADCTRL_DRIVER_DISABLED_PULL_UP; + } + + if (pull & MP_HAL_PIN_PULL_DOWN) { + pad_ctrl |= PADCTRL_DRIVER_DISABLED_PULL_DOWN; + } + + // Configure open-drain mode. + if (mode == MP_HAL_PIN_MODE_OPEN_DRAIN) { + pad_ctrl |= PADCTRL_DRIVER_OPEN_DRAIN; + } + + // For ALT mode, find alternate function. + if (mode == MP_HAL_PIN_MODE_ALT) { + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(pin->alt); i++) { + if (alt == pin->alt[i]) { + alt_func = i; + break; + } + } + if (alt_func == PINMUX_ALTERNATE_FUNCTION_0) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin af: %d"), alt); + } + } + + // Set pad config. + pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl); + + // For INPUT/OUTPUT/OD modes, set the GPIO direction. + switch (mode) { + case MP_HAL_PIN_MODE_INPUT: + gpio_set_direction_input(pin->gpio, pin->pin); + break; + case MP_HAL_PIN_MODE_OUTPUT: + case MP_HAL_PIN_MODE_OPEN_DRAIN: + gpio_set_direction_output(pin->gpio, pin->pin); + break; + default: + break; + } +} + + void system_tick_schedule_callback(void) { pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); } diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 7b054b671bcd6..3b23cb6a2c514 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -74,12 +74,49 @@ extern ringbuf_t stdin_ringbuf; #define MP_HAL_PIN_MODE_INPUT (0) #define MP_HAL_PIN_MODE_OUTPUT (1) #define MP_HAL_PIN_MODE_OPEN_DRAIN (2) +#define MP_HAL_PIN_MODE_ALT (3) #define MP_HAL_PIN_PULL_NONE (0) #define MP_HAL_PIN_PULL_UP (1) #define MP_HAL_PIN_PULL_DOWN (2) +#define MP_HAL_PIN_DRIVE_2MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_2MA) +#define MP_HAL_PIN_DRIVE_4MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_4MA) +#define MP_HAL_PIN_DRIVE_8MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_8MA) +#define MP_HAL_PIN_DRIVE_12MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA) +#define MP_HAL_PIN_SPEED_LOW (0) +#define MP_HAL_PIN_SPEED_HIGH (PADCTRL_SLEW_RATE_FAST) #define mp_hal_pin_obj_t const machine_pin_obj_t * +enum { + MP_HAL_PIN_ALT_NONE = 0, + MP_HAL_PIN_ALT_ANA, + MP_HAL_PIN_ALT_AUDIO, + MP_HAL_PIN_ALT_CAM, + MP_HAL_PIN_ALT_CDC, + MP_HAL_PIN_ALT_CMP, + MP_HAL_PIN_ALT_ETH, + MP_HAL_PIN_ALT_FAULT, + MP_HAL_PIN_ALT_HFXO, + MP_HAL_PIN_ALT_I2C, + MP_HAL_PIN_ALT_I2S, + MP_HAL_PIN_ALT_I3C, + MP_HAL_PIN_ALT_JTAG, + MP_HAL_PIN_ALT_LPCAM, + MP_HAL_PIN_ALT_LPI2C, + MP_HAL_PIN_ALT_LPI2S, + MP_HAL_PIN_ALT_LPPDM, + MP_HAL_PIN_ALT_LPSPI, + MP_HAL_PIN_ALT_LPTMR, + MP_HAL_PIN_ALT_LPUART, + MP_HAL_PIN_ALT_OSPI, + MP_HAL_PIN_ALT_PDM, + MP_HAL_PIN_ALT_QEC, + MP_HAL_PIN_ALT_SD, + MP_HAL_PIN_ALT_SPI, + MP_HAL_PIN_ALT_UART, + MP_HAL_PIN_ALT_UT, +}; + typedef struct _machine_pin_obj_t { mp_obj_base_t base; GPIO_Type *gpio; @@ -88,6 +125,7 @@ typedef struct _machine_pin_obj_t { uint8_t adc12_periph : 2; uint8_t adc12_channel : 3; qstr name; + const uint8_t alt[8]; } machine_pin_obj_t; mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); @@ -149,5 +187,8 @@ static inline void mp_hal_wake_main_task_from_isr(void) { // Defined for tinyusb support, nothing needs to be done here. } +void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode, + uint32_t pull, uint32_t speed, uint32_t drive, uint32_t alt, bool ren); + // Include all the pin definitions. #include "genhdr/pins_board.h" From 9073270c2e0daa1a3539aa14c0feadadd6c6b08d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 2 Jan 2025 06:29:05 +0100 Subject: [PATCH 45/77] alif/machine_i2c: Add machine.I2C peripheral support. Signed-off-by: iabdalkader --- ports/alif/alif.mk | 2 + ports/alif/machine_i2c.c | 301 ++++++++++++++++++++++++++++++++++++++ ports/alif/mpconfigport.h | 2 + 3 files changed, 305 insertions(+) create mode 100644 ports/alif/machine_i2c.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index b338e67c66c9a..5cfd37fc4b6e9 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -117,6 +117,7 @@ SRC_C = \ alif_flash.c \ fatfs_port.c \ machine_pin.c \ + machine_i2c.c \ main.c \ modalif.c \ mphalport.c \ @@ -186,6 +187,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/common/source/pm.c \ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/adc.c \ + drivers/source/i2c.c \ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ diff --git a/ports/alif/machine_i2c.c b/ports/alif/machine_i2c.c new file mode 100644 index 0000000000000..f117b93c2b1e8 --- /dev/null +++ b/ports/alif/machine_i2c.c @@ -0,0 +1,301 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "extmod/modmachine.h" + +#if MICROPY_HW_ENABLE_HW_I2C +#include "i2c.h" + +#define I2C_DEFAULT_FREQ (400000) +#define I2C_DEFAULT_TIMEOUT (50000) + +#define I2C_DAT_INDEX (0) +#define I2C_TX_FIFO_LEN (I2C_FIFO_DEPTH / 2) +#define I2C_RX_FIFO_LEN (I2C_FIFO_DEPTH / 2) + +#define I2C_SPEED(freq) \ + ((freq) <= 100000 ? I2C_SPEED_STANDARD : \ + ((freq) <= 400000 ? I2C_SPEED_FAST : \ + I2C_SPEED_FASTPLUS)) + +#define I2C_IC_CON_SPEED(freq) \ + ((freq) <= 100000 ? I2C_IC_CON_SPEED_STANDARD : \ + ((freq) <= 400000 ? I2C_IC_CON_SPEED_FAST : \ + I2C_IC_CON_SPEED_HIGH)) +#define I2C_IC_CON_MASTER_TX_EMPTY_CTRL (1 << 8) + +#define I2C_IC_STATUS_RFNE I2C_IC_STATUS_RECEIVE_FIFO_NOT_EMPTY +#define I2C_IC_STATUS_TFNF I2C_IC_STATUS_TRANSMIT_FIFO_NOT_FULL +#define I2C_STAT_ERRORS (I2C_IC_INTR_STAT_TX_ABRT | I2C_IC_INTR_STAT_TX_OVER | \ + I2C_IC_INTR_STAT_RX_OVER | I2C_IC_INTR_STAT_RX_UNDER) + +#define debug_printf(...) // mp_printf(&mp_plat_print, "i2c.c: " __VA_ARGS__) +#define I2C_CHECK_ERRORS(base) \ + if (base->I2C_RAW_INTR_STAT & I2C_STAT_ERRORS) { \ + uint32_t status = base->I2C_RAW_INTR_STAT; \ + debug_printf("status: 0x%lx raw_int: 0x%lx abort: 0x%lx line: %d\n", \ + base->I2C_STATUS, status, base->I2C_TX_ABRT_SOURCE, __LINE__); \ + (void)status; \ + (void)base->I2C_CLR_TX_ABRT; \ + (void)base->I2C_CLR_ACTIVITY; \ + return -MP_EIO; \ + } + +typedef struct _machine_i2c_obj_t { + mp_obj_base_t base; + uint32_t i2c_id; + I2C_Type *i2c; + mp_hal_pin_obj_t scl; + mp_hal_pin_obj_t sda; + uint32_t freq; + uint32_t timeout; +} machine_i2c_obj_t; + +static machine_i2c_obj_t machine_i2c_obj[] = { + #if defined(MICROPY_HW_I2C0_SCL) + [0] = {{&machine_i2c_type}, 0, (I2C_Type *)I2C0_BASE, MICROPY_HW_I2C0_SCL, MICROPY_HW_I2C0_SDA}, + #endif + #if defined(MICROPY_HW_I2C1_SCL) + [1] = {{&machine_i2c_type}, 1, (I2C_Type *)I2C1_BASE, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA}, + #endif + #if defined(MICROPY_HW_I2C2_SCL) + [2] = {{&machine_i2c_type}, 2, (I2C_Type *)I2C2_BASE, MICROPY_HW_I2C2_SCL, MICROPY_HW_I2C2_SDA}, + #endif + #if defined(MICROPY_HW_I2C3_SCL) + [3] = {{&machine_i2c_type}, 3, (I2C_Type *)I2C3_BASE, MICROPY_HW_I2C3_SCL, MICROPY_HW_I2C3_SDA}, + #endif +}; + +static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "I2C(%u, freq=%u, scl=%q, sda=%q, timeout=%u)", + self->i2c_id, self->freq, self->scl->name, self->sda->name, self->timeout); +} + +mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_freq, ARG_scl, ARG_sda, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_freq, MP_ARG_INT, {.u_int = I2C_DEFAULT_FREQ} }, + { MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2C_DEFAULT_TIMEOUT} }, + }; + + // Parse args. + 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); + + // Get I2C bus. + int i2c_id = mp_obj_get_int(args[ARG_id].u_obj); + if (i2c_id < 0 || i2c_id >= MP_ARRAY_SIZE(machine_i2c_obj) || !machine_i2c_obj[i2c_id].i2c) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); + } + + // Get static peripheral object. + machine_i2c_obj_t *self = &machine_i2c_obj[i2c_id]; + + // Set args + self->freq = args[ARG_freq].u_int; + self->timeout = args[ARG_timeout].u_int; + + // here we would check the scl/sda pins and configure them, but it's not implemented + if (args[ARG_scl].u_obj != mp_const_none || args[ARG_sda].u_obj != mp_const_none) { + mp_raise_ValueError(MP_ERROR_TEXT("explicit choice of scl/sda is not implemented")); + } + + // Disable I2C controller. + i2c_disable(self->i2c); + + // Configure I2C pins. + mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + + // Initialize I2C controller. + self->i2c->I2C_CON = I2C_IC_CON_ENABLE_MASTER_MODE | + I2C_IC_CON_MASTER_RESTART_EN | + I2C_IC_CON_MASTER_TX_EMPTY_CTRL | + I2C_IC_CON_SPEED(self->freq); + + // Configure FIFO threshold. + self->i2c->I2C_TX_TL = I2C_TX_FIFO_LEN; + self->i2c->I2C_RX_TL = I2C_RX_FIFO_LEN; + + // Configure clock. + i2c_master_set_clock(self->i2c, GetSystemAPBClock() / 1000, I2C_SPEED(self->freq)); + + // Enable I2C controller. + i2c_clear_all_interrupt(self->i2c); + i2c_enable(self->i2c); + + return MP_OBJ_FROM_PTR(self); +} + +static int machine_i2c_poll_flags(I2C_Type *base, uint32_t flags, uint32_t timeout_us) { + mp_uint_t tick_start = mp_hal_ticks_us(); + while (!(base->I2C_STATUS & flags)) { + I2C_CHECK_ERRORS(base); + if ((mp_hal_ticks_us() - tick_start) >= timeout_us) { + return -MP_ETIMEDOUT; + } + // Can't delay or handle pending events here otherwise we risk + // the FIFO getting empty, which will generate a STOP condition. + } + return 0; +} + +static int machine_i2c_write(machine_i2c_obj_t *self, uint8_t *buf, size_t tx_size) { + mp_uint_t tick_start = mp_hal_ticks_us(); + for (size_t tx_idx = 0; tx_idx < tx_size;) { + // Write data to FIFO + if (self->i2c->I2C_STATUS & I2C_IC_STATUS_TFNF) { + self->i2c->I2C_DATA_CMD = (uint16_t)buf[tx_idx++]; + I2C_CHECK_ERRORS(self->i2c); + tick_start = mp_hal_ticks_us(); + } + + // Check for timeout + if ((mp_hal_ticks_us() - tick_start) >= self->timeout) { + return -MP_ETIMEDOUT; + } + } + return 0; +} + +static int machine_i2c_read(machine_i2c_obj_t *self, uint8_t *buf, size_t rx_size) { + mp_uint_t tick_start = mp_hal_ticks_us(); + for (size_t tx_idx = 0, rx_idx = 0; rx_idx < rx_size;) { + // Write command to FIFO + if (tx_idx < rx_size && (self->i2c->I2C_STATUS & I2C_IC_STATUS_TFNF)) { + self->i2c->I2C_DATA_CMD = I2C_IC_DATA_CMD_READ_REQ; + I2C_CHECK_ERRORS(self->i2c); + ++tx_idx; + } + + // Read data from FIFO + while (rx_idx < rx_size && (self->i2c->I2C_STATUS & I2C_IC_STATUS_RFNE)) { + buf[rx_idx++] = self->i2c->I2C_DATA_CMD & 0xFF; + tick_start = mp_hal_ticks_us(); + } + + // Check for timeout + if ((mp_hal_ticks_us() - tick_start) >= self->timeout) { + return -MP_ETIMEDOUT; + } + } + return 0; +} + +int machine_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) { + int ret = 0; + uint32_t bytes = 0; + machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in; + + // The DesignWare I2C IP on AE3 is configured such that it auto-generates a STOP + // condition when the TX FIFO gets empty. In other words, the code can't have any + // control over STOP condition generation. The only fix for this would be to buffer + // complete read/write sequences and send them out when the STOP flag is set. + if (!(flags & MP_MACHINE_I2C_FLAG_STOP)) { + mp_raise_ValueError(MP_ERROR_TEXT("nostop flag is not supported")); + } + + i2c_clear_all_interrupt(self->i2c); + i2c_set_target_addr(self->i2c, addr, I2C_7BIT_ADDRESS, 0); + + // Workaround issue with hardware I2C not accepting zero-length writes. + if (!bufs->len) { + mp_machine_i2c_buf_t bufs = { 0 }; + + mp_machine_soft_i2c_obj_t soft_i2c = { + .base = { &mp_machine_soft_i2c_type }, + .scl = self->scl, + .sda = self->sda, + .us_timeout = self->timeout, + .us_delay = 500000 / self->freq + 1, + }; + + // Switch pins to GPIO/OD. + mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_NONE, true); + mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_NONE, true); + + // Perform the transfer. + ret = mp_machine_soft_i2c_transfer(&soft_i2c.base, addr, 1, &bufs, flags); + + // Re-configure I2C pins. + mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + + return ret; + } + + for (size_t i = 0; i < n; i++) { + mp_machine_i2c_buf_t *buf = &bufs[i]; + if (i == 0 && (flags & MP_MACHINE_I2C_FLAG_WRITE1)) { + ret = machine_i2c_write(self, buf->buf, buf->len); + } else if (flags & MP_MACHINE_I2C_FLAG_READ) { + ret = machine_i2c_read(self, buf->buf, buf->len); + } else if (bufs->len != 0) { + ret = machine_i2c_write(self, buf->buf, buf->len); + } + if (ret < 0) { + return ret; + } + bytes += bufs->len; + } + + // Wait for TX FIFO empty + ret = machine_i2c_poll_flags(self->i2c, I2C_IC_STATUS_TFE, self->timeout); + if (ret < 0) { + return ret; + } + + return bytes; +} + +static const mp_machine_i2c_p_t machine_i2c_p = { + .transfer_supports_write1 = true, + .transfer = machine_i2c_transfer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_i2c_type, + MP_QSTR_I2C, + MP_TYPE_FLAG_NONE, + make_new, machine_i2c_make_new, + print, machine_i2c_print, + protocol, &machine_i2c_p, + locals_dict, &mp_machine_i2c_locals_dict + ); +#endif // MICROPY_HW_ENABLE_HW_I2C diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index ae3ad7a30d352..b23886637e308 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -130,6 +130,8 @@ #define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/alif/machine_adc.c" #define MICROPY_PY_MACHINE_DHT_READINTO (1) #define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_I2C (MICROPY_HW_ENABLE_HW_I2C) +#define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) From 280e6e2a40dbddb3aec5424c1ca1bf9dea876dd0 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 19 Jan 2025 15:59:25 +0100 Subject: [PATCH 46/77] alif/machine_spi: Add machine.SPI peripheral support. Signed-off-by: iabdalkader Signed-off-by: Damien George --- ports/alif/alif.mk | 3 + ports/alif/machine_spi.c | 312 ++++++++++++++++++++++++++++++++++++++ ports/alif/mpconfigport.h | 1 + 3 files changed, 316 insertions(+) create mode 100644 ports/alif/machine_spi.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 5cfd37fc4b6e9..63003b58a0937 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -118,6 +118,7 @@ SRC_C = \ fatfs_port.c \ machine_pin.c \ machine_i2c.c \ + machine_spi.c \ main.c \ modalif.c \ mphalport.c \ @@ -188,6 +189,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/adc.c \ drivers/source/i2c.c \ + drivers/source/spi.c \ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ @@ -206,6 +208,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ ) $(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 +$(BUILD)/$(ALIF_DFP_REL_TOP)/drivers/source/spi.o: CFLAGS += -Wno-maybe-uninitialized $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c new file mode 100644 index 0000000000000..d13c6368f2515 --- /dev/null +++ b/ports/alif/machine_spi.c @@ -0,0 +1,312 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "extmod/modmachine.h" + +#if MICROPY_PY_MACHINE_SPI +#include "clk.h" +#include "spi.h" +#include "sys_ctrl_spi.h" + +typedef struct _machine_spi_obj_t { + mp_obj_base_t base; + uint8_t id; + SPI_Type *inst; + bool is_lp; +} machine_spi_obj_t; + +static machine_spi_obj_t machine_spi_obj[] = { + #if defined(MICROPY_HW_SPI0_SCK) + [0] = {{&machine_spi_type}, 0, (SPI_Type *)SPI0_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI1_SCK) + [1] = {{&machine_spi_type}, 1, (SPI_Type *)SPI1_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI2_SCK) + [2] = {{&machine_spi_type}, 2, (SPI_Type *)SPI2_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI3_SCK) + [3] = {{&machine_spi_type}, 3, (SPI_Type *)SPI3_BASE, false}, + #endif + #if defined(MICROPY_HW_LPSPI0_SCK) + [4] = {{&machine_spi_type}, 4, (SPI_Type *)LPSPI0_BASE, true}, + #endif + +}; + +static inline uint32_t spi_get_clk(machine_spi_obj_t *spi) { + return spi->is_lp ? GetSystemCoreClock() : GetSystemAHBClock(); +} + +static void spi_init(machine_spi_obj_t *spi, uint32_t baudrate, + uint32_t polarity, uint32_t phase, uint32_t bits, uint32_t firstbit) { + const machine_pin_obj_t *pins[4] = { NULL, NULL, NULL, NULL }; + switch (spi->id) { + #if defined(MICROPY_HW_SPI0_SCK) + case 0: + pins[0] = MICROPY_HW_SPI0_SCK; + pins[1] = MICROPY_HW_SPI0_MISO; + pins[2] = MICROPY_HW_SPI0_MOSI; + #if defined(MICROPY_HW_SPI0_NSS) + pins[3] = MICROPY_HW_SPI0_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI1_SCK) + case 1: + pins[0] = MICROPY_HW_SPI1_SCK; + pins[1] = MICROPY_HW_SPI1_MISO; + pins[2] = MICROPY_HW_SPI1_MOSI; + #if defined(MICROPY_HW_SPI1_NSS) + pins[3] = MICROPY_HW_SPI1_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI2_SCK) + case 2: + pins[0] = MICROPY_HW_SPI2_SCK; + pins[1] = MICROPY_HW_SPI2_MISO; + pins[2] = MICROPY_HW_SPI2_MOSI; + #if defined(MICROPY_HW_SPI2_NSS) + pins[3] = MICROPY_HW_SPI2_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI3_SCK) + case 3: + pins[0] = MICROPY_HW_SPI3_SCK; + pins[1] = MICROPY_HW_SPI3_MISO; + pins[2] = MICROPY_HW_SPI3_MOSI; + #if defined(MICROPY_HW_SPI3_NSS) + pins[3] = MICROPY_HW_SPI3_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_LPSPI0_SCK) + case 4: // LPSPI0 + pins[0] = MICROPY_HW_LPSPI0_SCK; + pins[1] = MICROPY_HW_LPSPI0_MISO; + pins[2] = MICROPY_HW_LPSPI0_MOSI; + #if defined(MICROPY_HW_LPSPI0_NSS) + pins[3] = MICROPY_HW_LPSPI0_NSS; + #endif + break; + #endif + default: + return; + } + + // Disable SPI. + spi_disable(spi->inst); + + // Enable clocks. + if (spi->is_lp) { + enable_lpspi_clk(); + } + + // Configure SPI pins. + for (size_t i = 0; i < MP_ARRAY_SIZE(pins) && pins[i]; i++) { + mp_hal_pin_config(pins[i], MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + } + + // Disable all interrupts. + spi_mask_interrupts(spi->inst); + + // Configure baudrate clock + spi_set_bus_speed(spi->inst, baudrate, spi_get_clk(spi)); + + // Configure FIFOs + spi_set_tx_threshold(spi->inst, 0); + spi_set_rx_threshold(spi->inst, 0); + if (!spi->is_lp) { + spi_set_rx_sample_delay(spi->inst, 0); + spi_set_tx_fifo_start_level(spi->inst, 0); + } + + // Configure SPI bus mode. + uint32_t spi_mode = (polarity << 1) | phase; + if (!spi->is_lp) { + spi_set_mode(spi->inst, spi_mode); + } else { + lpspi_set_mode(spi->inst, spi_mode); + } + + // Configure SPI bus protocol. + uint32_t spi_proto = SPI_PROTO_SPI; + if (!spi->is_lp) { + spi_set_protocol(spi->inst, spi_proto); + } else { + lpspi_set_protocol(spi->inst, spi_proto); + } + + // Configure SPI transfer mode. + if (!spi->is_lp) { + spi_mode_master(spi->inst); + } + + // Configure frame size. + if (!spi->is_lp) { + spi_set_dfs(spi->inst, bits); + } else { + lpspi_set_dfs(spi->inst, bits); + } + + // Configure slave select pin + spi_control_ss(spi->inst, 0, true); + if (!spi->is_lp) { + spi_set_sste(spi->inst, false); + } else { + lpspi_set_sste(spi->inst, false); + } + + // Clear IRQs. + (void)spi->inst->SPI_ICR; + + // Enable SPI. + spi_enable(spi->inst); +} + +static void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t baudrate = spi_get_bus_speed(self->inst, spi_get_clk(self)); + mp_printf(print, "SPI(%u, baudrate=%u, lp=%u)", self->id, baudrate, self->is_lp); +} + +mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + // Parse args. + 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); + + // Get spi bus. + int spi_id = mp_obj_get_int(args[ARG_id].u_obj); + if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(machine_spi_obj) || !machine_spi_obj[spi_id].inst) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); + } + + // Get static peripheral object. + machine_spi_obj_t *self = &machine_spi_obj[spi_id]; + + // here we would check the sck/mosi/miso pins and configure them, but it's not implemented + if (args[ARG_sck].u_obj != MP_OBJ_NULL || + args[ARG_mosi].u_obj != MP_OBJ_NULL || + args[ARG_miso].u_obj != MP_OBJ_NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("explicit choice of sck/mosi/miso is not implemented")); + } + + // Initialize and configure SPI. + spi_init(self, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, + args[ARG_phase].u_int, args[ARG_bits].u_int, args[ARG_firstbit].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + // Parse the arguments. + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Initialize and configure SPI. + spi_init(self, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, + args[ARG_phase].u_int, args[ARG_bits].u_int, args[ARG_firstbit].u_int); +} + +static void machine_spi_deinit(mp_obj_base_t *self_in) { + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + // Disable all interrupts. + spi_mask_interrupts(self->inst); + // Disable SS pin. + spi_control_ss(self->inst, 0, 0); + // Disable SPI. + spi_disable(self->inst); + // Deinitialize GPIOs and clocks. + if (self->is_lp) { + disable_lpspi_clk(); + } +} + +static void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + spi_transfer_t spi_xfer = { + .tx_buff = src, + .tx_total_cnt = len, + .rx_buff = dest, + .rx_total_cnt = len, + .tx_default_val = 0xFF, + .tx_default_enable = true, + .mode = SPI_TMOD_TX_AND_RX, + }; + // TODO redo transfer_blocking to timeout and poll events. + if (!self->is_lp) { + spi_transfer_blocking(self->inst, &spi_xfer); + } else { + lpspi_transfer_blocking(self->inst, &spi_xfer); + } +} + +static const mp_machine_spi_p_t machine_spi_p = { + .init = machine_spi_init, + .deinit = machine_spi_deinit, + .transfer = machine_spi_transfer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_spi_type, + MP_QSTR_SPI, + MP_TYPE_FLAG_NONE, + make_new, machine_spi_make_new, + print, machine_spi_print, + protocol, &machine_spi_p, + locals_dict, &mp_machine_spi_locals_dict + ); + +#endif // MICROPY_PY_MACHINE_SPI diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index b23886637e308..83f808b131f48 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -133,6 +133,7 @@ #define MICROPY_PY_MACHINE_I2C (MICROPY_HW_ENABLE_HW_I2C) #define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) +#define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) #define MICROPY_VFS (1) From ec92bcfeff787a4ceb25c3b9c1703043a7e3052d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 31 Jan 2025 14:15:06 +0100 Subject: [PATCH 47/77] alif/machine_rtc: Add basic machine.RTC support. Signed-off-by: iabdalkader --- ports/alif/alif.mk | 1 + ports/alif/irq.h | 1 + ports/alif/machine_rtc.c | 109 +++++++++++++++++++++++++++++++++++++++ ports/alif/modmachine.c | 1 + 4 files changed, 112 insertions(+) create mode 100644 ports/alif/machine_rtc.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 63003b58a0937..aa2451d7a6291 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -119,6 +119,7 @@ SRC_C = \ machine_pin.c \ machine_i2c.c \ machine_spi.c \ + machine_rtc.c \ main.c \ modalif.c \ mphalport.c \ diff --git a/ports/alif/irq.h b/ports/alif/irq.h index 59dbce9705947..ed454adcb4657 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -48,6 +48,7 @@ #define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 7, 0) #define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) #define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0) +#define IRQ_PRI_RTC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 100, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) // these states correspond to values from query_irq, enable_irq and disable_irq diff --git a/ports/alif/machine_rtc.c b/ports/alif/machine_rtc.c new file mode 100644 index 0000000000000..6473d1d80fbff --- /dev/null +++ b/ports/alif/machine_rtc.c @@ -0,0 +1,109 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "extmod/modmachine.h" +#include "rtc.h" +#include "sys_ctrl_rtc.h" + +typedef struct _machine_rtc_obj_t { + mp_obj_base_t base; + LPRTC_Type *rtc; +} machine_rtc_obj_t; + +// Singleton RTC object. +static const machine_rtc_obj_t machine_rtc = {{&machine_rtc_type}, (LPRTC_Type *)LPRTC_BASE}; + +void LPRTC_IRQHandler(void) { + lprtc_interrupt_ack(machine_rtc.rtc); + lprtc_interrupt_disable(machine_rtc.rtc); +} + +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) { + const machine_rtc_obj_t *self = &machine_rtc; + + // Check arguments. + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + enable_lprtc_clk(); + lprtc_prescaler_disable(self->rtc); + lprtc_counter_wrap_disable(self->rtc); + lprtc_interrupt_disable(self->rtc); + lprtc_interrupt_unmask(self->rtc); + + NVIC_SetPriority(LPRTC_IRQ_IRQn, IRQ_PRI_RTC); + NVIC_ClearPendingIRQ(LPRTC_IRQ_IRQn); + NVIC_EnableIRQ(LPRTC_IRQ_IRQn); + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_id, ARG_time, ARG_repeat }; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_time, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_repeat, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + machine_rtc_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(args), allowed_args, args); + + if (mp_obj_is_int(args[ARG_time].u_obj)) { + uint32_t seconds = mp_obj_get_int(args[1].u_obj) / 1000; + + lprtc_counter_disable(self->rtc); + lprtc_load_count(self->rtc, 1); + lprtc_load_counter_match_register(self->rtc, seconds * 32768); + + lprtc_interrupt_ack(self->rtc); + lprtc_interrupt_enable(self->rtc); + lprtc_counter_enable(self->rtc); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid argument(s)")); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_rtc_alarm_obj, 1, machine_rtc_alarm); + +static const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_alarm), MP_ROM_PTR(&machine_rtc_alarm_obj) }, +}; +static MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_rtc_type, + MP_QSTR_RTC, + MP_TYPE_FLAG_NONE, + make_new, machine_rtc_make_new, + locals_dict, &machine_rtc_locals_dict + ); diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index 98d7bffbad932..9868abbeeaf35 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -35,6 +35,7 @@ extern void dcd_uninit(void); #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, \ + { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, \ static void mp_machine_idle(void) { mp_event_wait_indefinite(); From facd0b7190d48d7d6f4af1d1eaba0c3a18734356 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 7 Feb 2025 15:41:42 +0100 Subject: [PATCH 48/77] alif/ospi_flash: Use mp_hal_pin_config to configure OSPI pins. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 42 +++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index f2f95a04cf241..17c8be8128da3 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -262,30 +262,40 @@ int ospi_flash_init(void) { self->pin = pin; - uint32_t ck_pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST; - uint32_t io_pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; - - pinconf_set(pin->pin_cs->port, pin->pin_cs->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); - pinconf_set(pin->pin_clk_p->port, pin->pin_clk_p->pin, OSPI_PIN_FUNCTION, ck_pad_ctrl); + mp_hal_pin_config(pin->pin_cs, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); + mp_hal_pin_config(pin->pin_clk_p, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); if (pin->pin_clk_n != NULL) { - pinconf_set(pin->pin_clk_n->port, pin->pin_clk_n->pin, OSPI_PIN_FUNCTION, ck_pad_ctrl); + mp_hal_pin_config(pin->pin_clk_n, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); } if (pin->pin_rwds != NULL) { - pinconf_set(pin->pin_rwds->port, pin->pin_rwds->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + mp_hal_pin_config(pin->pin_rwds, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); if (pin->pin_rwds->port == PORT_10 && pin->pin_rwds->pin == PIN_7) { // Alif: P5_6 is needed to support proper alt function selection of P10_7. - pinconf_set(PORT_5, PIN_6, OSPI_PIN_FUNCTION, io_pad_ctrl); + mp_hal_pin_config(pin_P5_6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); } } - pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, io_pad_ctrl | PADCTRL_DRIVER_DISABLED_PULL_UP); - pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + mp_hal_pin_config(pin->pin_d0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); if (pin->pin_d4 != NULL) { - pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d5->port, pin->pin_d5->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d6->port, pin->pin_d6->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d7->port, pin->pin_d7->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + mp_hal_pin_config(pin->pin_d4, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d5, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d7, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); } // Reset the SPI flash. From b9e5f1ffba8551164374bf732aa165c87519a569 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 15 Feb 2025 13:53:44 +0100 Subject: [PATCH 49/77] alif/se_services: Add a secondary MHU channel. This channel can be used to communicate (pass messages) between the M55 cores in the RTSS. Currently it's only used to notify the cores. Signed-off-by: iabdalkader --- ports/alif/se_services.c | 120 +++++++++++++++++++++++++++++---------- ports/alif/se_services.h | 4 ++ 2 files changed, 93 insertions(+), 31 deletions(-) diff --git a/ports/alif/se_services.c b/ports/alif/se_services.c index e79a5b97c8944..1ed6e3fbfdad0 100644 --- a/ports/alif/se_services.c +++ b/ports/alif/se_services.c @@ -37,8 +37,9 @@ #include "py/mphal.h" // MHU indices. -#define MHU_M55_SE_MHU0 0 -#define MAX_MHU 1 +#define MHU_SESS_MHU0 0 +#define MHU_RTSS_MHU0 1 +#define MAX_MHU 2 // The following timeout is implemented in se_services_handle.c as a // simple loop busy polling on a variable set from an IRQ. @@ -57,24 +58,36 @@ typedef struct { static const uint32_t mhu_sender_base_address_list[MAX_MHU] = { MHU_SESS_S_TX_BASE, + MHU_RTSS_S_TX_BASE }; static const uint32_t mhu_receiver_base_address_list[MAX_MHU] = { MHU_SESS_S_RX_BASE, + MHU_RTSS_S_RX_BASE }; // Must be aligned as a uint32_t. static uint32_t packet_buffer[SERVICES_MAX_PACKET_BUFFER_SIZE / sizeof(uint32_t)]; +static uint32_t se_sess_handle; +static uint32_t se_rtss_handle; + static mhu_driver_out_t mhu_driver_out; -static uint32_t se_services_handle; void MHU_SESS_S_TX_IRQHandler(void) { - mhu_driver_out.sender_irq_handler(MHU_M55_SE_MHU0); + mhu_driver_out.sender_irq_handler(MHU_SESS_MHU0); } void MHU_SESS_S_RX_IRQHandler(void) { - mhu_driver_out.receiver_irq_handler(MHU_M55_SE_MHU0); + mhu_driver_out.receiver_irq_handler(MHU_SESS_MHU0); +} + +void MHU_RTSS_S_TX_IRQHandler(void) { + mhu_driver_out.sender_irq_handler(MHU_RTSS_MHU0); +} + +void MHU_RTSS_S_RX_IRQHandler(void) { + mhu_driver_out.receiver_irq_handler(MHU_RTSS_MHU0); } int dummy_printf(const char *fmt, ...) { @@ -82,6 +95,33 @@ int dummy_printf(const char *fmt, ...) { return 0; } +static void se_services_irq_config(IRQn_Type irqn, bool enable) { + if (enable) { + NVIC_ClearPendingIRQ(irqn); + NVIC_SetPriority(irqn, IRQ_PRI_MHU); + NVIC_EnableIRQ(irqn); + } else { + NVIC_DisableIRQ(irqn); + NVIC_ClearPendingIRQ(irqn); + } +} + +void se_services_rx_callback(uint32_t id, uint32_t channel, uint32_t data) { + switch (id) { + case MHU_SESS_MHU0: + SERVICES_rx_msg_callback(id, channel, data); + break; + case MHU_RTSS_MHU0: + #if MICROPY_PY_OPENAMP + extern void metal_rproc_notified(void); + metal_rproc_notified(); + #endif + break; + default: + break; + } +} + void se_services_init(void) { // Initialize MHU. mhu_driver_in_t mhu_driver_in; @@ -89,7 +129,7 @@ void se_services_init(void) { mhu_driver_in.receiver_base_address_list = (uint32_t *)mhu_receiver_base_address_list; mhu_driver_in.mhu_count = MAX_MHU; mhu_driver_in.send_msg_acked_callback = SERVICES_send_msg_acked_callback; - mhu_driver_in.rx_msg_callback = SERVICES_rx_msg_callback; + mhu_driver_in.rx_msg_callback = se_services_rx_callback; mhu_driver_in.debug_print = NULL; // not currently used by MHU_driver_initialize MHU_driver_initialize(&mhu_driver_in, &mhu_driver_out); @@ -103,29 +143,38 @@ void se_services_init(void) { }; SERVICES_initialize(&services_init_params); - // Create SE services channel for sending requests. - se_services_handle = SERVICES_register_channel(MHU_M55_SE_MHU0, 0); + // Register SESS MHU channel. + se_sess_handle = SERVICES_register_channel(MHU_SESS_MHU0, 0); + se_services_irq_config(MHU_SESS_S_RX_IRQ_IRQn, true); + se_services_irq_config(MHU_SESS_S_TX_IRQ_IRQn, true); - // Enable MHU interrupts. - NVIC_ClearPendingIRQ(MHU_SESS_S_RX_IRQ_IRQn); - NVIC_SetPriority(MHU_SESS_S_RX_IRQ_IRQn, IRQ_PRI_MHU); - NVIC_EnableIRQ(MHU_SESS_S_RX_IRQ_IRQn); - NVIC_ClearPendingIRQ(MHU_SESS_S_TX_IRQ_IRQn); - NVIC_SetPriority(MHU_SESS_S_TX_IRQ_IRQn, IRQ_PRI_MHU); - NVIC_EnableIRQ(MHU_SESS_S_TX_IRQ_IRQn); + // Register RTSS MHU channel. + se_rtss_handle = SERVICES_register_channel(MHU_RTSS_MHU0, 0); + se_services_irq_config(MHU_RTSS_S_RX_IRQ_IRQn, true); + se_services_irq_config(MHU_RTSS_S_TX_IRQ_IRQn, true); // Send heartbeat services requests until one succeeds. - SERVICES_synchronize_with_se(se_services_handle); + SERVICES_synchronize_with_se(se_sess_handle); +} + +void se_services_deinit(void) { + // Disable SESS MHU channel IRQs. + se_services_irq_config(MHU_SESS_S_RX_IRQ_IRQn, false); + se_services_irq_config(MHU_SESS_S_TX_IRQ_IRQn, false); + + // Disable RTSS MHU channel IRQs. + se_services_irq_config(MHU_RTSS_S_RX_IRQ_IRQn, false); + se_services_irq_config(MHU_RTSS_S_TX_IRQ_IRQn, false); } void se_services_dump_device_data(void) { uint32_t error_code; uint8_t revision[80]; - SERVICES_get_se_revision(se_services_handle, revision, &error_code); + SERVICES_get_se_revision(se_sess_handle, revision, &error_code); SERVICES_version_data_t data; - SERVICES_system_get_device_data(se_services_handle, &data, &error_code); + SERVICES_system_get_device_data(se_sess_handle, &data, &error_code); printf("SE revision: %s\n", revision); printf("ALIF_PN: %s\n", data.ALIF_PN); @@ -141,11 +190,11 @@ void se_services_dump_device_data(void) { void se_services_get_unique_id(uint8_t id[8]) { uint32_t error_code; - SERVICES_system_get_eui_extension(se_services_handle, false, id, &error_code); + SERVICES_system_get_eui_extension(se_sess_handle, false, id, &error_code); } __attribute__((noreturn)) void se_services_reset_soc(void) { - SERVICES_boot_reset_soc(se_services_handle); + SERVICES_boot_reset_soc(se_sess_handle); NVIC_SystemReset(); } @@ -155,7 +204,7 @@ uint64_t se_services_rand64(void) { for (int retry = 0; retry < 100; ++retry) { uint64_t value; int32_t error_code; - uint32_t ret = SERVICES_cryptocell_get_rnd(se_services_handle, sizeof(uint64_t), &value, &error_code); + uint32_t ret = SERVICES_cryptocell_get_rnd(se_sess_handle, sizeof(uint64_t), &value, &error_code); if (ret == SERVICES_REQ_SUCCESS) { return value; } @@ -165,51 +214,59 @@ uint64_t se_services_rand64(void) { return 0; } +uint32_t se_services_notify(void) { + uint32_t ret = SERVICES_send_msg(se_rtss_handle, LocalToGlobal(0)); + if (ret != SERVICES_REQ_SUCCESS) { + return -1; + } + return 0; +} + uint32_t se_services_enable_clock(clock_enable_t clock, bool enable) { uint32_t error_code; - SERVICES_clocks_enable_clock(se_services_handle, clock, enable, &error_code); + SERVICES_clocks_enable_clock(se_sess_handle, clock, enable, &error_code); return error_code; } uint32_t se_services_select_pll_source(pll_source_t source, pll_target_t target) { uint32_t error_code; - SERVICES_clocks_select_pll_source(se_services_handle, source, target, &error_code); + SERVICES_clocks_select_pll_source(se_sess_handle, source, target, &error_code); return error_code; } uint32_t se_services_get_run_profile(run_profile_t *profile) { uint32_t error_code; - SERVICES_get_run_cfg(se_services_handle, profile, &error_code); + SERVICES_get_run_cfg(se_sess_handle, profile, &error_code); return error_code; } uint32_t se_services_set_run_profile(run_profile_t *profile) { uint32_t error_code; - SERVICES_set_run_cfg(se_services_handle, profile, &error_code); + SERVICES_set_run_cfg(se_sess_handle, profile, &error_code); return error_code; } uint32_t se_services_get_off_profile(off_profile_t *profile) { uint32_t error_code; - SERVICES_get_off_cfg(se_services_handle, profile, &error_code); + SERVICES_get_off_cfg(se_sess_handle, profile, &error_code); return error_code; } uint32_t se_services_set_off_profile(off_profile_t *profile) { uint32_t error_code; - SERVICES_set_off_cfg(se_services_handle, profile, &error_code); + SERVICES_set_off_cfg(se_sess_handle, profile, &error_code); return error_code; } uint32_t se_services_boot_process_toc_entry(const uint8_t *image_id) { uint32_t error_code; - SERVICES_boot_process_toc_entry(se_services_handle, image_id, &error_code); + SERVICES_boot_process_toc_entry(se_sess_handle, image_id, &error_code); return error_code; } uint32_t se_services_boot_cpu(uint32_t cpu_id, uint32_t address) { uint32_t error_code; - SERVICES_boot_cpu(se_services_handle, cpu_id, address, &error_code); + SERVICES_boot_cpu(se_sess_handle, cpu_id, address, &error_code); return error_code; } @@ -221,7 +278,7 @@ uint32_t se_services_boot_reset_cpu(uint32_t cpu_id) { } for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(1)) { - uint32_t ret = SERVICES_boot_reset_cpu(se_services_handle, cpu_id, &error_code); + uint32_t ret = SERVICES_boot_reset_cpu(se_sess_handle, cpu_id, &error_code); if (ret != SERVICES_REQ_SUCCESS) { return error_code; } @@ -234,11 +291,12 @@ uint32_t se_services_boot_reset_cpu(uint32_t cpu_id) { return SERVICES_REQ_TIMEOUT; } } + return error_code; } uint32_t se_services_boot_release_cpu(uint32_t cpu_id) { uint32_t error_code; - SERVICES_boot_release_cpu(se_services_handle, cpu_id, &error_code); + SERVICES_boot_release_cpu(se_sess_handle, cpu_id, &error_code); return error_code; } diff --git a/ports/alif/se_services.h b/ports/alif/se_services.h index 87deb05592990..44b6584a20000 100644 --- a/ports/alif/se_services.h +++ b/ports/alif/se_services.h @@ -29,12 +29,16 @@ #include "services_lib_api.h" void se_services_init(void); +void se_services_deinit(void); void se_services_dump_device_data(void); void se_services_get_unique_id(uint8_t id[8]); __attribute__((noreturn)) void se_services_reset_soc(void); uint64_t se_services_rand64(void); +uint32_t se_services_notify(void); + uint32_t se_services_enable_clock(clock_enable_t clock, bool enable); uint32_t se_services_select_pll_source(pll_source_t source, pll_target_t target); + uint32_t se_services_get_run_profile(run_profile_t *profile); uint32_t se_services_set_run_profile(run_profile_t *profile); uint32_t se_services_get_off_profile(off_profile_t *profile); From 182b5f3a12427d3dc21744b0164d546663ea696b Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 15 Feb 2025 14:00:42 +0100 Subject: [PATCH 50/77] alif/mpmetalport: Use MHU to notify remote cores. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlike HWSEM, the MHU IRQ can wake up cores from low-power modes, making it better suited for notifying remote cores. Note that no special function is required to wake up a remote core—the act of sending a message alone will notify it. Signed-off-by: iabdalkader --- ports/alif/mpmetalport.c | 31 +++++++------------------------ ports/alif/mpmetalport.h | 14 -------------- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/ports/alif/mpmetalport.c b/ports/alif/mpmetalport.c index a7d4025468a16..b0a017fdd797a 100644 --- a/ports/alif/mpmetalport.c +++ b/ports/alif/mpmetalport.c @@ -27,8 +27,6 @@ */ #include ALIF_CMSIS_H -#include "hwsem.h" - #include "py/mperrno.h" #include "py/mphal.h" @@ -36,18 +34,14 @@ #include "metal/utilities.h" #include "metal/device.h" +#include "se_services.h" + struct metal_state _metal; static mp_sched_node_t rproc_notify_node; int metal_sys_init(const struct metal_init_params *params) { metal_unused(params); - // Reset the hardware semaphore. - hwsem_reset(METAL_HSEM_DEVICE); - #if MICROPY_PY_OPENAMP_HOST - hwsem_reset(METAL_HSEM_REMOTE); - #endif - // If cache management is not enabled, configure the MPU to disable // caching for the entire Open-AMP shared memory region. #ifndef VIRTIO_USE_DCACHE @@ -59,18 +53,10 @@ int metal_sys_init(const struct metal_init_params *params) { #endif metal_bus_register(&metal_generic_bus); - - // Enable the hardware semaphore IRQ. - NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); - NVIC_SetPriority(METAL_HSEM_IRQn, IRQ_PRI_HWSEM); - NVIC_EnableIRQ(METAL_HSEM_IRQn); return 0; } void metal_sys_finish(void) { - NVIC_DisableIRQ(METAL_HSEM_IRQn); - NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); - hwsem_reset(METAL_HSEM_DEVICE); metal_bus_unregister(&metal_generic_bus); } @@ -99,15 +85,12 @@ void metal_machine_cache_invalidate(void *addr, unsigned int len) { } int metal_rproc_notify(void *priv, uint32_t id) { - // Release the HW semaphore to notify the other core. - hwsem_release(METAL_HSEM_REMOTE, HWSEM_MASTERID); + // Notify the remote core. + se_services_notify(); return 0; } -void METAL_HSEM_IRQ_HANDLER(void) { - // Schedule the node only if the other core released the Semaphore. - if (METAL_HSEM_DEVICE->HWSEM_REL_REG == 0) { - mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified); - } - hwsem_request(METAL_HSEM_DEVICE, METAL_HSEM_REMOTE_ID); +void metal_rproc_notified(void) { + // The remote core notified this core. + mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified); } diff --git a/ports/alif/mpmetalport.h b/ports/alif/mpmetalport.h index 7a428dbc6c62a..c11725a1f1e45 100644 --- a/ports/alif/mpmetalport.h +++ b/ports/alif/mpmetalport.h @@ -38,20 +38,6 @@ #define METAL_MAX_DEVICE_REGIONS 2 -#if MICROPY_PY_OPENAMP_HOST -#define METAL_HSEM_DEVICE ((HWSEM_Type *)HWSEM14_BASE) -#define METAL_HSEM_REMOTE ((HWSEM_Type *)HWSEM15_BASE) -#define METAL_HSEM_REMOTE_ID (0x410FD222U) -#define METAL_HSEM_IRQn HWSEM_IRQ14_IRQn -#define METAL_HSEM_IRQ_HANDLER HWSEM_IRQ14Handler -#else -#define METAL_HSEM_DEVICE ((HWSEM_Type *)HWSEM15_BASE) -#define METAL_HSEM_REMOTE ((HWSEM_Type *)HWSEM14_BASE) -#define METAL_HSEM_REMOTE_ID (0x410FD221U) -#define METAL_HSEM_IRQn HWSEM_IRQ15_IRQn -#define METAL_HSEM_IRQ_HANDLER HWSEM_IRQ15Handler -#endif - // Set to 1 to enable log output. #define METAL_LOG_HANDLER_ENABLE 0 From 68b1dae011dd9f2696e9f6f2f914317d827c879d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 19 Feb 2025 14:42:05 +0100 Subject: [PATCH 51/77] alif: Link with libnosys. This allows the correct start up functions to be called by the stdlib. Signed-off-by: iabdalkader --- ports/alif/alif.mk | 22 +++++++++---------- ports/alif/main.c | 2 +- ports/alif/nosys_stubs.c | 46 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 ports/alif/nosys_stubs.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index aa2451d7a6291..7e58aa5729f39 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -68,9 +68,9 @@ CFLAGS += $(INC) \ -mtune=cortex-m55 \ $(CFLAGS_FPU) \ -march=armv8.1-m.main+fp+mve.fp \ - -nostdlib \ -fdata-sections \ -ffunction-sections \ + --specs=nosys.specs \ -D$(MCU_CORE)=1 \ -DCORE_$(MCU_CORE) \ -DALIF_CMSIS_H="\"$(MCU_CORE).h\"" @@ -95,18 +95,17 @@ CFLAGS += $(CFLAGS_EXTRA) AFLAGS = -mthumb -march=armv8.1-m.main+fp+mve.fp $(CFLAGS_FPU) -LDFLAGS += -nostdlib \ - -T$(BUILD)/ensemble.ld \ - -Map=$@.map \ - --cref \ - --gc-sections \ - --print-memory-usage +CFLAGS += -Wl,-T$(BUILD)/ensemble.ld \ + -Wl,-Map=$@.map \ + -Wl,--cref \ + -Wl,--gc-sections \ + -Wl,--print-memory-usage \ + -Wl,--no-warn-rwx-segment + ifeq ($(MCU_CORE),M55_HP) -LDFLAGS += --wrap=dcd_event_handler +CFLAGS += -Wl,--wrap=dcd_event_handler endif -LIBS += "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)" - ################################################################################ # Source files and libraries @@ -126,6 +125,7 @@ SRC_C = \ mpu.c \ mpuart.c \ msc_disk.c \ + nosys_stubs.c \ ospi_ext.c \ ospi_flash.c \ pendsv.c \ @@ -256,7 +256,7 @@ $(BUILD)/ensemble.ld: $(LD_FILE) $(BUILD)/firmware.elf: $(OBJ) $(BUILD)/ensemble.ld $(ECHO) "Link $@" - $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + $(Q)$(CC) $(CFLAGS) -o $@ $(OBJ) $(LIBS) $(Q)$(SIZE) $@ $(BUILD)/firmware.bin: $(BUILD)/firmware.elf diff --git a/ports/alif/main.c b/ports/alif/main.c index 975ee7ed2d328..793425fff2e74 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -55,7 +55,7 @@ NORETURN void panic(const char *msg) { } } -void _start(void) { +int main(void) { system_tick_init(); MICROPY_BOARD_STARTUP(); diff --git a/ports/alif/nosys_stubs.c b/ports/alif/nosys_stubs.c new file mode 100644 index 0000000000000..a394ec8f1cf66 --- /dev/null +++ b/ports/alif/nosys_stubs.c @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 + +int _write(int handle, char *buffer, int size) { + errno = ENOSYS; + return -1; +} + +int _read(int handle, char *buffer, int size) { + errno = ENOSYS; + return -1; +} + +int _close(int f) { + errno = ENOSYS; + return -1; +} + +int _lseek(int f, int ptr, int dir) { + errno = ENOSYS; + return -1; +} From 7e32c232183441e1022f59e58c31ba431c25fe2f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Mar 2025 23:29:58 +1100 Subject: [PATCH 52/77] alif/mpmetalport: Only notify after metal subsystem is init'd. Signed-off-by: Damien George --- ports/alif/mpmetalport.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/alif/mpmetalport.c b/ports/alif/mpmetalport.c index b0a017fdd797a..9d774cb906766 100644 --- a/ports/alif/mpmetalport.c +++ b/ports/alif/mpmetalport.c @@ -37,6 +37,8 @@ #include "se_services.h" struct metal_state _metal; + +static bool metal_active; static mp_sched_node_t rproc_notify_node; int metal_sys_init(const struct metal_init_params *params) { @@ -53,10 +55,13 @@ int metal_sys_init(const struct metal_init_params *params) { #endif metal_bus_register(&metal_generic_bus); + metal_active = true; + return 0; } void metal_sys_finish(void) { + metal_active = false; metal_bus_unregister(&metal_generic_bus); } @@ -91,6 +96,9 @@ int metal_rproc_notify(void *priv, uint32_t id) { } void metal_rproc_notified(void) { + if (!metal_active) { + return; + } // The remote core notified this core. mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified); } From ca3d50a096ffa999e47b98335ceb0b7f459196c2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Mar 2025 23:31:58 +1100 Subject: [PATCH 53/77] alif/mpuart: Use mp_hal_pin_config for TX/RX configuration. Signed-off-by: Damien George --- ports/alif/mpuart.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c index e9cfcf34fed53..69734659a4101 100644 --- a/ports/alif/mpuart.c +++ b/ports/alif/mpuart.c @@ -35,10 +35,8 @@ #include "sys_ctrl_uart.h" #include "uart.h" -#define TX_PORT PORT_12 -#define TX_PIN PIN_2 -#define RX_PORT PORT_12 -#define RX_PIN PIN_1 +#define TX_PIN pin_P12_2 +#define RX_PIN pin_P12_1 #define UART_ID 4 #define UART_IRQN UART4_IRQ_IRQn #define UART_PTR ((UART_Type *)UART4_BASE) @@ -48,8 +46,8 @@ static UART_TRANSFER transfer; void mp_uart_init(void) { - pinconf_set(TX_PORT, TX_PIN, PINMUX_ALTERNATE_FUNCTION_2, 0); - pinconf_set(RX_PORT, RX_PIN, PINMUX_ALTERNATE_FUNCTION_2, PADCTRL_READ_ENABLE); + mp_hal_pin_config(TX_PIN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_UART, false); + mp_hal_pin_config(RX_PIN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_UART, true); select_uart_clock_syst_pclk(UART_ID); enable_uart_clock(UART_ID); uart_software_reset(UART_PTR); From af574a86c2ec3af02b167d449b8a8718f39f1ca8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 24 Dec 2024 00:18:56 +1100 Subject: [PATCH 54/77] alif/alif_flash: Distinguish between total flash size and FS size. Signed-off-by: Damien George --- ports/alif/alif_flash.c | 8 ++++---- ports/alif/msc_disk.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/alif/alif_flash.c b/ports/alif/alif_flash.c index f2ea5ac85a88e..de7b37804f955 100644 --- a/ports/alif/alif_flash.c +++ b/ports/alif/alif_flash.c @@ -37,10 +37,10 @@ typedef struct _alif_flash_obj_t { uint32_t flash_size; } alif_flash_obj_t; -static alif_flash_obj_t alif_flash_obj = { +static const alif_flash_obj_t alif_flash_fs_obj = { .base = { &alif_flash_type }, .flash_base_addr = MICROPY_HW_FLASH_STORAGE_BASE_ADDR, - .flash_size = MICROPY_HW_FLASH_STORAGE_BYTES, + .flash_size = MICROPY_HW_FLASH_STORAGE_FS_BYTES, }; static mp_obj_t alif_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { @@ -54,8 +54,8 @@ static mp_obj_t alif_flash_make_new(const mp_obj_type_t *type, size_t n_args, si mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if (args[ARG_start].u_int == -1 && args[ARG_len].u_int == -1) { - // Default singleton object that accesses entire flash - return MP_OBJ_FROM_PTR(&alif_flash_obj); + // Default singleton object that accesses writable-filesystem flash + return MP_OBJ_FROM_PTR(&alif_flash_fs_obj); } alif_flash_obj_t *self = mp_obj_malloc(alif_flash_obj_t, &alif_flash_type); diff --git a/ports/alif/msc_disk.c b/ports/alif/msc_disk.c index a01494d20f0e4..0b8ddddeda248 100644 --- a/ports/alif/msc_disk.c +++ b/ports/alif/msc_disk.c @@ -37,7 +37,7 @@ #endif #define BLOCK_SIZE (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES) -#define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_BYTES / BLOCK_SIZE) +#define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_FS_BYTES / BLOCK_SIZE) #define FLASH_BASE_ADDR (MICROPY_HW_FLASH_STORAGE_BASE_ADDR) static bool ejected = false; From d895a62b0703d28fb2b15427760aae8a67ad666f Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 24 Dec 2024 00:19:20 +1100 Subject: [PATCH 55/77] alif/alif_flash: Make flash respond to the buffer protocol. Signed-off-by: Damien George --- ports/alif/alif_flash.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ports/alif/alif_flash.c b/ports/alif/alif_flash.c index de7b37804f955..722afadae1f7b 100644 --- a/ports/alif/alif_flash.c +++ b/ports/alif/alif_flash.c @@ -80,6 +80,19 @@ static mp_obj_t alif_flash_make_new(const mp_obj_type_t *type, size_t n_args, si return MP_OBJ_FROM_PTR(self); } +static mp_int_t alif_flash_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + alif_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)(ospi_flash_get_xip_base() + self->flash_base_addr); + bufinfo->len = self->flash_size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Can't return a writable buffer. + return 1; + } +} + static mp_obj_t alif_flash_readblocks(size_t n_args, const mp_obj_t *args) { alif_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); uint32_t offset = mp_obj_get_int(args[1]) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; @@ -157,6 +170,7 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_Flash, MP_TYPE_FLAG_NONE, make_new, alif_flash_make_new, + buffer, alif_flash_get_buffer, locals_dict, &alif_flash_locals_dict ); #endif // MICROPY_HW_ENABLE_OSPI From f83f6e7eed39f961ff9788814d3ee9104d3577a6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Mar 2025 16:30:15 +1100 Subject: [PATCH 56/77] alif/mpu: Add function to set read-only bit on MRAM MPU region. To allow writing to MRAM region. Signed-off-by: Damien George --- ports/alif/mpu.c | 11 +++++++++++ ports/alif/mpu.h | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/ports/alif/mpu.c b/ports/alif/mpu.c index 471eff20a2c5b..60753674ae1e5 100644 --- a/ports/alif/mpu.c +++ b/ports/alif/mpu.c @@ -25,6 +25,7 @@ */ #include "py/mpconfig.h" +#include "irq.h" #include "mpu.h" #include ALIF_CMSIS_H @@ -76,3 +77,13 @@ void MPU_Load_Regions(void) { // Load the MPU regions from the table. ARM_MPU_Load(0, mpu_table, sizeof(mpu_table) / sizeof(ARM_MPU_Region_t)); } + +void mpu_config_mram(bool read_only) { + uintptr_t atomic = disable_irq(); + ARM_MPU_Disable(); + MPU->RNR = MP_MPU_REGION_MRAM; + MPU->RBAR = ARM_MPU_RBAR(MRAM_BASE, ARM_MPU_SH_NON, read_only, 1, 0); + MPU->RLAR = ARM_MPU_RLAR(MRAM_BASE + MRAM_SIZE - 1, MP_MPU_ATTR_NORMAL_WT_RA); + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); + enable_irq(atomic); +} diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h index 88fbe011206ed..1d3602941efb7 100644 --- a/ports/alif/mpu.h +++ b/ports/alif/mpu.h @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include + #define MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT (0) #define MP_MPU_ATTR_DEVICE_nGnRE (1) #define MP_MPU_ATTR_NORMAL_WB_RA_WA (2) @@ -37,3 +39,5 @@ #define MP_MPU_REGION_OSPI_REGISTERS (4) #define MP_MPU_REGION_OSPI0_XIP (5) #define MP_MPU_REGION_OPENAMP (6) + +void mpu_config_mram(bool read_only); From 8297c95c22fd15aa706243bfebb0688a61827855 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Mar 2025 14:51:45 +1100 Subject: [PATCH 57/77] alif/vfs_rom_ioctl: Add vfs_rom_ioctl with support for OSPI and MRAM. Signed-off-by: Damien George --- ports/alif/alif.mk | 3 + ports/alif/mpconfigport.h | 1 + ports/alif/vfs_rom_ioctl.c | 175 +++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 ports/alif/vfs_rom_ioctl.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 7e58aa5729f39..f0ff8d91a53d9 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -132,6 +132,7 @@ SRC_C = \ system_tick.c \ se_services.c \ usbd.c \ + vfs_rom_ioctl.c \ $(wildcard $(BOARD_DIR)/*.c) ifeq ($(MICROPY_FLOAT_IMPL),float) @@ -194,6 +195,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ + drivers/source/mram.c \ drivers/source/pinconf.c \ drivers/source/uart.c \ drivers/source/utimer.c \ @@ -209,6 +211,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ ) $(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 +$(BUILD)/$(ALIF_DFP_REL_TOP)/drivers/source/mram.o: CFLAGS += -Wno-strict-aliasing $(BUILD)/$(ALIF_DFP_REL_TOP)/drivers/source/spi.o: CFLAGS += -Wno-maybe-uninitialized $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 83f808b131f48..33f300641fbba 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -137,6 +137,7 @@ #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) #define MICROPY_VFS (1) +#define MICROPY_VFS_ROM (1) // fatfs configuration #define MICROPY_FATFS_ENABLE_LFN (1) diff --git a/ports/alif/vfs_rom_ioctl.c b/ports/alif/vfs_rom_ioctl.c new file mode 100644 index 0000000000000..d82ac6f8417e2 --- /dev/null +++ b/ports/alif/vfs_rom_ioctl.c @@ -0,0 +1,175 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 OpenMV LLC. + * + * 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/mperrno.h" +#include "py/objarray.h" +#include "py/runtime.h" +#include "extmod/vfs.h" +#include "modalif.h" +#include "mpu.h" +#include "mram.h" +#include "ospi_flash.h" + +#if MICROPY_VFS_ROM_IOCTL + +#if MICROPY_HW_ROMFS_ENABLE_PART0 && !defined(MICROPY_HW_ROMFS_PART0_START) +#define MICROPY_HW_ROMFS_PART0_START (uintptr_t)(&_micropy_hw_romfs_part0_start) +#define MICROPY_HW_ROMFS_PART0_SIZE (uintptr_t)(&_micropy_hw_romfs_part0_size) +extern uint8_t _micropy_hw_romfs_part0_start; +extern uint8_t _micropy_hw_romfs_part0_size; +#endif + +#if MICROPY_HW_ROMFS_ENABLE_PART1 && !defined(MICROPY_HW_ROMFS_PART1_START) +#define MICROPY_HW_ROMFS_PART1_START (uintptr_t)(&_micropy_hw_romfs_part1_start) +#define MICROPY_HW_ROMFS_PART1_SIZE (uintptr_t)(&_micropy_hw_romfs_part1_size) +extern uint8_t _micropy_hw_romfs_part1_start; +extern uint8_t _micropy_hw_romfs_part1_size; +#endif + +#define ROMFS_MEMORYVIEW(base, size) {{&mp_type_memoryview}, 'B', 0, (size), (void *)(base)} + +static const mp_obj_array_t romfs_obj_table[] = { + #if MICROPY_HW_ROMFS_ENABLE_PART0 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART0_START, MICROPY_HW_ROMFS_PART0_SIZE), + #endif + #if MICROPY_HW_ROMFS_ENABLE_PART1 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART1_START, MICROPY_HW_ROMFS_PART1_SIZE), + #endif +}; + +static inline bool mram_is_valid_addr(uintptr_t addr) { + return MRAM_BASE <= addr && addr < MRAM_BASE + MRAM_SIZE; +} + +static inline bool ospi_is_valid_addr(uintptr_t xip_base, uintptr_t addr) { + MP_STATIC_ASSERT(OSPI0_XIP_SIZE == OSPI1_XIP_SIZE); + return xip_base <= addr && addr < xip_base + OSPI0_XIP_SIZE; +} + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + mp_int_t cmd = mp_obj_get_int(args[0]); + if (cmd == MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS) { + return MP_OBJ_NEW_SMALL_INT(MP_ARRAY_SIZE(romfs_obj_table)); + } + + if (n_args < 2) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + mp_int_t romfs_id = mp_obj_get_int(args[1]); + if (!(0 <= romfs_id && romfs_id < MP_ARRAY_SIZE(romfs_obj_table))) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + const mp_obj_array_t *romfs_obj = &romfs_obj_table[romfs_id]; + + if (cmd == MP_VFS_ROM_IOCTL_GET_SEGMENT) { + // Return the ROMFS memoryview object. + return MP_OBJ_FROM_PTR(romfs_obj); + } + + uintptr_t romfs_base = (uintptr_t)romfs_obj->items; + uintptr_t romfs_len = romfs_obj->len; + + #if MICROPY_HW_ENABLE_OSPI + const uintptr_t ospi_base = ospi_flash_get_xip_base(); + #endif + + if (cmd == MP_VFS_ROM_IOCTL_WRITE_PREPARE) { + if (n_args < 3) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = romfs_base; + uint32_t dest_max = dest + mp_obj_get_int(args[2]); + if (dest_max > romfs_base + romfs_len) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + if (mram_is_valid_addr(dest)) { + // No preparation needed for MRAM. + // Return the minimum write size. + return MP_OBJ_NEW_SMALL_INT(MRAM_SECTOR_SIZE); + } + + #if MICROPY_HW_ENABLE_OSPI + if (ospi_is_valid_addr(ospi_base, dest)) { + // Erase OSPI flash. + dest -= ospi_base; + dest_max -= ospi_base; + while (dest < dest_max) { + int ret = ospi_flash_erase_sector(dest); + mp_event_handle_nowait(); + if (ret < 0) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + dest += MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + } + // Return the minimum write size. + return MP_OBJ_NEW_SMALL_INT(4); + } + #endif + } + + if (cmd == MP_VFS_ROM_IOCTL_WRITE) { + if (n_args < 4) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = romfs_base + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + if (dest + bufinfo.len > romfs_base + romfs_len) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + if (mram_is_valid_addr(dest)) { + // Write data to MRAM. + mpu_config_mram(false); + const uint8_t *src = bufinfo.buf; + const uint8_t *max = src + bufinfo.len; + while (src < max) { + mram_write_128bit((uint8_t *)dest, src); + dest += MRAM_SECTOR_SIZE; + src += MRAM_SECTOR_SIZE; + } + mpu_config_mram(true); + return MP_OBJ_NEW_SMALL_INT(0); // success + } + + #if MICROPY_HW_ENABLE_OSPI + if (ospi_is_valid_addr(ospi_base, dest)) { + // Write data to OSPI flash. + dest -= ospi_base; + int ret = ospi_flash_write(dest, bufinfo.len, bufinfo.buf); + mp_event_handle_nowait(); + return MP_OBJ_NEW_SMALL_INT(ret); + } + #endif + } + + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); +} + +#endif // MICROPY_VFS_ROM_IOCTL From d1b12cb6766d8b48d0e8cdf551808c324d0599c1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 8 Mar 2025 23:19:47 +1100 Subject: [PATCH 58/77] alif/modules: Make HE core set /rom as current dir. This allows HE to execute code from the ROMFS in MRAM. Signed-off-by: Damien George --- ports/alif/modules/he/_boot.py | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/ports/alif/modules/he/_boot.py b/ports/alif/modules/he/_boot.py index bc1320f190694..3df1804709073 100644 --- a/ports/alif/modules/he/_boot.py +++ b/ports/alif/modules/he/_boot.py @@ -1,21 +1,7 @@ -import openamp -import time -from machine import Pin +# Change working directory to ROMFS, so boot.py and main.py can run from there. +try: + import os - -def ept_recv_callback(src_addr, data): - print("Received message on endpoint", data) - - -# Create a new RPMsg endpoint to communicate with main core. -ept = openamp.Endpoint("vuart-channel", callback=ept_recv_callback) - -pin = Pin("LED_BLUE", Pin.OUT) - -count = 0 -while True: - if ept.is_ready(): - ept.send("Hello from HE %d" % count, timeout=1000) - count += 1 - time.sleep_ms(100) - pin(not pin()) + os.chdir("/rom") +except: + pass From 4f2a8bd99f81f88fd173aa717523345ba32083e0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 23 Oct 2024 16:25:22 +1100 Subject: [PATCH 59/77] alif/mphalport: Add mp_hal_pin_config_irq_falling helper. Signed-off-by: Damien George --- ports/alif/mphalport.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 3b23cb6a2c514..5f1e3fff8296e 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -155,6 +155,16 @@ static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) { gpio_set_direction_output(pin->gpio, pin->pin); } +static inline void mp_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) { + if (enable) { + gpio_enable_interrupt(pin->gpio, pin->pin); + gpio_interrupt_set_edge_trigger(pin->gpio, pin->pin); + gpio_interrupt_set_polarity_low(pin->gpio, pin->pin); + } else { + gpio_disable_interrupt(pin->gpio, pin->pin); + } +} + static inline void mp_hal_pin_low(mp_hal_pin_obj_t pin) { gpio_set_value_low(pin->gpio, pin->pin); } From 411146b0ed0fa2d70fc13264b1bd2cda9045c99e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 14 Oct 2024 21:48:51 +1100 Subject: [PATCH 60/77] alif/mpuart: Generalise UART driver to suppot all UART instances. Signed-off-by: Damien George --- ports/alif/main.c | 2 +- ports/alif/mpconfigport.h | 2 +- ports/alif/mphalport.c | 2 +- ports/alif/mpuart.c | 294 ++++++++++++++++++++++++++++++-------- ports/alif/mpuart.h | 17 ++- 5 files changed, 251 insertions(+), 66 deletions(-) diff --git a/ports/alif/main.c b/ports/alif/main.c index 793425fff2e74..77e413e1eb492 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -66,7 +66,7 @@ int main(void) { MICROPY_BOARD_EARLY_INIT(); #if MICROPY_HW_ENABLE_UART_REPL - mp_uart_init(); + mp_uart_init_repl(); #endif #if MICROPY_HW_ENABLE_OSPI if (ospi_flash_init() != 0) { diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 33f300641fbba..a5fd4f6dbfa87 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -73,7 +73,7 @@ #define MICROPY_HW_USB_PID (0x9802) // interface has CDC only #endif #ifndef MICROPY_HW_ENABLE_UART_REPL -#define MICROPY_HW_ENABLE_UART_REPL (CORE_M55_HP) // useful if there is no USB +#define MICROPY_HW_ENABLE_UART_REPL (0) #endif #define MICROPY_HW_FLASH_BLOCK_SIZE_BYTES (4096) diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 1a6136c31abeb..8f7780bb8acdf 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -91,7 +91,7 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { #endif #if MICROPY_HW_ENABLE_UART_REPL - mp_uart_write_strn(str, len); + mp_uart_write_strn_repl(str, len); did_write = true; #endif diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c index 69734659a4101..7f14ff5444702 100644 --- a/ports/alif/mpuart.c +++ b/ports/alif/mpuart.c @@ -24,88 +24,260 @@ * THE SOFTWARE. */ -#include -#include "py/runtime.h" #include "py/mphal.h" +#include "py/runtime.h" #include "mpuart.h" -#if MICROPY_HW_ENABLE_UART_REPL - -#include "pinconf.h" #include "sys_ctrl_uart.h" #include "uart.h" -#define TX_PIN pin_P12_2 -#define RX_PIN pin_P12_1 -#define UART_ID 4 -#define UART_IRQN UART4_IRQ_IRQn -#define UART_PTR ((UART_Type *)UART4_BASE) -#define BAUDRATE 115200 -#define SYST_PCLK 100000000 - -static UART_TRANSFER transfer; - -void mp_uart_init(void) { - mp_hal_pin_config(TX_PIN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_UART, false); - mp_hal_pin_config(RX_PIN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_UART, true); - select_uart_clock_syst_pclk(UART_ID); - enable_uart_clock(UART_ID); - uart_software_reset(UART_PTR); - uart_enable_fifo(UART_PTR); - uart_disable_tx_irq(UART_PTR); - uart_disable_rx_irq(UART_PTR); - uart_set_baudrate(UART_PTR, SYST_PCLK, BAUDRATE); - uart_set_data_parity_stop_bits(UART_PTR, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1); - uart_set_flow_control(UART_PTR, UART_FLOW_CONTROL_NONE); - NVIC_ClearPendingIRQ(UART_IRQN); - NVIC_SetPriority(UART_IRQN, IRQ_PRI_UART_REPL); - NVIC_EnableIRQ(UART_IRQN); - uart_set_tx_trigger(UART_PTR, UART_TX_FIFO_EMPTY); - uart_set_rx_trigger(UART_PTR, UART_RX_ONE_CHAR_IN_FIFO); - uart_enable_rx_irq(UART_PTR); +#define UART_MAX (8) +#define SYST_PCLK (100000000) + +typedef struct _uart_state_t { + UART_TRANSFER_STATUS status; + ringbuf_t *rx_ringbuf; + const uint8_t *tx_src; + const uint8_t *tx_src_max; + void (*irq_callback)(void); +} uart_state_t; + +static const uint8_t uart_irqn[UART_MAX] = { + UART0_IRQ_IRQn, + UART1_IRQ_IRQn, + UART2_IRQ_IRQn, + UART3_IRQ_IRQn, + UART4_IRQ_IRQn, + UART5_IRQ_IRQn, + UART6_IRQ_IRQn, + UART7_IRQ_IRQn, +}; + +static UART_Type *const uart_periph[UART_MAX] = { + (UART_Type *)UART0_BASE, + (UART_Type *)UART1_BASE, + (UART_Type *)UART2_BASE, + (UART_Type *)UART3_BASE, + (UART_Type *)UART4_BASE, + (UART_Type *)UART5_BASE, + (UART_Type *)UART6_BASE, + (UART_Type *)UART7_BASE, +}; + +static uart_state_t uart_state[UART_MAX]; + +void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf) { + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + + // Configure TX/RX pins. + mp_hal_pin_config(tx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, false); + mp_hal_pin_config(rx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, true); + + // Configure the UART peripheral. + select_uart_clock_syst_pclk(uart_id); + enable_uart_clock(uart_id); + uart_software_reset(uart); + uart_enable_fifo(uart); + uart_disable_tx_irq(uart); + uart_disable_rx_irq(uart); + uart_set_baudrate(uart, SYST_PCLK, baudrate); + uart_set_data_parity_stop_bits(uart, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1); + uart_set_flow_control(uart, UART_FLOW_CONTROL_NONE); + uart->UART_FCR |= UART_FCR_RCVR_FIFO_RESET; + uart_set_tx_trigger(uart, UART_TX_FIFO_EMPTY); + uart_set_rx_trigger(uart, UART_RX_ONE_CHAR_IN_FIFO); + + // Initialise the state. + state->status = UART_TRANSFER_STATUS_NONE; + state->rx_ringbuf = rx_ringbuf; + state->tx_src = NULL; + state->tx_src_max = NULL; + state->irq_callback = NULL; + + // Enable interrupts. + NVIC_ClearPendingIRQ(uart_irqn[uart_id]); + NVIC_SetPriority(uart_irqn[uart_id], IRQ_PRI_UART_REPL); + NVIC_EnableIRQ(uart_irqn[uart_id]); + uart_enable_rx_irq(uart); +} + +void mp_uart_deinit(unsigned int uart_id) { + UART_Type *uart = uart_periph[uart_id]; + + uart_disable_rx_irq(uart); + NVIC_DisableIRQ(uart_irqn[uart_id]); +} + +void mp_uart_set_irq_callback(unsigned int uart_id, void (*callback)(void)) { + uart_state_t *state = &uart_state[uart_id]; + state->irq_callback = callback; } -void mp_uart_write_strn(const char *str, size_t len) { - memset(&transfer, 0, sizeof(transfer)); - transfer.tx_buf = (uint8_t *)str; - transfer.tx_total_num = len; - transfer.tx_curr_cnt = 0U; - transfer.status = UART_TRANSFER_STATUS_NONE; +void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts) { + UART_Type *uart = uart_periph[uart_id]; - uart_enable_tx_irq(UART_PTR); + unsigned int flow = UART_FLOW_CONTROL_NONE; + if (rts != NULL) { + flow |= UART_FLOW_CONTROL_RTS; + mp_hal_pin_config(rts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, false); + } + if (cts != NULL) { + flow |= UART_FLOW_CONTROL_CTS; + mp_hal_pin_config(cts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, true); + } + uart_set_flow_control(uart, flow); +} + +void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate) { + UART_Type *uart = uart_periph[uart_id]; + + uart_set_baudrate(uart, SYST_PCLK, baudrate); +} + +size_t mp_uart_rx_any(unsigned int uart_id) { + uart_state_t *state = &uart_state[uart_id]; + if (state->rx_ringbuf != NULL) { + return ringbuf_avail(state->rx_ringbuf); + } + return 0; +} +int mp_uart_rx_char(unsigned int uart_id) { + uart_state_t *state = &uart_state[uart_id]; + if (state->rx_ringbuf != NULL && ringbuf_avail(state->rx_ringbuf)) { + return ringbuf_get(state->rx_ringbuf); + } + return -1; +} + +void mp_uart_tx_data_blocking(unsigned int uart_id, const uint8_t *src, size_t len) { + UART_Type *uart = uart_periph[uart_id]; + + for (size_t i = 0; i < len; ++i) { + for (;;) { + size_t tx_avail = UART_FIFO_DEPTH - uart->UART_TFL; + if (tx_avail > 0) { + break; + } + mp_event_handle_nowait(); + } + uart->UART_THR = src[i]; + } +} + +void mp_uart_tx_data(unsigned int uart_id, const uint8_t *src, size_t len) { + UART_Type *uart = uart_periph[uart_id]; + + // Configure transmission, to be handled by IRQ. + uart_state_t *state = &uart_state[uart_id]; + state->status = UART_TRANSFER_STATUS_NONE; + state->tx_src = src; + state->tx_src_max = src + len; + + // Enable TX IRQ to start transmission. + uart_enable_tx_irq(uart); + + // Wait for transfer to complete. + const uint32_t total_timeout_ms = 100 * len; uint32_t start = mp_hal_ticks_ms(); - while (transfer.status == UART_TRANSFER_STATUS_NONE) { - if (mp_hal_ticks_ms() - start > 10 * len) { + while (state->status == UART_TRANSFER_STATUS_NONE) { + if (mp_hal_ticks_ms() - start > total_timeout_ms) { break; } - __WFE(); + mp_event_wait_indefinite(); } - uart_disable_tx_irq(UART_PTR); + + // Disable TX IRQ. + uart_disable_tx_irq(uart); } -void UART4_IRQHandler(void) { - if (UART_PTR->UART_RFL) { - for (;;) { - uint32_t rx_fifo_available_cnt = UART_PTR->UART_RFL; - if (rx_fifo_available_cnt == 0) { - break; +static void mp_uart_irq_handler(unsigned int uart_id) { + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + + // Process pending interrupt (below is order of highest to lowest priority). + uint32_t iir = uart->UART_IIR & UART_IIR_INTERRUPT_ID_MASK; + switch (iir) { + case UART_IIR_RECEIVER_LINE_STATUS: { + uint32_t lsr = uart->UART_LSR; + if (lsr & (UART_LSR_RECEIVER_FIFO_ERR | UART_LSR_OVERRUN_ERR)) { + state->status = UART_TRANSFER_STATUS_ERROR; } - for (uint32_t i = 0; i < rx_fifo_available_cnt; ++i) { - int c = UART_PTR->UART_RBR; - #if MICROPY_KBD_EXCEPTION - if (c == mp_interrupt_char) { - mp_sched_keyboard_interrupt(); - continue; + break; + } + + case UART_IIR_RECEIVED_DATA_AVAILABLE: + case UART_IIR_CHARACTER_TIMEOUT: + while (uart->UART_USR & UART_USR_RECEIVE_FIFO_NOT_EMPTY) { + for (uint32_t rfl = uart->UART_RFL; rfl; --rfl) { + int c = uart->UART_RBR; + #if MICROPY_HW_ENABLE_UART_REPL && MICROPY_KBD_EXCEPTION + if (uart_id == MICROPY_HW_UART_REPL) { + if (c == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + continue; + } + } + #endif + if (state->rx_ringbuf != NULL) { + ringbuf_put(state->rx_ringbuf, c); + } } - #endif - ringbuf_put(&stdin_ringbuf, c); } - } - } else { - uart_irq_handler(UART_PTR, &transfer); + + if (iir == UART_IIR_CHARACTER_TIMEOUT) { + if (state->irq_callback != NULL) { + state->irq_callback(); + } + } + + break; + + case UART_IIR_TRANSMIT_HOLDING_REG_EMPTY: + while (uart->UART_USR & UART_USR_TRANSMIT_FIFO_NOT_FULL) { + if (state->tx_src < state->tx_src_max) { + uart->UART_THR = *state->tx_src++; + } else { + uart_disable_tx_irq(uart); + state->status = UART_TRANSFER_STATUS_SEND_COMPLETE; + break; + } + } + break; + + case UART_IIR_MODEM_STATUS: + (void)uart->UART_MSR; + break; } + __SEV(); } +#define DEFINE_IRQ_HANDLER(id) \ + void UART##id##_IRQHandler(void) { \ + mp_uart_irq_handler(id); \ + } + +DEFINE_IRQ_HANDLER(0) +DEFINE_IRQ_HANDLER(1) +DEFINE_IRQ_HANDLER(2) +DEFINE_IRQ_HANDLER(3) +DEFINE_IRQ_HANDLER(4) +DEFINE_IRQ_HANDLER(5) +DEFINE_IRQ_HANDLER(6) +DEFINE_IRQ_HANDLER(7) + +#if MICROPY_HW_ENABLE_UART_REPL + +#define REPL_BAUDRATE (115200) + +void mp_uart_init_repl(void) { + mp_uart_init(MICROPY_HW_UART_REPL, REPL_BAUDRATE, pin_REPL_UART_TX, pin_REPL_UART_RX, &stdin_ringbuf); +} + +void mp_uart_write_strn_repl(const char *str, size_t len) { + mp_uart_tx_data(MICROPY_HW_UART_REPL, (const uint8_t *)str, len); +} + #endif diff --git a/ports/alif/mpuart.h b/ports/alif/mpuart.h index 0c2e510a94e7c..1c28da4627859 100644 --- a/ports/alif/mpuart.h +++ b/ports/alif/mpuart.h @@ -26,7 +26,20 @@ #ifndef MICROPY_INCLUDED_ALIF2_UART_H #define MICROPY_INCLUDED_ALIF2_UART_H -void mp_uart_init(void); -void mp_uart_write_strn(const char *str, size_t len); +#include "py/ringbuf.h" + +void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf); +void mp_uart_deinit(unsigned int uart_id); + +void mp_uart_set_irq_callback(unsigned int uart_id, void (*callback)(void)); +void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts); +void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate); + +size_t mp_uart_rx_any(unsigned int uart_id); +int mp_uart_rx_char(unsigned int uart_id); +void mp_uart_tx_data(unsigned int uart_id, const uint8_t *src, size_t len); + +void mp_uart_init_repl(void); +void mp_uart_write_strn_repl(const char *str, size_t len); #endif // MICROPY_INCLUDED_ALIF2_UART_H From 526c7eabcec97c5705ce43fe2e9588506c4a936a Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 14 Oct 2024 21:48:51 +1100 Subject: [PATCH 61/77] alif: Integrate lwIP and mbedTLS. Signed-off-by: Damien George --- ports/alif/alif.mk | 7 ++- ports/alif/boards/manifest.py | 1 + ports/alif/lwip_inc/arch/cc.h | 10 ++++ ports/alif/lwip_inc/arch/sys_arch.h | 1 + ports/alif/lwip_inc/lwipopts.h | 60 +++++++++++++++++++++ ports/alif/main.c | 17 ++++++ ports/alif/mbedtls/mbedtls_config_port.h | 41 +++++++++++++++ ports/alif/mbedtls/mbedtls_port.c | 65 +++++++++++++++++++++++ ports/alif/mpconfigport.h | 4 ++ ports/alif/mphalport.c | 30 +++++++++++ ports/alif/mphalport.h | 19 +++++++ ports/alif/mpnetworkport.c | 66 ++++++++++++++++++++++++ 12 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 ports/alif/lwip_inc/arch/cc.h create mode 100644 ports/alif/lwip_inc/arch/sys_arch.h create mode 100644 ports/alif/lwip_inc/lwipopts.h create mode 100644 ports/alif/mbedtls/mbedtls_config_port.h create mode 100644 ports/alif/mbedtls/mbedtls_port.c create mode 100644 ports/alif/mpnetworkport.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index f0ff8d91a53d9..e21fe74c46ec6 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -47,6 +47,7 @@ INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/include/ INC += -I$(ALIF_DFP_REL_HERE)/Device/$(MCU_SERIES)/$(MCU_VARIANT)/ INC += -I$(TOP)/lib/tinyusb/src INC += -Itinyusb_port +INC += -Ilwip_inc GEN_PIN_MKPINS = mcu/make-pins.py GEN_PIN_PREFIX = mcu/pins_prefix.c @@ -122,6 +123,7 @@ SRC_C = \ main.c \ modalif.c \ mphalport.c \ + mpnetworkport.c \ mpu.c \ mpuart.c \ msc_disk.c \ @@ -135,6 +137,10 @@ SRC_C = \ vfs_rom_ioctl.c \ $(wildcard $(BOARD_DIR)/*.c) +ifeq ($(MICROPY_SSL_MBEDTLS),1) +SRC_C += mbedtls/mbedtls_port.c +endif + ifeq ($(MICROPY_FLOAT_IMPL),float) LIBM_SRC_C += $(SRC_LIB_LIBM_C) LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) @@ -148,7 +154,6 @@ endif SHARED_SRC_C += $(addprefix shared/,\ libc/string0.c \ netutils/dhcpserver.c \ - netutils/netutils.c \ netutils/trace.c \ readline/readline.c \ runtime/gchelper_native.c \ diff --git a/ports/alif/boards/manifest.py b/ports/alif/boards/manifest.py index 1dc9e179acc14..834aa46702731 100644 --- a/ports/alif/boards/manifest.py +++ b/ports/alif/boards/manifest.py @@ -3,3 +3,4 @@ require("dht") require("neopixel") require("onewire") +require("bundle-networking") diff --git a/ports/alif/lwip_inc/arch/cc.h b/ports/alif/lwip_inc/arch/cc.h new file mode 100644 index 0000000000000..35d45afa71c8c --- /dev/null +++ b/ports/alif/lwip_inc/arch/cc.h @@ -0,0 +1,10 @@ +#ifndef MICROPY_INCLUDED_ALIF_LWIP_ARCH_CC_H +#define MICROPY_INCLUDED_ALIF_LWIP_ARCH_CC_H + +#include +#define LWIP_PLATFORM_DIAG(x) +#define LWIP_PLATFORM_ASSERT(x) { assert(1); } + +#define LWIP_NO_CTYPE_H 1 + +#endif // MICROPY_INCLUDED_ALIF_LWIP_ARCH_CC_H diff --git a/ports/alif/lwip_inc/arch/sys_arch.h b/ports/alif/lwip_inc/arch/sys_arch.h new file mode 100644 index 0000000000000..8b1a393741c96 --- /dev/null +++ b/ports/alif/lwip_inc/arch/sys_arch.h @@ -0,0 +1 @@ +// empty diff --git a/ports/alif/lwip_inc/lwipopts.h b/ports/alif/lwip_inc/lwipopts.h new file mode 100644 index 0000000000000..c0622225e10de --- /dev/null +++ b/ports/alif/lwip_inc/lwipopts.h @@ -0,0 +1,60 @@ +#ifndef MICROPY_INCLUDED_ALIF_LWIP_LWIPOPTS_H +#define MICROPY_INCLUDED_ALIF_LWIP_LWIPOPTS_H + +#include + +// This protection is not needed, instead we execute all lwIP code at PendSV priority +#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) +#define SYS_ARCH_PROTECT(lev) do { } while (0) +#define SYS_ARCH_UNPROTECT(lev) do { } while (0) + +#define NO_SYS 1 +#define SYS_LIGHTWEIGHT_PROT 1 +#define MEM_ALIGNMENT 4 + +#define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 + +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_RAW 1 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define LWIP_STATS 0 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETIF_EXT_STATUS_CALLBACK 1 + +#define LWIP_LOOPIF_MULTICAST 1 +#define LWIP_LOOPBACK_MAX_PBUFS 8 + +#define LWIP_IPV6 0 +#define LWIP_DHCP 1 +#define LWIP_DHCP_CHECK_LINK_UP 1 +#define LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up +#define LWIP_DNS 1 +#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 +#define LWIP_MDNS_RESPONDER 1 +#define LWIP_IGMP 1 + +#define LWIP_NUM_NETIF_CLIENT_DATA LWIP_MDNS_RESPONDER +#define MEMP_NUM_UDP_PCB (4 + LWIP_MDNS_RESPONDER) +#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + LWIP_MDNS_RESPONDER) + +#define SO_REUSE 1 +#define TCP_LISTEN_BACKLOG 1 + +extern uint64_t se_services_rand64(void); +#define LWIP_RAND() se_services_rand64() + +#define MEM_SIZE (16 * 1024) +#define TCP_MSS (1460) +#define TCP_OVERSIZE (TCP_MSS) +#define TCP_WND (8 * TCP_MSS) +#define TCP_SND_BUF (8 * TCP_MSS) +#define TCP_SND_QUEUELEN (2 * (TCP_SND_BUF / TCP_MSS)) +#define TCP_QUEUE_OOSEQ (1) +#define MEMP_NUM_TCP_SEG (2 * TCP_SND_QUEUELEN) + +typedef uint32_t sys_prot_t; + +#endif // MICROPY_INCLUDED_ALIF_LWIP_LWIPOPTS_H diff --git a/ports/alif/main.c b/ports/alif/main.c index 77e413e1eb492..15a77348cf7e3 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -30,6 +30,7 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "py/stackctrl.h" +#include "extmod/modnetwork.h" #include "shared/readline/readline.h" #include "shared/runtime/gchelper.h" #include "shared/runtime/pyexec.h" @@ -42,6 +43,11 @@ #include "se_services.h" #include "system_tick.h" +#if MICROPY_PY_LWIP +#include "lwip/init.h" +#include "lwip/apps/mdns.h" +#endif + extern uint8_t __StackTop, __StackLimit; extern uint8_t __GcHeapStart, __GcHeapEnd; @@ -83,6 +89,17 @@ int main(void) { mp_stack_set_limit(&__StackTop - &__StackLimit - 1024); gc_init(&__GcHeapStart, &__GcHeapEnd); + #if MICROPY_PY_LWIP + // lwIP doesn't allow to reinitialise itself by subsequent calls to this function + // because the system timeout list (next_timeout) is only ever reset by BSS clearing. + // So for now we only init the lwIP stack once on power-up. + lwip_init(); + #if LWIP_MDNS_RESPONDER + mdns_resp_init(); + #endif + mod_network_lwip_init(); + #endif + for (;;) { // Initialise MicroPython runtime. mp_init(); diff --git a/ports/alif/mbedtls/mbedtls_config_port.h b/ports/alif/mbedtls/mbedtls_config_port.h new file mode 100644 index 0000000000000..d9566304d28b9 --- /dev/null +++ b/ports/alif/mbedtls/mbedtls_config_port.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_MBEDTLS_CONFIG_H +#define MICROPY_INCLUDED_MBEDTLS_CONFIG_H + +// Time hook. +#include +extern time_t alif_mbedtls_time(time_t *timer); +#define MBEDTLS_PLATFORM_TIME_MACRO alif_mbedtls_time +#define MBEDTLS_PLATFORM_MS_TIME_ALT undefined_and_unused + +// Set MicroPython-specific options. +#define MICROPY_MBEDTLS_CONFIG_BARE_METAL (1) + +// Include common mbedtls configuration. +#include "extmod/mbedtls/mbedtls_config_common.h" + +#endif /* MICROPY_INCLUDED_MBEDTLS_CONFIG_H */ diff --git a/ports/alif/mbedtls/mbedtls_port.c b/ports/alif/mbedtls/mbedtls_port.c new file mode 100644 index 0000000000000..a8c155e31ae65 --- /dev/null +++ b/ports/alif/mbedtls/mbedtls_port.c @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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 "se_services.h" +#include "mbedtls_config_port.h" + +#if defined(MBEDTLS_HAVE_TIME) +#include "shared/timeutils/timeutils.h" +#include "mbedtls/platform_time.h" +#endif + +int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen) { + uint32_t val = 0; + int n = 0; + *olen = len; + while (len--) { + if (!n) { + val = se_services_rand64(); + n = 4; + } + *output++ = val; + val >>= 8; + --n; + } + return 0; +} + +#if defined(MBEDTLS_HAVE_TIME) + +time_t alif_mbedtls_time(time_t *timer) { + // TODO implement proper RTC time + unsigned int year = 2025; + unsigned int month = 1; + unsigned int date = 1; + unsigned int hours = 12; + unsigned int minutes = 0; + unsigned int seconds = 0; + return timeutils_seconds_since_epoch(year, month, date, hours, minutes, seconds); +} + +#endif diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index a5fd4f6dbfa87..a437c1385cb0c 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -136,6 +136,10 @@ #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) +#define MICROPY_PY_NETWORK (CORE_M55_HP) +#ifndef MICROPY_PY_NETWORK_HOSTNAME_DEFAULT +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-alif" +#endif #define MICROPY_VFS (1) #define MICROPY_VFS_ROM (1) diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 8f7780bb8acdf..39528a4b9f272 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -38,6 +38,7 @@ #include "tusb.h" #include "mpuart.h" #include "pendsv.h" +#include "se_services.h" #include "system_tick.h" #ifndef MICROPY_HW_STDIN_BUFFER_LEN @@ -237,3 +238,32 @@ void soft_timer_schedule_at_ms(uint32_t ticks_ms) { } #endif + +/*******************************************************************************/ +// MAC address + +// Generate a random locally administered MAC address (LAA) +void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]) { + uint8_t id[8]; + se_services_get_unique_id(id); + buf[0] = 0x02; // LAA range + buf[1] = id[4]; + buf[2] = id[3]; + buf[3] = id[2]; + buf[4] = id[1]; + buf[5] = (id[0] << 2) | idx; +} + +// A board can override this if needed +MP_WEAK void mp_hal_get_mac(int idx, uint8_t buf[6]) { + mp_hal_generate_laa_mac(idx, buf); +} + +void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest) { + static const char hexchr[16] = "0123456789ABCDEF"; + uint8_t mac[6]; + mp_hal_get_mac(idx, mac); + for (; chr_len; ++chr_off, --chr_len) { + *dest++ = hexchr[mac[chr_off >> 1] >> (4 * (1 - (chr_off & 1))) & 0xf]; + } +} diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 5f1e3fff8296e..2ba62db2d2e5a 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -39,6 +39,11 @@ #define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); #define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state); +// Prevent the "lwIP task" from running. +#define MICROPY_PY_LWIP_ENTER MICROPY_PY_PENDSV_ENTER +#define MICROPY_PY_LWIP_REENTER MICROPY_PY_PENDSV_REENTER +#define MICROPY_PY_LWIP_EXIT MICROPY_PY_PENDSV_EXIT + // Port level Wait-for-Event macro // // Do not use this macro directly, include py/runtime.h and @@ -202,3 +207,17 @@ void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode, // Include all the pin definitions. #include "genhdr/pins_board.h" + +/******************************************************************************/ +// Other HAL functions. + +enum { + MP_HAL_MAC_WLAN0 = 0, + MP_HAL_MAC_WLAN1, + MP_HAL_MAC_BDADDR, + MP_HAL_MAC_ETH0, +}; + +void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]); +void mp_hal_get_mac(int idx, uint8_t buf[6]); +void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest); diff --git a/ports/alif/mpnetworkport.c b/ports/alif/mpnetworkport.c new file mode 100644 index 0000000000000..312fadf08a9c3 --- /dev/null +++ b/ports/alif/mpnetworkport.c @@ -0,0 +1,66 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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/mphal.h" +#include "py/obj.h" + +#if MICROPY_PY_LWIP + +#include "shared/runtime/softtimer.h" +#include "lwip/timeouts.h" + +// Poll lwIP every 64ms by default +#define LWIP_TICK_RATE_MS 64 + +// Soft timer for running lwIP in the background. +static soft_timer_entry_t mp_network_soft_timer; + +u32_t sys_now(void) { + return mp_hal_ticks_ms(); +} + +// This is called by soft_timer and executes at PendSV level. +static void mp_network_soft_timer_callback(soft_timer_entry_t *self) { + // Run the lwIP internal updates. + sys_check_timeouts(); + + #if LWIP_NETIF_LOOPBACK + netif_poll_all(); + #endif +} + +void mod_network_lwip_init(void) { + soft_timer_static_init( + &mp_network_soft_timer, + SOFT_TIMER_MODE_PERIODIC, + LWIP_TICK_RATE_MS, + mp_network_soft_timer_callback + ); + + soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS); +} + +#endif // MICROPY_PY_LWIP From d6e33423da471ddcba91632f032b18c5a79e9880 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 10 Mar 2025 14:09:04 +1100 Subject: [PATCH 62/77] alif: Integrate cyw43 WLAN driver. Signed-off-by: Damien George --- ports/alif/alif.mk | 3 +- ports/alif/cyw43_configport.h | 123 ++++++++++++++++++++++++++++ ports/alif/cyw43_port_spi.c | 150 ++++++++++++++++++++++++++++++++++ ports/alif/irq.h | 1 + ports/alif/main.c | 15 ++++ ports/alif/mpconfigport.h | 15 ++++ ports/alif/mpnetworkport.c | 14 ++++ ports/alif/pendsv.h | 3 + 8 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 ports/alif/cyw43_configport.h create mode 100644 ports/alif/cyw43_port_spi.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index e21fe74c46ec6..179df67a58c30 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -115,6 +115,7 @@ SRC_O += \ SRC_C = \ alif_flash.c \ + cyw43_port_spi.c \ fatfs_port.c \ machine_pin.c \ machine_i2c.c \ @@ -196,12 +197,12 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/adc.c \ drivers/source/i2c.c \ - drivers/source/spi.c \ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ drivers/source/mram.c \ drivers/source/pinconf.c \ + drivers/source/spi.c \ drivers/source/uart.c \ drivers/source/utimer.c \ ospi_xip/source/ospi/ospi_drv.c \ diff --git a/ports/alif/cyw43_configport.h b/ports/alif/cyw43_configport.h new file mode 100644 index 0000000000000..b7e4ad702a599 --- /dev/null +++ b/ports/alif/cyw43_configport.h @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H +#define MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H + +// The board-level config will be included here, so it can set some CYW43 values. +#include "py/mpconfig.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "extmod/modnetwork.h" +#include "pendsv.h" + +#ifndef static_assert +#define static_assert(expr, msg) typedef int static_assert_##__LINE__[(expr) ? 1 : -1] +#endif + +#define CYW43_USE_SPI (1) +#define CYW43_LWIP (1) +#define CYW43_USE_STATS (0) +#define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) + +#if 0 +#define CYW43_VERBOSE_DEBUG (1) +#define CYW43_VDEBUG(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#define CYW43_DEBUG(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#define CYW43_INFO(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#define CYW43_WARN(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#endif + +#define CYW43_IOCTL_TIMEOUT_US (1000000) +#define CYW43_SLEEP_MAX (50) +#define CYW43_NETUTILS (1) +#define CYW43_CLEAR_SDIO_INT (1) + +#define CYW43_EPERM MP_EPERM // Operation not permitted +#define CYW43_EIO MP_EIO // I/O error +#define CYW43_EINVAL MP_EINVAL // Invalid argument +#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out + +#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER +#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT +#define CYW43_THREAD_LOCK_CHECK + +#define CYW43_HOST_NAME mod_network_hostname_data + +#define CYW43_SDPCM_SEND_COMMON_WAIT __WFE() +#define CYW43_DO_IOCTL_WAIT // __WFE() +#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() + +#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) + +#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT +#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT +#define CYW43_HAL_PIN_PULL_NONE 0 + +#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 + +#define cyw43_hal_ticks_us mp_hal_ticks_us +#define cyw43_hal_ticks_ms mp_hal_ticks_ms + +#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t +#define cyw43_hal_pin_read mp_hal_pin_read +#define cyw43_hal_pin_low mp_hal_pin_low +#define cyw43_hal_pin_high mp_hal_pin_high + +#define cyw43_hal_get_mac mp_hal_get_mac +#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii +#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac + +#define CYW43_PIN_WL_REG_ON pin_WL_REG_ON +#define CYW43_PIN_WL_IRQ pin_WL_IRQ + +#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) + +void cyw43_post_poll_hook(void); + +static inline void cyw43_delay_us(uint32_t us) { + uint32_t start = mp_hal_ticks_us(); + while (mp_hal_ticks_us() - start < us) { + } +} + +static inline void cyw43_delay_ms(uint32_t ms) { + mp_hal_delay_ms(ms); +} + +static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, uint32_t alt) { + if (mode == MP_HAL_PIN_MODE_INPUT) { + mp_hal_pin_input(pin); + } else { + mp_hal_pin_output(pin); + } +} + +static inline void cyw43_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) { + mp_hal_pin_config_irq_falling(pin, enable); +} + +#endif // MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H diff --git a/ports/alif/cyw43_port_spi.c b/ports/alif/cyw43_port_spi.c new file mode 100644 index 0000000000000..84d84b0e1de48 --- /dev/null +++ b/ports/alif/cyw43_port_spi.c @@ -0,0 +1,150 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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. + */ + +#if MICROPY_PY_NETWORK_CYW43 + +#include "lib/cyw43-driver/src/cyw43.h" +#include "lib/cyw43-driver/src/cyw43_internal.h" +#include "lib/cyw43-driver/src/cyw43_spi.h" + +#include "spi.h" +#include "sys_ctrl_spi.h" + +// CYW43 is connected to SPI3. +#define HW_SPI ((SPI_Type *)SPI3_BASE) +#define SPI_BAUDRATE (16000000) +#define SPI_RX_FIFO_SIZE (16) + +// WL_IRQ is on P9_6. +#define WL_IRQ_IRQN (GPIO9_IRQ6_IRQn) +#define WL_IRQ_HANDLER GPIO9_IRQ6Handler + +// Must run at IRQ priority above PendSV so it can wake cyw43-driver when PendSV is disabled. +void WL_IRQ_HANDLER(void) { + if (gpio_read_int_rawstatus(pin_WL_IRQ->gpio, pin_WL_IRQ->pin)) { + gpio_interrupt_eoi(pin_WL_IRQ->gpio, pin_WL_IRQ->pin); + pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll); + __SEV(); + } +} + +static void spi_bus_init(void) { + // Configure pins. + mp_hal_pin_output(pin_WL_CS); + mp_hal_pin_high(pin_WL_CS); + // NOTE: Alif recommends enabled input read for all SPI pins. + mp_hal_pin_config(pin_WL_SCLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + mp_hal_pin_config(pin_WL_MOSI, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + mp_hal_pin_config(pin_WL_MISO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + + // Starts out clock_polarity=1, clock_phase=0. + spi_mode_master(HW_SPI); + spi_set_bus_speed(HW_SPI, SPI_BAUDRATE, GetSystemAHBClock()); + spi_set_mode(HW_SPI, SPI_MODE_2); + spi_set_protocol(HW_SPI, SPI_PROTO_SPI); + spi_set_dfs(HW_SPI, 8); + spi_set_tmod(HW_SPI, SPI_TMOD_TX_AND_RX); + spi_control_ss(HW_SPI, 0, SPI_SS_STATE_ENABLE); +} + +int cyw43_spi_init(cyw43_int_t *self) { + spi_bus_init(); + + // Configure IRQ for WL_IRQ (active low input). + NVIC_SetPriority(WL_IRQ_IRQN, IRQ_PRI_CYW43); + NVIC_ClearPendingIRQ(WL_IRQ_IRQN); + NVIC_EnableIRQ(WL_IRQ_IRQN); + + return 0; +} + +void cyw43_spi_deinit(cyw43_int_t *self) { + // Disable clock, SS and SPI. + spi_mask_interrupts(HW_SPI); + spi_control_ss(HW_SPI, 0, 0); + spi_disable(HW_SPI); + + // Disable SPI IRQ. + NVIC_DisableIRQ(WL_IRQ_IRQN); + NVIC_ClearPendingIRQ(WL_IRQ_IRQN); +} + +void cyw43_spi_gpio_setup(void) { +} + +void cyw43_spi_reset(void) { +} + +void cyw43_spi_set_polarity(cyw43_int_t *self, int pol) { + (void)self; + + if (pol == 0) { + spi_set_mode(HW_SPI, SPI_MODE_0); + } else { + spi_set_mode(HW_SPI, SPI_MODE_2); + } +} + +// tx must not be NULL. +// rx_len must be 0, or the same as tx_len. +int cyw43_spi_transfer(cyw43_int_t *self, const uint8_t *tx, size_t tx_len, uint8_t *rx, size_t rx_len) { + (void)self; + + if (tx_len == 0 && rx_len == 0) { + return 0; + } + + mp_hal_pin_low(pin_WL_CS); + + // Must read the same amount of data that is written out to SPI. + rx_len = tx_len; + + while (tx_len || rx_len) { + // Only add data to the TX FIFO if: + // - there's data to add + // - and TX is not ahead of RX by more than the RX FIFO size + // - and there's space in the TX FIFO + if (tx_len && tx_len + SPI_RX_FIFO_SIZE > rx_len && (HW_SPI->SPI_SR & SPI_SR_TFNF) != 0) { + HW_SPI->SPI_DR[0] = *tx++; + --tx_len; + } + + // Take data from the RX FIFO and store it into the output buffer (if given). + if (rx_len && (HW_SPI->SPI_SR & SPI_SR_RFNE) != 0) { + uint8_t data = HW_SPI->SPI_DR[0]; + if (rx != NULL) { + *rx++ = data; + } + --rx_len; + } + } + + mp_hal_pin_high(pin_WL_CS); + + return 0; +} + +#endif diff --git a/ports/alif/irq.h b/ports/alif/irq.h index ed454adcb4657..08b8ef8086bc3 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -49,6 +49,7 @@ #define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) #define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0) #define IRQ_PRI_RTC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 100, 0) +#define IRQ_PRI_CYW43 NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 126, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) // these states correspond to values from query_irq, enable_irq and disable_irq diff --git a/ports/alif/main.c b/ports/alif/main.c index 15a77348cf7e3..3bff8cf4ae9f8 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -47,6 +47,9 @@ #include "lwip/init.h" #include "lwip/apps/mdns.h" #endif +#if MICROPY_PY_NETWORK_CYW43 +#include "lib/cyw43-driver/src/cyw43.h" +#endif extern uint8_t __StackTop, __StackLimit; extern uint8_t __GcHeapStart, __GcHeapEnd; @@ -100,6 +103,18 @@ int main(void) { mod_network_lwip_init(); #endif + #if MICROPY_PY_NETWORK_CYW43 + { + cyw43_init(&cyw43_state); + uint8_t buf[8]; + memcpy(&buf[0], "ALIF", 4); + mp_hal_get_mac_ascii(MP_HAL_MAC_WLAN0, 8, 4, (char *)&buf[4]); + cyw43_wifi_ap_set_ssid(&cyw43_state, 8, buf); + cyw43_wifi_ap_set_auth(&cyw43_state, CYW43_AUTH_WPA2_AES_PSK); + cyw43_wifi_ap_set_password(&cyw43_state, 8, (const uint8_t *)"alif0123"); + } + #endif + for (;;) { // Initialise MicroPython runtime. mp_init(); diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index a437c1385cb0c..76b603027fcf1 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -155,6 +155,21 @@ #define MICROPY_FATFS_MAX_SS (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES) #endif +#if MICROPY_PY_NETWORK_CYW43 +extern const struct _mp_obj_type_t mp_network_cyw43_type; +#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, +#else +#define MICROPY_HW_NIC_CYW43 +#endif + +#ifndef MICROPY_BOARD_NETWORK_INTERFACES +#define MICROPY_BOARD_NETWORK_INTERFACES +#endif + +#define MICROPY_PORT_NETWORK_INTERFACES \ + MICROPY_HW_NIC_CYW43 \ + MICROPY_BOARD_NETWORK_INTERFACES \ + #define MP_STATE_PORT MP_STATE_VM // Miscellaneous settings diff --git a/ports/alif/mpnetworkport.c b/ports/alif/mpnetworkport.c index 312fadf08a9c3..40def7f7fd7e9 100644 --- a/ports/alif/mpnetworkport.c +++ b/ports/alif/mpnetworkport.c @@ -32,6 +32,10 @@ #include "shared/runtime/softtimer.h" #include "lwip/timeouts.h" +#if MICROPY_PY_NETWORK_CYW43 +#include "lib/cyw43-driver/src/cyw43.h" +#endif + // Poll lwIP every 64ms by default #define LWIP_TICK_RATE_MS 64 @@ -50,6 +54,16 @@ static void mp_network_soft_timer_callback(soft_timer_entry_t *self) { #if LWIP_NETIF_LOOPBACK netif_poll_all(); #endif + + #if MICROPY_PY_NETWORK_CYW43 + if (cyw43_poll) { + if (cyw43_sleep != 0) { + if (--cyw43_sleep == 0) { + cyw43_poll(); + } + } + } + #endif } void mod_network_lwip_init(void) { diff --git a/ports/alif/pendsv.h b/ports/alif/pendsv.h index 43a4b7a853846..17d7e82dfc7e7 100644 --- a/ports/alif/pendsv.h +++ b/ports/alif/pendsv.h @@ -34,6 +34,9 @@ enum { PENDSV_DISPATCH_SOFT_TIMER, + #if MICROPY_PY_NETWORK_CYW43 + PENDSV_DISPATCH_CYW43, + #endif MICROPY_BOARD_PENDSV_ENTRIES PENDSV_DISPATCH_MAX }; From 30dfbe5dc0d8a8ce4af3724b9b2a49b60a323a31 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 10 Mar 2025 13:57:48 +1100 Subject: [PATCH 63/77] alif: Integrate cyw43 Bluetooth with NimBLE. Signed-off-by: Damien George --- ports/alif/alif.mk | 4 + ports/alif/cyw43_configport.h | 18 +++++ ports/alif/main.c | 9 +++ ports/alif/mpbthciport.c | 140 ++++++++++++++++++++++++++++++++++ ports/alif/mpbthciport.h | 46 +++++++++++ ports/alif/mpconfigport.h | 6 ++ ports/alif/mpnimbleport.c | 78 +++++++++++++++++++ ports/alif/mpnimbleport.h | 32 ++++++++ 8 files changed, 333 insertions(+) create mode 100644 ports/alif/mpbthciport.c create mode 100644 ports/alif/mpbthciport.h create mode 100644 ports/alif/mpnimbleport.c create mode 100644 ports/alif/mpnimbleport.h diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 179df67a58c30..eee27b3b7d698 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -142,6 +142,10 @@ ifeq ($(MICROPY_SSL_MBEDTLS),1) SRC_C += mbedtls/mbedtls_port.c endif +ifeq ($(MICROPY_PY_BLUETOOTH),1) +SRC_C += mpbthciport.c mpnimbleport.c +endif + ifeq ($(MICROPY_FLOAT_IMPL),float) LIBM_SRC_C += $(SRC_LIB_LIBM_C) LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) diff --git a/ports/alif/cyw43_configport.h b/ports/alif/cyw43_configport.h index b7e4ad702a599..4b5cce67132e9 100644 --- a/ports/alif/cyw43_configport.h +++ b/ports/alif/cyw43_configport.h @@ -32,6 +32,7 @@ #include "py/mphal.h" #include "py/runtime.h" #include "extmod/modnetwork.h" +#include "extmod/mpbthci.h" #include "pendsv.h" #ifndef static_assert @@ -39,6 +40,7 @@ #endif #define CYW43_USE_SPI (1) +#define CYW43_ENABLE_BLUETOOTH_OVER_UART (1) #define CYW43_LWIP (1) #define CYW43_USE_STATS (0) #define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) @@ -78,6 +80,7 @@ #define CYW43_HAL_PIN_PULL_NONE 0 #define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 +#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR #define cyw43_hal_ticks_us mp_hal_ticks_us #define cyw43_hal_ticks_ms mp_hal_ticks_ms @@ -87,6 +90,10 @@ #define cyw43_hal_pin_low mp_hal_pin_low #define cyw43_hal_pin_high mp_hal_pin_high +#define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate +#define cyw43_hal_uart_write mp_bluetooth_hci_uart_write +#define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar + #define cyw43_hal_get_mac mp_hal_get_mac #define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii #define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac @@ -94,6 +101,11 @@ #define CYW43_PIN_WL_REG_ON pin_WL_REG_ON #define CYW43_PIN_WL_IRQ pin_WL_IRQ +#define CYW43_PIN_BT_REG_ON pin_BT_REG_ON +#define CYW43_PIN_BT_HOST_WAKE pin_BT_HOST_WAKE +#define CYW43_PIN_BT_DEV_WAKE pin_BT_DEV_WAKE +#define CYW43_PIN_BT_CTS pin_BT_UART_CTS + #define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) void cyw43_post_poll_hook(void); @@ -120,4 +132,10 @@ static inline void cyw43_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool e mp_hal_pin_config_irq_falling(pin, enable); } +#define cyw43_bluetooth_controller_init mp_bluetooth_hci_controller_init +#define cyw43_bluetooth_controller_deinit mp_bluetooth_hci_controller_deinit +#define cyw43_bluetooth_controller_sleep_maybe mp_bluetooth_hci_controller_sleep_maybe +#define cyw43_bluetooth_controller_woken mp_bluetooth_hci_controller_woken +#define cyw43_bluetooth_controller_wakeup mp_bluetooth_hci_controller_wakeup + #endif // MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H diff --git a/ports/alif/main.c b/ports/alif/main.c index 3bff8cf4ae9f8..fd35604cbc234 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -30,6 +30,7 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "py/stackctrl.h" +#include "extmod/modbluetooth.h" #include "extmod/modnetwork.h" #include "shared/readline/readline.h" #include "shared/runtime/gchelper.h" @@ -37,6 +38,7 @@ #include "shared/runtime/softtimer.h" #include "shared/tinyusb/mp_usbd.h" #include "tusb.h" +#include "mpbthciport.h" #include "mpuart.h" #include "ospi_flash.h" #include "pendsv.h" @@ -103,6 +105,10 @@ int main(void) { mod_network_lwip_init(); #endif + #if MICROPY_PY_BLUETOOTH + mp_bluetooth_hci_init(); + #endif + #if MICROPY_PY_NETWORK_CYW43 { cyw43_init(&cyw43_state); @@ -154,6 +160,9 @@ int main(void) { soft_reset_exit: mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + #if MICROPY_PY_BLUETOOTH + mp_bluetooth_deinit(); + #endif soft_timer_deinit(); gc_sweep_all(); mp_deinit(); diff --git a/ports/alif/mpbthciport.c b/ports/alif/mpbthciport.c new file mode 100644 index 0000000000000..a37c9621564a1 --- /dev/null +++ b/ports/alif/mpbthciport.c @@ -0,0 +1,140 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 OpenMV LLC. + * + * 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/mphal.h" +#include "py/runtime.h" +#include "extmod/mpbthci.h" +#include "shared/runtime/softtimer.h" +#include "mpbthciport.h" + +#if MICROPY_PY_BLUETOOTH + +uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + +// Soft timer and scheduling node for scheduling a HCI poll. +static soft_timer_entry_t mp_bluetooth_hci_soft_timer; +static mp_sched_node_t mp_bluetooth_hci_sched_node; + +// This is called by soft_timer and executes at IRQ_PRI_PENDSV. +static void mp_bluetooth_hci_soft_timer_callback(soft_timer_entry_t *self) { + mp_bluetooth_hci_poll_now(); +} + +void mp_bluetooth_hci_init(void) { + soft_timer_static_init( + &mp_bluetooth_hci_soft_timer, + SOFT_TIMER_MODE_ONE_SHOT, + 0, + mp_bluetooth_hci_soft_timer_callback + ); +} + +static void mp_bluetooth_hci_start_polling(void) { + mp_bluetooth_hci_poll_now(); +} + +void mp_bluetooth_hci_poll_in_ms(uint32_t ms) { + soft_timer_reinsert(&mp_bluetooth_hci_soft_timer, ms); +} + +// For synchronous mode, we run all BLE stack code inside a scheduled task. +// This task is scheduled periodically via a timer, or immediately after UART RX IRQ. +static void run_events_scheduled_task(mp_sched_node_t *node) { + (void)node; + // This will process all buffered HCI UART data, and run any callouts or events. + mp_bluetooth_hci_poll(); +} + +// Called periodically (systick) or directly (e.g. UART RX IRQ) in order to +// request that processing happens ASAP in the scheduler. +void mp_bluetooth_hci_poll_now(void) { + mp_sched_schedule_node(&mp_bluetooth_hci_sched_node, run_events_scheduled_task); +} + +/******************************************************************************/ +// HCI over UART + +#include "mpuart.h" + +static uint32_t hci_uart_id; +static bool hci_uart_first_char; +static uint8_t hci_rx_ringbuf_array[768]; +static ringbuf_t hci_rx_ringbuf = { + .buf = hci_rx_ringbuf_array, + .size = sizeof(hci_rx_ringbuf_array), + .iget = 0, + .iput = 0, +}; + +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { + hci_uart_id = port; + hci_uart_first_char = true; + + // Initialise the UART. + mp_uart_init(hci_uart_id, baudrate, pin_BT_UART_TX, pin_BT_UART_RX, &hci_rx_ringbuf); + mp_uart_set_flow(hci_uart_id, pin_BT_UART_RTS, pin_BT_UART_CTS); + mp_uart_set_irq_callback(hci_uart_id, mp_bluetooth_hci_poll_now); + + // Start the HCI polling to process any initial events/packets. + mp_bluetooth_hci_start_polling(); + + return 0; +} + +int mp_bluetooth_hci_uart_deinit(void) { + mp_uart_deinit(hci_uart_id); + return 0; +} + +int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + mp_uart_set_baudrate(hci_uart_id, baudrate); + return 0; +} + +int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { + mp_bluetooth_hci_controller_wakeup(); + mp_uart_tx_data(hci_uart_id, (void *)buf, len); + return 0; +} + +// This function expects the controller to be in the wake state via a previous call +// to mp_bluetooth_hci_controller_woken. +int mp_bluetooth_hci_uart_readchar(void) { + if (mp_uart_rx_any(hci_uart_id)) { + int c = mp_uart_rx_char(hci_uart_id); + if (hci_uart_first_char) { + if (c == 0) { + return -1; + } + hci_uart_first_char = false; + } + return c; + } else { + return -1; + } +} + +#endif // MICROPY_PY_BLUETOOTH diff --git a/ports/alif/mpbthciport.h b/ports/alif/mpbthciport.h new file mode 100644 index 0000000000000..65e9d1752a798 --- /dev/null +++ b/ports/alif/mpbthciport.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_MPBTHCIPORT_H +#define MICROPY_INCLUDED_ALIF_MPBTHCIPORT_H + +#include "py/mpconfig.h" + +// Initialise the HCI subsystem (should be called once, early on). +void mp_bluetooth_hci_init(void); + +// Call this to poll the HCI now. +void mp_bluetooth_hci_poll_now(void); + +// Call this to poll the HCI after a certain timeout. +void mp_bluetooth_hci_poll_in_ms(uint32_t ms); + +// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c). +// Request new data from the uart and pass to the stack, and run pending events/callouts. +// This is a low-level function and should not be called directly, use +// mp_bluetooth_hci_poll_now/mp_bluetooth_hci_poll_in_ms instead. +void mp_bluetooth_hci_poll(void); + +#endif // MICROPY_INCLUDED_ALIF_MPBTHCIPORT_H diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 76b603027fcf1..73abb8924c393 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -170,6 +170,12 @@ extern const struct _mp_obj_type_t mp_network_cyw43_type; MICROPY_HW_NIC_CYW43 \ MICROPY_BOARD_NETWORK_INTERFACES \ +// Bluetooth code only runs in the scheduler, no locking/mutex required. +#define MICROPY_PY_BLUETOOTH_ENTER uint32_t atomic_state = 0; +#define MICROPY_PY_BLUETOOTH_EXIT (void)atomic_state; +#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) +#define MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS (1) + #define MP_STATE_PORT MP_STATE_VM // Miscellaneous settings diff --git a/ports/alif/mpnimbleport.c b/ports/alif/mpnimbleport.c new file mode 100644 index 0000000000000..8da5ce293c9bd --- /dev/null +++ b/ports/alif/mpnimbleport.c @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jim Mussared + * Copyright (c) 2020 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 "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE + +#define DEBUG_printf(...) // printf("mpnimbleport.c: " __VA_ARGS__) + +#include "host/ble_hs.h" +#include "nimble/nimble_npl.h" + +#include "extmod/mpbthci.h" +#include "extmod/modbluetooth.h" +#include "extmod/nimble/modbluetooth_nimble.h" +#include "extmod/nimble/hal/hal_uart.h" +#include "mpbthciport.h" + +// Get any pending data from the UART and send it to NimBLE's HCI buffers. +// Any further processing by NimBLE will be run via its event queue. +void mp_bluetooth_hci_poll(void) { + if (mp_bluetooth_nimble_ble_state >= MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) { + // DEBUG_printf("mp_bluetooth_hci_poll_uart %d\n", mp_bluetooth_nimble_ble_state); + + // Run any timers. + mp_bluetooth_nimble_os_callout_process(); + + // Process incoming UART data, and run events as they are generated. + mp_bluetooth_nimble_hci_uart_process(true); + + // Run any remaining events (e.g. if there was no UART data). + mp_bluetooth_nimble_os_eventq_run_all(); + } + + if (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { + // Call this function again in 128ms to check for new events. + // TODO: improve this by only calling back when needed. + mp_bluetooth_hci_poll_in_ms(128); + } +} + +// --- Port-specific helpers for the generic NimBLE bindings. ----------------- + +void mp_bluetooth_nimble_hci_uart_wfi(void) { + __WFE(); + + // This is called while NimBLE is waiting in ble_npl_sem_pend, i.e. waiting for an HCI ACK. + // Do not need to run events here (it must not invoke Python code), only processing incoming HCI data. + mp_bluetooth_nimble_hci_uart_process(false); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/ports/alif/mpnimbleport.h b/ports/alif/mpnimbleport.h new file mode 100644 index 0000000000000..150ec42da76d7 --- /dev/null +++ b/ports/alif/mpnimbleport.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_ALIF_NIMBLE_PORT_H +#define MICROPY_INCLUDED_ALIF_NIMBLE_PORT_H + +// To allow MICROPY_HW_BLE_UART_ID to be resolved. + +#endif // MICROPY_INCLUDED_ALIF_NIMBLE_PORT_H From da46b4d7089e6b0be553798568bbdf9e7ea9368b Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 13 Mar 2025 14:54:49 +0100 Subject: [PATCH 64/77] alif/mcu: Remove json config files. They will be generated as part of the build. Signed-off-by: iabdalkader --- ports/alif/mcu/M55_DUAL_cfg.json | 26 -------------------------- ports/alif/mcu/M55_HE_cfg.json | 17 ----------------- ports/alif/mcu/M55_HP_cfg.json | 17 ----------------- 3 files changed, 60 deletions(-) delete mode 100644 ports/alif/mcu/M55_DUAL_cfg.json delete mode 100644 ports/alif/mcu/M55_HE_cfg.json delete mode 100644 ports/alif/mcu/M55_HP_cfg.json diff --git a/ports/alif/mcu/M55_DUAL_cfg.json b/ports/alif/mcu/M55_DUAL_cfg.json deleted file mode 100644 index 81beef5f03614..0000000000000 --- a/ports/alif/mcu/M55_DUAL_cfg.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "DEVICE": { - "disabled" : false, - "binary": "app-device-config.json", - "version" : "0.5.00", - "signed": false - }, - "HP_APP": { - "disabled" : false, - "binary": "M55_HP/firmware.bin", - "mramAddress": "0x80020000", - "version": "1.0.0", - "cpu_id": "M55_HP", - "flags": ["boot"], - "signed": false - }, - "HE_APP": { - "disabled" : false, - "binary": "M55_HE/firmware.bin", - "mramAddress": "0x80320000", - "version": "1.0.0", - "cpu_id": "M55_HE", - "flags": ["deferred"], - "signed": false - } -} diff --git a/ports/alif/mcu/M55_HE_cfg.json b/ports/alif/mcu/M55_HE_cfg.json deleted file mode 100644 index 83898418043ec..0000000000000 --- a/ports/alif/mcu/M55_HE_cfg.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "DEVICE": { - "disabled" : false, - "binary": "app-device-config.json", - "version" : "0.5.00", - "signed": false - }, - "HE_APP": { - "disabled" : false, - "binary": "M55_HE/firmware.bin", - "mramAddress": "0x80320000", - "version": "1.0.0", - "cpu_id": "M55_HE", - "flags": ["boot"], - "signed": false - } -} diff --git a/ports/alif/mcu/M55_HP_cfg.json b/ports/alif/mcu/M55_HP_cfg.json deleted file mode 100644 index d6710f69c77b6..0000000000000 --- a/ports/alif/mcu/M55_HP_cfg.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "DEVICE": { - "disabled" : false, - "binary": "app-device-config.json", - "version" : "0.5.00", - "signed": false - }, - "HP_APP": { - "disabled" : false, - "binary": "M55_HP/firmware.bin", - "mramAddress": "0x80020000", - "version": "1.0.0", - "cpu_id": "M55_HP", - "flags": ["boot"], - "signed": false - } -} From 19a4689c6b0b652f8d50be6b658adc649649d5d1 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 13 Mar 2025 14:55:19 +0100 Subject: [PATCH 65/77] alif/mcu: Pre-process Alif ToC config file. Signed-off-by: iabdalkader --- ports/alif/Makefile | 24 +++++++++++------ ports/alif/mcu/alif_cfg.json.in | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 ports/alif/mcu/alif_cfg.json.in diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 7d6731dacf200..68706674365c1 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -20,10 +20,14 @@ JLINK_CMD_SUFFIX = \ Reset\n\ Exit +ALIF_TOC_CONFIG = alif_cfg.json +ALIF_TOC_APPS = $(BUILD)/$(ALIF_TOC_CONFIG) +ALIF_TOC_CFLAGS += -DTOC_CFG_FILE=$(ALIF_TOOLKIT_CFG_FILE) + ifeq ($(MCU_CORE),M55_HP) -ALIF_TOC_CONFIG = mcu/M55_HP_cfg.json -ALIF_TOC_APPS = $(BUILD)/M55_HP/firmware.bin +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HP_APP=1 +ALIF_TOC_APPS += $(BUILD)/M55_HP/firmware.bin JLINK_CMD = '\ $(JLINK_CMD_PREFIX)\ LoadFile "$(BUILD)/M55_HP/firmware.bin",0x80020000\n\ @@ -31,8 +35,8 @@ $(JLINK_CMD_SUFFIX)' else ifeq ($(MCU_CORE),M55_HE) -ALIF_TOC_CONFIG = mcu/M55_HE_cfg.json -ALIF_TOC_APPS = $(BUILD)/M55_HE/firmware.bin +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HE_APP=1 +ALIF_TOC_APPS += $(BUILD)/M55_HE/firmware.bin JLINK_CMD = '\ $(JLINK_CMD_PREFIX)\ LoadFile "$(BUILD)/M55_HE/firmware.bin",0x80320000\n\ @@ -40,8 +44,9 @@ $(JLINK_CMD_SUFFIX)' else ifeq ($(MCU_CORE),M55_DUAL) -ALIF_TOC_CONFIG = mcu/M55_DUAL_cfg.json -ALIF_TOC_APPS = $(BUILD)/M55_HP/firmware.bin $(BUILD)/M55_HE/firmware.bin +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HP_APP=1 +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HE_APP=1 +ALIF_TOC_APPS += $(BUILD)/M55_HP/firmware.bin $(BUILD)/M55_HE/firmware.bin JLINK_CMD = '\ $(JLINK_CMD_PREFIX)\ LoadFile "$(BUILD)/M55_HP/firmware.bin",0x80020000\n\ @@ -81,10 +86,13 @@ $(BUILD)/M55_HP/firmware.bin: $(BUILD)/M55_HE/firmware.bin: make -f alif.mk MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 +$(BUILD)/$(ALIF_TOC_CONFIG): mcu/$(ALIF_TOC_CONFIG).in | $(BUILD) + $(ECHO) "Preprocess toc config $@" + $(Q)$(CPP) -P -E $(ALIF_TOC_CFLAGS) - < mcu/$(ALIF_TOC_CONFIG).in > $@ + $(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS) $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ - --filename $(abspath $(ALIF_TOC_CONFIG)) \ - --config-dir $(BOARD_DIR) \ + --filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \ --output-dir $(BUILD) \ --firmware-dir $(BUILD) \ --output $@ diff --git a/ports/alif/mcu/alif_cfg.json.in b/ports/alif/mcu/alif_cfg.json.in new file mode 100644 index 0000000000000..7760bd91d2e43 --- /dev/null +++ b/ports/alif/mcu/alif_cfg.json.in @@ -0,0 +1,47 @@ +{ + "DEVICE": { + "disabled" : false, + "binary": TOC_CFG_FILE, + "version" : "0.5.00", + "signed": false + }, + "HP_BOOT": { + #if TOC_CORE_M55_HP_BOOT + "disabled" : false, + #else + "disabled" : true, + #endif + "binary": "bootloader.bin", + "mramAddress": "0x80000000", + "version": "1.0.0", + "cpu_id": "M55_HP", + "flags": ["boot"], + "signed": false + }, + "HP_APP": { + #if TOC_CORE_M55_HP_APP + "disabled" : false, + #else + "disabled" : true, + #endif + "binary": "M55_HP/firmware.bin", + "mramAddress": "0x80020000", + "version": "1.0.0", + "cpu_id": "M55_HP", + "flags": ["boot"], + "signed": false + }, + "HE_APP": { + #if TOC_CORE_M55_HE_APP + "disabled" : false, + #else + "disabled" : true, + #endif + "binary": "M55_HE/firmware.bin", + "mramAddress": "0x80320000", + "version": "1.0.0", + "cpu_id": "M55_HE", + "flags": ["deferred"], + "signed": false + } +} From 293e8db9d739c4e32420a48304ac9d2251e63570 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 17 Mar 2025 13:19:07 +1100 Subject: [PATCH 66/77] alif/mpuart: Enhance UART to support bits/parity/stop and more IRQs. Signed-off-by: Damien George --- ports/alif/mpbthciport.c | 10 ++++- ports/alif/mpuart.c | 85 ++++++++++++++++++++++++++++++++++------ ports/alif/mpuart.h | 14 ++++++- 3 files changed, 92 insertions(+), 17 deletions(-) diff --git a/ports/alif/mpbthciport.c b/ports/alif/mpbthciport.c index a37c9621564a1..10a0505fe02d8 100644 --- a/ports/alif/mpbthciport.c +++ b/ports/alif/mpbthciport.c @@ -89,14 +89,20 @@ static ringbuf_t hci_rx_ringbuf = { .iput = 0, }; +static void mp_bluetooth_hci_uart_irq_callback(unsigned int uart_id, unsigned int trigger) { + if (trigger == MP_UART_IRQ_RXIDLE) { + mp_bluetooth_hci_poll_now(); + } +} + int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { hci_uart_id = port; hci_uart_first_char = true; // Initialise the UART. - mp_uart_init(hci_uart_id, baudrate, pin_BT_UART_TX, pin_BT_UART_RX, &hci_rx_ringbuf); + mp_uart_init(hci_uart_id, baudrate, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1, pin_BT_UART_TX, pin_BT_UART_RX, &hci_rx_ringbuf); mp_uart_set_flow(hci_uart_id, pin_BT_UART_RTS, pin_BT_UART_CTS); - mp_uart_set_irq_callback(hci_uart_id, mp_bluetooth_hci_poll_now); + mp_uart_set_irq_callback(hci_uart_id, MP_UART_IRQ_RXIDLE, mp_bluetooth_hci_uart_irq_callback); // Start the HCI polling to process any initial events/packets. mp_bluetooth_hci_start_polling(); diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c index 7f14ff5444702..4fe82ce123176 100644 --- a/ports/alif/mpuart.c +++ b/ports/alif/mpuart.c @@ -26,20 +26,28 @@ #include "py/mphal.h" #include "py/runtime.h" +#include "shared/runtime/softtimer.h" #include "mpuart.h" - #include "sys_ctrl_uart.h" -#include "uart.h" -#define UART_MAX (8) +#define mp_container_of(ptr, structure, member) (void *)((uintptr_t)(ptr) - offsetof(structure, member)) + +#define UART_LSR_TEMT_Pos (6) #define SYST_PCLK (100000000) +#define CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits) \ + (1 + ((data_bits) + 5) + ((parity) != UART_PARITY_NONE) + ((stop_bits) + 1)) + typedef struct _uart_state_t { UART_TRANSFER_STATUS status; + uint32_t baudrate; + uint32_t bits_per_char; ringbuf_t *rx_ringbuf; const uint8_t *tx_src; const uint8_t *tx_src_max; - void (*irq_callback)(void); + soft_timer_entry_t rx_idle_timer; + unsigned int irq_trigger; + void (*irq_callback)(unsigned int uart_id, unsigned int trigger); } uart_state_t; static const uint8_t uart_irqn[UART_MAX] = { @@ -66,7 +74,19 @@ static UART_Type *const uart_periph[UART_MAX] = { static uart_state_t uart_state[UART_MAX]; -void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf) { +// This is called by soft_timer and executes at IRQ_PRI_PENDSV. +static void rx_idle_timer_callback(soft_timer_entry_t *self) { + uart_state_t *state = mp_container_of(self, uart_state_t, rx_idle_timer); + if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_RXIDLE) { + unsigned int uart_id = state - &uart_state[0]; + state->irq_callback(uart_id, MP_UART_IRQ_RXIDLE); + } +} + +void mp_uart_init(unsigned int uart_id, uint32_t baudrate, + UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits, + mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf) { + UART_Type *uart = uart_periph[uart_id]; uart_state_t *state = &uart_state[uart_id]; @@ -82,17 +102,20 @@ void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, uart_disable_tx_irq(uart); uart_disable_rx_irq(uart); uart_set_baudrate(uart, SYST_PCLK, baudrate); - uart_set_data_parity_stop_bits(uart, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1); + uart_set_data_parity_stop_bits(uart, data_bits, parity, stop_bits); uart_set_flow_control(uart, UART_FLOW_CONTROL_NONE); uart->UART_FCR |= UART_FCR_RCVR_FIFO_RESET; uart_set_tx_trigger(uart, UART_TX_FIFO_EMPTY); - uart_set_rx_trigger(uart, UART_RX_ONE_CHAR_IN_FIFO); + uart_set_rx_trigger(uart, UART_RX_FIFO_QUARTER_FULL); // Initialise the state. state->status = UART_TRANSFER_STATUS_NONE; + state->baudrate = baudrate; + state->bits_per_char = CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits); state->rx_ringbuf = rx_ringbuf; state->tx_src = NULL; state->tx_src_max = NULL; + state->irq_trigger = 0; state->irq_callback = NULL; // Enable interrupts. @@ -100,6 +123,8 @@ void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, NVIC_SetPriority(uart_irqn[uart_id], IRQ_PRI_UART_REPL); NVIC_EnableIRQ(uart_irqn[uart_id]); uart_enable_rx_irq(uart); + + soft_timer_static_init(&state->rx_idle_timer, SOFT_TIMER_MODE_ONE_SHOT, 0, rx_idle_timer_callback); } void mp_uart_deinit(unsigned int uart_id) { @@ -109,9 +134,16 @@ void mp_uart_deinit(unsigned int uart_id) { NVIC_DisableIRQ(uart_irqn[uart_id]); } -void mp_uart_set_irq_callback(unsigned int uart_id, void (*callback)(void)) { +void mp_uart_set_irq_callback(unsigned int uart_id, unsigned int trigger, void (*callback)(unsigned int uart_id, unsigned int trigger)) { + UART_Type *uart = uart_periph[uart_id]; uart_state_t *state = &uart_state[uart_id]; + state->irq_trigger = trigger; state->irq_callback = callback; + if (trigger & MP_UART_IRQ_RX) { + uart_set_rx_trigger(uart, UART_RX_ONE_CHAR_IN_FIFO); + } else { + uart_set_rx_trigger(uart, UART_RX_FIFO_QUARTER_FULL); + } } void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts) { @@ -131,10 +163,20 @@ void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate) { UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + state->baudrate = baudrate; uart_set_baudrate(uart, SYST_PCLK, baudrate); } +void mp_uart_set_bits_parity_stop(unsigned int uart_id, UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits) { + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + + state->bits_per_char = CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits); + uart_set_data_parity_stop_bits(uart, data_bits, parity, stop_bits); +} + size_t mp_uart_rx_any(unsigned int uart_id) { uart_state_t *state = &uart_state[uart_id]; if (state->rx_ringbuf != NULL) { @@ -143,6 +185,11 @@ size_t mp_uart_rx_any(unsigned int uart_id) { return 0; } +size_t mp_uart_tx_any(unsigned int uart_id) { + UART_Type *uart = uart_periph[uart_id]; + return uart->UART_TFL + !((uart->UART_LSR >> UART_LSR_TEMT_Pos) & 1); +} + int mp_uart_rx_char(unsigned int uart_id) { uart_state_t *state = &uart_state[uart_id]; if (state->rx_ringbuf != NULL && ringbuf_avail(state->rx_ringbuf)) { @@ -208,9 +255,11 @@ static void mp_uart_irq_handler(unsigned int uart_id) { } case UART_IIR_RECEIVED_DATA_AVAILABLE: - case UART_IIR_CHARACTER_TIMEOUT: + case UART_IIR_CHARACTER_TIMEOUT: { + bool had_char = false; while (uart->UART_USR & UART_USR_RECEIVE_FIFO_NOT_EMPTY) { for (uint32_t rfl = uart->UART_RFL; rfl; --rfl) { + had_char = true; int c = uart->UART_RBR; #if MICROPY_HW_ENABLE_UART_REPL && MICROPY_KBD_EXCEPTION if (uart_id == MICROPY_HW_UART_REPL) { @@ -226,13 +275,18 @@ static void mp_uart_irq_handler(unsigned int uart_id) { } } - if (iir == UART_IIR_CHARACTER_TIMEOUT) { - if (state->irq_callback != NULL) { - state->irq_callback(); + if (had_char) { + if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_RX) { + state->irq_callback(uart_id, MP_UART_IRQ_RX); } } + if (state->irq_trigger & MP_UART_IRQ_RXIDLE) { + // Wait for 2 characters worth of time before triggering the RXIDLE event. + soft_timer_reinsert(&state->rx_idle_timer, 2000 * state->bits_per_char / state->baudrate + 1); + } break; + } case UART_IIR_TRANSMIT_HOLDING_REG_EMPTY: while (uart->UART_USR & UART_USR_TRANSMIT_FIFO_NOT_FULL) { @@ -241,6 +295,9 @@ static void mp_uart_irq_handler(unsigned int uart_id) { } else { uart_disable_tx_irq(uart); state->status = UART_TRANSFER_STATUS_SEND_COMPLETE; + if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_TXIDLE) { + state->irq_callback(uart_id, MP_UART_IRQ_TXIDLE); + } break; } } @@ -273,7 +330,9 @@ DEFINE_IRQ_HANDLER(7) #define REPL_BAUDRATE (115200) void mp_uart_init_repl(void) { - mp_uart_init(MICROPY_HW_UART_REPL, REPL_BAUDRATE, pin_REPL_UART_TX, pin_REPL_UART_RX, &stdin_ringbuf); + mp_uart_init(MICROPY_HW_UART_REPL, + REPL_BAUDRATE, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1, + pin_REPL_UART_TX, pin_REPL_UART_RX, &stdin_ringbuf); } void mp_uart_write_strn_repl(const char *str, size_t len) { diff --git a/ports/alif/mpuart.h b/ports/alif/mpuart.h index 1c28da4627859..76dadaab38015 100644 --- a/ports/alif/mpuart.h +++ b/ports/alif/mpuart.h @@ -27,15 +27,25 @@ #define MICROPY_INCLUDED_ALIF2_UART_H #include "py/ringbuf.h" +#include "uart.h" -void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf); +#define UART_MAX (8) +#define MP_UART_IRQ_RX (1) +#define MP_UART_IRQ_RXIDLE (2) +#define MP_UART_IRQ_TXIDLE (4) + +void mp_uart_init(unsigned int uart_id, uint32_t baudrate, + UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits, + mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf); void mp_uart_deinit(unsigned int uart_id); -void mp_uart_set_irq_callback(unsigned int uart_id, void (*callback)(void)); +void mp_uart_set_irq_callback(unsigned int uart_id, unsigned int trigger, void (*callback)(unsigned int uart_id, unsigned int trigger)); void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts); void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate); +void mp_uart_set_bits_parity_stop(unsigned int uart_id, UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits); size_t mp_uart_rx_any(unsigned int uart_id); +size_t mp_uart_tx_any(unsigned int uart_id); int mp_uart_rx_char(unsigned int uart_id); void mp_uart_tx_data(unsigned int uart_id, const uint8_t *src, size_t len); From 29a873ec0778107bcc9b25b0ab8a32a7f9e5bba9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 14 Mar 2025 23:38:56 +1100 Subject: [PATCH 67/77] alif/machine_uart: Add machine.UART peripheral support. Signed-off-by: Damien George --- ports/alif/machine_uart.c | 560 ++++++++++++++++++++++++++++++++++++++ ports/alif/mpconfigport.h | 3 + 2 files changed, 563 insertions(+) create mode 100644 ports/alif/machine_uart.c diff --git a/ports/alif/machine_uart.c b/ports/alif/machine_uart.c new file mode 100644 index 0000000000000..746c86677f956 --- /dev/null +++ b/ports/alif/machine_uart.c @@ -0,0 +1,560 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 OpenMV LLC. + * + * 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. + */ + +// This file is never compiled standalone, it's included directly from +// extmod/machine_uart.c via MICROPY_PY_MACHINE_UART_INCLUDEFILE. + +#include "py/mphal.h" +#include "py/mperrno.h" +#include "mpuart.h" + +#define DEFAULT_UART_BAUDRATE (115200) +#define DEFAULT_UART_BITS (8) +#define DEFAULT_UART_PARITY (MP_ROM_NONE) +#define DEFAULT_UART_STOP (1) +#define DEFAULT_UART_RX_BUFFER_SIZE (256) + +typedef struct _machine_uart_obj_t { + mp_obj_base_t base; + unsigned int uart_id; + uint32_t baudrate; + UART_DATA_BITS bits; + UART_PARITY parity; + UART_STOP_BITS stop; + mp_hal_pin_obj_t tx; + mp_hal_pin_obj_t rx; + mp_hal_pin_obj_t rts; + mp_hal_pin_obj_t cts; + uint32_t flow; + uint16_t timeout; // timeout waiting for first char (in ms) + uint16_t timeout_char; // timeout waiting between chars (in ms) + uint8_t *rx_ringbuf_array; + ringbuf_t rx_ringbuf; + uint32_t irq_flags; + uint32_t irq_trigger; + mp_irq_obj_t irq_obj; +} machine_uart_obj_t; + +typedef struct _machine_uart_default_pins_t { + mp_hal_pin_obj_t tx; + mp_hal_pin_obj_t rx; + mp_hal_pin_obj_t rts; + mp_hal_pin_obj_t cts; +} machine_uart_default_pins_t; + +static const machine_uart_default_pins_t machine_uart_default_pins[UART_MAX] = { + [0] = { + #if defined(MICROPY_HW_UART0_TX) && defined(MICROPY_HW_UART0_RX) + MICROPY_HW_UART0_TX, MICROPY_HW_UART0_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART0_RTS) && defined(MICROPY_HW_UART0_CTS) + MICROPY_HW_UART0_RTS, MICROPY_HW_UART0_CTS, + #else + NULL, NULL, + #endif + }, + [1] = { + #if defined(MICROPY_HW_UART1_TX) && defined(MICROPY_HW_UART1_RX) + MICROPY_HW_UART1_TX, MICROPY_HW_UART1_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART1_RTS) && defined(MICROPY_HW_UART1_CTS) + MICROPY_HW_UART1_RTS, MICROPY_HW_UART1_CTS, + #else + NULL, NULL, + #endif + }, + [2] = { + #if defined(MICROPY_HW_UART2_TX) && defined(MICROPY_HW_UART2_RX) + MICROPY_HW_UART2_TX, MICROPY_HW_UART2_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART2_RTS) && defined(MICROPY_HW_UART2_CTS) + MICROPY_HW_UART2_RTS, MICROPY_HW_UART2_CTS, + #else + NULL, NULL, + #endif + }, + [3] = { + #if defined(MICROPY_HW_UART3_TX) && defined(MICROPY_HW_UART3_RX) + MICROPY_HW_UART3_TX, MICROPY_HW_UART3_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART3_RTS) && defined(MICROPY_HW_UART3_CTS) + MICROPY_HW_UART3_RTS, MICROPY_HW_UART3_CTS, + #else + NULL, NULL, + #endif + }, + [4] = { + #if defined(MICROPY_HW_UART4_TX) && defined(MICROPY_HW_UART4_RX) + MICROPY_HW_UART4_TX, MICROPY_HW_UART4_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART4_RTS) && defined(MICROPY_HW_UART4_CTS) + MICROPY_HW_UART4_RTS, MICROPY_HW_UART4_CTS, + #else + NULL, NULL, + #endif + }, + [5] = { + #if defined(MICROPY_HW_UART5_TX) && defined(MICROPY_HW_UART5_RX) + MICROPY_HW_UART5_TX, MICROPY_HW_UART5_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART5_RTS) && defined(MICROPY_HW_UART5_CTS) + MICROPY_HW_UART5_RTS, MICROPY_HW_UART5_CTS, + #else + NULL, NULL, + #endif + }, + [6] = { + #if defined(MICROPY_HW_UART6_TX) && defined(MICROPY_HW_UART6_RX) + MICROPY_HW_UART6_TX, MICROPY_HW_UART6_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART6_RTS) && defined(MICROPY_HW_UART6_CTS) + MICROPY_HW_UART6_RTS, MICROPY_HW_UART6_CTS, + #else + NULL, NULL, + #endif + }, + [7] = { + #if defined(MICROPY_HW_UART7_TX) && defined(MICROPY_HW_UART7_RX) + MICROPY_HW_UART7_TX, MICROPY_HW_UART7_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART7_RTS) && defined(MICROPY_HW_UART7_CTS) + MICROPY_HW_UART7_RTS, MICROPY_HW_UART7_CTS, + #else + NULL, NULL, + #endif + }, +}; + +static void machine_uart_irq_callback(unsigned int uart_id, unsigned int trigger); + +MP_REGISTER_ROOT_POINTER(struct _machine_uart_obj_t *machine_uart_obj_all[UART_MAX]); + +/******************************************************************************/ +// MicroPython bindings for UART + +#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_uart___del___obj) }, \ + { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_FLOW_CONTROL_RTS) }, \ + { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_FLOW_CONTROL_CTS) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(MP_UART_IRQ_RX) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(MP_UART_IRQ_RXIDLE) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_TXIDLE), MP_ROM_INT(MP_UART_IRQ_TXIDLE) }, \ + +#define GET_PIN_WITH_DEFAULT(uart_id_, pin_name, pin_selection) \ + (pin_selection == mp_const_none ? machine_uart_default_pins[uart_id_].pin_name : mp_hal_get_pin_obj(pin_selection)) + +static void machine_uart_set_bits(machine_uart_obj_t *self, mp_int_t bits) { + if (!(5 <= bits && bits <= 8)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); + } + self->bits = UART_DATA_BITS_5 + (bits - 5); +} + +static void machine_uart_set_parity(machine_uart_obj_t *self, mp_obj_t parity) { + if (parity == mp_const_none) { + self->parity = UART_PARITY_NONE; + } else if (mp_obj_get_int(parity) & 1) { + self->parity = UART_PARITY_ODD; + } else { + self->parity = UART_PARITY_EVEN; + } +} + +static void machine_uart_set_stop(machine_uart_obj_t *self, mp_int_t stop) { + if (!(1 <= stop && stop <= 2)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid stop")); + } + self->stop = UART_STOP_BITS_1 + (stop - 1); +} + +static void machine_uart_set_timeout_char(machine_uart_obj_t *self, mp_int_t timeout_char) { + // Make sure timeout_char is at least as long as a whole character (13 bits to be safe). + uint32_t min_timeout_char = 13000 / self->baudrate + 1; + self->timeout_char = MAX(min_timeout_char, timeout_char); +} + +static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + static const char *parity_name[] = {[UART_PARITY_NONE] = "None", [UART_PARITY_EVEN] = "0", [UART_PARITY_ODD] = "1"}; + + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + unsigned int bits = 5 + (self->bits - UART_DATA_BITS_5); + const char *parity = parity_name[self->parity]; + unsigned int stop = 1 + (self->stop - UART_STOP_BITS_1); + + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, " + "tx=%q, rx=%q, ", + self->uart_id, self->baudrate, bits, parity, stop, + mp_hal_pin_name(self->tx), mp_hal_pin_name(self->rx)); + if (self->rts != NULL) { + mp_printf(print, "rts=%q, ", mp_hal_pin_name(self->rts)); + } + if (self->cts != NULL) { + mp_printf(print, "cts=%q, ", mp_hal_pin_name(self->cts)); + } + mp_printf(print, "flow=", self->rts); + if (self->flow == 0) { + mp_printf(print, "0"); + } else { + if (self->flow & UART_FLOW_CONTROL_RTS) { + mp_printf(print, "RTS"); + if (self->flow & UART_FLOW_CONTROL_CTS) { + mp_printf(print, "|"); + } + } + if (self->flow & UART_FLOW_CONTROL_CTS) { + mp_printf(print, "CTS"); + } + } + mp_printf(print, ", timeout=%u, timeout_char=%u, rxbuf=%u)", + self->timeout, self->timeout_char, self->rx_ringbuf.size - 1); +} + +static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { + ARG_id, + ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, + ARG_tx, ARG_rx, ARG_rts, ARG_cts, + ARG_flow, ARG_timeout, ARG_timeout_char, ARG_rxbuf, + }; + static const mp_arg_t allowed_args[] = { + #if !defined(MICROPY_HW_DEFAULT_UART_ID) + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + #else + { MP_QSTR_id, MP_ARG_INT, {.u_int = MICROPY_HW_DEFAULT_UART_ID} }, + #endif + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = DEFAULT_UART_BAUDRATE} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = DEFAULT_UART_BITS} }, + { MP_QSTR_parity, MP_ARG_OBJ, {.u_rom_obj = DEFAULT_UART_PARITY} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = DEFAULT_UART_STOP} }, + { MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_UART_RX_BUFFER_SIZE} }, + }; + + // Parse args. + 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); + + // Get UART bus. + int uart_id = args[ARG_id].u_int; + if (uart_id < 0 || uart_id >= UART_MAX) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), uart_id); + } + + machine_uart_obj_t *self; + bool need_init; + if (MP_STATE_PORT(machine_uart_obj_all)[uart_id] == NULL) { + // Create new UART object. + self = mp_obj_malloc_with_finaliser(machine_uart_obj_t, &machine_uart_type); + self->uart_id = uart_id; + need_init = true; + } else { + // Reference existing UART object. + self = MP_STATE_PORT(machine_uart_obj_all)[uart_id]; + need_init = n_args > 1 || n_kw > 0; + } + + if (need_init) { + // Set the UART parameters. + self->baudrate = args[ARG_baudrate].u_int; + machine_uart_set_bits(self, args[ARG_bits].u_int); + machine_uart_set_parity(self, args[ARG_parity].u_obj); + machine_uart_set_stop(self, args[ARG_stop].u_int); + self->tx = GET_PIN_WITH_DEFAULT(self->uart_id, tx, args[ARG_tx].u_obj); + self->rx = GET_PIN_WITH_DEFAULT(self->uart_id, rx, args[ARG_rx].u_obj); + self->rts = GET_PIN_WITH_DEFAULT(self->uart_id, rts, args[ARG_rts].u_obj); + self->cts = GET_PIN_WITH_DEFAULT(self->uart_id, cts, args[ARG_cts].u_obj); + self->flow = args[ARG_flow].u_int; + self->timeout = args[ARG_timeout].u_int; + uint32_t min_timeout_char = 13000 / self->baudrate + 1; // 13 bits to be safe + self->timeout = MAX(min_timeout_char, args[ARG_timeout_char].u_int); + + // Check TX/RX pins are given. + if (self->tx == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("tx not given")); + } + if (self->rx == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("rx not given")); + } + + // Configure flow control. + mp_hal_pin_obj_t rts = NULL; + mp_hal_pin_obj_t cts = NULL; + if (self->flow & UART_FLOW_CONTROL_RTS) { + if (self->rts == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("rts not given")); + } + rts = self->rts; + } + if (self->flow & UART_FLOW_CONTROL_CTS) { + if (self->cts == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("cts not given")); + } + cts = self->cts; + } + + // Allocate the RX buffer. + size_t rxbuf_len = args[ARG_rxbuf].u_int; + self->rx_ringbuf_array = m_new(uint8_t, rxbuf_len + 1); + self->rx_ringbuf.buf = self->rx_ringbuf_array; + self->rx_ringbuf.size = rxbuf_len + 1; + self->rx_ringbuf.iput = 0; + self->rx_ringbuf.iget = 0; + + // Reset the IRQ state + self->irq_flags = 0; + self->irq_trigger = 0; + self->irq_obj.base.type = NULL; + + // Initialize the UART hardware. + mp_uart_init(self->uart_id, self->baudrate, self->bits, self->parity, self->stop, self->tx, self->rx, &self->rx_ringbuf); + + mp_uart_set_flow(self->uart_id, rts, cts); + } + + MP_STATE_PORT(machine_uart_obj_all)[uart_id] = self; + + return MP_OBJ_FROM_PTR(self); +} + +static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, + ARG_timeout, ARG_timeout_char, + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_parity, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + bool change_baudrate = false; + bool change_bits_parity_stop = false; + + // Change baudrate if requested. + if (args[ARG_baudrate].u_int > 0) { + self->baudrate = args[ARG_baudrate].u_int; + change_baudrate = true; + } + + // Change data bits if requested. + if (args[ARG_bits].u_int > 0) { + machine_uart_set_bits(self, args[ARG_bits].u_int); + change_bits_parity_stop = true; + } + + // Change parity if requested. + if (args[ARG_parity].u_obj != MP_OBJ_NEW_SMALL_INT(-1)) { + machine_uart_set_parity(self, args[ARG_parity].u_obj); + change_bits_parity_stop = true; + } + + // Change stop bits if requested. + if (args[ARG_stop].u_int > 0) { + machine_uart_set_stop(self, args[ARG_stop].u_int); + change_bits_parity_stop = true; + } + + // Change timeout if requested. + if (args[ARG_timeout].u_int >= 0) { + self->timeout = args[ARG_timeout].u_int; + } + + // Change timeout_char if requested. + if (args[ARG_timeout_char].u_int >= 0) { + machine_uart_set_timeout_char(self, args[ARG_timeout_char].u_int); + } + + // Reconfigure the hardware parameters that have changed. + if (change_baudrate) { + mp_uart_set_baudrate(self->uart_id, self->baudrate); + } + if (change_bits_parity_stop) { + mp_uart_set_bits_parity_stop(self->uart_id, self->bits, self->parity, self->stop); + } +} + +static mp_obj_t machine_uart___del__(mp_obj_t self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uart_deinit(self->uart_id); + MP_STATE_PORT(machine_uart_obj_all)[self->uart_id] = NULL; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_uart___del___obj, machine_uart___del__); + +// Turn off the UART bus. +static void mp_machine_uart_deinit(machine_uart_obj_t *self) { + mp_uart_deinit(self->uart_id); +} + +// Return number of characters waiting. +static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) { + return mp_uart_rx_any(self->uart_id); +} + +// Since uart.write() waits up to the last byte, uart.txdone() always returns True. +static bool mp_machine_uart_txdone(machine_uart_obj_t *self) { + return !!mp_uart_tx_any(self->uart_id); +} + +// Change the trigger for the UART IRQ. +static mp_uint_t machine_uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->irq_trigger = new_trigger; + mp_uart_set_irq_callback(self->uart_id, self->irq_trigger, machine_uart_irq_callback); + return 0; +} + +// Get UART IRQ state. +static mp_uint_t machine_uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return self->irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->irq_trigger; + } + return 0; +} + +static const mp_irq_methods_t machine_uart_irq_methods = { + .trigger = machine_uart_irq_trigger, + .info = machine_uart_irq_info, +}; + +// Retrieve and configure the UART IRQ object. +static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + if (self->irq_obj.base.type == NULL) { + mp_irq_init(&self->irq_obj, &machine_uart_irq_methods, MP_OBJ_FROM_PTR(self)); + } + + if (any_args) { + self->irq_obj.handler = args[MP_IRQ_ARG_INIT_handler].u_obj; + self->irq_obj.ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; + machine_uart_irq_trigger(MP_OBJ_FROM_PTR(self), args[MP_IRQ_ARG_INIT_trigger].u_int); + } + + return &self->irq_obj; +} + +static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t start = mp_hal_ticks_ms(); + mp_uint_t timeout = self->timeout; + uint8_t *dest = buf_in; + + for (size_t i = 0; i < size; i++) { + // Wait for the first/next character + while (!mp_uart_rx_any(self->uart_id)) { + mp_uint_t elapsed = mp_hal_ticks_ms() - start; + if (elapsed > timeout) { // timed out + if (i <= 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } else { + return i; + } + } + mp_event_handle_nowait(); + } + *dest++ = mp_uart_rx_char(self->uart_id); + start = mp_hal_ticks_ms(); // Inter-character timeout + timeout = self->timeout_char; + } + return size; +} + +static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uart_tx_data(self->uart_id, buf_in, size); + return size; +} + +static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + machine_uart_obj_t *self = self_in; + mp_uint_t ret; + if (request == MP_STREAM_POLL) { + uintptr_t flags = arg; + ret = 0; + if ((flags & MP_STREAM_POLL_RD) && mp_uart_rx_any(self->uart_id)) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR)) { + ret |= MP_STREAM_POLL_WR; + } + } else if (request == MP_STREAM_FLUSH) { + uint32_t t0 = mp_hal_ticks_ms(); + const uint32_t timeout_ms = 100 + 10 * mp_uart_tx_any(self->uart_id); + while (mp_uart_tx_any(self->uart_id)) { + if (mp_hal_ticks_ms() - t0 > timeout_ms) { + *errcode = MP_ETIMEDOUT; + ret = MP_STREAM_ERROR; + } + mp_event_handle_nowait(); + } + return 0; + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +static void machine_uart_irq_callback(unsigned int uart_id, unsigned int trigger) { + machine_uart_obj_t *self = MP_STATE_PORT(machine_uart_obj_all)[uart_id]; + self->irq_flags = trigger; + if (self->irq_obj.base.type != NULL && (self->irq_flags & self->irq_trigger)) { + mp_irq_handler(&self->irq_obj); + } +} diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 73abb8924c393..6c9e6af635f57 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -136,6 +136,9 @@ #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) +#define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/alif/machine_uart.c" +#define MICROPY_PY_MACHINE_UART_IRQ (1) #define MICROPY_PY_NETWORK (CORE_M55_HP) #ifndef MICROPY_PY_NETWORK_HOSTNAME_DEFAULT #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-alif" From 737acef5cbd8ffb5a7c0ef605741debd70e72606 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Mar 2025 14:28:57 +1100 Subject: [PATCH 68/77] alif: Support more fine-grained pin alternate function selection. Now raises an exception if the pin doesn't support the alternate function unit number and line type, eg UART0_TX (previously it only checked the peripheral). Signed-off-by: Damien George --- ports/alif/cyw43_port_spi.c | 10 +- ports/alif/machine_i2c.c | 12 +- ports/alif/machine_spi.c | 22 ++- ports/alif/mcu/ensemble_pin_alt.csv | 258 ++++++++++++++-------------- ports/alif/mcu/make-pins.py | 14 +- ports/alif/mphalport.h | 201 +++++++++++++++++++--- ports/alif/mpuart.c | 12 +- ports/alif/ospi_flash.c | 27 +-- 8 files changed, 371 insertions(+), 185 deletions(-) diff --git a/ports/alif/cyw43_port_spi.c b/ports/alif/cyw43_port_spi.c index 84d84b0e1de48..6ebcaa057dc97 100644 --- a/ports/alif/cyw43_port_spi.c +++ b/ports/alif/cyw43_port_spi.c @@ -34,6 +34,7 @@ #include "sys_ctrl_spi.h" // CYW43 is connected to SPI3. +#define HW_SPI_UNIT (3) #define HW_SPI ((SPI_Type *)SPI3_BASE) #define SPI_BAUDRATE (16000000) #define SPI_RX_FIFO_SIZE (16) @@ -56,9 +57,12 @@ static void spi_bus_init(void) { mp_hal_pin_output(pin_WL_CS); mp_hal_pin_high(pin_WL_CS); // NOTE: Alif recommends enabled input read for all SPI pins. - mp_hal_pin_config(pin_WL_SCLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); - mp_hal_pin_config(pin_WL_MOSI, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); - mp_hal_pin_config(pin_WL_MISO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + mp_hal_pin_config(pin_WL_SCLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(SPI_SCLK, HW_SPI_UNIT), true); + mp_hal_pin_config(pin_WL_MOSI, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(SPI_MOSI, HW_SPI_UNIT), true); + mp_hal_pin_config(pin_WL_MISO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(SPI_MISO, HW_SPI_UNIT), true); // Starts out clock_polarity=1, clock_phase=0. spi_mode_master(HW_SPI); diff --git a/ports/alif/machine_i2c.c b/ports/alif/machine_i2c.c index f117b93c2b1e8..a710aeeb01132 100644 --- a/ports/alif/machine_i2c.c +++ b/ports/alif/machine_i2c.c @@ -135,9 +135,9 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n // Configure I2C pins. mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SCL, self->i2c_id), true); mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SDA, self->i2c_id), true); // Initialize I2C controller. self->i2c->I2C_CON = I2C_IC_CON_ENABLE_MASTER_MODE | @@ -244,18 +244,18 @@ int machine_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_mac // Switch pins to GPIO/OD. mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_NONE, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, 0, true); mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_NONE, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, 0, true); // Perform the transfer. ret = mp_machine_soft_i2c_transfer(&soft_i2c.base, addr, 1, &bufs, flags); // Re-configure I2C pins. mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SCL, self->i2c_id), true); mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SDA, self->i2c_id), true); return ret; } diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c index d13c6368f2515..abb60bc4e8325 100644 --- a/ports/alif/machine_spi.c +++ b/ports/alif/machine_spi.c @@ -60,6 +60,20 @@ static machine_spi_obj_t machine_spi_obj[] = { }; +static const uint8_t spi_pin_alt[] = { + MP_HAL_PIN_ALT_SPI_SCLK, + MP_HAL_PIN_ALT_SPI_MISO, + MP_HAL_PIN_ALT_SPI_MOSI, + MP_HAL_PIN_ALT_SPI_SS0, +}; + +static const uint8_t lpspi_pin_alt[] = { + MP_HAL_PIN_ALT_LPSPI_SCLK, + MP_HAL_PIN_ALT_LPSPI_MISO, + MP_HAL_PIN_ALT_LPSPI_MOSI, + MP_HAL_PIN_ALT_LPSPI_SS, +}; + static inline uint32_t spi_get_clk(machine_spi_obj_t *spi) { return spi->is_lp ? GetSystemCoreClock() : GetSystemAHBClock(); } @@ -131,9 +145,15 @@ static void spi_init(machine_spi_obj_t *spi, uint32_t baudrate, } // Configure SPI pins. + const uint8_t *alt; + if (spi->id <= 3) { + alt = spi_pin_alt; + } else { + alt = lpspi_pin_alt; + } for (size_t i = 0; i < MP_ARRAY_SIZE(pins) && pins[i]; i++) { mp_hal_pin_config(pins[i], MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_MAKE(alt[i], spi->id), true); } // Disable all interrupts. diff --git a/ports/alif/mcu/ensemble_pin_alt.csv b/ports/alif/mcu/ensemble_pin_alt.csv index 10bfedc9a1d68..34f13e86f6d3c 100644 --- a/ports/alif/mcu/ensemble_pin_alt.csv +++ b/ports/alif/mcu/ensemble_pin_alt.csv @@ -1,130 +1,130 @@ Pin,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7 -P0_0,,OSPI,UART,I3C,UT,LPCAM,CAM,ANA -P0_1,,OSPI,UART,I3C,UT,LPCAM,CAM,ANA -P0_2,,OSPI,UART,I2C,UT,LPCAM,CAM,ANA -P0_3,,OSPI,UART,I2C,UT,LPCAM,CAM,ANA -P0_4,,OSPI,UART,PDM,I2C,UT,,ANA -P0_5,,OSPI,UART,PDM,I2C,UT,,ANA -P0_6,,OSPI,UART,PDM,I2C,UT,,ANA -P0_7,,OSPI,UART,PDM,I2C,UT,CDC,ANA -P1_0,,UART,SPI,I2C,UT,LPCAM,ETH,ANA -P1_1,,UART,SPI,I2C,UT,LPCAM,ETH,ANA -P1_2,,UART,SPI,I3C,UT,LPCAM,ETH,ANA -P1_3,,UART,SPI,I3C,UT,LPCAM,ETH,ANA -P1_4,,OSPI,UART,SPI,UT,LPCAM,ETH,ANA -P1_5,,OSPI,UART,SPI,UT,LPCAM,ETH,ANA -P1_6,,OSPI,UART,I2S,UT,LPCAM,ETH,ANA -P1_7,,OSPI,UART,I2S,UT,LPCAM,ETH,ANA -P2_0,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA -P2_1,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA -P2_2,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA -P2_3,,OSPI,UART,LPPDM,UT,LPCAM,CDC,ANA -P2_4,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA -P2_5,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA -P2_6,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA -P2_7,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA -P3_0,,OSPI,UART,PDM,I2S,QEC,LPCAM,CAM -P3_1,,OSPI,UART,PDM,I2S,QEC,LPCAM,CAM -P3_2,,OSPI,PDM,I2S,I3C,QEC,LPCAM,CAM -P3_3,,OSPI,PDM,I2S,I3C,QEC,LPCAM,CAM -P3_4,,OSPI,UART,LPPDM,I2S,I2C,QEC,CAM -P3_5,,OSPI,UART,LPPDM,SPI,I2C,QEC,CAM -P3_6,,HFXO,LPUART,LPPDM,SPI,I2C,QEC,CAM -P3_7,,JTAG,LPUART,LPPDM,SPI,I2C,QEC,CAM -P4_0,,JTAG,,I2S,SPI,QEC,CDC,CAM -P4_1,,JTAG,I2S,SPI,QEC,SD,CDC,CAM -P4_2,,JTAG,,I2S,SPI,QEC,SD,CAM -P4_3,,JTAG,,I2S,SPI,QEC,SD,CAM -P4_4,,JTAG,I2S,SPI,FAULT,,, -P4_5,,JTAG,SPI,FAULT,,,, -P4_6,,JTAG,SPI,FAULT,,,, -P4_7,,JTAG,SPI,FAULT,,,, -P5_0,,OSPI,UART,PDM,SPI,I2C,UT,SD -P5_1,,OSPI,UART,PDM,SPI,I2C,UT,SD -P5_2,,OSPI,UART,PDM,SPI,LPI2C,UT,SD -P5_3,,OSPI,UART,SPI,LPI2C,UT,SD,CDC -P5_4,,OSPI,UART,PDM,SPI,UT,SD,CDC -P5_5,,OSPI,UART,PDM,UT,SD,ETH,CDC -# P5_6 doesn't really have OSPI on AF1 but it's needed for P10_7 to be in OSPI mode -P5_6,,OSPI,UART,I2C,UT,SD,ETH,CDC -P5_7,,OSPI,UART,I2C,UT,SD,ETH, -P6_0,,OSPI,UART,PDM,UT,SD,ETH, -P6_1,,OSPI,UART,PDM,UT,SD,ETH, -P6_2,,OSPI,UART,,PDM,UT,SD,ETH -P6_3,,OSPI,UART,,PDM,UT,SD,ETH -P6_4,,OSPI,UART,,SPI,UT,SD,ETH -P6_5,,OSPI,UART,,SPI,UT,SD,ETH -P6_6,,OSPI,UART,,SPI,UT,SD,ETH -P6_7,,OSPI,UART,PDM,SPI,UT,SD,ETH -P7_0,,,CMP,SPI,I2C,UT,SD, -P7_1,,,CMP,SPI,I2C,UT,SD, -P7_2,,,UART,CMP,SPI,I2C,UT,SD -P7_3,,,UART,CMP,SPI,I2C,UT, -P7_4,,,LPUART,LPPDM,LPSPI,LPI2C,UT, -P7_5,,,LPUART,,LPPDM,LPSPI,LPI2C,UT -P7_6,,,LPUART,,LPPDM,LPSPI,I3C,UT -P7_7,,,LPUART,,LPPDM,LPSPI,I3C,UT -P8_0,,OSPI,AUDIO,FAULT,LPCAM,SD,CDC,CAM -P8_1,,I2S,FAULT,LPCAM,SD,CDC,CAM, -P8_2,,I2S,SPI,FAULT,LPCAM,SD,CDC,CAM -P8_3,,I2S,SPI,FAULT,LPCAM,SD,CDC,CAM -P8_4,,I2S,SPI,QEC,LPCAM,SD,CDC,CAM -P8_5,,,SPI,QEC,LPCAM,SD,CDC,CAM -P8_6,,,I2S,QEC,LPCAM,SD,CDC,CAM -P8_7,,,I2S,QEC,LPCAM,SD,CDC,CAM -P9_0,,,I2S,QEC,SD,CDC,CAM, -P9_1,,LPUART,I2S,QEC,SD,CDC,CAM, -P9_2,,LPUART,I2S,SPI,QEC,SD,CDC,CAM -P9_3,,HFXO,UART,I2S,SPI,QEC,CDC,CAM -P9_4,,UART,I2S,SPI,I2C,QEC,CDC,CAM -P9_5,,OSPI,I2S,SPI,I2C,QEC,CDC,CAM -P9_6,,OSPI,AUDIO,SPI,I2C,QEC,CDC,CAM -P9_7,,OSPI,UART,SPI,I2C,QEC,CDC,CAM -P10_0,,OSPI,UART,SPI,UT,LPCAM,CDC,CAM -P10_1,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM -P10_2,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM -P10_3,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM -P10_4,,OSPI,,LPI2S,I2C,UT,ETH,CDC -P10_5,,UART,I2S,SPI,I2C,UT,ETH,CDC -P10_6,,UART,I2S,SPI,I2C,UT,ETH,CDC -P10_7,,UART,I2S,SPI,I2C,UT,CDC,OSPI -P11_0,,OSPI,UART,I2S,SPI,UT,ETH,CDC -P11_1,,OSPI,UART,SPI,UT,ETH,CDC, -P11_2,,OSPI,UART,LPPDM,SPI,UT,ETH,CDC -P11_3,,OSPI,UART,LPPDM,SPI,UT,ETH,CDC -P11_4,,OSPI,UART,PDM,LPSPI,UT,ETH,CDC -P11_5,,OSPI,UART,PDM,LPSPI,UT,ETH,CDC -P11_6,,OSPI,UART,LPPDM,LPSPI,UT,ETH,CDC -P11_7,,OSPI,UART,LPPDM,LPSPI,UT,ETH,CDC -P12_0,,OSPI,AUDIO,I2S,UT,CDC,, -P12_1,,OSPI,UART,I2S,UT,CDC,, -P12_2,,OSPI,UART,I2S,UT,CDC,, -P12_3,,OSPI,UART,I2S,UT,CDC,, -P12_4,,OSPI,SPI,UT,,CDC,, -P12_5,,,SPI,UT,,CDC,, -P12_6,,,SPI,UT,,CDC,, -P12_7,,OSPI,,SPI,UT,CDC,, -P13_0,,OSPI,,SPI,QEC,SD,CDC, -P13_1,,OSPI,SPI,QEC,SD,CDC,, -P13_2,,OSPI,SPI,QEC,SD,CDC,, -P13_3,,OSPI,SPI,QEC,SD,CDC,, -P13_4,,OSPI,LPI2S,QEC,SD,CDC,, -P13_5,,OSPI,LPI2S,QEC,SD,CDC,, -P13_6,,OSPI,LPI2S,QEC,SD,CDC,, -P13_7,,OSPI,LPI2S,QEC,SD,CDC,, -P14_0,,OSPI,UART,QEC,SD,,, -P14_1,,OSPI,UART,,QEC,SD,, -P14_2,,OSPI,UART,,QEC,SD,, -P14_3,,OSPI,UART,,QEC,,, -P14_4,,CMP,SPI,FAULT,,,, -P14_5,,CMP,SPI,FAULT,,,, -P14_6,,CMP,SPI,FAULT,,,, -P14_7,,CMP,SPI,FAULT,,,, -P15_0,,LPTMR,,,,,, -P15_1,,LPTMR,,,,,, -P15_2,,LPTMR,,,,,, -P15_3,,LPTMR,,,,,, -P15_4,,,,,,,, -P15_5,,,,,,,, -P15_6,,,,,,,, -P15_7,,,,,,,, +P0_0,GPIO,OSPI0_D0,UART0_RX,I3C_SDA,UT0_T0,LPCAM_HSYNC,CAM_HSYNC,ANA_S0 +P0_1,GPIO,OSPI0_D1,UART0_TX,I3C_SCL,UT0_T1,LPCAM_VSYNC,CAM_VSYNC,ANA_S1 +P0_2,GPIO,OSPI0_D2,UART0_CTS,I2C0_SDA,UT1_T0,LPCAM_PCLK,CAM_PCLK,ANA_S2 +P0_3,GPIO,OSPI0_D3,UART0_RTS,I2C0_SCL,UT1_T1,LPCAM_XVCLK,CAM_XVCLK,ANA_S3 +P0_4,GPIO,OSPI0_D4,UART1_RX,PDM_D0,I2C1_SDA,UT2_T0,,ANA_S4 +P0_5,GPIO,OSPI0_D5,UART1_TX,PDM_C0,I2C1_SCL,UT2_T1,,ANA_S5 +P0_6,GPIO,OSPI0_D6,UART1_CTS,PDM_D1,I2C2_SCL,UT3_T0,,ANA_S6 +P0_7,GPIO,OSPI0_D7,UART1_RTS,PDM_C1,I2C2_SDA,UT3_T1,CDC_DE,ANA_S7 +P1_0,GPIO,UART2_RX,SPI0_MISO,I2C3_SDA,UT4_T0,LPCAM_HSYNC,ETH_RXD0,ANA_S8 +P1_1,GPIO,UART2_TX,SPI0_MOSI,I2C3_SCL,UT4_T1,LPCAM_VSYNC,ETH_RXD1,ANA_S9 +P1_2,GPIO,UART3_RX,SPI0_SCLK,I3C_SDA,UT5_T0,LPCAM_PCLK,ETH_RST,ANA_S10 +P1_3,GPIO,UART3_TX,SPI0_SS0,I3C_SCL,UT5_T1,LPCAM_XVCLK,ETH_TXD0,ANA_S11 +P1_4,GPIO,OSPI0_SS0,UART0_RX,SPI0_SS1,UT6_T0,LPCAM_D0,ETH_TXD1,ANA_S12 +P1_5,GPIO,OSPI0_SS1,UART0_TX,SPI0_SS2,UT6_T1,LPCAM_D1,ETH_TXEN,ANA_S13 +P1_6,GPIO,OSPI0_RXDS,UART1_RX,I2S0_SDI,UT7_T0,LPCAM_D2,ETH_IRQ,ANA_S14 +P1_7,GPIO,OSPI0_SCLK,UART1_TX,I2S0_SDO,UT7_T1,LPCAM_D3,ETH_REFCLK,ANA_S15 +P2_0,GPIO,OSPI0_D0,UART2_RX,LPPDM_D0,UT8_T0,LPCAM_D4,ETH_MDIO,ANA_S16 +P2_1,GPIO,OSPI0_D1,UART2_TX,LPPDM_C0,UT8_T1,LPCAM_D5,ETH_MDC,ANA_S17 +P2_2,GPIO,OSPI0_D2,UART3_RX,LPPDM_D1,UT9_T0,LPCAM_D6,ETH_CRS_DV_C,ANA_S18 +P2_3,GPIO,OSPI0_D3,UART3_TX,LPPDM_C1,UT9_T1,LPCAM_D7,CDC_PCLK,ANA_S19 +P2_4,GPIO,OSPI0_D4,LPI2S_SDI,SPI1_MISO,UT10_T0,LPCAM_D0,CAM_D0,ANA_S20 +P2_5,GPIO,OSPI0_D5,LPI2S_SDO,SPI1_MOSI,UT10_T1,LPCAM_D1,CAM_D1,ANA_S21 +P2_6,GPIO,OSPI0_D6,LPI2S_SCLK,SPI1_SCLK,UT11_T0,LPCAM_D2,CAM_D2,ANA_S22 +P2_7,GPIO,OSPI0_D7,LPI2S_WS,SPI1_SS0,UT11_T1,LPCAM_D3,CAM_D3,ANA_S23 +P3_0,GPIO,OSPI0_SCLK,UART4_RX,PDM_D0,I2S0_SCLK,QEC0_X,LPCAM_D4,CAM_D4 +P3_1,GPIO,OSPI0_SCLKN,UART4_TX,PDM_C0,I2S0_WS,QEC0_Y,LPCAM_D5,CAM_D5 +P3_2,GPIO,OSPI0_SS0,PDM_D1,I2S1_SDI,I3C_SDA,QEC0_Z,LPCAM_D6,CAM_D6 +P3_3,GPIO,OSPI0_SS1,PDM_C1,I2S1_SDO,I3C_SCL,QEC1_X,LPCAM_D7,CAM_D7 +P3_4,GPIO,OSPI0_RXDS,UART5_RX,LPPDM_C0,I2S1_SCLK,I2C0_SCL,QEC1_Y,CAM_D8 +P3_5,GPIO,OSPI0_SCLKN,UART5_TX,LPPDM_D0,SPI0_SS1,I2C0_SDA,QEC1_Z,CAM_D9 +P3_6,GPIO,HFXO_OUT,LPUART_CTS,LPPDM_C1,SPI0_SS2,I2C1_SDA,QEC2_X,CAM_D10 +P3_7,GPIO,JTAG_TRACECLK,LPUART_RTS,LPPDM_D1,SPI1_SS1,I2C1_SCL,QEC2_Y,CAM_D11 +P4_0,GPIO,JTAG_TDATA0,,I2S1_WS,SPI1_SS2,QEC2_Z,CDC_VSYNC,CAM_D12 +P4_1,GPIO,JTAG_TDATA1,I2S0_SDI,SPI1_SS3,QEC3_X,SD_CLK,CDC_HSYNC,CAM_D13 +P4_2,GPIO,JTAG_TDATA2,,I2S0_SDO,SPI2_MISO,QEC3_Y,SD_CMD,CAM_D14 +P4_3,GPIO,JTAG_TDATA3,,I2S0_SCLK,SPI2_MOSI,QEC3_Z,SD_RST,CAM_D15 +P4_4,GPIO,JTAG_TCK,I2S0_WS,SPI2_SCLK,FAULT0_A,,, +P4_5,GPIO,JTAG_TMS,SPI2_SS0,FAULT1_A,,,, +P4_6,GPIO,JTAG_TDI,SPI2_SS1,FAULT2_A,,,, +P4_7,GPIO,JTAG_TDO,SPI2_SS2,FAULT3_A,,,, +P5_0,GPIO,OSPI1_RXDS,UART4_RX,PDM_D2,SPI0_MISO,I2C2_SDA,UT0_T0,SD_D0 +P5_1,GPIO,OSPI1_SS0,UART4_TX,PDM_D3,SPI0_MOSI,I2C2_SCL,UT0_T1,SD_D1 +P5_2,GPIO,OSPI1_SCLKN,UART5_RX,PDM_C3,SPI0_SS0,LPI2C_SCL,UT1_T0,SD_D2 +P5_3,GPIO,OSPI1_SCLK,UART5_TX,SPI0_SCLK,LPI2C_SDA,UT1_T1,SD_D3,CDC_PCLK +P5_4,GPIO,OSPI1_SS1,UART3_CTS,PDM_D2,SPI0_SS3,UT2_T0,SD_D4,CDC_DE +P5_5,GPIO,OSPI1_SCLK,UART3_RTS,PDM_D3,UT2_T1,SD_D5,ETH_RXD0,CDC_HSYNC +# P5_6 doesn't really have OSPI on AF1 but it's needed for P10_7 to be in OSPI1_RXDS mode +P5_6,GPIO,OSPI1_RXDS,UART1_CTS,I2C2_SCL,UT3_T0,SD_D6,ETH_RXD1,CDC_VSYNC +P5_7,GPIO,OSPI1_SS0,UART1_RTS,I2C2_SDA,UT3_T1,SD_D7,ETH_RST, +P6_0,GPIO,OSPI0_D0,UART4_DE,PDM_D0,UT4_T0,SD_D0,ETH_TXD0, +P6_1,GPIO,OSPI0_D1,UART5_DE,PDM_C0,UT4_T1,SD_D1,ETH_TXD1, +P6_2,GPIO,OSPI0_D2,UART2_CTS,,PDM_D1,UT5_T0,SD_D2,ETH_TXEN +P6_3,GPIO,OSPI0_D3,UART2_RTS,,PDM_C1,UT5_T1,SD_D3,ETH_IRQ +P6_4,GPIO,OSPI0_D4,UART2_CTS,,SPI1_SS0,UT6_T0,SD_D4,ETH_REFCLK +P6_5,GPIO,OSPI0_D5,UART2_RTS,,SPI1_SS1,UT6_T1,SD_D5,ETH_MDIO +P6_6,GPIO,OSPI0_D6,UART0_CTS,,SPI1_SS2,UT7_T0,SD_D6,ETH_MDC +P6_7,GPIO,OSPI0_D7,UART0_RTS,PDM_C2,SPI1_SS3,UT7_T1,SD_D7,ETH_CRS_DV_A +P7_0,GPIO,,CMP3_OUT,SPI0_MISO,I2C0_SDA,UT8_T0,SD_CMD, +P7_1,GPIO,,CMP2_OUT,SPI0_MOSI,I2C0_SCL,UT8_T1,SD_CLK, +P7_2,GPIO,,UART3_CTS,CMP1_OUT,SPI0_SCLK,I2C1_SDA,UT9_T0,SD_RST +P7_3,GPIO,,UART3_RTS,CMP0_OUT,SPI0_SS0,I2C1_SCL,UT9_T1, +P7_4,GPIO,,LPUART_CTS,LPPDM_C2,LPSPI_MISO,LPI2C_SCL,UT10_T0, +P7_5,GPIO,,LPUART_RTS,,LPPDM_D2,LPSPI_MOSI,LPI2C_SDA,UT10_T1 +P7_6,GPIO,,LPUART_RX,,LPPDM_C3,LPSPI_SCLK,I3C_SDA,UT11_T0 +P7_7,GPIO,,LPUART_TX,,LPPDM_D3,LPSPI_SS,I3C_SCL,UT11_T1 +P8_0,GPIO,OSPI1_SCLKN,AUDIO_CLK,FAULT0_B,LPCAM_D0,SD_D0,CDC_D0,CAM_D0 +P8_1,GPIO,I2S2_SDI,FAULT1_B,LPCAM_D1,SD_D1,CDC_D1,CAM_D1, +P8_2,GPIO,I2S2_SDO,SPI0_SS3,FAULT2_B,LPCAM_D2,SD_D2,CDC_D2,CAM_D2 +P8_3,GPIO,I2S2_SCLK,SPI1_MISO,FAULT3_B,LPCAM_D3,SD_D3,CDC_D3,CAM_D3 +P8_4,GPIO,I2S2_WS,SPI1_MOSI,QEC0_X,LPCAM_D4,SD_D4,CDC_D4,CAM_D4 +P8_5,GPIO,,SPI1_SCLK,QEC0_Y,LPCAM_D5,SD_D5,CDC_D5,CAM_D5 +P8_6,GPIO,,I2S3_SCLK,QEC0_Z,LPCAM_D6,SD_D6,CDC_D6,CAM_D6 +P8_7,GPIO,,I2S3_WS,QEC1_X,LPCAM_D7,SD_D7,CDC_D7,CAM_D7 +P9_0,GPIO,,I2S3_SDI,QEC1_Y,SD_CMD,CDC_D8,CAM_D8, +P9_1,GPIO,LPUART_RX,I2S3_SDO,QEC1_Z,SD_CLK,CDC_D9,CAM_D9, +P9_2,GPIO,LPUART_TX,I2S3_SDI,SPI2_MISO,QEC2_X,SD_RST,CDC_D10,CAM_D10 +P9_3,GPIO,HFXO_OUT,UART7_RX,I2S3_SDO,SPI2_MOSI,QEC2_Y,CDC_D11,CAM_D11 +P9_4,GPIO,UART7_TX,I2S3_SCLK,SPI2_SCLK,I2C3_SDA,QEC2_Z,CDC_D12,CAM_D12 +P9_5,GPIO,OSPI1_D0,I2S3_WS,SPI2_SS0,I2C3_SCL,QEC3_X,CDC_D13,CAM_D13 +P9_6,GPIO,OSPI1_D1,AUDIO_CLK,SPI2_SS1,I2C3_SDA,QEC3_Y,CDC_D14,CAM_D14 +P9_7,GPIO,OSPI1_D2,UART7_DE,SPI2_SS2,I2C3_SCL,QEC3_Z,CDC_D15,CAM_D15 +P10_0,GPIO,OSPI1_D3,UART6_DE,SPI2_SS3,UT0_T0,LPCAM_HSYNC,CDC_D16,CAM_HSYNC +P10_1,GPIO,OSPI1_D4,,LPI2S_SDI,UT0_T1,LPCAM_VSYNC,CDC_D17,CAM_VSYNC +P10_2,GPIO,OSPI1_D5,,LPI2S_SDO,UT1_T0,LPCAM_PCLK,CDC_D18,CAM_PCLK +P10_3,GPIO,OSPI1_D6,,LPI2S_SCLK,UT1_T1,LPCAM_XVCLK,CDC_D19,CAM_XVCLK +P10_4,GPIO,OSPI1_D7,,LPI2S_WS,I2C0_SDA,UT2_T0,ETH_TXD0,CDC_D20 +P10_5,GPIO,UART6_RX,I2S2_SDI,SPI3_MISO,I2C0_SCL,UT2_T1,ETH_TXD1,CDC_D21 +P10_6,GPIO,UART6_TX,I2S2_SDO,SPI3_MOSI,I2C1_SDA,UT3_T0,ETH_TXEN,CDC_D22 +P10_7,GPIO,UART7_RX,I2S2_SCLK,SPI3_SCLK,I2C1_SCL,UT3_T1,CDC_D23,OSPI1_RXDS +P11_0,GPIO,OSPI1_D0,UART7_TX,I2S2_WS,SPI3_SS0,UT4_T0,ETH_REFCLK,CDC_D0 +P11_1,GPIO,OSPI1_D1,UART7_DE,SPI3_SS1,UT4_T1,ETH_MDIO,CDC_D1, +P11_2,GPIO,OSPI1_D2,UART6_DE,LPPDM_C2,SPI3_SS2,UT5_T0,ETH_MDC,CDC_D2 +P11_3,GPIO,OSPI1_D3,UART5_RX,LPPDM_C3,SPI3_SS3,UT5_T1,ETH_RXD0,CDC_D3 +P11_4,GPIO,OSPI1_D4,UART5_TX,PDM_C2,LPSPI_MISO,UT6_T0,ETH_RXD1,CDC_D4 +P11_5,GPIO,OSPI1_D5,UART6_RX,PDM_C3,LPSPI_MOSI,UT6_T1,ETH_CRS_DV_B,CDC_D5 +P11_6,GPIO,OSPI1_D6,UART6_TX,LPPDM_D2,LPSPI_SCLK,UT7_T0,ETH_RST,CDC_D6 +P11_7,GPIO,OSPI1_D7,UART5_DE,LPPDM_D3,LPSPI_SS,UT7_T1,ETH_IRQ,CDC_D7 +P12_0,GPIO,OSPI0_SCLK,AUDIO_CLK,I2S1_SDI,UT8_T0,CDC_D8,, +P12_1,GPIO,OSPI0_SCLKN,UART4_RX,I2S1_SDO,UT8_T1,CDC_D9,, +P12_2,GPIO,OSPI0_RXDS,UART4_TX,I2S1_SCLK,UT9_T0,CDC_D10,, +P12_3,GPIO,OSPI0_SS0,UART4_DE,I2S1_WS,UT9_T1,CDC_D11,, +P12_4,GPIO,OSPI0_SS1,SPI3_MISO,UT10_T0,,CDC_D12,, +P12_5,GPIO,,SPI3_MOSI,UT10_T1,,CDC_D13,, +P12_6,GPIO,,SPI3_SCLK,UT11_T0,,CDC_D14,, +P12_7,GPIO,OSPI1_RXDS,,SPI3_SS0,UT11_T1,CDC_D15,, +P13_0,GPIO,OSPI1_D0,,SPI3_SS1,QEC0_X,SD_D0,CDC_D16, +P13_1,GPIO,OSPI1_D1,SPI3_SS2,QEC0_Y,SD_D1,CDC_D17,, +P13_2,GPIO,OSPI1_D2,SPI3_SS3,QEC0_Z,SD_D2,CDC_D18,, +P13_3,GPIO,OSPI1_D3,SPI2_SS3,QEC1_X,SD_D3,CDC_D19,, +P13_4,GPIO,OSPI1_D4,LPI2S_SDI,QEC1_Y,SD_D4,CDC_D20,, +P13_5,GPIO,OSPI1_D5,LPI2S_SDO,QEC1_Z,SD_D5,CDC_D21,, +P13_6,GPIO,OSPI1_D6,LPI2S_SCLK,QEC2_X,SD_D6,CDC_D22,, +P13_7,GPIO,OSPI1_D7,LPI2S_WS,QEC2_Y,SD_D7,CDC_D23,, +P14_0,GPIO,OSPI1_SCLK,UART6_RX,QEC2_Z,SD_CMD,,, +P14_1,GPIO,OSPI1_SCLKN,UART6_TX,,QEC3_X,SD_CLK,, +P14_2,GPIO,OSPI1_SS0,UART7_RX,,QEC3_Y,SD_RST,, +P14_3,GPIO,OSPI1_SS1,UART7_TX,,QEC3_Z,,, +P14_4,GPIO,CMP3_OUT,SPI1_MISO,FAULT0_C,,,, +P14_5,GPIO,CMP2_OUT,SPI1_MOSI,FAULT1_C,,,, +P14_6,GPIO,CMP1_OUT,SPI1_SCLK,FAULT2_C,,,, +P14_7,GPIO,CMP0_OUT,SPI1_SS0,FAULT3_C,,,, +P15_0,GPIO,LPTMR0_CLK,,,,,, +P15_1,GPIO,LPTMR1_CLK,,,,,, +P15_2,GPIO,LPTMR2_CLK,,,,,, +P15_3,GPIO,LPTMR3_CLK,,,,,, +P15_4,GPIO,,,,,,, +P15_5,GPIO,,,,,,, +P15_6,GPIO,,,,,,, +P15_7,GPIO,,,,,,, diff --git a/ports/alif/mcu/make-pins.py b/ports/alif/mcu/make-pins.py index fb000082642bc..3666953e613f5 100755 --- a/ports/alif/mcu/make-pins.py +++ b/ports/alif/mcu/make-pins.py @@ -36,11 +36,19 @@ class AlifPin(boardgen.Pin): def __init__(self, cpu_pin_name): super().__init__(cpu_pin_name) - self._afs = ["MP_HAL_PIN_ALT_NONE"] * 8 + self._afs = [("NONE", -1)] * 8 # Called for each AF defined in the csv file for this pin. def add_af(self, af_idx, af_name, af): - self._afs[af_idx] = f"MP_HAL_PIN_ALT_{af}" + if af == "GPIO": + self._afs[af_idx] = "GPIO", -1 + elif af.startswith("ANA_S"): + self._afs[af_idx] = "ANA", int(af[5:]) + else: + m = re.match(r"([A-Z0-9]+[A-Z])(\d*)_([A-Z0-9_]+)$", af) + periph, unit, line = m.groups() + unit = -1 if unit == "" else int(unit) + self._afs[af_idx] = f"{periph}_{line}", unit # Emit the struct which contains the pin instance. def definition(self): @@ -63,7 +71,7 @@ def definition(self): base=base, adc12_periph=adc12_periph, adc12_channel=adc12_channel, - alt=", ".join([f"{af}" for af in self._afs]), + alt=", ".join([f"MP_HAL_PIN_ALT({func}, {unit})" for func, unit in self._afs]), ) ) diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 2ba62db2d2e5a..4f81439b5492e 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -92,34 +92,183 @@ extern ringbuf_t stdin_ringbuf; #define mp_hal_pin_obj_t const machine_pin_obj_t * +#define MP_HAL_PIN_ALT(function, unit) (MP_HAL_PIN_ALT_MAKE((MP_HAL_PIN_ALT_##function), (unit))) +#define MP_HAL_PIN_ALT_MAKE(function, unit) ((function) | ((unit) << 8)) +#define MP_HAL_PIN_ALT_GET_FUNC(alt) ((alt) & 0xff) +#define MP_HAL_PIN_ALT_GET_UNIT(alt) ((alt) >> 8) + enum { MP_HAL_PIN_ALT_NONE = 0, + MP_HAL_PIN_ALT_GPIO, MP_HAL_PIN_ALT_ANA, - MP_HAL_PIN_ALT_AUDIO, - MP_HAL_PIN_ALT_CAM, - MP_HAL_PIN_ALT_CDC, - MP_HAL_PIN_ALT_CMP, - MP_HAL_PIN_ALT_ETH, - MP_HAL_PIN_ALT_FAULT, - MP_HAL_PIN_ALT_HFXO, - MP_HAL_PIN_ALT_I2C, - MP_HAL_PIN_ALT_I2S, - MP_HAL_PIN_ALT_I3C, - MP_HAL_PIN_ALT_JTAG, - MP_HAL_PIN_ALT_LPCAM, - MP_HAL_PIN_ALT_LPI2C, - MP_HAL_PIN_ALT_LPI2S, - MP_HAL_PIN_ALT_LPPDM, - MP_HAL_PIN_ALT_LPSPI, - MP_HAL_PIN_ALT_LPTMR, - MP_HAL_PIN_ALT_LPUART, - MP_HAL_PIN_ALT_OSPI, - MP_HAL_PIN_ALT_PDM, - MP_HAL_PIN_ALT_QEC, - MP_HAL_PIN_ALT_SD, - MP_HAL_PIN_ALT_SPI, - MP_HAL_PIN_ALT_UART, - MP_HAL_PIN_ALT_UT, + MP_HAL_PIN_ALT_AUDIO_CLK, + MP_HAL_PIN_ALT_CAM_D0, + MP_HAL_PIN_ALT_CAM_D1, + MP_HAL_PIN_ALT_CAM_D2, + MP_HAL_PIN_ALT_CAM_D3, + MP_HAL_PIN_ALT_CAM_D4, + MP_HAL_PIN_ALT_CAM_D5, + MP_HAL_PIN_ALT_CAM_D6, + MP_HAL_PIN_ALT_CAM_D7, + MP_HAL_PIN_ALT_CAM_D8, + MP_HAL_PIN_ALT_CAM_D9, + MP_HAL_PIN_ALT_CAM_D10, + MP_HAL_PIN_ALT_CAM_D11, + MP_HAL_PIN_ALT_CAM_D12, + MP_HAL_PIN_ALT_CAM_D13, + MP_HAL_PIN_ALT_CAM_D14, + MP_HAL_PIN_ALT_CAM_D15, + MP_HAL_PIN_ALT_CAM_HSYNC, + MP_HAL_PIN_ALT_CAM_PCLK, + MP_HAL_PIN_ALT_CAM_VSYNC, + MP_HAL_PIN_ALT_CAM_XVCLK, + MP_HAL_PIN_ALT_CDC_D0, + MP_HAL_PIN_ALT_CDC_D1, + MP_HAL_PIN_ALT_CDC_D2, + MP_HAL_PIN_ALT_CDC_D3, + MP_HAL_PIN_ALT_CDC_D4, + MP_HAL_PIN_ALT_CDC_D5, + MP_HAL_PIN_ALT_CDC_D6, + MP_HAL_PIN_ALT_CDC_D7, + MP_HAL_PIN_ALT_CDC_D8, + MP_HAL_PIN_ALT_CDC_D9, + MP_HAL_PIN_ALT_CDC_D10, + MP_HAL_PIN_ALT_CDC_D11, + MP_HAL_PIN_ALT_CDC_D12, + MP_HAL_PIN_ALT_CDC_D13, + MP_HAL_PIN_ALT_CDC_D14, + MP_HAL_PIN_ALT_CDC_D15, + MP_HAL_PIN_ALT_CDC_D16, + MP_HAL_PIN_ALT_CDC_D17, + MP_HAL_PIN_ALT_CDC_D18, + MP_HAL_PIN_ALT_CDC_D19, + MP_HAL_PIN_ALT_CDC_D20, + MP_HAL_PIN_ALT_CDC_D21, + MP_HAL_PIN_ALT_CDC_D22, + MP_HAL_PIN_ALT_CDC_D23, + MP_HAL_PIN_ALT_CDC_DE, + MP_HAL_PIN_ALT_CDC_HSYNC, + MP_HAL_PIN_ALT_CDC_PCLK, + MP_HAL_PIN_ALT_CDC_VSYNC, + MP_HAL_PIN_ALT_CMP_OUT, + MP_HAL_PIN_ALT_ETH_CRS_DV_A, + MP_HAL_PIN_ALT_ETH_CRS_DV_B, + MP_HAL_PIN_ALT_ETH_CRS_DV_C, + MP_HAL_PIN_ALT_ETH_IRQ, + MP_HAL_PIN_ALT_ETH_MDC, + MP_HAL_PIN_ALT_ETH_MDIO, + MP_HAL_PIN_ALT_ETH_REFCLK, + MP_HAL_PIN_ALT_ETH_RST, + MP_HAL_PIN_ALT_ETH_RXD0, + MP_HAL_PIN_ALT_ETH_RXD1, + MP_HAL_PIN_ALT_ETH_TXD0, + MP_HAL_PIN_ALT_ETH_TXD1, + MP_HAL_PIN_ALT_ETH_TXEN, + MP_HAL_PIN_ALT_FAULT_A, + MP_HAL_PIN_ALT_FAULT_B, + MP_HAL_PIN_ALT_FAULT_C, + MP_HAL_PIN_ALT_HFXO_OUT, + MP_HAL_PIN_ALT_I2C_SCL, + MP_HAL_PIN_ALT_I2C_SDA, + MP_HAL_PIN_ALT_I2S_SCLK, + MP_HAL_PIN_ALT_I2S_SDI, + MP_HAL_PIN_ALT_I2S_SDO, + MP_HAL_PIN_ALT_I2S_WS, + MP_HAL_PIN_ALT_I3C_SCL, + MP_HAL_PIN_ALT_I3C_SDA, + MP_HAL_PIN_ALT_JTAG_TCK, + MP_HAL_PIN_ALT_JTAG_TDATA0, + MP_HAL_PIN_ALT_JTAG_TDATA1, + MP_HAL_PIN_ALT_JTAG_TDATA2, + MP_HAL_PIN_ALT_JTAG_TDATA3, + MP_HAL_PIN_ALT_JTAG_TDI, + MP_HAL_PIN_ALT_JTAG_TDO, + MP_HAL_PIN_ALT_JTAG_TMS, + MP_HAL_PIN_ALT_JTAG_TRACECLK, + MP_HAL_PIN_ALT_LPCAM_D0, + MP_HAL_PIN_ALT_LPCAM_D1, + MP_HAL_PIN_ALT_LPCAM_D2, + MP_HAL_PIN_ALT_LPCAM_D3, + MP_HAL_PIN_ALT_LPCAM_D4, + MP_HAL_PIN_ALT_LPCAM_D5, + MP_HAL_PIN_ALT_LPCAM_D6, + MP_HAL_PIN_ALT_LPCAM_D7, + MP_HAL_PIN_ALT_LPCAM_HSYNC, + MP_HAL_PIN_ALT_LPCAM_PCLK, + MP_HAL_PIN_ALT_LPCAM_VSYNC, + MP_HAL_PIN_ALT_LPCAM_XVCLK, + MP_HAL_PIN_ALT_LPI2C_SCL, + MP_HAL_PIN_ALT_LPI2C_SDA, + MP_HAL_PIN_ALT_LPI2S_SCLK, + MP_HAL_PIN_ALT_LPI2S_SDI, + MP_HAL_PIN_ALT_LPI2S_SDO, + MP_HAL_PIN_ALT_LPI2S_WS, + MP_HAL_PIN_ALT_LPPDM_C0, + MP_HAL_PIN_ALT_LPPDM_C1, + MP_HAL_PIN_ALT_LPPDM_C2, + MP_HAL_PIN_ALT_LPPDM_C3, + MP_HAL_PIN_ALT_LPPDM_D0, + MP_HAL_PIN_ALT_LPPDM_D1, + MP_HAL_PIN_ALT_LPPDM_D2, + MP_HAL_PIN_ALT_LPPDM_D3, + MP_HAL_PIN_ALT_LPSPI_MISO, + MP_HAL_PIN_ALT_LPSPI_MOSI, + MP_HAL_PIN_ALT_LPSPI_SCLK, + MP_HAL_PIN_ALT_LPSPI_SS, + MP_HAL_PIN_ALT_LPTMR_CLK, + MP_HAL_PIN_ALT_LPUART_CTS, + MP_HAL_PIN_ALT_LPUART_RTS, + MP_HAL_PIN_ALT_LPUART_RX, + MP_HAL_PIN_ALT_LPUART_TX, + MP_HAL_PIN_ALT_OSPI_D0, + MP_HAL_PIN_ALT_OSPI_D1, + MP_HAL_PIN_ALT_OSPI_D2, + MP_HAL_PIN_ALT_OSPI_D3, + MP_HAL_PIN_ALT_OSPI_D4, + MP_HAL_PIN_ALT_OSPI_D5, + MP_HAL_PIN_ALT_OSPI_D6, + MP_HAL_PIN_ALT_OSPI_D7, + MP_HAL_PIN_ALT_OSPI_RXDS, + MP_HAL_PIN_ALT_OSPI_SCLK, + MP_HAL_PIN_ALT_OSPI_SCLKN, + MP_HAL_PIN_ALT_OSPI_SS0, + MP_HAL_PIN_ALT_OSPI_SS1, + MP_HAL_PIN_ALT_PDM_C0, + MP_HAL_PIN_ALT_PDM_C1, + MP_HAL_PIN_ALT_PDM_C2, + MP_HAL_PIN_ALT_PDM_C3, + MP_HAL_PIN_ALT_PDM_D0, + MP_HAL_PIN_ALT_PDM_D1, + MP_HAL_PIN_ALT_PDM_D2, + MP_HAL_PIN_ALT_PDM_D3, + MP_HAL_PIN_ALT_QEC_X, + MP_HAL_PIN_ALT_QEC_Y, + MP_HAL_PIN_ALT_QEC_Z, + MP_HAL_PIN_ALT_SD_CLK, + MP_HAL_PIN_ALT_SD_CMD, + MP_HAL_PIN_ALT_SD_D0, + MP_HAL_PIN_ALT_SD_D1, + MP_HAL_PIN_ALT_SD_D2, + MP_HAL_PIN_ALT_SD_D3, + MP_HAL_PIN_ALT_SD_D4, + MP_HAL_PIN_ALT_SD_D5, + MP_HAL_PIN_ALT_SD_D6, + MP_HAL_PIN_ALT_SD_D7, + MP_HAL_PIN_ALT_SD_RST, + MP_HAL_PIN_ALT_SPI_MISO, + MP_HAL_PIN_ALT_SPI_MOSI, + MP_HAL_PIN_ALT_SPI_SCLK, + MP_HAL_PIN_ALT_SPI_SS0, + MP_HAL_PIN_ALT_SPI_SS1, + MP_HAL_PIN_ALT_SPI_SS2, + MP_HAL_PIN_ALT_SPI_SS3, + MP_HAL_PIN_ALT_UART_CTS, + MP_HAL_PIN_ALT_UART_DE, + MP_HAL_PIN_ALT_UART_RTS, + MP_HAL_PIN_ALT_UART_RX, + MP_HAL_PIN_ALT_UART_TX, + MP_HAL_PIN_ALT_UT_T0, + MP_HAL_PIN_ALT_UT_T1, }; typedef struct _machine_pin_obj_t { @@ -130,7 +279,7 @@ typedef struct _machine_pin_obj_t { uint8_t adc12_periph : 2; uint8_t adc12_channel : 3; qstr name; - const uint8_t alt[8]; + const uint16_t alt[8]; // holds result of MP_HAL_PIN_ALT_MAKE(function, unit) } machine_pin_obj_t; mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c index 4fe82ce123176..b646e6f2be471 100644 --- a/ports/alif/mpuart.c +++ b/ports/alif/mpuart.c @@ -91,8 +91,10 @@ void mp_uart_init(unsigned int uart_id, uint32_t baudrate, uart_state_t *state = &uart_state[uart_id]; // Configure TX/RX pins. - mp_hal_pin_config(tx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, false); - mp_hal_pin_config(rx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, true); + mp_hal_pin_config(tx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_TX, uart_id), false); + mp_hal_pin_config(rx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_RX, uart_id), true); // Configure the UART peripheral. select_uart_clock_syst_pclk(uart_id); @@ -152,11 +154,13 @@ void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj unsigned int flow = UART_FLOW_CONTROL_NONE; if (rts != NULL) { flow |= UART_FLOW_CONTROL_RTS; - mp_hal_pin_config(rts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, false); + mp_hal_pin_config(rts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_RTS, uart_id), false); } if (cts != NULL) { flow |= UART_FLOW_CONTROL_CTS; - mp_hal_pin_config(cts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, true); + mp_hal_pin_config(cts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_CTS, uart_id), true); } uart_set_flow_control(uart, flow); } diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 17c8be8128da3..9ba7ebe6eca63 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -262,40 +262,41 @@ int ospi_flash_init(void) { self->pin = pin; + unsigned int unit = pin->peripheral_number; mp_hal_pin_config(pin->pin_cs, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_SS0, unit), false); mp_hal_pin_config(pin->pin_clk_p, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_SCLK, unit), false); if (pin->pin_clk_n != NULL) { mp_hal_pin_config(pin->pin_clk_n, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_SCLKN, unit), false); } if (pin->pin_rwds != NULL) { mp_hal_pin_config(pin->pin_rwds, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_RXDS, unit), true); if (pin->pin_rwds->port == PORT_10 && pin->pin_rwds->pin == PIN_7) { // Alif: P5_6 is needed to support proper alt function selection of P10_7. mp_hal_pin_config(pin_P5_6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_RXDS, unit), true); } } mp_hal_pin_config(pin->pin_d0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D0, unit), true); mp_hal_pin_config(pin->pin_d1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D1, unit), true); mp_hal_pin_config(pin->pin_d2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D2, unit), true); mp_hal_pin_config(pin->pin_d3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D3, unit), true); if (pin->pin_d4 != NULL) { mp_hal_pin_config(pin->pin_d4, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D4, unit), true); mp_hal_pin_config(pin->pin_d5, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D5, unit), true); mp_hal_pin_config(pin->pin_d6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D6, unit), true); mp_hal_pin_config(pin->pin_d7, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D7, unit), true); } // Reset the SPI flash. From 3564ce5bd8c22254c39a556232891f9d43201eac Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Mar 2025 12:35:26 +1100 Subject: [PATCH 69/77] alif/ospi_flash: Don't invalidate cache after erasing/writing. It's not needed, the MPU configures the XIP as non-cacheable. Signed-off-by: Damien George --- ports/alif/ospi_flash.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 9ba7ebe6eca63..8bcd1fb5b13af 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -393,14 +393,11 @@ int ospi_flash_erase_sector(uint32_t addr) { ospi_flash_write_cmd(self, self->set->write_en); int ret = ospi_flash_wait_wel1(self); - if (ret < 0) { - return ret; + if (ret == 0) { + ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); + ret = ospi_flash_wait_wip0(self); } - ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); - ret = ospi_flash_wait_wip0(self); - - SCB_InvalidateDCache_by_Addr(global_flash.cfg.xip_base + addr, MICROPY_HW_FLASH_BLOCK_SIZE_BYTES); return ret; } @@ -463,7 +460,6 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { offset = 0; } - SCB_InvalidateDCache_by_Addr(global_flash.cfg.xip_base + addr, len); return ret; } From df5e4ced762aac83200906c34e6dfcda7b7ccd6e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Mar 2025 12:35:50 +1100 Subject: [PATCH 70/77] alif/ospi_flash_settings: Use 8-bit DFS for XIP. To match the instruction length, so the DFS is restored to the XIP value after an erase or write (due to the final wait WIP). Signed-off-by: Damien George --- ports/alif/ospi_flash_settings.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/alif/ospi_flash_settings.h b/ports/alif/ospi_flash_settings.h index 72403c26b67b5..89686c4946c4b 100644 --- a/ports/alif/ospi_flash_settings.h +++ b/ports/alif/ospi_flash_settings.h @@ -53,7 +53,7 @@ .rxds = false, \ .bswap16 = false, \ .inst_len = OSPI_INST_L_8bit, \ - .xip_data_len = OSPI_DATA_L_16bit, \ + .xip_data_len = OSPI_DATA_L_8bit, \ .read_sr = 0x05, \ .read_sr_dummy_cycles = 8, \ .write_en = 0x06, \ @@ -70,7 +70,7 @@ .rxds = false, \ .bswap16 = false, \ .inst_len = OSPI_INST_L_8bit, \ - .xip_data_len = OSPI_DATA_L_16bit, \ + .xip_data_len = OSPI_DATA_L_8bit, \ .read_sr = 0x05, \ .read_sr_dummy_cycles = 8, \ .write_en = 0x06, \ From c395f5ebb07a2ebcafba32178f5d7680771969a3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 1 Apr 2025 23:07:27 +1100 Subject: [PATCH 71/77] alif/ospi_flash: Restore XIP settings after erase and write. Signed-off-by: Damien George --- ports/alif/ospi_ext.c | 28 ++++++++++++++++++---------- ports/alif/ospi_ext.h | 1 + ports/alif/ospi_flash.c | 16 +++++++++++++++- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/ports/alif/ospi_ext.c b/ports/alif/ospi_ext.c index e3389d8bd54da..6ddaaf4e5a325 100644 --- a/ports/alif/ospi_ext.c +++ b/ports/alif/ospi_ext.c @@ -206,20 +206,22 @@ void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_l spi_enable(ospi_cfg); } +static inline uint32_t ospi_xip_ctrlr0(uint32_t data_len) { + return CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (0 << CTRLR0_SCPOL_OFFSET) + | (0 << CTRLR0_SCPH_OFFSET) + | (0 << CTRLR0_SSTE_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); +} + void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles) { spi_disable(ospi_cfg); - uint32_t val = CTRLR0_IS_MST - | (OCTAL << CTRLR0_SPI_FRF_OFFSET) - | (0 << CTRLR0_SCPOL_OFFSET) - | (0 << CTRLR0_SCPH_OFFSET) - | (0 << CTRLR0_SSTE_OFFSET) - | (TMOD_RO << CTRLR0_TMOD_OFFSET) - | (data_len << CTRLR0_DFS_OFFSET); - - ospi_writel(ospi_cfg, ctrlr0, val); + ospi_writel(ospi_cfg, ctrlr0, ospi_xip_ctrlr0(data_len)); - val = (OCTAL << XIP_CTRL_FRF_OFFSET) + uint32_t val = (OCTAL << XIP_CTRL_FRF_OFFSET) | (0x2 << XIP_CTRL_TRANS_TYPE_OFFSET) | (ADDR_L32bit << XIP_CTRL_ADDR_L_OFFSET) | (inst_len << XIP_CTRL_INST_L_OFFSET) @@ -292,3 +294,9 @@ void ospi_xip_exit_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint16_t i ospi_xip_enable(ospi_cfg); ospi_xip_disable(ospi_cfg); } + +void ospi_xip_restore_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len) { + spi_disable(ospi_cfg); + ospi_writel(ospi_cfg, ctrlr0, ospi_xip_ctrlr0(data_len)); + spi_enable(ospi_cfg); +} diff --git a/ports/alif/ospi_ext.h b/ports/alif/ospi_ext.h index e5a2b50beb693..65e2ced1ff7bc 100644 --- a/ports/alif/ospi_ext.h +++ b/ports/alif/ospi_ext.h @@ -53,5 +53,6 @@ void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_l void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles); void ospi_xip_exit_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint16_t incr_command, uint16_t wrap_command); +void ospi_xip_restore_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len); #endif // MICROPY_INCLUDED_ALIF_OSPI_EXT_H diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 8bcd1fb5b13af..f78002eb0ef97 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -354,8 +354,9 @@ int ospi_flash_init(void) { } } - // Enter XIP mode. It will be disabled during flash read/erase/write. + // Enter XIP mode. ospi_flash_xip_enter(self); + return 0; } @@ -385,6 +386,13 @@ int ospi_flash_xip_exit(ospi_flash_t *self) { return 0; } +int ospi_flash_xip_restore(ospi_flash_t *self) { + if (self->xip_active) { + ospi_xip_restore_ext(&self->cfg, self->set->xip_data_len); + } + return 0; +} + /******************************************************************************/ // Top-level read/erase/write functions. @@ -398,6 +406,8 @@ int ospi_flash_erase_sector(uint32_t addr) { ret = ospi_flash_wait_wip0(self); } + ospi_flash_xip_restore(self); + return ret; } @@ -442,6 +452,8 @@ static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src } int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { + ospi_flash_t *self = &global_flash; + int ret = 0; uint32_t offset = addr & (PAGE_SIZE - 1); @@ -460,6 +472,8 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { offset = 0; } + ospi_flash_xip_restore(self); + return ret; } From b79b64a726d9f1796605f75980d2ae52595863f8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Mar 2025 22:40:58 +1100 Subject: [PATCH 72/77] alif/mpu: Add MPU region for OSPI1 XIP memory range. Signed-off-by: Damien George --- ports/alif/mpu.c | 4 ++++ ports/alif/mpu.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/alif/mpu.c b/ports/alif/mpu.c index 60753674ae1e5..9a051794de845 100644 --- a/ports/alif/mpu.c +++ b/ports/alif/mpu.c @@ -54,6 +54,10 @@ static const ARM_MPU_Region_t mpu_table[] __STARTUP_RO_DATA_ATTRIBUTE = { .RBAR = ARM_MPU_RBAR(0xA0000000, ARM_MPU_SH_NON, 1, 1, 0), .RLAR = ARM_MPU_RLAR(0xBFFFFFFF, MP_MPU_ATTR_NORMAL_NON_CACHEABLE) }, + [MP_MPU_REGION_OSPI1_XIP] = { /* OSPI1 XIP flash - 512MB : RO-1, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0xC0000000, ARM_MPU_SH_NON, 1, 1, 0), + .RLAR = ARM_MPU_RLAR(0xDFFFFFFF, MP_MPU_ATTR_NORMAL_NON_CACHEABLE) + }, }; void MPU_Load_Regions(void) { diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h index 1d3602941efb7..f4df496683e55 100644 --- a/ports/alif/mpu.h +++ b/ports/alif/mpu.h @@ -38,6 +38,7 @@ #define MP_MPU_REGION_MRAM (3) #define MP_MPU_REGION_OSPI_REGISTERS (4) #define MP_MPU_REGION_OSPI0_XIP (5) -#define MP_MPU_REGION_OPENAMP (6) +#define MP_MPU_REGION_OSPI1_XIP (6) +#define MP_MPU_REGION_OPENAMP (7) void mpu_config_mram(bool read_only); From 7c216d17b6e1d8394d05fd5eaae1c812c3846963 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 17 Dec 2023 12:12:06 +0100 Subject: [PATCH 73/77] alif/boards/ALIF_ENSEMBLE: Add Alif Ensemble board config. Signed-off-by: iabdalkader Signed-off-by: Damien George --- ports/alif/boards/ALIF_ENSEMBLE/board.c | 56 +++++++++++++++++++ ports/alif/boards/ALIF_ENSEMBLE/board.ld.S | 14 +++++ .../alif/boards/ALIF_ENSEMBLE/mpconfigboard.h | 56 +++++++++++++++++++ .../boards/ALIF_ENSEMBLE/mpconfigboard.mk | 11 ++++ .../alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h | 5 ++ ports/alif/boards/ALIF_ENSEMBLE/pins.csv | 30 ++++++++++ 6 files changed, 172 insertions(+) create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/board.c create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/board.ld.S create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/pins.csv diff --git a/ports/alif/boards/ALIF_ENSEMBLE/board.c b/ports/alif/boards/ALIF_ENSEMBLE/board.c new file mode 100644 index 0000000000000..72b93e31ffbea --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/board.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mphal.h" +#include "ospi_ext.h" +#include "ospi_flash.h" + +const ospi_pin_settings_t ospi_pin_settings = { + .peripheral_number = 1, + .pin_reset = pin_OSPI1_RESET, + .pin_cs = pin_OSPI1_CS, + .pin_clk_p = pin_OSPI1_SCLK, + .pin_clk_n = NULL, + .pin_rwds = pin_OSPI1_RXDS, + .pin_d0 = pin_OSPI1_D0, + .pin_d1 = pin_OSPI1_D1, + .pin_d2 = pin_OSPI1_D2, + .pin_d3 = pin_OSPI1_D3, + .pin_d4 = pin_OSPI1_D4, + .pin_d5 = pin_OSPI1_D5, + .pin_d6 = pin_OSPI1_D6, + .pin_d7 = pin_OSPI1_D7, +}; + +const ospi_flash_settings_t ospi_flash_settings[] = { + { + .jedec_id = 0x1a5b9d, + .freq_hz = 100000000, + .read_dummy_cycles = 9, + OSPI_FLASH_SETTINGS_IS25, + }, +}; +const size_t ospi_flash_settings_len = 1; diff --git a/ports/alif/boards/ALIF_ENSEMBLE/board.ld.S b/ports/alif/boards/ALIF_ENSEMBLE/board.ld.S new file mode 100644 index 0000000000000..7adcdbbcc3761 --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/board.ld.S @@ -0,0 +1,14 @@ +#include "mcu/ensemble.ld.S" + +/* Define ROMFS partition locations. */ +#if CORE_M55_HP +/* The HP core has access to the external OSPI1 flash and MRAM ROMFS partitions. */ +_micropy_hw_romfs_part0_start = 0xc1000000; +_micropy_hw_romfs_part0_size = 16M; +_micropy_hw_romfs_part1_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part1_size = LENGTH(MRAM_FS); +#else +/* The HP core has access to the MRAM ROMFS partition. */ +_micropy_hw_romfs_part0_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part0_size = LENGTH(MRAM_FS); +#endif diff --git a/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h new file mode 100644 index 0000000000000..8a6003ebbfb53 --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h @@ -0,0 +1,56 @@ +#define MICROPY_HW_BOARD_NAME "Alif Ensemble DevKit" +#define MICROPY_HW_MCU_NAME "AE722F80F55D5XX" + +#define MICROPY_HW_ENABLE_UART_REPL (CORE_M55_HP) +#define MICROPY_HW_UART_REPL (4) +#define MICROPY_HW_USB_MSC (1) +#define MICROPY_HW_ENABLE_HW_I2C (1) + +// ROMFS partitions +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (CORE_M55_HP) + +// I2C buses +#define MICROPY_HW_I2C0_SCL (pin_P0_3) +#define MICROPY_HW_I2C0_SDA (pin_P0_2) +#define MICROPY_HW_I2C1_SCL (pin_P3_7) +#define MICROPY_HW_I2C1_SDA (pin_P3_6) +#define MICROPY_HW_I2C2_SCL (pin_P5_1) +#define MICROPY_HW_I2C2_SDA (pin_P5_0) +#define MICROPY_HW_I2C3_SCL (pin_P1_1) +#define MICROPY_HW_I2C3_SDA (pin_P1_0) + +// SPI buses +#define MICROPY_HW_SPI0_MISO (pin_P1_0) +#define MICROPY_HW_SPI0_MOSI (pin_P1_1) +#define MICROPY_HW_SPI0_SCK (pin_P1_2) +#define MICROPY_HW_SPI1_MISO (pin_P2_4) +#define MICROPY_HW_SPI1_MOSI (pin_P2_5) +#define MICROPY_HW_SPI1_SCK (pin_P2_6) +#define MICROPY_HW_SPI2_MISO (pin_P4_2) +#define MICROPY_HW_SPI2_MOSI (pin_P4_3) +#define MICROPY_HW_SPI2_SCK (pin_P4_4) +#define MICROPY_HW_SPI3_MISO (pin_P12_4) +#define MICROPY_HW_SPI3_MOSI (pin_P12_5) +#define MICROPY_HW_SPI3_SCK (pin_P12_6) +#define MICROPY_HW_LPSPI0_MISO (pin_P7_4) +#define MICROPY_HW_LPSPI0_MOSI (pin_P7_5) +#define MICROPY_HW_LPSPI0_SCK (pin_P7_6) + +// UART buses +#define MICROPY_HW_UART0_TX (pin_P0_1) +#define MICROPY_HW_UART0_RX (pin_P0_0) +#define MICROPY_HW_UART0_RTS (pin_P0_3) +#define MICROPY_HW_UART0_CTS (pin_P0_2) +#define MICROPY_HW_UART1_TX (pin_P0_5) +#define MICROPY_HW_UART1_RX (pin_P0_4) +#define MICROPY_HW_UART1_RTS (pin_P0_7) +#define MICROPY_HW_UART1_CTS (pin_P0_6) +#define MICROPY_HW_REPL_UART_TX (pin_P12_2) +#define MICROPY_HW_REPL_UART_RX (pin_P12_1) + +// This is used for alif.Flash() and USB MSC. +#define MICROPY_HW_FLASH_STORAGE_BASE_ADDR (0) +#define MICROPY_HW_FLASH_STORAGE_BYTES (32 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_FS_BYTES (16 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_ROMFS_BYTES (16 * 1024 * 1024) diff --git a/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk new file mode 100644 index 0000000000000..d35a7aad84a07 --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk @@ -0,0 +1,11 @@ +MCU_SERIES = E7 +MCU_VARIANT = AE722F80F55D5XX +JLINK_DEV = AE722F80F55D5_HP +LD_FILE = boards/ALIF_ENSEMBLE/board.ld.S + +ALIF_TOOLKIT_CFG_PART = AE722F80F55D5LS +ALIF_TOOLKIT_CFG_FILE = \"app-device-config-ae7.json\" + +MICROPY_FLOAT_IMPL = float +MICROPY_PY_OPENAMP = 1 +MICROPY_PY_OPENAMP_REMOTEPROC = 1 diff --git a/ports/alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h b/ports/alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h new file mode 100644 index 0000000000000..49921bdd22ddc --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h @@ -0,0 +1,5 @@ +// This file is needed by ospi_xip/source/ospi/ospi_drv.c. +#define OSPI_XIP_ENABLE_AES_DECRYPTION (0) +#define OSPI_XIP_RX_SAMPLE_DELAY (3) +#define OSPI_XIP_DDR_DRIVE_EDGE (1) +#define OSPI_XIP_RXDS_DELAY (12) diff --git a/ports/alif/boards/ALIF_ENSEMBLE/pins.csv b/ports/alif/boards/ALIF_ENSEMBLE/pins.csv new file mode 100644 index 0000000000000..ec397d9cf604c --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/pins.csv @@ -0,0 +1,30 @@ +# OSP1 flash +OSPI1_RESET,P15_7 +OSPI1_CS,P5_7 +OSPI1_SCLK,P5_5 +OSPI1_D0,P9_5 +OSPI1_D1,P9_6 +OSPI1_D2,P9_7 +OSPI1_D3,P10_0 +OSPI1_D4,P10_1 +OSPI1_D5,P10_2 +OSPI1_D6,P10_3 +OSPI1_D7,P10_4 +OSPI1_RXDS,P10_7 + +LED_BLUE,P12_0 +LED_RED,P12_3 +JOY_LEFT,P15_0 +JOY_RIGHT,P15_1 + +# UART buses +UART0_TX,P0_1 +UART0_RX,P0_0 +UART0_RTS,P0_3 +UART0_CTS,P0_2 +UART1_TX,P0_5 +UART1_RX,P0_4 +UART1_RTS,P0_7 +UART1_CTS,P0_6 +REPL_UART_TX,P12_2 +REPL_UART_RX,P12_1 From 704d2f2d57b3e30b3b0d0cf02849e9784b60ccb6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 2 Sep 2024 21:24:47 +1000 Subject: [PATCH 74/77] alif/boards/OPENMV_AE3: Add OpenMV AE3 board definition. Supports Murata 1YN for WiFi and BLE. Signed-off-by: iabdalkader Signed-off-by: Damien George --- ports/alif/boards/OPENMV_AE3/board.c | 192 ++++++++++++++++++ ports/alif/boards/OPENMV_AE3/board.ld.S | 14 ++ ports/alif/boards/OPENMV_AE3/mpconfigboard.h | 85 ++++++++ ports/alif/boards/OPENMV_AE3/mpconfigboard.mk | 22 ++ ports/alif/boards/OPENMV_AE3/ospi_xip_user.h | 6 + ports/alif/boards/OPENMV_AE3/pins.csv | 69 +++++++ 6 files changed, 388 insertions(+) create mode 100644 ports/alif/boards/OPENMV_AE3/board.c create mode 100644 ports/alif/boards/OPENMV_AE3/board.ld.S create mode 100644 ports/alif/boards/OPENMV_AE3/mpconfigboard.h create mode 100644 ports/alif/boards/OPENMV_AE3/mpconfigboard.mk create mode 100644 ports/alif/boards/OPENMV_AE3/ospi_xip_user.h create mode 100644 ports/alif/boards/OPENMV_AE3/pins.csv diff --git a/ports/alif/boards/OPENMV_AE3/board.c b/ports/alif/boards/OPENMV_AE3/board.c new file mode 100644 index 0000000000000..65da152d8536f --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/board.c @@ -0,0 +1,192 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mphal.h" +#include "ospi_ext.h" +#include "ospi_flash.h" +#include "se_services.h" + +#define OMV_BOOT_MAGIC_ADDR (0x200FFFFCU) +#define OMV_BOOT_MAGIC_VALUE (0xB00710ADU) + +#if CORE_M55_HP +#define NPU_IRQ_NUMBER NPU_HP_IRQ_IRQn +#define NPU_BASE_ADDRESS (void *)NPU_HP_BASE +#else +#define NPU_IRQ_NUMBER NPU_HE_IRQ_IRQn +#define NPU_BASE_ADDRESS (void *)NPU_HE_BASE +#endif + +typedef struct { + volatile uint32_t ID; // 0x0 + volatile uint32_t STATUS; // 0x4 + volatile uint32_t CMD; // 0x8 + volatile uint32_t RESET; // 0xC +} npu_regs_t; + +#define NPU ((npu_regs_t *)NPU_BASE_ADDRESS) + +const ospi_pin_settings_t ospi_pin_settings = { + .peripheral_number = 0, + .pin_reset = pin_FLASH_RESET, + .pin_cs = pin_FLASH_CS, + .pin_clk_p = pin_FLASH_SCLK_P, + .pin_clk_n = pin_FLASH_SCLK_N, + .pin_rwds = pin_FLASH_DQSM, + .pin_d0 = pin_FLASH_D0, + .pin_d1 = pin_FLASH_D1, + .pin_d2 = pin_FLASH_D2, + .pin_d3 = pin_FLASH_D3, + .pin_d4 = pin_FLASH_D4, + .pin_d5 = pin_FLASH_D5, + .pin_d6 = pin_FLASH_D6, + .pin_d7 = pin_FLASH_D7, +}; + +const ospi_flash_settings_t ospi_flash_settings[] = { + { + .jedec_id = 0x3980c2, + .freq_hz = 100000000, + .read_dummy_cycles = 10, + OSPI_FLASH_SETTINGS_MX25, + }, + { + .jedec_id = 0x195b9d, + .freq_hz = 100000000, + .read_dummy_cycles = 9, + OSPI_FLASH_SETTINGS_IS25, + }, + { + .jedec_id = 0x17bb6b, + .freq_hz = 100000000, + .read_dummy_cycles = 7, + OSPI_FLASH_SETTINGS_EM, + }, +}; +const size_t ospi_flash_settings_len = 3; + +void board_startup(void) { + // Switch the USB multiplexer to use the Alif USB port. + mp_hal_pin_output(pin_USB_D_SEL); + mp_hal_pin_high(pin_USB_D_SEL); +} + +void board_enter_bootloader(void) { + *((uint32_t *)OMV_BOOT_MAGIC_ADDR) = OMV_BOOT_MAGIC_VALUE; + NVIC_SystemReset(); +} + +void board_early_init(void) { + // Set default run profile + run_profile_t run_profile = { + .dcdc_mode = DCDC_MODE_PWM, + .dcdc_voltage = DCDC_VOUT_0825, + // CLK_SRC_LFRC or CLK_SRC_LFXO + .aon_clk_src = CLK_SRC_LFXO, + // CLK_SRC_HFRC, CLK_SRC_HFXO or CLK_SRC_PLL + .run_clk_src = CLK_SRC_PLL, + #if CORE_M55_HP + .cpu_clk_freq = CLOCK_FREQUENCY_400MHZ, + #else + .cpu_clk_freq = CLOCK_FREQUENCY_160MHZ, + #endif + .scaled_clk_freq = SCALED_FREQ_XO_HIGH_DIV_38_4_MHZ, + // AON, modem aon, SSE-700 AON, modem, SYSTOP, DEBUG, SE + .power_domains = PD_VBAT_AON_MASK | PD_SSE700_AON_MASK | PD_SYST_MASK | + PD_DBSS_MASK | PD_SESS_MASK | PD_SRAMS_MASK | PD_SRAM_CTRL_AON_MASK, + // Add all memories + .memory_blocks = SERAM_MASK | SRAM0_MASK | SRAM1_MASK | MRAM_MASK | BACKUP4K_MASK | + SRAM6A_MASK | SRAM6B_MASK | SRAM7_1_MASK | SRAM7_2_MASK | SRAM7_3_MASK | + SRAM8_MASK | SRAM9_MASK | FWRAM_MASK, + .phy_pwr_gating = LDO_PHY_MASK | USB_PHY_MASK | MIPI_TX_DPHY_MASK | MIPI_RX_DPHY_MASK | + MIPI_PLL_DPHY_MASK, + .vdd_ioflex_3V3 = IOFLEX_LEVEL_3V3, + }; + + if (se_services_set_run_profile(&run_profile)) { + MICROPY_BOARD_FATAL_ERROR("se_services_set_run_profile"); + } + + // Set default off profile + off_profile_t off_profile = { + .dcdc_mode = DCDC_MODE_PWM, + .dcdc_voltage = DCDC_VOUT_0825, + // CLK_SRC_LFRC or CLK_SRC_LFXO + .aon_clk_src = CLK_SRC_LFXO, + // CLK_SRC_HFRC, CLK_SRC_HFXO or CLK_SRC_PLL + .stby_clk_src = CLK_SRC_HFRC, + .stby_clk_freq = SCALED_FREQ_RC_STDBY_76_8_MHZ, + // Disable all power domains. + .power_domains = 0, + // Add all memories + .memory_blocks = SERAM_MASK | SRAM0_MASK | SRAM1_MASK | MRAM_MASK | BACKUP4K_MASK | + SRAM6A_MASK | SRAM6B_MASK | SRAM7_1_MASK | SRAM7_2_MASK | SRAM7_3_MASK | + SRAM8_MASK | SRAM9_MASK | FWRAM_MASK, + .phy_pwr_gating = LDO_PHY_MASK | USB_PHY_MASK | MIPI_TX_DPHY_MASK | MIPI_RX_DPHY_MASK | + MIPI_PLL_DPHY_MASK, + .vdd_ioflex_3V3 = IOFLEX_LEVEL_3V3, + .vtor_address = SCB->VTOR, + .vtor_address_ns = SCB->VTOR, + .ewic_cfg = EWIC_RTC_A, + .wakeup_events = WE_LPRTC, + }; + + if (se_services_set_off_profile(&off_profile)) { + MICROPY_BOARD_FATAL_ERROR("se_services_set_off_profile"); + } + + // Select PLL for PD4 memory. + if (se_services_select_pll_source(PLL_SOURCE_PLL, PLL_TARGET_PD4_SRAM)) { + MICROPY_BOARD_FATAL_ERROR("se_services_select_pll_source"); + } +} + +MP_WEAK void board_enter_stop(void) { + // Disable NPU interrupt + NVIC_DisableIRQ(NPU_IRQ_NUMBER); + NVIC_ClearPendingIRQ(NPU_IRQ_NUMBER); + + // Soft-reset NPU + NPU->RESET = 0x00000000; + + // Wait until reset + uint32_t data = 0; + do { + // Poll channel0 status registers + data = NPU->STATUS; + } while (data); + + // Set default value, enables off for clocks and power. + NPU->CMD = 0x0000000C; +} + +MP_WEAK void board_enter_standby(void) { + +} + +MP_WEAK void board_exit_standby(void) { + +} diff --git a/ports/alif/boards/OPENMV_AE3/board.ld.S b/ports/alif/boards/OPENMV_AE3/board.ld.S new file mode 100644 index 0000000000000..0d09bb15f874f --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/board.ld.S @@ -0,0 +1,14 @@ +#include "mcu/ensemble.ld.S" + +/* Define ROMFS partition locations. */ +#if CORE_M55_HP +/* The HP core has access to the external OSPI flash and MRAM ROMFS partitions. */ +_micropy_hw_romfs_part0_start = 0xa1000000; +_micropy_hw_romfs_part0_size = 16M; +_micropy_hw_romfs_part1_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part1_size = LENGTH(MRAM_FS); +#else +/* The HP core has access to the MRAM ROMFS partition. */ +_micropy_hw_romfs_part0_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part0_size = LENGTH(MRAM_FS); +#endif diff --git a/ports/alif/boards/OPENMV_AE3/mpconfigboard.h b/ports/alif/boards/OPENMV_AE3/mpconfigboard.h new file mode 100644 index 0000000000000..cbdbd063ed3df --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/mpconfigboard.h @@ -0,0 +1,85 @@ +#define MICROPY_HW_BOARD_NAME "OpenMV-AE3" +#define MICROPY_HW_MCU_NAME "AE302F80F55D5AE" + +#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size +typedef intptr_t mp_off_t; + +#define MICROPY_HW_USB_MSC (CORE_M55_HP) +#define MICROPY_HW_ENABLE_HW_I2C (1) +#define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) + +// ROMFS partitions +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (CORE_M55_HP) + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pin_P0_5) +#define MICROPY_HW_I2C1_SDA (pin_P0_4) + +#define MICROPY_HW_I2C2_SCL (pin_P5_1) +#define MICROPY_HW_I2C2_SDA (pin_P5_0) + +#define MICROPY_HW_I2C3_SCL (pin_P1_1) +#define MICROPY_HW_I2C3_SDA (pin_P1_0) + +// SPI buses +#define MICROPY_HW_SPI0_MISO (pin_P5_0) +#define MICROPY_HW_SPI0_MOSI (pin_P5_1) +// #define MICROPY_HW_SPI0_NSS (pin_P5_2) +#define MICROPY_HW_SPI0_SCK (pin_P5_3) + +// UART buses +#define MICROPY_HW_UART1_TX (pin_P0_5) +#define MICROPY_HW_UART1_RX (pin_P0_4) +#define MICROPY_HW_UART3_TX (pin_P1_3) +#define MICROPY_HW_UART3_RX (pin_P1_2) +#define MICROPY_HW_UART3_RTS (pin_P7_3) +#define MICROPY_HW_UART3_CTS (pin_P7_2) +#define MICROPY_HW_UART4_TX (pin_P5_1) +#define MICROPY_HW_UART4_RX (pin_P5_0) +#define MICROPY_HW_UART5_TX (pin_P5_3) +#define MICROPY_HW_UART5_RX (pin_P5_2) + +#define MICROPY_HW_USB_VID 0x37C5 +#define MICROPY_HW_USB_PID 0x16E3 + +#define MICROPY_HW_USB_MANUFACTURER_STRING "OpenMV" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "OpenMV Camera" +#define MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING "OpenMV" + +extern void board_startup(void); +#define MICROPY_BOARD_STARTUP board_startup + +extern void board_early_init(void); +#define MICROPY_BOARD_EARLY_INIT board_early_init + +extern void board_enter_bootloader(void); +#define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) board_enter_bootloader() + +extern void board_enter_stop(void); +#define MICROPY_BOARD_ENTER_STOP board_enter_stop + +extern void board_enter_standby(void); +#define MICROPY_BOARD_ENTER_STANDBY board_enter_standby + +extern void board_exit_standby(void); +#define MICROPY_BOARD_EXIT_STANDBY board_exit_standby + +// This is used for alif.Flash() and USB MSC. +#define MICROPY_HW_FLASH_STORAGE_BASE_ADDR (0) +#define MICROPY_HW_FLASH_STORAGE_BYTES (32 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_FS_BYTES (16 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_ROMFS_BYTES (16 * 1024 * 1024) + +// Murata 1YN configuration +#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/w43439_sdio_1yn_7_95_59_combined.h" +#define CYW43_WIFI_NVRAM_INCLUDE_FILE "lib/cyw43-driver/firmware/wifi_nvram_1yn.h" +#define CYW43_BT_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/cyw43_btfw_1yn.h" +#define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE (2000000) +#define CYW43_BT_UART_BAUDRATE_ACTIVE_USE (2000000) + +// Bluetooth config +#define MICROPY_HW_BLE_UART_ID (0) +#define MICROPY_HW_BLE_UART_BAUDRATE (115200) diff --git a/ports/alif/boards/OPENMV_AE3/mpconfigboard.mk b/ports/alif/boards/OPENMV_AE3/mpconfigboard.mk new file mode 100644 index 0000000000000..83cc17dd15ed9 --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/mpconfigboard.mk @@ -0,0 +1,22 @@ +# TODO: alif_ensemble-cmsis-dfp only supports AE722F80F55D5XX at the moment. +MCU_SERIES = E7 +MCU_VARIANT = AE722F80F55D5XX +JLINK_DEV = AE302F80F55D5_HP +LD_FILE = boards/OPENMV_AE3/board.ld.S +PORT = /dev/ttyUSB0 + +ALIF_TOOLKIT_CFG_PART = AE302F80F55D5AE +ALIF_TOOLKIT_CFG_FILE = \"app-device-config-ae3.json\" + +CORE_M55_HP := $(if $(filter M55_HP,$(MCU_CORE)),1,0) + +# MicroPython settings +MICROPY_FLOAT_IMPL = float +MICROPY_PY_BLUETOOTH = $(CORE_M55_HP) +MICROPY_BLUETOOTH_NIMBLE = $(CORE_M55_HP) +MICROPY_PY_LWIP = $(CORE_M55_HP) +MICROPY_PY_NETWORK_CYW43 = $(CORE_M55_HP) +MICROPY_PY_SSL = $(CORE_M55_HP) +MICROPY_SSL_MBEDTLS = $(CORE_M55_HP) +MICROPY_PY_OPENAMP = 1 +MICROPY_PY_OPENAMP_REMOTEPROC = 1 diff --git a/ports/alif/boards/OPENMV_AE3/ospi_xip_user.h b/ports/alif/boards/OPENMV_AE3/ospi_xip_user.h new file mode 100644 index 0000000000000..c0bd9afeb5cea --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/ospi_xip_user.h @@ -0,0 +1,6 @@ +// This file is needed by ospi_xip/source/ospi/ospi_drv.c. +#define OSPI_XIP_ENABLE_AES_DECRYPTION (0) +#define OSPI_XIP_RX_SAMPLE_DELAY (4) +#define OSPI_XIP_DDR_DRIVE_EDGE (0) +// floor(1/4 OSPI clock cycle + 3.6ns) * 2 +#define OSPI_XIP_RXDS_DELAY (12) diff --git a/ports/alif/boards/OPENMV_AE3/pins.csv b/ports/alif/boards/OPENMV_AE3/pins.csv new file mode 100644 index 0000000000000..ab4f1a34f7e11 --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/pins.csv @@ -0,0 +1,69 @@ +# USB multiplexer +USB_D_SEL,P15_0 +USB_D_SEL_FB,P15_1 + +# LEDs +LED_RED,P0_0 +LED_GREEN,P6_3 +LED_BLUE,P6_0 + +# User switch +USR_SW,P15_7 + +# Flash on OSPI0 +FLASH_RESET,P3_3 +FLASH_CS,P3_2 +FLASH_SCLK_P,P3_0 +FLASH_SCLK_N,P3_1 +FLASH_DQSM,P1_6 +FLASH_D0,P2_0 +FLASH_D1,P2_1 +FLASH_D2,P2_2 +FLASH_D3,P2_3 +FLASH_D4,P2_4 +FLASH_D5,P2_5 +FLASH_D6,P2_6 +FLASH_D7,P2_7 + +# Murata 1YN +BT_UART_RX,P1_4 +BT_UART_TX,P1_5 +BT_UART_CTS,P6_6 +BT_UART_RTS,P6_7 +BT_REG_ON,P5_7 +BT_DEV_WAKE,P9_2 +BT_HOST_WAKE,P13_3 +WL_REG_ON,P10_4 +WL_HOST_WAKE,P11_0 +WL_I2S_SDI,P12_0 +WL_I2S_SDO,P12_1 +WL_I2S_SCLK,P12_2 +WL_I2S_WS,P12_3 +WL_IRQ,P9_6 +WL_MISO,P12_4 +WL_MOSI,P12_5 +WL_SCLK,P12_6 +WL_CS,P12_7 + +P0,P5_1 +P1,P5_0 +P2,P5_3 +P3,P5_2 +P4,P0_5 +P5,P0_4 +P6,P7_2 +P7,P7_3 +P8,P1_2 +P9,P1_3 + +# UART buses +UART1_TX,P0_5 +UART1_RX,P0_4 +UART3_TX,P1_3 +UART3_RX,P1_2 +UART3_RTS,P7_3 +UART3_CTS,P7_2 +UART4_TX,P5_1 +UART4_RX,P5_0 +UART5_TX,P5_3 +UART5_RX,P5_2 From 547207ddc889a395f831a45f508d676aff197e17 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 20 Feb 2025 12:24:19 +0100 Subject: [PATCH 75/77] github/workflows: Add Alif port to CI. Signed-off-by: iabdalkader --- .github/workflows/ports_alif.yml | 33 ++++++++++++++++++++++++++++++++ tools/ci.sh | 15 +++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 .github/workflows/ports_alif.yml diff --git a/.github/workflows/ports_alif.yml b/.github/workflows/ports_alif.yml new file mode 100644 index 0000000000000..0e96e7d816e50 --- /dev/null +++ b/.github/workflows/ports_alif.yml @@ -0,0 +1,33 @@ +name: alif port + +on: + push: + pull_request: + paths: + - '.github/workflows/*.yml' + - 'tools/**' + - 'py/**' + - 'extmod/**' + - 'shared/**' + - 'lib/**' + - 'drivers/**' + - 'ports/alif/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_alif: + strategy: + fail-fast: false + matrix: + ci_func: # names are functions in ci.sh + - alif_ae3_build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install packages + run: source tools/ci.sh && ci_alif_setup + - name: Build ci_${{matrix.ci_func }} + run: source tools/ci.sh && ci_${{ matrix.ci_func }} diff --git a/tools/ci.sh b/tools/ci.sh index 948eeeaef2d22..cfc9754837f76 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -871,3 +871,18 @@ function ci_zephyr_run_tests { # - inf_nan_arith fails pow(-1, nan) test (cd tests && ./run-tests.py -t execpty:"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -monitor null -serial pty -kernel ../ports/zephyr/build/zephyr/zephyr.elf" -d basics float --exclude inf_nan_arith) } + +######################################################################################## +# ports/alif + +function ci_alif_setup { + ci_gcc_arm_setup +} + +function ci_alif_ae3_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_HP submodules + make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_HE submodules + make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_DUAL + make ${MAKEOPTS} -C ports/alif BOARD=ALIF_ENSEMBLE MCU_CORE=M55_DUAL +} From 2ad592530256ebfdab6459e918748b729a3de7de Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 21 Oct 2024 13:31:57 +1100 Subject: [PATCH 76/77] tests/ports/alif_hardware: Add flash testing script. This test is not intended to be run automatically and does not have a corresponding .exp file. Signed-off-by: Damien George --- tests/ports/alif_hardware/flash_test.py | 149 ++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 tests/ports/alif_hardware/flash_test.py diff --git a/tests/ports/alif_hardware/flash_test.py b/tests/ports/alif_hardware/flash_test.py new file mode 100644 index 0000000000000..5be3fc2d02f3e --- /dev/null +++ b/tests/ports/alif_hardware/flash_test.py @@ -0,0 +1,149 @@ +# MIT license; Copyright (c) 2024 OpenMV LLC. +# +# Note: this test completely erases the filesystem! +# It is intended to be run manually. + +import alif +import hashlib +import os +import sys +import time +import vfs + +hash_algo = "sha256" + + +def flash_make_filesystem(): + try: + vfs.umount("/flash") + except: + pass + bdev = alif.Flash() + vfs.VfsFat.mkfs(bdev) + vfs.mount(vfs.VfsFat(bdev), "/flash") + sys.path.append("/flash") + sys.path.append("/flash/lib") + os.chdir("/flash") + + +def flash_block_test(): + try: + vfs.umount("/flash") + except: + pass + dev = alif.Flash() + + data512 = os.urandom(512) + buf512 = bytearray(512) + block_numbers = tuple(range(32)) + tuple(range(250, 266)) + + print("Block read/write integrity: ", end="") + ok = True + for block_n in block_numbers: + dev.writeblocks(block_n, data512) + dev.readblocks(block_n, buf512) + if buf512 != data512: + ok = False + print(ok) + + print("Block read back integrity: ", end="") + ok = True + for block_n in block_numbers: + dev.readblocks(block_n, buf512) + if buf512 != data512: + ok = False + print(ok) + + N = 16 * 1024 + data_big = os.urandom(N) + t0 = time.ticks_us() + dev.writeblocks(0, data_big) + dt = time.ticks_diff(time.ticks_us(), t0) + print(f"Block write speed: {len(data_big) / 1024 / dt * 1_000_000} KiB/sec") + + buf_big = bytearray(N) + t0 = time.ticks_us() + dev.readblocks(0, buf_big) + dt = time.ticks_diff(time.ticks_us(), t0) + print(f"Block read speed: {len(buf_big) / 1024 / dt * 1_000_000} KiB/sec") + + if buf_big != data_big: + raise RuntimeError("big block read-back failed") + + try: + import uctypes + + xip = memoryview(dev) + except: + xip = None + if xip is not None: + t0 = time.ticks_us() + buf_big[:] = xip[: len(buf_big)] + dt = time.ticks_diff(time.ticks_us(), t0) + print(f"XIP read speed: {len(buf_big) / 1024 / dt * 1_000_000} KiB/sec") + + if buf_big != data_big: + raise RuntimeError("XIP read-back failed") + for i in range(len(buf_big)): + if xip[i] != data_big[i]: + raise RuntimeError("XIP byte-wise read-back failed") + + +def flash_write_verify(path, size=1024 * 1024, block_size=1024): + hash_sum = getattr(hashlib, hash_algo)() + block_count = size // block_size + + print("-" * 16) + print(f"Writing file {size=} {block_size=}") + t0 = time.ticks_ms() + total_size = 0 + with open(path, "wb") as file: + for i in range(block_count): + buf = os.urandom(block_size) + # Update digest + hash_sum.update(buf) + total_size += file.write(buf) + if i % (block_count // 16) == 0: + print(f"{i}/{block_count}", end="\r") + dt = time.ticks_diff(time.ticks_ms(), t0) + print(f"Flash write finished: {total_size / 1024 / dt * 1000} KiB/sec") + digest = hash_sum.digest() + + print("Reading file... ", end="") + hash_sum = getattr(hashlib, hash_algo)() + buf = bytearray(block_size) + t0 = time.ticks_ms() + total_size = 0 + with open(path, "rb") as file: + for i in range(block_count): + total_size += file.readinto(buf) + dt = time.ticks_diff(time.ticks_ms(), t0) + print(f"finished: {total_size / 1024 / dt * 1000} KiB/sec") + + print("Verifying file... ", end="") + hash_sum = getattr(hashlib, hash_algo)() + t0 = time.ticks_ms() + total_size = 0 + with open(path, "rb") as file: + for i in range(block_count): + buf = file.read(block_size) + total_size += len(buf) + # Update digest + hash_sum.update(buf) + dt = time.ticks_diff(time.ticks_ms(), t0) + print(f"finished: {total_size / 1024 / dt * 1000} KiB/sec; ", end="") + + if digest != hash_sum.digest(): + raise RuntimeError(f"{hash_algo} checksum verify failed") + + print(f"{hash_algo} checksum verified") + + +if __name__ == "__main__": + flash_block_test() + flash_make_filesystem() + flash_write_verify("test0.bin", size=64 * 1024, block_size=1024) + flash_write_verify("test1.bin", size=128 * 1024, block_size=1024) + flash_write_verify("test2.bin", size=64 * 1024, block_size=2048) + flash_write_verify("test4.bin", size=256 * 1024, block_size=4096) + flash_write_verify("test4.bin", size=512 * 1024, block_size=16384) From 037f2dad72a7d11d461330873b50bb9ccf4fed69 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 17 Mar 2025 13:18:20 +1100 Subject: [PATCH 77/77] tests: Update UART and SPI tests to work on Alif boards. Signed-off-by: Damien George --- tests/extmod/machine_spi_rate.py | 5 ++++- tests/extmod/machine_uart_irq_txidle.py | 5 ++++- tests/extmod/machine_uart_tx.py | 6 +++++- tests/extmod_hardware/machine_uart_irq_rx.py | 6 +++++- tests/extmod_hardware/machine_uart_irq_rxidle.py | 6 +++++- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/tests/extmod/machine_spi_rate.py b/tests/extmod/machine_spi_rate.py index 7022955a3da75..c65095f22a1a5 100644 --- a/tests/extmod/machine_spi_rate.py +++ b/tests/extmod/machine_spi_rate.py @@ -13,7 +13,10 @@ # Configure pins based on the port/board details. # Values are tuples of (spi_id, sck, mosi, miso) -if "pyboard" in sys.platform: +if "alif" in sys.platform: + MAX_DELTA_MS = 20 + spi_instances = ((0, None, None, None),) +elif "pyboard" in sys.platform: spi_instances = ( (1, None, None, None), # "explicit choice of sck/mosi/miso is not implemented" (2, None, None, None), diff --git a/tests/extmod/machine_uart_irq_txidle.py b/tests/extmod/machine_uart_irq_txidle.py index feb95fabaa44f..084e9825768a8 100644 --- a/tests/extmod/machine_uart_irq_txidle.py +++ b/tests/extmod/machine_uart_irq_txidle.py @@ -12,7 +12,10 @@ import time, sys # Configure pins based on the target. -if "rp2" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + tx_pin = None +elif "rp2" in sys.platform: uart_id = 0 tx_pin = "GPIO0" rx_pin = "GPIO1" diff --git a/tests/extmod/machine_uart_tx.py b/tests/extmod/machine_uart_tx.py index e9652f3c7ac21..f0cc912da66e0 100644 --- a/tests/extmod/machine_uart_tx.py +++ b/tests/extmod/machine_uart_tx.py @@ -14,7 +14,11 @@ timing_margin_us = 100 # Configure pins based on the target. -if "esp32" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + pins = {} + bit_margin = 1 +elif "esp32" in sys.platform: uart_id = 1 pins = {} timing_margin_us = 400 diff --git a/tests/extmod_hardware/machine_uart_irq_rx.py b/tests/extmod_hardware/machine_uart_irq_rx.py index bf34900bd0826..ecc95e62ae570 100644 --- a/tests/extmod_hardware/machine_uart_irq_rx.py +++ b/tests/extmod_hardware/machine_uart_irq_rx.py @@ -15,7 +15,11 @@ byte_by_byte = False # Configure pins based on the target. -if "esp32" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + tx_pin = None + rx_pin = None +elif "esp32" in sys.platform: uart_id = 1 tx_pin = 4 rx_pin = 5 diff --git a/tests/extmod_hardware/machine_uart_irq_rxidle.py b/tests/extmod_hardware/machine_uart_irq_rxidle.py index 182ab24ebe0c4..af2412c75eedc 100644 --- a/tests/extmod_hardware/machine_uart_irq_rxidle.py +++ b/tests/extmod_hardware/machine_uart_irq_rxidle.py @@ -14,7 +14,11 @@ import time, sys # Configure pins based on the target. -if "esp32" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + tx_pin = None + rx_pin = None +elif "esp32" in sys.platform: uart_id = 1 tx_pin = 4 rx_pin = 5 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy