Bootrom: Enable serial number from flash

* Add `.ramfunc` section to bootrom loader script
* exclude spiffs functionality from flashmem.h/flashmem.c
   (allows bootrom to use flashmem)
* hide unused tick.h / flashmem.h functions from bootrom
   (not technically necessary; see comments)
* bootrom: add source files, include path, and defines when
  `PLATFORM_DEFS` defines `WITH_FLASH`
* Define `AS_BOOTROM` to indicate code is building for bootrom
This commit is contained in:
Henry Gabryjelski 2023-02-17 16:59:00 -08:00
commit 44676bde72
9 changed files with 373 additions and 272 deletions

628
common_arm/flashmem.c Normal file
View file

@ -0,0 +1,628 @@
//-----------------------------------------------------------------------------
// Borrowed initially from Arduino SPIFlash Library v.2.5.0
// Copyright (C) 2015 by Prajwal Bhattaram.
// 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.
//-----------------------------------------------------------------------------
#include "flashmem.h"
#include "pmflash.h"
#include "proxmark3_arm.h"
#include "ticks.h"
#ifndef AS_BOOTROM
#include "dbprint.h"
#endif // AS_BOOTROM
#include "string.h"
#include "usb_cdc.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)
static uint32_t FLASHMEM_SPIBAUDRATE = FLASH_BAUD;
#define FASTFLASH (FLASHMEM_SPIBAUDRATE > FLASH_MINFAST)
#ifndef AS_BOOTROM
void FlashmemSetSpiBaudrate(uint32_t baudrate) {
FLASHMEM_SPIBAUDRATE = baudrate;
Dbprintf("Spi Baudrate : %dMHz", FLASHMEM_SPIBAUDRATE / 1000000);
}
// read ID out
uint8_t Flash_ReadID(void) {
if (Flash_CheckBusy(BUSY_TIMEOUT)) return 0;
// Manufacture ID / device ID
FlashSendByte(ID);
FlashSendByte(0x00);
FlashSendByte(0x00);
FlashSendByte(0x00);
uint8_t man_id = FlashSendByte(0xFF);
uint8_t dev_id = FlashSendLastByte(0xFF);
if (g_dbglevel > 3) Dbprintf("Flash ReadID | Man ID %02x | Device ID %02x", man_id, dev_id);
if ((man_id == WINBOND_MANID) && (dev_id == WINBOND_DEVID))
return dev_id;
return 0;
}
uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len) {
if (!FlashInit()) return 0;
// length should never be zero
if (!len || 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);
out[i] = FlashSendLastByte(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) 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);
out[i] = FlashSendLastByte(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)
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;
}
// out-of-range
if (((address >> 16) & 0xFF) > MAX_BLOCKS) {
Dbprintf("Flash_WriteData, block out-of-range");
return 0;
}
if (!FlashInit()) {
if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail");
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;
}
// length should never be zero
// Max 256 bytes write
// out-of-range
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) > MAX_BLOCKS) {
Dbprintf("Flash_WriteDataCont, block out-of-range");
return 0;
}
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]);
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)
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()) {
if (g_dbglevel > 3) 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()) {
if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail");
return false;
}
Flash_ReadStat1();
// Each block is 64Kb. Four blocks
// one block erase takes 1s ( 1000ms )
Flash_WriteEnable();
Flash_Erase64k(0);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase64k(1);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase64k(2);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase64k(3);
Flash_CheckBusy(BUSY_TIMEOUT);
FlashStop();
return true;
}
// enable the flash write
void Flash_WriteEnable(void) {
FlashSendLastByte(WRITEENABLE);
if (g_dbglevel > 3) 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 > MAX_BLOCKS || sector > MAX_SECTORS) return false;
FlashSendByte(SECTORERASE);
FlashSendByte(block);
FlashSendByte(sector << 4);
FlashSendLastByte(00);
return true;
}
/*
// erase 32K at one time
// execution time: 0,3s / 300ms
bool Flash_Erase32k(uint32_t address) {
if (address & (32*1024 - 1)) {
if ( g_dbglevel > 1 ) Dbprintf("Flash_Erase32k : Address is not align at 4096");
return false;
}
FlashSendByte(BLOCK32ERASE);
FlashSendByte((address >> 16) & 0xFF);
FlashSendByte((address >> 8) & 0xFF);
FlashSendLastByte((address >> 0) & 0xFF);
return true;
}
*/
// erase 64k at one time
// since a block is 64kb, and there is four blocks.
// we only need block number, as MSB
// execution time: 1s / 1000ms
// 0x00 00 00 -- 0x 00 FF FF == block 0
// 0x01 00 00 -- 0x 01 FF FF == block 1
// 0x02 00 00 -- 0x 02 FF FF == block 2
// 0x03 00 00 -- 0x 03 FF FF == block 3
bool Flash_Erase64k(uint8_t block) {
if (block > MAX_BLOCKS) return false;
FlashSendByte(BLOCK64ERASE);
FlashSendByte(block);
FlashSendByte(0x00);
FlashSendLastByte(0x00);
return true;
}
/*
// Erase chip
void Flash_EraseChip(void) {
FlashSendLastByte(CHIPERASE);
}
*/
void Flashmem_print_status(void) {
DbpString(_CYAN_("Flash memory"));
Dbprintf(" Baudrate................ " _GREEN_("%d MHz"), FLASHMEM_SPIBAUDRATE / 1000000);
if (!FlashInit()) {
DbpString(" Init.................... " _RED_("FAILED"));
return;
}
DbpString(" Init.................... " _GREEN_("OK"));
uint8_t dev_id = Flash_ReadID();
switch (dev_id) {
case 0x11 :
DbpString(" Memory size............. " _YELLOW_("2 mbits / 256 kb"));
break;
case 0x10 :
DbpString(" Memory size..... ....... " _YELLOW_("1 mbits / 128 kb"));
break;
case 0x05 :
DbpString(" Memory size............. " _YELLOW_("512 kbits / 64 kb"));
break;
default :
DbpString(" Device ID............... " _YELLOW_(" --> Unknown <--"));
break;
}
uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0};
Flash_UniqueID(uid);
Dbprintf( " Unique ID............... " _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();
}
void Flashmem_print_info(void) {
if (!FlashInit()) return;
DbpString(_CYAN_("Flash memory dictionary loaded"));
// load dictionary offsets.
uint8_t keysum[2];
uint16_t num;
Flash_CheckBusy(BUSY_TIMEOUT);
uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET, keysum, 2);
if (isok == 2) {
num = ((keysum[1] << 8) | keysum[0]);
if (num != 0xFFFF && num != 0x0)
Dbprintf(" Mifare.................. "_YELLOW_("%d")" / "_GREEN_("%d")" keys", num, DEFAULT_MF_KEYS_MAX);
}
Flash_CheckBusy(BUSY_TIMEOUT);
isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET, keysum, 2);
if (isok == 2) {
num = ((keysum[1] << 8) | keysum[0]);
if (num != 0xFFFF && num != 0x0)
Dbprintf(" T55x7................... "_YELLOW_("%d")" / "_GREEN_("%d")" keys", num, DEFAULT_T55XX_KEYS_MAX);
}
Flash_CheckBusy(BUSY_TIMEOUT);
isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET, keysum, 2);
if (isok == 2) {
num = ((keysum[1] << 8) | keysum[0]);
if (num != 0xFFFF && num != 0x0)
Dbprintf(" iClass.................. "_YELLOW_("%d")" / "_GREEN_("%d")" keys", num, DEFAULT_ICLASS_KEYS_MAX);
}
FlashStop();
}
#endif // #ifndef AS_BOOTROM
// initialize
bool FlashInit(void) {
FlashSetup(FLASHMEM_SPIBAUDRATE);
StartTicks();
if (Flash_CheckBusy(BUSY_TIMEOUT)) {
StopTicks();
return false;
}
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);
uid[6] = FlashSendByte(0xFF);
uid[5] = FlashSendByte(0xFF);
uid[4] = FlashSendByte(0xFF);
uid[3] = FlashSendByte(0xFF);
uid[2] = FlashSendByte(0xFF);
uid[1] = FlashSendByte(0xFF);
uid[0] = FlashSendLastByte(0xFF);
}
void FlashStop(void) {
//Bof
//* 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;
#ifndef AS_BOOTROM
if (g_dbglevel > 3) Dbprintf("FlashStop");
#endif // AS_BOOTROM
StopTicks();
}
void FlashSetup(uint32_t baudrate) {
//WDT_DISABLE
AT91C_BASE_WDTC->WDTC_WDMR = AT91C_WDTC_WDDIS;
// PA10 -> SPI_NCS2 chip select (FLASHMEM)
// 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 | GPIO_NCS2);
// Pull-up Enable
AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_NCS0 | GPIO_MISO | GPIO_MOSI | GPIO_SPCK | GPIO_NCS2);
// 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);
//reset spi needs double SWRST, see atmel's errata on this case
AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SWRST;
AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SWRST;
// Enable SPI
AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIEN;
// NPCS2 Mode 0
AT91C_BASE_SPI->SPI_MR =
(0 << 24) | // Delay between chip selects = DYLBCS/MCK BUT:
// If DLYBCS is less than or equal to six, six MCK periods
// will be inserted by default.
SPI_PCS(SPI_CSR_NUM) | // Peripheral Chip Select (selects SPI_NCS2 or PA10)
(0 << 7) | // Disable LLB (1=MOSI2MISO test mode)
(1 << 4) | // Disable ModeFault Protection
(0 << 3) | // makes spi operate at MCK (1 is MCK/2)
(0 << 2) | // Chip selects connected directly to peripheral
AT91C_SPI_PS_FIXED | // Fixed Peripheral Select
AT91C_SPI_MSTR; // Master Mode
uint8_t csaat = 1;
uint32_t dlybct = 0;
uint8_t ncpha = 1;
uint8_t cpol = 0;
if (baudrate > FLASH_MINFAST) {
baudrate = FLASH_FASTBAUD;
//csaat = 0;
dlybct = 1500;
ncpha = 0;
cpol = 0;
}
AT91C_BASE_SPI->SPI_CSR[2] =
SPI_DLYBCT(dlybct, MCK) | // Delay between Consecutive Transfers (32 MCK periods)
SPI_DLYBS(0, MCK) | // Delay Beforce SPCK CLock
SPI_SCBR(baudrate, MCK) | // SPI Baudrate Selection
AT91C_SPI_BITS_8 | // Bits per Transfer (8 bits)
//AT91C_SPI_CSAAT | // Chip Select inactive after transfer
// 40.4.6.2 SPI: Bad tx_ready Behavior when CSAAT = 1 and SCBR = 1
// If the SPI is programmed with CSAAT = 1, SCBR(baudrate) = 1 and two transfers are performed consecutively on
// the same slave with an IDLE state between them, the tx_ready signal does not rise after the second data has been
// transferred in the shifter. This can imply for example, that the second data is sent twice.
// COLIN :: For now we STILL use CSAAT=1 to avoid having to (de)assert NPCS manually via PIO lines and we deal with delay
(csaat << 3) |
/* Spi modes:
Mode CPOL CPHA NCPHA
0 0 0 1 clock normally low read on rising edge
1 0 1 0 clock normally low read on falling edge
2 1 0 1 clock normally high read on falling edge
3 1 1 0 clock normally high read on rising edge
However, page 512 of the AT91SAM7Sx datasheet say "Note that in SPI
master mode the ATSAM7S512/256/128/64/321/32 does not sample the data
(MISO) on the opposite edge where data clocks out (MOSI) but the same
edge is used as shown in Figure 36-3 and Figure 36-4." Figure 36-3
shows that CPOL=NCPHA=0 or CPOL=NCPHA=1 samples on the rising edge and
that the data changes sometime after the rising edge (about 2 ns). To
be consistent with normal SPI operation, it is probably safe to say
that the data changes on the falling edge and should be sampled on the
rising edge. Therefore, it appears that NCPHA should be treated the
same as CPHA. Thus:
Mode CPOL CPHA NCPHA
0 0 0 0 clock normally low read on rising edge
1 0 1 1 clock normally low read on falling edge
2 1 0 0 clock normally high read on falling edge
3 1 1 1 clock normally high read on rising edge
Update: for 24MHz, writing is more stable with ncpha=1, else bitflips occur.
*/
(ncpha << 1) | // Clock Phase data captured on leading edge, changes on following edge
(cpol << 0); // Clock Polarity inactive state is logic 0
// read first, empty buffer
if (AT91C_BASE_SPI->SPI_RDR == 0) {};
}
bool Flash_CheckBusy(uint32_t timeout) {
WaitUS(WINBOND_WRITE_DELAY);
StartCountUS();
uint32_t _time = GetCountUS();
#ifndef AS_BOOTROM
if (g_dbglevel > 3) Dbprintf("Checkbusy in...");
#endif // AS_BOOTROM
do {
if (!(Flash_ReadStat1() & BUSY)) {
return false;
}
} while ((GetCountUS() - _time) < timeout);
if (timeout <= (GetCountUS() - _time)) {
return true;
}
return false;
}
// read state register 1
uint8_t Flash_ReadStat1(void) {
FlashSendByte(READSTAT1);
return FlashSendLastByte(0xFF);
}
// 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);
}

