Compare commits
80 Commits
fix-ch340
...
debian-fix
Author | SHA1 | Date | |
---|---|---|---|
1a5cf18590 | |||
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 | |||
8ad77586d4 | |||
276c696fa4 | |||
26ef34991b | |||
f90fe4152b | |||
d6ef028dc7 | |||
979d7f513f | |||
fce2f01232 | |||
854f36100b | |||
92f4def11a | |||
6c0af88551 | |||
366a3a5bd3 | |||
c046e886e3 | |||
7ba95eab68 | |||
61a4fa0e4f | |||
da5f6678c5 | |||
a8f141584d | |||
adcb8d8ced | |||
81c890337e | |||
9af984a191 | |||
2738118e8f | |||
eedf9169a7 | |||
cb739e6f94 | |||
2a770bb37f | |||
8fe94e001f | |||
b1ed017137 | |||
678d001ec5 | |||
f8e8d66baa | |||
afba6c6805 | |||
d1f464c387 | |||
e268b282cb | |||
f9a19c927c | |||
372f854c77 | |||
41cabb587b | |||
6a38127c0d | |||
d3911870c3 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,7 +1,12 @@
|
||||
*~
|
||||
*.pyc
|
||||
*.egg-info
|
||||
__pycache__
|
||||
*.eggs/
|
||||
*.pybuild/
|
||||
__pycache__/
|
||||
/build
|
||||
/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 pyserial pyusb tqdm
|
||||
script:
|
||||
- python setup.py build
|
||||
- python setup.py test
|
||||
before_deploy:
|
||||
- deactivate
|
||||
- python3 setup.py bdist_rpm
|
||||
- dpkg-buildpackage -uc -us
|
||||
- cp ../*.deb dist/
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key: $GH_TOKEN
|
||||
file_glob: true
|
||||
file:
|
||||
- dist/stcgal*_all.deb
|
||||
- dist/stcgal*.noarch.rpm
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
python: "3.4"
|
115
README.md
115
README.md
@ -1,18 +1,21 @@
|
||||
[](https://travis-ci.org/grigorig/stcgal)
|
||||
|
||||
stcgal - STC MCU ISP flash tool
|
||||
===============================
|
||||
|
||||
stcgal is a command line flash programming tool for STC MCU Ltd. [1]
|
||||
8051 compatible microcontrollers. The name was inspired by avrdude [2].
|
||||
|
||||
STC microcontrollers have a UART 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
|
||||
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
|
||||
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.
|
||||
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.
|
||||
|
||||
[1] http://stcmcu.com/
|
||||
[2] http://www.nongnu.org/avrdude/
|
||||
@ -24,24 +27,33 @@ 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)
|
||||
* 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)
|
||||
* STC12C5A60S2 (BSL version: 6.2I)
|
||||
* 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.1Q)
|
||||
* IAP15F2K61S2 (BSL version: 7.1S)
|
||||
* 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)
|
||||
* STC15W4K56S4 (BSL version: 7.3.4T, UART and USB mode)
|
||||
|
||||
More compatibility testing is going to happen soon.
|
||||
Compatibility reports, both negative and positive, are welcome.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* UART and USB BSL support
|
||||
* Display part info
|
||||
* Determine operating frequency
|
||||
* Program flash memory
|
||||
@ -49,11 +61,14 @@ Features
|
||||
* Set device options
|
||||
* Read unique device ID (STC 10/11/12/15)
|
||||
* Trim RC oscillator frequency (STC 15)
|
||||
* Automatic power-cycling with DTR toggle or a custom shell command
|
||||
* Automatic UART protocol detection
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
stcgal requires Python 3.2 (or later) and pySerial. You can run stcgal
|
||||
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. The recommended
|
||||
method for permanent installation is to use Python's setuptools. Run
|
||||
```./setup.py build``` to build and ```sudo ./setup.py install```
|
||||
@ -66,24 +81,30 @@ Usage
|
||||
Call stcgal with ```-h``` for usage information.
|
||||
|
||||
```
|
||||
usage: stcgal.py [-h] [-P {stc89,stc12a,stc12,stc15a,stc15}] [-p PORT]
|
||||
[-b BAUD] [-l HANDSHAKE] [-o OPTION] [-t TRIM] [-D]
|
||||
[code_binary] [eeprom_binary]
|
||||
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_binary code segment binary file to flash
|
||||
eeprom_binary eeprom segment binary file to flash
|
||||
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
|
||||
-P {stc89,stc12a,stc12,stc15a,stc15}, --protocol {stc89,stc12a,stc12,stc15a,stc15}
|
||||
-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: 1200)
|
||||
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)
|
||||
@ -95,14 +116,18 @@ 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 must be specified with the ```-P``` flag. Here's
|
||||
the general mapping between protocols and MCU series:
|
||||
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``` STC 89/90 series
|
||||
* ```stc12a``` STC12Cx052AD and possibly others
|
||||
* ```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,
|
||||
@ -221,7 +246,9 @@ Option key | Possible values | Protocols/Models | Descri
|
||||
```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 ``` | true/false | STC12A+ | Low-voltage reset (brownout)
|
||||
```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.
|
||||
@ -244,6 +271,46 @@ 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.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
|
19
debian/changelog
vendored
19
debian/changelog
vendored
@ -1,3 +1,22 @@
|
||||
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
|
||||
|
||||
* Update to 1.2
|
||||
* Add optional python3-usb dependency
|
||||
|
||||
-- Grigori Goronzy <greg@chown.ath.cx> Fri, 20 May 2016 03:21:25 +0200
|
||||
|
||||
stcgal (1.0git) unstable; urgency=low
|
||||
|
||||
* Initial Debianized Release
|
||||
|
22
debian/control
vendored
22
debian/control
vendored
@ -2,25 +2,25 @@ Source: stcgal
|
||||
Section: electronics
|
||||
Priority: optional
|
||||
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
|
||||
Homepage: https://github.com/grigorig/stcgal
|
||||
X-Python3-Version: >= 3.2
|
||||
|
||||
Package: stcgal
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, python3, python3-serial
|
||||
Depends: ${misc:Depends}, python3, python3-serial, python3-tqdm
|
||||
Recommends: python3-usb (>= 1.0.0~b2)
|
||||
Description: STC MCU ISP flash tool
|
||||
stcgal is a command line flash programming tool for STC MCU Ltd. 8051
|
||||
compatible microcontrollers. The name was inspired by avrdude.
|
||||
stcgal is a command line flash programming tool for STC MCU Ltd.
|
||||
8051 compatible microcontrollers. The name was inspired by avrdude.
|
||||
.
|
||||
STC microcontrollers have a UART 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.
|
||||
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
|
||||
|
@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
# This curious script dumps all model info from STC-ISP.
|
||||
# Data is directly read from the binary.
|
||||
# Offsets are for STC-ISP 6.85I, sha1sum a1a625d6c491fe98d0286ebac0a8d78b94dca81d
|
||||
# Offsets are for stc-isp-15xx-v6.85K.exe, sha1sum aa66e4c1ab49de27369b83c954a7c202acce0950
|
||||
|
||||
MCU_TABLE_OFFSET = 0x00063550
|
||||
MCU_TABLE_SIZE = 914
|
||||
MCU_TABLE_OFFSET = 0x00064550
|
||||
MCU_TABLE_SIZE = 941
|
||||
MCU_RECORD_SIZE = 32
|
||||
MCU_NAMES_OFFSET = 0x0007d708
|
||||
MCU_NAMES_PTR_OFFSET = 0x0047d708
|
||||
MCU_NAMES_OFFSET = 0x0007e80c
|
||||
MCU_NAMES_PTR_OFFSET = 0x0047e80c
|
||||
|
||||
import struct
|
||||
import sys
|
||||
@ -25,12 +25,12 @@ for i in range(MCU_TABLE_SIZE):
|
||||
inp.seek(mcu_name_offset)
|
||||
name_str = inp.read(16).split(b'\00')[0].decode("ascii")
|
||||
|
||||
# XXX: 1 KB are reserved one *some* MCUs for some reason
|
||||
#if ee_size > 0 and not name_str.startswith("IAP"):
|
||||
# ee_size -= 1024
|
||||
# TODO: With some MCUs, the amount of available EEPROM depends on the BSL version.
|
||||
# Generally, newer BSLs free up a KB of additional EEPROM. Currently, always the
|
||||
# maximum amount (with newer BSL) is reported.
|
||||
|
||||
# STC12C54xx always have 12 KB eeprom
|
||||
if name_str.startswith("STC12C54"):
|
||||
# STC12x54xx always have 12 KB eeprom
|
||||
if name_str.startswith("STC12C54") or name_str.startswith("STC12LE54"):
|
||||
ee_size = 12 * 1024
|
||||
|
||||
print("MCUModel(name='%s', magic=0x%02x%02x, total=%d, code=%d, eeprom=%d)," %
|
||||
|
@ -4,7 +4,7 @@ Placement of configuration values
|
||||
"~" means the bit is a negated boolean. Sometimes values overlap,
|
||||
depending on MCU model.
|
||||
|
||||
In STC12A series, the first 4 MCS bytes have active
|
||||
In STC12A series, the first 7 MCS bytes have active
|
||||
values. Generally, unused bits should be set to 1.
|
||||
|
||||
MCS0
|
||||
@ -47,14 +47,9 @@ MSB 7 6 5 4 3 2 1 0 LSB
|
||||
(or others, depends on MCU model) are held low on POR.
|
||||
|
||||
|
||||
MCS3
|
||||
----
|
||||
|
||||
Unused.
|
||||
|
||||
|
||||
MCS4
|
||||
----
|
||||
MCS3 (at index 6!)
|
||||
------------------
|
||||
|
||||
MSB 7 6 5 4 3 2 1 0 LSB
|
||||
LVD
|
||||
|
@ -111,7 +111,31 @@ in set options packet:
|
||||
46 B9 6A 00 4B 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 00 FF A8 FF EE FF E0 FF FD 03 FF FF FF FF FF
|
||||
^^
|
||||
MCSP
|
||||
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
|
||||
FF FD FF FF FF 75 BF F7 BC 9F 3A 80 16
|
||||
^^
|
||||
MCSY
|
||||
|
||||
password setting
|
||||
|
||||
the password is sent with packet type 0x07 and checked before erase with packet type 0x05. setting the password uses two fields.
|
||||
index 22 of the option block encodes the password length in bytes (MCSP, see above). bit 3 in MCS3 decides whether the password
|
||||
will be checked. if the bit is set, no password check occurs. if it is reset, a password check occurs.
|
||||
|
||||
quick dump from USB-ISP packets:
|
||||
|
||||
set: foobar
|
||||
0000 ff ff ff 00 ff ff 00 05 ff ff ff ff ff ff ff 07 ................
|
||||
0010 ff ff ff ff ff ff ff 07 ff 06 01 ff 6e ff 36 58 ............n.6X
|
||||
0020 ff 00 ff f5 03 ff ff 0c ff ff ff ff ff ff ff 07 ................
|
||||
0030 ff ff ff ff ff ff ff 07 ff ff ff ff ff ff ec 1a ................
|
||||
0040 ff ff ff 99 7f f7 bc 38 9f 61 .......8.a
|
||||
|
||||
reset:
|
||||
0000 ff ff ff 00 ff ff 00 05 ff ff ff ff ff ff ff 07 ................
|
||||
0010 ff ff ff ff ff ff ff 07 ff 00 01 ff 6e ff 36 5e ............n.6^
|
||||
0020 ff 00 ff fd 03 ff ff 04 ff ff ff ff ff ff ff 07 ................
|
||||
0030 ff ff ff ff ff ff ff 07 ff ff ff ff ff ff ec 1a ................
|
||||
0040 ff ff ff 99 7f f7 bc 38 9f 61 .......8.a
|
||||
|
46
doc/stc15-usb-protocol.txt
Normal file
46
doc/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
|
||||
|
35
doc/usb15-protocol.txt
Normal file
35
doc/usb15-protocol.txt
Normal file
@ -0,0 +1,35 @@
|
||||
STC15 series USB ISP protocol
|
||||
=============================
|
||||
|
||||
- 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 arbitrary size are used for writes
|
||||
|
||||
- packets from MCU
|
||||
always start with 0x46 0xb9, similar to serial protocols
|
||||
third byte is packet length
|
||||
followed by data bytes
|
||||
8 bit checksum at the end, looks like 8 bit modular subtraction
|
||||
|
||||
- packet types
|
||||
most likely derived from the serial protocol, at least partially
|
||||
|
||||
info packet
|
||||
- same as with serial protocol
|
||||
|
||||
option packet
|
||||
- generally same as with serial protocol, some header stuff omitted
|
||||
|
||||
- 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 inverted modular sum
|
||||
|
||||
- option packet
|
||||
wIndex is 0
|
||||
wValue is 0xa55a
|
||||
bRequest is 4
|
||||
seems to use the same checksumming scheme
|
||||
|
28
setup.py
28
setup.py
@ -1,4 +1,25 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2016 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 stcgal
|
||||
from setuptools import setup, find_packages
|
||||
@ -6,8 +27,11 @@ from setuptools import setup, find_packages
|
||||
setup(
|
||||
name = "stcgal",
|
||||
version = stcgal.__version__,
|
||||
packages = find_packages(exclude=["doc"]),
|
||||
packages = find_packages(exclude=["doc", "tests"]),
|
||||
install_requires = ["pyserial"],
|
||||
extras_require = {
|
||||
"usb": ["pyusb>=1.0.0"]
|
||||
},
|
||||
entry_points = {
|
||||
"console_scripts": [
|
||||
"stcgal = stcgal.frontend:cli",
|
||||
@ -31,4 +55,6 @@ setup(
|
||||
"Topic :: Software Development :: Embedded Systems",
|
||||
"Topic :: Software Development",
|
||||
],
|
||||
test_suite = "tests",
|
||||
tests_require = ["PyYAML"],
|
||||
)
|
||||
|
@ -1 +1 @@
|
||||
__version__ = "1.0"
|
||||
__version__ = "1.4"
|
||||
|
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,10 +20,10 @@
|
||||
# SOFTWARE.
|
||||
#
|
||||
|
||||
import sys, os, time, struct
|
||||
import sys
|
||||
import argparse
|
||||
import stcgal
|
||||
from stcgal.utils import Utils, BaudType
|
||||
from stcgal.utils import BaudType
|
||||
from stcgal.protocols import *
|
||||
from stcgal.ihex import IHex
|
||||
|
||||
@ -32,29 +32,41 @@ class StcGal:
|
||||
|
||||
def __init__(self, opts):
|
||||
self.opts = opts
|
||||
self.initialize_protocol(opts)
|
||||
|
||||
def initialize_protocol(self, opts):
|
||||
"""Initialize protocol backend"""
|
||||
if opts.protocol == "stc89":
|
||||
self.protocol = Stc89Protocol(opts.port, opts.handshake, opts.baud)
|
||||
elif opts.protocol == "stc12a":
|
||||
self.protocol = Stc12AProtocol(opts.port, opts.handshake, opts.baud)
|
||||
elif opts.protocol == "stc12b":
|
||||
self.protocol = Stc12BProtocol(opts.port, opts.handshake, opts.baud)
|
||||
elif opts.protocol == "stc12":
|
||||
self.protocol = Stc12Protocol(opts.port, opts.handshake, opts.baud)
|
||||
elif opts.protocol == "stc15a":
|
||||
self.protocol = Stc15AProtocol(opts.port, opts.handshake, opts.baud,
|
||||
round(opts.trim * 1000))
|
||||
else:
|
||||
round(opts.trim * 1000))
|
||||
elif opts.protocol == "stc15":
|
||||
self.protocol = Stc15Protocol(opts.port, opts.handshake, opts.baud,
|
||||
round(opts.trim * 1000))
|
||||
|
||||
elif opts.protocol == "usb15":
|
||||
self.protocol = StcUsb15Protocol()
|
||||
else:
|
||||
self.protocol = StcAutoProtocol(opts.port, opts.handshake, opts.baud)
|
||||
self.protocol.debug = opts.debug
|
||||
|
||||
def emit_options(self, options):
|
||||
for o in options:
|
||||
"""Set options from command line to protocol handler."""
|
||||
|
||||
for opt in options:
|
||||
try:
|
||||
kv = o.split("=", 1)
|
||||
if len(kv) < 2: raise ValueError("incorrect format")
|
||||
kv = opt.split("=", 1)
|
||||
if len(kv) < 2:
|
||||
raise ValueError("incorrect format")
|
||||
self.protocol.set_option(kv[0], kv[1])
|
||||
except ValueError as e:
|
||||
raise NameError("invalid option '%s' (%s)" % (kv[0], e))
|
||||
except ValueError as ex:
|
||||
raise NameError("invalid option '%s' (%s)" % (kv[0], ex))
|
||||
|
||||
def load_file_auto(self, fileobj):
|
||||
"""Load file with Intel Hex autodetection."""
|
||||
@ -67,14 +79,16 @@ class StcGal:
|
||||
binary = hexfile.extract_data()
|
||||
print("%d bytes (Intel HEX)" %len(binary))
|
||||
return binary
|
||||
except ValueError as e:
|
||||
raise IOError("invalid Intel HEX file (%s)" %e)
|
||||
except ValueError as ex:
|
||||
raise IOError("invalid Intel HEX file (%s)" %ex)
|
||||
else:
|
||||
binary = fileobj.read()
|
||||
print("%d bytes (Binary)" %len(binary))
|
||||
return binary
|
||||
|
||||
def program_mcu(self):
|
||||
"""Execute the standard programming flow."""
|
||||
|
||||
code_size = self.protocol.model.code
|
||||
ee_size = self.protocol.model.eeprom
|
||||
|
||||
@ -104,9 +118,9 @@ class StcGal:
|
||||
bindata = bindata[0:code_size]
|
||||
bindata += eedata
|
||||
|
||||
# pad to 256 byte boundary
|
||||
if len(bindata) % 256:
|
||||
bindata += bytes(256 - len(bindata) % 256)
|
||||
# pad to 512 byte boundary
|
||||
if len(bindata) % 512:
|
||||
bindata += b'\xff' * (512 - len(bindata) % 512)
|
||||
|
||||
if self.opts.option: self.emit_options(self.opts.option)
|
||||
|
||||
@ -117,19 +131,38 @@ class StcGal:
|
||||
self.protocol.disconnect()
|
||||
|
||||
def run(self):
|
||||
try: self.protocol.connect()
|
||||
"""Run programmer, main entry point."""
|
||||
|
||||
try:
|
||||
self.protocol.connect(autoreset=self.opts.autoreset, resetcmd=self.opts.resetcmd)
|
||||
if isinstance(self.protocol, StcAutoProtocol):
|
||||
if not self.protocol.protocol_name:
|
||||
raise StcProtocolException("cannot detect protocol")
|
||||
base_protocol = self.protocol
|
||||
self.opts.protocol = self.protocol.protocol_name
|
||||
print("Protocol detected: %s" % self.opts.protocol)
|
||||
# recreate self.protocol with proper protocol class
|
||||
self.initialize_protocol(self.opts)
|
||||
else:
|
||||
base_protocol = None
|
||||
|
||||
self.protocol.initialize(base_protocol)
|
||||
except KeyboardInterrupt:
|
||||
sys.stdout.flush();
|
||||
sys.stdout.flush()
|
||||
print("interrupted")
|
||||
return 2
|
||||
except (StcFramingException, StcProtocolException) as e:
|
||||
sys.stdout.flush();
|
||||
print("Protocol error: %s" % e, file=sys.stderr)
|
||||
except (StcFramingException, StcProtocolException) as ex:
|
||||
sys.stdout.flush()
|
||||
print("Protocol error: %s" % ex, file=sys.stderr)
|
||||
self.protocol.disconnect()
|
||||
return 1
|
||||
except serial.SerialException as e:
|
||||
sys.stdout.flush();
|
||||
print("Serial port error: %s" % e, file=sys.stderr)
|
||||
except serial.SerialException as ex:
|
||||
sys.stdout.flush()
|
||||
print("Serial port error: %s" % ex, file=sys.stderr)
|
||||
return 1
|
||||
except IOError as ex:
|
||||
sys.stdout.flush()
|
||||
print("I/O error: %s" % ex, file=sys.stderr)
|
||||
return 1
|
||||
|
||||
try:
|
||||
@ -139,27 +172,27 @@ class StcGal:
|
||||
else:
|
||||
self.protocol.disconnect()
|
||||
return 0
|
||||
except NameError as e:
|
||||
sys.stdout.flush();
|
||||
print("Option error: %s" % e, file=sys.stderr)
|
||||
except NameError as ex:
|
||||
sys.stdout.flush()
|
||||
print("Option error: %s" % ex, file=sys.stderr)
|
||||
self.protocol.disconnect()
|
||||
return 1
|
||||
except (StcFramingException, StcProtocolException) as e:
|
||||
sys.stdout.flush();
|
||||
print("Protocol error: %s" % e, file=sys.stderr)
|
||||
except (StcFramingException, StcProtocolException) as ex:
|
||||
sys.stdout.flush()
|
||||
print("Protocol error: %s" % ex, file=sys.stderr)
|
||||
self.protocol.disconnect()
|
||||
return 1
|
||||
except KeyboardInterrupt:
|
||||
sys.stdout.flush();
|
||||
sys.stdout.flush()
|
||||
print("interrupted", file=sys.stderr)
|
||||
self.protocol.disconnect()
|
||||
return 2
|
||||
except serial.SerialException as e:
|
||||
print("Serial port error: %s" % e, file=sys.stderr)
|
||||
except serial.SerialException as ex:
|
||||
print("Serial port error: %s" % ex, file=sys.stderr)
|
||||
return 1
|
||||
except IOError as e:
|
||||
sys.stdout.flush();
|
||||
print("I/O error: %s" % e, file=sys.stderr)
|
||||
except IOError as ex:
|
||||
sys.stdout.flush()
|
||||
print("I/O error: %s" % ex, file=sys.stderr)
|
||||
self.protocol.disconnect()
|
||||
return 1
|
||||
|
||||
@ -167,13 +200,15 @@ class StcGal:
|
||||
def cli():
|
||||
# check arguments
|
||||
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 %s - an STC MCU ISP flash tool\n(C) 2014-2017 Grigori Goronzy\nhttps://github.com/grigorig/stcgal" %stcgal.__version__)
|
||||
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("-P", "--protocol", help="protocol version", choices=["stc89", "stc12a", "stc12", "stc15a", "stc15"], default="stc12")
|
||||
parser.add_argument("-a", "--autoreset", help="cycle power automatically by asserting DTR", action="store_true")
|
||||
parser.add_argument("-r", "--resetcmd", help="Use this shell command for board power-cycling (instead of DTR assertion)", action="store")
|
||||
parser.add_argument("-P", "--protocol", help="protocol version (default: auto)", choices=["stc89", "stc12a", "stc12b", "stc12", "stc15a", "stc15", "usb15", "auto"], default="auto")
|
||||
parser.add_argument("-p", "--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("-l", "--handshake", help="handshake baud rate (default: 1200)", type=BaudType(), default=1200)
|
||||
parser.add_argument("-l", "--handshake", help="handshake baud rate (default: 2400)", type=BaudType(), default=2400)
|
||||
parser.add_argument("-o", "--option", help="set option (can be used multiple times)", action="append")
|
||||
parser.add_argument("-t", "--trim", help="RC oscillator frequency in kHz (STC15 series only)", type=float, default=0.0)
|
||||
parser.add_argument("-D", "--debug", help="enable debug output", action="store_true")
|
||||
|
309
stcgal/ihex.py
309
stcgal/ihex.py
@ -5,201 +5,214 @@
|
||||
import struct
|
||||
import codecs
|
||||
|
||||
class IHex(object):
|
||||
@classmethod
|
||||
def read(cls, lines):
|
||||
ihex = cls()
|
||||
|
||||
segbase = 0
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line: continue
|
||||
class IHex:
|
||||
"""Intel HEX parser and writer"""
|
||||
|
||||
t, a, d = ihex.parse_line(line)
|
||||
if t == 0x00:
|
||||
ihex.insert_data(segbase + a, d)
|
||||
@classmethod
|
||||
def read(cls, lines):
|
||||
"""Read Intel HEX data from string or lines"""
|
||||
ihex = cls()
|
||||
|
||||
elif t == 0x01:
|
||||
break # Should we check for garbage after this?
|
||||
segbase = 0
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
elif t == 0x02:
|
||||
ihex.set_mode(16)
|
||||
segbase = struct.unpack(">H", d[0:2])[0] << 4
|
||||
t, a, d = ihex.parse_line(line)
|
||||
if t == 0x00:
|
||||
ihex.insert_data(segbase + a, d)
|
||||
|
||||
elif t == 0x03:
|
||||
ihex.set_mode(16)
|
||||
elif t == 0x01:
|
||||
break # Should we check for garbage after this?
|
||||
|
||||
cs, ip = struct.unpack(">2H", d[0:2])
|
||||
ihex.set_start((cs, ip))
|
||||
elif t == 0x02:
|
||||
ihex.set_mode(16)
|
||||
segbase = struct.unpack(">H", d[0:2])[0] << 4
|
||||
|
||||
elif t == 0x04:
|
||||
ihex.set_mode(32)
|
||||
segbase = struct.unpack(">H", d[0:2])[0] << 16
|
||||
elif t == 0x03:
|
||||
ihex.set_mode(16)
|
||||
|
||||
elif t == 0x05:
|
||||
ihex.set_mode(32)
|
||||
ihex.set_start(struct.unpack(">I", d[0:4])[0])
|
||||
cs, ip = struct.unpack(">2H", d[0:2])
|
||||
ihex.set_start((cs, ip))
|
||||
|
||||
else:
|
||||
raise ValueError("Invalid type byte")
|
||||
elif t == 0x04:
|
||||
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
|
||||
def read_file(cls, fname):
|
||||
f = open(fname, "rb")
|
||||
ihex = cls.read(f)
|
||||
f.close()
|
||||
return ihex
|
||||
else:
|
||||
raise ValueError("Invalid type byte")
|
||||
|
||||
def __init__(self):
|
||||
self.areas = {}
|
||||
self.start = None
|
||||
self.mode = 8
|
||||
self.row_bytes = 16
|
||||
return ihex
|
||||
|
||||
def set_row_bytes(self, row_bytes):
|
||||
"""Set output hex file row width (bytes represented per row)."""
|
||||
if row_bytes < 1 or row_bytes > 0xff:
|
||||
raise ValueError("Value out of range: (%r)" % row_bytes)
|
||||
self.row_bytes = row_bytes
|
||||
@classmethod
|
||||
def read_file(cls, fname):
|
||||
"""Read Intel HEX data from file"""
|
||||
f = open(fname, "rb")
|
||||
ihex = cls.read(f)
|
||||
f.close()
|
||||
return ihex
|
||||
|
||||
def extract_data(self, start=None, end=None):
|
||||
if start is None:
|
||||
start = 0
|
||||
def __init__(self):
|
||||
self.areas = {}
|
||||
self.start = None
|
||||
self.mode = 8
|
||||
self.row_bytes = 16
|
||||
|
||||
if end is None:
|
||||
result = bytearray()
|
||||
def set_row_bytes(self, row_bytes):
|
||||
"""Set output hex file row width (bytes represented per row)."""
|
||||
if row_bytes < 1 or row_bytes > 0xff:
|
||||
raise ValueError("Value out of range: (%r)" % row_bytes)
|
||||
self.row_bytes = row_bytes
|
||||
|
||||
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
|
||||
def extract_data(self, start=None, end=None):
|
||||
"""Extract binary data"""
|
||||
if start is None:
|
||||
start = 0
|
||||
|
||||
return bytes(result)
|
||||
if end is None:
|
||||
result = bytearray()
|
||||
|
||||
else:
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
return bytes(result)
|
||||
else:
|
||||
result = bytearray()
|
||||
|
||||
def set_start(self, start=None):
|
||||
self.start = start
|
||||
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
|
||||
|
||||
def set_mode(self, mode):
|
||||
self.mode = mode
|
||||
return bytes(result)
|
||||
|
||||
def get_area(self, addr):
|
||||
for start, data in self.areas.items():
|
||||
end = start + len(data)
|
||||
if addr >= start and addr <= end:
|
||||
return start
|
||||
def set_start(self, start=None):
|
||||
self.start = start
|
||||
|
||||
return None
|
||||
def set_mode(self, mode):
|
||||
self.mode = mode
|
||||
|
||||
def insert_data(self, istart, idata):
|
||||
iend = istart + len(idata)
|
||||
def get_area(self, addr):
|
||||
for start, data in self.areas.items():
|
||||
end = start + len(data)
|
||||
if addr >= start and addr <= end:
|
||||
return start
|
||||
|
||||
area = self.get_area(istart)
|
||||
if area is None:
|
||||
self.areas[istart] = idata
|
||||
return None
|
||||
|
||||
else:
|
||||
data = self.areas[area]
|
||||
# istart - iend + len(idata) + len(data)
|
||||
self.areas[area] = data[:istart-area] + idata + data[iend-area:]
|
||||
def insert_data(self, istart, idata):
|
||||
iend = istart + len(idata)
|
||||
|
||||
def calc_checksum(self, bytes):
|
||||
total = sum(bytes)
|
||||
return (-total) & 0xFF
|
||||
area = self.get_area(istart)
|
||||
if area is None:
|
||||
self.areas[istart] = idata
|
||||
|
||||
def parse_line(self, rawline):
|
||||
if rawline[0:1] != b":":
|
||||
raise ValueError("Invalid line start character (%r)" % rawline[0])
|
||||
else:
|
||||
data = self.areas[area]
|
||||
# istart - iend + len(idata) + len(data)
|
||||
self.areas[area] = data[
|
||||
:istart - area] + idata + data[iend - area:]
|
||||
|
||||
try:
|
||||
#line = rawline[1:].decode("hex")
|
||||
line = codecs.decode(rawline[1:], "hex_codec")
|
||||
except:
|
||||
raise ValueError("Invalid hex data")
|
||||
def calc_checksum(self, data):
|
||||
total = sum(data)
|
||||
return (-total) & 0xFF
|
||||
|
||||
length, addr, type = struct.unpack(">BHB", line[:4])
|
||||
def parse_line(self, rawline):
|
||||
if rawline[0:1] != b":":
|
||||
raise ValueError("Invalid line start character (%r)" % rawline[0])
|
||||
|
||||
dataend = length + 4
|
||||
data = line[4:dataend]
|
||||
try:
|
||||
line = codecs.decode(rawline[1:], "hex_codec")
|
||||
except:
|
||||
raise ValueError("Invalid hex data")
|
||||
|
||||
#~ print line[dataend:dataend + 2], repr(line)
|
||||
cs1 = line[dataend]
|
||||
cs2 = self.calc_checksum(line[:dataend])
|
||||
length, addr, line_type = struct.unpack(">BHB", line[:4])
|
||||
|
||||
if cs1 != cs2:
|
||||
raise ValueError("Checksums do not match")
|
||||
dataend = length + 4
|
||||
data = line[4:dataend]
|
||||
|
||||
return (type, addr, data)
|
||||
cs1 = line[dataend]
|
||||
cs2 = self.calc_checksum(line[:dataend])
|
||||
|
||||
def make_line(self, type, addr, data):
|
||||
line = struct.pack(">BHB", len(data), addr, type)
|
||||
line += data
|
||||
line += chr(self.calc_checksum(line))
|
||||
#~ return ":" + line.encode("hex")
|
||||
return ":" + line.encode("hex").upper() + "\r\n"
|
||||
if cs1 != cs2:
|
||||
raise ValueError("Checksums do not match")
|
||||
|
||||
def write(self):
|
||||
output = ""
|
||||
return (line_type, addr, data)
|
||||
|
||||
for start, data in sorted(self.areas.items()):
|
||||
i = 0
|
||||
segbase = 0
|
||||
def make_line(self, line_type, addr, data):
|
||||
line = struct.pack(">BHB", len(data), addr, line_type)
|
||||
line += data
|
||||
line += chr(self.calc_checksum(line))
|
||||
return ":" + line.encode("hex").upper() + "\r\n"
|
||||
|
||||
while i < len(data):
|
||||
chunk = data[i:i + self.row_bytes]
|
||||
def write(self):
|
||||
"""Write Intel HEX data to string"""
|
||||
output = ""
|
||||
|
||||
addr = start
|
||||
newsegbase = segbase
|
||||
for start, data in sorted(self.areas.items()):
|
||||
i = 0
|
||||
segbase = 0
|
||||
|
||||
if self.mode == 8:
|
||||
addr = addr & 0xFFFF
|
||||
while i < len(data):
|
||||
chunk = data[i:i + self.row_bytes]
|
||||
|
||||
elif self.mode == 16:
|
||||
t = addr & 0xFFFF
|
||||
newsegbase = (addr - t) >> 4
|
||||
addr = t
|
||||
addr = start
|
||||
newsegbase = segbase
|
||||
|
||||
if newsegbase != segbase:
|
||||
output += self.make_line(0x02, 0, struct.pack(">H", newsegbase))
|
||||
segbase = newsegbase
|
||||
if self.mode == 8:
|
||||
addr = addr & 0xFFFF
|
||||
|
||||
elif self.mode == 32:
|
||||
newsegbase = addr >> 16
|
||||
addr = addr & 0xFFFF
|
||||
elif self.mode == 16:
|
||||
t = addr & 0xFFFF
|
||||
newsegbase = (addr - t) >> 4
|
||||
addr = t
|
||||
|
||||
if newsegbase != segbase:
|
||||
output += self.make_line(0x04, 0, struct.pack(">H", newsegbase))
|
||||
segbase = newsegbase
|
||||
if newsegbase != segbase:
|
||||
output += self.make_line(
|
||||
0x02, 0, struct.pack(">H", newsegbase))
|
||||
segbase = newsegbase
|
||||
|
||||
output += self.make_line(0x00, addr, chunk)
|
||||
elif self.mode == 32:
|
||||
newsegbase = addr >> 16
|
||||
addr = addr & 0xFFFF
|
||||
|
||||
i += self.row_bytes
|
||||
start += self.row_bytes
|
||||
if newsegbase != segbase:
|
||||
output += self.make_line(
|
||||
0x04, 0, struct.pack(">H", newsegbase))
|
||||
segbase = newsegbase
|
||||
|
||||
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(0x00, addr, chunk)
|
||||
|
||||
output += self.make_line(0x01, 0, "")
|
||||
return output
|
||||
i += self.row_bytes
|
||||
start += self.row_bytes
|
||||
|
||||
def write_file(self, fname):
|
||||
f = open(fname, "w")
|
||||
f.write(self.write())
|
||||
f.close()
|
||||
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()
|
||||
|
126
stcgal/models.py
126
stcgal/models.py
@ -32,14 +32,38 @@ class MCUModelDatabase:
|
||||
MCUModel = collections.namedtuple("MCUModel", ["name", "magic", "total", "code", "eeprom"])
|
||||
|
||||
models = (
|
||||
MCUModel(name='STC15H4K08S4', magic=0xf601, total=65536, code=8192, eeprom=0),
|
||||
MCUModel(name='STC15H4K16S4', magic=0xf602, total=65536, code=16384, eeprom=0),
|
||||
MCUModel(name='STC15H4K24S4', magic=0xf603, total=65536, code=24576, eeprom=0),
|
||||
MCUModel(name='STC15H4K32S4', magic=0xf604, total=65536, code=32768, eeprom=0),
|
||||
MCUModel(name='STC15H4K40S4', magic=0xf605, total=65536, code=40960, eeprom=0),
|
||||
MCUModel(name='STC15H4K48S4', magic=0xf606, total=65536, code=49152, eeprom=0),
|
||||
MCUModel(name='STC15H4K56S4', magic=0xf607, total=65536, code=57344, eeprom=0),
|
||||
MCUModel(name='STC15H4K64S4', magic=0xf608, total=65536, code=65024, eeprom=0),
|
||||
MCUModel(name='STC8F8K08S4A10', magic=0xf611, total=65536, code=8192, eeprom=57344),
|
||||
MCUModel(name='STC8F8K16S4A10', magic=0xf612, total=65536, code=16384, eeprom=49152),
|
||||
MCUModel(name='STC8F8K24S4A10', magic=0xf613, total=65536, code=24576, eeprom=40960),
|
||||
MCUModel(name='STC8F8K32S4A10', magic=0xf614, total=65536, code=32768, eeprom=32768),
|
||||
MCUModel(name='STC8F8K40S4A10', magic=0xf615, total=65536, code=40960, eeprom=24576),
|
||||
MCUModel(name='STC8F8K48S4A10', magic=0xf616, total=65536, code=49152, eeprom=16384),
|
||||
MCUModel(name='STC8F8K56S4A10', magic=0xf617, total=65536, code=57344, eeprom=8192),
|
||||
MCUModel(name='STC8F8K64S4A10', magic=0xf618, total=65536, code=65024, eeprom=512),
|
||||
MCUModel(name='STC8A8K08S4A12', magic=0xf621, total=65536, code=8192, eeprom=57344),
|
||||
MCUModel(name='STC8A8K16S4A12', magic=0xf622, total=65536, code=16384, eeprom=49152),
|
||||
MCUModel(name='STC8A8K24S4A12', magic=0xf623, total=65536, code=24576, eeprom=40960),
|
||||
MCUModel(name='STC8A8K32S4A12', magic=0xf624, total=65536, code=32768, eeprom=32768),
|
||||
MCUModel(name='STC8A8K40S4A12', magic=0xf625, total=65536, code=40960, eeprom=24576),
|
||||
MCUModel(name='STC8A8K48S4A12', magic=0xf626, total=65536, code=49152, eeprom=16384),
|
||||
MCUModel(name='STC8A8K56S4A12', magic=0xf627, total=65536, code=57344, eeprom=8192),
|
||||
MCUModel(name='STC8A8K64S4A12', magic=0xf628, total=65536, code=65024, eeprom=512),
|
||||
MCUModel(name='STC8F2K08S4', magic=0xf631, total=65536, code=8192, eeprom=57344),
|
||||
MCUModel(name='STC8F2K16S4', magic=0xf632, total=65536, code=16384, eeprom=49152),
|
||||
MCUModel(name='STC8F2K24S4', magic=0xf633, total=65536, code=24576, eeprom=40960),
|
||||
MCUModel(name='STC8F2K32S4', magic=0xf634, total=65536, code=32768, eeprom=32768),
|
||||
MCUModel(name='STC8F2K40S4', magic=0xf635, total=65536, code=40960, eeprom=24576),
|
||||
MCUModel(name='STC8F2K48S4', magic=0xf636, total=65536, code=49152, eeprom=16384),
|
||||
MCUModel(name='STC8F2K56S4', magic=0xf637, total=65536, code=57344, eeprom=8192),
|
||||
MCUModel(name='STC8F2K64S4', magic=0xf638, total=65536, code=65024, eeprom=512),
|
||||
MCUModel(name='STC15H4K08S4', magic=0xf601, total=65536, code=8192, eeprom=57344),
|
||||
MCUModel(name='STC15H4K16S4', magic=0xf602, total=65536, code=16384, eeprom=49152),
|
||||
MCUModel(name='STC15H4K24S4', magic=0xf603, total=65536, code=24576, eeprom=40960),
|
||||
MCUModel(name='STC15H4K32S4', magic=0xf604, total=65536, code=32768, eeprom=32768),
|
||||
MCUModel(name='STC15H4K40S4', magic=0xf605, total=65536, code=40960, eeprom=24576),
|
||||
MCUModel(name='STC15H4K48S4', magic=0xf606, total=65536, code=49152, eeprom=16384),
|
||||
MCUModel(name='STC15H4K56S4', magic=0xf607, total=65536, code=57344, eeprom=8192),
|
||||
MCUModel(name='STC15H4K64S4', magic=0xf608, total=65536, code=65024, eeprom=512),
|
||||
MCUModel(name='STC15F2K08S2', magic=0xf401, total=65536, code=8192, eeprom=54272),
|
||||
MCUModel(name='STC15F2K16S2', magic=0xf402, total=65536, code=16384, eeprom=46080),
|
||||
MCUModel(name='STC15F2K24S2', magic=0xf403, total=65536, code=24576, eeprom=37888),
|
||||
@ -209,6 +233,9 @@ class MCUModelDatabase:
|
||||
MCUModel(name='STC15W1K08PWM', magic=0xf52d, total=65536, code=8192, eeprom=52224),
|
||||
MCUModel(name='STC15W1K16PWM', magic=0xf52e, total=65536, code=16384, eeprom=44032),
|
||||
MCUModel(name='STC15W1K20S', magic=0xf52f, total=65536, code=20480, eeprom=39936),
|
||||
MCUModel(name='STC15W1K20AS', magic=0xf534, total=65536, code=20480, eeprom=39936),
|
||||
MCUModel(name='STC15W1K32AS', magic=0xf535, total=65536, code=32768, eeprom=27648),
|
||||
MCUModel(name='STC15W1K48AS', magic=0xf536, total=65536, code=49152, eeprom=11264),
|
||||
MCUModel(name='STC15W2K32S2', magic=0xf530, total=65536, code=32768, eeprom=27648),
|
||||
MCUModel(name='STC15W2K48S2', magic=0xf531, total=65536, code=49152, eeprom=11264),
|
||||
MCUModel(name='STC15W2K32AS', magic=0xf532, total=65536, code=32768, eeprom=27648),
|
||||
@ -812,28 +839,28 @@ class MCUModelDatabase:
|
||||
MCUModel(name='STC12C5420', magic=0xe014, total=32768, code=20480, eeprom=12288),
|
||||
MCUModel(name='STC12C5424', magic=0xe018, total=32768, code=24576, eeprom=12288),
|
||||
MCUModel(name='STC12C5428', magic=0xe01c, total=32768, code=28672, eeprom=12288),
|
||||
MCUModel(name='STC12LE5401AD', magic=0xe0e1, total=32768, code=1024, eeprom=22016),
|
||||
MCUModel(name='STC12LE5402AD', magic=0xe0e2, total=32768, code=2048, eeprom=20992),
|
||||
MCUModel(name='STC12LE5404AD', magic=0xe0e4, total=32768, code=4096, eeprom=18944),
|
||||
MCUModel(name='STC12LE5406AD', magic=0xe0e6, total=32768, code=6144, eeprom=16896),
|
||||
MCUModel(name='STC12LE5408AD', magic=0xe0e8, total=32768, code=8192, eeprom=10752),
|
||||
MCUModel(name='STC12LE5410AD', magic=0xe0ea, total=32768, code=10240, eeprom=4608),
|
||||
MCUModel(name='STC12LE5412AD', magic=0xe0ec, total=32768, code=12288, eeprom=11776),
|
||||
MCUModel(name='STC12LE5401AD', magic=0xe0e1, total=32768, code=1024, eeprom=12288),
|
||||
MCUModel(name='STC12LE5402AD', magic=0xe0e2, total=32768, code=2048, eeprom=12288),
|
||||
MCUModel(name='STC12LE5404AD', magic=0xe0e4, total=32768, code=4096, eeprom=12288),
|
||||
MCUModel(name='STC12LE5406AD', magic=0xe0e6, total=32768, code=6144, eeprom=12288),
|
||||
MCUModel(name='STC12LE5408AD', magic=0xe0e8, total=32768, code=8192, eeprom=12288),
|
||||
MCUModel(name='STC12LE5410AD', magic=0xe0ea, total=32768, code=10240, eeprom=12288),
|
||||
MCUModel(name='STC12LE5412AD', magic=0xe0ec, total=32768, code=12288, eeprom=12288),
|
||||
MCUModel(name='STC12LE5416AD', magic=0xe0f0, total=32768, code=16384, eeprom=12288),
|
||||
MCUModel(name='STC12LE5420AD', magic=0xe0f4, total=32768, code=20480, eeprom=8192),
|
||||
MCUModel(name='STC12LE5424AD', magic=0xe0f8, total=32768, code=24576, eeprom=4096),
|
||||
MCUModel(name='STC12LE5428AD', magic=0xe0fc, total=32768, code=28672, eeprom=0),
|
||||
MCUModel(name='STC12LE5401', magic=0xe081, total=32768, code=1024, eeprom=22016),
|
||||
MCUModel(name='STC12LE5402', magic=0xe082, total=32768, code=2048, eeprom=20992),
|
||||
MCUModel(name='STC12LE5404', magic=0xe084, total=32768, code=4096, eeprom=18944),
|
||||
MCUModel(name='STC12LE5406', magic=0xe086, total=32768, code=6144, eeprom=16896),
|
||||
MCUModel(name='STC12LE5408', magic=0xe088, total=32768, code=8192, eeprom=10752),
|
||||
MCUModel(name='STC12LE5410', magic=0xe08a, total=32768, code=10240, eeprom=4608),
|
||||
MCUModel(name='STC12LE5412', magic=0xe08c, total=32768, code=12288, eeprom=11776),
|
||||
MCUModel(name='STC12LE5420AD', magic=0xe0f4, total=32768, code=20480, eeprom=12288),
|
||||
MCUModel(name='STC12LE5424AD', magic=0xe0f8, total=32768, code=24576, eeprom=12288),
|
||||
MCUModel(name='STC12LE5428AD', magic=0xe0fc, total=32768, code=28672, eeprom=12288),
|
||||
MCUModel(name='STC12LE5401', magic=0xe081, total=32768, code=1024, eeprom=12288),
|
||||
MCUModel(name='STC12LE5402', magic=0xe082, total=32768, code=2048, eeprom=12288),
|
||||
MCUModel(name='STC12LE5404', magic=0xe084, total=32768, code=4096, eeprom=12288),
|
||||
MCUModel(name='STC12LE5406', magic=0xe086, total=32768, code=6144, eeprom=12288),
|
||||
MCUModel(name='STC12LE5408', magic=0xe088, total=32768, code=8192, eeprom=12288),
|
||||
MCUModel(name='STC12LE5410', magic=0xe08a, total=32768, code=10240, eeprom=12288),
|
||||
MCUModel(name='STC12LE5412', magic=0xe08c, total=32768, code=12288, eeprom=12288),
|
||||
MCUModel(name='STC12LE5416', magic=0xe090, total=32768, code=16384, eeprom=12288),
|
||||
MCUModel(name='STC12LE5420', magic=0xe094, total=32768, code=20480, eeprom=8192),
|
||||
MCUModel(name='STC12LE5424', magic=0xe098, total=32768, code=24576, eeprom=4096),
|
||||
MCUModel(name='STC12LE5428', magic=0xe09c, total=32768, code=28672, eeprom=0),
|
||||
MCUModel(name='STC12LE5420', magic=0xe094, total=32768, code=20480, eeprom=12288),
|
||||
MCUModel(name='STC12LE5424', magic=0xe098, total=32768, code=24576, eeprom=12288),
|
||||
MCUModel(name='STC12LE5428', magic=0xe09c, total=32768, code=28672, eeprom=12288),
|
||||
MCUModel(name='STC12C1052AD', magic=0xf211, total=8192, code=1024, eeprom=5120),
|
||||
MCUModel(name='STC12C2052AD', magic=0xf212, total=8192, code=2048, eeprom=4096),
|
||||
MCUModel(name='STC12C3052AD', magic=0xf213, total=8192, code=3072, eeprom=3072),
|
||||
@ -946,6 +973,46 @@ class MCUModelDatabase:
|
||||
MCUModel(name='STC90LE513AD', magic=0xf18d, total=65536, code=53248, eeprom=10240),
|
||||
MCUModel(name='STC90LE514AD', magic=0xf18e, total=65536, code=57344, eeprom=6144),
|
||||
MCUModel(name='STC90LE516AD', magic=0xf190, total=65536, code=63488, eeprom=0),
|
||||
|
||||
# Warning, these definitions lack a valid eeprom size.
|
||||
MCUModel(name='STC15F04AD', magic=0xd444, total=4096, code=4096, eeprom=0),
|
||||
MCUModel(name='STC15F06AD', magic=0xd446, total=6144, code=6144, eeprom=0),
|
||||
MCUModel(name='STC15F08AD', magic=0xd448, total=8192, code=8192, eeprom=0),
|
||||
MCUModel(name='STC15F10AD', magic=0xd44a, total=10240, code=10240, eeprom=0),
|
||||
MCUModel(name='STC15F12AD', magic=0xd44c, total=12288, code=12288, eeprom=0),
|
||||
MCUModel(name='STC15F04CCP', magic=0xd434, total=4096, code=4096, eeprom=0),
|
||||
MCUModel(name='STC15F06CCP', magic=0xd436, total=6144, code=6144, eeprom=0),
|
||||
MCUModel(name='STC15F08CCP', magic=0xd438, total=8192, code=8192, eeprom=0),
|
||||
MCUModel(name='STC15F10CCP', magic=0xd43a, total=10240, code=10240, eeprom=0),
|
||||
MCUModel(name='STC15F12CCP', magic=0xd43c, total=12288, code=12288, eeprom=0),
|
||||
MCUModel(name='STC15F04', magic=0xd404, total=4096, code=4096, eeprom=0),
|
||||
MCUModel(name='STC15F06', magic=0xd406, total=6144, code=6144, eeprom=0),
|
||||
MCUModel(name='STC15F08', magic=0xd408, total=8192, code=8192, eeprom=0),
|
||||
MCUModel(name='STC15F10', magic=0xd40a, total=10240, code=10240, eeprom=0),
|
||||
MCUModel(name='STC15F12', magic=0xd40c, total=12288, code=12288, eeprom=0),
|
||||
MCUModel(name='IAP15F08AD', magic=0xd458, total=8192, code=8192, eeprom=0),
|
||||
MCUModel(name='IAP15F10AD', magic=0xd45a, total=10240, code=10240, eeprom=0),
|
||||
MCUModel(name='IAP15F12AD', magic=0xd45c, total=12288, code=12288, eeprom=0),
|
||||
MCUModel(name='IAP15F14AD', magic=0xd45e, total=14336, code=14336, eeprom=0),
|
||||
MCUModel(name='STC15L04AD', magic=0xd4c4, total=4096, code=4096, eeprom=0),
|
||||
MCUModel(name='STC15L06AD', magic=0xd4c6, total=6144, code=6144, eeprom=0),
|
||||
MCUModel(name='STC15L08AD', magic=0xd4c8, total=8192, code=8192, eeprom=0),
|
||||
MCUModel(name='STC15L10AD', magic=0xd4ca, total=10240, code=10240, eeprom=0),
|
||||
MCUModel(name='STC15L12AD', magic=0xd4cc, total=12288, code=12288, eeprom=0),
|
||||
MCUModel(name='STC15L04CCP', magic=0xd4b4, total=4096, code=4096, eeprom=0),
|
||||
MCUModel(name='STC15L06CCP', magic=0xd4b6, total=6144, code=6144, eeprom=0),
|
||||
MCUModel(name='STC15L08CCP', magic=0xd4b8, total=8192, code=8192, eeprom=0),
|
||||
MCUModel(name='STC15L10CCP', magic=0xd4ba, total=10240, code=10240, eeprom=0),
|
||||
MCUModel(name='STC15L12CCP', magic=0xd4bc, total=12288, code=12288, eeprom=0),
|
||||
MCUModel(name='STC15L04', magic=0xd484, total=4096, code=4096, eeprom=0),
|
||||
MCUModel(name='STC15L06', magic=0xd486, total=6144, code=6144, eeprom=0),
|
||||
MCUModel(name='STC15L08', magic=0xd488, total=8192, code=8192, eeprom=0),
|
||||
MCUModel(name='STC15L10', magic=0xd48a, total=10240, code=10240, eeprom=0),
|
||||
MCUModel(name='STC15L12', magic=0xd48c, total=12288, code=12288, eeprom=0),
|
||||
MCUModel(name='IAP15L08AD', magic=0xd4d8, total=8192, code=8192, eeprom=0),
|
||||
MCUModel(name='IAP15L10AD', magic=0xd4da, total=10240, code=10240, eeprom=0),
|
||||
MCUModel(name='IAP15L12AD', magic=0xd4dc, total=12288, code=12288, eeprom=0),
|
||||
MCUModel(name='IAP15L14AD', magic=0xd4de, total=14336, code=14336, eeprom=0),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@ -961,6 +1028,3 @@ class MCUModelDatabase:
|
||||
print(" Magic: %02X%02X" % (model.magic >> 8, model.magic & 0xff))
|
||||
print(" Code flash: %.1f KB" % (model.code / 1024.0))
|
||||
print(" EEPROM flash: %.1f KB" % (model.eeprom / 1024.0))
|
||||
|
||||
|
||||
|
||||
|
624
stcgal/options.py
Normal file
624
stcgal/options.py
Normal file
@ -0,0 +1,624 @@
|
||||
#
|
||||
# Copyright (c) 2013-2016 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 struct
|
||||
from abc import ABC
|
||||
from stcgal.utils import Utils
|
||||
|
||||
class BaseOption(ABC):
|
||||
"""Base class for options"""
|
||||
|
||||
def __init__(self):
|
||||
self.options = ()
|
||||
self.msr = None
|
||||
|
||||
def print(self):
|
||||
"""Print current configuration to standard output"""
|
||||
print("Target options:")
|
||||
for name, get_func, _ in self.options:
|
||||
print(" %s=%s" % (name, get_func()))
|
||||
|
||||
def set_option(self, name, value):
|
||||
"""Set value of a specific option"""
|
||||
for opt, _, set_func in self.options:
|
||||
if opt == name:
|
||||
print("Option %s=%s" % (name, value))
|
||||
set_func(value)
|
||||
return
|
||||
raise ValueError("unknown")
|
||||
|
||||
def get_option(self, name):
|
||||
"""Get option value for a specific option"""
|
||||
for opt, get_func, _ in self.options:
|
||||
if opt == name:
|
||||
return get_func(name)
|
||||
raise ValueError("unknown")
|
||||
|
||||
def get_msr(self):
|
||||
"""Get array of model-specific configuration registers"""
|
||||
return bytes(self.msr)
|
||||
|
||||
|
||||
class Stc89Option(BaseOption):
|
||||
"""Manipulation STC89 series option byte"""
|
||||
|
||||
def __init__(self, msr):
|
||||
super().__init__()
|
||||
self.msr = msr
|
||||
self.options = (
|
||||
("cpu_6t_enabled", self.get_t6, self.set_t6),
|
||||
("bsl_pindetect_enabled", self.get_pindetect, self.set_pindetect),
|
||||
("eeprom_erase_enabled", self.get_ee_erase, self.set_ee_erase),
|
||||
("clock_gain", self.get_clock_gain, self.set_clock_gain),
|
||||
("ale_enabled", self.get_ale, self.set_ale),
|
||||
("xram_enabled", self.get_xram, self.set_xram),
|
||||
("watchdog_por_enabled", self.get_watchdog, self.set_watchdog),
|
||||
)
|
||||
|
||||
def get_msr(self):
|
||||
return self.msr
|
||||
|
||||
def get_t6(self):
|
||||
return not bool(self.msr & 1)
|
||||
|
||||
def set_t6(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr &= 0xfe
|
||||
self.msr |= 0x01 if not bool(val) else 0x00
|
||||
|
||||
def get_pindetect(self):
|
||||
return not bool(self.msr & 4)
|
||||
|
||||
def set_pindetect(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr &= 0xfb
|
||||
self.msr |= 0x04 if not bool(val) else 0x00
|
||||
|
||||
def get_ee_erase(self):
|
||||
return not bool(self.msr & 8)
|
||||
|
||||
def set_ee_erase(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr &= 0xf7
|
||||
self.msr |= 0x08 if not bool(val) else 0x00
|
||||
|
||||
def get_clock_gain(self):
|
||||
gain = bool(self.msr & 16)
|
||||
return "high" if gain else "low"
|
||||
|
||||
def set_clock_gain(self, val):
|
||||
gains = {"low": 0, "high": 0x10}
|
||||
if val not in gains.keys():
|
||||
raise ValueError("must be one of %s" % list(gains.keys()))
|
||||
self.msr &= 0xef
|
||||
self.msr |= gains[val]
|
||||
|
||||
def get_ale(self):
|
||||
return bool(self.msr & 32)
|
||||
|
||||
def set_ale(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr &= 0xdf
|
||||
self.msr |= 0x20 if bool(val) else 0x00
|
||||
|
||||
def get_xram(self):
|
||||
return bool(self.msr & 64)
|
||||
|
||||
def set_xram(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr &= 0xbf
|
||||
self.msr |= 0x40 if bool(val) else 0x00
|
||||
|
||||
def get_watchdog(self):
|
||||
return not bool(self.msr & 128)
|
||||
|
||||
def set_watchdog(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr &= 0x7f
|
||||
self.msr |= 0x80 if not bool(val) else 0x00
|
||||
|
||||
|
||||
class Stc12AOption(BaseOption):
|
||||
"""Manipulate STC12A series option bytes"""
|
||||
|
||||
def __init__(self, msr):
|
||||
super().__init__()
|
||||
assert len(msr) == 4
|
||||
self.msr = bytearray(msr)
|
||||
|
||||
"""list of options and their handlers"""
|
||||
self.options = (
|
||||
("low_voltage_reset", self.get_low_voltage_detect, self.set_low_voltage_detect),
|
||||
("clock_source", self.get_clock_source, self.set_clock_source),
|
||||
("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),
|
||||
("eeprom_erase_enabled", self.get_ee_erase, self.set_ee_erase),
|
||||
("bsl_pindetect_enabled", self.get_pindetect, self.set_pindetect),
|
||||
)
|
||||
|
||||
def get_low_voltage_detect(self):
|
||||
lvd = bool(self.msr[3] & 64)
|
||||
return "high" if not lvd else "low"
|
||||
|
||||
def set_low_voltage_detect(self, val):
|
||||
lvds = {"low": 1, "high": 0}
|
||||
if val not in lvds.keys():
|
||||
raise ValueError("must be one of %s" % list(lvds.keys()))
|
||||
self.msr[3] &= 0xbf
|
||||
self.msr[3] |= lvds[val] << 6
|
||||
|
||||
def get_clock_source(self):
|
||||
source = bool(self.msr[0] & 2)
|
||||
return "external" if source else "internal"
|
||||
|
||||
def set_clock_source(self, val):
|
||||
sources = {"internal": 0, "external": 1}
|
||||
if val not in sources.keys():
|
||||
raise ValueError("must be one of %s" % list(sources.keys()))
|
||||
self.msr[0] &= 0xfd
|
||||
self.msr[0] |= sources[val] << 1
|
||||
|
||||
def get_watchdog(self):
|
||||
return not bool(self.msr[1] & 32)
|
||||
|
||||
def set_watchdog(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[1] &= 0xdf
|
||||
self.msr[1] |= 0x20 if not val else 0x00
|
||||
|
||||
def get_watchdog_idle(self):
|
||||
return not bool(self.msr[1] & 8)
|
||||
|
||||
def set_watchdog_idle(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[1] &= 0xf7
|
||||
self.msr[1] |= 0x08 if not val else 0x00
|
||||
|
||||
def get_watchdog_prescale(self):
|
||||
return 2 ** (((self.msr[1]) & 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[1] &= 0xf8
|
||||
self.msr[1] |= wd_vals[val]
|
||||
|
||||
def get_ee_erase(self):
|
||||
return not bool(self.msr[2] & 2)
|
||||
|
||||
def set_ee_erase(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[2] &= 0xfd
|
||||
self.msr[2] |= 0x02 if not val else 0x00
|
||||
|
||||
def get_pindetect(self):
|
||||
return not bool(self.msr[2] & 1)
|
||||
|
||||
def set_pindetect(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[2] &= 0xfe
|
||||
self.msr[2] |= 0x01 if not val else 0x00
|
||||
|
||||
|
||||
class Stc12Option(BaseOption):
|
||||
"""Manipulate STC10/11/12 series option bytes"""
|
||||
|
||||
def __init__(self, msr):
|
||||
super().__init__()
|
||||
assert len(msr) == 4
|
||||
self.msr = bytearray(msr)
|
||||
|
||||
"""list of options and their handlers"""
|
||||
self.options = (
|
||||
("reset_pin_enabled", self.get_reset_pin_enabled, self.set_reset_pin_enabled),
|
||||
("low_voltage_reset", self.get_low_voltage_detect, self.set_low_voltage_detect),
|
||||
("oscillator_stable_delay", self.get_osc_stable_delay, self.set_osc_stable_delay),
|
||||
("por_reset_delay", self.get_por_delay, self.set_por_delay),
|
||||
("clock_gain", self.get_clock_gain, self.set_clock_gain),
|
||||
("clock_source", self.get_clock_source, self.set_clock_source),
|
||||
("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),
|
||||
("eeprom_erase_enabled", self.get_ee_erase, self.set_ee_erase),
|
||||
("bsl_pindetect_enabled", self.get_pindetect, self.set_pindetect),
|
||||
)
|
||||
|
||||
def get_reset_pin_enabled(self):
|
||||
return bool(self.msr[0] & 1)
|
||||
|
||||
def set_reset_pin_enabled(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[0] &= 0xfe
|
||||
self.msr[0] |= 0x01 if bool(val) else 0x00
|
||||
|
||||
def get_low_voltage_detect(self):
|
||||
return not bool(self.msr[0] & 64)
|
||||
|
||||
def set_low_voltage_detect(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[0] &= 0xbf
|
||||
self.msr[0] |= 0x40 if not val else 0x00
|
||||
|
||||
def get_osc_stable_delay(self):
|
||||
return 2 ** (((self.msr[0] >> 4) & 0x03) + 12)
|
||||
|
||||
def set_osc_stable_delay(self, val):
|
||||
val = Utils.to_int(val)
|
||||
osc_vals = {4096: 0, 8192: 1, 16384: 2, 32768: 3}
|
||||
if val not in osc_vals.keys():
|
||||
raise ValueError("must be one of %s" % list(osc_vals.keys()))
|
||||
self.msr[0] &= 0xcf
|
||||
self.msr[0] |= osc_vals[val] << 4
|
||||
|
||||
def get_por_delay(self):
|
||||
delay = not bool(self.msr[1] & 128)
|
||||
return "long" if delay else "short"
|
||||
|
||||
def set_por_delay(self, val):
|
||||
delays = {"short": 1, "long": 0}
|
||||
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_clock_gain(self):
|
||||
gain = bool(self.msr[1] & 64)
|
||||
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] &= 0xbf
|
||||
self.msr[1] |= gains[val] << 6
|
||||
|
||||
def get_clock_source(self):
|
||||
source = bool(self.msr[1] & 2)
|
||||
return "external" if source else "internal"
|
||||
|
||||
def set_clock_source(self, val):
|
||||
sources = {"internal": 0, "external": 1}
|
||||
if val not in sources.keys():
|
||||
raise ValueError("must be one of %s" % list(sources.keys()))
|
||||
self.msr[1] &= 0xfd
|
||||
self.msr[1] |= sources[val] << 1
|
||||
|
||||
def get_watchdog(self):
|
||||
return not bool(self.msr[2] & 32)
|
||||
|
||||
def set_watchdog(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[2] &= 0xdf
|
||||
self.msr[2] |= 0x20 if not val else 0x00
|
||||
|
||||
def get_watchdog_idle(self):
|
||||
return not bool(self.msr[2] & 8)
|
||||
|
||||
def set_watchdog_idle(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[2] &= 0xf7
|
||||
self.msr[2] |= 0x08 if not val else 0x00
|
||||
|
||||
def get_watchdog_prescale(self):
|
||||
return 2 ** (((self.msr[2]) & 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[2] &= 0xf8
|
||||
self.msr[2] |= wd_vals[val]
|
||||
|
||||
def get_ee_erase(self):
|
||||
return not bool(self.msr[3] & 2)
|
||||
|
||||
def set_ee_erase(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[3] &= 0xfd
|
||||
self.msr[3] |= 0x02 if not val else 0x00
|
||||
|
||||
def get_pindetect(self):
|
||||
return not bool(self.msr[3] & 1)
|
||||
|
||||
def set_pindetect(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[3] &= 0xfe
|
||||
self.msr[3] |= 0x01 if not val else 0x00
|
||||
|
||||
|
||||
class Stc15AOption(BaseOption):
|
||||
def __init__(self, msr):
|
||||
super().__init__()
|
||||
assert len(msr) == 13
|
||||
self.msr = bytearray(msr)
|
||||
|
||||
self.options = (
|
||||
("reset_pin_enabled", self.get_reset_pin_enabled, self.set_reset_pin_enabled),
|
||||
("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_lvd_inhibit", self.get_eeprom_lvd, self.set_eeprom_lvd),
|
||||
("eeprom_erase_enabled", self.get_ee_erase, self.set_ee_erase),
|
||||
("bsl_pindetect_enabled", self.get_pindetect, self.set_pindetect),
|
||||
)
|
||||
|
||||
def set_trim(self, val):
|
||||
self.msr[3:5] = struct.pack(">H", val)
|
||||
|
||||
def get_reset_pin_enabled(self):
|
||||
return bool(self.msr[0] & 16)
|
||||
|
||||
def set_reset_pin_enabled(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[0] &= 0xef
|
||||
self.msr[0] |= 0x10 if bool(val) else 0x00
|
||||
|
||||
def get_watchdog(self):
|
||||
return not bool(self.msr[2] & 32)
|
||||
|
||||
def set_watchdog(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[2] &= 0xdf
|
||||
self.msr[2] |= 0x20 if not val else 0x00
|
||||
|
||||
def get_watchdog_idle(self):
|
||||
return not bool(self.msr[2] & 8)
|
||||
|
||||
def set_watchdog_idle(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[2] &= 0xf7
|
||||
self.msr[2] |= 0x08 if not val else 0x00
|
||||
|
||||
def get_watchdog_prescale(self):
|
||||
return 2 ** (((self.msr[2]) & 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[2] &= 0xf8
|
||||
self.msr[2] |= wd_vals[val]
|
||||
|
||||
def get_lvrs(self):
|
||||
return bool(self.msr[1] & 64)
|
||||
|
||||
def set_lvrs(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[1] &= 0xbf
|
||||
self.msr[1] |= 0x40 if val else 0x00
|
||||
|
||||
def get_eeprom_lvd(self):
|
||||
return bool(self.msr[1] & 128)
|
||||
|
||||
def set_eeprom_lvd(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[1] &= 0x7f
|
||||
self.msr[1] |= 0x80 if val else 0x00
|
||||
|
||||
def get_low_voltage(self):
|
||||
return self.msr[1] & 0x07
|
||||
|
||||
def set_low_voltage(self, val):
|
||||
val = Utils.to_int(val)
|
||||
if val not in range(0, 8):
|
||||
raise ValueError("must be one of %s" % list(range(0, 8)))
|
||||
self.msr[1] &= 0xf8
|
||||
self.msr[1] |= val
|
||||
|
||||
def get_ee_erase(self):
|
||||
return not bool(self.msr[12] & 2)
|
||||
|
||||
def set_ee_erase(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[12] &= 0xfd
|
||||
self.msr[12] |= 0x02 if not val else 0x00
|
||||
|
||||
def get_pindetect(self):
|
||||
return not bool(self.msr[12] & 1)
|
||||
|
||||
def set_pindetect(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[12] &= 0xfe
|
||||
self.msr[12] |= 0x01 if not val else 0x00
|
||||
|
||||
|
||||
class Stc15Option(BaseOption):
|
||||
def __init__(self, msr):
|
||||
super().__init__()
|
||||
assert len(msr) >= 4
|
||||
self.msr = bytearray(msr)
|
||||
|
||||
self.options = (
|
||||
("reset_pin_enabled", self.get_reset_pin_enabled, self.set_reset_pin_enabled),
|
||||
("clock_source", self.get_clock_source, self.set_clock_source),
|
||||
("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_lvd_inhibit", self.get_eeprom_lvd, self.set_eeprom_lvd),
|
||||
("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_p33_state, self.set_p33_state),
|
||||
("uart2_passthrough", self.get_uart_passthrough, self.set_uart_passthrough),
|
||||
("uart2_pin_mode", self.get_uart_pin_mode, self.set_uart_pin_mode),
|
||||
)
|
||||
|
||||
if len(msr) > 4:
|
||||
self.options += ("cpu_core_voltage", self.get_core_voltage, self.set_core_voltage),
|
||||
|
||||
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_source(self):
|
||||
source = bool(self.msr[2] & 0x01)
|
||||
return "internal" if source else "external"
|
||||
|
||||
def set_clock_source(self, val):
|
||||
sources = {"internal": 1, "external": 0}
|
||||
if val not in sources.keys():
|
||||
raise ValueError("must be one of %s" % list(sources.keys()))
|
||||
self.msr[2] &= 0xfe
|
||||
self.msr[2] |= sources[val]
|
||||
|
||||
def get_clock_gain(self):
|
||||
gain = bool(self.msr[2] & 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[2] &= 0xfd
|
||||
self.msr[2] |= gains[val] << 1
|
||||
|
||||
def get_watchdog(self):
|
||||
return not bool(self.msr[0] & 32)
|
||||
|
||||
def set_watchdog(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[0] &= 0xdf
|
||||
self.msr[0] |= 0x20 if not val else 0x00
|
||||
|
||||
def get_watchdog_idle(self):
|
||||
return not bool(self.msr[0] & 8)
|
||||
|
||||
def set_watchdog_idle(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[0] &= 0xf7
|
||||
self.msr[0] |= 0x08 if not val else 0x00
|
||||
|
||||
def get_watchdog_prescale(self):
|
||||
return 2 ** (((self.msr[0]) & 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[0] &= 0xf8
|
||||
self.msr[0] |= wd_vals[val]
|
||||
|
||||
def get_lvrs(self):
|
||||
return not bool(self.msr[1] & 64)
|
||||
|
||||
def set_lvrs(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[1] &= 0xbf
|
||||
self.msr[1] |= 0x40 if not val else 0x00
|
||||
|
||||
def get_eeprom_lvd(self):
|
||||
return bool(self.msr[1] & 128)
|
||||
|
||||
def set_eeprom_lvd(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[1] &= 0x7f
|
||||
self.msr[1] |= 0x80 if val else 0x00
|
||||
|
||||
def get_low_voltage(self):
|
||||
return self.msr[1] & 0x07
|
||||
|
||||
def set_low_voltage(self, val):
|
||||
val = Utils.to_int(val)
|
||||
if val not in range(0, 8):
|
||||
raise ValueError("must be one of %s" % list(range(0, 8)))
|
||||
self.msr[1] &= 0xf8
|
||||
self.msr[1] |= val
|
||||
|
||||
def get_ee_erase(self):
|
||||
return bool(self.msr[3] & 2)
|
||||
|
||||
def set_ee_erase(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[3] &= 0xfd
|
||||
self.msr[3] |= 0x02 if val else 0x00
|
||||
|
||||
def get_pindetect(self):
|
||||
return not bool(self.msr[3] & 1)
|
||||
|
||||
def set_pindetect(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[3] &= 0xfe
|
||||
self.msr[3] |= 0x01 if not val else 0x00
|
||||
|
||||
def get_por_delay(self):
|
||||
delay = bool(self.msr[2] & 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[2] &= 0x7f
|
||||
self.msr[2] |= delays[val] << 7
|
||||
|
||||
def get_p33_state(self):
|
||||
return "high" if self.msr[2] & 0x08 else "low"
|
||||
|
||||
def set_p33_state(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[2] &= 0xf7
|
||||
self.msr[2] |= 0x08 if val else 0x00
|
||||
|
||||
def get_uart_passthrough(self):
|
||||
return bool(self.msr[2] & 0x40)
|
||||
|
||||
def set_uart_passthrough(self, val):
|
||||
val = Utils.to_bool(val)
|
||||
self.msr[2] &= 0xbf
|
||||
self.msr[2] |= 0x40 if val else 0x00
|
||||
|
||||
def get_uart_pin_mode(self):
|
||||
return "push-pull" if bool(self.msr[2] & 0x20) else "normal"
|
||||
|
||||
def set_uart_pin_mode(self, val):
|
||||
delays = {"normal": 0, "push-pull": 1}
|
||||
if val not in delays.keys():
|
||||
raise ValueError("must be one of %s" % list(delays.keys()))
|
||||
self.msr[2] &= 0xdf
|
||||
self.msr[2] |= 0x20 if val else 0x00
|
||||
|
||||
def get_core_voltage(self):
|
||||
if self.msr[4] == 0xea: return "low"
|
||||
elif self.msr[4] == 0xf7: return "mid"
|
||||
elif self.msr[4] == 0xfd: return "high"
|
||||
else: return "unknown"
|
||||
|
||||
def set_core_voltage(self, val):
|
||||
volt_vals = {"low": 0xea, "mid": 0xf7, "high": 0xfd}
|
||||
if val not in volt_vals.keys():
|
||||
raise ValueError("must be one of %s" % list(volt_vals.keys()))
|
||||
self.msr[4] = volt_vals[val]
|
1442
stcgal/protocols.py
1442
stcgal/protocols.py
File diff suppressed because it is too large
Load Diff
@ -19,31 +19,39 @@
|
||||
# SOFTWARE.
|
||||
#
|
||||
|
||||
import serial
|
||||
import argparse
|
||||
import serial
|
||||
|
||||
class Utils:
|
||||
"""Common utility functions"""
|
||||
|
||||
@classmethod
|
||||
def to_bool(self, val):
|
||||
def to_bool(cls, val):
|
||||
"""make sensible boolean from string or other type value"""
|
||||
|
||||
if isinstance(val, bool): return val
|
||||
if isinstance(val, int): return bool(val)
|
||||
if len(val) == 0: return False
|
||||
return True if val[0].lower() == "t" or val[0] == "1" else False
|
||||
if not val:
|
||||
return False
|
||||
if isinstance(val, bool):
|
||||
return val
|
||||
elif isinstance(val, int):
|
||||
return bool(val)
|
||||
else:
|
||||
return True if val[0].lower() == "t" or val[0] == "1" else False
|
||||
|
||||
@classmethod
|
||||
def to_int(self, val):
|
||||
def to_int(cls, val):
|
||||
"""make int from any value, nice error message if not possible"""
|
||||
|
||||
try: return int(val, 0)
|
||||
except: raise ValueError("invalid integer")
|
||||
try:
|
||||
return int(val, 0)
|
||||
except:
|
||||
raise ValueError("invalid integer")
|
||||
|
||||
@classmethod
|
||||
def hexstr(self, bytestr, sep=""):
|
||||
def hexstr(cls, bytestr, sep=""):
|
||||
"""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)])
|
||||
|
||||
|
||||
class BaudType:
|
||||
@ -55,5 +63,5 @@ class BaudType:
|
||||
raise argparse.ArgumentTypeError("illegal baudrate")
|
||||
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]
|
119
tests/test_fuzzing.py
Normal file
119
tests/test_fuzzing.py
Normal file
@ -0,0 +1,119 @@
|
||||
#
|
||||
# Copyright (c) 2017 Grigori Goronzy <greg@chown.ath.cx>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
|
||||
"""Tests with fuzzing of input data"""
|
||||
|
||||
import random
|
||||
import sys
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
import yaml
|
||||
import stcgal.frontend
|
||||
import stcgal.protocols
|
||||
from tests.test_program import get_default_opts, convert_to_bytes
|
||||
|
||||
class ByteArrayFuzzer:
|
||||
"""Fuzzer for byte arrays"""
|
||||
|
||||
def __init__(self):
|
||||
self.rng = random.Random()
|
||||
self.cut_propability = 0.01 # probability for cutting off an array early
|
||||
self.cut_min = 0 # minimum cut amount
|
||||
self.cut_max = sys.maxsize # maximum cut amount
|
||||
self.bitflip_probability = 0.0001 # probability for flipping a bit
|
||||
self.randomize_probability = 0.001 # probability for randomizing a char
|
||||
|
||||
def fuzz(self, inp):
|
||||
"""Fuzz an array of bytes according to predefined settings"""
|
||||
arr = bytearray(inp)
|
||||
arr = self.cut_off(arr)
|
||||
self.randomize(arr)
|
||||
return bytes(arr)
|
||||
|
||||
def randomize(self, arr):
|
||||
"""Randomize array contents with bitflips and random bytes"""
|
||||
for i, _ in enumerate(arr):
|
||||
for j in range(8):
|
||||
if self.rng.random() < self.bitflip_probability:
|
||||
arr[i] ^= (1 << j)
|
||||
if self.rng.random() < self.randomize_probability:
|
||||
arr[i] = self.rng.getrandbits(8)
|
||||
|
||||
def cut_off(self, arr):
|
||||
"""Cut off data from end of array"""
|
||||
if self.rng.random() < self.cut_propability:
|
||||
cut_limit = min(len(arr), self.cut_max)
|
||||
cut_len = self.rng.randrange(self.cut_min, cut_limit)
|
||||
arr = arr[0:len(arr) - cut_len]
|
||||
return arr
|
||||
|
||||
class TestProgramFuzzed(unittest.TestCase):
|
||||
"""Special programming cycle tests that use a fuzzing approach"""
|
||||
|
||||
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
|
||||
@patch("stcgal.protocols.Stc89Protocol.write_packet")
|
||||
@patch("stcgal.protocols.serial.Serial", autospec=True)
|
||||
@patch("stcgal.protocols.time.sleep")
|
||||
@patch("sys.stdout")
|
||||
@patch("sys.stderr")
|
||||
def test_program_fuzz(self, err, out, sleep_mock, serial_mock, write_mock, read_mock):
|
||||
"""Test programming cycles with fuzzing enabled"""
|
||||
yml = [
|
||||
"./tests/iap15f2k61s2.yml",
|
||||
"./tests/stc12c2052ad.yml",
|
||||
"./tests/stc15w4k56s4.yml",
|
||||
"./tests/stc12c5a60s2.yml",
|
||||
"./tests/stc89c52rc.yml",
|
||||
"./tests/stc15l104w.yml",
|
||||
"./tests/stc15f104e.yml",
|
||||
]
|
||||
fuzzer = ByteArrayFuzzer()
|
||||
fuzzer.cut_propability = 0.01
|
||||
fuzzer.bitflip_probability = 0.005
|
||||
fuzzer.rng = random.Random(1)
|
||||
for y in yml:
|
||||
with self.subTest(msg="trace {}".format(y)):
|
||||
self.single_fuzz(y, serial_mock, fuzzer, read_mock, err, out,
|
||||
sleep_mock, write_mock)
|
||||
|
||||
def single_fuzz(self, yml, serial_mock, fuzzer, read_mock, err, out, sleep_mock, write_mock):
|
||||
"""Test a single programming cycle with fuzzing"""
|
||||
with open(yml) as test_file:
|
||||
test_data = yaml.load(test_file.read())
|
||||
for _ in range(1000):
|
||||
with self.subTest():
|
||||
opts = get_default_opts()
|
||||
opts.protocol = test_data["protocol"]
|
||||
opts.code_image.read.return_value = bytes(test_data["code_data"])
|
||||
serial_mock.return_value.inWaiting.return_value = 1
|
||||
fuzzed_responses = []
|
||||
for arr in convert_to_bytes(test_data["responses"]):
|
||||
fuzzed_responses.append(fuzzer.fuzz(arr))
|
||||
read_mock.side_effect = fuzzed_responses
|
||||
gal = stcgal.frontend.StcGal(opts)
|
||||
self.assertGreaterEqual(gal.run(), 0)
|
||||
err.reset_mock()
|
||||
out.reset_mock()
|
||||
sleep_mock.reset_mock()
|
||||
serial_mock.reset_mock()
|
||||
write_mock.reset_mock()
|
||||
read_mock.reset_mock()
|
135
tests/test_program.py
Normal file
135
tests/test_program.py
Normal file
@ -0,0 +1,135 @@
|
||||
#
|
||||
# Copyright (c) 2017 Grigori Goronzy <greg@chown.ath.cx>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
|
||||
"""Tests that simulate a whole programming cycle"""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
import yaml
|
||||
import stcgal.frontend
|
||||
import stcgal.protocols
|
||||
|
||||
def convert_to_bytes(list_of_lists):
|
||||
"""Convert lists of integer lists to list of byte lists"""
|
||||
return [bytes(x) for x in list_of_lists]
|
||||
|
||||
def get_default_opts():
|
||||
"""Get a default preconfigured option object"""
|
||||
opts = unittest.mock.MagicMock()
|
||||
opts.protocol = "stc89"
|
||||
opts.autoreset = False
|
||||
opts.port = ""
|
||||
opts.baud = 19200
|
||||
opts.handshake = 9600
|
||||
opts.trim = 22118
|
||||
opts.eeprom_image = None
|
||||
opts.debug = False
|
||||
opts.code_image.name = "test.bin"
|
||||
opts.code_image.read.return_value = b"123456789"
|
||||
return opts
|
||||
|
||||
class ProgramTests(unittest.TestCase):
|
||||
"""Test MCU programming cycles for different families, based on traces"""
|
||||
|
||||
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
|
||||
@patch("stcgal.protocols.Stc89Protocol.write_packet")
|
||||
@patch("stcgal.protocols.serial.Serial", autospec=True)
|
||||
@patch("stcgal.protocols.time.sleep")
|
||||
@patch("sys.stdout")
|
||||
def test_program_stc89(self, out, sleep_mock, serial_mock, write_mock, read_mock):
|
||||
"""Test a programming cycle with STC89 protocol"""
|
||||
self._program_yml("./tests/stc89c52rc.yml", serial_mock, read_mock)
|
||||
|
||||
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
|
||||
@patch("stcgal.protocols.Stc89Protocol.write_packet")
|
||||
@patch("stcgal.protocols.serial.Serial", autospec=True)
|
||||
@patch("stcgal.protocols.time.sleep")
|
||||
@patch("sys.stdout")
|
||||
def test_program_stc12(self, out, sleep_mock, serial_mock, write_mock, read_mock):
|
||||
"""Test a programming cycle with STC12 protocol"""
|
||||
self._program_yml("./tests/stc12c5a60s2.yml", serial_mock, read_mock)
|
||||
|
||||
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
|
||||
@patch("stcgal.protocols.Stc89Protocol.write_packet")
|
||||
@patch("stcgal.protocols.serial.Serial", autospec=True)
|
||||
@patch("stcgal.protocols.time.sleep")
|
||||
@patch("sys.stdout")
|
||||
def test_program_stc12a(self, out, sleep_mock, serial_mock, write_mock, read_mock):
|
||||
"""Test a programming cycle with STC12A protocol"""
|
||||
self._program_yml("./tests/stc12c2052ad.yml", serial_mock, read_mock)
|
||||
|
||||
def test_program_stc12b(self):
|
||||
"""Test a programming cycle with STC12B protocol"""
|
||||
self.skipTest("trace missing")
|
||||
|
||||
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
|
||||
@patch("stcgal.protocols.Stc89Protocol.write_packet")
|
||||
@patch("stcgal.protocols.serial.Serial", autospec=True)
|
||||
@patch("stcgal.protocols.time.sleep")
|
||||
@patch("sys.stdout")
|
||||
def test_program_stc15f2(self, out, sleep_mock, serial_mock, write_mock, read_mock):
|
||||
"""Test a programming cycle with STC15 protocol, F2 series"""
|
||||
self._program_yml("./tests/iap15f2k61s2.yml", serial_mock, read_mock)
|
||||
|
||||
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
|
||||
@patch("stcgal.protocols.Stc89Protocol.write_packet")
|
||||
@patch("stcgal.protocols.serial.Serial", autospec=True)
|
||||
@patch("stcgal.protocols.time.sleep")
|
||||
@patch("sys.stdout")
|
||||
def test_program_stc15w4(self, out, sleep_mock, serial_mock, write_mock, read_mock):
|
||||
"""Test a programming cycle with STC15 protocol, W4 series"""
|
||||
self._program_yml("./tests/stc15w4k56s4.yml", serial_mock, read_mock)
|
||||
|
||||
@unittest.skip("trace is broken")
|
||||
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
|
||||
@patch("stcgal.protocols.Stc89Protocol.write_packet")
|
||||
@patch("stcgal.protocols.serial.Serial", autospec=True)
|
||||
@patch("stcgal.protocols.time.sleep")
|
||||
@patch("sys.stdout")
|
||||
def test_program_stc15a(self, out, sleep_mock, serial_mock, write_mock, read_mock):
|
||||
"""Test a programming cycle with STC15A protocol"""
|
||||
self._program_yml("./tests/stc15f104e.yml", serial_mock, read_mock)
|
||||
|
||||
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
|
||||
@patch("stcgal.protocols.Stc89Protocol.write_packet")
|
||||
@patch("stcgal.protocols.serial.Serial", autospec=True)
|
||||
@patch("stcgal.protocols.time.sleep")
|
||||
@patch("sys.stdout")
|
||||
def test_program_stc15l1(self, out, sleep_mock, serial_mock, write_mock, read_mock):
|
||||
"""Test a programming cycle with STC15 protocol, L1 series"""
|
||||
self._program_yml("./tests/stc15l104w.yml", serial_mock, read_mock)
|
||||
|
||||
def test_program_stc15w4_usb(self):
|
||||
"""Test a programming cycle with STC15W4 USB protocol"""
|
||||
self.skipTest("USB not supported yet, trace missing")
|
||||
|
||||
def _program_yml(self, yml, serial_mock, read_mock):
|
||||
"""Program MCU with data from YAML file"""
|
||||
with open(yml) as test_file:
|
||||
test_data = yaml.load(test_file.read())
|
||||
opts = get_default_opts()
|
||||
opts.protocol = test_data["protocol"]
|
||||
opts.code_image.read.return_value = bytes(test_data["code_data"])
|
||||
serial_mock.return_value.inWaiting.return_value = 1
|
||||
read_mock.side_effect = convert_to_bytes(test_data["responses"])
|
||||
gal = stcgal.frontend.StcGal(opts)
|
||||
self.assertEqual(gal.run(), 0)
|
76
tests/test_utils.py
Normal file
76
tests/test_utils.py
Normal file
@ -0,0 +1,76 @@
|
||||
#
|
||||
# Copyright (c) 2017 Grigori Goronzy <greg@chown.ath.cx>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
|
||||
"""Tests for utility functions and other misc parts"""
|
||||
|
||||
import argparse
|
||||
import unittest
|
||||
from stcgal.utils import Utils, BaudType
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
"""Test for utility functions in the Utils class"""
|
||||
|
||||
def test_to_bool(self):
|
||||
"""Test special utility function for bool conversion"""
|
||||
self.assertTrue(Utils.to_bool(True))
|
||||
self.assertTrue(Utils.to_bool("true"))
|
||||
self.assertTrue(Utils.to_bool("True"))
|
||||
self.assertTrue(Utils.to_bool("t"))
|
||||
self.assertTrue(Utils.to_bool("T"))
|
||||
self.assertTrue(Utils.to_bool(1))
|
||||
self.assertTrue(Utils.to_bool(-1))
|
||||
self.assertFalse(Utils.to_bool(0))
|
||||
self.assertFalse(Utils.to_bool(None))
|
||||
self.assertFalse(Utils.to_bool("false"))
|
||||
self.assertFalse(Utils.to_bool("False"))
|
||||
self.assertFalse(Utils.to_bool("f"))
|
||||
self.assertFalse(Utils.to_bool("F"))
|
||||
self.assertFalse(Utils.to_bool(""))
|
||||
|
||||
def test_to_int(self):
|
||||
"""Test wrapped integer conversion"""
|
||||
self.assertEqual(Utils.to_int("2"), 2)
|
||||
self.assertEqual(Utils.to_int("0x10"), 16)
|
||||
with self.assertRaises(ValueError):
|
||||
Utils.to_int("a")
|
||||
with self.assertRaises(ValueError):
|
||||
Utils.to_int("")
|
||||
with self.assertRaises(ValueError):
|
||||
Utils.to_int(None)
|
||||
|
||||
def test_hexstr(self):
|
||||
"""Test byte array formatter"""
|
||||
self.assertEqual(Utils.hexstr([10]), "0A")
|
||||
self.assertEqual(Utils.hexstr([1, 2, 3]), "010203")
|
||||
with self.assertRaises(Exception):
|
||||
Utils.hexstr([400, 500])
|
||||
|
||||
class TestBaudType(unittest.TestCase):
|
||||
"""Test BaudType class"""
|
||||
|
||||
def test_create_baud_type(self):
|
||||
"""Test creation of BaudType instances"""
|
||||
baud_type = BaudType()
|
||||
self.assertEqual(baud_type("2400"), 2400)
|
||||
self.assertEqual(baud_type("115200"), 115200)
|
||||
with self.assertRaises(argparse.ArgumentTypeError):
|
||||
baud_type("2374882")
|
Reference in New Issue
Block a user