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/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