184
common_arm/flashmem.h Normal file
View file

@ -0,0 +1,184 @@
//-----------------------------------------------------------------------------
// Borrowed initially from Arduino SPIFlash Library v.2.5.0
// Copyright (C) 2015 by Prajwal Bhattaram.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Common Instructions //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#ifndef __FLASHMEM_H
#define __FLASHMEM_H
#include "common.h"
// Used Command
#define ID 0x90
#define MANID 0x90
#define JEDECID 0x9F
#define READSTAT1 0x05
#define READSTAT2 0x35
#define WRITESTAT 0x01
#define WRITEDISABLE 0x04
#define WRITEENABLE 0x06
#define READDATA 0x03
#define FASTREAD 0x0B
#define PAGEPROG 0x02
#define SECTORERASE 0x20
#define BLOCK32ERASE 0x52
#define BLOCK64ERASE 0xD8
#define CHIPERASE 0xC7
#define UNIQUE_ID 0x4B
// Not used or not support command
#define RELEASE 0xAB
#define POWERDOWN 0xB9
#define SUSPEND 0x75
#define RESUME 0x7A
// Flash busy timeout: 20ms is the strict minimum when writing 256kb
#define BUSY_TIMEOUT 200000L
#define WINBOND_MANID 0xEF
#define WINBOND_DEVID 0x11
#define PAGESIZE 0x100
#define WINBOND_WRITE_DELAY 0x02
#define SPI_CLK 48000000
#define BUSY 0x01
#define WRTEN 0x02
#define SUS 0x40
#define DUMMYBYTE 0xEE
#define NULLBYTE 0x00
#define NULLINT 0x0000
#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
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
bool FlashInit(void);
void Flash_UniqueID(uint8_t *uid);
void FlashStop(void);
void FlashSetup(uint32_t baudrate);
bool Flash_CheckBusy(uint32_t timeout);
uint8_t Flash_ReadStat1(void);
uint16_t FlashSendByte(uint32_t data);
uint16_t FlashSendLastByte(uint32_t data);
#ifndef AS_BOOTROM
// Bootrom does not require these functions.
// Wrap in #ifndef to avoid accidental bloat of bootrom
// Bootrom needs only enough to get uniqueID from flash.
// It calls three functions. Full call trees listed:
//
// FlashInit()
// |
// \____ FlashSetup()
// | \____ leaf
// |
// \____ StartTicks()
// | \____ leaf
// |
// \____ Flash_CheckBusy() [*]
// | \____ WaitUS()
// | | \____ WaitTicks()
// | | \____ leaf
// | |
// | \____ StartCountUS()
// | | \____ leaf
// | |
// | \____ GetCountUS()
// | | \____ leaf
// | |
// | \____ Flash_ReadStat1()
// | \____ FlashSendByte()
// | | \____ leaf
// | |
// | \____ FlashSendLastByte()
// | \____ FlashSendByte()
// | \____ leaf
// |
// \____ StopTicks()
// \____ leaf
//
// Flash_UniqueID()
// \____ FlashCheckBusy() (see FlashInit)
// \____ FlashSendByteByte() (see FlashInit)
// \____ FlashSendByteLastByte() (see FlashInit)
//
//
// FlashStop() [*]
void FlashmemSetSpiBaudrate(uint32_t baudrate);
bool Flash_WaitIdle(void);
void Flash_TransferAdresse(uint32_t address);
void 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);
uint8_t Flash_ReadID(void);
uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len);
uint16_t Flash_ReadDataCont(uint32_t address, uint8_t *out, uint16_t len);
uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len);
uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len);
uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len);
void Flashmem_print_status(void);
void Flashmem_print_info(void);
#endif // #ifndef AS_BOOTROM
#endif

