Compare commits
75 Commits
no_verific
...
v1.5
Author | SHA1 | Date | |
---|---|---|---|
e0e2ab5526 | |||
4cc0deb8e9 | |||
5ab2a73411 | |||
4a40d5613a | |||
83c0b47f62 | |||
b0e882ff32 | |||
ccd4b1e26b | |||
71d7257422 | |||
0ff7e16f38 | |||
aca713595b | |||
69b83f0ea1 | |||
170008971d | |||
cd229eab47 | |||
ce251f9d30 | |||
11d2ea22e6 | |||
c7c4937628 | |||
ac119e180e | |||
fcbc560ade | |||
7b4758499b | |||
fe60e647bf | |||
0ffcbd197b | |||
85e815366c | |||
8bc9d89257 | |||
b47092093e | |||
bc5e8ce6cb | |||
d9e71a8694 | |||
4dcde5cc49 | |||
3ec6f5b6bd | |||
eb6df3b645 | |||
eb8eecbc9b | |||
c71e455f16 | |||
a5e1cc26ee | |||
b77157bc40 | |||
092fbdc842 | |||
e0bda73fed | |||
57100062af | |||
030497beb0 | |||
fd923f3a92 | |||
b145fb364a | |||
a29c9bf42e | |||
1cde6da007 | |||
ca30a508aa | |||
b9208c4772 | |||
ad5a89297f | |||
0cb56f4919 | |||
f195258eb5 | |||
ff9530833d | |||
8b0fdcb42a | |||
ebcfeb467c | |||
d7e226df6b | |||
191a580469 | |||
c131a9d901 | |||
3f4263e8fe | |||
ba4faf9c43 | |||
f1bafb1e0d | |||
fdd6707d2d | |||
532363d97b | |||
5865b06f7f | |||
1b69257cd3 | |||
38ac5f0788 | |||
6dccf13fb6 | |||
0ca8b2ea2d | |||
53184b549e | |||
cf68e3c6dc | |||
5d10c06f1e | |||
7e84b8e0fb | |||
f34ba6644f | |||
f15b64f4f7 | |||
2e822375e0 | |||
7d6e8e9bfd | |||
506289b8ee | |||
f417b6eed5 | |||
86e289b65c | |||
53f9544281 | |||
65a7759647 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,7 +1,12 @@
|
|||||||
*~
|
*~
|
||||||
*.pyc
|
*.pyc
|
||||||
*.egg-info
|
*.egg-info
|
||||||
__pycache__
|
*.eggs/
|
||||||
|
*.pybuild/
|
||||||
|
__pycache__/
|
||||||
/build
|
/build
|
||||||
/dist
|
/dist
|
||||||
/deb_dist
|
/deb_dist
|
||||||
|
/debian/stcgal*
|
||||||
|
/debian/files
|
||||||
|
/.vscode
|
33
.travis.yml
Normal file
33
.travis.yml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
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 pyusb
|
||||||
|
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"
|
276
README.md
276
README.md
@ -1,8 +1,10 @@
|
|||||||
|
[](https://travis-ci.org/grigorig/stcgal)
|
||||||
|
|
||||||
stcgal - STC MCU ISP flash tool
|
stcgal - STC MCU ISP flash tool
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
stcgal is a command line flash programming tool for STC MCU Ltd. [1]
|
stcgal is a command line flash programming tool for [STC MCU Ltd](http://stcmcu.com/).
|
||||||
8051 compatible microcontrollers. The name was inspired by avrdude [2].
|
8051 compatible microcontrollers.
|
||||||
|
|
||||||
STC microcontrollers have an UART/USB based boot strap loader (BSL). It
|
STC microcontrollers have an UART/USB based boot strap loader (BSL). It
|
||||||
utilizes a packet-based protocol to flash the code memory and IAP
|
utilizes a packet-based protocol to flash the code memory and IAP
|
||||||
@ -15,281 +17,31 @@ stcgal is a full-featured Open Source replacement for STC's Windows
|
|||||||
software; it supports a wide range of MCUs, it is very portable and
|
software; it supports a wide range of MCUs, it is very portable and
|
||||||
suitable for automation.
|
suitable for automation.
|
||||||
|
|
||||||
[1] http://stcmcu.com/
|
|
||||||
[2] http://www.nongnu.org/avrdude/
|
|
||||||
|
|
||||||
Supported MCU models
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
stcgal should fully support STC 89/90/10/11/12/15 series MCUs.
|
|
||||||
|
|
||||||
So far, stcgal was tested with the following MCU models:
|
|
||||||
|
|
||||||
* STC89C52RC (BSL version: 4.3C)
|
|
||||||
* STC90C52RC (BSL version: 4.3C)
|
|
||||||
* STC89C54RD+ (BSL version: 4.3C)
|
|
||||||
* STC12C2052 (BSL version: 5.8D)
|
|
||||||
* STC12C2052AD (BSL version: 5.8D)
|
|
||||||
* STC12C5608AD (BSL version: 6.0G)
|
|
||||||
* STC12C5A16S2 (BSL version: 6.2I)
|
|
||||||
* STC12C5A60S2 (BSL version: 6.2I)
|
|
||||||
* STC11F02E (BSL version: 6.5K)
|
|
||||||
* STC10F04XE (BSL version: 6.5J)
|
|
||||||
* STC11F08XE (BSL version: 6.5M)
|
|
||||||
* STC12C5204AD (BSL version: 6.6H)
|
|
||||||
* STC15F104E (BSL version: 6.7Q)
|
|
||||||
* STC15F204EA (BSL version: 6.7R)
|
|
||||||
* STC15L104W (BSL version: 7.1.4Q)
|
|
||||||
* STC15F104W (BSL version: 7.1.4Q)
|
|
||||||
* IAP15F2K61S2 (BSL version: 7.1.4S)
|
|
||||||
* STC15L2K16S2 (BSL version: 7.2.4S)
|
|
||||||
* STC15W408AS (BSL version: 7.2.4T)
|
|
||||||
* STC15W4K56S4 (BSL version: 7.3.4T, UART and USB mode)
|
|
||||||
|
|
||||||
Compatibility reports, both negative and positive, are welcome.
|
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
* Support for STC 89/90/10/11/12/15/8 series
|
||||||
* UART and USB BSL support
|
* UART and USB BSL support
|
||||||
* Display part info
|
* Display part info
|
||||||
* Determine operating frequency
|
* Determine operating frequency
|
||||||
* 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)
|
* Read unique device ID (STC 10/11/12/15/8)
|
||||||
* Trim RC oscillator frequency (STC 15)
|
* Trim RC oscillator frequency (STC 15/8)
|
||||||
* Automatic power-cycling with DTR toggle
|
* Automatic power-cycling with DTR toggle or a custom shell command
|
||||||
* Automatic UART protocol detection
|
* Automatic UART protocol detection
|
||||||
|
|
||||||
Installation
|
Further information
|
||||||
------------
|
-------------------
|
||||||
|
|
||||||
stcgal requires Python 3.2 (or later) and pySerial. USB support is
|
[Installation](doc/INSTALL.md)
|
||||||
optional and requires pyusb 1.0.0b2 or later. You can run stcgal
|
|
||||||
directly with the included ```stcgal.py``` script. The recommended
|
|
||||||
method for permanent installation is to use Python's setuptools. Run
|
|
||||||
```./setup.py build``` to build and ```sudo ./setup.py install```
|
|
||||||
to install stcgal. A permanent installation provides the ```stcgal```
|
|
||||||
command.
|
|
||||||
|
|
||||||
Usage
|
[How to use stcgal](doc/USAGE.md)
|
||||||
-----
|
|
||||||
|
|
||||||
Call stcgal with ```-h``` for usage information.
|
[Frequently Asked Questions](doc/FAQ.md)
|
||||||
|
|
||||||
```
|
[List of tested MCU models](doc/MODELS.md)
|
||||||
usage: stcgal.py [-h] [-a] [-P {stc89,stc12a,stc12,stc15a,stc15,auto}]
|
|
||||||
[-p PORT] [-b BAUD] [-l HANDSHAKE] [-o OPTION] [-t TRIM] [-D]
|
|
||||||
[code_image] [eeprom_image]
|
|
||||||
|
|
||||||
stcgal 1.0 - an STC MCU ISP flash tool
|
|
||||||
(C) 2014-2015 Grigori Goronzy
|
|
||||||
https://github.com/grigorig/stcgal
|
|
||||||
|
|
||||||
positional arguments:
|
|
||||||
code_image code segment file to flash (BIN/HEX)
|
|
||||||
eeprom_image eeprom segment file to flash (BIN/HEX)
|
|
||||||
|
|
||||||
optional arguments:
|
|
||||||
-h, --help show this help message and exit
|
|
||||||
-a, --autoreset cycle power automatically by asserting DTR
|
|
||||||
-P {stc89,stc12a,stc12,stc15a,stc15,auto}, --protocol {stc89,stc12a,stc12,stc15a,stc15,auto}
|
|
||||||
protocol version
|
|
||||||
-p PORT, --port PORT serial port device
|
|
||||||
-b BAUD, --baud BAUD transfer baud rate (default: 19200)
|
|
||||||
-l HANDSHAKE, --handshake HANDSHAKE
|
|
||||||
handshake baud rate (default: 2400)
|
|
||||||
-o OPTION, --option OPTION
|
|
||||||
set option (can be used multiple times)
|
|
||||||
-t TRIM, --trim TRIM RC oscillator frequency in kHz (STC15 series only)
|
|
||||||
-D, --debug enable debug output
|
|
||||||
```
|
|
||||||
|
|
||||||
Most importantly, ```-p``` sets the serial port to be used for programming.
|
|
||||||
|
|
||||||
### Protocols
|
|
||||||
|
|
||||||
STC MCUs use a variety of related but incompatible protocols for the
|
|
||||||
BSL. The protocol can be specified with the ```-P``` flag. By default
|
|
||||||
UART protocol autodetection is used. The mapping between protocols
|
|
||||||
and MCU series is as follows:
|
|
||||||
|
|
||||||
* ```stc89``` STC89/90 series
|
|
||||||
* ```stc12a``` STC12x052 series and possibly others
|
|
||||||
* ```stc12b``` STC12x52 series, STC12x56 series and possibly others
|
|
||||||
* ```stc12``` Most STC10/11/12 series
|
|
||||||
* ```stc15a``` STC15x104E and STC15x204E(A) series
|
|
||||||
* ```stc15``` Most STC15 series
|
|
||||||
* ```usb15``` USB support on STC15W4 series
|
|
||||||
* ```auto``` Automatic detection of UART based protocols (default)
|
|
||||||
|
|
||||||
The text files in the doc/ subdirectory provide an overview over
|
|
||||||
the reverse engineered protocols used by the BSLs. For more details,
|
|
||||||
please read the source code.
|
|
||||||
|
|
||||||
### Getting MCU information
|
|
||||||
|
|
||||||
Call stcgal without any file to program. It will dump information
|
|
||||||
about the MCU, e.g.:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ./stcgal.py -P stc15
|
|
||||||
Waiting for MCU, please cycle power: done
|
|
||||||
Target model:
|
|
||||||
Name: IAP15F2K61S2
|
|
||||||
Magic: F449
|
|
||||||
Code flash: 61.0 KB
|
|
||||||
EEPROM flash: 0.0 KB
|
|
||||||
Target frequency: 10.046 MHz
|
|
||||||
Target BSL version: 7.1S
|
|
||||||
Target wakeup frequency: 34.771 KHz
|
|
||||||
Target options:
|
|
||||||
reset_pin_enabled=False
|
|
||||||
clock_source=internal
|
|
||||||
clock_gain=high
|
|
||||||
watchdog_por_enabled=False
|
|
||||||
watchdog_stop_idle=True
|
|
||||||
watchdog_prescale=256
|
|
||||||
low_voltage_reset=True
|
|
||||||
low_voltage_threshold=3
|
|
||||||
eeprom_lvd_inhibit=True
|
|
||||||
eeprom_erase_enabled=False
|
|
||||||
bsl_pindetect_enabled=False
|
|
||||||
por_reset_delay=long
|
|
||||||
rstout_por_state=high
|
|
||||||
uart2_passthrough=False
|
|
||||||
uart2_pin_mode=normal
|
|
||||||
Disconnected!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Program the flash memory
|
|
||||||
|
|
||||||
stcgal supports Intel HEX encoded files as well as binary files. Intel
|
|
||||||
HEX is autodetected by file extension (.hex, .ihx or .ihex).
|
|
||||||
|
|
||||||
Call stcgal just like before, but provide the path to the code image:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ./stcgal.py -P stc15 hello.hex
|
|
||||||
Waiting for MCU, please cycle power: done
|
|
||||||
Target model:
|
|
||||||
Name: IAP15F2K61S2
|
|
||||||
Magic: F449
|
|
||||||
Code flash: 61.0 KB
|
|
||||||
EEPROM flash: 0.0 KB
|
|
||||||
Target frequency: 10.046 MHz
|
|
||||||
Target BSL version: 7.1S
|
|
||||||
Target wakeup frequency: 34.771 KHz
|
|
||||||
Target options:
|
|
||||||
reset_pin_enabled=False
|
|
||||||
clock_source=internal
|
|
||||||
clock_gain=high
|
|
||||||
watchdog_por_enabled=False
|
|
||||||
watchdog_stop_idle=True
|
|
||||||
watchdog_prescale=256
|
|
||||||
low_voltage_reset=True
|
|
||||||
low_voltage_threshold=3
|
|
||||||
eeprom_lvd_inhibit=True
|
|
||||||
eeprom_erase_enabled=False
|
|
||||||
bsl_pindetect_enabled=False
|
|
||||||
por_reset_delay=long
|
|
||||||
rstout_por_state=high
|
|
||||||
uart2_passthrough=False
|
|
||||||
uart2_pin_mode=normal
|
|
||||||
Loading flash: 80 bytes (Intel HEX)
|
|
||||||
Trimming frequency: 10.046 MHz
|
|
||||||
Switching to 19200 baud: done
|
|
||||||
Erasing flash: done
|
|
||||||
Writing 256 bytes: .... done
|
|
||||||
Setting options: done
|
|
||||||
Target UID: 0D000021022632
|
|
||||||
Disconnected!
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also program the EEPROM part of the memory, if applicable. Add
|
|
||||||
the EEPROM image path to the commandline after the flash image path.
|
|
||||||
|
|
||||||
stcgal uses a conservative baud rate of 19200 bps by
|
|
||||||
default. Programming can be sped up by choosing a faster baud rate
|
|
||||||
with the flag ```-b```.
|
|
||||||
|
|
||||||
### Device options
|
|
||||||
|
|
||||||
stcgal dumps a number of target options. These can be modified as
|
|
||||||
well. Provide one (or more) ```-o``` flags followed by a key-value
|
|
||||||
pair on the commandline to adjust these settings. For instance, you can
|
|
||||||
enable the external crystal as clock source:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ./stcgal.py -P stc15 -o clock_source=external hello.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
Please note that device options can only be set when flash memory is
|
|
||||||
programmed!
|
|
||||||
|
|
||||||
#### Option keys
|
|
||||||
|
|
||||||
Not all parts support all options. The protocols or parts that support each option are listed in the description.
|
|
||||||
|
|
||||||
Option key | Possible values | Protocols/Models | Description
|
|
||||||
------------------------------|-------------------|---------------------|------------
|
|
||||||
```cpu_6t_enabled``` | true/false | STC89 only | 6T fast mode
|
|
||||||
```bsl_pindetect_enabled``` | true/false | All | BSL only enabled when P3.2/P3.3 or P1.0/P1.1 (depends on model) are low
|
|
||||||
```eeprom_erase_enabled``` | true/false | All | Erase EEPROM with next programming cycle
|
|
||||||
```clock_gain``` | low/high | All with XTAL pins | Clock gain for external crystal
|
|
||||||
```ale_enabled``` | true/false | STC89 only | ALE pin enabled if true, normal GPIO if false
|
|
||||||
```xram_enabled``` | true/false | STC89 only | Use internal XRAM (STC89 only)
|
|
||||||
```watchdog_por_enabled``` | true/false | All | Watchdog state after power-on reset (POR)
|
|
||||||
```low_voltage_reset``` | low/high | STC12A/STC12 | Low-voltage reset level (low: ~3.3V, high: ~3.7V)
|
|
||||||
```low_voltage_reset``` | true/false | STC12 | Enable RESET2 pin low voltage detect
|
|
||||||
```low_voltage_reset``` | true/false | STC15A | Enable low-voltage reset (brownout)
|
|
||||||
```clock_source``` | internal/external | STC12A+ with XTAL | Use internal (RC) or external (crystal) clock
|
|
||||||
```watchdog_stop_idle``` | true/false | STC12A+ | Stop watchdog in IDLE mode
|
|
||||||
```watchdog_prescale``` | 2,4,8,...,256 | STC12A+ | Watchdog timer prescaler, must be a power of two.
|
|
||||||
```reset_pin_enabled``` | true/false | STC12+ | RESET pin enabled if true, normal GPIO if false
|
|
||||||
```oscillator_stable_delay``` | 4096,...,32768 | STC11F series only | Crystal stabilization delay in clocks. Must be a power of two.
|
|
||||||
```por_reset_delay``` | short/long | STC12+ | Power-on reset (POR) delay
|
|
||||||
```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
|
|
||||||
```rstout_por_state``` | low/high | STC15+ | RSTOUT pin state after power-on reset
|
|
||||||
```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
|
|
||||||
```cpu_core_voltage``` | low/mid/high | STC15W+ | CPU core voltage (low: ~2.7V, mid: ~3.3V, high: ~3.6V)
|
|
||||||
|
|
||||||
### Frequency trimming
|
|
||||||
|
|
||||||
If the internal RC oscillator is used (```clock_source=internal```),
|
|
||||||
stcgal can execute a trim procedure to adjust it to a given value. This
|
|
||||||
is only supported by STC15 series. The trim values are stored with
|
|
||||||
device options. Use the ```-t``` flag to request trimming to a certain
|
|
||||||
value. Generally, frequencies between 4 and 35 MHz can be achieved. If
|
|
||||||
trimming fails, stcgal will abort.
|
|
||||||
|
|
||||||
### Automatic power-cycling
|
|
||||||
|
|
||||||
STC's microcontrollers require a power-on reset to invoke the bootloader,
|
|
||||||
which can be inconvenient. stcgal can use the DTR control signal of a
|
|
||||||
serial interface to automate this. The DTR signal is asserted for
|
|
||||||
approximately 500 ms when the autoreset feature is enabled with the
|
|
||||||
```-a``` flag. This requires external circuitry to actually switch the
|
|
||||||
power. In some cases, when the microcontroller draws only little power,
|
|
||||||
it is possible to directly supply power from the DTR signal, however.
|
|
||||||
|
|
||||||
### Exit status
|
|
||||||
|
|
||||||
The exit status is 0 if no error occured while executing stcgal. Any
|
|
||||||
error, such as a protocol error or I/O error, results in an exit
|
|
||||||
status of 1. If the the user aborted stcgal by pressing CTRL-C,
|
|
||||||
that results in an exit status of 2.
|
|
||||||
|
|
||||||
### USB support
|
|
||||||
|
|
||||||
STC15W4 series have an USB-based BSL that can be optionally
|
|
||||||
used. USB support in stcgal is experimental and might change in the
|
|
||||||
future. USB mode is enabled by using the ```usb15``` protocol. The
|
|
||||||
port (```-p```) flag as well as the baudrate options are ignored for
|
|
||||||
the USB protocol. RC frequency trimming is not supported.
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
10
TODO.md
10
TODO.md
@ -1,10 +0,0 @@
|
|||||||
TODO
|
|
||||||
====
|
|
||||||
|
|
||||||
- some more documentation / comments
|
|
||||||
- private member naming, other style issues
|
|
||||||
- sensible default serial port (e.g. on windows)
|
|
||||||
- automatic protocol detection
|
|
||||||
- verify stc12a/stc12 options (e.g. low_voltage_threshold)
|
|
||||||
- also verify low_voltage_threshold on stc15
|
|
||||||
- check if stc15 handles 64 byte blocks correctly
|
|
12
debian/changelog
vendored
12
debian/changelog
vendored
@ -1,3 +1,15 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
* Update to 1.3
|
||||||
|
|
||||||
|
-- Grigori Goronzy <greg@chown.ath.cx> Sat, 10 Jun 2017 10:01:07 +0200
|
||||||
|
|
||||||
stcgal (1.2) unstable; urgency=low
|
stcgal (1.2) unstable; urgency=low
|
||||||
|
|
||||||
* Update to 1.2
|
* Update to 1.2
|
||||||
|
4
debian/control
vendored
4
debian/control
vendored
@ -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
|
Build-Depends: debhelper (>= 9), python3, python3-setuptools, dh-python, python3-serial, python3-tqdm, python3-yaml
|
||||||
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
|
Depends: ${misc:Depends}, python3, python3-serial, python3-tqdm
|
||||||
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.
|
||||||
|
68
doc/FAQ.md
Normal file
68
doc/FAQ.md
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
Frequently Asked Questions
|
||||||
|
==========================
|
||||||
|
|
||||||
|
### Is it possible to read code (or EEPROM) memory out of a chip?
|
||||||
|
|
||||||
|
By design, this is not possible with STC's bootloader protocols. This is considered a security feature by STC. There is no known workaround at this time. See issue #7 for more details and discussion.
|
||||||
|
|
||||||
|
### Which serial interfaces have been tested with stcgal?
|
||||||
|
|
||||||
|
The following USB-based UART interface chips have been successfully tested with stcgal:
|
||||||
|
|
||||||
|
* FT232 family (OS: Linux, Windows)
|
||||||
|
* CH340/CH341 (OS: Windows, Linux requires Kernel 4.10)
|
||||||
|
* PL2303 (OS: Windows, Linux)
|
||||||
|
* CP2102 (OS: Windows, Linux, macOS)
|
||||||
|
|
||||||
|
Interfaces that are known to not work:
|
||||||
|
|
||||||
|
* Raspberry Pi Mini UART (lacks parity support, enable the PL011 UART instead)
|
||||||
|
|
||||||
|
In general, stcgal requires accurate baud rate timings and parity support.
|
||||||
|
|
||||||
|
### stcgal fails to recognize the MCU and is stuck at "Waiting for MCU"
|
||||||
|
|
||||||
|
There are a number of issues that can result in this symptom:
|
||||||
|
|
||||||
|
* Electrical issues and wrong connections. Make sure that RX/TX, GND and VCC are connected correctly. If you do not use the autoreset feature, also make sure to connect power only after stcgal starts, as the bootloader is only invoked on power-on reset.
|
||||||
|
* Parasitic powering through I/O pins. The MCU can be powered through I/O pins (such as RX/TX) even if VCC is not connected. In this case, the power-on reset logic does not work. See next question.
|
||||||
|
* Serial interface incompatibilities. Some USB-based UARTs have bad compatibility with STC MCUs for various reasons. You can try to lower the handshake baudrate from the standard 2400 baud to 1200 baud with the option `-l 1200`, which works around these issues in some cases.
|
||||||
|
|
||||||
|
### How can I avoid parasitic powering?
|
||||||
|
|
||||||
|
Various remedies are possible to avoid parasitic powering.
|
||||||
|
|
||||||
|
* You can try to connect a resistor (< 1k) between MCU VCC and GND to short-circuit injected power and hopefully drop the voltage below the brown-out value.
|
||||||
|
* Another option is to insert series resistor on I/O lines that might inject power. Try a value like 1k on the RX/TX lines, for instance.
|
||||||
|
* Yet another possibility is to switch GND instead of VCC. This should be a fairly reliable solution in most cases.
|
||||||
|
|
||||||
|
### RC frequency trimming fails
|
||||||
|
|
||||||
|
First, make sure that the frequency specified uses the correct unit. The frequency is specified in kHz and the safe range is approximately 5000 kHz - 30000 kHz. Furthermore, frequency trimming uses the UART clock as the clock reference, so UART incompatibilities or clock inaccuracies can also result in issues with frequency trimming. If possible, try another UART chip.
|
||||||
|
|
||||||
|
### Baud rate switching fails
|
||||||
|
|
||||||
|
This can especially happen at high programming baud rates, e.g. 115200 baud. Try a lower baudrate, or stick to the default of 19200 baud. Some USB UARTs are known to cause problems due to inaccurate timing as well, which can lead to various issues.
|
||||||
|
|
||||||
|
### How can I use the autoreset feature?
|
||||||
|
|
||||||
|
The standard autoreset feature works somewhat similarly to Arduino. DTR is an active low signal, and is asserted on startup of stcgal for 500 ms and then deasserted for the rest of the programming sequence. On a standard USB UART, this results in 500 ms low pulse, followed by a high phase. The stcgal author recommends the following circuit:
|
||||||
|
|
||||||
|
```
|
||||||
|
VCC --o o-- MCU GND
|
||||||
|
| |
|
||||||
|
.-. |
|
||||||
|
| | 1k |
|
||||||
|
| | |
|
||||||
|
'_' |
|
||||||
|
| |
|
||||||
|
| ||-+
|
||||||
|
DTR --o --||<- BS170/BSS138
|
||||||
|
||-| (N-CH MOSFET)
|
||||||
|
|
|
||||||
|
|
|
||||||
|
GND ---------o
|
||||||
|
```
|
||||||
|
|
||||||
|
This circuit uses an N-channel MOSFET as a low-side switch to switch the MCU's GND. VCC is directly connected. This avoids parasitic powering issues. The pull-up resistor ensures that the MCU is switched on when the DTR input is floating.
|
||||||
|
|
18
doc/INSTALL.md
Normal file
18
doc/INSTALL.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
stcgal requires Python 3.2 (or later) and pySerial. USB support is
|
||||||
|
optional and requires pyusb 1.0.0b2 or later. You can run stcgal
|
||||||
|
directly with the included ```stcgal.py``` script if the dependencies
|
||||||
|
are already installed.
|
||||||
|
|
||||||
|
There are several options for permanent installation:
|
||||||
|
|
||||||
|
* Use Python3 and ```pip```. Run
|
||||||
|
```pip3 install git+https://github.com/grigorig/stcgal.git```
|
||||||
|
to install the latest version of stcgal globally on your system.
|
||||||
|
This may require administrator/root permissions for write access
|
||||||
|
to system directories.
|
||||||
|
|
||||||
|
* Use setuptools. Run ```./setup.py build``` to build and
|
||||||
|
```sudo ./setup.py install``` to install stcgal.
|
32
doc/MODELS.md
Normal file
32
doc/MODELS.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
So far, stcgal was tested with the following MCU models:
|
||||||
|
|
||||||
|
* STC89C52RC (BSL version: 4.3C/6.6C)
|
||||||
|
* STC90C52RC (BSL version: 4.3C)
|
||||||
|
* STC89C54RD+ (BSL version: 4.3C)
|
||||||
|
* STC12C2052 (BSL version: 5.8D)
|
||||||
|
* STC12C2052AD (BSL version: 5.8D)
|
||||||
|
* STC12C5608AD (BSL version: 6.0G)
|
||||||
|
* STC12C5A16S2 (BSL version: 6.2I)
|
||||||
|
* STC12C5A60S2 (BSL version: 6.2I/7.1I)
|
||||||
|
* STC11F02E (BSL version: 6.5K)
|
||||||
|
* STC10F04XE (BSL version: 6.5J)
|
||||||
|
* STC11F08XE (BSL version: 6.5M)
|
||||||
|
* STC12C5204AD (BSL version: 6.6H)
|
||||||
|
* STC15F104E (BSL version: 6.7Q)
|
||||||
|
* STC15F204EA (BSL version: 6.7R)
|
||||||
|
* STC15L104W (BSL version: 7.1.4Q)
|
||||||
|
* STC15F104W (BSL version: 7.1.4Q)
|
||||||
|
* IAP15F2K61S2 (BSL version: 7.1.4S)
|
||||||
|
* STC15L2K16S2 (BSL version: 7.2.4S)
|
||||||
|
* IAP15L2K61S2 (BSL version: 7.2.5S)
|
||||||
|
* STC15W408AS (BSL version: 7.2.4T)
|
||||||
|
* STC15W4K56S4 (BSL version: 7.3.4T, UART and USB mode)
|
||||||
|
* STC8A8K64S4A12 (BSL version: 7.3.9U)
|
||||||
|
* STC8F2K08S2 (BSL version: 7.3.10U)
|
||||||
|
|
||||||
|
Compatibility reports, both negative and positive, are welcome.
|
18
doc/PyPI.md
Normal file
18
doc/PyPI.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
stcgal - STC MCU ISP flash tool
|
||||||
|
===============================
|
||||||
|
|
||||||
|
stcgal is a command line flash programming tool for [STC MCU Ltd](http://stcmcu.com/).
|
||||||
|
8051 compatible microcontrollers.
|
||||||
|
|
||||||
|
STC microcontrollers have an UART/USB based boot strap loader (BSL). It
|
||||||
|
utilizes a packet-based protocol to flash the code memory and IAP
|
||||||
|
memory over a serial link. This is referred to as in-system programming
|
||||||
|
(ISP). The BSL is also used to configure various (fuse-like) device
|
||||||
|
options. Unfortunately, this protocol is not publicly documented and
|
||||||
|
STC only provide a (crude) Windows GUI application for programming.
|
||||||
|
|
||||||
|
stcgal is a full-featured Open Source replacement for STC's Windows
|
||||||
|
software; it supports a wide range of MCUs, it is very portable and
|
||||||
|
suitable for automation.
|
||||||
|
|
||||||
|
[See the GitHub page for more information](https://github.com/grigorig/stcgal).
|
239
doc/USAGE.md
Normal file
239
doc/USAGE.md
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
Call stcgal with ```-h``` for usage information.
|
||||||
|
|
||||||
|
```
|
||||||
|
usage: stcgal.py [-h] [-a] [-P {stc89,stc12a,stc12,stc15a,stc15,auto}]
|
||||||
|
[-p PORT] [-b BAUD] [-l HANDSHAKE] [-o OPTION] [-t TRIM] [-D]
|
||||||
|
[code_image] [eeprom_image]
|
||||||
|
|
||||||
|
stcgal 1.0 - an STC MCU ISP flash tool
|
||||||
|
(C) 2014-2015 Grigori Goronzy
|
||||||
|
https://github.com/grigorig/stcgal
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
code_image code segment file to flash (BIN/HEX)
|
||||||
|
eeprom_image eeprom segment file to flash (BIN/HEX)
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
-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}
|
||||||
|
protocol version
|
||||||
|
-p PORT, --port PORT serial port device
|
||||||
|
-b BAUD, --baud BAUD transfer baud rate (default: 19200)
|
||||||
|
-l HANDSHAKE, --handshake HANDSHAKE
|
||||||
|
handshake baud rate (default: 2400)
|
||||||
|
-o OPTION, --option OPTION
|
||||||
|
set option (can be used multiple times)
|
||||||
|
-t TRIM, --trim TRIM RC oscillator frequency in kHz (STC15 series only)
|
||||||
|
-D, --debug enable debug output
|
||||||
|
```
|
||||||
|
|
||||||
|
Most importantly, ```-p``` sets the serial port to be used for programming.
|
||||||
|
|
||||||
|
### Protocols
|
||||||
|
|
||||||
|
STC MCUs use a variety of related but incompatible protocols for the
|
||||||
|
BSL. The protocol can be specified with the ```-P``` flag. By default
|
||||||
|
UART protocol autodetection is used. The mapping between protocols
|
||||||
|
and MCU series is as follows:
|
||||||
|
|
||||||
|
* ```stc89``` STC89/90 series
|
||||||
|
* ```stc12a``` STC12x052 series and possibly others
|
||||||
|
* ```stc12b``` STC12x52 series, STC12x56 series and possibly others
|
||||||
|
* ```stc12``` Most STC10/11/12 series
|
||||||
|
* ```stc15a``` STC15x104E and STC15x204E(A) series
|
||||||
|
* ```stc15``` Most STC15 series
|
||||||
|
* ```stc8``` STC8 series
|
||||||
|
* ```usb15``` USB support on STC15W4 series
|
||||||
|
* ```auto``` Automatic detection of UART based protocols (default)
|
||||||
|
|
||||||
|
The text files in the doc/ subdirectory provide an overview over
|
||||||
|
the reverse engineered protocols used by the BSLs. For more details,
|
||||||
|
please read the source code.
|
||||||
|
|
||||||
|
### Getting MCU information
|
||||||
|
|
||||||
|
Call stcgal without any file to program. It will dump information
|
||||||
|
about the MCU, e.g.:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./stcgal.py -P stc15
|
||||||
|
Waiting for MCU, please cycle power: done
|
||||||
|
Target model:
|
||||||
|
Name: IAP15F2K61S2
|
||||||
|
Magic: F449
|
||||||
|
Code flash: 61.0 KB
|
||||||
|
EEPROM flash: 0.0 KB
|
||||||
|
Target frequency: 10.046 MHz
|
||||||
|
Target BSL version: 7.1S
|
||||||
|
Target wakeup frequency: 34.771 KHz
|
||||||
|
Target options:
|
||||||
|
reset_pin_enabled=False
|
||||||
|
clock_source=internal
|
||||||
|
clock_gain=high
|
||||||
|
watchdog_por_enabled=False
|
||||||
|
watchdog_stop_idle=True
|
||||||
|
watchdog_prescale=256
|
||||||
|
low_voltage_reset=True
|
||||||
|
low_voltage_threshold=3
|
||||||
|
eeprom_lvd_inhibit=True
|
||||||
|
eeprom_erase_enabled=False
|
||||||
|
bsl_pindetect_enabled=False
|
||||||
|
por_reset_delay=long
|
||||||
|
rstout_por_state=high
|
||||||
|
uart2_passthrough=False
|
||||||
|
uart2_pin_mode=normal
|
||||||
|
Disconnected!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Program the flash memory
|
||||||
|
|
||||||
|
stcgal supports Intel HEX encoded files as well as binary files. Intel
|
||||||
|
HEX is autodetected by file extension (.hex, .ihx or .ihex).
|
||||||
|
|
||||||
|
Call stcgal just like before, but provide the path to the code image:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./stcgal.py -P stc15 hello.hex
|
||||||
|
Waiting for MCU, please cycle power: done
|
||||||
|
Target model:
|
||||||
|
Name: IAP15F2K61S2
|
||||||
|
Magic: F449
|
||||||
|
Code flash: 61.0 KB
|
||||||
|
EEPROM flash: 0.0 KB
|
||||||
|
Target frequency: 10.046 MHz
|
||||||
|
Target BSL version: 7.1S
|
||||||
|
Target wakeup frequency: 34.771 KHz
|
||||||
|
Target options:
|
||||||
|
reset_pin_enabled=False
|
||||||
|
clock_source=internal
|
||||||
|
clock_gain=high
|
||||||
|
watchdog_por_enabled=False
|
||||||
|
watchdog_stop_idle=True
|
||||||
|
watchdog_prescale=256
|
||||||
|
low_voltage_reset=True
|
||||||
|
low_voltage_threshold=3
|
||||||
|
eeprom_lvd_inhibit=True
|
||||||
|
eeprom_erase_enabled=False
|
||||||
|
bsl_pindetect_enabled=False
|
||||||
|
por_reset_delay=long
|
||||||
|
rstout_por_state=high
|
||||||
|
uart2_passthrough=False
|
||||||
|
uart2_pin_mode=normal
|
||||||
|
Loading flash: 80 bytes (Intel HEX)
|
||||||
|
Trimming frequency: 10.046 MHz
|
||||||
|
Switching to 19200 baud: done
|
||||||
|
Erasing flash: done
|
||||||
|
Writing 256 bytes: .... done
|
||||||
|
Setting options: done
|
||||||
|
Target UID: 0D000021022632
|
||||||
|
Disconnected!
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also program the EEPROM part of the memory, if applicable. Add
|
||||||
|
the EEPROM image path to the commandline after the flash image path.
|
||||||
|
|
||||||
|
stcgal uses a conservative baud rate of 19200 bps by
|
||||||
|
default. Programming can be sped up by choosing a faster baud rate
|
||||||
|
with the flag ```-b```.
|
||||||
|
|
||||||
|
### Device options
|
||||||
|
|
||||||
|
stcgal dumps a number of target options. These can be modified as
|
||||||
|
well. Provide one (or more) ```-o``` flags followed by a key-value
|
||||||
|
pair on the commandline to adjust these settings. For instance, you can
|
||||||
|
enable the external crystal as clock source:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./stcgal.py -P stc15 -o clock_source=external hello.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that device options can only be set when flash memory is
|
||||||
|
programmed!
|
||||||
|
|
||||||
|
#### Option keys
|
||||||
|
|
||||||
|
Not all parts support all options. The protocols or parts that support each option are listed in the description.
|
||||||
|
|
||||||
|
Option key | Possible values | Protocols/Models | Description
|
||||||
|
------------------------------|-------------------|---------------------|------------
|
||||||
|
```cpu_6t_enabled``` | true/false | STC89 only | 6T fast mode
|
||||||
|
```bsl_pindetect_enabled``` | true/false | All | BSL only enabled when P3.2/P3.3 or P1.0/P1.1 (depends on model) are low
|
||||||
|
```eeprom_erase_enabled``` | true/false | All | Erase EEPROM with next programming cycle
|
||||||
|
```clock_gain``` | low/high | All with XTAL pins | Clock gain for external crystal
|
||||||
|
```ale_enabled``` | true/false | STC89 only | ALE pin enabled if true, normal GPIO if false
|
||||||
|
```xram_enabled``` | true/false | STC89 only | Use internal XRAM (STC89 only)
|
||||||
|
```watchdog_por_enabled``` | true/false | All | Watchdog state after power-on reset (POR)
|
||||||
|
```low_voltage_reset``` | low/high | STC12A/STC12 | Low-voltage reset level (low: ~3.3V, high: ~3.7V)
|
||||||
|
```low_voltage_reset``` | true/false | STC12 | Enable RESET2 pin low voltage detect
|
||||||
|
```low_voltage_reset``` | true/false | STC15A | Enable low-voltage reset (brownout)
|
||||||
|
```clock_source``` | internal/external | STC12A+ with XTAL | Use internal (RC) or external (crystal) clock
|
||||||
|
```watchdog_stop_idle``` | true/false | STC12A+ | Stop watchdog in IDLE mode
|
||||||
|
```watchdog_prescale``` | 2,4,8,...,256 | STC12A+ | Watchdog timer prescaler, must be a power of two.
|
||||||
|
```reset_pin_enabled``` | true/false | STC12+ | RESET pin enabled if true, normal GPIO if false
|
||||||
|
```oscillator_stable_delay``` | 4096,...,32768 | STC11F series only | Crystal stabilization delay in clocks. Must be a power of two.
|
||||||
|
```por_reset_delay``` | short/long | STC12+ | Power-on reset (POR) delay
|
||||||
|
```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
|
||||||
|
```rstout_por_state``` | low/high | STC15+ | RSTOUT/RSTSV 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_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)
|
||||||
|
```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
|
||||||
|
|
||||||
|
If the internal RC oscillator is used (```clock_source=internal```),
|
||||||
|
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
|
||||||
|
with device options. Use the ```-t``` flag to request trimming to a certain
|
||||||
|
value. Generally, frequencies between 4 and 35 MHz can be achieved. If
|
||||||
|
trimming fails, stcgal will abort.
|
||||||
|
|
||||||
|
### Automatic power-cycling
|
||||||
|
|
||||||
|
STC's microcontrollers require a power-on reset to invoke the bootloader,
|
||||||
|
which can be inconvenient. stcgal can use the DTR control signal of a
|
||||||
|
serial interface to automate this. The DTR signal is asserted for
|
||||||
|
approximately 500 ms when the autoreset feature is enabled with the
|
||||||
|
```-a``` flag. This requires external circuitry to actually switch the
|
||||||
|
power. In some cases, when the microcontroller draws only little power,
|
||||||
|
it is possible to directly supply power from the DTR signal.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
The exit status is 0 if no error occured while executing stcgal. Any
|
||||||
|
error, such as a protocol error or I/O error, results in an exit
|
||||||
|
status of 1. If the the user aborted stcgal by pressing CTRL-C,
|
||||||
|
that results in an exit status of 2.
|
||||||
|
|
||||||
|
### USB support
|
||||||
|
|
||||||
|
STC15W4 series have an USB-based BSL that can be optionally
|
||||||
|
used. USB support in stcgal is experimental and might change in the
|
||||||
|
future. USB mode is enabled by using the ```usb15``` protocol. The
|
||||||
|
port (```-p```) flag as well as the baudrate options are ignored for
|
||||||
|
the USB protocol. RC frequency trimming is not supported.
|
@ -1,13 +1,13 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# This curious script dumps all model info from STC-ISP.
|
# This curious script dumps all model info from STC-ISP.
|
||||||
# Data is directly read from the binary.
|
# Data is directly read from the binary.
|
||||||
# Offsets are for stc-isp-15xx-v6.85K.exe, sha1sum aa66e4c1ab49de27369b83c954a7c202acce0950
|
# Offsets are for stc-isp-15xx-v6.86O.exe, sha1sum f70e317d758ef8c942613a8b0540147d7170589b
|
||||||
|
|
||||||
MCU_TABLE_OFFSET = 0x00064550
|
MCU_TABLE_OFFSET = 0x0006ac80
|
||||||
MCU_TABLE_SIZE = 941
|
MCU_TABLE_SIZE = 984
|
||||||
MCU_RECORD_SIZE = 32
|
MCU_RECORD_SIZE = 32
|
||||||
MCU_NAMES_OFFSET = 0x0007e80c
|
MCU_NAMES_OFFSET = 0x00087810
|
||||||
MCU_NAMES_PTR_OFFSET = 0x0047e80c
|
MCU_NAMES_PTR_OFFSET = 0x00487810
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
46
doc/reverse-engineering/stc15-usb-protocol.txt
Normal file
46
doc/reverse-engineering/stc15-usb-protocol.txt
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
STC15 series USB ISP protocol
|
||||||
|
=============================
|
||||||
|
|
||||||
|
General principle
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
- host does OUT and IN control transfers for write and read
|
||||||
|
- IN transfer with wLength = 132, wValue = 0, wIndex = 0, bRequest = 0 are used for all reads
|
||||||
|
- OUT transfers with with specific bRequest, wValue, wIndex are used for writes
|
||||||
|
|
||||||
|
|
||||||
|
Packet coding
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- packets from MCU
|
||||||
|
always start with 0x46 0xb9, similar to serial protocols
|
||||||
|
third byte is packet length, followed by data bytes
|
||||||
|
checksum at the end: 8 bit modular sum
|
||||||
|
|
||||||
|
- packets from host
|
||||||
|
no header bytes
|
||||||
|
bRequest sets packet type
|
||||||
|
wValue, wIndex interpretation according to packet type
|
||||||
|
8 bit modular checksum for every 7 bytes, interleaved
|
||||||
|
|
||||||
|
- packet types derived from the serial protocol
|
||||||
|
|
||||||
|
Specific packet information
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
- flash data
|
||||||
|
wIndex specifies write address
|
||||||
|
wValue is 0xa55a
|
||||||
|
bRequest is 0x22 for first packet, 0x02 for the following ones
|
||||||
|
unusually encoded: a total of 128 bytes per packet,
|
||||||
|
with every 7 byte checksummed in some way,
|
||||||
|
for a total of 18x7 byte segments and a final 2 byte segment
|
||||||
|
checksum: 8 bit modular sum
|
||||||
|
|
||||||
|
- option packet
|
||||||
|
generally same as with serial protocol, some header stuff omitted
|
||||||
|
wIndex is 0
|
||||||
|
wValue is 0xa55a
|
||||||
|
bRequest is 4
|
||||||
|
seems to use the same checksumming scheme as flash writes
|
||||||
|
|
71
doc/reverse-engineering/stc8-options.txt
Normal file
71
doc/reverse-engineering/stc8-options.txt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
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: ?
|
138
doc/reverse-engineering/stc8-protocol.txt
Normal file
138
doc/reverse-engineering/stc8-protocol.txt
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
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 ^^^^^ reference voltage
|
||||||
|
^^^^^^^^ mfg. date
|
||||||
|
|
||||||
|
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.
|
18
setup.py
18
setup.py
@ -24,11 +24,15 @@
|
|||||||
import stcgal
|
import stcgal
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
with open("doc/PyPI.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"]),
|
packages = find_packages(exclude=["doc", "tests"]),
|
||||||
install_requires = ["pyserial"],
|
data_files = [("doc", ["README.md", "doc/FAQ.md", "doc/MODELS.md", "doc/USAGE.md"])],
|
||||||
|
install_requires = ["pyserial>=3.0", "tqdm>=4.0.0"],
|
||||||
extras_require = {
|
extras_require = {
|
||||||
"usb": ["pyusb>=1.0.0"]
|
"usb": ["pyusb>=1.0.0"]
|
||||||
},
|
},
|
||||||
@ -38,13 +42,16 @@ 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",
|
||||||
license = "MIT License",
|
license = "MIT License",
|
||||||
platforms = "any",
|
platforms = "any",
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Environment :: Console",
|
"Environment :: Console",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
@ -52,7 +59,12 @@ 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"],
|
||||||
)
|
)
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = "1.2"
|
__version__ = "1.5"
|
||||||
|
27
stcgal/__main__.py
Executable file
27
stcgal/__main__.py
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2013-2015 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import stcgal.frontend
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(stcgal.frontend.cli())
|
@ -20,11 +20,22 @@
|
|||||||
# SOFTWARE.
|
# SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
import sys, os, time, struct
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
import stcgal
|
import stcgal
|
||||||
from stcgal.utils import Utils, BaudType
|
import serial
|
||||||
from stcgal.protocols import *
|
from stcgal.utils import BaudType
|
||||||
|
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:
|
||||||
@ -32,7 +43,10 @@ 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":
|
||||||
@ -47,21 +61,26 @@ 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 = StcBaseProtocol(opts.port, opts.handshake, opts.baud)
|
self.protocol = StcAutoProtocol(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):
|
||||||
for o in options:
|
"""Set options from command line to protocol handler."""
|
||||||
|
|
||||||
|
for opt in options:
|
||||||
try:
|
try:
|
||||||
kv = o.split("=", 1)
|
kv = opt.split("=", 1)
|
||||||
if len(kv) < 2: raise ValueError("incorrect format")
|
if len(kv) < 2:
|
||||||
|
raise ValueError("incorrect format")
|
||||||
self.protocol.set_option(kv[0], kv[1])
|
self.protocol.set_option(kv[0], kv[1])
|
||||||
except ValueError as e:
|
except ValueError as ex:
|
||||||
raise NameError("invalid option '%s' (%s)" % (kv[0], e))
|
raise NameError("invalid option '%s' (%s)" % (kv[0], ex))
|
||||||
|
|
||||||
def load_file_auto(self, fileobj):
|
def load_file_auto(self, fileobj):
|
||||||
"""Load file with Intel Hex autodetection."""
|
"""Load file with Intel Hex autodetection."""
|
||||||
@ -74,14 +93,16 @@ 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 e:
|
except ValueError as ex:
|
||||||
raise IOError("invalid Intel HEX file (%s)" %e)
|
raise IOError("invalid Intel HEX file (%s)" %ex)
|
||||||
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
|
||||||
|
|
||||||
@ -124,67 +145,67 @@ class StcGal:
|
|||||||
self.protocol.disconnect()
|
self.protocol.disconnect()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
"""Run programmer, main entry point."""
|
||||||
self.protocol.connect(autoreset=self.opts.autoreset)
|
|
||||||
|
|
||||||
if self.opts.protocol == "auto":
|
try:
|
||||||
|
self.protocol.connect(autoreset=self.opts.autoreset, resetcmd=self.opts.resetcmd)
|
||||||
|
if isinstance(self.protocol, StcAutoProtocol):
|
||||||
if not self.protocol.protocol_name:
|
if not self.protocol.protocol_name:
|
||||||
raise StcProtocolException("cannot detect protocol")
|
raise StcProtocolException("cannot detect protocol")
|
||||||
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.__init__(self.opts)
|
self.initialize_protocol(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 e:
|
except (StcFramingException, StcProtocolException) as ex:
|
||||||
sys.stdout.flush();
|
sys.stdout.flush()
|
||||||
print("Protocol error: %s" % e, file=sys.stderr)
|
print("Protocol error: %s" % ex, file=sys.stderr)
|
||||||
self.protocol.disconnect()
|
self.protocol.disconnect()
|
||||||
return 1
|
return 1
|
||||||
except serial.SerialException as e:
|
except serial.SerialException as ex:
|
||||||
sys.stdout.flush();
|
sys.stdout.flush()
|
||||||
print("Serial port error: %s" % e, file=sys.stderr)
|
print("Serial port error: %s" % ex, file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
except IOError as e:
|
except IOError as ex:
|
||||||
sys.stdout.flush();
|
sys.stdout.flush()
|
||||||
print("I/O error: %s" % e, file=sys.stderr)
|
print("I/O error: %s" % ex, 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
|
||||||
else:
|
self.protocol.disconnect()
|
||||||
self.protocol.disconnect()
|
return 0
|
||||||
return 0
|
except NameError as ex:
|
||||||
except NameError as e:
|
sys.stdout.flush()
|
||||||
sys.stdout.flush();
|
print("Option error: %s" % ex, file=sys.stderr)
|
||||||
print("Option error: %s" % e, file=sys.stderr)
|
|
||||||
self.protocol.disconnect()
|
self.protocol.disconnect()
|
||||||
return 1
|
return 1
|
||||||
except (StcFramingException, StcProtocolException) as e:
|
except (StcFramingException, StcProtocolException) as ex:
|
||||||
sys.stdout.flush();
|
sys.stdout.flush()
|
||||||
print("Protocol error: %s" % e, file=sys.stderr)
|
print("Protocol error: %s" % ex, 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 e:
|
except serial.SerialException as ex:
|
||||||
print("Serial port error: %s" % e, file=sys.stderr)
|
print("Serial port error: %s" % ex, file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
except IOError as e:
|
except IOError as ex:
|
||||||
sys.stdout.flush();
|
sys.stdout.flush()
|
||||||
print("I/O error: %s" % e, file=sys.stderr)
|
print("I/O error: %s" % ex, file=sys.stderr)
|
||||||
self.protocol.disconnect()
|
self.protocol.disconnect()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@ -192,11 +213,14 @@ 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 %s - an STC MCU ISP flash tool\n(C) 2014-2015 Grigori Goronzy\nhttps://github.com/grigorig/stcgal" %stcgal.__version__)
|
description="stcgal {} - an STC MCU ISP flash tool\n".format(stcgal.__version__) +
|
||||||
|
"(C) 2014-2018 Grigori Goronzy and others\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("-P", "--protocol", help="protocol version (default: auto)", choices=["stc89", "stc12a", "stc12b", "stc12", "stc15a", "stc15", "usb15", "auto"], default="auto")
|
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", "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)
|
||||||
|
326
stcgal/ihex.py
326
stcgal/ihex.py
@ -5,201 +5,213 @@
|
|||||||
import struct
|
import struct
|
||||||
import codecs
|
import codecs
|
||||||
|
|
||||||
class IHex(object):
|
|
||||||
@classmethod
|
|
||||||
def read(cls, lines):
|
|
||||||
ihex = cls()
|
|
||||||
|
|
||||||
segbase = 0
|
class IHex:
|
||||||
for line in lines:
|
"""Intel HEX parser and writer"""
|
||||||
line = line.strip()
|
|
||||||
if not line: continue
|
|
||||||
|
|
||||||
t, a, d = ihex.parse_line(line)
|
@classmethod
|
||||||
if t == 0x00:
|
def read(cls, lines):
|
||||||
ihex.insert_data(segbase + a, d)
|
"""Read Intel HEX data from string or lines"""
|
||||||
|
ihex = cls()
|
||||||
|
|
||||||
elif t == 0x01:
|
segbase = 0
|
||||||
break # Should we check for garbage after this?
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
elif t == 0x02:
|
t, a, d = ihex.parse_line(line)
|
||||||
ihex.set_mode(16)
|
if t == 0x00:
|
||||||
segbase = struct.unpack(">H", d[0:2])[0] << 4
|
ihex.insert_data(segbase + a, d)
|
||||||
|
|
||||||
elif t == 0x03:
|
elif t == 0x01:
|
||||||
ihex.set_mode(16)
|
break # Should we check for garbage after this?
|
||||||
|
|
||||||
cs, ip = struct.unpack(">2H", d[0:2])
|
elif t == 0x02:
|
||||||
ihex.set_start((cs, ip))
|
ihex.set_mode(16)
|
||||||
|
segbase = struct.unpack(">H", d[0:2])[0] << 4
|
||||||
|
|
||||||
elif t == 0x04:
|
elif t == 0x03:
|
||||||
ihex.set_mode(32)
|
ihex.set_mode(16)
|
||||||
segbase = struct.unpack(">H", d[0:2])[0] << 16
|
|
||||||
|
|
||||||
elif t == 0x05:
|
cs, ip = struct.unpack(">2H", d[0:2])
|
||||||
ihex.set_mode(32)
|
ihex.set_start((cs, ip))
|
||||||
ihex.set_start(struct.unpack(">I", d[0:4])[0])
|
|
||||||
|
|
||||||
else:
|
elif t == 0x04:
|
||||||
raise ValueError("Invalid type byte")
|
ihex.set_mode(32)
|
||||||
|
segbase = struct.unpack(">H", d[0:2])[0] << 16
|
||||||
|
|
||||||
return ihex
|
elif t == 0x05:
|
||||||
|
ihex.set_mode(32)
|
||||||
|
ihex.set_start(struct.unpack(">I", d[0:4])[0])
|
||||||
|
|
||||||
@classmethod
|
else:
|
||||||
def read_file(cls, fname):
|
raise ValueError("Invalid type byte")
|
||||||
f = open(fname, "rb")
|
|
||||||
ihex = cls.read(f)
|
|
||||||
f.close()
|
|
||||||
return ihex
|
|
||||||
|
|
||||||
def __init__(self):
|
return ihex
|
||||||
self.areas = {}
|
|
||||||
self.start = None
|
|
||||||
self.mode = 8
|
|
||||||
self.row_bytes = 16
|
|
||||||
|
|
||||||
def set_row_bytes(self, row_bytes):
|
@classmethod
|
||||||
"""Set output hex file row width (bytes represented per row)."""
|
def read_file(cls, fname):
|
||||||
if row_bytes < 1 or row_bytes > 0xff:
|
"""Read Intel HEX data from file"""
|
||||||
raise ValueError("Value out of range: (%r)" % row_bytes)
|
f = open(fname, "rb")
|
||||||
self.row_bytes = row_bytes
|
ihex = cls.read(f)
|
||||||
|
f.close()
|
||||||
def extract_data(self, start=None, end=None):
|
return ihex
|
||||||
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 set_mode(self, mode):
|
def __init__(self):
|
||||||
self.mode = mode
|
self.areas = {}
|
||||||
|
self.start = None
|
||||||
|
self.mode = 8
|
||||||
|
self.row_bytes = 16
|
||||||
|
|
||||||
def get_area(self, addr):
|
def set_row_bytes(self, row_bytes):
|
||||||
for start, data in self.areas.items():
|
"""Set output hex file row width (bytes represented per row)."""
|
||||||
end = start + len(data)
|
if row_bytes < 1 or row_bytes > 0xff:
|
||||||
if addr >= start and addr <= end:
|
raise ValueError("Value out of range: (%r)" % row_bytes)
|
||||||
return start
|
self.row_bytes = row_bytes
|
||||||
|
|
||||||
return None
|
def extract_data(self, start=None, end=None):
|
||||||
|
"""Extract binary data"""
|
||||||
|
if start is None:
|
||||||
|
start = 0
|
||||||
|
|
||||||
def insert_data(self, istart, idata):
|
if end is None:
|
||||||
iend = istart + len(idata)
|
result = bytearray()
|
||||||
|
|
||||||
area = self.get_area(istart)
|
for addr, data in self.areas.items():
|
||||||
if area is None:
|
if addr >= start:
|
||||||
self.areas[istart] = idata
|
if len(result) < (addr - start):
|
||||||
|
result[len(result):addr - start] = bytes(
|
||||||
|
addr - start - len(result))
|
||||||
|
result[addr - start:addr - start + len(data)] = data
|
||||||
|
|
||||||
else:
|
return bytes(result)
|
||||||
data = self.areas[area]
|
|
||||||
# istart - iend + len(idata) + len(data)
|
|
||||||
self.areas[area] = data[:istart-area] + idata + data[iend-area:]
|
|
||||||
|
|
||||||
def calc_checksum(self, bytes):
|
result = bytearray()
|
||||||
total = sum(bytes)
|
|
||||||
return (-total) & 0xFF
|
|
||||||
|
|
||||||
def parse_line(self, rawline):
|
for addr, data in self.areas.items():
|
||||||
if rawline[0:1] != b":":
|
if addr >= start and addr < end:
|
||||||
raise ValueError("Invalid line start character (%r)" % rawline[0])
|
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
|
||||||
|
|
||||||
try:
|
return bytes(result)
|
||||||
#line = rawline[1:].decode("hex")
|
|
||||||
line = codecs.decode(rawline[1:], "hex_codec")
|
|
||||||
except:
|
|
||||||
raise ValueError("Invalid hex data")
|
|
||||||
|
|
||||||
length, addr, type = struct.unpack(">BHB", line[:4])
|
def set_start(self, start=None):
|
||||||
|
self.start = start
|
||||||
|
|
||||||
dataend = length + 4
|
def set_mode(self, mode):
|
||||||
data = line[4:dataend]
|
self.mode = mode
|
||||||
|
|
||||||
#~ print line[dataend:dataend + 2], repr(line)
|
def get_area(self, addr):
|
||||||
cs1 = line[dataend]
|
for start, data in self.areas.items():
|
||||||
cs2 = self.calc_checksum(line[:dataend])
|
end = start + len(data)
|
||||||
|
if addr >= start and addr <= end:
|
||||||
|
return start
|
||||||
|
|
||||||
if cs1 != cs2:
|
return None
|
||||||
raise ValueError("Checksums do not match")
|
|
||||||
|
|
||||||
return (type, addr, data)
|
def insert_data(self, istart, idata):
|
||||||
|
iend = istart + len(idata)
|
||||||
|
|
||||||
def make_line(self, type, addr, data):
|
area = self.get_area(istart)
|
||||||
line = struct.pack(">BHB", len(data), addr, type)
|
if area is None:
|
||||||
line += data
|
self.areas[istart] = idata
|
||||||
line += chr(self.calc_checksum(line))
|
|
||||||
#~ return ":" + line.encode("hex")
|
|
||||||
return ":" + line.encode("hex").upper() + "\r\n"
|
|
||||||
|
|
||||||
def write(self):
|
else:
|
||||||
output = ""
|
data = self.areas[area]
|
||||||
|
# istart - iend + len(idata) + len(data)
|
||||||
for start, data in sorted(self.areas.items()):
|
self.areas[area] = data[
|
||||||
i = 0
|
:istart - area] + idata + data[iend - area:]
|
||||||
segbase = 0
|
|
||||||
|
|
||||||
while i < len(data):
|
def calc_checksum(self, data):
|
||||||
chunk = data[i:i + self.row_bytes]
|
total = sum(data)
|
||||||
|
return (-total) & 0xFF
|
||||||
|
|
||||||
addr = start
|
def parse_line(self, rawline):
|
||||||
newsegbase = segbase
|
if rawline[0:1] != b":":
|
||||||
|
raise ValueError("Invalid line start character (%r)" % rawline[0])
|
||||||
|
|
||||||
if self.mode == 8:
|
try:
|
||||||
addr = addr & 0xFFFF
|
line = codecs.decode(rawline[1:], "hex_codec")
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("Invalid hex data")
|
||||||
|
|
||||||
elif self.mode == 16:
|
length, addr, line_type = struct.unpack(">BHB", line[:4])
|
||||||
t = addr & 0xFFFF
|
|
||||||
newsegbase = (addr - t) >> 4
|
|
||||||
addr = t
|
|
||||||
|
|
||||||
if newsegbase != segbase:
|
dataend = length + 4
|
||||||
output += self.make_line(0x02, 0, struct.pack(">H", newsegbase))
|
data = line[4:dataend]
|
||||||
segbase = newsegbase
|
|
||||||
|
|
||||||
elif self.mode == 32:
|
cs1 = line[dataend]
|
||||||
newsegbase = addr >> 16
|
cs2 = self.calc_checksum(line[:dataend])
|
||||||
addr = addr & 0xFFFF
|
|
||||||
|
|
||||||
if newsegbase != segbase:
|
if cs1 != cs2:
|
||||||
output += self.make_line(0x04, 0, struct.pack(">H", newsegbase))
|
raise ValueError("Checksums do not match")
|
||||||
segbase = newsegbase
|
|
||||||
|
|
||||||
output += self.make_line(0x00, addr, chunk)
|
return (line_type, addr, data)
|
||||||
|
|
||||||
i += self.row_bytes
|
def make_line(self, line_type, addr, data):
|
||||||
start += self.row_bytes
|
line = struct.pack(">BHB", len(data), addr, line_type)
|
||||||
|
line += data
|
||||||
|
line += chr(self.calc_checksum(line))
|
||||||
|
return ":" + line.encode("hex").upper() + "\r\n"
|
||||||
|
|
||||||
if self.start is not None:
|
def write(self):
|
||||||
if self.mode == 16:
|
"""Write Intel HEX data to string"""
|
||||||
output += self.make_line(0x03, 0, struct.pack(">2H", self.start[0], self.start[1]))
|
output = ""
|
||||||
elif self.mode == 32:
|
|
||||||
output += self.make_line(0x05, 0, struct.pack(">I", self.start))
|
|
||||||
|
|
||||||
output += self.make_line(0x01, 0, "")
|
for start, data in sorted(self.areas.items()):
|
||||||
return output
|
i = 0
|
||||||
|
segbase = 0
|
||||||
|
|
||||||
def write_file(self, fname):
|
while i < len(data):
|
||||||
f = open(fname, "w")
|
chunk = data[i:i + self.row_bytes]
|
||||||
f.write(self.write())
|
|
||||||
f.close()
|
addr = start
|
||||||
|
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()
|
||||||
|
103
stcgal/models.py
103
stcgal/models.py
@ -32,14 +32,26 @@ class MCUModelDatabase:
|
|||||||
MCUModel = collections.namedtuple("MCUModel", ["name", "magic", "total", "code", "eeprom"])
|
MCUModel = collections.namedtuple("MCUModel", ["name", "magic", "total", "code", "eeprom"])
|
||||||
|
|
||||||
models = (
|
models = (
|
||||||
MCUModel(name='STC8F8K08S4A10', magic=0xf611, total=65536, code=8192, eeprom=57344),
|
MCUModel(name='STC8F1K02S2', magic=0xf661, total=20480, code=2048, eeprom=10240),
|
||||||
MCUModel(name='STC8F8K16S4A10', magic=0xf612, total=65536, code=16384, eeprom=49152),
|
MCUModel(name='STC8F1K04S2', magic=0xf662, total=20480, code=4096, eeprom=8192),
|
||||||
MCUModel(name='STC8F8K24S4A10', magic=0xf613, total=65536, code=24576, eeprom=40960),
|
MCUModel(name='STC8F1K06S2', magic=0xf663, total=20480, code=6144, eeprom=6144),
|
||||||
MCUModel(name='STC8F8K32S4A10', magic=0xf614, total=65536, code=32768, eeprom=32768),
|
MCUModel(name='STC8F1K08S2', magic=0xf664, total=20480, code=8192, eeprom=4096),
|
||||||
MCUModel(name='STC8F8K40S4A10', magic=0xf615, total=65536, code=40960, eeprom=24576),
|
MCUModel(name='STC8F1K10S2', magic=0xf665, total=20480, code=10240, eeprom=2048),
|
||||||
MCUModel(name='STC8F8K48S4A10', magic=0xf616, total=65536, code=49152, eeprom=16384),
|
MCUModel(name='STC8F1K12S2', magic=0xf666, total=20480, code=12288, eeprom=0),
|
||||||
MCUModel(name='STC8F8K56S4A10', magic=0xf617, total=65536, code=57344, eeprom=8192),
|
MCUModel(name='STC8F1K17S2', magic=0xf667, total=20480, code=17408, eeprom=0),
|
||||||
MCUModel(name='STC8F8K64S4A10', magic=0xf618, total=65536, code=65024, eeprom=512),
|
MCUModel(name='STC8F1K02', magic=0xf671, total=20480, code=2048, eeprom=10240),
|
||||||
|
MCUModel(name='STC8F1K04', magic=0xf672, total=20480, code=4096, eeprom=8192),
|
||||||
|
MCUModel(name='STC8F1K06', magic=0xf673, total=20480, code=6144, eeprom=6144),
|
||||||
|
MCUModel(name='STC8F1K08', magic=0xf674, total=20480, code=8192, eeprom=4096),
|
||||||
|
MCUModel(name='STC8F1K10', magic=0xf675, total=20480, code=10240, eeprom=2048),
|
||||||
|
MCUModel(name='STC8F1K12', magic=0xf676, total=20480, code=12288, eeprom=0),
|
||||||
|
MCUModel(name='STC8F1K17', magic=0xf677, total=20480, code=17408, eeprom=0),
|
||||||
|
MCUModel(name='STC15U4K16S4', magic=0xf580, total=65536, code=16384, eeprom=44032),
|
||||||
|
MCUModel(name='STC15U4K24S4', magic=0xf581, total=65536, code=24576, eeprom=35840),
|
||||||
|
MCUModel(name='STC15U4K32S4', magic=0xf582, total=65536, code=32768, eeprom=27648),
|
||||||
|
MCUModel(name='STC15U4K40S4', magic=0xf583, total=65536, code=40960, eeprom=19456),
|
||||||
|
MCUModel(name='STC15U4K48S4', magic=0xf584, total=65536, code=49152, eeprom=11264),
|
||||||
|
MCUModel(name='STC15U4K56S4', magic=0xf585, total=65536, code=57344, eeprom=3072),
|
||||||
MCUModel(name='STC8A8K08S4A12', magic=0xf621, total=65536, code=8192, eeprom=57344),
|
MCUModel(name='STC8A8K08S4A12', magic=0xf621, total=65536, code=8192, eeprom=57344),
|
||||||
MCUModel(name='STC8A8K16S4A12', magic=0xf622, total=65536, code=16384, eeprom=49152),
|
MCUModel(name='STC8A8K16S4A12', magic=0xf622, total=65536, code=16384, eeprom=49152),
|
||||||
MCUModel(name='STC8A8K24S4A12', magic=0xf623, total=65536, code=24576, eeprom=40960),
|
MCUModel(name='STC8A8K24S4A12', magic=0xf623, total=65536, code=24576, eeprom=40960),
|
||||||
@ -47,7 +59,26 @@ class MCUModelDatabase:
|
|||||||
MCUModel(name='STC8A8K40S4A12', magic=0xf625, total=65536, code=40960, eeprom=24576),
|
MCUModel(name='STC8A8K40S4A12', magic=0xf625, total=65536, code=40960, eeprom=24576),
|
||||||
MCUModel(name='STC8A8K48S4A12', magic=0xf626, total=65536, code=49152, eeprom=16384),
|
MCUModel(name='STC8A8K48S4A12', magic=0xf626, total=65536, code=49152, eeprom=16384),
|
||||||
MCUModel(name='STC8A8K56S4A12', magic=0xf627, total=65536, code=57344, eeprom=8192),
|
MCUModel(name='STC8A8K56S4A12', magic=0xf627, total=65536, code=57344, eeprom=8192),
|
||||||
|
MCUModel(name='STC8A8K60S4A12', magic=0xf629, total=65536, code=61440, eeprom=4096),
|
||||||
MCUModel(name='STC8A8K64S4A12', magic=0xf628, total=65536, code=65024, eeprom=512),
|
MCUModel(name='STC8A8K64S4A12', magic=0xf628, total=65536, code=65024, eeprom=512),
|
||||||
|
MCUModel(name='STC8A4K08S2A12', magic=0xf651, total=65536, code=8192, eeprom=57344),
|
||||||
|
MCUModel(name='STC8A4K16S2A12', magic=0xf652, total=65536, code=16384, eeprom=49152),
|
||||||
|
MCUModel(name='STC8A4K24S2A12', magic=0xf653, total=65536, code=24576, eeprom=40960),
|
||||||
|
MCUModel(name='STC8A4K32S2A12', magic=0xf654, total=65536, code=32768, eeprom=32768),
|
||||||
|
MCUModel(name='STC8A4K40S2A12', magic=0xf655, total=65536, code=40960, eeprom=24576),
|
||||||
|
MCUModel(name='STC8A4K48S2A12', magic=0xf656, total=65536, code=49152, eeprom=16384),
|
||||||
|
MCUModel(name='STC8A4K56S2A12', magic=0xf657, total=65536, code=57344, eeprom=8192),
|
||||||
|
MCUModel(name='STC8A4K60S2A12', magic=0xf659, total=65536, code=61440, eeprom=4096),
|
||||||
|
MCUModel(name='STC8A4K64S2A12', magic=0xf658, total=65536, code=65024, eeprom=512),
|
||||||
|
MCUModel(name='STC8F8K08S4A12', magic=0xf611, total=65536, code=8192, eeprom=57344),
|
||||||
|
MCUModel(name='STC8F8K16S4A12', magic=0xf612, total=65536, code=16384, eeprom=49152),
|
||||||
|
MCUModel(name='STC8F8K24S4A12', magic=0xf613, total=65536, code=24576, eeprom=40960),
|
||||||
|
MCUModel(name='STC8F8K32S4A12', magic=0xf614, total=65536, code=32768, eeprom=32768),
|
||||||
|
MCUModel(name='STC8F8K40S4A12', magic=0xf615, total=65536, code=40960, eeprom=24576),
|
||||||
|
MCUModel(name='STC8F8K48S4A12', magic=0xf616, total=65536, code=49152, eeprom=16384),
|
||||||
|
MCUModel(name='STC8F8K56S4A12', magic=0xf617, total=65536, code=57344, eeprom=8192),
|
||||||
|
MCUModel(name='STC8F8K60S4A12', magic=0xf619, total=65536, code=61440, eeprom=4096),
|
||||||
|
MCUModel(name='STC8F8K64S4A12', magic=0xf618, total=65536, code=65024, eeprom=512),
|
||||||
MCUModel(name='STC8F2K08S4', magic=0xf631, total=65536, code=8192, eeprom=57344),
|
MCUModel(name='STC8F2K08S4', magic=0xf631, total=65536, code=8192, eeprom=57344),
|
||||||
MCUModel(name='STC8F2K16S4', magic=0xf632, total=65536, code=16384, eeprom=49152),
|
MCUModel(name='STC8F2K16S4', magic=0xf632, total=65536, code=16384, eeprom=49152),
|
||||||
MCUModel(name='STC8F2K24S4', magic=0xf633, total=65536, code=24576, eeprom=40960),
|
MCUModel(name='STC8F2K24S4', magic=0xf633, total=65536, code=24576, eeprom=40960),
|
||||||
@ -55,7 +86,17 @@ class MCUModelDatabase:
|
|||||||
MCUModel(name='STC8F2K40S4', magic=0xf635, total=65536, code=40960, eeprom=24576),
|
MCUModel(name='STC8F2K40S4', magic=0xf635, total=65536, code=40960, eeprom=24576),
|
||||||
MCUModel(name='STC8F2K48S4', magic=0xf636, total=65536, code=49152, eeprom=16384),
|
MCUModel(name='STC8F2K48S4', magic=0xf636, total=65536, code=49152, eeprom=16384),
|
||||||
MCUModel(name='STC8F2K56S4', magic=0xf637, total=65536, code=57344, eeprom=8192),
|
MCUModel(name='STC8F2K56S4', magic=0xf637, total=65536, code=57344, eeprom=8192),
|
||||||
|
MCUModel(name='STC8F2K60S4', magic=0xf639, total=65536, code=61440, eeprom=4096),
|
||||||
MCUModel(name='STC8F2K64S4', magic=0xf638, total=65536, code=65024, eeprom=512),
|
MCUModel(name='STC8F2K64S4', magic=0xf638, total=65536, code=65024, eeprom=512),
|
||||||
|
MCUModel(name='STC8F2K08S2', magic=0xf641, total=65536, code=8192, eeprom=57344),
|
||||||
|
MCUModel(name='STC8F2K16S2', magic=0xf642, total=65536, code=16384, eeprom=49152),
|
||||||
|
MCUModel(name='STC8F2K24S2', magic=0xf643, total=65536, code=24576, eeprom=40960),
|
||||||
|
MCUModel(name='STC8F2K32S2', magic=0xf644, total=65536, code=32768, eeprom=32768),
|
||||||
|
MCUModel(name='STC8F2K40S2', magic=0xf645, total=65536, code=40960, eeprom=24576),
|
||||||
|
MCUModel(name='STC8F2K48S2', magic=0xf646, total=65536, code=49152, eeprom=16384),
|
||||||
|
MCUModel(name='STC8F2K56S2', magic=0xf647, total=65536, code=57344, eeprom=8192),
|
||||||
|
MCUModel(name='STC8F2K60S2', magic=0xf649, total=65536, code=61440, eeprom=4096),
|
||||||
|
MCUModel(name='STC8F2K64S2', magic=0xf648, total=65536, code=65024, eeprom=512),
|
||||||
MCUModel(name='STC15H4K08S4', magic=0xf601, total=65536, code=8192, eeprom=57344),
|
MCUModel(name='STC15H4K08S4', magic=0xf601, total=65536, code=8192, eeprom=57344),
|
||||||
MCUModel(name='STC15H4K16S4', magic=0xf602, total=65536, code=16384, eeprom=49152),
|
MCUModel(name='STC15H4K16S4', magic=0xf602, total=65536, code=16384, eeprom=49152),
|
||||||
MCUModel(name='STC15H4K24S4', magic=0xf603, total=65536, code=24576, eeprom=40960),
|
MCUModel(name='STC15H4K24S4', magic=0xf603, total=65536, code=24576, eeprom=40960),
|
||||||
@ -222,6 +263,7 @@ class MCUModelDatabase:
|
|||||||
MCUModel(name='STC15W412S', magic=0xf51c, total=16384, code=12288, eeprom=1024),
|
MCUModel(name='STC15W412S', magic=0xf51c, total=16384, code=12288, eeprom=1024),
|
||||||
MCUModel(name='IAP15W413S', magic=0xf55d, total=16384, code=13312, eeprom=0),
|
MCUModel(name='IAP15W413S', magic=0xf55d, total=16384, code=13312, eeprom=0),
|
||||||
MCUModel(name='IRC15W415S', magic=0xf55e, total=16384, code=15872, eeprom=0),
|
MCUModel(name='IRC15W415S', magic=0xf55e, total=16384, code=15872, eeprom=0),
|
||||||
|
MCUModel(name='JX15W415S', magic=0xf55f, total=16384, code=15872, eeprom=0),
|
||||||
MCUModel(name='STC15W401AS', magic=0xf52a, total=16384, code=1024, eeprom=12288),
|
MCUModel(name='STC15W401AS', magic=0xf52a, total=16384, code=1024, eeprom=12288),
|
||||||
MCUModel(name='STC15W402AS', magic=0xf52b, total=16384, code=2048, eeprom=11264),
|
MCUModel(name='STC15W402AS', magic=0xf52b, total=16384, code=2048, eeprom=11264),
|
||||||
MCUModel(name='STC15W404AS', magic=0xf51e, total=16384, code=4096, eeprom=9216),
|
MCUModel(name='STC15W404AS', magic=0xf51e, total=16384, code=4096, eeprom=9216),
|
||||||
@ -247,6 +289,7 @@ class MCUModelDatabase:
|
|||||||
MCUModel(name='STC15W4K48S4', magic=0xf527, total=65536, code=49152, eeprom=11264),
|
MCUModel(name='STC15W4K48S4', magic=0xf527, total=65536, code=49152, eeprom=11264),
|
||||||
MCUModel(name='STC15W4K56S4', magic=0xf528, total=65536, code=57344, eeprom=3072),
|
MCUModel(name='STC15W4K56S4', magic=0xf528, total=65536, code=57344, eeprom=3072),
|
||||||
MCUModel(name='IAP15W4K58S4', magic=0xf569, total=65536, code=59392, eeprom=0),
|
MCUModel(name='IAP15W4K58S4', magic=0xf569, total=65536, code=59392, eeprom=0),
|
||||||
|
MCUModel(name='IAP15W4K58S4-Stu', magic=0xf56d, total=65536, code=59392, eeprom=0),
|
||||||
MCUModel(name='IAP15W4K61S4', magic=0xf56a, total=65536, code=62464, eeprom=0),
|
MCUModel(name='IAP15W4K61S4', magic=0xf56a, total=65536, code=62464, eeprom=0),
|
||||||
MCUModel(name='IRC15W4K63S4', magic=0xf56b, total=65536, code=65024, eeprom=0),
|
MCUModel(name='IRC15W4K63S4', magic=0xf56b, total=65536, code=65024, eeprom=0),
|
||||||
MCUModel(name='U8W', magic=0xf56c, total=65536, code=62464, eeprom=0),
|
MCUModel(name='U8W', magic=0xf56c, total=65536, code=62464, eeprom=0),
|
||||||
@ -973,6 +1016,47 @@ 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.
|
||||||
|
# XXX: It's unknown whether these actually exist, they were removed in STC-ISP.
|
||||||
|
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
|
||||||
@ -988,6 +1072,3 @@ 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))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,15 +21,24 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
|
from abc import ABC
|
||||||
from stcgal.utils import Utils
|
from stcgal.utils import Utils
|
||||||
|
|
||||||
class BaseOption:
|
class BaseOption(ABC):
|
||||||
|
"""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))
|
||||||
@ -38,12 +47,14 @@ class BaseOption:
|
|||||||
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)
|
||||||
|
|
||||||
|
|
||||||
@ -51,6 +62,7 @@ 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),
|
||||||
@ -69,7 +81,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
|
||||||
|
|
||||||
@ -77,7 +89,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
|
||||||
|
|
||||||
@ -85,7 +97,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
|
||||||
|
|
||||||
@ -104,7 +116,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
|
||||||
|
|
||||||
@ -112,7 +124,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
|
||||||
|
|
||||||
@ -120,7 +132,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
|
||||||
|
|
||||||
@ -129,6 +141,7 @@ 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)
|
||||||
|
|
||||||
@ -150,7 +163,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(sources.keys()))
|
raise ValueError("must be one of %s" % list(lvds.keys()))
|
||||||
self.msr[3] &= 0xbf
|
self.msr[3] &= 0xbf
|
||||||
self.msr[3] |= lvds[val] << 6
|
self.msr[3] |= lvds[val] << 6
|
||||||
|
|
||||||
@ -169,7 +182,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
|
||||||
|
|
||||||
@ -177,7 +190,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
|
||||||
|
|
||||||
@ -196,7 +209,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
|
||||||
|
|
||||||
@ -204,7 +217,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
|
||||||
|
|
||||||
@ -213,6 +226,7 @@ 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)
|
||||||
|
|
||||||
@ -235,7 +249,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
|
||||||
|
|
||||||
@ -243,7 +257,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
|
||||||
|
|
||||||
@ -295,7 +309,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
|
||||||
|
|
||||||
@ -303,7 +317,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
|
||||||
|
|
||||||
@ -322,7 +336,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
|
||||||
|
|
||||||
@ -330,13 +344,14 @@ 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)
|
||||||
|
|
||||||
@ -359,7 +374,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
|
||||||
|
|
||||||
@ -367,7 +382,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
|
||||||
|
|
||||||
@ -375,7 +390,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
|
||||||
|
|
||||||
@ -394,7 +409,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
|
||||||
|
|
||||||
@ -402,7 +417,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
|
||||||
|
|
||||||
@ -420,7 +435,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
|
||||||
|
|
||||||
@ -428,13 +443,14 @@ 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)
|
||||||
|
|
||||||
@ -457,13 +473,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
|
||||||
|
|
||||||
@ -493,7 +509,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
|
||||||
|
|
||||||
@ -501,7 +517,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
|
||||||
|
|
||||||
@ -520,7 +536,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
|
||||||
|
|
||||||
@ -528,7 +544,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
|
||||||
|
|
||||||
@ -546,7 +562,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
|
||||||
|
|
||||||
@ -554,7 +570,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
|
||||||
|
|
||||||
@ -599,10 +615,177 @@ 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"
|
||||||
else: return "unknown"
|
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
|
@ -21,16 +21,28 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import serial
|
import serial
|
||||||
import sys, os, time, struct, re, errno
|
import sys
|
||||||
import argparse
|
import os
|
||||||
import collections
|
import time
|
||||||
|
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 *
|
from stcgal.options import Stc89Option
|
||||||
|
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, usb.util
|
import usb.core
|
||||||
|
import usb.util
|
||||||
_usb_available = True
|
_usb_available = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
_usb_available = False
|
_usb_available = False
|
||||||
@ -46,22 +58,23 @@ class StcProtocolException(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class StcBaseProtocol:
|
class StcBaseProtocol(ABC):
|
||||||
"""Basic functionality for STC BSL protocols"""
|
"""Basic functionality for STC BSL protocols"""
|
||||||
|
|
||||||
"""magic word that starts a packet"""
|
|
||||||
PACKET_START = bytes([0x46, 0xb9])
|
PACKET_START = bytes([0x46, 0xb9])
|
||||||
|
"""magic word that starts a packet"""
|
||||||
|
|
||||||
"""magic byte that ends a packet"""
|
|
||||||
PACKET_END = bytes([0x16])
|
PACKET_END = bytes([0x16])
|
||||||
|
"""magic byte that ends a packet"""
|
||||||
|
|
||||||
"""magic byte for packets received from MCU"""
|
|
||||||
PACKET_MCU = bytes([0x68])
|
PACKET_MCU = bytes([0x68])
|
||||||
|
"""magic byte for packets received from MCU"""
|
||||||
|
|
||||||
"""magic byte for packets sent by host"""
|
|
||||||
PACKET_HOST = bytes([0x6a])
|
PACKET_HOST = bytes([0x6a])
|
||||||
|
"""magic byte for packets sent by host"""
|
||||||
|
|
||||||
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
|
||||||
@ -77,6 +90,22 @@ class StcBaseProtocol:
|
|||||||
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:
|
||||||
@ -103,6 +132,10 @@ class StcBaseProtocol:
|
|||||||
|
|
||||||
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.
|
||||||
|
|
||||||
@ -142,7 +175,7 @@ class StcBaseProtocol:
|
|||||||
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)
|
||||||
|
|
||||||
@ -192,20 +225,6 @@ class StcBaseProtocol:
|
|||||||
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"""
|
||||||
|
|
||||||
@ -241,7 +260,22 @@ class StcBaseProtocol:
|
|||||||
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 connect(self, autoreset=False):
|
def reset_device(self, resetcmd=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.
|
||||||
@ -260,14 +294,7 @@ class StcBaseProtocol:
|
|||||||
self.ser.flushInput()
|
self.ser.flushInput()
|
||||||
|
|
||||||
if autoreset:
|
if autoreset:
|
||||||
print("Cycling power: ", end="")
|
self.reset_device(resetcmd)
|
||||||
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()
|
||||||
@ -279,6 +306,8 @@ class StcBaseProtocol:
|
|||||||
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")
|
||||||
|
|
||||||
@ -288,7 +317,21 @@ class StcBaseProtocol:
|
|||||||
|
|
||||||
self.initialize_model()
|
self.initialize_model()
|
||||||
|
|
||||||
def initialize(self, base_protocol = None):
|
@abstractmethod
|
||||||
|
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
|
||||||
@ -316,14 +359,48 @@ class StcBaseProtocol:
|
|||||||
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"""
|
||||||
|
|
||||||
"""block size for programming flash"""
|
|
||||||
PROGRAM_BLOCKSIZE = 128
|
PROGRAM_BLOCKSIZE = 128
|
||||||
|
"""block size for programming flash"""
|
||||||
|
|
||||||
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)
|
||||||
@ -342,7 +419,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, data):
|
def write_packet(self, packet_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.
|
||||||
@ -354,8 +431,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(data) + 5)
|
packet += struct.pack(">H", len(packet_data) + 5)
|
||||||
packet += data
|
packet += packet_data
|
||||||
|
|
||||||
# checksum and end code
|
# checksum and end code
|
||||||
packet += bytes([sum(packet[2:]) & 0xff])
|
packet += bytes([sum(packet[2:]) & 0xff])
|
||||||
@ -376,6 +453,9 @@ 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()
|
||||||
|
|
||||||
@ -408,21 +488,21 @@ class Stc89Protocol(StcBaseProtocol):
|
|||||||
|
|
||||||
return brt, brt_csum, iap_wait, delay
|
return brt, brt_csum, iap_wait, delay
|
||||||
|
|
||||||
def initialize_status(self, packet):
|
def initialize_status(self, status_packet):
|
||||||
"""Decode status packet and store basic MCU info"""
|
"""Decode status packet and store basic MCU info"""
|
||||||
|
|
||||||
self.cpu_6t = not bool(packet[19] & 1)
|
self.cpu_6t = not bool(status_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", packet[1+2*i:3+2*i])[0]
|
freq_counter += struct.unpack(">H", status_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", packet[17:19])
|
bl_version, bl_stepping = struct.unpack("BB", status_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
|
||||||
@ -465,7 +545,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 i in range(4):
|
for _ 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:
|
||||||
@ -473,7 +553,7 @@ class Stc89Protocol(StcBaseProtocol):
|
|||||||
|
|
||||||
print("done")
|
print("done")
|
||||||
|
|
||||||
def erase_flash(self, erase_size, flash_size):
|
def erase_flash(self, erase_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.
|
||||||
@ -497,8 +577,6 @@ 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)
|
||||||
@ -508,13 +586,12 @@ 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 response[0] != 0x80:
|
if len(response) < 1 or response[0] != 0x80:
|
||||||
raise StcProtocolException("incorrect magic in write packet")
|
raise StcProtocolException("incorrect magic in write packet")
|
||||||
elif response[1] != csum:
|
elif len(response) < 2 or response[1] != csum:
|
||||||
raise StcProtocolException("verification checksum mismatch")
|
raise StcProtocolException("verification checksum mismatch")
|
||||||
print(".", end="")
|
self.progress_cb(i, self.PROGRAM_BLOCKSIZE, len(data))
|
||||||
sys.stdout.flush()
|
self.progress_cb(len(data), self.PROGRAM_BLOCKSIZE, len(data))
|
||||||
print(" done")
|
|
||||||
|
|
||||||
def program_options(self):
|
def program_options(self):
|
||||||
"""Program option byte into flash"""
|
"""Program option byte into flash"""
|
||||||
@ -568,18 +645,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, packet):
|
def initialize_status(self, status_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", packet[1+2*i:3+2*i])[0]
|
freq_counter += struct.unpack(">H", status_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", packet[17:19])
|
bl_version, bl_stepping = struct.unpack("BB", status_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
|
||||||
|
|
||||||
@ -612,6 +689,9 @@ 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()
|
||||||
@ -653,7 +733,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 i in range(4):
|
for _ 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:
|
||||||
@ -710,14 +790,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"""
|
||||||
|
|
||||||
"""block size for programming flash"""
|
|
||||||
PROGRAM_BLOCKSIZE = 128
|
PROGRAM_BLOCKSIZE = 128
|
||||||
|
"""block size for programming flash"""
|
||||||
|
|
||||||
"""countdown value for flash erase"""
|
|
||||||
ERASE_COUNTDOWN = 0x0d
|
ERASE_COUNTDOWN = 0x0d
|
||||||
|
"""countdown value for flash erase"""
|
||||||
|
|
||||||
"""Parity for error correction was introduced with STC12"""
|
|
||||||
PARITY = serial.PARITY_EVEN
|
PARITY = serial.PARITY_EVEN
|
||||||
|
"""Parity for error correction was introduced with STC12"""
|
||||||
|
|
||||||
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)
|
||||||
@ -734,7 +814,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, data):
|
def write_packet(self, packet_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.
|
||||||
@ -746,8 +826,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(data) + 6)
|
packet += struct.pack(">H", len(packet_data) + 6)
|
||||||
packet += data
|
packet += 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)
|
||||||
@ -757,18 +837,18 @@ class Stc12BaseProtocol(StcBaseProtocol):
|
|||||||
self.ser.write(packet)
|
self.ser.write(packet)
|
||||||
self.ser.flush()
|
self.ser.flush()
|
||||||
|
|
||||||
def initialize_status(self, packet):
|
def initialize_status(self, status_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", packet[1+2*i:3+2*i])[0]
|
freq_counter += struct.unpack(">H", status_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", packet[17:19])
|
bl_version, bl_stepping = struct.unpack("BB", status_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
|
||||||
|
|
||||||
@ -801,6 +881,9 @@ 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()
|
||||||
@ -878,24 +961,18 @@ 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")
|
||||||
elif response[1] != csum:
|
self.progress_cb(i, self.PROGRAM_BLOCKSIZE, len(data))
|
||||||
raise StcProtocolException("verification checksum mismatch")
|
self.progress_cb(len(data), self.PROGRAM_BLOCKSIZE, len(data))
|
||||||
print(".", end="")
|
|
||||||
sys.stdout.flush()
|
|
||||||
print(" done")
|
|
||||||
|
|
||||||
print("Finishing write: ", end="")
|
print("Finishing write: ", end="")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
@ -938,6 +1015,9 @@ 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()
|
||||||
@ -956,20 +1036,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, packet):
|
def initialize_status(self, status_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", packet[1+2*i:3+2*i])[0]
|
freq_counter += struct.unpack(">H", status_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", packet[17:19])
|
bl_version, bl_stepping = struct.unpack("BB", status_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 = packet[51:58]
|
self.trim_data = status_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):
|
||||||
@ -1021,7 +1101,8 @@ class Stc15AProtocol(Stc12Protocol):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
user_speed = self.trim_frequency
|
user_speed = self.trim_frequency
|
||||||
if user_speed <= 0: user_speed = self.mcu_clock_hz
|
if user_speed <= 0:
|
||||||
|
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))
|
||||||
@ -1049,15 +1130,19 @@ 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 response[0] != 0x65:
|
if len(response) < 36 or 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])
|
||||||
@ -1076,10 +1161,14 @@ 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])
|
||||||
@ -1091,7 +1180,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 response[0] != 0x65:
|
if len(response) < 56 or 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
|
||||||
@ -1150,37 +1239,40 @@ 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, packet):
|
def initialize_status(self, status_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 = (packet[7] & 0x01) == 0
|
self.external_clock = (status_packet[7] & 0x01) == 0
|
||||||
if self.external_clock:
|
if self.external_clock:
|
||||||
count, = struct.unpack(">H", packet[13:15])
|
count, = struct.unpack(">H", status_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", packet[8:12])
|
self.mcu_clock_hz, = struct.unpack(">I", status_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 = packet[4]
|
self.freq_count_24 = status_packet[4]
|
||||||
|
|
||||||
# wakeup timer factory value
|
# wakeup timer factory value
|
||||||
self.wakeup_freq, = struct.unpack(">H", packet[1:3])
|
self.wakeup_freq, = struct.unpack(">H", status_packet[1:3])
|
||||||
|
|
||||||
bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
|
bl_version, bl_stepping = struct.unpack("BB", status_packet[17:19])
|
||||||
bl_minor = packet[22] & 0x0f
|
bl_minor = status_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,
|
bl_minor, chr(bl_stepping))
|
||||||
chr(bl_stepping))
|
|
||||||
self.bsl_version = bl_version
|
self.bsl_version = bl_version
|
||||||
|
|
||||||
def print_mcu_info(self):
|
def print_mcu_info(self):
|
||||||
@ -1196,6 +1288,8 @@ 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])
|
||||||
@ -1205,6 +1299,8 @@ 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
|
||||||
@ -1216,6 +1312,8 @@ 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
|
||||||
@ -1226,6 +1324,9 @@ 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):
|
||||||
@ -1255,13 +1356,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 response[0] != 0x00:
|
if len(response) < 2 or 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 == None or prog_trim == None:
|
if user_trim is None or prog_trim is None:
|
||||||
raise StcProtocolException("frequency trimming unsuccessful")
|
raise StcProtocolException("frequency trimming unsuccessful")
|
||||||
|
|
||||||
# calibration, round 2
|
# calibration, round 2
|
||||||
@ -1274,12 +1375,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 response[0] != 0x00:
|
if len(response) < 2 or 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, prog_count = self.choose_trim(packet, response, target_prog_count)
|
prog_trim, _ = 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))
|
||||||
@ -1295,12 +1396,12 @@ class Stc15Protocol(Stc15AProtocol):
|
|||||||
# This is a bit of a hack, but it works.
|
# This is a bit of a hack, but it works.
|
||||||
bauds = self.baud_transfer if (self.mcu_magic >> 8) == 0xf2 else self.baud_transfer * 4
|
bauds = self.baud_transfer if (self.mcu_magic >> 8) == 0xf2 else self.baud_transfer * 4
|
||||||
packet += struct.pack(">H", int(65535 - program_speed / bauds))
|
packet += struct.pack(">H", int(65535 - program_speed / bauds))
|
||||||
packet += struct.pack(">H", int(65535 - (program_speed / bauds) * 1.5))
|
packet += bytes(user_trim)
|
||||||
iap_wait = self.get_iap_delay(program_speed)
|
iap_wait = self.get_iap_delay(program_speed)
|
||||||
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 response[0] != 0x01:
|
if len(response) < 1 or 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
|
||||||
@ -1317,7 +1418,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 response[0] != 0x01:
|
if len(response) < 1 or 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
|
||||||
@ -1343,9 +1444,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 response[0] == 0x0f:
|
if len(response) == 1 and response[0] == 0x0f:
|
||||||
raise StcProtocolException("MCU is locked")
|
raise StcProtocolException("MCU is locked")
|
||||||
if response[0] != 0x05:
|
if len(response) < 1 or response[0] != 0x05:
|
||||||
raise StcProtocolException("incorrect magic in handshake packet")
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
|
||||||
print("done")
|
print("done")
|
||||||
@ -1366,18 +1467,20 @@ 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 response[0] != 0x03:
|
if len(response) < 1 or 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)
|
||||||
@ -1387,11 +1490,10 @@ 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 response[0] != 0x02 or response[1] != 0x54:
|
if len(response) < 2 or response[0] != 0x02 or response[1] != 0x54:
|
||||||
raise StcProtocolException("incorrect magic in write packet")
|
raise StcProtocolException("incorrect magic in write packet")
|
||||||
print(".", end="")
|
self.progress_cb(i, self.PROGRAM_BLOCKSIZE, len(data))
|
||||||
sys.stdout.flush()
|
self.progress_cb(len(data), self.PROGRAM_BLOCKSIZE, len(data))
|
||||||
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:
|
||||||
@ -1400,7 +1502,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 response[0] != 0x07 or response[1] != 0x54:
|
if len(response) < 2 or 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")
|
||||||
|
|
||||||
@ -1409,7 +1511,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,
|
||||||
@ -1439,13 +1541,163 @@ 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 response[0] != 0x04 or response[1] != 0x54:
|
if len(response) < 2 or 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
|
||||||
|
self.reference_voltage = None
|
||||||
|
self.mfg_date = ()
|
||||||
|
|
||||||
|
def initialize_options(self, status_packet):
|
||||||
|
"""Initialize options"""
|
||||||
|
if len(status_packet) < 17:
|
||||||
|
raise StcProtocolException("invalid options in status packet")
|
||||||
|
|
||||||
|
# create option state
|
||||||
|
self.options = Stc8Option(status_packet[9:12] + status_packet[15:17])
|
||||||
|
self.options.print()
|
||||||
|
|
||||||
|
def initialize_status(self, packet):
|
||||||
|
"""Decode status packet and store basic MCU info"""
|
||||||
|
|
||||||
|
if len(packet) < 39:
|
||||||
|
raise StcProtocolException("invalid status packet")
|
||||||
|
|
||||||
|
self.mcu_clock_hz, = struct.unpack(">I", packet[1:5])
|
||||||
|
self.external_clock = False
|
||||||
|
# all ones means no calibration
|
||||||
|
# new chips are shipped without any calibration
|
||||||
|
# XXX: somehow check if that still holds
|
||||||
|
if self.mcu_clock_hz == 0xffffffff: self.mcu_clock_hz = 0
|
||||||
|
|
||||||
|
# wakeup timer factory value
|
||||||
|
self.wakeup_freq, = struct.unpack(">H", packet[23:25])
|
||||||
|
self.reference_voltage, = struct.unpack(">H", packet[35:37])
|
||||||
|
self.mfg_date = (
|
||||||
|
2000 + Utils.decode_packed_bcd(packet[37]),
|
||||||
|
Utils.decode_packed_bcd(packet[38]),
|
||||||
|
Utils.decode_packed_bcd(packet[39])
|
||||||
|
)
|
||||||
|
|
||||||
|
bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
|
||||||
|
bl_minor = packet[22] & 0x0f
|
||||||
|
self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
|
||||||
|
bl_minor, chr(bl_stepping))
|
||||||
|
self.bsl_version = bl_version
|
||||||
|
|
||||||
|
def print_mcu_info(self):
|
||||||
|
"""Print additional STC8 info"""
|
||||||
|
super().print_mcu_info()
|
||||||
|
print("Target ref. voltage: %d mV" % self.reference_voltage)
|
||||||
|
print("Target mfg. date: %04d-%02d-%02d" % self.mfg_date)
|
||||||
|
|
||||||
|
def calibrate(self):
|
||||||
|
"""Calibrate selected user frequency frequency and switch to selected baudrate."""
|
||||||
|
|
||||||
|
# handle uncalibrated chips
|
||||||
|
if self.mcu_clock_hz == 0 and self.trim_frequency <= 0:
|
||||||
|
raise StcProtocolException("uncalibrated, please provide a trim value")
|
||||||
|
|
||||||
|
# determine target counter
|
||||||
|
user_speed = self.trim_frequency
|
||||||
|
if user_speed <= 0: user_speed = self.mcu_clock_hz
|
||||||
|
target_user_count = round(user_speed / (self.baud_handshake/2))
|
||||||
|
|
||||||
|
# calibration, round 1
|
||||||
|
print("Trimming frequency: ", end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
packet = bytes([0x00])
|
||||||
|
packet += struct.pack(">B", 12)
|
||||||
|
packet += bytes([0x00, 0x00, 23*1, 0x00, 23*2, 0x00])
|
||||||
|
packet += bytes([23*3, 0x00, 23*4, 0x00, 23*5, 0x00])
|
||||||
|
packet += bytes([23*6, 0x00, 23*7, 0x00, 23*8, 0x00])
|
||||||
|
packet += bytes([23*9, 0x00, 23*10, 0x00, 255, 0x00])
|
||||||
|
self.write_packet(packet)
|
||||||
|
self.pulse(b"\xfe", timeout=1.0)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 2 or response[0] != 0x00:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
|
||||||
|
# select ranges and trim values
|
||||||
|
for divider in (1, 2, 3, 4, 5):
|
||||||
|
user_trim = self.choose_range(packet, response, target_user_count * divider)
|
||||||
|
if user_trim is not None:
|
||||||
|
self.trim_divider = divider
|
||||||
|
break
|
||||||
|
if user_trim is None:
|
||||||
|
raise StcProtocolException("frequency trimming unsuccessful")
|
||||||
|
|
||||||
|
# calibration, round 2
|
||||||
|
packet = bytes([0x00])
|
||||||
|
packet += struct.pack(">B", 12)
|
||||||
|
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
||||||
|
packet += bytes([i & 0xff, 0x00])
|
||||||
|
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
||||||
|
packet += bytes([i & 0xff, 0x01])
|
||||||
|
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
||||||
|
packet += bytes([i & 0xff, 0x02])
|
||||||
|
for i in range(user_trim[0] - 1, user_trim[0] + 2):
|
||||||
|
packet += bytes([i & 0xff, 0x03])
|
||||||
|
self.write_packet(packet)
|
||||||
|
self.pulse(b"\xfe", timeout=1.0)
|
||||||
|
response = self.read_packet()
|
||||||
|
if len(response) < 2 or response[0] != 0x00:
|
||||||
|
raise StcProtocolException("incorrect magic in handshake packet")
|
||||||
|
|
||||||
|
# select final values
|
||||||
|
user_trim, user_count = self.choose_trim(packet, response, target_user_count)
|
||||||
|
self.trim_value = user_trim
|
||||||
|
self.trim_frequency = round(user_count * (self.baud_handshake / 2) / self.trim_divider)
|
||||||
|
print("%.03f MHz" % (self.trim_frequency / 1E6))
|
||||||
|
|
||||||
|
# switch to programming frequency
|
||||||
|
print("Switching to %d baud: " % self.baud_transfer, end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
packet = bytes([0x01, 0x00, 0x00])
|
||||||
|
bauds = self.baud_transfer * 4
|
||||||
|
packet += struct.pack(">H", round(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
|
||||||
@ -1463,8 +1715,9 @@ 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" % (("<-" if receive else "->"),
|
print("%s bRequest=%02X wValue=%04X wIndex=%04X data: %s" %
|
||||||
request, value, index, Utils.hexstr(data, " ")), file=sys.stderr)
|
(("<-" if receive else "->"), request, value, index,
|
||||||
|
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"""
|
||||||
@ -1504,15 +1757,16 @@ 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):
|
def connect(self, autoreset=False, resetcmd=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 _usb_available == False:
|
if not _usb_available:
|
||||||
raise StcProtocolException("USB support not available. "
|
raise StcProtocolException(
|
||||||
+ "pyusb is not installed or not working correctly.")
|
"USB support not available. " +
|
||||||
|
"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()
|
||||||
@ -1572,8 +1826,6 @@ 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"
|
||||||
@ -1583,9 +1835,8 @@ 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")
|
||||||
print(".", end="")
|
self.progress_cb(i, self.PROGRAM_BLOCKSIZE, len(data))
|
||||||
sys.stdout.flush()
|
self.progress_cb(len(data), self.PROGRAM_BLOCKSIZE, len(data))
|
||||||
print(" done")
|
|
||||||
|
|
||||||
def program_options(self):
|
def program_options(self):
|
||||||
print("Setting options: ", end="")
|
print("Setting options: ", end="")
|
||||||
|
@ -19,31 +19,43 @@
|
|||||||
# SOFTWARE.
|
# SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
import serial
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import serial
|
||||||
|
|
||||||
class Utils:
|
class Utils:
|
||||||
|
"""Common utility functions"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_bool(self, val):
|
def to_bool(cls, val):
|
||||||
"""make sensible boolean from string or other type value"""
|
"""make sensible boolean from string or other type value"""
|
||||||
|
|
||||||
if isinstance(val, bool): return val
|
if not val:
|
||||||
if isinstance(val, int): return bool(val)
|
return False
|
||||||
if len(val) == 0: return False
|
if isinstance(val, bool):
|
||||||
|
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(self, val):
|
def to_int(cls, val):
|
||||||
"""make int from any value, nice error message if not possible"""
|
"""make int from any value, nice error message if not possible"""
|
||||||
|
|
||||||
try: return int(val, 0)
|
try:
|
||||||
except: raise ValueError("invalid integer")
|
return int(val, 0)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
raise ValueError("invalid integer")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def hexstr(self, bytestr, sep=""):
|
def hexstr(cls, 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 bytestr])
|
return sep.join(["%02X" % x for x in bytes(bytestr)])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def decode_packed_bcd(cls, byt):
|
||||||
|
"""Decode two-digit packed BCD value"""
|
||||||
|
return (byt & 0x0f) + (10 * (byt >> 4))
|
||||||
|
|
||||||
|
|
||||||
class BaudType:
|
class BaudType:
|
||||||
@ -55,5 +67,5 @@ class BaudType:
|
|||||||
raise argparse.ArgumentTypeError("illegal baudrate")
|
raise argparse.ArgumentTypeError("illegal baudrate")
|
||||||
return baud
|
return baud
|
||||||
|
|
||||||
def __repr__(self): return "baudrate"
|
def __repr__(self):
|
||||||
|
return "baudrate"
|
||||||
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
19
tests/iap15f2k61s2.yml
Normal file
19
tests/iap15f2k61s2.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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]
|
19
tests/stc12c2052ad.yml
Normal file
19
tests/stc12c2052ad.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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]
|
15
tests/stc12c5a60s2.yml
Normal file
15
tests/stc12c5a60s2.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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]
|
20
tests/stc15f104e.yml
Normal file
20
tests/stc15f104e.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
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]
|
19
tests/stc15l104w.yml
Normal file
19
tests/stc15l104w.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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]
|
20
tests/stc15w4k56s4.yml
Normal file
20
tests/stc15w4k56s4.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
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]
|
19
tests/stc89c52rc.yml
Normal file
19
tests/stc89c52rc.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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]
|
20
tests/stc8a8k64s4a12.yml
Normal file
20
tests/stc8a8k64s4a12.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
name: STC8A8K64S4A12 programming test
|
||||||
|
protocol: stc8
|
||||||
|
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
|
||||||
|
responses:
|
||||||
|
- [0x50, 0x01, 0x6E, 0x0B, 0xD0, 0x78, 0x00, 0x01, 0xFF, 0xFF, 0x8B, 0xBF, 0xFF, 0x28, 0x43, 0xF7, 0xFE, 0x73, 0x55, 0x00, 0xF6, 0x28, 0x09, 0x85, 0xE3, 0x5F, 0x80, 0x07, 0x20, 0x20, 0x20, 0x01, 0x00, 0x00, 0xFE, 0x05, 0x3A, 0x17, 0x05, 0x25, 0x91, 0xFF]
|
||||||
|
- [0x00, 0x0C, 0x37, 0x49, 0x3B, 0x8D, 0x3F, 0xE0, 0x44, 0x57, 0x48, 0x9E, 0x4D, 0x06, 0x51, 0x35, 0x55, 0x94, 0x59, 0xCF, 0x5D, 0xDA, 0x62, 0x3C, 0x66, 0xDA]
|
||||||
|
- [0x00, 0x0C, 0x51, 0x59, 0x51, 0x8C, 0x51, 0xB3, 0x51, 0x71, 0x51, 0x9B, 0x51, 0xC2, 0x51, 0x77, 0x51, 0xAA, 0x51, 0xC8, 0x51, 0x62, 0x51, 0x89, 0x51, 0xB0]
|
||||||
|
- [0x01]
|
||||||
|
- [0x05]
|
||||||
|
- [0x03, 0xF6, 0x28, 0x02, 0xBC, 0x26, 0x98, 0xDF]
|
||||||
|
- [0x02, 0x54]
|
||||||
|
- [0x02, 0x54]
|
||||||
|
- [0x02, 0x54]
|
||||||
|
- [0x02, 0x54]
|
||||||
|
- [0x02, 0x54]
|
||||||
|
- [0x02, 0x54]
|
||||||
|
- [0x02, 0x54]
|
||||||
|
- [0x02, 0x54]
|
||||||
|
- [0x07, 0x54]
|
||||||
|
- [0x04, 0x54]
|
120
tests/test_fuzzing.py
Normal file
120
tests/test_fuzzing.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#
|
||||||
|
# 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",
|
||||||
|
"./tests/stc8a8k64s4a12.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()
|
144
tests/test_program.py
Normal file
144
tests/test_program.py
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
@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_stc8a8(self, out, sleep_mock, serial_mock, write_mock, read_mock):
|
||||||
|
"""Test a programming cycle with STC8 protocol, STC8A8 series"""
|
||||||
|
self._program_yml("./tests/stc8a8k64s4a12.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)
|
84
tests/test_utils.py
Normal file
84
tests/test_utils.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#
|
||||||
|
# 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])
|
||||||
|
|
||||||
|
def test_decode_packed_bcd(self):
|
||||||
|
"""Test packed BCD decoder"""
|
||||||
|
self.assertEqual(Utils.decode_packed_bcd(0x01), 1)
|
||||||
|
self.assertEqual(Utils.decode_packed_bcd(0x10), 10)
|
||||||
|
self.assertEqual(Utils.decode_packed_bcd(0x11), 11)
|
||||||
|
self.assertEqual(Utils.decode_packed_bcd(0x25), 25)
|
||||||
|
self.assertEqual(Utils.decode_packed_bcd(0x99), 99)
|
||||||
|
|
||||||
|
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")
|
Reference in New Issue
Block a user