Merge pull request #78 from grigorig/stcgal-patched
Integration of work done on stcgal-patched
This commit is contained in:
commit
f41ae5679f
2
.github/workflows/python.yml
vendored
2
.github/workflows/python.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.5, 3.7, 3.8]
|
python-version: [3.7, 3.8]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
453
doc/reverse-engineering/dump-mcu.c
Normal file
453
doc/reverse-engineering/dump-mcu.c
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Vincent DEFERT. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* This program automates the procedure explained below and generates
|
||||||
|
* on the standard output the content of the 'models' list of models.py.
|
||||||
|
*
|
||||||
|
* It takes the name of the stc-isp executable as argument.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Manual procedure to read MCU definitions from a new STC-ISP executable
|
||||||
|
* ========================================================================
|
||||||
|
*
|
||||||
|
* We want to extract 2 tables from the executable, one with MCU names and
|
||||||
|
* the other with their characteristics, let's call them "Name Table" and
|
||||||
|
* "Info Table" respectively.
|
||||||
|
*
|
||||||
|
* The Info Table appears first in the executable and contains references
|
||||||
|
* to the MCU name in the Name Table. Each entry in the Name Table is 16
|
||||||
|
* bytes long, 32 for the Info Table. New entries are prepended to the
|
||||||
|
* Info Table, and appended to the Name Table. Of course, both have the
|
||||||
|
* same number of entries.
|
||||||
|
*
|
||||||
|
* This means that the Name Table is very easy to locate, as well as the
|
||||||
|
* end of the Info Table, but not its beginning, which must be calculated.
|
||||||
|
*
|
||||||
|
* Finally, the field of an Info Table entry that references the MCU name
|
||||||
|
* is expressed as a memory address, not a file position, so we'll need to
|
||||||
|
* determine the base memory address of the name table.
|
||||||
|
*
|
||||||
|
* 1. Dump the content of the executable in a text file.
|
||||||
|
*
|
||||||
|
* hexdump -C stc-isp-v6.89G.exe > stc-isp-v6.89G.txt
|
||||||
|
*
|
||||||
|
* 2. Locate the first entry of the Name Table.
|
||||||
|
*
|
||||||
|
* Search for the following byte sequence:
|
||||||
|
* 53 54 43 39 30 4c 45 35 31 36 41 44 00 00 00 00
|
||||||
|
* (i.e. nul-terminated "STC90LE516AD" string).
|
||||||
|
*
|
||||||
|
* Let's call this file position NTS (Name Table Start).
|
||||||
|
*
|
||||||
|
* 3. Locate the end of the Name Table.
|
||||||
|
*
|
||||||
|
* Search for the following byte sequence:
|
||||||
|
* 55 4e 4b 4e 4f 57 4e 00 25 30 36 58 00 00 00 00
|
||||||
|
* (i.e. nul-terminated "UNKNOWN" and "%06X" strings).
|
||||||
|
*
|
||||||
|
* Let's call this file position NTE (Name Table End).
|
||||||
|
*
|
||||||
|
* 4. Find the end of the Info Table.
|
||||||
|
*
|
||||||
|
* Search for the following byte sequence (fixed last entry):
|
||||||
|
* 05 46 01 00 xx xx xx xx 90 f1 00 00 00 f8 00 00
|
||||||
|
* 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00
|
||||||
|
*
|
||||||
|
* Bytes marked as 'xx' must be ignored while searching
|
||||||
|
*
|
||||||
|
* [Note: searching for '90 f1 00 00 00 f8 00 00' is sufficient.]
|
||||||
|
*
|
||||||
|
* It should be followed by 32 zeroed bytes. Let's call the file position
|
||||||
|
* of the first zeroed byte ITE (Info Table End).
|
||||||
|
*
|
||||||
|
* 5. Find the beginning of the Info Table.
|
||||||
|
*
|
||||||
|
* The Info Table start with a block of 32 zeroed bytes except bytes
|
||||||
|
* 4-7 which point at NTE, i.e. an info block pointing at the 'UNKNOWN'
|
||||||
|
* MCU name. It's the only reliable way to determine the location of
|
||||||
|
* the Info Table.
|
||||||
|
*
|
||||||
|
* Our first valid info block will thus be the offset of the Unknown
|
||||||
|
* block + 32. Let's call this file position ITS (Info Table Start).
|
||||||
|
*
|
||||||
|
* 6. Calculate the number of MCU definitions (i.e. Info Table entries).
|
||||||
|
*
|
||||||
|
* NB_MCU = (ITE - ITS) / 32
|
||||||
|
*
|
||||||
|
* 7. Determine the base memory address of the name table.
|
||||||
|
*
|
||||||
|
* Let's suppose 'xx xx xx xx' is '9c f7 4a 00'. As it belongs to the Info
|
||||||
|
* Table entry describing the first item of the Name Table, we directly
|
||||||
|
* have what we're looking for, i.e. 0x004af79c.
|
||||||
|
*
|
||||||
|
* NTBA = littleEndianOf32bitUnsignedInt('xx xx xx xx')
|
||||||
|
*
|
||||||
|
* The index in the Name Table corresponding to a given Info Table item
|
||||||
|
* is thus:
|
||||||
|
*
|
||||||
|
* NAME_IDX = (nameAddressFieldOfInfoTableItem - NTBA) / 0x10
|
||||||
|
*
|
||||||
|
* NOTE: for some reason, the Info Table entries of the STC08XE-3V and
|
||||||
|
* STC08XE-5V each have 2 distinct mcuId, which gives 1115 Info Table
|
||||||
|
* entries for 1113 strings in the Name Table.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Must be updated with the "UNKNOWN" name offset before use.
|
||||||
|
static uint8_t infoTableStartSignature[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 0x90, 0xf1 is the magic number of the STC90LE516AD
|
||||||
|
// We test only the last 24 byte of its 32-byte entry, as they are
|
||||||
|
// sufficiently discriminating and do not depend on a particular
|
||||||
|
// executable release.
|
||||||
|
static const uint8_t infoTableEndSignature[] = {
|
||||||
|
0x90, 0xf1, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
// NUL-terminated "STC90LE516AD" followed by 3 NUL bytes
|
||||||
|
static const uint8_t nameTableStartSignature[] = {
|
||||||
|
0x53, 0x54, 0x43, 0x39, 0x30, 0x4c, 0x45, 0x35,
|
||||||
|
0x31, 0x36, 0x41, 0x44, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
// NUL-terminated "UNKNOWN" and "%06X" followed by 3 NUL bytes
|
||||||
|
static const uint8_t nameTableEndSignature[] = {
|
||||||
|
0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x00,
|
||||||
|
0x25, 0x30, 0x36, 0x58, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t nameAddr;
|
||||||
|
uint32_t mcuId;
|
||||||
|
uint32_t flashSize;
|
||||||
|
uint32_t eepromSize;
|
||||||
|
uint32_t eepromStartAddr; // STC89 & STC90 only. 0 means IAP.
|
||||||
|
uint32_t totalSize;
|
||||||
|
uint32_t unknown2;
|
||||||
|
} MCUInfo;
|
||||||
|
|
||||||
|
// Bit 1 is 1 for MCU which can accept 5V power supply voltage, be it
|
||||||
|
// exclusively or not, and 0 for low-voltage only MCU (around 3.3V).
|
||||||
|
#define FLAG_ACCEPT_5V_SUPPLY_VOLTAGE 0x00000002
|
||||||
|
|
||||||
|
// Bit 3 is 1 for so-called "IAP" MCU, meaning the start address of the
|
||||||
|
// flash portion used for EEPROM emulation can be configured.
|
||||||
|
#define FLAG_CONFIGURABLE_EEPROM_SIZE 0x00000008
|
||||||
|
|
||||||
|
// Bit 7 is 1 for MCU with an adjustable internal RC oscillator, i.e.
|
||||||
|
// that supports calibration. When bits 7 and 8 are both 0, the MCU has
|
||||||
|
// no IRCO at all (external crystal only).
|
||||||
|
#define FLAG_CONFIGURABLE_IRCO_FREQ 0x00000080
|
||||||
|
|
||||||
|
// Bit 8 is 1 for MCU with a fixed-frequency internal RC oscillator
|
||||||
|
// (the old IRC* models).
|
||||||
|
#define FLAG_FIXED_FREQUENCY_IRCO 0x00000100
|
||||||
|
|
||||||
|
// Bit 12 is 1 for MCS-251 MCU, i.e. with a flash size that can be
|
||||||
|
// larger than 64KB.
|
||||||
|
#define FLAG_IS_MCS251_MCU 0x00001000
|
||||||
|
|
||||||
|
#define SEARCH_BUFFER_LEN 8192
|
||||||
|
#define MCU_NAME_LEN 16
|
||||||
|
|
||||||
|
#define NO_MATCH -1
|
||||||
|
#define FOUND -2
|
||||||
|
|
||||||
|
/*
|
||||||
|
// May help to guess the meaning of new flags as they are added.
|
||||||
|
|
||||||
|
void toBits(uint32_t n, char *result) {
|
||||||
|
*result = '\0';
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
for (uint32_t mask = 0x80000000; mask; mask >>= 1, pos++) {
|
||||||
|
if (pos) {
|
||||||
|
strcat(result, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n & mask) {
|
||||||
|
strcat(result, "1");
|
||||||
|
} else {
|
||||||
|
strcat(result, "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printCSVHeader() {
|
||||||
|
printf("name,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,flags (hex),mcuId,flashSize,eepromSize,eepromStartAddr,totalSize,unknown2\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printCSVRow(const MCUInfo *info, const char *name) {
|
||||||
|
char flags[64];
|
||||||
|
|
||||||
|
toBits(info->flags, flags);
|
||||||
|
|
||||||
|
printf(
|
||||||
|
"%s,%s,0x%08x,0x%04x,%u,%u,0x%08x,%u,0x%08x\n",
|
||||||
|
name,
|
||||||
|
flags,
|
||||||
|
info->flags,
|
||||||
|
(uint16_t) info->mcuId,
|
||||||
|
info->flashSize,
|
||||||
|
info->eepromSize,
|
||||||
|
info->eepromStartAddr,
|
||||||
|
info->totalSize,
|
||||||
|
info->unknown2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char *toBool(uint32_t flags, uint32_t mask) {
|
||||||
|
return (flags & mask) ? "True" : "False";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printMCU(const MCUInfo *info, const char *name) {
|
||||||
|
printf(
|
||||||
|
" MCUModel(name='%s', magic=0x%04x, total=%u, code=%u, eeprom=%u, iap=%s, calibrate=%s, mcs251=%s),\n",
|
||||||
|
name,
|
||||||
|
(uint16_t) info->mcuId,
|
||||||
|
info->totalSize,
|
||||||
|
info->flashSize,
|
||||||
|
info->eepromSize,
|
||||||
|
toBool(info->flags, FLAG_CONFIGURABLE_EEPROM_SIZE),
|
||||||
|
toBool(info->flags, FLAG_CONFIGURABLE_IRCO_FREQ),
|
||||||
|
toBool(info->flags, FLAG_IS_MCS251_MCU)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char **argv) {
|
||||||
|
int rc = 1;
|
||||||
|
MCUInfo *infoTable = NULL;
|
||||||
|
char *nameTable = NULL;
|
||||||
|
int mcuCount = 0;
|
||||||
|
uint32_t infoTableStartOffset = 0;
|
||||||
|
uint32_t infoTableEndOffset = 0;
|
||||||
|
uint32_t nameTableStartOffset = 0;
|
||||||
|
uint32_t nameTableEndOffset = 0;
|
||||||
|
uint32_t baseAddr = 0;
|
||||||
|
int nameTableSize = 0;
|
||||||
|
FILE *exeFile = fopen(argv[1], "rb");
|
||||||
|
|
||||||
|
if (exeFile != NULL) {
|
||||||
|
rc = 2;
|
||||||
|
uint8_t *buffer = (uint8_t *) malloc(SEARCH_BUFFER_LEN);
|
||||||
|
|
||||||
|
if (buffer != NULL) {
|
||||||
|
rc = 3;
|
||||||
|
int infoTableEndMatch = NO_MATCH;
|
||||||
|
int nameTableStartMatch = NO_MATCH;
|
||||||
|
int nameTableEndMatch = NO_MATCH;
|
||||||
|
uint32_t fileOffset = 0;
|
||||||
|
int bytesRead = 0;
|
||||||
|
|
||||||
|
while ((bytesRead = fread(buffer, 1, SEARCH_BUFFER_LEN, exeFile)) != 0) {
|
||||||
|
for (int curByte = 0; curByte < SEARCH_BUFFER_LEN; curByte++) {
|
||||||
|
int noMatch = 1;
|
||||||
|
|
||||||
|
if (infoTableEndMatch > NO_MATCH) {
|
||||||
|
if (infoTableEndSignature[infoTableEndMatch + 1] == buffer[curByte]) {
|
||||||
|
infoTableEndMatch++;
|
||||||
|
noMatch = 0;
|
||||||
|
|
||||||
|
if (infoTableEndMatch == (sizeof(infoTableEndSignature) -1)) {
|
||||||
|
infoTableEndMatch = FOUND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
infoTableEndMatch = NO_MATCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nameTableStartMatch > NO_MATCH) {
|
||||||
|
if (nameTableStartSignature[nameTableStartMatch + 1] == buffer[curByte]) {
|
||||||
|
nameTableStartMatch++;
|
||||||
|
noMatch = 0;
|
||||||
|
|
||||||
|
if (nameTableStartMatch == (sizeof(nameTableStartSignature) -1)) {
|
||||||
|
nameTableStartMatch = FOUND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nameTableStartMatch = NO_MATCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nameTableEndMatch > NO_MATCH) {
|
||||||
|
if (nameTableEndSignature[nameTableEndMatch + 1] == buffer[curByte]) {
|
||||||
|
nameTableEndMatch++;
|
||||||
|
noMatch = 0;
|
||||||
|
|
||||||
|
if (nameTableEndMatch == (sizeof(nameTableEndSignature) - 1)) {
|
||||||
|
nameTableEndMatch = FOUND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nameTableEndMatch = NO_MATCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noMatch) {
|
||||||
|
if (infoTableEndMatch == NO_MATCH && infoTableEndSignature[0] == buffer[curByte]) {
|
||||||
|
infoTableEndMatch = 0;
|
||||||
|
infoTableEndOffset = fileOffset + curByte;
|
||||||
|
} else if (nameTableStartMatch == NO_MATCH && nameTableStartSignature[0] == buffer[curByte]) {
|
||||||
|
nameTableStartMatch = 0;
|
||||||
|
nameTableStartOffset = fileOffset + curByte;
|
||||||
|
} else if (nameTableEndMatch == NO_MATCH && nameTableEndSignature[0] == buffer[curByte]) {
|
||||||
|
nameTableEndMatch = 0;
|
||||||
|
nameTableEndOffset = fileOffset + curByte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (infoTableEndMatch == FOUND && nameTableStartMatch == FOUND && nameTableEndMatch == FOUND) {
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileOffset += SEARCH_BUFFER_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
// Point to the byte immediately following the table's last entry.
|
||||||
|
infoTableEndOffset += sizeof(infoTableEndSignature);
|
||||||
|
// Read last item of Info Table
|
||||||
|
fseek(exeFile, infoTableEndOffset - sizeof(MCUInfo), SEEK_SET);
|
||||||
|
MCUInfo lastItem;
|
||||||
|
fread(&lastItem, sizeof(MCUInfo), 1, exeFile);
|
||||||
|
// We need it now in order to calculate the memory address
|
||||||
|
// corresponding to the UNKNOWN name.
|
||||||
|
// We'll also need baseAddr later, anyway.
|
||||||
|
baseAddr = lastItem.nameAddr;
|
||||||
|
|
||||||
|
rc = 4;
|
||||||
|
int infoTableStartMatch = NO_MATCH;
|
||||||
|
uint32_t fileOffset = 0;
|
||||||
|
int bytesRead = 0;
|
||||||
|
*((uint32_t *)(infoTableStartSignature)) = (baseAddr - nameTableStartOffset) + nameTableEndOffset;
|
||||||
|
fseek(exeFile, 0, SEEK_SET);
|
||||||
|
|
||||||
|
while ((bytesRead = fread(buffer, 1, SEARCH_BUFFER_LEN, exeFile)) != 0) {
|
||||||
|
for (int curByte = 0; curByte < SEARCH_BUFFER_LEN; curByte++) {
|
||||||
|
if (infoTableStartMatch > NO_MATCH) {
|
||||||
|
if (infoTableStartSignature[infoTableStartMatch + 1] == buffer[curByte]) {
|
||||||
|
infoTableStartMatch++;
|
||||||
|
|
||||||
|
if (infoTableStartMatch == (sizeof(infoTableStartSignature) - 1)) {
|
||||||
|
infoTableStartMatch = FOUND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
infoTableStartMatch = NO_MATCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (infoTableStartMatch == NO_MATCH && infoTableStartSignature[0] == buffer[curByte]) {
|
||||||
|
infoTableStartMatch = 0;
|
||||||
|
infoTableStartOffset = fileOffset + curByte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (infoTableStartMatch == FOUND) {
|
||||||
|
// Point to the first entry following the Unknown one.
|
||||||
|
infoTableStartOffset += sizeof(MCUInfo) - 4;
|
||||||
|
// Calculate number of entries while we're at it
|
||||||
|
mcuCount = (infoTableEndOffset - infoTableStartOffset) / sizeof(MCUInfo);
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileOffset += SEARCH_BUFFER_LEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
nameTableSize = nameTableEndOffset - nameTableStartOffset;
|
||||||
|
|
||||||
|
nameTable = (char *) malloc(nameTableSize);
|
||||||
|
|
||||||
|
if (nameTable == NULL) {
|
||||||
|
rc = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
fseek(exeFile, nameTableStartOffset, SEEK_SET);
|
||||||
|
fread(nameTable, nameTableSize, 1, exeFile);
|
||||||
|
|
||||||
|
infoTable = (MCUInfo *) malloc(infoTableEndOffset - infoTableStartOffset);
|
||||||
|
|
||||||
|
if (infoTable != NULL) {
|
||||||
|
fseek(exeFile, infoTableStartOffset, SEEK_SET);
|
||||||
|
fread(infoTable, infoTableEndOffset - infoTableStartOffset, 1, exeFile);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
rc = 6;
|
||||||
|
free(nameTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(exeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
//printCSVHeader();
|
||||||
|
|
||||||
|
for (int mcu = 0; mcu < mcuCount; mcu++) {
|
||||||
|
const char *mcuName = &nameTable[infoTable[mcu].nameAddr - baseAddr];
|
||||||
|
|
||||||
|
if (strncmp(mcuName, "STC12C54", 8) == 0 || strncmp(mcuName, "STC12LE54", 9) == 0) {
|
||||||
|
// STC12x54xx always have 12KB EEPROM
|
||||||
|
infoTable[mcu].eepromSize = 12 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printCSVRow(&infoTable[mcu], mcuName);
|
||||||
|
printMCU(&infoTable[mcu], mcuName);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(infoTable);
|
||||||
|
free(nameTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
@ -1,40 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# This curious script dumps all model info from STC-ISP.
|
|
||||||
# Data is directly read from the binary.
|
|
||||||
# Offsets are for stc-isp-15xx-v6.87P.exe, sha256sum d5413728d87cf5d7a6e036348ade5b38cce13113ae3bb090cfac7a232ba82a53
|
|
||||||
|
|
||||||
MCU_TABLE_OFFSET = 0x00071c30
|
|
||||||
MCU_TABLE_SIZE = 1068
|
|
||||||
MCU_RECORD_SIZE = 32
|
|
||||||
MCU_NAMES_OFFSET = 0x000924E8
|
|
||||||
MCU_NAMES_PTR_OFFSET = 0x004924e8
|
|
||||||
|
|
||||||
import struct
|
|
||||||
import sys
|
|
||||||
|
|
||||||
inp = open(sys.argv[1], "rb")
|
|
||||||
|
|
||||||
for i in range(MCU_TABLE_SIZE):
|
|
||||||
mcu_record_offset = MCU_TABLE_OFFSET + MCU_RECORD_SIZE * i
|
|
||||||
inp.seek(mcu_record_offset)
|
|
||||||
mcu_record = inp.read(MCU_RECORD_SIZE)
|
|
||||||
flags, name_ptr, mcu_id, code_size, ee_size, _, total_size, _ = struct.unpack("<8I", mcu_record)
|
|
||||||
mcu_id &= 0xffff
|
|
||||||
|
|
||||||
mcu_name_offset = MCU_NAMES_OFFSET + (name_ptr - MCU_NAMES_PTR_OFFSET)
|
|
||||||
inp.seek(mcu_name_offset)
|
|
||||||
name_str = inp.read(16).split(b'\00')[0].decode("ascii")
|
|
||||||
|
|
||||||
# TODO: With some MCUs, the amount of available EEPROM depends on the BSL version.
|
|
||||||
# Generally, newer BSLs free up a KB of additional EEPROM. Currently, always the
|
|
||||||
# maximum amount (with newer BSL) is reported.
|
|
||||||
|
|
||||||
# STC12x54xx always have 12 KB eeprom
|
|
||||||
if name_str.startswith("STC12C54") or name_str.startswith("STC12LE54"):
|
|
||||||
ee_size = 12 * 1024
|
|
||||||
|
|
||||||
print("MCUModel(name='%s', magic=0x%02x%02x, total=%d, code=%d, eeprom=%d)," %
|
|
||||||
(name_str, mcu_id >> 8, mcu_id & 0xff, total_size, code_size, ee_size))
|
|
||||||
|
|
||||||
inp.close()
|
|
||||||
|
|
BIN
doc/reverse-engineering/models.ods
Normal file
BIN
doc/reverse-engineering/models.ods
Normal file
Binary file not shown.
@ -33,6 +33,8 @@ from stcgal.protocols import Stc15Protocol
|
|||||||
from stcgal.protocols import Stc15AProtocol
|
from stcgal.protocols import Stc15AProtocol
|
||||||
from stcgal.protocols import StcUsb15Protocol
|
from stcgal.protocols import StcUsb15Protocol
|
||||||
from stcgal.protocols import Stc8Protocol
|
from stcgal.protocols import Stc8Protocol
|
||||||
|
from stcgal.protocols import Stc8dProtocol
|
||||||
|
from stcgal.protocols import Stc8gProtocol
|
||||||
from stcgal.protocols import StcAutoProtocol
|
from stcgal.protocols import StcAutoProtocol
|
||||||
from stcgal.protocols import StcProtocolException
|
from stcgal.protocols import StcProtocolException
|
||||||
from stcgal.protocols import StcFramingException
|
from stcgal.protocols import StcFramingException
|
||||||
@ -43,6 +45,7 @@ class StcGal:
|
|||||||
|
|
||||||
def __init__(self, opts):
|
def __init__(self, opts):
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
|
self.hexFileType = 8
|
||||||
self.initialize_protocol(opts)
|
self.initialize_protocol(opts)
|
||||||
|
|
||||||
def initialize_protocol(self, opts):
|
def initialize_protocol(self, opts):
|
||||||
@ -56,14 +59,19 @@ class StcGal:
|
|||||||
elif opts.protocol == "stc12":
|
elif opts.protocol == "stc12":
|
||||||
self.protocol = Stc12Protocol(opts.port, opts.handshake, opts.baud)
|
self.protocol = Stc12Protocol(opts.port, opts.handshake, opts.baud)
|
||||||
elif opts.protocol == "stc15a":
|
elif opts.protocol == "stc15a":
|
||||||
self.protocol = Stc15AProtocol(opts.port, opts.handshake, opts.baud,
|
self.protocol = Stc15AProtocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
|
||||||
round(opts.trim * 1000))
|
|
||||||
elif opts.protocol == "stc15":
|
elif opts.protocol == "stc15":
|
||||||
self.protocol = Stc15Protocol(opts.port, opts.handshake, opts.baud,
|
self.protocol = Stc15Protocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
|
||||||
round(opts.trim * 1000))
|
|
||||||
elif opts.protocol == "stc8":
|
elif opts.protocol == "stc8":
|
||||||
self.protocol = Stc8Protocol(opts.port, opts.handshake, opts.baud,
|
self.protocol = Stc8Protocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
|
||||||
round(opts.trim * 1000))
|
elif opts.protocol == "stc8d":
|
||||||
|
self.protocol = Stc8dProtocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
|
||||||
|
elif opts.protocol == "stc8g":
|
||||||
|
"""FIXME Ugly hack, but works until I fully implement the STC8G protocol"""
|
||||||
|
if opts.trim < 27360:
|
||||||
|
self.protocol = Stc8dProtocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
|
||||||
|
else:
|
||||||
|
self.protocol = Stc8gProtocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
|
||||||
elif opts.protocol == "usb15":
|
elif opts.protocol == "usb15":
|
||||||
self.protocol = StcUsb15Protocol()
|
self.protocol = StcUsb15Protocol()
|
||||||
else:
|
else:
|
||||||
@ -90,6 +98,7 @@ class StcGal:
|
|||||||
fname.endswith(".ihex")):
|
fname.endswith(".ihex")):
|
||||||
try:
|
try:
|
||||||
hexfile = IHex.read(fileobj)
|
hexfile = IHex.read(fileobj)
|
||||||
|
self.hexFileType = hexfile.get_mode()
|
||||||
binary = hexfile.extract_data()
|
binary = hexfile.extract_data()
|
||||||
print("%d bytes (Intel HEX)" %len(binary))
|
print("%d bytes (Intel HEX)" %len(binary))
|
||||||
return binary
|
return binary
|
||||||
@ -103,45 +112,55 @@ class StcGal:
|
|||||||
def program_mcu(self):
|
def program_mcu(self):
|
||||||
"""Execute the standard programming flow."""
|
"""Execute the standard programming flow."""
|
||||||
|
|
||||||
code_size = self.protocol.model.code
|
if self.opts.option: self.emit_options(self.opts.option)
|
||||||
ee_size = self.protocol.model.eeprom
|
|
||||||
|
if self.protocol.split_code and self.protocol.model.iap:
|
||||||
|
code_size = self.protocol.split_code
|
||||||
|
ee_size = self.protocol.split_eeprom
|
||||||
|
else:
|
||||||
|
code_size = self.protocol.model.code
|
||||||
|
ee_size = self.protocol.model.eeprom
|
||||||
|
|
||||||
print("Loading flash: ", end="")
|
print("Loading flash: ", end="")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
bindata = self.load_file_auto(self.opts.code_image)
|
bindata = self.load_file_auto(self.opts.code_image)
|
||||||
|
|
||||||
# warn if it overflows
|
if self.protocol.model.mcs251 and self.hexFileType != 32:
|
||||||
if len(bindata) > code_size:
|
print("Invalid input file. MCU is an MCS-251, input file MUST specify a linear", file=sys.stderr)
|
||||||
print("WARNING: code_image overflows into eeprom segment!", file=sys.stderr)
|
print("base address, i.e. contain a type 04 record. More information at:", file=sys.stderr)
|
||||||
if len(bindata) > (code_size + ee_size):
|
print("https://en.wikipedia.org/wiki/Intel_HEX", file=sys.stderr)
|
||||||
print("WARNING: code_image truncated!", file=sys.stderr)
|
else:
|
||||||
bindata = bindata[0:code_size + ee_size]
|
# warn if it overflows
|
||||||
|
if len(bindata) > code_size:
|
||||||
|
print("WARNING: code_image overflows into eeprom segment!", file=sys.stderr)
|
||||||
|
if len(bindata) > (code_size + ee_size):
|
||||||
|
print("WARNING: code_image truncated!", file=sys.stderr)
|
||||||
|
bindata = bindata[0:code_size + ee_size]
|
||||||
|
|
||||||
# add eeprom data if supplied
|
# add eeprom data if supplied
|
||||||
if self.opts.eeprom_image:
|
if self.opts.eeprom_image:
|
||||||
print("Loading EEPROM: ", end="")
|
print("Loading EEPROM: ", end="")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
eedata = self.load_file_auto(self.opts.eeprom_image)
|
eedata = self.load_file_auto(self.opts.eeprom_image)
|
||||||
if len(eedata) > ee_size:
|
if len(eedata) > ee_size:
|
||||||
print("WARNING: eeprom_image truncated!", file=sys.stderr)
|
print("WARNING: eeprom_image truncated!", file=sys.stderr)
|
||||||
eedata = eedata[0:ee_size]
|
eedata = eedata[0:ee_size]
|
||||||
if len(bindata) < code_size:
|
if len(bindata) < code_size:
|
||||||
bindata += bytes([0xff] * (code_size - len(bindata)))
|
bindata += bytes([0xff] * (code_size - len(bindata)))
|
||||||
elif len(bindata) > code_size:
|
elif len(bindata) > code_size:
|
||||||
print("WARNING: eeprom_image overlaps code_image!", file=sys.stderr)
|
print("WARNING: eeprom_image overlaps code_image!", file=sys.stderr)
|
||||||
bindata = bindata[0:code_size]
|
bindata = bindata[0:code_size]
|
||||||
bindata += eedata
|
bindata += eedata
|
||||||
|
|
||||||
# pad to 512 byte boundary
|
# pad to 512 byte boundary
|
||||||
if len(bindata) % 512:
|
if len(bindata) % 512:
|
||||||
bindata += b'\xff' * (512 - len(bindata) % 512)
|
bindata += b'\xff' * (512 - len(bindata) % 512)
|
||||||
|
|
||||||
if self.opts.option: self.emit_options(self.opts.option)
|
self.protocol.handshake()
|
||||||
|
self.protocol.erase_flash(len(bindata), code_size)
|
||||||
|
self.protocol.program_flash(bindata)
|
||||||
|
self.protocol.program_options()
|
||||||
|
|
||||||
self.protocol.handshake()
|
|
||||||
self.protocol.erase_flash(len(bindata), code_size)
|
|
||||||
self.protocol.program_flash(bindata)
|
|
||||||
self.protocol.program_options()
|
|
||||||
self.protocol.disconnect()
|
self.protocol.disconnect()
|
||||||
|
|
||||||
def erase_mcu(self):
|
def erase_mcu(self):
|
||||||
@ -161,7 +180,7 @@ class StcGal:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.protocol.connect(autoreset=self.opts.autoreset, resetcmd=self.opts.resetcmd)
|
self.protocol.connect(autoreset=self.opts.autoreset, resetcmd=self.opts.resetcmd, resetpin=self.opts.resetpin)
|
||||||
if isinstance(self.protocol, StcAutoProtocol):
|
if isinstance(self.protocol, StcAutoProtocol):
|
||||||
if not self.protocol.protocol_name:
|
if not self.protocol.protocol_name:
|
||||||
raise StcProtocolException("cannot detect protocol")
|
raise StcProtocolException("cannot detect protocol")
|
||||||
@ -240,11 +259,13 @@ def cli():
|
|||||||
parser.add_argument("eeprom_image", help="eeprom segment file to flash (BIN/HEX)", type=argparse.FileType("rb"), nargs='?')
|
parser.add_argument("eeprom_image", help="eeprom segment file to flash (BIN/HEX)", type=argparse.FileType("rb"), nargs='?')
|
||||||
exclusives.add_argument("-e", "--erase", help="only erase flash memory", action="store_true")
|
exclusives.add_argument("-e", "--erase", help="only erase flash memory", action="store_true")
|
||||||
parser.add_argument("-a", "--autoreset", help="cycle power automatically by asserting DTR", action="store_true")
|
parser.add_argument("-a", "--autoreset", help="cycle power automatically by asserting DTR", action="store_true")
|
||||||
|
parser.add_argument("-A", "--resetpin", help="pin to hold down when using --autoreset (default: DTR)",
|
||||||
|
choices=["dtr", "rts"], default="dtr")
|
||||||
parser.add_argument("-r", "--resetcmd", help="shell command for board power-cycling (instead of DTR assertion)", action="store")
|
parser.add_argument("-r", "--resetcmd", help="shell command for board power-cycling (instead of DTR assertion)", action="store")
|
||||||
parser.add_argument("-P", "--protocol", help="protocol version (default: auto)",
|
parser.add_argument("-P", "--protocol", help="protocol version (default: auto)",
|
||||||
choices=["stc89", "stc12a", "stc12b", "stc12", "stc15a", "stc15", "stc8", "usb15", "auto"], default="auto")
|
choices=["stc89", "stc12a", "stc12b", "stc12", "stc15a", "stc15", "stc8", "stc8d", "stc8g", "usb15", "auto"], default="auto")
|
||||||
parser.add_argument("-p", "--port", help="serial port device", default="/dev/ttyUSB0")
|
parser.add_argument("-p", "--port", help="serial port device", default="/dev/ttyUSB0")
|
||||||
parser.add_argument("-b", "--baud", help="transfer baud rate (default: 19200)", type=BaudType(), default=19200)
|
parser.add_argument("-b", "--baud", help="transfer baud rate (default: 19200)", type=BaudType(), default=115200)
|
||||||
parser.add_argument("-l", "--handshake", help="handshake baud rate (default: 2400)", type=BaudType(), default=2400)
|
parser.add_argument("-l", "--handshake", help="handshake baud rate (default: 2400)", type=BaudType(), default=2400)
|
||||||
parser.add_argument("-o", "--option", help="set option (can be used multiple times, see documentation)", action="append")
|
parser.add_argument("-o", "--option", help="set option (can be used multiple times, see documentation)", action="append")
|
||||||
parser.add_argument("-t", "--trim", help="RC oscillator frequency in kHz (STC15+ series only)", type=float, default=0.0)
|
parser.add_argument("-t", "--trim", help="RC oscillator frequency in kHz (STC15+ series only)", type=float, default=0.0)
|
||||||
|
@ -14,7 +14,6 @@ class IHex:
|
|||||||
"""Read Intel HEX data from string or lines"""
|
"""Read Intel HEX data from string or lines"""
|
||||||
ihex = cls()
|
ihex = cls()
|
||||||
|
|
||||||
segbase = 0
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line:
|
if not line:
|
||||||
@ -22,14 +21,14 @@ class IHex:
|
|||||||
|
|
||||||
t, a, d = ihex.parse_line(line)
|
t, a, d = ihex.parse_line(line)
|
||||||
if t == 0x00:
|
if t == 0x00:
|
||||||
ihex.insert_data(segbase + a, d)
|
ihex.insert_data(a, d)
|
||||||
|
|
||||||
elif t == 0x01:
|
elif t == 0x01:
|
||||||
break # Should we check for garbage after this?
|
break # Should we check for garbage after this?
|
||||||
|
|
||||||
elif t == 0x02:
|
elif t == 0x02:
|
||||||
ihex.set_mode(16)
|
ihex.set_mode(16)
|
||||||
segbase = struct.unpack(">H", d[0:2])[0] << 4
|
ihex.linearBaseAddress = struct.unpack(">H", d[0:2])[0] << 4
|
||||||
|
|
||||||
elif t == 0x03:
|
elif t == 0x03:
|
||||||
ihex.set_mode(16)
|
ihex.set_mode(16)
|
||||||
@ -39,7 +38,7 @@ class IHex:
|
|||||||
|
|
||||||
elif t == 0x04:
|
elif t == 0x04:
|
||||||
ihex.set_mode(32)
|
ihex.set_mode(32)
|
||||||
segbase = struct.unpack(">H", d[0:2])[0] << 16
|
ihex.linearBaseAddress = struct.unpack(">H", d[0:2])[0] << 16
|
||||||
|
|
||||||
elif t == 0x05:
|
elif t == 0x05:
|
||||||
ihex.set_mode(32)
|
ihex.set_mode(32)
|
||||||
@ -63,6 +62,7 @@ class IHex:
|
|||||||
self.start = None
|
self.start = None
|
||||||
self.mode = 8
|
self.mode = 8
|
||||||
self.row_bytes = 16
|
self.row_bytes = 16
|
||||||
|
self.linearBaseAddress = 0
|
||||||
|
|
||||||
def set_row_bytes(self, row_bytes):
|
def set_row_bytes(self, row_bytes):
|
||||||
"""Set output hex file row width (bytes represented per row)."""
|
"""Set output hex file row width (bytes represented per row)."""
|
||||||
@ -105,6 +105,12 @@ class IHex:
|
|||||||
def set_mode(self, mode):
|
def set_mode(self, mode):
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
|
||||||
|
def get_mode(self):
|
||||||
|
return self.mode
|
||||||
|
|
||||||
|
def get_linearBaseAddress(self):
|
||||||
|
return self.linearBaseAddress
|
||||||
|
|
||||||
def get_area(self, addr):
|
def get_area(self, addr):
|
||||||
for start, data in self.areas.items():
|
for start, data in self.areas.items():
|
||||||
end = start + len(data)
|
end = start + len(data)
|
||||||
@ -193,6 +199,7 @@ class IHex:
|
|||||||
output += self.make_line(
|
output += self.make_line(
|
||||||
0x04, 0, struct.pack(">H", newsegbase))
|
0x04, 0, struct.pack(">H", newsegbase))
|
||||||
segbase = newsegbase
|
segbase = newsegbase
|
||||||
|
segbase = newsegbase
|
||||||
|
|
||||||
output += self.make_line(0x00, addr, chunk)
|
output += self.make_line(0x00, addr, chunk)
|
||||||
|
|
||||||
|
2260
stcgal/models.py
2260
stcgal/models.py
File diff suppressed because it is too large
Load Diff
@ -86,12 +86,15 @@ class StcBaseProtocol(ABC):
|
|||||||
self.mcu_bsl_version = ""
|
self.mcu_bsl_version = ""
|
||||||
self.options = None
|
self.options = None
|
||||||
self.model = None
|
self.model = None
|
||||||
|
self.split_eeprom = None
|
||||||
|
self.split_code = None
|
||||||
self.uid = None
|
self.uid = None
|
||||||
self.debug = False
|
self.debug = False
|
||||||
self.status_packet = None
|
self.status_packet = None
|
||||||
self.protocol_name = None
|
self.protocol_name = None
|
||||||
self.progress = None
|
self.progress = None
|
||||||
self.progress_cb = self.progress_bar_cb
|
self.progress_cb = self.progress_bar_cb
|
||||||
|
self.linearBaseAddress = 0
|
||||||
|
|
||||||
def progress_text_cb(self, current, written, maximum):
|
def progress_text_cb(self, current, written, maximum):
|
||||||
print(current, written, maximum)
|
print(current, written, maximum)
|
||||||
@ -133,7 +136,7 @@ class StcBaseProtocol(ABC):
|
|||||||
return packet[5:-1]
|
return packet[5:-1]
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def write_packet(self, packet_data):
|
def write_packet(self, packet_data, epilogue_len = 0):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def read_packet(self):
|
def read_packet(self):
|
||||||
@ -262,13 +265,23 @@ class StcBaseProtocol(ABC):
|
|||||||
def set_option(self, name, value):
|
def set_option(self, name, value):
|
||||||
self.options.set_option(name, value)
|
self.options.set_option(name, value)
|
||||||
|
|
||||||
def reset_device(self, resetcmd=False):
|
def reset_device(self, resetcmd=False, resetpin=False):
|
||||||
if not resetcmd:
|
if not resetcmd:
|
||||||
print("Cycling power: ", end="")
|
print("Cycling power: ", end="")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
self.ser.setDTR(True)
|
|
||||||
time.sleep(0.5)
|
if resetpin == "rts":
|
||||||
self.ser.setDTR(False)
|
self.ser.setRTS(True)
|
||||||
|
else:
|
||||||
|
self.ser.setDTR(True)
|
||||||
|
|
||||||
|
time.sleep(0.25)
|
||||||
|
|
||||||
|
if resetpin == "rts":
|
||||||
|
self.ser.setRTS(False)
|
||||||
|
else:
|
||||||
|
self.ser.setDTR(False)
|
||||||
|
|
||||||
time.sleep(0.030)
|
time.sleep(0.030)
|
||||||
print("done")
|
print("done")
|
||||||
else:
|
else:
|
||||||
@ -278,7 +291,7 @@ class StcBaseProtocol(ABC):
|
|||||||
print("Waiting for MCU: ", end="")
|
print("Waiting for MCU: ", end="")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
def connect(self, autoreset=False, resetcmd=False):
|
def connect(self, autoreset=False, resetcmd=False, resetpin=False):
|
||||||
"""Connect to MCU and initialize communication.
|
"""Connect to MCU and initialize communication.
|
||||||
|
|
||||||
Set up serial port, send sync sequence and get part info.
|
Set up serial port, send sync sequence and get part info.
|
||||||
@ -297,7 +310,7 @@ class StcBaseProtocol(ABC):
|
|||||||
self.ser.flushInput()
|
self.ser.flushInput()
|
||||||
|
|
||||||
if autoreset:
|
if autoreset:
|
||||||
self.reset_device(resetcmd)
|
self.reset_device(resetcmd, resetpin)
|
||||||
else:
|
else:
|
||||||
print("Waiting for MCU, please cycle power: ", end="")
|
print("Waiting for MCU, please cycle power: ", end="")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
@ -377,7 +390,12 @@ class StcAutoProtocol(StcBaseProtocol):
|
|||||||
("stc12", r"(STC|IAP)(10|11|12)\D"),
|
("stc12", r"(STC|IAP)(10|11|12)\D"),
|
||||||
("stc15a", r"(STC|IAP)15[FL][012]0\d(E|EA|)$"),
|
("stc15a", r"(STC|IAP)15[FL][012]0\d(E|EA|)$"),
|
||||||
("stc15", r"(STC|IAP|IRC)15\D"),
|
("stc15", r"(STC|IAP|IRC)15\D"),
|
||||||
("stc8", r"(STC|IAP|IRC)8")]
|
("stc8d", r"STC8H(3|4|8)K"),
|
||||||
|
("stc8d", r"STC32G"),
|
||||||
|
("stc8d", r"STC8A8K\d\dD4"),
|
||||||
|
("stc8g", r"STC8H"),
|
||||||
|
("stc8g", r"STC8G"),
|
||||||
|
("stc8", r"STC8\D")]
|
||||||
|
|
||||||
for protocol_name, pattern in protocol_database:
|
for protocol_name, pattern in protocol_database:
|
||||||
if re.match(pattern, self.model.name):
|
if re.match(pattern, self.model.name):
|
||||||
@ -392,7 +410,7 @@ class StcAutoProtocol(StcBaseProtocol):
|
|||||||
def initialize_status(self, status_packet):
|
def initialize_status(self, status_packet):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def write_packet(self, packet_data):
|
def write_packet(self, packet_data, epilogue_len = 0):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
@ -422,7 +440,7 @@ class Stc89Protocol(StcBaseProtocol):
|
|||||||
payload = StcBaseProtocol.extract_payload(self, packet)
|
payload = StcBaseProtocol.extract_payload(self, packet)
|
||||||
return payload[:-1]
|
return payload[:-1]
|
||||||
|
|
||||||
def write_packet(self, packet_data):
|
def write_packet(self, packet_data, epilogue_len = 0):
|
||||||
"""Send packet to MCU.
|
"""Send packet to MCU.
|
||||||
|
|
||||||
Constructs a packet with supplied payload and sends it to the MCU.
|
Constructs a packet with supplied payload and sends it to the MCU.
|
||||||
@ -817,7 +835,7 @@ class Stc12BaseProtocol(StcBaseProtocol):
|
|||||||
payload = StcBaseProtocol.extract_payload(self, packet)
|
payload = StcBaseProtocol.extract_payload(self, packet)
|
||||||
return payload[:-2]
|
return payload[:-2]
|
||||||
|
|
||||||
def write_packet(self, packet_data):
|
def write_packet(self, packet_data, epilogue_len = 0):
|
||||||
"""Send packet to MCU.
|
"""Send packet to MCU.
|
||||||
|
|
||||||
Constructs a packet with supplied payload and sends it to the MCU.
|
Constructs a packet with supplied payload and sends it to the MCU.
|
||||||
@ -836,6 +854,11 @@ class Stc12BaseProtocol(StcBaseProtocol):
|
|||||||
packet += struct.pack(">H", sum(packet[2:]) & 0xffff)
|
packet += struct.pack(">H", sum(packet[2:]) & 0xffff)
|
||||||
packet += self.PACKET_END
|
packet += self.PACKET_END
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < epilogue_len:
|
||||||
|
packet += bytes([0x66])
|
||||||
|
i += 1
|
||||||
|
|
||||||
self.dump_packet(packet, receive=False)
|
self.dump_packet(packet, receive=False)
|
||||||
self.ser.write(packet)
|
self.ser.write(packet)
|
||||||
self.ser.flush()
|
self.ser.flush()
|
||||||
@ -1561,156 +1584,6 @@ class Stc15Protocol(Stc15AProtocol):
|
|||||||
print("Target UID: %s" % Utils.hexstr(self.uid))
|
print("Target UID: %s" % Utils.hexstr(self.uid))
|
||||||
|
|
||||||
|
|
||||||
class Stc8Protocol(Stc15Protocol):
|
|
||||||
"""Protocol handler for STC8 series"""
|
|
||||||
|
|
||||||
def __init__(self, port, handshake, baud, trim):
|
|
||||||
Stc15Protocol.__init__(self, port, handshake, baud, trim)
|
|
||||||
self.trim_divider = None
|
|
||||||
self.reference_voltage = None
|
|
||||||
self.mfg_date = ()
|
|
||||||
|
|
||||||
def initialize_options(self, status_packet):
|
|
||||||
"""Initialize options"""
|
|
||||||
if len(status_packet) < 17:
|
|
||||||
raise StcProtocolException("invalid options in status packet")
|
|
||||||
|
|
||||||
# create option state
|
|
||||||
self.options = Stc8Option(status_packet[9:12] + status_packet[15:17])
|
|
||||||
self.options.print()
|
|
||||||
|
|
||||||
def initialize_status(self, packet):
|
|
||||||
"""Decode status packet and store basic MCU info"""
|
|
||||||
|
|
||||||
if len(packet) < 39:
|
|
||||||
raise StcProtocolException("invalid status packet")
|
|
||||||
|
|
||||||
self.mcu_clock_hz, = struct.unpack(">I", packet[1:5])
|
|
||||||
self.external_clock = False
|
|
||||||
# all ones means no calibration
|
|
||||||
# new chips are shipped without any calibration
|
|
||||||
# XXX: somehow check if that still holds
|
|
||||||
if self.mcu_clock_hz == 0xffffffff: self.mcu_clock_hz = 0
|
|
||||||
|
|
||||||
# wakeup timer factory value
|
|
||||||
self.wakeup_freq, = struct.unpack(">H", packet[23:25])
|
|
||||||
self.reference_voltage, = struct.unpack(">H", packet[35:37])
|
|
||||||
self.mfg_date = (
|
|
||||||
2000 + Utils.decode_packed_bcd(packet[37]),
|
|
||||||
Utils.decode_packed_bcd(packet[38]),
|
|
||||||
Utils.decode_packed_bcd(packet[39])
|
|
||||||
)
|
|
||||||
|
|
||||||
bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
|
|
||||||
bl_minor = packet[22] & 0x0f
|
|
||||||
self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
|
|
||||||
bl_minor, chr(bl_stepping))
|
|
||||||
self.bsl_version = bl_version
|
|
||||||
|
|
||||||
def print_mcu_info(self):
|
|
||||||
"""Print additional STC8 info"""
|
|
||||||
super().print_mcu_info()
|
|
||||||
print("Target ref. voltage: %d mV" % self.reference_voltage)
|
|
||||||
print("Target mfg. date: %04d-%02d-%02d" % self.mfg_date)
|
|
||||||
|
|
||||||
def calibrate(self):
|
|
||||||
"""Calibrate selected user frequency frequency and switch to selected baudrate."""
|
|
||||||
|
|
||||||
# handle uncalibrated chips
|
|
||||||
if self.mcu_clock_hz == 0 and self.trim_frequency <= 0:
|
|
||||||
raise StcProtocolException("uncalibrated, please provide a trim value")
|
|
||||||
|
|
||||||
# determine target counter
|
|
||||||
user_speed = self.trim_frequency
|
|
||||||
if user_speed <= 0: user_speed = self.mcu_clock_hz
|
|
||||||
target_user_count = round(user_speed / (self.baud_handshake/2))
|
|
||||||
|
|
||||||
# calibration, round 1
|
|
||||||
print("Trimming frequency: ", end="")
|
|
||||||
sys.stdout.flush()
|
|
||||||
packet = bytes([0x00])
|
|
||||||
packet += struct.pack(">B", 12)
|
|
||||||
packet += bytes([0x00, 0x00, 23*1, 0x00, 23*2, 0x00])
|
|
||||||
packet += bytes([23*3, 0x00, 23*4, 0x00, 23*5, 0x00])
|
|
||||||
packet += bytes([23*6, 0x00, 23*7, 0x00, 23*8, 0x00])
|
|
||||||
packet += bytes([23*9, 0x00, 23*10, 0x00, 255, 0x00])
|
|
||||||
self.write_packet(packet)
|
|
||||||
self.pulse(b"\xfe", timeout=1.0)
|
|
||||||
response = self.read_packet()
|
|
||||||
if len(response) < 2 or response[0] != 0x00:
|
|
||||||
raise StcProtocolException("incorrect magic in handshake packet")
|
|
||||||
|
|
||||||
# select ranges and trim values
|
|
||||||
for divider in (1, 2, 3, 4, 5):
|
|
||||||
user_trim = self.choose_range(packet, response, target_user_count * divider)
|
|
||||||
if user_trim is not None:
|
|
||||||
self.trim_divider = divider
|
|
||||||
break
|
|
||||||
if user_trim is None:
|
|
||||||
raise StcProtocolException("frequency trimming unsuccessful")
|
|
||||||
|
|
||||||
# calibration, round 2
|
|
||||||
packet = bytes([0x00])
|
|
||||||
packet += struct.pack(">B", 12)
|
|
||||||
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
|
||||||
packet += bytes([i & 0xff, 0x00])
|
|
||||||
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
|
||||||
packet += bytes([i & 0xff, 0x01])
|
|
||||||
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
|
||||||
packet += bytes([i & 0xff, 0x02])
|
|
||||||
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
|
||||||
packet += bytes([i & 0xff, 0x03])
|
|
||||||
self.write_packet(packet)
|
|
||||||
self.pulse(b"\xfe", timeout=1.0)
|
|
||||||
response = self.read_packet()
|
|
||||||
if len(response) < 2 or response[0] != 0x00:
|
|
||||||
raise StcProtocolException("incorrect magic in handshake packet")
|
|
||||||
|
|
||||||
# select final values
|
|
||||||
user_trim, user_count = self.choose_trim(packet, response, target_user_count)
|
|
||||||
self.trim_value = user_trim
|
|
||||||
self.trim_frequency = round(user_count * (self.baud_handshake / 2) / self.trim_divider)
|
|
||||||
print("%.03f MHz" % (self.trim_frequency / 1E6))
|
|
||||||
|
|
||||||
# switch to programming frequency
|
|
||||||
print("Switching to %d baud: " % self.baud_transfer, end="")
|
|
||||||
sys.stdout.flush()
|
|
||||||
packet = bytes([0x01, 0x00, 0x00])
|
|
||||||
bauds = self.baud_transfer * 4
|
|
||||||
packet += struct.pack(">H", round(65536 - 24E6 / bauds))
|
|
||||||
packet += bytes([user_trim[1], user_trim[0]])
|
|
||||||
iap_wait = self.get_iap_delay(24E6)
|
|
||||||
packet += bytes([iap_wait])
|
|
||||||
self.write_packet(packet)
|
|
||||||
response = self.read_packet()
|
|
||||||
if len(response) < 1 or response[0] != 0x01:
|
|
||||||
raise StcProtocolException("incorrect magic in handshake packet")
|
|
||||||
self.ser.baudrate = self.baud_transfer
|
|
||||||
|
|
||||||
def build_options(self):
|
|
||||||
"""Build a packet of option data from the current configuration."""
|
|
||||||
|
|
||||||
msr = self.options.get_msr()
|
|
||||||
packet = 40 * bytearray([0xff])
|
|
||||||
packet[3] = 0
|
|
||||||
packet[6] = 0
|
|
||||||
packet[22] = 0
|
|
||||||
packet[24:28] = struct.pack(">I", self.trim_frequency)
|
|
||||||
packet[28:30] = self.trim_value
|
|
||||||
packet[30] = self.trim_divider
|
|
||||||
packet[32] = msr[0]
|
|
||||||
packet[36:40] = msr[1:5]
|
|
||||||
return bytes(packet)
|
|
||||||
|
|
||||||
def disconnect(self):
|
|
||||||
"""Disconnect from MCU"""
|
|
||||||
|
|
||||||
# reset mcu
|
|
||||||
packet = bytes([0xff])
|
|
||||||
self.write_packet(packet)
|
|
||||||
self.ser.close()
|
|
||||||
print("Disconnected!")
|
|
||||||
|
|
||||||
class StcUsb15Protocol(Stc15Protocol):
|
class StcUsb15Protocol(Stc15Protocol):
|
||||||
"""USB should use large blocks"""
|
"""USB should use large blocks"""
|
||||||
PROGRAM_BLOCKSIZE = 128
|
PROGRAM_BLOCKSIZE = 128
|
||||||
@ -1772,7 +1645,7 @@ class StcUsb15Protocol(Stc15Protocol):
|
|||||||
host2dev = usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.CTRL_OUT
|
host2dev = usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.CTRL_OUT
|
||||||
self.dev.ctrl_transfer(host2dev, request, value, index, chunks)
|
self.dev.ctrl_transfer(host2dev, request, value, index, chunks)
|
||||||
|
|
||||||
def connect(self, autoreset=False, resetcmd=False):
|
def connect(self, autoreset=False, resetcmd=False, resetpin=False):
|
||||||
"""Connect to USB device and read info packet"""
|
"""Connect to USB device and read info packet"""
|
||||||
|
|
||||||
# USB support is optional. Provide an error if pyusb is not available.
|
# USB support is optional. Provide an error if pyusb is not available.
|
||||||
@ -1874,3 +1747,399 @@ class StcUsb15Protocol(Stc15Protocol):
|
|||||||
if self.dev:
|
if self.dev:
|
||||||
self.write_packet(0xff)
|
self.write_packet(0xff)
|
||||||
print("Disconnected!")
|
print("Disconnected!")
|
||||||
|
|
||||||
|
|
||||||
|
class Stc8Protocol(Stc15Protocol):
|
||||||
|
"""Protocol handler for STC8 series"""
|
||||||
|
|
||||||
|
def __init__(self, port, handshake, baud, trim):
|
||||||
|
Stc15Protocol.__init__(self, port, handshake, baud, trim)
|
||||||
|
self.trim_divider = None
|
||||||
|
self.reference_voltage = None
|
||||||
|
self.mfg_date = ()
|
||||||
|
|
||||||
|
def initialize_options(self, status_packet):
|
||||||
|
"""Initialize options"""
|
||||||
|
if len(status_packet) < 17:
|
||||||
|
raise StcProtocolException("invalid options in status packet")
|
||||||
|
|
||||||
|
# create option state
|
||||||
|
self.options = Stc8Option(status_packet[9:12] + status_packet[15:17])
|
||||||
|
self.options.print()
|
||||||
|
|
||||||
|
def initialize_status(self, packet):
|
||||||
|
"""Decode status packet and store basic MCU info"""
|
||||||
|
|
||||||
|
if len(packet) < 39:
|
||||||
|
raise StcProtocolException("invalid status packet")
|
||||||
|
|
||||||
|
self.mcu_clock_hz, = struct.unpack(">I", packet[1:5])
|
||||||
|
self.external_clock = False
|
||||||
|
# all ones means no calibration
|
||||||
|
# new chips are shipped without any calibration
|
||||||
|
# XXX: somehow check if that still holds
|
||||||
|
if self.mcu_clock_hz == 0xffffffff: self.mcu_clock_hz = 0
|
||||||
|
|
||||||
|
# wakeup timer factory value
|
||||||
|
self.wakeup_freq, = struct.unpack(">H", packet[23:25])
|
||||||
|
self.reference_voltage, = struct.unpack(">H", packet[35:37])
|
||||||
|
self.mfg_date = (
|
||||||
|
2000 + Utils.decode_packed_bcd(packet[37]),
|
||||||
|
Utils.decode_packed_bcd(packet[38]),
|
||||||
|
Utils.decode_packed_bcd(packet[39])
|
||||||
|
)
|
||||||
|
|
||||||
|
bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
|
||||||
|
bl_minor = packet[22] & 0x0f
|
||||||
|
self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
|
||||||
|
bl_minor, chr(bl_stepping))
|
||||||
|
self.bsl_version = bl_version
|
||||||
|
|
||||||
|
def print_mcu_info(self):
|
||||||
|
"""Print additional STC8 info"""
|
||||||
|
super().print_mcu_info()
|
||||||
|
print("Target ref. voltage: %d mV" % self.reference_voltage)
|
||||||
|
print("Target mfg. date: %04d-%02d-%02d" % self.mfg_date)
|
||||||
|
|
||||||
|
def set_option(self, name, value):
|
||||||
|
super().set_option(name, value)
|
||||||
|
|
||||||
|
def calibrate(self):
|
||||||
|
"""Calibrate selected user frequency frequency and switch to selected baudrate."""
|
||||||
|
|
||||||
|
# handle uncalibrated chips
|
||||||
|
if self.mcu_clock_hz == 0 and self.trim_frequency <= 0:
|
||||||
|
raise StcProtocolException("uncalibrated, please provide a trim value")
|
||||||
|
|
||||||
|
# determine target counter
|
||||||
|
user_speed = self.trim_frequency
|
||||||
|
if user_speed <= 0: user_speed = self.mcu_clock_hz
|
||||||
|
target_user_count = round(user_speed / (self.baud_handshake/2))
|
||||||
|
|
||||||
|
# calibration, round 1
|
||||||
|
print("Trimming frequency: ", end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
packet = bytes([0x00])
|
||||||
|
packet += struct.pack(">B", 12)
|
||||||
|
packet += bytes([0x00, 0x00, 23*1, 0x00, 23*2, 0x00])
|
||||||
|
packet += bytes([23*3, 0x00, 23*4, 0x00, 23*5, 0x00])
|
||||||
|
packet += bytes([23*6, 0x00, 23*7, 0x00, 23*8, 0x00])
|
||||||
|
packet += bytes([23*9, 0x00, 23*10, 0x00, 255, 0x00])
|
||||||
|
self.write_packet(packet)
|
||||||
|
self.pulse(b"\xfe", timeout=1.0)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 2 or response[0] != 0x00:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
|
||||||
|
# select ranges and trim values
|
||||||
|
for divider in (1, 2, 3, 4, 5):
|
||||||
|
user_trim = self.choose_range(packet, response, target_user_count * divider)
|
||||||
|
if user_trim is not None:
|
||||||
|
self.trim_divider = divider
|
||||||
|
break
|
||||||
|
if user_trim is None:
|
||||||
|
raise StcProtocolException("frequency trimming unsuccessful")
|
||||||
|
|
||||||
|
# calibration, round 2
|
||||||
|
packet = bytes([0x00])
|
||||||
|
packet += struct.pack(">B", 12)
|
||||||
|
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
||||||
|
packet += bytes([i & 0xff, 0x00])
|
||||||
|
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
||||||
|
packet += bytes([i & 0xff, 0x01])
|
||||||
|
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
||||||
|
packet += bytes([i & 0xff, 0x02])
|
||||||
|
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
||||||
|
packet += bytes([i & 0xff, 0x03])
|
||||||
|
self.write_packet(packet)
|
||||||
|
self.pulse(b"\xfe", timeout=1.0)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 2 or response[0] != 0x00:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
|
||||||
|
# select final values
|
||||||
|
user_trim, user_count = self.choose_trim(packet, response, target_user_count)
|
||||||
|
self.trim_value = user_trim
|
||||||
|
self.trim_frequency = round(user_count * (self.baud_handshake / 2) / self.trim_divider)
|
||||||
|
print("%.03f MHz" % (self.trim_frequency / 1E6))
|
||||||
|
|
||||||
|
# switch to programming frequency
|
||||||
|
print("Switching to %d baud: " % self.baud_transfer, end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
packet = bytes([0x01, 0x00, 0x00])
|
||||||
|
bauds = self.baud_transfer * 4
|
||||||
|
packet += struct.pack(">H", round(65536 - 24E6 / bauds))
|
||||||
|
packet += bytes([user_trim[1], user_trim[0]])
|
||||||
|
iap_wait = self.get_iap_delay(24E6)
|
||||||
|
packet += bytes([iap_wait])
|
||||||
|
self.write_packet(packet)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 1 or response[0] != 0x01:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
self.ser.baudrate = self.baud_transfer
|
||||||
|
|
||||||
|
def build_options(self):
|
||||||
|
"""Build a packet of option data from the current configuration."""
|
||||||
|
|
||||||
|
msr = self.options.get_msr()
|
||||||
|
packet = 40 * bytearray([0xff])
|
||||||
|
packet[3] = 0
|
||||||
|
packet[6] = 0
|
||||||
|
packet[22] = 0
|
||||||
|
packet[24:28] = struct.pack(">I", self.trim_frequency)
|
||||||
|
packet[28:30] = self.trim_value
|
||||||
|
packet[30] = self.trim_divider
|
||||||
|
packet[32] = msr[0]
|
||||||
|
packet[36:40] = msr[1:5]
|
||||||
|
return bytes(packet)
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
"""Disconnect from MCU"""
|
||||||
|
|
||||||
|
# reset mcu
|
||||||
|
packet = bytes([0xff])
|
||||||
|
self.write_packet(packet)
|
||||||
|
self.ser.close()
|
||||||
|
print("Disconnected!")
|
||||||
|
|
||||||
|
|
||||||
|
class Stc8dProtocol(Stc8Protocol):
|
||||||
|
"""Protocol handler for STC8A8K64D4 series"""
|
||||||
|
|
||||||
|
def __init__(self, port, handshake, baud, trim):
|
||||||
|
Stc8Protocol.__init__(self, port, handshake, baud, trim)
|
||||||
|
|
||||||
|
def set_option(self, name, value):
|
||||||
|
super().set_option(name, value)
|
||||||
|
if name == 'program_eeprom_split':
|
||||||
|
split_point = Utils.to_int(value);
|
||||||
|
|
||||||
|
if self.model.mcs251:
|
||||||
|
"""Minimum size is 1K in STC-ISP"""
|
||||||
|
if split_point == 0 and self.model.iap:
|
||||||
|
split_point = 0x400;
|
||||||
|
|
||||||
|
# CODE starts at 0xFF0000
|
||||||
|
self.split_code = 0x10000;
|
||||||
|
# EEPROM starts at 0xFE0000
|
||||||
|
self.split_eeprom = split_point;
|
||||||
|
else:
|
||||||
|
if split_point == 0 and self.model.iap:
|
||||||
|
split_point = self.model.code;
|
||||||
|
|
||||||
|
self.split_code = split_point;
|
||||||
|
self.split_eeprom = self.model.total - self.split_code;
|
||||||
|
|
||||||
|
def choose_range(self, packet, response, target_count):
|
||||||
|
"""Choose appropriate trim value mean for next round from challenge
|
||||||
|
responses."""
|
||||||
|
|
||||||
|
|
||||||
|
challenge_data = packet[2:]
|
||||||
|
calib_data = response[2:]
|
||||||
|
calib_len = response[1]
|
||||||
|
if len(calib_data) < 2 * calib_len:
|
||||||
|
raise StcProtocolException("range calibration data missing")
|
||||||
|
for i in range(calib_len >> 1):
|
||||||
|
count_a, count_b = struct.unpack(
|
||||||
|
">HH", calib_data[4 * i: 4 * i + 4])
|
||||||
|
trim_a, trim_b, trim_range = struct.unpack(
|
||||||
|
">BxBB", challenge_data[4 * i:4 * i + 4])
|
||||||
|
if ((count_a <= target_count and count_b >= target_count)):
|
||||||
|
target_trim = round(
|
||||||
|
(target_count - count_a) * (trim_b - trim_a) / (count_b - count_a) + trim_a)
|
||||||
|
# target_trim will be set at the center of packet in the 2nd calibration
|
||||||
|
if target_trim < 6 or target_trim > 255 - 5:
|
||||||
|
raise StcProtocolException("frequency trimming failed")
|
||||||
|
return (target_trim, trim_range)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def choose_trim(self, packet, response, target_count):
|
||||||
|
"""Choose best trim for given target count from challenge
|
||||||
|
responses."""
|
||||||
|
calib_data = response[2:]
|
||||||
|
challenge_data = packet[2:]
|
||||||
|
calib_len = response[1]
|
||||||
|
if len(calib_data) < 2 * calib_len:
|
||||||
|
raise StcProtocolException("trim calibration data missing")
|
||||||
|
best = None
|
||||||
|
best_count = sys.maxsize
|
||||||
|
for i in range(calib_len):
|
||||||
|
count, = struct.unpack(">H", calib_data[2 * i: 2 * i + 2])
|
||||||
|
trim_adj, trim_range = struct.unpack(
|
||||||
|
">BB", challenge_data[2 * i: 2 * i + 2])
|
||||||
|
if abs(count - target_count) < best_count:
|
||||||
|
best_count = abs(count - target_count)
|
||||||
|
best = (trim_adj, trim_range), count
|
||||||
|
if not best:
|
||||||
|
raise StcProtocolException("frequency trimming failed")
|
||||||
|
return best
|
||||||
|
|
||||||
|
|
||||||
|
def calibrate(self):
|
||||||
|
"""Calibrate selected user frequency frequency and switch to selected baudrate."""
|
||||||
|
|
||||||
|
# handle uncalibrated chips
|
||||||
|
if self.mcu_clock_hz == 0 and self.trim_frequency <= 0:
|
||||||
|
raise StcProtocolException(
|
||||||
|
"uncalibrated, please provide a trim value")
|
||||||
|
|
||||||
|
# determine target counter
|
||||||
|
user_speed = self.trim_frequency
|
||||||
|
if user_speed <= 0:
|
||||||
|
user_speed = self.mcu_clock_hz
|
||||||
|
target_user_count = round(user_speed / self.baud_handshake)
|
||||||
|
|
||||||
|
# calibration, round 1
|
||||||
|
print("Target frequency: ", end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
packet = bytes([0x00, 0x08])
|
||||||
|
packet += bytes([0x00, 0x00, 0xFF, 0x00])
|
||||||
|
packet += bytes([0x00, 0x10, 0xFF, 0x10])
|
||||||
|
packet += bytes([0x00, 0x20, 0xFF, 0x20])
|
||||||
|
packet += bytes([0x00, 0x30, 0xFF, 0x30])
|
||||||
|
self.write_packet(packet)
|
||||||
|
self.pulse(b"\xfe", timeout=1.0)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 2 or response[0] != 0x00:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
|
||||||
|
# select ranges and trim values
|
||||||
|
for divider in range(1, 6):
|
||||||
|
user_trim = self.choose_range(
|
||||||
|
packet, response, target_user_count * divider)
|
||||||
|
if user_trim is not None:
|
||||||
|
self.trim_divider = divider
|
||||||
|
break
|
||||||
|
if user_trim is None:
|
||||||
|
raise StcProtocolException("frequency trimming unsuccessful")
|
||||||
|
|
||||||
|
# calibration, round 2
|
||||||
|
packet = bytes([0x00, 0x0C])
|
||||||
|
for i in range(-6, 6):
|
||||||
|
packet += bytes([user_trim[0] + i, user_trim[1]])
|
||||||
|
self.write_packet(packet)
|
||||||
|
self.pulse(b"\xfe", timeout=1.0)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 2 or response[0] != 0x00:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
|
||||||
|
# select final values
|
||||||
|
user_trim, user_count = self.choose_trim(
|
||||||
|
packet, response, target_user_count * self.trim_divider)
|
||||||
|
self.trim_value = user_trim
|
||||||
|
self.trim_frequency = round(
|
||||||
|
user_count * self.baud_handshake/self.trim_divider)
|
||||||
|
print("Target %.03f MHz" % (user_speed / 1E6))
|
||||||
|
print("Adjusted frequency: %.03f MHz(%.03f%%)" % (
|
||||||
|
(self.trim_frequency / 1E6), (self.trim_frequency*100/user_speed-100)))
|
||||||
|
|
||||||
|
# switch to programming frequency
|
||||||
|
print("Switching to %d baud: " % self.baud_transfer, end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
packet = bytes([0x01, 0x00, 0x00])
|
||||||
|
bauds = self.baud_transfer * 4
|
||||||
|
packet += struct.pack(">H", round(65536 - 24E6 / bauds))
|
||||||
|
packet += bytes([user_trim[1], user_trim[0]])
|
||||||
|
# iap_wait = self.get_iap_delay(24E6)
|
||||||
|
iap_wait = 0x98 # iap_wait for "STC8A8K64D4"
|
||||||
|
packet += bytes([iap_wait])
|
||||||
|
self.write_packet(packet)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 1 or response[0] != 0x01:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
self.ser.baudrate = self.baud_transfer
|
||||||
|
|
||||||
|
def build_options(self):
|
||||||
|
"""Build a packet of option data from the current configuration."""
|
||||||
|
msr = self.options.get_msr()
|
||||||
|
packet = 40 * bytearray([0xff])
|
||||||
|
packet[3] = 0x00
|
||||||
|
packet[6] = 0x00
|
||||||
|
packet[22] = 0x00
|
||||||
|
packet[24:28] = struct.pack(">I", self.trim_frequency)
|
||||||
|
packet[28:30] = self.trim_value
|
||||||
|
packet[30] = self.trim_divider
|
||||||
|
packet[32] = msr[0]
|
||||||
|
packet[36:40] = msr[1:5]
|
||||||
|
return bytes(packet)
|
||||||
|
|
||||||
|
|
||||||
|
class Stc8gProtocol(Stc8dProtocol):
|
||||||
|
"""Protocol handler for STC8G series"""
|
||||||
|
|
||||||
|
def __init__(self, port, handshake, baud, trim):
|
||||||
|
Stc8dProtocol.__init__(self, port, handshake, baud, trim)
|
||||||
|
|
||||||
|
def calibrate(self):
|
||||||
|
"""Calibrate selected user frequency frequency and switch to selected baudrate."""
|
||||||
|
|
||||||
|
# handle uncalibrated chips
|
||||||
|
if self.mcu_clock_hz == 0 and self.trim_frequency <= 0:
|
||||||
|
raise StcProtocolException(
|
||||||
|
"uncalibrated, please provide a trim value")
|
||||||
|
|
||||||
|
# determine target counter
|
||||||
|
user_speed = self.trim_frequency
|
||||||
|
if user_speed <= 0:
|
||||||
|
user_speed = self.mcu_clock_hz
|
||||||
|
target_user_count = round(user_speed / self.baud_handshake)
|
||||||
|
|
||||||
|
# calibration, round 1
|
||||||
|
print("Target frequency: ", end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
packet = bytes([0x00, 0x05])
|
||||||
|
packet += bytes([0x00, 0x00, 0x80, 0x00])
|
||||||
|
packet += bytes([0x00, 0x80, 0x80, 0x80])
|
||||||
|
packet += bytes([0xFF, 0x00])
|
||||||
|
self.write_packet(packet, 12)
|
||||||
|
self.pulse(b"\xfe", timeout=1.0)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 2 or response[0] != 0x00:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
|
||||||
|
# select ranges and trim values
|
||||||
|
for divider in range(1, 6):
|
||||||
|
user_trim = self.choose_range(
|
||||||
|
packet, response, target_user_count * divider)
|
||||||
|
if user_trim is not None:
|
||||||
|
self.trim_divider = divider
|
||||||
|
break
|
||||||
|
if user_trim is None:
|
||||||
|
raise StcProtocolException("frequency trimming unsuccessful")
|
||||||
|
|
||||||
|
# calibration, round 2
|
||||||
|
packet = bytes([0x00, 0x0C])
|
||||||
|
for i in range(-6, 6):
|
||||||
|
packet += bytes([user_trim[0] + i, user_trim[1]])
|
||||||
|
self.write_packet(packet, 19)
|
||||||
|
self.pulse(b"\xfe", timeout=1.0)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 2 or response[0] != 0x00:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
|
||||||
|
# select final values
|
||||||
|
user_trim, user_count = self.choose_trim(
|
||||||
|
packet, response, target_user_count * self.trim_divider)
|
||||||
|
self.trim_value = user_trim
|
||||||
|
self.trim_frequency = round(
|
||||||
|
user_count * self.baud_handshake/self.trim_divider)
|
||||||
|
print("Target %.03f MHz" % (user_speed / 1E6))
|
||||||
|
print("Adjusted frequency: %.03f MHz(%.03f%%)" % (
|
||||||
|
(self.trim_frequency / 1E6), (self.trim_frequency*100/user_speed-100)))
|
||||||
|
|
||||||
|
# switch to programming frequency
|
||||||
|
print("Switching to %d baud: " % self.baud_transfer, end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
packet = bytes([0x01, 0x00, 0x00])
|
||||||
|
bauds = self.baud_transfer * 4
|
||||||
|
packet += struct.pack(">H", round(65536 - 24E6 / bauds))
|
||||||
|
packet += bytes([user_trim[1], user_trim[0]])
|
||||||
|
# iap_wait = self.get_iap_delay(24E6)
|
||||||
|
iap_wait = 0x98 # iap_wait for "STC8A8K64D4"
|
||||||
|
packet += bytes([iap_wait])
|
||||||
|
self.write_packet(packet)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 1 or response[0] != 0x01:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
self.ser.baudrate = self.baud_transfer
|
||||||
|
@ -99,7 +99,7 @@ class TestProgramFuzzed(unittest.TestCase):
|
|||||||
def single_fuzz(self, yml, serial_mock, fuzzer, read_mock, err, out, sleep_mock, write_mock):
|
def single_fuzz(self, yml, serial_mock, fuzzer, read_mock, err, out, sleep_mock, write_mock):
|
||||||
"""Test a single programming cycle with fuzzing"""
|
"""Test a single programming cycle with fuzzing"""
|
||||||
with open(yml) as test_file:
|
with open(yml) as test_file:
|
||||||
test_data = yaml.load(test_file.read())
|
test_data = yaml.load(test_file.read(), Loader=yaml.SafeLoader)
|
||||||
for _ in range(1000):
|
for _ in range(1000):
|
||||||
with self.subTest():
|
with self.subTest():
|
||||||
opts = get_default_opts()
|
opts = get_default_opts()
|
||||||
|
Loading…
Reference in New Issue
Block a user