Merge pull request #78 from grigorig/stcgal-patched

Integration of work done on stcgal-patched
This commit is contained in:
area-8051 2023-06-01 18:07:27 +02:00 committed by GitHub
commit f41ae5679f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 2103 additions and 1369 deletions

View File

@ -9,7 +9,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.5, 3.7, 3.8]
python-version: [3.7, 3.8]
steps:
- uses: actions/checkout@v2

View 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;
}

View File

@ -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()

Binary file not shown.

View File

@ -33,6 +33,8 @@ from stcgal.protocols import Stc15Protocol
from stcgal.protocols import Stc15AProtocol
from stcgal.protocols import StcUsb15Protocol
from stcgal.protocols import Stc8Protocol
from stcgal.protocols import Stc8dProtocol
from stcgal.protocols import Stc8gProtocol
from stcgal.protocols import StcAutoProtocol
from stcgal.protocols import StcProtocolException
from stcgal.protocols import StcFramingException
@ -43,6 +45,7 @@ class StcGal:
def __init__(self, opts):
self.opts = opts
self.hexFileType = 8
self.initialize_protocol(opts)
def initialize_protocol(self, opts):
@ -56,14 +59,19 @@ class StcGal:
elif opts.protocol == "stc12":
self.protocol = Stc12Protocol(opts.port, opts.handshake, opts.baud)
elif opts.protocol == "stc15a":
self.protocol = Stc15AProtocol(opts.port, opts.handshake, opts.baud,
round(opts.trim * 1000))
self.protocol = Stc15AProtocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
elif opts.protocol == "stc15":
self.protocol = Stc15Protocol(opts.port, opts.handshake, opts.baud,
round(opts.trim * 1000))
self.protocol = Stc15Protocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
elif opts.protocol == "stc8":
self.protocol = Stc8Protocol(opts.port, opts.handshake, opts.baud,
round(opts.trim * 1000))
self.protocol = Stc8Protocol(opts.port, opts.handshake, opts.baud, 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":
self.protocol = StcUsb15Protocol()
else:
@ -90,6 +98,7 @@ class StcGal:
fname.endswith(".ihex")):
try:
hexfile = IHex.read(fileobj)
self.hexFileType = hexfile.get_mode()
binary = hexfile.extract_data()
print("%d bytes (Intel HEX)" %len(binary))
return binary
@ -103,6 +112,12 @@ class StcGal:
def program_mcu(self):
"""Execute the standard programming flow."""
if self.opts.option: self.emit_options(self.opts.option)
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
@ -110,6 +125,11 @@ class StcGal:
sys.stdout.flush()
bindata = self.load_file_auto(self.opts.code_image)
if self.protocol.model.mcs251 and self.hexFileType != 32:
print("Invalid input file. MCU is an MCS-251, input file MUST specify a linear", file=sys.stderr)
print("base address, i.e. contain a type 04 record. More information at:", file=sys.stderr)
print("https://en.wikipedia.org/wiki/Intel_HEX", file=sys.stderr)
else:
# warn if it overflows
if len(bindata) > code_size:
print("WARNING: code_image overflows into eeprom segment!", file=sys.stderr)
@ -136,12 +156,11 @@ class StcGal:
if 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.disconnect()
def erase_mcu(self):
@ -161,7 +180,7 @@ class StcGal:
return 0
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 not self.protocol.protocol_name:
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='?')
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", "--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("-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("-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("-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)

View File

@ -14,7 +14,6 @@ class IHex:
"""Read Intel HEX data from string or lines"""
ihex = cls()
segbase = 0
for line in lines:
line = line.strip()
if not line:
@ -22,14 +21,14 @@ class IHex:
t, a, d = ihex.parse_line(line)
if t == 0x00:
ihex.insert_data(segbase + a, d)
ihex.insert_data(a, d)
elif t == 0x01:
break # Should we check for garbage after this?
elif t == 0x02:
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:
ihex.set_mode(16)
@ -39,7 +38,7 @@ class IHex:
elif t == 0x04:
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:
ihex.set_mode(32)
@ -63,6 +62,7 @@ class IHex:
self.start = None
self.mode = 8
self.row_bytes = 16
self.linearBaseAddress = 0
def set_row_bytes(self, row_bytes):
"""Set output hex file row width (bytes represented per row)."""
@ -105,6 +105,12 @@ class IHex:
def set_mode(self, mode):
self.mode = mode
def get_mode(self):
return self.mode
def get_linearBaseAddress(self):
return self.linearBaseAddress
def get_area(self, addr):
for start, data in self.areas.items():
end = start + len(data)
@ -193,6 +199,7 @@ class IHex:
output += self.make_line(
0x04, 0, struct.pack(">H", newsegbase))
segbase = newsegbase
segbase = newsegbase
output += self.make_line(0x00, addr, chunk)

File diff suppressed because it is too large Load Diff

View File

@ -86,12 +86,15 @@ class StcBaseProtocol(ABC):
self.mcu_bsl_version = ""
self.options = None
self.model = None
self.split_eeprom = None
self.split_code = None
self.uid = None
self.debug = False
self.status_packet = None
self.protocol_name = None
self.progress = None
self.progress_cb = self.progress_bar_cb
self.linearBaseAddress = 0
def progress_text_cb(self, current, written, maximum):
print(current, written, maximum)
@ -133,7 +136,7 @@ class StcBaseProtocol(ABC):
return packet[5:-1]
@abstractmethod
def write_packet(self, packet_data):
def write_packet(self, packet_data, epilogue_len = 0):
pass
def read_packet(self):
@ -262,13 +265,23 @@ class StcBaseProtocol(ABC):
def set_option(self, 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:
print("Cycling power: ", end="")
sys.stdout.flush()
if resetpin == "rts":
self.ser.setRTS(True)
else:
self.ser.setDTR(True)
time.sleep(0.5)
time.sleep(0.25)
if resetpin == "rts":
self.ser.setRTS(False)
else:
self.ser.setDTR(False)
time.sleep(0.030)
print("done")
else:
@ -278,7 +291,7 @@ class StcBaseProtocol(ABC):
print("Waiting for MCU: ", end="")
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.
Set up serial port, send sync sequence and get part info.
@ -297,7 +310,7 @@ class StcBaseProtocol(ABC):
self.ser.flushInput()
if autoreset:
self.reset_device(resetcmd)
self.reset_device(resetcmd, resetpin)
else:
print("Waiting for MCU, please cycle power: ", end="")
sys.stdout.flush()
@ -377,7 +390,12 @@ class StcAutoProtocol(StcBaseProtocol):
("stc12", r"(STC|IAP)(10|11|12)\D"),
("stc15a", r"(STC|IAP)15[FL][012]0\d(E|EA|)$"),
("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:
if re.match(pattern, self.model.name):
@ -392,7 +410,7 @@ class StcAutoProtocol(StcBaseProtocol):
def initialize_status(self, status_packet):
raise NotImplementedError
def write_packet(self, packet_data):
def write_packet(self, packet_data, epilogue_len = 0):
raise NotImplementedError
@ -422,7 +440,7 @@ class Stc89Protocol(StcBaseProtocol):
payload = StcBaseProtocol.extract_payload(self, packet)
return payload[:-1]
def write_packet(self, packet_data):
def write_packet(self, packet_data, epilogue_len = 0):
"""Send packet to 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)
return payload[:-2]
def write_packet(self, packet_data):
def write_packet(self, packet_data, epilogue_len = 0):
"""Send packet to 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 += self.PACKET_END
i = 0
while i < epilogue_len:
packet += bytes([0x66])
i += 1
self.dump_packet(packet, receive=False)
self.ser.write(packet)
self.ser.flush()
@ -1561,156 +1584,6 @@ class Stc15Protocol(Stc15AProtocol):
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):
"""USB should use large blocks"""
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
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"""
# USB support is optional. Provide an error if pyusb is not available.
@ -1874,3 +1747,399 @@ class StcUsb15Protocol(Stc15Protocol):
if self.dev:
self.write_packet(0xff)
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

View File

@ -99,7 +99,7 @@ class TestProgramFuzzed(unittest.TestCase):
def single_fuzz(self, yml, serial_mock, fuzzer, read_mock, err, out, sleep_mock, write_mock):
"""Test a single programming cycle with fuzzing"""
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):
with self.subTest():
opts = get_default_opts()