diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..e36c800fa --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.20) + +# Option to build the firmware for the Proxmark3/5. This will include the armsrc and bootrom directories. +option(BUILD_FIRMWARE "Build the firmware for the Proxmark3/5" ON) + +# If building for pm3 device, not client, the compiler will be arm-none-eabi-gcc, so the test program will not compile. +# This is a workaround to prevent CMake from trying to compile the test program, +# which will fail because the compiler is not for the host system. +if (BUILD_FIRMWARE) + include(${CMAKE_CURRENT_LIST_DIR}/tools/FixCompileTest.cmake) + include(${CMAKE_CURRENT_LIST_DIR}/tools/ToolchainForArm.cmake) +endif () + +# Project name and languages +project(Proxmark3 C CXX ASM) + +# add subdirectories, which will contain their own CMakeLists.txt files +if (BUILD_FIRMWARE) + add_subdirectory(armsrc) + add_subdirectory(bootrom) +else () + add_subdirectory(client) +endif () diff --git a/armlib/Exports.mk b/armlib/Exports.mk new file mode 100644 index 000000000..76c7e8c3d --- /dev/null +++ b/armlib/Exports.mk @@ -0,0 +1,40 @@ +#----------------------------------------------------------------------------- +# Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# See LICENSE.txt for the text of the license. +#----------------------------------------------------------------------------- + +ifeq ($(PLATFORM),PM5) + +ARMLIB_EXPORT_DEFS = -DCHIP_AT32F435_37 -DAT32F435RGT7 -DUSE_STDPERIPH_DRIVER -DAT_START_F435_V1 -DUSBD_SUPPORT_WINUSB=1 +ARMLIB_EXPORT_DEFS += -DCRM_MODULE_ENABLED -DMISC_MODULE_ENABLED -DGPIO_MODULE_ENABLED +ARMLIB_EXPORT_DEFS += -DUSB_MODULE_ENABLED -DQSPI_MODULE_ENABLED -DTMR_MODULE_ENABLED +ARMLIB_EXPORT_DEFS += -DERTC_MODULE_ENABLED -DPWC_MODULE_ENABLED -DSPI_MODULE_ENABLED +ARMLIB_EXPORT_DEFS += -DI2C_MODULE_ENABLED -DDMA_MODULE_ENABLED -DADC_MODULE_ENABLED +ARMLIB_EXPORT_DEFS += -DUSART_MODULE_ENABLED -DEXINT_MODULE_ENABLED -DSCFG_MODULE_ENABLED +ARMLIB_EXPORT_DEFS += -DFLASH_MODULE_ENABLED -DCRC_MODULE_ENABLED -DWDT_MODULE_ENABLED +ARMLIB_EXPORT_DEFS += -DWWDT_MODULE_ENABLED -DCAN_MODULE_ENABLED -DDAC_MODULE_ENABLED +ARMLIB_EXPORT_DEFS += -DDEBUG_MODULE_ENABLED -DDVP_MODULE_ENABLED -DEDMA_MODULE_ENABLED +ARMLIB_EXPORT_DEFS += -DSDIO_MODULE_ENABLED -DACC_MODULE_ENABLED + +ARMLIB_EXPORT_INCLUDES = -I. +ARMLIB_EXPORT_INCLUDES += -isystem ../armlib/at32_sys -isystem ../armlib/at32_sys/cmsis/cm4/core_support +ARMLIB_EXPORT_INCLUDES += -isystem ../armlib/at32_sys/cmsis/cm4/device_support -isystem ../armlib/at32_sys/drivers/inc +ARMLIB_EXPORT_INCLUDES += -isystem ../armlib/at32_usb -isystem ../armlib/at32_usb/usb_drivers/inc -isystem ../armlib/at32_usb/usbd_class/cdc +ARMLIB_EXPORT_INCLUDES += -isystem ../armlib/at32_i2c + +ARMLIB_EXPORT_CFLAGS = $(ARMLIB_EXPORT_DEFS) $(ARMLIB_EXPORT_INCLUDES) + +ARMLIB_EXPORT_LIBNAME = libarmlib.a + +endif diff --git a/armlib/Makefile b/armlib/Makefile new file mode 100644 index 000000000..fa76b6a57 --- /dev/null +++ b/armlib/Makefile @@ -0,0 +1,97 @@ +#----------------------------------------------------------------------------- +# Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# See LICENSE.txt for the text of the license. +#----------------------------------------------------------------------------- + +ifeq ($(PLATFORM),PM5) + +# Flags + Inc +include ../armlib/Exports.mk + +# Toolchain + dirs +include ../common_arm/Makefile.common + +# Allow standalone invocation when parent make doesn't export toolchain vars. +CROSS ?= arm-none-eabi- +CROSS_CC ?= $(CROSS)gcc +CROSS_CFLAGS ?= +CROSS_AR ?= $(CROSS)ar +OBJDIR ?= obj + +ARMLIBSRC = \ + ../armlib/at32_sys/cmsis/cm4/device_support/system_at32f435_437.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_crm.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_misc.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_gpio.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_usb.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_qspi.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_tmr.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_ertc.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_pwc.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_spi.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_i2c.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_dma.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_adc.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_usart.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_exint.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_scfg.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_flash.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_crc.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_wdt.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_wwdt.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_can.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_dac.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_debug.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_dvp.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_edma.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_sdio.c \ + ../armlib/at32_sys/drivers/src/at32f435_437_acc.c \ + ../armlib/at32_usb/usb_drivers/src/usb_core.c \ + ../armlib/at32_usb/usb_drivers/src/usbd_core.c \ + ../armlib/at32_usb/usb_drivers/src/usbd_int.c \ + ../armlib/at32_usb/usb_drivers/src/usbd_sdr.c \ + ../armlib/at32_usb/usbd_class/cdc/cdc_class.c \ + ../armlib/at32_i2c/at32f435_437_i2c_app.c + +ARMLIB = $(OBJDIR)/$(ARMLIB_EXPORT_LIBNAME) +ARMLIB_OBJ_FROM_SRC = $(OBJDIR)/armlib_$(subst /,_,$(patsubst ../armlib/%.c,%,$(1))).o +ARMLIB_OBJ = $(foreach src,$(ARMLIBSRC),$(call ARMLIB_OBJ_FROM_SRC,$(src))) +ARMLIB_DEP = $(ARMLIB_OBJ:.o=.d) +.DEFAULT_GOAL := $(ARMLIB) + +# TODO Artery's library has many warnings, which we can only ignore for now, unfortunately. +ARMLIB_PRIVATE_CFLAGS = -mcpu=cortex-m4 -Wno-missing-prototypes -Wno-missing-declarations +ARMLIB_CFLAGS = $(ARMLIB_EXPORT_CFLAGS) $(ARMLIB_PRIVATE_CFLAGS) + +define ARMLIB_COMPILE_RULE +$(2): $(1) $(INCLUDES) | $(OBJDIR) + $$(info [-] CC $$(notdir $(1))) + $$(Q)$$(CROSS_CC) $$(CROSS_CFLAGS) $$(ARMLIB_CFLAGS) -MMD -MP -MF $(2:.o=.d) -mthumb -c -o $(2) $(1) +endef + +define ARMLIB_REGISTER_RULES +$(foreach src,$(ARMLIBSRC),$(eval $(call ARMLIB_COMPILE_RULE,$(src),$(call ARMLIB_OBJ_FROM_SRC,$(src))))) +endef + +$(OBJDIR): + $(Q)mkdir -p $@ + +$(ARMLIB): $(ARMLIB_OBJ) | $(OBJDIR) + $(info [-] AR $@) + $(Q)$(CROSS_AR) rcs $@ $^ + +$(call ARMLIB_REGISTER_RULES) +-include $(ARMLIB_DEP) + +endif \ No newline at end of file diff --git a/armlib/at32_sys/drivers/src/at32f435_437_can.c b/armlib/at32_sys/drivers/src/at32f435_437_can.c index 29028f8a4..41efb9b7e 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_can.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_can.c @@ -23,6 +23,8 @@ */ #include "at32f435_437_conf.h" +#include "at32f435_437_can.h" +#include "at32f435_437_crm.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_sys/drivers/src/at32f435_437_crc.c b/armlib/at32_sys/drivers/src/at32f435_437_crc.c index 1dd90c835..1b28f83e5 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_crc.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_crc.c @@ -23,6 +23,7 @@ */ #include "at32f435_437_conf.h" +#include "at32f435_437_crc.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_sys/drivers/src/at32f435_437_dac.c b/armlib/at32_sys/drivers/src/at32f435_437_dac.c index eab69e080..7609200ef 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_dac.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_dac.c @@ -23,6 +23,8 @@ */ #include "at32f435_437_conf.h" +#include "at32f435_437_dac.h" +#include "at32f435_437_crm.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_sys/drivers/src/at32f435_437_debug.c b/armlib/at32_sys/drivers/src/at32f435_437_debug.c index dcad0d7de..ec7b3202f 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_debug.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_debug.c @@ -23,6 +23,7 @@ */ #include "at32f435_437_conf.h" +#include "at32f435_437_debug.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_sys/drivers/src/at32f435_437_dvp.c b/armlib/at32_sys/drivers/src/at32f435_437_dvp.c index 62c167c6d..0a5bd65fe 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_dvp.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_dvp.c @@ -23,6 +23,8 @@ */ #include "at32f435_437_conf.h" +#include "at32f435_437_dvp.h" +#include "at32f435_437_crm.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_sys/drivers/src/at32f435_437_edma.c b/armlib/at32_sys/drivers/src/at32f435_437_edma.c index d88e29cd3..2a593908d 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_edma.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_edma.c @@ -23,6 +23,8 @@ */ #include "at32f435_437_conf.h" +#include "at32f435_437_edma.h" +#include "at32f435_437_crm.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_sys/drivers/src/at32f435_437_sdio.c b/armlib/at32_sys/drivers/src/at32f435_437_sdio.c index a22a28079..24f622e83 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_sdio.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_sdio.c @@ -23,6 +23,7 @@ */ #include "at32f435_437_conf.h" +#include "at32f435_437_sdio.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_sys/drivers/src/at32f435_437_tmr.c b/armlib/at32_sys/drivers/src/at32f435_437_tmr.c index 4ddbb408e..55569e020 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_tmr.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_tmr.c @@ -23,8 +23,8 @@ */ #include "at32f435_437_conf.h" -#include "at32f435_437_crm.h" #include "at32f435_437_tmr.h" +#include "at32f435_437_crm.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_sys/drivers/src/at32f435_437_wdt.c b/armlib/at32_sys/drivers/src/at32f435_437_wdt.c index 5e9e54ce4..ea3470a8f 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_wdt.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_wdt.c @@ -23,6 +23,7 @@ */ #include "at32f435_437_conf.h" +#include "at32f435_437_wdt.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_sys/drivers/src/at32f435_437_wwdt.c b/armlib/at32_sys/drivers/src/at32f435_437_wwdt.c index 1b338f47b..d819bfadc 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_wwdt.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_wwdt.c @@ -23,6 +23,8 @@ */ #include "at32f435_437_conf.h" +#include "at32f435_437_wwdt.h" +#include "at32f435_437_crm.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_sys/drivers/src/at32f435_437_xmc.c b/armlib/at32_sys/drivers/src/at32f435_437_xmc.c index 43f84b5dc..ba4914dce 100644 --- a/armlib/at32_sys/drivers/src/at32f435_437_xmc.c +++ b/armlib/at32_sys/drivers/src/at32f435_437_xmc.c @@ -23,6 +23,7 @@ */ #include "at32f435_437_conf.h" +#include "at32f435_437_xmc.h" /** @addtogroup AT32F435_437_periph_driver * @{ diff --git a/armlib/at32_usb/usbd_class/cdc/cdc_desc.c b/armlib/at32_usb/usbd_class/cdc/cdc_desc.c deleted file mode 100644 index f920c289f..000000000 --- a/armlib/at32_usb/usbd_class/cdc/cdc_desc.c +++ /dev/null @@ -1,185 +0,0 @@ -#include "stdio.h" -#include "usbd_core.h" -#include "usb_cdc_desc.h" - - -static usbd_desc_t *get_device_descriptor(void); - -static usbd_desc_t *get_device_qualifier(void); - -static usbd_desc_t *get_device_configuration(void); - -static usbd_desc_t *get_device_other_speed(void); - -static usbd_desc_t *get_device_lang_id(void); - -static usbd_desc_t *get_device_manufacturer_string(void); - -static usbd_desc_t *get_device_product_string(void); - -static usbd_desc_t *get_device_serial_string(void); - -static usbd_desc_t *get_device_interface_string(void); - -static usbd_desc_t *get_device_config_string(void); - -static usbd_desc_t *get_winusb_os_string(void); - -/** - * @brief device descriptor handler structure - */ -usbd_desc_handler cdc_desc_handler = -{ - get_device_descriptor, - get_device_qualifier, - get_device_configuration, - get_device_other_speed, - get_device_lang_id, - // --- - get_device_manufacturer_string, - get_device_product_string, - get_device_serial_string, - get_device_interface_string, - get_device_config_string, - // --- - get_winusb_os_string, - NULL, - NULL -}; - - -/* device descriptor */ -static usbd_desc_t device_descriptor = -{ - sizeof(devDescriptor), - (uint8_t *) devDescriptor -}; - -/* config descriptor */ -static usbd_desc_t config_descriptor = -{ - sizeof(cfgDescriptor), - (uint8_t *) cfgDescriptor -}; - -/* langid descriptor */ -static usbd_desc_t langid_descriptor = -{ - sizeof(StrLanguageCodes), - (uint8_t *) StrLanguageCodes -}; - -/* serial descriptor */ -static usbd_desc_t serial_descriptor = -{ - sizeof(StrSerialNumber), - (uint8_t *) StrSerialNumber -}; - -static usbd_desc_t vp_desc; - -/** - * @brief get device descriptor - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_device_descriptor(void) { - return &device_descriptor; -} - -/** - * @brief get device qualifier - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_device_qualifier(void) { - return NULL; -} - -/** - * @brief get config descriptor - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_device_configuration(void) { - return &config_descriptor; -} - -/** - * @brief get other speed descriptor - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_device_other_speed(void) { - return NULL; -} - -/** - * @brief get lang id descriptor - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_device_lang_id(void) { - return &langid_descriptor; -} - - -/** - * @brief get manufacturer descriptor - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_device_manufacturer_string(void) { - vp_desc.length = StrManufacturer[0]; - vp_desc.descriptor = (uint8_t *) StrManufacturer; - return &vp_desc; -} - -/** - * @brief get product descriptor - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_device_product_string(void) { - vp_desc.length = StrProduct[0]; - vp_desc.descriptor = (uint8_t *) StrProduct; - return &vp_desc; -} - -/** - * @brief get serial descriptor - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_device_serial_string(void) { - return &serial_descriptor; -} - -/** - * @brief get interface descriptor - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_device_interface_string(void) { - return NULL; -} - -/** - * @brief get device config descriptor - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_device_config_string(void) { - return NULL; -} - -/** - * @brief get device config descriptor - * @param none - * @retval usbd_desc - */ -static usbd_desc_t *get_winusb_os_string(void) { - vp_desc.length = StrMS_OSDescriptor[0]; - vp_desc.descriptor = (uint8_t *) StrMS_OSDescriptor; - return &vp_desc; -} diff --git a/armlib/pm5_at32_armlib.cmake b/armlib/pm5_at32_armlib.cmake new file mode 100644 index 000000000..63d6826b2 --- /dev/null +++ b/armlib/pm5_at32_armlib.cmake @@ -0,0 +1,89 @@ +if (PM5 AND NOT TARGET pm5_at32_armlib) + add_library(pm5_at32_armlib STATIC + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/cmsis/cm4/device_support/system_at32f435_437.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_crm.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_misc.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_gpio.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_usb.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_qspi.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_tmr.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_ertc.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_pwc.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_spi.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_i2c.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_dma.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_adc.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_usart.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_exint.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_scfg.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_flash.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_crc.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_wdt.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_wwdt.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_can.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_dac.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_debug.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_dvp.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_edma.c + # ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_emac.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_sdio.c + # ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_xmc.c + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/src/at32f435_437_acc.c + ${CMAKE_CURRENT_LIST_DIR}/at32_usb/usb_drivers/src/usb_core.c + ${CMAKE_CURRENT_LIST_DIR}/at32_usb/usb_drivers/src/usbd_core.c + ${CMAKE_CURRENT_LIST_DIR}/at32_usb/usb_drivers/src/usbd_int.c + ${CMAKE_CURRENT_LIST_DIR}/at32_usb/usb_drivers/src/usbd_sdr.c + ${CMAKE_CURRENT_LIST_DIR}/at32_usb/usbd_class/cdc/cdc_class.c + ${CMAKE_CURRENT_LIST_DIR}/at32_i2c/at32f435_437_i2c_app.c + ) + + target_compile_options(pm5_at32_armlib + PRIVATE ${CROSS_CFLAGS} + PUBLIC -mcpu=cortex-m4 -Wno-missing-prototypes -Wno-missing-declarations + ) + target_compile_definitions(pm5_at32_armlib PUBLIC + AT32F435RGT7 + USE_STDPERIPH_DRIVER + AT_START_F435_V1 + CRM_MODULE_ENABLED + MISC_MODULE_ENABLED + GPIO_MODULE_ENABLED + USB_MODULE_ENABLED + QSPI_MODULE_ENABLED + TMR_MODULE_ENABLED + ERTC_MODULE_ENABLED + PWC_MODULE_ENABLED + SPI_MODULE_ENABLED + I2C_MODULE_ENABLED + DMA_MODULE_ENABLED + ADC_MODULE_ENABLED + USART_MODULE_ENABLED + EXINT_MODULE_ENABLED + SCFG_MODULE_ENABLED + FLASH_MODULE_ENABLED + CRC_MODULE_ENABLED + WDT_MODULE_ENABLED + WWDT_MODULE_ENABLED + CAN_MODULE_ENABLED + DAC_MODULE_ENABLED + DEBUG_MODULE_ENABLED + DVP_MODULE_ENABLED + EDMA_MODULE_ENABLED + # EMAC_MODULE_ENABLED + SDIO_MODULE_ENABLED + # XMC_MODULE_ENABLED + ACC_MODULE_ENABLED + USBD_SUPPORT_WINUSB=1 + ) + target_include_directories(pm5_at32_armlib PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/at32_sys + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/cmsis/cm4/core_support + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/cmsis/cm4/device_support + ${CMAKE_CURRENT_LIST_DIR}/at32_sys/drivers/inc + ${CMAKE_CURRENT_LIST_DIR}/at32_usb + ${CMAKE_CURRENT_LIST_DIR}/at32_usb/usb_drivers/inc + ${CMAKE_CURRENT_LIST_DIR}/at32_usb/usbd_class/cdc + ${CMAKE_CURRENT_LIST_DIR}/at32_i2c + ) + set_property(TARGET pm5_at32_armlib PROPERTY POSITION_INDEPENDENT_CODE ON) +endif () diff --git a/armsrc/CMakeLists.txt b/armsrc/CMakeLists.txt new file mode 100644 index 000000000..2c2574b20 --- /dev/null +++ b/armsrc/CMakeLists.txt @@ -0,0 +1,556 @@ +cmake_minimum_required(VERSION 3.20) + +# Fix error when 'compile a simple test program.' +include(${CMAKE_CURRENT_LIST_DIR}/../tools/FixCompileTest.cmake) + +# Ensure direct armsrc builds also select ARM cross-compilers. +include(${CMAKE_CURRENT_LIST_DIR}/../tools/ToolchainForArm.cmake) + +# Set variables needs before the project defining. +project(fullimage C CXX ASM) + +# Compile tools for project build step, preprocessor and postprocessor +# So this compiler need run on host platform +find_program(C_COMPILER_HOST gcc) +if (NOT C_COMPILER_HOST) + message(FATAL_ERROR "GCC for tools build on host is no found, Plz give the cmake arg like 'cmake -DC_COMPILER_HOST=/your path/'") +endif () + +# Genarate version info by mkversion script +include(${CMAKE_CURRENT_LIST_DIR}/../tools/MKVersionScript.cmake) + +# Import options for standalone mode +# Used 'STANDALONE_REQ_DEFS' & 'STANDALONE_PLATFORM_DEFS' variables. +include(${CMAKE_CURRENT_LIST_DIR}/Standalone/StandAloneOption.cmake) + +# Hardware abstraction layer for ARM platform. +include(${CMAKE_CURRENT_LIST_DIR}/../common_arm/Hal.cmake) + +# Setup FPGA Compress. +include(${CMAKE_CURRENT_LIST_DIR}/../tools/FpgaCompress.cmake) + +# --------------------------------------------------------------------------------- + +set(APP_CFLAGS ${PLATFORM_DEFS} -ffunction-sections -fdata-sections) + +set(INC_DIRS + ../include + ../common_arm + ../common_arm/rssi + ../common_arm/ticks + ../common_arm/usb + ../common_arm/flash_data + ../common_arm/flash_code + ../common_arm/gpio + ../common_arm/sys + ../common_arm/fpga + ../common_arm/wdt + ../common_fpga + ../common + ../common/lz4 + . +) + +# Hal for platform +if (PM5) + set(SRC_TICKS ../common_arm/ticks/ticks_hw_at32.c) + set(SRC_GPIO ../common_arm/gpio/gpio_hw_at32.c) + set(SRC_WDT ../common_arm/wdt/wdt_hw_at32.c) + set(SRC_RSSI ../common_arm/rssi/rssi_hw_at32.c) + set(SRC_SYS ../common_arm/sys/sys_hw_at32.c) + set(SRC_FLASH_DATA ../common_arm/flash_data/flashmem_hw_at32.c) + set(SRC_USB_CDC ../common_arm/usb/usb_cdc_at32.c) + set(SRC_FPGA ../common_arm/fpga/fpga_hw_at32.c ../common_arm/fpga/fpga_gw_jtag.c) + set(LDSCRIPT ${CMAKE_CURRENT_LIST_DIR}/ldscript.osimage.at32) +else () + set(SRC_TICKS ../common_arm/ticks/ticks_hw_at91.c) + set(SRC_GPIO ../common_arm/gpio/gpio_hw_at91.c) + set(SRC_WDT ../common_arm/wdt/wdt_hw_at91.c) + set(SRC_RSSI ../common_arm/rssi/rssi_hw_at91.c) + set(SRC_SYS ../common_arm/sys/sys_hw_at91.c) + set(SRC_FLASH_DATA ../common_arm/flash_data/flashmem_hw_at91.c) + set(SRC_USB_CDC ../common_arm/usb/usb_cdc_at91.c) + set(SRC_FPGA ../common_arm/fpga/fpga_hw_at91.c) + set(LDSCRIPT ${CMAKE_CURRENT_LIST_DIR}/ldscript.osimage.at91) +endif () + +set(SRC_LF + lfops.c + lfsampling.c + pcf7931.c + ../common/lfdemod.c + lfadc.c +) + +set(SRC_HF hfops.c) + +set(SRC_ISO15693 + iso15693.c + ../common/iso15693tools.c) + +set(SRC_ISO14443a + iso14443a.c + secc.c + mifareutil.c + mifarecmd.c + epa.c + mifaresim.c + sam_common.c + sam_mfc.c + sam_seos.c + sam_sc.c +) + +#UNUSED: mifaresniff.c + +set(SRC_ISO14443b iso14443b.c) + +set(SRC_FELICA felica.c felicasim.c) + +set(SRC_CRAPTO1 + ../common/crapto1/crypto1.c + desfire_crypto.c + mifaredesfire.c + ../common/mbedtls/des.c + ../common/mbedtls/aes.c + ../common/mbedtls/platform_util.c +) + +set(SRC_CRC + ../common/crc.c + ../common/crc16.c + ../common/crc32.c) + +set(SRC_ICLASS + iclass.c + optimized_cipherutils.c + optimized_ikeys.c + optimized_elite.c + optimized_cipher.c + sam_picopass.c +) + +set(SRC_SEOS + seos.c + ../common/mbedtls/sha1.c + ../common/mbedtls/sha256.c) + +set(SRC_LEGIC + legicrf.c + legicrfsim.c + ../common/legic_prng.c) + +set(SRC_NFCBARCODE thinfilm.c) + +# SRC_BEE = bee.c +# set(SRC_BEE bee.c) + +message(STATUS "APP_CFLAGS = ${APP_CFLAGS}") + +# RDV4 or PM5 related hardware support +if ("${APP_CFLAGS}" MATCHES "WITH_FLASH") + set(SRC_FLASH ${SRC_FLASH_DATA} ../common_arm/flash_data/flashmem_core.c) + set(SRC_SPIFFS + spiffs.c + spiffs_cache.c + spiffs_check.c + spiffs_gc.c + spiffs_nucleus.c + spiffs_hydrogen.c + ) +else () + set(SRC_FLASH) + set(SRC_SPIFFS) +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_SMARTCARD") + set(SRC_SMARTCARD i2c.c i2c_direct.c emvsim.c) +else () + set(SRC_SMARTCARD) +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_FPC_USART") + set(SRC_FPC usart.c) +else () + set(SRC_FPC) +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_HITAG") + set(SRC_HITAG + ../common/hitag2/hitag2_crypto.c + hitag_common.c + hitag2.c + hitagS.c + hitagu.c + hitag2_crack.c + ) + list(APPEND INC_DIRS ../common/hitag2) +else () + set(SRC_HITAG) +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_EM4x50") + set(SRC_EM4x50 + em4x50.c + ../common/bruteforce.c) +else () + set(SRC_EM4x50) +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_EM4x70") + set(SRC_EM4x70 em4x70.c) +else () + set(SRC_EM4x70) +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_LCD") + # LCD module is disabled. file name is fonts_disabled.c and LCD_disabled.c + # So, if WITH_LCD enable, we need show error for DEV. + set(SRC_LCD fonts.c LCD.c) + message(FATAL_ERROR "LCD functions is unavailable") +else () + set(SRC_LCD) +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_ZX8211") + set(SRC_ZX lfzx.c) +else () + set(SRC_ZX) +endif () + +# Generic standalone Mode injection of source code +include(${CMAKE_CURRENT_LIST_DIR}/Standalone/StandAloneSource.cmake) + +# The lz4 source files required for decompressing the fpga config at run time +set(SRC_LZ4 ../common/lz4/lz4.c) +# Additional defines required to compile lz4 +set(LZ4_CFLAGS "-DLZ4_MEMORY_USAGE=8" "-DLZ4_HEAPMODE=0") +list(APPEND APP_CFLAGS ${LZ4_CFLAGS}) +# lz4 includes: +list(APPEND INC_DIRS ../common/lz4) + +# stdint.h provided locally until GCC 4.5 becomes C99 compliant, +# stack-protect , no-pie reduces size on Gentoo Hardened 8.2 gcc +list(APPEND APP_CFLAGS -I. -fno-stack-protector -fno-pie) + +# Compile these in thumb mode (small size) +set(THUMBSRC + start.c + ${SRC_LCD} + ${SRC_ISO15693} + ${SRC_NFCBARCODE} + ${SRC_LF} + ${SRC_LZ4} + ${SRC_LEGIC} + ${SRC_FLASH} + ${SRC_SMARTCARD} + ${SRC_FPC} + ${SRC_HITAG} + ${SRC_EM4x50} + ${SRC_EM4x70} + ${SRC_SPIFFS} + ${SRC_HF} + ${SRC_ISO14443a} + ${SRC_ISO14443b} + ${SRC_CRAPTO1} + ${SRC_ICLASS} + ${SRC_SEOS} + ${SRC_EMV} + ${SRC_CRC} + ${SRC_FELICA} + ${SRC_STANDALONE} + ${SRC_ZX} + appmain.c + printf.c + dbprint.c + ../common/commonutil.c + util.c + string.c + BigBuf.c + ${SRC_RSSI} + ${SRC_TICKS} + ${SRC_GPIO} + ${SRC_WDT} + ${SRC_SYS} + ../common_arm/rssi/rssi_core.c + ../common_arm/ticks/ticks_core.c + hfsnoop.c + ../common/generator.c + cmac_calc.c + cmac_3des.c +) + +if (PM5) + list(APPEND THUMBSRC startup_at32f435_437.s) + list(APPEND THUMBSRC i2c.c) + list(APPEND THUMBSRC at32_unit_test.c) # TODO DXL: AT32单元测试代码 +endif () + +# These are to be compiled in ARM mode +set(ARMSRC + ../common_arm/fpga/fpga_loader.c + ../common_arm/fpga/fpga_core.c + ${SRC_FPGA} + ../common_arm/usb/usb_read_ng.c + ../common_arm/usb/usb_cdc_desc.c + ${SRC_USB_CDC} # AT32 or AT91 usb module, api defs all in usb_cdc.h + cmd.c +) + +set(VERSIONSRC version_pm3.c fpga_version_info.c) + +# Import common settings for arm build, such as common cflags, etc. +# Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC +include(${CMAKE_CURRENT_LIST_DIR}/../common_arm/Common.cmake) + +if (PM5) + include(${CMAKE_CURRENT_LIST_DIR}/../armlib/pm5_at32_armlib.cmake) + set(LIBS ${LIBS} pm5_at32_armlib) + # set(CROSS_LDFLAGS ${CROSS_LDFLAGS} -mfloat-abi=hard -mfpu=fpv4-sp-d16) # TODO DXL: Hardware float core enable? + set(CROSS_LDFLAGS ${CROSS_LDFLAGS} -mcpu=cortex-m4) +endif () + +message(STATUS "LDSCRIPT = ${LDSCRIPT}") +message(STATUS "CROSS_CFLAGS = ${CROSS_CFLAGS}") + +# ===================================================== +# FPGA_COMPRESS_EXE -> 可执行文件完整路径(如 /path/to/fpga_compress) +# FPGA_BITSTREAMS -> 所有 .bit 文件列表 +# OBJDIR -> 构建输出目录(如 ${CMAKE_BINARY_DIR}/obj) +# APP_CFLAGS -> 编译标志(如 "WITH_COMPRESSION") +# THUMBSRC -> 需用 -mthumb 编译的 C 源文件 +# ARMSRC -> 可用 ARM 模式编译的 C 源文件 +# ASMSRC -> 汇编文件(.s) +# VERSIONSRC -> version_pm3.c和fpga_version_info.c(或其路径) +# INC_DIRS -> 包含目录列表 +# CROSS_CFLAGS -> 通用编译标志(如 -O2, -Wall) +# CROSS_LDFLAGS -> 链接标志(如 -nostartfiles) +# CMAKE_OBJCOPY -> objcopy 工具路径 +# LDSCRIPT -> 链接脚本路径 +# DEFAULT_VERSION_C -> 默认 version 模板文件 +# ===================================================== + +#[[ +# Firmware and Compressed Data Integration: LZ4-based .data Section Compression Scheme + +1. Compile Firmware + - Compile C/C++ source code normally to generate an ELF file with the original .data section. + - The linker calculates the .data size and reserves VMA (Virtual Memory Address) space in RAM. + +2. Extract .data Section to Binary + - Use objcopy to extract the raw .data section from the ELF, generating an uncompressed binary (e.g., data.bin). + +3. Compress Data and Add Metadata + - Compress data.bin using LZ4 (or similar) to produce a compressed stream (e.g., data.bin.z). + - Prepend a 4-byte header containing the original .data size (avail_out), used for decompression bounds checking. + +4. Update .data Content in ELF + - Use objcopy --update-section to replace the .data section in the ELF with the compressed data (including 4-byte header). + - This operation only changes the content and LMA (Load Memory Address) size, not the VMA (RAM address or reserved space). + +5. Generate Final Firmware Image + - Convert the updated ELF to .bin or .hex format for flashing. + - The .data section in Flash now contains compressed data, significantly reducing firmware size. + +6. Runtime Decompression + - At startup, read the .data section start address (__data_src_start__) from Flash. + - Read the first 4 bytes to get the original size (avail_out). + - Call LZ4_decompress_safe to decompress the data into the RAM region from __data_start__ to __data_end__. + - After decompression, .data variables are restored and the program continues. + +# Key Insight: VMA Reservation Mechanism +- The linker reserves sufficient RAM space for the .data section at link time, based on the original (uncompressed) size. +- Compression only affects storage in Flash (LMA), not the runtime layout in RAM (VMA). +- The decompression target is precisely defined by linker symbols __data_start__ and __data_end__, ensuring safety and correctness. +]] + +set(OBJDIR ${CMAKE_BINARY_DIR}/obj) +set(DEFAULT_VERSION_C ${CMAKE_CURRENT_LIST_DIR}/../common/default_version_pm3.c) + +# ---------------- Output dir ---------------- +file(MAKE_DIRECTORY ${OBJDIR}) +get_filename_component(OBJDIR_NAME ${OBJDIR} NAME) + +# ---------------- Output files ---------------- +get_filename_component(FPGA_VERSION_C ${CMAKE_CURRENT_LIST_DIR}/fpga_version_info.c ABSOLUTE) +get_filename_component(FPGA_BIT_Z ${OBJDIR}/fpga_all.bit.z ABSOLUTE) +get_filename_component(FPGA_BIT_Z_NAME ${FPGA_BIT_Z} NAME) +get_filename_component(FPGA_ALL_O ${OBJDIR}/fpga_all.o ABSOLUTE) +get_filename_component(VERSION_PM3_C ${CMAKE_CURRENT_LIST_DIR}/version_pm3.c ABSOLUTE) +get_filename_component(STAGE1_ELF ${OBJDIR}/fullimage.stage1.elf ABSOLUTE) +get_filename_component(DATA_BIN ${OBJDIR}/fullimage.data.bin ABSOLUTE) +get_filename_component(DATA_BIN_Z ${OBJDIR}/fullimage.data.bin.z ABSOLUTE) +get_filename_component(FINAL_ELF ${OBJDIR}/fullimage.elf ABSOLUTE) +get_filename_component(OUTPUT_S19 ${OBJDIR}/fullimage.s19 ABSOLUTE) +get_filename_component(OUTPUT_STAGE1_HEX ${OBJDIR}/fullimage.stage1.hex ABSOLUTE) +get_filename_component(OUTPUT_STAGE1_BIN ${OBJDIR}/fullimage.stage1.bin ABSOLUTE) +get_filename_component(OUTPUT_FINAL_HEX ${OBJDIR}/fullimage.hex ABSOLUTE) +get_filename_component(OUTPUT_FINAL_BIN ${OBJDIR}/fullimage.bin ABSOLUTE) + +# ---------------- 1. Create fpga_version_info.c ---------------- +set(FPGA_BITSTREAMS_ABSOLUTE_PATH) # Convert all .bit files from relative to absolute path +foreach (bitFile ${FPGA_BITSTREAMS}) + get_filename_component(bitFileAbPath ${bitFile} ABSOLUTE) # get absolute path + list(APPEND FPGA_BITSTREAMS_ABSOLUTE_PATH ${bitFileAbPath}) # append to list +endforeach () +add_custom_command( + OUTPUT ${FPGA_VERSION_C} + # COMMAND ${CMAKE_COMMAND} -E echo "[=] GEN(1) fpga_version_info.c" + COMMAND ${FPGA_COMPRESS_EXE} -v ${FPGA_BITSTREAMS_ABSOLUTE_PATH} ${FPGA_VERSION_C} + DEPENDS ${FPGA_BITSTREAMS_ABSOLUTE_PATH} + DEPENDS ${FPGA_COMPRESS_EXE} # Before this step run, we need to build 'fpga_compress' executable + COMMAND_EXPAND_LISTS + COMMENT "Call the 'fpga_compress' to generate 'fpga_version_info.c' for armsrc" + VERBATIM +) + +# ---------------- 2. Create fpga_all.bit.z ---------------- +# If no fpga pack to arm fw required, skip this. +add_custom_command( + OUTPUT ${FPGA_BIT_Z} + # COMMAND ${CMAKE_COMMAND} -E echo "[=] GEN(2) fpga_all.bit.z" + COMMAND ${FPGA_COMPRESS_EXE} ${FPGA_BITSTREAMS_ABSOLUTE_PATH} ${FPGA_BIT_Z} + DEPENDS ${FPGA_BITSTREAMS_ABSOLUTE_PATH} + DEPENDS ${FPGA_COMPRESS_EXE} # Ensure host tool is built before running it. + COMMAND_EXPAND_LISTS + COMMENT "Call the 'fpga_compress' to compress fpga bit files, output 'fpga_all.bit.z'" + VERBATIM +) + +# ---------------- 3. Create fpga_all.o ---------------- +# If no fpga pack to arm fw required, skip this step. +# The _binary_obj_fpga_all_bit_z_start and _binary_obj_fpga_all_bit_z_end will generate by 'objcopy' +# Yes, fpga_all.o contain 'binary__start', 'binary__size', 'binary__end' +# More info view: https://stackoverflow.com/questions/60295013/is-it-possible-to-make-a-hardcoding-with-the-help-of-the-command-objcopy +# Tips: the binary file put in '.data' section. +add_custom_command( + OUTPUT ${FPGA_ALL_O} + # !!! Important !!! Must enter parent dir of obj output dir, to create fpga_all.o + # The objcopy command needs to generate object file for 'fpga_all.bit.z' outside the obj directory. + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + # COMMAND ${CMAKE_COMMAND} -E echo "[=] GEN(3) fpga_all.o" + COMMAND ${CMAKE_OBJCOPY} -O elf32-littlearm -I binary -B arm --prefix-sections=fpga_all_bit ${OBJDIR_NAME}/${FPGA_BIT_Z_NAME} ${FPGA_ALL_O} + DEPENDS ${FPGA_BIT_Z} + COMMENT "Call the objcopy to convert binary to elf file." + VERBATIM +) + +# ---------------- 4. Create version_pm3.c ---------------- +if (SKIP_FPGA_EMBED) # If pack fpga bit files to arm, we need from step1,2,3 start build, otherwise skip step2,3. + set(FPGA_ALL_O_MKVERSION_DEP) # <-- Important, unset it will let this step(4) no DEPENDS with 'FPGA_ALL_O'. +else () + set(FPGA_ALL_O_MKVERSION_DEP ${FPGA_ALL_O}) +endif () + +add_custom_command( + OUTPUT ${VERSION_PM3_C} + # COMMAND ${CMAKE_COMMAND} -E echo "[=] GEN(4) version_pm3.c" + DEPENDS ${DEFAULT_VERSION_C} ${FPGA_VERSION_C} ${FPGA_ALL_O_MKVERSION_DEP} + COMMAND ${MKVERSION_CMD} ${VERSION_PM3_C} || ${CMAKE_COMMAND} -E copy ${DEFAULT_VERSION_C} ${VERSION_PM3_C} + COMMENT "Call mkversion.xx script to generate 'version_pm3.c', if mkversion return fail, fallback is default_version_pm3.c" + VERBATIM +) + +# ---------------- 5. Build fulliamge elf but stage1, not '.data' section compressed ---------------- +set(ALL_SRCS ${THUMBSRC} ${ARMSRC} ${ASMSRC} ${VERSIONSRC}) + +add_executable(fullimage.stage1.elf ${ALL_SRCS}) +set_target_properties(fullimage.stage1.elf PROPERTIES # The 'fullimage.stage1.elf' output to ${OBJDIR} + RUNTIME_OUTPUT_DIRECTORY "${OBJDIR}" + OUTPUT_NAME "fullimage.stage1" + SUFFIX ".elf" # OK, OBJDIR + OUTPUT_NAME + SUFFIX = /xxx/obj/fullimage.stage1.elf +) +target_link_options(fullimage.stage1.elf PRIVATE -Wl,-Map=${PROJECT_BINARY_DIR}/${PROJECT_NAME}.map) # output map file +target_link_options(fullimage.stage1.elf PRIVATE -L ${CMAKE_CURRENT_LIST_DIR} -T ${LDSCRIPT} ${CROSS_LDFLAGS}) +target_compile_options(fullimage.stage1.elf PRIVATE ${CROSS_CFLAGS}) # !!!! WARN !!!! No -mthumb on here +set_source_files_properties(${THUMBSRC} PROPERTIES COMPILE_OPTIONS "-mthumb") # THUMBSRC is '-mthumb' flag required. +target_include_directories(fullimage.stage1.elf PRIVATE ${INC_DIRS}) +target_link_libraries(fullimage.stage1.elf ${LIBS}) +if (NOT SKIP_FPGA_EMBED) # Link fpga data(not compressed) + set_property(TARGET fullimage.stage1.elf APPEND PROPERTY LINK_LIBRARIES ${FPGA_ALL_O}) +endif () + +# Generate HEX and BIN files for stage 1 for easy downloading to device and debugging device. +add_custom_command(TARGET ${PROJECT_NAME}.stage1.elf POST_BUILD + # COMMAND ${CMAKE_COMMAND} -E echo "[=] GEN(5) ${PROJECT_NAME}.stage1.(HEX,BIN)" + COMMAND ${CMAKE_OBJCOPY} -Oihex $ ${OUTPUT_STAGE1_HEX} + COMMAND ${CMAKE_OBJCOPY} -Obinary $ ${OUTPUT_STAGE1_BIN} + COMMENT "Build ${PROJECT_NAME}.stage1.hex & ${PROJECT_NAME}.stage1.bin" +) + +# ---------------- 6. Pull '.data' section from 'fullimage.stage1.elf' ---------------- +# Tips: fpga_all.o maybe pack to elf on stage1, is uncompress data binary, next step will compress(all '.data' section). +# When this step is reached, the linker has allocated a specific vma for the data segment. Compression is only to reduce the firmware volume. +add_custom_command( + OUTPUT ${DATA_BIN} + # COMMAND ${CMAKE_COMMAND} -E echo "[=] GEN(6) pull '.data'(uncompressed)" + COMMAND ${CMAKE_OBJCOPY} -O binary --only-section .data ${STAGE1_ELF} ${DATA_BIN} + DEPENDS fullimage.stage1.elf + COMMENT "Call the 'objcopy' to pull the '.data' section from 'fullimage.stage1.elf'" + VERBATIM +) + +# ---------------- 7. Compress '.data' section binary to 'fullimage.data.bin.z' ---------------- +# About '.data' section compress: https://www.eevblog.com/forum/microcontrollers/compression-of-data-(initialized-variables)-section/ +add_custom_command( + OUTPUT ${DATA_BIN_Z} + # COMMAND ${CMAKE_COMMAND} -E echo "[=] GEN(7) fullimage.data.bin.z" + COMMAND ${FPGA_COMPRESS_EXE} ${DATA_BIN} ${DATA_BIN_Z} + DEPENDS ${DATA_BIN} + DEPENDS ${FPGA_COMPRESS_EXE} # Ensure host tool is built before running it. + COMMENT "Call the 'fpga_compress' to compress '.data' section from 'fullimage.stage1.elf'" + VERBATIM +) + +# ---------------- 8. Pack data bin and gen fullimage.elf ---------------- +if ("${APP_CFLAGS}" MATCHES "WITH_COMPRESSION|WITH_COMPRESSION=1") + add_custom_command( + OUTPUT ${FINAL_ELF} + # COMMAND ${CMAKE_COMMAND} -E echo "[=] LD(8) fullimage.elf (with compression)" + COMMAND ${CMAKE_OBJCOPY} -O elf32-littlearm --strip-all --update-section .data=${DATA_BIN_Z} ${STAGE1_ELF} ${FINAL_ELF} + DEPENDS ${STAGE1_ELF} ${DATA_BIN_Z} + COMMENT "Put the compressed '.data' section to 'fullimage.stage1.elf' for build 'fullimage.elf'" + VERBATIM + ) +else () + add_custom_command( + OUTPUT ${FINAL_ELF} + # COMMAND ${CMAKE_COMMAND} -E echo "[=] MIN(8) fullimage.elf (without compression) " + COMMAND ${CMAKE_OBJCOPY} -O elf32-littlearm --strip-all ${STAGE1_ELF} ${FINAL_ELF} + DEPENDS ${STAGE1_ELF} + COMMENT "Copy 'fullimage.stage1.elf' to 'fullimage.elf' if no compress required" + VERBATIM + ) +endif () + +# ---------------- 9. Generate S19 format file ---------------- +add_custom_command( + OUTPUT ${OUTPUT_S19} + # COMMAND ${CMAKE_COMMAND} -E echo "[=] GEN(9) S19 file" + COMMAND ${CMAKE_OBJCOPY} + -Osrec + --srec-forceS3 + --strip-debug + --no-change-warnings + --change-addresses=-0x100000 + --change-start=0 + --change-section-address=.bss+0 + --change-section-address=.data-0x100000 + --change-section-address=.commonarea+0 + ${FINAL_ELF} + ${OUTPUT_S19} + DEPENDS ${FINAL_ELF} + COMMENT "Call the objcopy to convert ELF to S19 format" + VERBATIM +) + +# ---------------- 10. Finally, start build the fullimage, the 'All' build will from here start ---------------- +add_custom_target(fullimage ALL DEPENDS ${OUTPUT_S19} COMMENT "Build fullimage") +add_dependencies(fullimage fullimage.stage1.elf) # fullimage dependencies tree start, 9 -> 1 -> 9 +# Generate HEX and BIN files for fullimage finally +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + # COMMAND ${CMAKE_COMMAND} -E echo "[=] GEN(10) ${PROJECT_NAME}(HEX,BIN)" + COMMAND ${CMAKE_OBJCOPY} -Oihex ${FINAL_ELF} ${OUTPUT_FINAL_HEX} + COMMAND ${CMAKE_OBJCOPY} -Obinary ${FINAL_ELF} ${OUTPUT_FINAL_BIN} + COMMENT "Build ${PROJECT_NAME}.hex & ${PROJECT_NAME}.bin" +) diff --git a/armsrc/Makefile b/armsrc/Makefile index ab7fd3d9c..9be51e4f2 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -29,10 +29,43 @@ ifeq ($(PLTNAME),) endif endif +# Proxmark5 requires armlib. +include ../armlib/Exports.mk + #remove one of the following defines and comment out the relevant line #in the next section to remove that particular feature from compilation. # NO space,TABs after the "\" sign. APP_CFLAGS = $(PLATFORM_DEFS) +APP_CFLAGS += $(ARMLIB_EXPORT_CFLAGS) + +# HAL for platform +ifeq ($(PLATFORM),PM5) + SRC_STARTUP = startup_at32f435_437.s + SRC_TICKS = ../common_arm/ticks/ticks_hw_at32.c i2c.c # TODO DXL: It always depends on I2C, so the compilation location of this source file needs to be optimized. + SRC_GPIO = ../common_arm/gpio/gpio_hw_at32.c + SRC_WDT = ../common_arm/wdt/wdt_hw_at32.c + SRC_RSSI = ../common_arm/rssi/rssi_hw_at32.c + SRC_SYS = ../common_arm/sys/sys_hw_at32.c + SRC_FPGA = ../common_arm/fpga/fpga_hw_at32.c ../common_arm/fpga/fpga_gw_jtag.c + SRC_USB_CDC = ../common_arm/usb/usb_cdc_at32.c + SRC_FLASH_DATA = ../common_arm/flash_data/flashmem_hw_at32.c + APP_CFLAGS += -mcpu=cortex-m4 + CROSS_LDFLAGS += -mcpu=cortex-m4 + LD_SCRIPT = ldscript.osimage.at32 + ARMLIB_OBJ = $(OBJDIR)/$(ARMLIB_EXPORT_LIBNAME) + LIBS += $(ARMLIB_OBJ) +else + SRC_STARTUP = + SRC_TICKS = ../common_arm/ticks/ticks_hw_at91.c + SRC_GPIO = ../common_arm/gpio/gpio_hw_at91.c + SRC_WDT = ../common_arm/wdt/wdt_hw_at91.c + SRC_RSSI = ../common_arm/rssi/rssi_hw_at91.c + SRC_SYS = ../common_arm/sys/sys_hw_at91.c + SRC_FPGA = ../common_arm/fpga/fpga_hw_at91.c + SRC_USB_CDC = ../common_arm/usb/usb_cdc_at91.c + SRC_FLASH_DATA = ../common_arm/flash_data/flashmem_hw_at91.c + LD_SCRIPT = ldscript.osimage.at91 +endif SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c SRC_HF = hfops.c @@ -51,9 +84,9 @@ SRC_NFCBARCODE = thinfilm.c # SRC_BEE = bee.c -# RDV40 related hardware support +# RDV40 or PM5 related hardware support ifneq (,$(findstring WITH_FLASH,$(APP_CFLAGS))) - SRC_FLASH = flashmem.c + SRC_FLASH = flashmem_core.c $(SRC_FLASH_DATA) SRC_SPIFFS = spiffs.c spiffs_cache.c spiffs_check.c spiffs_gc.c spiffs_nucleus.c spiffs_hydrogen.c else SRC_FLASH = @@ -143,6 +176,11 @@ THUMBSRC = start.c \ $(SRC_FELICA) \ $(SRC_STANDALONE) \ $(SRC_ZX) \ + $(SRC_RSSI) \ + $(SRC_TICKS) \ + $(SRC_GPIO) \ + $(SRC_WDT) \ + $(SRC_SYS) \ appmain.c \ printf.c \ dbprint.c \ @@ -150,8 +188,8 @@ THUMBSRC = start.c \ util.c \ string.c \ BigBuf.c \ - ticks.c \ - clocks.c \ + ticks_core.c \ + rssi_core.c \ hfsnoop.c \ generator.c \ cmac_calc.c \ @@ -159,10 +197,16 @@ THUMBSRC = start.c \ # These are to be compiled in ARM mode -ARMSRC = fpgaloader.c \ - usb_cdc.c \ +ARMSRC = fpga_loader.c \ + fpga_core.c \ + $(SRC_FPGA) \ + usb_cdc_desc.c \ + usb_read_ng.c \ + $(SRC_USB_CDC) \ cmd.c +ASMSRC = $(SRC_STARTUP) + VERSIONSRC = version_pm3.c \ fpga_version_info.c @@ -176,6 +220,11 @@ else INSTALLFWTAG = $(notdir $(INSTALLFW)) endif +# On some platforms, the FPGA firmware does not need to be packaged into the ARM, because the FPGA has its own flash. +ifneq ($(strip $(SKIP_FPGA_EMBED)),true) + FPGA_ALL_O_DEP = $(OBJDIR)/fpga_all.o +endif + OBJS = $(OBJDIR)/fullimage.s19 FPGA_COMPRESSOR = ../tools/fpga_compress/fpga_compress @@ -187,7 +236,7 @@ showinfo: .DELETE_ON_ERROR: # version_pm3.c should be checked on every time fullimage.stage1.elf should be remade -version_pm3.c: default_version_pm3.c $(OBJDIR)/fpga_version_info.o $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) .FORCE +version_pm3.c: default_version_pm3.c $(OBJDIR)/fpga_version_info.o $(FPGA_ALL_O_DEP) $(THUMBOBJ) $(ARMOBJ) $(ASMOBJ) .FORCE $(info [-] CHECK $@) $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ @@ -211,10 +260,16 @@ $(FPGA_COMPRESSOR): $(error [!] MISSING $@ => To build it, go the root of the repo and do "make $(notdir $@)") $(error [!] MISSING $@) -$(OBJDIR)/fullimage.stage1.elf: $(VERSIONOBJ) $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) +ifeq ($(PLATFORM),PM5) +$(ARMLIB_OBJ): + $(info [=] MAKE $(notdir $@)) + @$(MAKE) --no-print-directory -f ../armlib/Makefile +endif + +$(OBJDIR)/fullimage.stage1.elf: $(VERSIONOBJ) $(FPGA_ALL_O_DEP) $(THUMBOBJ) $(ARMOBJ) $(ASMOBJ) $(ARMLIB_OBJ) $(info [=] LD $@) # Using -T instead of -Wl,-T is needed to prevent the linker from using the default ldscript when using picolibc instead of newlib - $(Q)$(CROSS_LD) $(CROSS_LDFLAGS) -T ldscript -Wl,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^ $(LIBS) + $(Q)$(CROSS_LD) $(CROSS_LDFLAGS) -T $(LD_SCRIPT) -Wl,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^ $(LIBS) $(OBJDIR)/fullimage.data.bin: $(OBJDIR)/fullimage.stage1.elf $(info [-] GEN $@) @@ -243,6 +298,7 @@ tarbin: $(OBJS) clean: $(Q)$(RM) $(DEPENDENCY_FILES) $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.o + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.a $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.elf $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.s19 $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.map @@ -266,4 +322,3 @@ help: @echo Possible targets: @echo + all - Build the full image $(OBJDIR)/fullimage.s19 @echo + clean - Clean $(OBJDIR) - diff --git a/armsrc/Standalone/StandAloneOption.cmake b/armsrc/Standalone/StandAloneOption.cmake new file mode 100644 index 000000000..77e02d29d --- /dev/null +++ b/armsrc/Standalone/StandAloneOption.cmake @@ -0,0 +1,185 @@ +#[[ ++==========================================================+ +| STANDALONE | DESCRIPTION | ++==========================================================+ +| (empty) | No standalone mode | ++----------------------------------------------------------+ +| LF_SKELETON | standalone mode skeleton | +| | - iceman | ++----------------------------------------------------------+ +| LF_EM4100EMUL | Simulate predefined em4100 tags only | +| | | ++----------------------------------------------------------+ +| LF_EM4100RSWB | Read/simulate/brute em4100 tags & | +| | clone it to T555x tags | ++----------------------------------------------------------+ +| LF_EM4100RSWW | Read/simulate/validate em4100 tags & | +| | clone it to T55xx tags, wipe T55xx tags| ++----------------------------------------------------------+ +| LF_EM4100RWC | Read/simulate em4100 tags & clone it | +| | to T555x tags | ++----------------------------------------------------------+ +| LF_HIDBRUTE | HID corporate 1000 bruteforce | +| | - Federico dotta & Maurizio Agazzini | ++----------------------------------------------------------+ +| LF_HIDFCBRUTE | HID Facility Code bruteforce | +| (RDV4 only) | | ++----------------------------------------------------------+ +| LF_ICEHID | LF HID collector to flashmem | +| (RDV4 only) | | ++----------------------------------------------------------+ +| LF_MULTIHID | LF HID 26 Bit (H1031) multi simulator | +| | - Shain Lakin | ++----------------------------------------------------------+ +| LF_NEDAP_SIM | LF Nedap ID simple simulator | +| | | ++----------------------------------------------------------+ +| LF_NEXID | LF Nexwatch collector to flashmem | +| (RDV4 only) | | ++----------------------------------------------------------+ +| LF_PROXBRUTE | HID ProxII bruteforce | +| | - Brad Antoniewicz | ++----------------------------------------------------------+ +| LF_PROX2BRUTE | HID ProxII bruteforce v2 | +| | | ++----------------------------------------------------------+ +| LF_SAMYRUN | HID26 read/clone/sim | +| (default) | - Samy Kamkar | ++----------------------------------------------------------+ +| LF_THAREXDE | Simulate/read EM4x50 tags | +| (RDV4 only) | storing in flashmem | ++----------------------------------------------------------+ +| HF_14ASNIFF | 14a sniff to flashmem (rdv4) or ram | +| | | ++----------------------------------------------------------+ +| HF_14BSNIFF | 14b sniff to flashmem (rdv4) or ram | +| | | ++----------------------------------------------------------+ +| HF_15SNIFF | 15693 sniff to flashmem (rdv4) or ram | +| | | ++----------------------------------------------------------+ +| HF_15SIM | 15693 tag simulator | +| | | ++----------------------------------------------------------+ +| HF_AVEFUL | Mifare ultralight read/simulation | +| | - Ave Ozkal | ++----------------------------------------------------------+ +| HF_BOG | 14a sniff with ULC/ULEV1/NTAG auth | +| (RDV4 only) | storing in flashmem - Bogito | ++----------------------------------------------------------+ +| HF_CARDHOPPER | Relay 14a protocols over long distances| +| (RDV4 only) | (w/ IP backbone) - Sam Haskins | ++----------------------------------------------------------+ +| HF_COLIN | Mifare ultra fast sniff/sim/clone | +| (RDV4 only) | - Colin Brigato | ++----------------------------------------------------------+ +| HF_CRAFTBYTE | UID stealer - Emulates scanned 14a UID | +| | - Anze Jensterle | ++----------------------------------------------------------+ +| HF_ICECLASS | Simulate HID iCLASS legacy ags | +| (RDV4 only) | storing in flashmem | ++----------------------------------------------------------+ +| HF_LEGIC | Read/simulate Legic Prime tags | +| | storing in flashmem | ++----------------------------------------------------------+ +| HF_LEGICSIM | Simulate Legic Prime tags | +| (RDV4 only) | stored on flashmem | ++----------------------------------------------------------+ +| HF_MATTYRUN | Mifare sniff/clone | +| | - Matías A. Ré Medina | ++----------------------------------------------------------+ +| HF_MFCSIM | Simulate Mifare Classic 1k card | +| (RDV4 only) | storing in flashmem - Ray Lee | ++----------------------------------------------------------+ +| HF_MSDSAL | Read and emulate MSD Visa cards | +| | - Salvador Mendoza | ++----------------------------------------------------------+ +| HF_REBLAY | 14A Relay over BT | +| (RDV4 only) | - Salvador Mendoza | ++----------------------------------------------------------+ +| HF_ST25_TEAROFF | Store/restore ST25TB tags with | +| | tear-off for counters - SecLabz | ++----------------------------------------------------------+ +| HF_TCPRST | IKEA Rothult read/sim/dump/emul | +| | - Nick Draffen | ++----------------------------------------------------------+ +| HF_TMUDFORD | Read and emulate 15 tags | +| | - Tim Mudford | ++----------------------------------------------------------+ +| HF_UNISNIFF | Sniff 14a/14b/15 (optionally to flash) | +| | - hazardousvoltage | ++----------------------------------------------------------+ +| HF_YOUNG | Mifare sniff/simulation | +| | - Craig Young | ++----------------------------------------------------------+ +| DANKARMULTI | Load multiple standalone modes. | +| | - Daniel Karling | ++----------------------------------------------------------+ +| HF_EMVPNG | Read and emulate EMV Visa cards | +| | - Davi Mikael (Penegui) | ++----------------------------------------------------------+ +]] + +# Default standalone if no standalone specified +set(DEFAULT_STANDALONE LF_SAMYRUN) +# (you can set explicitly STANDALONE= to disable standalone modes) +if (NOT DEFINED STANDALONE) + set(STANDALONE ${DEFAULT_STANDALONE}) +endif () +set(STANDALONE_REQ_DEFS) + +# List of all standalone modes, and which ones require bluetooth, smartcard or flash. +# (for now, we only support one standalone mode at a time) +set(STANDALONE_MODES + LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC + LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_MULTIHID LF_NEDAP_SIM LF_NEXID + LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE + HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_15SIM + HF_AVEFUL HF_BOG HF_CARDHOPPER HF_COLIN HF_CRAFTBYTE HF_ICECLASS + HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY + HF_ST25_TEAROFF HF_TCPRST HF_TMUDFORD HF_UNISNIFF HF_YOUNG HF_EMVPNG DANKARMULTI) +# List of modes that require bluetooth +set(STANDALONE_MODES_REQ_BT HF_CARDHOPPER HF_REBLAY) +# List of modes that require smartcard +set(STANDALONE_MODES_REQ_SMARTCARD) +# List of modes that require flash +set(STANDALONE_MODES_REQ_FLASH + LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN + HF_ICECLASS HF_LEGICSIM HF_MFCSIM) + +message(STATUS "STANDALONE = ${STANDALONE}") +message(STATUS "STANDALONE_MODES = ${STANDALONE_MODES}") + +# Check if the specified standalone mode is valid, and set the corresponding definitions. +# 'MATCHES' is used to check if the specified standalone mode is in the list of valid modes. +if (DEFINED STANDALONE) + if ("${STANDALONE_MODES}" MATCHES "${STANDALONE}") + string(TOUPPER ${STANDALONE} STANDALONE_UPPER) + set(STANDALONE_PLATFORM_DEFS ${STANDALONE_PLATFORM_DEFS} "-DWITH_STANDALONE_${STANDALONE_UPPER}") + # Required for SmartCard, set '-DWITH_SMARTCARD' + if ("${STANDALONE_MODES_REQ_SMARTCARD}" MATCHES "${STANDALONE}") + set(STANDALONE_REQ_DEFS ${STANDALONE_REQ_DEFS} -DWITH_SMARTCARD) + endif () + # Required for Flash, set '-DWITH_FLASH' + if ("${STANDALONE_MODES_REQ_FLASH}" MATCHES "${STANDALONE}") + set(STANDALONE_REQ_DEFS ${STANDALONE_REQ_DEFS} -DWITH_FLASH) + endif () + # Required for Bluetooth, set '-DWITH_FPC_USART_HOST' + # (we use USART host for bluetooth communication in standalone modes) + if ("${STANDALONE_MODES_REQ_BT}" MATCHES "${STANDALONE}") + set(STANDALONE_REQ_DEFS ${STANDALONE_REQ_DEFS} -DWITH_FPC_USART_HOST) + endif () + else () + message(FATAL_ERROR "Invalid STANDALONE: ${STANDALONE}. ${KNOWN_DEFINITIONS}") + endif () +endif () + +# Export the definitions for standalone mode to be used in the main CMakeLists.txt +# --- Usually referenced externally: +# STANDALONE_REQ_DEFS -> Definitions required by the selected standalone mode (e.g., -DWITH_FLASH) +# STANDALONE_PLATFORM_DEFS -> Definitions for the selected standalone mode (e.g., -DWITH_STANDALONE_LF_SAMYRUN) +# --- It is not usually used externally: +# STANDALONE_MODES -> List of all valid standalone modes (for error checking) +# STANDALONE_MODES_REQ_BT -> List of standalone modes that require bluetooth (for error checking) +# STANDALONE_MODES_REQ_SMARTCARD -> List of standalone modes that require smartcard (for error checking) +# STANDALONE_MODES_REQ_FLASH -> List of standalone modes that require flash (for error checking) diff --git a/armsrc/Standalone/StandAloneSource.cmake b/armsrc/Standalone/StandAloneSource.cmake new file mode 100644 index 000000000..6f4dce3ba --- /dev/null +++ b/armsrc/Standalone/StandAloneSource.cmake @@ -0,0 +1,161 @@ +# --------------------- Standalone Source Files --------------------- + +set(DIR_STANDALONE ${CMAKE_CURRENT_LIST_DIR}) +set(SRC_STANDALONE "${DIR_STANDALONE}/placeholder.c") + +# Check which standalone mode is selected, and set the corresponding source file. +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_SKELETON") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_skeleton.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_EM4100EMUL") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_em4100emul.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_EM4100RSWB") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_em4100rswb.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_EM4100RSWW") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_em4100rsww.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_EM4100RWC") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_em4100rwc.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_HIDBRUTE") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_hidbrute.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_HIDFCBRUTE") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_hidfcbrute.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_ICEHID") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_icehid.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_MULTIHID") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_multihid.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_NEDAP_SIM") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_nedap_sim.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_NEXID") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_nexid.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_SAMYRUN") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_samyrun.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_PROXBRUTE") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_proxbrute.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_PROX2BRUTE") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_prox2brute.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_LF_THAREXDE") + set(SRC_STANDALONE "${DIR_STANDALONE}/lf_tharexde.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_14ASNIFF") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_14asniff.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_14BSNIFF") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_14bsniff.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_15SNIFF") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_15sniff.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_15SIM") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_15sim.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_AVEFUL") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_aveful.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_BOG") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_bog.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_CARDHOPPER") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_cardhopper.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_COLIN") + set(SRC_STANDALONE + "${DIR_STANDALONE}/vtsend.c" + "${DIR_STANDALONE}/hf_colin.c" + "${DIR_STANDALONE}/frozen.c" + "${DIR_STANDALONE}/nprintf.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_CRAFTBYTE") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_craftbyte.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_ICECLASS") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_iceclass.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_LEGIC") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_legic.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_LEGICSIM") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_legicsim.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_MATTYRUN") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_mattyrun.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_MFCSIM") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_mfcsim.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_MSDSAL") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_msdsal.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_REBLAY") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_reblay.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_TCPRST") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_tcprst.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_TMUDFORD") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_tmudford.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_UNISNIFF") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_unisniff.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_YOUNG") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_young.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_ST25_TEAROFF") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_st25_tearoff.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_HF_EMVPNG") + set(SRC_STANDALONE "${DIR_STANDALONE}/hf_emvpng.c") +endif () + +if ("${APP_CFLAGS}" MATCHES "WITH_STANDALONE_DANKARMULTI") + set(SRC_STANDALONE "${DIR_STANDALONE}/dankarmulti.c") +endif () diff --git a/armsrc/Standalone/dankarmulti.c b/armsrc/Standalone/dankarmulti.c index f127a1b74..66c43c9e4 100644 --- a/armsrc/Standalone/dankarmulti.c +++ b/armsrc/Standalone/dankarmulti.c @@ -19,8 +19,9 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_apis.h" +#include "fpga_loader.h" +#include "ticks_apis.h" #include "util.h" #include "dbprint.h" diff --git a/armsrc/Standalone/hf_14asniff.c b/armsrc/Standalone/hf_14asniff.c index dc0c0905c..15c85cfd8 100644 --- a/armsrc/Standalone/hf_14asniff.c +++ b/armsrc/Standalone/hf_14asniff.c @@ -73,7 +73,7 @@ #include "spiffs.h" #include "appmain.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "BigBuf.h" #define HF_14ASNIFF_LOGFILE "hf_14asniff.trace" diff --git a/armsrc/Standalone/hf_14bsniff.c b/armsrc/Standalone/hf_14bsniff.c index d3adaeb23..e7111eccc 100644 --- a/armsrc/Standalone/hf_14bsniff.c +++ b/armsrc/Standalone/hf_14bsniff.c @@ -46,7 +46,7 @@ #include "spiffs.h" #include "appmain.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "BigBuf.h" #define HF_14BSNIFF_LOGFILE "hf_14bsniff.trace" diff --git a/armsrc/Standalone/hf_15sim.c b/armsrc/Standalone/hf_15sim.c index eb5736ca5..fb72941f0 100644 --- a/armsrc/Standalone/hf_15sim.c +++ b/armsrc/Standalone/hf_15sim.c @@ -21,7 +21,8 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "iso15693.h" #include "iso15.h" #include "protocols.h" @@ -30,7 +31,7 @@ #include "spiffs.h" #include "appmain.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "BigBuf.h" #include "crc16.h" diff --git a/armsrc/Standalone/hf_15sniff.c b/armsrc/Standalone/hf_15sniff.c index ad1d3f8b6..04c031c42 100644 --- a/armsrc/Standalone/hf_15sniff.c +++ b/armsrc/Standalone/hf_15sniff.c @@ -67,14 +67,15 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "iso15693.h" #include "iso15.h" #include "util.h" #include "spiffs.h" #include "appmain.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "BigBuf.h" diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index 695ea9254..ea9032dfa 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -33,11 +33,12 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" // SpinDelay +#include "ticks_apis.h" // SpinDelay #include "protocols.h" // MIFARE_ULEV1_VERSION, MIFARE_ULEV1_READSIG, MIFARE_ULEV1_READ_CNT, MIFARE_ULEV1_CHECKTEAR #include // memcmp #include "mifareutil.h" diff --git a/armsrc/Standalone/hf_bog.c b/armsrc/Standalone/hf_bog.c index b906a71eb..7c4b3e5d5 100644 --- a/armsrc/Standalone/hf_bog.c +++ b/armsrc/Standalone/hf_bog.c @@ -37,9 +37,10 @@ from the client to view the stored quadlets. #include "util.h" #include "spiffs.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "BigBuf.h" #include "string.h" @@ -89,9 +90,9 @@ static void RAMFUNC SniffAndStore(uint8_t param) { Uart14aInit(receivedCmd, MAX_FRAME_SIZE, receivedCmdPar); // Setup and start DMA. - if (FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE) == false) { + if (FpgaSetupSscRxDmaRepeat((uint8_t *)dmaBuf, DMA_BUFFER_SIZE) == false) { if (g_dbglevel > DBG_ERROR) { - Dbprintf("FpgaSetupSscDma failed. Exiting"); + Dbprintf("FpgaSetupSscRxDmaRepeat failed. Exiting"); } return; } @@ -118,7 +119,7 @@ static void RAMFUNC SniffAndStore(uint8_t param) { LED_A_ON(); int register readBufDataP = data - dmaBuf; - int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; + int register dmaBufDataP = DMA_BUFFER_SIZE - FPGA_SSC_DMA_RX_Remaining_Count(); if (readBufDataP <= dmaBufDataP) dataLen = dmaBufDataP - readBufDataP; else @@ -132,16 +133,8 @@ static void RAMFUNC SniffAndStore(uint8_t param) { if (dataLen < 1) continue; - // primary buffer was stopped( <-- we lost data! - if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t)dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; - // Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary - } - // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + if (FPGA_SSC_DMA_RX_Done()) { + FPGA_SSC_DMA_RX_Refresh_Repeat(dmaBuf, DMA_BUFFER_SIZE); } LED_A_OFF(); @@ -216,7 +209,7 @@ static void RAMFUNC SniffAndStore(uint8_t param) { } } // end main loop - FpgaDisableSscDma(); + FPGA_SSC_DMA_RX_Disable(); set_tracing(false); Dbprintf("Stopped sniffing"); diff --git a/armsrc/Standalone/hf_cardhopper.c b/armsrc/Standalone/hf_cardhopper.c index 8baa511d2..cc600a865 100644 --- a/armsrc/Standalone/hf_cardhopper.c +++ b/armsrc/Standalone/hf_cardhopper.c @@ -19,16 +19,17 @@ #include "appmain.h" #include "BigBuf.h" #include "dbprint.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "iso14443a.h" #include "protocols.h" #include "proxmark3_arm.h" #include "standalone.h" -#include "ticks.h" +#include "ticks_apis.h" #include "util.h" #include "usart.h" #include "cmd.h" -#include "usb_cdc.h" +#include "usb_cdc_apis.h" #ifdef CARDHOPPER_USB #define cardhopper_write usb_write @@ -535,7 +536,7 @@ static bool GetIso14443aCommandFromReaderInterruptible(uint8_t *received, uint16 Uart14aInit(received, received_max_len, par); - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + uint8_t b = (uint8_t)FPGA_SSC_RX_Value(); (void)b; uint8_t flip = 0; @@ -555,8 +556,8 @@ static bool GetIso14443aCommandFromReaderInterruptible(uint8_t *received, uint16 checker = 4000; } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + b = (uint8_t)FPGA_SSC_RX_Value(); if (MillerDecoding(b, 0)) { *len = GetUart14a()->len; return true; diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 7a87347ba..aedad8f1b 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -22,9 +22,10 @@ #include "hf_colin.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "util.h" #include "commonutil.h" #include "BigBuf.h" diff --git a/armsrc/Standalone/hf_craftbyte.c b/armsrc/Standalone/hf_craftbyte.c index d12c5c5dd..a01f86767 100644 --- a/armsrc/Standalone/hf_craftbyte.c +++ b/armsrc/Standalone/hf_craftbyte.c @@ -21,10 +21,11 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "string.h" #include "BigBuf.h" #include "iso14443a.h" diff --git a/armsrc/Standalone/hf_doegox_auth0.c b/armsrc/Standalone/hf_doegox_auth0.c index c1796ff2a..2e1c9ff60 100644 --- a/armsrc/Standalone/hf_doegox_auth0.c +++ b/armsrc/Standalone/hf_doegox_auth0.c @@ -67,12 +67,13 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "iso14443a.h" #include "util.h" #include "appmain.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "protocols.h" #include "mifareutil.h" #include "string.h" @@ -208,8 +209,8 @@ static bool RAMFUNC sniff_wait_for_rnda_reply(tag_t type) { uint8_t *data = dma->buf; // Setup and start DMA. - if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { - if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + if (FpgaSetupSscRxDmaRepeat((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscRxDmaRepeat failed. Exiting"); goto out; } @@ -222,7 +223,7 @@ static bool RAMFUNC sniff_wait_for_rnda_reply(tag_t type) { uint32_t ledb_counter = 0; while (BUTTON_PRESS() == false) { register int readBufDataP = data - dma->buf; - register int dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; + register int dmaBufDataP = DMA_BUFFER_SIZE - FPGA_SSC_DMA_RX_Remaining_Count(); if (readBufDataP <= dmaBufDataP) { dataLen = dmaBufDataP - readBufDataP; } else { @@ -246,17 +247,7 @@ static bool RAMFUNC sniff_wait_for_rnda_reply(tag_t type) { ledb_counter = 0; } - // primary buffer was stopped( <-- we lost data! - if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; - // Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary - } - // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; - } + FPGA_SSC_DMA_RX_Refresh_Repeat(dma->buf, DMA_BUFFER_SIZE); // Need two samples to feed Miller and Manchester-Decoder if (rx_samples & 0x01) { @@ -328,7 +319,7 @@ out: FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); } FpgaDisableTracing(); - FpgaDisableSscDma(); + FPGA_SSC_DMA_RX_Disable(); return found_rnda_reply; } diff --git a/armsrc/Standalone/hf_doegox_commit.c b/armsrc/Standalone/hf_doegox_commit.c index 89dc91b20..c46f79080 100644 --- a/armsrc/Standalone/hf_doegox_commit.c +++ b/armsrc/Standalone/hf_doegox_commit.c @@ -67,12 +67,13 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "iso14443a.h" #include "util.h" #include "appmain.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "protocols.h" #include "mifareutil.h" #include "string.h" @@ -184,8 +185,8 @@ static bool RAMFUNC sniff_wait_for_commit(tag_t type) { uint8_t *data = dma->buf; // Setup and start DMA. - if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { - if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + if (FpgaSetupSscRxDmaRepeat((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscRxDmaRepeat failed. Exiting"); goto out; } @@ -195,7 +196,7 @@ static bool RAMFUNC sniff_wait_for_commit(tag_t type) { uint32_t ledb_counter = 0; while (BUTTON_PRESS() == false) { register int readBufDataP = data - dma->buf; - register int dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; + register int dmaBufDataP = DMA_BUFFER_SIZE - FPGA_SSC_DMA_RX_Remaining_Count(); if (readBufDataP <= dmaBufDataP) { dataLen = dmaBufDataP - readBufDataP; } else { @@ -219,17 +220,7 @@ static bool RAMFUNC sniff_wait_for_commit(tag_t type) { ledb_counter = 0; } - // primary buffer was stopped( <-- we lost data! - if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; - // Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary - } - // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; - } + FPGA_SSC_DMA_RX_Refresh_Repeat(dma->buf, DMA_BUFFER_SIZE); // Need two samples to feed Miller and Manchester-Decoder if (rx_samples & 0x01) { @@ -294,7 +285,7 @@ out: FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); } FpgaDisableTracing(); - FpgaDisableSscDma(); + FPGA_SSC_DMA_RX_Disable(); return found_commit; } diff --git a/armsrc/Standalone/hf_emvpng.c b/armsrc/Standalone/hf_emvpng.c index 5f9d5f2fa..1125eb9fa 100644 --- a/armsrc/Standalone/hf_emvpng.c +++ b/armsrc/Standalone/hf_emvpng.c @@ -31,10 +31,11 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "string.h" #include "BigBuf.h" #include "iso14443a.h" diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 56b662024..8a6345d79 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -26,9 +26,10 @@ #include "proxmark3_arm.h" #include "appmain.h" #include "BigBuf.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" -#include "ticks.h" +#include "ticks_apis.h" #include "dbprint.h" #include "spiffs.h" #include "iclass.h" diff --git a/armsrc/Standalone/hf_legic.c b/armsrc/Standalone/hf_legic.c index a15bb2de3..46432a836 100644 --- a/armsrc/Standalone/hf_legic.c +++ b/armsrc/Standalone/hf_legic.c @@ -21,10 +21,11 @@ #include "proxmark3_arm.h" #include "BigBuf.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "legicrf.h" #include "legicrfsim.h" #include "legic.h" // legic_card_select_t struct diff --git a/armsrc/Standalone/hf_legicsim.c b/armsrc/Standalone/hf_legicsim.c index 849d2be3b..1b35f13da 100644 --- a/armsrc/Standalone/hf_legicsim.c +++ b/armsrc/Standalone/hf_legicsim.c @@ -18,11 +18,12 @@ // main code for legic prime simulator aka LEGICSIM //----------------------------------------------------------------------------- #include -#include "ticks.h" +#include "ticks_apis.h" #include "proxmark3_arm.h" #include "BigBuf.h" #include "commonutil.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" #include "spiffs.h" diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c index 92ce5e5cf..d35345528 100644 --- a/armsrc/Standalone/hf_mattyrun.c +++ b/armsrc/Standalone/hf_mattyrun.c @@ -27,7 +27,8 @@ #include "commonutil.h" #include "crc16.h" #include "dbprint.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "iso14443a.h" #include "mifarecmd.h" #include "mifaresim.h" // mifare1ksim @@ -36,7 +37,7 @@ #include "spiffs.h" #include "standalone.h" // standalone definitions #include "string.h" -#include "ticks.h" +#include "ticks_apis.h" #include "util.h" /* diff --git a/armsrc/Standalone/hf_mfcsim.c b/armsrc/Standalone/hf_mfcsim.c index 667696f86..9dd6afd00 100644 --- a/armsrc/Standalone/hf_mfcsim.c +++ b/armsrc/Standalone/hf_mfcsim.c @@ -17,11 +17,12 @@ // main code for mifare classic simulator aka MFCSIM //----------------------------------------------------------------------------- #include -#include "ticks.h" +#include "ticks_apis.h" #include "proxmark3_arm.h" #include "BigBuf.h" #include "commonutil.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" #include "spiffs.h" diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index 6eb5b46b2..94d32c5a1 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -19,10 +19,11 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "string.h" #include "BigBuf.h" #include "iso14443a.h" diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c index 30db64f41..484c2f596 100644 --- a/armsrc/Standalone/hf_reblay.c +++ b/armsrc/Standalone/hf_reblay.c @@ -19,10 +19,11 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "string.h" #include "BigBuf.h" #include "iso14443a.h" diff --git a/armsrc/Standalone/hf_st25_tearoff.c b/armsrc/Standalone/hf_st25_tearoff.c index aca569882..e32b86c40 100644 --- a/armsrc/Standalone/hf_st25_tearoff.c +++ b/armsrc/Standalone/hf_st25_tearoff.c @@ -59,12 +59,13 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "iso14443b.h" // ISO14443B operations #include "util.h" #include "spiffs.h" // Flash memory filesystem access #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "BigBuf.h" #include "protocols.h" #include "crc16.h" // compute_crc @@ -368,7 +369,7 @@ static void iso14443b_setup_light(void) { FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Signal field is on with the appropriate LED -#ifdef RDV4 +#if defined RDV4 || defined PM5 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4); #else FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); diff --git a/armsrc/Standalone/hf_tcprst.c b/armsrc/Standalone/hf_tcprst.c index 58fb00163..ea95780ab 100644 --- a/armsrc/Standalone/hf_tcprst.c +++ b/armsrc/Standalone/hf_tcprst.c @@ -19,10 +19,11 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "string.h" #include "BigBuf.h" #include "iso14443a.h" diff --git a/armsrc/Standalone/hf_tmudford.c b/armsrc/Standalone/hf_tmudford.c index aa986aba0..5f69c9bd3 100644 --- a/armsrc/Standalone/hf_tmudford.c +++ b/armsrc/Standalone/hf_tmudford.c @@ -21,10 +21,11 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "iso15693.h" #include "iso15.h" diff --git a/armsrc/Standalone/hf_unisniff.c b/armsrc/Standalone/hf_unisniff.c index fcf1bb48d..c26b53632 100644 --- a/armsrc/Standalone/hf_unisniff.c +++ b/armsrc/Standalone/hf_unisniff.c @@ -122,7 +122,7 @@ #include "spiffs.h" #include "appmain.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "BigBuf.h" #include "string.h" diff --git a/armsrc/Standalone/hf_young.c b/armsrc/Standalone/hf_young.c index 079ae7c3b..3d2935a9e 100644 --- a/armsrc/Standalone/hf_young.c +++ b/armsrc/Standalone/hf_young.c @@ -21,10 +21,11 @@ #include #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "string.h" #include "commonutil.h" #include "mifarecmd.h" diff --git a/armsrc/Standalone/lf_em4100emul.c b/armsrc/Standalone/lf_em4100emul.c index b9244c59a..4e5a0b8b9 100644 --- a/armsrc/Standalone/lf_em4100emul.c +++ b/armsrc/Standalone/lf_em4100emul.c @@ -20,11 +20,12 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "lfops.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "string.h" #include "BigBuf.h" #include "commonutil.h" diff --git a/armsrc/Standalone/lf_em4100rswb.c b/armsrc/Standalone/lf_em4100rswb.c index c5fd61475..0138cd7ef 100644 --- a/armsrc/Standalone/lf_em4100rswb.c +++ b/armsrc/Standalone/lf_em4100rswb.c @@ -46,10 +46,11 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "string.h" #include "BigBuf.h" #include "spiffs.h" diff --git a/armsrc/Standalone/lf_em4100rsww.c b/armsrc/Standalone/lf_em4100rsww.c index 91b708599..959a064b6 100644 --- a/armsrc/Standalone/lf_em4100rsww.c +++ b/armsrc/Standalone/lf_em4100rsww.c @@ -47,10 +47,11 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "string.h" #include "BigBuf.h" #include "spiffs.h" diff --git a/armsrc/Standalone/lf_em4100rwc.c b/armsrc/Standalone/lf_em4100rwc.c index 95e34871f..dd1e15810 100644 --- a/armsrc/Standalone/lf_em4100rwc.c +++ b/armsrc/Standalone/lf_em4100rwc.c @@ -28,11 +28,12 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "lfops.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "string.h" #include "BigBuf.h" #include "spiffs.h" diff --git a/armsrc/Standalone/lf_hidbrute.c b/armsrc/Standalone/lf_hidbrute.c index da78d0975..57973d61a 100644 --- a/armsrc/Standalone/lf_hidbrute.c +++ b/armsrc/Standalone/lf_hidbrute.c @@ -33,10 +33,11 @@ #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "lfops.h" #define OPTS 3 diff --git a/armsrc/Standalone/lf_hidfcbrute.c b/armsrc/Standalone/lf_hidfcbrute.c index 284b098b7..b6d453544 100644 --- a/armsrc/Standalone/lf_hidfcbrute.c +++ b/armsrc/Standalone/lf_hidfcbrute.c @@ -40,15 +40,15 @@ #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "lfsampling.h" #include "util.h" #include "dbprint.h" #include "spiffs.h" -#include "ticks.h" +#include "ticks_apis.h" #include "lfops.h" #include "BigBuf.h" -#include "fpgaloader.h" #include "parity.h" // What card number should be used for the bruteforce? diff --git a/armsrc/Standalone/lf_icehid.c b/armsrc/Standalone/lf_icehid.c index c44069a12..70f00a6a0 100644 --- a/armsrc/Standalone/lf_icehid.c +++ b/armsrc/Standalone/lf_icehid.c @@ -23,12 +23,13 @@ #include "lfops.h" #include "lfsampling.h" #include "BigBuf.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" #include "printf.h" #include "spiffs.h" -#include "ticks.h" +#include "ticks_apis.h" #include "lfdemod.h" /* * `lf_hidcollect` sniffs after LF HID credentials, and stores them in internal diff --git a/armsrc/Standalone/lf_multihid.c b/armsrc/Standalone/lf_multihid.c index d2edd867c..cf471da33 100644 --- a/armsrc/Standalone/lf_multihid.c +++ b/armsrc/Standalone/lf_multihid.c @@ -25,10 +25,11 @@ #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "lfops.h" #define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0])) diff --git a/armsrc/Standalone/lf_nedap_sim.c b/armsrc/Standalone/lf_nedap_sim.c index 726f13ca8..b2002b71d 100644 --- a/armsrc/Standalone/lf_nedap_sim.c +++ b/armsrc/Standalone/lf_nedap_sim.c @@ -19,7 +19,8 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "lfops.h" #include "util.h" #include "dbprint.h" diff --git a/armsrc/Standalone/lf_nexid.c b/armsrc/Standalone/lf_nexid.c index 5789226a1..b1697d81e 100644 --- a/armsrc/Standalone/lf_nexid.c +++ b/armsrc/Standalone/lf_nexid.c @@ -24,12 +24,13 @@ #include "lfops.h" #include "lfsampling.h" #include "BigBuf.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" #include "printf.h" #include "spiffs.h" -#include "ticks.h" +#include "ticks_apis.h" #include "lfdemod.h" #include "commonutil.h" diff --git a/armsrc/Standalone/lf_prox2brute.c b/armsrc/Standalone/lf_prox2brute.c index 86c06a91a..21d7fb59e 100644 --- a/armsrc/Standalone/lf_prox2brute.c +++ b/armsrc/Standalone/lf_prox2brute.c @@ -27,7 +27,8 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" #include "lfops.h" diff --git a/armsrc/Standalone/lf_proxbrute.c b/armsrc/Standalone/lf_proxbrute.c index 112693077..740297d53 100644 --- a/armsrc/Standalone/lf_proxbrute.c +++ b/armsrc/Standalone/lf_proxbrute.c @@ -19,10 +19,11 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "lfops.h" void ModInfo(void) { diff --git a/armsrc/Standalone/lf_samyrun.c b/armsrc/Standalone/lf_samyrun.c index 215ccfa44..4da554a20 100644 --- a/armsrc/Standalone/lf_samyrun.c +++ b/armsrc/Standalone/lf_samyrun.c @@ -19,11 +19,11 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_loader.h" #include "lfops.h" #include "util.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #define OPTS 2 diff --git a/armsrc/Standalone/lf_skeleton.c b/armsrc/Standalone/lf_skeleton.c index 4272cfc7e..2008cd0ae 100644 --- a/armsrc/Standalone/lf_skeleton.c +++ b/armsrc/Standalone/lf_skeleton.c @@ -18,7 +18,8 @@ #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" #include "appmain.h" -#include "fpgaloader.h" +#include "fpga_apis.h" +#include "fpga_loader.h" #include "util.h" #include "dbprint.h" diff --git a/armsrc/Standalone/lf_tharexde.c b/armsrc/Standalone/lf_tharexde.c index 735270328..76b143a4b 100644 --- a/armsrc/Standalone/lf_tharexde.c +++ b/armsrc/Standalone/lf_tharexde.c @@ -17,13 +17,13 @@ // main code for EM4x50 simulator and collector aka THAREXDE //----------------------------------------------------------------------------- #include -#include "ticks.h" +#include "ticks_apis.h" #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" #include "BigBuf.h" #include "commonutil.h" -#include "fpgaloader.h" +#include "fpga_apis.h" #include "util.h" #include "dbprint.h" #include "spiffs.h" @@ -279,11 +279,7 @@ void RunMod(void) { } } - // reset timer - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // re-enable timer and wait for TC0 - AT91C_BASE_TC0->TC_RC = 0; // set TIOA (carry bit) on overflow, return to zero - AT91C_BASE_TC0->TC_RA = 1; // clear carry bit on next clock cycle - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // reset and re-enable timer + ResetTicks(); } if (state == STATE_READ) { diff --git a/armsrc/appmain.c b/armsrc/appmain.c index a37642b38..9b8946c27 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -20,13 +20,15 @@ //----------------------------------------------------------------------------- #include "appmain.h" -#include "clocks.h" -#include "usb_cdc.h" +#include "sys_apis.h" +#include "usb_cdc_apis.h" #include "proxmark3_arm.h" #include "dbprint.h" #include "pmflash.h" #include "fpga.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" +#include "rssi_apis.h" #include "string.h" #include "printf.h" #include "legicrf.h" @@ -62,7 +64,7 @@ #include "pcf7931.h" #include "Standalone/standalone.h" #include "util.h" -#include "ticks.h" +#include "ticks_apis.h" #include "commonutil.h" #include "crc16.h" #include "protocols.h" @@ -72,6 +74,7 @@ #include "sam_mfc.h" #include "sam_sc.h" #include "cmac_calc.h" +#include "i2c.h" #ifdef WITH_LCD #include "LCD_disabled.h" @@ -146,47 +149,6 @@ void send_wtx(uint16_t wtx) { } } -//----------------------------------------------------------------------------- -// Read an ADC channel and block till it completes, then return the result -// in ADC units (0 to 1023). Also a routine to sum up a number of samples and -// return that. -//----------------------------------------------------------------------------- -static uint16_t ReadAdc(uint8_t ch) { - - // Note: ADC_MODE_PRESCALE and ADC_MODE_SAMPLE_HOLD_TIME are set to the maximum allowed value. - // AMPL_HI is are high impedance (10MOhm || 1MOhm) output, the input capacitance of the ADC is 12pF (typical). This results in a time constant - // of RC = (0.91MOhm) * 12pF = 10.9us. Even after the maximum configurable sample&hold time of 40us the input capacitor will not be fully charged. - // - // The maths are: - // If there is a voltage v_in at the input, the voltage v_cap at the capacitor (this is what we are measuring) will be - // - // v_cap = v_in * (1 - exp(-SHTIM/RC)) = v_in * (1 - exp(-40us/10.9us)) = v_in * 0,97 (i.e. an error of 3%) - - AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; - AT91C_BASE_ADC->ADC_MR = - ADC_MODE_PRESCALE(63) // ADC_CLK = MCK / ((63+1) * 2) = 48MHz / 128 = 375kHz - | ADC_MODE_STARTUP_TIME(1) // Startup Time = (1+1) * 8 / ADC_CLK = 16 / 375kHz = 42,7us Note: must be > 20us - | ADC_MODE_SAMPLE_HOLD_TIME(15); // Sample & Hold Time SHTIM = 15 / ADC_CLK = 15 / 375kHz = 40us - - AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ch); - AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; - - while (!(AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ch))) {}; - - return (AT91C_BASE_ADC->ADC_CDR[ch] & 0x3FF); -} - -// was static - merlok -uint16_t AvgAdc(uint8_t ch) { - return SumAdc(ch, 32) >> 5; -} - -uint16_t SumAdc(uint8_t ch, uint8_t NbSamples) { - uint16_t a = 0; - for (uint8_t i = 0; i < NbSamples; i++) - a += ReadAdc(ch); - return (a + (NbSamples >> 1) - 1); -} #ifdef WITH_LF static void MeasureAntennaTuning(void) { @@ -230,7 +192,7 @@ static void MeasureAntennaTuning(void) { WDT_HIT(); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, i); SpinDelay(20); - uint32_t adcval = ((MAX_ADC_LF_VOLTAGE * (SumAdc(ADC_CHAN_LF, 32) >> 1)) >> 14); + uint32_t adcval = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_LF); if (i == LF_DIVISOR_125) payload.v_lf125 = adcval; // voltage at 125kHz @@ -255,24 +217,15 @@ static void MeasureAntennaTuning(void) { FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); SpinDelay(50); - payload.v_hf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; + payload.v_hf = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); reply_ng(CMD_MEASURE_ANTENNA_TUNING, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); LEDsoff(); } #endif -// Measure HF in milliVolt -static uint16_t MeasureAntennaTuningHfData(void) { - return (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; - -} - -// Measure LF in milliVolt -static uint32_t MeasureAntennaTuningLfData(void) { - return (MAX_ADC_LF_VOLTAGE * (SumAdc(ADC_CHAN_LF, 32) >> 1)) >> 14; -} +#ifndef PM5 // TODO DXL: PM5 is temporarily incompatible. // Measure HF antenna decay after field-off. // Captures peak-detect capacitor discharge curve via burst ADC sampling. @@ -297,7 +250,7 @@ static void MeasureAntennaTuningHfDecay(const hf_decay_params_t *params) { SpinDelay(stabilize_ms); // Baseline measurement (averaged) - payload.baseline_mv = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; + payload.baseline_mv = (MAX_ADC_HF_VOLTAGE * AdcRssiSum(ADC_RSSI_CH_HF, 32)) >> 15; // Configure ADC for fast burst mode. // Faster ADC clock + shorter S&H trades absolute accuracy for speed. @@ -354,6 +307,8 @@ static void MeasureAntennaTuningHfDecay(const hf_decay_params_t *params) { LEDsoff(); } +#endif + void print_stack_usage(void) { for (uint32_t *p = _stack_start; ; ++p) { if (*p != 0xdeadbeef) { @@ -429,7 +384,10 @@ static void SendVersion(void) { } PACKED; struct p payload; - payload.id = *(AT91C_DBGU_CIDR); + + // Set a CHIP ID(not unique id) + payload.id = GetChipId(); + #ifndef WITH_COMPRESSION payload.section_size = (uint32_t)_bootrom_end - (uint32_t)_bootrom_start + (uint32_t)__os_size__; #else @@ -441,6 +399,8 @@ static void SendVersion(void) { reply_ng(CMD_VERSION, PM3_SUCCESS, (uint8_t *)&payload, 12 + payload.versionstr_len); } +#ifdef CHIP_AT91SAM7S // Only AT91SAM7S chip series need calibration. + static void TimingIntervalAcquisition(void) { // trigger new acquisition by turning main oscillator off and on mck_from_pll_to_slck(); @@ -449,6 +409,8 @@ static void TimingIntervalAcquisition(void) { StartTickCount(); } +#endif + static void print_debug_level(void) { char dbglvlstr[20] = {0}; switch (g_dbglevel) { @@ -529,6 +491,9 @@ static void SendStatus(uint32_t wait) { tosend_t *ts = get_tosend(); Dbprintf(" ToSendMax........... %d", ts->max); Dbprintf(" ToSend BUFFERSIZE... %d", TOSEND_BUFFER_SIZE); + +#ifdef CHIP_AT91SAM7S + while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available... uint16_t mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF; // Get # main clocks within 16 slow clocks Dbprintf(" Slow clock.......... %d Hz", (16 * MAINCK) / mainf); @@ -543,6 +508,9 @@ static void SendStatus(uint32_t wait) { Dbprintf(_YELLOW_(" Slow Clock actual speed seems closer to %d kHz"), (16 * MAINCK / 1000) / mainf * delta_time / SLCK_CHECK_MS); } + +#endif + DbpString(_CYAN_("Installed StandAlone Mode")); ModInfo(); @@ -816,7 +784,7 @@ void ListenReaderField(uint8_t limit) { LEDsoff(); if (limit == LF_ONLY || limit == LF_HF_BOTH) { - lf_av = lf_max = (MAX_ADC_LF_VOLTAGE * SumAdc(ADC_CHAN_LF, 32)) >> 15; + lf_av = lf_max = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_LF); Dbprintf("LF 125/134kHz Baseline: %dmV", lf_av); lf_baseline = lf_av; } @@ -824,7 +792,7 @@ void ListenReaderField(uint8_t limit) { if (limit == HF_ONLY || limit == LF_HF_BOTH) { // iceman, useless, since we are measuring readerfield, not our field. My tests shows a max of 20v from a reader. - hf_av = hf_max = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; + hf_av = hf_max = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF);; Dbprintf("HF 13.56MHz Baseline: %dmV", hf_av); hf_baseline = hf_av; } @@ -864,7 +832,7 @@ void ListenReaderField(uint8_t limit) { LED_D_OFF(); } - lf_av_new = (MAX_ADC_LF_VOLTAGE * SumAdc(ADC_CHAN_LF, 32)) >> 15; + lf_av_new = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_LF); // see if there's a significant change if (ABS(lf_av - lf_av_new) > REPORT_CHANGE) { Dbprintf("LF 125/134kHz Field Change: %5dmV", lf_av_new); @@ -882,7 +850,7 @@ void ListenReaderField(uint8_t limit) { LED_B_OFF(); } - hf_av_new = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; + hf_av_new = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF); // see if there's a significant change if (ABS(hf_av - hf_av_new) > REPORT_CHANGE) { Dbprintf("HF 13.56MHz Field Change: %5dmV", hf_av_new); @@ -965,6 +933,188 @@ void ListenReaderField(uint8_t limit) { } } } + +#ifdef PM5 + +#include "at32f435_437_crm.h" +#include "at32f435_437_tmr.h" + +// TODO DXL: 一部分QC逻辑可以放在PM5设备端实现,这个函数后面记得复用代码,并且不要放在 appmain.c 中(考虑移动到平台专属的模块) +// failed_item == 0: BLUE LED in Antenna +// failed_item == 1: RGB in mainboard +// failed_item == 2: LEDs * 4 or Buzzer or Button in mainboard +static bool QCTestPM5(uint8_t *failed_item) { + // 天线蓝灯、主板RGB、主板四颗LED、蜂鸣器、按钮 + StartTicks(); + I2C_init(true); + + uint8_t addr_ant = 0x51; // TODO DXL define move to header? + uint8_t addr_rgb = 0x48; + uint8_t data_u8; + bool isok = false; + + // 读取天线当前MAP配置,如果读取不到,则认为天线的控制芯片可能有问题 + isok = I2C_BufferReadRaw(&data_u8, 1, 0x02, addr_ant << 1); + if (!isok) { + *failed_item = 0; + return false; + } + // 重新写入天线的MAP配置,去开灯 + data_u8 |= 0x06; // 0000 0110 // 125 134 250 375 500 HFLED LFLED Q + isok = I2C_BufferWrite(&data_u8, 1, 0x02, addr_ant << 1); + + // 开启RGB灯自动闪烁 + uint8_t buf_rgb[3] = {0, 0, 128}; + uint8_t buf_flash_time[] = {50, 50}; // 1s on, 500ms off. + isok = I2C_WriteByte(0, 0x02, addr_rgb << 1); // 写索引寄存器,设置后续操作的RGB索引 + if (!isok) { + *failed_item = 1; + return false; + } + isok = I2C_WriteByte(1, 0x01, addr_rgb << 1); // 写数量寄存器,设置硬件挂1个灯,很重要!!!,不然无法闪灯 + if (!isok) { + *failed_item = 1; + return false; + } + isok = I2C_BufferWrite(buf_rgb, sizeof(buf_rgb), 0x03, addr_rgb << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + if (!isok) { + *failed_item = 1; + return false; + } + isok = I2C_WriteByte(1, 0x06, addr_rgb << 1); // 写闪灯使能寄存器,使能 0 号灯珠的可控闪烁 + if (!isok) { + *failed_item = 1; + return false; + } + isok = I2C_BufferWrite(buf_flash_time, sizeof(buf_flash_time), 0x07, addr_rgb << 1); // 写闪灯使能寄存器,使能 0 号灯珠的可控闪烁 + if (!isok) { + *failed_item = 1; + return false; + } + + // 在循环中测试LED、蜂鸣器、按钮 + +#define BEEPER_EN_GPIO GPIOB +#define BEEPER_EN_GPIO_PIN GPIO_PINS_13 +#define BEEPER_MOD_GPIO GPIOC +#define BEEPER_MOD_GPIO_PIN GPIO_PINS_9 +#define BEEPER_MOD_GPIO_SRC GPIO_PINS_SOURCE9 +#define BEEPER_MOD_GPIO_MUX GPIO_MUX_3 +#define BEEPER_MOD_TMR TMR8 +#define BEEPER_MOD_TMR_CH TMR_SELECT_CHANNEL_4 + + // PB13 使能,PC9 调制,使用 TMR8_CH4 输出调制 + crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE); + crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE); + crm_periph_clock_enable(CRM_TMR8_PERIPH_CLOCK, TRUE); + + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + // 蜂鸣器使能脚 + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; + gpio_init_struct.gpio_pins = BEEPER_EN_GPIO_PIN; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init(BEEPER_EN_GPIO, &gpio_init_struct); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, FALSE); + // 蜂鸣器调制脚 + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_pins = BEEPER_MOD_GPIO_PIN; + gpio_init(BEEPER_MOD_GPIO, &gpio_init_struct); + gpio_pin_mux_config(BEEPER_MOD_GPIO, BEEPER_MOD_GPIO_SRC, BEEPER_MOD_GPIO_MUX); + + tmr_internal_clock_set(BEEPER_MOD_TMR); + tmr_reset(BEEPER_MOD_TMR); + tmr_base_init(BEEPER_MOD_TMR, 999, 95); // 192M出2k + tmr_output_config_type tmr_output_struct; + tmr_output_default_para_init(&tmr_output_struct); + tmr_output_struct.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A; + tmr_output_struct.oc_polarity = TMR_OUTPUT_ACTIVE_HIGH; + tmr_output_struct.oc_output_state = TRUE; + tmr_output_channel_config(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, &tmr_output_struct); + tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 500); // 比较值=500 (50%占空比) + tmr_counter_enable(BEEPER_MOD_TMR, TRUE); + tmr_output_enable(BEEPER_MOD_TMR, TRUE); + + LEDsoff(); // 在开始测试之前线关闭所有LED + + *failed_item = 2; + // 在开始测试之前,如果按钮是按下的,则认为失败,有可能按钮不良卡住了 + if (BUTTON_PRESS()) { + return false; + } + + while (1) { + if (BUTTON_PRESS()) { + return true; + } + if (data_available()) { + return false; + } + + LED_A_ON(); + BEEPER_MOD_TMR->pr = 999; + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, TRUE); + SpinDelay(20); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, FALSE); + SpinDelay(200); + LED_A_OFF(); + + if (BUTTON_PRESS()) { + return true; + } + if (data_available()) { + return false; + } + + LED_B_ON(); + BEEPER_MOD_TMR->pr = 1100; + tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 550); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, TRUE); + SpinDelay(20); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, FALSE); + SpinDelay(200); + LED_B_OFF(); + + if (BUTTON_PRESS()) { + return true; + } + if (data_available()) { + return false; + } + + LED_C_ON(); + BEEPER_MOD_TMR->pr = 1200; + tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 600); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, TRUE); + SpinDelay(20); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, FALSE); + SpinDelay(200); + LED_C_OFF(); + + if (BUTTON_PRESS()) { + return true; + } + if (data_available()) { + return false; + } + + LED_D_ON(); + BEEPER_MOD_TMR->pr = 1300; + tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 650); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, TRUE); + SpinDelay(20); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, FALSE); + SpinDelay(200); + LED_D_OFF(); + } + + return true; +} + +#endif + static void PacketReceived(PacketCommandNG *packet) { /* if (packet->ng) { @@ -2684,7 +2834,7 @@ static void PacketReceived(PacketCommandNG *packet) { if (button_status == BUTTON_SINGLE_CLICK) { reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_EOPABORTED, NULL, 0); } - uint16_t volt = MeasureAntennaTuningHfData(); + uint32_t volt = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF); reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_SUCCESS, (uint8_t *)&volt, sizeof(volt)); break; case 3: @@ -2697,10 +2847,12 @@ static void PacketReceived(PacketCommandNG *packet) { } break; } +#ifndef PM5 case CMD_HF_DECAY: { MeasureAntennaTuningHfDecay((const hf_decay_params_t *)packet->data.asBytes); break; } +#endif case CMD_MEASURE_ANTENNA_TUNING_LF: { if (packet->length != 2) reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_EINVARG, NULL, 0); @@ -2718,7 +2870,7 @@ static void PacketReceived(PacketCommandNG *packet) { reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_EOPABORTED, NULL, 0); } - uint32_t volt = MeasureAntennaTuningLfData(); + uint32_t volt = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_LF); reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_SUCCESS, (uint8_t *)&volt, sizeof(volt)); break; case 3: @@ -2848,7 +3000,7 @@ static void PacketReceived(PacketCommandNG *packet) { base = (uint8_t *) _flash_start; - size_t flash_size = get_flash_size(); + size_t flash_size = GetChipFlashSize(); // Boundary check the offset. if (offset > flash_size) { @@ -3072,7 +3224,7 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_FLASHMEM_SET_SPIBAUDRATE: { if (packet->length != sizeof(uint32_t)) break; - FlashmemSetSpiBaudrate(packet->data.asDwords[0]); + Flash_SetSpiBaudrate(packet->data.asDwords[0]); break; } case CMD_FLASHMEM_WRITE: { @@ -3199,6 +3351,16 @@ static void PacketReceived(PacketCommandNG *packet) { LED_B_OFF(); break; } + case CMD_FLASHMEM_INFO: { + uint64_t flash_uniqueID = 0; + bool isok = FlashInit(); + if (isok) { + isok = Flash_UniqueID((uint8_t *)(&flash_uniqueID)); + FlashStop(); + } + reply_ng(CMD_FLASHMEM_INFO, (isok) ? PM3_SUCCESS : PM3_EFLASH, (uint8_t *)&flash_uniqueID, sizeof(flash_uniqueID)); + break; + } #endif #ifdef WITH_LF case CMD_LF_SET_DIVISOR: { @@ -3210,17 +3372,17 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_SET_ADC_MUX: { switch (packet->data.asBytes[0]) { case 0: - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + SetAdcMuxFor(ADC_MUXSEL_LOPKD); break; case 2: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); break; #ifndef WITH_FPC_USART case 1: - SetAdcMuxFor(GPIO_MUXSEL_LORAW); + SetAdcMuxFor(ADC_MUXSEL_LORAW); break; case 3: - SetAdcMuxFor(GPIO_MUXSEL_HIRAW); + SetAdcMuxFor(ADC_MUXSEL_HIRAW); break; #endif } @@ -3238,7 +3400,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_TIA: { - +#ifdef CHIP_AT91SAM7S while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available... uint16_t mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF; Dbprintf(" Slow clock old measured value:.........%d Hz", (16 * MAINCK) / mainf); @@ -3249,6 +3411,10 @@ static void PacketReceived(PacketCommandNG *packet) { Dbprintf(""); // first message gets lost Dbprintf(" Slow clock new measured value:.........%d Hz", (16 * MAINCK) / mainf); reply_ng(CMD_TIA, PM3_SUCCESS, NULL, 0); +#else + Dbprintf("Chip is not AT91SAM7S, TIA is " _RED_("unsupported")); + reply_ng(CMD_TIA, PM3_EDEVNOTSUPP, NULL, 0); +#endif break; } case CMD_STANDALONE: { @@ -3294,8 +3460,8 @@ static void PacketReceived(PacketCommandNG *packet) { usb_disable(); // (iceman) why this wait? - SpinDelay(1000); - AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; + SpinDelay(1000); // Go wait for the USB to completely go offline on the host side. + ResetChip(); // We're going to reset, and the bootrom will take control. for (;;) {} break; @@ -3305,7 +3471,7 @@ static void PacketReceived(PacketCommandNG *packet) { g_common_area.command = COMMON_AREA_COMMAND_ENTER_FLASH_MODE; } usb_disable(); - AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; + ResetChip(); // We're going to flash, and the bootrom will take control. for (;;) {} break; @@ -3318,13 +3484,126 @@ static void PacketReceived(PacketCommandNG *packet) { reply_old(CMD_DEVICE_INFO, dev_info, 0, 0, 0, 0); break; } + case CMD_FPGA_BITSTREAM_CONFIG_START: // Merge 3 cmds to reuse some code. + case CMD_FPGA_BITSTREAM_CONFIG_WRITE: + case CMD_FPGA_BITSTREAM_CONFIG_FINISH: { + // Dbprintf("Received FPGA config command 0x%04x", packet->cmd); + int res; + // Process + if (packet->cmd == CMD_FPGA_BITSTREAM_CONFIG_START) { + struct p { + uint8_t sram_mode; + uint32_t file_length; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + res = FpgaStartConfig(payload->sram_mode, payload->file_length); + } else if (packet->cmd == CMD_FPGA_BITSTREAM_CONFIG_WRITE) { + res = FpgaConfigWrite(packet->data.asBytes, packet->length); + } else { + res = FpgaStopConfig(); + } + // Response + if (res == PM3_EFAILED) { + uint32_t plat_status = FpgaConfigPlatformStatus(); // Return status code of platform when res is PM3_EFAILED + reply_ng(packet->cmd, res, (uint8_t*)&plat_status, sizeof(plat_status)); + } else { + reply_ng(packet->cmd, res, NULL, 0); + } + break; + } +#ifdef PM5 + case CMD_ANT_CONTROL_WRITE: { + struct p { + uint8_t data; + uint8_t reg_type; // 0 is io reg, 1 is map reg. + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + + StartTicks(); + I2C_init(true); + + uint8_t addr = 0x51; // TODO DXL define move to header? + uint8_t cmd = payload->reg_type == 0 ? 0x01 : 0x02; + + bool isok = I2C_BufferWrite(&payload->data, 1, cmd, addr << 1); + reply_ng(CMD_ANT_CONTROL_WRITE, isok ? PM3_SUCCESS : PM3_EFAILED, NULL, 0); + break; + } + case CMD_ANT_CONTROL_READ: { + struct p { + uint8_t reg_type; // 0 is io reg, 1 is map reg. + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + + StartTicks(); + I2C_init(true); + + uint8_t addr = 0x51; // TODO DXL define move to header? + uint8_t cmd = payload->reg_type == 0 ? 0x01 : 0x02; + uint8_t data; + + bool isok = I2C_BufferReadRaw(&data, 1, cmd, addr << 1); + reply_ng(CMD_ANT_CONTROL_READ, isok ? PM3_SUCCESS : PM3_EFAILED, &data, sizeof(data)); + break; + } + case CMD_EEPROM_FACTORY_INFO_READ: { + StartTicks(); + I2C_init(true); + + uint8_t addr = 0x50; // TODO DXL define move to header? + uint8_t data[256]; // 24c02: 256byte + bool isok = I2C_BufferReadRaw(data, sizeof(data), 0x00, addr << 1); + reply_ng(CMD_EEPROM_FACTORY_INFO_READ, isok ? PM3_SUCCESS : PM3_EFAILED, data, sizeof(data)); + break; + } + case CMD_EEPROM_FACTORY_INFO_WRITE: { + StartTicks(); + I2C_init(true); + + uint8_t addr = 0x50; // TODO DXL define move to header? + uint16_t len = packet->length; + while (len) { + uint16_t write_len = MIN(len, 16); + uint16_t write_pos = packet->length - len; + bool isok = I2C_BufferWrite(packet->data.asBytes + write_pos, write_len, write_pos, addr << 1); + if (!isok) { + reply_ng(CMD_EEPROM_FACTORY_INFO_WRITE, PM3_EFAILED, NULL, 0); + return; + } + len -= write_len; + // 24C02 writes to a page write buffer of only 16 bytes. + // If the write speed is too fast, it may cause data write failure. + // Therefore, a delay or ACK judgment is required between page writes + SpinDelay(5); // 24C02 write cycle time is about 5ms + } + reply_ng(CMD_EEPROM_FACTORY_INFO_WRITE, PM3_SUCCESS, NULL, 0); + break; + } +#endif + case CMD_FPGA_CMD_SET_PWR_PWM_LOW_COUNT: { + struct p { + uint8_t is_lf; + uint16_t count; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + FpgaDownloadAndGo(payload->is_lf ? FPGA_BITSTREAM_LF : FPGA_BITSTREAM_HF); + FpgaSendCommand(FPGA_CMD_SET_PWR_PWM_LOW_COUNT, payload->count & 0xFFF); + reply_ng(CMD_FPGA_CMD_SET_PWR_PWM_LOW_COUNT, PM3_SUCCESS, NULL, 0); + break; + } case CMD_MAIN_CHIP_UNIQUEID: { - // PM3 placeholder, to be replaced when correct commit gets merged uint8_t size = 0; - uint8_t* uid = NULL; + uint8_t* uid = GetChipUniqueId(&size); reply_ng(CMD_MAIN_CHIP_UNIQUEID, PM3_SUCCESS, uid, size); break; } +#ifdef PM5 + case CMD_PM5_QC_TEST: { + uint8_t failed_item = 0; + reply_ng(CMD_PM5_QC_TEST, QCTestPM5(&failed_item) ? PM3_SUCCESS : PM3_EFAILED, &failed_item, 1); + break; + } +#endif default: { Dbprintf("%s: 0x%04x", "unknown command:", packet->cmd); break; @@ -3332,7 +3611,7 @@ static void PacketReceived(PacketCommandNG *packet) { } } -void __attribute__((noreturn)) AppMain(void) { +void __attribute__((noreturn)) AppMain(void) { SpinDelay(100); BigBuf_initialize(); @@ -3344,23 +3623,12 @@ void __attribute__((noreturn)) AppMain(void) { LEDsoff(); - // The FPGA gets its clock from us from PCK0 output, so set that up. - AT91C_BASE_PIOA->PIO_BSR = GPIO_PCK0; - AT91C_BASE_PIOA->PIO_PDR = GPIO_PCK0; - AT91C_BASE_PMC->PMC_SCER |= AT91C_PMC_PCK0; - // PCK0 is PLL clock / 4 = 96MHz / 4 = 24MHz - AT91C_BASE_PMC->PMC_PCKR[0] = AT91C_PMC_CSS_PLL_CLK | AT91C_PMC_PRES_CLK_4; // 4 for 24MHz pck0, 2 for 48 MHZ pck0 - AT91C_BASE_PIOA->PIO_OER = GPIO_PCK0; - - // Reset SPI - AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SWRST; - AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SWRST; // errata says it needs twice to be correctly set. - - // Reset SSC - AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST; + // Setup FPGA clock & Reset COM + FpgaSetup24MHzClk(); + FpgaResetComInterface(); // Configure MUX - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); // Load the FPGA image, which we have stored in our flash. // (the HF version by default) @@ -3389,12 +3657,10 @@ void __attribute__((noreturn)) AppMain(void) { } #endif - #ifdef WITH_FLASH // If flash is not present, BUSY_TIMEOUT kicks in, let's do it after USB loadT55xxConfig(); - // // Enforce a spiffs check/garbage collection at boot so we are likely to never // fall under the 2 contigous free blocks availables // This is a time-consuming process on large flash. @@ -3457,9 +3723,68 @@ void __attribute__((noreturn)) AppMain(void) { * So this is the trigger to execute a standalone mod. Generic entrypoint by following the standalone/standalone.h headerfile * All standalone mod "main loop" should be the RunMod() function. */ - allow_send_wtx = false; - RunMod(); - allow_send_wtx = true; + // allow_send_wtx = false; + // RunMod(); + // allow_send_wtx = true; + +#ifdef PM5 // TODO DXL Test long press to device shutdown, temporarily blocking standalone mod + + /* + StartTicks(); + I2C_init(true); + uint8_t addr = 0x51; + // 125 134 250 375 500 HFLED LFLED Q + // 1 0 0 0 0 1 1 1 + uint8_t data = 0x87; + I2C_BufferWrite(&data, 1, 0x02, addr << 1); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + FpgaSendCommand(FPGA_CMD_SET_PWR_PWM_LOW_COUNT, 4095); + + static bool b = 0; + if (b) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + b = 0; + } else { + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); + b = 1; + } + */ + + LEDsoff(); + while (BUTTON_PRESS()) { + SpinDelay(50); + LED_A_INV(); + SpinDelay(50); + LED_B_INV(); + SpinDelay(50); + LED_C_INV(); + SpinDelay(50); + LED_D_INV(); + } + // Release for more than 100ms before truly shutting down, anti shake + uint8_t idx = 0; + while (!BUTTON_PRESS()) { + SpinDelay(10); + idx += 1; + if (idx == 10) { + break; + } + } + LEDsoff(); + if (idx == 10) { + SpinDelay(100); + LED_A_INV(); + SpinDelay(100); + LED_A_INV(); + SpinDelay(100); + LED_A_INV(); + Gpio_ARM_Power_ON_Low(); + while (1); // Wait for system power off. + } + +#endif } } } diff --git a/armsrc/appmain.h b/armsrc/appmain.h index 40cbdb52d..b2fafc107 100644 --- a/armsrc/appmain.h +++ b/armsrc/appmain.h @@ -28,16 +28,6 @@ extern bool g_hf_field_timeout_active; void hf_field_off(void); int tearoff_hook(void); -#if defined RDV4 || defined ICOPYX -// ADC Vref = 3300mV, and an (10000k+240k):240k voltage divider on the LF input can measure voltages up to 140800 mV -#define MAX_ADC_HF_VOLTAGE 140800 -#else -// ADC Vref = 3300mV, and an (10M+1M):1M voltage divider on the HF input can measure voltages up to 36300 mV -#define MAX_ADC_HF_VOLTAGE 36300 -#endif -// ADC Vref = 3300mV, (240k-10M):240k voltage divider, 140800 mV -#define MAX_ADC_LF_VOLTAGE 140800 - // Default connection speed test timeout, used in hw status #define CONN_SPEED_TEST_MIN_TIME_DEFAULT 500 // in milliseconds @@ -48,9 +38,6 @@ void send_wtx(uint16_t wtx); void ReadMem(int addr); void __attribute__((noreturn)) AppMain(void); -uint16_t AvgAdc(uint8_t ch); -uint16_t SumAdc(uint8_t ch, uint8_t NbSamples); - //void PrintToSendBuffer(void); void ToSendStuffBit(int b); void ToSendReset(void); diff --git a/armsrc/at32_unit_test.c b/armsrc/at32_unit_test.c new file mode 100644 index 000000000..66e9e1c98 --- /dev/null +++ b/armsrc/at32_unit_test.c @@ -0,0 +1,2212 @@ +// +// Created by dxl on 2026/6/4. +// +#include +#include +#include + +#include "at32f435_437.h" +#include "at32f435_437_misc.h" +#include "at32f435_437_crm.h" +#include "at32f435_437_gpio.h" +#include "at32f435_437_spi.h" +#include "at32f435_437_dma.h" +#include "at32f435_437_i2c.h" +#include "at32f435_437_i2c_app.h" +#include "at32f435_437_usart.h" +#include "at32f435_437_tmr.h" +#include "at32f435_437_exint.h" +#include "at32f435_437_scfg.h" + +#include "cdc_class.h" +#include "usb_core.h" +#include "printf.h" +#include "usb_cdc_apis.h" +#include "flashmem.h" +#include "gpio_apis.h" +#include "util.h" +#include "fpga_apis.h" +#include "rssi_apis.h" +#include "fpga_gw_jtag.h" +#include "i2c.h" +#include "commonutil.h" +#include "sys_apis.h" +#include "ticks_apis.h" +#include "proxmark3_arm.h" +#include "appmain.h" + +// Enable or Disable unit test. +#define DXL_DEBUG 1 +#if DXL_DEBUG + +// 检查按钮是否按下 +static uint8_t is_btn_pressed(void) { + if (BUTTON_PRESS()) { + SpinDelay(2); // 等待一小会儿,简单防抖 + if (BUTTON_PRESS()) { + return 1; + } + } + return 0; +} + +char debug_pbuf[1024] = {0}; + +void dxl_print_dbg(const char *fmt, ...); + +void dxl_print_dbg(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + kvsprintf(fmt, debug_pbuf, 10, ap); + va_end(ap); + usb_write((uint8_t *) debug_pbuf, strlen(debug_pbuf)); // 直接串口传回去,这样子不需要开客户端 +} + +void test_i2c_rgb_simple(void) { + int idx = 0; + uint8_t addr = 0x48; + uint8_t buf_rgb[3] = {0, 0, 200}; + // uint8_t buf_flash_time[] = {50, 50}; // 1s on, 500ms off. + + StartTicks(); + I2C_init(true); + I2C_WriteByte(idx, 0x02, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + I2C_WriteByte(1, 0x01, addr << 1); // 写数量寄存器,设置硬件挂1个灯,很重要!!!,不然无法闪灯 + I2C_BufferWrite(buf_rgb, sizeof(buf_rgb), 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + // I2C_WriteByte(1, 0x06, addr << 1); // 写闪灯使能寄存器,使能 idx 对应的灯珠的可控闪烁 + // I2C_BufferWrite(buf_flash_time, sizeof(buf_flash_time), 0x07, addr << 1); // 写闪灯使能寄存器,使能 idx 对应的灯珠的可控闪烁 +} + +void test_usb_id_pin(void) { + gpio_init_type gpio_init_struct; + crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); + gpio_default_para_init(&gpio_init_struct); + usb_enable(); // 要初始化USB口,printf调试大法好 + SpinDelay(1000); +#define TEST_USB1_ID_PIN_MODE 1 // 测试模式,为 0 是直接测试读取IO,为1测试OTGFS外设中断 +#if TEST_USB1_ID_PIN_MODE == 0 // 测试读取IO的模式 + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; + gpio_init_struct.gpio_pins = GPIO_PINS_10; // PA10_USB1_ID + gpio_init(GPIOA, &gpio_init_struct); + while (1) { + dxl_print_dbg("当前设备是%s模式\n", GpioInputStatus(GPIOA, GPIO_PINS_10) ? "从机" : "主机"); + SpinDelay(800); + } +#endif +#if TEST_USB1_ID_PIN_MODE == 1 // 测试OTGFS外设中断的模式 + crm_periph_clock_enable(CRM_OTGFS1_PERIPH_CLOCK, TRUE); // 使能USB_OTG1互联口的时钟 + // 初始化和MUX互联口的ID脚 + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init_struct.gpio_pins = GPIO_PINS_10; + gpio_init(GPIOA, &gpio_init_struct); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE10, GPIO_MUX_10); + while (1) { + dxl_print_dbg("当前设备是%s模式\n", OTG1_GLOBAL->gotgctrl_bit.cidsts ? "从机" : "主机"); + SpinDelay(800); + } +#endif +} + +#define I2Cx_ADDRESS (0x58 << 1) +#define I2C_TIMEOUT 0xFFFFFFFF + +void i2c_lowlevel_init(i2c_handle_type *hi2c) { + gpio_init_type gpio_init_structure; + + /* i2c periph clock enable */ + crm_periph_clock_enable(CRM_I2C1_PERIPH_CLOCK, TRUE); + crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE); + + /* configure i2c pins: sda &scl */ + gpio_init_structure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_structure.gpio_mode = GPIO_MODE_MUX; + gpio_init_structure.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN; + gpio_init_structure.gpio_pull = GPIO_PULL_NONE; + gpio_init_structure.gpio_pins = GPIO_PINS_7 | GPIO_PINS_6; // PC7_I2C1_SDA | PC6_I2C1_SCL + gpio_init(GPIOC, &gpio_init_structure); + + /* gpio configuration */ + gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE7, GPIO_MUX_4); + gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE6, GPIO_MUX_4); + + /* config i2c */ + // 0xB170FFFF // 10K + // 0xC0E06969 // 50K + // 0x80504C4E // 100K + // 0x30F03C6B // 200K + i2c_init(hi2c->i2cx, 0x0F, 0xB170FFFF); + + i2c_own_address1_set(hi2c->i2cx, I2C_ADDRESS_MODE_7BIT, I2Cx_ADDRESS); +} + +void test_i2c_rgb(void) { + i2c_status_type i2c_status; + + usb_enable(); // 要初始化USB口,printf调试大法好 + SpinDelay(1000); + + // 初始化I2C外设 + i2c_handle_type hi2cx; + hi2cx.i2cx = I2C1; + i2c_config(&hi2cx); + +#define I2C_RGB_TEST_BUF_SIZE (6) + uint8_t tx_buf[I2C_RGB_TEST_BUF_SIZE]; + uint8_t rx_buf[I2C_RGB_TEST_BUF_SIZE]; + (void) tx_buf; + (void) rx_buf; + + while (1) { + while (is_btn_pressed()) { + } // 等待松开按钮 + +#if 0 // 需要测试读取吗 + + dxl_print_dbg("按下按钮开始测试读取 > \r\n"); + + // 等待按钮按下,就去读取一次 + while (!is_btn_pressed()) { + } + + if ((i2c_status = i2c_master_receive(&hi2cx, I2Cx_ADDRESS, rx_buf, I2C_RGB_TEST_BUF_SIZE, I2C_TIMEOUT)) != + I2C_OK) { + dxl_print_dbg("读取异常:%d\r\n", i2c_status); + continue; // 异常的话,直接跳过下面的代码,重新尝试执行 + } + + dxl_print_dbg("读取完成:"); + for (int i = 0; i < I2C_RGB_TEST_BUF_SIZE; ++i) dxl_print_dbg("%02x ", rx_buf[i]); + dxl_print_dbg("\r\n"); + +#endif + + SpinDelay(500); + +#if 1 // 需要测试写入吗 + + dxl_print_dbg("按下按钮开始测试写入 > \r\n"); + + // 等待按钮按下,就去写入一次 + while (!is_btn_pressed()) { + } + + // 初始化tx_buf,填充一些奇怪的数据进去 + for (int i = 0; i < I2C_RGB_TEST_BUF_SIZE; ++i) { + tx_buf[i] = i; // 把序号填进去就行了 + } + + dxl_print_dbg("开始写入... \r\n"); + + if ((i2c_status = i2c_master_transmit(&hi2cx, I2Cx_ADDRESS, tx_buf, I2C_RGB_TEST_BUF_SIZE, I2C_TIMEOUT)) != + I2C_OK) { + dxl_print_dbg("写入异常:%d\r\n", i2c_status); + continue; // 异常的话,直接跳过下面的代码,重新尝试执行 + } + + dxl_print_dbg("写入完成\r\n"); + +#endif + } +} + +void test_i2c_cc(void) { + i2c_status_type i2c_status; + + usb_enable(); // 要初始化USB口,printf调试大法好 + SpinDelay(1000); + + // 初始化I2C外设 + i2c_handle_type hi2cx; + hi2cx.i2cx = I2C1; + i2c_config(&hi2cx); + + uint8_t rx_buf[7]; // ID 寄存器 7 个字节 + + while (1) { + SpinDelay(1000); + + dxl_print_dbg("开始读取...\r\n"); + + // 手册上 0x47 ,实际上发送的时候,这个封装库没有处理位移,因此我们需要自行处理。 + // 需要将实际地址左移一位,也就是低八位的地址 + if ((i2c_status = i2c_master_receive(&hi2cx, 0x47 << 1, rx_buf, sizeof(rx_buf), I2C_TIMEOUT)) != I2C_OK) { + dxl_print_dbg("CC 控制器 ID 读取异常:%d\r\n", i2c_status); + continue; // 异常的话,直接跳过下面的代码,重新尝试执行 + } + + dxl_print_dbg("读取完成:"); + for (int i = 0; i < sizeof(rx_buf); ++i) dxl_print_dbg("%02x ", rx_buf[i]); + dxl_print_dbg("\r\n"); + } +} + +// extern uint32_t _stack_start[], _stack_end[]; + +void test_i2c_rgb_software(void) { + uint8_t buf[12] = {255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0,}; // 四个 R + uint8_t buf1[12] = {0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0,}; // 四个 G + uint8_t buf2[12] = {0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255,}; // 四个 B + uint8_t buf_off_1rgb[] = {0x00, 0x00, 0x00}; + uint8_t buf24[24] = { + 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0 + }; // 四个 R + 四个 G + uint8_t buf24_empty[24] = {0x00}; + (void) buf; + (void) buf1; + (void) buf2; + (void) buf_off_1rgb; + (void) buf24; + (void) buf24_empty; + + usb_enable(); // 要初始化USB口,printf调试大法好 + SpinDelay(1000); + + StartTicks(); + + while (1) { + while (is_btn_pressed()) { + } // 等待松开按钮 + + dxl_print_dbg("按下按钮开始测试软件I2C > \r\n"); + + I2C_init(true); + + // 等待按钮按下 + while (!is_btn_pressed()) { + } + + // 测试是否会干扰到 CC 和 RGB 灯 + // I2C_Reset_EnterMainProgram(); + + // 尝试读取数据 + // 0x47 << 1 + // 0x58 << 1 + // int16_t res = I2C_BufferRead(rx_buf, sizeof(rx_buf), 0x01, 0x58 << 1); + // dxl_print_dbg("读取结果: %d\r\n", res); + + uint8_t addr = 0x58; + (void) addr; + + uint8_t idx = 0; + (void) idx; + bool ret = true; + (void) ret; + +#if 1 + + uint8_t addrs[] = {0x48, 0x49, 0x68, 0x69,}; + for (int i = 0; i < sizeof(addrs); ++i) { + ret = I2C_BufferReadRaw(buf24_empty, 1, 0xFF, addrs[i] << 1); + dxl_print_dbg("固件版本寄存器读取结果: %d, v%d.%d\r\n", ret, buf24_empty[0], buf24_empty[1]); + if (ret) { + dxl_print_dbg("轮询地址为 %02x\r\n", addrs[i]); + addr = addrs[i]; + } + } + +#endif + + ret = I2C_BufferReadRaw(buf24_empty, 1, 0xFF, addr << 1); + dxl_print_dbg("固件版本寄存器读取结果: %d, v%d.%d\r\n", ret, buf24_empty[0], buf24_empty[1]); + + ret = I2C_BufferWrite(buf24_empty, 2, 0xFF, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + dxl_print_dbg("固件版本寄存器写入结果: %d\r\n", ret); + +#if 0 + + uint8_t addrs[4] = {0x48, 0x58, 0x68, 0x78}; + dxl_print_dbg("开始检测RGB-I2C地址接法\r\n"); + +#if 0 + + uint8_t idx_last_ok = 255; + while (1) { + ret = I2C_BufferReadRaw(buf24_empty, 1, 0xFF, addrs[idx] << 1); + if (ret) { + if (idx_last_ok == idx) { + continue; + } + idx_last_ok = idx; + dxl_print_dbg("接法变动,当前是:"); + if (0 == idx) { + dxl_print_dbg("VCC接法\r\n"); + } + if (1 == idx) { + dxl_print_dbg("GND接法\r\n"); + } + if (2 == idx) { + dxl_print_dbg("SCL接法\r\n"); + } + if (3 == idx) { + dxl_print_dbg("SDA接法\r\n"); + } + } + if (++idx == 4) { + idx = 0; + } + SpinDelay(200); + } + +#endif + + idx = 2; + while (1) { + ret = I2C_BufferReadRaw(buf24_empty, 1, 0xFF, addrs[idx] << 1); + if (!ret) { + dxl_print_dbg("通信失败\r\n"); + } + } + +#endif + +#if 0 + + ret = I2C_BufferReadRaw(buf24_empty, 1, 0x01, addr << 1); + dxl_print_dbg("数量寄存器读取结果: %d, 值 = %d\r\n", ret, buf24_empty[0]); + + ret = I2C_BufferReadRaw(buf24_empty, 1, 0x02, addr << 1); + dxl_print_dbg("索引寄存器读取结果: %d, 值 = %d\r\n", ret, buf24_empty[0]); + + // ---- 写入相关数据,测试后续的数据读取功能是否正常 + + ret = I2C_WriteByte(8, 0x01, addr << 1); // 写数量寄存器,设置硬件挂8个灯 + dxl_print_dbg("数量寄存器写入结果: %d\r\n", ret); + + ret = I2C_WriteByte(0, 0x02, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + dxl_print_dbg("索引寄存器写入结果: %d\r\n", ret); + + ret = I2C_BufferWrite(buf24, sizeof(buf24), 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + dxl_print_dbg("数据寄存器写入结果: %d\r\n", ret); + + // ---- 写入结束 + + ret = I2C_BufferReadRaw(buf24_empty, sizeof(buf24_empty), 0x03, addr << 1); + dxl_print_dbg("数据寄存器读取结果: %d\r\n", ret); + for (int i = 0; i < sizeof(buf24); ++i) dxl_print_dbg("%02x ", (uint8_t) buf24_empty[i]); + dxl_print_dbg("\r\n"); + + ret = I2C_BufferReadRaw(buf24_empty, 1, 0x04, addr << 1); + dxl_print_dbg("熄灯寄存器读取结果: %d, 值 = %d\r\n", ret, buf24_empty[0]); + + // ---- 测试写索引锁定寄存器然后再读取 + + ret = I2C_WriteByte(1, 0x05, addr << 1); // 写索引锁定寄存器,使能自增 + dxl_print_dbg("索引锁定寄存器(使能锁定)写入结果: %d\r\n", ret); + + ret = I2C_BufferReadRaw(buf24_empty, 1, 0x05, addr << 1); + dxl_print_dbg("索引锁定寄存器读取结果: %d, 值 = %d\r\n", ret, buf24_empty[0]); + + ret = I2C_WriteByte(0, 0x05, addr << 1); // 写索引锁定寄存器,使能自增 + dxl_print_dbg("索引锁定寄存器(关闭锁定)写入结果: %d\r\n", ret); + + ret = I2C_BufferReadRaw(buf24_empty, 1, 0x05, addr << 1); + dxl_print_dbg("索引锁定寄存器读取结果: %d, 值 = %d\r\n", ret, buf24_empty[0]); + + // ---- 测试结束 + + // ---- 测试写闪灯使能寄存器然后再读取 + + ret = I2C_WriteByte(1, 0x06, addr << 1); // 写闪灯使能寄存器,使能自增 + dxl_print_dbg("闪灯使能寄存器(使能闪灯)写入结果: %d\r\n", ret); + + ret = I2C_BufferReadRaw(buf24_empty, 1, 0x06, addr << 1); + dxl_print_dbg("闪灯使能寄存器读取结果: %d, 值 = %d\r\n", ret, buf24_empty[0]); + + ret = I2C_WriteByte(0, 0x06, addr << 1); // 写闪灯使能寄存器,使能自增 + dxl_print_dbg("闪灯使能寄存器(关闭闪灯)写入结果: %d\r\n", ret); + + ret = I2C_BufferReadRaw(buf24_empty, 1, 0x06, addr << 1); + dxl_print_dbg("闪灯使能寄存器读取结果: %d, 值 = %d\r\n", ret, buf24_empty[0]); + + // ---- 测试结束 + + ret = I2C_BufferReadRaw(buf24_empty, 2, 0x07, addr << 1); + dxl_print_dbg("闪灯使能寄存器读取结果: %d, 亮时长 = %d, 灭时长 = %d\r\n", ret, buf24_empty[0], buf24_empty[1]); + +#endif + +#if 0 + + ret = I2C_WriteByte(8, 0x01, addr << 1); // 写数量寄存器,设置硬件挂8个灯 + dxl_print_dbg("数量寄存器写入结果: %d\r\n", ret); + + // 熄灭所有的灯,重新开始跑新的一轮流水 + ret = I2C_WriteByte(0x00, 0x04, addr << 1); // 写熄灯寄存器,数据可传可不传,无所谓 + dxl_print_dbg("熄灯寄存器写入结果: %d\r\n", ret); + SpinDelay(10); + +#endif + +#if 0 + + ret = I2C_WriteByte(0, 0x02, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + dxl_print_dbg("索引寄存器写入结果: %d\r\n", ret); + + while (1) { + ret = I2C_BufferWrite(buf24, sizeof(buf24), 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + dxl_print_dbg("数据寄存器写入结果: %d\r\n", ret); + + SpinDelay(50); + + // 按一次修改一次第一个灯的颜色 + if (BUTTON_PRESS()) { + buf24[0] += 5; + } + } + +#endif + +#if 1 + + ret = I2C_WriteByte(idx, 0x02, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + dxl_print_dbg("索引寄存器写入结果: %d\r\n", ret); + + ret = I2C_WriteByte(0, 0x05, addr << 1); // 写索引锁定寄存器,使能自增 + dxl_print_dbg("索引锁定寄存器(关闭锁定)写入结果: %d\r\n", ret); + + ret = I2C_BufferWrite(buf, sizeof(buf), 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + dxl_print_dbg("数据寄存器写入结果: %d\r\n", ret); + + SpinDelay(1000); + + ret = I2C_WriteByte(idx, 0x02, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + dxl_print_dbg("索引寄存器写入结果: %d\r\n", ret); + + ret = I2C_WriteByte(1, 0x06, addr << 1); // 写闪灯使能寄存器,使能 idx 对应的灯珠的可控闪烁 + dxl_print_dbg("闪灯使能寄存器写入结果(开): %d\r\n", ret); + + uint8_t buf_flash_time[] = {50, 50}; // 1s on, 500ms off. + + ret = I2C_BufferWrite(buf_flash_time, sizeof(buf_flash_time), 0x07, addr << 1); // 写闪灯使能寄存器,使能 idx 对应的灯珠的可控闪烁 + dxl_print_dbg("闪灯时长寄存器写入结果(1): %d\r\n", ret); + + SpinDelay(1000); + SpinDelay(1000); + SpinDelay(1000); + SpinDelay(1000); + + buf_flash_time[0] = 100; + buf_flash_time[1] = 50; + + ret = I2C_BufferWrite(buf_flash_time, sizeof(buf_flash_time), 0x07, addr << 1); // 写闪灯使能寄存器,使能 idx 对应的灯珠的可控闪烁 + dxl_print_dbg("闪灯时长寄存器写入结果(2): %d\r\n", ret); + + SpinDelay(1000); + SpinDelay(1000); + SpinDelay(1000); + SpinDelay(1000); + + buf_flash_time[0] = 50; + buf_flash_time[1] = 100; + + ret = I2C_BufferWrite(buf_flash_time, sizeof(buf_flash_time), 0x07, addr << 1); // 写闪灯使能寄存器,使能 idx 对应的灯珠的可控闪烁 + dxl_print_dbg("闪灯时长寄存器写入结果(3): %d\r\n", ret); + + SpinDelay(1000); + SpinDelay(1000); + SpinDelay(1000); + SpinDelay(1000); + + // 以下逻辑是测试5个关闪条件的,1、主动关闪 2、写索引关闪 3、写数据关闪 4、写熄灯关闪 5、读数据关闪 + + //ret = I2C_WriteByte(0, 0x06, addr << 1); // 写闪灯使能寄存器,使能 idx 对应的灯珠的可控闪烁 + //dxl_print_dbg("闪灯使能寄存器写入结果(关): %d\r\n", ret); + + //ret = I2C_WriteByte(idx, 0x02, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + //dxl_print_dbg("索引寄存器写入结果: %d\r\n", ret); + + //ret = I2C_BufferWrite(buf, sizeof(buf), 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + //dxl_print_dbg("数据寄存器写入结果: %d\r\n", ret); + + //ret = I2C_WriteByte(0x00, 0x04, addr << 1); // 写熄灯寄存器,数据可传可不传,无所谓 + //dxl_print_dbg("熄灯寄存器写入结果: %d\r\n", ret); + + ret = I2C_BufferReadRaw(buf24_empty, sizeof(buf24_empty), 0x03, addr << 1); + dxl_print_dbg("数据寄存器读取结果: %d\r\n", ret); + for (int i = 0; i < sizeof(buf24); ++i) dxl_print_dbg("%02x ", (uint8_t) buf24_empty[i]); + dxl_print_dbg("\r\n"); + +#endif + + +#if 0 + + ret = I2C_WriteByte(idx, 0x02, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + dxl_print_dbg("索引寄存器写入结果: %d\r\n", ret); + + ret = I2C_WriteByte(0, 0x05, addr << 1); // 写索引锁定寄存器,使能自增 + dxl_print_dbg("索引锁定寄存器写入结果: %d\r\n", ret); + + uint8_t buf_8r[] = { + 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, + }; // 8个 R + + ret = I2C_BufferWrite(buf_8r, sizeof(buf_8r), 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + dxl_print_dbg("数据寄存器写入结果: %d\r\n", ret); + +#endif + + +#if 0 + + ret = I2C_WriteByte(idx, 0x00, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + dxl_print_dbg("索引寄存器写入结果: %d\r\n", ret); + + ret = I2C_WriteByte(1, 0x05, addr << 1); // 写索引锁定寄存器,让操作的RGB索引不会自增 + dxl_print_dbg("索引锁定寄存器写入结果: %d\r\n", ret); + + while (1) { + ret = I2C_BufferWrite(buf_off_1rgb, 3, 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + (void) ret; + //dxl_print_dbg("数据寄存器写入结果1: %d\r\n", ret); + + // GpioOutputInv(GPIOE, GPIO_PINS_8); // 反转调试脚 + + SpinDelay(150); + + // GpioOutputInv(GPIOE, GPIO_PINS_8); // 反转调试脚 + + // 只传3个字节,RGB888,表示只刷一个灯 + ret = I2C_BufferWrite(buf, 3, 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + (void) ret; + //dxl_print_dbg("数据寄存器写入结果2: %d\r\n", ret); + + // GpioOutputInv(GPIOE, GPIO_PINS_8); // 反转调试脚 + + SpinDelay(150); + + // GpioOutputInv(GPIOE, GPIO_PINS_8); // 反转调试脚 + } + +#endif + + +#if 0 + + ret = I2C_WriteByte(0, 0x05, addr << 1); // 写索引锁定寄存器,让操作的RGB索引不会自增 + dxl_print_dbg("索引锁定寄存器写入结果: %d\r\n", ret); + + while (1) { + ret = I2C_WriteByte(idx, 0x02, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + //dxl_print_dbg("索引寄存器写入结果: %d\r\n", ret); + + // 只传3个字节,RGB888,表示只刷一个灯 + ret = I2C_BufferWrite(buf, 3, 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + //dxl_print_dbg("数据寄存器写入结果: %d\r\n", ret); + + SpinDelay(100); + + ret = I2C_WriteByte(idx, 0x02, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + //dxl_print_dbg("索引寄存器写入结果: %d\r\n", ret); + + uint8_t buf_off[] = {0x00, 0x00, 0x00}; + ret = I2C_BufferWrite(buf_off, 3, 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + //dxl_print_dbg("数据寄存器写入结果: %d\r\n", ret); + + SpinDelay(100); + + if (++idx == 8) { + idx = 0; + } + } + +#endif + +#if 0 + + ret = I2C_WriteByte(0, 0x05, addr << 1); // 写索引锁定寄存器,使能自增 + dxl_print_dbg("索引锁定寄存器写入结果: %d\r\n", ret); + + while (1) { + ret = I2C_WriteByte(0, 0x02, addr << 1); // 写索引寄存器,设置后续操作的RGB索引 + // dxl_print_dbg("索引寄存器写入结果: %d\r\n", ret); + + uint8_t *pbuf = NULL; + if (idx == 0) { + pbuf = buf; + idx = 1; + } else if (idx == 1) { + pbuf = buf1; + idx = 2; + } else if (idx == 2) { + pbuf = buf2; + idx = 0; + } + + ret = I2C_BufferWrite(pbuf, sizeof(buf), 0x03, addr << 1); // 写数据寄存器,每三个字节就是对应的RGB888值 + // dxl_print_dbg("数据寄存器写入结果: %d\r\n", ret); + + SpinDelay(300); + } + +#endif + } +} + +void test_i2c_ant_software(void) { + usb_enable(); // 要初始化USB口,printf调试大法好 + SpinDelay(1000); + StartTicks(); + + while (1) { + while (is_btn_pressed()) { + } // 等待松开按钮 + + dxl_print_dbg("按下按钮开始测试软件I2C(多频复合天线) > \r\n"); + + I2C_init(true); + + // 等待按钮按下 + while (!is_btn_pressed()) { + } + + uint8_t addr = 0x51; + (void) addr; + uint8_t buf8_empty[8] = {0x00}; + (void) buf8_empty; + bool ret = true; + (void) ret; + + // 读版本号,通信如果没问题应当成功 + ret = I2C_BufferReadRaw(buf8_empty, 1, 0xFF, addr << 1); + dxl_print_dbg("固件版本寄存器读取结果: %d, v%d.%d\r\n", ret, buf8_empty[0], buf8_empty[1]); + + // 写版本号寄存器,肯定要失败才对的 + ret = I2C_BufferWrite(buf8_empty, 2, 0xFF, addr << 1); + dxl_print_dbg("固件版本寄存器写入结果: %d\r\n", ret); + + // 设备标志寄存器,标志当前是pm5的多频复合天线,理论上要读取到:0x70 0x6D 0x35 0x5F 0x61 0x6E 0x74 0x78 + ret = I2C_BufferReadRaw(buf8_empty, 8, 0xFE, addr << 1); + dxl_print_dbg("设备标志寄存器读取结果(%d): %02x %02x %02x %02x %02x %02x %02x %02x\r\n", ret, + buf8_empty[0], buf8_empty[1], buf8_empty[2], buf8_empty[3], + buf8_empty[4], buf8_empty[5], buf8_empty[6], buf8_empty[7]); + + // 写设备标志寄存器,肯定要失败才对的 + ret = I2C_BufferWrite(buf8_empty, 8, 0xFE, addr << 1); + dxl_print_dbg("设备标志寄存器写入结果: %d\r\n", ret); + + // 读取IO数据寄存器,8个bit控制8个IO + ret = I2C_BufferReadRaw(buf8_empty, 1, 0x01, addr << 1); + dxl_print_dbg("IO数据寄存器读取结果: %d\r\n", ret); + + // 等待按钮重新按下 + while (is_btn_pressed()) { + } + dxl_print_dbg("按下按钮开始测试IO写入 > \r\n"); + while (!is_btn_pressed()) { + } + int io_idx = 0; + while (1) { +#if 0 // 测试直接写IO寄存器 + buf8_empty[0] = 0; // 先清除所有之前的设置 + buf8_empty[0] |= 1 << io_idx++; // 然后设置当前的idx位置的io为1,然后顺带自增一下idx + ret = I2C_BufferWrite(buf8_empty, 1, 0x01, addr << 1); + dxl_print_dbg("IO数据寄存器写入结果: value = %d, ret = %d\r\n", buf8_empty[0], ret); +#else // 测试写映射寄存器 + buf8_empty[0] = 0x00; // 如果五个高位都不为1,则默认应该是125k + // 125 134 250 375 500 HFLED LFLED Q + // buf8_empty[0] = 1 << 7; // 配置为125+低q+关灯 + // buf8_empty[0] = 1 << 6; // 配置为134+低q+关灯 + // buf8_empty[0] = 1 << 5; // 配置为250+低q+关灯 + // buf8_empty[0] = 1 << 4; // 配置为375+低q+关灯 + // buf8_empty[0] = 1 << 3; // 配置为500+低q+关灯 + // buf8_empty[0] |= 1 << 2; // HFLED + // buf8_empty[0] |= 1 << 1; // LFLED + // buf8_empty[0] |= 0x01; // 高q + ret = I2C_BufferWrite(buf8_empty, 1, 0x02, addr << 1); + dxl_print_dbg("IO映射寄存器写入结果: value = %d, ret = %d\r\n", buf8_empty[0], ret); +#endif + + // 读取IO数据寄存器,8个bit控制8个IO + ret = I2C_BufferReadRaw(buf8_empty, 1, 0x01, addr << 1); + dxl_print_dbg("IO数据寄存器读取结果: value = %d, ret = %d\r\n", buf8_empty[0], ret); + // 延迟一下,流水灯测试 + SpinDelay(50); + // 重置idx + if (io_idx == sizeof(buf8_empty)) { + io_idx = 0; + } + } + } +} + +void test_init_debug_pin(void) { + gpio_init_type gpio_init_struct; + crm_periph_clock_enable(CRM_GPIOE_PERIPH_CLOCK, TRUE); + gpio_default_para_init(&gpio_init_struct); + gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init_struct.gpio_pins = GPIO_PINS_8; // EXP_IO_IO1 + gpio_init(GPIOE, &gpio_init_struct); +} + +void test_usb_xlink_spi(void) { + // 当前设备上运行的测试模式,两台机器,一主一从 + uint16_t spi1_mode = SPI_MODE_SLAVE; + uint8_t spi1_buffer[20] = {0x01, 0x02, 0x03, 0x00}; + + // 初始化身份切换口 + gpio_inter_usb_spi_role_setup(); + + // 主从身份不一样,需要做某些特定的参数配置 + if (spi1_mode == SPI_MODE_MASTER) { + usb_update_serial(110000001); // 主从设备的USB序列号不同,解决上线慢的问题 + Gpio_Inter_USB_SPI_Role_High(); // spi口需要切换为特定的gpio组,否则使typec协议的txrx再次交叉回来 + } else { + usb_update_serial(110000002); + Gpio_Inter_USB_SPI_Role_Low(); + } + + usb_enable(); // 要初始化USB口,printf调试大法好 + SpinDelay(1000); + + gpio_init_type gpio_initstructure; + crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); + + /* spi1 cs pin */ + gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_initstructure.gpio_pull = GPIO_PULL_UP; + gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + if (spi1_mode == SPI_MODE_MASTER) { + gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT; + } else { + gpio_initstructure.gpio_mode = GPIO_MODE_MUX; + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE4, GPIO_MUX_5); + } + gpio_initstructure.gpio_pins = GPIO_PINS_4; + gpio_init(GPIOA, &gpio_initstructure); + + /* spi1 sck pin */ + gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_initstructure.gpio_pull = GPIO_PULL_DOWN; + gpio_initstructure.gpio_mode = GPIO_MODE_MUX; + gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_initstructure.gpio_pins = GPIO_PINS_5; + gpio_init(GPIOA, &gpio_initstructure); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_5); + + /* spi1 miso pin */ + gpio_initstructure.gpio_pull = GPIO_PULL_UP; + gpio_initstructure.gpio_pins = GPIO_PINS_6; + gpio_init(GPIOA, &gpio_initstructure); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE6, GPIO_MUX_5); + + /* spi1 mosi pin */ + gpio_initstructure.gpio_pull = GPIO_PULL_UP; + gpio_initstructure.gpio_pins = GPIO_PINS_7; + gpio_init(GPIOA, &gpio_initstructure); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_5); + + /* non communication time: master pull up CS pin release slave */ + if (spi1_mode == SPI_MODE_MASTER) { + gpio_bits_set(GPIOA, GPIO_PINS_4); + } + + // ------------- spi 初始化 + + spi_init_type spi_init_struct; + + /* master spi initialization */ + crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE); + spi_default_para_init(&spi_init_struct); + + /* dual line unidirectional full-duplex mode */ + spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX; + spi_init_struct.master_slave_mode = spi1_mode; + spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_1024; + spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_LSB; + spi_init_struct.frame_bit_num = SPI_FRAME_8BIT; + spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW; + spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE; + if (spi1_mode == SPI_MODE_MASTER) { + spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE; + } else { + spi_init_struct.cs_mode_selection = SPI_CS_HARDWARE_MODE; + } + spi_init(SPI1, &spi_init_struct); + spi_enable(SPI1, TRUE); + + // while (1) { + // gpio_bits_reset(GPIOA, GPIO_PINS_4); + // SpinDelay(100); + // gpio_bits_set(GPIOA, GPIO_PINS_4); + // SpinDelay(100); + // } + + // ------------- 只测试主机发送,从机接收 + uint8_t idx = 0; + while (1) { + if (spi1_mode == SPI_MODE_MASTER) { + dxl_print_dbg("USB互联口,开始发送\r\n"); + // 主机拉低,片选从机 + gpio_bits_reset(GPIOA, GPIO_PINS_4); + while (idx < sizeof(spi1_buffer)) { + while (spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET); + spi_i2s_data_transmit(SPI1, spi1_buffer[idx]); + idx++; + } + dxl_print_dbg("USB互联口,SPI主机发送完成\r\n"); + idx = 0; + /* wait master and slave idle when communication end */ + while (spi_i2s_flag_get(SPI1, SPI_I2S_BF_FLAG) != RESET); + // 主机拉高,释放从机 + gpio_bits_set(GPIOA, GPIO_PINS_4); + SpinDelay(500); + } else { + dxl_print_dbg("USB互联口,开始接收\r\n"); + while (idx < sizeof(spi1_buffer)) { + while (spi_i2s_flag_get(SPI1, SPI_I2S_RDBF_FLAG) == RESET); + spi1_buffer[idx] = spi_i2s_data_receive(SPI1); + idx++; + } + dxl_print_dbg("USB互联口,SPI从机接收完成: "); + for (idx = 0; idx < sizeof(spi1_buffer); idx++) dxl_print_dbg("%02x ", spi1_buffer[idx]); + dxl_print_dbg("\r\n"); + idx = 0; + /* wait master and slave idle when communication end */ + while (spi_i2s_flag_get(SPI1, SPI_I2S_BF_FLAG) != RESET); + } + } +} + +void test_usb_xlink_1line_uart(void) { + // 标记当前身份为主机 + const bool masterIam = 0; + + gpio_init_type gpio_init_struct; + + /* enable the usart1 and gpio clock */ + crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE); + crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); + + gpio_default_para_init(&gpio_init_struct); + + /* configure the usart1 tx pin */ + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN; + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_pins = GPIO_PINS_9; + gpio_init_struct.gpio_pull = GPIO_PULL_UP; + gpio_init(GPIOA, &gpio_init_struct); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE9, GPIO_MUX_7); + + /* configure usart1 param */ + usart_init(USART1, 57600, USART_DATA_8BITS, USART_STOP_1_BIT); + usart_transmitter_enable(USART1, TRUE); + usart_receiver_enable(USART1, TRUE); + usart_single_line_halfduplex_select(USART1, TRUE); + usart_enable(USART1, TRUE); + + // 主从设备的USB序列号不同,解决上线慢的问题 + if (masterIam) { + usb_update_serial(110000001); + } else { + usb_update_serial(110000002); + } + + usb_enable(); // 要初始化USB口,printf调试大法好 + SpinDelay(1000); + + dxl_print_dbg("单线测试模式启动\r\n"); + + uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x03, 0x02, 0x01,}; + + while (1) { + // 只测试主机发送从机接收 + if (masterIam) { + for (int i = 0; i < sizeof(data); ++i) { + while (usart_flag_get(USART1, USART_TDBE_FLAG) == RESET); + usart_data_transmit(USART1, data[i]); + } + // 发完了就打印一下,然后等一会儿再继续发 + dxl_print_dbg("USB互联口,单线串口发送完毕,稍后继续发送\r\n"); + SpinDelay(500); + } else { + for (int i = 0; i < sizeof(data); ++i) { + while (usart_flag_get(USART1, USART_RDBF_FLAG) == RESET); + data[i] = usart_data_receive(USART1); + } + dxl_print_dbg("USB互联口,单线串口接收完成: "); + for (int i = 0; i < sizeof(data); ++i) dxl_print_dbg("%02x ", data[i]); + dxl_print_dbg("\r\n"); + } + } +} + +void test_isp_exit(void) { + usb_enable(); + SpinDelay(1000); // 等一会儿,USB上线以后,PC端重连完成了再继续后面的步骤,避免错过打印的消息。 + + dxl_print_dbg("Send 'exit' cmd for isp mode exit >\r\n"); + + // 等待发送退出指令 + uint8_t buf[100] = {0x00}; + uint32_t length = 0; + while (1) { + length += usb_read(buf + length, 4); + if (length >= 4 && memcmp(buf, "exit", 4) == 0) { + length = 0; + break; + } + } + + dxl_print_dbg("Received 'exit' cmd, start exit isp...\r\n"); + + // 模拟按钮按下退出isp + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + crm_periph_clock_enable(AT32_GPIO_BTN_CLK, TRUE); + gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; + gpio_init_struct.gpio_pins = AT32_GPIO_BTN_PIN; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + AT32_GPIO_BTN->scr = AT32_GPIO_BTN_PIN; // 按钮按下超过强制退出isp的指定时长,则会强制退出isp模式 + gpio_init(AT32_GPIO_BTN, &gpio_init_struct); + for (int i = 0; i < 11; i++) { + // 新版本是3s,老版本是10s,我们先测老版本的 + SpinDelay(1000); + } + AT32_GPIO_BTN->clr = AT32_GPIO_BTN_PIN; + SpinDelay(1000); + + dxl_print_dbg("ISP mode exited.\r\n"); +} + +void test_beep(void) { + gpio_init_type gpio_init_struct; + + // PB13 使能,PC9 调制,使用 TMR8_CH4 输出调制 + crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE); + crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE); + crm_periph_clock_enable(CRM_TMR8_PERIPH_CLOCK, TRUE); + +#define BEEPER_EN_GPIO GPIOB +#define BEEPER_EN_GPIO_PIN GPIO_PINS_13 +#define BEEPER_MOD_GPIO GPIOC +#define BEEPER_MOD_GPIO_PIN GPIO_PINS_9 +#define BEEPER_MOD_GPIO_SRC GPIO_PINS_SOURCE9 +#define BEEPER_MOD_GPIO_MUX GPIO_MUX_3 +#define BEEPER_MOD_TMR TMR8 +#define BEEPER_MOD_TMR_CH TMR_SELECT_CHANNEL_4 + + gpio_default_para_init(&gpio_init_struct); + // 蜂鸣器使能脚 + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; + gpio_init_struct.gpio_pins = BEEPER_EN_GPIO_PIN; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init(BEEPER_EN_GPIO, &gpio_init_struct); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, FALSE); + // 蜂鸣器调制脚 + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_pins = BEEPER_MOD_GPIO_PIN; + gpio_init(BEEPER_MOD_GPIO, &gpio_init_struct); + gpio_pin_mux_config(BEEPER_MOD_GPIO, BEEPER_MOD_GPIO_SRC, BEEPER_MOD_GPIO_MUX); + + tmr_internal_clock_set(BEEPER_MOD_TMR); + tmr_reset(BEEPER_MOD_TMR); + tmr_base_init(BEEPER_MOD_TMR, 999, 95); // 192M出2k + tmr_output_config_type tmr_output_struct; + tmr_output_default_para_init(&tmr_output_struct); + tmr_output_struct.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A; + tmr_output_struct.oc_polarity = TMR_OUTPUT_ACTIVE_HIGH; + tmr_output_struct.oc_output_state = TRUE; + tmr_output_channel_config(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, &tmr_output_struct); + tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 500); // 比较值=500 (50%占空比) + tmr_counter_enable(BEEPER_MOD_TMR, TRUE); + tmr_output_enable(BEEPER_MOD_TMR, TRUE); + + while (1) { + BEEPER_MOD_TMR->pr = 999; + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, TRUE); + SpinDelay(20); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, FALSE); + SpinDelay(400); + BEEPER_MOD_TMR->pr = 1100; + tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 550); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, TRUE); + SpinDelay(20); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, FALSE); + SpinDelay(400); + BEEPER_MOD_TMR->pr = 1200; + tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 600); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, TRUE); + SpinDelay(20); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, FALSE); + SpinDelay(400); + BEEPER_MOD_TMR->pr = 1300; + tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 650); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, TRUE); + SpinDelay(20); + gpio_bits_write(BEEPER_EN_GPIO,BEEPER_EN_GPIO_PIN, FALSE); + SpinDelay(400); + + // 响完一轮之后直接软件reset + // ResetChip(); + } +} + +void test_power_of_by_btn(void) { + usb_enable(); + SpinDelay(1000); + dxl_print_dbg("SystemStart\n"); + while (1) { + if (is_btn_pressed()) { + // 拉低直接关机 + dxl_print_dbg("SystemOff\n"); + Gpio_ARM_Power_ON_Low(); + // 拉低关机的话,还会有一段PWR电容放电时间,此时我们应当让系统进入死循环,不再处理任何事情 + while (1) { + dxl_print_dbg("Waiting Power Off\n"); + } + } + } +} + +void test_bat_coulometer(void) { + usb_enable(); // 要初始化USB口,printf调试大法好 + SpinDelay(1000); + StartTicks(); + + while (1) { + while (is_btn_pressed()) { + } // 等待松开按钮 + + dxl_print_dbg("按下按钮开始测试库仑计 > \r\n"); + + I2C_init(true); + + // 等待按钮按下 + while (!is_btn_pressed()) { + } + + // 用 I2C_BufferReadRaw 读取 + // 用 I2C_BufferWrite 写入 + + uint8_t addr = 0xAA; // 最高位为 1010101(地址位) [0|1](读写位),所以我们给出的整字节地址就是 0xAA 即可 + uint8_t data[20]; + + while (1) { + // Voltage(): 0x04 and 0x05 + bool ret = I2C_BufferReadRaw(data, 2, 0x04, addr); + dxl_print_dbg("Read result: %d, voltage = %dmv\n", ret, *(uint16_t *) data); + SpinDelay(500); + } + } +} + +void test_bat_charger(void) { + usb_enable(); // 要初始化USB口,printf调试大法好 + SpinDelay(1000); + StartTicks(); + + while (1) { + while (is_btn_pressed()) { + } // 等待松开按钮 + + dxl_print_dbg("按下按钮开始测试充电器 > \r\n"); + + I2C_init(true); + + // 等待按钮按下 + while (!is_btn_pressed()) { + } + + // 用 I2C_BufferReadRaw 读取 + // 用 I2C_BufferWrite 写入 + + uint8_t addr = 0x93; // 最高位为 1001001(地址位) [0|1](读写位),所以我们给出的整字节地址就是 0x92 即可 + uint8_t data[20]; + + // 0x00 是输入控制,电压和电流 + bool ret = I2C_BufferReadRaw(data, 1, 0x00, addr); + dxl_print_dbg("Read result: %d, InputSourceCtrl: 0x%02x\n", ret, data[0]); + + // 0x09 故障寄存器 + ret = I2C_BufferReadRaw(data, 1, 0x09, addr); + dxl_print_dbg("Read result: %d, Fault: 0x%02x\n", ret, data[0]); + +#if 0 // 测试运输模式,会导致vmi关闭输出 + // 0x06 MainControl 默认0xC0 B5 是 FET_DIS + ret = I2C_BufferReadRaw(data, 1, 0x06, addr); + dxl_print_dbg("Read result: %d, MainControl: 0x%02x\n", ret, data[0]); + // 判断是否处于运输模式,如果是,则不做啥,如果不是,则进入运输模式 + if ((data[0] >> 5) & 0x01) { + dxl_print_dbg("Is shipping mode\n"); + } else { + dxl_print_dbg("Not shipping mode, enter now.\n"); + data[0] |= (0x01 << 5); // 使能运输模式(FET_DIS = 1) + ret = I2C_BufferWrite(data, 1, 0x06, addr); + dxl_print_dbg("Write result: %d, MainControl: 0x%02x\n", ret, data[0]); + } +#endif + +#if 1 + // 0x09 PowerOnConfig 寄存器 + ret = I2C_BufferReadRaw(data, 1, 0x01, addr); + dxl_print_dbg("Read result: %d, PowerOnCfg: 0x%02x\n", ret, data[0]); + // 第三位是充电控制位,为0时使能充电 + if (!(data[0] >> 3 & 0x01)) { + dxl_print_dbg("Is charge mode\n"); + } else { + dxl_print_dbg("Not charge mode, enter now.\n"); + data[0] &= ~(0x01 << 3); + ret = I2C_BufferWrite(data, 1, 0x01, addr); + dxl_print_dbg("Write result: %d, MainControl: 0x%02x\n", ret, data[0]); + } + // 0x02 ChargeCurrentControl 寄存器 + ret = I2C_BufferReadRaw(data, 1, 0x02, addr); + dxl_print_dbg("Read result: %d, ChargeCurrentControl: 0x%02x\n", ret, data[0]); + // 写充电电流控制寄存器为 0x1F 将设置充电电流为 256ma + data[0] = 0x1F; + ret = I2C_BufferWrite(data, 1, 0x02, addr); + dxl_print_dbg("Write result: %d, ChargeCurrentControl: 0x%02x\n", ret, data[0]); +#endif + } +} + +void test_bat_charger_only_settings(void) { + StartTicks(); + I2C_init(true); + + uint8_t addr = 0x93; // 最高位为 1001001(地址位) [0|1](读写位),所以我们给出的整字节地址就是 0x92 即可 + uint8_t data[20]; + + // 0x09 PowerOnConfig 寄存器 + I2C_BufferReadRaw(data, 1, 0x01, addr); + // 第三位是充电控制位,为0时使能充电 + if (!(data[0] >> 3 & 0x01)) { + // 已经是充电模式 + } else { + // 不是充电模式,现在进入充电模式 + data[0] &= ~(0x01 << 3); + I2C_BufferWrite(data, 1, 0x01, addr); + } + + // 0x02 ChargeCurrentControl 寄存器 + data[0] = 0x1F; // 写充电电流控制寄存器为 0x1F 将设置充电电流为 256ma + I2C_BufferWrite(data, 1, 0x02, addr); + + // 0x03 DischargeCurrentControl 寄存器 + data[0] = 0xE1; // 修改放电电流到 3A + I2C_BufferWrite(data, 1, 0x03, addr); + + // 0x05 ChargerTermination/TimerControl 寄存器 + data[0] = 0x1A; // 禁用定时器,正常为了安全可能需要在MainLoop喂狗 + I2C_BufferWrite(data, 1, 0x05, addr); + + // 0x0B IndividualChargeRegister 寄存器 + data[0] = 0x6B; // 将预充电电流调整到11ma + I2C_BufferWrite(data, 1, 0x0B, addr); +} + +void EXINT3_IRQHandler(void) { + if (exint_interrupt_flag_get(EXINT_LINE_3) != RESET) { + GpioOutputInv(GPIOA, GPIO_PINS_2); // 翻转pa2,传递库仑计+充电器的中断信号 + exint_flag_clear(EXINT_LINE_3); + } +} + +void test_coulometer_charger_int(void) { + usb_enable(); // 要初始化USB口,printf调试大法好 + SpinDelay(1000); + StartTicks(); + + // 我们测试的时候,把 PWR_INT 接到扩展口的 RX 上了,需要配置该口为上拉输入 + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); + gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; + gpio_init_struct.gpio_pull = GPIO_PULL_UP; + gpio_init_struct.gpio_pins = GPIO_PINS_3; + gpio_init(GPIOA, &gpio_init_struct); // PA3_RX + + gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init_struct.gpio_pins = GPIO_PINS_2; + gpio_init(GPIOA, &gpio_init_struct); // PA2_TX + + // 配置复用为外部中断源 + exint_init_type exint_init_struct; + crm_periph_clock_enable(CRM_SCFG_PERIPH_CLOCK, TRUE); + scfg_exint_line_config(SCFG_PORT_SOURCE_GPIOA, SCFG_PINS_SOURCE3); + exint_default_para_init(&exint_init_struct); + exint_init_struct.line_enable = TRUE; + exint_init_struct.line_mode = EXINT_LINE_INTERRUPT; + exint_init_struct.line_select = EXINT_LINE_3; + exint_init_struct.line_polarity = EXINT_TRIGGER_FALLING_EDGE; // PWR_INT 默认高电平,触发中断时产生下降沿 + exint_init(&exint_init_struct); + nvic_priority_group_config(NVIC_PRIORITY_GROUP_4); + nvic_irq_enable(EXINT3_IRQn, 1, 0); + + + while (1) { + while (is_btn_pressed()) { + } // 等待松开按钮 + + dxl_print_dbg("按下按钮开始测试充电器&库仑计中断事件 > \r\n"); + + I2C_init(true); + + // 等待按钮按下 + while (!is_btn_pressed()) { + } + + // 用 I2C_BufferReadRaw 读取 + // 用 I2C_BufferWrite 写入 + + bool ret; + uint8_t data[20]; + uint8_t charger_addr = 0x93; // 最高位为 1001001(地址位) [0|1](读写位),所以我们给出的整字节地址就是 0x92 即可 + uint8_t coulometer_addr = 0xAA; // 最高位为 1010101(地址位) [0|1](读写位),所以我们给出的整字节地址就是 0xAA 即可 + + // 我们需要打开充电 + ret = I2C_BufferReadRaw(data, 1, 0x01, charger_addr); + dxl_print_dbg("Read result: %d, PowerOnCfg: 0x%02x\n", ret, data[0]); + // 第三位是充电控制位,为0时使能充电 + if (!(data[0] >> 3 & 0x01)) { + dxl_print_dbg("Is charge mode\n"); + } else { + dxl_print_dbg("Not charge mode, enter now.\n"); + data[0] &= ~(0x01 << 3); + ret = I2C_BufferWrite(data, 1, 0x01, charger_addr); + dxl_print_dbg("Write result: %d, MainControl: 0x%02x\n", ret, data[0]); + } + + // 读取事件 + uint8_t int_status = GpioInputStatus(GPIOA, GPIO_PINS_3); + dxl_print_dbg("start listen event for PWR_INT, status on init: %d\n", int_status); + while (1) { +#if 1 // 测试主动触发库仑计触发中断 + data[0] = 0x00; + data[0] = 0x23; + ret = I2C_BufferWrite(data, 2, 0x00, coulometer_addr); + // dxl_print_dbg("PULSE_SOC_INT trigger: %d, status: %d\n", ret, GpioInputStatus(GPIOA, GPIO_PINS_3)); + SpinDelay(100); +#endif +#if 0 // 测试读取中断脚 + uint8_t int_new_status = GpioInputStatus(GPIOA, GPIO_PINS_3); + if (int_status != int_new_status) { + int_status = int_new_status; + dxl_print_dbg("PWR_INT trigger\n"); + } +#endif + } + } +} + +void test_4leds(void) { + while (1) { + // A灯闪烁 + LED_A_ON(); + SpinDelay(200); + LED_A_OFF(); + SpinDelay(200); + // B灯闪烁 + LED_B_ON(); + SpinDelay(200); + LED_B_OFF(); + SpinDelay(200); + // C灯闪烁 + LED_C_ON(); + SpinDelay(200); + LED_C_OFF(); + SpinDelay(200); + // D灯闪烁 + LED_D_ON(); + SpinDelay(200); + LED_D_OFF(); + SpinDelay(200); + } +} + +void EXINT9_5_IRQHandler(void) { + if (exint_interrupt_flag_get(EXINT_LINE_8) != RESET) { + GpioOutputInv(GPIOA, GPIO_PINS_2); // 翻转pa2,传递中断信号 + exint_flag_clear(EXINT_LINE_8); + } +} + +#include "dbprint.h" + +bool cep_spi_data_available(void) { + uint8_t len_header[2] = {0x00}; + for (size_t i = 0; i < sizeof(len_header); ++i) { + uint64_t timeout = 0; + while (spi_i2s_flag_get(SPI1, SPI_I2S_RDBF_FLAG) == RESET) { + if (timeout++ > 100000) { + return 0; // 超时了,识别长度失败 + } + } + len_header[i] = spi_i2s_data_receive(SPI1); + } + uint16_t data_len = (len_header[1] << 8) | len_header[0]; + if (data_len > PM3_CMD_DATA_SIZE * 2) { + return false; // 无效的数据长度,大于两倍payload大小这怎么可能 + } + return true; +} + +// 临时实现cep端口的spi读写函数,后续可以根据实际需求完善成通用的spi读写函数,目前先这样测试互联功能 +uint32_t cep_spi_read_ng(uint8_t *data, size_t len) { + for (size_t i = 0; i < len; ++i) { + uint64_t timeout = 0; + while (spi_i2s_flag_get(SPI1, SPI_I2S_RDBF_FLAG) == RESET) { + if (timeout++ > 100000) { + return i; // 超时了,返回已经读取的长度 + } + } + data[i] = spi_i2s_data_receive(SPI1); + } + return len; +} + +int cep_spi_write_sync(uint8_t *data, size_t len) { + // 头部两个字节是数据长度,这是我们约定好的SPI通信规范,SPI的从机应答的数据的头部两个字节一定要是数据长度 + while (spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET); + spi_i2s_data_transmit(SPI1, len & 0xFF); + + while (spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET); + spi_i2s_data_transmit(SPI1, (len >> 8) & 0xFF); + + // 循环发送数据 + for (size_t i = 0; i < len; ++i) { + while (spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET); + spi_i2s_data_transmit(SPI1, data[i]); + } + + // 让SPI电平归位为0,这是我们约定的每次通信结束后的电平状态,SPI的从机可以通过检测这个电平来判断通信是否结束 + while (spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET); + spi_i2s_data_transmit(SPI1, 0x00); + while (spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET); + spi_i2s_data_transmit(SPI1, 0x00); + + // 等待传输结束 + while (spi_i2s_flag_get(SPI1, SPI_I2S_BF_FLAG) == SET); + + return PM3_SUCCESS; +} + +void test_f0_com_by_usb_cep(void) { +#define TEST_F0_PRINT 0 +#define TEST_F0_HANDSHAKE 0 +#define TEST_F0_CONNECTION 1 + + // 要初始化USB口,printf调试大法好 +#if TEST_F0_PRINT + usb_enable(); + SpinDelay(1000); + dxl_print_dbg("The communication test for FlipperZero & Proxmark5 started.\r\n"); +#endif + + LED_D_ON(); + + // 配置UART + crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE); + crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN; + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_pins = GPIO_PINS_9; + gpio_init_struct.gpio_pull = GPIO_PULL_UP; + gpio_init(GPIOA, &gpio_init_struct); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE9, GPIO_MUX_7); + + usart_init(USART1, 2400, USART_DATA_8BITS, USART_STOP_1_BIT); + usart_parity_selection_config(USART1, USART_PARITY_NONE); // 8 n 1 + usart_transmitter_enable(USART1, FALSE); // 直接接收,不发送 + usart_receiver_enable(USART1, TRUE); + usart_single_line_halfduplex_select(USART1, TRUE); + usart_enable(USART1, TRUE); + + + // 配置ID脚,识别主从 + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; + gpio_init_struct.gpio_pins = GPIO_PINS_10; // PA10_USB1_ID + gpio_init(GPIOA, &gpio_init_struct); + + + // PA2_TX 拿来调试中断切换 + gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init_struct.gpio_pins = GPIO_PINS_2; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_init(GPIOA, &gpio_init_struct); // PA2_TX + + + // 配置INT脚,专门就是CC控制器用的 + crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE); + gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; + gpio_init_struct.gpio_pull = GPIO_PULL_UP; + gpio_init_struct.gpio_pins = GPIO_PINS_8; + gpio_init(GPIOC, &gpio_init_struct); // PC8_I2C_INT + // 配置复用为外部中断源 + exint_init_type exint_init_struct; + crm_periph_clock_enable(CRM_SCFG_PERIPH_CLOCK, TRUE); + scfg_exint_line_config(SCFG_PORT_SOURCE_GPIOC, SCFG_PINS_SOURCE8); + exint_default_para_init(&exint_init_struct); + exint_init_struct.line_enable = TRUE; + exint_init_struct.line_mode = EXINT_LINE_INTERRUPT; + exint_init_struct.line_select = EXINT_LINE_8; + exint_init_struct.line_polarity = EXINT_TRIGGER_BOTH_EDGE; + exint_init(&exint_init_struct); + nvic_priority_group_config(NVIC_PRIORITY_GROUP_4); + nvic_irq_enable(EXINT9_5_IRQn, 1, 0); + + + uint8_t data[20]; + uint8_t rx_len = 0; + uint8_t is_cep_connected = false; + uint8_t cc_ctrl_data; + + (void) data; + (void) rx_len; + (void) is_cep_connected; + +#if TEST_F0_HANDSHAKE + + // 主从切换 + bool is_slave_mode = GpioInputStatus(GPIOA, GPIO_PINS_10); + gpio_inter_usb_spi_role_setup(); + if (is_slave_mode) { + Gpio_Inter_USB_SPI_Role_High(); + } else { + Gpio_Inter_USB_SPI_Role_Low(); + } + +#if TEST_F0_PRINT + dxl_print_dbg("Current device mode: %s\n", is_slave_mode ? "Slave" : "Master"); +#endif + + // SPI相关配置 + spi_master_slave_mode_type spi1_mode = is_slave_mode ? SPI_MODE_SLAVE : SPI_MODE_MASTER; + gpio_init_type gpio_initstructure; + crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); + /* spi1 cs pin */ + gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_initstructure.gpio_pull = GPIO_PULL_UP; + gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + if (spi1_mode == SPI_MODE_MASTER) { + gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT; + } else { + gpio_initstructure.gpio_mode = GPIO_MODE_MUX; + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE4, GPIO_MUX_5); + } + gpio_initstructure.gpio_pins = GPIO_PINS_4; + gpio_init(GPIOA, &gpio_initstructure); + /* spi1 sck pin */ + gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_initstructure.gpio_pull = GPIO_PULL_DOWN; + gpio_initstructure.gpio_mode = GPIO_MODE_MUX; + gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_initstructure.gpio_pins = GPIO_PINS_5; + gpio_init(GPIOA, &gpio_initstructure); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_5); + /* spi1 miso pin */ + gpio_initstructure.gpio_pull = GPIO_PULL_UP; + gpio_initstructure.gpio_pins = GPIO_PINS_6; + gpio_init(GPIOA, &gpio_initstructure); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE6, GPIO_MUX_5); + /* spi1 mosi pin */ + gpio_initstructure.gpio_pull = GPIO_PULL_UP; + gpio_initstructure.gpio_pins = GPIO_PINS_7; + gpio_init(GPIOA, &gpio_initstructure); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_5); + /* non communication time: master pull up CS pin release slave */ + if (spi1_mode == SPI_MODE_MASTER) { + gpio_bits_set(GPIOA, GPIO_PINS_4); + } + spi_init_type spi_init_struct; + /* master spi initialization */ + crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE); + spi_default_para_init(&spi_init_struct); + /* dual line unidirectional full-duplex mode */ + spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX; + spi_init_struct.master_slave_mode = spi1_mode; + spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_1024; + spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB; + spi_init_struct.frame_bit_num = SPI_FRAME_8BIT; + spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW; + spi_init_struct.clock_phase = SPI_CLOCK_PHASE_1EDGE; + if (spi1_mode == SPI_MODE_MASTER) { + spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE; + } else { + spi_init_struct.cs_mode_selection = SPI_CS_HARDWARE_MODE; + } + spi_init(SPI1, &spi_init_struct); + spi_enable(SPI1, TRUE); + +#endif + + + // 初始化I2C,等下需要拿来做状态切换 + StartTicks(); + I2C_init(true); + uint8_t cc_controller_addr = 0x47; + + + while (1) { +#if TEST_F0_HANDSHAKE + + bool is_f0_msg = false; + while (1) { + if (usart_flag_get(USART1, USART_FERR_FLAG) != RESET) { + dxl_print_dbg("RX frame error\n"); + GpioOutputInv(GPIOA, GPIO_PINS_2); // 翻转pa2,传递中断信号 + } + if (usart_flag_get(USART1, USART_NERR_FLAG) != RESET) { + dxl_print_dbg("RX noise error\n"); + GpioOutputInv(GPIOA, GPIO_PINS_2); // 翻转pa2,传递中断信号 + } + + // 一直尝试接收来自于互联口的UART的数据 + if (usart_flag_get(USART1, USART_RDBF_FLAG) != RESET) { + uint8_t rx_data = usart_data_receive(USART1); + if (rx_data == 0x02) { + rx_len = 0; // STX received, restart rx frame. + rx_data = usart_data_receive(USART1); // Clear data for next RX. + (void) rx_data; + continue; + } + data[rx_len++] = rx_data; + if (rx_len == sizeof(data)) { + rx_len = 0; + } + } + if (rx_len == 10) { + // 打印调试 +#if TEST_F0_PRINT + data[10] = '\0'; + dxl_print_dbg("Rx msg: %s\n", data); +#endif + rx_len = 0; + // 如果确定是f0的轮询,则可以尝试进行回应 + if (memcmp("iamf0rupm5", data, 10) == 0) { + is_f0_msg = true; + break; + } + } + } + + + if (is_f0_msg) { + // dxl_print_dbg("The message from FlipperZero by CEP(TypeC Extend Port)\n"); + uint8_t response_f0[] = {0x04, 0x00, 'y', 'e', 's', 0x00}; + for (int i = 0; i < sizeof(response_f0); i++) { + uint32_t resp_wait_spi_tdbe = GetTicks(); // 1us = 1.5t + while (spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET) { + if (GetTicks() - resp_wait_spi_tdbe > 1000 * 1000) { + // 等待超过1s还没有发送出去,说明可能和F0的连接已经断开了 + // 如果和F0的连接已经断开,则不再进行等待传输 + bool ret = I2C_BufferReadRaw(&cc_ctrl_data, 1, 0x09, cc_controller_addr << 1); + if (ret && (cc_ctrl_data >> 6 & 0x03) == 0) { + is_cep_connected = false; +#if TEST_F0_PRINT + dxl_print_dbg("Disconnected with F0 during spi transmission\n"); +#endif + break; + } + } + } + spi_i2s_data_transmit(SPI1, response_f0[i]); + } + + // TODO DXL 测试,直接进入主循环,通过SPI进行通信交互(CEP端口) + AppMain(); + } + +#endif + +#if TEST_F0_CONNECTION + + // 检查和F0的互联是否已经断开 + bool ret = I2C_BufferReadRaw(&cc_ctrl_data, 1, 0x09, cc_controller_addr << 1); + if (ret) { +#if TEST_F0_PRINT + // dxl_print_dbg("cc_ctrl_data: %d\n", cc_ctrl_data); +#endif + uint8_t attached_state = cc_ctrl_data >> 6 & 0x03; + if (attached_state == 0x00) { + // 断开状态,如果之前是连接状态的话,则我们需要告知断开事件发生 + if (is_cep_connected) { +#if TEST_F0_PRINT + dxl_print_dbg("Disconnected with F0\n"); +#endif + } + is_cep_connected = false; + } else { + // 连接状态的话,如果之前是断开状态,则我们需要告知连接事件发生 + if (!is_cep_connected) { +#if TEST_F0_PRINT + dxl_print_dbg("Connected with F0\n"); +#endif + } + is_cep_connected = true; + } + // 根据手册描述如果触发过CC控制器的中断,则我们需要清除,否则不会触发下一次中断 + uint8_t int_state = cc_ctrl_data >> 4 & 0x01; + if (int_state) { + cc_ctrl_data |= 1 << 4; // 正确的清除方式是bit4写1 + ret = I2C_BufferWrite(&cc_ctrl_data, 1, 0x09, cc_controller_addr << 1); +#if TEST_F0_PRINT + dxl_print_dbg("Clear CC int reg: %d\n", ret); +#endif + SpinDelay(100); + } + } + +#endif + } +} + +void test_24c02(void) { + // 初始化I2C,等下需要拿来做状态切换 + StartTicks(); + I2C_init(true); + uint8_t addr_24c02 = 0x50; + + SpinDelay(1000); + usb_enable(); // 要初始化USB口,printf调试大法好 + + while (1) { + while (is_btn_pressed()) { + } // 等待松开按钮 + + dxl_print_dbg("按下按钮开始测试 24c02 外部EEPROM > \r\n"); + while (!is_btn_pressed()) { + } // 等待按下按钮 + + // 用 I2C_BufferReadRaw 读取 + // 用 I2C_BufferWrite 写入 + + uint8_t data[256]; + bool ret = I2C_BufferReadRaw(data, sizeof(data), 0x00, addr_24c02 << 1); + dxl_print_dbg("Read result: %d, data: %s\n", ret, data); + + // 尝试解析为结构化的出厂数据 + struct factory_info_v1 { + uint8_t factory_info_version; + uint8_t ecdsa_secp256k1_signature[64]; + + struct { + uint64_t unix_timestamp; + uint8_t chip_unique_id[12]; + uint32_t production_id; + uint32_t hardware_version; + uint8_t aes_key[16]; + uint8_t reserved[147]; + } + PACKED info; + } + PACKED; + struct factory_info_v1 *factory_info = (struct factory_info_v1 *) data; + // 打印全部信息 + dxl_print_dbg("factory_info_version: %d\n", factory_info->factory_info_version); + dxl_print_dbg("ecdsa_secp256k1_signature: "); + for (size_t i = 0; i < sizeof(factory_info->ecdsa_secp256k1_signature); ++i) { + dxl_print_dbg("%02x", factory_info->ecdsa_secp256k1_signature[i]); + } + dxl_print_dbg("\n"); + dxl_print_dbg("unix_timestamp: %llu\n", factory_info->info.unix_timestamp); + dxl_print_dbg("chip_unique_id: "); + for (size_t i = 0; i < sizeof(factory_info->info.chip_unique_id); ++i) { + dxl_print_dbg("%02x", factory_info->info.chip_unique_id[i]); + } + + // 打印从芯片端获取的唯一id + dxl_print_dbg("\n"); + dxl_print_dbg("chip_unique_id(From runtime): "); + uint8_t *chip_unique_id_runtime = GetChipUniqueId(NULL); + for (size_t i = 0; i < sizeof(factory_info->info.chip_unique_id); ++i) { + dxl_print_dbg("%02x", chip_unique_id_runtime[i]); + } + + dxl_print_dbg("\n"); + dxl_print_dbg("production_id: %u\n", factory_info->info.production_id); + dxl_print_dbg("hardware_version: %u\n", factory_info->info.hardware_version); + dxl_print_dbg("aes_key: "); + for (size_t i = 0; i < sizeof(factory_info->info.aes_key); ++i) { + dxl_print_dbg("%02x", factory_info->info.aes_key[i]); + } + dxl_print_dbg("\n"); + +#if 0 + + const char *test_str = "hello"; + ret = I2C_BufferWrite((uint8_t *) test_str, strlen(test_str), 0x00, addr_24c02 << 1); + dxl_print_dbg("Write result: %d\n", ret); + +#endif + } +} + +void test_vusb_check(void) { + gpio_vusb_setup(); + volatile bool vusb = false; + // 测试VUSB的供电检测很简单,直接读取GPIO口的电平然后亮灯即可,不需要USB通信(需要插着电池) + while (1) { + vusb = Gpio_VUSB_Read(); + if (vusb) { + LED_D_ON(); + } else { + LED_D_OFF(); + } + (void) vusb; + } +} + +void test_bwm_uart(void) { + usb_enable(); // 要初始化USB口,printf调试大法好 + + /* enable the uart4 and gpio clock */ + crm_periph_clock_enable(CRM_UART4_PERIPH_CLOCK, TRUE); + crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); + + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + + /* configure the uart4 tx, rx pin */ + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_pins = GPIO_PINS_0 | GPIO_PINS_1; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init(GPIOA, &gpio_init_struct); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE0, GPIO_MUX_8); + gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE1, GPIO_MUX_8); + + /* configure uart4 param */ + usart_init(UART4, 460800, USART_DATA_8BITS, USART_STOP_1_BIT); + usart_parity_selection_config(UART4, USART_PARITY_NONE); + usart_transmitter_enable(UART4, TRUE); + usart_receiver_enable(UART4, TRUE); + usart_enable(UART4, TRUE); + + while (1) { + while (is_btn_pressed()) { + } // 等待松开按钮 + + dxl_print_dbg("按下按钮开始测试 电池套件UART通信 > \r\n"); + while (!is_btn_pressed()) { + } // 等待按下按钮 + + // 测试发送和接收数据,看看BWM是否正常通信 + uint8_t data_tx[] = { 0x7c, 0xc7, 0xfa, 0x03, 0x00, 0x00, 0xb5, 0xba }; + for (size_t i = 0; i < sizeof(data_tx); ++i) { + while(usart_flag_get(UART4, USART_TDBE_FLAG) == RESET); + usart_data_transmit(UART4, data_tx[i]); + } + uint8_t data_rx[9] = { 0x00 }; // 应答是 2d3dfa03010001c8d1 + for (size_t i = 0; i < sizeof(data_rx); ++i) { + while(usart_flag_get(UART4, USART_RDBF_FLAG) == RESET); + data_rx[i] = usart_data_receive(UART4); + } + dxl_print_dbg("Received data from BWM: "); + for (size_t i = 0; i < sizeof(data_rx); ++i) { + dxl_print_dbg("%02x", data_rx[i]); + } + dxl_print_dbg("\n"); + } +} + +void test_config_uart_tx2_to_dbgio(void) { + // 配置uart tx2 为推挽输出 + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); + gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init_struct.gpio_pins = GPIO_PINS_2; + gpio_init(GPIOA, &gpio_init_struct); // PA2_TX +} + +// 覆盖 UnitTestMain 实现单元测试 +void UnitTestMain(void); + +void UnitTestMain(void) { + // ------------------------------- 测试 等待第一次烧录之后,按钮松开 ------------------------------- + while (1) { + if (!is_btn_pressed()) break; + LED_A_ON(); + SpinDelay(200); + LED_B_ON(); + SpinDelay(200); + LED_C_ON(); + SpinDelay(200); + LED_D_ON(); + SpinDelay(200); + } + + // ------------------------------- 关闭所有的LED ------------------------------- + LEDsoff(); + + // ------------------------------- 关闭FPGA输出 ------------------------------- + // 不然的话调试的时候也会很发热 + SpinDelay(500); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + // ------------------------------- 测试 配置UART TX2为DBGIO输出 ------------------------------- + test_config_uart_tx2_to_dbgio(); + + // ------------------------------- 测试 蓝牙电池套件UART通信 ------------------------------- + // test_bwm_uart(); + + // ------------------------------- 测试 VUSB供电检测 ------------------------------- + // test_vusb_check(); + + // ------------------------------- 测试 24c02 ------------------------------- + // test_24c02(); + + // ------------------------------- 测试 F0 通信 ------------------------------- + // test_f0_com_by_usb_cep(); + + // ------------------------------- 测试 四个灯 ------------------------------- + // test_4leds(); + + // ------------------------------- 测试 库仑计和充电器的中断事件 ------------------------------- + // test_coulometer_charger_int(); + + // ------------------------------- 测试 充电器 ------------------------------- + // test_bat_charger(); + + // ------------------------------- 测试 充电器(测完就进主循环) ------------------------------- + test_bat_charger_only_settings(); + + // ------------------------------- 测试 库仑计 ------------------------------- + // test_bat_coulometer(); + + // ------------------------------- 测试 复合多频天线切换IO ------------------------------- + // test_i2c_ant_software(); + + // ------------------------------- 测试按钮关机 ------------------------------- + // test_power_of_by_btn(); + + // ------------------------------- 测试蜂鸣器 ------------------------------- + // usb_enable(); + // SpinDelay(1000); + // dxl_print_dbg("SystemStart\n"); + // test_beep(); + + // ------------------------------- 测试模拟按钮长按退出isp ------------------------------- + // test_isp_exit(); + + // ------------------------------- 测试 USB互联口上的单线串口 ------------------------------- + // test_usb_xlink_1line_uart(); + + // ------------------------------- 测试 USB互联口上的SPI ------------------------------- + // test_usb_xlink_spi(); + + // ------------------------------- 测试 软件I2C ------------------------------- + // test_init_debug_pin(); + test_i2c_rgb_simple(); + + // ------------------------------- 测试 硬件I2C配置CC控制器 ------------------------------- + // test_i2c_cc(); + + // ------------------------------- 测试 硬件I2C配置RGB灯的状态 ------------------------------- + // test_i2c_rgb(); + + // ------------------------------- 测试 USB主从机切换状态 ------------------------------- + // test_usb_id_pin(); + + // ------------------------------- 测试 ADC采样RSSI电压值 ------------------------------- + // FpgaSetup24MHzClk(); + // FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); // 开场 + // AdcSetupRssiChannel(ADC_RSSI_CH_HF); + // volatile uint16_t adc_val = 0; + // volatile uint16_t adc_vref = g_adc_vref_value; + // while (1) { + // if (AdcRssiDataReady(ADC_RSSI_CH_HF)) { + // adc_val = AdcRssiDataRead(ADC_RSSI_CH_HF); + // } + // (void)adc_val; + // (void)adc_vref; + // // printf("vref_value = %f V\r\n", ((double)1.2 * 4095) / adc1_ordinary_value); + // AdcRssiConversionStart(); + // + // SpinDelay(1); + // + // adc_val = AdcRssiAvg(ADC_RSSI_CH_HF); + // + // SpinDelay(1); + // + // adc_val = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF); + // + // SpinDelay(1); + // } + + // Gpio_FPGA_SWITCH_High(); + // bool rfON = true; + // while (1) { + // LED_A_ON(); + // LED_B_ON(); + // LED_C_ON(); + // LED_D_ON(); + // if (!is_btn_pressed()) continue; + // if (rfON) { + // FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // } else { + // FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); + // } + // rfON = !rfON; + // LED_C_OFF(); + // LED_D_OFF(); + // SpinDelay(500); + // } + + // ------------------------------- 测试 按下按钮清除外部flash ------------------------------- + // while (1) { + // LED_C_ON(); + // LED_D_ON(); + // if (!is_btn_pressed()) continue; + // LED_C_OFF(); + // LED_D_OFF(); + // LED_A_ON(); + // if (Flash_WipeMemory()) { + // LED_A_OFF(); + // } else { + // LED_A_ON(); + // LED_B_ON(); + // } + // } + + // while (1) { + // if (is_btn_pressed()) { + // LED_A_ON(); + // } else { + // LED_A_OFF(); + // } + // } + + // while (1) { + // Gpio_LED_A_High(); + // SpinDelay(500); + // Gpio_LED_A_Low(); + // SpinDelay(500); + // } + + // ------------------------------- 测试 utils.c 中封装的按钮检测的逻辑(长按,双击) ------------------------------- + // StartTickCount(); + // while (1) { + // volatile uint32_t ticks = GetTickCount(); + // volatile int click = BUTTON_CLICKED(800); // BUTTON_HELD 或者 BUTTON_CLICKED + // ticks = GetTickCount() - ticks; + // (void)ticks; + // if (click == BUTTON_NO_CLICK) { + // SpinDelay(1); + // } else if (click == BUTTON_HOLD) { + // SpinDelay(2); + // } else if (click == BUTTON_DOUBLE_CLICK) { + // SpinDelay(3); + // } else if (click == BUTTON_SINGLE_CLICK) { + // SpinDelay(4); + // } else { + // SpinDelay(5); + // } + // } + + // ------------------------------- 测试 Flash相关的操作 ------------------------------- + // StartTickCount(); + // FlashInit(); + // while (1) { + // // volatile uint8_t sts = Flash_ReadStat1(); + // // if (1) { + // // (void)sts; + // // } + // + // volatile uint8_t flash_uid[8] = {0x01}; + // Flash_UniqueID((uint8_t *) flash_uid); + // if (1) { + // (void) flash_uid; + // SpinDelay(1); + // } + // + // // volatile uint8_t flash_data[256] = { 0x00 }; + // // __NOP(); + // // Flash_ReadDataCont(0x00, (uint8_t *)flash_data, sizeof(flash_data)); + // // if (1) { + // // (void)flash_data; + // // __NOP(); + // // } + // + // uint8_t flash_rw[255] = {0x00}; + // + // Flash_ReadDataCont(0x00, flash_rw, sizeof(flash_rw)); + // SpinDelay(1); + // + // Flash_WriteEnable(); + // Flash_Erase4k(0x00, 0x00); + // if (Flash_CheckBusy(BUSY_TIMEOUT)) return; + // + // SpinDelay(1000); // wait erase finish + // if (Flash_ReadDataCont(0x00, flash_rw, sizeof(flash_rw)) == 0) return; + // SpinDelay(1); + // + // for (int i = 0; i < sizeof(flash_rw); i++) flash_rw[i] = i; + // Flash_WriteEnable(); + // if (Flash_WriteDataCont(0x00, flash_rw, sizeof(flash_rw)) == 0) return; + // if (Flash_CheckBusy(BUSY_TIMEOUT)) return; + // SpinDelay(1000); + // + // memset(flash_rw, 'a', sizeof(flash_rw)); + // Flash_ReadDataCont(0x00, flash_rw, sizeof(flash_rw)); + // SpinDelay(1); + // + // // uint8_t flash_rw[256] = { 0x00 }; + // // Flash_ReadData(0x00, flash_rw, sizeof(flash_rw)); + // // SpinDelay(1); + // } + + + // ------------------------------- 测试 StartTickCount 和 GetTickCountDelta的精度 ------------------------------- + // StartTickCount(); + // while (1) { + // volatile uint32_t tickCount = GetTickCount(); + // (void)tickCount; + // while (GetTickCountDelta(tickCount) != 500) { + // /* set pa.01 */ + // GPIOA->scr = GPIO_PINS_1; + // } + // while (GetTickCountDelta(tickCount) != 1000) { + // /* reset pa.01 */ + // GPIOA->clr = GPIO_PINS_1; + // } + // } + + // ------------------------------- 测试 SpinDelayUs 的精度 ------------------------------- + // while (1) { + // GPIOA->scr = GPIO_PINS_1; // set pa.01 + // SpinDelayUsPrecision(500); + // GPIOA->clr = GPIO_PINS_1; // reset pa.01 + // SpinDelayUsPrecision(500); + // } + + // ------------------------------- 测试 StartTicks 的精度 ------------------------------- + // 注意,StartTicks和StartCountUS和StartCountSspClk不能同时使用 + // StartTicks(); + // volatile uint32_t tickCount; + // while (1) { + // // 拉高测试 + // GPIOA->scr = GPIO_PINS_1; // set pa.01 + // tickCount = GetTicks(); + // while (GetTicks() - tickCount < 3) {} // 1.5tick = 1us, 3tick = 2us, 4.5tick = 3us, 6tick = 4us + // + // // 拉低测试 + // GPIOA->clr = GPIO_PINS_1; // reset pa.01 + // tickCount = GetTicks(); + // while (GetTicks() - tickCount < 3) {} + // } + + // ------------------------------- 测试 StartTicks 重置计数值 ------------------------------- + // 注意,StartTicks和StartCountUS和StartCountSspClk不能同时使用 + // StartTicks(); + // volatile uint32_t tickCount; + // while (1) { + // tickCount = GetTicks(); + // (void)tickCount; + // SpinDelay(1); + // tickCount = GetTicks(); + // (void)tickCount; + // SpinDelay(1); + // + // ResetTicks(); + // tickCount = GetTicks(); + // (void)tickCount; + // SpinDelay(1); + // } + + // ------------------------------- 测试 StartCountSspClk 外部输入时钟计数 ------------------------------- + // 注意,StartTicks和StartCountUS和StartCountSspClk不能同时使用 + // StartCountSspClk(); + // volatile uint32_t clkCount; + // while (1) { + // GPIOA->scr = GPIO_PINS_1; // set pa.01 + // clkCount = GetCountSspClk(); + // while (GetCountSspClk() == clkCount) {} + // GPIOA->clr = GPIO_PINS_1; // reset pa.01 + // clkCount = GetCountSspClk(); + // while (GetCountSspClk() == clkCount) {} + // } + + // ------------------------------- 测试 StartCountUS 的精度 ------------------------------- + // 注意,StartCountUS和StartTicks和和StartCountSspClk不能同时使用 + // StartCountUS(); + // volatile uint32_t usCount; + // while (1) { + // // 拉高测试 + // GPIOA->scr = GPIO_PINS_1; // set pa.01 + // usCount = GetCountUS(); + // while (GetTicks() - usCount < 3) {} + // + // // 拉低测试 + // GPIOA->clr = GPIO_PINS_1; // reset pa.01 + // usCount = GetCountUS(); + // while (GetTicks() - usCount < 3) {} + // } + + // StartTickCount(); + // + // uint32_t tStart = GetTickCount(); + // while (GetTickCountDelta(tStart) != 2000); // 等一会儿再启动发送,USB可能还在枚举 + // + // while (1) { + // GPIOC->clr = GPIO_PINS_0; // 熄灯 + // GPIOC->scr = GPIO_PINS_1; // 熄灯 + // if (!is_btn_pressed()) { + // continue; + // } + // GPIOC->clr = GPIO_PINS_1; // 熄灯 + // + // // ------------------------------- 开始堵塞收发相关的处理逻辑 ------------------------------- + // uint8_t buffer[150]; + // while (1) { + // uint32_t recv_len = usb_read(buffer, sizeof(buffer)); // 收一串数据 + // if (recv_len != 0) { + // if (usb_write(buffer, recv_len) != PM3_SUCCESS) { + // // 回环测试,发回去 + // while (1) + // __NOP(); // send error!!! + // } + // } + // } + + // ------------------------------- 测试USB失能 ------------------------------- + // usb_disable(); + // while (!is_btn_pressed()) {} + // usb_enable(); + + // ------------------------------- 开始异步发送相关的处理逻辑 ------------------------------- + // if (async_usb_write_start() != PM3_SUCCESS) { + // GPIOC->scr = GPIO_PINS_0; + // while (1) {} // 出错了 + // } + // while (1) { + // tickCount = GetTickCount(); + // while (GetTickCountDelta(tickCount) <= 1); // 等待一小会儿,模拟数据采集的时间差 + // + // // 提交数据到缓冲区,但是先不发送,先堆积着。 + // for (uint8_t i = 0; i < 64; i++) { + // async_usb_write_pushByte(i); // 提交一个字节。 + // } + // + // // 放完了数据,可以提交写入确认,让HOST在下一次的IN事务中取走数据,并且此函数还切换了双buffer的idx + // if (!async_usb_write_requestWrite()) { + // GPIOC->scr = GPIO_PINS_0; + // while (1) {} // 不能到这里,到这里说明提交速度大于USB的发送速度了 + // } + // } + // // 然后等待结束发送。 + // if (async_usb_write_stop() != PM3_SUCCESS) { + // GPIOC->scr = GPIO_PINS_0; + // while (1) {} // 不能到这里,到这里说明数据写入有问题。 + // } +} + +#endif diff --git a/armsrc/cmd.c b/armsrc/cmd.c index 2c708c826..492b4aa26 100644 --- a/armsrc/cmd.c +++ b/armsrc/cmd.c @@ -14,7 +14,8 @@ // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- #include "cmd.h" -#include "usb_cdc.h" +#include "usb_cdc_apis.h" +#include "usb_read_ng.h" #include "usart.h" #include "crc16.h" #include "string.h" @@ -73,6 +74,9 @@ int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const v return PM3_SUCCESS; } +// TODO DXL 测试阶段,暂时通过SPI应答 +extern int cep_spi_write_sync(uint8_t *data, size_t len); + static int reply_ng_internal(uint16_t cmd, int8_t status, uint8_t reason, const uint8_t *data, size_t len, bool ng) { PacketResponseNGRaw txBufferNG; size_t txBufferNGLen; @@ -119,6 +123,10 @@ static int reply_ng_internal(uint16_t cmd, int8_t status, uint8_t reason, const resultusb = usb_write((uint8_t *)&txBufferNG, txBufferNGLen); } if (g_reply_via_fpc) { + + // TODO DXL 测试阶段,暂时通过SPI应答 + // resultusb = cep_spi_write_sync((uint8_t *)&txBufferNG, txBufferNGLen); + #ifdef WITH_FPC_USART_HOST resultfpc = usart_writebuffer_sync((uint8_t *)&txBufferNG, txBufferNGLen); #else @@ -251,6 +259,10 @@ static int receive_ng_internal(PacketCommandNG *rx, uint32_t read_ng(uint8_t *da return PM3_SUCCESS; } +// TODO DXL 临时在此处定义外部实现的CEP端口的SPI通信实现,做视频通信测试用的,后期需要重构设计 +// extern uint32_t cep_spi_read_ng(uint8_t *data, size_t len); +// extern bool cep_spi_data_available(void); + int receive_ng(PacketCommandNG *rx) { // Check if there is a packet available @@ -258,6 +270,10 @@ int receive_ng(PacketCommandNG *rx) { return receive_ng_internal(rx, usb_read_ng, true, false); } + // if (cep_spi_data_available()) { + // return receive_ng_internal(rx, cep_spi_read_ng, false, true); // TODO DXL 临时用fpc这种标志 + // } + #ifdef WITH_FPC_USART_HOST // Check if there is a FPC packet available if (usart_rxdata_available() > 0) diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 8de00ccae..0aa9fb78d 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -16,10 +16,10 @@ // Low frequency EM4x50 commands //----------------------------------------------------------------------------- -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "ticks_apis.h" +#include "fpga_apis.h" #include "dbprint.h" -#include "lfsampling.h" #include "lfadc.h" #include "lfdemod.h" #include "commonutil.h" @@ -29,15 +29,8 @@ #include "appmain.h" // tear #include "bruteforce.h" -// Sam7s has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS_TIMER_DIV1_CLOCK) -// TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz -// EM4x50 units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier) -// T0 = TIMER_CLOCK1 / 125000 = 192 - -#define T0 192 - // conversions (carrier frequency 125 kHz): -// 1 us = 1.5 ticks +// 1 us = 1.5 ticks for GetTicks() // 1 cycle = 1 period = 8 us = 12 ticks // 1 bit = 64 cycles = 768 ticks = 512 us (for Opt64) #define CYCLES2TICKS 12 @@ -59,8 +52,8 @@ #define EM4X50_T_ZERO_DETECTION 3 // timeout values (empirical) for simulation mode (may vary with regard to reader) -#define EM4X50_T_SIMULATION_TIMEOUT_READ 600 -#define EM4X50_T_SIMULATION_TIMEOUT_WAIT 50 +#define EM4X50_T_SIMULATION_TIMEOUT_READ 318 // only for em4x50_sim_read_bit() +#define EM4X50_T_SIMULATION_TIMEOUT_EDGE 24 // the following value (pulses) seems to be critical; if it's too low //(e.g. < 120) some cards are no longer readable although they're ok @@ -69,7 +62,7 @@ // div #define EM4X50_TAG_WORD 45 #define EM4X50_TAG_MAX_NO_BYTES 136 -#define EM4X50_TIMEOUT_PULSE_EVAL 2500 +#define EM4X50_TIMEOUT_PULSE_EVAL (4 * EM4X50_T_TAG_FULL_PERIOD * CYCLES2TICKS) // 4bit data periods uint8_t g_High = 190; uint8_t g_Low = 60; @@ -136,7 +129,6 @@ static bool extract_parities(uint64_t word, uint32_t *data) { } void em4x50_setup_read(void) { - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); @@ -151,15 +143,14 @@ void em4x50_setup_read(void) { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); // Connect the A/D to the peak-detected low-frequency path. - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + SetAdcMuxFor(ADC_MUXSEL_LOPKD); // Steal this pin from the SSP (SPI communication channel with fpga) and // use it to control the modulation - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + gpio_fpga_mod_only_setup(); // Disable modulation at default, which means enable the field - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); // Watchdog hit WDT_HIT(); @@ -170,9 +161,7 @@ void em4x50_setup_sim(void) { FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; + gpio_fpga_mod_feedback_setup(); StartTicks(); @@ -200,8 +189,10 @@ static bool get_signalproperties(void) { // about 2 samples per bit period WaitUS(EM4X50_T_TAG_HALF_PERIOD * CYCLES2MUSEC); + FPGA_SSC_RX_READY_WAIT(); + // ignore first samples - if ((i > SIGNAL_IGNORE_FIRST_SAMPLES) && (AT91C_BASE_SSC->SSC_RHR > noise)) { + if ((i > SIGNAL_IGNORE_FIRST_SAMPLES) && (FPGA_SSC_RX_Value() > noise)) { signal_found = true; break; } @@ -220,7 +211,9 @@ static bool get_signalproperties(void) { if (BUTTON_PRESS()) return false; - volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + FPGA_SSC_RX_READY_WAIT(); + + volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value(); if (sample > sample_max[i]) sample_max[i] = sample; @@ -247,7 +240,9 @@ static bool invalid_bit(void) { // get sample at 3/4 of bit period WaitUS(EM4X50_T_TAG_THREE_QUARTER_PERIOD * CYCLES2MUSEC); - uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + FPGA_SSC_RX_READY_WAIT(); + + uint8_t sample = (uint8_t)FPGA_SSC_RX_Value(); // wait until end of bit period WaitUS(EM4X50_T_TAG_QUARTER_PERIOD * CYCLES2MUSEC); @@ -261,36 +256,46 @@ static bool invalid_bit(void) { static uint32_t get_pulse_length(void) { - int32_t timeout = EM4X50_TIMEOUT_PULSE_EVAL, tval = 0; + uint64_t timeout = GetTicks() + EM4X50_TIMEOUT_PULSE_EVAL; // iterates pulse lengths (low -> high -> low) - volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + FPGA_SSC_RX_READY_WAIT(); // Wait for valid data received. + volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value(); - while (sample > g_Low && (timeout--)) - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + while (sample > g_Low && (GetTicks() < timeout)) { + if (FPGA_SSC_RX_Ready()) { + sample = (uint8_t)FPGA_SSC_RX_Value(); + } + } - if (timeout <= 0) + if (GetTicks() >= timeout) { return 0; + } - tval = GetTicks(); - timeout = EM4X50_TIMEOUT_PULSE_EVAL; + uint32_t start_ticks = GetTicks(); - while (sample < g_High && (timeout--)) - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + while (sample < g_High && (GetTicks() < timeout)) { + if (FPGA_SSC_RX_Ready()) { + sample = (uint8_t)FPGA_SSC_RX_Value(); + } + } - if (timeout <= 0) + if (GetTicks() >= timeout) { return 0; + } - timeout = EM4X50_TIMEOUT_PULSE_EVAL; - while (sample > g_Low && (timeout--)) - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + while (sample > g_Low && (GetTicks() < timeout)) { + if (FPGA_SSC_RX_Ready()) { + sample = (uint8_t)FPGA_SSC_RX_Value(); + } + } - if (timeout <= 0) + if (GetTicks() >= timeout) { return 0; + } - return GetTicks() - tval; - + return GetTicks() - start_ticks; } // check if pulse length corresponds to given length @@ -309,22 +314,22 @@ static void em4x50_reader_send_bit(int bit) { // disable modulation (activate the field) for 7 cycles of carrier // period (Opt64) - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); while (GetTicks() - tval < 7 * CYCLES2TICKS); // enable modulation (drop the field) for remaining first // half of bit period - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); while (GetTicks() - tval < EM4X50_T_TAG_HALF_PERIOD * CYCLES2TICKS); // disable modulation for second half of bit period - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); while (GetTicks() - tval < EM4X50_T_TAG_FULL_PERIOD * CYCLES2TICKS); } else { // bit = "1" means disable modulation for full bit period - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); while (GetTicks() - tval < EM4X50_T_TAG_FULL_PERIOD * CYCLES2TICKS); } } @@ -903,7 +908,7 @@ void em4x50_read(const em4x50_data_t *etd, bool ledcontrol) { } if (ledcontrol) LEDsoff(); - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); lf_finalize(ledcontrol); reply_ng(CMD_LF_EM4X50_READ, status, (uint8_t *)words, EM4X50_TAG_MAX_NO_BYTES); } @@ -956,7 +961,7 @@ void em4x50_reader(bool ledcontrol) { } if (ledcontrol) LEDsoff(); - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); lf_finalize(ledcontrol); reply_ng(CMD_LF_EM4X50_READER, now, (uint8_t *)words, 4 * now); } @@ -1145,37 +1150,50 @@ void em4x50_writepwd(const em4x50_data_t *etd, bool ledcontrol) { reply_ng(CMD_LF_EM4X50_WRITEPWD, status, NULL, 0); } +// wait for ssc clk edge change to high, clk is from cross_lo +// mode: +// 1: wait to high +// 2: wait to low +// 3: wait to high & low +static void em4x50_sim_wait_edge(const int mode) { + uint32_t ticks_start; + if (mode & 0x01) { + ticks_start = GET_TICKS; // reset start time value + do { + if (GetTicksDelta(ticks_start) > EM4X50_T_SIMULATION_TIMEOUT_EDGE) { + // Dbprintf("timeout wait clk(1)"); + return; + } + } + while (!Gpio_SSC_CLK_Read()); // wait to high + } + if (mode & 0x02) { + ticks_start = GET_TICKS; // reset start time value + do { + if (GetTicksDelta(ticks_start) > EM4X50_T_SIMULATION_TIMEOUT_EDGE) { + // Dbprintf("timeout wait clk(2)"); + return; + } + } while (Gpio_SSC_CLK_Read()); // wait to low + } +} + // send bit in receive mode by counting carrier cycles static void em4x50_sim_send_bit(uint8_t bit) { - uint16_t timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; - for (int t = 0; t < EM4X50_T_TAG_FULL_PERIOD; t++) { - // wait until SSC_CLK goes HIGH - // used as a simple detection of a reader field? - while ((timeout--) && !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); - - if (timeout == 0) { - return; - } - timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; + em4x50_sim_wait_edge(1); if (bit) OPEN_COIL(); else SHORT_COIL(); - //wait until SSC_CLK goes LOW - while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); - if (timeout == 0) { - return; - } - timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; + em4x50_sim_wait_edge(2); if (t == EM4X50_T_TAG_HALF_PERIOD) bit ^= 1; - } } @@ -1227,23 +1245,9 @@ static void em4x50_sim_send_word(uint32_t word) { // wait for pulses of carrier frequency static void wait_cycles(int maxperiods) { - - int period = 0, timeout = EM4X50_T_SIMULATION_TIMEOUT_WAIT; - + int period = 0; while (period < maxperiods) { - - while ((timeout--) && !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); - if (timeout <= 0) { - return; - } - timeout = EM4X50_T_SIMULATION_TIMEOUT_WAIT; - - while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); - if (timeout <= 0) { - return; - } - timeout = EM4X50_T_SIMULATION_TIMEOUT_WAIT; - + em4x50_sim_wait_edge(3); period++; } } @@ -1252,7 +1256,6 @@ static void wait_cycles(int maxperiods) { static int em4x50_sim_read_bit(void) { int cycles = 0; - int timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; // wait 16 cycles to make sure there is no field when reading a "0" bit uint32_t waitval = GetTicks(); @@ -1260,18 +1263,24 @@ static int em4x50_sim_read_bit(void) { while (cycles < EM4X50_T_TAG_THREE_QUARTER_PERIOD) { + uint32_t timeout_start = GET_TICKS; + // wait until reader field disappears - while ((timeout--) && !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); - if (timeout <= 0) { - return PM3_ETIMEOUT; - } - timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; + do { + if (GetTicksDelta(timeout_start) >= EM4X50_T_SIMULATION_TIMEOUT_READ) { + DBG Dbprintf("read bit timeout(1): %d", GetTicksDelta(timeout_start)); + return PM3_ETIMEOUT; + } + } while (!Gpio_SSC_CLK_Read()); + + timeout_start = GET_TICKS; // now check until reader switches on carrier field uint32_t tval = GetTicks(); - while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { + while (Gpio_SSC_CLK_Read()) { - if (timeout <= 0) { + if (GetTicksDelta(timeout_start) >= EM4X50_T_SIMULATION_TIMEOUT_READ) { + DBG Dbprintf("read bit timeout(2): %d", GetTicksDelta(timeout_start)); return PM3_ETIMEOUT; } @@ -1279,11 +1288,12 @@ static int em4x50_sim_read_bit(void) { if (GetTicks() - tval > EM4X50_T_ZERO_DETECTION * CYCLES2TICKS) { // gap detected; wait until reader field is switched on again - while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); - - if (timeout <= 0) { - return PM3_ETIMEOUT; - } + do { + if (GetTicksDelta(timeout_start) >= EM4X50_T_SIMULATION_TIMEOUT_READ) { + DBG Dbprintf("read bit timeout(3): %d", GetTicksDelta(timeout_start)); + return PM3_ETIMEOUT; + } + } while (Gpio_SSC_CLK_Read()); // now we have a reference "position", from here it will take // slightly less than 32 cycles until the end of the bit period @@ -1294,7 +1304,6 @@ static int em4x50_sim_read_bit(void) { return 0; } } - timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; // no gap detected, i.e. reader field is still up; // continue with counting cycles diff --git a/armsrc/em4x70.c b/armsrc/em4x70.c index 14a114e5b..bb20a9950 100644 --- a/armsrc/em4x70.c +++ b/armsrc/em4x70.c @@ -16,8 +16,9 @@ // Low frequency EM4x70 commands //----------------------------------------------------------------------------- -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "ticks_apis.h" +#include "fpga_apis.h" #include "dbprint.h" #include "lfadc.h" #include "commonutil.h" @@ -222,7 +223,6 @@ static void init_tag(void) { } static void em4x70_setup_read(void) { - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); @@ -235,15 +235,14 @@ static void em4x70_setup_read(void) { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); // Connect the A/D to the peak-detected low-frequency path. - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + SetAdcMuxFor(ADC_MUXSEL_LOPKD); // Steal this pin from the SSP (SPI communication channel with fpga) and // use it to control the modulation - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + gpio_fpga_mod_only_setup(); // Disable modulation at default, which means enable the field - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); // Start the timer StartTicks(); @@ -263,7 +262,9 @@ static bool get_signalproperties(void) { // about 2 samples per bit period WaitTicks(EM4X70_T_TAG_HALF_PERIOD); - if (AT91C_BASE_SSC->SSC_RHR > HIGH_SIGNAL_THRESHOLD) { + FPGA_SSC_RX_READY_WAIT(); + + if (FPGA_SSC_RX_Value() > HIGH_SIGNAL_THRESHOLD) { return true; } } @@ -279,19 +280,35 @@ static uint32_t get_falling_pulse_length(void) { uint32_t timeout = GetTicks() + EM4X70_T_TAG_TIMEOUT; - while (IS_HIGH(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); + FPGA_SSC_RX_READY_WAIT(); + + uint32_t sample = FPGA_SSC_RX_Value(); + + while (IS_HIGH(sample) && !IS_TIMEOUT(timeout)) { + if (FPGA_SSC_RX_Ready()) { + sample = FPGA_SSC_RX_Value(); + } + } if (IS_TIMEOUT(timeout)) return 0; uint32_t start_ticks = GetTicks(); - while (IS_LOW(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); + while (IS_LOW(sample) && !IS_TIMEOUT(timeout)) { + if (FPGA_SSC_RX_Ready()) { + sample = FPGA_SSC_RX_Value(); + } + } if (IS_TIMEOUT(timeout)) return 0; - while (IS_HIGH(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); + while (IS_HIGH(sample) && !IS_TIMEOUT(timeout)) { + if (FPGA_SSC_RX_Ready()) { + sample = FPGA_SSC_RX_Value(); + } + } if (IS_TIMEOUT(timeout)) return 0; @@ -308,19 +325,35 @@ static uint32_t get_rising_pulse_length(void) { uint32_t timeout = GetTicks() + EM4X70_T_TAG_TIMEOUT; - while (IS_LOW(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); + FPGA_SSC_RX_READY_WAIT(); + + uint32_t sample = FPGA_SSC_RX_Value(); + + while (IS_LOW(sample) && !IS_TIMEOUT(timeout)) { + if (FPGA_SSC_RX_Ready()) { + sample = FPGA_SSC_RX_Value(); + } + } if (IS_TIMEOUT(timeout)) return 0; uint32_t start_ticks = GetTicks(); - while (IS_HIGH(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); + while (IS_HIGH(sample) && !IS_TIMEOUT(timeout)) { + if (FPGA_SSC_RX_Ready()) { + sample = FPGA_SSC_RX_Value(); + } + } if (IS_TIMEOUT(timeout)) return 0; - while (IS_LOW(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); + while (IS_LOW(sample) && !IS_TIMEOUT(timeout)) { + if (FPGA_SSC_RX_Ready()) { + sample = FPGA_SSC_RX_Value(); + } + } if (IS_TIMEOUT(timeout)) return 0; @@ -448,22 +481,22 @@ static void em4x70_send_bit(bool bit) { if (bit == 0) { // disable modulation (drop the field) n cycles of carrier - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_BITMOD); // enable modulation (activates the field) for remaining first // half of bit period - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_HALF_PERIOD); // disable modulation for second half of bit period - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_FULL_PERIOD); } else { // bit = "1" means disable modulation for full bit period - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_FULL_PERIOD); } log_sent_bit_end(GetTicks()); diff --git a/armsrc/emvsim.c b/armsrc/emvsim.c index 5b2c2b58e..605ec5244 100644 --- a/armsrc/emvsim.c +++ b/armsrc/emvsim.c @@ -43,13 +43,14 @@ #include "string.h" #include "mifareutil.h" #include "mifaresim.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "proxmark3_arm.h" #include "protocols.h" #include "util.h" #include "commonutil.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "i2c_direct.h" // Hardcoded response to the reader for file not found, plus the checksum diff --git a/armsrc/epa.c b/armsrc/epa.c index 583b59d8f..d2bdeb8dd 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -21,14 +21,15 @@ #include "epa.h" #include "cmd.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "iso14443a.h" #include "iso14443b.h" #include "string.h" #include "util.h" #include "dbprint.h" #include "commonutil.h" -#include "ticks.h" +#include "ticks_apis.h" #ifdef WITH_ISO14443a // Protocol and Parameter Selection Request for ISO 14443 type A cards diff --git a/armsrc/felica.c b/armsrc/felica.c index d6425b32c..95bab9990 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -19,11 +19,12 @@ #include "util.h" #include "protocols.h" #include "crc16.h" -#include "fpgaloader.h" +#include "fpga_loader.h" #include "string.h" #include "commonutil.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" +#include "fpga_apis.h" #include "iso18.h" #define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len)) @@ -416,8 +417,8 @@ void TransmitFor18092_AsReaderEx(const uint8_t *frame, uint16_t len, const uint3 uint16_t c = 0; while (c < 6) { // keep tx buffer in a defined state anyway. - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(0x00); c++; } } @@ -426,17 +427,17 @@ void TransmitFor18092_AsReaderEx(const uint8_t *frame, uint16_t len, const uint3 while (c < len) { // Put byte into tx holding register as soon as it is ready - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = frame[c++]; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(frame[c++]); } } /**/ - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) {}; - AT91C_BASE_SSC->SSC_THR = 0x00; //minimum delay + while (!FPGA_SSC_TX_Ready()) {}; + FPGA_SSC_TX_Value(0x00); //minimum delay - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) {}; - AT91C_BASE_SSC->SSC_THR = 0x00; //spin + while (!FPGA_SSC_TX_Ready()) {}; + FPGA_SSC_TX_Value(0x00); //spin /**/ const uint32_t frame_start = felica_lasttime_prox2air_start + (FELICA_PREAMBLE_BYTES * FELICA_BITS_PER_BYTE); @@ -475,7 +476,7 @@ bool WaitForFelicaReply(uint16_t maxbytes) { FelicaFrameReset(&FelicaFrame); // clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + uint8_t b = (uint8_t)FPGA_SSC_RX_Value(); (void)b; uint32_t timeout = iso18092_get_timeout(); @@ -484,9 +485,9 @@ bool WaitForFelicaReply(uint16_t maxbytes) { WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + if (FPGA_SSC_RX_Ready()) { - b = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); + b = (uint8_t)(FPGA_SSC_RX_Value()); Process18092Byte(&FelicaFrame, b, felica_get_rx_byte_start_time()); felica_frame_t *received = NULL; @@ -552,7 +553,7 @@ bool WaitForFelicaReply(uint16_t maxbytes) { bool iso18092_setup_ex(uint8_t fpga_minor_mode, uint32_t preserve_low_bytes) { LEDsoff(); -#if defined XC3 +#if defined XC3 || defined PM5 FpgaDownloadAndGo(FPGA_BITSTREAM_HF); #else FpgaDownloadAndGo(FPGA_BITSTREAM_HF_FELICA); @@ -576,13 +577,13 @@ bool iso18092_setup_ex(uint8_t fpga_minor_mode, uint32_t preserve_low_bytes) { init_table(CRC_FELICA); // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); // Set up the synchronous serial port FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO18092); - // LSB transfer. Remember to set it back to MSB with - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + // RX LSB transfer. TX MSB transfer, Remember to set it(RX) back to MSB with + FpgaUpdateFrameMode(8, false, true); // Signal field is on with the appropriate LED FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | fpga_minor_mode); @@ -605,8 +606,8 @@ void iso18092_setup(uint8_t fpga_minor_mode) { void felica_reset_frame_mode(void) { switch_off(); felica_field_active = false; - //Resetting Frame mode (First set in fpgaloader.c) - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + // Resetting Frame mode (First set in FpgaSetupSsc() function) + FpgaUpdateFrameMode(8, true, true); } //----------------------------------------------------------------------------- @@ -780,9 +781,9 @@ void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) { } ++checker; - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + if (FPGA_SSC_RX_Ready()) { - uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); + uint8_t dist = (uint8_t)FPGA_SSC_RX_Value(); Process18092Byte(&FelicaFrame, dist, felica_get_rx_byte_start_time()); if ((dist >= 178) && (++trigger_cnt > triggersToSkip)) { @@ -888,9 +889,9 @@ void felica_sim_lite(const uint8_t *uid) { if (listenmode) { // waiting for request... - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + if (FPGA_SSC_RX_Ready()) { - uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); + uint8_t dist = (uint8_t)(FPGA_SSC_RX_Value()); // frtm = GetCountSspClk(); Process18092Byte(&FelicaFrame, dist, felica_get_rx_byte_start_time()); diff --git a/armsrc/felicasim.c b/armsrc/felicasim.c index cb0f3ade2..4ad9216fc 100644 --- a/armsrc/felicasim.c +++ b/armsrc/felicasim.c @@ -21,11 +21,12 @@ #include "util.h" #include "protocols.h" #include "crc16.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "string.h" #include "commonutil.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "iso18.h" #define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len)) @@ -879,8 +880,8 @@ static int felica_sim_standard_loop(const felica_sim_model_header_t *hdr, const } ++checker; - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); + if (FPGA_SSC_RX_Ready()) { + uint8_t dist = (uint8_t)FPGA_SSC_RX_Value(); Process18092Byte(&FelicaFrame, dist, felica_get_rx_byte_start_time()); if (FelicaFrame.state == STATE_FULL) { diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h deleted file mode 100644 index 191120155..000000000 --- a/armsrc/fpgaloader.h +++ /dev/null @@ -1,182 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// Routines to load the FPGA image, and then to configure the FPGA's major -// mode once it is configured. -//----------------------------------------------------------------------------- -#ifndef __FPGALOADER_H -#define __FPGALOADER_H - -#include "common.h" -#include "fpga.h" - -#define FpgaDisableSscDma(void) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; -#define FpgaEnableSscDma(void) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; - -/* - Communication between ARM / FPGA is done inside armsrc/fpgaloader.c see: function FpgaSendCommand() - Send 16 bit command / data pair to FPGA with the bit format: - -+------ frame layout circa 2020 ------------------+ -| 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | -+-------------------------------------------------+ -| C C C C M M M M P P P P P P P P | C = FPGA_CMD_SET_CONFREG, M = FPGA_MAJOR_MODE_*, P = FPGA_LF_* or FPGA_HF_* parameter -| C C C C D D D D D D D D | C = FPGA_CMD_SET_DIVISOR, D = divisor -| C C C C T T T T T T T T | C = FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, T = threshold -| C C C C E | C = FPGA_CMD_TRACE_ENABLE, E=0 off, E=1 on -+-------------------------------------------------+ - -+------ frame layout current ---------------------+ -| 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | -+-------------------------------------------------+ -| C C C C M M M P P P P P P | C = FPGA_CMD_SET_CONFREG, M = FPGA_MAJOR_MODE_*, P = FPGA_LF_* or FPGA_HF_* parameter -| C C C C D D D D D D D D | C = FPGA_CMD_SET_DIVISOR, D = divisor -| C C C C T T T T T T T T | C = FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, T = threshold -| C C C C E | C = FPGA_CMD_TRACE_ENABLE, E=0 off, E=1 on -+-------------------------------------------------+ - - shift_reg receive this 16bit frame - - LF command - ---------- - shift_reg[15:12] == 4bit command - LF has three commands (FPGA_CMD_SET_CONFREG, FPGA_CMD_SET_DIVISOR, FPGA_CMD_SET_EDGE_DETECT_THRESHOLD) - Current commands uses only 2bits. We have room for up to 4bits of commands total (7). - - LF data - ------- - shift_reg[11:0] == 12bit data - lf data is divided into MAJOR MODES and configuration values. - - The major modes uses 3bits (0,1,2,3,7 | 000, 001, 010, 011, 111) - 000 FPGA_MAJOR_MODE_LF_READER = Act as LF reader (modulate) - 001 FPGA_MAJOR_MODE_LF_EDGE_DETECT = Simulate LF - 010 FPGA_MAJOR_MODE_LF_PASSTHRU = Passthrough mode, CROSS_LO line connected to SSP_DIN. SSP_DOUT logic level controls if we modulate / listening - 011 FPGA_MAJOR_MODE_LF_ADC = refactor hitag 2, clear ADC sampling - 111 FPGA_MAJOR_MODE_OFF = turn off sampling. - - Each one of this major modes can have options. Currently these two major modes uses options. - - FPGA_MAJOR_MODE_LF_READER - - FPGA_MAJOR_MODE_LF_EDGE_DETECT - - FPGA_MAJOR_MODE_LF_READER - ------------------------------------- - lf_field = 1bit (FPGA_LF_ADC_READER_FIELD) - - You can send FPGA_CMD_SET_DIVISOR to set with FREQUENCY the fpga should sample at - divisor = 8bits shift_reg[7:0] - - FPGA_MAJOR_MODE_LF_EDGE_DETECT - ------------------------------------------ - lf_ed_toggle_mode = 1bits - lf_ed_threshold = 8bits threshold defaults to 127 - - You can send FPGA_CMD_SET_EDGE_DETECT_THRESHOLD to set a custom threshold - lf_ed_threshold = 8bits threshold value. - - conf_word 12bits - conf_word[7:5] = 3bit major mode. - conf_word[0] = 1bit lf_field - conf_word[1] = 1bit lf_ed_toggle_mode - conf_word[7:0] = 8bit divisor - conf_word[7:0] = 8bit threshold - -*/ -// Defining commands, modes and options. This must be aligned to the definitions in fpga/define.v -#define FPGA_MAJOR_MODE_MASK 0x01C0 -#define FPGA_MINOR_MODE_MASK 0x003F - -// Definitions for the FPGA commands. -#define FPGA_CMD_SET_CONFREG (1<<12) -#define FPGA_CMD_SET_DIVISOR (2<<12) -#define FPGA_CMD_SET_EDGE_DETECT_THRESHOLD (3<<12) -#define FPGA_CMD_TRACE_ENABLE (2<<12) - -// Major modes -#define FPGA_MAJOR_MODE_LF_READER (0<<6) -#define FPGA_MAJOR_MODE_LF_EDGE_DETECT (1<<6) -#define FPGA_MAJOR_MODE_LF_PASSTHRU (2<<6) -#define FPGA_MAJOR_MODE_LF_ADC (3<<6) - -#define FPGA_MAJOR_MODE_HF_READER (0<<6) -#define FPGA_MAJOR_MODE_HF_SIMULATOR (1<<6) -#define FPGA_MAJOR_MODE_HF_ISO14443A (2<<6) -#define FPGA_MAJOR_MODE_HF_SNIFF (3<<6) -#define FPGA_MAJOR_MODE_HF_ISO18092 (4<<6) -#define FPGA_MAJOR_MODE_HF_GET_TRACE (5<<6) -#define FPGA_MAJOR_MODE_OFF (7<<6) - -// Options for LF_READER -#define FPGA_LF_ADC_READER_FIELD ( 1 ) - -// Options for LF_EDGE_DETECT -#define FPGA_LF_EDGE_DETECT_READER_FIELD ( 1 ) -#define FPGA_LF_EDGE_DETECT_TOGGLE_MODE ( 2 ) - -// Options for the generic HF reader -#define FPGA_HF_READER_MODE_RECEIVE_IQ ( 0 ) -#define FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE ( 1 ) -#define FPGA_HF_READER_MODE_RECEIVE_PHASE ( 2 ) -#define FPGA_HF_READER_MODE_SEND_FULL_MOD ( 3 ) -#define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD ( 4 ) -#define FPGA_HF_READER_MODE_SNIFF_IQ ( 5 ) -#define FPGA_HF_READER_MODE_SNIFF_AMPLITUDE ( 6 ) -#define FPGA_HF_READER_MODE_SNIFF_PHASE ( 7 ) -#define FPGA_HF_READER_MODE_SEND_JAM ( 8 ) -#define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4 ( 9 ) - -#define FPGA_HF_READER_SUBCARRIER_848_KHZ (0<<4) -#define FPGA_HF_READER_SUBCARRIER_424_KHZ (1<<4) -#define FPGA_HF_READER_SUBCARRIER_212_KHZ (2<<4) -#define FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ (3<<4) - -// Options for the HF simulated tag, how to modulate -#define FPGA_HF_SIMULATOR_NO_MODULATION ( 0 ) -#define FPGA_HF_SIMULATOR_MODULATE_BPSK ( 1 ) -#define FPGA_HF_SIMULATOR_MODULATE_212K ( 2 ) -#define FPGA_HF_SIMULATOR_MODULATE_424K ( 4 ) -#define FPGA_HF_SIMULATOR_MODULATE_424K_8BIT ( 5 ) - -// Options for ISO14443A -#define FPGA_HF_ISO14443A_SNIFFER ( 0 ) -#define FPGA_HF_ISO14443A_TAGSIM_LISTEN ( 1 ) -#define FPGA_HF_ISO14443A_TAGSIM_MOD ( 2 ) -#define FPGA_HF_ISO14443A_READER_LISTEN ( 3 ) -#define FPGA_HF_ISO14443A_READER_MOD ( 4 ) - -// Options for ISO18092 / Felica -#define FPGA_HF_ISO18092_FLAG_NOMOD ( 1 ) // 0001 disable modulation module -#define FPGA_HF_ISO18092_FLAG_424K ( 2 ) // 0010 should enable 414k mode (untested). No autodetect -#define FPGA_HF_ISO18092_FLAG_READER ( 4 ) // 0100 enables antenna power, to act as a reader instead of tag - -void FpgaSendCommand(uint16_t cmd, uint16_t v); -void FpgaWriteConfWord(uint16_t v); -void FpgaEnableTracing(void); -void FpgaDisableTracing(void); -void FpgaDownloadAndGo(int bitstream_target); -void FpgaDownloadAndGo_keep_EM(int bitstream_target); -// void FpgaGatherVersion(int bitstream_target, char *dst, int len); -void FpgaSetupSsc(uint16_t fpga_mode); -void SetupSpi(int mode); -bool FpgaSetupSscDma(uint8_t *buf, uint16_t len); -void Fpga_print_status(void); -int FpgaGetCurrent(void); -void FpgaResetBitstream(void); -void SetAdcMuxFor(uint32_t whichGpio); - -// extern and generel turn off the antenna method -void switch_off(void); - -#endif diff --git a/armsrc/hfops.c b/armsrc/hfops.c index df42c8685..1c38ae73a 100644 --- a/armsrc/hfops.c +++ b/armsrc/hfops.c @@ -23,8 +23,10 @@ #include "proxmark3_arm.h" #include "cmd.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "ticks_apis.h" +#include "fpga_apis.h" +#include "rssi_apis.h" #include "dbprint.h" #include "util.h" #include "commonutil.h" @@ -35,7 +37,7 @@ int HfReadADC(uint32_t samplesCount, bool ledcontrol) { BigBuf_Clear_ext(false); // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // And put the FPGA in the appropriate mode @@ -55,8 +57,8 @@ int HfReadADC(uint32_t samplesCount, bool ledcontrol) { break; } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint16_t sample = AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + volatile uint16_t sample = FPGA_SSC_RX_Value(); // FPGA side: // corr_i_out <= {2'b00, corr_amplitude[13:8]}; @@ -216,7 +218,7 @@ int HfSimulateTkm(const uint8_t *uid, uint8_t modulation, uint32_t timeout) { LED_C_ON(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); @@ -238,7 +240,7 @@ int HfSimulateTkm(const uint8_t *uid, uint8_t modulation, uint32_t timeout) { break; // in mV - int vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; + uint32_t vHf = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF); if (vHf > MF_MINFIELDV) { if (field_on == false) { LED_A_ON(); @@ -256,8 +258,8 @@ int HfSimulateTkm(const uint8_t *uid, uint8_t modulation, uint32_t timeout) { SpinDelay(3); for (int i = 0; i < elen;) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - AT91C_BASE_SSC->SSC_THR = data[i++]; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(data[i++]); } } } diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index 939ee4319..8a941811f 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -18,8 +18,9 @@ #include "hfsnoop.h" #include "proxmark3_arm.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "ticks_apis.h" +#include "fpga_apis.h" #include "dbprint.h" #include "util.h" #include "fpga.h" @@ -28,8 +29,8 @@ static void RAMFUNC optimizedSniff(uint16_t *dest, uint16_t dsize) { while (dsize > 0) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - *dest = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); + if (FPGA_SSC_RX_Ready()) { + *dest = (uint16_t)(FPGA_SSC_RX_Value()); dest++; dsize -= sizeof(dsize); } @@ -40,8 +41,8 @@ static void RAMFUNC skipSniff(uint8_t *dest, uint16_t dsize, uint8_t skipMode, u uint32_t accum = (skipMode == HF_SNOOP_SKIP_MIN) ? 0xffffffff : 0; uint8_t ratioindx = 0; while (dsize > 0) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - volatile uint16_t val = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); + if (FPGA_SSC_RX_Ready()) { + volatile uint16_t val = (uint16_t)(FPGA_SSC_RX_Value()); switch (skipMode) { case HF_SNOOP_SKIP_MAX: if (accum < (val & 0xff)) @@ -95,13 +96,13 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len, uint FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); // Set up the synchronous serial port FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SNIFF); // Setting Frame Mode For better performance on high speed data transfer. - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16); + FpgaUpdateFrameMode(16, false, false); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SNIFF); SpinDelay(100); @@ -127,8 +128,8 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len, uint } // check if trigger is reached - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - r = (uint16_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + r = (uint16_t)FPGA_SSC_RX_Value(); r = MAX(r & 0xFF, r >> 8); @@ -149,7 +150,7 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len, uint // skip samples loop while (samplesToSkip != 0) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + if (FPGA_SSC_RX_Ready()) { samplesToSkip--; } } @@ -165,8 +166,8 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len, uint } } - //Resetting Frame mode (First set in fpgaloader.c) - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + // Resetting Frame mode (First set in FpgaSetupSsc() function) + FpgaUpdateFrameMode(8, true, true); LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); BigBuf_free(); @@ -182,25 +183,20 @@ void HfPlotDownload(void) { FpgaSetupSsc(FPGA_MAJOR_MODE_HF_GET_TRACE); - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; // Disable DMA Transfer - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) this_buf; // start transfer to this memory address - AT91C_BASE_PDC_SSC->PDC_RCR = PM3_CMD_DATA_SIZE; // transfer this many samples - ts->buf[0] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // clear receive register - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // Start DMA transfer + FpgaSetupSscRxDmaSingle(this_buf, PM3_CMD_DATA_SIZE); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_GET_TRACE); // let FPGA transfer its internal Block-RAM LED_B_ON(); for (size_t i = 0; i < FPGA_TRACE_SIZE; i += PM3_CMD_DATA_SIZE) { + size_t len = MIN(FPGA_TRACE_SIZE - i, PM3_CMD_DATA_SIZE); // prepare next DMA transfer: uint8_t *next_buf = ts->buf + ((i + PM3_CMD_DATA_SIZE) % (2 * PM3_CMD_DATA_SIZE)); - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)next_buf; - AT91C_BASE_PDC_SSC->PDC_RNCR = PM3_CMD_DATA_SIZE; + while (!FPGA_SSC_DMA_RX_Done()) {}; // wait for DMA transfer to complete - size_t len = MIN(FPGA_TRACE_SIZE - i, PM3_CMD_DATA_SIZE); - - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX))) {}; // wait for DMA transfer to complete + // The main buf has stopped receiving, so it needs to be refreshed. + FPGA_SSC_DMA_RX_Refresh_Single(next_buf, PM3_CMD_DATA_SIZE); reply_old(CMD_FPGAMEM_DOWNLOADED, i, len, FPGA_TRACE_SIZE, this_buf, len); this_buf = next_buf; diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index db71f9dce..477c43414 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -20,8 +20,9 @@ #include "proxmark3_arm.h" #include "cmd.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "fpga_apis.h" +#include "ticks_apis.h" #include "dbprint.h" #include "util.h" #include "lfadc.h" @@ -96,7 +97,6 @@ static uint8_t key_no; static uint64_t cipher_state; static int16_t blocknr; -static size_t flipped_bit = 0; static uint32_t byte_value = 0; static void hitag2_reset(void) { @@ -108,12 +108,11 @@ static void hitag2_init(void) { hitag2_reset(); } -// Sam7s has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS_TIMER_DIV1_CLOCK) -// TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz -// Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier) -// T0 = TIMER_CLOCK1 / 125000 = 192 +// The input-capture timer (StartInputCapture, see common_arm/ticks) runs at +// TIMER_CLOCK3 = MCK/32 = 1.5 MHz. Hitag units (T0) have a duration of 8 us +// (1/125000 s, the carrier period), so T0 = 1.5 MHz / 125 kHz = 12 counter ticks. #ifndef HITAG_T0 -#define HITAG_T0 192 +#define HITAG_T0 12 #endif #define HITAG_FRAME_LEN 20 @@ -147,50 +146,6 @@ static void hitag2_init(void) { #define HT2_MAX_NRSZ ((8 * HITAG_FRAME_LEN + 5) * 2) -/* -// sim -static void hitag_send_bit(int bit, bool ledcontrol) { - if (ledcontrol) LED_A_ON(); - - // Reset clock for the next bit - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - - // Fixed modulation, earlier proxmark version used inverted signal - // check datasheet if reader uses BiPhase? - if (bit == 0) { - // Manchester: Unloaded, then loaded |__--| - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_HALF_PERIOD); - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_FULL_PERIOD); - } else { - // Manchester: Loaded, then unloaded |--__| - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_HALF_PERIOD); - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_FULL_PERIOD); - } - if (ledcontrol) LED_A_OFF(); -} - -// sim -static void hitag_send_frame(const uint8_t *frame, size_t frame_len) { - // SOF - send start of frame - hitag_send_bit(1); - hitag_send_bit(1); - hitag_send_bit(1); - hitag_send_bit(1); - hitag_send_bit(1); - - // Send the content of the frame - for (size_t i = 0; i < frame_len; i++) { - hitag_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); - } - - // Drop the modulation - LOW(GPIO_SSC_DOUT); -} -*/ // sim static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) { @@ -1053,7 +1008,7 @@ void hitag_sniff(void) { // and analog mux selection. FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); // 125Khz - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + SetAdcMuxFor(ADC_MUXSEL_LOPKD); RELAY_OFF(); } @@ -1074,7 +1029,7 @@ void SniffHitag2(bool ledcontrol) { set_tracing(true); /* - lf_init(false, false, ledcontrol); + lf_init(LF_ADC_SNIFF, LF_ADC_WAV_REVERSED, ledcontrol); // no logging of the raw signal g_logging = true; @@ -1216,7 +1171,7 @@ void SniffHitag2(bool ledcontrol) { // and analog mux selection. FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); // 125Khz - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + SetAdcMuxFor(ADC_MUXSEL_LOPKD); RELAY_OFF(); // Configure output pin that is connected to the FPGA (for modulating) @@ -1224,30 +1179,16 @@ void SniffHitag2(bool ledcontrol) { // AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; // Disable modulation, we are going to eavesdrop, not modulate ;) -// LOW(GPIO_SSC_DOUT); +// Gpio_SSC_DOUT_Low(); - // Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the reader frames - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, - // external trigger rising edge, load RA on rising edge of TIOA. - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_BOTH | AT91C_TC_ABETRG | AT91C_TC_LDRA_BOTH; - - // Enable and reset counter - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - // Assert a sync signal. This sets all timers to 0 on next active clock edge - AT91C_BASE_TCB->TCB_BCR = 1; + // Configure the input capture (TC1) via the HAL: it enables the TC1 clock, routes + // SSC_FRAME to the timer input, resets on each falling edge and captures RB + // (falling->falling) / RA (falling->rising). + StartLoEdgeCapture(); int frame_count = 0, response = 0, lastbit = 1, tag_sof = 4; - int overflow = 0; - bool rising_edge, reader_frame = false, bSkip = true; + bool reader_frame = false, bSkip = true; -// bool exit_due_to_overflow; // HACK -- add one byte to avoid rewriting manchester decoder for edge case uint8_t rx[HITAG_FRAME_LEN + 1] = {0}; size_t rxlen = 0; @@ -1261,57 +1202,45 @@ void SniffHitag2(bool ledcontrol) { WDT_HIT(); -// bool exit_due_to_overflow = false; + // Receive frame, watch for at most HITAG_T0 * HITAG_T_EOF periods since the last edge + while (GetLoEdgeCaptureCount() < (HITAG_T0 * HITAG_T_EOF)) { - // Receive frame, watch for at most T0 * EOF periods - while (AT91C_BASE_TC1->TC_CV < (HITAG_T0 * HITAG_T_EOF)) { + // Read (and clear) the input-capture edge-event flags. + lo_edge_t lo_edge = GetLoEdgeCaptureStatus(); - // Check if rising edge in modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Rising edge: RA holds the falling->rising sub-period (the reader's tlow). + if (lo_edge == LO_EDGE_RISING) { + int ra = GetLoEdgeCaptureRising() / HITAG_T0; - // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / HITAG_T0) + overflow; - overflow = 0; - - // Find out if we are dealing with a rising or falling edge - rising_edge = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME) > 0; - - // Shorter periods will only happen with reader frames - if (reader_frame == false && rising_edge && ra < HITAG_T_TAG_CAPTURE_ONE_HALF) { + // Shorter periods only happen with reader frames (reader tlow is 4..10 T0, + // while the shortest tag Manchester half-period is 16 T0). + if (reader_frame == false && ra < HITAG_T_TAG_CAPTURE_ONE_HALF) { // Switch from tag to reader capture if (ledcontrol) LED_C_OFF(); reader_frame = true; rxlen = 0; } + } - // Only handle if reader frame and rising edge, or tag frame and falling edge - if (reader_frame != rising_edge) { - overflow += ra; - continue; - } - - // Add the buffered timing values of earlier captured edges which were skipped - ra += overflow; - overflow = 0; + // Falling edge: RB holds the falling->falling full period (the bit timing). + if (lo_edge == LO_EDGE_FALLING) { + int rb = GetLoEdgeCaptureFalling() / HITAG_T0; if (reader_frame) { if (ledcontrol) LED_B_ON(); // Capture reader frame - if (ra >= HITAG_T_STOP) { -// if (rxlen != 0) { - //DbpString("wierd0?"); -// } + if (rb >= HITAG_T_STOP) { // Capture the T0 periods that have passed since last communication or field drop (reset) - response = (ra - HITAG_T_LOW); - if (rxlen != 0) { Dbprintf("ra - HITAG_T_LOW... %i", response); } + response = (rb - HITAG_T_LOW); + if (rxlen != 0) { Dbprintf("rb - HITAG_T_LOW... %i", response); } - } else if (ra >= HITAG_T_1_MIN) { + } else if (rb >= HITAG_T_1_MIN) { // '1' bit rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); rxlen++; - } else if (ra >= HITAG_T_0_MIN) { + } else if (rb >= HITAG_T_0_MIN) { // '0' bit rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); rxlen++; @@ -1320,22 +1249,19 @@ void SniffHitag2(bool ledcontrol) { } else { if (ledcontrol) LED_C_ON(); // Capture tag frame (manchester decoding using only falling edges) - if (ra >= HITAG_T_EOF) { -// if (rxlen != 0) { - //DbpString("wierd1?"); -// } + if (rb >= HITAG_T_EOF) { // Capture the T0 periods that have passed since last communication or field drop (reset) // We always receive a 'one' first, which has the falling edge after a half period |-_| - response = ra - HITAG_T_TAG_HALF_PERIOD; + response = rb - HITAG_T_TAG_HALF_PERIOD; - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { + } else if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { // Manchester coding example |-_|_-|-_| (101) rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); rxlen++; rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); rxlen++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { + } else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF) { // Manchester coding example |_-|...|_-|-_| (0...01) rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); rxlen++; @@ -1347,7 +1273,7 @@ void SniffHitag2(bool ledcontrol) { lastbit = !lastbit; bSkip = !bSkip; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { + } else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF) { // Manchester coding example |_-|_-| (00) or |-_|-_| (11) if (tag_sof) { // Ignore bits that are transmitted during SOF @@ -1387,32 +1313,24 @@ void SniffHitag2(bool ledcontrol) { lastbit = 1; bSkip = true; tag_sof = 4; - overflow = 0; if (ledcontrol) { LED_B_OFF(); LED_C_OFF(); } - } else { - // Save the timer overflow, will be 0 when frame was received - overflow += (AT91C_BASE_TC1->TC_CV / HITAG_T0); } // Reset the frame length rxlen = 0; - // Reset the timer to restart while-loop that receives frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; - - // Assert a sync signal. This sets all timers to 0 on next active clock edge - AT91C_BASE_TCB->TCB_BCR = 1; + // Reset the capture counter to restart the while-loop that receives frames. + ResetLoEdgeCapture(); } if (ledcontrol) LEDsoff(); - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + StopLoEdgeCapture(); DBG Dbprintf("frames.......... %d", frame_count); Dbprintf("Auth attempts... %d", (auth_table_len / 8)); @@ -1430,7 +1348,7 @@ void SimulateHitag2(bool ledcontrol) { set_tracing(true); // empties bigbuff etc - lf_init(false, true, ledcontrol); + lf_init(LF_ADC_TAG_SIM, LF_ADC_WAV_REVERSED, ledcontrol); int response = 0; uint8_t rx[HITAG_FRAME_LEN] = {0}; @@ -1619,7 +1537,6 @@ void SimulateHitag2(bool ledcontrol) { // Send and store the tag answer (if there is any) if (txlen) { // Transmit the tag frame - //hitag_send_frame(tx, txlen); lf_manchester_send_bytes(tx, txlen, ledcontrol); // Store the frame in the trace @@ -1777,7 +1694,6 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { t_wait_1 = 204; t_wait_2 = 128; tag_size = 256; - flipped_bit = 0; DBG DbpString("Configured for " _YELLOW_("Hitag 1") " reader"); } else if (payload->cmd <= HT2_LAST_CMD) { // hitag 2 settings @@ -1788,7 +1704,7 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { } // init as reader - lf_init(true, false, ledcontrol); + lf_init(LF_ADC_READER, LF_ADC_WAV_REVERSED, ledcontrol); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); uint8_t tag_modulation; @@ -2127,7 +2043,7 @@ void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol) { hitag2_init(); // init as reader - lf_init(true, false, ledcontrol); + lf_init(LF_ADC_READER, LF_ADC_WAV_REVERSED, ledcontrol); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // Tag specific configuration settings (sof, timings, etc.) @@ -2147,7 +2063,6 @@ void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol) { t_wait_1 = 204; t_wait_2 = 128; tag_size = 256; - flipped_bit = 0; DBG DbpString("Configured for " _YELLOW_("Hitag 1") " writer"); } else if (payload->cmd <= HT2_LAST_CMD) { // hitag 2 settings @@ -2591,6 +2506,7 @@ bool ht2_packbits(uint8_t *nrz_samples, size_t nrzs, uint8_t *rx, size_t *rxlen) } return true; } + int ht2_read_uid(uint8_t *uid, bool ledcontrol, bool send_answer, bool keep_field_up) { g_logging = false; @@ -2600,12 +2516,11 @@ int ht2_read_uid(uint8_t *uid, bool ledcontrol, bool send_answer, bool keep_fiel clear_trace(); } - // hitag 2 state machine? hitag2_init(); // init as reader - lf_init(true, false, true); + lf_init(LF_ADC_READER, LF_ADC_WAV_REVERSED, true); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -2642,7 +2557,7 @@ int ht2_read_uid(uint8_t *uid, bool ledcontrol, bool send_answer, bool keep_fiel // receive raw samples if (ht2_receive(&response_start, &response_duration, nrz_samples, &nrzs) == false) { - continue;; + continue; } // Store the transmit frame ( TX ), we do this now at this point, to avoid delay in processing diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index b1afc9f49..d5b996d57 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -24,8 +24,9 @@ #include "proxmark3_arm.h" #include "cmd.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "fpga_apis.h" +#include "ticks_apis.h" #include "dbprint.h" #include "util.h" #include "string.h" @@ -56,7 +57,7 @@ static int block_data_left = 0; static bool enable_page_tearoff = false; static uint8_t protocol_mode = HITAGS_UID_REQ_ADV1; -static MOD m = AC2K; // used modulation +static hitag_mod_t m = AC2K; // used modulation static uint32_t reader_selected_uid; static int rotate_uid = 0; static int sof_bits; // number of start-of-frame bits @@ -423,7 +424,7 @@ void hts_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, LogTraceBits(rx, rxlen, start_time, TIMESTAMP, true); // Disable timer 1 with external trigger to avoid triggers during our own modulation - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + StopLoEdgeCapture(); // Process the incoming frame (rx) and prepare the outgoing frame (tx) hts_handle_reader_command(rx, rxlen, tx, &txlen); @@ -433,7 +434,7 @@ void hts_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, // with respect to the falling edge, we need to wait actually (T_Wait1 - T_Low) // periods. The gap time T_Low varies (4..10). All timer values are in // terms of T0 units - while (AT91C_BASE_TC0->TC_CV < T0 * (HITAG_T_WAIT_RESP - HITAG_T_LOW)) {}; + while (GetPrecisionCounter() < T0 * (HITAG_T_WAIT_RESP - HITAG_T_LOW)) {}; // Send and store the tag answer (if there is any) if (txlen > 0) { @@ -444,7 +445,7 @@ void hts_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, } // Enable and reset external trigger in timer for capturing future frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + EnableLoEdgeCapture(); // Reset the received frame and response timing info memset(rx, 0x00, sizeof(rx)); @@ -454,9 +455,9 @@ void hts_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, // Reset the frame length rxlen = 0; // Save the timer overflow, will be 0 when frame was received - overflow += (AT91C_BASE_TC1->TC_CV / T0); + overflow += (GetLoEdgeCaptureCount() / T0); // Reset the timer to restart while-loop that receives frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + ResetLoEdgeCapture(); } @@ -472,7 +473,7 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t // Send and store the reader command // Disable timer 1 with external trigger to avoid triggers during our own modulation - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + StopLoEdgeCapture(); DBG Dbprintf("tx %d bits:", txlen); DBG Dbhexdump((txlen + 7) / 8, tx, false); @@ -482,7 +483,7 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t // falling edge occurred halfway the period. with respect to this falling edge, // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. // All timer values are in terms of T0 units - while (AT91C_BASE_TC0->TC_CV < T0 * t_wait) {}; + while (GetPrecisionCounter() < T0 * t_wait) {}; start_time = TIMESTAMP; @@ -496,7 +497,7 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t LogTraceBits(tx, txlen, start_time, TIMESTAMP, true); // Enable and reset external trigger in timer for capturing future frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + EnableLoEdgeCapture(); hts_set_frame_modulation(protocol_mode, ac_seq); diff --git a/armsrc/hitag_common.c b/armsrc/hitag_common.c index e5af48f27..5e9c3b046 100644 --- a/armsrc/hitag_common.c +++ b/armsrc/hitag_common.c @@ -21,8 +21,9 @@ #include "proxmark3_arm.h" #include "cmd.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "fpga_apis.h" +#include "ticks_apis.h" #include "dbprint.h" #include "util.h" #include "string.h" @@ -33,56 +34,6 @@ #include "protocols.h" #include "appmain.h" // tearoff_hook() -uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, combined with TC2 counter for ~47min timing - -static void hitag_stop_clock(void) { - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; -} - -static void hitag_init_clock(void) { - // Enable Peripheral Clock for - // Timer Counter 0, used to measure exact timing before answering - // Timer Counter 1, used to capture edges of the tag frames - // Timer Counter 2, used to log trace time - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2); - - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - hitag_stop_clock(); - - // TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - - // TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger, - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK // use MCK/32 (TIMER_CLOCK3) - | AT91C_TC_ABETRG // TIOA is used as an external trigger - | AT91C_TC_ETRGEDG_FALLING // external trigger on falling edge - | AT91C_TC_LDRA_RISING // load RA on on rising edge of TIOA - | AT91C_TC_LDRB_FALLING; // load RB on on falling edge of TIOA - - // TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers - AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - - // Enable and reset counters - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - // Assert a sync signal. This sets all timers to 0 on next active clock edge - AT91C_BASE_TCB->TCB_BCR = 1; - - // synchronized startup procedure - // In theory, with MCK/32, we shouldn't be waiting longer than 32 instruction statements, right? - while (AT91C_BASE_TC0->TC_CV != 0) { - }; // wait until TC0 returned to zero - - // reset timestamp - timestamp_high = 0; -} - // Initialize FPGA and timer for Hitag operations void hitag_setup_fpga(uint16_t conf, uint8_t threshold, bool ledcontrol) { StopTicks(); @@ -95,25 +46,30 @@ void hitag_setup_fpga(uint16_t conf, uint8_t threshold, bool ledcontrol) { if (ledcontrol) LED_D_ON(); - hitag_init_clock(); + // Configure the timers via the HAL: a precision counter (T0 timing), an + // input capture (tag frame edges) and a timestamp counter (trace timing). + StartPrecisionCounter(); + StartLoEdgeCapture(); + StartTimestamp(); // Set fpga in edge detect with/without reader field, we can modulate as reader/tag now FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | conf); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz if (threshold != 127) FpgaSendCommand(FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, threshold); - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + SetAdcMuxFor(ADC_MUXSEL_LOPKD); // Configure output and enable pin that is connected to the FPGA (for modulating) - AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT; + gpio_fpga_mod_only_setup(); // Disable modulation at default, which means enable the field - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); } // Clean up and finalize Hitag operations void hitag_cleanup(bool ledcontrol) { - hitag_stop_clock(); + StopPrecisionCounter(); + StopLoEdgeCapture(); + StopTimestamp(); set_tracing(false); lf_finalize(ledcontrol); } @@ -121,26 +77,25 @@ void hitag_cleanup(bool ledcontrol) { // Reader functions static void hitag_reader_send_bit(int bit, bool ledcontrol) { // Reset clock for the next bit - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV != 0) {}; + ResetPrecisionCounter(); if (ledcontrol) LED_A_ON(); // Binary puls length modulation (BPLM) is used to encode the data stream // This means that a transmission of a one takes longer than that of a zero - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); // Wait for 4-10 times the carrier period - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {}; + while (GetPrecisionCounter() < T0 * HITAG_T_LOW) {}; - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); if (bit == 0) { // Zero bit: |_-| - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_0) {}; + while (GetPrecisionCounter() < T0 * HITAG_T_0) {}; } else { // One bit: |_--| - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_1) {}; + while (GetPrecisionCounter() < T0 * HITAG_T_1) {}; } if (ledcontrol) LED_A_OFF(); @@ -152,18 +107,17 @@ void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcon hitag_reader_send_bit(0, ledcontrol); // Reset clock for the code violation - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV != 0) {}; + ResetPrecisionCounter(); if (ledcontrol) LED_A_ON(); // SOF is HIGH for HITAG_T_LOW - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * HITAG_T_LOW) {}; // Then LOW for HITAG_T_CODE_VIOLATION - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_CODE_VIOLATION) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * HITAG_T_CODE_VIOLATION) {}; if (ledcontrol) LED_A_OFF(); } @@ -174,19 +128,18 @@ void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcon } // Send EOF - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV != 0) {}; + ResetPrecisionCounter(); - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); // Wait for 4-10 times the carrier period - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {}; + while (GetPrecisionCounter() < T0 * HITAG_T_LOW) {}; - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); } void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol, - MOD modulation, int sof_bits) { + hitag_mod_t modulation, int sof_bits) { // Reset values for receiving frames memset(rx, 0x00, sizeofrx); *rxlen = 0; @@ -195,7 +148,7 @@ void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uin bool bSkip = true; uint32_t errorCount = 0; bool bStarted = false; - uint16_t next_edge_event = AT91C_TC_LDRBS; + lo_edge_t next_edge = LO_EDGE_FALLING; int double_speed = (modulation == AC4K || modulation == MC8K) ? 2 : 1; uint32_t rb_i = 0; @@ -205,22 +158,26 @@ void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uin bool sof_received = false; // Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods - while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) { + while (GetPrecisionCounter() < (T0 * HITAG_T_PROG_MAX)) { // Check if edge in tag modulation is detected - if (AT91C_BASE_TC1->TC_SR & next_edge_event) { - next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS); + if (GetLoEdgeCaptureStatus() == next_edge) { + next_edge = next_edge == LO_EDGE_RISING ? LO_EDGE_FALLING : LO_EDGE_RISING; - // only use AT91C_TC_LDRBS falling edge for now - if (next_edge_event == AT91C_TC_LDRBS) { + // only use INPUT_CAPTURE_EVT_RB falling edge for now + if (next_edge == LO_EDGE_FALLING) { continue; } // Retrieve the new timing values - uint32_t rb = AT91C_BASE_TC1->TC_RB / T0; - edges[rb_i++] = rb; + uint32_t rb = GetLoEdgeCaptureFalling() / T0; + + // For debug, save the edges for decoding manual + if (rb_i < sizeof(edges)) { + edges[rb_i++] = rb; + } // Reset timer every frame, we have to capture the last edge for timing - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + ResetPrecisionCounter(); if (ledcontrol) LED_B_INV(); @@ -342,7 +299,7 @@ void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uin // max periods between 2 falling edge // RTF AC64 |--__|--__| (00) 64 * T0 // RTF MC32 |_-|-_|_-| (010) 48 * T0 - if (AT91C_BASE_TC1->TC_CV > (T0 * 80)) { + if (GetLoEdgeCaptureCount() > (T0 * 80)) { if (bStarted) { break; } @@ -350,15 +307,75 @@ void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uin } DBG { - Dbprintf("RX %i:%02X.. resptime:%i edges:", *rxlen, rx[0], *resptime); + Dbprintf("bStarted:%d bSkip:%d lastbit:%d sof_received:%d", bStarted, bSkip, lastbit, sof_received); + Dbprintf("RX %i:%02X.. resptime:%i", *rxlen, rx[0], *resptime); + Dbprintf("Edges count: %d, hex: ", rb_i); Dbhexdump(rb_i, edges, false); } } +int hitag_reader_transfer(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen, int t_wait, + bool ledcontrol, hitag_mod_t modulation, uint8_t sof_bits, uint8_t send_sof) { + uint32_t start_time = 0; + + DBG Dbprintf("tx %d bits:", txlen); + DBG Dbhexdump((txlen + 7) / 8, tx, false); + + // Disable input capture to avoid triggers during our own modulation. + StopLoEdgeCapture(); + + // Wait for HITAG_T_WAIT_SC carrier periods after the last tag bit before transmitting, + // Since the clock counts since the last falling edge, a 'one' means that the + // falling edge occurred halfway the period. with respect to this falling edge, + // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. + // All timer values are in terms of T0 units + while (GetPrecisionCounter() < T0 * t_wait) {}; + + start_time = TIMESTAMP; + + // Transmit the reader frame + hitag_reader_send_frame(tx, txlen, ledcontrol, send_sof); + + // tearoff + if (g_tearoff_enabled && tearoff_hook() == PM3_ETEAROFF) { + return PM3_ETEAROFF; + } + + LogTraceBits(tx, txlen, start_time, TIMESTAMP, true); + + // Enable and reset input capture for capturing the tag response. + EnableLoEdgeCapture(); + + hitag_reader_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol, modulation, sof_bits); + + DBG Dbprintf("rx %d bits:", *rxlen); + DBG Dbhexdump((int)(*rxlen + 7) / 8, rx, false); + + // Check if frame was captured and store it + if (*rxlen > 0) { + DBG { + uint8_t response_bit[sizeofrx * 8]; + + for (size_t i = 0; i < *rxlen; i++) { + response_bit[i] = (rx[i / 8] >> (7 - (i % 8))) & 1; + } + + Dbprintf("ht?: rxlen...... %zu", *rxlen); + Dbprintf("ht?: sizeofrx... %zu", sizeofrx); + DbpString("ht?: response_bit:"); + Dbhexdump((int) *rxlen, response_bit, false); + } + + LogTraceBits(rx, *rxlen, start_time, TIMESTAMP, false); + } + + return PM3_SUCCESS; +} + // Tag functions - depends on modulation type -static void hitag_tag_send_bit(int bit, MOD modulation, bool ledcontrol) { +static void hitag_tag_send_bit(int bit, hitag_mod_t modulation, bool ledcontrol) { // Reset clock for the next bit - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + ResetPrecisionCounter(); if (ledcontrol) LED_A_ON(); @@ -366,84 +383,84 @@ static void hitag_tag_send_bit(int bit, MOD modulation, bool ledcontrol) { case AC2K: { if (bit == 0) { // AC Coding --__ - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * 32) {}; - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 64) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * 64) {}; } else { // AC coding -_-_ - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * 16) {}; - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * 32) {}; - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 48) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * 48) {}; - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 64) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * 64) {}; } break; } case AC4K: { if (bit == 0) { // AC Coding --__ - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * HITAG_T_TAG_HALF_PERIOD) {}; - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * HITAG_T_TAG_FULL_PERIOD) {}; } else { // AC coding -_-_ - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * 8) {}; - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * 16) {}; - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 24) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * 24) {}; - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * 32) {}; } break; } case MC4K: { if (bit == 0) { // Manchester: Unloaded, then loaded |__--| - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * 16) {}; - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * 32) {}; } else { // Manchester: Loaded, then unloaded |--__| - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * 16) {}; - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * 32) {}; } break; } case MC8K: { if (bit == 0) { // Manchester: Unloaded, then loaded |__--| - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * 8) {}; - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * 16) {}; } else { // Manchester: Loaded, then unloaded |--__| - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; + Gpio_SSC_DOUT_High(); + while (GetPrecisionCounter() < T0 * 8) {}; - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + Gpio_SSC_DOUT_Low(); + while (GetPrecisionCounter() < T0 * 16) {}; } break; } @@ -453,22 +470,24 @@ static void hitag_tag_send_bit(int bit, MOD modulation, bool ledcontrol) { } void hitag_tag_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *start_time, bool ledcontrol, int *overflow) { - uint16_t next_edge_event = AT91C_TC_LDRBS; + lo_edge_t next_edge = LO_EDGE_FALLING; uint8_t edges[160] = {0}; uint32_t rb_i = 0; // Receive frame, watch for at most T0*EOF periods - while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) { + while (GetLoEdgeCaptureCount() < T0 * HITAG_T_EOF) { // Check if edge in modulation is detected - if (AT91C_BASE_TC1->TC_SR & next_edge_event) { - next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS); + if (GetLoEdgeCaptureStatus() == next_edge) { + next_edge = next_edge == LO_EDGE_RISING ? LO_EDGE_FALLING : LO_EDGE_RISING; - // only use AT91C_TC_LDRBS falling edge for now - if (next_edge_event == AT91C_TC_LDRBS) continue; + // only use INPUT_CAPTURE_EVT_RB falling edge for now + if (next_edge == LO_EDGE_FALLING) { + continue; + } // Retrieve the new timing values - uint32_t rb = AT91C_BASE_TC1->TC_RB / T0 + *overflow; + uint32_t rb = GetLoEdgeCaptureFalling() / T0 + *overflow; *overflow = 0; edges[rb_i++] = rb; @@ -510,20 +529,20 @@ void hitag_tag_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32 } } -void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, MOD modulation, bool ledcontrol) { +void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, hitag_mod_t modulation, bool ledcontrol) { // The beginning of the frame is hidden in some high level; pause until our bits will have an effect - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - HIGH(GPIO_SSC_DOUT); + ResetPrecisionCounter(); + Gpio_SSC_DOUT_High(); switch (modulation) { case AC4K: case MC8K: { - while (AT91C_BASE_TC0->TC_CV < T0 * 40) {}; // FADV + while (GetPrecisionCounter() < T0 * 40) {}; // FADV break; } case AC2K: case MC4K: { - while (AT91C_BASE_TC0->TC_CV < T0 * 20) {}; // STD + ADV + while (GetPrecisionCounter() < T0 * 20) {}; // STD + ADV break; } } @@ -543,5 +562,5 @@ void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, hitag_tag_send_bit(TEST_BIT_MSB(frame, i), modulation, ledcontrol); } - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); } diff --git a/armsrc/hitag_common.h b/armsrc/hitag_common.h index 16a966240..11830c599 100644 --- a/armsrc/hitag_common.h +++ b/armsrc/hitag_common.h @@ -26,7 +26,9 @@ #define HITAG_T_WAIT_RESP 200 /* T_wresp should be 204..212 */ #define HITAG_T_WAIT_SC 200 /* T_wsc should be 90..5000 */ +// Read/Write Device waiting time before sending the first command #define HITAG_T_WAIT_FIRST 300 /* T_wfc should be 280..565 (T_ttf) */ +// HITAG S Transponder programming time #define HITAG_T_PROG_MAX 750 /* T_prog should be 716..726 */ #define HITAG_T_TAG_ONE_HALF_PERIOD 10 @@ -42,16 +44,18 @@ #define HITAG_T_TAG_CAPTURE_THREE_HALF 41 #define HITAG_T_TAG_CAPTURE_FOUR_HALF 57 -extern uint16_t timestamp_high; -#define TIMESTAMP ( (AT91C_BASE_TC2->TC_SR & AT91C_TC_COVFS) ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0) +// Trace timestamp in T0 units, provided by the timers HAL (GetTimestamp). +#define TIMESTAMP GetTimestamp() // Common hitag functions void hitag_setup_fpga(uint16_t conf, uint8_t threshold, bool ledcontrol); void hitag_cleanup(bool ledcontrol); void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol, bool send_sof); -void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol, MOD modulation, +void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol, hitag_mod_t modulation, int sof_bits); +int hitag_reader_transfer(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen, int t_wait, + bool ledcontrol, hitag_mod_t modulation, uint8_t sof_bits, uint8_t send_sof); void hitag_tag_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *start_time, bool ledcontrol, int *overflow); -void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, MOD modulation, bool ledcontrol); +void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, hitag_mod_t modulation, bool ledcontrol); #endif diff --git a/armsrc/hitagu.c b/armsrc/hitagu.c index 34e337803..cb67cd31f 100644 --- a/armsrc/hitagu.c +++ b/armsrc/hitagu.c @@ -14,6 +14,10 @@ // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- // Low frequency HITAG µ (micro) functions +// HitagU. There are two frequency models, of which the 125kHz model has been discontinued. +// 125k clock: https://www.nxp.com/products/no-longer-manufactured/hitag-%CE%BC-iso18000-2-transponder-ic:HTMS8301FTK +// 134k clock: https://www.nxp.com/products/rfid-nfc/hitag-lf/hitag-%C2%B5-advanced-advanced-plus:HTMS1X01_HTMS8X01 +//----------------------------------------------------------------------------- #include "hitagu.h" #include "hitag_common.h" @@ -24,19 +28,19 @@ #include "commonutil.h" #include "crc16.h" #include "dbprint.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "hitag2/hitag2_crypto.h" -#include "lfadc.h" #include "protocols.h" #include "proxmark3_arm.h" #include "string.h" -#include "ticks.h" +#include "ticks_apis.h" #include "util.h" // Hitag µ specific definitions #define HTU_SOF_BITS 4 // Start of frame bits is always 3 for Hitag µ (110) plus 1 bit error flag -MOD M = MC4K; // Modulation type +hitag_mod_t M = MC4K; // Modulation type // Structure to hold the state of the Hitag µ tag static struct hitagU_tag tag = { @@ -414,7 +418,7 @@ void htu_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, LogTraceBits(rx, rxlen, start_time, TIMESTAMP, true); // Disable timer 1 with external trigger to avoid triggers during our own modulation - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + StopLoEdgeCapture(); // Prepare tag response (tx) memset(tx, 0x00, sizeof(tx)); @@ -428,7 +432,7 @@ void htu_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, // with respect to the falling edge, we need to wait actually (T_Wait1 - T_Low) // periods. The gap time T_Low varies (4..10). All timer values are in // terms of T0 units - while (AT91C_BASE_TC0->TC_CV < T0 * (HITAG_T_WAIT_RESP - HITAG_T_LOW)) { + while (GetPrecisionCounter() < T0 * (HITAG_T_WAIT_RESP - HITAG_T_LOW)) { }; // Send and store the tag answer (if there is any) @@ -440,7 +444,7 @@ void htu_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, } // Enable and reset external trigger in timer for capturing future frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + EnableLoEdgeCapture(); // Reset the received frame and response timing info memset(rx, 0x00, sizeof(rx)); @@ -449,9 +453,9 @@ void htu_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, // Reset the frame length rxlen = 0; // Save the timer overflow, will be 0 when frame was received - overflow += (AT91C_BASE_TC1->TC_CV / T0); + overflow += (GetLoEdgeCaptureCount() / T0); // Reset the timer to restart while-loop that receives frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + ResetLoEdgeCapture(); } hitag_cleanup(ledcontrol); @@ -470,14 +474,13 @@ static int htu_reader_send_receive(uint8_t *tx, size_t txlen, uint8_t *rx, size_ memset(rx, 0x00, sizeofrx); // Disable timer 1 with external trigger to avoid triggers during our own modulation - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + StopLoEdgeCapture(); DBG Dbprintf("tx %d bits:", txlen); DBG Dbhexdump((txlen + 7) / 8, tx, false); // Wait until we can send the command - while (AT91C_BASE_TC0->TC_CV < T0 * t_wait) { - }; + while (GetPrecisionCounter() < T0 * t_wait) {}; // Set up tracing uint32_t start_time = TIMESTAMP; @@ -492,7 +495,7 @@ static int htu_reader_send_receive(uint8_t *tx, size_t txlen, uint8_t *rx, size_ LogTraceBits(tx, txlen, start_time, TIMESTAMP, true); // Enable and reset external trigger in timer for capturing future frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + EnableLoEdgeCapture(); // Capture response - SOF is automatically stripped by hitag_reader_receive_frame hitag_reader_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol, modulation, sof_bits); diff --git a/armsrc/i2c.c b/armsrc/i2c.c index 4df227d18..1fa73fe17 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -20,38 +20,37 @@ #include "proxmark3_arm.h" #include "cmd.h" #include "BigBuf.h" -#include "ticks.h" +#include "ticks_apis.h" #include "dbprint.h" #include "util.h" #include "string.h" -#define GPIO_RST AT91C_PIO_PA1 -#define GPIO_SCL AT91C_PIO_PA5 -#define GPIO_SDA AT91C_PIO_PA7 +#define SCL_H Gpio_I2C_SCL_High() +#define SCL_L Gpio_I2C_SCL_Low() +#define SDA_H Gpio_I2C_SDA_High() +#define SDA_L Gpio_I2C_SDA_Low() +#define RST_H Gpio_I2C_RST_High() +#define RST_L Gpio_I2C_RST_Low() -#define SCL_H HIGH(GPIO_SCL) -#define SCL_L LOW(GPIO_SCL) -#define SDA_H HIGH(GPIO_SDA) -#define SDA_L LOW(GPIO_SDA) - -#define SCL_read ((AT91C_BASE_PIOA->PIO_PDSR & GPIO_SCL) == GPIO_SCL) -#define SDA_read ((AT91C_BASE_PIOA->PIO_PDSR & GPIO_SDA) == GPIO_SDA) +#define SCL_read Gpio_I2C_SCL_Read() +#define SDA_read Gpio_I2C_SDA_Read() #define I2C_ERROR "I2C_WaitAck Error" -// Direct use the loop to delay. 6 instructions loop, Masterclock 48MHz, // delay=1 is about 200kbps -// timer. -// I2CSpinDelayClk(4) = 12.31us -// I2CSpinDelayClk(1) = 3.07us -static volatile uint32_t c; -static void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { - for (c = delay * 2; c; c--) {}; -} +// I2CSpinDelayClk(4) about 12us +// I2CSpinDelayClk(1) about 3us +// static void I2CSpinDelayClk(const uint16_t delay) { +// for (uint16_t i = 0; i < delay; i++) { +// SpinDelayUsPrecision(2); +// } +// } -#define I2C_DELAY_1CLK I2CSpinDelayClk(1) -#define I2C_DELAY_2CLK I2CSpinDelayClk(2) -#define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x)) +// TODO DXL 修改了速度到比较慢的情况,测完需要改回来,原先是2和4 + +#define I2C_DELAY_1CLK SpinDelayUsPrecision(20) +#define I2C_DELAY_2CLK SpinDelayUsPrecision(22) +// #define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x)) // try i2c bus recovery at 100kHz = 5us high, 5us low void I2C_recovery(void) { @@ -89,19 +88,7 @@ void I2C_recovery(void) { } void I2C_init(bool has_ticks) { - // Configure reset pin, close up pull up, push-pull output, default high - AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST; - AT91C_BASE_PIOA->PIO_MDDR = GPIO_RST; - - // Configure I2C pin, open up, open leakage - AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_SCL | GPIO_SDA); - AT91C_BASE_PIOA->PIO_MDER |= (GPIO_SCL | GPIO_SDA); - - // default three lines all pull up - AT91C_BASE_PIOA->PIO_SODR |= (GPIO_SCL | GPIO_SDA | GPIO_RST); - - AT91C_BASE_PIOA->PIO_OER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); - AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); + gpio_sw_i2c_rst_setup(); if (has_ticks) { WaitMS(2); @@ -115,19 +102,19 @@ void I2C_init(bool has_ticks) { // set the reset state void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) { if (LineRST) - HIGH(GPIO_RST); + RST_H; else - LOW(GPIO_RST); + RST_L; if (LineSCK) - HIGH(GPIO_SCL); + SCL_H; else - LOW(GPIO_SCL); + SCL_L; if (LineSDA) - HIGH(GPIO_SDA); + SDA_H; else - LOW(GPIO_SDA); + SDA_L; } // Reset the SIM_Adapter, then enter the main program @@ -594,6 +581,84 @@ int16_t I2C_BufferRead(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t return readcount - 2; } +// read one array of data (Data array, Readout length, command to be written , SlaveDevice address ). +// len = uint16 because we need to read up to 256bytes +// No data process logic, only raw rx. +int16_t I2C_BufferReadRaw(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address) { + + // sanity check + if (data == NULL || len == 0) { + return 0; + } + +// uint8_t *pd = data; + + // extra wait 500us (514us measured) + // 200us (xx measured) + WaitUS(600); + + bool _break = true; + + do { + if (I2C_Start() == false) { + return 0; + } + + // 0xB0 / 0xC0 == i2c write + I2C_SendByte(device_address & 0xFE); + if (I2C_WaitAck() == false) { + break; + } + + I2C_SendByte(device_cmd); + if (I2C_WaitAck() == false) { + break; + } + + // 0xB1 / 0xC1 == i2c read + I2C_Start(); + I2C_SendByte(device_address | 1); + if (I2C_WaitAck() == false) { + break; + } + + _break = false; + } while (false); + + if (_break) { + I2C_Stop(); + if (g_dbglevel > DBG_DEBUG) DbpString(I2C_ERROR); + return 0; + } + + int16_t count = 0; + + while (len) { + int16_t tmp = I2C_ReadByte(); + if (tmp < 0) { + return tmp; + } + + data[count] = (uint8_t)tmp & 0xFF; + len--; + count++; + + // acknowledgements. After last byte send NACK. + if (len == 0) { + I2C_NoAck(); + } else { + I2C_Ack(); + } + } + + I2C_Stop(); + +// Dbprintf("rec len... %u count... %u", recv_len, count); +// Dbhexdump(count, data, false); + + return count; +} + int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { //START, 0xB0, 0x00, 0x00, START, 0xB1, xx, yy, zz, ......, STOP bool _break = true; diff --git a/armsrc/i2c.h b/armsrc/i2c.h index be2ced3f4..d1bcd0559 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -43,7 +43,7 @@ void I2C_recovery(void); void I2C_init(bool has_ticks); -void I2C_Reset(void); +void I2C_Reset(void); // TODO DXL: Not implemented but defined? void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA); void I2C_Reset_EnterMainProgram(void); @@ -53,6 +53,7 @@ bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address); bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address); bool I2C_BufferWrite(const uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address); +int16_t I2C_BufferReadRaw(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address); int16_t I2C_BufferRead(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address); // for firmware diff --git a/armsrc/i2c_direct.c b/armsrc/i2c_direct.c index 096a5daa9..bdfe26823 100644 --- a/armsrc/i2c_direct.c +++ b/armsrc/i2c_direct.c @@ -23,7 +23,8 @@ #include "BigBuf.h" #include "string.h" #include "mifareutil.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "proxmark3_arm.h" #include "cmd.h" #include "protocols.h" @@ -32,7 +33,7 @@ #include "commonutil.h" #include "crc16.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "i2c.h" #include "i2c_direct.h" diff --git a/armsrc/iclass.c b/armsrc/iclass.c index e98e1f77e..c374d12ee 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -29,12 +29,13 @@ #include "appmain.h" #include "BigBuf.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "string.h" #include "util.h" #include "dbprint.h" #include "protocols.h" -#include "ticks.h" +#include "ticks_apis.h" #include "iso15693.h" #include "iclass_cmd.h" // iclass_card_select_t struct #include "i2c.h" // i2c defines (SIM module access) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index df4d73ea9..631ddc60d 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -24,8 +24,10 @@ #include "cmd.h" #include "appmain.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "ticks_apis.h" +#include "fpga_apis.h" +#include "rssi_apis.h" #include "dbprint.h" #include "util.h" #include "parity.h" @@ -37,7 +39,7 @@ #include "desfire_crypto.h" // UL-C authentication helpers #include "mifare.h" // for iso14a_polling_frame_t structure #include "cmac_calc.h" -#include "usb_cdc.h" +#include "usb_cdc_apis.h" // Forward declaration: HID Config Card jam support (implemented in secc.c). // Called from SniffIso14443a when param bit 0x04 is set. @@ -45,8 +47,6 @@ bool hid_config_card_jam(const uint8_t *cmd, int len, uint8_t *dma_buf); static uint32_t iso14a_timeout; -static uint8_t colpos = 0; - // the block number for the ISO14443-4 PCB static uint8_t iso14_pcb_blocknum = 0; @@ -845,8 +845,8 @@ void RAMFUNC SniffIso14443a(uint8_t param) { uint8_t *data = dma->buf; // Setup and start DMA. - if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { - if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + if (FpgaSetupSscRxDmaRepeat((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscRxDmaRepeat failed. Exiting"); return; } @@ -875,7 +875,7 @@ void RAMFUNC SniffIso14443a(uint8_t param) { } register int readBufDataP = data - dma->buf; - register int dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; + register int dmaBufDataP = DMA_BUFFER_SIZE - FPGA_SSC_DMA_RX_Remaining_Count(); if (readBufDataP <= dmaBufDataP) { dataLen = dmaBufDataP - readBufDataP; } else { @@ -886,6 +886,11 @@ void RAMFUNC SniffIso14443a(uint8_t param) { maxDataLen = dataLen; } + // TODO DXL This cross platform issue needs to be addressed + // When the current buffer address is zeroed, it usually means the next buffer hasn't been assigned a value in time, + // indicating that the MCU's processing speed is slow. + // The logic here is to reset the state machine to receive data again. + // DMA fully stalled: both buffers exhausted. Re-arm primary + secondary, // resync the read pointer, and drop the in-flight frame. if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { @@ -918,6 +923,13 @@ void RAMFUNC SniffIso14443a(uint8_t param) { continue; } + /** + * TODO DXL This problem needs to be addressed. + * if (FPGA_SSC_DMA_RX_Done()) { + * FPGA_SSC_DMA_RX_Refresh_Repeat(dma->buf, DMA_BUFFER_SIZE); + * } + */ + // secondary buffer exhausted, primary still running — refill secondary if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; @@ -929,6 +941,7 @@ void RAMFUNC SniffIso14443a(uint8_t param) { // Need two samples to feed Miller and Manchester-Decoder if (rx_samples & 0x01) { + // Reader -> Tag // no need to try decoding reader data if the tag is sending if (TagIsActive == false) { @@ -967,6 +980,7 @@ void RAMFUNC SniffIso14443a(uint8_t param) { ReaderIsActive = (Uart.state != STATE_14A_UNSYNCD); } + // Tag -> Reader // no need to try decoding tag data if the reader is sending - and we cannot afford the time if (ReaderIsActive == false) { @@ -1151,7 +1165,7 @@ bool GetIso14443aCommandFromReader(uint8_t *received, uint16_t received_maxlen, Uart14aInit(received, received_maxlen, par); // clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + uint8_t b = (uint8_t)FPGA_SSC_RX_Value(); (void)b; uint8_t flip = 0; @@ -1180,8 +1194,8 @@ bool GetIso14443aCommandFromReader(uint8_t *received, uint16_t received_maxlen, checker = 4000; } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + b = (uint8_t)FPGA_SSC_RX_Value(); if (MillerDecoding(b, 0)) { *len = Uart.len; return true; @@ -2597,7 +2611,6 @@ static void PrepareDelayedTransfer(uint16_t delay) { } } - //------------------------------------------------------------------------------------- // Transmit the command (to the tag) that was placed in ToSend[]. // Parameter timing: @@ -2613,7 +2626,10 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing return; } - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + // DXL: If the mode is set to FPGA_MAJOR_MODE_OFF before transmission, the timing wait will freeze. + // If you need to handle this situation, you can uncomment the code below(SPEED is affected). + // And do not use the FPGA_HF_ISO14443A_READER_MOD!!! + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); if (timing) { @@ -2632,14 +2648,28 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing ThisTransferTime = ((MAX(NextTransferTime, GetCountSspClk()) & 0xfffffff8) + 8); while (GetCountSspClk() < ThisTransferTime) {}; - LastTimeProxToAirStart = ThisTransferTime; + } + // DXL: Switch to this mode before actually starting to send. Otherwise, it may cause delays between frames to fail. + // 14b also has this problem, which requires waiting for the frame delay to complete + // before switching to modulation transmission mode to send data. + // If we don't do this, there is a possibility of randomly encountering communication exception bugs + // on high-performance processors such as AT32, which is very fatal! + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + + // If the transmission is not cleared, there is a high probability of communication abnormalities. + // I suspect that the wrong DOUT level may have modulated data that should not have been modulated. + // If further research is needed, an oscilloscope needs to be used to observe the specific DOUT modulation status. + // --- + // Clear TXRDY: + FPGA_SSC_TX_Value(SEC_Y); + uint16_t c = 0; while (c < len) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c]; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(cmd[c]); c++; } } @@ -2750,23 +2780,13 @@ int EmGetCmd(uint8_t *received, uint16_t received_max_len, uint16_t *len, uint8_ LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); - // Set ADC to read field strength - AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; - AT91C_BASE_ADC->ADC_MR = - ADC_MODE_PRESCALE(63) | - ADC_MODE_STARTUP_TIME(1) | - ADC_MODE_SAMPLE_HOLD_TIME(15); - - AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF); - - // start ADC - AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; + AdcSetupRssiChannel(ADC_RSSI_CH_HF); // Now run a 'software UART' on the stream of incoming samples. Uart14aInit(received, received_max_len, par); // Clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + uint8_t b = (uint8_t)FPGA_SSC_RX_Value(); (void)b; uint8_t flip = 0; @@ -2802,17 +2822,17 @@ int EmGetCmd(uint8_t *received, uint16_t received_max_len, uint16_t *len, uint8_ // test if the field exists - if (AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF)) { + if (AdcRssiDataReady(ADC_RSSI_CH_HF)) { analogCnt++; - analogAVG += (AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF] & 0x3FF); + analogAVG += AdcRssiDataRead(ADC_RSSI_CH_HF); - AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; + AdcRssiConversionStart(); if (analogCnt >= 32) { - if ((MAX_ADC_HF_VOLTAGE * (analogAVG / analogCnt) >> 10) < MF_MINFIELDV) { + if (AdcRssiDataToMilliVolt(analogAVG / analogCnt, ADC_RSSI_CH_HF) < MF_MINFIELDV) { if (timer == 0) { timer = GetTickCount(); @@ -2831,8 +2851,8 @@ int EmGetCmd(uint8_t *received, uint16_t received_max_len, uint16_t *len, uint8_ } // receive and test the miller decoding - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + b = (uint8_t)FPGA_SSC_RX_Value(); if (MillerDecoding(b, 0)) { *len = Uart.len; return 0; @@ -2862,14 +2882,14 @@ int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen) { i = (correction_needed) ? 0 : 1; // clear receiving shift register and holding register - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); - b = AT91C_BASE_SSC->SSC_RHR; + FPGA_SSC_RX_READY_WAIT(); + b = FPGA_SSC_RX_Value(); (void) b; // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) for (uint8_t j = 0; j < 5; j++) { // allow timeout - better late than never - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); - if (AT91C_BASE_SSC->SSC_RHR) { + FPGA_SSC_RX_READY_WAIT(); + if (FPGA_SSC_RX_Value()) { break; } } @@ -2877,22 +2897,24 @@ int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen) { while ((ThisTransferTime = GetCountSspClk()) & 0x00000007); // Clear TXRDY: - AT91C_BASE_SSC->SSC_THR = SEC_F; + FPGA_SSC_TX_Value(SEC_F); // send cycle for (; i < respLen;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = resp[i++]; - FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(resp[i++]); + FPGA_SSC_RX_READY_WAIT(); + FpgaSendQueueDelay = (uint8_t)FPGA_SSC_RX_Value(); } } // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN again: uint8_t fpga_queued_bits = FpgaSendQueueDelay >> 3; for (i = 0; i <= (fpga_queued_bits >> 3) + 1;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = SEC_F; - FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(SEC_F); + FPGA_SSC_RX_READY_WAIT(); + FpgaSendQueueDelay = (uint8_t)FPGA_SSC_RX_Value(); i++; } } @@ -3014,7 +3036,7 @@ bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t rec_ Demod14aInit(receivedResponse, rec_maxlen, NULL); // clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + uint8_t b = (uint8_t)FPGA_SSC_RX_Value(); (void)b; uint32_t timeout = iso14a_get_timeout(); @@ -3023,8 +3045,8 @@ bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t rec_ for (;;) { WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + b = (uint8_t)FPGA_SSC_RX_Value(); if (ManchesterDecoding_Thinfilm(b)) { *received_len = Demod.len; LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); @@ -3064,7 +3086,7 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint16_t rec_max Demod14aInit(receivedResponse, rec_maxlen, receivedResponsePar); // clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + uint8_t b = (uint8_t)FPGA_SSC_RX_Value(); (void)b; volatile uint32_t c = 0; @@ -3073,8 +3095,8 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint16_t rec_max for (;;) { WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + b = (uint8_t)FPGA_SSC_RX_Value(); if (ManchesterDecoding(b, offset, 0)) { NextTransferTime = MAX(NextTransferTime, Demod.endTime - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / 16 + FRAME_DELAY_TIME_PICC_TO_PCD); return true; @@ -3186,11 +3208,9 @@ void iso14443a_antifuzz(uint32_t flags) { resp[2] = 0xFF; resp[3] = 0xFF; resp[4] = resp[0] ^ resp[1] ^ resp[2] ^ resp[3]; - colpos = 0; if (IS_FLAG_UID_IN_DATA(flags, 7)) { resp[0] = MIFARE_SELECT_CT; - colpos = 8; } // trigger a faulty/collision response @@ -3635,7 +3655,7 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { // Set up the synchronous serial port FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); LED_D_OFF(); // Signal field is on with the appropriate LED diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 237dbb57c..e719470c9 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -29,10 +29,12 @@ #include "appmain.h" #include "BigBuf.h" #include "cmd.h" -#include "fpgaloader.h" +#include "fpga_loader.h" #include "commonutil.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" +#include "fpga_apis.h" +#include "rssi_apis.h" #include "iso14b.h" // defines for ETU conversions #include "iclass.h" // picopass buffer defines @@ -714,8 +716,8 @@ static bool GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { while (BUTTON_PRESS() == false) { WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + uint8_t b = (uint8_t)FPGA_SSC_RX_Value(); for (uint8_t mask = 0x80; mask != 0x00; mask >>= 1) { if (Handle14443bSampleFromReader(b & mask)) { *len = Uart.byteCnt; @@ -742,8 +744,8 @@ static void TransmitFor14443b_AsTag(const uint8_t *response, uint16_t len) { for (uint16_t i = 0; i < len;) { // Put byte into tx holding register as soon as it is ready - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - AT91C_BASE_SSC->SSC_THR = response[i++]; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(response[i++]); // Start-up SSC once first byte is in SSC_THR if (i == 1) { @@ -774,7 +776,7 @@ void SimulateIso14443bTag(const uint8_t *pupi) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); // Set up the synchronous serial port FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); @@ -837,7 +839,7 @@ void SimulateIso14443bTag(const uint8_t *pupi) { } // find reader field - vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; + vHf = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF); if (vHf > MF_MINFIELDV) { if (cardSTATE == SIM_POWER_OFF) { cardSTATE = SIM_IDLE; @@ -1002,7 +1004,7 @@ void Simulate_iso14443b_srx_tag(uint8_t *uid) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); // Set up the synchronous serial port FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); @@ -1045,7 +1047,7 @@ void Simulate_iso14443b_srx_tag(uint8_t *uid) { // find reader field if (cardSTATE == SIM_NOFIELD) { - vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; + vHf = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF); if (vHf > MF_MINFIELDV) { cardSTATE = SIM_IDLE; LED_A_ON(); @@ -1378,8 +1380,8 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t return PM3_EMALLOC; } - if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { - if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + if (FpgaSetupSscRxDmaRepeat((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscRxDmaRepeat failed. Exiting"); return PM3_EMALLOC; } @@ -1395,7 +1397,7 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t for (;;) { - volatile uint16_t behindBy = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + volatile uint16_t behindBy = ((uint16_t *)FPGA_SSC_DMA_RX_Current_Address() - upTo) & (DMA_BUFFER_SIZE - 1); if (behindBy == 0) { WDT_HIT(); if (BUTTON_PRESS()) { @@ -1429,18 +1431,9 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t upTo = dma->buf; // DMA Counter Register had reached 0, already rotated. - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { + if (FPGA_SSC_DMA_RX_Done()) { - // primary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; - } - // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; - } + FPGA_SSC_DMA_RX_Refresh_Repeat(dma->buf, DMA_BUFFER_SIZE); WDT_HIT(); if (BUTTON_PRESS()) { @@ -1451,7 +1444,6 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t } if (Handle14443bSamplesFromTag(ci, cq)) { - // Response timing is measured from DMA start, but trace rows use // absolute SSP time like reader frames. uint32_t eof_delta = GetCountSspClkDelta(dma_start_time); @@ -1472,7 +1464,7 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t } } - FpgaDisableSscDma(); + FPGA_SSC_DMA_RX_Disable(); if (ret < 0) { return ret; } @@ -1492,6 +1484,7 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t if (retlen) { *retlen = Demod.len; } + return PM3_SUCCESS; } @@ -1503,28 +1496,33 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) { tosend_t *ts = get_tosend(); -#ifdef RDV4 + // TODO DXL 可能此函数会被 Get14443bAnswerFromTag 的改动所影响,需要进行测试。 + + // TR2 minimum 14 ETUs + if (*start_time < ISO14B_TR0) { + // *start_time = DELAY_ARM_TO_TAG; + *start_time = ISO14B_TR0; + } + // *start_time = (*start_time - DELAY_ARM_TO_TAG) & 0xfffffff0; + *start_time = (*start_time & 0xfffffff0); + if (GetCountSspClk() > *start_time) { // we may miss the intended time + *start_time = (GetCountSspClk() + 32) & 0xfffffff0; // next possible time + } + // waiting for T2(minimum delay between two frames) + while (GetCountSspClk() < *start_time) {} + + // DXL: It is best to perform a clearance once. + FPGA_SSC_TX_Clear(); + + // DXL: + // We only switch to the transmission modulation mode before starting the transmission, and before that, + // we may still be delaying and waiting for T2 (14 ETUs) between two frames +#if defined RDV4 || defined PM5 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4); #else FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); #endif - // TR2 minimum 14 ETUs - if (*start_time < ISO14B_TR0) { -// *start_time = DELAY_ARM_TO_TAG; - *start_time = ISO14B_TR0; - } - -// *start_time = (*start_time - DELAY_ARM_TO_TAG) & 0xfffffff0; - *start_time = (*start_time & 0xfffffff0); - - if (GetCountSspClk() > *start_time) { // we may miss the intended time - *start_time = (GetCountSspClk() + 32) & 0xfffffff0; // next possible time - } - - // wait - while (GetCountSspClk() < *start_time); - LED_B_ON(); for (int c = 0; c < ts->max; c++) { volatile uint8_t data = ts->buf[c]; @@ -1532,11 +1530,11 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) { for (uint8_t i = 0; i < 8; i++) { volatile uint16_t send_word = (data & 0x80) ? 0x0000 : 0xFFFF; - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; - AT91C_BASE_SSC->SSC_THR = send_word; + while (!FPGA_SSC_TX_Ready()) ; + FPGA_SSC_TX_Value(send_word); - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; - AT91C_BASE_SSC->SSC_THR = send_word; + while (!FPGA_SSC_TX_Ready()) ; + FPGA_SSC_TX_Value(send_word); data <<= 1; } @@ -1550,23 +1548,25 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) { for (uint8_t i = 0; i < last_bits; i++) { volatile uint16_t send_word = (data & 0x80) ? 0x0000 : 0xFFFF; - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; - AT91C_BASE_SSC->SSC_THR = send_word; + while (!FPGA_SSC_TX_Ready()) ; + FPGA_SSC_TX_Value(send_word); - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; - AT91C_BASE_SSC->SSC_THR = send_word; + while (!FPGA_SSC_TX_Ready()) ; + FPGA_SSC_TX_Value(send_word); data <<= 1; } + + // DXL: Very important!!! If not cleared, ST25 will always fail communication on at32 platform. + FPGA_SSC_TX_Clear(); + WDT_HIT(); - - LED_B_OFF(); // *start_time += DELAY_ARM_TO_TAG; // wait for last transfer to complete - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) {}; + while (!FPGA_SSC_TX_Done()) {}; } static uint32_t ToSendBitCount(const tosend_t *ts) { @@ -2457,13 +2457,13 @@ void iso14443b_setup(void) { Uart14bInit(BigBuf_calloc(MAX_FRAME_SIZE)); // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); // Set up the synchronous serial port FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Signal field is on with the appropriate LED -#ifdef RDV4 +#if defined RDV4 || defined PM5 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4); #else FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); @@ -2594,7 +2594,7 @@ void SniffIso14443b(void) { // FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); StartCountSspClk(); @@ -2603,8 +2603,8 @@ void SniffIso14443b(void) { dmabuf16_t *dma = get_dma16(); // Setup and start DMA. - if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { - if (g_dbglevel > DBG_ERROR) DbpString("FpgaSetupSscDma failed. Exiting"); + if (FpgaSetupSscRxDmaRepeat((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (g_dbglevel > DBG_ERROR) DbpString("FpgaSetupSscRxDmaRepeat failed. Exiting"); switch_off(); return; } @@ -2615,7 +2615,7 @@ void SniffIso14443b(void) { bool tag_is_active = false; bool reader_is_active = false; bool expect_tag_answer = false; - int dma_start_time = 0; + uint32_t dma_start_time = 0; // Count of samples received so far, so that we can include timing int samples = 0; @@ -2624,7 +2624,7 @@ void SniffIso14443b(void) { for (;;) { - volatile int behind_by = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + volatile int behind_by = ((uint16_t *)FPGA_SSC_DMA_RX_Current_Address() - upTo) & (DMA_BUFFER_SIZE - 1); if (behind_by < 1) continue; samples++; @@ -2644,18 +2644,9 @@ void SniffIso14443b(void) { upTo = dma->buf; // DMA Counter Register had reached 0, already rotated. - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { + if (FPGA_SSC_DMA_RX_Done()) { - // primary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; - } - // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; - } + FPGA_SSC_DMA_RX_Refresh_Repeat(dma->buf, DMA_BUFFER_SIZE); WDT_HIT(); if (BUTTON_PRESS()) { @@ -2782,7 +2773,7 @@ static void tearoff_field_on(void) { Demod14bInit(BigBuf_calloc(MAX_FRAME_SIZE), MAX_FRAME_SIZE); Uart14bInit(BigBuf_calloc(MAX_FRAME_SIZE)); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // programs PDC with fresh BigBuf address #ifdef RDV4 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4); diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index e8b82c241..276640305 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -69,9 +69,11 @@ #include "cmd.h" #include "appmain.h" #include "dbprint.h" -#include "fpgaloader.h" +#include "fpga_loader.h" #include "commonutil.h" -#include "ticks.h" +#include "ticks_apis.h" +#include "fpga_apis.h" +#include "rssi_apis.h" #include "BigBuf.h" #include "crc16.h" @@ -300,7 +302,7 @@ void CodeIso15693AsTag(const uint8_t *cmd, size_t len) { // Transmit the command (to the tag) that was placed in cmd[]. void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time, bool shallow_mod) { -#ifdef RDV4 +#if defined RDV4 || defined PM5 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | (shallow_mod ? FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4 : FPGA_HF_READER_MODE_SEND_FULL_MOD)); #else FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | (shallow_mod ? FPGA_HF_READER_MODE_SEND_SHALLOW_MOD : FPGA_HF_READER_MODE_SEND_FULL_MOD)); @@ -328,11 +330,11 @@ void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time, bool for (uint8_t i = 0; i < 8; i++) { uint16_t send_word = (data & 0x80) ? 0xffff : 0x0000; - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; - AT91C_BASE_SSC->SSC_THR = send_word; + while (!FPGA_SSC_TX_Ready()) ; + FPGA_SSC_TX_Value(send_word); - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; - AT91C_BASE_SSC->SSC_THR = send_word; + while (!FPGA_SSC_TX_Ready()) ; + FPGA_SSC_TX_Value(send_word); data <<= 1; } @@ -379,9 +381,9 @@ void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; for (int j = 0; j < (slow ? 4 : 1);) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + if (FPGA_SSC_TX_Ready()) { bits_to_send = bits_to_shift << (8 - shift_delay) | cmd_bits >> shift_delay; - AT91C_BASE_SSC->SSC_THR = bits_to_send; + FPGA_SSC_TX_Value(bits_to_send); bits_to_shift = cmd_bits; j++; } @@ -394,8 +396,8 @@ void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, bits_to_send = bits_to_shift << (8 - shift_delay); if (bits_to_send) { for (; ;) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - AT91C_BASE_SSC->SSC_THR = bits_to_send; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(bits_to_send); break; } } @@ -1018,7 +1020,7 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo } // wait for last transfer to complete - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + while (!FPGA_SSC_TX_Done()); // And put the FPGA in the appropriate mode FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); @@ -1030,10 +1032,8 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo dmabuf16_t *dma = get_dma16(); // Setup and start DMA. - if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { - if (g_dbglevel > DBG_ERROR) { - Dbprintf("FpgaSetupSscDma failed. Exiting"); - } + if (FpgaSetupSscRxDmaRepeat((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscRxDmaRepeat failed. Exiting"); return PM3_EINIT; } @@ -1042,7 +1042,7 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo for (;;) { - volatile uint16_t behindBy = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + volatile uint16_t behindBy = ((uint16_t *)FPGA_SSC_DMA_RX_Current_Address() - upTo) & (DMA_BUFFER_SIZE - 1); if (behindBy == 0) { continue; } @@ -1059,18 +1059,9 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo upTo = dma->buf; // start reading the circular buffer from the beginning // DMA Counter Register had reached 0, already rotated. - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { + if (FPGA_SSC_DMA_RX_Done()) { - // primary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; - } - // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; - } + FPGA_SSC_DMA_RX_Refresh_Repeat(dma->buf, DMA_BUFFER_SIZE); WDT_HIT(); if (BUTTON_PRESS()) { @@ -1137,7 +1128,7 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo } } - FpgaDisableSscDma(); + FPGA_SSC_DMA_RX_Disable(); FpgaDisableTracing(); uint32_t sof_time = *eof_time - (32 * 16); // time for SOF transfer @@ -1530,22 +1521,20 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo DecodeReaderInit(dr, received, max_len, 0, NULL); // wait for last transfer to complete - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + while (!FPGA_SSC_TX_Done()); LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); // clear receive register and wait for next transfer - uint32_t temp = AT91C_BASE_SSC->SSC_RHR; + uint32_t temp = FPGA_SSC_RX_Value(); (void) temp; - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) ; + FPGA_SSC_RX_READY_WAIT(); // Setup and start DMA. dmabuf8_t *dma = get_dma8(); - if (FpgaSetupSscDma(dma->buf, DMA_BUFFER_SIZE) == false) { - if (g_dbglevel > DBG_ERROR) { - Dbprintf("FpgaSetupSscDma failed. Exiting"); - } + if (FpgaSetupSscRxDmaRepeat(dma->buf, DMA_BUFFER_SIZE) == false) { + if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscRxDmaRepeat failed. Exiting"); return -4; } const uint8_t *upTo = dma->buf; @@ -1553,7 +1542,7 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8; for (;;) { - volatile uint16_t behindBy = ((uint8_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + volatile uint16_t behindBy = ((uint8_t *)FPGA_SSC_DMA_RX_Current_Address() - upTo) & (DMA_BUFFER_SIZE - 1); if (behindBy == 0) { continue; } @@ -1571,9 +1560,9 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo break; } } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; // DMA Next Counter registers + + if (FPGA_SSC_DMA_RX_Done()) { // DMA Counter Register had reached 0, already rotated. + FPGA_SSC_DMA_RX_Refresh_Single(dma->buf, DMA_BUFFER_SIZE); } for (int i = 7; i >= 0; i--) { @@ -1605,7 +1594,7 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo WDT_HIT(); } - FpgaDisableSscDma(); + FPGA_SSC_DMA_RX_Disable(); if (dr->byteCount >= 0) { uint32_t sof_time = *eof_time @@ -1651,7 +1640,7 @@ void AcquireRawAdcSamplesIso15693(void) { // initialize SSC and select proper AD input FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); StartCountSspClk(); @@ -1665,19 +1654,19 @@ void AcquireRawAdcSamplesIso15693(void) { TransmitTo15693Tag(ts->buf, ts->max, &start_time, false); // wait for last transfer to complete - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) ; + while (!FPGA_SSC_TX_Done()) ; FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); for (int c = 0; c < 4000;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint16_t r = AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + uint16_t r = FPGA_SSC_RX_Value(); dest[c++] = r >> 5; } } - FpgaDisableSscDma(); + FPGA_SSC_DMA_RX_Disable(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); } @@ -1712,7 +1701,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla LED_D_OFF(); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); StartCountSspClk(); @@ -1721,10 +1710,8 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla dmabuf16_t *dma = get_dma16(); // Setup and start DMA. - if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { - if (g_dbglevel > DBG_ERROR) { - DbpString("FpgaSetupSscDma failed. Exiting"); - } + if (FpgaSetupSscRxDmaRepeat((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (g_dbglevel > DBG_ERROR) DbpString("FpgaSetupSscRxDmaRepeat failed. Exiting"); switch_off(); return; } @@ -1743,7 +1730,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla for (;;) { - volatile int behind_by = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + volatile int behind_by = ((uint16_t *)FPGA_SSC_DMA_RX_Current_Address() - upTo) & (DMA_BUFFER_SIZE - 1); if (behind_by < 1) { continue; } @@ -1765,18 +1752,9 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla upTo = dma->buf; // DMA Counter Register had reached 0, already rotated. - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { + if (FPGA_SSC_DMA_RX_Done()) { - // primary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; - } - // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; - } + FPGA_SSC_DMA_RX_Refresh_Repeat(dma->buf, DMA_BUFFER_SIZE); WDT_HIT(); if (BUTTON_PRESS()) { @@ -1925,7 +1903,7 @@ void Iso15693InitReader(void) { // initialize SSC and select proper AD input FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); set_tracing(true); @@ -2173,7 +2151,7 @@ void Iso15693InitTag(void) { // initialize SSC and select proper AD input FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); clear_trace(); set_tracing(true); @@ -2285,7 +2263,7 @@ void SimTagIso15693(const uint8_t *uid, uint8_t block_size) { // find reader field if (tag->state == TAG_STATE_NO_FIELD) { - vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; + vHf = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF); if (vHf > MF_MINFIELDV) { tag->state = TAG_STATE_READY; LED_A_ON(); diff --git a/armsrc/ldscript b/armsrc/ldscript deleted file mode 100644 index 0824205a2..000000000 --- a/armsrc/ldscript +++ /dev/null @@ -1,64 +0,0 @@ -/* ------------------------------------------------------------------------------ - This code is licensed to you under the terms of the GNU GPL, version 2 or, - at your option, any later version. See the LICENSE.txt file for the text of - the license. ------------------------------------------------------------------------------ - Linker script for the ARM binary ------------------------------------------------------------------------------ -*/ -INCLUDE ../common_arm/ldscript.common - -PHDRS -{ - text PT_LOAD FLAGS(5); - data PT_LOAD; - bss PT_LOAD; -} - -ENTRY(Vector) -SECTIONS -{ - .start : { - *(.startos) - } >osimage :text - - .text : { - *(.text) - *(.text.*) - *(.eh_frame) - *(.glue_7) - *(.glue_7t) - } >osimage :text - - .rodata : { - *(.rodata) - *(.rodata.*) - *(fpga_all_bit.data) - . = ALIGN(8); - } >osimage :text - - .data : { - *(.data) - *(.data.*) - *(.ramfunc) - . = ALIGN(4); - } >ram AT>osimage :data - - __data_src_start__ = LOADADDR(.data); - __data_start__ = ADDR(.data); - __data_end__ = __data_start__ + SIZEOF(.data); - __os_size__ = SIZEOF(.text) + SIZEOF(.data) + SIZEOF(.rodata); - - .bss : { - __bss_start__ = .; - *(.bss) - *(.bss.*) - . = ALIGN(4); - __bss_end__ = .; - } >ram AT>ram :bss - - .commonarea (NOLOAD) : { - *(.commonarea) - } >commonarea :NONE -} diff --git a/armsrc/ldscript.osimage.at32 b/armsrc/ldscript.osimage.at32 new file mode 100644 index 000000000..02eb6eb84 --- /dev/null +++ b/armsrc/ldscript.osimage.at32 @@ -0,0 +1,29 @@ +/* +----------------------------------------------------------------------------- + This code is licensed to you under the terms of the GNU GPL, version 2 or, + at your option, any later version. See the LICENSE.txt file for the text of + the license. +----------------------------------------------------------------------------- + Linker script for the ARM binary +----------------------------------------------------------------------------- +*/ + +INCLUDE ../common_arm/ldscript.defs.at32 +INCLUDE ../common_arm/ldscript.common + +INCLUDE ./ldscript.osimage.phdrs + +ENTRY(Reset_Handler) + +SECTIONS +{ + /* The startup code goes first into FLASH, AT32 is startup from 'startup_at32f435_437.s' */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >osimage :text +} + +INCLUDE ./ldscript.osimage.sections diff --git a/armsrc/ldscript.osimage.at91 b/armsrc/ldscript.osimage.at91 new file mode 100644 index 000000000..c492526cd --- /dev/null +++ b/armsrc/ldscript.osimage.at91 @@ -0,0 +1,18 @@ +/* +----------------------------------------------------------------------------- + This code is licensed to you under the terms of the GNU GPL, version 2 or, + at your option, any later version. See the LICENSE.txt file for the text of + the license. +----------------------------------------------------------------------------- + Linker script for the ARM binary +----------------------------------------------------------------------------- +*/ + +INCLUDE ../common_arm/ldscript.defs.at91 +INCLUDE ../common_arm/ldscript.common + +INCLUDE ./ldscript.osimage.phdrs + +ENTRY(Vector) + +INCLUDE ./ldscript.osimage.sections diff --git a/armsrc/ldscript.osimage.phdrs b/armsrc/ldscript.osimage.phdrs new file mode 100644 index 000000000..8b212ebe0 --- /dev/null +++ b/armsrc/ldscript.osimage.phdrs @@ -0,0 +1,6 @@ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD; + bss PT_LOAD; +} \ No newline at end of file diff --git a/armsrc/ldscript.osimage.sections b/armsrc/ldscript.osimage.sections new file mode 100644 index 000000000..bcfc8f362 --- /dev/null +++ b/armsrc/ldscript.osimage.sections @@ -0,0 +1,65 @@ +/*----------------------------------------------------------------------------- + * Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * See LICENSE.txt for the text of the license. + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + * Common linker script, for os image for sections. + *----------------------------------------------------------------------------- + */ + +SECTIONS +{ + .start : { + *(.startos) + } >osimage :text + + .text : { + *(.text) + *(.text.*) + *(.eh_frame) + *(.glue_7) + *(.glue_7t) + } >osimage :text + + .rodata : { + *(.rodata) + *(.rodata.*) + *(fpga_all_bit.data) + . = ALIGN(8); + } >osimage :text + + .data : { + *(.data) + *(.data.*) + *(.ramfunc) + . = ALIGN(4); + } >ram AT>osimage :data + + __data_src_start__ = LOADADDR(.data); + __data_start__ = ADDR(.data); + __data_end__ = __data_start__ + SIZEOF(.data); + __os_size__ = SIZEOF(.text) + SIZEOF(.data) + SIZEOF(.rodata); + + .bss : { + __bss_start__ = .; + *(.bss) + *(.bss.*) + . = ALIGN(4); + __bss_end__ = .; + } >ram AT>ram :bss + + .commonarea (NOLOAD) : { + *(.commonarea) + } >commonarea :NONE +} diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 6239f27cb..f11067118 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -24,8 +24,9 @@ #include "proxmark3_arm.h" #include "cmd.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "ticks_apis.h" +#include "fpga_apis.h" #include "dbprint.h" #include "util.h" #include "string.h" @@ -75,8 +76,8 @@ static uint16_t rx_frame_from_fpga(void) { WDT_HIT(); // wait for frame be become available in rx holding register - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - return AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + return FPGA_SSC_RX_Value(); } } return 0; @@ -143,12 +144,12 @@ static bool rx_bit(void) { static void tx_bit(bool bit) { // insert pause - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); last_frame_end += RWD_TIME_PAUSE; while (GET_TICKS < last_frame_end) { }; // return to carrier on, wait for bit periode to end - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); last_frame_end += (bit ? RWD_TIME_1 : RWD_TIME_0) - RWD_TIME_PAUSE; while (GET_TICKS < last_frame_end) { }; } @@ -181,10 +182,10 @@ static void tx_frame(uint32_t frame, uint8_t len) { }; // add pause to mark end of the frame - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); last_frame_end += RWD_TIME_PAUSE; while (GET_TICKS < last_frame_end) { }; - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); // log uint8_t cmdbytes[] = {len, BYTEx(frame, 0), BYTEx(frame, 1), BYTEx(frame, 2)}; @@ -288,16 +289,15 @@ static void init_reader(void) { // configure FPGA FpgaDownloadAndGo(FPGA_BITSTREAM_HF); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); LED_A_ON(); // configure SSC with defaults FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // re-claim GPIO_SSC_DOUT as GPIO and enable output - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - LOW(GPIO_SSC_DOUT); + gpio_fpga_mod_only_setup(); + Gpio_SSC_DOUT_Low(); // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. legic_mem = BigBuf_get_EM_addr(); diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c index ccd3c7c63..f66d2e3b6 100644 --- a/armsrc/legicrfsim.c +++ b/armsrc/legicrfsim.c @@ -24,8 +24,9 @@ #include "cmd.h" #include "proxmark3_arm.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "fpga_apis.h" +#include "ticks_apis.h" #include "dbprint.h" #include "util.h" @@ -75,7 +76,7 @@ static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ // Returns true if a pulse/pause is received within timeout // Note: inlining this function would fail with -Os static bool wait_for(bool value, const uint32_t timeout) { - while ((bool)(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN) != value) { + while (Gpio_SSC_DIN_Read() != value) { WDT_HIT(); if (GetCountSspClk() > timeout) { return false; @@ -142,10 +143,10 @@ static void tx_bit(bool bit) { if (bit) { // modulate subcarrier - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); } else { // do not modulate subcarrier - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); } // wait for tx timeslot to end @@ -181,7 +182,7 @@ static void tx_frame(uint32_t frame, uint8_t len) { }; // disable subcarrier - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); // log uint8_t cmdbytes[] = {len, BYTEx(frame, 0), BYTEx(frame, 1)}; @@ -202,7 +203,7 @@ static void tx_ack(void) { legic_prng_forward(1); // disable subcarrier - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); // log uint8_t cmdbytes[] = {1, 1}; @@ -313,15 +314,14 @@ static void init_tag(void) { // configure FPGA FpgaDownloadAndGo(FPGA_BITSTREAM_HF); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); // configure SSC with defaults FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // first pull output to low to prevent glitches then re-claim GPIO_SSC_DOUT - LOW(GPIO_SSC_DOUT); - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + Gpio_SSC_DOUT_Low(); + gpio_fpga_mod_only_setup(); // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. legic_mem = BigBuf_get_EM_addr(); diff --git a/armsrc/lfadc.c b/armsrc/lfadc.c index 18712a7bb..e89881cab 100644 --- a/armsrc/lfadc.c +++ b/armsrc/lfadc.c @@ -18,8 +18,9 @@ #include "lfadc.h" #include "lfsampling.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "ticks_apis.h" +#include "fpga_apis.h" #include "dbprint.h" #include "appmain.h" @@ -40,14 +41,15 @@ // Exported global variables ////////////////////////////////////////////////////////////////////////////// -bool g_logging = true; +bool g_logging = false; // TODO DXL 在某些情况下,读不到卡的时候,此处可能会造成内存溢出,需要解决 ////////////////////////////////////////////////////////////////////////////// // Global variables ////////////////////////////////////////////////////////////////////////////// static bool rising_edge = false; -static bool reader_mode = false; +static lf_adc_init_mode_t g_init_mode = LF_ADC_READER; +static lf_adc_edge_mode_t g_edge_mode = LF_ADC_WAV_REVERSED; ////////////////////////////////////////////////////////////////////////////// // Auxiliary functions @@ -62,32 +64,35 @@ bool lf_test_periods(size_t expected, size_t count) { ////////////////////////////////////////////////////////////////////////////// // Low frequency (LF) adc passthrough functionality ////////////////////////////////////////////////////////////////////////////// -static uint8_t previous_adc_val = 0; //0xFF; +static uint8_t previous_adc_val = 0; // 0xFF; static uint8_t adc_avg = 0; +static uint8_t adc_max; +static uint8_t adc_min; -uint8_t get_adc_avg(void) { +uint8_t lf_get_adc_avg(void) { return adc_avg; } + void lf_sample_mean(void) { uint8_t periods = 0; uint32_t adc_sum = 0; + adc_max = 0; + adc_min = 255; while (periods < 32) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - adc_sum += AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + const uint8_t adc_val = FPGA_SSC_RX_Value(); + if (adc_val < adc_min) adc_min = adc_val; + if (adc_val > adc_max) adc_max = adc_val; + adc_sum += adc_val; periods++; } } - // division by 32 - adc_avg = adc_sum >> 5; + adc_avg = adc_sum >> 5; // division by 32 previous_adc_val = adc_avg; - - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("LF ADC average %u", adc_avg); - } + DBG Dbprintf("LF ADC average %u, max %u, min %u, diff %u", adc_avg, adc_max, adc_min, adc_max - adc_min); } static size_t lf_count_edge_periods_ex(size_t max, bool wait, bool detect_gap) { - #define LIMIT_DEV 20 // timeout limit to 100 000 w/o @@ -101,61 +106,68 @@ static size_t lf_count_edge_periods_ex(size_t max, bool wait, bool detect_gap) { timeout--; if (timeout == 0) { + DBG Dbprintf("Error, timeout for wait adc value rx"); return 0; } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(0x00); continue; } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + if (FPGA_SSC_RX_Ready() == false) { + continue; + } - periods++; + periods++; // T0 increment, 1(TO) = 8us, same with 125khz clock. + timeout = 100000; // reset timeout + volatile uint8_t adc_val = FPGA_SSC_RX_Value(); // Get current adc value. - // reset timeout - timeout = 100000; + if (g_logging) { + logSampleSimple(adc_val); + } - volatile uint8_t adc_val = AT91C_BASE_SSC->SSC_RHR; - - if (g_logging) { - logSampleSimple(adc_val); - } - - // Only test field changes if state of adc values matter - if (wait == false) { - // Test if we are locating a field modulation (100% ASK = complete field drop) - if (detect_gap) { - // Only return when the field completely disappeared - if (adc_val == 0) { - return periods; - } - - } else { - // Trigger on a modulation swap by observing an edge change + // Only test field changes if state of adc values matter + if (wait == false) { + // Test if we are locating a field modulation (100% ASK = complete field drop) + if (detect_gap) { + // Only return when the field completely disappeared + if (adc_val == 0) { + return periods; + } + } else { + if (g_edge_mode == LF_ADC_WAV_REVERSED) { if (rising_edge) { - if ((previous_adc_val > avg_peak) && (adc_val <= previous_adc_val)) { rising_edge = false; return periods; } - } else { - if ((previous_adc_val < avg_through) && (adc_val >= previous_adc_val)) { rising_edge = true; return periods; } - + } + } else if (g_edge_mode == LF_ADC_NOT_REVERSED) { + if (rising_edge) { + if (adc_val <= adc_avg && adc_val <= avg_through) { + rising_edge = false; + return periods; + } + } else { + if (adc_val >= avg_peak) { + rising_edge = true; + return periods; + } } } } + } - previous_adc_val = adc_val; + previous_adc_val = adc_val; - if (periods >= max) { - return 0; - } + if (periods >= max) { + return 0; } } @@ -175,18 +187,17 @@ size_t lf_detect_gap(size_t max) { } void lf_reset_counter(void) { - // TODO: find out the correct reset settings for tag and reader mode -// if (reader_mode) { + // if (g_init_mode == LF_ADC_READER) { // Reset values for reader mode rising_edge = false; previous_adc_val = 0xFF; -// } else { + // } else { // Reset values for tag/transponder mode -// rising_edge = false; -// previous_adc_val = 0xFF; -// } + // rising_edge = false; + // previous_adc_val = 0xFF; + // } } bool lf_get_tag_modulation(void) { @@ -198,15 +209,15 @@ bool lf_get_reader_modulation(void) { } void lf_wait_periods(size_t periods) { - // wait detect gap + // wait for detect gap lf_count_edge_periods_ex(periods, true, false); } -void lf_init(bool reader, bool simulate, bool ledcontrol) { - +void lf_init(lf_adc_init_mode_t init_mode, lf_adc_edge_mode_t edge_mode, bool ledcontrol) { StopTicks(); - reader_mode = reader; + g_init_mode = init_mode; + g_edge_mode = edge_mode; FpgaDownloadAndGo(FPGA_BITSTREAM_LF); @@ -216,21 +227,22 @@ void lf_init(bool reader, bool simulate, bool ledcontrol) { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); - if (reader) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - } else { - - if (simulate) { + // Different fpga config for different mode. + switch (g_init_mode) { + case LF_ADC_READER: + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + break; + case LF_ADC_TAG_SIM: FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC); - } else { - // Sniff + break; + case LF_ADC_SNIFF: FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC); // FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE); - } + break; } // Connect the A/D to the peak-detected low-frequency path. - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + SetAdcMuxFor(ADC_MUXSEL_LOPKD); // Now set up the SSC to get the ADC samples that are now streaming at us. FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); @@ -242,12 +254,16 @@ void lf_init(bool reader, bool simulate, bool ledcontrol) { // maximum: 545T0 = 545 * 8us = 4360us = 4.36ms - Hitag2 command waiting time before it starts transmitting in public mode (if configured so) // 565T0 = 565 * 8us = 4520us = 4.52ms - HitagS waiting time before entering TTF mode (if configured so) // Thus (2.50 ms + 4.36 ms) / 2 ~= 3 ms (rounded down to integer), should be a good timing for both tag models - SpinDelay(3); + SpinDelay(2); // TODO DXL 之前是 3ms,改成2ms测试hitagU + // TODO DXL 经测试,3ms时hitag2工作的很好,但是hitagu会进入TTF模式导致无法寻卡 + // hitagu用2ms延时比较靠谱 + // lf_wait_periods(200); // TODO DXL zx8268 用这个时长才能正常寻卡,3ms的话,8268直接开始广播了,没办法通信上了 // Steal this pin from the SSP (SPI communication channel with fpga) and use it to control the modulation - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - LOW(GPIO_SSC_DOUT); + gpio_fpga_mod_only_setup(); + Gpio_SSC_DOUT_Low(); + + /* TODO DXL 暂时注释,看看哪里没用到这些定时器的,如果都没用到,则可以删掉 // Enable peripheral Clock for TIMER_CLOCK 0 AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); @@ -259,9 +275,13 @@ void lf_init(bool reader, bool simulate, bool ledcontrol) { AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV4_CLOCK; + */ + // Clear all leds if (ledcontrol) LEDsoff(); + /* TODO DXL 暂时注释,看看哪里没用到这些定时器的,如果都没用到,则可以删掉 + // Reset and enable timers AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; @@ -269,6 +289,8 @@ void lf_init(bool reader, bool simulate, bool ledcontrol) { // Assert a sync signal. This sets all timers to 0 on next active clock edge AT91C_BASE_TCB->TCB_BCR = 1; + */ + // Prepare data trace uint32_t bufsize = 10000; @@ -281,6 +303,8 @@ void lf_init(bool reader, bool simulate, bool ledcontrol) { } void lf_finalize(bool ledcontrol) { + /* TODO DXL 暂时注释,看看哪里没用到这些定时器的,如果都没用到,则可以删掉 + // Disable timers AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; @@ -289,6 +313,8 @@ void lf_finalize(bool ledcontrol) { AT91C_BASE_PIOA->PIO_PDR = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_ASR = GPIO_SSC_DIN | GPIO_SSC_DOUT; + */ + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); if (ledcontrol) LEDsoff(); @@ -316,9 +342,9 @@ size_t lf_detect_field_drop(size_t max) { WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + if (FPGA_SSC_RX_Ready()) { periods++; - volatile uint8_t adc_val = AT91C_BASE_SSC->SSC_RHR; + volatile uint8_t adc_val = FPGA_SSC_RX_Value(); if (g_logging) logSampleSimple(adc_val); @@ -334,11 +360,19 @@ size_t lf_detect_field_drop(size_t max) { return 0; } +void lf_reset_field(size_t periods) { + // FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + Gpio_SSC_DOUT_High(); + lf_wait_periods(periods); + Gpio_SSC_DOUT_Low(); + // FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); +} + void lf_modulation(bool modulation) { if (modulation) { - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); } else { - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); } } @@ -352,8 +386,8 @@ static void lf_manchester_send_bit(uint8_t bit) { // simulation bool lf_manchester_send_bytes(const uint8_t *frame, size_t frame_len, bool ledcontrol) { - - if (ledcontrol) LED_B_ON(); + if (ledcontrol) + LED_B_ON(); lf_manchester_send_bit(1); lf_manchester_send_bit(1); @@ -366,6 +400,7 @@ bool lf_manchester_send_bytes(const uint8_t *frame, size_t frame_len, bool ledco lf_manchester_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); } - if (ledcontrol) LED_B_OFF(); + if (ledcontrol) + LED_B_OFF(); return true; } diff --git a/armsrc/lfadc.h b/armsrc/lfadc.h index 322584de2..6cfe56935 100644 --- a/armsrc/lfadc.h +++ b/armsrc/lfadc.h @@ -27,7 +27,30 @@ extern bool g_logging; -uint8_t get_adc_avg(void); +/* + * Trigger on a modulation swap by observing an edge change + * PEAK_THROUGH_LEFT: + * 1. When the peak of the falling edge appears to rise, it is considered to be the rising edge. + * 2. When the peak of the rising edge drops, it is considered a falling edge. + * Note: The edge status is from waveform of the analog signal is reversed. + * PEAK_THROUGH_CENTER: + * 1. The timing for edge judgment is based on the level range without modulation. + * 2. The edge switching will not be determined within the range of the peak of the falling edge and the peak of the rising edge. + * Note: not reversed. + */ +typedef enum { + LF_ADC_WAV_REVERSED = 0U, + LF_ADC_NOT_REVERSED = 1U, +} lf_adc_edge_mode_t; + +// What working mode should the module be initialized to? +typedef enum { + LF_ADC_READER = 0U, + LF_ADC_TAG_SIM = 1U, + LF_ADC_SNIFF = 2U, +} lf_adc_init_mode_t; + +uint8_t lf_get_adc_avg(void); void lf_sample_mean(void); bool lf_test_periods(size_t expected, size_t count); size_t lf_count_edge_periods(size_t max); @@ -38,10 +61,10 @@ bool lf_get_tag_modulation(void); bool lf_get_reader_modulation(void); void lf_wait_periods(size_t periods); -//void lf_init(bool reader); -void lf_init(bool reader, bool simulate, bool ledcontrol); +void lf_init(lf_adc_init_mode_t init_mode, lf_adc_edge_mode_t edge_mode, bool ledcontrol); void lf_finalize(bool ledcontrol); size_t lf_detect_field_drop(size_t max); +void lf_reset_field(size_t periods); bool lf_manchester_send_bytes(const uint8_t *frame, size_t frame_len, bool ledcontrol); void lf_modulation(bool modulation); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 39e20f696..776dd5907 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -24,8 +24,9 @@ #include "proxmark3_arm.h" #include "cmd.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "ticks_apis.h" +#include "fpga_apis.h" #include "dbprint.h" #include "util.h" #include "commonutil.h" @@ -36,7 +37,7 @@ #include "lfsampling.h" #include "protocols.h" #include "pmflash.h" -#include "flashmem.h" // persistence on flash +#include "flashmem.h" #include "spiffs.h" // spiffs #include "appmain.h" // print stack @@ -517,6 +518,12 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint16_t period_0, uint 101010101010101[0]000... [5555fe852c5555555555555555fe0000] + + +The current read-write implementation is based on the discontinued model RI-TRP-WR2B-30. +The old model is single page, while the new model is multi page, with different operation instructions and communication formats. +https://e2e.ti.com/support/wireless-connectivity/other-wireless-group/other-wireless/f/other-wireless-technologies-forum/863988/ri-trp-wr2b-30-replacement-part?tisearch=e2e-sitesearch&keymatch=RI-TRP-WR2B# + */ void ReadTItag(bool ledcontrol) { StartTicks(); @@ -554,6 +561,19 @@ void ReadTItag(bool ledcontrol) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // 周期的判断,就是实际上固定的频率采集到的数据,计算实际上所需的过零点的数量,在整个频率内所占用的采集点的数量 + // 比如123khz的数据调制,从0跨越到1需要更多的时间,那实际上所耗费的在固定频率下所采集的数据的数量更多。 + // 模拟固定频率采集的 CROSS_LO 数据(123):000001111100000 + // 模拟固定频率采集的 CROSS_LO 数据(134):000011110000111 + // 以上例子可以描述出大概的数据变化在采集到的数据中的特征 + // 采样点数量所需的计算实际易于理解的公式 + // 123.2khz 一个周期需要 8.116us + // 123.2khz 16个周期需要 129.856us + // 2mhz 采样一个周期需要 0.0000005s = 500ns + // 129.856us / 500ns(0.5us) 就是所需的采样点数量。 + // 所以看16个fsk的过零点所需要的采样点数量,就基本上能猜测出来当前调制的频率是多少。 + // HDX调制16个周期的134khz或者123khz,所以这个判断的方法可以这么工作起来。 + for (i = 0; i < n - 1; i++) { // count cycles by looking for lo to hi zero crossings if ((dest[i] < 0) && (dest[i + 1] > 0)) { @@ -655,17 +675,17 @@ static void WriteTIbyte(uint8_t b) { for (i = 0; i < 8; i++) { if (b & (1 << i)) { // stop modulating antenna 1ms - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); WaitUS(1000); // modulate antenna 1ms - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); WaitUS(1000); } else { // stop modulating antenna 0.3ms - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); WaitUS(300); // modulate antenna 1.7ms - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); WaitUS(1700); } } @@ -683,14 +703,12 @@ void AcquireTiType(bool ledcontrol) { //clear buffer now so it does not interfere with timing later BigBuf_Clear_ext(false); + // TODO DXL Waiting for cross-platform implementation. + // Set up the synchronous serial port AT91C_BASE_PIOA->PIO_PDR = GPIO_SSC_DIN; AT91C_BASE_PIOA->PIO_ASR = GPIO_SSC_DIN; - // steal this pin from the SSP and use it to control the modulation - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST; AT91C_BASE_SSC->SSC_CR = AT91C_SSC_RXEN | AT91C_SSC_TXEN; @@ -704,24 +722,29 @@ void AcquireTiType(bool ledcontrol) { AT91C_BASE_SSC->SSC_TCMR = 0; // Transmit Frame Mode Register AT91C_BASE_SSC->SSC_TFMR = 0; + // iceman, FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER) ?? the code above? can it be replaced? + + // steal this pin from the SSP and use it to control the modulation + gpio_fpga_mod_only_setup(); + if (ledcontrol) LED_D_ON(); - // modulate antenna - HIGH(GPIO_SSC_DOUT); + // start modulate antenna + Gpio_SSC_DOUT_High(); // Charge TI tag for 50ms. WaitMS(50); // stop modulating antenna and listen - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); if (ledcontrol) LED_D_OFF(); i = 0; for (;;) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - buf[i] = AT91C_BASE_SSC->SSC_RHR; // store 32 bit values in buffer + if (FPGA_SSC_RX_Ready()) { + buf[i] = FPGA_SSC_RX_Value(); // store 32 bit values in buffer i++; if (i >= TIBUFLEN) break; } @@ -779,8 +802,7 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc, bool ledcontrol) { if (ledcontrol) LED_A_ON(); // steal this pin from the SSP and use it to control the modulation - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + gpio_fpga_mod_only_setup(); // writing algorithm: // a high bit consists of a field off for 1ms and field on for 1ms @@ -793,7 +815,7 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc, bool ledcontrol) { // finish with 50ms programming time // modulate antenna - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); WaitMS(50); // charge time WriteTIbyte(0xbb); // keyword @@ -810,7 +832,7 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc, bool ledcontrol) { WriteTIbyte((crc >> 8) & 0xff); // crc hi WriteTIbyte(0x00); // write frame lo WriteTIbyte(0x03); // write frame hi - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); WaitMS(50); // programming time if (ledcontrol) LED_A_OFF(); @@ -826,7 +848,6 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc, bool ledcontrol) { // note: a call to FpgaDownloadAndGo(FPGA_BITSTREAM_LF) must be done before, but // this may destroy the bigbuf so be sure this is called before calling SimulateTagLowFrequencyEx void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycles) { - // start us timer StartTicks(); @@ -847,9 +868,7 @@ void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycl else FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; + gpio_fpga_mod_feedback_setup(); uint16_t check = 0; @@ -868,7 +887,7 @@ void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycl // wait until SSC_CLK goes HIGH // used as a simple detection of a reader field? - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { + while (!(Gpio_SSC_CLK_Read())) { WDT_HIT(); if (check == 1000) { if (data_available() || BUTTON_PRESS()) @@ -888,7 +907,7 @@ void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycl check = 0; //wait until SSC_CLK goes LOW - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { + while (Gpio_SSC_CLK_Read()) { WDT_HIT(); if (check == 2000) { if (BUTTON_PRESS() || data_available()) @@ -1665,7 +1684,7 @@ void turn_read_lf_on(uint32_t delay) { FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD); // measure antenna strength. - //int adcval = ((MAX_ADC_LF_VOLTAGE * (SumAdc(ADC_CHAN_LF, 32) >> 1)) >> 14); + //int adcval = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_LF); WaitUS(delay); } diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 8d2bb17d4..25245adec 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -20,14 +20,15 @@ #include "proxmark3_arm.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "ticks_apis.h" +#include "fpga_apis.h" #include "dbprint.h" #include "util.h" #include "lfdemod.h" #include "string.h" // memset #include "appmain.h" // print stack -#include "usb_cdc.h" // real-time sampling +#include "usb_cdc_apis.h" #include "lfops.h" /* @@ -273,7 +274,7 @@ void LFSetupFPGAForADC(int divisor, bool reader_field) { FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | (reader_field ? FPGA_LF_ADC_READER_FIELD : 0)); // Connect the A/D to the peak-detected low-frequency path. - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + SetAdcMuxFor(ADC_MUXSEL_LOPKD); // Now set up the SSC to get the ADC samples that are now streaming at us. FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); @@ -335,12 +336,12 @@ uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, in WDT_HIT(); - if (ledcontrol && (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY)) { + if (ledcontrol && FPGA_SSC_TX_Ready()) { LED_D_ON(); } - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value(); // (RDV4) Test point 8 (TP8) can be used to trigger oscilloscope if (ledcontrol) LED_D_OFF(); @@ -399,6 +400,7 @@ uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, in uint32_t DoAcquisition_default(int trigger_threshold, bool verbose, bool ledcontrol) { return DoAcquisition(1, 8, 0, trigger_threshold, verbose, 0, 0, 0, ledcontrol); } + uint32_t DoAcquisition_config(bool verbose, uint32_t sample_size, bool ledcontrol) { return DoAcquisition(config.decimation , config.bits_per_sample @@ -468,9 +470,11 @@ int ReadLF_realtime(bool reader_field, bool cotag) { uint8_t curr_byte = 0; int return_value = PM3_SUCCESS; - uint32_t sample_buffer_len = AT91C_USB_EP_IN_SIZE; + uint32_t usb_buffer_len = 0, sample_buffer_len; + usb_get_ep_size(NULL, &usb_buffer_len, NULL); + sample_buffer_len = usb_buffer_len; // If sample_buffer_len is not specified, a buffer of random length may be requested. initSampleBuffer(&sample_buffer_len); - if (sample_buffer_len != AT91C_USB_EP_IN_SIZE) { + if (sample_buffer_len != usb_buffer_len) { return PM3_EFAILED; } @@ -503,12 +507,12 @@ int ReadLF_realtime(bool reader_field, bool cotag) { WDT_HIT(); - if ((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY)) { + if (FPGA_SSC_TX_Ready()) { LED_D_ON(); } - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value(); // (RDV4) Test point 8 (TP8) can be used to trigger oscilloscope LED_D_OFF(); @@ -621,12 +625,12 @@ void doT55x7Acquisition(size_t sample_size, bool ledcontrol) { WDT_HIT(); - if (ledcontrol && (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY)) { + if (ledcontrol && FPGA_SSC_TX_Ready()) { LED_D_ON(); } - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value(); if (ledcontrol) LED_D_OFF(); // skip until the first high sample above threshold @@ -700,9 +704,8 @@ void doCotagAcquisition(void) { WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - - volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value(); // find first peak if (firsthigh == false) { @@ -770,8 +773,8 @@ uint16_t doCotagAcquisitionManchester(uint8_t *dest, uint16_t destlen) { } - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value(); // find first peak if (firsthigh == false) { diff --git a/armsrc/lfzx.c b/armsrc/lfzx.c index f95b2c2b7..0f69d4a82 100644 --- a/armsrc/lfzx.c +++ b/armsrc/lfzx.c @@ -20,13 +20,14 @@ #include "BigBuf.h" #include "crc.h" // CRC-8 / Hitag1 / ZX8211 -#include "fpgaloader.h" +#include "fpga_loader.h" #include "dbprint.h" #include "lfops.h" // turn_read_lf_on / off #include "lfadc.h" #include "lfsampling.h" // getSamplingConfig #include "pm3_cmd.h" // struct -#include "ticks.h" +#include "ticks_apis.h" +#include "fpga_apis.h" /* ZX8211 @@ -106,7 +107,7 @@ static void zx8211_setup_read(void) { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); // Connect the A/D to the peak-detected low-frequency path. - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + SetAdcMuxFor(ADC_MUXSEL_LOPKD); // Start the timer StartTicks(); @@ -145,12 +146,12 @@ static void zx_get(bool ledcontrol) { WDT_HIT(); - if (ledcontrol && (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY)) { + if (ledcontrol && FPGA_SSC_TX_Ready()) { LED_D_ON(); } - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_RX_Ready()) { + volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value(); (void)sample; // (RDV4) Test point 8 (TP8) can be used to trigger oscilloscope diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 2162e48af..3dabebad1 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -26,15 +26,16 @@ #include "BigBuf.h" #include "cmd.h" #include "flashmem.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "iso14443a.h" #include "mifaredesfire.h" #include "util.h" #include "commonutil.h" #include "crc16.h" #include "dbprint.h" -#include "ticks.h" -#include "usb_cdc.h" // usb_poll_validate_length +#include "ticks_apis.h" +#include "usb_cdc_apis.h" #include "spiffs.h" // spiffs #include "appmain.h" // print_stack_usage #include "cmac_calc.h" @@ -3431,7 +3432,7 @@ void MifareHasStaticNonce(void) { nt = bytes_to_num(rec, 4); // some cards with static nonce need to be reset before next query - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // TODO DXL Can we use mf_reset_card(); ? LEDsoff(); CHK_TIMEOUT(); diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c index 7553453bc..dcc8d7234 100644 --- a/armsrc/mifaredesfire.c +++ b/armsrc/mifaredesfire.c @@ -23,13 +23,14 @@ #include "desfire_crypto.h" #include "cmd.h" #include "dbprint.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "iso14443a.h" #include "crc16.h" #include "commonutil.h" #include "util.h" #include "mifare.h" -#include "ticks.h" +#include "ticks_apis.h" #include "protocols.h" #define MAX_APPLICATION_COUNT 28 diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 989345493..29b69689a 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -35,7 +35,7 @@ #include "BigBuf.h" #include "string.h" #include "mifareutil.h" -#include "fpgaloader.h" +#include "fpga_apis.h" #include "proxmark3_arm.h" #include "cmd.h" #include "protocols.h" @@ -44,7 +44,7 @@ #include "commonutil.h" #include "crc16.h" #include "dbprint.h" -#include "ticks.h" +#include "ticks_apis.h" #include "parity.h" static bool IsKeyBReadable(uint8_t blockNo) { diff --git a/armsrc/mifaresniff_disabled.c b/armsrc/mifaresniff_disabled.c index ac6142e91..95f4bb61f 100644 --- a/armsrc/mifaresniff_disabled.c +++ b/armsrc/mifaresniff_disabled.c @@ -83,8 +83,8 @@ void RAMFUNC SniffMifare(uint8_t param) { // Setup and start DMA. // set transfer address and number of bytes. Start transfer. - if (FpgaSetupSscDma(dmaBuf, DMA_BUFFER_SIZE) == false) { - if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + if (FpgaSetupSscRxDmaRepeat(dmaBuf, DMA_BUFFER_SIZE) == false) { + if (g_dbglevel > DBG_ERROR) Dbprintf("[!] FpgaSetupSscRxDmaRepeat failed. Exiting"); return; } @@ -111,7 +111,7 @@ void RAMFUNC SniffMifare(uint8_t param) { maxDataLen = 0; ReaderIsActive = false; TagIsActive = false; - FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); // set transfer address and number of bytes. Start transfer. + FpgaSetupSscRxDmaRepeat((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); // set transfer address and number of bytes. Start transfer. } } */ @@ -119,7 +119,7 @@ void RAMFUNC SniffMifare(uint8_t param) { // number of bytes we have processed so far int register readBufDataP = data - dmaBuf; // number of bytes already transferred - int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; + int register dmaBufDataP = DMA_BUFFER_SIZE - FPGA_SSC_DMA_RX_Remaining_Length(); if (readBufDataP <= dmaBufDataP) // we are processing the same block of data which is currently being transferred dataLen = dmaBufDataP - readBufDataP; // number of bytes still to be processed else @@ -135,16 +135,8 @@ void RAMFUNC SniffMifare(uint8_t param) { } if (dataLen < 1) continue; - // primary buffer was stopped ( <-- we lost data! - if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t)dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; - Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary - } - // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + if (FPGA_SSC_DMA_RX_Done()) { + FPGA_SSC_DMA_RX_Refresh_Repeat(dmaBuf, DMA_BUFFER_SIZE); } LED_A_OFF(); diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index e223b14fb..d9542fd60 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -24,7 +24,7 @@ #include "string.h" #include "BigBuf.h" #include "iso14443a.h" -#include "ticks.h" +#include "ticks_apis.h" #include "dbprint.h" #include "parity.h" #include "commonutil.h" diff --git a/armsrc/pcf7931.c b/armsrc/pcf7931.c index 8eefd354e..2ca53d15e 100644 --- a/armsrc/pcf7931.c +++ b/armsrc/pcf7931.c @@ -18,8 +18,9 @@ #include "proxmark3_arm.h" #include "cmd.h" #include "BigBuf.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "fpga_apis.h" +#include "ticks_apis.h" #include "dbprint.h" #include "util.h" #include "lfsampling.h" @@ -564,10 +565,8 @@ void SendCmdPCF7931(uint32_t *tab, bool ledcontrol) { break; } - // steal this pin from the SSP and use it to control the modulation - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + gpio_fpga_mod_only_setup(); //initialization of the timer AT91C_BASE_PMC->PMC_PCER |= (0x1 << AT91C_ID_TC0); @@ -582,19 +581,19 @@ void SendCmdPCF7931(uint32_t *tab, bool ledcontrol) { tempo = AT91C_BASE_TC0->TC_CV; for (u = 0; tab[u] != 0; u += 3) { // modulate antenna - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); while ((uint32_t)tempo < tab[u]) { tempo = AT91C_BASE_TC0->TC_CV; } // stop modulating antenna - LOW(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_Low(); while ((uint32_t)tempo < tab[u + 1]) { tempo = AT91C_BASE_TC0->TC_CV; } // modulate antenna - HIGH(GPIO_SSC_DOUT); + Gpio_SSC_DOUT_High(); while ((uint32_t)tempo < tab[u + 2]) { tempo = AT91C_BASE_TC0->TC_CV; } diff --git a/armsrc/sam_common.c b/armsrc/sam_common.c index 4f1002d50..843d6c409 100644 --- a/armsrc/sam_common.c +++ b/armsrc/sam_common.c @@ -23,7 +23,7 @@ #include "proxmark3_arm.h" #include "BigBuf.h" #include "commonutil.h" -#include "ticks.h" +#include "ticks_apis.h" #include "dbprint.h" #include "i2c.h" #include "iso15693.h" diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index 147eeeac0..093558dcf 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -23,13 +23,14 @@ #include "BigBuf.h" #include "cmd.h" #include "commonutil.h" -#include "ticks.h" +#include "ticks_apis.h" #include "dbprint.h" #include "i2c.h" #include "iso15693.h" #include "protocols.h" #include "optimized_cipher.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "pm3_cmd.h" /** diff --git a/armsrc/sam_sc.c b/armsrc/sam_sc.c index dad76591d..47945eb2d 100644 --- a/armsrc/sam_sc.c +++ b/armsrc/sam_sc.c @@ -25,7 +25,7 @@ #include "i2c.h" // ISO7816_MAX_FRAME, I2C_Reset_EnterMainProgram #include "proxmark3_arm.h" #include "sam_common.h" -#include "ticks.h" +#include "ticks_apis.h" #include "util.h" // LED_D_ON, LEDsoff // Tracks whether the SIM module has been initialised since the last reset. diff --git a/armsrc/sam_seos.c b/armsrc/sam_seos.c index 3f8c806e9..9d8d2c26c 100644 --- a/armsrc/sam_seos.c +++ b/armsrc/sam_seos.c @@ -29,12 +29,13 @@ #include "BigBuf.h" #include "cmd.h" #include "commonutil.h" -#include "ticks.h" +#include "ticks_apis.h" #include "dbprint.h" #include "i2c.h" #include "protocols.h" #include "optimized_cipher.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "pm3_cmd.h" #include "cmd.h" diff --git a/armsrc/secc.c b/armsrc/secc.c index 9a20cfcf3..85166b656 100644 --- a/armsrc/secc.c +++ b/armsrc/secc.c @@ -25,7 +25,8 @@ #include "dbprint.h" #include "BigBuf.h" // DMA_BUFFER_SIZE, MAX_PARITY_SIZE #include "crc16.h" // AddCrc14A, CheckCrc14A -#include "fpgaloader.h" // FpgaWriteConfWord, FpgaSetupSscDma +#include "fpga_loader.h" // FpgaWriteConfWord, FpgaSetupSscDma +#include "fpga_apis.h" // FPGA_MAJOR_MODE_HF_ISO14443A #include "desfire_crypto.h" // tdes_nxp_send #include "mbedtls/des.h" // mbedtls_des_*, mbedtls_des3_* #include "iso14443a.h" // ReaderTransmit, ReaderReceive, iso14a_get/set_timeout, iso14a_get/toggle_pcb_blocknum, MAX_ISO14A_TIMEOUT @@ -34,7 +35,7 @@ #include "cmd.h" // reply_ng #include "pm3_cmd.h" // CMD_HF_HIDCONFIG_SIM, CMD_HF_HIDCONFIG_SNIFF #include "dbprint.h" // Dbprintf, LED_* -#include "ticks.h" // WDT_HIT +#include "ticks_apis.h" // WDT_HIT #include "protocols.h" // ISO14443A_CMD_* constants // --------------------------------------------------------------------------- @@ -404,8 +405,8 @@ bool hid_config_card_jam(const uint8_t *cmd, int len, uint8_t *dma_buf) { // Restore sniffer FPGA mode and re-arm DMA FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER); - if (FpgaSetupSscDma(dma_buf, DMA_BUFFER_SIZE) == false) { - if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + if (FpgaSetupSscRxDmaRepeat(dma_buf, DMA_BUFFER_SIZE) == false) { + if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscRxDmaRepeat failed. Exiting"); return false; } diff --git a/armsrc/seos.c b/armsrc/seos.c index 6e2b40798..77104ef64 100644 --- a/armsrc/seos.c +++ b/armsrc/seos.c @@ -20,7 +20,8 @@ #include "iso14443a.h" #include "BigBuf.h" -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "string.h" #include "dbprint.h" #include "protocols.h" diff --git a/armsrc/start.c b/armsrc/start.c index a5e385ae7..95d802a31 100644 --- a/armsrc/start.c +++ b/armsrc/start.c @@ -18,9 +18,6 @@ // with the linker script. //----------------------------------------------------------------------------- -#ifndef __START_H -#define __START_H - #include "proxmark3_arm.h" #include "appmain.h" #ifdef WITH_COMPRESSION @@ -28,19 +25,32 @@ #endif #include "BigBuf.h" #include "string.h" -#include "ticks.h" +#include "ticks_apis.h" +#include "gpio_apis.h" +#include "sys_apis.h" extern common_area_t g_common_area; +// export by ldscript, is a fixed space in ram, the data from rom will copy and decompress(optional) to ram. extern uint32_t __data_src_start__[], __data_start__[], __data_end__[], __bss_start__[], __bss_end__[]; +// Define an empty unit test main program entry, +// and if the developer compiles the source file that implements the UnitTestMain function, +// this empty function will be automatically overwritten. +void __attribute__((weak)) UnitTestMain(void); +void UnitTestMain(void) { + // Nothing to do... + // In general, this function will be overridden. +} + #ifdef WITH_COMPRESSION static void uncompress_data_section(void) { int avail_in; memcpy(&avail_in, __data_src_start__, sizeof(int)); - int avail_out = (uint32_t)__data_end__ - (uint32_t)__data_start__; // uncompressed size. Correct. + // if compressed, the head 4byte of '.data' section will be uncompressed size. + int avail_out = (uint32_t) __data_end__ - (uint32_t) __data_start__; // uncompressed size. Correct. // uncompress data segment to RAM - char *p = (char *)__data_src_start__; - int res = LZ4_decompress_safe(p + 4, (char *)__data_start__, avail_in, avail_out); + char *p = (char *) __data_src_start__; + int res = LZ4_decompress_safe(p + 4, (char *) __data_start__, avail_in, avail_out); if (res < 0) { while (true) { LED_A_INV(); @@ -80,6 +90,12 @@ void Vector(void) { uint32_t *bss_dst = __bss_start__; while (bss_dst < __bss_end__) *bss_dst++ = 0; + // For some platforms (such as AT32), it is best to init the system clock after jump to app, + // otherwise the USB may not work. + // Because after jumping from boot to app, some variables in RAM will be lost. + ConfigSystemClocks(); + // Run the unit test(If enable) + UnitTestMain(); + // Run App main loop AppMain(); } -#endif diff --git a/armsrc/thinfilm.c b/armsrc/thinfilm.c index b5a06bf11..96d8ef07d 100644 --- a/armsrc/thinfilm.c +++ b/armsrc/thinfilm.c @@ -23,8 +23,10 @@ #include "appmain.h" #include "BigBuf.h" #include "iso14443a.h" -#include "fpgaloader.h" -#include "ticks.h" +#include "fpga_loader.h" +#include "ticks_apis.h" +#include "fpga_apis.h" +#include "rssi_apis.h" #include "dbprint.h" #include "util.h" @@ -59,7 +61,7 @@ void ReadThinFilm(void) { #define SEC_F 0x00 static uint16_t ReadReaderField(void) { - return AvgAdc(ADC_CHAN_HF); + return AdcRssiAvg(ADC_RSSI_CH_HF); } static void CodeThinfilmAsTag(const uint8_t *cmd, uint16_t len) { @@ -86,34 +88,35 @@ static int EmSendCmdThinfilmRaw(const uint8_t *resp, uint16_t respLen) { uint32_t ThisTransferTime ; // clear receiving shift register and holding register - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); - b = AT91C_BASE_SSC->SSC_RHR; + FPGA_SSC_RX_READY_WAIT(); + b = FPGA_SSC_RX_Value(); (void) b; // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) for (uint8_t j = 0; j < 5; j++) { // allow timeout - better late than never - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); - if (AT91C_BASE_SSC->SSC_RHR) { + FPGA_SSC_RX_READY_WAIT(); + if (FPGA_SSC_RX_Value()) { break; } } while ((ThisTransferTime = GetCountSspClk()) & 0x00000007); // Clear TXRDY: - AT91C_BASE_SSC->SSC_THR = SEC_F; + FPGA_SSC_TX_Value(SEC_F); uint16_t FpgaSendQueueDelay = 0; // send cycle size_t i = 0; for (; i < respLen;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = resp[i++]; - FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(resp[i++]); + FPGA_SSC_RX_READY_WAIT(); + FpgaSendQueueDelay = (uint8_t)FPGA_SSC_RX_Value(); } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); + if (FPGA_SSC_RX_Ready()) { + b = (uint8_t)(FPGA_SSC_RX_Value()); (void)b; } if (BUTTON_PRESS()) break; @@ -124,9 +127,10 @@ static int EmSendCmdThinfilmRaw(const uint8_t *resp, uint16_t respLen) { fpga_queued_bits >>= 3; // divide by 8 (again?) fpga_queued_bits += 1u; for (i = 0; i <= fpga_queued_bits;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = SEC_F; - FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (FPGA_SSC_TX_Ready()) { + FPGA_SSC_TX_Value(SEC_F); + FPGA_SSC_RX_READY_WAIT(); + FpgaSendQueueDelay = (uint8_t)FPGA_SSC_RX_Value(); i++; } } @@ -147,7 +151,7 @@ void SimulateThinFilm(uint8_t *data, size_t len) { Dbprintf("Simulate " _YELLOW_("%i-bit Thinfilm") " tag", len * 8); // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + SetAdcMuxFor(ADC_MUXSEL_HIPKD); // Set up the synchronous serial port FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); @@ -185,14 +189,12 @@ void SimulateThinFilm(uint8_t *data, size_t len) { uint16_t hf_av = ReadReaderField(); + /* TODO DXL: Do not use the ADC value directly, which will result in cross platform failure. if (hf_av < hf_baseline) { hf_baseline = hf_av; } - if (hf_av > hf_baseline + 10) { - EmSendCmdThinfilmRaw(ts->buf, ts->max); - if (len == 16) { // wait 3.6ms SpinDelayUs(3600); @@ -201,6 +203,23 @@ void SimulateThinFilm(uint8_t *data, size_t len) { SpinDelayUs(2400); } } + */ + + if (hf_av < hf_baseline) { + hf_baseline = hf_av; + } else if (hf_av > hf_baseline) { + if (AdcRssiDataToMilliVolt(hf_av - hf_baseline, ADC_RSSI_CH_HF) > 1375) { + EmSendCmdThinfilmRaw(ts->buf, ts->max); + if (len == 16) { + // wait 3.6ms + SpinDelayUs(3600); + } else { + // wait 2.4ms + SpinDelayUs(2400); + } + } + } + } LED_A_OFF(); diff --git a/armsrc/util.c b/armsrc/util.c index 0df89352d..c41c0c57a 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -19,11 +19,11 @@ #include "util.h" #include "proxmark3_arm.h" -#include "ticks.h" +#include "ticks_apis.h" #include "commonutil.h" #include "dbprint.h" #include "string.h" -#include "usb_cdc.h" +#include "usb_cdc_apis.h" #include "usart.h" size_t nbytes(size_t nbits) { @@ -231,37 +231,25 @@ void SpinUp(uint32_t speed) { LED_D_OFF(); } - // Determine if a button is double clicked, single clicked, // not clicked, or held down (for ms || 1sec) // In general, don't use this function unless you expect a // double click, otherwise it will waste 500ms -- use BUTTON_HELD instead +// Note: StartTickCount required. int BUTTON_CLICKED(int ms) { - // Up to 500ms in between clicks to mean a double click - // timer counts in 21.3us increments (1024/48MHz) - // WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) - if (ms > 1390) { - if (g_dbglevel >= DBG_ERROR) Dbprintf(_RED_("Error, BUTTON_CLICKED called with %i > 1390"), ms); - ms = 1390; - } - int ticks = ((MCK / 1000) * (ms ? ms : 1000)) >> 10; - // If we're not even pressed, forget about it! if (BUTTON_PRESS() == false) return BUTTON_NO_CLICK; - // Borrow a PWM unit for my real-time clock - AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); - // 48 MHz / 1024 gives 46.875 kHz - AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); - AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; - AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; - - uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + ms = ms ? ms : 1000; // use ms param if valid. int letoff = 0; for (;;) { - uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + + // Using a very short delay period to count how long has passed is much safer than using other methods, + // as other timers may be stopped/reset + SpinDelay(1); + --ms; // We haven't let off the button yet if (!letoff) { @@ -269,15 +257,14 @@ int BUTTON_CLICKED(int ms) { if (BUTTON_PRESS() == false) { letoff = 1; - // reset our timer for 500ms - start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; - ticks = ((MCK / 1000) * (500)) >> 10; + // reset our timer for 500ms next press waiting + ms = 500; } // Still haven't let it off else // Have we held down a full second? - if (now == (uint16_t)(start + ticks)) + if (ms <= 0) return BUTTON_HOLD; } @@ -287,8 +274,8 @@ int BUTTON_CLICKED(int ms) { if (BUTTON_PRESS()) return BUTTON_DOUBLE_CLICK; - // Have we ran out of time to double click? - else if (now == (uint16_t)(start + ticks)) + // Have we ran out of time to double click? + else if (ms <= 0) // At least we did a single click return BUTTON_SINGLE_CLICK; @@ -300,40 +287,29 @@ int BUTTON_CLICKED(int ms) { } // Determine if a button is held down +// Note: StartTickCount required. int BUTTON_HELD(int ms) { - // timer counts in 21.3us increments (1024/48MHz) - // WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) - if (ms > 1390) { - if (g_dbglevel >= DBG_ERROR) Dbprintf(_RED_("Error, BUTTON_HELD called with %i > 1390"), ms); - ms = 1390; - } - // If button is held for one second - int ticks = (48000 * (ms ? ms : 1000)) >> 10; - // If we're not even pressed, forget about it! if (BUTTON_PRESS() == false) { return BUTTON_NO_CLICK; } - // Borrow a PWM unit for my real-time clock - AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); - // 48 MHz / 1024 gives 46.875 kHz - AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); - AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; - AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; - - uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + ms = ms ? ms : 1000; // use ms param if valid. for (;;) { - uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; // As soon as our button let go, we didn't hold long enough if (BUTTON_PRESS() == false) { return BUTTON_SINGLE_CLICK; } + // Using a very short delay period to count how long has passed is much safer than using other methods, + // as other timers may be stopped/reset + SpinDelay(1); + --ms; + // Have we waited the full second? - else if (now == (uint16_t)(start + ticks)) { + if (ms <= 0) { return BUTTON_HOLD; } @@ -365,37 +341,6 @@ bool data_available_fast(void) { #endif } -uint32_t flash_size_from_cidr(uint32_t cidr) { - uint8_t nvpsiz = (cidr & 0xF00) >> 8; - switch (nvpsiz) { - case 0: - return 0; - case 1: - return 8 * 1024; - case 2: - return 16 * 1024; - case 3: - return 32 * 1024; - case 5: - return 64 * 1024; - case 7: - return 128 * 1024; - case 9: - return 256 * 1024; - case 10: - return 512 * 1024; - case 12: - return 1024 * 1024; - case 14: - default: // for 'reserved' values, guess 2MB - return 2048 * 1024; - } -} - -uint32_t get_flash_size(void) { - return flash_size_from_cidr(*AT91C_DBGU_CIDR); -} - // Combined function to convert an unsigned int to an array of hex values corresponding to the last three bits of k1 void convertToHexArray(uint32_t num, uint8_t *partialkey) { char binaryStr[25]; // 24 bits for binary representation + 1 for null terminator diff --git a/armsrc/util.h b/armsrc/util.h index fd99ac73e..d69e754d3 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -30,11 +30,11 @@ // Basic macros #ifndef SHORT_COIL -#define SHORT_COIL() LOW(GPIO_SSC_DOUT) +#define SHORT_COIL() Gpio_SSC_DOUT_Low() #endif #ifndef OPEN_COIL -#define OPEN_COIL() HIGH(GPIO_SSC_DOUT) +#define OPEN_COIL() Gpio_SSC_DOUT_High() #endif #ifndef BYTEx @@ -102,7 +102,4 @@ int BUTTON_HELD(int ms); bool data_available(void); bool data_available_fast(void); -uint32_t flash_size_from_cidr(uint32_t cidr); -uint32_t get_flash_size(void); - #endif diff --git a/bootrom/CMakeLists.txt b/bootrom/CMakeLists.txt new file mode 100644 index 000000000..b4d50bb09 --- /dev/null +++ b/bootrom/CMakeLists.txt @@ -0,0 +1,131 @@ +cmake_minimum_required(VERSION 3.20) + +# Fix error when 'compile a simple test program.' +include(${CMAKE_CURRENT_LIST_DIR}/../tools/FixCompileTest.cmake) + +# Ensure direct bootrom builds also select ARM cross-compilers. +include(${CMAKE_CURRENT_LIST_DIR}/../tools/ToolchainForArm.cmake) + +project(bootrom C ASM) # Set variables needs before the project defining. + +# Hardware abstraction layer for ARM platform. +include(${CMAKE_CURRENT_LIST_DIR}/../common_arm/Hal.cmake) + +# Include the MKVersion script for generating version_pm3.c +include(${CMAKE_CURRENT_LIST_DIR}/../tools/MKVersionScript.cmake) + +# --- + +set(OBJDIR ${CMAKE_BINARY_DIR}/obj) +set(DEFAULT_VERSION_C ${CMAKE_CURRENT_LIST_DIR}/../common/default_version_pm3.c) +set(VERSION_PM3_C ${CMAKE_CURRENT_BINARY_DIR}/version_pm3.c) + +set(INC_DIRS + ${CMAKE_CURRENT_LIST_DIR}/../include + ${CMAKE_CURRENT_LIST_DIR}/../common + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/sys + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/wdt + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/flash_code + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/gpio + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/usb + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/ticks +) + +set(THUMBSRC + ${CMAKE_CURRENT_LIST_DIR}/bootrom.c + ${VERSION_PM3_C} + ../common_arm/usb/usb_cdc_desc.c) + +if (PM5) + set(THUMBSRC ${THUMBSRC} + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/sys/sys_hw_at32.c + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/ticks/ticks_hw_at32.c + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/gpio/gpio_hw_at32.c + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/usb/usb_cdc_at32.c + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/flash_code/flash_code_hw_at32.c + ) + set(ASMSRC flash-reset-at32.s ram-reset-at32.s) +else () + set(THUMBSRC ${THUMBSRC} + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/sys/sys_hw_at91.c + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/ticks/ticks_hw_at91.c + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/gpio/gpio_hw_at91.c + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/usb/usb_cdc_at91.c + ${CMAKE_CURRENT_LIST_DIR}/../common_arm/flash_code/flash_code_hw_at91.c + ) + set(ASMSRC flash-reset-at91.s ram-reset-at91.s) +endif () + +# stdint.h provided locally until GCC 4.5 becomes C99 compliant +set(APP_CFLAGS -I. -ffunction-sections -fdata-sections -DAS_BOOTROM) + +# stack-protect , no-pie reduces size on Gentoo Hardened 8.2 gcc, no-common makes sure uninitialized vars don't end up in COMMON area +set(APP_CFLAGS ${APP_CFLAGS} -fno-stack-protector -fno-pie -fno-common) + +if ("${PLATFORM_DEFS}" MATCHES "WITH_FLASH") + set(INC_DIRS ${INC_DIRS} ${CMAKE_CURRENT_LIST_DIR}/../common_arm/flash_data) + if (PM5) + set(SRC_FLASH ../common_arm/flash_data/flashmem_hw_at32.c) + set(SRC_TICKS ../common_arm/ticks/ticks_hw_at32.c) + else () + set(SRC_FLASH ../common_arm/flash_data/flashmem_hw_at91.c) + set(SRC_TICKS ../common_arm/ticks/ticks_hw_at91.c) + endif () + set(SRC_FLASH ${SRC_FLASH} ../common_arm/flash_data/flashmem_core.c) + set(SRC_TICKS ${SRC_TICKS} ../common_arm/ticks/ticks_core.c) + set(THUMBSRC ${THUMBSRC} ${SRC_FLASH} ${SRC_TICKS}) +endif () + +if (PM5) + set(LDSCRIPT ${CMAKE_CURRENT_LIST_DIR}/ldscript-flash-at32) +else () + set(LDSCRIPT ${CMAKE_CURRENT_LIST_DIR}/ldscript-flash-at91) +endif () + +set(APP_CFLAGS ${APP_CFLAGS} ${PLATFORM_DEFS}) + +# Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC +include(${CMAKE_CURRENT_LIST_DIR}/../common_arm/Common.cmake) + +if (PM5) + include(${CMAKE_CURRENT_LIST_DIR}/../armlib/pm5_at32_armlib.cmake) + set(LIBS ${LIBS} pm5_at32_armlib) +endif () + +# Fix: warning: bootrom\bootrom.elf has a LOAD segment with RWX permissions +set(CROSS_LDFLAGS ${CROSS_LDFLAGS} -Wl,--no-warn-rwx-segments) + +# Create version_pm3.c before bootrom build. +add_custom_command( + OUTPUT ${VERSION_PM3_C} + DEPENDS ${DEFAULT_VERSION_C} + COMMAND ${MKVERSION_CMD} ${VERSION_PM3_C} || ${CMAKE_COMMAND} -E copy ${DEFAULT_VERSION_C} ${VERSION_PM3_C} + COMMENT "Call mkversion.xx script to generate 'version_pm3.c', if mkversion return fail, fallback is default_version_pm3.c" + VERBATIM +) + +# ---------------- Output dir ---------------- +file(MAKE_DIRECTORY ${OBJDIR}) +get_filename_component(OUTPUT_BOOTROM_HEX ${OBJDIR}/${PROJECT_NAME}.hex ABSOLUTE) +get_filename_component(OUTPUT_BOOTROM_BIN ${OBJDIR}/${PROJECT_NAME}.bin ABSOLUTE) + +add_executable(bootrom.elf ${THUMBSRC} ${ASMSRC}) +set_target_properties(bootrom.elf PROPERTIES # The 'bootrom.elf' output to ${OBJDIR} + RUNTIME_OUTPUT_DIRECTORY "${OBJDIR}" + OUTPUT_NAME "bootrom" + SUFFIX ".elf" # OK, OBJDIR + OUTPUT_NAME + SUFFIX = /xxx/obj/bootrom.elf +) +target_link_options(bootrom.elf PRIVATE -Wl,-Map=${PROJECT_BINARY_DIR}/${PROJECT_NAME}.map) # output map file +target_link_options(bootrom.elf PRIVATE -L ${CMAKE_CURRENT_LIST_DIR} -T ${LDSCRIPT} ${CROSS_LDFLAGS}) +target_compile_options(bootrom.elf PRIVATE ${CROSS_CFLAGS}) # !!!! WARN !!!! No -mthumb on here +set_source_files_properties(${THUMBSRC} PROPERTIES COMPILE_OPTIONS "-mthumb") # THUMBSRC is '-mthumb' flag required. +target_include_directories(bootrom.elf PRIVATE ${INC_DIRS}) +target_link_libraries(bootrom.elf ${LIBS}) + +# Generate HEX and BIN files for fullimage finally +add_custom_command(TARGET bootrom.elf POST_BUILD + # COMMAND ${CMAKE_COMMAND} -E echo "[=] GEN(10) bootrom.elf(HEX,BIN)" + COMMAND ${CMAKE_OBJCOPY} -Oihex $ ${OUTPUT_BOOTROM_HEX} + COMMAND ${CMAKE_OBJCOPY} -Obinary $ ${OUTPUT_BOOTROM_BIN} + COMMENT "Build bootrom.hex & bootrom.bin" +) diff --git a/bootrom/Makefile b/bootrom/Makefile index 6a662dede..519e14800 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -17,13 +17,39 @@ # Makefile for bootrom, see ../common_arm/Makefile.common for common settings #----------------------------------------------------------------------------- +# Proxmark5 requires armlib. +include ../armlib/Exports.mk + +# HAL for platform +ifeq ($(PLATFORM),PM5) + SRC_SYS = sys_hw_at32.c + SRC_TICKS = ticks_hw_at32.c + SRC_GPIO = gpio_hw_at32.c + SRC_USB_CDC = usb_cdc_at32.c + SRC_FLASH_CODE = flash_code_hw_at32.c + SRC_FLASH_DATA = flashmem_hw_at32.c + SRC_STARTUP = flash-reset-at32.s ram-reset-at32.s + LD_SCRIPT = ldscript-flash-at32 + APP_CFLAGS += -mcpu=cortex-m4 -DPM5 + CROSS_LDFLAGS += -mcpu=cortex-m4 + ARMLIB_OBJ = $(OBJDIR)/$(ARMLIB_EXPORT_LIBNAME) + LIBS += $(ARMLIB_OBJ) +else + SRC_SYS = sys_hw_at91.c + SRC_TICKS = ticks_hw_at91.c + SRC_GPIO = gpio_hw_at91.c + SRC_USB_CDC = usb_cdc_at91.c + SRC_FLASH_CODE = flash_code_hw_at91.c + SRC_FLASH_DATA = flashmem_hw_at91.c + SRC_STARTUP = flash-reset-at91.s ram-reset-at91.s + LD_SCRIPT = ldscript-flash-at91 +endif + # DO NOT use thumb mode in the phase 1 bootloader since that generates a section with glue code ARMSRC = -THUMBSRC = usb_cdc.c \ - clocks.c \ - bootrom.c +THUMBSRC = bootrom.c $(SRC_SYS) $(SRC_TICKS) $(SRC_GPIO) $(SRC_USB_CDC) $(SRC_FLASH_CODE) usb_cdc_desc.c -ASMSRC = ram-reset.s flash-reset.s +ASMSRC = $(SRC_STARTUP) VERSIONSRC = version_pm3.c ## There is a strange bug with the linker: Sometimes it will not emit the glue to call @@ -34,18 +60,18 @@ VERSIONSRC = version_pm3.c # THUMBSRC := # stdint.h provided locally until GCC 4.5 becomes C99 compliant -APP_CFLAGS = -I. -DAS_BOOTROM - +APP_CFLAGS += -I. -DAS_BOOTROM # no-common makes sure uninitialized vars don't end up in COMMON area APP_CFLAGS += -fno-common +# For PM5, armlib is required +APP_CFLAGS += $(ARMLIB_EXPORT_CFLAGS) ifneq (,$(findstring WITH_FLASH,$(PLATFORM_DEFS))) APP_CFLAGS += -DWITH_FLASH APP_CFLAGS += -I../common_arm - THUMBSRC += flashmem.c ticks.c + THUMBSRC += ticks_core.c flashmem_core.c $(SRC_FLASH_DATA) endif - # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC include ../common_arm/Makefile.common @@ -67,10 +93,16 @@ tarbin: $(OBJS) $(info [=] GEN $@) $(Q)$(TAR) $(TARFLAGS) ../proxmark3-$(platform)-bin.tar $(OBJS:%=bootrom/%) $(OBJS:%.s19=bootrom/%.elf) -$(OBJDIR)/bootrom.elf: $(VERSIONOBJ) $(ASMOBJ) $(ARMOBJ) $(THUMBOBJ) +ifeq ($(PLATFORM),PM5) +$(ARMLIB_OBJ): + $(info [=] MAKE $(notdir $@)) + @$(MAKE) --no-print-directory -f ../armlib/Makefile +endif + +$(OBJDIR)/bootrom.elf: $(VERSIONOBJ) $(ASMOBJ) $(ARMOBJ) $(THUMBOBJ) $(ARMLIB_OBJ) $(info [=] LD $@) # Using -T instead of -Wl,-T is needed to prevent the linker from using the default ldscript when using picolibc instead of newlib - $(Q)$(CROSS_LD) $(CROSS_LDFLAGS) -T ldscript-flash -Wl,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^ $(LIBS) + $(Q)$(CROSS_LD) $(CROSS_LDFLAGS) -T $(LD_SCRIPT) -Wl,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^ $(LIBS) clean: $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.o diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index ddd4f731c..3583adbd9 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -17,21 +17,42 @@ // Main code for the bootloader //----------------------------------------------------------------------------- -#include "clocks.h" -#include "usb_cdc.h" +#include "commonutil.h" +#include "flash_code_apis.h" +#include "usb_cdc_apis.h" +#include "gpio_apis.h" +#include "sys_apis.h" +#include "ticks_apis.h" +#include "proxmark3_arm.h" #ifdef WITH_FLASH #include "flashmem.h" #endif -#include "proxmark3_arm.h" #define DEBUG 0 +// At present, in the case of at32 with a flash size of 4m byte, a sector is 4096 bytes. +// If there is a larger size sector in the future, remember to modify it here. +#define FLASH_MIN_UNIT_DATA_SIZE 4096 +typedef struct { + uint32_t count; + uint32_t data[FLASH_MIN_UNIT_DATA_SIZE / sizeof(uint32_t)]; +} flash_min_unit_data_t; + +// An information segment memory shared between bootrom and osimage. common_area_t g_common_area __attribute__((section(".commonarea"))); +// The start address & end address of flash for writing. uint32_t start_addr, end_addr; +// Is bootrom unlocked? if true, the bootrom can be overwritten. bool bootrom_unlocked; -extern uint32_t _bootrom_start[], _bootrom_end[], _flash_start[], _flash_end[], _osimage_entry[], __bss_start__[], __bss_end__[]; +// Buffer the firmware block data from USB, and write it to FLASH once when the minimum write unit is reached. +flash_min_unit_data_t flash_min_unit_data; +// Define in link script(ld) +extern uint32_t _bootrom_start[], _bootrom_end[], _flash_start[], _flash_end[], __bss_start__[], __bss_end__[]; +extern uint32_t _osimage_entry[], _stack_start[], _stack_end[]; + +// Send an old frame response packet. static int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) { PacketResponseOLD txcmd; @@ -56,6 +77,18 @@ static int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, return usb_write((uint8_t *)&txcmd, sizeof(PacketResponseOLD)); } +// Check the table to see if the magic is valid. +// TODO DXL Reuse functions similar to CheckValidInformationMagic? +static bool is_valid_magic(int magic) { + int magics[] = { VERSION_INFORMATION_MAGIC_PM3V, VERSION_INFORMATION_MAGIC_PM5V }; + for (int i = 0; i < ARRAYLEN(magics); i++) { + if (magics[i] == magic) { + return true; + } + } + return false; +} + #if DEBUG static void DbpString(char *str) { uint8_t len = 0; @@ -66,60 +99,10 @@ static void DbpString(char *str) { } #endif -static void ConfigClocks(void) { - // we are using a 16 MHz crystal as the basis for everything - // slow clock runs at 32kHz typical regardless of crystal - - // enable system clock and USB clock - AT91C_BASE_PMC->PMC_SCER |= AT91C_PMC_PCK | AT91C_PMC_UDP; - - // enable the clock to the following peripherals - AT91C_BASE_PMC->PMC_PCER = - (1 << AT91C_ID_PIOA) | - (1 << AT91C_ID_ADC) | - (1 << AT91C_ID_SPI) | - (1 << AT91C_ID_SSC) | - (1 << AT91C_ID_PWMC) | - (1 << AT91C_ID_UDP); - - mck_from_slck_to_pll(); -} - static void Fatal(void) { for (;;) {}; } -static uint32_t flash_size_from_cidr(uint32_t cidr) { - uint8_t nvpsiz = (cidr & 0xF00) >> 8; - switch (nvpsiz) { - case 0: - return 0; - case 1: - return 8 * 1024; - case 2: - return 16 * 1024; - case 3: - return 32 * 1024; - case 5: - return 64 * 1024; - case 7: - return 128 * 1024; - case 9: - return 256 * 1024; - case 10: - return 512 * 1024; - case 12: - return 1024 * 1024; - case 14: - default: // for 'reserved' values, guess 2MB - return 2048 * 1024; - } -} - -static uint32_t get_flash_size(void) { - return flash_size_from_cidr(*AT91C_DBGU_CIDR); -} - static void UsbPacketReceived(uint8_t *packet) { bool ack = true; PacketCommandOLD *c = (PacketCommandOLD *)packet; @@ -137,7 +120,8 @@ static void UsbPacketReceived(uint8_t *packet) { DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH | DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO | DEVICE_INFO_FLAG_UNDERSTANDS_VERSION | - DEVICE_INFO_FLAG_UNDERSTANDS_READ_MEM; + DEVICE_INFO_FLAG_UNDERSTANDS_READ_MEM | + DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_TYPE; if (g_common_area.flags.osimage_present) { arg0 |= DEVICE_INFO_FLAG_OSIMAGE_PRESENT; @@ -149,11 +133,18 @@ static void UsbPacketReceived(uint8_t *packet) { case CMD_CHIP_INFO: { ack = false; - arg0 = *(AT91C_DBGU_CIDR); + arg0 = GetChipId(); reply_old(CMD_CHIP_INFO, arg0, 0, 0, 0, 0); } break; + case CMD_CHIP_TYPE: { + ack = false; + arg0 = GetChipType(); + reply_old(CMD_CHIP_TYPE, arg0, 0, 0, 0, 0); + } + break; + case CMD_BL_VERSION: { ack = false; arg0 = BL_VERSION_1_0_0; @@ -177,7 +168,7 @@ static void UsbPacketReceived(uint8_t *packet) { base = (uint8_t *) _flash_start; - size_t flash_size = get_flash_size(); + size_t flash_size = GetChipFlashSize(); // Boundary check the offset. if (offset > flash_size) { @@ -217,53 +208,85 @@ static void UsbPacketReceived(uint8_t *packet) { } case CMD_FINISH_WRITE: { -#if defined ICOPYX - if (c->arg[1] == 0xff && c->arg[2] == 0x1fd) { -#endif - for (int j = 0; j < 2; j++) { - uint32_t flash_address = arg0 + (0x100 * j); - AT91PS_EFC efc_bank = AT91C_BASE_EFC0; - int offset = 0; - uint32_t page_n = (flash_address - (uint32_t)_flash_start) / AT91C_IFLASH_PAGE_SIZE; - if (page_n >= AT91C_IFLASH_NB_OF_PAGES / 2) { - page_n -= AT91C_IFLASH_NB_OF_PAGES / 2; - efc_bank = AT91C_BASE_EFC1; - // We need to offset the writes or it will not fill the correct bank write buffer. - offset = (AT91C_IFLASH_NB_OF_PAGES / 2) * AT91C_IFLASH_PAGE_SIZE / sizeof(uint32_t); - } - for (int i = 0 + (64 * j); i < 64 + (64 * j); i++) { - _flash_start[offset + i] = c->d.asDwords[i]; - } - /* Check that the address that we are supposed to write to is within our allowed region */ - if (((flash_address + AT91C_IFLASH_PAGE_SIZE - 1) >= end_addr) || (flash_address < start_addr)) { - /* Disallow write */ - ack = false; - reply_old(CMD_NACK, 0, 0, 0, 0, 0); - } else { + // For this COMMAND Note + // --- + // 20260604: In older versions, the response arg1 was 0x00; in newer versions, it will be changed to PM3_E* error codes. + // These codes are used to transmit specific error information to the client in cases such as out-of-bounds access; + // they are unrelated to the FLASH error status. + // --- - efc_bank->EFC_FCR = MC_FLASH_COMMAND_KEY | - MC_FLASH_COMMAND_PAGEN(page_n) | - AT91C_MC_FCMD_START_PROG; - } - - // Wait until flashing of page finishes - uint32_t sr; - while (!((sr = efc_bank->EFC_FSR) & AT91C_MC_FRDY)); - if (sr & (AT91C_MC_LOCKE | AT91C_MC_PROGE)) { - ack = false; - reply_old(CMD_NACK, sr, 0, 0, 0, 0); - } - } -#if defined ICOPYX +#if defined ICOPYX // ICopyX needs special parameters to unlock boot write. + if (c->arg[1] != 0xff || c->arg[2] != 0x1fd) { + // arg[1] must be 0xff, arg[2] must be 0x1fd + // The reason why icopyx locks the boot is that the device cannot be used + // due to the possibility of incorrect firmware flash. Because fpga and other hardware features are different. + // If there is a better way to prevent the firmware from entering an inoperable state, this check is theoretically unnecessary. + break; } #endif + // If a valid magic is passed in, we need to check if the magic is the same as the current firmware. + if (is_valid_magic((int)c->arg[1]) && g_version_information.magic != c->arg[1]) { + ack = false; + reply_old(CMD_NACK, 0, PM3_EINVARG, 0, 0, 0); + break; + } + // Get current flash min erase/write unit of platform in bytes(not u32). + const uint16_t flash_ew_unit = FlashCodeGetEWMinUnit(); + const uint16_t flash_ew_unit_u32 = flash_ew_unit / sizeof(uint32_t); // count of min erase/write unit(u32) + // The fixed data payload is 512 bytes, which is 128 u32. + const uint16_t usb_payload_u32_len = sizeof(c->d) / sizeof(uint32_t); + // Copy data from usb to flash_min_unit_data buffer. A single usb payload may hold more + // than one erase/write unit (e.g. AT91 pages of 256 bytes), so always copy it in and flush + // the whole units below instead of assuming the payload is no larger than one unit. + bool copy_overflow = false; + for (int i = 0; i < usb_payload_u32_len; i++) { + // Check data buffer is no overflow. + if (flash_min_unit_data.count >= ARRAYLEN(flash_min_unit_data.data)) { + copy_overflow = true; + break; + } + flash_min_unit_data.data[flash_min_unit_data.count++] = c->d.asDwords[i]; + } + if (copy_overflow) { + ack = false; + flash_min_unit_data.count = 0; + reply_old(CMD_NACK, 0, PM3_EOVFLOW, 0, 0, 0); + break; + } + // How many min unit are stored in the data buffer? + const uint16_t flash_unit_num_u32 = flash_min_unit_data.count / flash_ew_unit_u32; + for (int idx_unit = 0; idx_unit < flash_unit_num_u32; idx_unit++) { + // Calculate the write start address of the new flash unit. + uint32_t flash_address = arg0 + idx_unit * flash_ew_unit; + // Check that the address that we are supposed to write to is within our allowed region + if (((flash_address + flash_ew_unit - 1) >= end_addr) || (flash_address < start_addr)) { + ack = false; // Disallow write + reply_old(CMD_NACK, 0, PM3_EOUTOFBOUND, 0, 0, 0); + break; + } + uint32_t *flash_min_unit_addr = &flash_min_unit_data.data[idx_unit * flash_ew_unit_u32]; + // Call the cross-platform flash api to write firmware to flash. + uint32_t status = 0x00; + bool isok = FlashCodeEWriteMinUnit(flash_address, flash_min_unit_addr, _flash_start, &status); + if (!isok) { + ack = false; + reply_old(CMD_NACK, status, 0, 0, 0, 0); + break; + } + } + if (ack) { + // After flushing whole units, keep the remaining partial unit for the next transfer. + flash_min_unit_data.count %= flash_ew_unit_u32; + } else { + flash_min_unit_data.count = 0; // Discard buffered data after a failed write. + } } break; case CMD_HARDWARE_RESET: { usb_disable(); - AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; + ResetChip(); } break; @@ -273,12 +296,13 @@ static void UsbPacketReceived(uint8_t *packet) { else bootrom_unlocked = false; - uint32_t cmd_start = c->arg[0]; - uint32_t cmd_end = c->arg[1]; + uint32_t cmd_start = c->arg[0]; // code flash start address + uint32_t cmd_end = c->arg[1]; // code flash end address - /* Only allow command if the bootrom is unlocked, or the parameters are outside of the protected - * bootrom area. In any case they must be within the flash area. - */ + /* + * Only allow command if the bootrom is unlocked, or the parameters are outside of the protected + * bootrom area. In any case they must be within the flash area. + */ if ((bootrom_unlocked || ((cmd_start >= (uint32_t)_bootrom_end) || (cmd_end < (uint32_t)_bootrom_start))) && (cmd_start >= (uint32_t)_flash_start) && (cmd_end <= (uint32_t)_flash_end)) { @@ -286,6 +310,9 @@ static void UsbPacketReceived(uint8_t *packet) { end_addr = cmd_end; } else { start_addr = end_addr = 0; + flash_min_unit_data.count = 0; + // In this command, flasher.c does not care what arg0 is; + // it considers the process to have failed as long as a NACK response is received. ack = false; reply_old(CMD_NACK, 0, 0, 0, 0, 0); } @@ -303,16 +330,11 @@ static void UsbPacketReceived(uint8_t *packet) { } } -// delay_loop(1) = 3.07us -static volatile uint32_t ccc; -static void __attribute__((optimize("O0"))) delay_loop(uint32_t delay) { - for (ccc = delay * 2; ccc; ccc--) {}; -} - static void flash_mode(void) { start_addr = 0; end_addr = 0; bootrom_unlocked = false; + flash_min_unit_data.count = 0; uint8_t rx[sizeof(PacketCommandOLD)]; g_common_area.command = COMMON_AREA_COMMAND_NONE; if (!g_common_area.flags.button_pressed && BUTTON_PRESS()) { @@ -331,7 +353,7 @@ static void flash_mode(void) { usb_enable(); // wait for reset to be complete? - delay_loop(100000); + SpinDelayUs(300 * 1000); // Wait for 300ms for (;;) { WDT_HIT(); @@ -344,8 +366,7 @@ static void flash_mode(void) { } bool button_state = BUTTON_PRESS(); - // ~10ms, prevent jitter - delay_loop(3333); + SpinDelayUs(10000); // ~10ms, prevent jitter if (button_state != BUTTON_PRESS()) { // in jitter state, ignore continue; @@ -358,93 +379,25 @@ static void flash_mode(void) { g_common_area.flags.button_pressed = 1; usb_disable(); LED_B_ON(); - AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; + ResetChip(); for (;;) {}; } } } -void BootROM(void); -void BootROM(void) { - /* Set up (that is: clear) BSS. */ - uint32_t *bss_dst = __bss_start__; - while (bss_dst < __bss_end__) *bss_dst++ = 0; - - //------------ - // First set up all the I/O pins; GPIOs configured directly, other ones - // just need to be assigned to the appropriate peripheral. - - // Kill all the pullups, especially the one on USB D+; leave them for - // the unused pins, though. - AT91C_BASE_PIOA->PIO_PPUDR = - GPIO_USB_PU | - GPIO_LED_A | - GPIO_LED_B | - GPIO_LED_C | - GPIO_LED_D | - GPIO_FPGA_DIN | - GPIO_FPGA_DOUT | - GPIO_FPGA_CCLK | - GPIO_FPGA_NINIT | - GPIO_FPGA_NPROGRAM | - GPIO_FPGA_DONE | - GPIO_MUXSEL_HIPKD | - GPIO_MUXSEL_HIRAW | - GPIO_MUXSEL_LOPKD | - GPIO_MUXSEL_LORAW | - GPIO_RELAY | - GPIO_NVDD_ON; - // (and add GPIO_FPGA_ON) - // These pins are outputs - AT91C_BASE_PIOA->PIO_OER = - GPIO_LED_A | - GPIO_LED_B | - GPIO_LED_C | - GPIO_LED_D | - GPIO_RELAY | - GPIO_NVDD_ON; - // PIO controls the following pins - AT91C_BASE_PIOA->PIO_PER = - GPIO_USB_PU | - GPIO_LED_A | - GPIO_LED_B | - GPIO_LED_C | - GPIO_LED_D; - - // USB_D_PLUS_PULLUP_OFF(); - usb_disable(); - LED_D_OFF(); - LED_C_ON(); - LED_B_OFF(); - LED_A_OFF(); - - // Set the first 256KB memory flashspeed - AT91C_BASE_EFC0->EFC_FMR = AT91C_MC_FWS_1FWS | MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48); - - // 9 = 256, 10+ is 512KB - uint8_t id = (*(AT91C_DBGU_CIDR) & 0xF00) >> 8; - if (id > 9) - AT91C_BASE_EFC1->EFC_FMR = AT91C_MC_FWS_1FWS | MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48); - - // Initialize all system clocks - ConfigClocks(); - - LED_A_ON(); - - int g_common_area_present = 0; - switch (AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_RSTTYP) { - case AT91C_RSTC_RSTTYP_WATCHDOG: - case AT91C_RSTC_RSTTYP_SOFTWARE: - case AT91C_RSTC_RSTTYP_USER: - /* In these cases the g_common_area in RAM should be ok, retain it if it's there */ - if (g_common_area.magic == COMMON_AREA_MAGIC && g_common_area.version == 1) - g_common_area_present = 1; - break; - default: /* Otherwise, initialize it from scratch */ - break; +// Detect whether to enter flash mode. If the button is pressed for more than 2s, +// or if the command is set to enter flash mode, or if the OS image entry point is invalid, then enter flash mode. +static bool check_goto_flash_mode(void) { + int common_area_present = 0; + // Check if RESET is SRAM retention? if not, the content of g_common_area in RAM is not reliable, must to init. + if (CheckRSTWithSRAMRetention()) { + // In these cases the g_common_area in RAM should be ok, retain it if it's there + if (g_common_area.magic == COMMON_AREA_MAGIC && g_common_area.version == 1) { + common_area_present = 1; + } } - if (!g_common_area_present) { + if (!common_area_present) { /* Common area not ok, initialize it */ size_t i; /* Makeshift memset, no need to drag util.c into this */ @@ -456,14 +409,66 @@ void BootROM(void) { } g_common_area.flags.bootrom_present = 1; - if ((g_common_area.command == COMMON_AREA_COMMAND_ENTER_FLASH_MODE) || - (!g_common_area.flags.button_pressed && BUTTON_PRESS()) || - (*_osimage_entry == 0xffffffffU)) { + // Handle the event of button startup separately. (Pressing the button is no longer considered as necessary to enter BOOT.) + bool to_flash_mode = false; + if (!g_common_area.flags.button_pressed && BUTTON_PRESS()) { + uint32_t time_counter = 0; + to_flash_mode = true; + while (time_counter++ < 3000) { // It is necessary to press and hold the button for more than 2s before entering BOOT. + if (!BUTTON_PRESS()) { + to_flash_mode = false; // If the button is not pressed for more than 2s, exit BOOT. + break; + } + SpinDelayUs(1000); // 1ms + } + } else if ((g_common_area.command == COMMON_AREA_COMMAND_ENTER_FLASH_MODE) || (*_osimage_entry == 0xffffffffU)) { + to_flash_mode = true; + } + + return to_flash_mode; +} + +void BootROM(void); +void BootROM(void) { + // __BKPT(0); // For debug + + /* Set up (that is: clear) BSS. */ + uint32_t *bss_dst = __bss_start__; + while (bss_dst < __bss_end__) *bss_dst++ = 0; + + //------------ + // First set up all the I/O pins; GPIOs configured directly, other ones + // just need to be assigned to the appropriate peripheral. + gpio_sysboot_setup(); + + // Turn off all leds + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + + // USB_D_PLUS_PULLUP_OFF(); + usb_disable(); + + // Initialize the FLASH area for firmware/code. + FlashCodeInit(); + + // Initialize all system clocks + ConfigSystemClocks(); + + // Check whether to enter the FLASH mode. + const bool to_flash_mode = check_goto_flash_mode(); + + LED_C_ON(); + LED_A_ON(); + + // Keep running in BOOT or jump to App image? + if (to_flash_mode) { flash_mode(); } else { // clear button status, even if button still pressed g_common_area.flags.button_pressed = 0; - // jump to Flash address of the osimage entry point (LSBit set for thumb mode) - __asm("bx %0\n" : : "r"(((uint32_t)_osimage_entry) | 0x1)); + // jump to OS image + JumpToAnyImage((uint32_t)_stack_end, (uint32_t)_osimage_entry); } } diff --git a/bootrom/flash-reset-at32.s b/bootrom/flash-reset-at32.s new file mode 100644 index 000000000..7977817d2 --- /dev/null +++ b/bootrom/flash-reset-at32.s @@ -0,0 +1,70 @@ +.section .startup,"ax" +.syntax unified +.cpu cortex-m4 +.fpu softvfp +.thumb + +/* ------------------------------------------------------------------- */ + +.global flashstart +.type flashstart, %object +flashstart: + + .word _stack_end + .word reset + .word default_handler + .word default_handler + .word default_handler + .word default_handler + .word default_handler + .word 0 + .word 0 + .word 0 + .word 0 + .word default_handler + .word default_handler + .word 0 + .word default_handler + .word default_handler + +/* ------------------------------------------------------------------- */ + +.thumb_func +.type reset, %function +reset: + /* Copy the bootrom to sram */ + ldr r0, =__bootphase2_src_start__ + ldr r1, =__bootphase2_start__ + ldr r2, =__bootphase2_end__ +1: + ldr r3, [r0], #4 + str r3, [r1], #4 + cmp r1, r2 + blo 1b + + /* + * DXL Note: + * It is very important to ensure that the memory expansion configuration is correct before accessing the memory! + * Do not use _stack_end, because this memory area may be temporarily inaccessible. + * If you adjust from small sram to large sram, using _stack_end and function call will result in hardware fault. + */ + ldr sp, =__stack_boot_end__ + bl Extend_SRAM + + /* Load the stack pointer(Such as _stack_end?) from the first word of the bootphase2. */ + ldr sp, =_stack_end /* Reset the stack pointer once to call JumpToAnyImage. */ + ldr r0, =__bootphase2_start__ + ldr r0, [r0] + ldr r1, =__bootphase2_start__ /* Vector table base / entry point. */ + ldr r2, =JumpToAnyImage + blx r2 + +/* ------------------------------------------------------------------- */ + +.thumb_func +.type default_handler, %function +default_handler: +1: + b 1b + + .ltorg diff --git a/bootrom/flash-reset.s b/bootrom/flash-reset-at91.s similarity index 100% rename from bootrom/flash-reset.s rename to bootrom/flash-reset-at91.s diff --git a/bootrom/ldscript-flash-at32 b/bootrom/ldscript-flash-at32 new file mode 100644 index 000000000..bdb526ff4 --- /dev/null +++ b/bootrom/ldscript-flash-at32 @@ -0,0 +1,40 @@ +/* +----------------------------------------------------------------------------- + Copyright (C) Jonathan Westhues, Mar 2006 + Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See LICENSE.txt for the text of the license. +----------------------------------------------------------------------------- + Bootrom linker script for AT32 microcontrollers + By DXL +----------------------------------------------------------------------------- +*/ + +INCLUDE ../common_arm/ldscript.defs.at32 +INCLUDE ../common_arm/ldscript.common +INCLUDE ./ldscript-flash-common + +PHDRS +{ + stack_boot PT_LOAD; +} + +SECTIONS +{ + /* It is used in flash-reset-at32.s. To called some c functions for init/config. */ + .stack_boot : { + __stack_boot_start__ = .; + . += stacksize; + __stack_boot_end__ = .; + } >ram AT>ram :stack_boot +} diff --git a/bootrom/ldscript-flash-at91 b/bootrom/ldscript-flash-at91 new file mode 100644 index 000000000..3af530029 --- /dev/null +++ b/bootrom/ldscript-flash-at91 @@ -0,0 +1,24 @@ +/* +----------------------------------------------------------------------------- + Copyright (C) Jonathan Westhues, Mar 2006 + Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See LICENSE.txt for the text of the license. +----------------------------------------------------------------------------- + Bootrom linker script for AT91 microcontrollers +----------------------------------------------------------------------------- +*/ + +INCLUDE ../common_arm/ldscript.defs.at91 +INCLUDE ../common_arm/ldscript.common +INCLUDE ./ldscript-flash-common \ No newline at end of file diff --git a/bootrom/ldscript-flash b/bootrom/ldscript-flash-common similarity index 95% rename from bootrom/ldscript-flash rename to bootrom/ldscript-flash-common index 374c2d6c7..50578a204 100644 --- a/bootrom/ldscript-flash +++ b/bootrom/ldscript-flash-common @@ -15,12 +15,10 @@ See LICENSE.txt for the text of the license. ----------------------------------------------------------------------------- - Bootrom linker script + Bootrom linker script common to all flash-based targets. ----------------------------------------------------------------------------- */ -INCLUDE ../common_arm/ldscript.common - PHDRS { phase1 PT_LOAD; @@ -43,7 +41,7 @@ SECTIONS } >bootphase1 :phase1 .bootphase2 : { - *(.startphase2) + KEEP(*(.startphase2)) *(.text) *(.text.*) *(.eh_frame) @@ -72,4 +70,4 @@ SECTIONS .commonarea (NOLOAD) : { *(.commonarea) } >commonarea -} +} \ No newline at end of file diff --git a/bootrom/ram-reset-at32.s b/bootrom/ram-reset-at32.s new file mode 100644 index 000000000..784ba3b13 --- /dev/null +++ b/bootrom/ram-reset-at32.s @@ -0,0 +1,486 @@ +@----------------------------------------------------------------------------- +@ RAM reset vector for relaunching the bootloader of AT32F435 +@ By DXL +@----------------------------------------------------------------------------- + +.syntax unified +.cpu cortex-m4 +.thumb + +.extern BootROM +.global g_pfnVectors +.global Default_Handler + +/* ----------------------------------------------------------------------------- */ + +@ This is the code that gets called when the processor receives an +@ unexpected interrupt. This simply enters an infinite loop, preserving +@ the system state for examination by a debugger. + +.section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop +.size Default_Handler, .-Default_Handler + +/* ----------------------------------------------------------------------------- */ + +.section .startphase2,"ax",%progbits +.type g_pfnVectors, %object +.size g_pfnVectors, .-g_pfnVectors + +g_pfnVectors: + .word _stack_end + .word BootROM /* Start from BootRom (reset_handler) */ + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + + /* External Interrupts */ + .word WWDT_IRQHandler /* Window Watchdog Timer */ + .word PVM_IRQHandler /* PVM through EXINT Line detect */ + .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXINT line */ + .word ERTC_WKUP_IRQHandler /* ERTC Wakeup through the EXINT line */ + .word FLASH_IRQHandler /* Flash */ + .word CRM_IRQHandler /* CRM */ + .word EXINT0_IRQHandler /* EXINT Line 0 */ + .word EXINT1_IRQHandler /* EXINT Line 1 */ + .word EXINT2_IRQHandler /* EXINT Line 2 */ + .word EXINT3_IRQHandler /* EXINT Line 3 */ + .word EXINT4_IRQHandler /* EXINT Line 4 */ + .word EDMA_Stream1_IRQHandler /* EDMA Stream 1 */ + .word EDMA_Stream2_IRQHandler /* EDMA Stream 2 */ + .word EDMA_Stream3_IRQHandler /* EDMA Stream 3 */ + .word EDMA_Stream4_IRQHandler /* EDMA Stream 4 */ + .word EDMA_Stream5_IRQHandler /* EDMA Stream 5 */ + .word EDMA_Stream6_IRQHandler /* EDMA Stream 6 */ + .word EDMA_Stream7_IRQHandler /* EDMA Stream 7 */ + .word ADC1_2_3_IRQHandler /* ADC1 & ADC2 & ADC3 */ + .word CAN1_TX_IRQHandler /* CAN1 TX */ + .word CAN1_RX0_IRQHandler /* CAN1 RX0 */ + .word CAN1_RX1_IRQHandler /* CAN1 RX1 */ + .word CAN1_SE_IRQHandler /* CAN1 SE */ + .word EXINT9_5_IRQHandler /* EXINT Line [9:5] */ + .word TMR1_BRK_TMR9_IRQHandler /* TMR1 Brake and TMR9 */ + .word TMR1_OVF_TMR10_IRQHandler /* TMR1 Overflow and TMR10 */ + .word TMR1_TRG_HALL_TMR11_IRQHandler /* TMR1 Trigger and hall and TMR11 */ + .word TMR1_CH_IRQHandler /* TMR1 Channel */ + .word TMR2_GLOBAL_IRQHandler /* TMR2 */ + .word TMR3_GLOBAL_IRQHandler /* TMR3 */ + .word TMR4_GLOBAL_IRQHandler /* TMR4 */ + .word I2C1_EVT_IRQHandler /* I2C1 Event */ + .word I2C1_ERR_IRQHandler /* I2C1 Error */ + .word I2C2_EVT_IRQHandler /* I2C2 Event */ + .word I2C2_ERR_IRQHandler /* I2C2 Error */ + .word SPI1_IRQHandler /* SPI1 */ + .word SPI2_I2S2EXT_IRQHandler /* SPI2 */ + .word USART1_IRQHandler /* USART1 */ + .word USART2_IRQHandler /* USART2 */ + .word USART3_IRQHandler /* USART3 */ + .word EXINT15_10_IRQHandler /* EXINT Line [15:10] */ + .word ERTCAlarm_IRQHandler /* RTC Alarm through EXINT Line */ + .word OTGFS1_WKUP_IRQHandler /* OTGFS1 Wakeup from suspend */ + .word TMR8_BRK_TMR12_IRQHandler /* TMR8 Brake and TMR12 */ + .word TMR8_OVF_TMR13_IRQHandler /* TMR8 Overflow and TMR13 */ + .word TMR8_TRG_HALL_TMR14_IRQHandler /* TMR8 Trigger and hall and TMR14 */ + .word TMR8_CH_IRQHandler /* TMR8 Channel */ + .word EDMA_Stream8_IRQHandler /* EDMA Stream 8 */ + .word XMC_IRQHandler /* XMC */ + .word SDIO1_IRQHandler /* SDIO1 */ + .word TMR5_GLOBAL_IRQHandler /* TMR5 */ + .word SPI3_I2S3EXT_IRQHandler /* SPI3 */ + .word UART4_IRQHandler /* UART4 */ + .word UART5_IRQHandler /* UART5 */ + .word TMR6_DAC_GLOBAL_IRQHandler /* TMR6 & DAC */ + .word TMR7_GLOBAL_IRQHandler /* TMR7 */ + .word DMA1_Channel1_IRQHandler /* DMA1 Channel 1 */ + .word DMA1_Channel2_IRQHandler /* DMA1 Channel 2 */ + .word DMA1_Channel3_IRQHandler /* DMA1 Channel 3 */ + .word DMA1_Channel4_IRQHandler /* DMA1 Channel 4 */ + .word DMA1_Channel5_IRQHandler /* DMA1 Channel 5 */ + .word EMAC_IRQHandler /* EMAC */ + .word EMAC_WKUP_IRQHandler /* EMAC Wakeup */ + .word CAN2_TX_IRQHandler /* CAN2 TX */ + .word CAN2_RX0_IRQHandler /* CAN2 RX0 */ + .word CAN2_RX1_IRQHandler /* CAN2 RX1 */ + .word CAN2_SE_IRQHandler /* CAN2 SE */ + .word OTGFS1_IRQHandler /* OTGFS1 */ + .word DMA1_Channel6_IRQHandler /* DMA1 Channel 6 */ + .word DMA1_Channel7_IRQHandler /* DMA1 Channel 7 */ + .word 0 /* Reserved */ + .word USART6_IRQHandler /* USART6 */ + .word I2C3_EVT_IRQHandler /* I2C3 Event */ + .word I2C3_ERR_IRQHandler /* I2C3 Error */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word OTGFS2_WKUP_IRQHandler /* OTGFS2 Wakeup from suspend */ + .word OTGFS2_IRQHandler /* OTGFS2 */ + .word DVP_IRQHandler /* DVP */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word FPU_IRQHandler /* FPU */ + .word UART7_IRQHandler /* UART7 */ + .word UART8_IRQHandler /* UART8 */ + .word SPI4_IRQHandler /* SPI4 */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word QSPI2_IRQHandler /* QSPI2 */ + .word QSPI1_IRQHandler /* QSPI1 */ + .word 0 /* Reserved */ + .word DMAMUX_IRQHandler /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word SDIO2_IRQHandler /* SDIO2 */ + .word ACC_IRQHandler /* ACC */ + .word TMR20_BRK_IRQHandler /* TMR20 Brake */ + .word TMR20_OVF_IRQHandler /* TMR20 Overflow */ + .word TMR20_TRG_HALL_IRQHandler /* TMR20 Trigger and hall */ + .word TMR20_CH_IRQHandler /* TMR20 Channel */ + .word DMA2_Channel1_IRQHandler /* DMA2 Channel 1 */ + .word DMA2_Channel2_IRQHandler /* DMA2 Channel 2 */ + .word DMA2_Channel3_IRQHandler /* DMA2 Channel 3 */ + .word DMA2_Channel4_IRQHandler /* DMA2 Channel 4 */ + .word DMA2_Channel5_IRQHandler /* DMA2 Channel 5 */ + .word DMA2_Channel6_IRQHandler /* DMA2 Channel 6 */ + .word DMA2_Channel7_IRQHandler /* DMA2 Channel 7 */ + +@----------------------------------------------------------------------------- +@ Provide weak aliases for each Exception handler to the Default_Handler. +@ As they are weak aliases, any function with the same name will override +@ this definition. +@----------------------------------------------------------------------------- + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDT_IRQHandler + .thumb_set WWDT_IRQHandler,Default_Handler + + .weak PVM_IRQHandler + .thumb_set PVM_IRQHandler,Default_Handler + + .weak TAMP_STAMP_IRQHandler + .thumb_set TAMP_STAMP_IRQHandler,Default_Handler + + .weak ERTC_WKUP_IRQHandler + .thumb_set ERTC_WKUP_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak CRM_IRQHandler + .thumb_set CRM_IRQHandler,Default_Handler + + .weak EXINT0_IRQHandler + .thumb_set EXINT0_IRQHandler,Default_Handler + + .weak EXINT1_IRQHandler + .thumb_set EXINT1_IRQHandler,Default_Handler + + .weak EXINT2_IRQHandler + .thumb_set EXINT2_IRQHandler,Default_Handler + + .weak EXINT3_IRQHandler + .thumb_set EXINT3_IRQHandler,Default_Handler + + .weak EXINT4_IRQHandler + .thumb_set EXINT4_IRQHandler,Default_Handler + + .weak EDMA_Stream1_IRQHandler + .thumb_set EDMA_Stream1_IRQHandler,Default_Handler + + .weak EDMA_Stream2_IRQHandler + .thumb_set EDMA_Stream2_IRQHandler,Default_Handler + + .weak EDMA_Stream3_IRQHandler + .thumb_set EDMA_Stream3_IRQHandler,Default_Handler + + .weak EDMA_Stream4_IRQHandler + .thumb_set EDMA_Stream4_IRQHandler,Default_Handler + + .weak EDMA_Stream5_IRQHandler + .thumb_set EDMA_Stream5_IRQHandler,Default_Handler + + .weak EDMA_Stream6_IRQHandler + .thumb_set EDMA_Stream6_IRQHandler,Default_Handler + + .weak EDMA_Stream7_IRQHandler + .thumb_set EDMA_Stream7_IRQHandler,Default_Handler + + .weak ADC1_2_3_IRQHandler + .thumb_set ADC1_2_3_IRQHandler,Default_Handler + + .weak CAN1_TX_IRQHandler + .thumb_set CAN1_TX_IRQHandler,Default_Handler + + .weak CAN1_RX0_IRQHandler + .thumb_set CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SE_IRQHandler + .thumb_set CAN1_SE_IRQHandler,Default_Handler + + .weak EXINT9_5_IRQHandler + .thumb_set EXINT9_5_IRQHandler,Default_Handler + + .weak TMR1_BRK_TMR9_IRQHandler + .thumb_set TMR1_BRK_TMR9_IRQHandler,Default_Handler + + .weak TMR1_OVF_TMR10_IRQHandler + .thumb_set TMR1_OVF_TMR10_IRQHandler,Default_Handler + + .weak TMR1_TRG_HALL_TMR11_IRQHandler + .thumb_set TMR1_TRG_HALL_TMR11_IRQHandler,Default_Handler + + .weak TMR1_CH_IRQHandler + .thumb_set TMR1_CH_IRQHandler,Default_Handler + + .weak TMR2_GLOBAL_IRQHandler + .thumb_set TMR2_GLOBAL_IRQHandler,Default_Handler + + .weak TMR3_GLOBAL_IRQHandler + .thumb_set TMR3_GLOBAL_IRQHandler,Default_Handler + + .weak TMR4_GLOBAL_IRQHandler + .thumb_set TMR4_GLOBAL_IRQHandler,Default_Handler + + .weak I2C1_EVT_IRQHandler + .thumb_set I2C1_EVT_IRQHandler,Default_Handler + + .weak I2C1_ERR_IRQHandler + .thumb_set I2C1_ERR_IRQHandler,Default_Handler + + .weak I2C2_EVT_IRQHandler + .thumb_set I2C2_EVT_IRQHandler,Default_Handler + + .weak I2C2_ERR_IRQHandler + .thumb_set I2C2_ERR_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_I2S2EXT_IRQHandler + .thumb_set SPI2_I2S2EXT_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXINT15_10_IRQHandler + .thumb_set EXINT15_10_IRQHandler,Default_Handler + + .weak ERTCAlarm_IRQHandler + .thumb_set ERTCAlarm_IRQHandler,Default_Handler + + .weak OTGFS1_WKUP_IRQHandler + .thumb_set OTGFS1_WKUP_IRQHandler,Default_Handler + + .weak TMR8_BRK_TMR12_IRQHandler + .thumb_set TMR8_BRK_TMR12_IRQHandler,Default_Handler + + .weak TMR8_OVF_TMR13_IRQHandler + .thumb_set TMR8_OVF_TMR13_IRQHandler,Default_Handler + + .weak TMR8_TRG_HALL_TMR14_IRQHandler + .thumb_set TMR8_TRG_HALL_TMR14_IRQHandler,Default_Handler + + .weak TMR8_CH_IRQHandler + .thumb_set TMR8_CH_IRQHandler,Default_Handler + + .weak EDMA_Stream8_IRQHandler + .thumb_set EDMA_Stream8_IRQHandler,Default_Handler + + .weak XMC_IRQHandler + .thumb_set XMC_IRQHandler,Default_Handler + + .weak SDIO1_IRQHandler + .thumb_set SDIO1_IRQHandler,Default_Handler + + .weak TMR5_GLOBAL_IRQHandler + .thumb_set TMR5_GLOBAL_IRQHandler,Default_Handler + + .weak SPI3_I2S3EXT_IRQHandler + .thumb_set SPI3_I2S3EXT_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TMR6_DAC_GLOBAL_IRQHandler + .thumb_set TMR6_DAC_GLOBAL_IRQHandler,Default_Handler + + .weak TMR7_GLOBAL_IRQHandler + .thumb_set TMR7_GLOBAL_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak EMAC_IRQHandler + .thumb_set EMAC_IRQHandler,Default_Handler + + .weak EMAC_WKUP_IRQHandler + .thumb_set EMAC_WKUP_IRQHandler,Default_Handler + + .weak CAN2_TX_IRQHandler + .thumb_set CAN2_TX_IRQHandler,Default_Handler + + .weak CAN2_RX0_IRQHandler + .thumb_set CAN2_RX0_IRQHandler ,Default_Handler + + .weak CAN2_RX1_IRQHandler + .thumb_set CAN2_RX1_IRQHandler ,Default_Handler + + .weak CAN2_SE_IRQHandler + .thumb_set CAN2_SE_IRQHandler,Default_Handler + + .weak OTGFS1_IRQHandler + .thumb_set OTGFS1_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak USART6_IRQHandler + .thumb_set USART6_IRQHandler,Default_Handler + + .weak I2C3_EVT_IRQHandler + .thumb_set I2C3_EVT_IRQHandler,Default_Handler + + .weak I2C3_ERR_IRQHandler + .thumb_set I2C3_ERR_IRQHandler,Default_Handler + + .weak OTGFS2_WKUP_IRQHandler + .thumb_set OTGFS2_WKUP_IRQHandler,Default_Handler + + .weak OTGFS2_IRQHandler + .thumb_set OTGFS2_IRQHandler,Default_Handler + + .weak DVP_IRQHandler + .thumb_set DVP_IRQHandler,Default_Handler + + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + + .weak UART7_IRQHandler + .thumb_set UART7_IRQHandler,Default_Handler + + .weak UART8_IRQHandler + .thumb_set UART8_IRQHandler,Default_Handler + + .weak SPI4_IRQHandler + .thumb_set SPI4_IRQHandler,Default_Handler + + .weak QSPI2_IRQHandler + .thumb_set QSPI2_IRQHandler,Default_Handler + + .weak QSPI1_IRQHandler + .thumb_set QSPI1_IRQHandler,Default_Handler + + .weak DMAMUX_IRQHandler + .thumb_set DMAMUX_IRQHandler ,Default_Handler + + .weak SDIO2_IRQHandler + .thumb_set SDIO2_IRQHandler ,Default_Handler + + .weak ACC_IRQHandler + .thumb_set ACC_IRQHandler,Default_Handler + + .weak TMR20_BRK_IRQHandler + .thumb_set TMR20_BRK_IRQHandler,Default_Handler + + .weak TMR20_OVF_IRQHandler + .thumb_set TMR20_OVF_IRQHandler,Default_Handler + + .weak TMR20_TRG_HALL_IRQHandler + .thumb_set TMR20_TRG_HALL_IRQHandler,Default_Handler + + .weak TMR20_CH_IRQHandler + .thumb_set TMR20_CH_IRQHandler,Default_Handler + + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + + .weak DMA2_Channel4_IRQHandler + .thumb_set DMA2_Channel4_IRQHandler,Default_Handler + + .weak DMA2_Channel5_IRQHandler + .thumb_set DMA2_Channel5_IRQHandler,Default_Handler + + .weak DMA2_Channel6_IRQHandler + .thumb_set DMA2_Channel6_IRQHandler,Default_Handler + + .weak DMA2_Channel7_IRQHandler + .thumb_set DMA2_Channel7_IRQHandler,Default_Handler diff --git a/bootrom/ram-reset.s b/bootrom/ram-reset-at91.s similarity index 100% rename from bootrom/ram-reset.s rename to bootrom/ram-reset-at91.s diff --git a/client/src/flash.c b/client/src/flash.c index b1c557897..962192cda 100644 --- a/client/src/flash.c +++ b/client/src/flash.c @@ -27,20 +27,28 @@ #include "ui.h" #include "elf.h" #include "proxendian.h" -#include "at91sam7s512.h" #include "util_posix.h" #include "comms.h" #include "commonutil.h" #include "fileutils.h" #include "frame_progress.h" -#define FLASH_START 0x100000 +#include "at91sam7s512.h" +// #include "at32f435_437_flash.h" TODO DXL makefile include dirs need add 'armlib/at32_sys/drivers/inc' -#define BOOTLOADER_SIZE 0x2000 -#define BOOTLOADER_END (FLASH_START + BOOTLOADER_SIZE) +// #define BLOCK_SIZE_AT32 0x800 // For at32, if flash size is 4m, the sector size is 4096byte, otherwise 2048byte. +#define FLASH_START_AT32 0x08000000 +#define BOOTLOADER_SIZE_AT32 0x4000 // defined in 'ldscript.defs.at32' +#define BOOTLOADER_END_AT32 (FLASH_START_AT32 + BOOTLOADER_SIZE_AT32) -#define BLOCK_SIZE 0x200 +// AT91 series universal definition. +#define BLOCK_SIZE_AT91 0x200 // For at91, 512byte = 2page +#define FLASH_START_AT91 0x100000 +#define BOOTLOADER_SIZE_AT91 0x2000 // defined in 'ldscript.defs.at91' +#define BOOTLOADER_END_AT91 (FLASH_START_AT91 + BOOTLOADER_SIZE_AT91) +// It is best for the version number of the flasher to be consistent with the version number of the bootrom, +// otherwise some capabilities may be missing. #define FLASHER_VERSION BL_VERSION_1_0_0 static const uint8_t elf_ident[] = { @@ -50,6 +58,7 @@ static const uint8_t elf_ident[] = { EV_CURRENT }; +// TODO DXL It's best to encapsulate and reuse the code here, and put it in commonutil static int chipid_to_mem_avail(uint32_t iChipID) { int mem_avail = 0; switch ((iChipID & 0xF00) >> 8) { @@ -86,10 +95,48 @@ static int chipid_to_mem_avail(uint32_t iChipID) { return mem_avail; } +// TODO DXL It's best to encapsulate and reuse the code here, and put it in commonutil +static int chipid_to_mem_avail_at32(uint32_t idcode) { + struct { + uint32_t id; // idcode + uint32_t flash_size; // KB + } at32_idcode_mem_map[] = { + {0x70084540, 4032}, // AT32F435ZMT7 + {0x70083341, 1024}, // AT32F435ZGT7 + {0x70083242, 256}, // AT32F435ZCT7 + {0x70084543, 4032}, // AT32F435VMT7 + {0x70083344, 1024}, // AT32F435VGT7 + {0x70083245, 256}, // AT32F435VCT7 + {0x70084546, 4032}, // AT32F435RMT7 + {0x70083347, 1024}, // AT32F435RGT7 + {0x70083248, 256}, // AT32F435RCT7 + {0x70084549, 4032}, // AT32F435CMT7 + {0x7008334A, 1024}, // AT32F435CGT7 + {0x7008324B, 256}, // AT32F435CCT7 + {0x7008454C, 4032}, // AT32F435CMU7 + {0x7008334D, 1024}, // AT32F435CGU7 + {0x7008324E, 256}, // AT32F435CCU7 + {0x7008454F, 4032}, // AT32F437ZMT7 + {0x70083350, 1024}, // AT32F437ZGT7 + {0x70083251, 256}, // AT32F437ZCT7 + {0x70084552, 4032}, // AT32F437VMT7 + {0x70083353, 1024}, // AT32F437VGT7 + {0x70083254, 256}, // AT32F437VCT7 + {0x70084555, 4032}, // AT32F437RMT7 + {0x70083356, 1024}, // AT32F437RGT7 + {0x70083257, 256}, // AT32F437RCT7 + }; + for (size_t i = 0; i < ARRAYLEN(at32_idcode_mem_map); i++) { + if (at32_idcode_mem_map[i].id == idcode) { + return at32_idcode_mem_map[i].flash_size; + } + } + return 256; // No idcode found? return a min size. +} + // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed -static int build_segs_from_phdrs(flash_file_t *ctx, uint32_t flash_size) { - uint32_t flash_end = FLASH_START + flash_size; +static int build_segs_from_phdrs(flash_file_t *ctx, flash_dev_t *flash_dev) { Elf32_Phdr_t *phdr = ctx->phdrs; flash_seg_t *seg; uint32_t last_end = 0; @@ -133,35 +180,35 @@ static int build_segs_from_phdrs(flash_file_t *ctx, uint32_t flash_size) { PrintAndLogEx(ERR, "Error: PHDRs not sorted or overlap"); return PM3_EFILE; } - if (paddr < FLASH_START || (paddr + filesz) > flash_end) { + if (paddr < flash_dev->flash_start || (paddr + filesz) > flash_dev->flash_end) { PrintAndLogEx(ERR, "Error: PHDR is not contained in Flash"); - if ((paddr + filesz) > flash_end) { + if ((paddr + filesz) > flash_dev->flash_end) { PrintAndLogEx(ERR, "Firmware is probably too big for your device"); PrintAndLogEx(ERR, "See README.md for information on compiling for platforms with 256KB of flash memory"); } return PM3_EFILE; } - if (vaddr >= FLASH_START && vaddr < flash_end && (flags & PF_W)) { + if (vaddr >= flash_dev->flash_start && vaddr < flash_dev->flash_end && (flags & PF_W)) { PrintAndLogEx(ERR, "Error: Flash VMA segment is writable"); return PM3_EFILE; } uint8_t *data; // make extra space if we need to move the data forward - data = calloc(filesz + BLOCK_SIZE, sizeof(uint8_t)); + data = calloc(filesz + flash_dev->block_size, sizeof(uint8_t)); if (!data) { PrintAndLogEx(ERR, "Error: Out of memory"); return PM3_EMALLOC; } memcpy(data, ctx->elf + offset, filesz); - uint32_t block_offset = paddr & (BLOCK_SIZE - 1); + uint32_t block_offset = paddr & (flash_dev->block_size - 1); if (block_offset) { if (ctx->num_segs) { flash_seg_t *prev_seg = seg - 1; uint32_t this_end = paddr + filesz; - uint32_t this_firstblock = paddr & ~(BLOCK_SIZE - 1); - uint32_t prev_lastblock = (last_end - 1) & ~(BLOCK_SIZE - 1); + uint32_t this_firstblock = paddr & ~(flash_dev->block_size - 1); + uint32_t prev_lastblock = (last_end - 1) & ~(flash_dev->block_size - 1); if (this_firstblock == prev_lastblock) { uint32_t new_length = this_end - prev_seg->start; @@ -209,28 +256,27 @@ static int build_segs_from_phdrs(flash_file_t *ctx, uint32_t flash_size) { } // Sanity check segments and check for bootloader writes -static int check_segs(flash_file_t *ctx, int can_write_bl, uint32_t flash_size) { - uint32_t flash_end = FLASH_START + flash_size; +static int check_segs(flash_file_t *ctx, int can_write_bl, flash_dev_t *flash_dev) { for (int i = 0; i < ctx->num_segs; i++) { flash_seg_t *seg = &ctx->segments[i]; - if (seg->start & (BLOCK_SIZE - 1)) { + if (seg->start & (flash_dev->block_size - 1)) { PrintAndLogEx(ERR, "Error: Segment is not aligned"); return PM3_EFILE; } - if (seg->start < FLASH_START) { + if (seg->start < flash_dev->flash_start) { PrintAndLogEx(ERR, "Error: Segment is outside of flash bounds"); return PM3_EFILE; } - if (seg->start + seg->length > flash_end) { + if (seg->start + seg->length > flash_dev->flash_end) { PrintAndLogEx(ERR, "Error: Segment is outside of flash bounds"); return PM3_EFILE; } - if (!can_write_bl && seg->start < BOOTLOADER_END) { + if (!can_write_bl && seg->start < flash_dev->boot_end) { PrintAndLogEx(ERR, "Attempted to write bootloader but bootloader writes are not enabled"); return PM3_EINVARG; } - if (can_write_bl && seg->start < BOOTLOADER_END && (seg->start + seg->length > BOOTLOADER_END)) { + if (can_write_bl && seg->start < flash_dev->boot_end && (seg->start + seg->length > flash_dev->boot_end)) { PrintAndLogEx(ERR, "Error: Segment is outside of bootloader bounds"); return PM3_EFILE; } @@ -238,18 +284,21 @@ static int check_segs(flash_file_t *ctx, int can_write_bl, uint32_t flash_size) return PM3_SUCCESS; } -static int print_and_validate_version(struct version_information_t *vi) { - if (vi->magic != VERSION_INFORMATION_MAGIC) { +// Check version information section for sanity and compatibility with the client, and print it if valid +static int print_and_validate_version(flash_file_t *ctx) { + if (!CheckValidInformationMagic(ctx->ver_info)) { + PrintAndLogEx(ERR, _RED_("ELF file does not contain valid version information" + "(magic = 0x%08x)"), ctx->ver_info->magic); return PM3_EFILE; } // same limit as for ARM image char temp[PM3_CMD_DATA_SIZE - 12] = {0}; - FormatVersionInformation(temp, sizeof(temp), "", vi); + FormatVersionInformation(temp, sizeof(temp), "", ctx->ver_info); PrintAndLogEx(SUCCESS, _CYAN_("ELF file version") _YELLOW_(" %s"), temp); if (strlen(g_version_information.armsrc) == 9) { - if (strncmp(vi->armsrc, g_version_information.armsrc, 9) != 0) { + if (strncmp(ctx->ver_info->armsrc, g_version_information.armsrc, 9) != 0) { PrintAndLogEx(WARNING, _RED_("ARM firmware does not match the source at the time the client was compiled")); return PM3_EINVARG; } else { @@ -265,7 +314,6 @@ int flash_load(flash_file_t *ctx, bool force) { Elf32_Ehdr_t *ehdr; Elf32_Shdr_t *shdrs = NULL; uint8_t *shstr = NULL; - struct version_information_t *vi = NULL; int res = PM3_EUNDEF; fd = fopen(ctx->filename, "rb"); @@ -347,8 +395,8 @@ int flash_load(flash_file_t *ctx, bool force) { for (uint16_t i = 0; i < le16(ehdr->e_shnum); i++) { if (strcmp(((char *)shstr) + shdrs[i].sh_name, ".version_information") == 0) { - vi = (struct version_information_t *)(ctx->elf + le32(shdrs[i].sh_offset)); - res = print_and_validate_version(vi); + ctx->ver_info = (struct version_information_t *)(ctx->elf + le32(shdrs[i].sh_offset)); + res = print_and_validate_version(ctx); break; } @@ -358,8 +406,8 @@ int flash_load(flash_file_t *ctx, bool force) { if (offset >= le32(shdrs[i].sh_addr)) { offset -= le32(shdrs[i].sh_addr); if (offset < le32(shdrs[i].sh_size)) { - vi = (struct version_information_t *)(ctx->elf + le32(shdrs[i].sh_offset) + offset); - res = print_and_validate_version(vi); + ctx->ver_info = (struct version_information_t *)(ctx->elf + le32(shdrs[i].sh_offset) + offset); + res = print_and_validate_version(ctx); } } break; @@ -387,15 +435,22 @@ fail: } // Prepare an ELF file for flashing -int flash_prepare(flash_file_t *ctx, int can_write_bl, int flash_size) { +int flash_prepare(flash_file_t *ctx, int can_write_bl, flash_dev_t *flash_dev) { int res = PM3_EUNDEF; - res = build_segs_from_phdrs(ctx, flash_size); + // Check elf file is build for currently connected device? + if (!CheckInformationMagicAndChipType(ctx->ver_info, flash_dev->chiptype)) { + PrintAndLogEx(ERR, "The elf file is not applicable to the currently connected device.", flash_dev->chiptype); + res = PM3_EFILE; + goto fail; + } + + res = build_segs_from_phdrs(ctx, flash_dev); if (res != PM3_SUCCESS) { goto fail; } - res = check_segs(ctx, can_write_bl, flash_size); + res = check_segs(ctx, can_write_bl, flash_dev); if (res != PM3_SUCCESS) { goto fail; } @@ -489,11 +544,11 @@ static int enter_bootloader(char *serial_port_name, bool wait_appear) { return PM3_EFATAL; } +// Wait for the device to respond with either ACK or NACK. static int wait_for_ack(PacketResponseNG *ack) { WaitForResponse(CMD_UNKNOWN, ack); - if (ack->cmd != CMD_ACK) { - PrintAndLogEx(ERR, "Error: Unexpected reply 0x%04x %s (expected ACK)", + PrintAndLogEx(ERR, "\nError: Unexpected reply 0x%04x %s (expected ACK)", ack->cmd, (ack->cmd == CMD_NACK) ? "NACK" : "" ); @@ -502,8 +557,10 @@ static int wait_for_ack(PacketResponseNG *ack) { return PM3_SUCCESS; } -static bool gs_printed_msg = false; +// If the BOOTLOADER is too old or damaged, we can suggest that the user update the BOOT. static void flash_suggest_update_bootloader(void) { + // Since it's only used internally, we can define it internally. + static bool gs_printed_msg = false; if (gs_printed_msg) { return; } @@ -522,12 +579,66 @@ static void flash_suggest_update_bootloader(void) { gs_printed_msg = true; } +// If the device's boot is newer than the current flasher, we can suggest the user update the flasher. static void flash_suggest_update_flasher(void) { PrintAndLogEx(ERR, _RED_("It is recommended that you first " _YELLOW_("update your flasher"))); } +// AT32 series has a wide range of flash sizes, so we check the chipinfo to set the flash end address and block size. +static void flash_dev_at32_init(uint32_t chipinfo, flash_dev_t *flash_dev) { + flash_dev->flash_start = FLASH_START_AT32; + uint32_t flash_size = chipid_to_mem_avail_at32(chipinfo); + if (flash_size > 1024) { + flash_dev->block_size = 0x1000; // 4K block size for >1M flash + } else { + flash_dev->block_size = 0x800; // 2K block size for <=1M flash + } + flash_dev->flash_end = FLASH_START_AT32 + flash_size * 1024; + flash_dev->boot_size = BOOTLOADER_SIZE_AT32; + flash_dev->boot_end = BOOTLOADER_END_AT32; +} + +// AT91 series has some variations in flash size, so we check the chipinfo to set the flash end address +// and warn the user if they have a large flash but an old bootloader that doesn't support it. +static void flash_dev_at91_init(uint32_t chipinfo, flash_dev_t *flash_dev, int version) { + flash_dev->block_size = BLOCK_SIZE_AT91; + flash_dev->flash_start = FLASH_START_AT91; + flash_dev->flash_end = FLASH_START_AT91 + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES / 2; // Default 256K MAX + flash_dev->boot_size = BOOTLOADER_SIZE_AT91; + flash_dev->boot_end = BOOTLOADER_END_AT91; + // Check the flash capacity based on the idcode returned by the device, that is, enable support for 512K FLASH. + int mem_avail = chipid_to_mem_avail(chipinfo); + if (mem_avail != 0) { + PrintAndLogEx(INFO, "Available memory on this board: "_YELLOW_("%uK") " bytes\n", mem_avail); + if (mem_avail > 256) { + if (BL_VERSION_MAJOR(version) < BL_VERSION_MAJOR(BL_VERSION_1_0_0)) { + PrintAndLogEx(ERR, _RED_("====================== OBS ! ======================")); + PrintAndLogEx(ERR, _RED_("Your bootloader does not support writing above 256k")); + flash_suggest_update_bootloader(); + } else { + // The capacity of the main chip of the device is greater than 256K, + // and BL also supports OTA for chips with such a large capacity. + flash_dev->flash_end = FLASH_START_AT91 + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES; + } + } + } else { + PrintAndLogEx(INFO, "Available memory on this board: "_RED_("UNKNOWN")"\n"); + PrintAndLogEx(ERR, _RED_("====================== OBS ! ======================================")); + PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new" _YELLOW_(" CHIP_INFO") _RED_(" command"))); + flash_suggest_update_bootloader(); + } +} + +// Sending simple cmd without any parameters or data payload, just for arg0. +static void send_cmd_for_arg0(const uint64_t cmd, uint32_t *arg0) { + SendCommandBL(cmd, 0, 0, 0, NULL, 0); + PacketResponseNG resp; + WaitForResponse(cmd, &resp); + *arg0 = resp.oldarg[0]; +} + // Go into flashing mode -int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *max_allowed) { +int flash_start_flashing(int enable_bl_writes, char *serial_port_name, flash_dev_t *flash_dev) { int ret = enter_bootloader(serial_port_name, true); if (ret != PM3_SUCCESS) { @@ -540,31 +651,26 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t return ret; } - uint32_t chipinfo = 0; + flash_dev->chiptype = MAIN_CHIP_TYPE_NONE; + if ((state & DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_TYPE) == DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_TYPE) { + send_cmd_for_arg0(CMD_CHIP_TYPE, &flash_dev->chiptype); + } + uint32_t chipinfo = 0; if ((state & DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO) == DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO) { - SendCommandBL(CMD_CHIP_INFO, 0, 0, 0, NULL, 0); - PacketResponseNG resp; - WaitForResponse(CMD_CHIP_INFO, &resp); - chipinfo = resp.oldarg[0]; + send_cmd_for_arg0(CMD_CHIP_INFO, &chipinfo); } int version = BL_VERSION_INVALID; - if ((state & DEVICE_INFO_FLAG_UNDERSTANDS_VERSION) == DEVICE_INFO_FLAG_UNDERSTANDS_VERSION) { - - SendCommandBL(CMD_BL_VERSION, 0, 0, 0, NULL, 0); - PacketResponseNG resp; - WaitForResponse(CMD_BL_VERSION, &resp); - version = resp.oldarg[0]; - + // Get bootrom version for features and sanity checks + send_cmd_for_arg0(CMD_BL_VERSION, (uint32_t *)&version); + // Is version invalid or outside of expected range? maybe bootrom is very old or corrupted? if ((BL_VERSION_MAJOR(version) < BL_VERSION_FIRST_MAJOR) || (BL_VERSION_MAJOR(version) > BL_VERSION_LAST_MAJOR)) { - // version info seems fishy - version = BL_VERSION_INVALID; + version = BL_VERSION_INVALID; // version info seems fishy PrintAndLogEx(ERR, _RED_("====================== OBS ! ===========================")); PrintAndLogEx(ERR, _RED_("Note: Your bootloader reported an invalid version number")); flash_suggest_update_bootloader(); - // } else if (BL_VERSION_MAJOR(version) < BL_VERSION_MAJOR(FLASHER_VERSION)) { PrintAndLogEx(ERR, _RED_("====================== OBS ! ===================================")); PrintAndLogEx(ERR, _RED_("Note: Your bootloader reported a version older than this flasher")); @@ -580,48 +686,39 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t flash_suggest_update_bootloader(); } - uint32_t flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES / 2; - *max_allowed = 256; + // 1. The old bootloader does not support pm5, nor does it support the 'CMD_CHIP_TYPE' command. + // 2. In the absence of CMD_CHIP_TYPE cmd support, pm3 (at91 platform) is selected as a backup solution. + // 3. Only by combining the parameters of chiptype and chipinfo can the detailed information of the chip currently used by the device be correctly obtained + // 4. This function does not check if the elf file is compatible with the device, so it needs to be checked within the flash_prepare function + switch (flash_dev->chiptype) { + case MAIN_CHIP_TYPE_NONE: + PrintAndLogEx(ERR, _RED_("Bootloader does not support CMD_CHIP_TYPE, assuming AT91 platform")); + flash_dev->chiptype = MAIN_CHIP_TYPE_AT91; + flash_suggest_update_bootloader(); + // break; -> Don't break !!! We want to execute the code for MAIN_CHIP_TYPE_AT91 as well to initialize flash_dev with correct values. - int mem_avail = chipid_to_mem_avail(chipinfo); - if (mem_avail != 0) { + case MAIN_CHIP_TYPE_AT91: + default: + flash_dev_at91_init(chipinfo, flash_dev, version); + break; - PrintAndLogEx(INFO, "Available memory on this board: "_YELLOW_("%uK") " bytes\n", mem_avail); - - if (mem_avail > 256) { - if (BL_VERSION_MAJOR(version) < BL_VERSION_MAJOR(BL_VERSION_1_0_0)) { - PrintAndLogEx(ERR, _RED_("====================== OBS ! ======================")); - PrintAndLogEx(ERR, _RED_("Your bootloader does not support writing above 256k")); - flash_suggest_update_bootloader(); - } else { - flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES; - *max_allowed = mem_avail; - } - } - - } else { - PrintAndLogEx(INFO, "Available memory on this board: "_RED_("UNKNOWN")"\n"); - PrintAndLogEx(ERR, _RED_("====================== OBS ! ======================================")); - PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new" _YELLOW_(" CHIP_INFO") _RED_(" command"))); - flash_suggest_update_bootloader(); + case MAIN_CHIP_TYPE_AT32: + flash_dev_at32_init(chipinfo, flash_dev); + break; } - if (enable_bl_writes) { - PrintAndLogEx(INFO, "Permitted flash range: 0x%08x-0x%08x", FLASH_START, flash_end); - } else { - PrintAndLogEx(INFO, "Permitted flash range: 0x%08x-0x%08x", BOOTLOADER_END, flash_end); - } + // If you need to flash bootrom, the start addr must be 'flash_start', otherwise, it can be 'boot_end' to skip the bootrom area and save some time. + uint32_t start_flash_addr = enable_bl_writes ? flash_dev->flash_start : flash_dev->boot_end; + PrintAndLogEx(INFO, "Permitted flash range: 0x%08x-0x%08x", start_flash_addr, flash_dev->flash_end); if ((state & DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH) == DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH) { - if (enable_bl_writes) { - SendCommandBL(CMD_START_FLASH, FLASH_START, flash_end, START_FLASH_MAGIC, NULL, 0); + SendCommandBL(CMD_START_FLASH, start_flash_addr, flash_dev->flash_end, START_FLASH_MAGIC, NULL, 0); } else { - SendCommandBL(CMD_START_FLASH, BOOTLOADER_END, flash_end, 0, NULL, 0); + SendCommandBL(CMD_START_FLASH, start_flash_addr, flash_dev->flash_end, 0, NULL, 0); } PacketResponseNG resp; return wait_for_ack(&resp); - } else { PrintAndLogEx(ERR, _RED_("====================== OBS ! ========================================")); PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new" _YELLOW_(" START_FLASH") _RED_(" command"))); @@ -635,32 +732,148 @@ int flash_reboot_bootloader(char *serial_port_name, bool wait_appear) { return enter_bootloader(serial_port_name, wait_appear); } -static int write_block(uint32_t address, uint8_t *data, uint32_t length) { - uint8_t block_buf[BLOCK_SIZE]; - memset(block_buf, 0xFF, BLOCK_SIZE); - memcpy(block_buf, data, length); - PacketResponseNG resp; -#if defined ICOPYX - SendCommandBL(CMD_FINISH_WRITE, address, 0xFF, 0x1FD, block_buf, length); -#else - SendCommandBL(CMD_FINISH_WRITE, address, 0, 0, block_buf, length); -#endif - int ret = wait_for_ack(&resp); - if (ret && resp.oldarg[0]) { - uint32_t lock_bits = resp.oldarg[0] >> 16; - bool lock_error = resp.oldarg[0] & AT91C_MC_LOCKE; - bool prog_error = resp.oldarg[0] & AT91C_MC_PROGE; - bool security_bit = resp.oldarg[0] & AT91C_MC_SECURITY; +// Show error information after write failed on AT91 platform. +static void flash_write_err_on_at91(uint32_t err) { + if (err) { + uint32_t lock_bits = err >> 16; + bool lock_error = err & AT91C_MC_LOCKE; + bool prog_error = err & AT91C_MC_PROGE; + bool security_bit = err & AT91C_MC_SECURITY; PrintAndLogEx(NORMAL, "%s", lock_error ? " Lock Error" : ""); PrintAndLogEx(NORMAL, "%s", prog_error ? " Invalid Command or bad Keyword" : ""); PrintAndLogEx(NORMAL, "%s", security_bit ? " Security Bit is set!" : ""); PrintAndLogEx(NORMAL, " Lock Bits: 0x%04x", lock_bits); } +} + +// Show error information after write failed on AT32 platform. +static void flash_write_err_on_at32(uint32_t err) { + // TODO DXL Need to add the header file path of at32 in the makefile/cake of the client(for flash_status_type). + // In order to quickly compile and verify, we will temporarily define constant value. + // However, later on, the types in the header file should be used. + switch (err) { + case 0: // FLASH_OPERATE_BUSY + PrintAndLogEx(ERR, "Flash is busy"); + break; + case 1: // FLASH_PROGRAM_ERROR + PrintAndLogEx(ERR, "Flash program error"); + break; + case 2: // FLASH_EPP_ERROR + PrintAndLogEx(ERR, "Erase/Program protection error"); + break; + case 3: // FLASH_OPERATE_DONE + // Nothing to do... + break; + case 4: // FLASH_OPERATE_TIMEOUT + PrintAndLogEx(ERR, "Flash operation timeout"); + break; + default: + PrintAndLogEx(ERR, "Unknown flash error"); + break; + } +} + +// The error did not occur while writing to the flash memory, but rather during data copying and write boundary checks. +// This is a software error and is unrelated to the hardware. +static void flash_write_err_software(int pm3_err) { + if (pm3_err == PM3_EINVARG) { + PrintAndLogEx(ERR, _RED_("Error:") " Device rejected the firmware, invalid argument"); + PrintAndLogEx(ERR, "This may be because the firmware is not compatible with the device or the bootloader is too old"); + PrintAndLogEx(ERR, "Make sure to use a compatible ELF file and try updating the bootloader if it's old"); + } else if (pm3_err == PM3_EOVFLOW) { + PrintAndLogEx(ERR, _RED_("Error:") " Device rejected the firmware, overflow"); + PrintAndLogEx(ERR, "This may be because the firmware is too large for the device"); + PrintAndLogEx(ERR, "Make sure to use a compatible ELF file and try updating the bootloader if it's old"); + } else if (pm3_err == PM3_EOUTOFBOUND) { + PrintAndLogEx(ERR, _RED_("Error:") " Device rejected the firmware, out of bound"); + PrintAndLogEx(ERR, "This may be because the firmware is trying to write outside of the flash bounds"); + PrintAndLogEx(ERR, "Make sure to use a compatible ELF file and try updating the bootloader if it's old"); + } else { + PrintAndLogEx(ERR, _RED_("Error:") " Device rejected the firmware with error code 0x%02x", pm3_err); + PrintAndLogEx(ERR, "Make sure to use a compatible ELF file and try updating the bootloader if it's old"); + } +} + +// Send finish write cmd and waiting for response. +// The send_buf length is always 512byte(PM3_CMD_DATA_SIZE) +static int send_finish_write_cmd(uint32_t address, int magic, uint8_t *send_buf, PacketResponseNG *resp) { + // The sending length is always PM3_CMD_DATA_SIZE, which is 512 bytes, because of the limitation of the old frame. + const int send_len = PM3_CMD_DATA_SIZE; +#if defined ICOPYX + // To prevent users from flashing unsupported firmware, icopyx checks arg1 and arg2 in this command. + // Therefore, when sending magic to the device, we should not choose a value that happens to be the same as icopyx. + // In fact, neither PM3V nor PM5V will be 0xff or 0x1fd, so this should have strong robustness. + SendCommandBL(CMD_FINISH_WRITE, address, 0xff, 0x1fd, send_buf, send_len); +#else + // If it's an older version of the flashher or a flashher specific to icopyx, then arg1 should be 0x00 or 0xff, + // not a valid magic value. The client is specifically designed for icopyx. + // --- + // For devices with older firmware, it doesn't care about arg1, + // so OTA can be performed regardless of whether it's a new version of flasher (sending arg1) + // or an old version of flasher (arg1 is not a valid magic). + // --- + // For devices with new firmware, if the sent magic is a valid magic value, + // but the firmware cannot work on the device, the device will refuse to write the firmware. + // --- + // The older client version could always OTA update older devices, + // but it couldn't OTA update newer PM5 versions. + // This met our needs because the older client version didn't support PM5's ELF files. + // --- + // The new client version can always continue to OTA update the device version, + // and can also OTA update the latest version of PM5. + SendCommandBL(CMD_FINISH_WRITE, address, magic, 0, send_buf, send_len); +#endif + return wait_for_ack(resp); +} + +// Write a block of data to flash, padding to the block size if needed. The bootloader will read the entire block, +// so we need to make sure to pad it with 0xFF if the data is smaller than the block size. +static int write_block(uint32_t address, int magic, uint8_t *data, uint32_t length, flash_dev_t *flash_dev) { + // Align length to PM3_CMD_DATA_SIZE or block_size + // It is necessary to align with the minimum write unit of the target chip, + // otherwise it may cause the device to lose the data or offset errors. + uint32_t padded_len = length % MAX(PM3_CMD_DATA_SIZE, flash_dev->block_size); + if (padded_len) { + padded_len = MAX(PM3_CMD_DATA_SIZE, flash_dev->block_size) - padded_len; + } + // After aligning PM3_CMD_DATA_SIZE, allocate a new buffer, copy the data, and pad the end with 0xFF. + uint32_t aligned_len = length + padded_len; + uint8_t *block_buf = malloc(aligned_len); + if (block_buf == NULL) { + return PM3_EMALLOC; + } + memset(block_buf, 0xFF, aligned_len); // fill 0xFF by aligned length + memcpy(block_buf, data, length); // copy data by valid length + // Send in packets + int ret = PM3_SUCCESS; + uint32_t sent = 0; + while (sent < aligned_len) { + PacketResponseNG resp; + ret = send_finish_write_cmd(address, magic, block_buf + sent, &resp); + if (ret) { + // On new version of flasher, the arg1 is error code of PM3_E*, old version is 0x00, so we can always check it. + if (resp.oldarg[1]) { // 0x00 == PM3_SUCCESS + flash_write_err_software(resp.oldarg[1]); + } else { + // If not PM3_E*, maybe some errors of flash write occurred. Or is old version boot. + if (flash_dev->chiptype == MAIN_CHIP_TYPE_AT91) { + flash_write_err_on_at91(resp.oldarg[0]); + } else if (flash_dev->chiptype == MAIN_CHIP_TYPE_AT32) { + flash_write_err_on_at32(resp.oldarg[0]); + } else { + PrintAndLogEx(ERR, "Unknown chip type, cannot decode error information"); + } + } + break; + } + sent += PM3_CMD_DATA_SIZE; + } + free(block_buf); // remember to free buffer return ret; } // Write a file's segments to Flash -int flash_write(flash_file_t *ctx) { +int flash_write(flash_file_t *ctx, flash_dev_t *flash_dev) { PrintAndLogEx(SUCCESS, "Writing segments for file: %s", ctx->filename); @@ -668,7 +881,7 @@ int flash_write(flash_file_t *ctx) { flash_seg_t *seg = &ctx->segments[i]; uint32_t length = seg->length; - uint32_t blocks = (length + BLOCK_SIZE - 1) / BLOCK_SIZE; + uint32_t blocks = (length + flash_dev->block_size - 1) / flash_dev->block_size; uint32_t end = seg->start + length; PrintAndLogEx(SUCCESS, " 0x%08x..0x%08x [0x%x / %u blocks]", seg->start, end - 1, length, blocks); @@ -692,12 +905,11 @@ int flash_write(flash_file_t *ctx) { while (length) { uint32_t block_size = length; - if (block_size > BLOCK_SIZE) { - block_size = BLOCK_SIZE; + if (block_size > flash_dev->block_size) { + block_size = flash_dev->block_size; } - if (write_block(baddr, data, block_size) < 0) { - + if (write_block(baddr, ctx->ver_info->magic, data, block_size, flash_dev) < 0) { if (blocks > 50) { hadouken_stop(); } diff --git a/client/src/flash.h b/client/src/flash.h index 8f66a2025..ef9415fb7 100644 --- a/client/src/flash.h +++ b/client/src/flash.h @@ -23,7 +23,6 @@ #include "elf.h" #define FLASH_MAX_FILES 4 -#define ONE_KB 1024 typedef struct { void *data; @@ -36,17 +35,26 @@ typedef struct { uint8_t *elf; Elf32_Phdr_t *phdrs; uint16_t num_phdrs; + struct version_information_t *ver_info; // point to an address in *elf, no need to free. int can_write_bl; int num_segs; flash_seg_t *segments; } flash_file_t; -int flash_load(flash_file_t *ctx, bool force); -int flash_prepare(flash_file_t *ctx, int can_write_bl, int flash_size); -int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *max_allowed); +typedef struct { + uint32_t chiptype; // see: main_chip_type_t + uint32_t block_size; + uint32_t flash_start; + uint32_t flash_end; + uint32_t boot_size; // Boot must be at the top of the flash, so flash_start is boot_start. + uint32_t boot_end; +} flash_dev_t; + int flash_reboot_bootloader(char *serial_port_name, bool wait_appear); -int flash_write(flash_file_t *ctx); +int flash_load(flash_file_t *ctx, bool force); +int flash_prepare(flash_file_t *ctx, int can_write_bl, flash_dev_t *flash_dev); +int flash_start_flashing(int enable_bl_writes, char *serial_port_name, flash_dev_t *flash_dev); +int flash_write(flash_file_t *ctx, flash_dev_t *flash_dev); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); #endif - diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index fa1d6c75d..85a2bbd03 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -931,8 +931,8 @@ finish: static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *filenames[FLASH_MAX_FILES], bool can_write_bl, bool force) { int ret = PM3_EUNDEF; - flash_file_t files[FLASH_MAX_FILES]; - memset(files, 0, sizeof(files)); + flash_file_t files[FLASH_MAX_FILES] = {0}; + flash_dev_t flash_dev = {0}; if (serial_port_name == NULL) { PrintAndLogEx(ERR, "You must specify a port.\n"); @@ -977,8 +977,7 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file goto finish2; } - uint32_t max_allowed = 0; - ret = flash_start_flashing(can_write_bl, serial_port_name, &max_allowed); + ret = flash_start_flashing(can_write_bl, serial_port_name, &flash_dev); if (ret != PM3_SUCCESS) { goto finish; } @@ -988,7 +987,7 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file } for (int i = 0 ; i < num_files; ++i) { - ret = flash_prepare(&files[i], can_write_bl, max_allowed * ONE_KB); + ret = flash_prepare(&files[i], can_write_bl, &flash_dev); if (ret != PM3_SUCCESS) { goto finish; } @@ -998,7 +997,7 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file PrintAndLogEx(SUCCESS, _CYAN_("Flashing...")); for (int i = 0; i < num_files; i++) { - ret = flash_write(&files[i]); + ret = flash_write(&files[i], &flash_dev); if (ret != PM3_SUCCESS) { goto finish; } diff --git a/common_arm/Common.cmake b/common_arm/Common.cmake new file mode 100644 index 000000000..e6052f776 --- /dev/null +++ b/common_arm/Common.cmake @@ -0,0 +1,117 @@ +#[[ VPATH in cmake is unavailable, so we need append path for source or include dir... +# Also search prerequisites in the common directory (for usb.c), the fpga directory (for fpga.bit), and the lz4 directory +VPATH = . ../common_arm ../common ../common/crapto1 ../common/mbedtls ../common/lz4 ../fpga ../armsrc/Standalone ../common/hitag2 +]] + +set(ARMCFLAGS -mthumb-interwork -fno-builtin) +set(DEFCFLAGS -Wall -Werror -Os -pedantic -fstrict-aliasing -pipe) + +# Some more warnings we want as errors: +set(DEFCFLAGS ${DEFCFLAGS} + -Wbad-function-cast + -Wchar-subscripts + -Wundef + -Wunused + -Wuninitialized + -Wpointer-arith + -Wformat + -Wformat-security + -Winit-self + -Wmissing-include-dirs + -Wnested-externs + -Wempty-body + -Wignored-qualifiers + -Wmissing-field-initializers + -Wtype-limits) + +# Some more warnings we need first to eliminate, so temporarely tolerated: +set(DEFCFLAGS ${DEFCFLAGS} + -Wshadow + -Wno-error=shadow + -Winline + -Wno-error=inline + -Wmissing-prototypes + -Wno-error=missing-prototypes + -Wmissing-declarations + -Wno-error=missing-declarations + -Wstrict-prototypes + -Wno-error=strict-prototypes +) + +# still vsnprintf etc to sort out... +# for makefile: DEFCFLAGS += -Wredundant-decls -Wno-error=redundant-decls +# for makefile: DEFCFLAGS += -Wcast-align -Wno-error=cast-align + +# Next ones are activated only if GCCEXTRA=1 +set(EXTRACFLAGS + -Wunused-parameter + -Wno-error=unused-parameter + -Wswitch-enum + -Wno-error=switch-enum + -Wsign-compare + -Wno-error=sign-compare + -Wold-style-definition + -Wno-error=old-style-definition + -Wconversion + -Wno-error=conversion + -Wno-error=sign-conversion + -Wno-error=float-conversion +) + +# unknown to clang or old gcc: +# First we activate Wextra then we explicitly list those we know about +# Those without -Wno-error are supposed to be completely solved +set(GCCEXTRACFLAGS -Wextra) + +# unknown to arm-none-eabi/4.9.3 +set(GCCEXTRACFLAGS ${GCCEXTRACFLAGS} + -Wwrite-strings + -Wno-error=discarded-qualifiers) + +set(GCCEXTRACFLAGS ${GCCEXTRACFLAGS} + -Wold-style-declaration + -Wno-error=old-style-declaration + -Wimplicit-fallthrough=3 + -Wno-error=implicit-fallthrough + -Wclobbered + -Wcast-function-type + -Wmissing-parameter-type + -Woverride-init + -Wshift-negative-value + -Wunused-but-set-parameter +) + +# Not yet enabled in DEFCFLAGS: +set(GCCEXTRACFLAGS ${GCCEXTRACFLAGS} + -Wredundant-decls + -Wno-error=redundant-decls + -Wcast-align + -Wno-error=cast-align +) + +if (GCCEXTRA) + set(DEFCFLAGS ${DEFCFLAGS} ${GCCEXTRACFLAGS} ${EXTRACFLAGS}) +endif () + +if (NOERROR) + set(DEFCFLAGS ${DEFCFLAGS} -Wno-error) +endif () + +if (NOT DEFINED CROSS_CFLAGS) + set(CROSS_CFLAGS ${DEFCFLAGS}) +endif () +set(CROSS_CFLAGS ${CROSS_CFLAGS} + ${ARMCFLAGS} + -c + ${INCLUDE} + # -std=c99 TODO 暂时切换为c11标准,因为新的平台需要c11标准,AT32的官方库一堆c11写法,不这么做的话改起来很麻烦 + -std=c11 + -DON_DEVICE + ${APP_CFLAGS}) +set(CROSS_LDFLAGS ${CROSS_LDFLAGS} + -Wl,-gc-sections + -nostartfiles + -nodefaultlibs + -Wl,--build-id=none + -Wl,-n) +set(LIBS gcc) diff --git a/common_arm/Hal.cmake b/common_arm/Hal.cmake new file mode 100644 index 000000000..64299887b --- /dev/null +++ b/common_arm/Hal.cmake @@ -0,0 +1,255 @@ +#[[ ++============================================+ +| PLATFORM | DESCRIPTION | ++============================================+ +| PM3RDV4 (def) | Proxmark3 RDV4 | ++--------------------------------------------+ +| PM3GENERIC | Proxmark3 generic target | ++--------------------------------------------+ +| PM3ICOPYX | iCopy-X with XC3S100E | ++--------------------------------------------+ +| PM5 | Proxmark5 | ++--------------------------------------------+ +]] +set(PLATFORM "PM3RDV4" CACHE STRING "Default platform is PM3RDV4 if no platform specified") +set_property(CACHE PLATFORM PROPERTY STRINGS "PM3RDV4" "PM3GENERIC" "PM3ICOPYX" "PM5") + +#[[ ++============================================+ +| PLATFORM_EXTRAS | DESCRIPTION | ++============================================+ +| BTADDON | Proxmark3 RDV4 BT add-on | ++--------------------------------------------+ +]] +set(PLATFORM_EXTRAS "" CACHE STRING "Default PLATFORM_EXTRAS is unset") +set_property(CACHE PLATFORM_EXTRAS PROPERTY STRINGS "BTADDON" "") + +# Skip fpga bit stream files pack to arm's fw? +# Some platform is no download in arm side required. +set(SKIP_FPGA_EMBED FALSE CACHE STRING "It is enabled by default. Package the fpga image to the arm firmware.") + +# The arm platform name & fpga platform name +set(PLTNAME "Unknown Platform") +set(PLATFORM_FPGA "fpga-undefined") + +if (PLATFORM STREQUAL "PM3RDV4") + # FPGA bitstream files, the order doesn't matter anymore + set(FPGA_BITSTREAMS ../fpga/fpga_pm3_hf.bit) + if (NOT SKIP_LF) + list(APPEND FPGA_BITSTREAMS ../fpga/fpga_pm3_lf.bit) + endif () + if (NOT SKIP_FELICA) + list(APPEND FPGA_BITSTREAMS ../fpga/fpga_pm3_felica.bit) + endif () + if (NOT SKIP_ISO15693) + list(APPEND FPGA_BITSTREAMS ../fpga/fpga_pm3_hf_15.bit) + endif () + set(PLATFORM_DEFS -DWITH_SMARTCARD -DWITH_FLASH -DRDV4 -DCHIP_AT91SAM7S) + set(PLTNAME "Proxmark3 RDV4") + set(PLATFORM_FPGA "xc2s30") + set(RDV4 TRUE) +elseif (PLATFORM STREQUAL "PM3OTHER") + message(WARNING "PLATFORM=PM3OTHER is deprecated, please use PLATFORM=PM3GENERIC") + set(_IS_GENERIC TRUE) # Fall through to PM3GENERIC behavior +elseif (PLATFORM STREQUAL "PM3GENERIC") + set(_IS_GENERIC TRUE) +elseif (PLATFORM STREQUAL "PM3ICOPYX") + set(FPGA_BITSTREAMS ../fpga/fpga_icopyx_hf.bit) + set(PLATFORM_DEFS -DWITH_FLASH -DICOPYX -DXC3 -DCHIP_AT91SAM7S) + set(PLTNAME "iCopy-X with XC3S100E") + set(PLATFORM_FPGA "xc3s100e") +elseif (PLATFORM STREQUAL "PM5") + # TODO DXL 我们暂时不需要指定FPGA比特流文件,因为实际上我们大概率要做FPGA的静态烧录,而不是附加到ARM固件中动态下载 + set(FPGA_BITSTREAMS ../fpga/fpga_pm3_hf.bit) # TODO DXL 虽然可以不把打包比特流,但是还是得把FPGA的版本信息给生成,让EXE依赖。 + set(SKIP_FPGA_EMBED TRUE) # important!!! disable the fpga bit files pack to arm! + set(SKIP_COMPRESSION TRUE) # Skip data section compress. The new mcu has enough flash space. + set(PLATFORM_DEFS -DWITH_FLASH -DPM5 -DCHIP_AT32F435_37) # TODO DXL 暂时不要编译i2c -DWITH_SMARTCARD + set(PLTNAME "Proxmark5") + set(PLATFORM_FPGA "GW1NR-LV2MG49GC6/i5") + set(PM5 TRUE) +else () + message(FATAL_ERROR "Invalid or empty PLATFORM: ${PLATFORM}. Known platforms: PM3RDV4, PM3GENERIC, PM3ICOPYX (PM3OTHER is deprecated)") +endif () + +# PM3GENERIC and PM3OTHER +if (_IS_GENERIC) + set(FPGA_BITSTREAMS ../fpga/fpga_pm3_hf.bit) + if (NOT SKIP_LF) + list(APPEND FPGA_BITSTREAMS ../fpga/fpga_pm3_lf.bit) + endif () + if (NOT SKIP_FELICA) + list(APPEND FPGA_BITSTREAMS ../fpga/fpga_pm3_felica.bit) + endif () + if (NOT SKIP_ISO15693) + list(APPEND FPGA_BITSTREAMS ../fpga/fpga_pm3_hf_15.bit) + endif () + set(PLTNAME "Proxmark3 generic target") + set(PLATFORM_FPGA "xc2s30") + set(PLATFORM_DEFS -DCHIP_AT91SAM7S) + if (LED_ORDER STREQUAL "PM3EASY") + list(APPEND PLATFORM_DEFS -DLED_ORDER_PM3EASY) + endif () +endif () + +# If no fpga bitstream pack to arm, set flag for arm compile. +if (SKIP_FPGA_EMBED) + list(APPEND PLATFORM_DEFS -DNO_FPGA_BITSTREAM_PACK) +endif () + +# parsing additional PLATFORM_EXTRAS tokens +set(PLATFORM_EXTRAS_TMP ${PLATFORM_EXTRAS}) +if ("${PLATFORM_EXTRAS_TMP}" MATCHES "SMARTCARD") + list(APPEND PLATFORM_DEFS -DWITH_SMARTCARD) + list(REMOVE_ITEM PLATFORM_EXTRAS_TMP "SMARTCARD") +endif () +if ("${PLATFORM_EXTRAS_TMP}" MATCHES "FLASH") + list(APPEND PLATFORM_DEFS -DWITH_FLASH) + list(REMOVE_ITEM PLATFORM_EXTRAS_TMP "FLASH") +endif () +if ("${PLATFORM_EXTRAS_TMP}" MATCHES "BTADDON") + list(APPEND PLATFORM_DEFS -DWITH_FPC_USART_HOST) + list(REMOVE_ITEM PLATFORM_EXTRAS_TMP "BTADDON") +endif () +if ("${PLATFORM_EXTRAS_TMP}" MATCHES "FPC_USART_DEV") + list(APPEND PLATFORM_DEFS -DWITH_FPC_USART_DEV) + list(REMOVE_ITEM PLATFORM_EXTRAS_TMP "FPC_USART_DEV") +endif () +if (PLATFORM_EXTRAS_TMP) + message(FATAL_ERROR "Unknown PLATFORM_EXTRAS token(s): ${PLATFORM_EXTRAS_TMP}") +endif () + +# common LF support +if (NOT SKIP_LF) + list(APPEND PLATFORM_DEFS -DWITH_LF) +endif () +if (NOT SKIP_HITAG) + list(APPEND PLATFORM_DEFS -DWITH_HITAG) +endif () +if (NOT SKIP_EM4x50) + list(APPEND PLATFORM_DEFS -DWITH_EM4x50) +endif () +if (NOT SKIP_EM4x70) + list(APPEND PLATFORM_DEFS -DWITH_EM4x70) +endif () +if (NOT SKIP_ZX8211) + list(APPEND PLATFORM_DEFS -DWITH_ZX8211) +endif () + +# common HF support +if (NOT SKIP_HF) + list(APPEND PLATFORM_DEFS -DWITH_GENERAL_HF) +endif () +if (NOT SKIP_ISO15693) + list(APPEND PLATFORM_DEFS -DWITH_ISO15693) +endif () +if (NOT SKIP_LEGICRF) + list(APPEND PLATFORM_DEFS -DWITH_LEGICRF) +endif () +if (NOT SKIP_ISO14443b) + list(APPEND PLATFORM_DEFS -DWITH_ISO14443b) +endif () +if (NOT SKIP_ISO14443a) + list(APPEND PLATFORM_DEFS -DWITH_ISO14443a) +endif () +if (NOT SKIP_ICLASS) + list(APPEND PLATFORM_DEFS -DWITH_ICLASS) +endif () +if (NOT SKIP_FELICA) + list(APPEND PLATFORM_DEFS -DWITH_FELICA) +endif () +if (NOT SKIP_NFCBARCODE) + list(APPEND PLATFORM_DEFS -DWITH_NFCBARCODE) +endif () +if (NOT SKIP_HFSNIFF) + list(APPEND PLATFORM_DEFS -DWITH_HFSNIFF) +endif () +if (NOT SKIP_HFPLOT) + list(APPEND PLATFORM_DEFS -DWITH_HFPLOT) +endif () +if (NOT SKIP_COMPRESSION) + list(APPEND PLATFORM_DEFS -DWITH_COMPRESSION) +endif () + +# Standalone mode +if (STANDALONE_REQ_DEFS) + message(STATUS "-------------- PLATFORM_DEFS = ${PLATFORM_DEFS}") + if (NOT "${PLATFORM_DEFS};" MATCHES ".*;(${STANDALONE_REQ_DEFS});.*") + message(FATAL_ERROR "Chosen Standalone mode ${STANDALONE} requires ${STANDALONE_REQ_DEFS}, unsupported by ${PLTNAME}") + endif () +endif () +if (DEFINED STANDALONE_PLATFORM_DEFS AND NOT "${STANDALONE_PLATFORM_DEFS}" STREQUAL "") + list(APPEND PLATFORM_DEFS ${STANDALONE_PLATFORM_DEFS}) +endif () +# Find and print standalone-related definitions +string(REGEX MATCHALL "WITH_STANDALONE_[^;]+" STANDALONE_DEFS_FOUND "${PLATFORM_DEFS}") + +# Misc (LCD support) +if ("${PLATFORM_DEFS}" MATCHES "WITH_LCD") + list(APPEND PLATFORM_DEFS -DWITH_LCD) +endif () + +# WITH_FPC_USART_* needs WITH_FPC_USART +string(FIND "${PLATFORM_DEFS}" "WITH_FPC_USART_" _FPC_POS) +if (NOT _FPC_POS STREQUAL "-1") + list(APPEND PLATFORM_DEFS -DWITH_FPC_USART) +endif () + +# Extract non-standalone platform defs (remove -DWITH_ prefix and STANDALONE* entries) +string(REPLACE "-DWITH_" "" PLATFORM_DEFS_CLEAN "${PLATFORM_DEFS}") +separate_arguments(PLATFORM_DEFS_CLEAN) +# Filter out STANDALONE entries +set(PLATFORM_DEFS_INFO) +foreach (def ${PLATFORM_DEFS_CLEAN}) + if (NOT def MATCHES "^STANDALONE_") + list(APPEND PLATFORM_DEFS_INFO ${def}) + endif () +endforeach () +list(REMOVE_DUPLICATES PLATFORM_DEFS_INFO) +# Extract standalone mode (remove 'STANDALONE_' prefix) + +message(STATUS "PLATFORM_DEFS_INFO = ${PLATFORM_DEFS_INFO}") + +set(PLATFORM_DEFS_INFO_STANDALONE) +foreach (def ${PLATFORM_DEFS_CLEAN}) + if (def MATCHES "^STANDALONE_(.+)") + list(APPEND PLATFORM_DEFS_INFO_STANDALONE "${CMAKE_MATCH_1}") + endif () +endforeach () +list(REMOVE_DUPLICATES PLATFORM_DEFS_INFO_STANDALONE) + +# Check that only one Standalone mode is selected +list(LENGTH PLATFORM_DEFS_INFO_STANDALONE STANDALONE_COUNT) +if (STANDALONE_COUNT GREATER 1) + message(FATAL_ERROR "You must choose only one Standalone mode!: ${PLATFORM_DEFS_INFO_STANDALONE}") +endif () + +# Set extras info +set(PLATFORM_EXTRAS_INFO ${PLATFORM_EXTRAS}) +if (NOT PLATFORM_EXTRAS_INFO) + set(PLATFORM_EXTRAS_INFO "No extra selected") +endif () + +message(STATUS "PLATFORM_DEFS_INFO_STANDALONE = ${PLATFORM_DEFS_INFO_STANDALONE}") + +# Set standalone info +if (NOT PLATFORM_DEFS_INFO_STANDALONE) + set(PLATFORM_DEFS_INFO_STANDALONE "No standalone mode selected") +endif () + +# Default platform size +if (NOT PLATFORM_SIZE) + set(PLATFORM_SIZE 512) +endif () + +# Show some vars. +message(STATUS "===================================================================") +message(STATUS "Version info : ${VERSION_INFO} ") +message(STATUS "Platform name : ${PLTNAME} ") +message(STATUS "PLATFORM : ${PLATFORM} ") +message(STATUS "PLATFORM_FPGA : ${PLATFORM_FPGA} ") +message(STATUS "PLATFORM_SIZE : ${PLATFORM_SIZE} ") +message(STATUS "Platform extras : ${PLATFORM_EXTRAS_INFO} ") +message(STATUS "Included options : ${PLATFORM_DEFS_INFO} ") +message(STATUS "Standalone mode : ${PLATFORM_DEFS_INFO_STANDALONE} ") +message(STATUS "C Compiler Host : ${C_COMPILER_HOST}") +message(STATUS "===================================================================") diff --git a/common_arm/Makefile.common b/common_arm/Makefile.common index 7ce4c8f19..56ef0753a 100644 --- a/common_arm/Makefile.common +++ b/common_arm/Makefile.common @@ -42,9 +42,13 @@ CROSS_OBJCOPY = $(CROSS)objcopy OBJDIR = obj INCLUDE = -I../include -I../common_arm -I../common_fpga -I../common -I. +INCLUDE += -I../common_arm/flash_code -I../common_arm/flash_data -I../common_arm/fpga -I../common_arm/gpio +INCLUDE += -I../common_arm/rssi -I../common_arm/sys -I../common_arm/ticks -I../common_arm/usb -I../common_arm/wdt # Also search prerequisites in the common directory (for usb.c), the fpga directory (for fpga.bit), and the lz4 directory VPATH = . ../common_arm ../common ../common/crapto1 ../common/mbedtls ../common/lz4 ../fpga ../armsrc/Standalone ../common/hitag2 +VPATH += ../common_arm/flash_code ../common_arm/flash_data ../common_arm/fpga ../common_arm/gpio +VPATH += ../common_arm/rssi ../common_arm/sys ../common_arm/ticks ../common_arm/usb ../common_arm/wdt INCLUDES = ../include/proxmark3_arm.h ../include/at91sam7s512.h ../include/config_gpio.h ../include/pm3_cmd.h @@ -106,7 +110,7 @@ CROSS_CFLAGS += -DON_DEVICE $(INCLUDE) $(APP_CFLAGS) CROSS_LDFLAGS += -nostartfiles -nodefaultlibs -Wl,--gc-sections -Wl,--build-id=none -Wl,--nmagic #CROSS_LDFLAGS += -Wl,--print-gc-sections -LIBS = -lgcc +LIBS += -lgcc # Flags to generate temporary dependency files DEPFLAGS = -MT $@ -MMD -MP -MF $(OBJDIR)/$*.Td diff --git a/common_arm/Makefile.hal b/common_arm/Makefile.hal index 2e10ccd3d..619a5e701 100644 --- a/common_arm/Makefile.hal +++ b/common_arm/Makefile.hal @@ -40,6 +40,8 @@ Known definitions: +--------------------------------------------------+ | PM3ULTIMATE | Proxmark3 Ultimate with XC2S50 | +--------------------------------------------------+ +| PM5 | Proxmark5 ICEMAN Edition | ++--------------------------------------------------+ +============================================+ | PLATFORM_EXTRAS | DESCRIPTION | @@ -111,7 +113,7 @@ ifeq ($(PLATFORM),PM3RDV4) ifneq ($(SKIP_ISO15693),1) FPGA_BITSTREAMS += fpga_pm3_hf_15.bit endif - PLATFORM_DEFS = -DWITH_SMARTCARD -DWITH_FLASH -DRDV4 + PLATFORM_DEFS = -DWITH_SMARTCARD -DWITH_FLASH -DRDV4 -DCHIP_AT91SAM7S PLTNAME = Proxmark3 RDV4 PLATFORM_FPGA = xc2s30 RDV4 = yes @@ -131,7 +133,7 @@ else ifeq ($(PLATFORM),PM3OTHER) PLTNAME = Proxmark3 generic target PLATFORM_FPGA = xc2s30 ifeq ($(LED_ORDER),PM3EASY) - PLATFORM_DEFS = -DLED_ORDER_PM3EASY + PLATFORM_DEFS = -DLED_ORDER_PM3EASY -DCHIP_AT91SAM7S endif else ifeq ($(PLATFORM),PM3GENERIC) # FPGA bitstream files, the order doesn't matter anymore @@ -148,12 +150,12 @@ else ifeq ($(PLATFORM),PM3GENERIC) PLTNAME = Proxmark3 generic target PLATFORM_FPGA = xc2s30 ifeq ($(LED_ORDER),PM3EASY) - PLATFORM_DEFS = -DLED_ORDER_PM3EASY + PLATFORM_DEFS = -DLED_ORDER_PM3EASY -DCHIP_AT91SAM7S endif else ifeq ($(PLATFORM),PM3ICOPYX) # FPGA bitstream files, the order doesn't matter anymore - only hf has a bitstream FPGA_BITSTREAMS = fpga_icopyx_hf.bit - PLATFORM_DEFS = -DWITH_FLASH -DICOPYX -DXC3 + PLATFORM_DEFS = -DWITH_FLASH -DICOPYX -DXC3 -DCHIP_AT91SAM7S PLTNAME = iCopy-X with XC3S100E PLATFORM_FPGA = xc3s100e else ifeq ($(PLATFORM),PM3ULTIMATE) @@ -168,9 +170,17 @@ else ifeq ($(PLATFORM),PM3ULTIMATE) ifneq ($(SKIP_ISO15693),1) FPGA_BITSTREAMS += fpga_pm3_ult_hf_15.bit endif - PLATFORM_DEFS = -DWITH_FLASH -DXC2S50 + PLATFORM_DEFS = -DWITH_FLASH -DXC2S50 -DCHIP_AT91SAM7S PLTNAME = Proxmark3 Ultimate with XC2S50 PLATFORM_FPGA = xc2s50 +else ifeq ($(PLATFORM),PM5) + # TODO DXL 我们暂时不需要指定FPGA比特流文件,因为实际上我们大概率要做FPGA的静态烧录,而不是附加到ARM固件中动态下载 + FPGA_BITSTREAMS = fpga_pm3_hf.bit # TODO DXL 虽然可以不把打包比特流,但是还是得把FPGA的版本信息给生成,让EXE依赖。 + SKIP_FPGA_EMBED = true # important!!! disable the fpga bit files pack to arm! + SKIP_COMPRESSION = true # Skip data section compress. The new mcu has enough flash space. + PLATFORM_DEFS = -DWITH_FLASH -DPM5 -DCHIP_AT32F435_37 # TODO 暂时不要编译i2c -DWITH_SMARTCARD + PLTNAME = Proxmark5 + PLATFORM_FPGA = GW1NR-LV2MG49GC6/i5 else $(error Invalid or empty PLATFORM: $(PLATFORM). $(KNOWN_DEFINITIONS)) endif diff --git a/common_arm/at32f435xG.cfg b/common_arm/at32f435xG.cfg new file mode 100644 index 000000000..5cf249107 --- /dev/null +++ b/common_arm/at32f435xG.cfg @@ -0,0 +1,98 @@ +# script for AT32f4xx family + +# +# AT32 devices support both JTAG and SWD transports. +# + +# What's your XX link using? +# source [find interface/jlink.cfg] +source [find interface/cmsis-dap.cfg] +source [find target/swj-dp.tcl] +source [find mem_helper.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME at32f435xx +} + +if { [info exists ENDIAN] } { + set _ENDIAN $ENDIAN +} else { + set _ENDIAN little +} + +# Work-area is a space in RAM used for flash programming +# By default use 64kB +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x1000 +} + +#jtag scan chain +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + if { [using_jtag] } { + set _CPUTAPID 0x4ba00477 + } else { + set _CPUTAPID 0x2ba01477 + } +} + +# Allow overriding the Flash bank size +if { [info exists FLASH_SIZE] } { + set _FLASH_SIZE $FLASH_SIZE +} else { + # autodetect size + set _FLASH_SIZE 0 +} + + +swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +# flash size will be probed +set _FLASHNAME $_CHIPNAME.bank1 +flash bank $_FLASHNAME $_CHIPNAME 0x08000000 0 0 0 $_TARGETNAME +set _FLASHNAME $_CHIPNAME.bank2 +flash bank $_FLASHNAME $_CHIPNAME 0x08080000 0 0 0 $_TARGETNAME + + +# JTAG speed should be <= F_CPU/6. F_CPU after reset is 8MHz, so use F_JTAG = 1MHz +adapter speed 5000 + +adapter srst delay 100 + +reset_config srst_nogate + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +$_TARGETNAME configure -event examine-end { + # DBGMCU_CR |= DBG_WWDG_STOP | DBG_IWDG_STOP | + # DBG_STANDBY | DBG_STOP | DBG_SLEEP + mmw 0xE0042004 0x00000307 0 +} + +$_TARGETNAME configure -event trace-config { + # Set TRACE_IOEN; TRACE_MODE is set to async; when using sync + # change this value accordingly to configure trace pins + # assignment + mmw 0xE0042004 0x00000020 0 +} + +$_TARGETNAME configure -event reset-init { + mww 0x400238A0 0x000F5000 + mww 0x40023C60 0x00000000 + sleep 1 +} diff --git a/common_arm/flash_code/flash_code_apis.h b/common_arm/flash_code/flash_code_apis.h new file mode 100644 index 000000000..b69789848 --- /dev/null +++ b/common_arm/flash_code/flash_code_apis.h @@ -0,0 +1,42 @@ +// +// Created by dxl on 2026/5/25. +// + +#ifndef FLASH_CODE_APIS_H +#define FLASH_CODE_APIS_H + +#include "common.h" + + +/** + * Write code flash minimum unit. The implementation of this function is very specific to different platforms. + * The minimum flash rewriting unit varies from platform to platform, + * so the data length must strictly comply with the length returned by FlashCodeGetEWMinUnit. + * + * @param flash_address The flash address to write to. Must be aligned to 4 bytes. + * @param data The data to write. u32 only. + * @param flash_start The flash start address of firmware code. + * @param status The pointer to store the platform specific status code of flash erase/write. + * @return Whether the operation is successful. If it fails, you can refer to the status. + */ +bool FlashCodeEWriteMinUnit(uint32_t flash_address, const uint32_t *data, uint32_t *flash_start, uint32_t *status); + +/** + * Minimum unit for Flash erase/write. + * @return Minimum number of bytes per erase/write. + */ +STATIC_FORCE_INLINE uint16_t FlashCodeGetEWMinUnit(void); + +/** + * Initialize the FLASH that stores firmware/code. + * Configure the clock speed of FLASH, for example. + */ +STATIC_FORCE_INLINE void FlashCodeInit(void); + +#ifdef PM5 +#include "flash_code_hw_at32.h" +#else +#include "flash_code_hw_at91.h" +#endif + +#endif //FLASH_CODE_APIS_H diff --git a/common_arm/flash_code/flash_code_hw_at32.c b/common_arm/flash_code/flash_code_hw_at32.c new file mode 100644 index 000000000..199ba7c46 --- /dev/null +++ b/common_arm/flash_code/flash_code_hw_at32.c @@ -0,0 +1,73 @@ +// +// Created by dxl on 2026/5/26. +// +#include "at32f435_437_flash.h" +#include "at32f435_437_misc.h" +#include "flash_code_apis.h" + + +// The configuration is 512K. If the code execution speed is desired, +// please define the functions as a code segment executed by RAM. +// 512K_SRAM -> Flash memory zero wait delay area 128K bytes +// 448K_SRAM -> Flash memory zero wait delay area 192K bytes +// 384K_SRAM -> Flash memory zero wait delay area 256K bytes +// 320K_SRAM -> Flash memory zero wait delay area 320K bytes +// 256K_SRAM -> Flash memory zero wait delay area 384K bytes +// 192K_SRAM -> Flash memory zero wait delay area 448K bytes +// 128K_SRAM -> Flash memory zero wait delay area 512K bytes +#define AT32_EXTEND_SRAM FLASH_EOPB0_SRAM_512K + + +void Extend_SRAM(void) { +#ifdef AS_BOOTROM // !!! Warning: this function only works in bootrom. Otherwise, it may cause crash/infinite restart. + // check if ram has been set to expectant size, if not, change eopb0 + if (((USD->eopb0) & 0x07) != AT32_EXTEND_SRAM) { + // unlock flash first + flash_unlock(); + // erase user system data bytes + flash_user_system_data_erase(); + // change sram size. Theoretically, we need to judge whether it can be set to this size according to the flash size, + // but PM5 is only 1M, so we will not judge it temporarily. + flash_eopb0_config(AT32_EXTEND_SRAM); + // system reset + nvic_system_reset(); + } +#endif +} + +bool FlashCodeEWriteMinUnit(uint32_t flash_address, const uint32_t *data, uint32_t *flash_start, uint32_t *status) { + const uint32_t min_ew_unit = FlashCodeGetEWMinUnit(); + const uint32_t min_ew_unit_u32 = min_ew_unit / sizeof(uint32_t); + UNUSED(flash_start); + + flash_unlock(); + + // Wait for operation to be completed + *status = flash_operation_wait_for(ERASE_TIMEOUT); + if((*status == FLASH_PROGRAM_ERROR) || (*status == FLASH_EPP_ERROR)) { + flash_flag_clear(FLASH_PRGMERR_FLAG | FLASH_EPPERR_FLAG); + } else if(*status == FLASH_OPERATE_TIMEOUT) { + return false; + } + + // Erase and write using the starting address of the sector. + flash_address = (flash_address / min_ew_unit) * min_ew_unit; + + // Erase + *status = flash_sector_erase(flash_address); + if(*status != FLASH_OPERATE_DONE) { + return false; + } + + // Write + for(uint32_t i = 0; i < min_ew_unit_u32; i++) { + uint32_t w_addr = flash_address + i * sizeof(uint32_t); + *status = flash_word_program(w_addr, data[i]); + if(*status != FLASH_OPERATE_DONE) { + return false; + } + } + + flash_lock(); + return true; +} diff --git a/common_arm/flash_code/flash_code_hw_at32.h b/common_arm/flash_code/flash_code_hw_at32.h new file mode 100644 index 000000000..eae6e36c5 --- /dev/null +++ b/common_arm/flash_code/flash_code_hw_at32.h @@ -0,0 +1,65 @@ +// +// Created by dxl on 2026/5/25. +// + +#ifndef FLASH_CODE_HW_AT32_H +#define FLASH_CODE_HW_AT32_H + +#include "common.h" +#include "sys_apis.h" +#include "at32f435_437_flash.h" + + +/** + * Config the sram extend for MORE ram size. + * Note: sacrifice non-0 wait FLASH area. And this configuration function must be called + * before accessing a larger memory area, otherwise HW FAULT may result. + */ +void Extend_SRAM(void); + +// It is not allowed to hard code 4096 or 2048, but should be determined according to the current chip capacity. +STATIC_FORCE_INLINE uint16_t FlashCodeGetEWMinUnit(void) { + // 4032K: + // The flash memory capacity of slice 1 is 2048K bytes, including 32 blocks, each block has 16 sectors, and each sector size is 4K bytes; + // The flash memory capacity of slice 2 is 1984K bytes, including 31 blocks. Each block has 16 sectors, and the size of each sector is 4K bytes. + // The user system data area is 4K bytes in total. + // 1024K: + // The main memory is divided into chip 1 and chip 2 flash memory. Each flash memory has a capacity of 512K bytes and contains 8 blocks, + // each block contains 32 sectors, and the size of each sector is 2K bytes. + // The user system data area is 512 bytes in total. + // 256K: + // The 256K byte main memory has only one flash memory, which contains 4 blocks. + // Each block contains 32 sectors, and each sector is 2K bytes in size. + // The user system data area is 512 bytes in total. + if (GetChipFlashSize() > 1024 * 1024) { + return 4096; + } + return 2048; +} + +/** + * Improve the performance of flash + * See: https://www.arterytek.com/download/APNOTE/AN0092_AT32F435_437_Performance_Improve_V2.0.1_EN.pdf + */ +STATIC_FORCE_INLINE void FlashCodeInit(void) { + /* + Note: If you want to improve the performance of the non-zero wait flash area, + you need to pay attention to the following specification limits. + +--------+--------------------------+--------------------------------+---------------------+-------+-------+------+ + | Symbol | Parameter | Condition | Sub-Condition | Min | Max | Unit | + +--------+--------------------------+--------------------------------+---------------------+-------+-------+------+ + | | | NZW_BST acceleration off | LDO Voltage 1.3 V | 0 | 288 | | + | | | | LDO Voltage 1.2 V | 0 | 240 | | + | f_HCLK | Internal AHB clock freq | | LDO Voltage 1.1 V | 0 | 144 | MHz | + | | |--------------------------------+---------------------+-------+-------+ | + | | | NZW_BST acceleration on | LDO Voltage 1.3 V | 0 | 192 | | + | | | | LDO Voltage 1.2 V | 0 | 160 | | + | | | | LDO Voltage 1.1 V | 0 | 108 | | + +--------+--------------------------+--------------------------------+---------------------+-------+-------+------+ + */ + + // Improve the performance of continuous flash reading, Note: increased power consumption. + flash_continue_read_enable(TRUE); // FLASH->contr_bit.fcontr_en = TRUE; +} + +#endif //FLASH_CODE_HW_AT32_H diff --git a/common_arm/flash_code/flash_code_hw_at91.c b/common_arm/flash_code/flash_code_hw_at91.c new file mode 100644 index 000000000..b858fbd19 --- /dev/null +++ b/common_arm/flash_code/flash_code_hw_at91.c @@ -0,0 +1,47 @@ +// +// Created by dxl on 2026/5/27. +// +#include "flash_code_apis.h" + + +RAMFUNC +bool FlashCodeEWriteMinUnit(uint32_t flash_address, const uint32_t *data, uint32_t *flash_start, uint32_t *status) { + *status = 0x00; + // The default is AT91C_BASE_EFC0. If the current write address exceeds AT91C_BASE_EFC0, + // it will automatically switch to AT91C_BASE_EFC1. + AT91PS_EFC efc_bank = AT91C_BASE_EFC0; + // If bank1 is currently being writing, we need to calculate the offset to get the starting position of bank1 in flash. + int offset = 0; + // Calculate how many pages have been written in total. + uint32_t page_n = (flash_address - (uint32_t) flash_start) / AT91C_IFLASH_PAGE_SIZE; + if (page_n >= AT91C_IFLASH_NB_OF_PAGES / 2) { + // When writing to bank2, we need to recalculate the page from 0 to 1023. + page_n -= AT91C_IFLASH_NB_OF_PAGES / 2; + // Switch to AT91C_BASE_EFC1 + efc_bank = AT91C_BASE_EFC1; + // We need to offset the writes or it will not fill the correct bank write buffer. + // offset = 65535, 65535 * 4(u32) = 262,140, for write bank1 not bank0. + offset = (AT91C_IFLASH_NB_OF_PAGES / 2) * AT91C_IFLASH_PAGE_SIZE / sizeof(uint32_t); + } + // The Flash of the SAM7S512/256/128 contains a 256-byte write buffer, accessible through a 32-bit interface. + // The Flash of the SAM7S64/321/32/161/16 contains a 128-byte write buffer, accessible through a 32-bit interface. + // The writing is not directly written to the flash, but committed to the latch buffer, + // and then to write the EFC register triggers the erase and write. + // In addition, the write operation only considers the address of the lower eight bits, + // so actually only needs to write data to flash_start, + // and the chip will automatically copy the data to the latch buffer and increase count. + for (int i = 0; i < FlashCodeGetEWMinUnit() / sizeof(uint32_t); i++) { + flash_start[offset + i] = data[i]; + } + efc_bank->EFC_FCR = MC_FLASH_COMMAND_KEY | + MC_FLASH_COMMAND_PAGEN(page_n) | + AT91C_MC_FCMD_START_PROG; + // Wait until flashing of page finishes + uint32_t sr; + while (!((sr = efc_bank->EFC_FSR) & AT91C_MC_FRDY)); + if (sr & (AT91C_MC_LOCKE | AT91C_MC_PROGE)) { + *status = sr; + return false; + } + return true; +} diff --git a/common_arm/flash_code/flash_code_hw_at91.h b/common_arm/flash_code/flash_code_hw_at91.h new file mode 100644 index 000000000..45ac3390e --- /dev/null +++ b/common_arm/flash_code/flash_code_hw_at91.h @@ -0,0 +1,32 @@ +// +// Created by dxl on 2026/5/25. +// + +#ifndef FLASH_CODE_HW_AT91_H +#define FLASH_CODE_HW_AT91_H + +#include "flash_code_apis.h" +#include "proxmark3_arm.h" +#include "at91sam7s512.h" +#include "sys_apis.h" + + +STATIC_FORCE_INLINE uint16_t FlashCodeGetEWMinUnit(void) { + // The page size of the chip used by pm3 is only 256 bytes. + // No 128/64 byte page size. + // If a pm3 device really uses such a small capacity chip, remember to add this compatibility support. + return AT91C_IFLASH_PAGE_SIZE; +} + +STATIC_FORCE_INLINE void FlashCodeInit(void) { + // Set the first 256KB memory flashspeed + AT91C_BASE_EFC0->EFC_FMR = AT91C_MC_FWS_1FWS | MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48); + // 9 = 256, 10+ is 512KB + uint8_t id = (GetChipId() & 0xF00) >> 8; + if (id > 9) { + // Set the second 256KB memory flashspeed, if it exists + AT91C_BASE_EFC1->EFC_FMR = AT91C_MC_FWS_1FWS | MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48); + } +} + +#endif //FLASH_CODE_HW_AT91_H diff --git a/common_arm/flashmem.h b/common_arm/flash_data/flashmem.h similarity index 74% rename from common_arm/flashmem.h rename to common_arm/flash_data/flashmem.h index 97a46fd38..2a53e0e4b 100644 --- a/common_arm/flashmem.h +++ b/common_arm/flash_data/flashmem.h @@ -17,13 +17,12 @@ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // Common Instructions // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -#ifndef __FLASHMEM_H -#define __FLASHMEM_H +#ifndef FLASHMEM_H_ +#define FLASHMEM_H_ #include "common.h" #include "pmflash.h" - // Used Command #define ID 0x90 #define MANID 0x90 @@ -38,6 +37,7 @@ #define READDATA 0x03 #define FASTREAD 0x0B +#define FASTREAD_QO 0x6B // Fast Read Quad Output, qspi, some platform unsupported(at91, haha). #define PAGEPROG 0x02 #define SECTORERASE 0x20 @@ -59,8 +59,6 @@ #define PAGESIZE 0x100 #define WINBOND_WRITE_DELAY 0x02 -#define SPI_CLK 48000000 - #define BUSY 0x01 #define WRTEN 0x02 #define SUS 0x40 @@ -71,58 +69,38 @@ #define NO_CONTINUE 0x00 #define PASS 0x01 #define FAIL 0x00 -#define maxAddress capacity - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// List of Error codes // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -#define SUCCESS 0x00 -#define CALLBEGIN 0x01 -#define UNKNOWNCHIP 0x02 -#define UNKNOWNCAP 0x03 -#define CHIPBUSY 0x04 -#define OUTOFBOUNDS 0x05 -#define CANTENWRITE 0x06 -#define PREVWRITTEN 0x07 -#define LOWRAM 0x08 -#define NOSUSPEND 0x09 -#define UNKNOWNERROR 0xFF // List of blocks #define MAX_BLOCKS 4 #define MAX_SECTORS 16 -//#define FLASH_BAUD 24000000 -#define FLASH_MINFAST 24000000 //33000000 -#define FLASH_BAUD MCK/2 -#define FLASH_FASTBAUD MCK -#define FLASH_MINBAUD FLASH_FASTBAUD - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// The default values returned by different platforms are different. +// This function is implemented by the platform. +uint32_t Flash_DefaultBaudrate(void); + bool FlashInit(void); -void Flash_UniqueID(uint8_t *uid); +bool FlashSetup(uint32_t baudrate); void FlashStop(void); -void FlashSetup(uint32_t baudrate); +bool Flash_UniqueID(uint8_t *uid); bool Flash_CheckBusy(uint32_t timeout); -uint8_t Flash_ReadStat1(void); -uint16_t FlashSendByte(uint32_t data); -uint16_t FlashSendLastByte(uint32_t data); - +bool Flash_ReadStat1(uint8_t *status); +bool Flash_ReadStat2(uint8_t *status); #ifndef AS_BOOTROM -void FlashmemSetSpiBaudrate(uint32_t baudrate); -bool Flash_WaitIdle(void); -void Flash_TransferAdresse(uint32_t address); -void Flash_WriteEnable(void); +uint32_t Flash_GetSpiBaudrate(void); +void Flash_SetSpiBaudrate(uint32_t baudrate); +bool Flash_WriteEnable(void); bool Flash_WipeMemoryPage(uint8_t page); bool Flash_WipeMemory(void); bool Flash_Erase4k(uint8_t block, uint8_t sector); //bool Flash_Erase32k(uint32_t address); bool Flash_Erase64k(uint8_t block); +// defs see: https://chromium.googlesource.com/chromiumos/third_party/flashrom/+/798d2adc9527f724bc5096a646cf99efdbb6b59e/flashchips.h typedef struct { uint8_t manufacturer_id; uint8_t device_id; @@ -149,4 +127,4 @@ bool FlashDetect(void); #endif // #ifndef AS_BOOTROM -#endif +#endif // FLASHMEM_H_ diff --git a/common_arm/flash_data/flashmem_core.c b/common_arm/flash_data/flashmem_core.c new file mode 100644 index 000000000..3a59c6e80 --- /dev/null +++ b/common_arm/flash_data/flashmem_core.c @@ -0,0 +1,286 @@ +#include "flashmem.h" +#include "pmflash.h" +#include "string.h" +#include "ticks_apis.h" + +#ifndef AS_BOOTROM +#include "dbprint.h" +#endif // AS_BOOTROM + +// default is 0, first set when FlashInit() call. +static uint32_t flashmem_spibaudrate = 0; + +#ifndef AS_BOOTROM + +// flash ids, first set when FlashInit() call. +static spi_flash_t spi_flash_data = {0}; +// The capacity information calculated after the flash information is detected. +// This variable is referenced in many places, so it cannot be modified with static. +uint8_t spi_flash_pages64k = 4; + +// Get spi baudrate +uint32_t Flash_GetSpiBaudrate(void) { + return flashmem_spibaudrate; +} + +// Set spi baudrate, not updated immediately. +// The new baud rate will take effect the next time the FlashSetup function is executed. +// And depending on the platform, the baud rate that is finally set may not be your expected value. +// Maybe some platforms can only communicate at certain fixed baud rates. +void Flash_SetSpiBaudrate(uint32_t baudrate) { + flashmem_spibaudrate = baudrate; + Dbprintf("Spi Baudrate : %dMHz", flashmem_spibaudrate / 1000000); +} + +// WARNING -- if callers are using a file system (such as SPIFFS), +// they should inform the file system of this change +// e.g., rdv40_spiffs_check() +bool Flash_WipeMemoryPage(uint8_t page) { + if (!FlashInit()) { + if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_WriteData init fail"); + return false; + } + + // Each block is 64Kb. One block erase takes 1s ( 1000ms ) + Flash_WriteEnable(); + Flash_Erase64k(page); + Flash_CheckBusy(BUSY_TIMEOUT); + + FlashStop(); + + return true; +} + +// Wipes flash memory completely, fills with 0xFF +bool Flash_WipeMemory(void) { + if (!FlashInit()) { + if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_WriteData init fail"); + return false; + } + + // Each block is 64Kb. Four blocks + // one block erase takes 1s ( 1000ms ) + for (uint8_t i = 0; i < spi_flash_pages64k; i++) { + Flash_WriteEnable(); + Flash_Erase64k(i); + Flash_CheckBusy(BUSY_TIMEOUT); + } + + FlashStop(); + return true; +} + +// ReadData with spi initialization +uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len) { + + if (!FlashInit()) return 0; + + // check busy only + if (Flash_CheckBusy(BUSY_TIMEOUT)) return 0; + + // function reused, length check inside. + len = Flash_ReadDataCont(address, out, len); + + FlashStop(); + return len; +} + +// Write data can only program one page. A page has 256 bytes. +// if len > 256, it might wrap around and overwrite pos 0. +uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len) { + + if (!FlashInit()) { + if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_WriteData init fail"); + return 0; + } + + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); + + // function reused, len and addr check inside. + len = Flash_WriteDataCont(address, in, len); + + FlashStop(); + return len; +} + +// assumes valid start 256 based 00 address +// Start writing flash from the specified location. +// Write FLASH_MEM_BLOCK_SIZE bytes at most each time. If the writing is nearly complete, write it as bytes_remaining bytes. +uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len) { + + bool isok; + uint16_t res, bytes_sent = 0, bytes_remaining = len; + uint8_t buf[FLASH_MEM_BLOCK_SIZE]; + while (bytes_remaining > 0) { + + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); + + uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); + + memcpy(buf, in + bytes_sent, bytes_in_packet); + + res = Flash_WriteDataCont(address + bytes_sent, buf, bytes_in_packet); + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + + isok = (res == bytes_in_packet); + + if (!isok) + goto out; + } + + out: + FlashStop(); + return len; +} + +void Flashmem_print_status(void) { + DbpString(_CYAN_("Flash memory")); + Dbprintf(" Baudrate................ " _GREEN_("%d MHz"), flashmem_spibaudrate / 1000000); + + if (FlashInit() == false) { + DbpString(" Init.................... " _RED_("failed")); + return; + } + DbpString(" Init.................... " _GREEN_("ok")); + + if (spi_flash_data.device_id > 0) { + Dbprintf(" Mfr ID / Dev ID......... " _YELLOW_("%02X / %02X"), + spi_flash_data.manufacturer_id, + spi_flash_data.device_id + ); + } + + if (spi_flash_data.jedec_id > 0) { + Dbprintf(" JEDEC Mfr ID / Dev ID... " _YELLOW_("%02X / %04X"), + spi_flash_data.manufacturer_id, + spi_flash_data.jedec_id + ); + } + + Dbprintf(" Memory size............. " _YELLOW_("%d Kb") " ( %d pages * 64k )", spi_flash_pages64k * 64, spi_flash_pages64k); + + uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + Flash_UniqueID(uid); + Dbprintf(" Unique ID (be).......... " _YELLOW_("0x%02X%02X%02X%02X%02X%02X%02X%02X"), + uid[0], uid[1], uid[2], uid[3], + uid[4], uid[5], uid[6], uid[7] + ); + if (g_dbglevel > DBG_DEBUG) { + Dbprintf(" Unique ID (le).......... " _YELLOW_("0x%02X%02X%02X%02X%02X%02X%02X%02X"), + uid[7], uid[6], uid[5], uid[4], + uid[3], uid[2], uid[1], uid[0] + ); + } + FlashStop(); +} + +spi_flash_t *flash_get_info(void) { + return &spi_flash_data; +} + +bool FlashDetect(void) { + + flash_device_type_t flash_data = {0}; + bool ret = false; + // read using 0x9F (JEDEC) + if (Flash_ReadID(&flash_data, true)) { + spi_flash_data.manufacturer_id = flash_data.manufacturer_id; + spi_flash_data.jedec_id = (flash_data.device_id << 8) + flash_data.device_id2; + ret = true; + } else { + if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_ReadID failed reading JEDEC (0x9F)"); + } + + // read using 0x90 (Manufacturer / Device ID) + if (Flash_ReadID(&flash_data, false)) { + if (spi_flash_data.manufacturer_id == 0) { + spi_flash_data.manufacturer_id = flash_data.manufacturer_id; + } + spi_flash_data.device_id = flash_data.device_id; + ret = true; + } else { + if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_ReadID failed reading Mfr/Dev (0x90)"); + } + + // Check JEDEC data is valid, compare the reported device types and then calculate the number of pages + // It is covering the most (known) cases of devices but probably there are vendors with different data + // They will be handled when there is such cases + if (ret) { + if (spi_flash_data.jedec_id > 0 && spi_flash_data.jedec_id < 0xFFFF) { + if (((spi_flash_data.device_id + 1) & 0x0F) == (spi_flash_data.jedec_id & 0x000F)) { + spi_flash_pages64k = 1 << (spi_flash_data.jedec_id & 0x000F); + } + } + } + + spi_flash_data.pages64k = spi_flash_pages64k; + return ret; +} + +#endif // #ifndef AS_BOOTROM + +// initialize +bool FlashInit(void) { + // set default baud rate from platform specific + if (!flashmem_spibaudrate) { // only set if current value == 0 + flashmem_spibaudrate = Flash_DefaultBaudrate(); + } + + // Prioritize call the StartTicks, as the subsequent initialization process may rely on the counter + // to determine if there is a communication timeout. + StartTicks(); + + // If it is a QSPI communication interface, an attempt will be made to enable 4-wire communication at this stage. + // If the enable fails, it indicates that the chip does not support QSPI or has poor soldering. + // Tip: Some platform related steps only need to be executed once during initialization, which will be done in this function. + if (!FlashSetup(flashmem_spibaudrate)) { + StopTicks(); + return false; + } + + if (Flash_CheckBusy(BUSY_TIMEOUT)) { + StopTicks(); + return false; + } + +#ifndef AS_BOOTROM + if (spi_flash_data.manufacturer_id == 0) { + if (FlashDetect() == false) { + return false; + } + } +#endif // #ifndef AS_BOOTROM + + return true; +} + +// check flash write/erase working. +bool Flash_CheckBusy(uint32_t timeout) { + WaitUS(WINBOND_WRITE_DELAY); + StartCountUS(); + uint32_t _time = GetCountUS(); + uint8_t status; + + do { + // Read status register failed! + if (!Flash_ReadStat1(&status)) { + // The chip may not be working properly, so it is meaningless to determine whether it is busy. + // We will return false first. If we consider returning true in the future, please modify it. + return false; + } + // Flash is busy for wipe/write + if (!(status & BUSY)) { + return false; + } + } while ((GetCountUS() - _time) < timeout); + + if (timeout <= (GetCountUS() - _time)) { + return true; + } + + return false; +} diff --git a/common_arm/flash_data/flashmem_hw_at32.c b/common_arm/flash_data/flashmem_hw_at32.c new file mode 100644 index 000000000..9f4ac186b --- /dev/null +++ b/common_arm/flash_data/flashmem_hw_at32.c @@ -0,0 +1,491 @@ +#include +#include "ticks_apis.h" +#include "gpio_hw_at32.h" +#include "flashmem.h" +#include "flashmem_hw_at32.h" + +#ifndef AS_BOOTROM +#include "dbprint.h" +#endif // AS_BOOTROM + +static qspi_cmd_type w25q_cmd_config; + +// Initialization of gpio related to qspi +static void qspi_gpio_config(void) { + gpio_init_type gpio_init_struct; + + /* enable the gpio clock */ + AT32_GPIO_PERIPH_CLKS_ENABLE(AT32_GPIO_PERIPH_QSPI_FLASH_CLK); + + /* set default parameter */ + gpio_default_para_init(&gpio_init_struct); + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + + /* configure the io0 gpio */ + gpio_init_struct.gpio_pins = AT32_GPIO_QSPI_FLASH_IO0_PIN; + gpio_init(AT32_GPIO_QSPI_FLASH_IO0, &gpio_init_struct); + gpio_pin_mux_config(AT32_GPIO_QSPI_FLASH_IO0, AT32_GPIO_QSPI_FLASH_IO0_SOURCE, AT32_GPIO_QSPI_FLASH_IO0_MUX); + + /* configure the io1 gpio */ + gpio_init_struct.gpio_pins = AT32_GPIO_QSPI_FLASH_IO1_PIN; + gpio_init(AT32_GPIO_QSPI_FLASH_IO1, &gpio_init_struct); + gpio_pin_mux_config(AT32_GPIO_QSPI_FLASH_IO1, AT32_GPIO_QSPI_FLASH_IO1_SOURCE, AT32_GPIO_QSPI_FLASH_IO1_MUX); + + /* configure the io2 gpio */ + gpio_init_struct.gpio_pins = AT32_GPIO_QSPI_FLASH_IO2_PIN; + gpio_init(AT32_GPIO_QSPI_FLASH_IO2, &gpio_init_struct); + gpio_pin_mux_config(AT32_GPIO_QSPI_FLASH_IO2, AT32_GPIO_QSPI_FLASH_IO2_SOURCE, AT32_GPIO_QSPI_FLASH_IO2_MUX); + + /* configure the io3 gpio */ + gpio_init_struct.gpio_pins = AT32_GPIO_QSPI_FLASH_IO3_PIN; + gpio_init(AT32_GPIO_QSPI_FLASH_IO3, &gpio_init_struct); + gpio_pin_mux_config(AT32_GPIO_QSPI_FLASH_IO3, AT32_GPIO_QSPI_FLASH_IO3_SOURCE, AT32_GPIO_QSPI_FLASH_IO3_MUX); + + /* configure the sck gpio */ + gpio_init_struct.gpio_pins = AT32_GPIO_QSPI_FLASH_SCK_PIN; + gpio_init(AT32_GPIO_QSPI_FLASH_SCK, &gpio_init_struct); + gpio_pin_mux_config(AT32_GPIO_QSPI_FLASH_SCK, AT32_GPIO_QSPI_FLASH_SCK_SOURCE, AT32_GPIO_QSPI_FLASH_SCK_MUX); + + /* configure the cs gpio */ + gpio_init_struct.gpio_pins = AT32_GPIO_QSPI_FLASH_CS_PIN; + gpio_init(AT32_GPIO_QSPI_FLASH_CS, &gpio_init_struct); + gpio_pin_mux_config(AT32_GPIO_QSPI_FLASH_CS, AT32_GPIO_QSPI_FLASH_CS_SOURCE, AT32_GPIO_QSPI_FLASH_CS_MUX); +} + +// Wait for flag setting within timeout, return false if timeout occurs. +static bool wait_flag_set(uint32_t flag, uint32_t timeoutMS) { + uint32_t start_tick = GET_TICKS; + while (qspi_flag_get(AT32_QSPI_FLASH, flag) == RESET) { + if (GetTicksDelta(start_tick) >= (timeoutMS * 1000 * 1.5)) { + // 100ms + return false; // timeout + } + } + return true; +} + +// Wait command completed +static bool wait_cmd_completed(void) { + if (wait_flag_set(QSPI_CMDSTS_FLAG, 100)) { + qspi_flag_clear(AT32_QSPI_FLASH, QSPI_CMDSTS_FLAG); + return true; + } + return false; +} + +// Read out data from qspi pio, no dma. +static bool read_wait_rx_done(uint8_t *out, uint32_t length) { + // wait rx ready for read out. + if (!wait_flag_set(QSPI_RXFIFORDY_FLAG, 200)) return false; + for (uint32_t i = 0; i < length; ++i) out[i] = qspi_byte_read(AT32_QSPI_FLASH); + return wait_cmd_completed(); +} + +// Get QSPI frequency division value +static qspi_clk_div_type from_baudrate_to_clk_div(uint32_t baudrate) { + uint32_t sck_candidate_value = 0; + uint8_t i; + // The clock of the QSPI of the AT32 is from the AHB clock, see datasheet: system architecture & crm + // so we need get current ahb clk speed, to calc div value. + crm_clocks_freq_type clk_freq_info; + crm_clocks_freq_get(&clk_freq_info); + // The ahb clock may be modified, so dynamic calculation is required! + uint32_t sck_lut[8]; // map div value to clk value. + sck_lut[QSPI_CLK_DIV_2] = clk_freq_info.ahb_freq / 2; // see datasheet 28.4.5 + sck_lut[QSPI_CLK_DIV_4] = clk_freq_info.ahb_freq / 4; + sck_lut[QSPI_CLK_DIV_6] = clk_freq_info.ahb_freq / 6; + sck_lut[QSPI_CLK_DIV_8] = clk_freq_info.ahb_freq / 8; + sck_lut[QSPI_CLK_DIV_3] = clk_freq_info.ahb_freq / 3; + sck_lut[QSPI_CLK_DIV_5] = clk_freq_info.ahb_freq / 5; + sck_lut[QSPI_CLK_DIV_10] = clk_freq_info.ahb_freq / 10; + sck_lut[QSPI_CLK_DIV_12] = clk_freq_info.ahb_freq / 12; + // map search, step1, get a maximum value from sck_lut + for (i = 0; i < 8; ++i) { + // Take the maximum value downward, that is, if the incoming value is 1000000, + // take the maximum clk speed value in the mapping table that is smaller than the incoming value + if (baudrate <= sck_lut[i]) { + if (sck_lut[i] > sck_candidate_value) { + sck_candidate_value = sck_lut[i]; + } + } + } + if (sck_candidate_value == 0) { + return QSPI_CLK_DIV_6; // return a default div value if no candidate clk found. + } + // map search, step2, from clk value to div value + for (i = 0; i < 8; ++i) { + if (sck_candidate_value == sck_lut[i]) { + return i; // 'i' is div value + } + } + return QSPI_CLK_DIV_6; // never come here... +} + +// Default baud rate for spi of current platform. +uint32_t Flash_DefaultBaudrate(void) { + return 24000000; // TODO DXL 测试时,功能优先,速度先降下去。 +} + +// When returning false, it indicates that the communication with flash has timed out. +// Under normal circumstances, the return value should be within the range of U8. +static bool Flash_ReadStatReg(uint8_t reg, uint8_t *status) { + // config update for read status register + w25q_cmd_config.pe_mode_enable = FALSE; + w25q_cmd_config.pe_mode_operate_code = 0; + w25q_cmd_config.instruction_code = reg; + w25q_cmd_config.instruction_length = QSPI_CMD_INSLEN_1_BYTE; + w25q_cmd_config.address_code = 0; + w25q_cmd_config.address_length = QSPI_CMD_ADRLEN_0_BYTE; // no address + w25q_cmd_config.data_counter = 0; + w25q_cmd_config.second_dummy_cycle_num = 0; + w25q_cmd_config.operation_mode = QSPI_OPERATE_MODE_111; + w25q_cmd_config.read_status_config = QSPI_RSTSC_SW_ONCE; // self to read. + w25q_cmd_config.read_status_enable = TRUE; + w25q_cmd_config.write_data_enable = FALSE; + qspi_cmd_operation_kick(AT32_QSPI_FLASH, &w25q_cmd_config); + + if (!wait_cmd_completed()) return false; + + *status = AT32_QSPI_FLASH->rsts_bit.spists; // see rm doc: 28.4.10 + return true; +} + +// Read state register 1 +bool Flash_ReadStat1(uint8_t *status) { + return Flash_ReadStatReg(READSTAT1, status); +} + +// Read state register 1 +bool Flash_ReadStat2(uint8_t *status) { + return Flash_ReadStatReg(READSTAT2, status); +} + +#ifndef AS_BOOTROM + +// Write data and wait finish, no dma. +static bool write_wait_tx_done(uint8_t *in, uint32_t length) { + // send data via qspi + for (uint32_t i = 0; i < length; ++i) { + if (!wait_flag_set(QSPI_TXFIFORDY_FLAG, 100)) return false; + qspi_byte_write(AT32_QSPI_FLASH, in[i]); + } + return wait_cmd_completed(); +} + +// Write status register by CMD(0x01 or 0x31) +// If CMD == 0x01, will write status1 & status2 register +// If CMD == 0x31, will write status2 register only +static bool Flash_WriteStatReg(uint8_t reg, uint8_t *in, uint8_t length) { + w25q_cmd_config.pe_mode_enable = FALSE; + w25q_cmd_config.pe_mode_operate_code = 0; + w25q_cmd_config.instruction_code = reg; + w25q_cmd_config.instruction_length = QSPI_CMD_INSLEN_1_BYTE; + w25q_cmd_config.address_code = 0; + w25q_cmd_config.address_length = QSPI_CMD_ADRLEN_0_BYTE; + w25q_cmd_config.data_counter = length; + w25q_cmd_config.second_dummy_cycle_num = 0; + w25q_cmd_config.operation_mode = QSPI_OPERATE_MODE_111; + w25q_cmd_config.read_status_config = QSPI_RSTSC_HW_AUTO; + w25q_cmd_config.read_status_enable = FALSE; + w25q_cmd_config.write_data_enable = TRUE; + qspi_cmd_operation_kick(AT32_QSPI_FLASH, &w25q_cmd_config); + + return write_wait_tx_done(in, length); +} + +// Flash quad line communication enable +// Some chips already enable QE bit default. such as: W25Q64FVSSIQ, the Q suffix is QE bit enable default. +static bool Flash_QE_Enable(void) { + uint8_t status[2]; + if (!Flash_ReadStat1(&status[0])) return false; + if (!Flash_ReadStat2(&status[1])) return false; + // Check if the QE bit has been enabled. On some winbond chips with Q as the suffix, this bit defaults to 1. + if ((status[1] & 0x02) != 0) return true; + // Set 'Quad Enable (QE)'(S9 bit) to 1. + status[1] |= 1 << 1; + // QE bit is a non-volatile Status Register bits, a standard Write Enable (06h) instruction must previously have + // been executed for the device to accept the Write Status Register instruction (Status Register bit WEL must equal 1). + if (!Flash_WriteEnable()) return false; + // Some new chips support 31H instruction to set the status register 2, + // but we need to use the 01H standard instruction to set the status register 2 for compatibility. + if (!Flash_WriteStatReg(WRITESTAT, status, 2)) return false; + // The BUSY bit is a 1 during the Write Status Register cycle + // and a 0 when the cycle is finished and ready to accept other instructions again. After the Write Status + // Register cycle has finished, the Write Enable Latch (WEL) bit in the Status Register will be cleared to 0. + return !Flash_CheckBusy(BUSY_TIMEOUT); // Waiting for write done. +} + +#endif + +// Flash spi & gpio setup +bool FlashSetup(uint32_t baudrate) { + qspi_gpio_config(); + // enable the qspi clock + crm_periph_clock_enable(AT32_CRM_QSPI_FLASH_CLK, TRUE); + // switch to cmd port + qspi_xip_enable(AT32_QSPI_FLASH, FALSE); + // set clk + qspi_clk_division_set(AT32_QSPI_FLASH, from_baudrate_to_clk_div(baudrate)); + // set sck idle mode 0 + qspi_sck_mode_set(AT32_QSPI_FLASH, QSPI_SCK_MODE_0); + // set wip in bit 0 + qspi_busy_config(AT32_QSPI_FLASH, QSPI_BUSY_OFFSET_0); + // disable encrypt + qspi_encryption_enable(AT32_QSPI_FLASH, FALSE); + // enable auto ispc + qspi_auto_ispc_enable(AT32_QSPI_FLASH); + +#ifndef AS_BOOTROM + return Flash_QE_Enable(); +#else + return true; +#endif +} + +// Flash spi deinit +void FlashStop(void) { + // Do not turn off the clock of GPIO. If you really want to turn off the clock, + // you must ensure that GPIO is not currently used in other codes + // crm_periph_clock_enable(CRM_GPIO?_PERIPH_CLOCK, TRUE); + // crm_periph_clock_enable(CRM_GPIO?_PERIPH_CLOCK, TRUE); + // crm_periph_clock_enable(CRM_GPIO?_PERIPH_CLOCK, TRUE); + + // disable qspi + crm_periph_clock_enable(AT32_CRM_QSPI_FLASH_CLK, FALSE); + qspi_interrupt_enable(AT32_QSPI_FLASH, FALSE); +} + +// Read unique id for chip. +bool Flash_UniqueID(uint8_t *uid) { + if (Flash_CheckBusy(BUSY_TIMEOUT)) return false; + + w25q_cmd_config.pe_mode_enable = FALSE; + w25q_cmd_config.pe_mode_operate_code = 0; + w25q_cmd_config.instruction_code = UNIQUE_ID; + w25q_cmd_config.instruction_length = QSPI_CMD_INSLEN_1_BYTE; + w25q_cmd_config.address_code = 0; + // see rm doc 28.4.2, if address_length = 0, second_dummy_cycle_num will no working. + // so we need use addr to create dummy clk + w25q_cmd_config.address_length = 4; + w25q_cmd_config.data_counter = 8; // 64bit unique id + w25q_cmd_config.second_dummy_cycle_num = 0; // dummy clk + w25q_cmd_config.operation_mode = QSPI_OPERATE_MODE_111; + w25q_cmd_config.read_status_config = QSPI_RSTSC_HW_AUTO; + w25q_cmd_config.read_status_enable = FALSE; + w25q_cmd_config.write_data_enable = FALSE; + qspi_cmd_operation_kick(AT32_QSPI_FLASH, &w25q_cmd_config); + + read_wait_rx_done(uid, 8); + + return true; +} + +#ifndef AS_BOOTROM + +// Read JEDEC id +static void read_jedecid(uint8_t *jedecid) { + w25q_cmd_config.pe_mode_enable = FALSE; + w25q_cmd_config.pe_mode_operate_code = 0; + w25q_cmd_config.instruction_code = JEDECID; + w25q_cmd_config.instruction_length = QSPI_CMD_INSLEN_1_BYTE; + w25q_cmd_config.address_code = 0; + w25q_cmd_config.address_length = 0; + w25q_cmd_config.data_counter = 3; // 24bit JEDECID info + w25q_cmd_config.second_dummy_cycle_num = 0; // no dummy clk + w25q_cmd_config.operation_mode = QSPI_OPERATE_MODE_111; + w25q_cmd_config.read_status_config = QSPI_RSTSC_HW_AUTO; + w25q_cmd_config.read_status_enable = FALSE; + w25q_cmd_config.write_data_enable = FALSE; + qspi_cmd_operation_kick(AT32_QSPI_FLASH, &w25q_cmd_config); + + read_wait_rx_done(jedecid, 3); +} + +// Read Manufacturer / Device ID +// the difference between this function and the read_jedecid function is that the capacity information is missing +// so only 2byte device_id readout. +static void read_deviceid(uint8_t *device_id) { + w25q_cmd_config.pe_mode_enable = FALSE; + w25q_cmd_config.pe_mode_operate_code = 0; + w25q_cmd_config.instruction_code = ID; + w25q_cmd_config.instruction_length = QSPI_CMD_INSLEN_1_BYTE; + w25q_cmd_config.address_code = 0; + w25q_cmd_config.address_length = 3; // for 3 byte dummy clk + w25q_cmd_config.data_counter = 2; // 16bit device id + w25q_cmd_config.second_dummy_cycle_num = 0; // no dummy clk + w25q_cmd_config.operation_mode = QSPI_OPERATE_MODE_111; + w25q_cmd_config.read_status_config = QSPI_RSTSC_HW_AUTO; + w25q_cmd_config.read_status_enable = FALSE; + w25q_cmd_config.write_data_enable = FALSE; + qspi_cmd_operation_kick(AT32_QSPI_FLASH, &w25q_cmd_config); + + read_wait_rx_done(device_id, 2); +} + +// Read ID out +bool Flash_ReadID(flash_device_type_t *result, bool read_jedec) { + if (Flash_CheckBusy(BUSY_TIMEOUT)) return false; + + if (read_jedec) { + uint8_t juid[3]; + read_jedecid(juid); + + result->manufacturer_id = juid[0]; + result->device_id = juid[1]; + result->device_id2 = juid[2]; + } else { + uint8_t duid[2]; + read_deviceid(duid); + + result->manufacturer_id = duid[0]; + result->device_id = duid[1]; + } + + return true; +} + +uint16_t Flash_ReadDataCont(uint32_t address, uint8_t *out, uint16_t len) { + // length should never be zero + if (!len) return 0; + + // cmd + w25q_cmd_config.pe_mode_enable = FALSE; + w25q_cmd_config.pe_mode_operate_code = 0; + w25q_cmd_config.instruction_code = FASTREAD_QO; + w25q_cmd_config.instruction_length = QSPI_CMD_INSLEN_1_BYTE; + // address + w25q_cmd_config.address_code = address; + w25q_cmd_config.address_length = QSPI_CMD_ADRLEN_3_BYTE; // 24bit address + + // dummy clk for qspi fast read output + w25q_cmd_config.second_dummy_cycle_num = 8; + + // more... + w25q_cmd_config.data_counter = len; + w25q_cmd_config.operation_mode = QSPI_OPERATE_MODE_114; + w25q_cmd_config.read_status_config = QSPI_RSTSC_SW_ONCE; + w25q_cmd_config.read_status_enable = FALSE; + w25q_cmd_config.write_data_enable = FALSE; + + qspi_cmd_operation_kick(AT32_QSPI_FLASH, &w25q_cmd_config); + + // readout from flash + read_wait_rx_done(out, len); + return len; +} + +bool Flash_WriteEnable(void) { + w25q_cmd_config.pe_mode_enable = FALSE; + w25q_cmd_config.pe_mode_operate_code = 0; + w25q_cmd_config.instruction_code = WRITEENABLE; + w25q_cmd_config.instruction_length = QSPI_CMD_INSLEN_1_BYTE; + w25q_cmd_config.address_code = 0; + w25q_cmd_config.address_length = 0; + w25q_cmd_config.data_counter = 0; + w25q_cmd_config.second_dummy_cycle_num = 0; + w25q_cmd_config.operation_mode = QSPI_OPERATE_MODE_111; + w25q_cmd_config.read_status_config = QSPI_RSTSC_HW_AUTO; + w25q_cmd_config.read_status_enable = FALSE; + w25q_cmd_config.write_data_enable = TRUE; + + qspi_cmd_operation_kick(AT32_QSPI_FLASH, &w25q_cmd_config); + + if (!wait_cmd_completed()) return false; + + if (g_dbglevel > 3) Dbprintf("Flash Write enabled"); + + return true; +} + +uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len) { + if (!len) + return 0; + + if (((address & 0xFF) + len) > 256) { + Dbprintf("Flash_WriteDataCont 256 fail [ 0x%02x ] [ %u ]", (address & 0xFF) + len, len); + return 0; + } + + if (((address >> 16) & 0xFF) > spi_flash_pages64k) { + Dbprintf("Flash_WriteDataCont, block out-of-range %02x > %02x", (address >> 16) & 0xFF, spi_flash_pages64k); + return 0; + } + + w25q_cmd_config.pe_mode_enable = FALSE; + w25q_cmd_config.pe_mode_operate_code = 0; + w25q_cmd_config.instruction_code = PAGEPROG; + w25q_cmd_config.instruction_length = QSPI_CMD_INSLEN_1_BYTE; + + w25q_cmd_config.address_code = address; + w25q_cmd_config.address_length = QSPI_CMD_ADRLEN_3_BYTE; + + w25q_cmd_config.data_counter = len; + w25q_cmd_config.second_dummy_cycle_num = 0; + + /* + * WHY using 111 single line mode? + * + * The Quad Page Program can improve performance for PROM Programmer and applications that have slow clock speeds <5MHz. + * Systems with faster clock speed will not realize much benefit for the Quad Page Program instruction since + * the inherent page program time is much greater than the time it take to clock-in the data. + */ + w25q_cmd_config.operation_mode = QSPI_OPERATE_MODE_111; + + w25q_cmd_config.read_status_config = QSPI_RSTSC_HW_AUTO; + w25q_cmd_config.read_status_enable = FALSE; + w25q_cmd_config.write_data_enable = TRUE; + + qspi_cmd_operation_kick(AT32_QSPI_FLASH, &w25q_cmd_config); + + write_wait_tx_done(in, len); + return len; +} + +bool Flash_Erase4k(uint8_t block, uint8_t sector) { + if (block > spi_flash_pages64k || sector > MAX_SECTORS) return false; + + w25q_cmd_config.pe_mode_enable = FALSE; + w25q_cmd_config.pe_mode_operate_code = 0; + w25q_cmd_config.instruction_code = SECTORERASE; + w25q_cmd_config.instruction_length = QSPI_CMD_INSLEN_1_BYTE; + + w25q_cmd_config.address_code = block << 16 | (sector << 4) << 8; + w25q_cmd_config.address_length = QSPI_CMD_ADRLEN_3_BYTE; + + w25q_cmd_config.data_counter = 0; + w25q_cmd_config.second_dummy_cycle_num = 0; + w25q_cmd_config.operation_mode = QSPI_OPERATE_MODE_111; + w25q_cmd_config.read_status_config = QSPI_RSTSC_HW_AUTO; + w25q_cmd_config.read_status_enable = FALSE; + w25q_cmd_config.write_data_enable = TRUE; + qspi_cmd_operation_kick(AT32_QSPI_FLASH, &w25q_cmd_config); + + return wait_cmd_completed(); +} + +bool Flash_Erase64k(uint8_t block) { + if (block > spi_flash_pages64k) return false; + + w25q_cmd_config.pe_mode_enable = FALSE; + w25q_cmd_config.pe_mode_operate_code = 0; + w25q_cmd_config.instruction_code = BLOCK64ERASE; + w25q_cmd_config.instruction_length = QSPI_CMD_INSLEN_1_BYTE; + + w25q_cmd_config.address_code = block; + w25q_cmd_config.address_length = QSPI_CMD_ADRLEN_3_BYTE; + + w25q_cmd_config.data_counter = 0; + w25q_cmd_config.second_dummy_cycle_num = 0; + w25q_cmd_config.operation_mode = QSPI_OPERATE_MODE_111; + w25q_cmd_config.read_status_config = QSPI_RSTSC_HW_AUTO; + w25q_cmd_config.read_status_enable = FALSE; + w25q_cmd_config.write_data_enable = TRUE; + qspi_cmd_operation_kick(AT32_QSPI_FLASH, &w25q_cmd_config); + + return wait_cmd_completed(); +} + +#endif // #ifndef AS_BOOTROM diff --git a/common_arm/flash_data/flashmem_hw_at32.h b/common_arm/flash_data/flashmem_hw_at32.h new file mode 100644 index 000000000..43b6b533f --- /dev/null +++ b/common_arm/flash_data/flashmem_hw_at32.h @@ -0,0 +1,14 @@ +// +// Created by dxl on 2026/2/7. +// + +#ifndef FLASHMEM_HW_AT32_H +#define FLASHMEM_HW_AT32_H + +#include "at32f435_437_crm.h" +#include "at32f435_437_qspi.h" + +#define AT32_CRM_QSPI_FLASH_CLK CRM_QSPI1_PERIPH_CLOCK +#define AT32_QSPI_FLASH QSPI1 + +#endif // FLASHMEM_HW_AT32_H diff --git a/common_arm/flashmem.c b/common_arm/flash_data/flashmem_hw_at91.c similarity index 52% rename from common_arm/flashmem.c rename to common_arm/flash_data/flashmem_hw_at91.c index 37ba1481d..6d5cfa7cb 100644 --- a/common_arm/flashmem.c +++ b/common_arm/flash_data/flashmem_hw_at91.c @@ -15,61 +15,61 @@ // // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- -#include "flashmem.h" -#include "pmflash.h" +#include "pmflash.h" +#include "flashmem_hw_at91.h" #include "proxmark3_arm.h" -#include "ticks.h" +#include "ticks_apis.h" #ifndef AS_BOOTROM #include "dbprint.h" #endif // AS_BOOTROM -#include "string.h" -#include "usb_cdc.h" +// send one byte over SPI +static uint16_t FlashSendByte(uint32_t data) { -/* here: use NCPS2 @ PA10: */ -#define SPI_CSR_NUM 2 -#define SPI_PCS(npcs) ((~(1 << (npcs)) & 0xF) << 16) -/// Calculates the value of the CSR SCBR field given the baudrate and MCK. -#define SPI_SCBR(baudrate, masterClock) ((uint32_t) ((masterClock) / (baudrate)) << 8) -/// Calculates the value of the CSR DLYBS field given the desired delay (in ns) -#define SPI_DLYBS(delay, masterClock) ((uint32_t) ((((masterClock) / 1000000) * (delay)) / 1000) << 16) -/// Calculates the value of the CSR DLYBCT field given the desired delay (in ns) -#define SPI_DLYBCT(delay, masterClock) ((uint32_t) ((((masterClock) / 1000000) * (delay)) / 32000) << 24) + // wait until SPI is ready for transfer + //if you are checking for incoming data returned then the TXEMPTY flag is redundant + //while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_TXEMPTY) == 0) {}; -static uint32_t FLASHMEM_SPIBAUDRATE = FLASH_BAUD; -#define FASTFLASH (FLASHMEM_SPIBAUDRATE > FLASH_MINFAST) + // send the data + AT91C_BASE_SPI->SPI_TDR = data; + + //while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_TDRE) == 0){}; + + // wait receive transfer is complete + while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_RDRF) == 0) {}; + + // reading incoming data + return ((AT91C_BASE_SPI->SPI_RDR) & 0xFFFF); +} + +// send last byte over SPI +static uint16_t FlashSendLastByte(uint32_t data) { + return FlashSendByte(data | AT91C_SPI_LASTXFER); +} #ifndef AS_BOOTROM -uint8_t spi_flash_pages64k = 4; -static spi_flash_t spi_flash_data = {0}; - -spi_flash_t *flash_get_info(void) { - return &spi_flash_data; -} - - -void FlashmemSetSpiBaudrate(uint32_t baudrate) { - FLASHMEM_SPIBAUDRATE = baudrate; - Dbprintf("Spi Baudrate : %dMHz", FLASHMEM_SPIBAUDRATE / 1000000); +// send address after R/W cmd. +static void Flash_TransferAddress(uint32_t address) { + FlashSendByte((address >> 16) & 0xFF); + FlashSendByte((address >> 8) & 0xFF); + FlashSendByte((address >> 0) & 0xFF); } // read ID out bool Flash_ReadID(flash_device_type_t *result, bool read_jedec) { - if (Flash_CheckBusy(BUSY_TIMEOUT)) { - return false; - } + if (Flash_CheckBusy(BUSY_TIMEOUT)) return false; if (read_jedec) { // 0x9F JEDEC FlashSendByte(JEDECID); result->manufacturer_id = (FlashSendByte(0xFF) & 0xFF); - result->device_id = (FlashSendByte(0xFF) & 0xFF); - result->device_id2 = (FlashSendLastByte(0xFF) & 0xFF); + result->device_id = (FlashSendByte(0xFF) & 0xFF); + result->device_id2 = (FlashSendLastByte(0xFF) & 0xFF); } else { // 0x90 Manufacture ID / device ID FlashSendByte(ID); @@ -78,60 +78,22 @@ bool Flash_ReadID(flash_device_type_t *result, bool read_jedec) { FlashSendByte(0x00); result->manufacturer_id = (FlashSendByte(0xFF) & 0xFF); - result->device_id = (FlashSendLastByte(0xFF) & 0xFF); + result->device_id = (FlashSendLastByte(0xFF) & 0xFF); } return true; } -uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len) { - - if (FlashInit() == false) { - return 0; - } - - // length should never be zero - if ((len == 0) || Flash_CheckBusy(BUSY_TIMEOUT)) { - return 0; - } - - uint8_t cmd = (FASTFLASH) ? FASTREAD : READDATA; - - FlashSendByte(cmd); - Flash_TransferAdresse(address); - - if (FASTFLASH) { - FlashSendByte(DUMMYBYTE); - } - - uint16_t i = 0; - for (; i < (len - 1); i++) { - out[i] = (FlashSendByte(0xFF) & 0xFF); - } - - out[i] = (FlashSendLastByte(0xFF) & 0xFF); - FlashStop(); - return len; -} - -void Flash_TransferAdresse(uint32_t address) { - FlashSendByte((address >> 16) & 0xFF); - FlashSendByte((address >> 8) & 0xFF); - FlashSendByte((address >> 0) & 0xFF); -} - /* This ensures we can ReadData without having to cycle through initialization every time */ uint16_t Flash_ReadDataCont(uint32_t address, uint8_t *out, uint16_t len) { // length should never be zero - if (len == 0) { - return 0; - } + if (!len) return 0; uint8_t cmd = (FASTFLASH) ? FASTREAD : READDATA; FlashSendByte(cmd); - Flash_TransferAdresse(address); + Flash_TransferAddress(address); if (FASTFLASH) { FlashSendByte(DUMMYBYTE); @@ -141,57 +103,15 @@ uint16_t Flash_ReadDataCont(uint32_t address, uint8_t *out, uint16_t len) { for (; i < (len - 1); i++) { out[i] = (FlashSendByte(0xFF) & 0xFF); } - out[i] = (FlashSendLastByte(0xFF) & 0xFF); return len; } -//////////////////////////////////////// -// Write data can only program one page. A page has 256 bytes. -// if len > 256, it might wrap around and overwrite pos 0. -uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len) { - - // length should never be zero - if (len == 0) { - return 0; - } - - // Max 256 bytes write - if (((address & 0xFF) + len) > 256) { - Dbprintf("Flash_WriteData 256 fail [ 0x%02x ] [ %u ]", (address & 0xFF) + len, len); - return 0; - } - - if (FlashInit() == false) { - if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_WriteData init fail"); - return 0; - } - - // out-of-range - if (((address >> 16) & 0xFF) > spi_flash_pages64k) { - Dbprintf("Flash_WriteData, block out-of-range %02x > %02x", (address >> 16) & 0xFF, spi_flash_pages64k); - FlashStop(); - return 0; - } - - Flash_CheckBusy(BUSY_TIMEOUT); - - Flash_WriteEnable(); - - FlashSendByte(PAGEPROG); - FlashSendByte((address >> 16) & 0xFF); - FlashSendByte((address >> 8) & 0xFF); - FlashSendByte((address >> 0) & 0xFF); - - uint16_t i = 0; - for (; i < (len - 1); i++) { - FlashSendByte(in[i]); - } - - FlashSendLastByte(in[i]); - - FlashStop(); - return len; +// enable the flash write +bool Flash_WriteEnable(void) { + FlashSendLastByte(WRITEENABLE); + if (g_dbglevel > 3) Dbprintf("Flash Write enabled"); + return true; } // length should never be zero @@ -213,108 +133,21 @@ uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len) { } FlashSendByte(PAGEPROG); - FlashSendByte((address >> 16) & 0xFF); - FlashSendByte((address >> 8) & 0xFF); - FlashSendByte((address >> 0) & 0xFF); + Flash_TransferAddress(address); uint16_t i = 0; - for (; i < (len - 1); i++) { + for (; i < (len - 1); i++) FlashSendByte(in[i]); - } FlashSendLastByte(in[i]); return len; } -// assumes valid start 256 based 00 address -// -uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len) { - - bool isok; - uint16_t res, bytes_sent = 0, bytes_remaining = len; - uint8_t buf[FLASH_MEM_BLOCK_SIZE]; - while (bytes_remaining > 0) { - - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - - uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); - - memcpy(buf, in + bytes_sent, bytes_in_packet); - - res = Flash_WriteDataCont(address + bytes_sent, buf, bytes_in_packet); - - bytes_remaining -= bytes_in_packet; - bytes_sent += bytes_in_packet; - - isok = (res == bytes_in_packet); - - if (isok == false) { - goto out; - } - } - -out: - FlashStop(); - return len; -} - -// WARNING -- if callers are using a file system (such as SPIFFS), -// they should inform the file system of this change -// e.g., rdv40_spiffs_check() -bool Flash_WipeMemoryPage(uint8_t page) { - - if (FlashInit() == false) { - if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_WriteData init fail"); - return false; - } - - Flash_ReadStat1(); - - // Each block is 64Kb. One block erase takes 1s ( 1000ms ) - Flash_WriteEnable(); - Flash_Erase64k(page); - Flash_CheckBusy(BUSY_TIMEOUT); - - FlashStop(); - - return true; -} -// Wipes flash memory completely, fills with 0xFF -bool Flash_WipeMemory(void) { - - if (FlashInit() == false) { - if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_WriteData init fail"); - return false; - } - - Flash_ReadStat1(); - - // Each block is 64Kb. Four blocks - // one block erase takes 1s ( 1000ms ) - for (uint8_t i = 0; i < spi_flash_pages64k; i++) { - Flash_WriteEnable(); - Flash_Erase64k(i); - Flash_CheckBusy(BUSY_TIMEOUT); - } - - FlashStop(); - return true; -} - -// enable the flash write -void Flash_WriteEnable(void) { - FlashSendLastByte(WRITEENABLE); - if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash Write enabled"); -} - // erase 4K at one time // execution time: 0.8ms / 800us bool Flash_Erase4k(uint8_t block, uint8_t sector) { - if (block > spi_flash_pages64k || sector > MAX_SECTORS) { - return false; - } + if (block > spi_flash_pages64k || sector > MAX_SECTORS) return false; FlashSendByte(SECTORERASE); FlashSendByte(block); @@ -349,9 +182,7 @@ bool Flash_Erase32k(uint32_t address) { // 0x03 00 00 -- 0x 03 FF FF == block 3 bool Flash_Erase64k(uint8_t block) { - if (block > spi_flash_pages64k) { - return false; - } + if (block > spi_flash_pages64k) return false; FlashSendByte(BLOCK64ERASE); FlashSendByte(block); @@ -367,135 +198,14 @@ void Flash_EraseChip(void) { } */ -void Flashmem_print_status(void) { - DbpString(_CYAN_("Flash memory")); - Dbprintf(" Baudrate................ " _GREEN_("%d MHz"), FLASHMEM_SPIBAUDRATE / 1000000); - - if (FlashInit() == false) { - DbpString(" Init.................... " _RED_("failed")); - return; - } - DbpString(" Init.................... " _GREEN_("ok")); - - if (spi_flash_data.device_id > 0) { - Dbprintf(" Mfr ID / Dev ID......... " _YELLOW_("%02X / %02X"), - spi_flash_data.manufacturer_id, - spi_flash_data.device_id - ); - } - - if (spi_flash_data.jedec_id > 0) { - Dbprintf(" JEDEC Mfr ID / Dev ID... " _YELLOW_("%02X / %04X"), - spi_flash_data.manufacturer_id, - spi_flash_data.jedec_id - ); - } - - Dbprintf(" Memory size............. " _YELLOW_("%d Kb") " ( %d pages * 64k )", spi_flash_pages64k * 64, spi_flash_pages64k); - - uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - Flash_UniqueID(uid); - Dbprintf(" Unique ID (be).......... " _YELLOW_("0x%02X%02X%02X%02X%02X%02X%02X%02X"), - uid[0], uid[1], uid[2], uid[3], - uid[4], uid[5], uid[6], uid[7] - ); - if (g_dbglevel > DBG_DEBUG) { - Dbprintf(" Unique ID (le).......... " _YELLOW_("0x%02X%02X%02X%02X%02X%02X%02X%02X"), - uid[7], uid[6], uid[5], uid[4], - uid[3], uid[2], uid[1], uid[0] - ); - } - FlashStop(); -} - -bool FlashDetect(void) { - - flash_device_type_t flash_data = {0}; - bool ret = false; - // read using 0x9F (JEDEC) - if (Flash_ReadID(&flash_data, true)) { - spi_flash_data.manufacturer_id = flash_data.manufacturer_id; - spi_flash_data.jedec_id = (flash_data.device_id << 8) + flash_data.device_id2; - ret = true; - } else { - if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_ReadID failed reading JEDEC (0x9F)"); - } - - // read using 0x90 (Manufacturer / Device ID) - if (Flash_ReadID(&flash_data, false)) { - if (spi_flash_data.manufacturer_id == 0) { - spi_flash_data.manufacturer_id = flash_data.manufacturer_id; - } - spi_flash_data.device_id = flash_data.device_id; - ret = true; - } else { - if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_ReadID failed reading Mfr/Dev (0x90)"); - } - - // Check JEDEC data is valid, compare the reported device types and then calculate the number of pages - // It is covering the most (known) cases of devices but probably there are vendors with different data - // They will be handled when there is such cases - if (ret) { - if (spi_flash_data.jedec_id > 0 && spi_flash_data.jedec_id < 0xFFFF) { - if (((spi_flash_data.device_id + 1) & 0x0F) == (spi_flash_data.jedec_id & 0x000F)) { - spi_flash_pages64k = 1 << (spi_flash_data.jedec_id & 0x000F); - } - } - } - - spi_flash_data.pages64k = spi_flash_pages64k; - return ret; -} - #endif // #ifndef AS_BOOTROM - -// initialize -bool FlashInit(void) { - FlashSetup(FLASHMEM_SPIBAUDRATE); - - StartTicks(); - - if (Flash_CheckBusy(BUSY_TIMEOUT)) { - StopTicks(); - return false; - } - -#ifndef AS_BOOTROM - if (spi_flash_data.manufacturer_id == 0) { - if (FlashDetect() == false) { - return false; - } - } -#endif // #ifndef AS_BOOTROM - - return true; -} - -// read unique id for chip. -void Flash_UniqueID(uint8_t *uid) { - - if (Flash_CheckBusy(BUSY_TIMEOUT)) { - return; - } - - // reading unique serial number - FlashSendByte(UNIQUE_ID); - FlashSendByte(0xFF); - FlashSendByte(0xFF); - FlashSendByte(0xFF); - FlashSendByte(0xFF); - - uid[7] = (FlashSendByte(0xFF) & 0xFF); - uid[6] = (FlashSendByte(0xFF) & 0xFF); - uid[5] = (FlashSendByte(0xFF) & 0xFF); - uid[4] = (FlashSendByte(0xFF) & 0xFF); - uid[3] = (FlashSendByte(0xFF) & 0xFF); - uid[2] = (FlashSendByte(0xFF) & 0xFF); - uid[1] = (FlashSendByte(0xFF) & 0xFF); - uid[0] = (FlashSendLastByte(0xFF) & 0xFF); +// default baud rate for spi of current platform. +uint32_t Flash_DefaultBaudrate(void) { + return FLASH_BAUD; } +// flash spi deinit void FlashStop(void) { //Bof //* Reset all the Chip Select register @@ -514,13 +224,14 @@ void FlashStop(void) { AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIDIS; #ifndef AS_BOOTROM - if (g_dbglevel > DBG_DEBUG) Dbprintf("FlashStop"); + if (g_dbglevel > 3) Dbprintf("FlashStop"); #endif // AS_BOOTROM StopTicks(); } -void FlashSetup(uint32_t baudrate) { +// flash spi&gpio setup +bool FlashSetup(uint32_t baudrate) { //WDT_DISABLE AT91C_BASE_WDTC->WDTC_WDMR = AT91C_WDTC_WDDIS; @@ -618,52 +329,46 @@ void FlashSetup(uint32_t baudrate) { // read first, empty buffer if (AT91C_BASE_SPI->SPI_RDR == 0) {}; + + return true; } -bool Flash_CheckBusy(uint32_t timeout) { - WaitUS(WINBOND_WRITE_DELAY); - StartCountUS(); - uint32_t _time = GetCountUS(); +// read unique id for chip. +bool Flash_UniqueID(uint8_t *uid) { - do { - if (!(Flash_ReadStat1() & BUSY)) { - return false; - } - } while ((GetCountUS() - _time) < timeout); + if (Flash_CheckBusy(BUSY_TIMEOUT)) false; - if (timeout <= (GetCountUS() - _time)) { - return true; - } + // reading unique serial number + FlashSendByte(UNIQUE_ID); + FlashSendByte(0xFF); + FlashSendByte(0xFF); + FlashSendByte(0xFF); + FlashSendByte(0xFF); - return false; + uid[7] = (FlashSendByte(0xFF) & 0xFF); + uid[6] = (FlashSendByte(0xFF) & 0xFF); + uid[5] = (FlashSendByte(0xFF) & 0xFF); + uid[4] = (FlashSendByte(0xFF) & 0xFF); + uid[3] = (FlashSendByte(0xFF) & 0xFF); + uid[2] = (FlashSendByte(0xFF) & 0xFF); + uid[1] = (FlashSendByte(0xFF) & 0xFF); + uid[0] = (FlashSendLastByte(0xFF) & 0xFF); + + return true; } // read state register 1 -uint8_t Flash_ReadStat1(void) { +bool Flash_ReadStat1(uint8_t *status) { + if (!status) return false; FlashSendByte(READSTAT1); - return FlashSendLastByte(0xFF); + *status = FlashSendLastByte(0xFF); + return true; } -// send one byte over SPI -uint16_t FlashSendByte(uint32_t data) { - - // wait until SPI is ready for transfer - //if you are checking for incoming data returned then the TXEMPTY flag is redundant - //while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_TXEMPTY) == 0) {}; - - // send the data - AT91C_BASE_SPI->SPI_TDR = data; - - //while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_TDRE) == 0){}; - - // wait receive transfer is complete - while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_RDRF) == 0) {}; - - // reading incoming data - return ((AT91C_BASE_SPI->SPI_RDR) & 0xFFFF); -} - -// send last byte over SPI -uint16_t FlashSendLastByte(uint32_t data) { - return FlashSendByte(data | AT91C_SPI_LASTXFER); -} +// read state register 2 +bool Flash_ReadStat2(uint8_t *status) { + if (!status) return false; + FlashSendByte(READSTAT2); + *status = FlashSendLastByte(0xFF); + return true; +} \ No newline at end of file diff --git a/common_arm/flash_data/flashmem_hw_at91.h b/common_arm/flash_data/flashmem_hw_at91.h new file mode 100644 index 000000000..120a2dff9 --- /dev/null +++ b/common_arm/flash_data/flashmem_hw_at91.h @@ -0,0 +1,29 @@ +// +// Created by dxl on 2026/2/7. +// + +#ifndef FLASHMEM_HW_AT91_H +#define FLASHMEM_HW_AT91_H + +#include "flashmem.h" +#include "proxmark3_arm.h" + +/* here: use NCPS2 @ PA10: */ +#define SPI_CSR_NUM 2 +#define SPI_PCS(npcs) ((~(1 << (npcs)) & 0xF) << 16) +/// Calculates the value of the CSR SCBR field given the baudrate and MCK. +#define SPI_SCBR(baudrate, masterClock) ((uint32_t) ((masterClock) / (baudrate)) << 8) +/// Calculates the value of the CSR DLYBS field given the desired delay (in ns) +#define SPI_DLYBS(delay, masterClock) ((uint32_t) ((((masterClock) / 1000000) * (delay)) / 1000) << 16) +/// Calculates the value of the CSR DLYBCT field given the desired delay (in ns) +#define SPI_DLYBCT(delay, masterClock) ((uint32_t) ((((masterClock) / 1000000) * (delay)) / 32000) << 24) + +// com speed +#define FLASH_MINFAST 24000000 +#define FLASH_BAUD (MCK / 2) +#define FLASH_FASTBAUD MCK +#define FLASH_MINBAUD FLASH_FASTBAUD + +#define FASTFLASH (Flash_GetSpiBaudrate() > FLASH_MINFAST) + +#endif // FLASHMEM_HW_AT91_H diff --git a/common_arm/fpga/fpga_apis.h b/common_arm/fpga/fpga_apis.h new file mode 100644 index 000000000..26b96ac22 --- /dev/null +++ b/common_arm/fpga/fpga_apis.h @@ -0,0 +1,377 @@ +#ifndef FPGA_APIS_H_ +#define FPGA_APIS_H_ + +#include "common.h" +#include "fpga.h" + + +/* + Communication between ARM / FPGA is done inside armsrc/fpgaloader.c see: function FpgaSendCommand() + Send 16 bit command / data pair to FPGA with the bit format: + ++------ frame layout circa 2020 ------------------+ +| 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | ++-------------------------------------------------+ +| C C C C M M M M P P P P P P P P | C = FPGA_CMD_SET_CONFREG, M = FPGA_MAJOR_MODE_*, P = FPGA_LF_* or FPGA_HF_* parameter +| C C C C D D D D D D D D | C = FPGA_CMD_SET_DIVISOR, D = divisor +| C C C C T T T T T T T T | C = FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, T = threshold +| C C C C E | C = FPGA_CMD_TRACE_ENABLE, E=0 off, E=1 on +| C C C C P P P P P P P P P P P P | C = FPGA_CMD_SET_PWR_PWM_LOW_COUNT, P = low count value for HF/LF driver power PWM (PM5) ++-------------------------------------------------+ + ++------ frame layout current ---------------------+ +| 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | ++-------------------------------------------------+ +| C C C C M M M P P P P P P | C = FPGA_CMD_SET_CONFREG, M = FPGA_MAJOR_MODE_*, P = FPGA_LF_* or FPGA_HF_* parameter +| C C C C D D D D D D D D | C = FPGA_CMD_SET_DIVISOR, D = divisor +| C C C C T T T T T T T T | C = FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, T = threshold +| C C C C E | C = FPGA_CMD_TRACE_ENABLE, E=0 off, E=1 on ++-------------------------------------------------+ + + shift_reg receive this 16bit frame + + LF command + ---------- + shift_reg[15:12] == 4bit command + LF has three commands (FPGA_CMD_SET_CONFREG, FPGA_CMD_SET_DIVISOR, FPGA_CMD_SET_EDGE_DETECT_THRESHOLD) + Current commands uses only 2bits. We have room for up to 4bits of commands total (7). + + LF data + ------- + shift_reg[11:0] == 12bit data + lf data is divided into MAJOR MODES and configuration values. + + The major modes uses 3bits (0,1,2,3,7 | 000, 001, 010, 011, 111) + 000 FPGA_MAJOR_MODE_LF_READER = Act as LF reader (modulate) + 001 FPGA_MAJOR_MODE_LF_EDGE_DETECT = Simulate LF + 010 FPGA_MAJOR_MODE_LF_PASSTHRU = Passthrough mode, CROSS_LO line connected to SSP_DIN. SSP_DOUT logic level controls if we modulate / listening + 011 FPGA_MAJOR_MODE_LF_ADC = refactor hitag 2, clear ADC sampling + 111 FPGA_MAJOR_MODE_OFF = turn off sampling. + + Each one of this major modes can have options. Currently these two major modes uses options. + - FPGA_MAJOR_MODE_LF_READER + - FPGA_MAJOR_MODE_LF_EDGE_DETECT + + FPGA_MAJOR_MODE_LF_READER + ------------------------------------- + lf_field = 1bit (FPGA_LF_ADC_READER_FIELD) + + You can send FPGA_CMD_SET_DIVISOR to set with FREQUENCY the fpga should sample at + divisor = 8bits shift_reg[7:0] + + FPGA_MAJOR_MODE_LF_EDGE_DETECT + ------------------------------------------ + lf_ed_toggle_mode = 1bits + lf_ed_threshold = 8bits threshold defaults to 127 + + You can send FPGA_CMD_SET_EDGE_DETECT_THRESHOLD to set a custom threshold + lf_ed_threshold = 8bits threshold value. + + conf_word 12bits + conf_word[7:5] = 3bit major mode. + conf_word[0] = 1bit lf_field + conf_word[1] = 1bit lf_ed_toggle_mode + conf_word[7:0] = 8bit divisor + conf_word[7:0] = 8bit threshold + +*/ +// Defining commands, modes and options. This must be aligned to the definitions in fpga/define.v +#define FPGA_MAJOR_MODE_MASK 0x01C0 +#define FPGA_MINOR_MODE_MASK 0x003F + +// Definitions for the FPGA commands. +#define FPGA_CMD_SET_CONFREG (1<<12) +#define FPGA_CMD_SET_DIVISOR (2<<12) +#define FPGA_CMD_SET_EDGE_DETECT_THRESHOLD (3<<12) +#define FPGA_CMD_TRACE_ENABLE (2<<12) +#define FPGA_CMD_SET_PWR_PWM_LOW_COUNT (4<<12) // For PM5 + +// Major modes +#define FPGA_MAJOR_MODE_LF_READER (0<<6) +#define FPGA_MAJOR_MODE_LF_EDGE_DETECT (1<<6) +#define FPGA_MAJOR_MODE_LF_PASSTHRU (2<<6) +#define FPGA_MAJOR_MODE_LF_ADC (3<<6) + +#define FPGA_MAJOR_MODE_HF_READER (0<<6) +#define FPGA_MAJOR_MODE_HF_SIMULATOR (1<<6) +#define FPGA_MAJOR_MODE_HF_ISO14443A (2<<6) +#define FPGA_MAJOR_MODE_HF_SNIFF (3<<6) +#define FPGA_MAJOR_MODE_HF_ISO18092 (4<<6) +#define FPGA_MAJOR_MODE_HF_GET_TRACE (5<<6) +#define FPGA_MAJOR_MODE_OFF (7<<6) + +// Options for LF_READER +#define FPGA_LF_ADC_READER_FIELD ( 1 ) + +// Options for LF_EDGE_DETECT +#define FPGA_LF_EDGE_DETECT_READER_FIELD ( 1 ) +#define FPGA_LF_EDGE_DETECT_TOGGLE_MODE ( 2 ) + +// Options for the generic HF reader +#define FPGA_HF_READER_MODE_RECEIVE_IQ ( 0 ) +#define FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE ( 1 ) +#define FPGA_HF_READER_MODE_RECEIVE_PHASE ( 2 ) +#define FPGA_HF_READER_MODE_SEND_FULL_MOD ( 3 ) +#define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD ( 4 ) +#define FPGA_HF_READER_MODE_SNIFF_IQ ( 5 ) +#define FPGA_HF_READER_MODE_SNIFF_AMPLITUDE ( 6 ) +#define FPGA_HF_READER_MODE_SNIFF_PHASE ( 7 ) +#define FPGA_HF_READER_MODE_SEND_JAM ( 8 ) +#define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4 ( 9 ) + +#define FPGA_HF_READER_SUBCARRIER_848_KHZ (0<<4) +#define FPGA_HF_READER_SUBCARRIER_424_KHZ (1<<4) +#define FPGA_HF_READER_SUBCARRIER_212_KHZ (2<<4) +#define FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ (3<<4) + +// Options for the HF simulated tag, how to modulate +#define FPGA_HF_SIMULATOR_NO_MODULATION ( 0 ) +#define FPGA_HF_SIMULATOR_MODULATE_BPSK ( 1 ) +#define FPGA_HF_SIMULATOR_MODULATE_212K ( 2 ) +#define FPGA_HF_SIMULATOR_MODULATE_424K ( 4 ) +#define FPGA_HF_SIMULATOR_MODULATE_424K_8BIT ( 5 ) + +// Options for ISO14443A +#define FPGA_HF_ISO14443A_SNIFFER ( 0 ) +#define FPGA_HF_ISO14443A_TAGSIM_LISTEN ( 1 ) +#define FPGA_HF_ISO14443A_TAGSIM_MOD ( 2 ) +#define FPGA_HF_ISO14443A_READER_LISTEN ( 3 ) +#define FPGA_HF_ISO14443A_READER_MOD ( 4 ) + +// Options for ISO18092 / Felica +#define FPGA_HF_ISO18092_FLAG_NOMOD ( 1 ) // 0001 disable modulation module +#define FPGA_HF_ISO18092_FLAG_424K ( 2 ) // 0010 should enable 414k mode (untested). No autodetect +#define FPGA_HF_ISO18092_FLAG_READER ( 4 ) // 0100 enables antenna power, to act as a reader instead of tag + +// Options for adc mux. +// The mux is no longer set directly through the GPIO PIN to solve the problem of high coupling with the platform. +typedef enum { + ADC_MUXSEL_HIPKD = 0U, + ADC_MUXSEL_LOPKD, + ADC_MUXSEL_LORAW, + ADC_MUXSEL_HIRAW, +} adc_mux_io_t; + +// Block and wait for SSC data to be ready. +#define FPGA_SSC_RX_READY_WAIT() while(!FPGA_SSC_RX_Ready()) {} + +// Check if data already ready. +// On the AT91 platform, There is no need to consider overflow, as the data is always up-to-date. +// On the AT32 platform, You must call this function to confirm that the data is ready before reading the value. +// Warn: Continuously call this function to refresh the rx state, to avoid receiving stopped due to unused old data! +// If no this function call after DELAY/SlowTask, you may always get a fixed old data!!! +// !!! For maximum platform compatibility, it is essential to call this function !!! +STATIC_FORCE_INLINE bool FPGA_SSC_RX_Ready(void); + +// Check if data can transmit next one. +// The data clk is from fpga, so if fpga rx & process done, the next byte can transmit. +STATIC_FORCE_INLINE bool FPGA_SSC_TX_Ready(void); + +// Check if RX by DMA is done. +// Note: Only call this function to check data ready when DMA running. +STATIC_FORCE_INLINE bool FPGA_SSC_DMA_RX_Done(void); + +// Check if TX done. if done? next byte can put in DT register by FPGA_SSC_TX_Value() function. +// Note: Call this function before FPGA_SSC_TX_Value() calling. +STATIC_FORCE_INLINE bool FPGA_SSC_TX_Done(void); + +// Read the data received by SSC. The number of bits and bits order of the data are determined when configuring SSC. +// Note: this function has different characteristics on different platforms. +// On the AT91 platform, you can always get the latest received data. +// On the AT32 platform, if you don't check if the data is already ready, you may get an old data. +// Warn: We must first ensure that the data is ready, call the FPGA_SSC_RX_READY_WAIT() or FPGA_SSC_RX_Ready() +STATIC_FORCE_INLINE uint32_t FPGA_SSC_RX_Value(void); + +// Send the data by SSC, no DMA. +// Warn: Before sending, it is necessary to check if the previous sending has been completed! +STATIC_FORCE_INLINE void FPGA_SSC_TX_Value(uint32_t v); + +// Some platforms' send(data) registers may not automatically reset to zero. +// We need to ensure that a clearing action is performed before and after sending. +// Problem solved: If the sending (data) register is not cleared or non-zero data is received, +// it may cause erroneous modulation by continuing to send non-zero data after the transmission is completed. +// Note: This function will not wait for the sending to complete(Just waiting for TX ready). +STATIC_FORCE_INLINE void FPGA_SSC_TX_Clear(void); + +// DMA rx disable +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Disable(void); + +// DMA rx enable +// Note: Just started DMA transfer, will not reconfigure DMA. +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Enable(void); + +// The buf address currently receiving and storing data (not the address of data that has already been received) +// |done|done|done|working| <- you will get 'working' address. +STATIC_FORCE_INLINE uint32_t* FPGA_SSC_DMA_RX_Current_Address(void); + +// How much data still needs to be received? +// After receiving an item each time, subtract 1 from this value. +// Note: Is not bytes count, the bytes count is from FPGA_SSC_DMA_RX_Remaining_Count() * SSC_DATA_WIDTH +STATIC_FORCE_INLINE uint16_t FPGA_SSC_DMA_RX_Remaining_Count(void); + +// Continuing to trigger the next reception, +// DMA will automatically perform address rotation when the device supports NEXT BUF. +// Attention: This may result in data being overwritten. +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Refresh_Repeat(void *buf, uint16_t len); + +// Continue to trigger the next reception. +// This function will only trigger one reception and will not automatically trigger two receptions using the same address. +// It can be used when data processing speed is slow or when asynchronous reception processing with multiple buffers is required. +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Refresh_Single(void *buf, uint16_t len); + +//----------------------------------------------------------------------------- +// Provide a 24MHz clock from ARM to FPGA +// This is the most important main clock for FPGA, so it must be implemented! +//----------------------------------------------------------------------------- +void FpgaSetup24MHzClk(void); + +//----------------------------------------------------------------------------- +// Reset the fpga communication interface of Fpga +// 1. SPI for CMD +// 2. SSC for DataStream +// In AT32, it's reset the both spi, no SSC. +//----------------------------------------------------------------------------- +void FpgaResetComInterface(void); + +//----------------------------------------------------------------------------- +// The working mode of FPGA corresponds to the SSC communication frame mode. For platform compatibility, +// the cross-platform design only supports 8 or 16 bits. +// If the function returns 1, it is 16 bits data and MSB, +// otherwise, it is 8 bits data and MSB. +//----------------------------------------------------------------------------- +bool FpgaIs16BitMsbMode(uint16_t fpga_mode); + +//----------------------------------------------------------------------------- +// Set up the synchronous serial port with the set of options that fits +// the FPGA mode. Both RX and TX are always enabled. +// For AT91, it is SSC, and for AT32, it is SPI-TI_MODE +// Note: at32 spi 16bit max, so please try to use 8-bit or 16 bit transmission, +// otherwise platform compatibility cannot be handled. +//----------------------------------------------------------------------------- +void FpgaSetupSsc(uint16_t fpga_mode); + +//----------------------------------------------------------------------------- +// Modify the mode settings for rx&tx frames +// bits: How many bits are received each time, 8 or 16 +// msb: Should we transfer MSB first? +// Note: It can only be used to overwrite the settings of FpgaSetupSsc. +// Warn: RX&TX must use data of the same width! Avoid platform compatibility issues. +//----------------------------------------------------------------------------- +void FpgaUpdateFrameMode(uint8_t bits, bool rx_msb, bool tx_msb); + +//----------------------------------------------------------------------------- +// Set up DMA to receive samples from the FPGA. We will use the PDC, with +// a single buffer as a circular buffer (so that we just chain back to +// ourselves, not to another buffer). +//----------------------------------------------------------------------------- +bool FpgaSetupSscRxDmaRepeat(void *buf, uint16_t len); + +//----------------------------------------------------------------------------- +// Set up DMA to receive samples from the FPGA. We will use the PDC, with +// a single buffer not circular buffer (So it will only trigger one collection to this buffer +// to avoid data being overwritten.). +//----------------------------------------------------------------------------- +bool FpgaSetupSscRxDmaSingle(void *buf, uint16_t len); + +//----------------------------------------------------------------------------- +// Send a 16 bit command/data pair to the FPGA. +// The bit format is: C3 C2 C1 C0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 +// where C is the 4 bit command and D is the 12 bit data +// +// @params cmd and v gets OR:ED over each other. Take careful note of overlapping bits. +//----------------------------------------------------------------------------- +void FpgaSendCommand(uint16_t cmd, uint16_t v); + +//----------------------------------------------------------------------------- +// Write the FPGA setup word (that determines what mode the logic is in, read +// vs. clone vs. etc.). This is now a special case of FpgaSendCommand() to +// avoid changing this function's occurrence everywhere in the source code. +//----------------------------------------------------------------------------- +void FpgaWriteConfWord(uint16_t v); + +//----------------------------------------------------------------------------- +// enable FPGA internal tracing +//----------------------------------------------------------------------------- +void FpgaEnableTracing(void); + +//----------------------------------------------------------------------------- +// disable FPGA internal tracing +//----------------------------------------------------------------------------- +void FpgaDisableTracing(void); + +//----------------------------------------------------------------------------- +// Print the current FPGA information. +//----------------------------------------------------------------------------- +void Fpga_print_status(void); + +//----------------------------------------------------------------------------- +// Set up the CMOS switches that mux the ADC: four switches, independently +// closable, but should only close one at a time. Not an FPGA thing, but +// the samples from the ADC always flow through the FPGA. +//----------------------------------------------------------------------------- +void SetAdcMuxFor(adc_mux_io_t muxTo); + +//----------------------------------------------------------------------------- +// general turn off the antenna method +//----------------------------------------------------------------------------- +void switch_off(void); + +//----------------------------------------------------------------------------- +// Start FPGA bitstream configuration. Once started, the configuration will +// restart from the beginning of the bitstream (any previous configuration +// progress will be discarded). +// configSram: If true, configure into SRAM; if false, configure into Flash. +// Note: FPGAs on certain platforms may not support Flash +// configuration. If Flash configuration is not supported, +// PM3_EDEVNOTSUPP will be returned. +// fileLength: The length of the bitstream file in bytes. Some platforms may +// require this parameter to determine the configuration result or +// perform pre-configuration preparations. If the length exceeds +// the maximum size supported by the FPGA, PM3_EOVFLOW will be +// returned. +// return: PM3_XXX error code. Returns PM3_SUCCESS on successful configuration, +// or an error code on failure. If the error code is PM3_EFAILED, refer +// to FpgaConfigPlatformStatus() for more detailed error information. +//----------------------------------------------------------------------------- +int FpgaStartConfig(bool configSram, uint32_t fileLength); + +//----------------------------------------------------------------------------- +// Write bitstream data to the FPGA. +// data: Pointer to the bitstream data. Memory alignment is not required for +// the passed pointer, as each platform implementation handles buffered +// writing based on its minimum write unit. +// data_length: Length of the bitstream data in bytes. If the length exceeds +// the maximum size supported by the FPGA, PM3_EOVFLOW will be +// returned. +// return: PM3_XXX error code. Returns PM3_SUCCESS on successful configuration, +// or an error code on failure. If the error code is PM3_EFAILED, refer +// to FpgaConfigPlatformStatus() for more detailed error information. +//----------------------------------------------------------------------------- +int FpgaConfigWrite(uint8_t *data, uint32_t data_length); + +//----------------------------------------------------------------------------- +// Stop FPGA bitstream configuration and release resources allocated during +// the configuration process. +// return: PM3_XXX error code. Returns PM3_SUCCESS if the configuration is +// successfully stopped, or an error code if stopping fails. If the +// error code is PM3_EFAILED, refer to FpgaConfigPlatformStatus() for +// more detailed error information. +//----------------------------------------------------------------------------- +int FpgaStopConfig(void); + +//----------------------------------------------------------------------------- +// Get the FPGA configuration status. The return value is a platform-specific +// status code. Please refer to the platform-related documentation or source +// code for specific status information. +// This function is primarily used to obtain more detailed error information +// when configuration fails, facilitating debugging and issue troubleshooting. +//----------------------------------------------------------------------------- +uint32_t FpgaConfigPlatformStatus(void); + +#ifdef PM5 +#include "fpga_hw_at32.h" +#else +#include "fpga_hw_at91.h" +#endif + +#endif // FPGA_APIS_H_ diff --git a/common_arm/fpga/fpga_core.c b/common_arm/fpga/fpga_core.c new file mode 100644 index 000000000..df7b312bc --- /dev/null +++ b/common_arm/fpga/fpga_core.c @@ -0,0 +1,130 @@ +#include "fpga_apis.h" +#include "gpio_apis.h" +#include "fpga_loader.h" +#include "fpga.h" +#include "dbprint.h" +#include "util.h" +#include "BigBuf.h" +#include "appmain.h" + +bool FpgaIs16BitMsbMode(uint16_t fpga_mode) { + if (((fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_READER) && + (FpgaGetCurrent() == FPGA_BITSTREAM_HF || FpgaGetCurrent() == FPGA_BITSTREAM_HF_15)) { + return true; + } + return false; +} + +void FpgaWriteConfWord(uint16_t v) { + const int current = FpgaGetCurrent(); + + // Keep track of whether or not we should be monitoring the HF field timeout + if (current == FPGA_BITSTREAM_HF || current == FPGA_BITSTREAM_HF_15 || current == FPGA_BITSTREAM_HF_FELICA) { + const uint16_t major = v & FPGA_MAJOR_MODE_MASK; + const uint16_t minor = v & FPGA_MINOR_MODE_MASK; + + switch (major) { + case FPGA_MAJOR_MODE_HF_READER: + g_hf_field_timeout_active = true; + break; + case FPGA_MAJOR_MODE_HF_ISO14443A: + g_hf_field_timeout_active = (minor == FPGA_HF_ISO14443A_READER_LISTEN || minor == FPGA_HF_ISO14443A_READER_MOD); + break; + case FPGA_MAJOR_MODE_HF_ISO18092: + g_hf_field_timeout_active = (minor & FPGA_HF_ISO18092_FLAG_READER) != 0; + break; + default: + g_hf_field_timeout_active = false; + break; + } + } else { + g_hf_field_timeout_active = false; + } + + FpgaSendCommand(FPGA_CMD_SET_CONFREG, v); +} + +void FpgaEnableTracing(void) { + FpgaSendCommand(FPGA_CMD_TRACE_ENABLE, 1); +} + +void FpgaDisableTracing(void) { + FpgaSendCommand(FPGA_CMD_TRACE_ENABLE, 0); +} + +void SetAdcMuxFor(adc_mux_io_t muxTo) { + +#ifdef PM5 // fpga_switch pin resue to switch adc mux. + if ((muxTo == ADC_MUXSEL_LORAW) || (muxTo == ADC_MUXSEL_HIRAW)) + return; + + gpio_adc_mux_setup(); + + if (muxTo == ADC_MUXSEL_HIPKD) { + Gpio_FPGA_SWITCH_High(); + } else { + Gpio_FPGA_SWITCH_Low(); + } + return; +#endif + +#ifndef WITH_FPC_USART + + gpio_adc_mux_setup(); + + Gpio_MUXSEL_HIPKD_Low(); + Gpio_MUXSEL_LOPKD_Low(); + Gpio_MUXSEL_HIRAW_Low(); + Gpio_MUXSEL_LORAW_Low(); + + switch (muxTo) { + case ADC_MUXSEL_HIPKD: + Gpio_MUXSEL_HIPKD_High(); + break; + case ADC_MUXSEL_LOPKD: + Gpio_MUXSEL_LOPKD_High(); + break; + case ADC_MUXSEL_HIRAW: + Gpio_MUXSEL_HIRAW_High(); + break; + case ADC_MUXSEL_LORAW: + Gpio_MUXSEL_LORAW_High(); + break; + } + +#else + if ((muxTo == ADC_MUXSEL_LORAW) || (muxTo == ADC_MUXSEL_HIRAW)) + return; + + gpio_adc_mux_setup(); + + Gpio_MUXSEL_HIPKD_Low(); + Gpio_MUXSEL_LOPKD_Low(); + + if (muxTo == ADC_MUXSEL_HIPKD) { + Gpio_MUXSEL_HIPKD_High(); + } + if (muxTo == ADC_MUXSEL_LOPKD) { + Gpio_MUXSEL_LOPKD_High(); + } +#endif + +} + +// Turns off the antenna, +// log message +// if HF, Disable SSC DMA +// turn off trace and leds off. +void switch_off(void) { + if (g_dbglevel > DBG_DEBUG) { + Dbprintf("switch_off"); + } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + if (FpgaGetCurrent() == FPGA_BITSTREAM_HF || FpgaGetCurrent() == FPGA_BITSTREAM_HF_15) { + FPGA_SSC_DMA_RX_Disable(); + } + + set_tracing(false); + LEDsoff(); +} diff --git a/common_arm/fpga/fpga_gw_jtag.c b/common_arm/fpga/fpga_gw_jtag.c new file mode 100644 index 000000000..a08fe78e1 --- /dev/null +++ b/common_arm/fpga/fpga_gw_jtag.c @@ -0,0 +1,790 @@ +/* + * GOWIN fpga JTAG software implement + * + * @Author DXL + * GPL license + */ + +#include +#include "fpga_gw_jtag.h" + +#define INST_BYPASS 0xFF +#define INST_IDCODE 0x11 +#define INST_STATUS 0x41 +#define INST_USERCODE 0x13 +#define INST_CONFIG_ENABLE 0x15 +#define INST_CONFIG_DISABLE 0x3A +#define INST_NOOP 0x02 +#define INST_SRAM_ERASE 0x05 +#define INST_SRAM_ERASE_DONE 0x09 +#define INST_EFLASH_ERASE 0x75 +#define INST_EF_PROGRAM 0x71 +#define INST_EF_READ 0x73 +#define INST_REPROGRAM 0x3C +#define INST_TRANSFER_CFG 0x17 +#define INST_ADDR_INIT 0x12 +#define INST_SRAM_READ 0x03 + + +typedef struct { + uint16_t sram_erase_ms; // 在发送 EraseSram(0x05)指令、Noop(0x02)之后,要给足够的时间等待其擦除完毕 + uint16_t y_page_w_wait_us; // 写一个y-page完成后需要延迟的时间长度 + uint16_t x_page_w_wait_us; // 写一个x-page完成后需要延迟的时间长度 +} gowin_timing_t; + +typedef struct { + uint32_t idcode; + gowin_device_t device; + const char *name; + gowin_flash_type_t flash_type; + bool reprogram; // 在部分器件中,如果 JTAG 的 4 个管脚或 JTAGSEL_N 复用为 GPIO,此时若需重新配置,需要先发送一次 reprogram 指令。 + gowin_timing_t timing; +} device_map_t; + +static gowin_device_t detected_device = GW_DEVICE_UNKNOWN; +static uint32_t cached_idcode = 0; +static bool m_flash_bg_update = false; +static uint8_t *m_flash_xpage_buf = NULL; +static uint16_t m_flash_xpage_pos = 0; + +static const device_map_t device_map[] = { + { + 0x0900281B, GW_DEVICE_GW1N_1, "GW1N-1", GW_FLASH_TYPE_HL, true, + {1, 0, 2400} + }, + { + 0x0900381B, GW_DEVICE_GW1N_1S, "GW1N-1S", GW_FLASH_TYPE_HL, true, + {1, 0, 2400} + }, + { + 0x0100681B, GW_DEVICE_GW1NZ_1, "GW1NZ-1", GW_FLASH_TYPE_TSMC, true, + {1, 0, 6} + }, + { + 0x0120681B, GW_DEVICE_GW1N_R_Z_2_2B_2C, "GW1N(R/Z)-2/2B/2C/1P5/1P5B/1P5C", GW_FLASH_TYPE_TSMC, false, + {2, 16, 6} + }, + // 以上将 GW1N-2 和 GW1N-1P5 系列合并映射 {0x0120681B, GW_DEVICE_GW1N_1P5_1P5B_1P5C, "GW1N-1P5/1P5B/1P5C", {2, 120, 0, 32}}, + { + 0x0100381B, GW_DEVICE_GW1N_R_4, "GW1N(R)-4", GW_FLASH_TYPE_TSMC, true, + {2, 16, 6} + }, + { + 0x1100381B, GW_DEVICE_GW1N_R_4B, "GW1N(R)-4B/4D", GW_FLASH_TYPE_TSMC, true, + {2, 16, 6} + }, + // 以上将 GW1NR-4B 和 GW1NR-4D 系列合并映射 {0x1100381B, GW_DEVICE_GW1N_R_4D, "GW1N(R)-4D", {2, 120, 0, 32}}, + { + 0x0100881B, GW_DEVICE_GW1NS_4, "GW1NS-4", GW_FLASH_TYPE_TSMC, false, + {2, 16, 6} + }, + { + 0x0100981B, GW_DEVICE_GW1NS_ER_4C, "GW1NS(ER)-4C", GW_FLASH_TYPE_TSMC, false, + {2, 16, 6} + }, + { + 0x1100581B, GW_DEVICE_GW1N_R_9, "GW1N(R)-9", GW_FLASH_TYPE_TSMC, true, + {4, 16, 6} + }, + { + 0x1100481B, GW_DEVICE_GW1N_R_9C, "GW1N(R)-9C", GW_FLASH_TYPE_TSMC, true, + {4, 16, 6} + }, + + // 根据手册描述: GW2ANR-18/GW2AN-55 内部封了一颗 SPI-Flash,编程方式与 GW2A-18、GW2A-55 相同 + // 也就是说,GW2A 系列是 spi-flash,需要让JTAG接口转接到MSPI的情况下,用SPI指令去操作最终的片上SPI—FLASH或者外部FLASH + // 大概流程就是JATG -> 0x16指令 -> MSPI -> 0x06(写使能) -> 0xC7(擦除)... + // 由此总结就是,除了转接到MSPI之前需要用到JTAG,其他时候都是和 SPI-FLASH 有关的操作了,因此擦除不需要像内部FLASH一样必须提供一个指定速率的时钟 + // {0x0000081B, GW_DEVICE_GW2A_R_18_18C, "GW2A(R)-18/18C", {6, 120, 0, 32}}, + // {0x0000281B, GW_DEVICE_GW2A_55_55C, "GW2A-55/55C", {10, 120, 0, 32}}, + + // 暂时不考虑这两个旧的型号的适配,这俩芯片官方貌似已经停产了,官方已经把芯片标记为old然后手册也删除了相关的信息,弄样品测试也麻烦。 + // 注:这俩芯片的内部FLASH工艺是SMIC,官方有STM32的例程和代码有封装了此芯片的内部FLASH烧录 + // 此系列要求的 Y page 写入之后的延迟时长是 30-35us + // #define ID_GW1NS_2 0x0300081B + // #define ID_GW1NS_2C 0x0300181B +}; + +// 计算当前设备映射表中的设备型号数量 +#define DEVICE_SIZE (sizeof(device_map) / sizeof(device_map[0])) + +// 根据ID索引到具体的设备信息映射表上,如果没有发现对应的设备存在,则返回NULL +static const device_map_t *get_device_map_by_idcode(void) { + for (size_t i = 0; i < DEVICE_SIZE; i++) { + if (device_map[i].idcode == cached_idcode) { + return &device_map[i]; + } + } + return NULL; +} + +// 适用于只需要考虑JTAG的时钟速率上限的情况,不可以用于擦除和编程 +static void jtag_pulse_tck(gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) { + return; + } + if (jtag_ops->tck_2m) { + // 如果实现了tck脉冲接口,则优先调用 + jtag_ops->tck_2m(0); + return; + } + if (!jtag_ops->delay_us) { + // delay_us 作为后备方案,如果未实现此后备接口,则通信无法正常执行。 + return; + } + // 理想情况下,是 500kHZ + jtag_ops->set_tck(0); + jtag_ops->delay_us(1); + jtag_ops->set_tck(1); + jtag_ops->delay_us(1); +} + +// 设置tap状态机并且产生一次驱动时钟,驱动时钟的速度取决于 jtag_pulse_tck() 函数 +static void jtag_tap_clock(bool tms, gowin_jtag_ops_t *jtag_ops) { + jtag_ops->set_tms(tms); + jtag_pulse_tck(jtag_ops); +} + +// 从 Run-Test/Idle 进入 Shift-IR(标准 IEEE 1149.1 路径) +static void jtag_goto_shift_ir(gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return; + jtag_tap_clock(1, jtag_ops); // -> Select-DR-Scan + jtag_tap_clock(1, jtag_ops); // -> Select-IR-Scan + jtag_tap_clock(0, jtag_ops); // -> Capture-IR + jtag_tap_clock(0, jtag_ops); // -> Shift-IR +} + +static void jtag_shift_ir_safe(uint8_t inst, gowin_jtag_ops_t *jtag_ops) { + jtag_goto_shift_ir(jtag_ops); + for (int i = 0; i < 8; i++) { + jtag_ops->set_tdi((inst >> i) & 1); + jtag_tap_clock(i == 7, jtag_ops); // -> Exit1-IR if is last bit + } + // Exit1-IR -> Update-IR -> Run-Test/Idle + jtag_tap_clock(1, jtag_ops); // -> Update-IR + jtag_tap_clock(0, jtag_ops); // -> Run-Test/Idle + // Per Gowin spec: ≥3 TCK cycles in Run-Test/Idle after IR load + for (int i = 0; i < 6; i++) { + jtag_pulse_tck(jtag_ops); + } +} + +// 从 Run-Test/Idle 进入 Shift-DR(标准 IEEE 1149.1 路径) +static void jtag_goto_shift_dr(gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return; + jtag_tap_clock(1, jtag_ops); // -> Select-DR + jtag_tap_clock(0, jtag_ops); // -> Capture-DR + jtag_tap_clock(0, jtag_ops); // -> Shift-DR +} + +// 仅用于从LSB开始发送的数据 +static void jtag_shift_dr_safe(const uint8_t *tx, uint8_t *rx, uint32_t bits, gowin_jtag_ops_t *jtag_ops) { + // From Run-Test/Idle -> Select-DR-Scan -> Capture-DR -> Shift-DR + jtag_goto_shift_dr(jtag_ops); + + uint8_t byte = 0; + for (uint32_t i = 0; i < bits; i++) { + int byte_idx = i / 8; + int bit_idx = i % 8; + bool tdi = tx ? ((tx[byte_idx] >> bit_idx) & 1) : false; + jtag_ops->set_tdi(tdi); + jtag_tap_clock(i == bits - 1, jtag_ops); // -> Exit1-DR if is last bit + + if (rx) { + bool tdo = jtag_ops->get_tdo(); + byte |= (tdo << bit_idx); + if (bit_idx == 7 || i == bits - 1) { + rx[byte_idx] = byte; + byte = 0; + } + } + } + + // Exit1-DR -> Update-DR -> Run-Test/Idle + jtag_tap_clock(1, jtag_ops); // -> Update-DR + jtag_tap_clock(0, jtag_ops); // -> Run-Test/Idle +} + +#if DEBUG_GW_JTAG +static void print_gowin_status(gowin_status_reg_t *status, gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return; + if (!jtag_ops->dbg_printf) return; + jtag_ops->dbg_printf("Gowin Status Register (raw = 0x%08X):", status->raw); + /* + jtag_ops->dbg_print(" crc_error : %u // CRC Error Flag", status->bits.crc_error); + jtag_ops->dbg_print(" bad_command_error : %u // Bad Command Error Flag", status->bits.bad_command_error); + jtag_ops->dbg_print(" id_verify_failed : %u // ID Verify Failed Error Flag", status->bits.id_verify_failed); + jtag_ops->dbg_print(" timeout_error : %u // Timeout Error Flag", status->bits.timeout_error); + jtag_ops->dbg_print(" reserved_4 : %u // Reserved (should be 0)", status->bits.reserved_4); + jtag_ops->dbg_print(" memory_erase : %u // Memory Erase Flag", status->bits.memory_erase); + jtag_ops->dbg_print(" preamble : %u // Preamble Flag", status->bits.preamble); + + */ + + jtag_ops->dbg_printf(" edit_mode : %u // Edit Mode Flag", status->bits.edit_mode); + + /* + + jtag_ops->dbg_print(" program_spi_directly : %u // Program SPI Directly Flag", status->bits.program_spi_directly); + jtag_ops->dbg_print(" autoboot_state : %u // AutoBoot State", status->bits.autoboot_state); + jtag_ops->dbg_print(" non_jtag_active : %u // Non-JTAG Active Flag", status->bits.non_jtag_active); + jtag_ops->dbg_print(" bypass_state : %u // Bypass State Flag", status->bits.bypass_state); + jtag_ops->dbg_print(" vld : %u // VLD (1=normal)", status->bits.vld); + + */ + + jtag_ops->dbg_printf(" done_final : %u // Done Final (1=success)", status->bits.done_final); + jtag_ops->dbg_printf(" security_final : %u // Security Final (1=secured)", status->bits.security_final); + jtag_ops->dbg_printf(" ready : %u // Ready (1=normal)", status->bits.ready); + jtag_ops->dbg_printf(" por : %u // POR (1=normal)", status->bits.por); + /* + jtag_ops->dbg_print(" flash_lock : %u // Flash Lock (1=locked)", status->bits.flash_lock); + jtag_ops->dbg_print(" reserved_18_31 : %u // Reserved bits [31:18] (should be 0)", status->bits.reserved_18_31); + */ +} +#endif + +static uint32_t gowin_jtag_read_idcode_u32(gowin_jtag_ops_t *jtag_ops) { + uint8_t buf[4] = {0}; + jtag_shift_ir_safe(INST_IDCODE, jtag_ops); + jtag_shift_dr_safe(NULL, buf, 32, jtag_ops); + return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; +} + +/** + * 根据传入的JTAG硬件实现初始化jtag接口 + * + * @param ops 此库会保存此引用,因此不可以在函数栈内进行非static定义,否则此init函数退出后,ops将会变为野指针导致后续操作随机跑飞 + * @return 初始化成功时,返回 GOWIN_JTAG_OK + */ +gowin_jtag_status_t gowin_jtag_init(gowin_jtag_ops_t *ops) { + if (!ops || !ops->tck_2m) { + return GOWIN_JTAG_ERROR_NULL_POINTER; + } + // 如果实现了 JTAGSEL 引脚的设置函数,则需要在启动JTAG操作之前,拉低 JTAGSEL 引脚,确保取消FPGA的JTAG复用 + if (ops->set_jtagsel) { + ops->set_jtagsel(false); + } + // 重置TAP状态机,确保和设备从 Run-Test/Idle 位置开始通信 + gowin_jtag_reset(ops); + // 读取IDCODE,并且缓存到全局域 + cached_idcode = gowin_jtag_read_idcode_u32(ops); + const device_map_t *dm = get_device_map_by_idcode(); + detected_device = dm ? dm->device : GW_DEVICE_UNKNOWN; + return detected_device == GW_DEVICE_UNKNOWN ? GOWIN_JTAG_ERROR_INVALID_IDCODE : GOWIN_JTAG_OK; +} + +void gowin_jtag_deinit(gowin_jtag_ops_t *jtag_ops) { + // 拉高JTAGSEL脚,恢复JTAG脚复用为GPIO + if (jtag_ops->set_jtagsel) { + jtag_ops->set_jtagsel(true); + } +} + +gowin_device_t gowin_jtag_get_device_type(void) { + return detected_device; +} + +const char *gowin_jtag_get_device_name(void) { + const device_map_t *dm = get_device_map_by_idcode(); + return dm ? dm->name : "Unknown"; +} + +gowin_flash_type_t gowin_get_flash_type(void) { + const device_map_t *dm = get_device_map_by_idcode(); + return dm ? dm->flash_type : GW_FLASH_TYPE_UNKNOWN; +} + +uint32_t gowin_jtag_get_idcode(void) { + return cached_idcode; +} + +void gowin_jtag_reset(gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return; + jtag_ops->set_tms(1); + for (int i = 0; i < 6; i++) { + jtag_pulse_tck(jtag_ops); // 替代原来的 set_tck toggle + } + // Enter Run-Test/Idle explicitly + jtag_tap_clock(0, jtag_ops); +} + +static uint32_t gowin_jtag_read_status_u32(gowin_jtag_ops_t *jtag_ops) { + jtag_shift_ir_safe(INST_STATUS, jtag_ops); + uint8_t buf[4] = {0}; + jtag_shift_dr_safe(NULL, buf, 32, jtag_ops); + return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; +} + +uint32_t gowin_jtag_read_status(gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return 0; + return gowin_jtag_read_status_u32(jtag_ops); +} + +uint32_t gowin_jtag_read_usercode(gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return 0; + jtag_shift_ir_safe(INST_USERCODE, jtag_ops); + uint8_t buf[4] = {0}; + jtag_shift_dr_safe(NULL, buf, 32, jtag_ops); + return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; +} + +void gowin_jtag_reprogram(gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return; + jtag_shift_ir_safe(INST_REPROGRAM, jtag_ops); + jtag_shift_ir_safe(INST_NOOP, jtag_ops); + jtag_ops->delay_ms(200); +} + +gowin_jtag_status_t gowin_jtag_read_status_reg(gowin_status_reg_t *reg_out, gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return GOWIN_JTAG_ERROR_NULL_POINTER; + if (reg_out) { + reg_out->raw = gowin_jtag_read_status_u32(jtag_ops); + } +#if DEBUG_GW_JTAG + print_gowin_status(reg_out, jtag_ops); +#endif + return GOWIN_JTAG_OK; +} + +static gowin_jtag_status_t gowin_jtag_cfg_enable(bool enable, gowin_jtag_ops_t *jtag_ops) { + gowin_jtag_status_t status; + gowin_status_reg_t status_reg; + + // send command + if (enable) { + jtag_shift_ir_safe(INST_CONFIG_ENABLE, jtag_ops); + } else { + jtag_shift_ir_safe(INST_CONFIG_DISABLE, jtag_ops); + jtag_shift_ir_safe(INST_NOOP, jtag_ops); + } + + // check status and waiting for edit mode enter. + uint32_t retry = 100000; // timeout + while (retry--) { + status = gowin_jtag_read_status_reg(&status_reg, jtag_ops); + if (status != GOWIN_JTAG_OK) { + return status; + } + if (enable && status_reg.bits.edit_mode) { + return GOWIN_JTAG_OK; + } + if (!enable && !status_reg.bits.edit_mode) { + return GOWIN_JTAG_OK; + } + } + + return GOWIN_JTAG_ERROR_ENABLE_CFG; +} + +gowin_jtag_status_t gowin_jtag_sram_config_start(uint32_t *tx_bits_pos, gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return GOWIN_JTAG_ERROR_NULL_POINTER; + if (detected_device == GW_DEVICE_UNKNOWN) return GOWIN_JTAG_ERROR_INVALID_IDCODE; + // TAP 复位,非常重要,让FPGA的TAP的状态机回到 Run-Test-Idle 状态 + gowin_jtag_reset(jtag_ops); + // 无论如何,总是在启动配置SRAM的时候,首先擦除SRAM + gowin_jtag_status_t status = gowin_jtag_sram_erase(jtag_ops); + if (status != GOWIN_JTAG_OK) { + return status; + } + jtag_shift_ir_safe(INST_CONFIG_ENABLE, jtag_ops); // 发送 ConfigEnable 指令 0x15 + jtag_shift_ir_safe(INST_ADDR_INIT, jtag_ops); // 发送 Address Initialize 指令 0x12 + jtag_shift_ir_safe(INST_TRANSFER_CFG, jtag_ops); // 发送 Transfer Configuration Data 指令 0x17 + jtag_goto_shift_dr(jtag_ops); // 移动状态到 Shift-DR(数据寄存器) + + // 将 Bitstream Data 从最高位开始(MSB),逐位发送,发送全部数据流文件内容,并回到 Run-Test-Idle状态 + // 注:在配置接口中进行此操作,对于配置接口来说,此操作可以分多步执行,一点点发送文件知道全部发送完毕 + *tx_bits_pos = 0; // 在此处进行传输的比特流位置的重置 + + return GOWIN_JTAG_OK; +} + +gowin_jtag_status_t gowin_jtag_sram_config_write(uint8_t *data, uint32_t data_length, uint32_t *tx_bytes_pos, + uint32_t tx_bytes_total, gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops || !data) return GOWIN_JTAG_ERROR_NULL_POINTER; + if (detected_device == GW_DEVICE_UNKNOWN) return GOWIN_JTAG_ERROR_INVALID_IDCODE; + + // 将 Bitstream Data 从最高位开始(MSB),逐位发送,发送全部数据流文件内容,并回到 Run-Test-Idle状态 + for (uint32_t i = 0; i < data_length; i++) { + // Send byte + for (uint8_t j = 0; j < 8; j++) { + // Send bits + jtag_ops->set_tdi(data[i] >> (7 - j) & 0x01); // MSB first + if (j == 7) { + // Increment tx_bytes_pos if one byte transfer finish. + (*tx_bytes_pos)++; + // -> Exit1-DR if is last bit and is last byte + jtag_tap_clock(*tx_bytes_pos == tx_bytes_total, jtag_ops); + } else { + jtag_tap_clock(0, jtag_ops); // One clock, no Exit1-DR + } + } + } + + return GOWIN_JTAG_OK; +} + +gowin_jtag_status_t gowin_jtag_sram_config_finish(gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return GOWIN_JTAG_ERROR_NULL_POINTER; + jtag_shift_ir_safe(INST_CONFIG_DISABLE, jtag_ops); + jtag_shift_ir_safe(INST_NOOP, jtag_ops); + + // SRAM 写完后等待 60ms, 以待 status code 刷新 + jtag_ops->delay_ms(60); + + // 记得,一定要重置状态机,让fpga回到 Run-Test/Idle 的状态,不然新固件不启动 + gowin_jtag_reset(jtag_ops); + + return GOWIN_JTAG_OK; +} + +gowin_jtag_status_t gowin_jtag_sram_erase(gowin_jtag_ops_t *jtag_ops) { + const device_map_t *dm = get_device_map_by_idcode(); + + if (!dm) return GOWIN_JTAG_ERROR_NULL_POINTER; + if (!jtag_ops) return GOWIN_JTAG_ERROR_NULL_POINTER; + + jtag_shift_ir_safe(INST_CONFIG_ENABLE, jtag_ops); + jtag_shift_ir_safe(INST_SRAM_ERASE, jtag_ops); + jtag_shift_ir_safe(INST_NOOP, jtag_ops); + + jtag_ops->tck_2m(dm->timing.sram_erase_ms * 1000); + // jtag_ops->delay_ms(dm->timing.sram_erase_ms); + + jtag_shift_ir_safe(INST_SRAM_ERASE_DONE, jtag_ops); + jtag_shift_ir_safe(INST_NOOP, jtag_ops); + jtag_shift_ir_safe(INST_CONFIG_DISABLE, jtag_ops); + jtag_shift_ir_safe(INST_NOOP, jtag_ops); + + return GOWIN_JTAG_OK; +} + +// readout status and check POR & VLD +static gowin_jtag_status_t gowin_check_status_gw1n(gowin_status_reg_t *reg_out, gowin_jtag_ops_t *jtag_ops) { + gowin_jtag_read_status_reg(reg_out, jtag_ops); + if (!reg_out->bits.vld) { + return GOWIN_JTAG_ERROR_VLD_STATUS; + } + if (!reg_out->bits.por) { + return GOWIN_JTAG_ERROR_POR_STATUS; + } + return GOWIN_JTAG_OK; +} + +// 读出并且检查是否擦除成功,此函数仅用于gw1n系列 +static gowin_jtag_status_t gowin_check_erase_gw1n(gowin_status_reg_t *reg_out, gowin_jtag_ops_t *jtag_ops) { + gowin_jtag_read_status_reg(reg_out, jtag_ops); + // 不检查 Security Final 位 + if (reg_out->bits.vld && reg_out->bits.por && reg_out->bits.ready && reg_out->bits.done_final) { + return GOWIN_JTAG_ERROR_ERASE_FAIL; + } + return GOWIN_JTAG_OK; +} + +gowin_jtag_status_t gowin_jtag_flash_erase(gowin_jtag_ops_t *jtag_ops) { + gowin_status_reg_t status_reg; + + if (!jtag_ops) return GOWIN_JTAG_ERROR_NULL_POINTER; + if (detected_device == GW_DEVICE_UNKNOWN) return GOWIN_JTAG_ERROR_INVALID_IDCODE; + + const device_map_t *dm = get_device_map_by_idcode(); + if (dm == NULL) { + return GOWIN_JTAG_ERROR_INVALID_IDCODE; + } + + // 读一下状态值,确认当前没问题 + gowin_jtag_status_t api_status = gowin_check_status_gw1n(&status_reg, jtag_ops); + if (api_status != GOWIN_JTAG_OK) { + return api_status; + } + + // if (jtag_ops->dbg_print) jtag_ops->dbg_print("m_flash_bg_update = %d", m_flash_bg_update); + + // 如果不是背景烧录的话,就得关注 done_final 位,如果 done_final位是高的,就得清除SRAM,否则不需要清除 + // 因为在背景烧录的情况下,我们仍需要保留SRAM中的FPGA固件,使其正常运行,更新操作只会操作FLASH,不会导致SRAM被覆盖,因此不会中断服务 + if (m_flash_bg_update == false && status_reg.bits.done_final) { + // Do sram erase + api_status = gowin_jtag_sram_erase(jtag_ops); + if (api_status != GOWIN_JTAG_OK) { + return api_status; + } + // Verify for erase sram result + api_status = gowin_check_erase_gw1n(&status_reg, jtag_ops); + if (api_status != GOWIN_JTAG_OK) { + return api_status; + } + if (jtag_ops->dbg_printf) jtag_ops->dbg_printf("erase the SRAM is finish, next step erase the FLASH"); + } + + // 擦除过程,FLASH工艺不同,所进行的操作也不同 + gowin_jtag_cfg_enable(true, jtag_ops); + + jtag_shift_ir_safe(INST_EFLASH_ERASE, jtag_ops); // 发送内嵌FLASH的擦除指令 0x75 + if (dm->flash_type == GW_FLASH_TYPE_HL) { + for (int i = 0; i < 65; i++) { + // H工艺要求重复此步骤65次,这是手册要求的 + // 移动状态到 Shift-DR(数据寄存器),并且产生32个时钟(TDI保持低电平) + jtag_shift_dr_safe(NULL, NULL, 32, jtag_ops); + } + jtag_ops->tck_2m(95 * 1000); // H 工艺要求后续在 Run-Test-Idle 状态下持续产生时钟95ms + // if (jtag_ops->dbg_print) jtag_ops->dbg_print("erase for GW_FLASH_TYPE_HL"); + } + if (dm->flash_type == GW_FLASH_TYPE_TSMC) { + jtag_shift_dr_safe(NULL, NULL, 32, jtag_ops); // T 工艺只要求产生一次32bit的输出传输时钟 + jtag_ops->tck_2m(150 * 1000); // T 工艺要求后续在 Run-Test-Idle 状态下持续产生时钟 120-150 ms + // if (jtag_ops->dbg_print) jtag_ops->dbg_print("erase for GW_FLASH_TYPE_TSMC"); + } + + gowin_jtag_cfg_enable(false, jtag_ops); + + // 官方的代码里,H工艺在发送了 0x02 之后延迟了 500ms才继续干活,T工艺则是200ms + if (dm->flash_type == GW_FLASH_TYPE_HL) { + jtag_ops->delay_ms(500); + if (m_flash_bg_update == false) { + // 如果背景烧录使能,则不需要检查任何状态码相关的异常,因为这个时候固件是在正常运行的 + api_status = gowin_check_erase_gw1n(&status_reg, jtag_ops); + if (api_status != GOWIN_JTAG_OK) { + // 擦除失败了,直接报错 + return api_status; + } + } + } + if (dm->flash_type == GW_FLASH_TYPE_TSMC) { + jtag_ops->delay_ms(200); + if (m_flash_bg_update == false) { + // 如果背景烧录使能,则不可以触发重新配置,否则会导致被清空的FLASH的数据加载到SRAM覆盖正在运行的固件 + gowin_jtag_reprogram(jtag_ops); + // 读取固件重新配置的结果,理论上应当是要停止运行的,非done和ready状态 + api_status = gowin_check_erase_gw1n(&status_reg, jtag_ops); + if (api_status != GOWIN_JTAG_OK) { + // 擦除失败了,直接报错 + return api_status; + } + } + } + + return GOWIN_JTAG_OK; +} + +gowin_jtag_status_t gowin_jtag_flash_config_start(uint8_t *xbuf_256, uint32_t *tx_bits_pos, + bool bg_update, gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return GOWIN_JTAG_ERROR_NULL_POINTER; + if (detected_device == GW_DEVICE_UNKNOWN) return GOWIN_JTAG_ERROR_INVALID_IDCODE; + + *tx_bits_pos = 0; // 在此处进行传输的比特流位置的重置 + m_flash_bg_update = bg_update; // 缓存背景升级的操作标志 + m_flash_xpage_buf = xbuf_256; // 由外部提供一个256byte的缓冲区,所有传过来的固件数据都依靠此buf进行整xpage的缓存 + m_flash_xpage_pos = 0; // 重置xpage的缓存位置,也就是将当前xpage的buf的有效字节数量归零 + + // TAP 复位,非常重要,让FPGA的TAP的状态机回到 Run-Test-Idle 状态 + gowin_jtag_reset(jtag_ops); + // 无论如何,总是在启动配置FLASH的时候,首先擦除FLASH + gowin_jtag_status_t status = gowin_jtag_flash_erase(jtag_ops); + if (status != GOWIN_JTAG_OK) { + return status; + } + + return GOWIN_JTAG_OK; +} + +static void gowin_jtag_flash_config_xpage(const uint8_t data[256], uint32_t page_index, gowin_jtag_ops_t *jtag_ops) { + const device_map_t *dm = get_device_map_by_idcode(); + + jtag_shift_ir_safe(INST_CONFIG_ENABLE, jtag_ops); // 发送配置使能指令 0x15 + jtag_shift_ir_safe(INST_EF_PROGRAM, jtag_ops); // 发送写内部FLASH指令 0x71 + + // 根据手册描述,在编程的页面地址大于0时,需要等待16us + if (page_index > 0) { + jtag_ops->tck_2m(16); + } + + // 地址数据格式共 32bits,其中低 6 位保留,例如地址为 b’00010011(0x13)时,写入的地 + // 址为 b’ 00000000000000000000010011000000,该地址数据遵循 LSB 方式写入,最后一个 bit 跳出 Shift-DR。 + uint32_t addr = (page_index << 6) & 0xFFFFFFC0; + uint8_t addr_bytes[4] = {addr >> 0, addr >> 8, addr >> 16, addr >> 24}; + jtag_shift_dr_safe(addr_bytes, NULL, 32, jtag_ops); + // 在地址传输完毕之后,也需要保持TCK时钟并且等待一段时间 + jtag_ops->tck_2m(16); + + // 开始编程Y-PAGE,固定64个,总数据字节长度为 256 也就是一个 X-PAGE 的大小 + for (int y = 0; y < 64; y++) { + const uint8_t *ypage = &data[y * 4]; + // 数据从 Configuration Data 取高位 4Bytes,在 Shift-DR 写数据时要从最低位开始写入(LSB)。 + uint8_t tx[4] = {ypage[3], ypage[2], ypage[1], ypage[0]}; + jtag_shift_dr_safe(tx, NULL, 32, jtag_ops); + // 每次写完一个 Y-page, GW1N(Z)-2/4/6/9 系列要求 Run-Test 13-15μs,GW1N-2(C)系列要求 Run-Test 30-35μs,其他系列器件不需要 + jtag_ops->tck_2m(dm->timing.y_page_w_wait_us); + } + + // 整个 X-PAGE 编程完成了,按照手册描述: + // GW1N-1(S)器件需要执行 2400μs 时长的时钟,GW1N(Z)-2/4/6/9 系列器件需要执行 6μs 时长的时钟,其他系列器件不需要额外时钟。 + jtag_ops->tck_2m(dm->timing.x_page_w_wait_us); +} + +// 给数据源的头部替换为指定的保留数据,根据官方FAE的描述,可以放心替换,头部有预留字节是给某些配置用的 +// type 为 1 时,替换为 Autoboot-pattern +// type 为 0 时,替换为 Readable-pattern +static void gowin_pattern_replace(uint8_t *data, const uint8_t type) { + // H 工艺器件:Readable-pattern 0x07,0x07,0x30,0x40 + // T 工艺器件:Readable-pattern 0xF7,0xF7,0x3F,0x4F + // 目前两个工艺的器件的 Autoboot-pattern 都是一样的 0x47,0x57,0x31,0x4E + if (type == 1) { + data[0] = 0x47; + data[1] = 0x57; + data[2] = 0x31; + data[3] = 0x4E; + return; + } + if (type == 0) { + const device_map_t *dm = get_device_map_by_idcode(); + if (dm == NULL) return; + if (dm->flash_type == GW_FLASH_TYPE_TSMC) { + data[0] = 0xF7; + data[1] = 0xF7; + data[2] = 0x3F; + data[3] = 0x4F; + } + if (dm->flash_type == GW_FLASH_TYPE_HL) { + data[0] = 0x07; + data[1] = 0x07; + data[2] = 0x30; + data[3] = 0x40; + } + } +} + +gowin_jtag_status_t gowin_jtag_flash_config_write(uint8_t *data, uint32_t data_length, uint32_t *tx_bytes_pos, + uint32_t tx_bytes_total, gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops || !data) return GOWIN_JTAG_ERROR_NULL_POINTER; + if (detected_device == GW_DEVICE_UNKNOWN) return GOWIN_JTAG_ERROR_INVALID_IDCODE; + if (m_flash_xpage_buf == NULL) return GOWIN_JTAG_ERROR_NULL_POINTER; + + // 在xbuf里面已经有缓存的数据的情况下,我们需要先确认本次攒够了一个xpage的大小,才去开工写入xbuf里面的数据 + if (data_length < 256 || m_flash_xpage_pos > 0) { + // 确保新到来的数据加上旧的数据的长度不会溢出,如果溢出的话,那我们就只取一部分写入到xbuf里,让xbuf先满一个page + uint16_t copy_length = data_length; + if (m_flash_xpage_pos + data_length > 256) { + copy_length = 256 - m_flash_xpage_pos; // 计算不会溢出xbuf的可复制数据的长度 + } + memcpy(m_flash_xpage_buf + m_flash_xpage_pos, data, copy_length); + data += copy_length; // 此时我们复制了一部分数据到xbuf里头,外部传进来的剩下的数据的指针要往前移,传入长度也要减去这部分 + data_length -= copy_length; + m_flash_xpage_pos += copy_length; // 复制之后,记录当前xpage的内容长度 + // 如果当前不是最后一包并且数据不够一个xbuf大小,那就得先把数据缓存下来,等足够一个xpage(256字节)了再去传 + if (m_flash_xpage_pos != 256) { + if (*tx_bytes_pos + data_length + m_flash_xpage_pos < tx_bytes_total) { + return GOWIN_JTAG_OK; // 此处直接返回,因为不够一个xbuf大小并且不是最后一包数据,仍需等待传输 + } + // 已经是最后一包了,不够256的话那我们就默认用 0x00 补齐剩下的数据,当作足额给发过去 + memset(m_flash_xpage_buf + m_flash_xpage_pos, 0x00, 256 - m_flash_xpage_pos); + // m_flash_xpage_pos = 256; 为了正确统计xbuf里面的自己数量,此处不要赋值为 256,否则padding的数据也会被计算进去 tx_bytes_pos 里 + } + } + + // 计算当前已传输的字节数量对应到的page位置 + uint32_t page_index = *tx_bytes_pos / 256; + // 处理 Readable-pattern / Autoboot-pattern,我们暂时不加入对 Verify 的支持,自然也就不需要考虑 Readable-pattern + if (page_index == 0) { + gowin_pattern_replace(m_flash_xpage_pos == 0 ? data : m_flash_xpage_buf, 1); + } + + // 完事儿了开始写X-PAGE,我们有两个BUF,一个是256大小的xbuf暂存区,一个是外部传入的数据源, + // 我们优先把xbuf暂存区给发出去(如果里面有数据的话) + if (m_flash_xpage_pos > 0) { + gowin_jtag_flash_config_xpage(m_flash_xpage_buf, page_index, jtag_ops); + *tx_bytes_pos += m_flash_xpage_pos; // 一个x-page传完了就记到总传输的字节数量里,记住,我们此处要加实际有效的字节数量 + page_index++; + m_flash_xpage_pos = 0; // 传完了记得归零xbuf的字节计数 + } + // xbuf传完了以后,还得继续看看外部数据源里有没有完整的x-page的数据,如果有的话,就继续传 + for (uint32_t p = 0; p < data_length / 256; p++) { + gowin_jtag_flash_config_xpage(&data[p * 256], page_index, jtag_ops); + *tx_bytes_pos += 256; // 同上描述 + page_index++; + } + // 如果有剩余数据,那剩余的数据一定是没发出去的,需要等到有完整的一包x-page才能发,所以我们计算余数,将其拷贝到xbuf里面暂存等待下一包 + uint8_t remain_bytes = data_length % 256; // 用u8是安全的,因为不可能有256个字节剩余,直接整除了 + if (remain_bytes > 0) { + memset(&m_flash_xpage_buf[remain_bytes], 0x00, 256 - remain_bytes); // 把后面的无效数据归零 + memcpy(m_flash_xpage_buf, &data[data_length - remain_bytes], remain_bytes); // 复制数据到缓冲区的开头 + m_flash_xpage_pos += remain_bytes; // 记录本次传输剩余的字节数 + // 如果是最后一包了的话,那就直接传过去,不要再缓存了 + if (*tx_bytes_pos + remain_bytes >= tx_bytes_total) { + gowin_jtag_flash_config_xpage(m_flash_xpage_buf, page_index, jtag_ops); + m_flash_xpage_pos = 0; + *tx_bytes_pos += remain_bytes; + } + } + + return GOWIN_JTAG_OK; +} + +gowin_jtag_status_t gowin_jtag_flash_config_finish(gowin_jtag_ops_t *jtag_ops) { + if (!jtag_ops) return GOWIN_JTAG_ERROR_NULL_POINTER; + if (detected_device == GW_DEVICE_UNKNOWN) return GOWIN_JTAG_ERROR_INVALID_IDCODE; + + jtag_shift_ir_safe(INST_CONFIG_DISABLE, jtag_ops); // 发送配置禁用指令 0x3A + gowin_jtag_reprogram(jtag_ops); // 经测试,flash的烧录只要执行 reprogram 就可以让程序开始执行,不需要重置JTAG端口 + + return GOWIN_JTAG_OK; +} + +void gowin_jtag_start_config(gowin_config_ctx_t *cctx) { + // 发起jtag初始化和读取ID + cctx->status = gowin_jtag_init(cctx->jtag_ops); + if (cctx->status == GOWIN_JTAG_OK) { + uint32_t idcode = gowin_jtag_get_idcode(); + const char* name = gowin_jtag_get_device_name(); + if (cctx->jtag_ops->dbg_printf) cctx->jtag_ops->dbg_printf("gowin_jtag OK: idcode = 0x%04lX, name = %s", idcode, name); + // 读取和打印详细的状态表 + gowin_status_reg_t status_reg; + gowin_jtag_read_status_reg(&status_reg, cctx->jtag_ops); + } else { + if (cctx->jtag_ops->dbg_printf) cctx->jtag_ops->dbg_printf("gowin_jtag NOT OK"); + return; + } + + // 初始化启动配置 + if (cctx->is_cfg_sram) { + if (cctx->jtag_ops->dbg_printf) cctx->jtag_ops->dbg_printf("Erase sram started"); + cctx->status = gowin_jtag_sram_config_start(&cctx->tx_pos, cctx->jtag_ops); + if (cctx->status != GOWIN_JTAG_OK) { + if (cctx->jtag_ops->dbg_printf) cctx->jtag_ops->dbg_printf("Failed to start sram config: %d", cctx->status); + return; + } + if (cctx->jtag_ops->dbg_printf) cctx->jtag_ops->dbg_printf("Erase sram done"); + } else { + if (cctx->jtag_ops->dbg_printf) cctx->jtag_ops->dbg_printf("Erase flash started"); + // 暂时只进行非背景升级(会终止FPGA的执行) + cctx->status = gowin_jtag_flash_config_start(cctx->x_page_buf, &cctx->tx_pos, false, cctx->jtag_ops); + if (cctx->status != GOWIN_JTAG_OK) { + if (cctx->jtag_ops->dbg_printf) cctx->jtag_ops->dbg_printf("Failed to start flash config: %d", cctx->status); + return; + } + if (cctx->jtag_ops->dbg_printf) cctx->jtag_ops->dbg_printf("Erase flash done"); + } + + // 打印个消息告知一下启动完成了 + if (cctx->jtag_ops->dbg_printf) { + cctx->jtag_ops->dbg_printf("gowin_jtag %s config started: %d", cctx->is_cfg_sram ? "sram" : "flash" , cctx->status); + } +} + +void gowin_jtag_config_write(uint8_t *data, uint32_t data_length, gowin_config_ctx_t *cctx) { + // 根据当前的配置类型,选择性调用对应的逻辑 + if (cctx->is_cfg_sram) { + cctx->status = gowin_jtag_sram_config_write(data, data_length, &cctx->tx_pos, cctx->tx_total, cctx->jtag_ops); + } else { + cctx->status = gowin_jtag_flash_config_write(data, data_length, &cctx->tx_pos, cctx->tx_total, cctx->jtag_ops); + } +} + +void gowin_jtag_stop_config(gowin_config_ctx_t *cctx) { + // 根据当前烧录模式的不同选择不同的收尾 + if (cctx->is_cfg_sram) { + cctx->status = gowin_jtag_sram_config_finish(cctx->jtag_ops); + } else { + cctx->status = gowin_jtag_flash_config_finish(cctx->jtag_ops); + } + gowin_jtag_deinit(cctx->jtag_ops); // 反初始化gowinjtag库,退出某些状态并且释放某些资源 +} diff --git a/common_arm/fpga/fpga_gw_jtag.h b/common_arm/fpga/fpga_gw_jtag.h new file mode 100644 index 000000000..bac2cbb53 --- /dev/null +++ b/common_arm/fpga/fpga_gw_jtag.h @@ -0,0 +1,210 @@ +/* +* GOWIN fpga JTAG software implement + * + * @Author DXL + * MIT license + */ +#ifndef GOWIN_JTAG_H_ +#define GOWIN_JTAG_H_ + +#include +#include + +// 是否将调试打印信息编译进当前模块中 +#define DEBUG_GW_JTAG 1 + +typedef enum { + GOWIN_JTAG_OK = 0U, + GOWIN_JTAG_ERROR_INVALID_IDCODE, + GOWIN_JTAG_ERROR_NULL_POINTER, + GOWIN_JTAG_ERROR_OUT_OF_RANGE, + GOWIN_JTAG_ERROR_POR_STATUS, + GOWIN_JTAG_ERROR_VLD_STATUS, + GOWIN_JTAG_ERROR_ERASE_FAIL, + GOWIN_JTAG_ERROR_ENABLE_CFG, +} gowin_jtag_status_t; + +typedef enum { + GW_DEVICE_UNKNOWN = 0, + GW_DEVICE_GW1N_1, + GW_DEVICE_GW1N_1S, + GW_DEVICE_GW1NZ_1, + GW_DEVICE_GW1N_R_Z_2_2B_2C, + GW_DEVICE_GW1N_1P5_1P5B_1P5C, + GW_DEVICE_GW1N_R_4, + GW_DEVICE_GW1N_R_4B, + GW_DEVICE_GW1N_R_4D, + GW_DEVICE_GW1NS_4, + GW_DEVICE_GW1NS_ER_4C, + GW_DEVICE_GW1N_R_9, + GW_DEVICE_GW1N_R_9C, + GW_DEVICE_GW2A_R_18_18C, + GW_DEVICE_GW2A_55_55C, +} gowin_device_t; + +typedef enum { + GW_FLASH_TYPE_UNKNOWN = 0U, // 未知工艺? + GW_FLASH_TYPE_TSMC, // T 工艺 + GW_FLASH_TYPE_HL, // H 工艺 + GW_FLASH_TYPE_SMIC, // SMIC 工艺有 ID_GW1NS_2 和 ID_GW1NS_2C,但是我们暂时不打算对接 + GW_FLASH_TYPE_SPI_FLASH, // 核封了一颗SPI-FLASH或者是只支持外部FLASH +} gowin_flash_type_t; + +typedef struct { + /** + * 设置TCK电平状态,true为高,false为低。 + */ + void (*set_tck)(bool level); + /** + * 设置TMS电平状态,true为高,false为低。 + */ + void (*set_tms)(bool level); + /** + * 设置TDI电平状态,true为高,false为低。 + */ + void (*set_tdi)(bool level); + /** + * 获取TDO电平状态,true为高,false为低。 + */ + bool (*get_tdo)(void); + /** + * 微秒延迟,不要求太高精度,在 tck_pulse 未实现时,此延时接口作为一个后备方案提供大概500kHZ的TCK时钟输出 + */ + void (*delay_us)(int us); + /** + * 毫秒延迟,不要求太高精度 + */ + void (*delay_ms)(int ms); + /** + * 产生 2mhz 的tck时钟,持续指定的us时长,如果传入参数为0,则只产生一个时钟周期的tck波形 + * 也就是拉低tck持续半周期 250ns,然后拉高tck持续半周期 250ns + * 注意:实际精度不能低于 1.8mhz 和高于 2.2mhz,也就是正负200K的精度都在可接受范围内 + */ + void (*tck_2m)(uint32_t us); + /** + * 可选的实现,如果不实现,则不会输出任何调试信息,并且你可选将当前模块的所有打印信息编译进模块中 + * 如果你是在资源紧张的平台,则可以通过 DEBUG_GW_JTAG 去除当前模块的所有调试信息 + */ + void (*dbg_printf)(const char *fmt, ...); + /** + * 可选的实现,如果复用了JTAG脚为普通IO,则需要在烧录之前,拉低 JTAGSEL_N 引脚 + */ + void (*set_jtagsel)(bool level); +} gowin_jtag_ops_t; + +/** + * @brief Gowin FPGA Device Status Register (32-bit) + * + * Reference: + * - 表7-12: GW1N(R)-(1/4B/4C/4D)/GW1NRF-4B 系列 + * - 表7-13: GW1N(R)-(1P5/2/6/9/9C)/GW1NS-4(4C)/GW1NSR-4(4C)/GW1NSE-4C/GW1NSER-4C/GW1NZ-(1/2) 系列 + * + * Note: + * - Bit 编号从 LSB (bit 0) 到 MSB (bit 31) + * - 某些位在不同系列中含义一致,部分位仅在特定系列存在(见注释) + */ +typedef union { + uint32_t raw; + struct { + /* Bit 0 */ + uint32_t crc_error : 1; ///< CRC Error Flag (1=发生错误, 0=正常). 所有系列通用. + /* Bit 1 */ + uint32_t bad_command_error : 1; ///< Bad Command Error Flag (1=发生错误). 所有系列通用. + /* Bit 2 */ + uint32_t id_verify_failed : 1; ///< ID Verify Failed Error Flag (1=ID校验失败). 所有系列通用. + /* Bit 3 */ + uint32_t timeout_error : 1; ///< Timeout Error Flag (1=超时错误). 所有系列通用. + /* Bit 4 */ + uint32_t reserved_4 : 1; ///< 保留位,固定为0. + /* Bit 5 */ + uint32_t memory_erase : 1; ///< Memory Erase 标志. 所有系列通用. + /* Bit 6 */ + uint32_t preamble : 1; ///< Preamble 标志. 所有系列通用. + /* Bit 7 */ + uint32_t edit_mode : 1; ///< Edit Mode 标志. 所有系列通用. + /* Bit 8 */ + uint32_t program_spi_directly : 1; ///< Program SPI Directly 标志. 所有系列通用. + /* Bit 9 */ + uint32_t autoboot_state : 1; ///< AutoBoot State. + ///< - 表7-13: 存在此字段(用于支持AutoBoot的型号) + ///< - 表7-12: 此位为0(即不支持AutoBoot的型号如GW1N-1/4B等) + /* Bit 10 */ + uint32_t non_jtag_active : 1; ///< Non-JTAG Active 标志(例如MSPI/SSPI配置中激活). 所有系列通用. + /* Bit 11 */ + uint32_t bypass_state : 1; ///< Bypass State 标志. 所有系列通用. + /* Bit 12 */ + uint32_t vld : 1; ///< Gowin VLD (1=正常, 0=异常). 内置Flash相关参数. + ///< 适用于所有带内置Flash的型号(如GW1NS、GW1NZ等). + /* Bit 13 */ + uint32_t done_final : 1; ///< Done Final (1=配置成功完成, 0=失败). 所有系列通用. + /* Bit 14 */ + uint32_t security_final : 1; ///< Security Final (1=已设置安全位, 0=未设置). 所有系列通用. + /* Bit 15 */ + uint32_t ready : 1; ///< Ready (1=正常, 0=异常). 所有系列通用. + /* Bit 16 */ + uint32_t por : 1; ///< POR (Power-On Reset) 状态 (1=正常, 0=异常). 所有系列通用. + /* Bit 17 */ + uint32_t flash_lock : 1; ///< Flash Lock 标志: + ///< - 1 = Flash锁定(禁止回读,但允许擦除) + ///< - 仅存在于表7-13所列型号(即带内置Flash的系列,如GW1NS/GW1NZ/GW1NSE等) + ///< - 表7-12型号(如GW1N-1/4B)此位为0(无Flash Lock功能) + /* Bits 18–31 */ + uint32_t reserved_18_31 : 14; ///< 保留位,固定为0(两表均明确说明). + } bits; +} gowin_status_reg_t; + +/** + * Information required during the configuration process + */ +typedef struct { + uint8_t x_page_buf[256]; // Buffer for incomplete data of x-page + uint32_t tx_pos; // Current position of data to be sent, in bytes + uint32_t tx_total; // Total size of the data to be sent, in bytes + bool is_cfg_sram; // true: config sram, false: config flash + gowin_jtag_ops_t *jtag_ops; // Pointer to the JTAG operations structure, used for functions during configuration + gowin_jtag_status_t status; // Status of the current configuration process, used to track errors of platform +} gowin_config_ctx_t; + +gowin_jtag_status_t gowin_jtag_init(gowin_jtag_ops_t *ops); +void gowin_jtag_deinit(gowin_jtag_ops_t *jtag_ops); + +gowin_device_t gowin_jtag_get_device_type(void); +const char *gowin_jtag_get_device_name(void); +gowin_flash_type_t gowin_get_flash_type(void); +uint32_t gowin_jtag_get_idcode(void); + +void gowin_jtag_reset(gowin_jtag_ops_t *jtag_ops); +uint32_t gowin_jtag_read_status(gowin_jtag_ops_t *jtag_ops); +uint32_t gowin_jtag_read_usercode(gowin_jtag_ops_t *jtag_ops); +void gowin_jtag_reprogram(gowin_jtag_ops_t *jtag_ops); +gowin_jtag_status_t gowin_jtag_read_status_reg(gowin_status_reg_t *reg_out, gowin_jtag_ops_t *jtag_ops); + +gowin_jtag_status_t gowin_jtag_sram_erase(gowin_jtag_ops_t *jtag_ops); +gowin_jtag_status_t gowin_jtag_sram_config_start(uint32_t *tx_bits_pos, gowin_jtag_ops_t *jtag_ops); +gowin_jtag_status_t gowin_jtag_sram_config_write( + uint8_t *data, + uint32_t data_length, + uint32_t *tx_bytes_pos, + uint32_t tx_bytes_total, + gowin_jtag_ops_t *jtag_ops); +gowin_jtag_status_t gowin_jtag_sram_config_finish(gowin_jtag_ops_t *jtag_ops); + +gowin_jtag_status_t gowin_jtag_flash_erase(gowin_jtag_ops_t *jtag_ops); +gowin_jtag_status_t gowin_jtag_flash_config_start( + uint8_t *xbuf_256, + uint32_t *tx_bits_pos, + bool bg_update, + gowin_jtag_ops_t *jtag_ops); +gowin_jtag_status_t gowin_jtag_flash_config_write( + uint8_t *data, + uint32_t data_length, + uint32_t *tx_bytes_pos, + uint32_t tx_bytes_total, + gowin_jtag_ops_t *jtag_ops); +gowin_jtag_status_t gowin_jtag_flash_config_finish(gowin_jtag_ops_t *jtag_ops); + +void gowin_jtag_start_config(gowin_config_ctx_t *cctx); +void gowin_jtag_config_write(uint8_t *data, uint32_t data_length, gowin_config_ctx_t *cctx); +void gowin_jtag_stop_config(gowin_config_ctx_t *cctx); + +#endif // GOWIN_JTAG_H_ diff --git a/common_arm/fpga/fpga_hw_at32.c b/common_arm/fpga/fpga_hw_at32.c new file mode 100644 index 000000000..92b2b2baf --- /dev/null +++ b/common_arm/fpga/fpga_hw_at32.c @@ -0,0 +1,439 @@ +#include "gpio_hw_at32.h" +#include "fpga_apis.h" +#include "fpga_gw_jtag.h" +#include "ticks_apis.h" +#include "gpio_apis.h" +#include "dbprint.h" +#include "pm3_cmd.h" +#include "string.h" + +uint16_t g_ssc_dma_rx_count; +uint8_t g_ssc_data_byte_width; +bool g_tx_lsb_first; + +void FpgaSetup24MHzClk(void) { + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + // gpio clk enable + crm_periph_clock_enable(AT32_GPIO_PERIPH_FPGA_24M_CLK, TRUE); // ARM2FPGA_PCK0 = PA8_CRM_CLKO1 + // clkout gpio init + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_pins = AT32_GPIO_FPGA_24M_CLK_PIN; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init(AT32_GPIO_FPGA_24M_CLK, &gpio_init_struct); + // config clkout division, 288/3/4=24mhz + crm_clkout_div_set(CRM_CLKOUT_INDEX_1, CRM_CLKOUT_DIV1_3, CRM_CLKOUT_DIV2_4); + crm_clock_out1_set(CRM_CLKOUT1_PLL); // config clkout1 clock + + /* 48m pll -> 24m clkout + gpio_init_type gpio_init_struct; + // enable periph clock + crm_periph_clock_enable(AT32_GPIO_PERIPH_FPGA_24M_CLK, TRUE); // ARM2FPGA_PCK0 = PA8_CRM_CLKO1 + // set default parameter + gpio_default_para_init(&gpio_init_struct); + // config gpio mux function + gpio_pin_mux_config(AT32_GPIO_FPGA_24M_CLK, GPIO_PINS_SOURCE8, GPIO_MUX_0); + // config gpio + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_pins = AT32_GPIO_FPGA_24M_CLK_PIN; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init(AT32_GPIO_FPGA_24M_CLK, &gpio_init_struct); + // config clkout1 output clock source + crm_clock_out1_set(CRM_CLKOUT1_PLL); + // config clkout1 div + crm_clkout_div_set(CRM_CLKOUT_INDEX_1, CRM_CLKOUT_DIV1_2, CRM_CLKOUT_DIV2_1); + */ +} + +// gpio for spi-timode init +static void spi_ssc_gpio_setup(void) { + gpio_init_type gpio_initstructure; + + crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE); + + // PB9_SPI4_MOSI = fpga -> arm + // PB8_SPI4_MISO = arm -> fpga + // PB7_SPI4_SCK = clk + // PB6_SPI4_CS = frame + + gpio_default_para_init(&gpio_initstructure); + gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_initstructure.gpio_pull = GPIO_PULL_DOWN; + gpio_initstructure.gpio_mode = GPIO_MODE_MUX; + + /* cs pin -> frame pin */ + gpio_initstructure.gpio_pull = GPIO_PULL_DOWN; + gpio_initstructure.gpio_pins = AT32_GPIO_SSC_FRAME_PIN; + gpio_init(AT32_GPIO_SSC_FRAME, &gpio_initstructure); + gpio_pin_mux_config(AT32_GPIO_SSC_FRAME, AT32_GPIO_SSC_FRAME_SOURCE, AT32_GPIO_SSC_FRAME_MUX); + + /* sck pin -> clk pin */ + gpio_initstructure.gpio_pull = GPIO_PULL_DOWN; + gpio_initstructure.gpio_pins = AT32_GPIO_SSC_CLK_PIN; + gpio_init(AT32_GPIO_SSC_CLK, &gpio_initstructure); + gpio_pin_mux_config(AT32_GPIO_SSC_CLK, AT32_GPIO_SSC_CLK_SOURCE, AT32_GPIO_SSC_CLK_MUX); + + /** + * miso pin -> SSC_DOUT + * --- + * SPI is configured in TI mode and ARM is the slave. + * In this case, according to the document description: + * "If the slave still does not detect a valid CS pulse when receiving the last bit of the current data frame, + * then after 1/2T SCK+3T PCLK, the output function of MISO will be turned off to control MISO floating. ”, + * If we do not perform weak pull-down, it will cause DOUT to be in an uncontrollable state, + * and some modules will use this pin for RF field modulation. + * --- + * It is best to configure it as a weak pull-down, + * otherwise the lf_init function of the lfadc.c module will collect the voltage value of the modulated field due to default pull-up. + * --- + * What would happen if gpio_pull is GPIO_PULL_UP? + * 1. call the FpgaSetupSsc to setup spi-timode + * ssc_dout pin will be controlled by SPI + * 2. call the gpio_fpga_mod_only_setup() to steal the dout pin for modulation + * and call Gpio_SSC_DOUT_Low() + * 3. call the adc read value by fpga immediately, you will get a wrong adc value, + * because it has been always modulation and no time to wait stable. + */ + gpio_initstructure.gpio_pull = GPIO_PULL_DOWN; // So, make sure the dout pin to be GPIO_PULL_DOWN is a good idea. + gpio_initstructure.gpio_pins = AT32_GPIO_SSC_DOUT_PIN; + gpio_init(AT32_GPIO_SSC_DOUT, &gpio_initstructure); + gpio_pin_mux_config(AT32_GPIO_SSC_DOUT, AT32_GPIO_SSC_DOUT_SOURCE, AT32_GPIO_SSC_DOUT_MUX); + + /* mosi pin -> SSC_DIN */ + gpio_initstructure.gpio_pull = GPIO_PULL_UP; + gpio_initstructure.gpio_pins = AT32_GPIO_SSC_DIN_PIN; + gpio_init(AT32_GPIO_SSC_DIN, &gpio_initstructure); + gpio_pin_mux_config(AT32_GPIO_SSC_DIN, AT32_GPIO_SSC_DIN_SOURCE, AT32_GPIO_SSC_DIN_MUX); +} + +void FpgaSetupSsc(uint16_t fpga_mode) { + spi_init_type spi_init_struct; + + crm_periph_clock_enable(SPI_CRM_CLOCK_SSC, TRUE); + spi_ssc_gpio_setup(); + + spi_default_para_init(&spi_init_struct); + spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX; + spi_init_struct.master_slave_mode = SPI_MODE_SLAVE; // 配置为从机模式,数据传输的时钟由fpga提供 + spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_8; + spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB; // msb always default + g_tx_lsb_first = false; // msb always default + // 8 or 16 bits data for current fpga mode. + if (FpgaIs16BitMsbMode(fpga_mode)) { + spi_init_struct.frame_bit_num = SPI_FRAME_16BIT; + g_ssc_data_byte_width = 2; + } else { + spi_init_struct.frame_bit_num = SPI_FRAME_8BIT; + g_ssc_data_byte_width = 1; + } + // The setting in clock_polarity/clock_phase/cs_mode_selection invalid for ti-mode + // spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW; + // spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE; + // spi_init_struct.cs_mode_selection = SPI_CS_HARDWARE_MODE; + // spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE; + spi_i2s_reset(SPI_SSC); // full reset spi-ti_mode + spi_init(SPI_SSC, &spi_init_struct); + spi_ti_mode_enable(SPI_SSC, TRUE); // enable ti mode(Somewhat similar to SSC of AT91) + spi_i2s_dma_receiver_enable(SPI_SSC,TRUE); // RX DMA enabled. + spi_enable(SPI_SSC, TRUE); +} + +void FpgaUpdateFrameMode(uint8_t bits, bool rx_msb, bool tx_msb) { + // Update the data width + g_ssc_data_byte_width = bits / 8; + // spi_frame_bit_num_set(SPI_SSC, SPI_FRAME_8BIT); + SPI_SSC->ctrl1_bit.fbn = g_ssc_data_byte_width - 1; // SPI_FRAME_8BIT = 0, SPI_FRAME_16BIT = 1 + // The AT32 encapsulation library does not provide a function to update the LFT register. + SPI_SSC->ctrl1_bit.ltf = rx_msb ? SPI_FIRST_BIT_MSB : SPI_FIRST_BIT_LSB; // SPI_FIRST_BIT_MSB = 0, SPI_FIRST_BIT_LSB = 1 + // Is the order of bits for tx and rx different? + g_tx_lsb_first = tx_msb == false; + // modify data width & bits order don't need spi disable. +} + +bool FpgaSetupSscRxDmaRepeat(void *buf, uint16_t len) { + // FpgaSetupSscRxDmaRepeat() 函数是替代原先的 FpgaSetupSscDma 的操作 + // 而 FpgaSetupSscRxDmaSingle() 函数是即将要实现的新的函数,作用是只设置主缓冲,对于AT91来说,就是下一buf不会被设置,避免覆盖数据 + // 对于at32来说,两者功能是一致的,所以 FpgaSetupSscRxDmaRepeat 内部直接封装调用 FpgaSetupSscRxDmaSingle 即可,两者功能是一致的。 + return FpgaSetupSscRxDmaSingle(buf, len); +} + +bool FpgaSetupSscRxDmaSingle(void *buf, uint16_t len) { + dma_init_type dma_init_struct; + + if (buf == NULL) { + return false; + } + + g_ssc_dma_rx_count = len; // Be sure to save the length value to this variable. + + crm_periph_clock_enable(DMA_CRM_CLOCK_SSC, TRUE); + dmamux_enable(DMA_SSC, TRUE); + + dma_reset(DMA_CHANNEL_SSC); + dma_default_para_init(&dma_init_struct); + dma_init_struct.buffer_size = len; + dma_init_struct.memory_inc_enable = TRUE; // address of buffer in memory need increment. + dma_init_struct.peripheral_inc_enable = FALSE; // peripheral data register is fixed. + // 我们在初始化DMA的时候,需要指定数据的宽度值但此函数是不具备宽度参数的,需要从SSC(SPI-TIMODE) 中了解到当前选择的数据宽度,然后做出映射。 + dma_init_struct.memory_data_width = g_ssc_data_byte_width == 1 ? DMA_MEMORY_DATA_WIDTH_BYTE : DMA_MEMORY_DATA_WIDTH_HALFWORD; + dma_init_struct.peripheral_data_width = g_ssc_data_byte_width == 1 ? DMA_PERIPHERAL_DATA_WIDTH_BYTE : DMA_PERIPHERAL_DATA_WIDTH_HALFWORD; + dma_init_struct.priority = DMA_PRIORITY_HIGH; + dma_init_struct.loop_mode_enable = FALSE; // loop disabled, only one time running. + dma_init_struct.memory_base_addr = (uint32_t)buf; + dma_init_struct.peripheral_base_addr = (uint32_t)&(SPI_SSC->dt); + dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY; // receive data from SPI-TI_MODE(SSC) + dma_init(DMA_CHANNEL_SSC, &dma_init_struct); + dmamux_init(DMA_CHANNEL_MUX_SSC, DMA_MUX_REQ_ID_SSC); + + if (FPGA_SSC_RX_Ready()) { + ((uint8_t*)buf)[0] = FPGA_SSC_RX_Value(); // Readout and discard old byte. It's important! + } + + FPGA_SSC_DMA_RX_Enable(); // Start rx channel + + return true; +} + +// gpio for spi-cmd init +static void spi_cmd_gpio_setup(void) { + gpio_init_type gpio_initstructure; + + AT32_GPIO_PERIPH_CLKS_ENABLE(AT32_GPIO_PERIPH_SPI_CLK); + + // init gpio structure + gpio_default_para_init(&gpio_initstructure); + gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_initstructure.gpio_pull = GPIO_PULL_NONE; + gpio_initstructure.gpio_mode = GPIO_MODE_MUX; + + /* sck pin */ + gpio_initstructure.gpio_pull = GPIO_PULL_NONE; + gpio_initstructure.gpio_pins = AT32_GPIO_SPI_SCK_PIN; + gpio_init(AT32_GPIO_SPI_SCK, &gpio_initstructure); + gpio_pin_mux_config(AT32_GPIO_SPI_SCK, AT32_GPIO_SPI_SCK_SOURCE, AT32_GPIO_SPI_SCK_MUX); + + /* miso pin */ + gpio_initstructure.gpio_pull = GPIO_PULL_NONE; + gpio_initstructure.gpio_pins = AT32_GPIO_SPI_MISO_PIN; + gpio_init(AT32_GPIO_SPI_MISO, &gpio_initstructure); + gpio_pin_mux_config(AT32_GPIO_SPI_MISO, AT32_GPIO_SPI_MISO_SOURCE, AT32_GPIO_SPI_MISO_MUX); + + /* mosi pin */ + gpio_initstructure.gpio_pull = GPIO_PULL_NONE; + gpio_initstructure.gpio_pins = AT32_GPIO_SPI_MOSI_PIN; + gpio_init(AT32_GPIO_SPI_MOSI, &gpio_initstructure); + gpio_pin_mux_config(AT32_GPIO_SPI_MOSI, AT32_GPIO_SPI_MOSI_SOURCE, AT32_GPIO_SPI_MOSI_MUX); + + // cs software + gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_initstructure.gpio_pull = GPIO_PULL_NONE; + gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_initstructure.gpio_pins = AT32_GPIO_SPI_CS_PIN; + gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT; + gpio_init(AT32_GPIO_SPI_CS, &gpio_initstructure); + gpio_bits_set(AT32_GPIO_SPI_CS, AT32_GPIO_SPI_CS_PIN); // default CS set to high for deselect +} + +static void spi_cmd_setup(void) { + spi_init_type spi_init_struct; + + // master spi initialization + crm_periph_clock_enable(SPI_CRM_CLOCK_CMD, TRUE); + spi_cmd_gpio_setup(); + + spi_default_para_init(&spi_init_struct); + + spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX; + spi_init_struct.master_slave_mode = SPI_MODE_MASTER; // arm master, fpga slave + spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_4; // 144MHZ / 4 == 36MHZ(48MHZ MAX) + spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB; + spi_init_struct.frame_bit_num = SPI_FRAME_16BIT; + spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW; + spi_init_struct.clock_phase = SPI_CLOCK_PHASE_1EDGE; + spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE; + spi_init(SPI_CMD, &spi_init_struct); + spi_enable(SPI_CMD, TRUE); +} + +void FpgaSendCommand(uint16_t cmd, uint16_t v) { + // Init spi + spi_cmd_setup(); + // Send data + gpio_bits_reset(AT32_GPIO_SPI_CS, AT32_GPIO_SPI_CS_PIN); // CS LOW + while(spi_i2s_flag_get(SPI_CMD, SPI_I2S_TDBE_FLAG) == RESET) {} + spi_i2s_data_transmit(SPI_CMD, cmd | v); + while(spi_i2s_flag_get(SPI_CMD, SPI_I2S_BF_FLAG) != RESET) {} // Waiting for SPI transmit finish. + gpio_bits_set(AT32_GPIO_SPI_CS, AT32_GPIO_SPI_CS_PIN); // CS HIGH +} + +void Fpga_print_status(void) { + DbpString(_CYAN_("Current FPGA image")); + Dbprintf(" mode.................... All-In-One"); +} + +static void set_tck(bool level) { + if (level) { + GPIOC->scr = GPIO_PINS_10; + } else { + GPIOC->clr = GPIO_PINS_10; + } +} + +static void set_tms(bool level) { + if (level) { + GPIOA->scr = GPIO_PINS_15; + } else { + GPIOA->clr = GPIO_PINS_15; + } +} + +static void set_tdi(bool level) { + if (level) { + GPIOC->scr = GPIO_PINS_12; + } else { + GPIOC->clr = GPIO_PINS_12; + } +} + +static bool get_tdo(void) { + return GpioInputStatus(GPIOC, GPIO_PINS_11); +} + +static void set_jtagsel(bool level) { + if (level) { + GPIOD->scr = GPIO_PINS_2; + } else { + GPIOD->clr = GPIO_PINS_2; + } +} + +// tck输出2mhz的时钟,实测2.01mhz左右,理论上可以稳定使用此方法,只要最终实现的误差在 ±200khz 都没问题 +// 一般只会更慢,不会更快,因为考虑到MCU的架构,主频,编译优化等级之类的,因此最终量产使用前还是得通过示波器测量实际输出频率 +static void tck_2mhz(uint32_t us) { + // 2MHz => period = 500ns, half = 250ns + // SysTick = 36MHz => 1 tick = 27.78ns + // 250ns / 27.78ns ≈ 9 ticks => LOAD = 8 (because 8+1=9) + // 8 - 1 = 7, because reserve one SysTick cycle (27.78ns) for loop, IO register operations, and SysTick operations. + const uint32_t HALF_PERIOD_TICKS = 7; // for 250ns at 36MHz + uint32_t cycles = us * 2; // each us has 2 half-cycles at 2MHz + + // If 'us' is zero, the cycles will also be 0. So we need pulse only one time. + if (cycles == 0) { + cycles = 1; + } + + // Configure SysTick: use AHB/8 = 36MHz + SysTick->CTRL = 0; // CLKSOURCE=0 => AHB/8 (if available), no interrupt, disable + SysTick->LOAD = HALF_PERIOD_TICKS; + SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; + + // 等待N个周期,因为我们是2mhz左右的频率,所以最终一次tck脉冲就是500ns,脉冲时间*2就差不多是实际要等待的us时长 + // 实际上,考虑到如果说执行速度比较慢的情况下,那么可能最终输出的频率达不到2mhz,此时等待的时间只会更长,对于gowin的要求来说,是允许的 + // 因为高云要求的是持续产生tck时钟多少毫秒,是为了正常驱动flash的擦除过程,一般来说只能长,不能短。 + while (cycles--) { + GPIOC->clr = GPIO_PINS_10; // low + + SysTick->VAL = HALF_PERIOD_TICKS; + while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0) {} + + GPIOC->scr = GPIO_PINS_10; // high + + SysTick->VAL = HALF_PERIOD_TICKS; + while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0) {} + } +} + +// 定义fpga的jtag实现 +static gowin_jtag_ops_t gjo = { + .delay_ms = SpinDelay, + .delay_us = SpinDelayUs, + .get_tdo = get_tdo, + .set_tck = set_tck, + .set_tms = set_tms, + .set_tdi = set_tdi, + .tck_2m = tck_2mhz, + // .dbg_print = Dbprintf, // For debug to print some msg. + .set_jtagsel = set_jtagsel, +}; + +// 定义fpga的jtag配置信息 +static gowin_config_ctx_t gci = { + .tx_pos = 0, + .tx_total = 0, + .is_cfg_sram = false, + .jtag_ops = &gjo, +}; + +int FpgaStartConfig(bool configSram, uint32_t fileLength) { + + // TODO DXL: Check the file length is valid in this platform? + // if not, return the PM3_EOVFLOW + + // Init jtag hardware link. + gpio_fpga_download_setup(); + + gjo.dbg_printf = Dbprintf; // Debug start + + // Reset for restart a new transfer + gci.tx_pos = 0; + gci.tx_total = fileLength; + gci.is_cfg_sram = configSram; // 标记当前正在配置sram而非flash + + gowin_jtag_start_config(&gci); + if (gci.status != GOWIN_JTAG_OK) { + return PM3_EFAILED; + } + + gjo.dbg_printf = NULL; // Debug stop + + return PM3_SUCCESS; +} + +int FpgaConfigWrite(uint8_t *data, uint32_t data_length) { + + // TODO DXL: Check the data_length length is valid in this platform? + // if not, return the PM3_EOVFLOW + + gowin_jtag_config_write(data, data_length, &gci); + if (gci.status != GOWIN_JTAG_OK) { + return PM3_EFAILED; + } + return PM3_SUCCESS; +} + +int FpgaStopConfig(void) { + gowin_jtag_stop_config(&gci); + if (gci.status != GOWIN_JTAG_OK) { + return PM3_EFAILED; + } + return PM3_SUCCESS; +} + +uint32_t FpgaConfigPlatformStatus(void) { + return gci.status; +} + +void FpgaResetComInterface(void) { + spi_i2s_reset(SPI_SSC); // full reset spi + spi_i2s_reset(SPI_CMD); // full reset spi + crm_periph_clock_enable(SPI_CRM_CLOCK_SSC, FALSE); + crm_periph_clock_enable(SPI_CRM_CLOCK_CMD, FALSE); + // Do not reset GPIO, or disable GPIO clock, as other functions may depend on GPIO. + + // Init JTAG link of FPGA to waiting for fpga work status check. + gpio_fpga_download_setup(); + while (1) { + gowin_jtag_status_t status = gowin_jtag_init(&gjo); + if (status == GOWIN_JTAG_OK) { + break; + } + SpinDelay(100); // Wait for 100ms before retrying + Gpio_LED_B_Inv(); // Show some indication that we are retrying to init JTAG link, which means waiting for FPGA to be ready. + } + gowin_jtag_deinit(&gjo); +} diff --git a/common_arm/fpga/fpga_hw_at32.h b/common_arm/fpga/fpga_hw_at32.h new file mode 100644 index 000000000..78a83fec0 --- /dev/null +++ b/common_arm/fpga/fpga_hw_at32.h @@ -0,0 +1,141 @@ +#ifndef _FPGA_HW_AT32_H_ +#define _FPGA_HW_AT32_H_ + +#include "common.h" +#include "at32f435_437_spi.h" +#include "at32f435_437_dma.h" +#include "fpga_gw_jtag.h" + +// The DMA memory address of AT32 does not self increment, +// and there are no useful registers to know the initial set count value, +// so we can only use one variable to store the set count value. +extern uint16_t g_ssc_dma_rx_count; +// Save the data width in bytes required for the fpga_mode parameter passed by the FpgaSetupSsc function. +extern uint8_t g_ssc_data_byte_width; +// Is tx lsb first? If diff with rx frame settings, the data will reverse before send. +extern bool g_tx_lsb_first; + +// TODO DXL 纠正SPI和DMA通道选择,为了方便修改,此处可先暂时定义SPI和DMA外设和DMA通道的对应宏 +// spi-ti_mode 用到了 SPI4, DMA1 +// spi-cmd 用到了 SPI3,无DMA +#define SPI_SSC SPI4 +#define SPI_CRM_CLOCK_SSC CRM_SPI4_PERIPH_CLOCK +#define DMA_SSC DMA1 +#define DMA_CHANNEL_SSC DMA1_CHANNEL1 +#define DMA_CRM_CLOCK_SSC CRM_DMA1_PERIPH_CLOCK +#define DMA_CHANNEL_MUX_SSC DMA1MUX_CHANNEL1 +#define DMA_MUX_REQ_ID_SSC DMAMUX_DMAREQ_ID_SPI4_RX +#define DMA_SSC_RX_DONE_FLAG DMA1_FDT1_FLAG // If the channel is changed, this FLAG also needs to be modified. +#define SPI_CMD SPI3 +#define SPI_CRM_CLOCK_CMD CRM_SPI3_PERIPH_CLOCK + +STATIC_FORCE_INLINE bool FPGA_SSC_RX_Ready(void) { + /* + * Note that according to the manual description, if SPI receives data but does not read it after startup, + * the SPI peripheral will generate an overflow interrupt and no longer receive new data. At this time, + * the RXRDY flag will remain set. If we read and use this data, we may obtain an incorrect result, + * resulting in decoding failure. + */ + + // When the following conditions are met, we can consider the data to have been effectively received. + // 1. spi_i2s_flag_get(SPI_SSC, SPI_I2S_RDBF_FLAG) == SET + // 2. spi_i2s_flag_get(SPI_SSC, SPI_I2S_ROERR_FLAG) == RESET + // Easy understand: Not overflow error and data buffer is full, when sts & 0x41 == 0x01, ROERR == 0. + // --- + // Reading SPI_DT register and SPI_STS register sequentially can clear ROERR(Must to read DT reg) + // Only when the ROERR flag is set, it is necessary to read DT, so the '&&' condition is very important. + // If the former does not hold, the DT register will not be read. + return ((SPI_SSC->sts & (SPI_I2S_RDBF_FLAG | SPI_I2S_ROERR_FLAG)) == SPI_I2S_RDBF_FLAG) + || (((SPI_SSC->sts & SPI_I2S_ROERR_FLAG) == SPI_I2S_ROERR_FLAG) && (SPI_SSC->dt & 0)); // Readout data for clear the ROERR flag. IMPORTANT! +} + +STATIC_FORCE_INLINE bool FPGA_SSC_TX_Ready(void) { + // spi_i2s_flag_get(SPI_SSC, SPI_I2S_TDBE_FLAG) == SET + return (SPI_SSC->sts & SPI_I2S_TDBE_FLAG) == SPI_I2S_TDBE_FLAG; +} + +STATIC_FORCE_INLINE bool FPGA_SSC_TX_Done(void) { + // spi_i2s_flag_get(SPI_SSC, SPI_I2S_BF_FLAG) == RESET + return (SPI_SSC->sts & SPI_I2S_BF_FLAG) != SPI_I2S_BF_FLAG; // SPI currently has no transmission transactions. +} + +STATIC_FORCE_INLINE uint32_t FPGA_SSC_RX_Value(void) { + // spi_i2s_data_receive(SPI_SSC) + return (uint16_t)SPI_SSC->dt; +} + +STATIC_FORCE_INLINE void FPGA_SSC_TX_Value(uint32_t v) { + // 'SPI_SSC->dt' is from 'spi_i2s_data_transmit()' + if (SPI_SSC->ctrl1_bit.ltf == g_tx_lsb_first) { + SPI_SSC->dt = (uint16_t)v; // The order of bits for tx and rx is the same, so we can send them directly. + } else { // Is different between tx&rx, need to reverse the data. + if (SPI_SSC->ctrl1_bit.fbn) { + SPI_SSC->dt = (__RBIT(v) >> 16) & 0xFFFF; + } else { + SPI_SSC->dt = (__RBIT(v) >> 24) & 0xFF; + } + } +} + +STATIC_FORCE_INLINE void FPGA_SSC_TX_Clear(void) { + while (!FPGA_SSC_TX_Ready()) { + // Waiting for last transfer finish. + // Nothing to do here... + } + FPGA_SSC_TX_Value(0x00); // Send a dummy data to clear the shift register and make the last data out. +} + +STATIC_FORCE_INLINE bool FPGA_SSC_DMA_RX_Done(void) { + // dma_flag_get(DMA_SSC_RX_DONE_FLAG) == RESET + // Note: Reading this register will not automatically clear the flag, + // and we need to write to the DMA_CR register to clear it. However, + // we can write it in the FPGA_SSC_DMA_RX_Refresh_XXX function + // because that function will always be called after FPGA_SSC_DMA_RX_Done returns true. + // see: FPGA_SSC_DMA_RX_Refresh_Single() + return DMA_SSC->sts & DMA_SSC_RX_DONE_FLAG; +} + +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Disable(void) { + // dma_channel_enable(DMA_CHANNEL_SSC, FALSE); + DMA_CHANNEL_SSC->ctrl_bit.chen = 0; +} + +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Enable(void) { + // dma_channel_enable(DMA_CHANNEL_SSC, TRUE); + DMA_CHANNEL_SSC->ctrl_bit.chen = 1; +} + +STATIC_FORCE_INLINE uint32_t* FPGA_SSC_DMA_RX_Current_Address(void) { + + // The DMA address of AT32 does not self increment. So reading the maddr register yields a fixed initial BUF starting address + // We can calculate the current rx address: Starting address + Current rx count + // Note: the count value register will increment on working, so we need save it. + // ret = starting_address(uint8) + ((g_ssc_dma_rx_count - remaining_count) * g_ssc_data_byte_width) + // > starting_address = DMA_CHANNEL_SSC->maddr + // > remaining_count = FPGA_SSC_DMA_RX_Remaining_Count() + + // Warn: Calc byte count first, last to convert to U32* + + return (uint32_t*)((uint8_t*)DMA_CHANNEL_SSC->maddr + ((g_ssc_dma_rx_count - FPGA_SSC_DMA_RX_Remaining_Count()) * g_ssc_data_byte_width)); +} + +STATIC_FORCE_INLINE uint16_t FPGA_SSC_DMA_RX_Remaining_Count(void) { + // dma_data_number_get(DMA_CHANNEL_SSC) or dma_init() + return (uint16_t)DMA_CHANNEL_SSC->dtcnt_bit.cnt; +} + +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Refresh_Repeat(void *buf, uint16_t len) { + // AT32 no next buf, so repeat & single is same logic. + FPGA_SSC_DMA_RX_Refresh_Single(buf, len); +} + +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Refresh_Single(void *buf, uint16_t len) { + g_ssc_dma_rx_count = len; + DMA_SSC->clr = DMA_SSC_RX_DONE_FLAG & 0x0FFFFFFF; // dma_flag_clear(DMA_SSC) + FPGA_SSC_DMA_RX_Disable(); // Writing to the CNT & ADDR registers requires closing the channel first. + DMA_CHANNEL_SSC->dtcnt_bit.cnt = len; + DMA_CHANNEL_SSC->maddr = (uint32_t)buf; + FPGA_SSC_DMA_RX_Enable(); +} + +#endif \ No newline at end of file diff --git a/common_arm/fpga/fpga_hw_at91.c b/common_arm/fpga/fpga_hw_at91.c new file mode 100644 index 000000000..d9d1a20bf --- /dev/null +++ b/common_arm/fpga/fpga_hw_at91.c @@ -0,0 +1,221 @@ +#include "at91sam7s512.h" +#include "fpga_apis.h" +#include "fpga_loader.h" +#include "common.h" +#include "proxmark3_arm.h" +#include "dbprint.h" + +void FpgaSetup24MHzClk(void) { + // The FPGA gets its clock from us from PCK0 output, so set that up. + AT91C_BASE_PIOA->PIO_BSR = GPIO_PCK0; + AT91C_BASE_PIOA->PIO_PDR = GPIO_PCK0; + AT91C_BASE_PMC->PMC_SCER |= AT91C_PMC_PCK0; + // PCK0 is PLL clock / 4 = 96MHz / 4 = 24MHz + AT91C_BASE_PMC->PMC_PCKR[0] = AT91C_PMC_CSS_PLL_CLK | AT91C_PMC_PRES_CLK_4; // 4 for 24MHz pck0, 2 for 48 MHZ pck0 + AT91C_BASE_PIOA->PIO_OER = GPIO_PCK0; +} + +void FpgaResetComInterface(void) { + // Reset SPI + AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SWRST; + AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SWRST; // errata says it needs twice to be correctly set. + + // Reset SSC + AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST; +} + +void FpgaSetupSsc(uint16_t fpga_mode) { + // First configure the GPIOs, and get ourselves a clock. + AT91C_BASE_PIOA->PIO_ASR = + GPIO_SSC_FRAME | + GPIO_SSC_DIN | + GPIO_SSC_DOUT | + GPIO_SSC_CLK; + AT91C_BASE_PIOA->PIO_PDR = GPIO_SSC_DOUT; + + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_SSC); + + // Now set up the SSC proper, starting from a known state. + AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST; + + // RX clock comes from TX clock, RX starts on Transmit Start, + // data and frame signal is sampled on falling edge of RK + AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(1) | SSC_CLOCK_MODE_START(1); + + // 8 or 16 per transfer, no loopback, MSB first, 1 transfer per sync pulse, no output sync + if (FpgaIs16BitMsbMode(fpga_mode)) { + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + } else { + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + } + + // TX clock comes from TK pin, no clock output, outputs change on rising edge of TK, + // TF (frame sync) is sampled on falling edge of TK, start TX on rising edge of TF + AT91C_BASE_SSC->SSC_TCMR = SSC_CLOCK_MODE_SELECT(2) | SSC_CLOCK_MODE_START(5); + + // tx framing is the same as the rx framing + AT91C_BASE_SSC->SSC_TFMR = AT91C_BASE_SSC->SSC_RFMR; + + AT91C_BASE_SSC->SSC_CR = AT91C_SSC_RXEN | AT91C_SSC_TXEN; +} + +void FpgaUpdateFrameMode(uint8_t bits, bool rx_msb, bool tx_msb) { + // AT91C_SSC_MSBF = (0x1 << 7) + // It's a magic, if we need msb, the msb param is 1, so we can set a valid enable bit to msb reg. + // 0 = 0 << 7, so lsb will skip update. + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(bits) | (rx_msb << 7); + AT91C_BASE_SSC->SSC_TFMR = SSC_FRAME_MODE_BITS_IN_WORD(bits) | (tx_msb << 7); +} + +bool FpgaSetupSscRxDmaRepeat(void *buf, uint16_t len) { + if (buf == NULL) { + return false; + } + + FPGA_SSC_DMA_RX_Disable(); + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // transfer to this memory address + AT91C_BASE_PDC_SSC->PDC_RCR = len; // transfer this many bytes + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; // next transfer to same memory address + AT91C_BASE_PDC_SSC->PDC_RNCR = len; // ... with same number of bytes + FPGA_SSC_DMA_RX_Enable(); + return true; +} + +bool FpgaSetupSscRxDmaSingle(void *buf, uint16_t len) { + if (buf == NULL) { + return false; + } + + FPGA_SSC_DMA_RX_Disable(); // Disable DMA Transfer + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // start transfer to this memory address + AT91C_BASE_PDC_SSC->PDC_RCR = len; // transfer this many samples + ((uint8_t*)buf)[0] = (uint8_t)FPGA_SSC_RX_Value(); // clear receive register + FPGA_SSC_DMA_RX_Enable(); // Start DMA transfer + + return true; +} + +//----------------------------------------------------------------------------- +// Set up the Serial Peripheral Interface as master +// Used to write the FPGA config word +// May also be used to write to other SPI attached devices like an LCD +//----------------------------------------------------------------------------- +static void DisableSpi(void) { + //* Reset all the Chip Select register + AT91C_BASE_SPI->SPI_CSR[0] = 0; + AT91C_BASE_SPI->SPI_CSR[1] = 0; + AT91C_BASE_SPI->SPI_CSR[2] = 0; + AT91C_BASE_SPI->SPI_CSR[3] = 0; + + // Reset the SPI mode + AT91C_BASE_SPI->SPI_MR = 0; + + // Disable all interrupts + AT91C_BASE_SPI->SPI_IDR = 0xFFFFFFFF; + + // SPI disable + AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIDIS; +} + +static void SetupSpi(int mode) { + // PA1 -> SPI_NCS3 chip select (MEM) + // PA10 -> SPI_NCS2 chip select (LCD) + // PA11 -> SPI_NCS0 chip select (FPGA) + // PA12 -> SPI_MISO Master-In Slave-Out + // PA13 -> SPI_MOSI Master-Out Slave-In + // PA14 -> SPI_SPCK Serial Clock + + // Disable PIO control of the following pins, allows use by the SPI peripheral + AT91C_BASE_PIOA->PIO_PDR = GPIO_NCS0 | GPIO_MISO | GPIO_MOSI | GPIO_SPCK; + + // Peripheral A + AT91C_BASE_PIOA->PIO_ASR = GPIO_NCS0 | GPIO_MISO | GPIO_MOSI | GPIO_SPCK; + + // Peripheral B + //AT91C_BASE_PIOA->PIO_BSR |= GPIO_NCS2; + + //enable the SPI Peripheral clock + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_SPI); + // Enable SPI + AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIEN; + + switch (mode) { + case SPI_FPGA_MODE: + AT91C_BASE_SPI->SPI_MR = + (0 << 24) | // Delay between chip selects (take default: 6 MCK periods) + (0xE << 16) | // Peripheral Chip Select (selects FPGA SPI_NCS0 or PA11) + (0 << 7) | // Local Loopback Disabled + AT91C_SPI_MODFDIS | // Mode Fault Detection disabled + (0 << 2) | // Chip selects connected directly to peripheral + AT91C_SPI_PS_FIXED | // Fixed Peripheral Select + AT91C_SPI_MSTR; // Master Mode + + AT91C_BASE_SPI->SPI_CSR[0] = + (1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) + (1 << 16) | // Delay Before SPCK (1 MCK period) + (6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24MHz/6 = 4M baud + AT91C_SPI_BITS_16 | // Bits per Transfer (16 bits) + (0 << 3) | // Chip Select inactive after transfer + AT91C_SPI_NCPHA | // Clock Phase data captured on leading edge, changes on following edge + (0 << 0); // Clock Polarity inactive state is logic 0 + break; + /* + case SPI_LCD_MODE: + AT91C_BASE_SPI->SPI_MR = + ( 0 << 24) | // Delay between chip selects (take default: 6 MCK periods) + (0xB << 16) | // Peripheral Chip Select (selects LCD SPI_NCS2 or PA10) + ( 0 << 7) | // Local Loopback Disabled + ( 1 << 4) | // Mode Fault Detection disabled + ( 0 << 2) | // Chip selects connected directly to peripheral + ( 0 << 1) | // Fixed Peripheral Select + ( 1 << 0); // Master Mode + + AT91C_BASE_SPI->SPI_CSR[2] = + ( 1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) + ( 1 << 16) | // Delay Before SPCK (1 MCK period) + ( 6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24MHz/6 = 4M baud + AT91C_SPI_BITS_9 | // Bits per Transfer (9 bits) + ( 0 << 3) | // Chip Select inactive after transfer + ( 1 << 1) | // Clock Phase data captured on leading edge, changes on following edge + ( 0 << 0); // Clock Polarity inactive state is logic 0 + break; + */ + default: + DisableSpi(); + break; + } +} + +void FpgaSendCommand(uint16_t cmd, uint16_t v) { + SetupSpi(SPI_FPGA_MODE); + while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_TXEMPTY) == 0); // wait for the transfer to complete + AT91C_BASE_SPI->SPI_TDR = AT91C_SPI_LASTXFER | cmd | v; // send the data + while (!(AT91C_BASE_SPI->SPI_SR & AT91C_SPI_RDRF)) {}; // wait till transfer is complete +} + +void Fpga_print_status(void) { + DbpString(_CYAN_("Current FPGA image")); + Dbprintf(" mode.................... %s", FpgaGetCurrentVersionString()); +} + +// ------------------------------------------------------------------- +// Config bitstream for FPGA +// Waiting for impl... + +int FpgaStartConfig(bool configSram, uint32_t fileLength) { + // TODO DXL: Not implemented + return PM3_ENOTIMPL; +} + +int FpgaConfigWrite(uint8_t *data, uint32_t data_length) { + return PM3_ENOTIMPL; +} +int FpgaStopConfig(void) { + return PM3_ENOTIMPL; +} + +uint32_t FpgaConfigPlatformStatus(void) { + return 0; +} + +// ------------------------------------------------------------------- diff --git a/common_arm/fpga/fpga_hw_at91.h b/common_arm/fpga/fpga_hw_at91.h new file mode 100644 index 000000000..1b9dba1a0 --- /dev/null +++ b/common_arm/fpga/fpga_hw_at91.h @@ -0,0 +1,86 @@ +#ifndef _FPGA_HW_AT91_H_ +#define _FPGA_HW_AT91_H_ + +#include "common.h" +#include "at91sam7s512.h" + +STATIC_FORCE_INLINE bool FPGA_SSC_RX_Ready(void) { + // There is no need to check the overflow flag, + // as according to the datasheet description, + // the latest data always moves from the shift register to the RHR register for overwriting. + return (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) == AT91C_SSC_RXRDY; +} + +STATIC_FORCE_INLINE bool FPGA_SSC_TX_Ready(void) { + return (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) == AT91C_SSC_TXRDY; +} + +STATIC_FORCE_INLINE bool FPGA_SSC_DMA_RX_Done(void) { + return (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_ENDRX) == AT91C_SSC_ENDRX; +} + +STATIC_FORCE_INLINE bool FPGA_SSC_TX_Done(void) { + return (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY) == AT91C_SSC_TXEMPTY; +} + +STATIC_FORCE_INLINE uint32_t FPGA_SSC_RX_Value(void) { + return AT91C_BASE_SSC->SSC_RHR; +} + +STATIC_FORCE_INLINE void FPGA_SSC_TX_Value(uint32_t v) { + AT91C_BASE_SSC->SSC_THR = v; +} + +STATIC_FORCE_INLINE void FPGA_SSC_TX_Clear(void) { + // TODO DXL: It is best to perform a clearing, + // but currently it seems that not clearing on RDV4 will not result in erroneous modulation. + // Afterwards, when we have time, we can conduct a test to see if adding the clearing logic affects anything. +} + +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Disable(void) { + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; +} + +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Enable(void) { + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; +} + +STATIC_FORCE_INLINE uint32_t* FPGA_SSC_DMA_RX_Current_Address(void) { + return (uint32_t*)AT91C_BASE_PDC_SSC->PDC_RPR; +} + +STATIC_FORCE_INLINE uint16_t FPGA_SSC_DMA_RX_Remaining_Count(void) { + return AT91C_BASE_PDC_SSC->PDC_RCR; +} + +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Refresh_Repeat(void *buf, uint16_t len) { + // primary buffer was stopped( <-- we lost data! + if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; + AT91C_BASE_PDC_SSC->PDC_RCR = len; + // Dbprintf("[-] RxEmpty ERROR | data length %d", len); // temporary + } + // secondary buffer sets as primary, secondary buffer was stopped + if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; + AT91C_BASE_PDC_SSC->PDC_RNCR = len; + } +} + +STATIC_FORCE_INLINE void FPGA_SSC_DMA_RX_Refresh_Single(void *buf, uint16_t len) { + // The previous code logic was to update the NEXT BUF information first and then wait for the event of receiving completion to arrive (the main receiving register count is reset to zero) + // Achieve the effect of setting buf ->waiting for reception completion and data processing (automatic rotation buf) ->setting buf (next cycle) ->waiting for reception completion and data processing (automatic rotation buf) + // Seamlessly initiate the next reception and ensure that data is not overwritten, as the address of the buf set each time is different. Therefore, the logic of AT91 can be implemented using NEXT buf, and the key is to prevent the main buf from stopping + // Otherwise, once the main buf stops, NEXT BUF will not be able to continue refreshing the next reception. Only when the main buf works normally until it ends, will it automatically rotate the reception information of NEXT BUF + // Therefore, based on the timing of the call, if the end of reception is judged first, the main buf should be used for refreshing. If the buf is refreshed first, the end of reception should be judged later! + // But in reality, for the sake of compatibility between platforms, we can only use the logic of first judging the end of the reception and then refreshing the reception buf! Because AT32 does not support NEXT BUF. + + // Warn: This code cannot be used because the NEXT BUF will only work when the MAIN BUF is working. + // AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)next_buf; + // AT91C_BASE_PDC_SSC->PDC_RNCR = PM3_CMD_DATA_SIZE; + + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // start transfer to this memory address + AT91C_BASE_PDC_SSC->PDC_RCR = len; // transfer this many samples +} + +#endif \ No newline at end of file diff --git a/armsrc/fpgaloader.c b/common_arm/fpga/fpga_loader.c similarity index 50% rename from armsrc/fpgaloader.c rename to common_arm/fpga/fpga_loader.c index 046c1e259..11d4e4f41 100644 --- a/armsrc/fpgaloader.c +++ b/common_arm/fpga/fpga_loader.c @@ -17,14 +17,14 @@ // Routines to load the FPGA image, and then to configure the FPGA's major // mode once it is configured. //----------------------------------------------------------------------------- -#include "fpgaloader.h" +#include "fpga_loader.h" +#include "fpga_apis.h" #include "proxmark3_arm.h" #include "appmain.h" #include "BigBuf.h" -#include "ticks.h" +#include "ticks_apis.h" #include "dbprint.h" -#include "util.h" #include "fpga.h" #include "string.h" @@ -38,165 +38,16 @@ typedef struct { typedef lz4_stream_t *lz4_streamp_t; -// remember which version of the bitstream we have already downloaded to the FPGA -static int downloaded_bitstream = FPGA_BITSTREAM_UNKNOWN; - // this is where the bitstreams are located in memory: extern uint32_t _binary_obj_fpga_all_bit_z_start[], _binary_obj_fpga_all_bit_z_end[]; static uint8_t *fpga_image_ptr = NULL; static uint32_t uncompressed_bytes_cnt; -//----------------------------------------------------------------------------- -// Set up the Serial Peripheral Interface as master -// Used to write the FPGA config word -// May also be used to write to other SPI attached devices like an LCD -//----------------------------------------------------------------------------- -static void DisableSpi(void) { - //* Reset all the Chip Select register - AT91C_BASE_SPI->SPI_CSR[0] = 0; - AT91C_BASE_SPI->SPI_CSR[1] = 0; - AT91C_BASE_SPI->SPI_CSR[2] = 0; - AT91C_BASE_SPI->SPI_CSR[3] = 0; - - // Reset the SPI mode - AT91C_BASE_SPI->SPI_MR = 0; - - // Disable all interrupts - AT91C_BASE_SPI->SPI_IDR = 0xFFFFFFFF; - - // SPI disable - AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIDIS; -} - -void SetupSpi(int mode) { - // PA1 -> SPI_NCS3 chip select (MEM) - // PA10 -> SPI_NCS2 chip select (LCD) - // PA11 -> SPI_NCS0 chip select (FPGA) - // PA12 -> SPI_MISO Master-In Slave-Out - // PA13 -> SPI_MOSI Master-Out Slave-In - // PA14 -> SPI_SPCK Serial Clock - - // Disable PIO control of the following pins, allows use by the SPI peripheral - AT91C_BASE_PIOA->PIO_PDR = GPIO_NCS0 | GPIO_MISO | GPIO_MOSI | GPIO_SPCK; - - // Peripheral A - AT91C_BASE_PIOA->PIO_ASR = GPIO_NCS0 | GPIO_MISO | GPIO_MOSI | GPIO_SPCK; - - // Peripheral B - //AT91C_BASE_PIOA->PIO_BSR |= GPIO_NCS2; - - //enable the SPI Peripheral clock - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_SPI); - // Enable SPI - AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIEN; - - switch (mode) { - case SPI_FPGA_MODE: - AT91C_BASE_SPI->SPI_MR = - (0 << 24) | // Delay between chip selects (take default: 6 MCK periods) - (0xE << 16) | // Peripheral Chip Select (selects FPGA SPI_NCS0 or PA11) - (0 << 7) | // Local Loopback Disabled - AT91C_SPI_MODFDIS | // Mode Fault Detection disabled - (0 << 2) | // Chip selects connected directly to peripheral - AT91C_SPI_PS_FIXED | // Fixed Peripheral Select - AT91C_SPI_MSTR; // Master Mode - - AT91C_BASE_SPI->SPI_CSR[0] = - (1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) - (1 << 16) | // Delay Before SPCK (1 MCK period) - (6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24MHz/6 = 4M baud - AT91C_SPI_BITS_16 | // Bits per Transfer (16 bits) - (0 << 3) | // Chip Select inactive after transfer - AT91C_SPI_NCPHA | // Clock Phase data captured on leading edge, changes on following edge - (0 << 0); // Clock Polarity inactive state is logic 0 - break; - /* - case SPI_LCD_MODE: - AT91C_BASE_SPI->SPI_MR = - ( 0 << 24) | // Delay between chip selects (take default: 6 MCK periods) - (0xB << 16) | // Peripheral Chip Select (selects LCD SPI_NCS2 or PA10) - ( 0 << 7) | // Local Loopback Disabled - ( 1 << 4) | // Mode Fault Detection disabled - ( 0 << 2) | // Chip selects connected directly to peripheral - ( 0 << 1) | // Fixed Peripheral Select - ( 1 << 0); // Master Mode - - AT91C_BASE_SPI->SPI_CSR[2] = - ( 1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) - ( 1 << 16) | // Delay Before SPCK (1 MCK period) - ( 6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24MHz/6 = 4M baud - AT91C_SPI_BITS_9 | // Bits per Transfer (9 bits) - ( 0 << 3) | // Chip Select inactive after transfer - ( 1 << 1) | // Clock Phase data captured on leading edge, changes on following edge - ( 0 << 0); // Clock Polarity inactive state is logic 0 - break; - */ - default: - DisableSpi(); - break; - } -} - -//----------------------------------------------------------------------------- -// Set up the synchronous serial port with the set of options that fits -// the FPGA mode. Both RX and TX are always enabled. -//----------------------------------------------------------------------------- -void FpgaSetupSsc(uint16_t fpga_mode) { - // First configure the GPIOs, and get ourselves a clock. - AT91C_BASE_PIOA->PIO_ASR = - GPIO_SSC_FRAME | - GPIO_SSC_DIN | - GPIO_SSC_DOUT | - GPIO_SSC_CLK; - AT91C_BASE_PIOA->PIO_PDR = GPIO_SSC_DOUT; - - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_SSC); - - // Now set up the SSC proper, starting from a known state. - AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST; - - // RX clock comes from TX clock, RX starts on Transmit Start, - // data and frame signal is sampled on falling edge of RK - AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(1) | SSC_CLOCK_MODE_START(1); - - // 8, 16 or 32 bits per transfer, no loopback, MSB first, 1 transfer per sync - // pulse, no output sync - if (((fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_READER) && - (FpgaGetCurrent() == FPGA_BITSTREAM_HF || FpgaGetCurrent() == FPGA_BITSTREAM_HF_15)) { - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); - } else { - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); - } - - // TX clock comes from TK pin, no clock output, outputs change on rising edge of TK, - // TF (frame sync) is sampled on falling edge of TK, start TX on rising edge of TF - AT91C_BASE_SSC->SSC_TCMR = SSC_CLOCK_MODE_SELECT(2) | SSC_CLOCK_MODE_START(5); - - // tx framing is the same as the rx framing - AT91C_BASE_SSC->SSC_TFMR = AT91C_BASE_SSC->SSC_RFMR; - - AT91C_BASE_SSC->SSC_CR = AT91C_SSC_RXEN | AT91C_SSC_TXEN; -} - -//----------------------------------------------------------------------------- -// Set up DMA to receive samples from the FPGA. We will use the PDC, with -// a single buffer as a circular buffer (so that we just chain back to -// ourselves, not to another buffer). -//----------------------------------------------------------------------------- -bool FpgaSetupSscDma(uint8_t *buf, uint16_t len) { - if (buf == NULL) { - return false; - } - - FpgaDisableSscDma(); - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // transfer to this memory address - AT91C_BASE_PDC_SSC->PDC_RCR = len; // transfer this many bytes - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; // next transfer to same memory address - AT91C_BASE_PDC_SSC->PDC_RNCR = len; // ... with same number of bytes - FpgaEnableSscDma(); - return true; -} +// remember which version of the bitstream we have already downloaded to the FPGA +// For high-capacity FPGA chips, the FPGA firmware may have been merged, +// and this flag can let us know which mode it is running in? +static int downloaded_bitstream = FPGA_BITSTREAM_UNKNOWN; //---------------------------------------------------------------------------- // Uncompress (inflate) the FPGA data. Returns one decompressed byte with each call. @@ -283,7 +134,7 @@ static bool reset_fpga_stream(int bitstream_target, lz4_streamp_t compressed_fpg } static void DownloadFPGA_byte(uint8_t w) { -#define SEND_BIT(x) { if(w & (1<PIO_OER = GPIO_FPGA_ON; - AT91C_BASE_PIOA->PIO_PER = GPIO_FPGA_ON; - HIGH(GPIO_FPGA_ON); // ensure everything is powered on + +#if !defined XC3 && !defined PM5 + gpio_fpga_on_setup(); + Gpio_FPGA_ON_High(); // ensure everything is powered on #endif SpinDelay(50); LED_D_ON(); - // These pins are inputs - AT91C_BASE_PIOA->PIO_ODR = - GPIO_FPGA_NINIT | - GPIO_FPGA_DONE; - // PIO controls the following pins - AT91C_BASE_PIOA->PIO_PER = - GPIO_FPGA_NINIT | -#if defined XC3 - //3S100E M2 & M3 PIO ENA - GPIO_SPCK | - GPIO_MOSI | -#endif - GPIO_FPGA_DONE; - - // Enable pull-ups - AT91C_BASE_PIOA->PIO_PPUER = - GPIO_FPGA_NINIT | - GPIO_FPGA_DONE; - // setup initial logic state - HIGH(GPIO_FPGA_NPROGRAM); - LOW(GPIO_FPGA_CCLK); - LOW(GPIO_FPGA_DIN); - // These pins are outputs - AT91C_BASE_PIOA->PIO_OER = - GPIO_FPGA_NPROGRAM | - GPIO_FPGA_CCLK | -#if defined XC3 - //3S100E M2 & M3 OUTPUT ENA - GPIO_SPCK | - GPIO_MOSI | -#endif - GPIO_FPGA_DIN; + Gpio_FPGA_NPROGRAM_High(); + Gpio_FPGA_CCLK_Low(); + Gpio_FPGA_DIN_Low(); + + // setup gpio function + gpio_fpga_download_setup(); #if defined XC3 - //3S100E M2 & M3 OUTPUT HIGH - HIGH(GPIO_SPCK); - HIGH(GPIO_MOSI); + // ICopyX(3S100E) M2 & M3 OUTPUT HIGH, for 'Slave Serial' mode select. + Gpio_FPGA_XC3_M1_High(); + Gpio_FPGA_XC3_M2_High(); #endif // enter FPGA configuration mode - LOW(GPIO_FPGA_NPROGRAM); + Gpio_FPGA_NPROGRAM_Low(); SpinDelay(50); - HIGH(GPIO_FPGA_NPROGRAM); + Gpio_FPGA_NPROGRAM_High(); i = 100000; // wait for FPGA ready to accept data signal - while ((i) && (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_FPGA_NINIT))) { + while ((i) && (!Gpio_FPGA_NINIT_Read())) { i--; } @@ -366,9 +192,9 @@ static void DownloadFPGA(int bitstream_target, int FpgaImageLen, lz4_streamp_t c } #if defined XC3 - //3S100E M2 & M3 RETURN TO NORMAL - LOW(GPIO_SPCK); - LOW(GPIO_MOSI); + // ICopyX(3S100E) M2 & M3 return to SPI peripheral + Gpio_FPGA_XC3_M1_Low(); + Gpio_FPGA_XC3_M2_Low(); AT91C_BASE_PIOA->PIO_PDR = GPIO_SPCK | GPIO_MOSI; #endif @@ -383,9 +209,9 @@ static void DownloadFPGA(int bitstream_target, int FpgaImageLen, lz4_streamp_t c // continue to clock FPGA until ready signal goes high i = 100000; - while ((i--) && (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_FPGA_DONE))) { - HIGH(GPIO_FPGA_CCLK); - LOW(GPIO_FPGA_CCLK); + while ((i--) && (!Gpio_FPGA_DONE_Read())) { + Gpio_FPGA_CCLK_High(); + Gpio_FPGA_CCLK_Low(); } // crude error indicator, leave both red LEDs on and return if (i == 0) { @@ -461,25 +287,20 @@ static int bitparse_find_section(int bitstream_target, char section_name, uint32 // return true if can change. // return false if image is unloaded. //---------------------------------------------------------------------------- -#if defined XC3 +#if defined XC3 || defined PM5 static bool FpgaConfCurrentMode(int bitstream_target) { - // fpga "XC3S100E" image merge - // If fpga image is no init - // We need load hf_lf_allinone.bit + // fpga "XC3S100E" image is merged. If fpga image is no init, We need load hf_lf_allinone.bit. if (downloaded_bitstream != FPGA_BITSTREAM_UNKNOWN) { - // test start - // PIO controls the following pins - AT91C_BASE_PIOA->PIO_PER = GPIO_FPGA_SWITCH; - // These pins are outputs - AT91C_BASE_PIOA->PIO_OER = GPIO_FPGA_SWITCH; + // gpio function setup + gpio_fpga_switch_setup(); // try to turn off antenna FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); if (bitstream_target == FPGA_BITSTREAM_LF) { - LOW(GPIO_FPGA_SWITCH); + Gpio_FPGA_SWITCH_Low(); } else { - HIGH(GPIO_FPGA_SWITCH); + Gpio_FPGA_SWITCH_High(); } // update downloaded_bitstream downloaded_bitstream = bitstream_target; @@ -496,12 +317,20 @@ static bool FpgaConfCurrentMode(int bitstream_target) { // decompress and load the correct (HF or LF) image to the FPGA //---------------------------------------------------------------------------- static void FpgaDownloadAndGoEx(int bitstream_target, bool keep_em) { + // check whether or not the bitstream is already loaded if (downloaded_bitstream == bitstream_target) { FpgaEnableTracing(); return; } +#if defined PM5 + // The FPGA of PM5 comes with built-in FLASH, so there is no need to download it at startup anymore. + downloaded_bitstream = bitstream_target; // FpgaConfCurrentMode() requires downloading for the first time, but we skipped it. + FpgaConfCurrentMode(bitstream_target); + return; // always return +#endif + #if defined XC3 // If we can change image version // direct return. @@ -566,109 +395,9 @@ void FpgaDownloadAndGo_keep_EM(int bitstream_target) { FpgaDownloadAndGoEx(bitstream_target, true); } -//----------------------------------------------------------------------------- -// Send a 16 bit command/data pair to the FPGA. -// The bit format is: C3 C2 C1 C0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 -// where C is the 4 bit command and D is the 12 bit data -// -// @params cmd and v gets OR:ED over each other. Take careful note of overlapping bits. -//----------------------------------------------------------------------------- -void FpgaSendCommand(uint16_t cmd, uint16_t v) { - SetupSpi(SPI_FPGA_MODE); - while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_TXEMPTY) == 0); // wait for the transfer to complete - AT91C_BASE_SPI->SPI_TDR = AT91C_SPI_LASTXFER | cmd | v; // send the data - while (!(AT91C_BASE_SPI->SPI_SR & AT91C_SPI_RDRF)) {}; // wait till transfer is complete -} - -//----------------------------------------------------------------------------- -// Write the FPGA setup word (that determines what mode the logic is in, read -// vs. clone vs. etc.). This is now a special case of FpgaSendCommand() to -// avoid changing this function's occurrence everywhere in the source code. -//----------------------------------------------------------------------------- -void FpgaWriteConfWord(uint16_t v) { - const int current = FpgaGetCurrent(); - - // Keep track of whether or not we should be monitoring the HF field timeout - if (current == FPGA_BITSTREAM_HF || current == FPGA_BITSTREAM_HF_15 || current == FPGA_BITSTREAM_HF_FELICA) { - const uint16_t major = v & FPGA_MAJOR_MODE_MASK; - const uint16_t minor = v & FPGA_MINOR_MODE_MASK; - - switch (major) { - case FPGA_MAJOR_MODE_HF_READER: - g_hf_field_timeout_active = true; - break; - case FPGA_MAJOR_MODE_HF_ISO14443A: - g_hf_field_timeout_active = (minor == FPGA_HF_ISO14443A_READER_LISTEN || minor == FPGA_HF_ISO14443A_READER_MOD); - break; - case FPGA_MAJOR_MODE_HF_ISO18092: - g_hf_field_timeout_active = (minor & FPGA_HF_ISO18092_FLAG_READER) != 0; - break; - default: - g_hf_field_timeout_active = false; - break; - } - } else { - g_hf_field_timeout_active = false; - } - - FpgaSendCommand(FPGA_CMD_SET_CONFREG, v); -} - -//----------------------------------------------------------------------------- -// enable/disable FPGA internal tracing -//----------------------------------------------------------------------------- -void FpgaEnableTracing(void) { - FpgaSendCommand(FPGA_CMD_TRACE_ENABLE, 1); -} - -void FpgaDisableTracing(void) { - FpgaSendCommand(FPGA_CMD_TRACE_ENABLE, 0); -} - -//----------------------------------------------------------------------------- -// Set up the CMOS switches that mux the ADC: four switches, independently -// closable, but should only close one at a time. Not an FPGA thing, but -// the samples from the ADC always flow through the FPGA. -//----------------------------------------------------------------------------- -void SetAdcMuxFor(uint32_t whichGpio) { - -#ifndef WITH_FPC_USART - // When compiled without FPC USART support - AT91C_BASE_PIOA->PIO_OER = - GPIO_MUXSEL_HIPKD | - GPIO_MUXSEL_LOPKD | - GPIO_MUXSEL_LORAW | - GPIO_MUXSEL_HIRAW; - - AT91C_BASE_PIOA->PIO_PER = - GPIO_MUXSEL_HIPKD | - GPIO_MUXSEL_LOPKD | - GPIO_MUXSEL_LORAW | - GPIO_MUXSEL_HIRAW; - - LOW(GPIO_MUXSEL_HIPKD); - LOW(GPIO_MUXSEL_LOPKD); - LOW(GPIO_MUXSEL_HIRAW); - LOW(GPIO_MUXSEL_LORAW); - HIGH(whichGpio); -#else - if ((whichGpio == GPIO_MUXSEL_LORAW) || (whichGpio == GPIO_MUXSEL_HIRAW)) - return; - // FPC USART uses HIRAW/LOWRAW pins, so they are excluded here. - AT91C_BASE_PIOA->PIO_OER = GPIO_MUXSEL_HIPKD | GPIO_MUXSEL_LOPKD; - AT91C_BASE_PIOA->PIO_PER = GPIO_MUXSEL_HIPKD | GPIO_MUXSEL_LOPKD; - LOW(GPIO_MUXSEL_HIPKD); - LOW(GPIO_MUXSEL_LOPKD); - HIGH(whichGpio); -#endif - -} - -void Fpga_print_status(void) { - DbpString(_CYAN_("Current FPGA image")); - Dbprintf(" mode.................... %s", g_fpga_version_information[bitstream_target_to_index(downloaded_bitstream)]); -} - +//---------------------------------------------------------------------------- +// Which FPGA bitstream has been downloaded currently. +//---------------------------------------------------------------------------- int FpgaGetCurrent(void) { return downloaded_bitstream; } @@ -677,20 +406,9 @@ void FpgaResetBitstream(void) { downloaded_bitstream = FPGA_BITSTREAM_UNKNOWN; } -// Turns off the antenna, -// log message -// if HF, Disable SSC DMA -// turn off trace and leds off. -void switch_off(void) { - if (g_dbglevel > DBG_DEBUG) { - Dbprintf("switch_off"); - } - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - if (downloaded_bitstream == FPGA_BITSTREAM_HF || downloaded_bitstream == FPGA_BITSTREAM_HF_15) { - FpgaDisableSscDma(); - } - - set_tracing(false); - LEDsoff(); +//---------------------------------------------------------------------------- +// The information of the bitstream of the FPGA that has been downloaded currently. +//---------------------------------------------------------------------------- +const char* FpgaGetCurrentVersionString(void) { + return g_fpga_version_information[bitstream_target_to_index(downloaded_bitstream)].versionString; } diff --git a/common_arm/clocks.h b/common_arm/fpga/fpga_loader.h similarity index 60% rename from common_arm/clocks.h rename to common_arm/fpga/fpga_loader.h index 47dcb0a9b..c85c35792 100644 --- a/common_arm/clocks.h +++ b/common_arm/fpga/fpga_loader.h @@ -13,13 +13,20 @@ // // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- -#ifndef _CLOCKS_H_ -#define _CLOCKS_H_ +// Routines to load the FPGA image, and then to configure the FPGA's major +// mode once it is configured. +//----------------------------------------------------------------------------- +#ifndef __FPGALOADER_H +#define __FPGALOADER_H #include "common.h" -#include "at91sam7s512.h" +#include "fpga.h" -void mck_from_pll_to_slck(void); -void mck_from_slck_to_pll(void); +int FpgaGetCurrent(void); +const char* FpgaGetCurrentVersionString(void); +void FpgaDownloadAndGo(int bitstream_target); +void FpgaDownloadAndGo_keep_EM(int bitstream_target); +void FpgaResetBitstream(void); +// void FpgaGatherVersion(int bitstream_target, char *dst, int len); -#endif // _CLOCKS_H_ +#endif diff --git a/common_arm/gpio/gpio_apis.h b/common_arm/gpio/gpio_apis.h new file mode 100644 index 000000000..ce9e6c37c --- /dev/null +++ b/common_arm/gpio/gpio_apis.h @@ -0,0 +1,141 @@ +#ifndef GPIO_APIS_H_ +#define GPIO_APIS_H_ + +#include "common.h" + + +/* + * Relying on forced inlining to achieve the same effect as macro definitions, + * while retaining function specific type and scope checks and limitations. + * --- + * Most importantly, due to compatibility with multiple platforms, + * if macro definitions are used, macro functions will not be clearly displayed to developers. + * --- + * To ensure proper function inlining, we need to ensure that the code is concise enough + * and does not have recursive/looping logic. + * --- + * If the function does not require very fast execution speed or the logic of the function is very complex, + * do not inline it, but declare it as a common function and define it in the C source file. + * --- + * Notice: + * Remember, the reason for using inline functions instead of macro functions is have to isolate platform differences as much as possible in this header file, + * only considering exposing interfaces that are supported by all platforms, + * rather than mixing all underlying operations, which can make maintenance very difficult. + * --- + * In fact, the main purpose is to standardize interface declarations and preserving code prompts. + * --- + * Note that this module only implement IO operations, + * IO initialization/operations related to peripheral/multiplexing are implemented in modules related to peripheral operations. + */ + +// TODO DXL 待实现具体调用初始化的位置的思考。(测试阶段暂时直接在start.c调用) +// 可能需要在boot里调用:gpio_button_setup 和 gpio_leds_setup 和 gpio_arm_power_on_setup + +void gpio_sysboot_setup(void); +void gpio_button_setup(void); +void gpio_leds_setup(void); +void gpio_arm_power_on_setup(void); +void gpio_inter_usb_spi_role_setup(void); +void gpio_sw_i2c_rst_setup(void); +void gpio_adc_mux_setup(void); +void gpio_fpga_switch_setup(void); +void gpio_fpga_download_setup(void); +void gpio_fpga_on_setup(void); +void gpio_fpga_mod_feedback_setup(void); +void gpio_fpga_mod_only_setup(void); +void gpio_vusb_setup(void); + +// -- Deprecated + +// Control the relay of antenna? Used on very old models. +__attribute__((deprecated)) void gpio_relay_setup(void); +// The original pm3 has this pin. If it is low, it means that the vdd reaches 5v (USB power supply) +__attribute__((deprecated)) void gpio_nvdd_setup(void); + +// -- Deprecated + +// ------------------------------------------ INLINE FUNCTIONS ------------------------------------------ + +STATIC_FORCE_INLINE void Gpio_ARM_Power_ON_High(void); +STATIC_FORCE_INLINE void Gpio_ARM_Power_ON_Low(void); + +STATIC_FORCE_INLINE bool Gpio_Button_Read(void); + +STATIC_FORCE_INLINE void Gpio_LED_A_High(void); +STATIC_FORCE_INLINE void Gpio_LED_A_Low(void); +STATIC_FORCE_INLINE void Gpio_LED_A_Inv(void); +STATIC_FORCE_INLINE void Gpio_LED_B_High(void); +STATIC_FORCE_INLINE void Gpio_LED_B_Low(void); +STATIC_FORCE_INLINE void Gpio_LED_B_Inv(void); +STATIC_FORCE_INLINE void Gpio_LED_C_High(void); +STATIC_FORCE_INLINE void Gpio_LED_C_Low(void); +STATIC_FORCE_INLINE void Gpio_LED_C_Inv(void); +STATIC_FORCE_INLINE void Gpio_LED_D_High(void); +STATIC_FORCE_INLINE void Gpio_LED_D_Low(void); +STATIC_FORCE_INLINE void Gpio_LED_D_Inv(void); + +STATIC_FORCE_INLINE void Gpio_SSC_DOUT_High(void); +STATIC_FORCE_INLINE void Gpio_SSC_DOUT_Low(void); +STATIC_FORCE_INLINE bool Gpio_SSC_DIN_Read(void); +STATIC_FORCE_INLINE bool Gpio_SSC_FRAME_Read(void); +STATIC_FORCE_INLINE bool Gpio_SSC_CLK_Read(void); + +STATIC_FORCE_INLINE void Gpio_FPGA_ON_High(void); +STATIC_FORCE_INLINE void Gpio_FPGA_ON_Low(void); +STATIC_FORCE_INLINE void Gpio_FPGA_DIN_High(void); +STATIC_FORCE_INLINE void Gpio_FPGA_DIN_Low(void); +STATIC_FORCE_INLINE void Gpio_FPGA_CCLK_High(void); +STATIC_FORCE_INLINE void Gpio_FPGA_CCLK_Low(void); +STATIC_FORCE_INLINE void Gpio_FPGA_NPROGRAM_High(void); +STATIC_FORCE_INLINE void Gpio_FPGA_NPROGRAM_Low(void); +STATIC_FORCE_INLINE bool Gpio_FPGA_NINIT_Read(void); +STATIC_FORCE_INLINE bool Gpio_FPGA_DONE_Read(void); + +STATIC_FORCE_INLINE void Gpio_FPGA_SWITCH_High(void); +STATIC_FORCE_INLINE void Gpio_FPGA_SWITCH_Low(void); + +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M1_High(void); +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M1_Low(void); +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M2_High(void); +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M2_Low(void); + +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIPKD_High(void); +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIPKD_Low(void); +STATIC_FORCE_INLINE void Gpio_MUXSEL_LOPKD_High(void); +STATIC_FORCE_INLINE void Gpio_MUXSEL_LOPKD_Low(void); +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIRAW_High(void); +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIRAW_Low(void); +STATIC_FORCE_INLINE void Gpio_MUXSEL_LORAW_High(void); +STATIC_FORCE_INLINE void Gpio_MUXSEL_LORAW_Low(void); + +STATIC_FORCE_INLINE void Gpio_I2C_SCL_High(void); +STATIC_FORCE_INLINE void Gpio_I2C_SDA_High(void); +STATIC_FORCE_INLINE void Gpio_I2C_RST_High(void); +STATIC_FORCE_INLINE void Gpio_I2C_SCL_Low(void); +STATIC_FORCE_INLINE void Gpio_I2C_SDA_Low(void); +STATIC_FORCE_INLINE void Gpio_I2C_RST_Low(void); +STATIC_FORCE_INLINE bool Gpio_I2C_SCL_Read(void); +STATIC_FORCE_INLINE bool Gpio_I2C_SDA_Read(void); + +STATIC_FORCE_INLINE void Gpio_Inter_USB_SPI_Role_High(void); +STATIC_FORCE_INLINE void Gpio_Inter_USB_SPI_Role_Low(void); + +STATIC_FORCE_INLINE bool Gpio_VUSB_Read(void); + +// -- Deprecated + +STATIC_FORCE_INLINE void Gpio_Relay_High(void); +STATIC_FORCE_INLINE void Gpio_Relay_Low(void); +STATIC_FORCE_INLINE bool Gpio_NVDD_Read(void); + +// -- Deprecated + +#ifdef PM5 +#include "gpio_hw_at32.h" +#else +#include "gpio_hw_at91.h" +#endif + +// ------------------------------------------ INLINE FUNCTIONS ------------------------------------------ + +#endif // GPIO_APIS_H_ diff --git a/common_arm/gpio/gpio_hw_at32.c b/common_arm/gpio/gpio_hw_at32.c new file mode 100644 index 000000000..f70877ae0 --- /dev/null +++ b/common_arm/gpio/gpio_hw_at32.c @@ -0,0 +1,169 @@ +#include "gpio_apis.h" +#include "at32f435_437_gpio.h" +#include "at32f435_437_crm.h" +#include "proxmark3_arm.h" + +// Simplify Enable GPIO Clock +#define GPIO_CLK_EN(clk) crm_periph_clock_enable(clk, TRUE) + +// common output init +static void gpio_output_init(gpio_init_type *gpio_init_struct, gpio_type *gpio_x, uint32_t pins) { + gpio_init_struct->gpio_mode = GPIO_MODE_OUTPUT; + gpio_init_struct->gpio_pins = pins; + gpio_init_struct->gpio_pull = GPIO_PULL_NONE; + gpio_init(gpio_x, gpio_init_struct); +} + +void gpio_button_setup(void) { + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + GPIO_CLK_EN(AT32_GPIO_BTN_CLK); + gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; + gpio_init_struct.gpio_pins = AT32_GPIO_BTN_PIN; + gpio_init_struct.gpio_pull = GPIO_PULL_DOWN; + gpio_init(AT32_GPIO_BTN, &gpio_init_struct); +} + +void gpio_leds_setup(void) { + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN; + + GPIO_CLK_EN(AT32_GPIO_LED_CLK); + // Off all leds before setup(Avoid flickering) + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + + gpio_output_init( + &gpio_init_struct, + AT32_GPIO_LED, + AT32_GPIO_LEDA_PIN | AT32_GPIO_LEDB_PIN | AT32_GPIO_LEDC_PIN | AT32_GPIO_LEDD_PIN); +} + +/** + * After power up, it is necessary to initialize and lock this IO (pull up) as soon as possible, + * otherwise the power will automatically shut down after a certain period of time. + * Note: + * 1. After power up, the button function will return to normal. + * 2. The buttons of the old models are directly connected to ARM and do not have power control function. + */ +void gpio_arm_power_on_setup(void) { + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + GPIO_CLK_EN(AT32_GPIO_ARM_POWER_LOCK_CLK); + Gpio_ARM_Power_ON_High(); // Self-lock power control, keep ARM power on. + gpio_output_init(&gpio_init_struct, AT32_GPIO_ARM_POWER_LOCK, AT32_GPIO_ARM_POWER_LOCK_PIN); +} + +/** + * For spi switch master/slave in 'inter-usb', High is Master, Low is slave + * Only PM5 supported(inter-usb ext spi functions) + */ +void gpio_inter_usb_spi_role_setup(void) { + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + + GPIO_CLK_EN(AT32_GPIO_INTER_USB_SPI_ROLE_CLK); + gpio_output_init(&gpio_init_struct, AT32_GPIO_INTER_USB_SPI_ROLE, AT32_GPIO_INTER_USB_SPI_ROLE_PIN); +} + +void gpio_sw_i2c_rst_setup(void) { + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + + // Software implemented I2C needs to be set to open drain output + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN; + + crm_periph_clock_enable(AT32_GPIO_I2C_SW_CLK, TRUE); + gpio_output_init(&gpio_init_struct, AT32_GPIO_I2C_SW, AT32_GPIO_I2C_SCL_PIN | AT32_GPIO_I2C_SDA_PIN); +} + +void gpio_fpga_switch_setup(void) { + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + GPIO_CLK_EN(AT32_GPIO_FPGA_SWITCH_CLK); + gpio_output_init(&gpio_init_struct, AT32_GPIO_FPGA_SWITCH, AT32_GPIO_FPGA_SWITCH_PIN); +} + +void gpio_adc_mux_setup(void) { + // The fpgaswitch linkage switches adcmux. The HF firmware is hipkd, otherwise it is lopkd + // So, we can reuse setup functions of 'fpga_switch' + gpio_fpga_switch_setup(); +} + +void gpio_fpga_download_setup(void) { + gpio_init_type gpio_init_struct; + + // TODO DXL: Move IO & CLK definition to 'config_gpio_proxmark5.h' + + GPIO_CLK_EN(CRM_GPIOA_PERIPH_CLOCK); + GPIO_CLK_EN(CRM_GPIOC_PERIPH_CLOCK); + GPIO_CLK_EN(CRM_GPIOD_PERIPH_CLOCK); + + gpio_default_para_init(&gpio_init_struct); + gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; + + gpio_init_struct.gpio_pins = GPIO_PINS_4; + gpio_init(GPIOB, &gpio_init_struct); + + gpio_init_struct.gpio_pins = GPIO_PINS_10; // PC10_SPI3_SCK -> TCK + gpio_init(GPIOC, &gpio_init_struct); + + gpio_init_struct.gpio_pins = GPIO_PINS_15; // PA15_SPI3_CS -> TMS + gpio_init(GPIOA, &gpio_init_struct); + + gpio_init_struct.gpio_pins = GPIO_PINS_12; // PC12_SPI3_MOSI -> TDI + gpio_init(GPIOC, &gpio_init_struct); + + gpio_init_struct.gpio_pins = GPIO_PINS_2; // PD2 -> FPGA_JTAGSEL + gpio_init(GPIOD, &gpio_init_struct); + + gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; + gpio_init_struct.gpio_pins = GPIO_PINS_11; // PC11_SPI3_MISO -> TDO + gpio_init(GPIOC, &gpio_init_struct); +} + +void gpio_fpga_on_setup(void) { + // Unsupported +} + +void gpio_fpga_mod_feedback_setup(void) { + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + GPIO_CLK_EN(AT32_GPIO_PERIPH_SSC_CLK); + gpio_output_init(&gpio_init_struct, AT32_GPIO_SSC_DOUT, AT32_GPIO_SSC_DOUT_PIN); + gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init_struct.gpio_pins = AT32_GPIO_SSC_CLK_PIN; + gpio_init(AT32_GPIO_SSC_CLK, &gpio_init_struct); +} + +void gpio_fpga_mod_only_setup(void) { + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + // ssc_out == miso, arm -> fpga + GPIO_CLK_EN(AT32_GPIO_PERIPH_SSC_CLK); + gpio_output_init(&gpio_init_struct, AT32_GPIO_SSC_DOUT, AT32_GPIO_SSC_DOUT_PIN); +} + +void gpio_sysboot_setup(void) { + // To keep power on for ARM, This is a power supply locking pin. + // Once released, the whole system will be powered off. + gpio_arm_power_on_setup(); + // 4 x leds(red) + gpio_leds_setup(); + // Button for POWER_CONTROL / User interaction + gpio_button_setup(); +} + +void gpio_vusb_setup(void) { + gpio_init_type gpio_init_struct; + gpio_default_para_init(&gpio_init_struct); + GPIO_CLK_EN(AT32_GPIO_VUSB_CLK); + gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; + gpio_init_struct.gpio_pins = AT32_GPIO_VUSB_PIN; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init(AT32_GPIO_VUSB, &gpio_init_struct); +} diff --git a/common_arm/gpio/gpio_hw_at32.h b/common_arm/gpio/gpio_hw_at32.h new file mode 100644 index 000000000..711d0311e --- /dev/null +++ b/common_arm/gpio/gpio_hw_at32.h @@ -0,0 +1,271 @@ +#ifndef GPIO_HW_AT32_H_ +#define GPIO_HW_AT32_H_ + +#include "common.h" +#include "config_gpio.h" + + +/** + * For at32 gpio peripheral clk enable by array + */ +#define AT32_GPIO_PERIPH_CLKS_ENABLE(...) \ + do { \ + const crm_periph_clock_type args[] = { __VA_ARGS__ }; \ + for (size_t i = 0; i < sizeof(args) / sizeof(args[0]); ++i) { \ + crm_periph_clock_enable(args[i], TRUE); \ + } \ + } while(0) + + +// Get gpio input status +STATIC_FORCE_INLINE uint8_t GpioInputStatus(const gpio_type *gpio_x, uint16_t pins) { + return pins == (pins & gpio_x->idt); +} + +// Get gpio output status +STATIC_FORCE_INLINE uint8_t GpioOutputStatus(gpio_type *gpio_x, uint16_t pins) { + return pins == (pins & gpio_x->odt); +} + +// Output inversion +STATIC_FORCE_INLINE void GpioOutputInv(gpio_type *gpio_x, uint16_t pins) { + if (GpioOutputStatus(gpio_x, pins)) { + gpio_x->clr = pins; + } else { + gpio_x->scr = pins; + } +} + +STATIC_FORCE_INLINE void Gpio_ARM_Power_ON_High(void) { + AT32_GPIO_ARM_POWER_LOCK->scr = AT32_GPIO_ARM_POWER_LOCK_PIN; +} + +STATIC_FORCE_INLINE void Gpio_ARM_Power_ON_Low(void) { + AT32_GPIO_ARM_POWER_LOCK->clr = AT32_GPIO_ARM_POWER_LOCK_PIN; +} + +STATIC_FORCE_INLINE bool Gpio_Button_Read(void) { + return GpioInputStatus(AT32_GPIO_BTN, AT32_GPIO_BTN_PIN); +} + +STATIC_FORCE_INLINE void Gpio_LED_A_High(void) { + AT32_GPIO_LED->scr = AT32_GPIO_LEDA_PIN; +} + +STATIC_FORCE_INLINE void Gpio_LED_B_High(void) { + AT32_GPIO_LED->scr = AT32_GPIO_LEDB_PIN; +} + +STATIC_FORCE_INLINE void Gpio_LED_C_High(void) { + AT32_GPIO_LED->scr = AT32_GPIO_LEDC_PIN; +} + +STATIC_FORCE_INLINE void Gpio_LED_D_High(void) { + AT32_GPIO_LED->scr = AT32_GPIO_LEDD_PIN; +} + +STATIC_FORCE_INLINE void Gpio_LED_A_Low(void) { + AT32_GPIO_LED->clr = AT32_GPIO_LEDA_PIN; +} + +STATIC_FORCE_INLINE void Gpio_LED_B_Low(void) { + AT32_GPIO_LED->clr = AT32_GPIO_LEDB_PIN; +} + +STATIC_FORCE_INLINE void Gpio_LED_C_Low(void) { + AT32_GPIO_LED->clr = AT32_GPIO_LEDC_PIN; +} + +STATIC_FORCE_INLINE void Gpio_LED_D_Low(void) { + AT32_GPIO_LED->clr = AT32_GPIO_LEDD_PIN; +} + +STATIC_FORCE_INLINE void Gpio_LED_A_Inv(void) { + GpioOutputInv(AT32_GPIO_LED, AT32_GPIO_LEDA_PIN); +} + +STATIC_FORCE_INLINE void Gpio_LED_B_Inv(void) { + GpioOutputInv(AT32_GPIO_LED, AT32_GPIO_LEDB_PIN); +} + +STATIC_FORCE_INLINE void Gpio_LED_C_Inv(void) { + GpioOutputInv(AT32_GPIO_LED, AT32_GPIO_LEDC_PIN); +} + +STATIC_FORCE_INLINE void Gpio_LED_D_Inv(void) { + GpioOutputInv(AT32_GPIO_LED, AT32_GPIO_LEDD_PIN); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_ON_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_FPGA_ON_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_SSC_DOUT_High(void) { + AT32_GPIO_SSC_DOUT->scr = AT32_GPIO_SSC_DOUT_PIN; +} + +STATIC_FORCE_INLINE void Gpio_SSC_DOUT_Low(void) { + AT32_GPIO_SSC_DOUT->clr = AT32_GPIO_SSC_DOUT_PIN; +} + +STATIC_FORCE_INLINE bool Gpio_SSC_DIN_Read(void) { + return GpioInputStatus(AT32_GPIO_SSC_DIN, AT32_GPIO_SSC_DIN_PIN); +} + +STATIC_FORCE_INLINE bool Gpio_SSC_FRAME_Read(void) { + return GpioInputStatus(AT32_GPIO_SSC_FRAME, AT32_GPIO_SSC_FRAME_PIN); +} + +STATIC_FORCE_INLINE bool Gpio_SSC_CLK_Read(void) { + return GpioInputStatus(AT32_GPIO_SSC_CLK, AT32_GPIO_SSC_CLK_PIN); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_DIN_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_FPGA_DIN_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_FPGA_CCLK_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_FPGA_CCLK_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_FPGA_NPROGRAM_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_FPGA_NPROGRAM_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE bool Gpio_FPGA_NINIT_Read(void) { + return false; // Unsupported +} + +STATIC_FORCE_INLINE bool Gpio_FPGA_DONE_Read(void) { + return false; // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_FPGA_SWITCH_High(void) { + AT32_GPIO_FPGA_SWITCH->scr = AT32_GPIO_FPGA_SWITCH_PIN; +} + +STATIC_FORCE_INLINE void Gpio_FPGA_SWITCH_Low(void) { + AT32_GPIO_FPGA_SWITCH->clr = AT32_GPIO_FPGA_SWITCH_PIN; +} + +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M1_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M1_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M2_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M2_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIPKD_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIPKD_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_LOPKD_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_LOPKD_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIRAW_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIRAW_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_LORAW_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_LORAW_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_I2C_SCL_High(void) { + AT32_GPIO_I2C_SW->scr = AT32_GPIO_I2C_SCL_PIN; +} + +STATIC_FORCE_INLINE void Gpio_I2C_SCL_Low(void) { + AT32_GPIO_I2C_SW->clr = AT32_GPIO_I2C_SCL_PIN; +} + +STATIC_FORCE_INLINE void Gpio_I2C_SDA_High(void) { + AT32_GPIO_I2C_SW->scr = AT32_GPIO_I2C_SDA_PIN; +} + +STATIC_FORCE_INLINE void Gpio_I2C_SDA_Low(void) { + AT32_GPIO_I2C_SW->clr = AT32_GPIO_I2C_SDA_PIN; +} + +STATIC_FORCE_INLINE void Gpio_I2C_RST_High(void) { + // TODO DXL 待实现 +} + +STATIC_FORCE_INLINE void Gpio_I2C_RST_Low(void) { + // TODO DXL 待实现 +} + +STATIC_FORCE_INLINE bool Gpio_I2C_SCL_Read(void) { + return GpioInputStatus(AT32_GPIO_I2C_SW, AT32_GPIO_I2C_SCL_PIN); +} + +STATIC_FORCE_INLINE bool Gpio_I2C_SDA_Read(void) { + return GpioInputStatus(AT32_GPIO_I2C_SW, AT32_GPIO_I2C_SDA_PIN); +} + +STATIC_FORCE_INLINE void Gpio_Inter_USB_SPI_Role_High(void) { + AT32_GPIO_INTER_USB_SPI_ROLE->scr = AT32_GPIO_INTER_USB_SPI_ROLE_PIN; +} + +STATIC_FORCE_INLINE void Gpio_Inter_USB_SPI_Role_Low(void) { + AT32_GPIO_INTER_USB_SPI_ROLE->clr = AT32_GPIO_INTER_USB_SPI_ROLE_PIN; +} + +STATIC_FORCE_INLINE bool Gpio_VUSB_Read(void) { + return GpioInputStatus(AT32_GPIO_VUSB, AT32_GPIO_VUSB_PIN); +} + +STATIC_FORCE_INLINE void Gpio_Relay_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_Relay_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE bool Gpio_NVDD_Read(void) { + return false; // Unsupported +} + +#endif // GPIO_HW_AT32_H_ diff --git a/common_arm/gpio/gpio_hw_at91.c b/common_arm/gpio/gpio_hw_at91.c new file mode 100644 index 000000000..72ede14b8 --- /dev/null +++ b/common_arm/gpio/gpio_hw_at91.c @@ -0,0 +1,157 @@ +#include "gpio_apis.h" +#include "at91sam7s512.h" +#include "config_gpio.h" + +void gpio_button_setup(void) { + AT91C_BASE_PIOA->PIO_PER = GPIO_BUTTON; + AT91C_BASE_PIOA->PIO_ODR = GPIO_BUTTON; +} + +void gpio_leds_setup(void) { + AT91C_BASE_PIOA->PIO_PER = AT91C_BASE_PIOA->PIO_OER = // Chained assignment + GPIO_LED_A | + GPIO_LED_B | + GPIO_LED_C | + GPIO_LED_D; +} + +void gpio_arm_power_on_setup(void) { + // Currently, there are no devices with AT91 as the core that support power control. +} + +void gpio_inter_usb_spi_role_setup(void) { + // Unsupported +} + +void gpio_sw_i2c_rst_setup(void) { + // Configure reset pin, close up pull, push-pull output, default high + AT91C_BASE_PIOA->PIO_PPUDR = GPIO_I2C_RST; + AT91C_BASE_PIOA->PIO_MDDR = GPIO_I2C_RST; + + // Configure I2C pin, open up, open leakage + AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_I2C_SCL | GPIO_I2C_SDA); + AT91C_BASE_PIOA->PIO_MDER |= (GPIO_I2C_SCL | GPIO_I2C_SDA); + + // default three lines all pull up + AT91C_BASE_PIOA->PIO_SODR |= (GPIO_I2C_SCL | GPIO_I2C_SDA | GPIO_I2C_RST); + + AT91C_BASE_PIOA->PIO_OER |= (GPIO_I2C_SCL | GPIO_I2C_SDA | GPIO_I2C_RST); + AT91C_BASE_PIOA->PIO_PER |= (GPIO_I2C_SCL | GPIO_I2C_SDA | GPIO_I2C_RST); +} + +void gpio_fpga_switch_setup(void) { +#ifdef GPIO_FPGA_SWITCH + AT91C_BASE_PIOA->PIO_PER = GPIO_FPGA_SWITCH; + AT91C_BASE_PIOA->PIO_OER = GPIO_FPGA_SWITCH; +#endif +} + +void gpio_adc_mux_setup(void) { + AT91C_BASE_PIOA->PIO_PER = AT91C_BASE_PIOA->PIO_OER = // Chained assignment + GPIO_MUXSEL_HIPKD | +#ifndef WITH_FPC_USART // FPC USART uses HIRAW/LOWRAW pins, so they are excluded here. + GPIO_MUXSEL_LORAW | + GPIO_MUXSEL_HIRAW | +#endif + GPIO_MUXSEL_LOPKD; +} + +void gpio_fpga_download_setup(void) { + + /** + * ICopyx(XC3S100E) reuse M1 & M2(M2,M3) pin for spi communication. + * When M2 & M3 is high before enter configuration, The mode 'Slave Serial (M[2:0] = 110)' selected. + * It is also to reuse the download code of xc2s30. + * Therefore, after the configuration mode is selected, these two PINs will free, so they can be reused as SPI communication ports. + * See docs at Table 44: Spartan-3E Configuration Mode Options and Pin Settings + */ + + // PIO controls the following pins for 'Slave Serial', need disable peripheral functions. + AT91C_BASE_PIOA->PIO_PER = + GPIO_FPGA_NINIT | + GPIO_FPGA_DONE | +#if defined XC3 + // ICopyX(3S100E) M2 & M3 PIO ENA + GPIO_SPCK | + GPIO_MOSI | +#endif + GPIO_FPGA_NPROGRAM | + GPIO_FPGA_CCLK | + GPIO_FPGA_DIN; + + // These pins are inputs + AT91C_BASE_PIOA->PIO_ODR = GPIO_FPGA_NINIT | GPIO_FPGA_DONE; + AT91C_BASE_PIOA->PIO_PPUER = GPIO_FPGA_NINIT | GPIO_FPGA_DONE; // Enable pull-ups + + // These pins are outputs + AT91C_BASE_PIOA->PIO_OER = + GPIO_FPGA_NPROGRAM | + GPIO_FPGA_CCLK | +#if defined XC3 + // ICopyX(3S100E) M2 & M3 OUTPUT ENA + GPIO_SPCK | + GPIO_MOSI | +#endif + GPIO_FPGA_DIN; +} + +void gpio_fpga_on_setup(void) { + AT91C_BASE_PIOA->PIO_OER = GPIO_FPGA_ON; + AT91C_BASE_PIOA->PIO_PER = GPIO_FPGA_ON; +} + +void gpio_fpga_mod_feedback_setup(void) { + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; +} + +void gpio_fpga_mod_only_setup(void) { + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; +} + +void gpio_sysboot_setup(void) { + // Kill all the pullups, especially the one on USB D+; leave them for + // the unused pins, though. + AT91C_BASE_PIOA->PIO_PPUDR = + GPIO_USB_PU | + GPIO_LED_A | + GPIO_LED_B | + GPIO_LED_C | + GPIO_LED_D | + GPIO_FPGA_DIN | + GPIO_FPGA_DOUT | + GPIO_FPGA_CCLK | + GPIO_FPGA_NINIT | + GPIO_FPGA_NPROGRAM | + GPIO_FPGA_DONE | + GPIO_MUXSEL_HIPKD | + GPIO_MUXSEL_HIRAW | + GPIO_MUXSEL_LOPKD | + GPIO_MUXSEL_LORAW | + GPIO_RELAY | + GPIO_NVDD_ON; + // (and add GPIO_FPGA_ON) + // These pins are outputs + AT91C_BASE_PIOA->PIO_OER = + GPIO_LED_A | + GPIO_LED_B | + GPIO_LED_C | + GPIO_LED_D | + GPIO_RELAY | + GPIO_NVDD_ON; + // PIO controls the following pins + AT91C_BASE_PIOA->PIO_PER = + GPIO_USB_PU | + GPIO_LED_A | + GPIO_LED_B | + GPIO_LED_C | + GPIO_LED_D; + + gpio_button_setup(); +} + +void gpio_vusb_setup(void) { + // Unsupported! +} diff --git a/common_arm/gpio/gpio_hw_at91.h b/common_arm/gpio/gpio_hw_at91.h new file mode 100644 index 000000000..5c5e5d2e3 --- /dev/null +++ b/common_arm/gpio/gpio_hw_at91.h @@ -0,0 +1,253 @@ +#ifndef _GPIO_HW_AT91_H +#define _GPIO_HW_AT91_H + +#include "common.h" +#include "config_gpio.h" + +#define LOW(x) AT91C_BASE_PIOA->PIO_CODR |= (x) +#define HIGH(x) AT91C_BASE_PIOA->PIO_SODR |= (x) + +#define GETBIT(x) (AT91C_BASE_PIOA->PIO_ODSR & (x)) ? 1:0 +#define SETBIT(x, y) (y) ? (HIGH(x)):(LOW(x)) +#define INVBIT(x) SETBIT((x), !(GETBIT(x))) + +STATIC_FORCE_INLINE void Gpio_ARM_Power_ON_High(void) { + // Unsupported! + // If AT91 devices support power self-locking in the future, please implement this function. + // And Gpio_ARM_Power_ON_Low() functions. +} + +STATIC_FORCE_INLINE void Gpio_ARM_Power_ON_Low(void) { + // Unsupported! +} + +STATIC_FORCE_INLINE bool Gpio_Button_Read(void) { + return (AT91C_BASE_PIOA->PIO_PDSR & GPIO_BUTTON) == GPIO_BUTTON; +} + +STATIC_FORCE_INLINE void Gpio_LED_A_High(void) { + HIGH(GPIO_LED_A); +} + +STATIC_FORCE_INLINE void Gpio_LED_B_High(void) { + HIGH(GPIO_LED_B); +} + +STATIC_FORCE_INLINE void Gpio_LED_C_High(void) { + HIGH(GPIO_LED_C); +} + +STATIC_FORCE_INLINE void Gpio_LED_D_High(void) { + HIGH(GPIO_LED_D); +} + +STATIC_FORCE_INLINE void Gpio_LED_A_Low(void) { + LOW(GPIO_LED_A); +} + +STATIC_FORCE_INLINE void Gpio_LED_B_Low(void) { + LOW(GPIO_LED_B); +} + +STATIC_FORCE_INLINE void Gpio_LED_C_Low(void) { + LOW(GPIO_LED_C); +} + +STATIC_FORCE_INLINE void Gpio_LED_D_Low(void) { + LOW(GPIO_LED_D); +} + +STATIC_FORCE_INLINE void Gpio_LED_A_Inv(void) { + INVBIT(GPIO_LED_A); +} + +STATIC_FORCE_INLINE void Gpio_LED_B_Inv(void) { + INVBIT(GPIO_LED_B); +} + +STATIC_FORCE_INLINE void Gpio_LED_C_Inv(void) { + INVBIT(GPIO_LED_C); +} + +STATIC_FORCE_INLINE void Gpio_LED_D_Inv(void) { + INVBIT(GPIO_LED_D); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_ON_High(void) { + HIGH(GPIO_FPGA_ON); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_ON_Low(void) { + LOW(GPIO_FPGA_ON); +} + +STATIC_FORCE_INLINE void Gpio_SSC_DOUT_High(void) { + HIGH(GPIO_SSC_DOUT); +} + +STATIC_FORCE_INLINE void Gpio_SSC_DOUT_Low(void) { + LOW(GPIO_SSC_DOUT); +} + +STATIC_FORCE_INLINE bool Gpio_SSC_DIN_Read(void) { + return (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN) == GPIO_SSC_DIN; +} + +STATIC_FORCE_INLINE bool Gpio_SSC_FRAME_Read(void) { + return (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME) == GPIO_SSC_FRAME; +} + +STATIC_FORCE_INLINE bool Gpio_SSC_CLK_Read(void) { + return (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) == GPIO_SSC_CLK; +} + +STATIC_FORCE_INLINE void Gpio_FPGA_DIN_High(void) { + HIGH(GPIO_FPGA_DIN); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_DIN_Low(void) { + LOW(GPIO_FPGA_DIN); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_CCLK_High(void) { + HIGH(GPIO_FPGA_CCLK); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_CCLK_Low(void) { + LOW(GPIO_FPGA_CCLK); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_NPROGRAM_High(void) { + HIGH(GPIO_FPGA_NPROGRAM); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_NPROGRAM_Low(void) { + LOW(GPIO_FPGA_NPROGRAM); +} + +STATIC_FORCE_INLINE bool Gpio_FPGA_NINIT_Read(void) { + return (AT91C_BASE_PIOA->PIO_PDSR & GPIO_FPGA_NINIT) == GPIO_FPGA_NINIT; +} + +STATIC_FORCE_INLINE bool Gpio_FPGA_DONE_Read(void) { + return (AT91C_BASE_PIOA->PIO_PDSR & GPIO_FPGA_DONE) == GPIO_FPGA_DONE; +} + +STATIC_FORCE_INLINE void Gpio_FPGA_SWITCH_High(void) { +#ifdef GPIO_FPGA_SWITCH + HIGH(GPIO_FPGA_SWITCH); +#endif +} + +STATIC_FORCE_INLINE void Gpio_FPGA_SWITCH_Low(void) { +#ifdef GPIO_FPGA_SWITCH + LOW(GPIO_FPGA_SWITCH); +#endif +} + +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M1_High(void) { + HIGH(GPIO_SPCK); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M1_Low(void) { + LOW(GPIO_SPCK); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M2_High(void) { + HIGH(GPIO_MOSI); +} + +STATIC_FORCE_INLINE void Gpio_FPGA_XC3_M2_Low(void) { + LOW(GPIO_MOSI); +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIPKD_High(void) { + HIGH(GPIO_MUXSEL_HIPKD); +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIPKD_Low(void) { + LOW(GPIO_MUXSEL_HIPKD); +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_LOPKD_High(void) { + HIGH(GPIO_MUXSEL_LOPKD); +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_LOPKD_Low(void) { + LOW(GPIO_MUXSEL_LOPKD); +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIRAW_High(void) { + HIGH(GPIO_MUXSEL_HIRAW); +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_HIRAW_Low(void) { + LOW(GPIO_MUXSEL_HIRAW); +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_LORAW_High(void) { + HIGH(GPIO_MUXSEL_LORAW); +} + +STATIC_FORCE_INLINE void Gpio_MUXSEL_LORAW_Low(void) { + LOW(GPIO_MUXSEL_LORAW); +} + +STATIC_FORCE_INLINE void Gpio_I2C_SCL_High(void) { + HIGH(GPIO_I2C_SCL); +} + +STATIC_FORCE_INLINE void Gpio_I2C_SCL_Low(void) { + LOW(GPIO_I2C_SCL); +} + +STATIC_FORCE_INLINE void Gpio_I2C_SDA_High(void) { + HIGH(GPIO_I2C_SDA); +} + +STATIC_FORCE_INLINE void Gpio_I2C_SDA_Low(void) { + LOW(GPIO_I2C_SDA); +} + +STATIC_FORCE_INLINE void Gpio_I2C_RST_High(void) { + HIGH(GPIO_I2C_RST); +} + +STATIC_FORCE_INLINE void Gpio_I2C_RST_Low(void) { + LOW(GPIO_I2C_RST); +} + +STATIC_FORCE_INLINE bool Gpio_I2C_SCL_Read(void) { + return (AT91C_BASE_PIOA->PIO_PDSR & GPIO_I2C_SCL) == GPIO_I2C_SCL; +} + +STATIC_FORCE_INLINE bool Gpio_I2C_SDA_Read(void) { + return (AT91C_BASE_PIOA->PIO_PDSR & GPIO_I2C_SDA) == GPIO_I2C_SDA; +} + +STATIC_FORCE_INLINE void Gpio_Inter_USB_SPI_Role_High(void) { + // Unsupported +} + +STATIC_FORCE_INLINE void Gpio_Inter_USB_SPI_Role_Low(void) { + // Unsupported +} + +STATIC_FORCE_INLINE bool Gpio_VUSB_Read(void) { + // Unsupported + return false; +} + +STATIC_FORCE_INLINE void Gpio_Relay_High(void) { + HIGH(GPIO_RELAY); +} + +STATIC_FORCE_INLINE void Gpio_Relay_Low(void) { + LOW(GPIO_RELAY); +} + +STATIC_FORCE_INLINE bool Gpio_NVDD_Read(void) { + return ((AT91C_BASE_PIOA->PIO_PDSR & GPIO_NVDD_ON) == GPIO_NVDD_ON); +} + +#endif diff --git a/common_arm/ldscript.common b/common_arm/ldscript.common index 640ccee52..d54d41b80 100644 --- a/common_arm/ldscript.common +++ b/common_arm/ldscript.common @@ -13,6 +13,24 @@ * * See LICENSE.txt for the text of the license. *----------------------------------------------------------------------------- + * Memory Layout: + * SRAM START + * ┌─────────────────┐ + * │ .data │ + * ├─────────────────┤ + * │ │ ← __bss_start__ + * │ .bss │ ← __bss_end__ + * ├─────────────────┤ + * │ │ + * │ unused │ ← BigBuf + * │ │ + * ├─────────────────┤ + * │ │ ← _stack_start + * │ stack │ ← _stack_end + * ├─────────────────┤ + * │ commonarea │ ← proxmark3_arm.h & common_area_t + * └─────────────────┘ + * SRAM END *----------------------------------------------------------------------------- * Common linker script *----------------------------------------------------------------------------- @@ -20,18 +38,25 @@ stacksize = DEFINED(stacksize) ? stacksize : 8488; commonareasize = 0x20; -/* AT91SAM7S256 has 256k Flash and 64k RAM */ -/* AT91SAM7S512 has 512k Flash and 64k RAM */ -/* boot space = 8192bytes (0x2000) */ -/* osimage space = (512k - 0x2000 == 524288 - 8192 == 516096bytes == 0x7E000 ) */ MEMORY { - bootphase1 : ORIGIN = 0x00100000, LENGTH = 0x200 /* Phase 1 bootloader: Copies real bootloader to RAM */ - bootphase2 : ORIGIN = 0x00100200, LENGTH = 0x2000 - 0x200 /* Main bootloader code, stored in Flash, executed from RAM */ - osimage : ORIGIN = 0x00102000, LENGTH = 512K - 0x2000 /* Place where the main OS will end up */ - ram : ORIGIN = 0x00200000, LENGTH = 64K - commonareasize /* RAM, minus small common area */ - stack : ORIGIN = 0x00200000 + 64K - stacksize - commonareasize, LENGTH = stacksize /* Stack */ - commonarea : ORIGIN = 0x00200000 + 64K - commonareasize, LENGTH = commonareasize /* Communication between bootloader and main OS */ + /* Phase 1 bootloader: Copies real bootloader to RAM */ + bootphase1 : ORIGIN = mcu_flash_base_addr , LENGTH = bootphase1_size + + /* Main bootloader code, stored in Flash, run in RAM */ + bootphase2 : ORIGIN = mcu_flash_base_addr + bootphase1_size , LENGTH = bootphase2_size + + /* Place where the main OS will end up */ + osimage : ORIGIN = os_image_origin , LENGTH = os_image_size + + /* RAM, minus small common area */ + ram : ORIGIN = mcu_sram_base_addr , LENGTH = mcu_sram_size - commonareasize + + /* Stack */ + stack : ORIGIN = mcu_sram_base_addr + mcu_sram_size - stacksize - commonareasize , LENGTH = stacksize + + /* Communication between bootloader and main OS */ + commonarea : ORIGIN = mcu_sram_base_addr + mcu_sram_size - commonareasize , LENGTH = commonareasize } /* Export some information that can be used from within the firmware */ diff --git a/common_arm/ldscript.defs.at32 b/common_arm/ldscript.defs.at32 new file mode 100644 index 000000000..ade7c5f2a --- /dev/null +++ b/common_arm/ldscript.defs.at32 @@ -0,0 +1,20 @@ +mcu_flash_size = 1024K; + +/* + * AT32F435RGT7 supports SRAM configuration of 512K at most, but after modifying mcu_sram_size, + * you must remember to modify USD->eopb0, and make sure to modify USD->eopb0 configuration + * before accessing a larger memory area in the startup script (.s assembly file), + * otherwise it will lead to Hard Fault Handler. + */ +mcu_sram_size = 512K; /* 384K or 512K ? */ + +mcu_flash_base_addr = 0x08000000; +mcu_sram_base_addr = 0x20000000; + +boot_image_size = 0x4000; /* boot space = 16384bytes (0x4000) */ +bootphase1_size = 0x200; +bootphase2_size = boot_image_size - bootphase1_size; + +os_image_size = mcu_flash_size - boot_image_size; +os_image_origin = mcu_flash_base_addr + boot_image_size; +/* os_image_origin = mcu_flash_base_addr; /* TODO 还在调试,没有实现BOOT,所以暂时设置为OS而不是BOOT的启动地址 */ diff --git a/common_arm/ldscript.defs.at91 b/common_arm/ldscript.defs.at91 new file mode 100644 index 000000000..d674f7afa --- /dev/null +++ b/common_arm/ldscript.defs.at91 @@ -0,0 +1,15 @@ +/* AT91SAM7S256 has 256k Flash and 64k RAM */ +/* AT91SAM7S512 has 512k Flash and 64k RAM */ +mcu_flash_size = 512K; +mcu_sram_size = 64K; + +mcu_flash_base_addr = 0x00100000; +mcu_sram_base_addr = 0x00200000; + +boot_image_size = 0x2000; /* boot space = 8192bytes (0x2000) */ +bootphase1_size = 0x200; +bootphase2_size = boot_image_size - bootphase1_size; + +/* osimage space: (512k - 0x2000 == 524288 - 8192 == 516096bytes == 0x7E000 ) */ +os_image_size = mcu_flash_size - boot_image_size; +os_image_origin = mcu_flash_base_addr + boot_image_size; diff --git a/common_arm/rssi/rssi_apis.h b/common_arm/rssi/rssi_apis.h new file mode 100644 index 000000000..c3ab3f9c1 --- /dev/null +++ b/common_arm/rssi/rssi_apis.h @@ -0,0 +1,40 @@ +#ifndef RSSI_APIS_H_ +#define RSSI_APIS_H_ + +#include "common.h" + +typedef enum { + ADC_RSSI_CH_HF, + ADC_RSSI_CH_LF, +} adc_rssi_ch_t; + +void AdcSetupRssiChannel(adc_rssi_ch_t ch); +uint32_t AdcRssiAvg(adc_rssi_ch_t ch); +uint32_t AdcRssiSum(adc_rssi_ch_t ch, uint8_t NbSamples); + +STATIC_FORCE_INLINE void AdcRssiConversionStart(void); +STATIC_FORCE_INLINE bool AdcRssiDataReady(adc_rssi_ch_t ch); +STATIC_FORCE_INLINE uint32_t AdcRssiDataRead(adc_rssi_ch_t ch); + +//----------------------------------------------------------------------------- +// Function for converting ADC values to millivolt units(cross platforms) +// The ADC sampling results for each platform have different values in millivolts. +// Warn: please use this function for cross platform compatibility. +//----------------------------------------------------------------------------- +STATIC_FORCE_INLINE uint32_t AdcRssiDataToMilliVolt(uint16_t data, adc_rssi_ch_t ch); + +//----------------------------------------------------------------------------- +// After collecting N times, calculate the average value and convert it to millivolts. +// This function calls the AdcRssiSum() function internally, so you don't need to call the setup function in advance. +// And the conversion result is the RSSI value in millivolts, dont need considering compatibility issues for cross platform conversion. +// Warn: please try to call this function as much as possible! +//----------------------------------------------------------------------------- +STATIC_FORCE_INLINE uint32_t AdcRssiAvgToMilliVolt(adc_rssi_ch_t ch); + +#ifdef PM5 +#include "rssi_hw_at32.h" +#else +#include "rssi_hw_at91.h" +#endif + +#endif // RSSI_APIS_H_ diff --git a/common_arm/rssi/rssi_core.c b/common_arm/rssi/rssi_core.c new file mode 100644 index 000000000..ffdd15054 --- /dev/null +++ b/common_arm/rssi/rssi_core.c @@ -0,0 +1,25 @@ +#include "rssi_apis.h" + +//----------------------------------------------------------------------------- +// Read an ADC channel and block till it completes, then return the result +// in ADC units (0 to 1023). Also a routine to sum up a number of samples and +// return that. +//----------------------------------------------------------------------------- +static uint32_t ReadAdc(adc_rssi_ch_t ch) { + AdcSetupRssiChannel(ch); + while (!AdcRssiDataReady(ch)) {}; + return AdcRssiDataRead(ch); +} + +// Collect 32 times and calculate the average value +uint32_t AdcRssiAvg(adc_rssi_ch_t ch) { + return AdcRssiSum(ch, 32) >> 5; // == /32 +} + +// Sample the specified RF field voltage N times, note that it cannot exceed 255 times. +uint32_t AdcRssiSum(adc_rssi_ch_t ch, uint8_t NbSamples) { + uint32_t a = 0; + for (uint8_t i = 0; i < NbSamples; i++) + a += ReadAdc(ch); + return (a + (NbSamples >> 1) - 1); +} diff --git a/common_arm/rssi/rssi_hw_at32.c b/common_arm/rssi/rssi_hw_at32.c new file mode 100644 index 000000000..064f4a4fa --- /dev/null +++ b/common_arm/rssi/rssi_hw_at32.c @@ -0,0 +1,78 @@ +#include "rssi_apis.h" +#include "config_gpio.h" + +uint16_t g_adc_vref_value; + +/** + * @brief gpio configuration. + * Note: view the 'Datasheet' not 'Reference Manual' for pin maping get. + */ +static void gpio_config(void) { + gpio_init_type gpio_initstructure; + gpio_default_para_init(&gpio_initstructure); + crm_periph_clock_enable(AT32_GPIO_ADC_RSSI_CLK, TRUE); + // config adc pin as analog input mode + gpio_initstructure.gpio_mode = GPIO_MODE_ANALOG; + gpio_initstructure.gpio_pins = AT32_GPIO_ADC_RSSI_LF_PIN | AT32_GPIO_ADC_RSSI_HF_PIN; + gpio_init(AT32_GPIO_ADC_RSSI, &gpio_initstructure); +} + +void AdcSetupRssiChannel(adc_rssi_ch_t ch) { + adc_common_config_type adc_common_struct; + adc_base_config_type adc_base_struct; + + adc_common_default_para_init(&adc_common_struct); + crm_periph_clock_enable(AT32_RSSI_ADC_PERIPH_CLK, TRUE); + gpio_config(); + adc_reset(); + + adc_common_struct.combine_mode = ADC_INDEPENDENT_MODE; // config combine mode + adc_common_struct.div = ADC_HCLK_DIV_10; // config division,adcclk is division by hclk + adc_common_struct.common_dma_mode = ADC_COMMON_DMAMODE_DISABLE; // config common dma mode,it's not useful in independent mode + adc_common_struct.common_dma_request_repeat_state = FALSE; // config common dma request repeat + adc_common_struct.sampling_interval = ADC_SAMPLING_INTERVAL_5CYCLES; // config adjacent adc sampling interval,it's useful for ordinary shifting mode + adc_common_struct.tempervintrv_state = TRUE; // config inner temperature sensor and vintrv, connect to ADC1_IN16 & ADC1_IN17, we need to detect vref + + /* config voltage battery */ + adc_common_struct.vbat_state = FALSE; + adc_common_config(&adc_common_struct); + + adc_base_default_para_init(&adc_base_struct); + adc_base_struct.sequence_mode = FALSE; // Disable sequence mode, acquire a channel once. + adc_base_struct.repeat_mode = FALSE; + adc_base_struct.data_align = ADC_RIGHT_ALIGNMENT; + adc_base_struct.ordinary_channel_length = 1; + adc_base_config(AT32_RSSI_ADC, &adc_base_struct); + adc_resolution_set(AT32_RSSI_ADC, ADC_RESOLUTION_12B); + + // adc_ordinary_conversion_trigger_set(AT32_RSSI_RSSI_ADC, ADC_ORDINARY_TRIG_TMR1CH1, ADC_ORDINARY_TRIG_EDGE_NONE); // config ordinary trigger source and trigger edge + adc_dma_mode_enable(AT32_RSSI_ADC, FALSE); // config dma mode,it's not useful when common dma mode is use + adc_dma_request_repeat_enable(AT32_RSSI_ADC, FALSE); // config dma request repeat,it's not useful when common dma mode is use + adc_occe_each_conversion_enable(AT32_RSSI_ADC, TRUE); // each ordinary channel conversion set occe flag + adc_interrupt_enable(AT32_RSSI_ADC, ADC_OCCO_INT, FALSE); // disable adc overflow interrupt + + // adc enable and wait ready + adc_enable(AT32_RSSI_ADC, TRUE); + while (adc_flag_get(AT32_RSSI_ADC, ADC_RDY_FLAG) == RESET); + + // adc calibration and wait finish + adc_calibration_init(AT32_RSSI_ADC); + while (adc_calibration_init_status_get(AT32_RSSI_ADC)); + adc_calibration_start(AT32_RSSI_ADC); + while (adc_calibration_status_get(AT32_RSSI_ADC)); + + // get vref value, ADC_CHANNEL_17 is fixed, don't change!!! + adc_ordinary_channel_set(AT32_RSSI_ADC, ADC_CHANNEL_17, 1, ADC_SAMPLETIME_640_5); + AdcRssiConversionStart(); + while(adc_flag_get(AT32_RSSI_ADC, ADC_OCCE_FLAG) == RESET); // Waiting for adc conversion done. + // printf("vref_value = %f V\r\n", ((double)1.2 * 4095) / adc1_ordinary_value); + g_adc_vref_value = adc_ordinary_conversion_data_get(AT32_RSSI_ADC); + + // config ordinary channel and start first time conversion. + if (ch == ADC_RSSI_CH_HF) { + adc_ordinary_channel_set(AT32_RSSI_ADC, AT32_RSSI_ADC_HF_CHANNEL, 1, ADC_SAMPLETIME_640_5); + } else { + adc_ordinary_channel_set(AT32_RSSI_ADC, AT32_RSSI_ADC_LF_CHANNEL, 1, ADC_SAMPLETIME_640_5); + } + AdcRssiConversionStart(); +} diff --git a/common_arm/rssi/rssi_hw_at32.h b/common_arm/rssi/rssi_hw_at32.h new file mode 100644 index 000000000..64a307f24 --- /dev/null +++ b/common_arm/rssi/rssi_hw_at32.h @@ -0,0 +1,44 @@ +#ifndef ADC_RSSI_HW_AT32_H +#define ADC_RSSI_HW_AT32_H + +#include "at32f435_437_adc.h" +#include "at32f435_437_crm.h" + +#define AT32_RSSI_ADC_PERIPH_CLK CRM_ADC1_PERIPH_CLOCK +#define AT32_RSSI_ADC ADC1 +#define AT32_RSSI_ADC_LF_CHANNEL ADC_CHANNEL_10 // ADC123_IN10 +#define AT32_RSSI_ADC_HF_CHANNEL ADC_CHANNEL_11 // ADC123_IN11 + +/** + * Save the reference voltage values collected each time the AdcSetupRssiChannel() function is called. + */ +extern uint16_t g_adc_vref_value; + +STATIC_FORCE_INLINE void AdcRssiConversionStart(void) { + adc_ordinary_software_trigger_enable(AT32_RSSI_ADC, TRUE); +} + +STATIC_FORCE_INLINE bool AdcRssiDataReady(adc_rssi_ch_t ch) { + return adc_flag_get(AT32_RSSI_ADC, ADC_OCCE_FLAG); +} + +STATIC_FORCE_INLINE uint32_t AdcRssiDataRead(adc_rssi_ch_t ch) { + return adc_ordinary_conversion_data_get(AT32_RSSI_ADC); +} + +STATIC_FORCE_INLINE uint32_t AdcRssiDataToMilliVolt(uint16_t data, adc_rssi_ch_t ch) { + // Analog input voltage (Vin) = (ADC digital value × reference voltage) / full-scale digital value + // Voltage division ratio: LF = 46.45,HF = 31.3 + if (ch == ADC_RSSI_CH_HF) { + // VIN = DATA * 1200 / g_adc_vref_value * 31.3 + return ((data * 1200) / g_adc_vref_value) * 313 / 10; // = *31.3 + } + // VIN = DATA * 1200 / g_adc_vref_value * 46.45 + return ((data * 1200) / g_adc_vref_value) * 4645 / 100; // = *46.45 +} + +STATIC_FORCE_INLINE uint32_t AdcRssiAvgToMilliVolt(adc_rssi_ch_t ch) { + return AdcRssiDataToMilliVolt(AdcRssiAvg(ch), ch); +} + +#endif // ADC_RSSI_HW_AT32_H diff --git a/common_arm/rssi/rssi_hw_at91.c b/common_arm/rssi/rssi_hw_at91.c new file mode 100644 index 000000000..67a55ba25 --- /dev/null +++ b/common_arm/rssi/rssi_hw_at91.c @@ -0,0 +1,29 @@ +#include "rssi_apis.h" +#include "at91sam7s512.h" +#include "proxmark3_arm.h" + +void AdcSetupRssiChannel(adc_rssi_ch_t ch) { + + // Note: ADC_MODE_PRESCALE and ADC_MODE_SAMPLE_HOLD_TIME are set to the maximum allowed value. + // AMPL_HI is are high impedance (10MOhm || 1MOhm) output, the input capacitance of the ADC is 12pF (typical). This results in a time constant + // of RC = (0.91MOhm) * 12pF = 10.9us. Even after the maximum configurable sample&hold time of 40us the input capacitor will not be fully charged. + // + // The maths are: + // If there is a voltage v_in at the input, the voltage v_cap at the capacitor (this is what we are measuring) will be + // + // v_cap = v_in * (1 - exp(-SHTIM/RC)) = v_in * (1 - exp(-40us/10.9us)) = v_in * 0,97 (i.e. an error of 3%) + + AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; + AT91C_BASE_ADC->ADC_MR = + ADC_MODE_PRESCALE(63) // ADC_CLK = MCK / ((63+1) * 2) = 48MHz / 128 = 375kHz + | ADC_MODE_STARTUP_TIME(1) // Startup Time = (1+1) * 8 / ADC_CLK = 16 / 375kHz = 42,7us Note: must be > 20us + | ADC_MODE_SAMPLE_HOLD_TIME(15); // Sample & Hold Time SHTIM = 15 / ADC_CLK = 15 / 375kHz = 40us + + if (ch == ADC_RSSI_CH_HF) { + AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF); + } else { + AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_LF); + } + + AdcRssiConversionStart(); +} \ No newline at end of file diff --git a/common_arm/rssi/rssi_hw_at91.h b/common_arm/rssi/rssi_hw_at91.h new file mode 100644 index 000000000..4b6530337 --- /dev/null +++ b/common_arm/rssi/rssi_hw_at91.h @@ -0,0 +1,56 @@ +#ifndef _ADC_RSSI_HW_AT91_H +#define _ADC_RSSI_HW_AT91_H + +#include "at91sam7s512.h" +#include "proxmark3_arm.h" + +#if defined RDV4 || defined ICOPYX +// ADC Vref = 3300mV, and an (10000k+240k):240k voltage divider on the LF input can measure voltages up to 140800 mV +#define MAX_ADC_HF_VOLTAGE 140800 +#else +// ADC Vref = 3300mV, and an (10M+1M):1M voltage divider on the HF input can measure voltages up to 36300 mV +#define MAX_ADC_HF_VOLTAGE 36300 +#endif +// ADC Vref = 3300mV, (240k-10M):240k voltage divider, 140800 mV +#define MAX_ADC_LF_VOLTAGE 140800 + +STATIC_FORCE_INLINE void AdcRssiConversionStart(void) { + AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; +} + +STATIC_FORCE_INLINE bool AdcRssiDataReady(adc_rssi_ch_t ch) { + if (ch == ADC_RSSI_CH_HF) { + return AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF); + } + return AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_LF); +} + +STATIC_FORCE_INLINE uint32_t AdcRssiDataRead(adc_rssi_ch_t ch) { + if (ch == ADC_RSSI_CH_HF) { + return AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF] & 0x3FF; + } + return AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_LF] & 0x3FF; +} + +STATIC_FORCE_INLINE uint32_t AdcRssiDataToMilliVolt(uint16_t data, adc_rssi_ch_t ch) { + if (ch == ADC_RSSI_CH_HF) { + return ((uint32_t)data * MAX_ADC_HF_VOLTAGE) >> 10; + } + return ((uint32_t)data * MAX_ADC_LF_VOLTAGE) >> 10; +} + +STATIC_FORCE_INLINE uint32_t AdcRssiAvgToMilliVolt(adc_rssi_ch_t ch) { + /* + * voltage = (sum_32 / 32) * (MAX_ADC_HF_VOLTAGE / 1024) + * = (sum_32 * MAX_ADC_HF_VOLTAGE) / (32 * 1024) + * = (sum_32 * MAX_ADC_HF_VOLTAGE) / 32768 + * = (sum_32 * MAX_ADC_HF_VOLTAGE) >> 15 + */ + if (ch == ADC_RSSI_CH_HF) { + return (MAX_ADC_HF_VOLTAGE * AdcRssiSum(ADC_RSSI_CH_HF, 32)) >> 15; + } + // Moving one bit to the right in advance is to avoid the risk of multiplication overflow. + return (MAX_ADC_LF_VOLTAGE * (AdcRssiSum(ADC_RSSI_CH_LF, 32) >> 1)) >> 14; +} + +#endif diff --git a/common_arm/sys/sys_apis.h b/common_arm/sys/sys_apis.h new file mode 100644 index 000000000..275babb02 --- /dev/null +++ b/common_arm/sys/sys_apis.h @@ -0,0 +1,62 @@ +#ifndef SYS_APIS_H_ +#define SYS_APIS_H_ + +#include "common.h" + +//----------------------------------------------------------------------------- +// Jump to the Any image after setting the stack pointer.(You jump, i jump) +// For chips that require setting the interrupt vector table, +// this function assumes by default that it is in the header of AnyImage. +//----------------------------------------------------------------------------- +void __attribute__((noreturn)) JumpToAnyImage(uint32_t stack_top, uint32_t entry_point); + +//----------------------------------------------------------------------------- +// Config system clock +// This is usually done in the BOOTROM firmware. +//----------------------------------------------------------------------------- +void ConfigSystemClocks(void); + +//----------------------------------------------------------------------------- +// Get the main chip type of the current firmware. +// Note: It is determined at compile time, rather than through some register information. +//----------------------------------------------------------------------------- +STATIC_FORCE_INLINE main_chip_type_t GetChipType(void); + +//----------------------------------------------------------------------------- +// Get ID of the chip +// Note: It is not the unique ID of the chip. It is the ID related the chip model. +//----------------------------------------------------------------------------- +STATIC_FORCE_INLINE uint32_t GetChipId(void); + +//----------------------------------------------------------------------------- +// Get the unique ID of the chip +// size: the size of the unique ID in bytes, set to 0 if no unique id available(and return null ptr). +// Note: Not all chips have a unique ID(Such as AT91SAM7S). +//----------------------------------------------------------------------------- +STATIC_FORCE_INLINE uint8_t* GetChipUniqueId(uint8_t *size); + +//----------------------------------------------------------------------------- +// Reset the chip processor +// Note: Resetting the chip will restart code execution from the bootROM. +//----------------------------------------------------------------------------- +STATIC_FORCE_INLINE void ResetChip(void); + +//----------------------------------------------------------------------------- +// Get flash size of chip +// ROM size max in bytes +//----------------------------------------------------------------------------- +STATIC_FORCE_INLINE uint32_t GetChipFlashSize(void); + +//----------------------------------------------------------------------------- +// Check if the reset is caused by a reset with SRAM retention, +// which means the RAM content is retained and not cleared. +//----------------------------------------------------------------------------- +STATIC_FORCE_INLINE bool CheckRSTWithSRAMRetention(void); + +#ifdef PM5 +#include "sys_hw_at32.h" +#else +#include "sys_hw_at91.h" +#endif + +#endif // SYS_APIS_H_ diff --git a/common_arm/sys/sys_hw_at32.c b/common_arm/sys/sys_hw_at32.c new file mode 100644 index 000000000..1884ddd2c --- /dev/null +++ b/common_arm/sys/sys_hw_at32.c @@ -0,0 +1,445 @@ +// +// Created by dxl on 2026/5/23. +// + +#include "sys_apis.h" +#include "config_gpio_proxmark5.h" + +// --- at32 --- +#include "at32f435_437_crm.h" +#include "at32f435_437_pwc.h" +#include "at32f435_437_flash.h" +#include "at32f435_437_misc.h" +#include "at32f435_437_ertc.h" +// --- + +#define SYS_SIMPLE_RESET_BPR_MAGIC 0x504D3352U +#define SYS_SIMPLE_RESET_BPR_UNLOCK_KEY1 0xCAU +#define SYS_SIMPLE_RESET_BPR_UNLOCK_KEY2 0x53U +#define SYS_SIMPLE_RESET_BPR_LOCK_KEY 0xFFU + +uint8_t g_system_reset_method = 0; // Default set to 0 for call system_simple_reset() + jump bootrom. + +/** + * @brief empty call definition, avoid errors linking libc.a + * @param fn_name function name to define for libc.a + */ +#define EMPTY_CALL(fn_name) \ + void fn_name(void); \ + __WEAK void fn_name(void) {} + +// For simple reset & jump to bootrom restart the device. +extern uint32_t _bootrom_start[], _stack_end[]; + +// Empty init definition to avoid errors linking libc.a +EMPTY_CALL(_init) + +// Empty _fini definition to avoid errors linking libc.a +EMPTY_CALL(_fini) + +/** + * Write data to BPR(Battery powered domain data) register 1 + * @param data The data to write to the BPR register 1 + */ +static void at32_bpr_write_dt1(uint32_t data) { + CRM->apb1en_bit.pwcen = TRUE; + PWC->ctrl_bit.bpwen = TRUE; + CRM->bpdc_bit.ertcen = TRUE; + + ERTC->wp = SYS_SIMPLE_RESET_BPR_UNLOCK_KEY1; + ERTC->wp = SYS_SIMPLE_RESET_BPR_UNLOCK_KEY2; + ERTC->dt1 = data; + ERTC->wp = SYS_SIMPLE_RESET_BPR_LOCK_KEY; + + CRM->apb1en_bit.pwcen = FALSE; + PWC->ctrl_bit.bpwen = FALSE; + CRM->bpdc_bit.ertcen = FALSE; +} + +/** + * @brief check BPR register 1, if it is equal to SYS_SIMPLE_RESET_BPR_MAGIC, + * clear it and return true, otherwise return false. + * @return true if the BPR register 1 is equal to SYS_SIMPLE_RESET_BPR_MAGIC, false otherwise + */ +bool system_bpr_chk_clear(void) { + CRM->apb1en_bit.pwcen = TRUE; + PWC->ctrl_bit.bpwen = TRUE; + CRM->bpdc_bit.ertcen = TRUE; + + if (ERTC->dt1 == SYS_SIMPLE_RESET_BPR_MAGIC) { + at32_bpr_write_dt1(0); + return true; + } + return false; +} + +/** + * @brief this function handles nmi exception. + * @retval none + */ +void NMI_Handler(void) { +} + +/** + * @brief this function handles hard fault exception. + * @retval none + */ +void HardFault_Handler(void) { + /* go to infinite loop when hard fault exception occurs */ + while (1) { + } +} + +/** + * @brief this function handles memory manage exception. + * @retval none + */ +void MemManage_Handler(void) { + /* go to infinite loop when memory manage exception occurs */ + while (1) { + } +} + +/** + * @brief this function handles bus fault exception. + * @retval none + */ +void BusFault_Handler(void) { + /* go to infinite loop when bus fault exception occurs */ + while (1) { + } +} + +/** + * @brief this function handles usage fault exception. + * @retval none + */ +void UsageFault_Handler(void) { + /* go to infinite loop when usage fault exception occurs */ + while (1) { + } +} + +/** + * @brief this function handles svcall exception. + * @retval none + */ +void SVC_Handler(void) { +} + +/** + * @brief this function handles debug monitor exception. + * @retval none + */ +void DebugMon_Handler(void) { +} + +/** + * @brief this function handles pendsv_handler exception. + * @retval none + */ +void PendSV_Handler(void) { +} + +/** + * @brief this function handles systick handler. + * @retval none + */ +void SysTick_Handler(void) { +} + +/** + * @brief system clock config program + * @note the system clock is configured as follow: + * system clock (sclk) = (hext * pll_ns)/(pll_ms * pll_fr) + * system clock source = HEXT_VALUE + * - hext = 8000000 + * - sclk = 48000000 + * - ahbdiv = 1 + * - ahbclk = 48000000 + * - apb1div = 2 + * - apb1clk = 24000000 + * - apb2div = 1 + * - apb2clk = 48000000 + * - pll_ns = 96 + * - pll_ms = 1 + * - pll_fr = 16 + * @retval none + */ +void system_clock_config_48m(void) { + /* reset crm */ + crm_reset(); + + /* enable pwc periph clock */ + crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE); + + /* config ldo voltage */ + pwc_ldo_output_voltage_set(PWC_LDO_OUTPUT_1V1); + + /* set the flash clock divider */ + flash_clock_divider_set(FLASH_CLOCK_DIV_2); + + /* enable hext */ + crm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE); + + /* wait till hext is ready */ + while (crm_hext_stable_wait() == ERROR) { + } + + /* config pll clock resource + common frequency config list: pll source selected hick or hext(8mhz) + _________________________________________________________________________________________________ + | | | | | | | | | | | + |pll(mhz)| 288 | 252 | 216 | 192 | 180 | 144 | 108 | 72 | 36 | + |________|_________|_________|_________|_________|_________|_________|_________|_________________| + | | | | | | | | | | | + |pll_ns | 144 | 126 | 108 | 96 | 90 | 72 | 108 | 72 | 72 | + | | | | | | | | | | | + |pll_ms | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | + | | | | | | | | | | | + |pll_fr | FR_4 | FR_4 | FR_4 | FR_4 | FR_4 | FR_4 | FR_8 | FR_8 | FR_16| + |________|_________|_________|_________|_________|_________|_________|_________|________|________| + + if pll clock source selects hext with other frequency values, or configure pll to other + frequency values, please use the at32 new clock configuration tool for configuration. */ + crm_pll_config(CRM_PLL_SOURCE_HEXT, 96, 1, CRM_PLL_FR_16); + + /* enable pll */ + crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE); + + /* wait till pll is ready */ + while (crm_flag_get(CRM_PLL_STABLE_FLAG) != SET) { + } + + /* config ahbclk */ + crm_ahb_div_set(CRM_AHB_DIV_1); + + /* config apb2clk */ + crm_apb2_div_set(CRM_APB2_DIV_1); + + /* config apb1clk */ + crm_apb1_div_set(CRM_APB1_DIV_2); + + /* select pll as system clock source */ + crm_sysclk_switch(CRM_SCLK_PLL); + + /* wait till pll is used as system clock source */ + while (crm_sysclk_switch_status_get() != CRM_SCLK_PLL) { + } + + /* update system_core_clock global variable */ + system_core_clock_update(); +} + +/** + * @brief system clock config + * @note the system clock is configured as follow: + * system clock (sclk) = (hext * pll_ns)/(pll_ms * pll_fr) + * system clock source = pll (hext) + * - hext = HEXT_VALUE + * - sclk = 288000000 + * - ahbdiv = 1 + * - ahbclk = 288000000 + * - apb2div = 2 + * - apb2clk = 144000000 + * - apb1div = 2 + * - apb1clk = 144000000 + * - pll_ns = 144 + * - pll_ms = 1 + * - pll_fr = 4 + * @retval none + */ +void system_clock_config_288m(void) { + nvic_priority_group_config(NVIC_PRIORITY_GROUP_4); + + /* reset crm */ + crm_reset(); + + /* enable pwc periph clock */ + crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE); + + /* config ldo voltage */ + pwc_ldo_output_voltage_set(PWC_LDO_OUTPUT_1V3); + + /* set the flash clock divider */ + flash_clock_divider_set(FLASH_CLOCK_DIV_3); + + crm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE); + + /* wait till hext is ready */ + while (crm_hext_stable_wait() == ERROR) { + } + + /* config pll clock resource + common frequency config list: pll source selected hick or hext(8mhz) + _________________________________________________________________________________________________ + | | | | | | | | | | | + |pll(mhz)| 288 | 252 | 216 | 192 | 180 | 144 | 108 | 72 | 36 | + |________|_________|_________|_________|_________|_________|_________|_________|_________________| + | | | | | | | | | | | + |pll_ns | 144 | 126 | 108 | 96 | 90 | 72 | 108 | 72 | 72 | + | | | | | | | | | | | + |pll_ms | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | + | | | | | | | | | | | + |pll_fr | FR_4 | FR_4 | FR_4 | FR_4 | FR_4 | FR_4 | FR_8 | FR_8 | FR_16| + |________|_________|_________|_________|_________|_________|_________|_________|________|________| + + if pll clock source selects hext with other frequency values, or configure pll to other + frequency values, please use the at32 new clock configuration tool for configuration. */ + crm_pll_config(CRM_PLL_SOURCE_HEXT, 144, 1, CRM_PLL_FR_4); + + /* enable pll */ + crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE); + + /* wait till pll is ready */ + while (crm_flag_get(CRM_PLL_STABLE_FLAG) != SET) { + } + + /* config ahbclk */ + crm_ahb_div_set(CRM_AHB_DIV_1); // 288mhz + + /* config apb2clk, the maximum frequency of APB1/APB2 clock is 144 MHz */ + crm_apb2_div_set(CRM_APB2_DIV_2); // 144mhz(288mhz / 2) + + /* config apb1clk, the maximum frequency of APB1/APB2 clock is 144 MHz */ + crm_apb1_div_set(CRM_APB1_DIV_2); // 144mhz(288mhz / 2) + + /* enable auto step mode */ + crm_auto_step_mode_enable(TRUE); + + /* select pll as system clock source */ + crm_sysclk_switch(CRM_SCLK_PLL); + + /* wait till pll is used as system clock source */ + while (crm_sysclk_switch_status_get() != CRM_SCLK_PLL) { + } + + /* disable auto step mode */ + crm_auto_step_mode_enable(FALSE); + + /* update system_core_clock global variable */ + system_core_clock_update(); +} + +/** + * @brief system clock config + * @note the system clock is configured as follow: + * system clock (sclk) = (hext * pll_ns)/(pll_ms * pll_fr) + * system clock source = pll (hext) + * - hext = HEXT_VALUE + * - sclk = 288000000 + * - ahbdiv = 1 + * - ahbclk = 288000000 + * - apb2div = 2 + * - apb2clk = 144000000 + * - apb1div = 2 + * - apb1clk = 144000000 + * - pll_ns = 144 + * - pll_ms = 1 + * - pll_fr = 4 + * @retval none + */ +void ConfigSystemClocks(void) { + // system_clock_config_48m(); + system_clock_config_288m(); +} + +/** + * The state of GPIOB (especially PB0: arm power on) is preserved, + * while other peripherals are reset/clock-gated as much as possible, without using NVIC reset. + */ +void system_simple_reset(void) { + const uint32_t keep_gpiob_clock_mask = CRM_REG_BIT(AT32_GPIO_ARM_POWER_LOCK_CLK); + uint32_t ahb1_reset_mask = + CRM_REG_BIT(CRM_GPIOA_PERIPH_RESET) | + CRM_REG_BIT(CRM_GPIOC_PERIPH_RESET) | + CRM_REG_BIT(CRM_GPIOD_PERIPH_RESET) | + CRM_REG_BIT(CRM_GPIOE_PERIPH_RESET) | + CRM_REG_BIT(CRM_GPIOF_PERIPH_RESET) | + CRM_REG_BIT(CRM_GPIOG_PERIPH_RESET) | + CRM_REG_BIT(CRM_GPIOH_PERIPH_RESET) | + CRM_REG_BIT(CRM_CRC_PERIPH_RESET) | + CRM_REG_BIT(CRM_EDMA_PERIPH_RESET) | + CRM_REG_BIT(CRM_DMA1_PERIPH_RESET) | + CRM_REG_BIT(CRM_DMA2_PERIPH_RESET) | + CRM_REG_BIT(CRM_OTGFS2_PERIPH_RESET); +#if defined(AT32F437xx) + ahb1_reset_mask |= CRM_REG_BIT(CRM_EMAC_PERIPH_RESET); +#endif + + __disable_irq(); + + /* Return to HICK first, then close PLL/HEXT and reset CRM clock tree settings. */ + CRM->ctrl_bit.hicken = TRUE; + while (CRM->ctrl_bit.hickstbl != SET) { + } + CRM->cfg_bit.sclksel = CRM_SCLK_HICK; + while (CRM->cfg_bit.sclksts != CRM_SCLK_HICK) { + } + CRM->ctrl &= ~(0x010D0000U); + CRM->cfg = 0; + CRM->pllcfg = 0x00033002U; + CRM->misc1 = 0; + CRM->misc2 = 0; + + /* Reset AHB peripherals directly, excluding GPIOB to keep PB0 state stable. */ + CRM->ahbrst1 = ahb1_reset_mask; + CRM->ahbrst2 = CRM_REG_BIT(CRM_DVP_PERIPH_RESET) | + CRM_REG_BIT(CRM_OTGFS1_PERIPH_RESET) | + CRM_REG_BIT(CRM_SDIO1_PERIPH_RESET); + CRM->ahbrst3 = CRM_REG_BIT(CRM_XMC_PERIPH_RESET) | + CRM_REG_BIT(CRM_QSPI1_PERIPH_RESET) | + CRM_REG_BIT(CRM_QSPI2_PERIPH_RESET) | + CRM_REG_BIT(CRM_SDIO2_PERIPH_RESET); + CRM->ahbrst1 = 0; + CRM->ahbrst2 = 0; + CRM->ahbrst3 = 0; + + /* Disable all peripheral clocks directly, but keep GPIOB clock for PB0 control path. */ + CRM->ahben1 = keep_gpiob_clock_mask; + CRM->ahben2 = 0; + CRM->ahben3 = 0; + CRM->apb1rst = 0xFFFF; + CRM->apb1rst = 0; + CRM->apb1en = 0; + CRM->apb2rst = 0xFFFF; + CRM->apb2rst = 0; + CRM->apb2en = 0; + CRM->clkint = 0x009F0000U; + + // Write BPR_1 before jump to bootrom to restart. + at32_bpr_write_dt1(SYS_SIMPLE_RESET_BPR_MAGIC); + + // Jump to bootrom + JumpToAnyImage((uint32_t) _stack_end, (uint32_t) _bootrom_start); +} + +// Refer to the code described in the following link to implement the jump. +// https://community.st.com/t5/stm32-mcus-products/jump-to-application-from-bootloader-not-working/td-p/620734 +void __NO_RETURN JumpToAnyImage(uint32_t stack_top, uint32_t entry_point) { + // Disable and clear all pending interrupts in the Bootloader + __disable_irq(); + for (int i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++) { + NVIC->ICER[i] = 0xFFFFFFFF; + NVIC->ICPR[i] = 0xFFFFFFFF; + } + + // Disable SysTick + SysTick->CTRL = 0; + SysTick->LOAD = 0; + SysTick->VAL = 0; + + SCB->VTOR = entry_point; // Update the Vector Table Offset Register (VTOR) + __set_MSP(stack_top); // Set the Main Stack Pointer to the App's stack address + + __DSB(); // Ensure the VTOR and SP operations are complete + __ISB(); // Flush the pipeline because of SP change + + // Re-enable all interrupts before new application running. + __enable_irq(); + + // Run the Application Reset Handler + uint32_t reset = *(uint32_t *)(entry_point + 4); + ((void (*)(void))(reset | 1U))(); + while (1); // No Warning. +} diff --git a/common_arm/sys/sys_hw_at32.h b/common_arm/sys/sys_hw_at32.h new file mode 100644 index 000000000..215f2d5ab --- /dev/null +++ b/common_arm/sys/sys_hw_at32.h @@ -0,0 +1,116 @@ +#ifndef SYS_HW_AT32_H +#define SYS_HW_AT32_H + +#include "common.h" +#include "at32f435_437_misc.h" +#include "at32f435_437_crm.h" +#include "at32f435_437_pwc.h" + +/** + * What's method for system reset we are using? + * 0(default): system_simple_reset() + jump to bootrom + * 1: nvic_system_reset() [NOT IMPLEMENT] + */ +extern uint8_t g_system_reset_method; + +// --- Handlers + +void NMI_Handler(void); + +void HardFault_Handler(void); + +void MemManage_Handler(void); + +void BusFault_Handler(void); + +void UsageFault_Handler(void); + +void SVC_Handler(void); + +void DebugMon_Handler(void); + +void PendSV_Handler(void); + +void SysTick_Handler(void); + +// --- + +void system_clock_config_48m(void); + +void system_clock_config_288m(void); + +void system_simple_reset(void); + +bool system_bpr_chk_clear(void); + +STATIC_FORCE_INLINE main_chip_type_t GetChipType(void) { + return MAIN_CHIP_TYPE_AT32; +} + +STATIC_FORCE_INLINE uint32_t GetChipId(void) { + // DEBUG_IDCODE + return *((uint32_t *) 0xE0042000); +} + +STATIC_FORCE_INLINE uint8_t *GetChipUniqueId(uint8_t *size) { + // See: Unique device ID register, doc 1.3.2 + // The unique device ID is a 96-bit value that is programmed by the manufacturer. + // It is used to uniquely identify each device and can be used for various purposes such as licensing, security, and tracking. + if (size) { + *size = 12; // 96 bits = 12 bytes + } + return (uint8_t *) 0x1FFFF7E8; +} + +STATIC_FORCE_INLINE void ResetChip(void) { + // Which reset method should be used? + if (g_system_reset_method == 0) { + // Call system_simple_reset() to simply reset the state of most peripherals, and jump to boot. + system_simple_reset(); + } else { + // On current hardware, resetting the NVIC will cause the ARM_POWER_ON pin to return to input mode. However, + // it takes 10ms to 25ms from RESET to startup, during which time the device has already been powered off. + // If future hardware support keeping the GPIO level of ARM_POWER_ON during RESET, then NVIC reset can be enabled. + nvic_system_reset(); + } +} + +STATIC_FORCE_INLINE uint32_t GetChipFlashSize(void) { + // See: Flash capacity register, doc 1.3.1 + // Flash storage capacity, measured in KBytes + // For example: 0x0080 = 128KByte + return *((uint32_t *) 0x1FFFF7E0) * 1024; // '<< 10' or '* 1024', Best to reduce the difficulty of understanding. +} + +STATIC_FORCE_INLINE bool CheckRSTWithSRAMRetention(void) { + if (system_bpr_chk_clear()) { + crm_flag_clear(CRM_ALL_RESET_FLAG); + return true; + } + // If enter Standby Mode, the sram will power off. + if (CRM->ctrlsts_bit.lprstf && PWC->ctrlsts_bit.swef && PWC->ctrlsts_bit.sef) { + crm_flag_clear(CRM_ALL_RESET_FLAG); + return false; + } + // WDT reset & WWDT reset + if (CRM->ctrlsts_bit.wwdtrstf || CRM->ctrlsts_bit.wdtrstf) { + crm_flag_clear(CRM_ALL_RESET_FLAG); + return true; + } + // CPU software reset + if (CRM->ctrlsts_bit.swrstf) { + crm_flag_clear(CRM_ALL_RESET_FLAG); + return true; + } + // NRST reset (pin) + // When powered on for the first time, nrstf will also be set, which we need to confirm together with por. + if (CRM->ctrlsts_bit.nrstf && CRM->ctrlsts_bit.porrstf == 0 && CRM->ctrlsts_bit.swrstf == 0) { + crm_flag_clear(CRM_ALL_RESET_FLAG); + return true; + } + // POR/LVR reset flag? Or otherwise case... + crm_flag_clear(CRM_ALL_RESET_FLAG); + return false; +} + +#endif //SYS_HW_AT32_H diff --git a/common_arm/clocks.c b/common_arm/sys/sys_hw_at91.c similarity index 70% rename from common_arm/clocks.c rename to common_arm/sys/sys_hw_at91.c index 6e39eef87..31cc1e37d 100644 --- a/common_arm/clocks.c +++ b/common_arm/sys/sys_hw_at91.c @@ -1,20 +1,9 @@ -//----------------------------------------------------------------------------- -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. // -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. +// Created by dxl on 2026/5/23. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -#include "clocks.h" +#include "sys_apis.h" #include "proxmark3_arm.h" +#include "at91sam7s512.h" void mck_from_pll_to_slck(void) { // switch main clk to slow clk, first CSS then PRES @@ -74,3 +63,31 @@ void mck_from_slck_to_pll(void) { // wait for main clock ready signal while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY)) {}; } + +void ConfigSystemClocks(void) { + // we are using a 16 MHz crystal as the basis for everything + // slow clock runs at 32kHz typical regardless of crystal + + // enable system clock and USB clock + AT91C_BASE_PMC->PMC_SCER |= AT91C_PMC_PCK | AT91C_PMC_UDP; + + // enable the clock to the following peripherals + AT91C_BASE_PMC->PMC_PCER = + (1 << AT91C_ID_PIOA) | + (1 << AT91C_ID_ADC) | + (1 << AT91C_ID_SPI) | + (1 << AT91C_ID_SSC) | + (1 << AT91C_ID_PWMC) | + (1 << AT91C_ID_UDP); + + mck_from_slck_to_pll(); +} + +void __attribute__((noreturn)) JumpToAnyImage(uint32_t stack_top, uint32_t entry_point) { + // Set stack top pointer + __asm("mov sp, %0\n" : : "r"(stack_top)); + // jump to Flash address of the osimage(any image) entry point (LSBit set for thumb mode) + __asm("bx %0\n" : : "r"(((uint32_t)entry_point) | 0x1)); + + while (1); // No warning. +} diff --git a/common_arm/sys/sys_hw_at91.h b/common_arm/sys/sys_hw_at91.h new file mode 100644 index 000000000..cc628a4c2 --- /dev/null +++ b/common_arm/sys/sys_hw_at91.h @@ -0,0 +1,75 @@ +#ifndef SYS_HW_AT91_H +#define SYS_HW_AT91_H + +#include "common.h" +#include "at91sam7s512.h" +#include "proxmark3_arm.h" + +void mck_from_pll_to_slck(void); + +void mck_from_slck_to_pll(void); + +STATIC_FORCE_INLINE main_chip_type_t GetChipType(void) { + return MAIN_CHIP_TYPE_AT91; +} + +STATIC_FORCE_INLINE uint32_t GetChipId(void) { + return *(AT91C_DBGU_CIDR); +} + +STATIC_FORCE_INLINE uint8_t* GetChipUniqueId(uint8_t *size) { + // !!! UNSUPPORTED !!! + if (size) { + *size = 0; + } + return NULL; +} + +STATIC_FORCE_INLINE void ResetChip(void) { + AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; +} + +STATIC_FORCE_INLINE uint32_t GetChipFlashSize(void) { + uint8_t nvpsiz = (GetChipId() & 0xF00) >> 8; + if (nvpsiz == 0) { + return 0; + } + if (nvpsiz == 1) { + return 8 * 1024; + } + if (nvpsiz == 2) { + return 16 * 1024; + } + if (nvpsiz == 3) { + return 32 * 1024; + } + if (nvpsiz == 5) { + return 64 * 1024; + } + if (nvpsiz == 7) { + return 128 * 1024; + } + if (nvpsiz == 9) { + return 256 * 1024; + } + if (nvpsiz == 10) { + return 512 * 1024; + } + if (nvpsiz == 12) { + return 1024 * 1024; + } + // for 'reserved' values, guess 2MB + return 2048 * 1024; +} + +STATIC_FORCE_INLINE bool CheckRSTWithSRAMRetention(void) { + if ((AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_RSTTYP) == AT91C_RSTC_RSTTYP_WATCHDOG || + (AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_RSTTYP) == AT91C_RSTC_RSTTYP_SOFTWARE || + (AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_RSTTYP) == AT91C_RSTC_RSTTYP_USER) { + return true; + } + /* Otherwise, initialize it from scratch */ + return false; +} + +#endif //SYS_HW_AT91_H diff --git a/common_arm/ticks.h b/common_arm/ticks.h deleted file mode 100644 index 4ec730131..000000000 --- a/common_arm/ticks.h +++ /dev/null @@ -1,66 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) Jonathan Westhues, Aug 2005 -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// Timers, Clocks functions used in LF or Legic where you would need detailed time. -//----------------------------------------------------------------------------- - -#ifndef __TICKS_H -#define __TICKS_H - -#include "common.h" - -#ifndef GET_TICKS -#define GET_TICKS GetTicks() -#endif - -void StartTicks(void); -uint32_t GetTicks(void); -uint32_t RAMFUNC GetTicksDelta(uint32_t start); -void WaitUS(uint32_t us); -void WaitTicks(uint32_t ticks); -void StartCountUS(void); -uint32_t RAMFUNC GetCountUS(void); -void StopTicks(void); - - -#ifndef AS_BOOTROM ////////////////////////////////////////////////////////////// -// Bootrom does not require these functions. -// Wrap in #ifndef to avoid accidental bloat of bootrom - -void SpinDelay(int ms); -void SpinDelayUs(int us); -void SpinDelayUsPrecision(int us); // precision 0.6us , running for 43ms before - -void StartTickCount(void); -uint32_t RAMFUNC GetTickCount(void); -uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks); -uint32_t GetTickCountLabel(void); - -void ResetUSClock(void); -void SpinDelayCountUs(uint32_t us); - -void StartCountSspClk(void); -void ResetSspClk(void); -uint32_t RAMFUNC GetCountSspClk(void); -uint32_t RAMFUNC GetCountSspClkDelta(uint32_t start); - -void WaitMS(uint32_t ms); - -#endif // #ifndef AS_BOOTROM - - - -#endif diff --git a/common_arm/ticks/ticks_apis.h b/common_arm/ticks/ticks_apis.h new file mode 100644 index 000000000..84fba2531 --- /dev/null +++ b/common_arm/ticks/ticks_apis.h @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Jonathan Westhues, Aug 2005 +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Timers, Clocks functions used in LF or Legic where you would need detailed time. +//----------------------------------------------------------------------------- + +#ifndef TICKS_H_ +#define TICKS_H_ + +#include "common.h" + +#ifndef GET_TICKS +#define GET_TICKS GetTicks() +#endif + +void StartTicks(void); +uint32_t GetTicks(void); +uint32_t RAMFUNC GetTicksDelta(uint32_t start); +void WaitUS(uint32_t us); +void WaitTicks(uint32_t ticks); +void ResetTicks(void); +void StopTicks(void); + +void StartCountUS(void); +uint32_t RAMFUNC GetCountUS(void); + +void SpinDelayUs(int us); + +#ifndef AS_BOOTROM ////////////////////////////////////////////////////////////// +// Bootrom does not require these functions. +// Wrap in #ifndef to avoid accidental bloat of bootrom + +void SpinDelay(int ms); +void SpinDelayUsPrecision(int us); // precision 0.6us , running for 43ms before + +void StartTickCount(void); +uint32_t RAMFUNC GetTickCount(void); +uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks); +void UpdateTickCountLabel(void); +uint32_t GetTickCountLabel(void); + +// void ResetUSClock(void); No implemented? +// void SpinDelayCountUs(uint32_t us); + +void StartCountSspClk(void); +void ResetSspClk(void); +uint32_t RAMFUNC GetCountSspClk(void); +uint32_t RAMFUNC GetCountSspClkDelta(uint32_t start); + +void WaitMS(uint32_t ms); + +// ------------------------------------------------------------------------- +// Generic precision timer counter, input capture and timestamp counter. +// These primitives back the precise timing / edge-capture needs of the LF +// protocols (e.g. Hitag). They are intentionally generic and platform-agnostic. +// +// The precision counter and timestamp counter both run at 1.5 MHz +// (12 counts = 1 T0 = 8 us, see hitag_common.h for the T0 definition). +// ------------------------------------------------------------------------- + +// Free-running precision counter @ 1.5 MHz (12 counts = 1 T0 = 8 us). +void StartPrecisionCounter(void); // configure + start + reset +void StopPrecisionCounter(void); +void ResetPrecisionCounter(void); // software reset to 0 +uint16_t RAMFUNC GetPrecisionCounter(void); // current count (16-bit) + +// Input capture(LF_EDGE_DETECT): rising/falling edges of an external signal. +void StartLoEdgeCapture(void); // configure + start + reset +void StopLoEdgeCapture(void); // disable capture +void EnableLoEdgeCapture(void); // re-enable + reset (no reconfiguration) +void ResetLoEdgeCapture(void); // software reset +typedef enum { LO_EDGE_NO = 0, LO_EDGE_RISING = 1, LO_EDGE_FALLING = 2 } lo_edge_t; +lo_edge_t RAMFUNC GetLoEdgeCaptureStatus(void); // edge-event flags (reading clears them) +uint16_t RAMFUNC GetLoEdgeCaptureCount(void); // current free-running count +uint16_t RAMFUNC GetLoEdgeCaptureFalling(void); // value captured on the falling edge +uint16_t RAMFUNC GetLoEdgeCaptureRising(void); // value captured on the rising edge + +// Monotonic timestamp counter (free-running + overflow accumulation). +// One 125 kHz carrier period (8 us) equals this many counter ticks at 1.5 MHz. +#define TICKS_PER_CARRIER_PERIOD 12 +void StartTimestamp(void); // configure + start + clear (counter and overflow) +void StopTimestamp(void); +uint32_t RAMFUNC GetTimestamp(void); // monotonic timestamp in 125 kHz carrier periods + +#endif // #ifndef AS_BOOTROM + +#ifdef PM5 +#include "ticks_hw_at32.h" +#else +#include "ticks_hw_at91.h" +#endif + +#endif // TICKS_H_ diff --git a/common_arm/ticks/ticks_core.c b/common_arm/ticks/ticks_core.c new file mode 100644 index 000000000..12ec67065 --- /dev/null +++ b/common_arm/ticks/ticks_core.c @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Jonathan Westhues, Sept 2005 +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Timers, Clocks functions used in LF or Legic where you would need detailed time. +//----------------------------------------------------------------------------- +#include "ticks_apis.h" + +// For OS include +#ifndef AS_BOOTROM +#include "dbprint.h" +#endif + +#ifndef AS_BOOTROM + +// Increments whenever StartTickCount() reconfigures/resets RTTC. +// Callers can use this to detect that previously saved tick deltas are no longer valid. +static uint32_t g_tickcount_label = 0; + +// WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) +void SpinDelay(int ms) { + if (ms > 1390) { + if (g_dbglevel >= DBG_ERROR) Dbprintf(_RED_("Error, SpinDelay called with %i > 1390"), ms); + ms = 1390; + } + // convert to us and call microsecond delay function + SpinDelayUs(ms * 1000); +} + +// Get tick count from start_ticks to now. +uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks) { + uint32_t stop_ticks = GetTickCount(); + if (stop_ticks >= start_ticks) { + return stop_ticks - start_ticks; + } + return (UINT32_MAX - start_ticks) + stop_ticks; +} + +/* + * Call this function within StartTickCount() to increment the tick count label. + * You must do it in all platform implementations. + */ +void UpdateTickCountLabel(void) { + g_tickcount_label++; +} + +/* + * Get current RTTC counter label. + * If counter config changes between calls, the value is incremented. + */ +uint32_t GetTickCountLabel(void) { + return g_tickcount_label; +} + +uint32_t RAMFUNC GetCountSspClkDelta(uint32_t start) { + uint32_t stop = GetCountSspClk(); + if (stop >= start) { + return stop - start; + } + return (UINT32_MAX - start) + stop; +} + +void WaitMS(uint32_t ms) { + WaitTicks((ms & 0x1FFFFF) * 1500); +} + +#endif + +uint32_t RAMFUNC GetTicksDelta(uint32_t start) { + uint32_t stop = GetTicks(); + if (stop >= start) { + return stop - start; + } + return (UINT32_MAX - start) + stop; +} + +// Wait - Spindelay in ticks. +// if called with a high number, this will trigger the WDT... +void WaitTicks(uint32_t ticks) { + if (ticks == 0) return; + ticks += GetTicks(); + while (GetTicks() < ticks); +} + +// Wait / Spindelay in us (microseconds) +// 1us = 1.5ticks. +void WaitUS(uint32_t us) { + WaitTicks((us & 0x3FFFFFFF) * 3 / 2); +} diff --git a/common_arm/ticks/ticks_hw_at32.c b/common_arm/ticks/ticks_hw_at32.c new file mode 100644 index 000000000..4784cb8df --- /dev/null +++ b/common_arm/ticks/ticks_hw_at32.c @@ -0,0 +1,412 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Jonathan Westhues, Sept 2005 +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Timers, Clocks functions used in LF or Legic where you would need detailed time. +//----------------------------------------------------------------------------- +#include "ticks_apis.h" +#include "proxmark3_arm.h" +#include "ticks_hw_at32.h" + +#include "at32f435_437.h" +#include "at32f435_437_misc.h" +#include "at32f435_437_pwc.h" +#include "at32f435_437_ertc.h" + +/** + * SysTick 频率计算,以下计算条件需要严格遵守 AHBCLK = 288mhz 且 systick的时钟输入是 AHBCLK 的8分频的条件 + * + * - AHBCLK = 288,000, 000 = 288mhz + * - systick-clk = 288mhz / 8 = 36,000,000 = 36mhz = 27.7ns + * - systick-val = 24bit = 0xFFFFFF = 16777215 + * - max time = 27.7ns * 16777215 = 464,728.8555us = 464.7288555ms + */ +#define MAX_US_STEP (464728U) + +// timer counts in 27.7ns increments (16777215/36MHz), rounding applies +// WARNING: timer can't measure more than 1.39s (27.7ns * 0xFFFFFF * 3), more loop delay may to decreased accuracy. +void SpinDelayUs(int us) { + uint32_t fac_us = system_core_clock / 8 / 1000000; + uint32_t temp = 0; + SysTick->CTRL &= ~(uint32_t)SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV; // ahbclk div8 = 36mhz + while (us) { + SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; + if (us > MAX_US_STEP) { + SysTick->LOAD = MAX_US_STEP * fac_us; + us -= MAX_US_STEP; + } else { + SysTick->LOAD = us * fac_us; + us = 0; + } + SysTick->VAL = 0x00; + SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; + do { + temp = SysTick->CTRL; + } while ((temp & 0x01) && !(temp & (1 << 16))); + SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; + SysTick->VAL = 0x00; + } +} + +// configCounter() is defined below (outside AS_BOOTROM); forward-declare it so the +// precision/timestamp counters inside the AS_BOOTROM block can reuse it. +static void configCounter(const uint32_t frequency); + +#ifndef AS_BOOTROM + +// timer counts in 27.7ns increments (16777215/36MHz), rounding applies +// WARNING: timer can't measure more than 464.7288555ms (27.7ns * 0xFFFFFF) +void SpinDelayUsPrecision(int us) { + uint32_t fac_us = system_core_clock / 8 / 1000000; + uint32_t temp = 0; + SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; + SysTick->CTRL &= ~(uint32_t)SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV; // ahbclk div8 = 36mhz + SysTick->VAL = 0x00; + SysTick->LOAD = us * fac_us; + SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; + do { + temp = SysTick->CTRL; + } while ((temp & 0x01) && !(temp & (1 << 16))); + SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; + SysTick->VAL = 0x00; +} + +// ------------------------------------------------------------------------- +// Timer lib: 1 kHz: TickCount functions +// +// Precision Test Procedure: +// ti = GetTickCount(); +// SpinDelay(1000); +// ti = GetTickCount() - ti; +// Dbprintf("timer(1s): %d t=%d", ti, GetTickCount()); +// ------------------------------------------------------------------------- + +// Cached tick start value when 'StartTickCount' call. +static volatile uint64_t tick_start_val; + +// from date to timestamp(unix format, UTC zone only) +// we can use 'mktime()' from 'time.h', but more rom space required, so custom first. +// tips: year is full length, such as: 2025, not 25 +static uint64_t mktime_utc_fast(int year, int month, int day, int hour, int minute, int second, uint32_t ms) { + static const uint16_t cum_days[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + + int years = year - 1970; + int leap_count = (years + 2) / 4; + if (year > 2100) leap_count--; + if (year > 2200) leap_count--; + if (year > 2300) leap_count--; + + uint64_t days = years * 365ULL + leap_count; + days += cum_days[month - 1]; + if (month > 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) { + days++; + } + days += (day - 1); + + return (days * 86400ULL + hour * 3600ULL + minute * 60ULL + second) * 1000ULL + ms; +} + +// Start tick count +void StartTickCount(void) { + UpdateTickCountLabel(); + crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE); // enable the pwc clock + pwc_battery_powered_domain_access(TRUE); // allow access to ertc + crm_battery_powered_domain_reset(TRUE); // reset ertc bpr domain + crm_battery_powered_domain_reset(FALSE); + // Select clock source: HEXT = 8mhz, ertc clk = 400khz + // When using an external high-speed crystal oscillator, the clock can be very accurate, + // so calibration does not need to be considered temporarily. + crm_ertc_clock_select(CRM_ERTC_CLOCK_HEXT_DIV_20); + crm_ertc_clock_enable(TRUE); // enable the ertc clock + ertc_reset(); // deinitializes the ertc registers + ertc_wait_update(); // wait for ertc apb registers update + // configure the ertc divider, ertc second(1hz) = ertc_clk / (div_a + 1) * (div_b + 1) + // the subsecond frequency is 3125(from div_b clk), so 1 clk = 0.32ms = 320us, the subsecond will -1 every 0.32ms + ertc_divider_set(127, 3124); // 400000 / (127 + 1) * (3124 + 1) = 1hz + ertc_hour_mode_set(ERTC_HOUR_MODE_24); // configure the ertc hour mode + // set datetime: 2025-08-15 13:00:00, format: YEAR-MONTH-DAY HOUR:MINUTE:SECOND + ertc_date_set(25, 8, 15, 5); // set date + ertc_time_set(13, 0, 0, ERTC_AM); // set time + // update tick start value when 'poweron' + // no need calc, we can hard code cause by 'ertc_date_set' and 'ertc_time_set' is hard code + // calc online: https://www.timestamp-converter.com/ + tick_start_val = 1755262800000ULL; // tick_start_val = mktime_utc_fast(2025, 8, 15, 13, 0, 0, 0); +} + +// Get the current count. +uint32_t RAMFUNC GetTickCount(void) { + ertc_time_type time; + ertc_calendar_get(&time); + return mktime_utc_fast( + // time.year is short length, not full, so 2025 is 25. + time.year + 2000, + // month & day & hour & min & sec is full length + time.month, time.day, time.hour, time.min, time.sec, + // this ms is from 0 -> 1000 of second, not timestamp value + // ms = ((divb + 1) - subsecond * 1000) / (divb + 1) + (3125 - ertc_sub_second_get()) * 1000 / 3125) - tick_start_val; // current - start = tick +} + +// ------------------------------------------------------------------------- +// Timer for iso14443 commands. Uses ssp_clk from FPGA +// ------------------------------------------------------------------------- + +void StartCountSspClk(void) { + crm_periph_clock_enable(CRM_GPIO_PERIPH_COUNT_SSP_CLK, TRUE); + crm_periph_clock_enable(AT32_CRM_TMR_PERIPH_COUNT_SSP_CLK, TRUE); + + // gpio init + gpio_init_type gpio_init_struct = {0}; + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_pins = CRM_GPIO_COUNT_SSP_CLK_PIN; + gpio_init(CRM_GPIO_COUNT_SSP_CLK, &gpio_init_struct); // gpio setup + gpio_pin_mux_config(CRM_GPIO_COUNT_SSP_CLK, CRM_GPIO_COUNT_SSP_CLK_SOURCE, CRM_GPIO_COUNT_SSP_CLK_MUX); // important !!! remap gpio to be timer EXT(CHx) function. + + // timer init + tmr_input_config_type tmr_input_config_struct; + tmr_input_config_struct.input_channel_select = AT32_TMR_COUNT_SSP_CLK_IN_CH; + tmr_input_config_struct.input_mapped_select = TMR_CC_CHANNEL_MAPPED_DIRECT; + tmr_input_config_struct.input_polarity_select = TMR_INPUT_RISING_EDGE; + tmr_input_channel_init(AT32_TMR_COUNT_SSP_CLK, &tmr_input_config_struct, TMR_CHANNEL_INPUT_DIV_1); + tmr_trigger_input_select(AT32_TMR_COUNT_SSP_CLK, TMR_SUB_INPUT_SEL_C2DF2); // select the timer input trigger: C2IF2 + tmr_sub_mode_select(AT32_TMR_COUNT_SSP_CLK, TMR_SUB_EXTERNAL_CLOCK_MODE_A); // select the slave mode: external mode a + tmr_32_bit_function_enable(AT32_TMR_COUNT_SSP_CLK, TRUE); // 32bit enable, reduce the complexity of cascading. + tmr_base_init(AT32_TMR_COUNT_SSP_CLK, UINT32_MAX - 1, 0); // 288mhz, not count increment frequency. + tmr_cnt_dir_set(AT32_TMR_COUNT_SSP_CLK, TMR_COUNT_UP); + // tmr_external_clock_mode2_config(CRM_TMR_COUNT_SSP_CLK, TMR_ES_FREQUENCY_DIV_1, TMR_ES_POLARITY_NON_INVERTED, 0x00); ext引脚而非ch2引脚时,使用此初始化函数 + tmr_counter_enable(AT32_TMR_COUNT_SSP_CLK, TRUE); + + // TODO DXL 可能还得像原先的逻辑那样,跳过8个clock,去同步ssp的frame和时钟,因为我们没有用级联定时器这种操作,理论上 + // 可能只需要同步一次frame的上升和下降,因为在ssp-timode的实现下,frame的上升刚好是在lsb的上升沿去执行的, + // 同步完成之后,理论上下一次clk的上升刚好就是下一帧的msb,这个时候重置一下clk值就刚好是新的一次帧计数?不过,这还不好说,具体得看后续的实现。 +} + +void ResetSspClk(void) { + // tmr_counter_value_set(CRM_TMR_COUNT_SSP_CLK, 0); + AT32_TMR_COUNT_SSP_CLK->cval = 0; +} + +uint32_t RAMFUNC GetCountSspClk(void) { + // return tmr_counter_value_get(CRM_TMR_COUNT_SSP_CLK); + return AT32_TMR_COUNT_SSP_CLK->cval; +} + +// ------------------------------------------------------------------------- +// Precision counter, input capture and timestamp counter. +// See ticks_apis.h for the generic contract. Both the precision counter and +// the timestamp counter run at 1.5 MHz (12 counts = 1 T0 = 8 us). +// ------------------------------------------------------------------------- + +// Timestamp counter overflow count, combined for ~47 min timing. +static uint16_t timestamp_high = 0; + +void StartPrecisionCounter(void) { + // Reuses the 32-bit timer @ 1.5 MHz (same source as StartTicks). + configCounter(1500000); +} + +void StopPrecisionCounter(void) { + tmr_counter_enable(AT32_TMR_PRECISE_COUNTER, FALSE); +} + +void ResetPrecisionCounter(void) { + tmr_counter_value_set(AT32_TMR_PRECISE_COUNTER, 0); +} + +uint16_t RAMFUNC GetPrecisionCounter(void) { + return (uint16_t)tmr_counter_value_get(AT32_TMR_PRECISE_COUNTER); +} + +void StartLoEdgeCapture(void) { + crm_periph_clock_enable(CRM_GPIO_PERIPH_INPUT_CAPTURE, TRUE); + crm_periph_clock_enable(AT32_CRM_TMR_PERIPH_INPUT_CAPTURE, TRUE); + + // GPIO: PB4 -> TMR3_CH1 (input capture on the LF SSC frame signal). + gpio_init_type gpio_init_struct = {0}; + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_pins = CRM_GPIO_INPUT_CAPTURE_PIN; + gpio_init(CRM_GPIO_INPUT_CAPTURE, &gpio_init_struct); // gpio setup + gpio_pin_mux_config(CRM_GPIO_INPUT_CAPTURE, CRM_GPIO_INPUT_CAPTURE_SOURCE, CRM_GPIO_INPUT_CAPTURE_MUX); // remap gpio to TMR3_CH1 + + // Time base: 16-bit counter @ 1.5 MHz (TIMER_CLK / (191 + 1) = 288MHz / 192), + // matching the AT91 TC1 (MCK/32) so that 12 counts = 1 T0 = 8 us. + tmr_reset(AT32_TMR_INPUT_CAPTURE); + tmr_base_init(AT32_TMR_INPUT_CAPTURE, UINT16_MAX, 191); + tmr_cnt_dir_set(AT32_TMR_INPUT_CAPTURE, TMR_COUNT_UP); + + // PWM input mode (dual-edge capture) on CH1 (TI1 = PB4): + // CH1 = direct + falling edge, CH2 = indirect (chained from TI1) + rising edge. + tmr_input_config_type ic = {0}; + ic.input_channel_select = TMR_SELECT_CHANNEL_1; + ic.input_mapped_select = TMR_CC_CHANNEL_MAPPED_DIRECT; + ic.input_polarity_select = TMR_INPUT_FALLING_EDGE; + tmr_pwm_input_config(AT32_TMR_INPUT_CAPTURE, &ic, TMR_CHANNEL_INPUT_DIV_1); + + // Slave reset mode: reset the counter on the CH1 (falling) edge, so C1DT holds + // the period since the previous falling edge (matches AT91 ABETRG + ETRGEDG_FALLING). + tmr_trigger_input_select(AT32_TMR_INPUT_CAPTURE, TMR_SUB_INPUT_SEL_C1DF1); + tmr_sub_mode_select(AT32_TMR_INPUT_CAPTURE, TMR_SUB_RESET_MODE); + tmr_sub_sync_mode_set(AT32_TMR_INPUT_CAPTURE, TRUE); + + tmr_counter_value_set(AT32_TMR_INPUT_CAPTURE, 0); + tmr_counter_enable(AT32_TMR_INPUT_CAPTURE, TRUE); +} + +void StopLoEdgeCapture(void) { + tmr_counter_enable(AT32_TMR_INPUT_CAPTURE, FALSE); +} + +void EnableLoEdgeCapture(void) { + tmr_counter_value_set(AT32_TMR_INPUT_CAPTURE, 0); + tmr_counter_enable(AT32_TMR_INPUT_CAPTURE, TRUE); +} + +void ResetLoEdgeCapture(void) { + tmr_counter_value_set(AT32_TMR_INPUT_CAPTURE, 0); +} + +uint16_t RAMFUNC GetLoEdgeCaptureCount(void) { + return (uint16_t)tmr_counter_value_get(AT32_TMR_INPUT_CAPTURE); +} + +uint16_t RAMFUNC GetLoEdgeCaptureFalling(void) { + // The falling-edge value is captured on CH1 (C1DT). + return (uint16_t)tmr_channel_value_get(AT32_TMR_INPUT_CAPTURE, TMR_SELECT_CHANNEL_1); +} + +uint16_t RAMFUNC GetLoEdgeCaptureRising(void) { + // The rising-edge value is captured on CH2 (C2DT). + return (uint16_t)tmr_channel_value_get(AT32_TMR_INPUT_CAPTURE, TMR_SELECT_CHANNEL_2); +} + +lo_edge_t RAMFUNC GetLoEdgeCaptureStatus(void) { + // Reading the status clears the edge-event flags (matches AT91 TC_SR semantics). + uint32_t ists = AT32_TMR_INPUT_CAPTURE->ists; + // Only clear the overflow flag if it is set, to avoid clearing the edge-event flags. + if (ists & TMR_OVF_FLAG) { + // Clear the overflow flag to avoid repeated interrupts. + AT32_TMR_INPUT_CAPTURE->ists = ~TMR_OVF_FLAG; + } + if (ists & INPUT_CAPTURE_EVT_RISING_EDGE) { + AT32_TMR_INPUT_CAPTURE->ists = ~INPUT_CAPTURE_EVT_RISING_EDGE; + return LO_EDGE_RISING; + } + if (ists & INPUT_CAPTURE_EVT_FALLING_EDGE) { + AT32_TMR_INPUT_CAPTURE->ists = ~INPUT_CAPTURE_EVT_FALLING_EDGE; + return LO_EDGE_FALLING; + } + return LO_EDGE_NO; +} + +void StartTimestamp(void) { + // TMR6: basic 16-bit timer, free-running @ 1.5 MHz. + crm_periph_clock_enable(AT32_CRM_TMR_PERIPH_TIMESTAMP, TRUE); + // APB1 = 144 MHz, divX = (144 MHz / 1.5 MHz) * 2 - 1 = 191 (see configCounter()). + tmr_base_init(AT32_TMR_TIMESTAMP, UINT16_MAX, 191); + tmr_cnt_dir_set(AT32_TMR_TIMESTAMP, TMR_COUNT_UP); + tmr_counter_value_set(AT32_TMR_TIMESTAMP, 0); + tmr_counter_enable(AT32_TMR_TIMESTAMP, TRUE); + timestamp_high = 0; +} + +void StopTimestamp(void) { + tmr_counter_enable(AT32_TMR_TIMESTAMP, FALSE); +} + +uint32_t RAMFUNC GetTimestamp(void) { + if (tmr_flag_get(AT32_TMR_TIMESTAMP, TMR_OVF_FLAG)) { + tmr_flag_clear(AT32_TMR_TIMESTAMP, TMR_OVF_FLAG); + timestamp_high++; + } + uint16_t cv = (uint16_t)tmr_counter_value_get(AT32_TMR_TIMESTAMP); + return (((uint32_t)timestamp_high << 16) + cv) / TICKS_PER_CARRIER_PERIOD; +} + +#endif // #ifndef AS_BOOTROM + +/** + * Configure the timer to count up at the specified frequency. + * @param frequency the frequency of timer running. + */ +static void configCounter(const uint32_t frequency) { + crm_periph_clock_enable(AT32_CRM_TMR_PERIPH_32B_TIMER_CLK, TRUE); + + // AT32 has a 32-bit timer, perhaps we can achieve higher counting time without connecting the timer? + tmr_32_bit_function_enable(AT32_TMR_32B_TIMER, TRUE); + + // TODO DXL 注意,如果apb1的预分频系数不是1,那么TIMER5的时钟速度会是apb1的两倍,这里记录下来,后期开发可能会遇到,如果完成移植,可将此段注释删除 + // See at32f435 manual reference 4.1.3 + // The timer uses APB1/2 as the clock. In particular, when the APB pre division coefficient is 1, + // the clock frequency of the timer is equal to the clock frequency of APB1/2; + // When the APB prescaler coefficient is not 1, the clock frequency of the timer is equal to twice the APB1/2 clock frequency. + // So, if we are using not apb from ahb/1, must to div2. +#define FREQUENCY_APB1 144000000UL // apb1 = ahb/2 = 144mhz, apb1*2 = TIMER_CLK, TIMER_CLK/192(divX) = 1.5mhz + const uint32_t divX = (FREQUENCY_APB1 / frequency) * 2 - 1; + tmr_base_init(AT32_TMR_32B_TIMER, UINT32_MAX - 1, divX); + + tmr_cnt_dir_set(AT32_TMR_32B_TIMER, TMR_COUNT_UP); + tmr_counter_enable(AT32_TMR_32B_TIMER, TRUE); +} + +// ------------------------------------------------------------------------- +// microseconds timer +// 1us = 1tick +// ------------------------------------------------------------------------- + +void StartCountUS(void) { + // see: https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/clocks.md#occasional-tc0tc1--countus-functions + configCounter(1000000); // 1 MHZ +} + +uint32_t RAMFUNC GetCountUS(void) { + // TODO DXL maybe no function call is a good idea? + // If it affects accuracy, you can consider directly reading the register. + // return AT32_TMR_32B_TIMER->cval; + return tmr_counter_value_get(AT32_TMR_32B_TIMER); +} + +// ------------------------------------------------------------------------- +// Timer for bitbanging, or LF stuff when you need a very precise timer +// 1us = 1.5ticks +// ------------------------------------------------------------------------- + +void StartTicks(void) { + // see: https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/clocks.md#occasional-tc0tc1--ticks-functions + configCounter(1500000); // 1.5 MHz +} + +// Reset the count value to 0 +void ResetTicks(void) { + tmr_counter_value_set(AT32_TMR_32B_TIMER, 0); +} + +void StopTicks(void) { + tmr_counter_enable(AT32_TMR_32B_TIMER, FALSE); + crm_periph_clock_enable(AT32_CRM_TMR_PERIPH_32B_TIMER_CLK, FALSE); + // TODO DXL 也许需要在这里停止 其他定时器,因为PM3原本的代码有这个设计,但是我们需要查一下用处,看看是否能这么做 +} + +uint32_t GetTicks(void) { + // TODO DXL maybe no function call is a good idea? + // If it affects accuracy, you can consider directly reading the register. + // return AT32_TMR_32B_TIMER->cval; + return tmr_counter_value_get(AT32_TMR_32B_TIMER); +} diff --git a/common_arm/ticks/ticks_hw_at32.h b/common_arm/ticks/ticks_hw_at32.h new file mode 100644 index 000000000..6d7c7bbeb --- /dev/null +++ b/common_arm/ticks/ticks_hw_at32.h @@ -0,0 +1,44 @@ +// +// Created by dxl on 2026/2/7. +// + +#ifndef TICKS_HW_AT32_H +#define TICKS_HW_AT32_H + +#include "at32f435_437_crm.h" +#include "at32f435_437_tmr.h" + +// TODO DXL 用 TIMER2 的 ch2 来统计来自于外部ssp-clk的时钟数量 +// 用于 at32 不支持类似 at91 那种 gpio 的输入直接两个外设就能同时使用的情况,所以必须要将实际上 ssp-clk 的脚,连接到 ch2 上 +// 这样子 TIMER2 选中为外部时钟输入模式时,才能最终链接 SSP 和 TMR + +// ssp clk counter +#define AT32_CRM_TMR_PERIPH_COUNT_SSP_CLK CRM_TMR2_PERIPH_CLOCK +#define AT32_TMR_COUNT_SSP_CLK TMR2 +#define AT32_TMR_COUNT_SSP_CLK_IN_CH TMR_SELECT_CHANNEL_2 + +// 32bit timer +#define AT32_CRM_TMR_PERIPH_32B_TIMER_CLK CRM_TMR5_PERIPH_CLOCK +#define AT32_TMR_32B_TIMER TMR5 + +// Input capture edge-event flags (single-bit masks in the TMR status register). +// PWM input mode: CH1 = falling edge, CH2 = rising edge (see StartInputCapture). +#define INPUT_CAPTURE_EVT_RISING_EDGE TMR_C2_FLAG // CH2 rising-edge capture event +#define INPUT_CAPTURE_EVT_FALLING_EDGE TMR_C1_FLAG // CH1 falling-edge capture event + +// Precision free-running counter @ 1.5MHz. +// Reuses the 32-bit timer (same source as StartTicks / StartCountUS). +#define AT32_CRM_TMR_PERIPH_PRECISE_COUNTER AT32_CRM_TMR_PERIPH_32B_TIMER_CLK +#define AT32_TMR_PRECISE_COUNTER AT32_TMR_32B_TIMER + +// Monotonic timestamp counter @ 1.5MHz (16-bit + software overflow tracking). +// Uses TMR6 (a basic 16-bit timer, unused elsewhere in the project). +#define AT32_CRM_TMR_PERIPH_TIMESTAMP CRM_TMR6_PERIPH_CLOCK +#define AT32_TMR_TIMESTAMP TMR6 + +// Input capture (CH1 rising + CH2 falling on the same input pin). +// Input pin is PB4 = TMR3_CH1 (the LF SSC frame signal). +#define AT32_CRM_TMR_PERIPH_INPUT_CAPTURE CRM_TMR3_PERIPH_CLOCK +#define AT32_TMR_INPUT_CAPTURE TMR3 + +#endif //TICKS_HW_AT32_H diff --git a/common_arm/ticks.c b/common_arm/ticks/ticks_hw_at91.c similarity index 65% rename from common_arm/ticks.c rename to common_arm/ticks/ticks_hw_at91.c index 6d49ef278..b1c186aa3 100644 --- a/common_arm/ticks.c +++ b/common_arm/ticks/ticks_hw_at91.c @@ -16,14 +16,39 @@ //----------------------------------------------------------------------------- // Timers, Clocks functions used in LF or Legic where you would need detailed time. //----------------------------------------------------------------------------- -#include "ticks.h" - +#include "ticks_apis.h" #include "proxmark3_arm.h" -#ifndef AS_BOOTROM -#include "dbprint.h" -#endif +// timer counts in 21.3us increments (1024/48MHz), rounding applies +// WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) +void SpinDelayUs(int us) { + int ticks = ((MCK / 1000000) * us + 512) >> 10; + + // Borrow a PWM unit for my real-time clock + AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); + + // 48 MHz / 1024 gives 46.875 kHz + AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); // Channel Mode Register + AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; // Channel Duty Cycle Register + AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; // Channel Period Register + + uint16_t end = AT91C_BASE_PWMC_CH0->PWMC_CCNTR + ticks; + if (end == 0) { // AT91C_BASE_PWMC_CH0->PWMC_CCNTR is never == 0 + end++; // so we have to end++ to avoid inivity loop + } + + for (;;) { + uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + + if (now == end) { + return; + } + + WDT_HIT(); + } +} + #ifndef AS_BOOTROM // timer counts in 666ns increments (32/48MHz), rounding applies @@ -41,7 +66,7 @@ void SpinDelayUsPrecision(int us) { uint16_t end = AT91C_BASE_PWMC_CH0->PWMC_CCNTR + ticks; if (end == 0) { // AT91C_BASE_PWMC_CH0->PWMC_CCNTR is never == 0 - end++; // so we have to end++ to avoid inivity loop + end++; // so we have to end++ to avoid inivity loop } for (;;) { @@ -55,59 +80,17 @@ void SpinDelayUsPrecision(int us) { } } -// timer counts in 21.3us increments (1024/48MHz), rounding applies -// WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) -void SpinDelayUs(int us) { - int ticks = ((MCK / 1000000) * us + 512) >> 10; - - // Borrow a PWM unit for my real-time clock - AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); - - // 48 MHz / 1024 gives 46.875 kHz - AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); // Channel Mode Register - AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; // Channel Duty Cycle Register - AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; // Channel Period Register - - uint16_t end = AT91C_BASE_PWMC_CH0->PWMC_CCNTR + ticks; - if (end == 0) { // AT91C_BASE_PWMC_CH0->PWMC_CCNTR is never == 0 - end++; // so we have to end++ to avoid inivity loop - } - - for (;;) { - uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; - - if (now == end) { - return; - } - WDT_HIT(); - } -} - -// WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) -void SpinDelay(int ms) { - if (ms > 1390) { - if (g_dbglevel >= DBG_ERROR) Dbprintf(_RED_("Error, SpinDelay called with %i > 1390"), ms); - ms = 1390; - } - // convert to us and call microsecond delay function - SpinDelayUs(ms * 1000); -} // ------------------------------------------------------------------------- -// timer lib -// ------------------------------------------------------------------------- -// test procedure: +// Timer lib: 1 kHz: TickCount functions // +// Precision Test Procedure: // ti = GetTickCount(); // SpinDelay(1000); // ti = GetTickCount() - ti; // Dbprintf("timer(1s): %d t=%d", ti, GetTickCount()); -// Increments whenever StartTickCount() reconfigures/resets RTTC. -// Callers can use this to detect that previously saved tick deltas are no longer valid. -static uint32_t g_tickcount_label = 0; - +// ------------------------------------------------------------------------- void StartTickCount(void) { - g_tickcount_label++; - + UpdateTickCountLabel(); // This timer is based on the slow clock. The slow clock frequency is between 22kHz and 40kHz. // We can determine the actual slow clock frequency by looking at the Main Clock Frequency Register. while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available... @@ -117,29 +100,11 @@ void StartTickCount(void) { // note: worst case precision is approx 2.5% } -/* -* Get the current count. -*/ +// Get the current count. uint32_t RAMFUNC GetTickCount(void) { return AT91C_BASE_RTTC->RTTC_RTVR; } -uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks) { - uint32_t stop_ticks = AT91C_BASE_RTTC->RTTC_RTVR; - if (stop_ticks >= start_ticks) { - return stop_ticks - start_ticks; - } - return (UINT32_MAX - start_ticks) + stop_ticks; -} - -/* -* Get current RTTC counter label. -* If counter config changes between calls, the value is incremented. -*/ -uint32_t GetTickCountLabel(void) { - return g_tickcount_label; -} - // ------------------------------------------------------------------------- // Timer for iso14443 commands. Uses ssp_clk from FPGA // ------------------------------------------------------------------------- @@ -187,20 +152,20 @@ void StartCountSspClk(void) { // synchronize the counter with the ssp_frame signal. // Note: FPGA must be in a FPGA mode with SSC transfer, otherwise SSC_FRAME and SSC_CLK signals would not be present // - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 1st ssp_clk after start of frame - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 2nd ssp_clk after start of frame + while (Gpio_SSC_FRAME_Read()); // wait for ssp_frame to be low + while (!(Gpio_SSC_FRAME_Read())); // wait for ssp_frame to go high (start of frame) + while (!(Gpio_SSC_CLK_Read())); // wait for ssp_clk to go high; 1st ssp_clk after start of frame + while (Gpio_SSC_CLK_Read()); // wait for ssp_clk to go low; + while (!(Gpio_SSC_CLK_Read())); // wait for ssp_clk to go high; 2nd ssp_clk after start of frame if ((AT91C_BASE_SSC->SSC_RFMR & SSC_FRAME_MODE_BITS_IN_WORD(32)) == SSC_FRAME_MODE_BITS_IN_WORD(16)) { // 16bit frame - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 3rd ssp_clk after start of frame - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 4th ssp_clk after start of frame - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 5th ssp_clk after start of frame - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 6th ssp_clk after start of frame + while (Gpio_SSC_CLK_Read()); // wait for ssp_clk to go low; + while (!(Gpio_SSC_CLK_Read())); // wait for ssp_clk to go high; 3rd ssp_clk after start of frame + while (Gpio_SSC_CLK_Read()); // wait for ssp_clk to go low; + while (!(Gpio_SSC_CLK_Read())); // wait for ssp_clk to go high; 4th ssp_clk after start of frame + while (Gpio_SSC_CLK_Read()); // wait for ssp_clk to go low; + while (!(Gpio_SSC_CLK_Read())); // wait for ssp_clk to go high; 5th ssp_clk after start of frame + while (Gpio_SSC_CLK_Read()); // wait for ssp_clk to go low; + while (!(Gpio_SSC_CLK_Read())); // wait for ssp_clk to go high; 6th ssp_clk after start of frame } // note: up to now two ssp_clk rising edges have passed since the rising edge of ssp_frame @@ -215,6 +180,7 @@ void StartCountSspClk(void) { // Therefore may need to wait a little bit before we can use the counter. while (AT91C_BASE_TC2->TC_CV > 0); } + void ResetSspClk(void) { //enable clock of timer and software trigger AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; @@ -233,22 +199,129 @@ uint32_t RAMFUNC GetCountSspClk(void) { return tmp_count; } -uint32_t RAMFUNC GetCountSspClkDelta(uint32_t start) { - uint32_t stop = GetCountSspClk(); - if (stop >= start) { - return stop - start; - } - return (UINT32_MAX - start) + stop; +// ------------------------------------------------------------------------- +// Precision counter (TC0), input capture (TC1) and timestamp (TC2). +// These are used by the LF protocols (e.g. Hitag) and are configured at +// 1.5 MHz (MCK/32), so 12 counts = 1 T0 = 8 us. +// ------------------------------------------------------------------------- + +// TC2 overflow count, combined with the TC2 counter for ~47 min timing. +static uint16_t timestamp_high = 0; + +void StartPrecisionCounter(void) { + // Enable peripheral clock for TC0 (precision counter). + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0); + + // Disable TC0 before reconfiguration. + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + + // TC0: capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers (free-running). + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV != 0) {}; // wait until the reset takes effect } -void WaitMS(uint32_t ms) { - WaitTicks((ms & 0x1FFFFF) * 1500); +void StopPrecisionCounter(void) { + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; +} + +void ResetPrecisionCounter(void) { + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV != 0) {}; +} + +uint16_t RAMFUNC GetPrecisionCounter(void) { + return (uint16_t)AT91C_BASE_TC0->TC_CV; +} + +void StartLoEdgeCapture(void) { + // Enable peripheral clock for TC1 (input capture). + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC1); + + // Route SSC_FRAME to the timer input (TIOA) so its edges can be captured by TC1. + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + + // Disable TC1 before reconfiguration. + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // TC1: capture mode, default timer source = MCK/32 (TIMER_CLOCK3), + // TIOA is external trigger, load RA on rising edge, load RB on falling edge. + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK // use MCK/32 (TIMER_CLOCK3) + | AT91C_TC_ABETRG // TIOA is used as an external trigger + | AT91C_TC_ETRGEDG_FALLING // external trigger on falling edge + | AT91C_TC_LDRA_RISING // load RA on rising edge of TIOA + | AT91C_TC_LDRB_FALLING; // load RB on falling edge of TIOA + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + while (AT91C_BASE_TC1->TC_CV != 0) {}; // wait until the reset takes effect +} + +void StopLoEdgeCapture(void) { + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; +} + +void EnableLoEdgeCapture(void) { + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; +} + +void ResetLoEdgeCapture(void) { + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; +} + +uint16_t RAMFUNC GetLoEdgeCaptureCount(void) { + return (uint16_t)AT91C_BASE_TC1->TC_CV; +} + +lo_edge_t RAMFUNC GetLoEdgeCaptureStatus(void) { + if (AT91C_BASE_TC1->TC_SR & INPUT_CAPTURE_EVT_RISING_EDGE) { + return LO_EDGE_RISING; + } + if (AT91C_BASE_TC1->TC_SR & INPUT_CAPTURE_EVT_FALLING_EDGE) { + return LO_EDGE_FALLING; + } + return LO_EDGE_NO; +} + +uint16_t RAMFUNC GetLoEdgeCaptureFalling(void) { + return (uint16_t)AT91C_BASE_TC1->TC_RB; +} + +uint16_t RAMFUNC GetLoEdgeCaptureRising(void) { + return (uint16_t)AT91C_BASE_TC1->TC_RA; +} + +void StartTimestamp(void) { + // Enable peripheral clock for TC2 (timestamp). + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC2); + + // Disable TC2 before reconfiguration. + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; + + // TC2: capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers (free-running). + AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + while (AT91C_BASE_TC2->TC_CV != 0) {}; // wait until the reset takes effect + + // Reset the overflow accumulator. + timestamp_high = 0; +} + +void StopTimestamp(void) { + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; +} + +uint32_t RAMFUNC GetTimestamp(void) { + // Reading TC_SR clears the COVFS overflow flag. + if (AT91C_BASE_TC2->TC_SR & AT91C_TC_COVFS) { + timestamp_high++; + } + return (((uint32_t)timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / TICKS_PER_CARRIER_PERIOD; } #endif // #ifndef AS_BOOTROM // ------------------------------------------------------------------------- // microseconds timer +// 1us = 1tick // ------------------------------------------------------------------------- void StartCountUS(void) { AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); @@ -281,9 +354,20 @@ uint32_t RAMFUNC GetCountUS(void) { return ((uint32_t)AT91C_BASE_TC1->TC_CV) * 0x8000 + (((uint32_t)AT91C_BASE_TC0->TC_CV) * 2) / 3; } +// Maybe we can make it a static inline function, but to avoid possible compiler quirks, +// it's best not to do so, otherwise it may increase the time wasted on stack entry and exit due to not expanding the inline function, +// leading to synchronization zeroing failure! +#define WaitSyncTicks() \ + /* synchronized startup procedure */ \ + while (AT91C_BASE_TC0->TC_CV > 0); /* wait until TC0 returned to zero */ \ + while (AT91C_BASE_TC0->TC_CV < 2); /* and has started (TC_CV > TC_RA, now TC1 is cleared) */ \ + /* return to zero */ \ + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; \ + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; \ + while (AT91C_BASE_TC0->TC_CV > 0); // ------------------------------------------------------------------------- -// Timer for bitbanging, or LF stuff when you need a very precis timer +// Timer for bitbanging, or LF stuff when you need a very precise timer // 1us = 1.5ticks // ------------------------------------------------------------------------- void StartTicks(void) { @@ -309,15 +393,27 @@ void StartTicks(void) { AT91C_BASE_TC0->TC_RA = 1; // clear carry bit on next clock cycle AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // reset and re-enable timer - // synchronized startup procedure - while (AT91C_BASE_TC0->TC_CV > 0); // wait until TC0 returned to zero - while (AT91C_BASE_TC0->TC_CV < 2); // and has started (TC_CV > TC_RA, now TC1 is cleared) - - // return to zero - AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV > 0); + WaitSyncTicks(); } + +// Reset the count value to 0 for TC0 & TC1 +void ResetTicks(void) { + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + WaitSyncTicks(); +} + +// stop clock +void StopTicks(void) { + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; // TODO StartTicks() did not use TC2, is this code worthless? +} + uint32_t GetTicks(void) { uint32_t hi, lo; @@ -328,32 +424,3 @@ uint32_t GetTicks(void) { return (hi << 16) | lo; } - -uint32_t RAMFUNC GetTicksDelta(uint32_t start) { - uint32_t stop = GetTicks(); - if (stop >= start) { - return stop - start; - } - return (UINT32_MAX - start) + stop; -} - -// Wait - Spindelay in ticks. -// if called with a high number, this will trigger the WDT... -void WaitTicks(uint32_t ticks) { - if (ticks == 0) return; - ticks += GetTicks(); - while (GetTicks() < ticks); -} - -// Wait / Spindelay in us (microseconds) -// 1us = 1.5ticks. -void WaitUS(uint32_t us) { - WaitTicks((us & 0x3FFFFFFF) * 3 / 2); -} - -// stop clock -void StopTicks(void) { - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; -} diff --git a/common_arm/ticks/ticks_hw_at91.h b/common_arm/ticks/ticks_hw_at91.h new file mode 100644 index 000000000..058a0dced --- /dev/null +++ b/common_arm/ticks/ticks_hw_at91.h @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Timers / Clocks HAL: AT91 (SAM7S) hardware definitions. +//----------------------------------------------------------------------------- + +#ifndef TICKS_HW_AT91_H +#define TICKS_HW_AT91_H + +#include "at91sam7s512.h" + +// Input capture edge-event flags (single-bit masks in the TC1 status register). +// On AT91, reading TC_SR clears these flags automatically. +#define INPUT_CAPTURE_EVT_RISING_EDGE AT91C_TC_LDRAS // rising-edge load (RA) event +#define INPUT_CAPTURE_EVT_FALLING_EDGE AT91C_TC_LDRBS // falling-edge load (RB) event + +#endif // TICKS_HW_AT91_H diff --git a/common_arm/usb_cdc.h b/common_arm/usb/usb_cdc_apis.h similarity index 62% rename from common_arm/usb_cdc.h rename to common_arm/usb/usb_cdc_apis.h index 2df1acc1e..3f6a5d3de 100644 --- a/common_arm/usb_cdc.h +++ b/common_arm/usb/usb_cdc_apis.h @@ -13,19 +13,13 @@ // // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- -// at91sam7s USB CDC device implementation -// based on the "Basic USB Example" from ATMEL (doc6123.pdf) +// 20250814: Abstract definition, without any platform related information. //----------------------------------------------------------------------------- -#ifndef _USB_CDC_H_ -#define _USB_CDC_H_ +#ifndef USB_CDC_H_ +#define USB_CDC_H_ #include "common.h" -#include "at91sam7s512.h" - -#define AT91C_USB_EP_CONTROL_SIZE 8 -#define AT91C_USB_EP_OUT_SIZE 64 -#define AT91C_USB_EP_IN_SIZE 64 void usb_disable(void); void usb_enable(void); @@ -34,23 +28,14 @@ bool usb_poll(void); uint16_t usb_available_length(void); bool usb_poll_validate_length(void); uint32_t usb_read(uint8_t *data, size_t len); -int usb_write(const uint8_t *data, const size_t len); +int usb_write(const uint8_t *data, size_t len); + int async_usb_write_start(void); void async_usb_write_pushByte(uint8_t data); bool async_usb_write_requestWrite(void); int async_usb_write_stop(void); -bool usb_read_ng_has_buffered_data(void); -uint32_t usb_read_ng(uint8_t *data, size_t len); + void usb_update_serial(uint64_t newSerialNumber); +void usb_get_ep_size(uint32_t *epCtl, uint32_t *epIn, uint32_t *epOut); -void SetUSBreconnect(int value); -int GetUSBreconnect(void); -void SetUSBconfigured(int value); -int GetUSBconfigured(void); - -void AT91F_USB_SendData(AT91PS_UDP pudp, const char *pData, uint32_t length); -void AT91F_USB_SendZlp(AT91PS_UDP pudp); -void AT91F_USB_SendStall(AT91PS_UDP pudp); -void AT91F_CDC_Enumerate(void); - -#endif // _USB_CDC_H_ +#endif // USB_CDC_H_ diff --git a/common_arm/usb/usb_cdc_at32.c b/common_arm/usb/usb_cdc_at32.c new file mode 100644 index 000000000..8f3416158 --- /dev/null +++ b/common_arm/usb/usb_cdc_at32.c @@ -0,0 +1,636 @@ +#include "pm3_cmd.h" +#include "ticks_apis.h" +#include "usb_cdc_apis.h" +#include "usb_read_ng.h" +#include "usb_cdc_desc.h" + +#include "at32f435_437_crm.h" +#include "at32f435_437_acc.h" +#include "at32f435_437_gpio.h" +#include "at32f435_437_misc.h" +#include "usb_conf.h" +#include "usb_core.h" +#include "usbd_int.h" +#include "cdc_class.h" + +static otg_core_type otg_core_struct; +static usbd_core_type *udev = &(otg_core_struct.dev); +static usbd_desc_t vp_desc; + +/** + * @brief get device descriptor + * @retval usbd_desc + */ +static usbd_desc_t *get_device_descriptor(void) { + // Must be static !!!!!! + static usbd_desc_t device_descriptor = { + .length = sizeof(devDescriptor), + .descriptor = (uint8_t *) devDescriptor + }; + return &device_descriptor; +} + +/** + * @brief get device qualifier + * @retval usbd_desc + */ +static usbd_desc_t *get_device_qualifier(void) { + return NULL; +} + +/** + * @brief get config descriptor + * @retval usbd_desc + */ +static usbd_desc_t *get_device_configuration(void) { + // Must be static !!!!!! + static usbd_desc_t config_descriptor = { + .length = sizeof(cfgDescriptor), + .descriptor = (uint8_t *) cfgDescriptor + }; + return &config_descriptor; +} + +/** + * @brief get other speed descriptor + * @retval usbd_desc + */ +static usbd_desc_t *get_device_other_speed(void) { + return NULL; +} + +/** + * @brief get lang id descriptor + * @retval usbd_desc + */ +static usbd_desc_t *get_device_lang_id(void) { + // Must be static !!!!!! + static usbd_desc_t langid_descriptor = { + .length = sizeof(StrLanguageCodes), + .descriptor = (uint8_t *) StrLanguageCodes + }; + return &langid_descriptor; +} + + +/** + * @brief get manufacturer descriptor + * @retval usbd_desc + */ +static usbd_desc_t *get_device_manufacturer_string(void) { + vp_desc.length = StrManufacturer[0]; + vp_desc.descriptor = (uint8_t *) StrManufacturer; + return &vp_desc; +} + +/** + * @brief get product descriptor + * @retval usbd_desc + */ +static usbd_desc_t *get_device_product_string(void) { + vp_desc.length = StrProduct[0]; + vp_desc.descriptor = (uint8_t *) StrProduct; + return &vp_desc; +} + +/** + * @brief get serial descriptor + * @retval usbd_desc + */ +static usbd_desc_t *get_device_serial_string(void) { + // Must be static !!!!!! + static usbd_desc_t serial_descriptor = { + .length = sizeof(StrSerialNumber), + .descriptor = (uint8_t *) StrSerialNumber + }; + return &serial_descriptor; +} + +/** + * @brief get interface descriptor + * @retval usbd_desc + */ +static usbd_desc_t *get_device_interface_string(void) { + return NULL; +} + +/** + * @brief get device config descriptor + * @retval usbd_desc + */ +static usbd_desc_t *get_device_config_string(void) { + return NULL; +} + +/** + * @brief get device config descriptor + * @retval usbd_desc + */ +static usbd_desc_t *get_winusb_os_string(void) { + vp_desc.length = StrMS_OSDescriptor[0]; + vp_desc.descriptor = (uint8_t *) StrMS_OSDescriptor; + return &vp_desc; +} + +/** + * @brief device descriptor handler structure + */ +static usbd_desc_handler cdc_desc_handler = +{ + .get_device_descriptor = get_device_descriptor, + .get_device_qualifier = get_device_qualifier, + .get_device_configuration = get_device_configuration, + .get_device_other_speed = get_device_other_speed, + .get_device_lang_id = get_device_lang_id, + // --- + .get_device_manufacturer_string = get_device_manufacturer_string, + .get_device_product_string = get_device_product_string, + .get_device_serial_string = get_device_serial_string, + .get_device_interface_string = get_device_interface_string, + .get_device_config_string = get_device_config_string, + // --- + .get_device_winusb_os_string = get_winusb_os_string, + .get_device_winusb_os_feature = NULL, + .get_device_winusb_os_property = NULL +}; + + +/** + * @brief usb 48M clock select + * @param clk_s:USB_CLK_HICK, USB_CLK_HEXT + * @retval none + */ +static void usb_clock48m_select(usb_clk48_s clk_s) { + if (clk_s == USB_CLK_HICK) { + /* UNUSED!!! + + crm_usb_clock_source_select(CRM_USB_CLOCK_SOURCE_HICK); + + // enable the acc calibration ready interrupt + crm_periph_clock_enable(CRM_ACC_PERIPH_CLOCK, TRUE); + + // update the c1\c2\c3 value + acc_write_c1(7980); + acc_write_c2(8000); + acc_write_c3(8020); +#if (USB_ID == 0) + acc_sof_select(ACC_SOF_OTG1); +#else + acc_sof_select(ACC_SOF_OTG2); +#endif + // open acc calibration + acc_calibration_mode_enable(ACC_CAL_HICKTRIM, TRUE); + + */ + } else { + switch (system_core_clock) { + /* 48MHz */ + case 48000000: + crm_usb_clock_div_set(CRM_USB_DIV_1); + break; + + /* 72MHz */ + case 72000000: + crm_usb_clock_div_set(CRM_USB_DIV_1_5); + break; + + /* 96MHz */ + case 96000000: + crm_usb_clock_div_set(CRM_USB_DIV_2); + break; + + /* 120MHz */ + case 120000000: + crm_usb_clock_div_set(CRM_USB_DIV_2_5); + break; + + /* 144MHz */ + case 144000000: + crm_usb_clock_div_set(CRM_USB_DIV_3); + break; + + /* 168MHz */ + case 168000000: + crm_usb_clock_div_set(CRM_USB_DIV_3_5); + break; + + /* 192MHz */ + case 192000000: + crm_usb_clock_div_set(CRM_USB_DIV_4); + break; + + /* 216MHz */ + case 216000000: + crm_usb_clock_div_set(CRM_USB_DIV_4_5); + break; + + /* 240MHz */ + case 240000000: + crm_usb_clock_div_set(CRM_USB_DIV_5); + break; + + /* 264MHz */ + case 264000000: + crm_usb_clock_div_set(CRM_USB_DIV_5_5); + break; + + /* 288MHz */ + case 288000000: + crm_usb_clock_div_set(CRM_USB_DIV_6); + break; + + default: + break; + } + } +} + +/** + * @brief this function config gpio. + * @retval none + */ +static void usb_gpio_config(void) { + gpio_init_type gpio_init_struct; + + crm_periph_clock_enable(OTG_PIN_GPIO_CLOCK, TRUE); + gpio_default_para_init(&gpio_init_struct); + + gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; + gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; + gpio_init_struct.gpio_mode = GPIO_MODE_MUX; + gpio_init_struct.gpio_pull = GPIO_PULL_NONE; + + /* dp and dm */ + gpio_init_struct.gpio_pins = OTG_PIN_DP | OTG_PIN_DM; + gpio_init(OTG_PIN_GPIO, &gpio_init_struct); + + gpio_pin_mux_config(OTG_PIN_GPIO, OTG_PIN_DP_SOURCE, OTG_PIN_MUX); + gpio_pin_mux_config(OTG_PIN_GPIO, OTG_PIN_DM_SOURCE, OTG_PIN_MUX); + +#ifdef USB_SOF_OUTPUT_ENABLE + crm_periph_clock_enable(OTG_PIN_SOF_GPIO_CLOCK, TRUE); + gpio_init_struct.gpio_pins = OTG_PIN_SOF; + gpio_init(OTG_PIN_SOF_GPIO, &gpio_init_struct); + gpio_pin_mux_config(OTG_PIN_SOF_GPIO, OTG_PIN_SOF_SOURCE, OTG_PIN_MUX); +#endif + + /* otgfs use vbus pin */ +#ifndef USB_VBUS_IGNORE + gpio_init_struct.gpio_pins = OTG_PIN_VBUS; + gpio_init_struct.gpio_pull = GPIO_PULL_DOWN; + gpio_pin_mux_config(OTG_PIN_GPIO, OTG_PIN_VBUS_SOURCE, OTG_PIN_MUX); + gpio_init(OTG_PIN_GPIO, &gpio_init_struct); +#endif +} + +// predefine, resolve compiler warning. +void OTG_IRQ_HANDLER(void); + +/** + * @brief this function handles otgfs interrupt. + * @retval none + */ +void OTG_IRQ_HANDLER(void) { + usbd_irq_handler(&otg_core_struct); +} + +/** + * @brief usb delay millisecond function. + * @param ms: number of millisecond delay + * @retval none + */ +void usb_delay_ms(uint32_t ms) { + // Did not use !!!! ANY delay if bootrom unsupported !!!! see: ticks.h -> AS_BOOTROM macro + SpinDelayUs(ms * 1000); +} + +// unused, don't need to implement. +// /** +// * @brief usb delay microsecond function. +// * @param us: number of microsecond delay +// * @retval none +// */ +// void usb_delay_us(uint32_t us) { +// delay_us(us); +// } + +#ifndef AS_BOOTROM + +static uint8_t usb_read_ng_buffer[64] = {0}; +static uint8_t usb_read_ng_fifo_pos = 0; + +// Implemented for read_ng +static bool usb_read_ng_link_ready(void) { + return usb_check(); // reuse 'usb_check()' +} + +// Implemented for read_ng +static bool usb_read_ng_data_ready(void) { + cdc_struct_type *pcdc = (cdc_struct_type *) (udev->class_handler->pdata); + return pcdc->g_rx_completed; +} + +// Implemented for read_ng +static uint16_t usb_read_ng_data_available(void) { + return usb_available_length(); +} + +// Implemented for read_ng +static uint8_t usb_read_ng_data_read(void) { + cdc_struct_type *pcdc = (cdc_struct_type *) (udev->class_handler->pdata); + return pcdc->g_rx_buff[usb_read_ng_fifo_pos++]; +} + +// Implemented for read_ng +static void usb_read_ng_clear(void) { + cdc_struct_type *pcdc = (cdc_struct_type *) (udev->class_handler->pdata); + // When receiving data from USB device is completed, the flag bit of receiving completion must be cleared, + // otherwise, it will enter the endless cycle of receiving completion and may repeatedly execute an instruction. + pcdc->g_rx_completed = 0; + // usb_read_ng_data_read() will increment position when read, so we need reset on read finished. + // if reset forgot, usb_read_ng_data_read() will read wrong data(cause by overflow). + usb_read_ng_fifo_pos = 0; + // receive enable + usbd_ept_recv(udev, USBD_CDC_BULK_OUT_EPT, pcdc->g_rx_buff, USBD_CDC_OUT_MAXPACKET_SIZE); +} + +// Instance for 'read_ng' apis +static const usb_read_ng_config_t g_usb_read_ng_config = { + .is_link_ready = usb_read_ng_link_ready, + .is_data_ready = usb_read_ng_data_ready, + .get_byte_count = usb_read_ng_data_available, + .read_fifo = usb_read_ng_data_read, + .clear_ready = usb_read_ng_clear, + .buffer = usb_read_ng_buffer, + .buffer_size = sizeof(usb_read_ng_buffer), + .timeout = 0x1FFF +}; +#endif + +/** + * This function Activates the USB device + */ +void usb_enable(void) { + usb_gpio_config(); + + crm_periph_clock_enable(OTG_CLOCK, TRUE); // enable otgfs clock + usb_clock48m_select(USB_CLK_HEXT); // select usb 48m clcok source + nvic_irq_enable(OTG_IRQ, 0, 0); // enable otgfs irq + usbd_init(&otg_core_struct, USB_FULL_SPEED_CORE_ID,USB_ID, &cdc_class_handler, &cdc_desc_handler); // init usb + +#ifndef AS_BOOTROM + usb_read_ng_init(&g_usb_read_ng_config); +#endif +} + +/** + * This function deactivates the USB device + */ +void usb_disable(void) { + nvic_irq_disable(OTG_IRQ); // disable otgfs irq + NVIC_ClearPendingIRQ(OTG_IRQ); // clear otgfs irq if pedding + crm_periph_clock_enable(OTG_CLOCK, FALSE); // disable otgfs clock +} + +/** + * Test if the device is configured and handle enumeration + * @return true if configured, otherwise fasle. + */ +bool usb_check(void) { + return udev->conn_state == USB_CONN_STATE_CONFIGURED; +} + +/** + * Test if the device link ok and data received. + * @return true if link ok and data received, otherwise fasle. + */ +bool usb_poll(void) { + if (usb_check() == false) { + return false; + } + // g_rx_completed will set to 1 when irq event: USB_OTG_DOEPINT_XFERC_FLAG + cdc_struct_type *pcdc = (cdc_struct_type *) (udev->class_handler->pdata); + return pcdc->g_rx_completed; +} + +/** + * Get data received length of out endpoint. + * @return data length, if no data received, return 0. + */ +uint16_t usb_available_length(void) { + cdc_struct_type *pcdc = (cdc_struct_type *) (udev->class_handler->pdata); + // Only when g_rx_completed is set, the g_rxlen is valid, otherwise, it may be 0 or invalid. + if (pcdc->g_rx_completed) { + return pcdc->g_rxlen; + } + return 0; +} + +/** + * Test if the device link ok and data received. + * note: this function will check data length > 0 + * @return + */ +bool usb_poll_validate_length(void) { + if (usb_poll() == false) { + return false; + } + return usb_available_length() > 0; +} + +/** + * Read available data from Endpoint 1 OUT (host to device, blocking read.) + * @param data the data buffer read into. + * @param len the max length of data. + * @return + */ +uint32_t usb_read(uint8_t *data, size_t len) { + if (len == 0) return 0; // invalid length + + uint16_t nbBytesRcv = 0; + uint16_t time_out = 0; + uint16_t packetSize = 0; + + while (len) { + if (usb_check() == false) { + break; + } + + // example: 150bytes receive from HOST + // OUT endpoint buffer size is 64. + // so, usb controller will split to 3 packet for send. + // 1. 64 -> packetSize(64) = usb_vcp_get_rxdata(udev, data + nbBytesRcv(0), len(150)); + // 2. 64 -> packetSize(64) = usb_vcp_get_rxdata(udev, data + nbBytesRcv(64), len(86)); + // 3. 22 -> packetSize(22) = usb_vcp_get_rxdata(udev, data + nbBytesRcv(128), len(22)); + // after the third time received, the len -= 22 get 0. loop end. + + packetSize = usb_vcp_get_rxdata(udev, data + nbBytesRcv, len); + if (packetSize != 0) { + len -= packetSize; + nbBytesRcv += packetSize; + } + + // simple timeout. + if (time_out++ == 0x1FFF) { + break; + } + } + + return nbBytesRcv; +} + +/** + * Send through endpoint 2 (device to host, blocking write.) + * @param data the data will send + * @param len the data length + * @return result value + */ +int usb_write(const uint8_t *data, const size_t len) { + if (len == 0) { + return PM3_EINVARG; + } + + if (usb_check() == false) { + return PM3_EIO; + } + + // 'usb_vcp_send_data()' will auto split packet. + if (usb_vcp_send_data(udev, (uint8_t *) data, len) != SUCCESS) { + return PM3_EIO; + } + + // wait for send complete + cdc_struct_type *pcdc = (cdc_struct_type *) (udev->class_handler->pdata); + while (pcdc->g_tx_completed != 1) { + if (usb_check() == false) { + return PM3_EIO; + } + // working for send to HOST... + // Have a cup of tea? + } + + return PM3_SUCCESS; +} + +// --------------------------------- ASYNC WRITE APIS --------------------------------- + +static uint8_t async_write_buffer[2][USBD_CDC_IN_MAXPACKET_SIZE]; // double buffer, like at91 double bank. +static uint8_t async_write_buf_select = 0; +static uint8_t async_write_index = 0; + +/** + * Check is write data finished. + * @return + */ +static uint8_t is_write_completed(otg_eptin_type *ept_in) { + if (ept_in->dieptsiz_bit.xfersize != 0) { + return FALSE; + } + return TRUE; +} + +/** + * Start the buffer write, wait for last send finished and flush fifo. + * @return error status + */ +int async_usb_write_start(void) { + otg_eptin_type *ept_in = USB_INEPT(udev->usb_reg, (USBD_CDC_BULK_IN_EPT & 0x7F)); + otg_device_type *dev = OTG_DEVICE(udev->usb_reg); + + // check usb state + if (!usb_check()) return PM3_EIO; + + // wait for tx end if working... + while (!is_write_completed(ept_in)) if (!usb_check()) return PM3_EIO; + + // disable fifo empty irq. + dev->diepempmsk &= ~(1 << (USBD_CDC_BULK_IN_EPT & 0x7F)); + // check fifo status before async write. + usbd_ept_in_check_fifo(udev, USBD_CDC_BULK_IN_EPT & 0x7F); + + // reset flag + async_write_buf_select = 0; + async_write_index = 0; + + // AT32 和 CH32 的USB功能区别挺大,注意不要陷入惯性思维的陷阱。 + // 对于AT32,需要先设置端点控制寄存器中的传输长度和包数目位,并使能端点来传输数据。最后然后再去写FIFO + // 对于CH32,需要先写入BUFF,然后再设置传输长度,最后再使能发送和ACK。 + + return PM3_SUCCESS; +} + +/** + * Push 1 byte data to usb fifo, but no send start. + * @param b the byte will push to usb fifo + */ +void async_usb_write_pushByte(uint8_t b) { + if (async_write_index >= USBD_CDC_IN_MAXPACKET_SIZE) { + return; // !!! WARN !!! Can't to here, will memory overflow. + } + async_write_buffer[async_write_buf_select][async_write_index] = b; + async_write_index++; +} + +/** + * Flush the send buffer, next IN event will trans to HOST + */ +bool async_usb_write_requestWrite(void) { + // get reg + otg_eptin_type *ept_in = USB_INEPT(udev->usb_reg, (USBD_CDC_BULK_IN_EPT & 0x7F)); + + // check last transmit is finish? cond: trans remain length not 0 or fifo have data. + if (!is_write_completed(ept_in)) return FALSE; + + // set transfer length and packet count. + ept_in->dieptsiz_bit.xfersize = async_write_index; + ept_in->dieptsiz_bit.pktcnt = 1; + // dieptsiz_bit register must set before 'eptena' set. it will lock after 'eptena' = TRUE + // clear endpoint nak + ept_in->diepctl_bit.cnak = TRUE; + // IN endpoint enable + ept_in->diepctl_bit.eptena = TRUE; + + // write data to fifo. + usb_write_packet( + udev->usb_reg, + async_write_buffer[async_write_buf_select], + USBD_CDC_BULK_IN_EPT & 0x7F, + async_write_index + ); + async_write_buf_select = ~async_write_buf_select; // after data write, we can change buffer, 0 or 1 + async_write_index = 0; // don't forget reset write index, next write will write to other buffer and from 0 start. + + return TRUE; +} + +/** + * Stop send and wait finish. + * @return SUCCESS if send stop success, otherwise ERROR + */ +int async_usb_write_stop(void) { + otg_eptin_type *ept_in = USB_INEPT(udev->usb_reg, (USBD_CDC_BULK_IN_EPT & 0x7F)); + + // Wait for the end of transfer + while (!is_write_completed(ept_in)) if (!usb_check()) return PM3_EIO; + + // still have data on local buffer, we need send before write stop. + if (async_write_index != 0) { + if (!async_usb_write_requestWrite()) { + return PM3_EIO; + } + } + + // Wait for the end of fifo flush transfer. + while (!is_write_completed(ept_in)) if (!usb_check()) return PM3_EIO; + + return PM3_SUCCESS; +} + +/** + * Get endpoint buffer size. + */ +void usb_get_ep_size(uint32_t *epCtl, uint32_t *epIn, uint32_t *epOut) { + if (epCtl) *epCtl = USBD_CDC_CMD_MAXPACKET_SIZE; + if (epIn) *epIn = USBD_CDC_IN_MAXPACKET_SIZE; + if (epOut) *epOut = USBD_CDC_OUT_MAXPACKET_SIZE; +} diff --git a/common_arm/usb_cdc.c b/common_arm/usb/usb_cdc_at91.c similarity index 59% rename from common_arm/usb_cdc.c rename to common_arm/usb/usb_cdc_at91.c index ce7bda325..086394568 100644 --- a/common_arm/usb_cdc.c +++ b/common_arm/usb/usb_cdc_at91.c @@ -17,9 +17,13 @@ // based on the "Basic USB Example" from ATMEL (doc6123.pdf) //----------------------------------------------------------------------------- -#include "usb_cdc.h" +#include "usb_cdc_apis.h" +#include "at91sam7s512.h" #include "proxmark3_arm.h" #include "usart_defs.h" +#include "ticks_apis.h" +#include "usb_read_ng.h" +#include "usb_cdc_desc.h" /* AT91SAM7S256 USB Device Port @@ -31,50 +35,17 @@ AT91SAM7S256 USB Device Port – Ping-pong Mode (two memory banks) for bulk endpoints */ -// -#define AT91C_EP_CONTROL 0 -#define AT91C_EP_OUT 1 // cfg bulk out -#define AT91C_EP_IN 2 // cfg bulk in -#define AT91C_EP_NOTIFY 3 // cfg cdc notification interrup - -// The endpoint size is defined in usb_cdc.h - -// Section: USB Descriptors -#define USB_DESCRIPTOR_DEVICE 0x01 // DescriptorType for a Device Descriptor. -#define USB_DESCRIPTOR_CONFIGURATION 0x02 // DescriptorType for a Configuration Descriptor. -#define USB_DESCRIPTOR_STRING 0x03 // DescriptorType for a String Descriptor. -#define USB_DESCRIPTOR_INTERFACE 0x04 // DescriptorType for an Interface Descriptor. -#define USB_DESCRIPTOR_ENDPOINT 0x05 // DescriptorType for an Endpoint Descriptor. -#define USB_DESCRIPTOR_DEVICE_QUALIFIER 0x06 // DescriptorType for a Device Qualifier. -#define USB_DESCRIPTOR_OTHER_SPEED 0x07 // DescriptorType for a Other Speed Configuration. -#define USB_DESCRIPTOR_INTERFACE_POWER 0x08 // DescriptorType for Interface Power. -#define USB_DESCRIPTOR_OTG 0x09 // DescriptorType for an OTG Descriptor. -#define USB_DESCRIPTOR_IAD 0x0B // DescriptorType for a Interface Association Descriptor -#define USB_DESCRIPTOR_TYPE_BO 0x0F // DescriptorType for a BOS Descriptor. - -/* Configuration Attributes */ -#define _DEFAULT (0x01<<7) //Default Value (Bit 7 is set) -#define _SELF (0x01<<6) //Self-powered (Supports if set) -#define _RWU (0x01<<5) //Remote Wakeup (Supports if set) -#define _HNP (0x01 << 1) //HNP (Supports if set) -#define _SRP (0x01) //SRP (Supports if set) - -/* Endpoint Transfer Type */ -#define _CTRL 0x00 //Control Transfer -#define _ISO 0x01 //Isochronous Transfer -#define _BULK 0x02 //Bulk Transfer -#define _INTERRUPT 0x03 //Interrupt Transfer - -// (bit7 | 0 = OUT, 1 = IN) -#define _EP_IN 0x80 -#define _EP_OUT 0x00 -#define _EP01_OUT 0x01 -#define _EP01_IN 0x81 -#define _EP02_OUT 0x02 -#define _EP02_IN 0x82 -#define _EP03_OUT 0x03 -#define _EP03_IN 0x83 +// EP for CDC definition +#define AT91C_EP_CONTROL 0 +#define AT91C_EP_OUT 1 // cfg bulk out - 0x01 +#define AT91C_EP_IN 2 // cfg bulk in - 0x82 +#define AT91C_EP_NOTIFY 3 // cfg cdc notification interrup - 0x83 +// The definition of endpoint size has been moved back from the original file to this source file. +// Because the usb_cdc.h header file is now a universally defined header file. +#define AT91C_USB_EP_CONTROL_SIZE 8 +#define AT91C_USB_EP_OUT_SIZE 64 +#define AT91C_USB_EP_IN_SIZE 64 /* WCID specific Request Code */ #define MS_OS_DESCRIPTOR_INDEX 0xEE @@ -111,320 +82,25 @@ AT91SAM7S256 USB Device Port #define SET_LINE_CODING 0x2021 #define SET_CONTROL_LINE_STATE 0x2221 -static bool isAsyncRequestFinished = false; -static AT91PS_UDP pUdp = AT91C_BASE_UDP; +// !!!! NOTE: If we need inline a function, then don't set the variables to static. + +bool isAsyncRequestFinished = false; +AT91PS_UDP pUdp = AT91C_BASE_UDP; + static uint8_t btConfiguration = 0; static uint8_t btConnection = 0; static uint8_t btReceiveBank = AT91C_UDP_RX_DATA_BK0; -static const char devDescriptor[] = { - /* Device descriptor */ - 0x12, // Length - USB_DESCRIPTOR_DEVICE, // Descriptor Type (DEVICE) - 0x00, 0x02, // Complies with USB Spec. Release (0200h = release 2.00) 0210 == release 2.10 - 2, // Device Class: Communication Device Class - 0, // Device Subclass: CDC class sub code ACM [ice 0x02 = win10 virtual comport ] - 0, // Device Protocol: CDC Device protocol (unused) - AT91C_USB_EP_CONTROL_SIZE, // MaxPacketSize0 - 0xc4, 0x9a, // Vendor ID [0x9ac4 = J. Westhues] - 0x8f, 0x4b, // Product ID [0x4b8f = Proxmark-3 RFID Instrument] - 0x00, 0x01, // BCD Device release number (1.00) - 1, // index Manufacturer - 2, // index Product - 3, // index SerialNumber - 1 // Number of Configs -}; - -static const char cfgDescriptor[] = { - - /* Configuration 1 descriptor */ - // ----------------------------- - 9, // Length - USB_DESCRIPTOR_CONFIGURATION, // Descriptor Type - (9 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7), 0, // Total Length 2 EP + Control - 2, // Number of Interfaces - 1, // Index value of this Configuration (used in SetConfiguration from Host) - 0, // Configuration string index - _DEFAULT, // Attributes 0xA0 - 0xFA, // Max Power consumption - - // IAD to associate the one CDC interface - // -------------------------------------- - /* - 8, // Length - USB_DESCRIPTOR_IAD, // IAD_DESCRIPTOR (0x0B) - 0, // CDC_INT_INTERFACE NUMBER ( - 2, // IAD INTERFACE COUNT (two interfaces) - 2, // Function Class: CDC_CLASS - 2, // Function SubClass: ACM - 1, // Function Protocol: v.25term - 0, // iInterface - */ - - /* Interface 0 Descriptor */ - /* CDC Communication Class Interface Descriptor Requirement for Notification*/ - // ----------------------------------------------------------- - 9, // Length - USB_DESCRIPTOR_INTERFACE, // Descriptor Type - 0, // Interface Number - 0, // Alternate Setting - 1, // Number of Endpoints in this interface - 2, // Interface Class code (Communication Interface Class) - 2, // Interface Subclass code (Abstract Control Model) - 1, // InterfaceProtocol (Common AT Commands, V.25term) - 0, // iInterface - - /* Header Functional Descriptor */ - 5, // Function Length - 0x24, // Descriptor type: CS_INTERFACE - 0, // Descriptor subtype: Header Functional Descriptor - 0x10, 0x01, // bcd CDC:1.1 - - /* ACM Functional Descriptor */ - 4, // Function Length - 0x24, // Descriptor Type: CS_INTERFACE - 2, // Descriptor Subtype: Abstract Control Management Functional Descriptor - 2, // Capabilities D1, Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State - - /* Union Functional Descriptor */ - 5, // Function Length - 0x24, // Descriptor Type: CS_INTERFACE - 6, // Descriptor Subtype: Union Functional Descriptor - 0, // MasterInterface: Communication Class Interface - 1, // SlaveInterface0: Data Class Interface - - /* Call Management Functional Descriptor */ - 5, // Function Length - 0x24, // Descriptor Type: CS_INTERFACE - 1, // Descriptor Subtype: Call Management Functional Descriptor - 0, // Capabilities: Device sends/receives call management information only over the Communication Class interface. Device does not handle call management itself - 1, // Data Interface: Data Class Interface - - /* Protocol Functional Descriptor */ - /* - 6, - 0x24, // Descriptor Type: CS_INTERFACE - 0x0B, // Descriptor Subtype: Protocol Unit functional Descriptor - 0xDD, // constant uniq ID of unit - 0xFE, // protocol - */ - - /* CDC Notification Endpoint descriptor */ - // --------------------------------------- - 7, // Length - USB_DESCRIPTOR_ENDPOINT, // Descriptor Type - _EP03_IN, // EndpointAddress: Endpoint 03 - IN - _INTERRUPT, // Attributes - AT91C_USB_EP_CONTROL_SIZE, 0x00, // MaxPacket Size: EP0 - 8 - 0xFF, // Interval polling - - - /* Interface 1 Descriptor */ - /* CDC Data Class Interface 1 Descriptor Requirement */ - 9, // Length - USB_DESCRIPTOR_INTERFACE, // Descriptor Type - 1, // Interface Number - 0, // Alternate Setting - 2, // Number of Endpoints - 0x0A, // Interface Class: CDC Data interface class - 0, // Interface Subclass: not used - 0, // Interface Protocol: No class specific protocol required (usb spec) - 0, // Interface - - /* Endpoint descriptor */ - 7, // Length - USB_DESCRIPTOR_ENDPOINT, // Descriptor Type - _EP01_OUT, // Endpoint Address: Endpoint 01 - OUT - _BULK, // Attributes: BULK - AT91C_USB_EP_OUT_SIZE, 0x00, // MaxPacket Size: 64 bytes - 0, // Interval: ignored for bulk - - /* Endpoint descriptor */ - 7, // Length - USB_DESCRIPTOR_ENDPOINT, // Descriptor Type - _EP02_IN, // Endpoint Address: Endpoint 02 - IN - _BULK, // Attribute: BULK - AT91C_USB_EP_IN_SIZE, 0x00, // MaxPacket Size: 64 bytes - 0 // Interval: ignored for bulk -}; - -// BOS descriptor -static const char bosDescriptor[] = { - 0x5, - USB_DESCRIPTOR_TYPE_BO, - 0xC, - 0x0, - 0x1, // 1 device capability - 0x7, - 0x10, // USB_DEVICE_CAPABITY_TYPE, - 0x2, - 0x2, // LPM capability bit set - 0x0, - 0x0, - 0x0 -}; - -// Microsoft OS Extended Configuration Compatible ID Descriptor -/* -static const char CompatIDFeatureDescriptor[] = { - 0x28, 0x00, 0x00, 0x00, // Descriptor Length 40bytes (0x28) - 0x00, 0x01, // Version ('1.0') - MS_EXTENDED_COMPAT_ID, 0x00, // Compatibility ID Descriptor Index 0x0004 - 0x01, // Number of sections. 0x1 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved (7bytes) - // -----function section 1------ - 0x00, // Interface Number #0 - 0x01, // reserved (0x1) - 0x57, 0x49, 0x4E, 0x55, 0x53, 0x42, 0x00, 0x00, // Compatible ID ('WINUSB\0\0') (8bytes) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Sub-Compatible ID (8byte) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved (6bytes) -}; -*/ - -// Microsoft Extended Properties Feature Descriptor -/* -static const char OSprop[] = { - // u32 Descriptor Length (10+132+64+102 == 308 - 0x34, 0x01, 0, 0, - // u16 Version ('1.0') - 0, 1, - // u16 wIndex - MS_EXTENDED_PROPERTIES, 0, - // u16 wCount -- three section - 3, 0, - - // -----property section 1------ - // u32 size ( 14+40+78 == 132) - 132, 0, 0, 0, - // u32 type - 1, 0, 0, 0, // unicode string - // u16 namelen (20*2 = 40) - 40, 0, - // name DeviceInterfaceGUID - 'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,'I',0,'n',0,'t',0,'e',0,'r',0,'f',0,'a',0,'c',0,'e',0,'G',0,'U',0,'I',0,'D',0,0,0, - // u32 datalen (39*2 = 78) - 78, 0, 0, 0, - // data {4D36E978-E325-11CE-BFC1-08002BE10318} - '{',0,'4',0,'d',0,'3',0,'6',0,'e',0,'9',0,'7',0,'8',0,'-',0,'e',0,'3',0,'2',0,'5',0, - '-',0,'1',0,'1',0,'c',0,'e',0,'-',0,'b',0,'f',0,'c',0,'1',0,'-',0,'0',0,'8',0,'0',0, - '0',0,'2',0,'b',0,'e',0,'1',0,'0',0,'3',0,'1',0,'8',0,'}',0,0,0, - - // -----property section 2------ - // u32 size ( 14+12+38 == 64) - 64, 0, 0, 0, - // u32 type - 1, 0, 0, 0, // unicode string - // u16 namelen (12) - 12, 0, - // name Label - 'L',0,'a',0,'b',0,'e',0,'l',0,0,0, - // u32 datalen ( 19*2 = 38 ) - 38, 0, 0, 0, - // data 'Awesome PM3 Device' - 'A',0,'w',0,'e',0,'s',0,'o',0,'m',0,'e',0,' ',0,'P',0,'M',0,'3',0,' ',0,'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,0,0, - - // -----property section 3------ - // u32 size ( 14+12+76 == 102) - 102, 0, 0, 0, - // u32 type - 2, 0, 0, 0, //Unicode string with environment variables - // u16 namelen (12) - 12, 0, - // name Icons - 'I',0,'c',0,'o',0,'n',0,'s',0,0,0, - // u32 datalen ( 38*2 == 76) - 76, 0, 0, 0, - // data '%SystemRoot%\\system32\\Shell32.dll,-13' - '%',0,'S',0,'y',0,'s',0,'t',0,'e',0,'m',0,'R',0,'o',0,'o',0,'t',0,'%',0, - '\\',0,'s',0,'y',0,'s',0,'t',0,'e',0,'m',0,'3',0,'2',0,'\\',0, - 'S',0,'h',0,'e',0,'l',0,'l',0,'3',0,'2',0,'.',0,'d',0,'l',0,'l',0,',',0, - '-',0,'1',0,'3',0,0,0 -}; - -*/ - -static const char StrLanguageCodes[] = { - 4, // Length - 0x03, // Type is string - 0x09, 0x04 // supported language Code 0 = 0x0409 (English) -}; - -// Note: ModemManager (Linux) ignores Proxmark3 devices by matching the -// manufacturer string "proxmark.org". Don't change this. -// or use the blacklisting file. -static const char StrManufacturer[] = { - 26, // Length - 0x03, // Type is string - 'p', 0, 'r', 0, 'o', 0, 'x', 0, 'm', 0, 'a', 0, 'r', 0, 'k', 0, '.', 0, 'o', 0, 'r', 0, 'g', 0, -}; - -static const char StrProduct[] = { - 20, // Length - 0x03, // Type is string - 'p', 0, 'r', 0, 'o', 0, 'x', 0, 'm', 0, 'a', 0, 'r', 0, 'k', 0, '3', 0 -}; - -#ifndef WITH_FLASH -static const char StrSerialNumber[] = { - 14, // Length - 0x03, // Type is string - 'i', 0, 'c', 0, 'e', 0, 'm', 0, 'a', 0, 'n', 0 -}; -#else // WITH_FLASH is defined - -// Manually calculated size of descriptor with unique ID: -// offset 0, lengt h 1: total length field -// offset 1, length 1: descriptor type field -// offset 2, length 12: 6x unicode chars (original string) -// offset 14, length 4: 2x unicode chars (underscores) [[ to avoid descriptor being (size % 8) == 0, OS bug workaround ]] -// offset 18, length 32: 16x unicode chars (8-byte serial as hex characters) -// ============================ -// total: 50 bytes -#define USB_STRING_DESCRIPTOR_SERIAL_NUMBER_LENGTH 50 -char StrSerialNumber[] = { - 14, // Length is initially identical to non-unique version ... The length updated at boot, if unique serial is available - 0x03, // Type is string - 'i', 0, 'c', 0, 'e', 0, 'm', 0, 'a', 0, 'n', 0, - '_', 0, '_', 0, - 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, - 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, -}; -void usb_update_serial(uint64_t newSerialNumber) { - static bool configured = false; // TODO: enable by setting to false here... - if (configured) { - return; - } - // run this only once per boot... even if it fails to find serial number - configured = true; - // reject serial number if all-zero or all-ones - if ((newSerialNumber == 0x0000000000000000) || (newSerialNumber == 0xFFFFFFFFFFFFFFFF)) { - return; - } - // Descriptor is, effectively, initially identical to non-unique serial - // number because it reports the shorter length in the first byte. - // Convert uniqueID's eight bytes to 16 unicode characters in the - // descriptor and, finally, update the descriptor's length, which - // causes the serial number to become visible. - for (uint8_t i = 0; i < 8; i++) { - // order of nibbles chosen to match display order from `hw status` - uint8_t nibble1 = (newSerialNumber >> ((8 * i) + 4)) & 0xFu; // bitmasks [0xF0, 0xF000, 0xF00000, ... 0xF000000000000000] - uint8_t nibble2 = (newSerialNumber >> ((8 * i) + 0)) & 0xFu; // bitmasks [0x0F, 0x0F00, 0x0F0000, ... 0x0F00000000000000] - char c1 = nibble1 < 10 ? '0' + nibble1 : 'A' + (nibble1 - 10); - char c2 = nibble2 < 10 ? '0' + nibble2 : 'A' + (nibble2 - 10); - StrSerialNumber[18 + (4 * i) + 0] = c1; // [ 18, 22, .., 42, 46 ] - StrSerialNumber[18 + (4 * i) + 2] = c2; // [ 20, 24, .., 44, 48 ] - } - StrSerialNumber[0] = USB_STRING_DESCRIPTOR_SERIAL_NUMBER_LENGTH; -} -#endif - - -// size includes their own field. -static const char StrMS_OSDescriptor[] = { - 18, // length 0x12 - 0x03, // Type is string - 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, MS_VENDOR_CODE, 0 -}; +// -- pre def functions +void AT91F_USB_SendData(AT91PS_UDP pudp, const char *pData, uint32_t length); +void AT91F_USB_SendZlp(AT91PS_UDP pudp); +void AT91F_USB_SendStall(AT91PS_UDP pudp); +void AT91F_CDC_Enumerate(void); +// -- +void SetUSBreconnect(int value); +int GetUSBreconnect(void); +void SetUSBconfigured(int value); +int GetUSBconfigured(void); static const char *getStringDescriptor(uint8_t idx) { switch (idx) { @@ -481,30 +157,58 @@ static AT91S_CDC_LINE_CODING line = { // purely informative, actual values don't 8 // 8 Data bits }; -// timer counts in 21.3us increments (1024/48MHz), rounding applies -// WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) -static void SpinDelayUs(int us) { - int ticks = ((MCK / 1000000) * us + 512) >> 10; +#ifndef AS_BOOTROM - // Borrow a PWM unit for my real-time clock - AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); +// buffer of read_ng apis. +static uint8_t usb_read_ng_buffer[64] = {0}; - // 48 MHz / 1024 gives 46.875 kHz - AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); // Channel Mode Register - AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; // Channel Duty Cycle Register - AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; // Channel Period Register +// Implemented for read_ng +static bool usb_read_ng_link_ready(void) { + // old: if (usb_check() == false) + return usb_check(); // reuse 'usb_check()' +} - uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; +// Implemented for read_ng +static bool usb_read_ng_data_ready(void) { + // old: if ((pUdp->UDP_CSR[AT91C_EP_OUT] & bank)) + return (pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank) != 0; +} - for (;;) { - uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; - if (now == (uint16_t)(start + ticks)) - return; +// Implemented for read_ng +static uint16_t usb_read_ng_data_available(void) { + // old: uint16_t available = (((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) & 0x7FF); + return usb_available_length(); +} - WDT_HIT(); +// Implemented for read_ng +static uint8_t usb_read_ng_data_read(void) { + return pUdp->UDP_FDR[AT91C_EP_OUT]; +} + +// Implemented for read_ng +static void usb_read_ng_clear(void) { + // flip bank + UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT, btReceiveBank) + if (btReceiveBank == AT91C_UDP_RX_DATA_BK0) { + btReceiveBank = AT91C_UDP_RX_DATA_BK1; + } else { + btReceiveBank = AT91C_UDP_RX_DATA_BK0; } } +// Instance for 'read_ng' apis +static const usb_read_ng_config_t g_usb_read_ng_config = { + .is_link_ready = usb_read_ng_link_ready, + .is_data_ready = usb_read_ng_data_ready, + .get_byte_count = usb_read_ng_data_available, + .read_fifo = usb_read_ng_data_read, + .clear_ready = usb_read_ng_clear, + .buffer = usb_read_ng_buffer, + .buffer_size = sizeof(usb_read_ng_buffer), + .timeout = 0x1FFF +}; +#endif + /* *---------------------------------------------------------------------------- * \fn usb_disable @@ -557,14 +261,13 @@ void usb_enable(void) { // Reconnect USB reconnect AT91C_BASE_PIOA->PIO_SODR = GPIO_USB_PU; AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; + +#ifndef AS_BOOTROM + // setup read_ng implement. + usb_read_ng_init(&g_usb_read_ng_config); +#endif } -/* - *---------------------------------------------------------------------------- - * \fn usb_check - * \brief Test if the device is configured and handle enumeration - *---------------------------------------------------------------------------- -*/ static int usb_reconnect = 0; static int usb_configured = 0; void SetUSBreconnect(int value) { @@ -580,6 +283,12 @@ int GetUSBconfigured(void) { return usb_configured; } +/* + *---------------------------------------------------------------------------- + * \fn usb_check + * \brief Test if the device is configured and handle enumeration + *---------------------------------------------------------------------------- +*/ bool usb_check(void) { /* @@ -631,15 +340,26 @@ bool usb_check(void) { return (btConfiguration) ? true : false; } +/* + *---------------------------------------------------------------------------- + * \fn usb_poll + * \brief Test if the device link ok and data received. + *---------------------------------------------------------------------------- +*/ bool usb_poll(void) { if (usb_check() == false) { return false; } - return (pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank); } -inline uint16_t usb_available_length(void) { +/* + *---------------------------------------------------------------------------- + * \fn usb_available_length + * \brief Get data received length of out endpoint. + *---------------------------------------------------------------------------- +*/ +FORCE_INLINE uint16_t usb_available_length(void) { return (((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) & 0x7FF); } @@ -652,22 +372,19 @@ inline uint16_t usb_available_length(void) { bug. **/ bool usb_poll_validate_length(void) { - - if (usb_check() == false) { + // Reuse 'usb_poll()' implemented. + if (usb_poll() == false) { return false; } - - if (!(pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank)) { - return false; - } - - return (((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) > 0); + // Why code this: return (((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) > 0); + // For speed? but 'usb_available_length()' is a inline function. + return (usb_available_length() > 0); } /* *---------------------------------------------------------------------------- * \fn usb_read - * \brief Read available data from Endpoint 1 OUT (host to device) + * \brief Read available data from Endpoint 1 OUT (host to device, blocking read.) *---------------------------------------------------------------------------- */ uint32_t usb_read(uint8_t *data, size_t len) { @@ -714,106 +431,10 @@ uint32_t usb_read(uint8_t *data, size_t len) { return nbBytesRcv; } -static uint8_t usb_read_ng_buffer[64] = {0}; -static uint8_t usb_read_ng_bufoffset = 0; -static size_t usb_read_ng_buflen = 0; - -bool usb_read_ng_has_buffered_data(void) { - return usb_read_ng_buflen > 0; -} - -uint32_t usb_read_ng(uint8_t *data, size_t len) { - - if (len == 0) { - return 0; - } - - uint8_t bank = btReceiveBank; - uint16_t packetSize, nbBytesRcv = 0; - uint16_t time_out = 0; - - // take first from local buffer - if (len <= usb_read_ng_buflen) { - - // if local buffer has all data - - for (size_t i = 0; i < len; i++) { - data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoffset + i]; - } - - usb_read_ng_buflen -= len; - - if (usb_read_ng_buflen == 0) { - usb_read_ng_bufoffset = 0; - } else { - usb_read_ng_bufoffset += len; - } - - return nbBytesRcv; - - } else { - - // take all data from local buffer, then read from usb - - for (size_t i = 0; i < usb_read_ng_buflen; i++) { - data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoffset + i]; - } - - len -= usb_read_ng_buflen; - usb_read_ng_buflen = 0; - usb_read_ng_bufoffset = 0; - } - - - while (len) { - - if (usb_check() == false) { - break; - } - - if ((pUdp->UDP_CSR[AT91C_EP_OUT] & bank)) { - - uint16_t available = (((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) & 0x7FF); - - packetSize = MIN(available, len); - available -= packetSize; - len -= packetSize; - - while (packetSize--) { - data[nbBytesRcv++] = pUdp->UDP_FDR[AT91C_EP_OUT]; - } - - // fill the local buffer with the remaining bytes - for (uint16_t i = 0; i < available; i++) { - usb_read_ng_buffer[i] = pUdp->UDP_FDR[AT91C_EP_OUT]; - } - - // update number of available bytes in local bytes - usb_read_ng_buflen = available; - - // flip bank - UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT, bank) - - if (bank == AT91C_UDP_RX_DATA_BK0) { - bank = AT91C_UDP_RX_DATA_BK1; - } else { - bank = AT91C_UDP_RX_DATA_BK0; - } - } - - if (time_out++ == 0x1FFF) { - break; - } - } - - btReceiveBank = bank; - return nbBytesRcv; -} - /* *---------------------------------------------------------------------------- * \fn usb_write - * \brief Send through endpoint 2 (device to host) + * \brief Send through endpoint 2 (device to host, blocking write.) *---------------------------------------------------------------------------- */ int usb_write(const uint8_t *data, const size_t len) { @@ -1051,7 +672,6 @@ void AT91F_USB_SendData(AT91PS_UDP pudp, const char *pData, uint32_t length) { } } - //*---------------------------------------------------------------------------- //* \fn AT91F_USB_SendZlp //* \brief Send zero length packet through the control endpoint @@ -1266,3 +886,13 @@ void AT91F_CDC_Enumerate(void) { break; } } + +//*---------------------------------------------------------------------------- +//* \fn usb_get_ep_size +//* \brief This function can get usb endpoint buffer size +//*---------------------------------------------------------------------------- +void usb_get_ep_size(uint32_t *epCtl, uint32_t *epIn, uint32_t *epOut) { + if (epCtl) *epCtl = AT91C_USB_EP_CONTROL_SIZE; + if (epIn) *epIn = AT91C_USB_EP_IN_SIZE; + if (epOut) *epOut = AT91C_USB_EP_OUT_SIZE; +} \ No newline at end of file diff --git a/common_arm/usb/usb_cdc_desc.c b/common_arm/usb/usb_cdc_desc.c new file mode 100644 index 000000000..68e2b94cb --- /dev/null +++ b/common_arm/usb/usb_cdc_desc.c @@ -0,0 +1,324 @@ +#include +#include +#include "usb_cdc_desc.h" +#include "usb_cdc_apis.h" + +#ifndef LBYTE +#define LBYTE(x) ((uint8_t)(x & 0x00FF)) /*!< low byte define */ +#endif +#ifndef HBYTE +#define HBYTE(x) ((uint8_t)((x & 0xFF00) >>8)) /*!< high byte define*/ +#endif + +const char devDescriptor[18] = { + /* Device descriptor */ + 0x12, // Length + 0x01, // Descriptor Type (DEVICE) + 0x00, 0x02, // Complies with USB Spec. Release (0200h = release 2.00) 0210 == release 2.10 + 2, // Device Class: Communication Device Class + 0, // Device Subclass: CDC class sub code ACM [ice 0x02 = win10 virtual comport ] + 0, // Device Protocol: CDC Device protocol (unused) + USB_CDC_DESC_MAX_EP0_SIZE, // MaxPacketSize0: The maximum packet size for endpoint 0 + 0xc4, 0x9a, // Vendor ID [0x9ac4 = J. Westhues] + 0x8f, 0x4b, // Product ID [0x4b8f = Proxmark-3 RFID Instrument] + 0x00, 0x01, // BCD Device release number (1.00) + 1, // index Manufacturer + 2, // index Product + 3, // index SerialNumber + 1 // Number of Configs +}; + +const char cfgDescriptor[67] = { + + /* Configuration 1 descriptor */ + // ----------------------------- + 9, // Length + 0x02, // Descriptor Type + (9 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7), 0, // Total Length 2 EP + Control + 2, // Number of Interfaces + 1, // Index value of this Configuration (used in SetConfiguration from Host) + 0, // Configuration string index + USB_CDC_DESC_CFG_POWER_MODE, // Attributes 0xA0 + 0xFA, // Max Power consumption + + // IAD to associate the one CDC interface + // -------------------------------------- + /* + 8, // Length + USB_DESCRIPTOR_IAD, // IAD_DESCRIPTOR (0x0B) + 0, // CDC_INT_INTERFACE NUMBER ( + 2, // IAD INTERFACE COUNT (two interfaces) + 2, // Function Class: CDC_CLASS + 2, // Function SubClass: ACM + 1, // Function Protocol: v.25term + 0, // iInterface + */ + + /* Interface 0 Descriptor */ + /* CDC Communication Class Interface Descriptor Requirement for Notification*/ + // ----------------------------------------------------------- + 9, // Length + 0x04, // Descriptor Type + 0, // Interface Number + 0, // Alternate Setting + 1, // Number of Endpoints in this interface + 2, // Interface Class code (Communication Interface Class) + 2, // Interface Subclass code (Abstract Control Model) + 1, // InterfaceProtocol (Common AT Commands, V.25term) + 0, // iInterface + + /* Header Functional Descriptor */ + 5, // Function Length + 0x24, // Descriptor type: CS_INTERFACE + 0, // Descriptor subtype: Header Functional Descriptor + 0x10, 0x01, // bcd CDC:1.1 + + /* ACM Functional Descriptor */ + 4, // Function Length + 0x24, // Descriptor Type: CS_INTERFACE + 2, // Descriptor Subtype: Abstract Control Management Functional Descriptor + 2, // Capabilities D1, Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State + + /* Union Functional Descriptor */ + 5, // Function Length + 0x24, // Descriptor Type: CS_INTERFACE + 6, // Descriptor Subtype: Union Functional Descriptor + 0, // MasterInterface: Communication Class Interface + 1, // SlaveInterface0: Data Class Interface + + /* Call Management Functional Descriptor */ + 5, // Function Length + 0x24, // Descriptor Type: CS_INTERFACE + 1, // Descriptor Subtype: Call Management Functional Descriptor + 0, // Capabilities: Device sends/receives call management information only over the Communication Class interface. Device does not handle call management itself + 1, // Data Interface: Data Class Interface + + /* Protocol Functional Descriptor */ + /* + 6, + 0x24, // Descriptor Type: CS_INTERFACE + 0x0B, // Descriptor Subtype: Protocol Unit functional Descriptor + 0xDD, // constant uniq ID of unit + 0xFE, // protocol + */ + + /* CDC Notification Endpoint descriptor */ + // --------------------------------------- + 7, // Length + 0x05, // Descriptor Type + USB_CDC_DESC_INT_EPT, // EndpointAddress: Endpoint x - IN + 0x03, // Attributes, Interrupt Transfer + // TODO: why set to ep0 size? + LBYTE(USB_CDC_DESC_MAX_EP0_SIZE), HBYTE(USB_CDC_DESC_MAX_EP0_SIZE), // MaxPacket Size + 0xFF, // Interval polling + + + /* Interface 1 Descriptor */ + /* CDC Data Class Interface 1 Descriptor Requirement */ + 9, // Length + 0x04, // Descriptor Type + 1, // Interface Number + 0, // Alternate Setting + 2, // Number of Endpoints + 0x0A, // Interface Class: CDC Data interface class + 0, // Interface Subclass: not used + 0, // Interface Protocol: No class specific protocol required (usb spec) + 0, // Interface + + /* Endpoint descriptor */ + 7, // Length + 0x05, // Descriptor Type + USB_CDC_DESC_BULK_OUT_EPT, // Endpoint Address: Endpoint 01 - OUT + 0x02, // Attributes: BULK + LBYTE(USB_CDC_DESC_OUT_PACKET_SIZE), HBYTE(USB_CDC_DESC_OUT_PACKET_SIZE), // MaxPacket Size + 0, // Interval: ignored for bulk + + /* Endpoint descriptor */ + 7, // Length + 0x05, // Descriptor Type + USB_CDC_DESC_BULK_IN_EPT, // Endpoint Address: Endpoint 02 - IN + 0x02, // Attribute: BULK + LBYTE(USB_CDC_DESC_IN_PACKET_SIZE), HBYTE(USB_CDC_DESC_IN_PACKET_SIZE), // MaxPacket Size + 0 // Interval: ignored for bulk +}; + +// BOS descriptor +const char bosDescriptor[12] = { + 0x5, + 0x0F, // DescriptorType for a BOS Descriptor. + 0xC, + 0x0, + 0x1, // 1 device capability + 0x7, + 0x10, // USB_DEVICE_CAPABITY_TYPE, + 0x2, + 0x2, // LPM capability bit set + 0x0, + 0x0, + 0x0 +}; + +// Microsoft OS Extended Configuration Compatible ID Descriptor +/* +const char CompatIDFeatureDescriptor[] = { + 0x28, 0x00, 0x00, 0x00, // Descriptor Length 40bytes (0x28) + 0x00, 0x01, // Version ('1.0') + MS_EXTENDED_COMPAT_ID, 0x00, // Compatibility ID Descriptor Index 0x0004 + 0x01, // Number of sections. 0x1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved (7bytes) + // -----function section 1------ + 0x00, // Interface Number #0 + 0x01, // reserved (0x1) + 0x57, 0x49, 0x4E, 0x55, 0x53, 0x42, 0x00, 0x00, // Compatible ID ('WINUSB\0\0') (8bytes) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Sub-Compatible ID (8byte) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved (6bytes) +}; +*/ + +// Microsoft Extended Properties Feature Descriptor +/* +const char OSprop[] = { + // u32 Descriptor Length (10+132+64+102 == 308 + 0x34, 0x01, 0, 0, + // u16 Version ('1.0') + 0, 1, + // u16 wIndex + MS_EXTENDED_PROPERTIES, 0, + // u16 wCount -- three section + 3, 0, + + // -----property section 1------ + // u32 size ( 14+40+78 == 132) + 132, 0, 0, 0, + // u32 type + 1, 0, 0, 0, // unicode string + // u16 namelen (20*2 = 40) + 40, 0, + // name DeviceInterfaceGUID + 'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,'I',0,'n',0,'t',0,'e',0,'r',0,'f',0,'a',0,'c',0,'e',0,'G',0,'U',0,'I',0,'D',0,0,0, + // u32 datalen (39*2 = 78) + 78, 0, 0, 0, + // data {4D36E978-E325-11CE-BFC1-08002BE10318} + '{',0,'4',0,'d',0,'3',0,'6',0,'e',0,'9',0,'7',0,'8',0,'-',0,'e',0,'3',0,'2',0,'5',0, + '-',0,'1',0,'1',0,'c',0,'e',0,'-',0,'b',0,'f',0,'c',0,'1',0,'-',0,'0',0,'8',0,'0',0, + '0',0,'2',0,'b',0,'e',0,'1',0,'0',0,'3',0,'1',0,'8',0,'}',0,0,0, + + // -----property section 2------ + // u32 size ( 14+12+38 == 64) + 64, 0, 0, 0, + // u32 type + 1, 0, 0, 0, // unicode string + // u16 namelen (12) + 12, 0, + // name Label + 'L',0,'a',0,'b',0,'e',0,'l',0,0,0, + // u32 datalen ( 19*2 = 38 ) + 38, 0, 0, 0, + // data 'Awesome PM3 Device' + 'A',0,'w',0,'e',0,'s',0,'o',0,'m',0,'e',0,' ',0,'P',0,'M',0,'3',0,' ',0,'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,0,0, + + // -----property section 3------ + // u32 size ( 14+12+76 == 102) + 102, 0, 0, 0, + // u32 type + 2, 0, 0, 0, //Unicode string with environment variables + // u16 namelen (12) + 12, 0, + // name Icons + 'I',0,'c',0,'o',0,'n',0,'s',0,0,0, + // u32 datalen ( 38*2 == 76) + 76, 0, 0, 0, + // data '%SystemRoot%\\system32\\Shell32.dll,-13' + '%',0,'S',0,'y',0,'s',0,'t',0,'e',0,'m',0,'R',0,'o',0,'o',0,'t',0,'%',0, + '\\',0,'s',0,'y',0,'s',0,'t',0,'e',0,'m',0,'3',0,'2',0,'\\',0, + 'S',0,'h',0,'e',0,'l',0,'l',0,'3',0,'2',0,'.',0,'d',0,'l',0,'l',0,',',0, + '-',0,'1',0,'3',0,0,0 +}; + +*/ + +const char StrLanguageCodes[4] = { + 4, // Length + 0x03, // Type is string + 0x09, 0x04 // supported language Code 0 = 0x0409 (English) +}; + +// Note: ModemManager (Linux) ignores Proxmark3 devices by matching the +// manufacturer string "proxmark.org". Don't change this. +// or use the blacklisting file. +const char StrManufacturer[26] = { + 26, // Length + 0x03, // Type is string + 'p', 0, 'r', 0, 'o', 0, 'x', 0, 'm', 0, 'a', 0, 'r', 0, 'k', 0, '.', 0, 'o', 0, 'r', 0, 'g', 0, +}; + +const char StrProduct[20] = { + 20, // Length + 0x03, // Type is string + 'p', 0, 'r', 0, 'o', 0, 'x', 0, 'm', 0, 'a', 0, 'r', 0, 'k', 0, '3', 0 +}; + +#ifndef WITH_FLASH // If there is no flash, then use a fixed(const) serial number. + +const char StrSerialNumber[14] = { + 14, // Length + 0x03, // Type is string + 'i', 0, 'c', 0, 'e', 0, 'm', 0, 'a', 0, 'n', 0 +}; + +#else // WITH_FLASH is defined + +// Manually calculated size of descriptor with unique ID: +// offset 0, lengt h 1: total length field +// offset 1, length 1: descriptor type field +// offset 2, length 12: 6x unicode chars (original string) +// offset 14, length 4: 2x unicode chars (underscores) [[ to avoid descriptor being (size % 8) == 0, OS bug workaround ]] +// offset 18, length 32: 16x unicode chars (8-byte serial as hex characters) +// ============================ +// total: 50 bytes +#define USB_STRING_DESCRIPTOR_SERIAL_NUMBER_LENGTH 50 +char StrSerialNumber[50] = { + 14, // Length is initially identical to non-unique version ... The length updated at boot, if unique serial is available + 0x03, // Type is string + 'i', 0, 'c', 0, 'e', 0, 'm', 0, 'a', 0, 'n', 0, + '_', 0, '_', 0, + 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, + 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, +}; +void usb_update_serial(uint64_t newSerialNumber) { + static bool configured = false; // TODO: enable by setting to false here... + if (configured) { + return; + } + // run this only once per boot... even if it fails to find serial number + configured = true; + // reject serial number if all-zero or all-ones + if ((newSerialNumber == 0x0000000000000000) || (newSerialNumber == 0xFFFFFFFFFFFFFFFF)) { + return; + } + // Descriptor is, effectively, initially identical to non-unique serial + // number because it reports the shorter length in the first byte. + // Convert uniqueID's eight bytes to 16 unicode characters in the + // descriptor and, finally, update the descriptor's length, which + // causes the serial number to become visible. + for (uint8_t i = 0; i < 8; i++) { + // order of nibbles chosen to match display order from `hw status` + uint8_t nibble1 = (newSerialNumber >> ((8 * i) + 4)) & 0xFu; // bitmasks [0xF0, 0xF000, 0xF00000, ... 0xF000000000000000] + uint8_t nibble2 = (newSerialNumber >> ((8 * i) + 0)) & 0xFu; // bitmasks [0x0F, 0x0F00, 0x0F0000, ... 0x0F00000000000000] + char c1 = nibble1 < 10 ? '0' + nibble1 : 'A' + (nibble1 - 10); + char c2 = nibble2 < 10 ? '0' + nibble2 : 'A' + (nibble2 - 10); + StrSerialNumber[18 + (4 * i) + 0] = c1; // [ 18, 22, .., 42, 46 ] + StrSerialNumber[18 + (4 * i) + 2] = c2; // [ 20, 24, .., 44, 48 ] + } + StrSerialNumber[0] = USB_STRING_DESCRIPTOR_SERIAL_NUMBER_LENGTH; +} + +#endif + + +// size includes their own field. +const char StrMS_OSDescriptor[18] = { + 18, // length 0x12 + 0x03, // Type is string + 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, USB_CDC_DESC_MS_VENDOR_CODE, 0 +}; diff --git a/common_arm/usb/usb_cdc_desc.h b/common_arm/usb/usb_cdc_desc.h new file mode 100644 index 000000000..206f7b1b2 --- /dev/null +++ b/common_arm/usb/usb_cdc_desc.h @@ -0,0 +1,74 @@ +#ifndef USB_CDC_DESC_H +#define USB_CDC_DESC_H + +#include + +// Please define USB-CDC configurations related to specific platforms here, such as endpoint size. + +/** + * Power mode: Bit4-0 reserved, D7: bus power supply, D6: self power supply, D5: remote wake-up + */ +#define USB_CDC_DESC_CFG_POWER_MODE (0x01 << 7) + +/** + * usb cdc use endpoint define + */ +#ifdef PM5 + +#define USB_CDC_DESC_INT_EPT 0x82 +#define USB_CDC_DESC_BULK_IN_EPT 0x81 +#define USB_CDC_DESC_BULK_OUT_EPT 0x01 + +#else + +#define USB_CDC_DESC_INT_EPT 0x83 +#define USB_CDC_DESC_BULK_IN_EPT 0x82 +#define USB_CDC_DESC_BULK_OUT_EPT 0x01 + +#endif + +/** + * endpoint buffer size + */ +#ifdef PM5 + +#define USB_CDC_DESC_MAX_EP0_SIZE 64 +#define USB_CDC_DESC_IN_PACKET_SIZE 0x40 +#define USB_CDC_DESC_OUT_PACKET_SIZE 0x40 + +#else + +#define USB_CDC_DESC_MAX_EP0_SIZE 8 +#define USB_CDC_DESC_IN_PACKET_SIZE 0x40 +#define USB_CDC_DESC_OUT_PACKET_SIZE 0x40 + +#endif + + +// Fixed value. To support WCID, the device needs to respond to a special string descriptor request and return a special character descriptor. +// The Windows system will initiate a manufacturer customized request to obtain the WCID of the device based on the parameters in this character descriptor. +// After obtaining the WCID, match and install the driver based on the WCID./ +// NOTE: There are no special specifications, so the relevant definitions cannot be found on the Internet. +// https://www.usbzh.com/article/detail-625.html +#define USB_CDC_DESC_MS_VENDOR_CODE 0x1C + +// exported all desc. +extern const char devDescriptor[18]; +extern const char cfgDescriptor[67]; +extern const char bosDescriptor[12]; +extern const char StrLanguageCodes[4]; +extern const char StrManufacturer[26]; +extern const char StrProduct[20]; + +// If the device has FLASH, the USB serial number is dynamically generated(NOT const). +#ifndef WITH_FLASH +extern const char StrSerialNumber[14]; +#else +extern char StrSerialNumber[50]; +#endif + +// WCID, for DRIVER auto install on windows platform. +// DOCS: https://www.usbzh.com/article/detail-625.html +extern const char StrMS_OSDescriptor[18]; + +#endif diff --git a/common_arm/usb/usb_read_ng.c b/common_arm/usb/usb_read_ng.c new file mode 100644 index 000000000..d0bbdd0f0 --- /dev/null +++ b/common_arm/usb/usb_read_ng.c @@ -0,0 +1,97 @@ +#include "string.h" +#include "usb_read_ng.h" + +static const usb_read_ng_config_t *g_config = NULL; +static size_t g_buf_len = 0; +static size_t g_buf_offset = 0; + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +void usb_read_ng_init(const usb_read_ng_config_t *config) +{ + if (!config || !config->is_link_ready || !config->is_data_ready || + !config->get_byte_count || !config->read_fifo || + !config->clear_ready || !config->buffer || config->buffer_size == 0) { + return; + } + g_config = config; + g_buf_len = 0; + g_buf_offset = 0; +} + +bool usb_read_ng_has_buffered_data(void) +{ + return g_buf_len > 0; +} + +uint32_t usb_read_ng(uint8_t *data, size_t len) +{ + if (!g_config || !data || len == 0) { + return 0; + } + + uint32_t nbBytesRcv = 0; + uint16_t time_out = 0; + const uint16_t timeout_limit = g_config->timeout; // Timeout value of platform + + // First from buffer of this module. + if (len <= g_buf_len) { + memcpy(data, g_config->buffer + g_buf_offset, len); + g_buf_len -= len; + g_buf_offset = g_buf_len ? g_buf_offset + len : 0; + return len; + } + + if (g_buf_len > 0) { + memcpy(data, g_config->buffer + g_buf_offset, g_buf_len); + nbBytesRcv = g_buf_len; + len -= g_buf_len; + g_buf_len = 0; + g_buf_offset = 0; + } + + while (len > 0) { + // 1. if usb status is disconnected or unopened, exit read for device side. + if (!g_config->is_link_ready()) { + break; + } + + // 2. check if data ready for usb device, if not, skip read and check timeout. + if (g_config->is_data_ready()) { + uint16_t available = g_config->get_byte_count(); + uint16_t packetSize = MIN(available, len); + + for (uint16_t i = 0; i < packetSize; i++) { + data[nbBytesRcv++] = g_config->read_fifo(); // read from device fifo. + } + available -= packetSize; + len -= packetSize; + + size_t to_buffer = (available < g_config->buffer_size) ? available : g_config->buffer_size; + for (size_t i = 0; i < to_buffer; i++) { + g_config->buffer[i] = g_config->read_fifo(); + } + g_buf_len = to_buffer; + g_buf_offset = 0; + + // gc gc gc gc !!! + g_config->clear_ready(); + time_out = 0; // Timeout reset. + } + else { + // usb link ready but no data. to check simple timeout. + if (timeout_limit > 0) { + time_out++; + if (time_out >= timeout_limit) { + break; // exit if timeout. + } + } else { + break; // no timeout. exit immediately. + } + } + } + + return nbBytesRcv; +} diff --git a/common_arm/usb/usb_read_ng.h b/common_arm/usb/usb_read_ng.h new file mode 100644 index 000000000..d9d625f5e --- /dev/null +++ b/common_arm/usb/usb_read_ng.h @@ -0,0 +1,52 @@ +#ifndef USB_READ_NG_H +#define USB_READ_NG_H + +#include +#include +#include + +// --------------------- HAL BY DXL --------------------- +// We need to consider whether it will affect the reading performance of USB in order to optimize it. +// The HAL layer should not contain any code related the platform. + +#ifdef __cplusplus +extern "C" { +#endif + + // Callbacks, implement functions on platform related. + typedef bool (*usb_link_ready_cb_t)(void); // is usb link ready? + typedef bool (*usb_data_ready_cb_t)(void); // is data received ready? + typedef uint16_t (*usb_get_byte_count_cb_t)(void); // how length of data received? + typedef uint8_t (*usb_read_fifo_cb_t)(void); // read byte from fifo + typedef void (*usb_clear_rx_ready_cb_t)(void); // clear + + // Configs, instance of platform. + typedef struct { + usb_link_ready_cb_t is_link_ready; + usb_data_ready_cb_t is_data_ready; + usb_get_byte_count_cb_t get_byte_count; + usb_read_fifo_cb_t read_fifo; + usb_clear_rx_ready_cb_t clear_ready; + + uint8_t *buffer; // buffer for read ng of platform. + size_t buffer_size; // buffer size + uint16_t timeout; // read timeout if no data ready. + } usb_read_ng_config_t; + + // setup usb read ng, implement all usb related function for platform. + // @param: config - the module will use this point on global, so don't instance in function stack! + void usb_read_ng_init(const usb_read_ng_config_t *config); + + // exported api, not platform related, for check data available of local usb ng buffer(not usb ep buffer) + // nonblocking api + bool usb_read_ng_has_buffered_data(void); + + // exported api, not platform related, for read data from local buffer or usb device online. + // nonblocking api + uint32_t usb_read_ng(uint8_t *data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/common_arm/wdt/wdt_apis.h b/common_arm/wdt/wdt_apis.h new file mode 100644 index 000000000..cb3abfe87 --- /dev/null +++ b/common_arm/wdt/wdt_apis.h @@ -0,0 +1,25 @@ +#ifndef WDT_APIS_H_ +#define WDT_APIS_H_ + +#include "common.h" + + +/* + * The AT32 platform cannot disable the watchdog! + * So it is important to handle the gap between blocking tasks and HIT operations. + */ + + +// feed the dog +STATIC_FORCE_INLINE void WDT_HIT(void); + +// hardware watch dog setup and enable +void WDTSetup(void); + +#ifdef PM5 +#include "wdt_hw_at32.h" +#else +#include "wdt_hw_at91.h" +#endif + +#endif // WDT_APIS_H_ diff --git a/common_arm/wdt/wdt_hw_at32.c b/common_arm/wdt/wdt_hw_at32.c new file mode 100644 index 000000000..a816acdf8 --- /dev/null +++ b/common_arm/wdt/wdt_hw_at32.c @@ -0,0 +1,6 @@ +#include "wdt_apis.h" + +void WDTSetup(void) { + // TODO DXL 待实现,记得尽量把时间拉长一些,让某些耗时堵塞逻辑不会导致看门狗复位 + // 特别关注flash相关的操作,在AT91上会在操作FLASH之前禁用看门狗,但是AT32是无法禁用看门狗的 +} diff --git a/common_arm/wdt/wdt_hw_at32.h b/common_arm/wdt/wdt_hw_at32.h new file mode 100644 index 000000000..0a575a9f2 --- /dev/null +++ b/common_arm/wdt/wdt_hw_at32.h @@ -0,0 +1,10 @@ +#ifndef __WDT_HW_AT32_H__ +#define __WDT_HW_AT32_H__ + +#include "common.h" + +STATIC_FORCE_INLINE void WDT_HIT(void) { + // TODO 待实现喂狗 +} + +#endif \ No newline at end of file diff --git a/common_arm/wdt/wdt_hw_at91.c b/common_arm/wdt/wdt_hw_at91.c new file mode 100644 index 000000000..f10de09d5 --- /dev/null +++ b/common_arm/wdt/wdt_hw_at91.c @@ -0,0 +1,5 @@ +#include "wdt_apis.h" + +void WDTSetup(void) { + // No need to implement. +} diff --git a/common_arm/wdt/wdt_hw_at91.h b/common_arm/wdt/wdt_hw_at91.h new file mode 100644 index 000000000..be9054282 --- /dev/null +++ b/common_arm/wdt/wdt_hw_at91.h @@ -0,0 +1,11 @@ +#ifndef __WDT_HW_AT91_H__ +#define __WDT_HW_AT91_H__ + +#include "common.h" +#include "at91sam7s512.h" + +STATIC_FORCE_INLINE void WDT_HIT(void) { + AT91C_BASE_WDTC->WDTC_WDCR = 0xa5000001; +} + +#endif \ No newline at end of file diff --git a/include/hitag.h b/include/hitag.h index 850ea68be..e6b4175e9 100644 --- a/include/hitag.h +++ b/include/hitag.h @@ -71,7 +71,7 @@ typedef enum modulation { AC4K, // Amplitude modulation 4000 bits/s MC4K, // Manchester modulation 4000 bits/s MC8K // Manchester modulation 8000 bits/s -} MOD; +} hitag_mod_t; typedef enum { HTSF_PLAIN, diff --git a/include/proxmark3_arm.h b/include/proxmark3_arm.h index aa02aea7c..86d5a42fc 100644 --- a/include/proxmark3_arm.h +++ b/include/proxmark3_arm.h @@ -25,13 +25,13 @@ #include "at91sam7s512.h" #include "config_gpio.h" #include "pm3_cmd.h" +#include "gpio_apis.h" +#include "wdt_apis.h" // Check bootrom.c for actual clock settings #define MAINCK 16000000 #define MCK (3 * MAINCK) -#define WDT_HIT() AT91C_BASE_WDTC->WDTC_WDCR = 0xa5000001 - #define PWM_CH_MODE_PRESCALER(x) ((x) << 0) #define PWM_CHANNEL(x) (1 << (x)) @@ -70,14 +70,58 @@ #define UDP_CSR_BYTES_RECEIVED(x) (((x) >> 16) & 0x7ff) //************************************************************** +// ------------------------------------------------------------------------------ +// For LED, is high on or low on? +// For BTN, is high pressed or low pressed? +// 0 = LOW, 1 = HIGH +// ------------------------------------------------------------------------------ +#ifdef PM5 +#define LED_PIN_DIR 0 // LOW = ON +#define BTN_PIN_DIR 1 // HIGH = PRESSED +#else +#define LED_PIN_DIR 1 // HIGH = ON +#define BTN_PIN_DIR 0 // LOW = PRESSED +#endif +#if LED_PIN_DIR // Is the light on when the PIN is high? +#define LED_A_ON() Gpio_LED_A_High() +#define LED_A_OFF() Gpio_LED_A_Low() +#define LED_B_ON() Gpio_LED_B_High() +#define LED_B_OFF() Gpio_LED_B_Low() +#define LED_C_ON() Gpio_LED_C_High() +#define LED_C_OFF() Gpio_LED_C_Low() +#define LED_D_ON() Gpio_LED_D_High() +#define LED_D_OFF() Gpio_LED_D_Low() +#else +#define LED_A_ON() Gpio_LED_A_Low() +#define LED_A_OFF() Gpio_LED_A_High() +#define LED_B_ON() Gpio_LED_B_Low() +#define LED_B_OFF() Gpio_LED_B_High() +#define LED_C_ON() Gpio_LED_C_Low() +#define LED_C_OFF() Gpio_LED_C_High() +#define LED_D_ON() Gpio_LED_D_Low() +#define LED_D_OFF() Gpio_LED_D_High() +#endif -#define LOW(x) AT91C_BASE_PIOA->PIO_CODR |= (x) -#define HIGH(x) AT91C_BASE_PIOA->PIO_SODR |= (x) +#define LED_A_INV() Gpio_LED_A_Inv() +#define LED_B_INV() Gpio_LED_B_Inv() +#define LED_C_INV() Gpio_LED_C_Inv() +#define LED_D_INV() Gpio_LED_D_Inv() -#define GETBIT(x) (AT91C_BASE_PIOA->PIO_ODSR & (x)) ? 1:0 -#define SETBIT(x, y) (y) ? (HIGH(x)):(LOW(x)) -#define INVBIT(x) SETBIT((x), !(GETBIT(x))) +#if BTN_PIN_DIR // Is the button pressed by the PIN is high? +#define BUTTON_PRESS() Gpio_Button_Read() +#else +#define BUTTON_PRESS() (!Gpio_Button_Read()) +#endif +#define WAIT_BUTTON_RELEASED() { while ( BUTTON_PRESS() ) { WDT_HIT(); }; } + +#define RELAY_ON() Gpio_Relay_High() +#define RELAY_OFF() Gpio_Relay_Low() + +// NVDD goes LOW when USB is attached. +// see: https://github.com/RfidResearchGroup/proxmark3/blob/8ce72a9dfe37a30d6a5f88c6490ad1b45f8b8ece/doc/original_proxmark3/system.txt#L104 +// This IO port is using in a very old pm3 device. +#define USB_ATTACHED() !Gpio_NVDD_Read() // Setup for SPI current modes #define SPI_FPGA_MODE 0 @@ -88,48 +132,8 @@ #define COTAG_BITS 264 #endif -#define LED_A_ON() HIGH(GPIO_LED_A) -#define LED_A_OFF() LOW(GPIO_LED_A) -#define LED_A_INV() INVBIT(GPIO_LED_A) -#define LED_B_ON() HIGH(GPIO_LED_B) -#define LED_B_OFF() LOW(GPIO_LED_B) -#define LED_B_INV() INVBIT(GPIO_LED_B) -#define LED_C_ON() HIGH(GPIO_LED_C) -#define LED_C_OFF() LOW(GPIO_LED_C) -#define LED_C_INV() INVBIT(GPIO_LED_C) -#define LED_D_ON() HIGH(GPIO_LED_D) -#define LED_D_OFF() LOW(GPIO_LED_D) -#define LED_D_INV() INVBIT(GPIO_LED_D) - - -// SPI -#define SCK_LOW LOW(GPIO_SPCK) -#define SCK_HIGH HIGH(GPIO_SPCK) -#define MOSI_HIGH HIGH(GPIO_MOSI) -#define MOSI_LOW LOW(GPIO_MOSI) -#define MISO_VALUE (AT91C_BASE_PIOA->PIO_PDSR & GPIO_MISO) - -// fpga -#define NCS_0_LOW LOW(GPIO_NCS0) -#define NCS_0_HIGH HIGH(GPIO_NCS0) - -// flash mem PA1 -#define NCS_1_LOW LOW(GPIO_NCS2) -#define NCS_1_HIGH HIGH(GPIO_NCS2) - -#define RELAY_ON() HIGH(GPIO_RELAY) -#define RELAY_OFF() LOW(GPIO_RELAY) - -#define BUTTON_PRESS() !((AT91C_BASE_PIOA->PIO_PDSR & GPIO_BUTTON) == GPIO_BUTTON) -#define WAIT_BUTTON_RELEASED() { while ( BUTTON_PRESS() ) { WDT_HIT(); }; } - -//NVDD goes LOW when USB is attached. -#define USB_ATTACHED() !((AT91C_BASE_PIOA->PIO_PDSR & GPIO_NVDD_ON) == GPIO_NVDD_ON) - - #define DBG if (g_dbglevel >= DBG_EXTENDED) - // VERSION_INFORMATION is now in common.h #define COMMON_AREA_MAGIC 0x43334d50 // "PM3C" diff --git a/tools/FixCompileTest.cmake b/tools/FixCompileTest.cmake new file mode 100644 index 000000000..308b8e155 --- /dev/null +++ b/tools/FixCompileTest.cmake @@ -0,0 +1,6 @@ +# ---------------------------------------------------------- +# Fix error when 'compile a simple test program.' +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR arm) +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +# ---------------------------------------------------------- \ No newline at end of file diff --git a/tools/FpgaCompress.cmake b/tools/FpgaCompress.cmake new file mode 100644 index 000000000..01dca996c --- /dev/null +++ b/tools/FpgaCompress.cmake @@ -0,0 +1,46 @@ +if (NOT DEFINED PLATFORM) + message(FATAL_ERROR "Include this module after PLATFORM check.") +endif () + +#[[ + Make All variables private(Exclude EXE)!!! +]] + +# For get_exe_suffix function to detect the suffix of executable for current compiler, like '.exe' for Windows. +include(${CMAKE_CURRENT_LIST_DIR}/GetExeSuffixByCompiler.cmake) + +set(_fc_MYINCLUDES -I${CMAKE_CURRENT_LIST_DIR}/../common_fpga) +set(_fc_MYCFLAGS -std=c99 -D_ISOC99_SOURCE) + +if (PLATFORM STREQUAL PM3ICOPYX) + set(_fc_MYDEFS -DXC3) +else () + set(_fc_MYDEFS) +endif () + +set(_fc_MYINCLUDES ${_fc_MYINCLUDES} -I${CMAKE_CURRENT_LIST_DIR}/../common/lz4) +set(_fc_MYCFLAGS ${_fc_MYCFLAGS} -DLZ4_MEMORY_USAGE=20 -Wno-redundant-decls -Wno-old-style-definition -Wno-missing-prototypes -Wno-missing-declarations) +set(_fc_MYSRCS ${_fc_MYSRCS} + ${CMAKE_CURRENT_LIST_DIR}/fpga_compress/fpga_compress.c + ${CMAKE_CURRENT_LIST_DIR}/../common/lz4/lz4hc.c + ${CMAKE_CURRENT_LIST_DIR}/../common/lz4/lz4.c) + +# The target name for fpga_compress +set(FPGA_COMPRESS fpga_compress) +get_exe_suffix(FPGA_COMPRESS_EXE_SUFFIX ${C_COMPILER_HOST}) +get_filename_component(FPGA_COMPRESS_EXE "${CMAKE_CURRENT_LIST_DIR}/fpga_compress/${FPGA_COMPRESS}${FPGA_COMPRESS_EXE_SUFFIX}" ABSOLUTE) # executable filename + suffix +add_custom_command(OUTPUT ${FPGA_COMPRESS_EXE} + # COMMAND ${CMAKE_COMMAND} -E echo "[=] CC ${FPGA_COMPRESS} executable" + COMMAND ${C_COMPILER_HOST} + ${_fc_MYCFLAGS} + ${_fc_MYDEFS} + ${_fc_MYINCLUDES} + ${_fc_MYSRCS} + -o ${FPGA_COMPRESS_EXE} + DEPENDS ${_fc_MYSRCS} # if src update, this custom command will run. tips: only .c files watch now, you can add .h files watch if need. + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/fpga_compress + COMMENT "Build 'fpga_compress' executable for next step" + VERBATIM) + +# add_custom_target always run if project build, but add_custom_command can cached output if no files update. +add_custom_target(${FPGA_COMPRESS} DEPENDS ${FPGA_COMPRESS_EXE}) diff --git a/tools/GetExeSuffixByCompiler.cmake b/tools/GetExeSuffixByCompiler.cmake new file mode 100644 index 000000000..9178bd4be --- /dev/null +++ b/tools/GetExeSuffixByCompiler.cmake @@ -0,0 +1,24 @@ +function(get_exe_suffix OUTPUT_VAR GCC_COMPILER) + set(TEST_SRC ${CMAKE_CURRENT_BINARY_DIR}/detect_suffix.c) + set(TEST_EXE ${CMAKE_CURRENT_BINARY_DIR}/detect_suffix) + + file(WRITE ${TEST_SRC} "int main(){return 0;}\n") + + execute_process( + COMMAND ${GCC_COMPILER} ${TEST_SRC} -o ${TEST_EXE} + RESULT_VARIABLE RES + ) + + if (EXISTS "${TEST_EXE}.exe") + set(${OUTPUT_VAR} ".exe" PARENT_SCOPE) + else () + set(${OUTPUT_VAR} "" PARENT_SCOPE) + endif () + + # Clear + file(REMOVE ${TEST_SRC} ${TEST_EXE} ${TEST_EXE}.exe ${TEST_EXE}.o) +endfunction() + +# Usage: +# get_exe_suffix(${C_COMPILER_HOST} DETECTED_SUFFIX) +# message(STATUS "Detected suffix: '${DETECTED_SUFFIX}'") diff --git a/tools/MKVersionScript.cmake b/tools/MKVersionScript.cmake new file mode 100644 index 000000000..566a6bdf5 --- /dev/null +++ b/tools/MKVersionScript.cmake @@ -0,0 +1,109 @@ +# ------------------------------------------------------------------------ +# Power by DXL +# +# Detect platform for run mkversion script +# +# Injection variable: +# MKVERSION_SCRIPT_FOUND - Is script found? +# MKVERSION_SCRIPT - The script full path +# MKVERSION_SCRIPT_TYPE - Script file type: "bat" or "sh" +# VERSION_INFO - Version info from mkversion.(bat|sh) --short +# MKVERSION_AVAILABLE - A boolean variable, tell me is script working and VERSION_INFO set. +# MKVERSION_CMD - The final command used to generate the source file version_pm3.c + +# Only once run on setup(MKVERSION_AVAILABLE is a cached variable. Next reload will direct set and skip.) +if (DEFINED MKVERSION_AVAILABLE) + return() +endif () + +# Setup variable and export +set(MKVERSION_SCRIPT_FOUND FALSE) +set(MKVERSION_SCRIPT "") +set(MKVERSION_SCRIPT_TYPE "") +set(VERSION_INFO "unknown") +set(MKVERSION_AVAILABLE FALSE) + +# The script for platform +set(MKVERSION_CANDIDATES_BAT "${CMAKE_CURRENT_LIST_DIR}/mkversion.bat") +set(MKVERSION_CANDIDATES_SH "${CMAKE_CURRENT_LIST_DIR}/mkversion.sh") + +# We are running on the Windows? +set(MKVERSION_RUN_NATIVE_WINDOWS FALSE) +execute_process(COMMAND uname OUTPUT_VARIABLE uname) +if (uname MATCHES "^MSYS" OR uname MATCHES "^MINGW" OR uname MATCHES "^Lin" OR uname MATCHES "^Darwin") + message(STATUS "Not running on native Windows (uname is ${uname}).") +else () + set(MKVERSION_RUN_NATIVE_WINDOWS TRUE) +endif () + +# First setup, we can run once '--short' for mkversion script to get like 'Iceman/master/b36b61feb-dirty' +if (MKVERSION_RUN_NATIVE_WINDOWS) + execute_process( + COMMAND cmd /c call "${MKVERSION_CANDIDATES_BAT}" --short + OUTPUT_VARIABLE _output + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + RESULT_VARIABLE _result + ) + if (${_result} EQUAL 0) + set(MKVERSION_SCRIPT "${MKVERSION_CANDIDATES_BAT}") + set(MKVERSION_SCRIPT_TYPE "bat") + set(VERSION_INFO "${_output}") + set(MKVERSION_SCRIPT_FOUND TRUE) + endif () +else () + # Is 'sh' available? + find_program(SH_COMMAND sh) + if (SH_COMMAND) + execute_process( + COMMAND "${SH_COMMAND}" "${MKVERSION_CANDIDATES_SH}" --short + OUTPUT_VARIABLE _output + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + RESULT_VARIABLE _result + ) + if (${_result} EQUAL 0) + set(MKVERSION_SCRIPT "${MKVERSION_CANDIDATES_SH}") + set(MKVERSION_SCRIPT_TYPE "sh") + set(VERSION_INFO "${_output}") + set(MKVERSION_SCRIPT_FOUND TRUE) + endif () + endif () +endif () + +# Finally, the script found and version info from '--short' available? +if (MKVERSION_SCRIPT_FOUND AND NOT "${VERSION_INFO}" STREQUAL "") + set(MKVERSION_AVAILABLE TRUE) +else () + set(MKVERSION_AVAILABLE FALSE) +endif () + +# Export finally cmd for generate version_pm3.c +if (MKVERSION_SCRIPT_TYPE STREQUAL "sh") # What's shell type we are? + set(MKVERSION_CMD ${CMAKE_COMMAND} -E env bash ${MKVERSION_SCRIPT}) # Unix/Linux/macOS: call sh +elseif (MKVERSION_SCRIPT_TYPE STREQUAL "bat") + set(MKVERSION_CMD cmd /c ${MKVERSION_SCRIPT}) # Windows: call bat +else () + message(FATAL_ERROR "MKVERSION_SCRIPT_TYPE must be 'sh' or 'bat', but got '${MKVERSION_SCRIPT_TYPE}'") +endif () + +# Set variables to cached level, next reload CmakeLists.txt will skip run this subscript(MKVersionScript.cmake). +set(MKVERSION_SCRIPT_FOUND ${MKVERSION_SCRIPT_FOUND} CACHE INTERNAL "Whether a mkversion script was found") +set(MKVERSION_SCRIPT ${MKVERSION_SCRIPT} CACHE INTERNAL "Full path to the mkversion script") +set(MKVERSION_SCRIPT_TYPE ${MKVERSION_SCRIPT_TYPE} CACHE INTERNAL "Type of script: 'bat' or 'sh'") +set(VERSION_INFO ${VERSION_INFO} CACHE INTERNAL "Version info string from --short") +set(MKVERSION_AVAILABLE ${MKVERSION_AVAILABLE} CACHE INTERNAL "Whether version info is available") +set(MKVERSION_CMD ${MKVERSION_CMD} CACHE INTERNAL "Command to generate version info") + +# Debug info(Optional, can enable when debuging...) +if (FALSE) + message(STATUS "===================================================================") + message(STATUS "MKVERSION_SCRIPT_FOUND: ${MKVERSION_SCRIPT_FOUND}") + message(STATUS "MKVERSION_SCRIPT: ${MKVERSION_SCRIPT}") + message(STATUS "MKVERSION_SCRIPT_TYPE: ${MKVERSION_SCRIPT_TYPE}") + message(STATUS "VERSION_INFO: ${VERSION_INFO}") + message(STATUS "MKVERSION_CMD: ${MKVERSION_CMD}") + message(STATUS "===================================================================") +endif () diff --git a/tools/ToolchainForArm.cmake b/tools/ToolchainForArm.cmake new file mode 100644 index 000000000..76ead1e80 --- /dev/null +++ b/tools/ToolchainForArm.cmake @@ -0,0 +1,23 @@ +# If it has already been found, there is no need to search again. +if(NOT DEFINED CMAKE_C_COMPILER) + set(TOOLCHAIN_PREFIX arm-none-eabi-) + + # 1. Find the compiler + find_program(ARM_GCC_COMPILER ${TOOLCHAIN_PREFIX}gcc) + find_program(ARM_GXX_COMPILER ${TOOLCHAIN_PREFIX}g++) + find_program(ARM_ASM_COMPILER ${TOOLCHAIN_PREFIX}gcc) + + # 2. Check if the core C compiler is found + if(ARM_GCC_COMPILER) + message(STATUS "Found ARM GCC: ${ARM_GCC_COMPILER}") + + # Set the found absolute paths (with or without .exe) directly to CMake + set(CMAKE_C_COMPILER ${ARM_GCC_COMPILER} CACHE PATH "C compiler") + set(CMAKE_CXX_COMPILER ${ARM_GXX_COMPILER} CACHE PATH "CXX compiler") + set(CMAKE_ASM_COMPILER ${ARM_ASM_COMPILER} CACHE PATH "ASM compiler") + + else() + message(WARNING "ARM GCC (${TOOLCHAIN_PREFIX}gcc) not found in PATH!") + endif() + +endif () \ No newline at end of file diff --git a/tools/mkversion.bat b/tools/mkversion.bat new file mode 100644 index 000000000..014458410 --- /dev/null +++ b/tools/mkversion.bat @@ -0,0 +1,20 @@ +@echo off +setlocal + +:: Power by DXL +:: The mkversion.ps1 wrapper. why not mkversion.bat implement all function? +:: > Not good idea to call sha256 for arm src, so you known.. + +:: Did not output localization +set LC_ALL=C +set LANG=C + +:: Where are this script? +set "SCRIPT_DIR=%~dp0" + +:: Call PowerShell script, and send params +powershell -ExecutionPolicy Bypass -File "%SCRIPT_DIR%mkversion.ps1" %* + +if %ERRORLEVEL% NEQ 0 ( + exit /b %ERRORLEVEL% +) \ No newline at end of file diff --git a/tools/mkversion.ps1 b/tools/mkversion.ps1 new file mode 100644 index 000000000..f8e19c644 --- /dev/null +++ b/tools/mkversion.ps1 @@ -0,0 +1,191 @@ +<# + Power By DXL + The mkversion.sh same functions. +#> + +param( + [string]$Destination, + [switch]$Short, + [switch]$Force, + [switch]$Undecided +) + +# 默认 fork 名称(可自定义) +$fullgitinfo = "Iceman" +$clean = 2 # 2 = undecided, 1 = clean, 0 = dirty + +# 获取脚本所在目录 +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$pm3Root = Join-Path $ScriptDir ".." + +# 检查是否在 Git 工作目录中 +$inGit = $false +$gitDir = Join-Path $pm3Root ".git" +if (Test-Path $gitDir -PathType Container) { + # 尝试执行 git 命令 + $result = $null + try { + $result = git -C $pm3Root rev-parse --is-inside-work-tree 2>$null + } catch { } + if ($result -eq "true") { + $inGit = $true + } +} + +if ($inGit) { + try { + $gitVersion = (git -C $pm3Root describe --dirty --always).Trim() + $gitBranch = (git -C $pm3Root rev-parse --abbrev-ref HEAD).Trim() + + if ($Undecided) { + if ($gitVersion -like "*-dirty") { + $clean = 0 + } else { + $clean = 1 + } + } + + $fullgitinfo = "$fullgitinfo/$gitBranch/$gitVersion" + + # 使用 FORCED_DATE 或当前时间 + if ($env:FORCED_DATE -match '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$') { + $ctime = $env:FORCED_DATE + } else { + $ctime = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + } + } catch { + $fullgitinfo = "$fullgitinfo/master/release (git_error)" + $ctime = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + } +} else { + $fullgitinfo = "$fullgitinfo/master/release (no_git)" + $readmePath = Join-Path $pm3Root "README.md" + if (Test-Path $readmePath) { + $dl_time = (Get-Item $readmePath).LastWriteTime + $ctime = $dl_time.ToString("yyyy-MM-dd HH:mm:ss") + } else { + $ctime = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + } +} + +# --short 模式:只输出版本字符串 +if ($Short) { + if ($fullgitinfo.Length -gt 49) { + $fullgitinfo = $fullgitinfo.Substring(0, 46) + "..." + } + Write-Output $fullgitinfo + exit 0 +} + +# 检查是否提供了输出文件路径 +if (-not $Destination) { + Write-Error "Error: missing destination filename" + exit 1 +} + +# 计算 ARM 源码的 SHA256 哈希(前 9 位) +$armSrc = Join-Path $pm3Root "armsrc" +$commonArm = Join-Path $pm3Root "common_arm" +$files = @() + +if (Test-Path $armSrc) { + $files += Get-ChildItem $armSrc | Where-Object { + ($_.Extension -eq ".c" -or $_.Extension -eq ".h") -and + $_.Name -notmatch "^(disabled|version_pm3|fpga_version_info)" + } +} + +if (Test-Path $commonArm) { + $files += Get-ChildItem $commonArm | Where-Object { + ($_.Extension -eq ".c" -or $_.Extension -eq ".h") -and + $_.Name -notmatch "^(disabled|version_pm3|fpga_version_info)" + } +} + +$sha = "no sha256" +if ($files.Count -gt 0) { + # 按文件名排序(确保一致性) + $sortedFiles = $files | Sort-Object Name + + # 创建 SHA256 实例 + $sha256 = New-Object System.Security.Cryptography.SHA256Managed + + foreach ($file in $sortedFiles) { + try { + $content = [System.IO.File]::ReadAllBytes($file.FullName) + $sha256.TransformBlock($content, 0, $content.Length, $null, 0) | Out-Null + } catch { + Write-Warning "Failed to read file: $($file.FullName)" + } + } + + # 完成哈希计算 + $sha256.TransformFinalBlock([byte[]]@(), 0, 0) + $hashBytes = $sha256.Hash + $shaHex = -join ($hashBytes | ForEach-Object { $_.ToString("x2") }) + $sha = $shaHex.Substring(0, 9) +} + +# 截断 fullgitinfo 到最多 49 字符 +if ($fullgitinfo.Length -gt 49) { + $fullgitinfo = $fullgitinfo.Substring(0, 46) + "..." +} + +# 检查是否需要重新生成文件(避免无意义更新) +$regenerate = $true + +if (-not $Force -and (Test-Path $Destination)) { + try { + $content = Get-Content $Destination -Raw + + # 提取 clean 状态(第13行附近) + if ($content -match '^\s*(\d+)\s*$' -and $matches[1] -eq $clean) { + # 提取 fullgitinfo(第14行) + if ($content -match '^\s*"([^"]+)"\s*$' -and $matches[1] -eq $fullgitinfo) { + # 提取 sha(第16行) + if ($content -match '^\s*"([^"]+)"\s*$' -and $matches[1] -eq $sha) { + $regenerate = $false + } + } + } + } catch { + $regenerate = $true + } +} + +# 仅当需要时才写入文件 +if ($regenerate) { + $tmpFile = "$Destination.tmp" + $output = @" +#include "common.h" +/* Generated file, do not edit */ +#ifndef ON_DEVICE +#define SECTVERSINFO +#else +#define SECTVERSINFO __attribute__((section(".version_information"))) +#endif + +const struct version_information_t SECTVERSINFO g_version_information = { + VERSION_INFORMATION_MAGIC, + 1, + 1, + $clean, + "$fullgitinfo", + "$ctime", + "$sha" +}; +"@ + + try { + $output | Out-File -FilePath $tmpFile -Encoding ASCII -Force + Move-Item -Path $tmpFile -Destination $Destination -Force + } catch { + Write-Error "Failed to write version file: $_" + if (Test-Path $tmpFile) { + Remove-Item $tmpFile -Force + } + exit 1 + } +} + +exit 0 \ No newline at end of file diff --git a/tools/mkversion.sh b/tools/mkversion.sh index f4dd350d6..dc8a182f1 100755 --- a/tools/mkversion.sh +++ b/tools/mkversion.sh @@ -102,9 +102,9 @@ sha=$( [ -f armsrc/appmain.c ] || return if [ "${OSTYPE#darwin}" != "$OSTYPE" ]; then # macOS - ls armsrc/*.[ch] common_arm/*.[ch]|grep -E -v "(disabled|version_pm3|fpga_version_info)"|sort|xargs shasum -a 256 -t|shasum -a 256|cut -c -9 + ls armsrc/*.[ch] common_arm/*/*.[ch]|grep -E -v "(disabled|version_pm3|fpga_version_info)"|sort|xargs shasum -a 256 -t|shasum -a 256|cut -c -9 else - ls armsrc/*.[ch] common_arm/*.[ch]|grep -E -v "(disabled|version_pm3|fpga_version_info)"|sort|xargs sha256sum -t|sha256sum|cut -c -9 + ls armsrc/*.[ch] common_arm/*/*.[ch]|grep -E -v "(disabled|version_pm3|fpga_version_info)"|sort|xargs sha256sum -t|sha256sum|cut -c -9 fi ) if [ "$sha" = "" ]; then