1 Commits

Author SHA1 Message Date
3ce6b565ef Possible fix/workaround for OSX frequency trimming 2017-06-16 09:47:12 +02:00
26 changed files with 390 additions and 1640 deletions

7
.gitignore vendored
View File

@ -1,12 +1,7 @@
*~ *~
*.pyc *.pyc
*.egg-info *.egg-info
*.eggs/ __pycache__
*.pybuild/
__pycache__/
/build /build
/dist /dist
/deb_dist /deb_dist
/debian/stcgal*
/debian/files
/.vscode

View File

@ -1,33 +0,0 @@
sudo: required
dist: trusty
language: python
cache:
- pip
python:
- "3.4"
- "3.5"
- "3.6"
- "pypy3"
before_install:
- sudo apt install rpm dpkg-dev debhelper dh-python python3-setuptools fakeroot python3-serial python3-yaml
install:
- pip install pyserial pyusb tqdm
script:
- python setup.py build
- python setup.py test
before_deploy:
- deactivate
- python3 setup.py bdist_rpm
- dpkg-buildpackage -uc -us
- cp ../*.deb dist/
deploy:
provider: releases
api_key: $GH_TOKEN
file_glob: true
file:
- dist/stcgal*_all.deb
- dist/stcgal*.noarch.rpm
skip_cleanup: true
on:
tags: true
python: "3.4"

View File

@ -1,5 +1,3 @@
[![Build Status](https://travis-ci.org/grigorig/stcgal.svg)](https://travis-ci.org/grigorig/stcgal)
stcgal - STC MCU ISP flash tool stcgal - STC MCU ISP flash tool
=============================== ===============================
@ -23,7 +21,7 @@ suitable for automation.
Supported MCU models Supported MCU models
-------------------- --------------------
stcgal should fully support STC 89/90/10/11/12/15 series MCUs. Support for STC8 series MCUs is work in progress. stcgal should fully support STC 89/90/10/11/12/15 series MCUs.
So far, stcgal was tested with the following MCU models: So far, stcgal was tested with the following MCU models:
@ -47,7 +45,6 @@ So far, stcgal was tested with the following MCU models:
* STC15L2K16S2 (BSL version: 7.2.4S) * STC15L2K16S2 (BSL version: 7.2.4S)
* STC15W408AS (BSL version: 7.2.4T) * STC15W408AS (BSL version: 7.2.4T)
* STC15W4K56S4 (BSL version: 7.3.4T, UART and USB mode) * STC15W4K56S4 (BSL version: 7.3.4T, UART and USB mode)
* STC8A8K64S4A12 (BSL version: 7.3.9U)
Compatibility reports, both negative and positive, are welcome. Compatibility reports, both negative and positive, are welcome.
@ -60,9 +57,9 @@ Features
* Program flash memory * Program flash memory
* Program IAP/EEPROM * Program IAP/EEPROM
* Set device options * Set device options
* Read unique device ID (STC 10/11/12/15/8) * Read unique device ID (STC 10/11/12/15)
* Trim RC oscillator frequency (STC 15/8) * Trim RC oscillator frequency (STC 15)
* Automatic power-cycling with DTR toggle or a custom shell command * Automatic power-cycling with DTR toggle
* Automatic UART protocol detection * Automatic UART protocol detection
Installation Installation
@ -97,9 +94,6 @@ positional arguments:
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
-a, --autoreset cycle power automatically by asserting DTR -a, --autoreset cycle power automatically by asserting DTR
-r RESETCMD, --resetcmd RESETCMD
Use this shell command for board power-cycling
(instead of DTR assertion)
-P {stc89,stc12a,stc12,stc15a,stc15,auto}, --protocol {stc89,stc12a,stc12,stc15a,stc15,auto} -P {stc89,stc12a,stc12,stc15a,stc15,auto}, --protocol {stc89,stc12a,stc12,stc15a,stc15,auto}
protocol version protocol version
-p PORT, --port PORT serial port device -p PORT, --port PORT serial port device
@ -127,7 +121,6 @@ and MCU series is as follows:
* ```stc12``` Most STC10/11/12 series * ```stc12``` Most STC10/11/12 series
* ```stc15a``` STC15x104E and STC15x204E(A) series * ```stc15a``` STC15x104E and STC15x204E(A) series
* ```stc15``` Most STC15 series * ```stc15``` Most STC15 series
* ```stc8``` STC8 series
* ```usb15``` USB support on STC15W4 series * ```usb15``` USB support on STC15W4 series
* ```auto``` Automatic detection of UART based protocols (default) * ```auto``` Automatic detection of UART based protocols (default)
@ -259,20 +252,17 @@ Option key | Possible values | Protocols/Models | Descri
```por_reset_delay``` | short/long | STC12+ | Power-on reset (POR) delay ```por_reset_delay``` | short/long | STC12+ | Power-on reset (POR) delay
```low_voltage_threshold``` | 0...7 | STC15A+ | Low-voltage detection threshold. Model specific. ```low_voltage_threshold``` | 0...7 | STC15A+ | Low-voltage detection threshold. Model specific.
```eeprom_lvd_inhibit``` | true/false | STC15A+ | Ignore EEPROM writes in low-voltage situations ```eeprom_lvd_inhibit``` | true/false | STC15A+ | Ignore EEPROM writes in low-voltage situations
```rstout_por_state``` | low/high | STC15+ | RSTOUT/RSTSV pin state after power-on reset ```rstout_por_state``` | low/high | STC15+ | RSTOUT pin state after power-on reset
```uart1_remap``` | true/false | STC8 | Remap UART1 pins (P3.0/P3.1) to UART2 pins (P3.6/P3.7)
```uart2_passthrough``` | true/false | STC15+ | Pass-through UART1 to UART2 pins (for single-wire UART mode) ```uart2_passthrough``` | true/false | STC15+ | Pass-through UART1 to UART2 pins (for single-wire UART mode)
```uart2_pin_mode``` | push-pull/normal | STC15+ | Output mode of UART2 TX pin ```uart2_pin_mode``` | push-pull/normal | STC15+ | Output mode of UART2 TX pin
```cpu_core_voltage``` | low/mid/high | STC15W+ | CPU core voltage (low: ~2.7V, mid: ~3.3V, high: ~3.6V) ```cpu_core_voltage``` | low/mid/high | STC15W+ | CPU core voltage (low: ~2.7V, mid: ~3.3V, high: ~3.6V)
```epwm_open_drain``` | true/false | STC8 | Use open-drain pin mode for EPWM pins after power-on reset
```program_eeprom_split``` | 512 - 65024 | STC8A8 w/ 64 KB | Select split between code flash and EEPROM flash (in 512 byte blocks)
### Frequency trimming ### Frequency trimming
If the internal RC oscillator is used (```clock_source=internal```), If the internal RC oscillator is used (```clock_source=internal```),
stcgal can execute a trim procedure to adjust it to a given value. This stcgal can execute a trim procedure to adjust it to a given value. This
is only supported by STC15 series and newer. The trim values are stored is only supported by STC15 series. The trim values are stored with
with device options. Use the ```-t``` flag to request trimming to a certain device options. Use the ```-t``` flag to request trimming to a certain
value. Generally, frequencies between 4 and 35 MHz can be achieved. If value. Generally, frequencies between 4 and 35 MHz can be achieved. If
trimming fails, stcgal will abort. trimming fails, stcgal will abort.
@ -284,22 +274,7 @@ serial interface to automate this. The DTR signal is asserted for
approximately 500 ms when the autoreset feature is enabled with the approximately 500 ms when the autoreset feature is enabled with the
```-a``` flag. This requires external circuitry to actually switch the ```-a``` flag. This requires external circuitry to actually switch the
power. In some cases, when the microcontroller draws only little power, power. In some cases, when the microcontroller draws only little power,
it is possible to directly supply power from the DTR signal. it is possible to directly supply power from the DTR signal, however.
As an alternative to DTR, you can use a custom shell command or an external
script (via -r option) to reset the device. You should specify the command
along with -a option. Do not forget the quotes!
Example:
```
$ ./stcgal.py -P stc15 -a -r "echo 1 > /sys/class/gpio/gpio666/value"
```
or
```
$ ./stcgal.py -P stc15 -a -r "./powercycle.sh"
```
### Exit status ### Exit status

6
debian/changelog vendored
View File

@ -1,9 +1,3 @@
stcgal (1.4) unstable; urgency=low
* Update to 1.4
-- Grigori <greg@chown.ath.cx> Tue, 19 Sep 2017 17:57:11 +0200
stcgal (1.3) unstable; urgency=low stcgal (1.3) unstable; urgency=low
* Update to 1.3 * Update to 1.3

4
debian/control vendored
View File

@ -2,14 +2,14 @@ Source: stcgal
Section: electronics Section: electronics
Priority: optional Priority: optional
Maintainer: Andrew Andrianov <andrew@ncrmnt.org> Maintainer: Andrew Andrianov <andrew@ncrmnt.org>
Build-Depends: debhelper (>= 9), python3, python3-setuptools, dh-python, python3-serial, python3-tqdm, python3-yaml Build-Depends: debhelper (>= 9), python3, python3-setuptools, dh-python
Standards-Version: 3.9.5 Standards-Version: 3.9.5
Homepage: https://github.com/grigorig/stcgal Homepage: https://github.com/grigorig/stcgal
X-Python3-Version: >= 3.2 X-Python3-Version: >= 3.2
Package: stcgal Package: stcgal
Architecture: all Architecture: all
Depends: ${misc:Depends}, python3, python3-serial, python3-tqdm Depends: ${misc:Depends}, python3, python3-serial
Recommends: python3-usb (>= 1.0.0~b2) Recommends: python3-usb (>= 1.0.0~b2)
Description: STC MCU ISP flash tool Description: STC MCU ISP flash tool
stcgal is a command line flash programming tool for STC MCU Ltd. stcgal is a command line flash programming tool for STC MCU Ltd.

View File

@ -1,71 +0,0 @@
MCS bytes
=========
46 b9 6a 00 33 04 00 00 5a a5 ff ff ff 00 ff ff
00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00 ff 01 31 20 80 34 00 01 ff ff ff ff ff 8b bf
^^^^^^^^^^^ ^^ ^^ ^^ ^^
frequency clkdiv 5) 1) 3)
^^^^^
trim?
f7 fe 1f cc 16
^^ ^^
4) 2)
1) not stricty related to some register
aka MCS1
bit 0: ? always 1
bit 1: oscillator high gain
bit 2: EPWM push-pull enabled
bit 3: p2.0 state after boot
bit 4: TXD signal source from RXD
bit 5: p3.7 push-pull enabled
bit 6: UART1 remap enabled
bit 7: long power-on reset delay
2) not strictly related to some register
aka MCS4
eeprom size / code space upper limit (in pages)
only seems to apply to devices with max. flash size
e.g. fe -> 63.5K, e0 -> 56K
3) like RSTCFG? inverted?
aka MCS2
bit 0: LVD0
bit 1: LVD1
bit 2: ? always 1
bit 3: ? always 1
bit 4: ~reset pin enabled
bit 5: ? always 1
bit 6: ~enable lvd reset
bit 7: ? always 1
LVD:
2.20V -> 0xbf
2.40V -> 0xbe
2.70V -> 0xbd
3.00V -> 0xbc
4) like WDT_CONTR
aka MCS3
bit 0: WDPS0
bit 1: WDPS1
bit 2: WDPS2
bit 3: ~stop wdt in idle
bit 4: ? always 1
bit 5: ~enable wdt on por
bit 6: ? always 1
bit 7: ? always 1
WDPS like in datasheet
5)
aka MCS0
bit 0: ? ~BSLD / bootloader enabled
bit 1: erase eeprom enabled
bit 2: ?
bit 3: ?
bit 4: ?
bit 5: ?
bit 6: ?
bit 7: ?

View File

@ -1,137 +0,0 @@
Overview of changes
-------------------
The following changes have been observed compared to STC15:
- Many differences in the status packet
- At least some differences in MCS
- Different challenge
- no separate program speed
- clock division was introduced; calibration always in the ~20-30 MHz range, lower clocks
use division
- the meaning of the calibration ranges and trim has changed
The good:
- Erase, Program, etc. operations are apparently unchanged. :)
Status packet
-------------
46 B9 68 00 30 50 00 54 62 58 5D 00 04 FF FD 8B BF FF 27 4A F7 FE 73 55 00 F6 28 09 85 E3 5F 80 07 20 20 20 01 00 00 FE 05 3A 17 05 25 91 FF 10 AE 16
^^^^^ wakeup clock
Clock set to 20 MHz by STC-ISP (encoding is different compared to STC15):
46 B9 68 00 30 50 01 31 2E 90 38 01 01 FF FD 8B BF FF 27 35 F7 FE 73 55 00 F6 28 09 85 E3 5F 80 07 20 20 20 01 00 00 FE 05 3A 17 05 25 91 FF 10 54 16
46 B9 68 00 30 50 01 31 2E 90 38 01 01 FF FD 8B BF FF 27 3B F7 FE 73 55 00 F6 28 09 85 E3 5F 80 07 20 20 20 01 00 00 FE 05 3A 17 05 25 91 FF 10 5A 16
^^^^^ some 24 MHz reference or other clk measurement?
^^^^^ trim/adjust?
^^ clkdiv
^^^^^^^^^^^ clk
MCS bytes
46 B9 68 00 30 50 01 31 2E 90 38 01 01 FF FD 8B BF FF 27 35 F7 FE 73 55 00 F6 28 09 85 E3 5F 80 07 20 20 20 01 00 00 FE 05 3A 17 05 25 91 FF 10 54 16
^^^^^^^^ ^^^^^
Disconnect
----------
Uses FF command byte.
Basic challenge operation
-------------------------
Host sends a challenge of some kind, followed by 0xfe pulsing
46 B9 6A 00 0C 00 02 00 00 80 00 00 F8 16
Much simpler than in STC15
MCU sends back some response:
46 B9 68 00 0C 00 02 36 AD 4E 83 02 2A 16
Host now sends some longer challenge, followed by more pulses:
46 B9 6A 00 20 00 0C 7C 00 7C 01 7C 02 7C 03 7D 00 7D 01 7D 02 7D 03 7E 00 7E 01 7E 02 7E 03 06 84 16
MCU sends back some response:
46 B9 68 00 20 00 0C 4D C6 4D DB 4D E7 4D F3 4D F6 4E 0E 4E 11 4E 26 4E 26 4E 32 4E 41 4E 56 09 DC 16
Host now seems to initiate a baud switch or something like that
46 B9 6A 00 0E 01 00 00 FF CC 01 7C 80 03 41 16
MCU acknowlegdes it:
46 B9 68 00 07 01 00 70 16
Now the MCU switches to the new baud rate.
Challenges observed
-------------------
6 MHz:
46B96A0020000C 1400 1401 1402 1403 1500 1501 1502 1503 1600 1601 1602 1603 01A416
5.5 MHz:
46B96A0020000C 5C00 5C01 5C02 5C03 5D00 5D01 5D02 5D03 5E00 5E01 5E02 5E03 050416
11 MHz:
46B96A0020000C 5B00 5B01 5B02 5B03 5C00 5C01 5C02 5C03 5D00 5D01 5D02 5D03 04F816
20 MHz:
46B96A0020000C 3600 3601 3602 3603 3700 3701 3702 3703 3800 3801 3802 3803 033C16
24 MHz:
46B96A0020000C 7C00 7C01 7C02 7C03 7D00 7D01 7D02 7D03 7E00 7E01 7E02 7E03 068416
27 MHz:
46B96A0020000C B000 B001 B002 B003 B100 B101 B102 B103 B200 B201 B202 B203 08F416
Ranges vs trim value
--------------------
46 B9 6A 00 20 00 0C 00 00 80 00 FF 00 00 01 80 01 FF 01 00 02 80 02 FF 02 00 03 80 03 FF 03 06 A4 16
46 B9 68 00 20 00 0C 36 9B 4E 92 65 E4 36 CB 4E 7D 66 29 36 D1 4E 83 66 05 36 CB 4E C2 66 47 0A EA 16
first byte determines general trim value... range of ~16 to ~30 MHz, the second byte (00..03) is a fine adjustment.
Clock division?
---------------
5.5 MHz vs 11 Mhz: challenge is about the same. it's likely some kind of clock divider is used!
5.5 Mhz switch: 01 00 00 FF CC 01 5C 80 clkdiv = 4?
11 MHz switch: 01 00 00 FF CC 01 5B 80 clkdiv = 2?
22 MHz switch: 01 00 00 FF CC 01 5C 80 clkdiv = 1?
22 Mhz option packet: 0400005AA5FFFFFF00FFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FF01516D405D0201FFFDFFFFFF8BBFF7FE
11 MHz option packet: 0400005AA5FFFFFF00FFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FF00A8AF985D0102FFFDFFFFFF8BBFF7FE
5.5 MHz option packet: 0400005AA5FFFFFF00FFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FF005462585D0004FFFDFFFFFF8BBFF7FE
^^ clkdiv?
^^^^^^^^ clkspeed
Always 24 MHz for programming
-----------------------------
Calibration for anything but 24 Mhz (and around that) fails when switching baud. Another observation is that there is no
programming speed being calibrated anymore. This may suggest that a fixed speed is used for programming.
Adjusting BRT calculation to 24 MHz in the switch packet seems to work. So it is really using 24 MHz by default;
probably some pre-calibrated value.

View File

@ -24,13 +24,10 @@
import stcgal import stcgal
from setuptools import setup, find_packages from setuptools import setup, find_packages
with open("README.md", "r") as fh:
long_description = fh.read()
setup( setup(
name = "stcgal", name = "stcgal",
version = stcgal.__version__, version = stcgal.__version__,
packages = find_packages(exclude=["doc", "tests"]), packages = find_packages(exclude=["doc"]),
install_requires = ["pyserial"], install_requires = ["pyserial"],
extras_require = { extras_require = {
"usb": ["pyusb>=1.0.0"] "usb": ["pyusb>=1.0.0"]
@ -41,9 +38,6 @@ setup(
], ],
}, },
description = "STC MCU ISP flash tool", description = "STC MCU ISP flash tool",
long_description = long_description,
long_description_content_type = "text/markdown",
keywords = "stc mcu microcontroller 8051 mcs-51",
url = "https://github.com/grigorig/stcgal", url = "https://github.com/grigorig/stcgal",
author = "Grigori Goronzy", author = "Grigori Goronzy",
author_email = "greg@kinoho.net", author_email = "greg@kinoho.net",
@ -58,12 +52,7 @@ setup(
"Operating System :: Microsoft :: Windows", "Operating System :: Microsoft :: Windows",
"Operating System :: MacOS", "Operating System :: MacOS",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Topic :: Software Development :: Embedded Systems", "Topic :: Software Development :: Embedded Systems",
"Topic :: Software Development", "Topic :: Software Development",
], ],
test_suite = "tests",
tests_require = ["PyYAML"],
) )

View File

@ -1 +1 @@
__version__ = "1.4" __version__ = "1.3"

View File

@ -20,22 +20,11 @@
# SOFTWARE. # SOFTWARE.
# #
import sys import sys, os, time, struct
import argparse import argparse
import stcgal import stcgal
import serial from stcgal.utils import Utils, BaudType
from stcgal.utils import BaudType from stcgal.protocols import *
from stcgal.protocols import Stc89Protocol
from stcgal.protocols import Stc12AProtocol
from stcgal.protocols import Stc12BProtocol
from stcgal.protocols import Stc12Protocol
from stcgal.protocols import Stc15Protocol
from stcgal.protocols import Stc15AProtocol
from stcgal.protocols import StcUsb15Protocol
from stcgal.protocols import Stc8Protocol
from stcgal.protocols import StcAutoProtocol
from stcgal.protocols import StcProtocolException
from stcgal.protocols import StcFramingException
from stcgal.ihex import IHex from stcgal.ihex import IHex
class StcGal: class StcGal:
@ -43,10 +32,7 @@ class StcGal:
def __init__(self, opts): def __init__(self, opts):
self.opts = opts self.opts = opts
self.initialize_protocol(opts)
def initialize_protocol(self, opts):
"""Initialize protocol backend"""
if opts.protocol == "stc89": if opts.protocol == "stc89":
self.protocol = Stc89Protocol(opts.port, opts.handshake, opts.baud) self.protocol = Stc89Protocol(opts.port, opts.handshake, opts.baud)
elif opts.protocol == "stc12a": elif opts.protocol == "stc12a":
@ -61,26 +47,21 @@ class StcGal:
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":
self.protocol = Stc8Protocol(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:
self.protocol = StcAutoProtocol(opts.port, opts.handshake, opts.baud) self.protocol = StcBaseProtocol(opts.port, opts.handshake, opts.baud)
self.protocol.debug = opts.debug self.protocol.debug = opts.debug
def emit_options(self, options): def emit_options(self, options):
"""Set options from command line to protocol handler.""" for o in options:
for opt in options:
try: try:
kv = opt.split("=", 1) kv = o.split("=", 1)
if len(kv) < 2: if len(kv) < 2: raise ValueError("incorrect format")
raise ValueError("incorrect format")
self.protocol.set_option(kv[0], kv[1]) self.protocol.set_option(kv[0], kv[1])
except ValueError as ex: except ValueError as e:
raise NameError("invalid option '%s' (%s)" % (kv[0], ex)) raise NameError("invalid option '%s' (%s)" % (kv[0], e))
def load_file_auto(self, fileobj): def load_file_auto(self, fileobj):
"""Load file with Intel Hex autodetection.""" """Load file with Intel Hex autodetection."""
@ -93,16 +74,14 @@ class StcGal:
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
except ValueError as ex: except ValueError as e:
raise IOError("invalid Intel HEX file (%s)" %ex) raise IOError("invalid Intel HEX file (%s)" %e)
else: else:
binary = fileobj.read() binary = fileobj.read()
print("%d bytes (Binary)" %len(binary)) print("%d bytes (Binary)" %len(binary))
return binary return binary
def program_mcu(self): def program_mcu(self):
"""Execute the standard programming flow."""
code_size = self.protocol.model.code code_size = self.protocol.model.code
ee_size = self.protocol.model.eeprom ee_size = self.protocol.model.eeprom
@ -145,67 +124,67 @@ class StcGal:
self.protocol.disconnect() self.protocol.disconnect()
def run(self): def run(self):
"""Run programmer, main entry point."""
try: try:
self.protocol.connect(autoreset=self.opts.autoreset, resetcmd=self.opts.resetcmd) self.protocol.connect(autoreset=self.opts.autoreset)
if isinstance(self.protocol, StcAutoProtocol):
if self.opts.protocol == "auto":
if not self.protocol.protocol_name: if not self.protocol.protocol_name:
raise StcProtocolException("cannot detect protocol") raise StcProtocolException("cannot detect protocol")
base_protocol = self.protocol base_protocol = self.protocol
self.opts.protocol = self.protocol.protocol_name self.opts.protocol = self.protocol.protocol_name
print("Protocol detected: %s" % self.opts.protocol) print("Protocol detected: %s" % self.opts.protocol)
# recreate self.protocol with proper protocol class # recreate self.protocol with proper protocol class
self.initialize_protocol(self.opts) self.__init__(self.opts)
else: else:
base_protocol = None base_protocol = None
self.protocol.initialize(base_protocol) self.protocol.initialize(base_protocol)
except KeyboardInterrupt: except KeyboardInterrupt:
sys.stdout.flush() sys.stdout.flush();
print("interrupted") print("interrupted")
return 2 return 2
except (StcFramingException, StcProtocolException) as ex: except (StcFramingException, StcProtocolException) as e:
sys.stdout.flush() sys.stdout.flush();
print("Protocol error: %s" % ex, file=sys.stderr) print("Protocol error: %s" % e, file=sys.stderr)
self.protocol.disconnect() self.protocol.disconnect()
return 1 return 1
except serial.SerialException as ex: except serial.SerialException as e:
sys.stdout.flush() sys.stdout.flush();
print("Serial port error: %s" % ex, file=sys.stderr) print("Serial port error: %s" % e, file=sys.stderr)
return 1 return 1
except IOError as ex: except IOError as e:
sys.stdout.flush() sys.stdout.flush();
print("I/O error: %s" % ex, file=sys.stderr) print("I/O error: %s" % e, file=sys.stderr)
return 1 return 1
try: try:
if self.opts.code_image: if self.opts.code_image:
self.program_mcu() self.program_mcu()
return 0 return 0
self.protocol.disconnect() else:
return 0 self.protocol.disconnect()
except NameError as ex: return 0
sys.stdout.flush() except NameError as e:
print("Option error: %s" % ex, file=sys.stderr) sys.stdout.flush();
print("Option error: %s" % e, file=sys.stderr)
self.protocol.disconnect() self.protocol.disconnect()
return 1 return 1
except (StcFramingException, StcProtocolException) as ex: except (StcFramingException, StcProtocolException) as e:
sys.stdout.flush() sys.stdout.flush();
print("Protocol error: %s" % ex, file=sys.stderr) print("Protocol error: %s" % e, file=sys.stderr)
self.protocol.disconnect() self.protocol.disconnect()
return 1 return 1
except KeyboardInterrupt: except KeyboardInterrupt:
sys.stdout.flush() sys.stdout.flush();
print("interrupted", file=sys.stderr) print("interrupted", file=sys.stderr)
self.protocol.disconnect() self.protocol.disconnect()
return 2 return 2
except serial.SerialException as ex: except serial.SerialException as e:
print("Serial port error: %s" % ex, file=sys.stderr) print("Serial port error: %s" % e, file=sys.stderr)
return 1 return 1
except IOError as ex: except IOError as e:
sys.stdout.flush() sys.stdout.flush();
print("I/O error: %s" % ex, file=sys.stderr) print("I/O error: %s" % e, file=sys.stderr)
self.protocol.disconnect() self.protocol.disconnect()
return 1 return 1
@ -213,14 +192,11 @@ class StcGal:
def cli(): def cli():
# check arguments # check arguments
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
description="stcgal {} - an STC MCU ISP flash tool\n".format(stcgal.__version__) + description="stcgal %s - an STC MCU ISP flash tool\n(C) 2014-2017 Grigori Goronzy\nhttps://github.com/grigorig/stcgal" %stcgal.__version__)
"(C) 2014-2017 Grigori Goronzy\nhttps://github.com/grigorig/stcgal")
parser.add_argument("code_image", help="code segment file to flash (BIN/HEX)", type=argparse.FileType("rb"), nargs='?') parser.add_argument("code_image", help="code 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='?') parser.add_argument("eeprom_image", help="eeprom segment file to flash (BIN/HEX)", type=argparse.FileType("rb"), nargs='?')
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("-r", "--resetcmd", help="Use this 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", "usb15", "auto"], default="auto")
parser.add_argument("-P", "--protocol", help="protocol version (default: auto)",
choices=["stc89", "stc12a", "stc12b", "stc12", "stc15a", "stc15", "stc8", "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=19200)
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)

View File

@ -5,213 +5,201 @@
import struct import struct
import codecs import codecs
class IHex(object):
@classmethod
def read(cls, lines):
ihex = cls()
class IHex: segbase = 0
"""Intel HEX parser and writer""" for line in lines:
line = line.strip()
if not line: continue
@classmethod t, a, d = ihex.parse_line(line)
def read(cls, lines): if t == 0x00:
"""Read Intel HEX data from string or lines""" ihex.insert_data(segbase + a, d)
ihex = cls()
segbase = 0 elif t == 0x01:
for line in lines: break # Should we check for garbage after this?
line = line.strip()
if not line:
continue
t, a, d = ihex.parse_line(line) elif t == 0x02:
if t == 0x00: ihex.set_mode(16)
ihex.insert_data(segbase + a, d) segbase = struct.unpack(">H", d[0:2])[0] << 4
elif t == 0x01: elif t == 0x03:
break # Should we check for garbage after this? ihex.set_mode(16)
elif t == 0x02: cs, ip = struct.unpack(">2H", d[0:2])
ihex.set_mode(16) ihex.set_start((cs, ip))
segbase = struct.unpack(">H", d[0:2])[0] << 4
elif t == 0x03: elif t == 0x04:
ihex.set_mode(16) ihex.set_mode(32)
segbase = struct.unpack(">H", d[0:2])[0] << 16
cs, ip = struct.unpack(">2H", d[0:2]) elif t == 0x05:
ihex.set_start((cs, ip)) ihex.set_mode(32)
ihex.set_start(struct.unpack(">I", d[0:4])[0])
elif t == 0x04: else:
ihex.set_mode(32) raise ValueError("Invalid type byte")
segbase = struct.unpack(">H", d[0:2])[0] << 16
elif t == 0x05: return ihex
ihex.set_mode(32)
ihex.set_start(struct.unpack(">I", d[0:4])[0])
else: @classmethod
raise ValueError("Invalid type byte") def read_file(cls, fname):
f = open(fname, "rb")
ihex = cls.read(f)
f.close()
return ihex
return ihex def __init__(self):
self.areas = {}
self.start = None
self.mode = 8
self.row_bytes = 16
@classmethod def set_row_bytes(self, row_bytes):
def read_file(cls, fname): """Set output hex file row width (bytes represented per row)."""
"""Read Intel HEX data from file""" if row_bytes < 1 or row_bytes > 0xff:
f = open(fname, "rb") raise ValueError("Value out of range: (%r)" % row_bytes)
ihex = cls.read(f) self.row_bytes = row_bytes
f.close()
return ihex def extract_data(self, start=None, end=None):
if start is None:
start = 0
if end is None:
result = bytearray()
for addr, data in self.areas.items():
if addr >= start:
if len(result) < (addr - start):
result[len(result):addr-start] = bytes(addr-start-len(result))
result[addr-start:addr-start+len(data)] = data
return bytes(result)
else:
result = bytearray()
for addr, data in self.areas.items():
if addr >= start and addr < end:
data = data[:end-addr]
if len(result) < (addr - start):
result[len(result):addr-start] = bytes(addr-start-len(result))
result[addr-start:addr-start+len(data)] = data
return bytes(result)
def set_start(self, start=None):
self.start = start
def __init__(self): def set_mode(self, mode):
self.areas = {} self.mode = mode
self.start = None
self.mode = 8
self.row_bytes = 16
def set_row_bytes(self, row_bytes): def get_area(self, addr):
"""Set output hex file row width (bytes represented per row).""" for start, data in self.areas.items():
if row_bytes < 1 or row_bytes > 0xff: end = start + len(data)
raise ValueError("Value out of range: (%r)" % row_bytes) if addr >= start and addr <= end:
self.row_bytes = row_bytes return start
def extract_data(self, start=None, end=None): return None
"""Extract binary data"""
if start is None:
start = 0
if end is None: def insert_data(self, istart, idata):
result = bytearray() iend = istart + len(idata)
for addr, data in self.areas.items(): area = self.get_area(istart)
if addr >= start: if area is None:
if len(result) < (addr - start): self.areas[istart] = idata
result[len(result):addr - start] = bytes(
addr - start - len(result))
result[addr - start:addr - start + len(data)] = data
return bytes(result) else:
data = self.areas[area]
# istart - iend + len(idata) + len(data)
self.areas[area] = data[:istart-area] + idata + data[iend-area:]
result = bytearray() def calc_checksum(self, bytes):
total = sum(bytes)
return (-total) & 0xFF
for addr, data in self.areas.items(): def parse_line(self, rawline):
if addr >= start and addr < end: if rawline[0:1] != b":":
data = data[:end - addr] raise ValueError("Invalid line start character (%r)" % rawline[0])
if len(result) < (addr - start):
result[len(result):addr - start] = bytes(
addr - start - len(result))
result[addr - start:addr - start + len(data)] = data
return bytes(result) try:
#line = rawline[1:].decode("hex")
line = codecs.decode(rawline[1:], "hex_codec")
except:
raise ValueError("Invalid hex data")
def set_start(self, start=None): length, addr, type = struct.unpack(">BHB", line[:4])
self.start = start
def set_mode(self, mode): dataend = length + 4
self.mode = mode data = line[4:dataend]
def get_area(self, addr): #~ print line[dataend:dataend + 2], repr(line)
for start, data in self.areas.items(): cs1 = line[dataend]
end = start + len(data) cs2 = self.calc_checksum(line[:dataend])
if addr >= start and addr <= end:
return start
return None if cs1 != cs2:
raise ValueError("Checksums do not match")
def insert_data(self, istart, idata): return (type, addr, data)
iend = istart + len(idata)
area = self.get_area(istart) def make_line(self, type, addr, data):
if area is None: line = struct.pack(">BHB", len(data), addr, type)
self.areas[istart] = idata line += data
line += chr(self.calc_checksum(line))
#~ return ":" + line.encode("hex")
return ":" + line.encode("hex").upper() + "\r\n"
else: def write(self):
data = self.areas[area] output = ""
# istart - iend + len(idata) + len(data)
self.areas[area] = data[ for start, data in sorted(self.areas.items()):
:istart - area] + idata + data[iend - area:] i = 0
segbase = 0
def calc_checksum(self, data): while i < len(data):
total = sum(data) chunk = data[i:i + self.row_bytes]
return (-total) & 0xFF
def parse_line(self, rawline): addr = start
if rawline[0:1] != b":": newsegbase = segbase
raise ValueError("Invalid line start character (%r)" % rawline[0])
try: if self.mode == 8:
line = codecs.decode(rawline[1:], "hex_codec") addr = addr & 0xFFFF
except ValueError:
raise ValueError("Invalid hex data")
length, addr, line_type = struct.unpack(">BHB", line[:4]) elif self.mode == 16:
t = addr & 0xFFFF
newsegbase = (addr - t) >> 4
addr = t
dataend = length + 4 if newsegbase != segbase:
data = line[4:dataend] output += self.make_line(0x02, 0, struct.pack(">H", newsegbase))
segbase = newsegbase
cs1 = line[dataend] elif self.mode == 32:
cs2 = self.calc_checksum(line[:dataend]) newsegbase = addr >> 16
addr = addr & 0xFFFF
if cs1 != cs2: if newsegbase != segbase:
raise ValueError("Checksums do not match") output += self.make_line(0x04, 0, struct.pack(">H", newsegbase))
segbase = newsegbase
return (line_type, addr, data) output += self.make_line(0x00, addr, chunk)
def make_line(self, line_type, addr, data): i += self.row_bytes
line = struct.pack(">BHB", len(data), addr, line_type) start += self.row_bytes
line += data
line += chr(self.calc_checksum(line))
return ":" + line.encode("hex").upper() + "\r\n"
def write(self): if self.start is not None:
"""Write Intel HEX data to string""" if self.mode == 16:
output = "" output += self.make_line(0x03, 0, struct.pack(">2H", self.start[0], self.start[1]))
elif self.mode == 32:
output += self.make_line(0x05, 0, struct.pack(">I", self.start))
for start, data in sorted(self.areas.items()): output += self.make_line(0x01, 0, "")
i = 0 return output
segbase = 0
while i < len(data): def write_file(self, fname):
chunk = data[i:i + self.row_bytes] f = open(fname, "w")
f.write(self.write())
addr = start f.close()
newsegbase = segbase
if self.mode == 8:
addr = addr & 0xFFFF
elif self.mode == 16:
t = addr & 0xFFFF
newsegbase = (addr - t) >> 4
addr = t
if newsegbase != segbase:
output += self.make_line(
0x02, 0, struct.pack(">H", newsegbase))
segbase = newsegbase
elif self.mode == 32:
newsegbase = addr >> 16
addr = addr & 0xFFFF
if newsegbase != segbase:
output += self.make_line(
0x04, 0, struct.pack(">H", newsegbase))
segbase = newsegbase
output += self.make_line(0x00, addr, chunk)
i += self.row_bytes
start += self.row_bytes
if self.start is not None:
if self.mode == 16:
output += self.make_line(
0x03, 0, struct.pack(">2H", self.start[0], self.start[1]))
elif self.mode == 32:
output += self.make_line(
0x05, 0, struct.pack(">I", self.start))
output += self.make_line(0x01, 0, "")
return output
def write_file(self, fname):
"""Write Intel HEX data to file"""
f = open(fname, "w")
f.write(self.write())
f.close()

View File

@ -973,46 +973,6 @@ class MCUModelDatabase:
MCUModel(name='STC90LE513AD', magic=0xf18d, total=65536, code=53248, eeprom=10240), MCUModel(name='STC90LE513AD', magic=0xf18d, total=65536, code=53248, eeprom=10240),
MCUModel(name='STC90LE514AD', magic=0xf18e, total=65536, code=57344, eeprom=6144), MCUModel(name='STC90LE514AD', magic=0xf18e, total=65536, code=57344, eeprom=6144),
MCUModel(name='STC90LE516AD', magic=0xf190, total=65536, code=63488, eeprom=0), MCUModel(name='STC90LE516AD', magic=0xf190, total=65536, code=63488, eeprom=0),
# Warning, these definitions lack a valid eeprom size.
MCUModel(name='STC15F04AD', magic=0xd444, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15F06AD', magic=0xd446, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15F08AD', magic=0xd448, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15F10AD', magic=0xd44a, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15F12AD', magic=0xd44c, total=12288, code=12288, eeprom=0),
MCUModel(name='STC15F04CCP', magic=0xd434, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15F06CCP', magic=0xd436, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15F08CCP', magic=0xd438, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15F10CCP', magic=0xd43a, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15F12CCP', magic=0xd43c, total=12288, code=12288, eeprom=0),
MCUModel(name='STC15F04', magic=0xd404, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15F06', magic=0xd406, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15F08', magic=0xd408, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15F10', magic=0xd40a, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15F12', magic=0xd40c, total=12288, code=12288, eeprom=0),
MCUModel(name='IAP15F08AD', magic=0xd458, total=8192, code=8192, eeprom=0),
MCUModel(name='IAP15F10AD', magic=0xd45a, total=10240, code=10240, eeprom=0),
MCUModel(name='IAP15F12AD', magic=0xd45c, total=12288, code=12288, eeprom=0),
MCUModel(name='IAP15F14AD', magic=0xd45e, total=14336, code=14336, eeprom=0),
MCUModel(name='STC15L04AD', magic=0xd4c4, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15L06AD', magic=0xd4c6, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15L08AD', magic=0xd4c8, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15L10AD', magic=0xd4ca, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15L12AD', magic=0xd4cc, total=12288, code=12288, eeprom=0),
MCUModel(name='STC15L04CCP', magic=0xd4b4, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15L06CCP', magic=0xd4b6, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15L08CCP', magic=0xd4b8, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15L10CCP', magic=0xd4ba, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15L12CCP', magic=0xd4bc, total=12288, code=12288, eeprom=0),
MCUModel(name='STC15L04', magic=0xd484, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15L06', magic=0xd486, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15L08', magic=0xd488, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15L10', magic=0xd48a, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15L12', magic=0xd48c, total=12288, code=12288, eeprom=0),
MCUModel(name='IAP15L08AD', magic=0xd4d8, total=8192, code=8192, eeprom=0),
MCUModel(name='IAP15L10AD', magic=0xd4da, total=10240, code=10240, eeprom=0),
MCUModel(name='IAP15L12AD', magic=0xd4dc, total=12288, code=12288, eeprom=0),
MCUModel(name='IAP15L14AD', magic=0xd4de, total=14336, code=14336, eeprom=0),
) )
@classmethod @classmethod
@ -1028,3 +988,6 @@ class MCUModelDatabase:
print(" Magic: %02X%02X" % (model.magic >> 8, model.magic & 0xff)) print(" Magic: %02X%02X" % (model.magic >> 8, model.magic & 0xff))
print(" Code flash: %.1f KB" % (model.code / 1024.0)) print(" Code flash: %.1f KB" % (model.code / 1024.0))
print(" EEPROM flash: %.1f KB" % (model.eeprom / 1024.0)) print(" EEPROM flash: %.1f KB" % (model.eeprom / 1024.0))

View File

@ -21,24 +21,15 @@
# #
import struct import struct
from abc import ABC
from stcgal.utils import Utils from stcgal.utils import Utils
class BaseOption(ABC): class BaseOption:
"""Base class for options"""
def __init__(self):
self.options = ()
self.msr = None
def print(self): def print(self):
"""Print current configuration to standard output"""
print("Target options:") print("Target options:")
for name, get_func, _ in self.options: for name, get_func, _ in self.options:
print(" %s=%s" % (name, get_func())) print(" %s=%s" % (name, get_func()))
def set_option(self, name, value): def set_option(self, name, value):
"""Set value of a specific option"""
for opt, _, set_func in self.options: for opt, _, set_func in self.options:
if opt == name: if opt == name:
print("Option %s=%s" % (name, value)) print("Option %s=%s" % (name, value))
@ -47,14 +38,12 @@ class BaseOption(ABC):
raise ValueError("unknown") raise ValueError("unknown")
def get_option(self, name): def get_option(self, name):
"""Get option value for a specific option"""
for opt, get_func, _ in self.options: for opt, get_func, _ in self.options:
if opt == name: if opt == name:
return get_func(name) return get_func(name)
raise ValueError("unknown") raise ValueError("unknown")
def get_msr(self): def get_msr(self):
"""Get array of model-specific configuration registers"""
return bytes(self.msr) return bytes(self.msr)
@ -62,7 +51,6 @@ class Stc89Option(BaseOption):
"""Manipulation STC89 series option byte""" """Manipulation STC89 series option byte"""
def __init__(self, msr): def __init__(self, msr):
super().__init__()
self.msr = msr self.msr = msr
self.options = ( self.options = (
("cpu_6t_enabled", self.get_t6, self.set_t6), ("cpu_6t_enabled", self.get_t6, self.set_t6),
@ -81,7 +69,7 @@ class Stc89Option(BaseOption):
return not bool(self.msr & 1) return not bool(self.msr & 1)
def set_t6(self, val): def set_t6(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr &= 0xfe self.msr &= 0xfe
self.msr |= 0x01 if not bool(val) else 0x00 self.msr |= 0x01 if not bool(val) else 0x00
@ -89,7 +77,7 @@ class Stc89Option(BaseOption):
return not bool(self.msr & 4) return not bool(self.msr & 4)
def set_pindetect(self, val): def set_pindetect(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr &= 0xfb self.msr &= 0xfb
self.msr |= 0x04 if not bool(val) else 0x00 self.msr |= 0x04 if not bool(val) else 0x00
@ -97,7 +85,7 @@ class Stc89Option(BaseOption):
return not bool(self.msr & 8) return not bool(self.msr & 8)
def set_ee_erase(self, val): def set_ee_erase(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr &= 0xf7 self.msr &= 0xf7
self.msr |= 0x08 if not bool(val) else 0x00 self.msr |= 0x08 if not bool(val) else 0x00
@ -116,7 +104,7 @@ class Stc89Option(BaseOption):
return bool(self.msr & 32) return bool(self.msr & 32)
def set_ale(self, val): def set_ale(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr &= 0xdf self.msr &= 0xdf
self.msr |= 0x20 if bool(val) else 0x00 self.msr |= 0x20 if bool(val) else 0x00
@ -124,7 +112,7 @@ class Stc89Option(BaseOption):
return bool(self.msr & 64) return bool(self.msr & 64)
def set_xram(self, val): def set_xram(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr &= 0xbf self.msr &= 0xbf
self.msr |= 0x40 if bool(val) else 0x00 self.msr |= 0x40 if bool(val) else 0x00
@ -132,7 +120,7 @@ class Stc89Option(BaseOption):
return not bool(self.msr & 128) return not bool(self.msr & 128)
def set_watchdog(self, val): def set_watchdog(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr &= 0x7f self.msr &= 0x7f
self.msr |= 0x80 if not bool(val) else 0x00 self.msr |= 0x80 if not bool(val) else 0x00
@ -141,7 +129,6 @@ class Stc12AOption(BaseOption):
"""Manipulate STC12A series option bytes""" """Manipulate STC12A series option bytes"""
def __init__(self, msr): def __init__(self, msr):
super().__init__()
assert len(msr) == 4 assert len(msr) == 4
self.msr = bytearray(msr) self.msr = bytearray(msr)
@ -163,7 +150,7 @@ class Stc12AOption(BaseOption):
def set_low_voltage_detect(self, val): def set_low_voltage_detect(self, val):
lvds = {"low": 1, "high": 0} lvds = {"low": 1, "high": 0}
if val not in lvds.keys(): if val not in lvds.keys():
raise ValueError("must be one of %s" % list(lvds.keys())) raise ValueError("must be one of %s" % list(sources.keys()))
self.msr[3] &= 0xbf self.msr[3] &= 0xbf
self.msr[3] |= lvds[val] << 6 self.msr[3] |= lvds[val] << 6
@ -182,7 +169,7 @@ class Stc12AOption(BaseOption):
return not bool(self.msr[1] & 32) return not bool(self.msr[1] & 32)
def set_watchdog(self, val): def set_watchdog(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[1] &= 0xdf self.msr[1] &= 0xdf
self.msr[1] |= 0x20 if not val else 0x00 self.msr[1] |= 0x20 if not val else 0x00
@ -190,7 +177,7 @@ class Stc12AOption(BaseOption):
return not bool(self.msr[1] & 8) return not bool(self.msr[1] & 8)
def set_watchdog_idle(self, val): def set_watchdog_idle(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[1] &= 0xf7 self.msr[1] &= 0xf7
self.msr[1] |= 0x08 if not val else 0x00 self.msr[1] |= 0x08 if not val else 0x00
@ -209,7 +196,7 @@ class Stc12AOption(BaseOption):
return not bool(self.msr[2] & 2) return not bool(self.msr[2] & 2)
def set_ee_erase(self, val): def set_ee_erase(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[2] &= 0xfd self.msr[2] &= 0xfd
self.msr[2] |= 0x02 if not val else 0x00 self.msr[2] |= 0x02 if not val else 0x00
@ -217,7 +204,7 @@ class Stc12AOption(BaseOption):
return not bool(self.msr[2] & 1) return not bool(self.msr[2] & 1)
def set_pindetect(self, val): def set_pindetect(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[2] &= 0xfe self.msr[2] &= 0xfe
self.msr[2] |= 0x01 if not val else 0x00 self.msr[2] |= 0x01 if not val else 0x00
@ -226,7 +213,6 @@ class Stc12Option(BaseOption):
"""Manipulate STC10/11/12 series option bytes""" """Manipulate STC10/11/12 series option bytes"""
def __init__(self, msr): def __init__(self, msr):
super().__init__()
assert len(msr) == 4 assert len(msr) == 4
self.msr = bytearray(msr) self.msr = bytearray(msr)
@ -249,7 +235,7 @@ class Stc12Option(BaseOption):
return bool(self.msr[0] & 1) return bool(self.msr[0] & 1)
def set_reset_pin_enabled(self, val): def set_reset_pin_enabled(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[0] &= 0xfe self.msr[0] &= 0xfe
self.msr[0] |= 0x01 if bool(val) else 0x00 self.msr[0] |= 0x01 if bool(val) else 0x00
@ -257,7 +243,7 @@ class Stc12Option(BaseOption):
return not bool(self.msr[0] & 64) return not bool(self.msr[0] & 64)
def set_low_voltage_detect(self, val): def set_low_voltage_detect(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[0] &= 0xbf self.msr[0] &= 0xbf
self.msr[0] |= 0x40 if not val else 0x00 self.msr[0] |= 0x40 if not val else 0x00
@ -309,7 +295,7 @@ class Stc12Option(BaseOption):
return not bool(self.msr[2] & 32) return not bool(self.msr[2] & 32)
def set_watchdog(self, val): def set_watchdog(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[2] &= 0xdf self.msr[2] &= 0xdf
self.msr[2] |= 0x20 if not val else 0x00 self.msr[2] |= 0x20 if not val else 0x00
@ -317,7 +303,7 @@ class Stc12Option(BaseOption):
return not bool(self.msr[2] & 8) return not bool(self.msr[2] & 8)
def set_watchdog_idle(self, val): def set_watchdog_idle(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[2] &= 0xf7 self.msr[2] &= 0xf7
self.msr[2] |= 0x08 if not val else 0x00 self.msr[2] |= 0x08 if not val else 0x00
@ -336,7 +322,7 @@ class Stc12Option(BaseOption):
return not bool(self.msr[3] & 2) return not bool(self.msr[3] & 2)
def set_ee_erase(self, val): def set_ee_erase(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[3] &= 0xfd self.msr[3] &= 0xfd
self.msr[3] |= 0x02 if not val else 0x00 self.msr[3] |= 0x02 if not val else 0x00
@ -344,14 +330,13 @@ class Stc12Option(BaseOption):
return not bool(self.msr[3] & 1) return not bool(self.msr[3] & 1)
def set_pindetect(self, val): def set_pindetect(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[3] &= 0xfe self.msr[3] &= 0xfe
self.msr[3] |= 0x01 if not val else 0x00 self.msr[3] |= 0x01 if not val else 0x00
class Stc15AOption(BaseOption): class Stc15AOption(BaseOption):
def __init__(self, msr): def __init__(self, msr):
super().__init__()
assert len(msr) == 13 assert len(msr) == 13
self.msr = bytearray(msr) self.msr = bytearray(msr)
@ -374,7 +359,7 @@ class Stc15AOption(BaseOption):
return bool(self.msr[0] & 16) return bool(self.msr[0] & 16)
def set_reset_pin_enabled(self, val): def set_reset_pin_enabled(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[0] &= 0xef self.msr[0] &= 0xef
self.msr[0] |= 0x10 if bool(val) else 0x00 self.msr[0] |= 0x10 if bool(val) else 0x00
@ -382,7 +367,7 @@ class Stc15AOption(BaseOption):
return not bool(self.msr[2] & 32) return not bool(self.msr[2] & 32)
def set_watchdog(self, val): def set_watchdog(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[2] &= 0xdf self.msr[2] &= 0xdf
self.msr[2] |= 0x20 if not val else 0x00 self.msr[2] |= 0x20 if not val else 0x00
@ -390,7 +375,7 @@ class Stc15AOption(BaseOption):
return not bool(self.msr[2] & 8) return not bool(self.msr[2] & 8)
def set_watchdog_idle(self, val): def set_watchdog_idle(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[2] &= 0xf7 self.msr[2] &= 0xf7
self.msr[2] |= 0x08 if not val else 0x00 self.msr[2] |= 0x08 if not val else 0x00
@ -409,7 +394,7 @@ class Stc15AOption(BaseOption):
return bool(self.msr[1] & 64) return bool(self.msr[1] & 64)
def set_lvrs(self, val): def set_lvrs(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[1] &= 0xbf self.msr[1] &= 0xbf
self.msr[1] |= 0x40 if val else 0x00 self.msr[1] |= 0x40 if val else 0x00
@ -417,7 +402,7 @@ class Stc15AOption(BaseOption):
return bool(self.msr[1] & 128) return bool(self.msr[1] & 128)
def set_eeprom_lvd(self, val): def set_eeprom_lvd(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[1] &= 0x7f self.msr[1] &= 0x7f
self.msr[1] |= 0x80 if val else 0x00 self.msr[1] |= 0x80 if val else 0x00
@ -435,7 +420,7 @@ class Stc15AOption(BaseOption):
return not bool(self.msr[12] & 2) return not bool(self.msr[12] & 2)
def set_ee_erase(self, val): def set_ee_erase(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[12] &= 0xfd self.msr[12] &= 0xfd
self.msr[12] |= 0x02 if not val else 0x00 self.msr[12] |= 0x02 if not val else 0x00
@ -443,14 +428,13 @@ class Stc15AOption(BaseOption):
return not bool(self.msr[12] & 1) return not bool(self.msr[12] & 1)
def set_pindetect(self, val): def set_pindetect(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[12] &= 0xfe self.msr[12] &= 0xfe
self.msr[12] |= 0x01 if not val else 0x00 self.msr[12] |= 0x01 if not val else 0x00
class Stc15Option(BaseOption): class Stc15Option(BaseOption):
def __init__(self, msr): def __init__(self, msr):
super().__init__()
assert len(msr) >= 4 assert len(msr) >= 4
self.msr = bytearray(msr) self.msr = bytearray(msr)
@ -473,13 +457,13 @@ class Stc15Option(BaseOption):
) )
if len(msr) > 4: if len(msr) > 4:
self.options += (("cpu_core_voltage", self.get_core_voltage, self.set_core_voltage),) self.options += ("cpu_core_voltage", self.get_core_voltage, self.set_core_voltage),
def get_reset_pin_enabled(self): def get_reset_pin_enabled(self):
return not bool(self.msr[2] & 16) return not bool(self.msr[2] & 16)
def set_reset_pin_enabled(self, val): def set_reset_pin_enabled(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[2] &= 0xef self.msr[2] &= 0xef
self.msr[2] |= 0x10 if not bool(val) else 0x00 self.msr[2] |= 0x10 if not bool(val) else 0x00
@ -509,7 +493,7 @@ class Stc15Option(BaseOption):
return not bool(self.msr[0] & 32) return not bool(self.msr[0] & 32)
def set_watchdog(self, val): def set_watchdog(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[0] &= 0xdf self.msr[0] &= 0xdf
self.msr[0] |= 0x20 if not val else 0x00 self.msr[0] |= 0x20 if not val else 0x00
@ -517,7 +501,7 @@ class Stc15Option(BaseOption):
return not bool(self.msr[0] & 8) return not bool(self.msr[0] & 8)
def set_watchdog_idle(self, val): def set_watchdog_idle(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[0] &= 0xf7 self.msr[0] &= 0xf7
self.msr[0] |= 0x08 if not val else 0x00 self.msr[0] |= 0x08 if not val else 0x00
@ -536,7 +520,7 @@ class Stc15Option(BaseOption):
return not bool(self.msr[1] & 64) return not bool(self.msr[1] & 64)
def set_lvrs(self, val): def set_lvrs(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[1] &= 0xbf self.msr[1] &= 0xbf
self.msr[1] |= 0x40 if not val else 0x00 self.msr[1] |= 0x40 if not val else 0x00
@ -544,7 +528,7 @@ class Stc15Option(BaseOption):
return bool(self.msr[1] & 128) return bool(self.msr[1] & 128)
def set_eeprom_lvd(self, val): def set_eeprom_lvd(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[1] &= 0x7f self.msr[1] &= 0x7f
self.msr[1] |= 0x80 if val else 0x00 self.msr[1] |= 0x80 if val else 0x00
@ -562,7 +546,7 @@ class Stc15Option(BaseOption):
return bool(self.msr[3] & 2) return bool(self.msr[3] & 2)
def set_ee_erase(self, val): def set_ee_erase(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[3] &= 0xfd self.msr[3] &= 0xfd
self.msr[3] |= 0x02 if val else 0x00 self.msr[3] |= 0x02 if val else 0x00
@ -570,7 +554,7 @@ class Stc15Option(BaseOption):
return not bool(self.msr[3] & 1) return not bool(self.msr[3] & 1)
def set_pindetect(self, val): def set_pindetect(self, val):
val = Utils.to_bool(val) val = Utils.to_bool(val);
self.msr[3] &= 0xfe self.msr[3] &= 0xfe
self.msr[3] |= 0x01 if not val else 0x00 self.msr[3] |= 0x01 if not val else 0x00
@ -615,177 +599,10 @@ class Stc15Option(BaseOption):
if self.msr[4] == 0xea: return "low" if self.msr[4] == 0xea: return "low"
elif self.msr[4] == 0xf7: return "mid" elif self.msr[4] == 0xf7: return "mid"
elif self.msr[4] == 0xfd: return "high" elif self.msr[4] == 0xfd: return "high"
return "unknown" else: return "unknown"
def set_core_voltage(self, val): def set_core_voltage(self, val):
volt_vals = {"low": 0xea, "mid": 0xf7, "high": 0xfd} volt_vals = {"low": 0xea, "mid": 0xf7, "high": 0xfd}
if val not in volt_vals.keys(): if val not in volt_vals.keys():
raise ValueError("must be one of %s" % list(volt_vals.keys())) raise ValueError("must be one of %s" % list(volt_vals.keys()))
self.msr[4] = volt_vals[val] self.msr[4] = volt_vals[val]
class Stc8Option(BaseOption):
def __init__(self, msr):
super().__init__()
assert len(msr) >= 5
self.msr = bytearray(msr)
self.options = (
("reset_pin_enabled", self.get_reset_pin_enabled, self.set_reset_pin_enabled),
("clock_gain", self.get_clock_gain, self.set_clock_gain),
("watchdog_por_enabled", self.get_watchdog, self.set_watchdog),
("watchdog_stop_idle", self.get_watchdog_idle, self.set_watchdog_idle),
("watchdog_prescale", self.get_watchdog_prescale, self.set_watchdog_prescale),
("low_voltage_reset", self.get_lvrs, self.set_lvrs),
("low_voltage_threshold", self.get_low_voltage, self.set_low_voltage),
("eeprom_erase_enabled", self.get_ee_erase, self.set_ee_erase),
("bsl_pindetect_enabled", self.get_pindetect, self.set_pindetect),
("por_reset_delay", self.get_por_delay, self.set_por_delay),
("rstout_por_state", self.get_p20_state, self.set_p20_state),
("uart1_remap", self.get_uart1_remap, self.set_uart1_remap),
("uart2_passthrough", self.get_uart_passthrough, self.set_uart_passthrough),
("uart2_pin_mode", self.get_uart_pin_mode, self.set_uart_pin_mode),
("epwm_open_drain", self.get_epwm_pp, self.set_epwm_pp),
("program_eeprom_split", self.get_flash_split, self.set_flash_split),
)
def get_reset_pin_enabled(self):
return not bool(self.msr[2] & 16)
def set_reset_pin_enabled(self, val):
val = Utils.to_bool(val)
self.msr[2] &= 0xef
self.msr[2] |= 0x10 if not bool(val) else 0x00
def get_clock_gain(self):
gain = bool(self.msr[1] & 0x02)
return "high" if gain else "low"
def set_clock_gain(self, val):
gains = {"low": 0, "high": 1}
if val not in gains.keys():
raise ValueError("must be one of %s" % list(gains.keys()))
self.msr[1] &= 0xfd
self.msr[1] |= gains[val] << 1
def get_watchdog(self):
return not bool(self.msr[3] & 32)
def set_watchdog(self, val):
val = Utils.to_bool(val)
self.msr[3] &= 0xdf
self.msr[3] |= 0x20 if not val else 0x00
def get_watchdog_idle(self):
return not bool(self.msr[3] & 8)
def set_watchdog_idle(self, val):
val = Utils.to_bool(val)
self.msr[3] &= 0xf7
self.msr[3] |= 0x08 if not val else 0x00
def get_watchdog_prescale(self):
return 2 ** (((self.msr[3]) & 0x07) + 1)
def set_watchdog_prescale(self, val):
val = Utils.to_int(val)
wd_vals = {2: 0, 4: 1, 8: 2, 16: 3, 32: 4, 64: 5, 128: 6, 256: 7}
if val not in wd_vals.keys():
raise ValueError("must be one of %s" % list(wd_vals.keys()))
self.msr[3] &= 0xf8
self.msr[3] |= wd_vals[val]
def get_lvrs(self):
return not bool(self.msr[2] & 64)
def set_lvrs(self, val):
val = Utils.to_bool(val)
self.msr[2] &= 0xbf
self.msr[2] |= 0x40 if not val else 0x00
def get_low_voltage(self):
return 3 - self.msr[2] & 0x03
def set_low_voltage(self, val):
val = Utils.to_int(val)
if val not in range(0, 4):
raise ValueError("must be one of %s" % list(range(0, 4)))
self.msr[2] &= 0xfc
self.msr[2] |= 3 - val
def get_ee_erase(self):
return bool(self.msr[0] & 2)
def set_ee_erase(self, val):
val = Utils.to_bool(val)
self.msr[0] &= 0xfd
self.msr[0] |= 0x02 if val else 0x00
def get_pindetect(self):
return not bool(self.msr[0] & 1)
def set_pindetect(self, val):
val = Utils.to_bool(val)
self.msr[0] &= 0xfe
self.msr[0] |= 0x01 if not val else 0x00
def get_por_delay(self):
delay = bool(self.msr[1] & 128)
return "long" if delay else "short"
def set_por_delay(self, val):
delays = {"short": 0, "long": 1}
if val not in delays.keys():
raise ValueError("must be one of %s" % list(delays.keys()))
self.msr[1] &= 0x7f
self.msr[1] |= delays[val] << 7
def get_p20_state(self):
return "high" if self.msr[1] & 0x08 else "low"
def set_p20_state(self, val):
val = Utils.to_bool(val)
self.msr[1] &= 0xf7
self.msr[1] |= 0x08 if val else 0x00
def get_uart_passthrough(self):
return bool(self.msr[1] & 0x10)
def set_uart_passthrough(self, val):
val = Utils.to_bool(val)
self.msr[1] &= 0xef
self.msr[1] |= 0x10 if val else 0x00
def get_uart_pin_mode(self):
return "push-pull" if bool(self.msr[1] & 0x20) else "normal"
def set_uart_pin_mode(self, val):
modes = {"normal": 0, "push-pull": 1}
if val not in modes.keys():
raise ValueError("must be one of %s" % list(modes.keys()))
self.msr[1] &= 0xdf
self.msr[1] |= 0x20 if modes[val] else 0x00
def get_epwm_pp(self):
return bool(self.msr[1] & 0x04)
def set_epwm_pp(self, val):
val = Utils.to_bool(val)
self.msr[1] &= 0xfb
self.msr[1] |= 0x04 if val else 0x00
def get_uart1_remap(self):
return bool(self.msr[1] & 0x40)
def set_uart1_remap(self, val):
val = Utils.to_bool(val)
self.msr[1] &= 0xbf
self.msr[1] |= 0x40 if val else 0x00
def get_flash_split(self):
return self.msr[4] * 256
def set_flash_split(self, val):
num_val = Utils.to_int(val)
if num_val < 512 or num_val > 65024 or (num_val % 512) != 0:
raise ValueError("must be between 512 and 65024 bytes and a multiple of 512 bytes")
self.msr[4] = num_val // 256

View File

@ -21,28 +21,16 @@
# #
import serial import serial
import sys import sys, os, time, struct, re, errno
import os import argparse
import time import collections
import struct
import re
import errno
from stcgal.models import MCUModelDatabase from stcgal.models import MCUModelDatabase
from stcgal.utils import Utils from stcgal.utils import Utils
from stcgal.options import Stc89Option from stcgal.options import *
from stcgal.options import Stc12Option
from stcgal.options import Stc12AOption
from stcgal.options import Stc15Option
from stcgal.options import Stc15AOption
from stcgal.options import Stc8Option
from abc import ABC
from abc import abstractmethod
import functools import functools
import tqdm
try: try:
import usb.core import usb.core, usb.util
import usb.util
_usb_available = True _usb_available = True
except ImportError: except ImportError:
_usb_available = False _usb_available = False
@ -58,23 +46,22 @@ class StcProtocolException(Exception):
pass pass
class StcBaseProtocol(ABC): class StcBaseProtocol:
"""Basic functionality for STC BSL protocols""" """Basic functionality for STC BSL protocols"""
PACKET_START = bytes([0x46, 0xb9])
"""magic word that starts a packet""" """magic word that starts a packet"""
PACKET_START = bytes([0x46, 0xb9])
PACKET_END = bytes([0x16])
"""magic byte that ends a packet""" """magic byte that ends a packet"""
PACKET_END = bytes([0x16])
PACKET_MCU = bytes([0x68])
"""magic byte for packets received from MCU""" """magic byte for packets received from MCU"""
PACKET_MCU = bytes([0x68])
PACKET_HOST = bytes([0x6a])
"""magic byte for packets sent by host""" """magic byte for packets sent by host"""
PACKET_HOST = bytes([0x6a])
PARITY = serial.PARITY_NONE PARITY = serial.PARITY_NONE
"""parity configuration for serial communication"""
def __init__(self, port, baud_handshake, baud_transfer): def __init__(self, port, baud_handshake, baud_transfer):
self.port = port self.port = port
@ -90,22 +77,6 @@ class StcBaseProtocol(ABC):
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_cb = self.progress_bar_cb
def progress_text_cb(self, current, written, maximum):
print(current, written, maximum)
def progress_bar_cb(self, current, written, maximum):
if not self.progress:
self.progress = tqdm.tqdm(
total = maximum,
unit = " Bytes",
desc = "Writing flash"
)
self.progress.update(written)
if current == maximum:
self.progress.close()
def dump_packet(self, data, receive=True): def dump_packet(self, data, receive=True):
if self.debug: if self.debug:
@ -132,10 +103,6 @@ class StcBaseProtocol(ABC):
return packet[5:-1] return packet[5:-1]
@abstractmethod
def write_packet(self, packet_data):
pass
def read_packet(self): def read_packet(self):
"""Read and check packet from MCU. """Read and check packet from MCU.
@ -147,7 +114,11 @@ class StcBaseProtocol(ABC):
# read and check frame start magic # read and check frame start magic
packet = bytes() packet = bytes()
packet += self.read_bytes_safe(1) # XXX: skip extraneous 0xFE byte?
leading = self.read_bytes_safe(1)
if leading == 0xfe:
leading = self.read_bytes_safe(1)
packet += leading
# Some (?) BSL versions don't send a frame start with the status # Some (?) BSL versions don't send a frame start with the status
# packet. Let's be liberal and accept that always, just in case. # packet. Let's be liberal and accept that always, just in case.
if packet[0] == self.PACKET_MCU[0]: if packet[0] == self.PACKET_MCU[0]:
@ -175,7 +146,7 @@ class StcBaseProtocol(ABC):
packet += self.read_bytes_safe(packet_len - 3) packet += self.read_bytes_safe(packet_len - 3)
# verify checksum and extract payload # verify checksum and extract payload
payload = self.extract_payload(packet) payload = self.extract_payload(packet);
self.dump_packet(packet, receive=True) self.dump_packet(packet, receive=True)
@ -225,6 +196,20 @@ class StcBaseProtocol(ABC):
mcu_name += "E" if self.status_packet[17] < 0x70 else "W" mcu_name += "E" if self.status_packet[17] < 0x70 else "W"
self.model = self.model._replace(name = mcu_name) self.model = self.model._replace(name = mcu_name)
protocol_database = [("stc89", "STC(89|90)(C|LE)\d"),
("stc12a", "STC12(C|LE)\d052"),
("stc12b", "STC12(C|LE)(52|56)"),
("stc12", "(STC|IAP)(10|11|12)\D"),
("stc15a", "(STC|IAP)15[FL][01]0\d(E|EA|)$"),
("stc15", "(STC|IAP|IRC)15\D")]
for protocol_name, pattern in protocol_database:
if re.match(pattern, self.model.name):
self.protocol_name = protocol_name
break
else:
self.protocol_name = None
def get_status_packet(self): def get_status_packet(self):
"""Read and decode status packet""" """Read and decode status packet"""
@ -260,22 +245,7 @@ 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 connect(self, autoreset=False):
if not resetcmd:
print("Cycling power: ", end="")
sys.stdout.flush()
self.ser.setDTR(True)
time.sleep(0.5)
self.ser.setDTR(False)
print("done")
else:
print("Cycling power via shell cmd: " + resetcmd)
os.system(resetcmd)
print("Waiting for MCU: ", end="")
sys.stdout.flush()
def connect(self, autoreset=False, resetcmd=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.
@ -294,7 +264,14 @@ class StcBaseProtocol(ABC):
self.ser.flushInput() self.ser.flushInput()
if autoreset: if autoreset:
self.reset_device(resetcmd) print("Cycling power: ", end="")
sys.stdout.flush()
self.ser.setDTR(True)
time.sleep(0.5)
self.ser.setDTR(False)
print("done")
print("Waiting for MCU: ", end="")
sys.stdout.flush()
else: else:
print("Waiting for MCU, please cycle power: ", end="") print("Waiting for MCU, please cycle power: ", end="")
sys.stdout.flush() sys.stdout.flush()
@ -306,8 +283,6 @@ class StcBaseProtocol(ABC):
try: try:
self.pulse() self.pulse()
self.status_packet = self.get_status_packet() self.status_packet = self.get_status_packet()
if len(self.status_packet) < 23:
raise StcProtocolException("status packet too short")
except (StcFramingException, serial.SerialTimeoutException): pass except (StcFramingException, serial.SerialTimeoutException): pass
print("done") print("done")
@ -317,21 +292,7 @@ class StcBaseProtocol(ABC):
self.initialize_model() self.initialize_model()
@abstractmethod def initialize(self, base_protocol = None):
def initialize_status(self, status_packet):
"""Initialize internal state from status packet"""
pass
@abstractmethod
def initialize_options(self, status_packet):
"""Initialize options from status packet"""
pass
def initialize(self, base_protocol=None):
"""
Initialize from another instance. This is an alternative for calling
connect() and is used by protocol autodetection.
"""
if base_protocol: if base_protocol:
self.ser = base_protocol.ser self.ser = base_protocol.ser
self.ser.parity = self.PARITY self.ser.parity = self.PARITY
@ -359,48 +320,14 @@ class StcBaseProtocol(ABC):
print("Disconnected!") print("Disconnected!")
class StcAutoProtocol(StcBaseProtocol):
"""
Protocol handler for autodetection of protocols. Does not implement full
functionality for any device class.
"""
def initialize_model(self):
super().initialize_model()
protocol_database = [("stc89", r"STC(89|90)(C|LE)\d"),
("stc12a", r"STC12(C|LE)\d052"),
("stc12b", r"STC12(C|LE)(52|56)"),
("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")]
for protocol_name, pattern in protocol_database:
if re.match(pattern, self.model.name):
self.protocol_name = protocol_name
break
else:
self.protocol_name = None
def initialize_options(self, status_packet):
raise NotImplementedError
def initialize_status(self, status_packet):
raise NotImplementedError
def write_packet(self, packet_data):
raise NotImplementedError
class Stc89Protocol(StcBaseProtocol): class Stc89Protocol(StcBaseProtocol):
"""Protocol handler for STC 89/90 series""" """Protocol handler for STC 89/90 series"""
"""These don't use any parity"""
PARITY = serial.PARITY_NONE PARITY = serial.PARITY_NONE
"""Parity configuration - these don't use any parity"""
PROGRAM_BLOCKSIZE = 128
"""block size for programming flash""" """block size for programming flash"""
PROGRAM_BLOCKSIZE = 128
def __init__(self, port, baud_handshake, baud_transfer): def __init__(self, port, baud_handshake, baud_transfer):
StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer) StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer)
@ -419,7 +346,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, data):
"""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.
@ -431,8 +358,8 @@ class Stc89Protocol(StcBaseProtocol):
packet += self.PACKET_HOST packet += self.PACKET_HOST
# packet length and payload # packet length and payload
packet += struct.pack(">H", len(packet_data) + 5) packet += struct.pack(">H", len(data) + 5)
packet += packet_data packet += data
# checksum and end code # checksum and end code
packet += bytes([sum(packet[2:]) & 0xff]) packet += bytes([sum(packet[2:]) & 0xff])
@ -453,9 +380,6 @@ class Stc89Protocol(StcBaseProtocol):
def initialize_options(self, status_packet): def initialize_options(self, status_packet):
"""Initialize options""" """Initialize options"""
if len(status_packet) < 20:
raise StcProtocolException("invalid options in status packet")
self.options = Stc89Option(status_packet[19]) self.options = Stc89Option(status_packet[19])
self.options.print() self.options.print()
@ -488,21 +412,21 @@ class Stc89Protocol(StcBaseProtocol):
return brt, brt_csum, iap_wait, delay return brt, brt_csum, iap_wait, delay
def initialize_status(self, status_packet): def initialize_status(self, packet):
"""Decode status packet and store basic MCU info""" """Decode status packet and store basic MCU info"""
self.cpu_6t = not bool(status_packet[19] & 1) self.cpu_6t = not bool(packet[19] & 1)
cpu_t = 6.0 if self.cpu_6t else 12.0 cpu_t = 6.0 if self.cpu_6t else 12.0
freq_counter = 0 freq_counter = 0
for i in range(8): for i in range(8):
freq_counter += struct.unpack(">H", status_packet[1+2*i:3+2*i])[0] freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
freq_counter /= 8.0 freq_counter /= 8.0
self.mcu_clock_hz = (self.baud_handshake * freq_counter * cpu_t) / 7.0 self.mcu_clock_hz = (self.baud_handshake * freq_counter * cpu_t) / 7.0
bl_version, bl_stepping = struct.unpack("BB", status_packet[17:19]) bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
chr(bl_stepping)) chr(bl_stepping))
def handshake(self): def handshake(self):
"""Switch to transfer baudrate """Switch to transfer baudrate
@ -545,7 +469,7 @@ class Stc89Protocol(StcBaseProtocol):
sys.stdout.flush() sys.stdout.flush()
packet = bytes([0x80, 0x00, 0x00, 0x36, 0x01]) packet = bytes([0x80, 0x00, 0x00, 0x36, 0x01])
packet += struct.pack(">H", self.mcu_magic) packet += struct.pack(">H", self.mcu_magic)
for _ in range(4): for i in range(4):
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if response[0] != 0x80: if response[0] != 0x80:
@ -553,7 +477,7 @@ class Stc89Protocol(StcBaseProtocol):
print("done") print("done")
def erase_flash(self, erase_size, _): def erase_flash(self, erase_size, flash_size):
"""Erase the MCU's flash memory. """Erase the MCU's flash memory.
Erase the flash memory with a block-erase command. Erase the flash memory with a block-erase command.
@ -577,6 +501,8 @@ class Stc89Protocol(StcBaseProtocol):
as the block size (depends on MCU's RAM size). as the block size (depends on MCU's RAM size).
""" """
print("Writing %d bytes: " % len(data), end="")
sys.stdout.flush()
for i in range(0, len(data), self.PROGRAM_BLOCKSIZE): for i in range(0, len(data), self.PROGRAM_BLOCKSIZE):
packet = bytes(3) packet = bytes(3)
packet += struct.pack(">H", i) packet += struct.pack(">H", i)
@ -586,12 +512,13 @@ class Stc89Protocol(StcBaseProtocol):
csum = sum(packet[7:]) & 0xff csum = sum(packet[7:]) & 0xff
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if len(response) < 1 or response[0] != 0x80: if response[0] != 0x80:
raise StcProtocolException("incorrect magic in write packet") raise StcProtocolException("incorrect magic in write packet")
elif len(response) < 2 or response[1] != csum: elif response[1] != csum:
raise StcProtocolException("verification checksum mismatch") raise StcProtocolException("verification checksum mismatch")
self.progress_cb(i, self.PROGRAM_BLOCKSIZE, len(data)) print(".", end="")
self.progress_cb(len(data), self.PROGRAM_BLOCKSIZE, len(data)) sys.stdout.flush()
print(" done")
def program_options(self): def program_options(self):
"""Program option byte into flash""" """Program option byte into flash"""
@ -645,18 +572,18 @@ class Stc12AProtocol(Stc12AOptionsMixIn, Stc89Protocol):
def __init__(self, port, baud_handshake, baud_transfer): def __init__(self, port, baud_handshake, baud_transfer):
Stc89Protocol.__init__(self, port, baud_handshake, baud_transfer) Stc89Protocol.__init__(self, port, baud_handshake, baud_transfer)
def initialize_status(self, status_packet): def initialize_status(self, packet):
"""Decode status packet and store basic MCU info""" """Decode status packet and store basic MCU info"""
freq_counter = 0 freq_counter = 0
for i in range(8): for i in range(8):
freq_counter += struct.unpack(">H", status_packet[1+2*i:3+2*i])[0] freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
freq_counter /= 8.0 freq_counter /= 8.0
self.mcu_clock_hz = (self.baud_handshake * freq_counter * 12.0) / 7.0 self.mcu_clock_hz = (self.baud_handshake * freq_counter * 12.0) / 7.0
bl_version, bl_stepping = struct.unpack("BB", status_packet[17:19]) bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
chr(bl_stepping)) chr(bl_stepping))
self.bsl_version = bl_version self.bsl_version = bl_version
@ -689,9 +616,6 @@ class Stc12AProtocol(Stc12AOptionsMixIn, Stc89Protocol):
def initialize_options(self, status_packet): def initialize_options(self, status_packet):
"""Initialize options""" """Initialize options"""
if len(status_packet) < 31:
raise StcProtocolException("invalid options in status packet")
# create option state # create option state
self.options = Stc12AOption(status_packet[23:26] + status_packet[29:30]) self.options = Stc12AOption(status_packet[23:26] + status_packet[29:30])
self.options.print() self.options.print()
@ -733,7 +657,7 @@ class Stc12AProtocol(Stc12AOptionsMixIn, Stc89Protocol):
sys.stdout.flush() sys.stdout.flush()
packet = bytes([0x80, 0x00, 0x00, 0x36, 0x01]) packet = bytes([0x80, 0x00, 0x00, 0x36, 0x01])
packet += struct.pack(">H", self.mcu_magic) packet += struct.pack(">H", self.mcu_magic)
for _ in range(4): for i in range(4):
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if response[0] != 0x80: if response[0] != 0x80:
@ -790,14 +714,14 @@ class Stc12OptionsMixIn:
class Stc12BaseProtocol(StcBaseProtocol): class Stc12BaseProtocol(StcBaseProtocol):
"""Base class for STC 10/11/12 series protocol handlers""" """Base class for STC 10/11/12 series protocol handlers"""
PROGRAM_BLOCKSIZE = 128
"""block size for programming flash""" """block size for programming flash"""
PROGRAM_BLOCKSIZE = 128
ERASE_COUNTDOWN = 0x0d
"""countdown value for flash erase""" """countdown value for flash erase"""
ERASE_COUNTDOWN = 0x0d
PARITY = serial.PARITY_EVEN
"""Parity for error correction was introduced with STC12""" """Parity for error correction was introduced with STC12"""
PARITY = serial.PARITY_EVEN
def __init__(self, port, baud_handshake, baud_transfer): def __init__(self, port, baud_handshake, baud_transfer):
StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer) StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer)
@ -814,7 +738,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, data):
"""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.
@ -826,8 +750,8 @@ class Stc12BaseProtocol(StcBaseProtocol):
packet += self.PACKET_HOST packet += self.PACKET_HOST
# packet length and payload # packet length and payload
packet += struct.pack(">H", len(packet_data) + 6) packet += struct.pack(">H", len(data) + 6)
packet += packet_data packet += data
# checksum and end code # checksum and end code
packet += struct.pack(">H", sum(packet[2:]) & 0xffff) packet += struct.pack(">H", sum(packet[2:]) & 0xffff)
@ -837,18 +761,18 @@ class Stc12BaseProtocol(StcBaseProtocol):
self.ser.write(packet) self.ser.write(packet)
self.ser.flush() self.ser.flush()
def initialize_status(self, status_packet): def initialize_status(self, packet):
"""Decode status packet and store basic MCU info""" """Decode status packet and store basic MCU info"""
freq_counter = 0 freq_counter = 0
for i in range(8): for i in range(8):
freq_counter += struct.unpack(">H", status_packet[1+2*i:3+2*i])[0] freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
freq_counter /= 8.0 freq_counter /= 8.0
self.mcu_clock_hz = (self.baud_handshake * freq_counter * 12.0) / 7.0 self.mcu_clock_hz = (self.baud_handshake * freq_counter * 12.0) / 7.0
bl_version, bl_stepping = struct.unpack("BB", status_packet[17:19]) bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
chr(bl_stepping)) chr(bl_stepping))
self.bsl_version = bl_version self.bsl_version = bl_version
@ -881,9 +805,6 @@ class Stc12BaseProtocol(StcBaseProtocol):
def initialize_options(self, status_packet): def initialize_options(self, status_packet):
"""Initialize options""" """Initialize options"""
if len(status_packet) < 29:
raise StcProtocolException("invalid options in status packet")
# create option state # create option state
self.options = Stc12Option(status_packet[23:26] + status_packet[27:28]) self.options = Stc12Option(status_packet[23:26] + status_packet[27:28])
self.options.print() self.options.print()
@ -961,18 +882,22 @@ class Stc12BaseProtocol(StcBaseProtocol):
as the block size (depends on MCU's RAM size). as the block size (depends on MCU's RAM size).
""" """
print("Writing %d bytes: " % len(data), end="")
sys.stdout.flush()
for i in range(0, len(data), self.PROGRAM_BLOCKSIZE): for i in range(0, len(data), self.PROGRAM_BLOCKSIZE):
packet = bytes(3) packet = bytes(3)
packet += struct.pack(">H", i) packet += struct.pack(">H", i)
packet += struct.pack(">H", self.PROGRAM_BLOCKSIZE) packet += struct.pack(">H", self.PROGRAM_BLOCKSIZE)
packet += data[i:i+self.PROGRAM_BLOCKSIZE] packet += data[i:i+self.PROGRAM_BLOCKSIZE]
while len(packet) < self.PROGRAM_BLOCKSIZE + 7: packet += b"\x00" while len(packet) < self.PROGRAM_BLOCKSIZE + 7: packet += b"\x00"
csum = sum(packet[7:]) & 0xff
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if response[0] != 0x00: if response[0] != 0x00:
raise StcProtocolException("incorrect magic in write packet") raise StcProtocolException("incorrect magic in write packet")
self.progress_cb(i, self.PROGRAM_BLOCKSIZE, len(data)) print(".", end="")
self.progress_cb(len(data), self.PROGRAM_BLOCKSIZE, len(data)) sys.stdout.flush()
print(" done")
print("Finishing write: ", end="") print("Finishing write: ", end="")
sys.stdout.flush() sys.stdout.flush()
@ -1015,9 +940,6 @@ class Stc15AProtocol(Stc12Protocol):
def initialize_options(self, status_packet): def initialize_options(self, status_packet):
"""Initialize options""" """Initialize options"""
if len(status_packet) < 37:
raise StcProtocolException("invalid options in status packet")
# create option state # create option state
self.options = Stc15AOption(status_packet[23:36]) self.options = Stc15AOption(status_packet[23:36])
self.options.print() self.options.print()
@ -1036,20 +958,20 @@ class Stc15AProtocol(Stc12Protocol):
raise StcProtocolException("incorrect magic in status packet") raise StcProtocolException("incorrect magic in status packet")
return status_packet return status_packet
def initialize_status(self, status_packet): def initialize_status(self, packet):
"""Decode status packet and store basic MCU info""" """Decode status packet and store basic MCU info"""
freq_counter = 0 freq_counter = 0
for i in range(4): for i in range(4):
freq_counter += struct.unpack(">H", status_packet[1+2*i:3+2*i])[0] freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
freq_counter /= 4.0 freq_counter /= 4.0
self.mcu_clock_hz = (self.baud_handshake * freq_counter * 12.0) / 7.0 self.mcu_clock_hz = (self.baud_handshake * freq_counter * 12.0) / 7.0
bl_version, bl_stepping = struct.unpack("BB", status_packet[17:19]) bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
chr(bl_stepping)) chr(bl_stepping))
self.trim_data = status_packet[51:58] self.trim_data = packet[51:58]
self.freq_counter = freq_counter self.freq_counter = freq_counter
def get_trim_sequence(self, frequency): def get_trim_sequence(self, frequency):
@ -1101,8 +1023,7 @@ class Stc15AProtocol(Stc12Protocol):
""" """
user_speed = self.trim_frequency user_speed = self.trim_frequency
if user_speed <= 0: if user_speed <= 0: user_speed = self.mcu_clock_hz
user_speed = self.mcu_clock_hz
program_speed = 22118400 program_speed = 22118400
user_count = int(self.freq_counter * (user_speed / self.mcu_clock_hz)) user_count = int(self.freq_counter * (user_speed / self.mcu_clock_hz))
@ -1130,19 +1051,15 @@ class Stc15AProtocol(Stc12Protocol):
self.write_packet(packet) self.write_packet(packet)
self.pulse(timeout=1.0) self.pulse(timeout=1.0)
response = self.read_packet() response = self.read_packet()
if len(response) < 36 or response[0] != 0x65: if response[0] != 0x65:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
# determine programming speed trim value # determine programming speed trim value
target_trim_a, target_count_a = struct.unpack(">HH", response[28:32]) target_trim_a, target_count_a = struct.unpack(">HH", response[28:32])
target_trim_b, target_count_b = struct.unpack(">HH", response[32:36]) target_trim_b, target_count_b = struct.unpack(">HH", response[32:36])
if target_count_a == target_count_b:
raise StcProtocolException("frequency trimming failed")
m = (target_trim_b - target_trim_a) / (target_count_b - target_count_a) m = (target_trim_b - target_trim_a) / (target_count_b - target_count_a)
n = target_trim_a - m * target_count_a n = target_trim_a - m * target_count_a
program_trim = round(m * program_count + n) program_trim = round(m * program_count + n)
if program_trim > 65535 or program_trim < 0:
raise StcProtocolException("frequency trimming failed")
# determine trim trials for second round # determine trim trials for second round
trim_a, count_a = struct.unpack(">HH", response[12:16]) trim_a, count_a = struct.unpack(">HH", response[12:16])
@ -1161,14 +1078,10 @@ class Stc15AProtocol(Stc12Protocol):
target_count_a = count_a target_count_a = count_a
target_count_b = count_b target_count_b = count_b
# linear interpolate to find range to try next # linear interpolate to find range to try next
if target_count_a == target_count_b:
raise StcProtocolException("frequency trimming failed")
m = (target_trim_b - target_trim_a) / (target_count_b - target_count_a) m = (target_trim_b - target_trim_a) / (target_count_b - target_count_a)
n = target_trim_a - m * target_count_a n = target_trim_a - m * target_count_a
target_trim = round(m * user_count + n) target_trim = round(m * user_count + n)
target_trim_start = min(max(target_trim - 5, target_trim_a), target_trim_b) target_trim_start = min(max(target_trim - 5, target_trim_a), target_trim_b)
if target_trim_start + 11 > 65535 or target_trim_start < 0:
raise StcProtocolException("frequency trimming failed")
# trim challenge-response, second round # trim challenge-response, second round
packet = bytes([0x65]) packet = bytes([0x65])
@ -1180,7 +1093,7 @@ class Stc15AProtocol(Stc12Protocol):
self.write_packet(packet) self.write_packet(packet)
self.pulse(timeout=1.0) self.pulse(timeout=1.0)
response = self.read_packet() response = self.read_packet()
if len(response) < 56 or response[0] != 0x65: if response[0] != 0x65:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
# determine best trim value # determine best trim value
@ -1239,40 +1152,37 @@ class Stc15Protocol(Stc15AProtocol):
def initialize_options(self, status_packet): def initialize_options(self, status_packet):
"""Initialize options""" """Initialize options"""
if len(status_packet) < 14:
raise StcProtocolException("invalid options in status packet")
# create option state # create option state
# XXX: check how option bytes are concatenated here
self.options = Stc15Option(status_packet[5:8] + status_packet[12:13] + status_packet[37:38]) self.options = Stc15Option(status_packet[5:8] + status_packet[12:13] + status_packet[37:38])
self.options.print() self.options.print()
def initialize_status(self, status_packet): def initialize_status(self, packet):
"""Decode status packet and store basic MCU info""" """Decode status packet and store basic MCU info"""
# check bit that control internal vs. external clock source # check bit that control internal vs. external clock source
# get frequency either stored from calibration or from # get frequency either stored from calibration or from
# frequency counter # frequency counter
self.external_clock = (status_packet[7] & 0x01) == 0 self.external_clock = (packet[7] & 0x01) == 0
if self.external_clock: if self.external_clock:
count, = struct.unpack(">H", status_packet[13:15]) count, = struct.unpack(">H", packet[13:15])
self.mcu_clock_hz = self.baud_handshake * count self.mcu_clock_hz = self.baud_handshake * count
else: else:
self.mcu_clock_hz, = struct.unpack(">I", status_packet[8:12]) self.mcu_clock_hz, = struct.unpack(">I", packet[8:12])
# all ones means no calibration # all ones means no calibration
# new chips are shipped without any calibration # new chips are shipped without any calibration
if self.mcu_clock_hz == 0xffffffff: self.mcu_clock_hz = 0 if self.mcu_clock_hz == 0xffffffff: self.mcu_clock_hz = 0
# pre-calibrated trim adjust for 24 MHz, range 0x40 # pre-calibrated trim adjust for 24 MHz, range 0x40
self.freq_count_24 = status_packet[4] self.freq_count_24 = packet[4]
# wakeup timer factory value # wakeup timer factory value
self.wakeup_freq, = struct.unpack(">H", status_packet[1:3]) self.wakeup_freq, = struct.unpack(">H", packet[1:3])
bl_version, bl_stepping = struct.unpack("BB", status_packet[17:19]) bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
bl_minor = status_packet[22] & 0x0f bl_minor = packet[22] & 0x0f
self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
bl_minor, chr(bl_stepping)) bl_minor,
chr(bl_stepping))
self.bsl_version = bl_version self.bsl_version = bl_version
def print_mcu_info(self): def print_mcu_info(self):
@ -1288,8 +1198,6 @@ class Stc15Protocol(Stc15AProtocol):
calib_data = response[2:] calib_data = response[2:]
challenge_data = packet[2:] challenge_data = packet[2:]
calib_len = response[1] calib_len = response[1]
if len(calib_data) < 2 * calib_len:
raise StcProtocolException("range calibration data missing")
for i in range(calib_len - 1): for i in range(calib_len - 1):
count_a, count_b = struct.unpack(">HH", calib_data[2*i:2*i+4]) count_a, count_b = struct.unpack(">HH", calib_data[2*i:2*i+4])
@ -1299,8 +1207,6 @@ class Stc15Protocol(Stc15AProtocol):
m = (trim_b - trim_a) / (count_b - count_a) m = (trim_b - trim_a) / (count_b - count_a)
n = trim_a - m * count_a n = trim_a - m * count_a
target_trim = round(m * target_count + n) target_trim = round(m * target_count + n)
if target_trim > 65536 or target_trim < 0:
raise StcProtocolException("frequency trimming failed")
return (target_trim, trim_range) return (target_trim, trim_range)
return None return None
@ -1312,8 +1218,6 @@ class Stc15Protocol(Stc15AProtocol):
calib_data = response[2:] calib_data = response[2:]
challenge_data = packet[2:] challenge_data = packet[2:]
calib_len = response[1] calib_len = response[1]
if len(calib_data) < 2 * calib_len:
raise StcProtocolException("trim calibration data missing")
best = None best = None
best_count = sys.maxsize best_count = sys.maxsize
@ -1324,9 +1228,6 @@ class Stc15Protocol(Stc15AProtocol):
best_count = abs(count - target_count) best_count = abs(count - target_count)
best = (trim_adj, trim_range), count best = (trim_adj, trim_range), count
if not best:
raise StcProtocolException("frequency trimming failed")
return best return best
def calibrate(self): def calibrate(self):
@ -1356,13 +1257,13 @@ class Stc15Protocol(Stc15AProtocol):
self.write_packet(packet) self.write_packet(packet)
self.pulse(b"\xfe", timeout=1.0) self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet() response = self.read_packet()
if len(response) < 2 or response[0] != 0x00: if response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
# select ranges and trim values # select ranges and trim values
user_trim = self.choose_range(packet, response, target_user_count) user_trim = self.choose_range(packet, response, target_user_count)
prog_trim = self.choose_range(packet, response, target_prog_count) prog_trim = self.choose_range(packet, response, target_prog_count)
if user_trim is None or prog_trim is None: if user_trim == None or prog_trim == None:
raise StcProtocolException("frequency trimming unsuccessful") raise StcProtocolException("frequency trimming unsuccessful")
# calibration, round 2 # calibration, round 2
@ -1375,12 +1276,12 @@ class Stc15Protocol(Stc15AProtocol):
self.write_packet(packet) self.write_packet(packet)
self.pulse(b"\xfe", timeout=1.0) self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet() response = self.read_packet()
if len(response) < 2 or response[0] != 0x00: if response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
# select final values # select final values
user_trim, user_count = self.choose_trim(packet, response, target_user_count) user_trim, user_count = self.choose_trim(packet, response, target_user_count)
prog_trim, _ = self.choose_trim(packet, response, target_prog_count) prog_trim, prog_count = self.choose_trim(packet, response, target_prog_count)
self.trim_value = user_trim self.trim_value = user_trim
self.trim_frequency = round(user_count * (self.baud_handshake / 2)) self.trim_frequency = round(user_count * (self.baud_handshake / 2))
print("%.03f MHz" % (self.trim_frequency / 1E6)) print("%.03f MHz" % (self.trim_frequency / 1E6))
@ -1401,7 +1302,7 @@ class Stc15Protocol(Stc15AProtocol):
packet += bytes([iap_wait]) packet += bytes([iap_wait])
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if len(response) < 1 or response[0] != 0x01: if response[0] != 0x01:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
time.sleep(0.2) time.sleep(0.2)
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
@ -1418,7 +1319,7 @@ class Stc15Protocol(Stc15AProtocol):
packet += bytes([0x00, 0x00, iap_wait]) packet += bytes([0x00, 0x00, iap_wait])
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if len(response) < 1 or response[0] != 0x01: if response[0] != 0x01:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
time.sleep(0.2) time.sleep(0.2)
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
@ -1444,9 +1345,9 @@ class Stc15Protocol(Stc15AProtocol):
packet += bytes([0x00, 0x00, 0x5a, 0xa5]) packet += bytes([0x00, 0x00, 0x5a, 0xa5])
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if len(response) == 1 and response[0] == 0x0f: if response[0] == 0x0f:
raise StcProtocolException("MCU is locked") raise StcProtocolException("MCU is locked")
if len(response) < 1 or response[0] != 0x05: if response[0] != 0x05:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
print("done") print("done")
@ -1467,20 +1368,18 @@ class Stc15Protocol(Stc15AProtocol):
packet += bytes([0x00, 0x5a, 0xa5]) packet += bytes([0x00, 0x5a, 0xa5])
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if len(response) < 1 or response[0] != 0x03: if response[0] != 0x03:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
print("done") print("done")
if len(response) >= 8: if len(response) >= 8:
self.uid = response[1:8] self.uid = response[1:8]
# we should have a UID at this point
if not self.uid:
raise StcProtocolException("UID is missing")
def program_flash(self, data): def program_flash(self, data):
"""Program the MCU's flash memory.""" """Program the MCU's flash memory."""
print("Writing %d bytes: " % len(data), end="")
sys.stdout.flush()
for i in range(0, len(data), self.PROGRAM_BLOCKSIZE): for i in range(0, len(data), self.PROGRAM_BLOCKSIZE):
packet = bytes([0x22]) if i == 0 else bytes([0x02]) packet = bytes([0x22]) if i == 0 else bytes([0x02])
packet += struct.pack(">H", i) packet += struct.pack(">H", i)
@ -1490,10 +1389,11 @@ class Stc15Protocol(Stc15AProtocol):
while len(packet) < self.PROGRAM_BLOCKSIZE + 3: packet += b"\x00" while len(packet) < self.PROGRAM_BLOCKSIZE + 3: packet += b"\x00"
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if len(response) < 2 or response[0] != 0x02 or response[1] != 0x54: if response[0] != 0x02 or response[1] != 0x54:
raise StcProtocolException("incorrect magic in write packet") raise StcProtocolException("incorrect magic in write packet")
self.progress_cb(i, self.PROGRAM_BLOCKSIZE, len(data)) print(".", end="")
self.progress_cb(len(data), self.PROGRAM_BLOCKSIZE, len(data)) sys.stdout.flush()
print(" done")
# BSL 7.2+ needs a write finish packet according to dumps # BSL 7.2+ needs a write finish packet according to dumps
if self.bsl_version >= 0x72: if self.bsl_version >= 0x72:
@ -1502,7 +1402,7 @@ class Stc15Protocol(Stc15AProtocol):
packet = bytes([0x07, 0x00, 0x00, 0x5a, 0xa5]) packet = bytes([0x07, 0x00, 0x00, 0x5a, 0xa5])
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if len(response) < 2 or response[0] != 0x07 or response[1] != 0x54: if response[0] != 0x07 or response[1] != 0x54:
raise StcProtocolException("incorrect magic in finish packet") raise StcProtocolException("incorrect magic in finish packet")
print("done") print("done")
@ -1511,7 +1411,7 @@ class Stc15Protocol(Stc15AProtocol):
configuration.""" configuration."""
msr = self.options.get_msr() msr = self.options.get_msr()
packet = bytes([0xff] * 23) packet = bytes([0xff] * 23)
packet += bytes([(self.trim_frequency >> 24) & 0xff, packet += bytes([(self.trim_frequency >> 24) & 0xff,
0xff, 0xff,
(self.trim_frequency >> 16) & 0xff, (self.trim_frequency >> 16) & 0xff,
@ -1541,152 +1441,13 @@ class Stc15Protocol(Stc15AProtocol):
packet += self.build_options() packet += self.build_options()
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if len(response) < 2 or response[0] != 0x04 or response[1] != 0x54: if response[0] != 0x04 or response[1] != 0x54:
raise StcProtocolException("incorrect magic in option packet") raise StcProtocolException("incorrect magic in option packet")
print("done") print("done")
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
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"""
self.mcu_clock_hz, = struct.unpack(">I", packet[1:5])
# XXX: external clock not supported nor tested
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
# pre-calibrated trim adjust for 24 MHz, range 0x40
self.freq_count_24 = packet[4]
# wakeup timer factory value
self.wakeup_freq, = struct.unpack(">H", packet[23:25])
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 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
# XXX: challenges need work. the ranges and how they related to clock frequency
# is different. A clock divider is used for lower frequencies.
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", int(65535 - 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
@ -1704,9 +1465,8 @@ class StcUsb15Protocol(Stc15Protocol):
def dump_packet(self, data, request=0, value=0, index=0, receive=True): def dump_packet(self, data, request=0, value=0, index=0, receive=True):
if self.debug: if self.debug:
print("%s bRequest=%02X wValue=%04X wIndex=%04X data: %s" % print("%s bRequest=%02X wValue=%04X wIndex=%04X data: %s" % (("<-" if receive else "->"),
(("<-" if receive else "->"), request, value, index, request, value, index, Utils.hexstr(data, " ")), file=sys.stderr)
Utils.hexstr(data, " ")), file=sys.stderr)
def read_packet(self): def read_packet(self):
"""Read a packet from the MCU""" """Read a packet from the MCU"""
@ -1746,16 +1506,15 @@ class StcUsb15Protocol(Stc15Protocol):
self.dump_packet(chunks, request, value, index, receive=False) self.dump_packet(chunks, request, value, index, receive=False)
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):
"""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.
if not _usb_available: if _usb_available == False:
raise StcProtocolException( raise StcProtocolException("USB support not available. "
"USB support not available. " + + "pyusb is not installed or not working correctly.")
"pyusb is not installed or not working correctly.")
print("Waiting for MCU, please cycle power: ", end="") print("Waiting for MCU, please cycle power: ", end="")
sys.stdout.flush() sys.stdout.flush()
@ -1815,6 +1574,8 @@ class StcUsb15Protocol(Stc15Protocol):
def program_flash(self, data): def program_flash(self, data):
"""Program the MCU's flash memory.""" """Program the MCU's flash memory."""
print("Writing %d bytes: " % len(data), end="")
sys.stdout.flush()
for i in range(0, len(data), self.PROGRAM_BLOCKSIZE): for i in range(0, len(data), self.PROGRAM_BLOCKSIZE):
packet = data[i:i+self.PROGRAM_BLOCKSIZE] packet = data[i:i+self.PROGRAM_BLOCKSIZE]
while len(packet) < self.PROGRAM_BLOCKSIZE: packet += b"\x00" while len(packet) < self.PROGRAM_BLOCKSIZE: packet += b"\x00"
@ -1824,8 +1585,9 @@ class StcUsb15Protocol(Stc15Protocol):
response = self.read_packet() response = self.read_packet()
if response[0] != 0x02 or response[1] != 0x54: if response[0] != 0x02 or response[1] != 0x54:
raise StcProtocolException("incorrect magic in write packet") raise StcProtocolException("incorrect magic in write packet")
self.progress_cb(i, self.PROGRAM_BLOCKSIZE, len(data)) print(".", end="")
self.progress_cb(len(data), self.PROGRAM_BLOCKSIZE, len(data)) sys.stdout.flush()
print(" done")
def program_options(self): def program_options(self):
print("Setting options: ", end="") print("Setting options: ", end="")

View File

@ -19,38 +19,31 @@
# SOFTWARE. # SOFTWARE.
# #
import argparse
import serial import serial
import argparse
class Utils: class Utils:
"""Common utility functions"""
@classmethod @classmethod
def to_bool(cls, val): def to_bool(self, val):
"""make sensible boolean from string or other type value""" """make sensible boolean from string or other type value"""
if not val: if isinstance(val, bool): return val
return False if isinstance(val, int): return bool(val)
if isinstance(val, bool): if len(val) == 0: return False
return val
elif isinstance(val, int):
return bool(val)
return True if val[0].lower() == "t" or val[0] == "1" else False return True if val[0].lower() == "t" or val[0] == "1" else False
@classmethod @classmethod
def to_int(cls, val): def to_int(self, val):
"""make int from any value, nice error message if not possible""" """make int from any value, nice error message if not possible"""
try: try: return int(val, 0)
return int(val, 0) except: raise ValueError("invalid integer")
except (TypeError, ValueError):
raise ValueError("invalid integer")
@classmethod @classmethod
def hexstr(cls, bytestr, sep=""): def hexstr(self, bytestr, sep=""):
"""make formatted hex string output from byte sequence""" """make formatted hex string output from byte sequence"""
return sep.join(["%02X" % x for x in bytes(bytestr)]) return sep.join(["%02X" % x for x in bytestr])
class BaudType: class BaudType:
@ -62,5 +55,5 @@ class BaudType:
raise argparse.ArgumentTypeError("illegal baudrate") raise argparse.ArgumentTypeError("illegal baudrate")
return baud return baud
def __repr__(self): def __repr__(self): return "baudrate"
return "baudrate"

View File

View File

@ -1,19 +0,0 @@
name: IAP15F2K61S2 programming test
protocol: stc15
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x50, 0x87, 0xD3, 0x75, 0x9C, 0xF5, 0x3B, 0x17, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0x81, 0x00, 0x00, 0x71, 0x53, 0x00, 0xF4, 0x49, 0x04, 0x06, 0x58, 0x9C, 0x02, 0x0E, 0x14, 0x17, 0x19, 0x19, 0x00, 0xF4, 0xF4, 0x04, 0xD2]
- [0x00, 0x0B, 0x03, 0x37, 0x04, 0x9A, 0x06, 0x02, 0x06, 0x6B, 0x09, 0x27, 0x0B, 0xE8, 0x0D, 0x0A, 0x12, 0x5A, 0x17, 0x9B, 0x14, 0x8F, 0x1C, 0x96, 0x00, 0x00]
- [0x00, 0x0C, 0x09, 0x04, 0x09, 0x09, 0x09, 0x0E, 0x09, 0x0E, 0x09, 0x18, 0x09, 0x1D, 0x12, 0x00, 0x12, 0x0F, 0x12, 0x19, 0x12, 0x23, 0x12, 0x2D, 0x12, 0x37]
- [0x01]
- [0x05]
- [0x03, 0x0D, 0x00, 0x00, 0x21, 0x02, 0x26, 0x32]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x04, 0x54]

View File

@ -1,19 +0,0 @@
name: STC12C2052AD programming test
protocol: stc12a
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x00, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEB, 0x04, 0xEB, 0x58, 0x44, 0x00, 0xF2, 0x12, 0x83, 0xFD, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFD, 0xF7, 0xF7, 0xFF]
- [0x8F, 0xC0, 0x79, 0x3F, 0xFE, 0x28, 0x85]
- [0x8E, 0xC0, 0x79, 0x3F, 0xFE, 0x28]
- [0x80]
- [0x80]
- [0x80]
- [0x80]
- [0x80]
- [0x80, 0x66]
- [0x80, 0x80]
- [0x80, 0x80]
- [0x80, 0x80]
- [0x80, 0xEE]
- [0x10, 0xC0, 0x16, 0xF7, 0xFF, 0xBF, 0x03, 0xFF, 0x58, 0x44, 0xFD, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFD, 0xF7, 0xF7, 0xFF]
- [0x80]

View File

@ -1,15 +0,0 @@
name: STC12C5A60S2 programming test
protocol: stc12
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x50, 0x04, 0xBD, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBD, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBC, 0x62, 0x49, 0x00, 0xD1, 0x7E, 0x8C, 0xFF, 0x7F, 0xF7, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0xB0, 0x02, 0x2E, 0x6B, 0x00, 0xCD, 0x80, 0x00, 0x00]
- [0x8F]
- [0x8F, 0xC0, 0x7E, 0x3F, 0xFE, 0xA0, 0x83, 0x04]
- [0x84, 0xC0, 0x7E, 0x3F, 0xFE, 0xA0, 0x04]
- [0x00]
- [0x00, 0x03]
- [0x00, 0x00]
- [0x00, 0x00]
- [0x00, 0x00]
- [0x8D]
- [0x50, 0xFF, 0x7F, 0xF7, 0xFF, 0xFF, 0x03, 0xFF, 0x62, 0x49, 0xFF, 0x7F, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x03, 0x00, 0xB0, 0x02, 0x2E, 0x6B, 0x00, 0xCD, 0x80, 0x00, 0x00]

View File

@ -1,20 +0,0 @@
name: STC15F104E programming test
protocol: stc15a
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x50, 0x02, 0xB0, 0x02, 0xB0, 0x02, 0xAF, 0x02, 0xB0, 0x02, 0xE6, 0x02, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x67, 0x51, 0xFF, 0xF2, 0x94, 0x8C, 0xEF, 0x3B, 0xF5, 0x58, 0x34, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x50, 0x0C, 0x94, 0x21, 0xFF, 0x29]
- [0x8f]
- [0x65, 0x58, 0x50, 0x0C, 0x95, 0x21, 0xFF, 0x2B, 0xFF, 0xFF, 0x06, 0x06, 0x58, 0x00, 0x02, 0x00, 0x58, 0x80, 0x02, 0x00, 0x58, 0x80, 0x02, 0x00, 0x58, 0xFF, 0x02, 0x00, 0x58, 0x00, 0x02, 0x00, 0x58, 0x80, 0x02, 0x00]
- [0x65, 0x58, 0x50, 0x0C, 0x95, 0x21, 0xFF, 0x2B, 0xFF, 0xFF, 0x06, 0x0B, 0x58, 0x24, 0x02, 0x00, 0x58, 0x25, 0x02, 0x00, 0x58, 0x26, 0x02, 0x00, 0x58, 0x27, 0x02, 0x00, 0x58, 0x28, 0x02, 0x00, 0x58, 0x29, 0x02, 0x00, 0x58, 0x2A, 0x02, 0x00, 0x58, 0x2B, 0x02, 0x00, 0x58, 0x2C, 0x02, 0x00, 0x58, 0x2D, 0x02, 0x00, 0x58, 0x2E, 0x02, 0x00]
- [0x01]
- [0x05]
- [0x03, 0x0C, 0x00, 0x00, 0x17, 0x01, 0xA0, 0xE0]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x04, 0x54]

View File

@ -1,19 +0,0 @@
name: STC15L104W programming test
protocol: stc15
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x50, 0x66, 0x3C, 0x93, 0xBA, 0xF7, 0xBB, 0x9F, 0x00, 0x5B, 0x68, 0x00, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x71, 0x51, 0x03, 0xF2, 0xD4, 0x04, 0x06, 0x58, 0xBA, 0x02, 0x2A, 0x31, 0x32, 0x38, 0x30, 0x80, 0x14, 0x10, 0x04, 0xD9]
- [0x00, 0x0B, 0x03, 0x0A, 0x04, 0x4F, 0x05, 0x9E, 0x06, 0x20, 0x08, 0xB9, 0x0B, 0x5C, 0x0C, 0x6A, 0x11, 0x7E, 0x16, 0x79, 0x13, 0x77, 0x1A, 0xB1, 0x00, 0x00]
- [0x00, 0x0C, 0x04, 0xD6, 0x04, 0xDB, 0x04, 0xE0, 0x04, 0xE0, 0x04, 0xE0, 0x04, 0xE5, 0x11, 0xE2, 0x11, 0xF1, 0x11, 0xFB, 0x12, 0x05, 0x12, 0x0A, 0x12, 0x19]
- [0x01]
- [0x05]
- [0x03, 0x0C, 0x00, 0x00, 0x17, 0x01, 0xA0, 0xE0]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x04, 0x54]

View File

@ -1,20 +0,0 @@
name: STC15W4K56S4 programming test
protocol: stc15
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x50, 0x8D, 0xFF, 0x73, 0x96, 0xF5, 0x7B, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x27, 0xED, 0x00, 0x00, 0x73, 0x54, 0x00, 0xF5, 0x28, 0x04, 0x06, 0x70, 0x96, 0x02, 0x15, 0x19, 0x1C, 0x1E, 0x23, 0x00, 0xEC, 0xE0, 0x04, 0xD7, 0xF8, 0x73, 0xBF, 0xFF, 0xFF, 0x15, 0x09, 0x25, 0x60]
- [0x00, 0x0B, 0x0D, 0x21, 0x12, 0xBC, 0x18, 0x3E, 0x1A, 0x05, 0x24, 0xFA, 0x2F, 0xB3, 0x34, 0xD1, 0x4A, 0x52, 0x5E, 0xC0, 0x52, 0xDB, 0x73, 0x1A, 0x00, 0x00]
- [0x00, 0x0C, 0x23, 0xBF, 0x23, 0xD3, 0x23, 0xE7, 0x23, 0xF6, 0x24, 0x0F, 0x24, 0x23, 0x47, 0x73, 0x47, 0xB9, 0x47, 0xE1, 0x48, 0x09, 0x48, 0x36, 0x48, 0x59]
- [0x01]
- [0x05]
- [0x03, 0xF5, 0x28, 0x00, 0xA5, 0x03, 0x27, 0x49]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x07, 0x54]
- [0x04, 0x54]

View File

@ -1,19 +0,0 @@
name: STC89C52RC programming test
protocol: stc89
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x00, 0x25, 0xE6, 0x25, 0xE6, 0x25, 0xE6, 0x25, 0xE6, 0x25, 0xE6, 0x25, 0xE6, 0x25, 0xE2, 0x25, 0xE6, 0x43, 0x43, 0xFC, 0xF0, 0x02, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- [0x8F, 0xFD, 0xF8, 0x02, 0x10, 0x28, 0x81]
- [0x8E, 0xFD, 0xF8, 0x02, 0x10, 0x28]
- [0x80]
- [0x80]
- [0x80]
- [0x80]
- [0x80]
- [0x80, 0x66]
- [0x80, 0x80]
- [0x80, 0x80]
- [0x80, 0x80]
- [0x8D, 0xFC, 0xFF, 0xF6, 0xFF]
- [0x10, 0xC0, 0x16, 0xF6, 0xFF, 0xF1, 0x03, 0xFF, 0x43, 0x43, 0xFC]
- [0x80]

View File

@ -1,119 +0,0 @@
#
# Copyright (c) 2017 Grigori Goronzy <greg@chown.ath.cx>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
"""Tests with fuzzing of input data"""
import random
import sys
import unittest
from unittest.mock import patch
import yaml
import stcgal.frontend
import stcgal.protocols
from tests.test_program import get_default_opts, convert_to_bytes
class ByteArrayFuzzer:
"""Fuzzer for byte arrays"""
def __init__(self):
self.rng = random.Random()
self.cut_propability = 0.01 # probability for cutting off an array early
self.cut_min = 0 # minimum cut amount
self.cut_max = sys.maxsize # maximum cut amount
self.bitflip_probability = 0.0001 # probability for flipping a bit
self.randomize_probability = 0.001 # probability for randomizing a char
def fuzz(self, inp):
"""Fuzz an array of bytes according to predefined settings"""
arr = bytearray(inp)
arr = self.cut_off(arr)
self.randomize(arr)
return bytes(arr)
def randomize(self, arr):
"""Randomize array contents with bitflips and random bytes"""
for i, _ in enumerate(arr):
for j in range(8):
if self.rng.random() < self.bitflip_probability:
arr[i] ^= (1 << j)
if self.rng.random() < self.randomize_probability:
arr[i] = self.rng.getrandbits(8)
def cut_off(self, arr):
"""Cut off data from end of array"""
if self.rng.random() < self.cut_propability:
cut_limit = min(len(arr), self.cut_max)
cut_len = self.rng.randrange(self.cut_min, cut_limit)
arr = arr[0:len(arr) - cut_len]
return arr
class TestProgramFuzzed(unittest.TestCase):
"""Special programming cycle tests that use a fuzzing approach"""
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
@patch("sys.stderr")
def test_program_fuzz(self, err, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test programming cycles with fuzzing enabled"""
yml = [
"./tests/iap15f2k61s2.yml",
"./tests/stc12c2052ad.yml",
"./tests/stc15w4k56s4.yml",
"./tests/stc12c5a60s2.yml",
"./tests/stc89c52rc.yml",
"./tests/stc15l104w.yml",
"./tests/stc15f104e.yml",
]
fuzzer = ByteArrayFuzzer()
fuzzer.cut_propability = 0.01
fuzzer.bitflip_probability = 0.005
fuzzer.rng = random.Random(1)
for y in yml:
with self.subTest(msg="trace {}".format(y)):
self.single_fuzz(y, 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"""
with open(yml) as test_file:
test_data = yaml.load(test_file.read())
for _ in range(1000):
with self.subTest():
opts = get_default_opts()
opts.protocol = test_data["protocol"]
opts.code_image.read.return_value = bytes(test_data["code_data"])
serial_mock.return_value.inWaiting.return_value = 1
fuzzed_responses = []
for arr in convert_to_bytes(test_data["responses"]):
fuzzed_responses.append(fuzzer.fuzz(arr))
read_mock.side_effect = fuzzed_responses
gal = stcgal.frontend.StcGal(opts)
self.assertGreaterEqual(gal.run(), 0)
err.reset_mock()
out.reset_mock()
sleep_mock.reset_mock()
serial_mock.reset_mock()
write_mock.reset_mock()
read_mock.reset_mock()

View File

@ -1,135 +0,0 @@
#
# Copyright (c) 2017 Grigori Goronzy <greg@chown.ath.cx>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
"""Tests that simulate a whole programming cycle"""
import unittest
from unittest.mock import patch
import yaml
import stcgal.frontend
import stcgal.protocols
def convert_to_bytes(list_of_lists):
"""Convert lists of integer lists to list of byte lists"""
return [bytes(x) for x in list_of_lists]
def get_default_opts():
"""Get a default preconfigured option object"""
opts = unittest.mock.MagicMock()
opts.protocol = "stc89"
opts.autoreset = False
opts.port = ""
opts.baud = 19200
opts.handshake = 9600
opts.trim = 22118
opts.eeprom_image = None
opts.debug = False
opts.code_image.name = "test.bin"
opts.code_image.read.return_value = b"123456789"
return opts
class ProgramTests(unittest.TestCase):
"""Test MCU programming cycles for different families, based on traces"""
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc89(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC89 protocol"""
self._program_yml("./tests/stc89c52rc.yml", serial_mock, read_mock)
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc12(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC12 protocol"""
self._program_yml("./tests/stc12c5a60s2.yml", serial_mock, read_mock)
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc12a(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC12A protocol"""
self._program_yml("./tests/stc12c2052ad.yml", serial_mock, read_mock)
def test_program_stc12b(self):
"""Test a programming cycle with STC12B protocol"""
self.skipTest("trace missing")
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc15f2(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC15 protocol, F2 series"""
self._program_yml("./tests/iap15f2k61s2.yml", serial_mock, read_mock)
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc15w4(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC15 protocol, W4 series"""
self._program_yml("./tests/stc15w4k56s4.yml", serial_mock, read_mock)
@unittest.skip("trace is broken")
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc15a(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC15A protocol"""
self._program_yml("./tests/stc15f104e.yml", serial_mock, read_mock)
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc15l1(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC15 protocol, L1 series"""
self._program_yml("./tests/stc15l104w.yml", serial_mock, read_mock)
def test_program_stc15w4_usb(self):
"""Test a programming cycle with STC15W4 USB protocol"""
self.skipTest("USB not supported yet, trace missing")
def _program_yml(self, yml, serial_mock, read_mock):
"""Program MCU with data from YAML file"""
with open(yml) as test_file:
test_data = yaml.load(test_file.read())
opts = get_default_opts()
opts.protocol = test_data["protocol"]
opts.code_image.read.return_value = bytes(test_data["code_data"])
serial_mock.return_value.inWaiting.return_value = 1
read_mock.side_effect = convert_to_bytes(test_data["responses"])
gal = stcgal.frontend.StcGal(opts)
self.assertEqual(gal.run(), 0)

View File

@ -1,76 +0,0 @@
#
# Copyright (c) 2017 Grigori Goronzy <greg@chown.ath.cx>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
"""Tests for utility functions and other misc parts"""
import argparse
import unittest
from stcgal.utils import Utils, BaudType
class TestUtils(unittest.TestCase):
"""Test for utility functions in the Utils class"""
def test_to_bool(self):
"""Test special utility function for bool conversion"""
self.assertTrue(Utils.to_bool(True))
self.assertTrue(Utils.to_bool("true"))
self.assertTrue(Utils.to_bool("True"))
self.assertTrue(Utils.to_bool("t"))
self.assertTrue(Utils.to_bool("T"))
self.assertTrue(Utils.to_bool(1))
self.assertTrue(Utils.to_bool(-1))
self.assertFalse(Utils.to_bool(0))
self.assertFalse(Utils.to_bool(None))
self.assertFalse(Utils.to_bool("false"))
self.assertFalse(Utils.to_bool("False"))
self.assertFalse(Utils.to_bool("f"))
self.assertFalse(Utils.to_bool("F"))
self.assertFalse(Utils.to_bool(""))
def test_to_int(self):
"""Test wrapped integer conversion"""
self.assertEqual(Utils.to_int("2"), 2)
self.assertEqual(Utils.to_int("0x10"), 16)
with self.assertRaises(ValueError):
Utils.to_int("a")
with self.assertRaises(ValueError):
Utils.to_int("")
with self.assertRaises(ValueError):
Utils.to_int(None)
def test_hexstr(self):
"""Test byte array formatter"""
self.assertEqual(Utils.hexstr([10]), "0A")
self.assertEqual(Utils.hexstr([1, 2, 3]), "010203")
with self.assertRaises(Exception):
Utils.hexstr([400, 500])
class TestBaudType(unittest.TestCase):
"""Test BaudType class"""
def test_create_baud_type(self):
"""Test creation of BaudType instances"""
baud_type = BaudType()
self.assertEqual(baud_type("2400"), 2400)
self.assertEqual(baud_type("115200"), 115200)
with self.assertRaises(argparse.ArgumentTypeError):
baud_type("2374882")