325
common_arm/ticks.c Normal file
View file

@ -0,0 +1,325 @@
//-----------------------------------------------------------------------------
// 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.h"
#include "proxmark3_arm.h"
#ifndef AS_BOOTROM
#include "dbprint.h"
#endif
#ifndef AS_BOOTROM
// timer counts in 666ns increments (32/48MHz), rounding applies
// WARNING: timer can't measure more than 43ms (666ns * 0xFFFF)
void SpinDelayUsPrecision(int us) {
int ticks = ((MCK / 1000000) * us + 16) >> 5;
// Borrow a PWM unit for my real-time clock
AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0);
// 48 MHz / 32 gives 1.5 Mhz
AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(5); // 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();
}
}
// 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:
//
// ti = GetTickCount();
// SpinDelay(1000);
// ti = GetTickCount() - ti;
// Dbprintf("timer(1s): %d t=%d", ti, GetTickCount());
void StartTickCount(void) {
// 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...
uint16_t mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF; // Get # main clocks within 16 slow clocks
// set RealTimeCounter divider to count at 1kHz, should be 32 if RC is exactly at 32kHz:
AT91C_BASE_RTTC->RTTC_RTMR = AT91C_RTTC_RTTRST | ((((MAINCK / 1000 * 16) + (mainf / 2)) / mainf) & AT91C_RTTC_RTPRES);
// note: worst case precision is approx 2.5%
}
/*
* 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;
}
// -------------------------------------------------------------------------
// Timer for iso14443 commands. Uses ssp_clk from FPGA
// -------------------------------------------------------------------------
void StartCountSspClk(void) {
AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2); // Enable Clock to all timers
AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_TIOA1 // XC0 Clock = TIOA1
| AT91C_TCB_TC1XC1S_NONE // XC1 Clock = none
| AT91C_TCB_TC2XC2S_TIOA0; // XC2 Clock = TIOA0
// configure TC1 to create a short pulse on TIOA1 when a rising edge on TIOB1 (= ssp_clk from FPGA) occurs:
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // disable TC1
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK // TC1 Clock = MCK(48MHz)/2 = 24MHz
| AT91C_TC_CPCSTOP // Stop clock on RC compare
| AT91C_TC_EEVTEDG_RISING // Trigger on rising edge of Event
| AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16)
| AT91C_TC_ENETRG // Enable external trigger event
| AT91C_TC_WAVESEL_UP // Upmode without automatic trigger on RC compare
| AT91C_TC_WAVE // Waveform Mode
| AT91C_TC_AEEVT_SET // Set TIOA1 on external event
| AT91C_TC_ACPC_CLEAR; // Clear TIOA1 on RC Compare
AT91C_BASE_TC1->TC_RC = 0x01; // RC Compare value = 0x01, pulse width to TC0
// use TC0 to count TIOA1 pulses
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // disable TC0
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_XC0 // TC0 clock = XC0 clock = TIOA1
| AT91C_TC_WAVE // Waveform Mode
| AT91C_TC_WAVESEL_UP // just count
| AT91C_TC_ACPA_CLEAR // Clear TIOA0 on RA Compare
| AT91C_TC_ACPC_SET; // Set TIOA0 on RC Compare
AT91C_BASE_TC0->TC_RA = 1; // RA Compare value = 1; pulse width to TC2
AT91C_BASE_TC0->TC_RC = 0; // RC Compare value = 0; increment TC2 on overflow
// use TC2 to count TIOA0 pulses (giving us a 32bit counter (TC0/TC2) clocked by ssp_clk)
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; // disable TC2
AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_XC2 // TC2 clock = XC2 clock = TIOA0
| AT91C_TC_WAVE // Waveform Mode
| AT91C_TC_WAVESEL_UP; // just count
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC0
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC1
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC2
//
// 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
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
}
// note: up to now two ssp_clk rising edges have passed since the rising edge of ssp_frame
// it is now safe to assert a sync signal. This sets all timers to 0 on next active clock edge
AT91C_BASE_TCB->TCB_BCR = 1; // assert Sync (set all timers to 0 on next active clock edge)
// at the next (3rd) ssp_clk rising edge, TC1 will be reset (and not generate a clock signal to TC0)
// at the next (4th) ssp_clk rising edge, TC0 (the low word of our counter) will be reset. From now on,
// whenever the last three bits of our counter go 0, we can be sure to be in the middle of a frame transfer.
// (just started with the transfer of the 4th Bit).
// The high word of the counter (TC2) will not reset until the low word (TC0) overflows.
// Therefore need to wait quite some time 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;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
while (AT91C_BASE_TC2->TC_CV > 0);
}
uint32_t RAMFUNC GetCountSspClk(void) {
uint32_t tmp_count = (AT91C_BASE_TC2->TC_CV << 16) | AT91C_BASE_TC0->TC_CV;
if ((tmp_count & 0x0000ffff) == 0) //small chance that we may have missed an increment in TC2
return (AT91C_BASE_TC2->TC_CV << 16);
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;
}
void WaitMS(uint32_t ms) {
WaitTicks((ms & 0x1FFFFF) * 1500);
}
#endif // #ifndef AS_BOOTROM
// -------------------------------------------------------------------------
// microseconds timer
// -------------------------------------------------------------------------
void StartCountUS(void) {
AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1);
AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE;
// fast clock
// tick=1.5mks
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32
AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR |
AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET;
AT91C_BASE_TC0->TC_RA = 1;
AT91C_BASE_TC0->TC_RC = 0xBFFF + 1; // 0xC000
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // timer disable
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // from timer 0
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Assert a sync signal. This sets all timers to 0 on next active clock edge
AT91C_BASE_TCB->TCB_BCR = 1;
while (AT91C_BASE_TC1->TC_CV > 0);
}
uint32_t RAMFUNC GetCountUS(void) {
//return (AT91C_BASE_TC1->TC_CV * 0x8000) + ((AT91C_BASE_TC0->TC_CV / 15) * 10);
// By suggestion from PwPiwi, http://www.proxmark.org/forum/viewtopic.php?pid=17548#p17548
return ((uint32_t)AT91C_BASE_TC1->TC_CV) * 0x8000 + (((uint32_t)AT91C_BASE_TC0->TC_CV) * 2) / 3;
}
// -------------------------------------------------------------------------
// Timer for bitbanging, or LF stuff when you need a very precis timer
// 1us = 1.5ticks
// -------------------------------------------------------------------------
void StartTicks(void) {
// initialization of the timer
AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1);
AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE;
// disable TC0 and TC1 for re-configuration
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
// first configure TC1 (higher, 0xFFFF0000) 16 bit counter
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // just connect to TIOA0 from TC0
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // re-enable timer and wait for TC0
// second configure TC0 (lower, 0x0000FFFF) 16 bit counter
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32
AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO |
AT91C_TC_ACPA_CLEAR | // RA comperator clears TIOA (carry bit)
AT91C_TC_ACPC_SET | // RC comperator sets TIOA (carry bit)
AT91C_TC_ASWTRG_SET; // SWTriger sets TIOA (carry bit)
AT91C_BASE_TC0->TC_RC = 0; // set TIOA (carry bit) on overflow, return to zero
AT91C_BASE_TC0->TC_RA = 1; // clear carry bit on next clock cycle
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // reset and re-enable timer
// 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);
}
uint32_t GetTicks(void) {
uint32_t hi, lo;
do {
hi = AT91C_BASE_TC1->TC_CV;
lo = AT91C_BASE_TC0->TC_CV;
} while (hi != AT91C_BASE_TC1->TC_CV);
return (hi << 16) | lo;
}
// 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;
}

64
common_arm/ticks.h Normal file
View file

@ -0,0 +1,64 @@
//-----------------------------------------------------------------------------
// 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);
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);
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

View file

@ -382,7 +382,6 @@ static const char StrSerialNumber[] = {
// 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
@ -399,6 +398,7 @@ void usb_update_serial(uint64_t newSerialNumber) {
}
// 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;
}
@ -407,10 +407,14 @@ void usb_update_serial(uint64_t newSerialNumber) {
// 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 < 16; i++) {
uint8_t nibble = (uint8_t)((newSerialNumber >> (60 - (4*i))) & 0xFu);
char c = nibble < 10 ? '0' + nibble : 'A' + (nibble-10);
StrSerialNumber[18+(2*i)] = c; // [ 18, 20, 22, .., 46, 48 ]
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[46-(4*i)] = c1; // [ 46, 42, .., 22, 18 ]
StrSerialNumber[48-(4*i)] = c2; // [ 48, 44, .., 24, 20 ]
}
StrSerialNumber[0] = USB_STRING_DESCRIPTOR_SERIAL_NUMBER_LENGTH;
}