diff --git a/doc/reverse-engineering/stc89a-c52rc.txt b/doc/reverse-engineering/stc89a-c52rc.txt new file mode 100644 index 0000000..d548649 --- /dev/null +++ b/doc/reverse-engineering/stc89a-c52rc.txt @@ -0,0 +1,80 @@ +001046: Read (UP): 2021-01-09 15:37:44.9125552 +0.0085328 + 46 b9 68 00 29 50 fd 84 1e 11 4d 05 8e 96 27 7c F.h.)P....M...'| + ^ + cpu_6t + 04 47 01 7f 40 81 72 43 00 f0 51 05 80 00 ff ff .G..@.rC..Q..... + ^ ^ ^ ^ + freq_counte | | | + bl_version | + bl_stepping | + bl_minor + + + ff ff 38 20 20 02 19 60 0d a0 16 ..8 ..`... + + +001057: Write (DOWN): 2021-01-09 15:37:44.9814096 +0.0129872 + 46 b9 6a 00 0a 01 ff fd 82 02 f3 16 F.j......... + ^ + handshake + + +001072: Read (UP): 2021-01-09 15:37:45.0836096 +0.0115168 + 46 b9 68 00 07 01 00 70 16 F.h....p. + +001113: Write (DOWN): 2021-01-09 15:37:45.1352048 +0.0171904 + 46 b9 6a 00 0b 05 00 00 46 b9 01 79 16 F.j.....F..y. + ^ + ping-pong + + +001116: Read (UP): 2021-01-09 15:37:45.1392768 +0.0017200 + 46 b9 68 00 07 05 00 74 16 F.h....t. + + +001127: Write (DOWN): 2021-01-09 15:37:45.1502464 +0.0109472 + 46 b9 6a 00 0b 03 00 00 46 b9 01 77 16 F.j.....F..w. + ^ + erase_flash ? + + +001170: Read (UP): 2021-01-09 15:37:45.4729040 +0.0099696 + 46 b9 68 00 0e 03 f0 51 c5 f2 06 7c 14 04 07 16 F.h....Q...|.... + ^ + mcu id + + +001181: Write (DOWN): 2021-01-09 15:37:45.4791856 +0.0062576 + 46 b9 6a 00 8b 32 00 00 46 b9 02 00 08 12 00 3f F.j..2..F......? + ^ ^ + write address + write data + 80 fe 75 81 07 12 00 4c e5 82 60 03 02 00 03 e4 ..u....L..`..... + 78 ff f6 d8 fd 02 00 03 ae 82 af 83 8e 04 8f 05 x............... + 1e be ff 01 1f ec 4d 60 0f 7c 90 7d 01 1c bc ff ......M`.|.}.... + 01 1d ec 4d 70 f7 80 e4 22 90 03 e8 12 00 1e e5 ...Mp..."....... + 80 f4 f5 80 80 f3 75 82 00 22 ff ff ff ff ff ff ......u.."...... + ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ + ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ + ff ff ff ff ff ff ff ff ff ff 52 f9 16 ..........R.. + + +001188: Read (UP): 2021-01-09 15:37:45.5301744 +0.0047088 + 46 b9 68 00 08 02 54 00 c6 16 F.h...T... + ^ + write done + + +001199: Write (DOWN): 2021-01-09 15:37:45.5575264 +0.0273248 + 46 b9 6a 00 0c 04 00 00 46 b9 fd 02 76 16 F.j.....F...v. + ^ + write msr + + +001202: Read (UP): 2021-01-09 15:37:45.5625296 +0.0018304 + 46 b9 68 00 08 04 54 00 c8 16 F.h...T... +unknown + +001213: Write (DOWN): 2021-01-09 15:37:45.5712992 +0.0087472 + 46 b9 6a 00 07 ff 01 70 16 F.j....p. +unknown diff --git a/stcgal/frontend.py b/stcgal/frontend.py index 48c5f60..f33f895 100644 --- a/stcgal/frontend.py +++ b/stcgal/frontend.py @@ -26,6 +26,7 @@ import stcgal import serial from stcgal.utils import BaudType from stcgal.protocols import Stc89Protocol +from stcgal.protocols import Stc89AProtocol from stcgal.protocols import Stc12AProtocol from stcgal.protocols import Stc12BProtocol from stcgal.protocols import Stc12Protocol @@ -52,6 +53,8 @@ class StcGal: """Initialize protocol backend""" if opts.protocol == "stc89": self.protocol = Stc89Protocol(opts.port, opts.handshake, opts.baud) + elif opts.protocol == "stc89a": + self.protocol = Stc89AProtocol(opts.port, opts.handshake, opts.baud) elif opts.protocol == "stc12a": self.protocol = Stc12AProtocol(opts.port, opts.handshake, opts.baud) elif opts.protocol == "stc12b": diff --git a/stcgal/protocols.py b/stcgal/protocols.py index e9ba8f6..ba385bb 100644 --- a/stcgal/protocols.py +++ b/stcgal/protocols.py @@ -628,6 +628,239 @@ class Stc89Protocol(StcBaseProtocol): print("done") +class Stc89AProtocol(StcBaseProtocol): + """Protocol handler for STC 89/90 series""" + + PARITY = serial.PARITY_NONE + """Parity configuration - these don't use any parity""" + + PROGRAM_BLOCKSIZE = 128 + """block size for programming flash""" + + def __init__(self, port, baud_handshake, baud_transfer): + StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer) + + self.cpu_6t = None + + def extract_payload(self, packet): + """Verify the checksum of packet and return its payload""" + + packet_csum = packet[-2] + (packet[-3] << 8) + calc_csum = sum(packet[2:-3]) & 0xffff + if packet_csum != calc_csum: + self.dump_packet(packet) + raise StcFramingException("packet checksum mismatch") + + payload = StcBaseProtocol.extract_payload(self, packet) + return payload[:-1] + + def write_packet(self, packet_data): + """Send packet to MCU. + + Constructs a packet with supplied payload and sends it to the MCU. + """ + + # frame start and direction magic + packet = bytes() + packet += self.PACKET_START + packet += self.PACKET_HOST + + # packet length and payload + packet += struct.pack(">H", len(packet_data) + 6) + packet += packet_data + + # checksum and end code + packet += struct.pack(">H", sum(packet[2:]) & 0xffff) + packet += self.PACKET_END + + self.dump_packet(packet, receive=False) + self.ser.write(packet) + self.ser.flush() + + def get_status_packet(self): + """Read and decode status packet""" + + status_packet = self.read_packet() + if status_packet[0] != 0x50: + raise StcProtocolException("incorrect magic in status packet" + str(status_packet[0])) + return status_packet + + def initialize_options(self, status_packet): + """Initialize options""" + + if len(status_packet) < 20: + raise StcProtocolException("invalid options in status packet") + self.options = Stc89Option(status_packet[1]) + self.options.print() + + self.ser.parity = "E" + + def calculate_baud(self): + """Calculate MCU baudrate setting. + + Calculate appropriate baudrate settings for the MCU's UART, + according to clock frequency and requested baud rate. + """ + + # timing is different in 6T mode + sample_rate = 32 #if self.cpu_6t else 32 + # baudrate is directly controlled by programming the MCU's BRT register + brt = 65536 - round((self.mcu_clock_hz) / (self.baud_transfer * sample_rate)) + + baud_actual = (self.mcu_clock_hz) / (sample_rate * (65536 - brt)) + baud_error = (abs(self.baud_transfer - baud_actual) * 100.0) / self.baud_transfer + if baud_error > 5.0: + print("WARNING: baudrate error is %.2f%%. You may need to set a slower rate." % + baud_error, file=sys.stderr) + + # IAP wait states (according to datasheet(s)) + iap_wait = 0x80 + if self.mcu_clock_hz < 10E6: iap_wait = 0x83 + elif self.mcu_clock_hz < 30E6: iap_wait = 0x82 + elif self.mcu_clock_hz < 50E6: iap_wait = 0x81 + + # MCU delay after switching baud rates + delay = 0xa0 + + return brt, iap_wait + + def initialize_status(self, status_packet): + """Decode status packet and store basic MCU info""" + + self.cpu_6t = not bool(status_packet[1] & 1) + + freq_counter = struct.unpack(">H", status_packet[13:15])[0] + self.mcu_clock_hz = (12 * freq_counter * self.baud_handshake) + + bl_version, bl_stepping = struct.unpack("BB", status_packet[17:19]) + bl_minor = status_packet[22] & 0x0f + self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, + bl_minor, chr(bl_stepping)) + + def handshake(self): + """Switch to transfer baudrate + + Switches to transfer baudrate and verifies that the setting works with + a ping-pong exchange of packets.""" + + # check new baudrate + print("Switching to %d baud: " % self.baud_transfer, end="") + sys.stdout.flush() + brt,iap = self.calculate_baud() + print("checking ", end="") + sys.stdout.flush() + packet = bytes([0x01]) + packet += struct.pack(">H", brt) + packet += bytes([iap]) + self.write_packet(packet) + time.sleep(0.2) + print(self.baud_transfer) + response = self.read_packet() + + if response[0] != 0x01: + raise StcProtocolException("incorrect magic in handshake packet") + + self.ser.baudrate = self.baud_transfer + + # ping-pong test + print("testing ", end="") + sys.stdout.flush() + packet = bytes([0x05, 0x00, 0x00, 0x46, 0xB9]) + self.write_packet(packet) + response = self.read_packet() + if response[0] != 0x05: + raise StcProtocolException("incorrect magic in handshake packet") + + print("done") + + def reset_device(self, resetcmd=False): + if not resetcmd: + print("Cycling power: ", end="") + sys.stdout.flush() + self.ser.setDTR(False) + time.sleep(0.5) + self.ser.setDTR(True) + print("done") + else: + print("Cycling power via shell cmd: " + resetcmd) + os.system(resetcmd) + + print("Waiting for MCU: ", end="") + sys.stdout.flush() + + def erase_flash(self, erase_size, _): + """Erase the MCU's flash memory. + + Erase the flash memory with a block-erase command. + flash_size is ignored; not used on STC 89 series. + """ + + print("Erasing All blocks: ", end="") + sys.stdout.flush() + packet = bytes([0x03, 0x00, 0x00, 0x46, 0xB9]) + self.write_packet(packet) + response = self.read_packet() + if response[0] != 0x03: + raise StcProtocolException("incorrect magic in erase packet") + + print("MCU ID: {:x}{:x}{:x}{:x}{:x}{:x}{:x}".format(response[1],response[2],response[3] + ,response[4],response[5],response[6],response[7])) + + print("done") + + def program_flash(self, data): + """Program the MCU's flash memory. + + Write data into flash memory, using the PROGRAM_BLOCKSIZE + as the block size (depends on MCU's RAM size). + """ + p = 0 + + for i in range(0, len(data), self.PROGRAM_BLOCKSIZE): + packet = bytes(3) + if p == 0: + packet = bytes([0x22,0x00,0x00]) + else: + packet = bytes([0x02]) + packet += int(128 * p).to_bytes(length=2, byteorder='big', signed=True) + + + p = p + 1 + packet += bytes([0x46, 0xB9]) + packet += data[i:i+self.PROGRAM_BLOCKSIZE] + + self.write_packet(packet) + + response = self.read_packet() + if len(response) < 1 or response[0] != 0x02: + raise StcProtocolException("incorrect magic in write packet") + + self.progress_cb(i, self.PROGRAM_BLOCKSIZE, len(data)) + self.progress_cb(len(data), self.PROGRAM_BLOCKSIZE, len(data)) + + def program_options(self): + """Program option byte into flash""" + + print("Setting options: ") + sys.stdout.flush() + msr = self.options.get_msr() + packet = bytes([0x04,0x00,0x00,0x46,0xB9, msr]) + self.write_packet(packet) + response = self.read_packet() + if response[0] != 0x04: + raise StcProtocolException("incorrect magic in option packet") + print("done") + + def disconnect(self): + """Disconnect from MCU""" + + # reset mcu + packet = bytes([0xFF]) + self.write_packet(packet) + self.ser.close() + print("Disconnected!") + + class Stc12AOptionsMixIn: def program_options(self): print("Setting options: ", end="")