From accbed7d3bd78a9644d05a64296ce4472ef9c6cc Mon Sep 17 00:00:00 2001 From: Patrick Moessler Date: Sun, 9 Jan 2022 18:20:49 +0100 Subject: [PATCH] initial --- i2c_interfaces.py | 327 ++++++++++++++++++++++++++++++++++++++++++++++ tw8836.py | 9 ++ tw88xx.py | 100 ++++++++++++++ tw88xx_util.py | 60 +++++++++ 4 files changed, 496 insertions(+) create mode 100644 i2c_interfaces.py create mode 100644 tw8836.py create mode 100644 tw88xx.py create mode 100644 tw88xx_util.py diff --git a/i2c_interfaces.py b/i2c_interfaces.py new file mode 100644 index 0000000..10a7b68 --- /dev/null +++ b/i2c_interfaces.py @@ -0,0 +1,327 @@ +from abc import abstractmethod +from time import sleep + + +class I2CInterface: + @abstractmethod + def read(self, addr: int, length: int) -> bytes: + pass + + @abstractmethod + def write(self, addr: int, data: bytes) -> None: + pass + + @abstractmethod + def write_read(self, addr: int, data: bytes, length: int) -> bytes: + pass + + @abstractmethod + def open(self) -> None: + pass + + @abstractmethod + def close(self) -> None: + pass + + class I2CException(Exception): + pass + + +class JLinkSwdI2CInterface(I2CInterface): + GPIO_BASE = { + 'PA': 0x40010800, + 'PB': 0x40010C00, + 'PC': 0x40011000, + 'PD': 0x40011400, + 'PE': 0x40011800, + } + + GPIO_REGS = { + 'CRL': 0x00, + 'CRH': 0x04, + 'IDR': 0x08, + 'ODR': 0x0C, + 'BSRR': 0x10, + 'BRR': 0x14, + 'LCKR': 0x18 + } + + BIT_TIME = (1/100000) / 2 + + def __init__(self, jlink_serial=None, chip_id='STM32F105RB', sda='PB7', scl='PB6') -> None: + super().__init__() + from pylink import JLink + self.jlink = JLink() + if not self.jlink.connected(): + self.jlink.open(serial_no=jlink_serial) + self.chip_id = chip_id + + self.sda = (JLinkSwdI2CInterface.GPIO_BASE[sda.upper()[0:2]], int(sda[2])) + self.scl = (JLinkSwdI2CInterface.GPIO_BASE[scl.upper()[0:2]], int(scl[2])) + + def open(self) -> None: + from pylink.enums import JLinkInterfaces + super().open() + if not self.jlink.target_connected(): + self.jlink.set_tif(JLinkInterfaces.SWD) + self.jlink.connect(self.chip_id) + assert self.jlink.halt(), 'STM32 could not be halted' + self._set_pin_od(self.scl) + self._set_pin_od(self.sda) + + def close(self) -> None: + if self.jlink.target_connected(): + if not self.jlink.halted(): + self.jlink.halt() + self.jlink.reset(halt=False) + self.jlink.close() + return super().close() + + def read(self, addr: int, length: int) -> bytes: + self._send_start() + JLinkSwdI2CInterface._bit_sleep() + ack = self._send_byte((addr << 1) | 1) + JLinkSwdI2CInterface._bit_sleep() + if not ack: + raise I2CInterface.I2CException('addr not acked') + data = [] + for _ in range(length-1): + data.append(self._recv_byte()) + JLinkSwdI2CInterface._bit_sleep() + data.append(self._recv_byte(ack=False)) + JLinkSwdI2CInterface._bit_sleep() + self._send_stop() + return bytes(data) + + def write(self, addr: int, data: bytes) -> None: + self._send_start() + JLinkSwdI2CInterface._bit_sleep() + ack = self._send_byte(addr << 1) + JLinkSwdI2CInterface._bit_sleep() + if not ack: + raise I2CInterface.I2CException('addr not acked') + for b in data: + ack = self._send_byte(b) + if not ack: + raise I2CInterface.I2CException('data not acked') + JLinkSwdI2CInterface._bit_sleep() + self._send_stop() + + def write_read(self, addr: int, data: bytes, length: int) -> bytes: + self._send_start() + ack = self._send_byte(addr << 1) + if not ack: + raise I2CInterface.I2CException('W addr not acked') + for b in data: + ack = self._send_byte(b) + if not ack: + raise I2CInterface.I2CException('data not acked') + self._send_start() + ack = self._send_byte((addr << 1) | 1) + if not ack: + raise I2CInterface.I2CException('R addr not acked') + data = [] + for _ in range(length): + data.append(self._recv_byte()) + self._send_stop() + + @classmethod + def _bit_sleep(cls): + # pass + sleep(cls.BIT_TIME) + + def _rmw(self, addr, offset, size, val) -> None: + mask = ((2**size)-1) << offset + nmask = 0xFFFFFFFF ^ mask + reg_val = self.jlink.memory_read32(addr, 1)[0] + reg_val &= nmask + reg_val |= (val << offset) & mask + self.jlink.memory_write32(addr, [reg_val]) + + def _set_pin_od(self, pin) -> None: + cr = JLinkSwdI2CInterface.GPIO_REGS['CRH'] if pin[1] >= 8 else JLinkSwdI2CInterface.GPIO_REGS['CRL'] + pin_off = 4 * (pin[1]-8 if pin[1] >= 8 else pin[1]) + self._rmw(pin[0] + cr, pin_off, 4, 0b1000) + self._set_pin(pin, True) + self._rmw(pin[0] + cr, pin_off, 4, 0b0110) + + def _set_pin(self, pin, state: bool): + reg = JLinkSwdI2CInterface.GPIO_REGS['BSRR'] if state else JLinkSwdI2CInterface.GPIO_REGS['BRR'] + mask = 1 << pin[1] + self.jlink.memory_write16(pin[0] + reg, [mask]) + + def _get_pin(self, pin) -> bool: + mask = 1 << pin[1] + reg_val = self.jlink.memory_read16(pin[0] + JLinkSwdI2CInterface.GPIO_REGS['IDR'], 1)[0] + return bool(reg_val & mask) + + def _send_start(self) -> None: + self._set_pin(self.sda, True) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.scl, True) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.sda, False) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.scl, False) + JLinkSwdI2CInterface._bit_sleep() + + def _send_byte(self, val) -> bool: + self._set_pin(self.scl, False) + JLinkSwdI2CInterface._bit_sleep() + for i in range(8): + bit = bool(val & (0x80 >> i)) + self._set_pin(self.sda, bit) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.scl, True) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.scl, False) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.sda, True) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.scl, True) + JLinkSwdI2CInterface._bit_sleep() + ack = not self._get_pin(self.sda) + self._set_pin(self.scl, False) + JLinkSwdI2CInterface._bit_sleep() + return ack + + def _recv_byte(self, ack=True) -> int: + val = 0 + self._set_pin(self.scl, False) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.sda, True) + JLinkSwdI2CInterface._bit_sleep() + for i in range(8): + self._set_pin(self.scl, True) + JLinkSwdI2CInterface._bit_sleep() + bit = self._get_pin(self.sda) + if bit: + val |= (0x80 >> i) + self._set_pin(self.scl, False) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.sda, not ack) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.scl, True) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.scl, False) + JLinkSwdI2CInterface._bit_sleep() + return val + + def _send_stop(self): + self._set_pin(self.scl, False) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.sda, False) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.scl, True) + JLinkSwdI2CInterface._bit_sleep() + self._set_pin(self.sda, True) + JLinkSwdI2CInterface._bit_sleep() + + + + + + + + + + + + + + + + +# class JLinkSwdI2CInterface_I2C(I2CInterface): +# def __init__(self, jlink_serial=None) -> None: +# super().__init__() +# from pylink import JLink +# self.jlink = JLink() +# if not self.jlink.connected(): +# self.jlink.open(serial_no=jlink_serial) +# self.chip_id = 'STM32F105RB' + +# def open(self) -> None: +# from pylink.enums import JLinkInterfaces +# super().open() +# if not self.jlink.target_connected(): +# self.jlink.set_tif(JLinkInterfaces.SWD) +# self.jlink.connect(self.chip_id) +# assert self.jlink.halt(), 'STM32 could not be halted' + +# self._rmw(0x40010C00,24,8,0b10001000) +# self. +# self._rmw(0x40010C00,24,8,0b11101110) + + +# def close(self) -> None: +# if self.jlink.target_connected(): +# if not self.jlink.halted(): +# self.jlink.halt() +# self.jlink.reset(halt=False) +# self.jlink.close() +# return super().close() + +# def read(self, addr: int, length: int) -> bytes: +# self._send_start() +# JLinkSwdI2CInterface._bit_sleep() +# ack = self._send_byte((addr << 1) | 1) +# JLinkSwdI2CInterface._bit_sleep() +# if not ack: +# raise I2CInterface.I2CException('addr not acked') +# data = [] +# for _ in range(length-1): +# data.append(self._recv_byte()) +# JLinkSwdI2CInterface._bit_sleep() +# data.append(self._recv_byte(ack=False)) +# JLinkSwdI2CInterface._bit_sleep() +# self._send_stop() +# return bytes(data) + +# def write(self, addr: int, data: bytes) -> None: +# self._send_start() +# JLinkSwdI2CInterface._bit_sleep() +# ack = self._send_byte(addr << 1) +# JLinkSwdI2CInterface._bit_sleep() +# if not ack: +# raise I2CInterface.I2CException('addr not acked') +# for b in data: +# ack = self._send_byte(b) +# if not ack: +# raise I2CInterface.I2CException('data not acked') +# JLinkSwdI2CInterface._bit_sleep() +# self._send_stop() + +# def write_read(self, addr: int, data: bytes, length: int) -> bytes: +# self._send_start() +# ack = self._send_byte(addr << 1) +# if not ack: +# raise I2CInterface.I2CException('W addr not acked') +# for b in data: +# ack = self._send_byte(b) +# if not ack: +# raise I2CInterface.I2CException('data not acked') +# self._send_start() +# ack = self._send_byte((addr << 1) | 1) +# if not ack: +# raise I2CInterface.I2CException('R addr not acked') +# data = [] +# for _ in range(length): +# data.append(self._recv_byte()) +# self._send_stop() + +# def _rmw(self, addr, offset, size, val) -> None: +# mask = ((2**size)-1) << offset +# nmask = 0xFFFFFFFF ^ mask +# reg_val = self.jlink.memory_read32(addr, 1)[0] +# reg_val &= nmask +# reg_val |= (val << offset) & mask +# self.jlink.memory_write32(addr, [reg_val]) + +# def _set_pin_af_od(self, pin) -> None: +# cr = 4 if pin[1] >= 8 else 0 +# pin_off = 4 * (pin[1]-8 if pin[1] >= 8 else pin[1]) +# self._rmw(pin[0] + cr, pin_off, 4, 0b1000) +# self._set_pin(pin, True) +# self._rmw(pin[0] + cr, pin_off, 4, 0b1110) + diff --git a/tw8836.py b/tw8836.py new file mode 100644 index 0000000..ba60eb5 --- /dev/null +++ b/tw8836.py @@ -0,0 +1,9 @@ +from i2c_interfaces import I2CInterface +from tw88xx import TW88xx + + +class TW8836(TW88xx): + ADDR = 0x45 + + def __init__(self, i2c: I2CInterface) -> None: + super().__init__(i2c) diff --git a/tw88xx.py b/tw88xx.py new file mode 100644 index 0000000..ece5c02 --- /dev/null +++ b/tw88xx.py @@ -0,0 +1,100 @@ +from i2c_interfaces import I2CInterface + + +class TW88xx: + + class Reg: + SPI_FLASH_MODE_CONTROL_0 = 0X4C0 + SPI_FLASH_MODE_CONTROL_1 = 0X4C1 + DMA_CONTROL = 0X4C3 + FLASH_BUSY_CONTROL = 0X4C4 + WAIT_CONTROL = 0X4C5 + DMA_PAGE = 0X4C6 + DMA_INDEX = 0X4C7 + DMA_LENGTH_MID_BYTE = 0X4C8 + DMA_LENGTH_LOW_BYTE = 0X4C9 + DMA_COMMAND_BUFFER = 0X4CA # LEN 5 + CLOCK_SWITCH_WAIT_COUNTER_VALUE = 0X4CF + DMA_READWRITE_BUFFER = 0X4D0 # LEN 8 + SPI_FLASH_STATUS_COMMAND = 0X4D8 + SPI_FLASH_BUSY_CONTROL = 0X4D9 + DMA_LENGTH_HIGH_BYTE = 0X4DA + + def __init__(self, i2c: I2CInterface) -> None: + self.i2c = i2c + self.page = None + + def _set_page(self, page: int) -> None: + self.i2c.write(self.ADDR, [0xFF, page]) + + def write_reg(self, reg: int, data: int) -> None: + page = reg >> 8 + if not self.page or self.page != page: + self._set_page(page) + self.page = page + self.i2c.write(self.ADDR, [reg & 0xFF, data]) + # print(f'{reg:03x} = {data:02x}') + + def read_reg(self, reg: int): + page = reg >> 8 + if not self.page or self.page != page: + self._set_page(page) + self.page = page + self.i2c.write(self.ADDR, [reg & 0xFF]) + return self.i2c.read(self.ADDR, 1)[0] + + def execute_flash_cmd(self, cmd: bytes, read_len=0, read_mode='slow', data=None) -> bytes: + # def execute_flash_cmd(self, cmd: bytes, read_len=0, read_mode='slow') -> bytes: + self.write_reg(TW88xx.Reg.SPI_FLASH_MODE_CONTROL_0, 0x00) + self.write_reg(TW88xx.Reg.DMA_CONTROL, 0x40 + len(cmd)) + + self.write_reg(TW88xx.Reg.DMA_PAGE, 0x04) + self.write_reg(TW88xx.Reg.DMA_INDEX, 0xD0) + + self.write_reg(TW88xx.Reg.DMA_LENGTH_MID_BYTE, 0) + self.write_reg(TW88xx.Reg.DMA_LENGTH_LOW_BYTE, read_len) + self.write_reg(TW88xx.Reg.DMA_LENGTH_HIGH_BYTE, 0) + + for i in range(len(cmd)): + self.write_reg(TW88xx.Reg.DMA_COMMAND_BUFFER + i, cmd[i]) + + if data: + for i in range(len(data)): + self.write_reg(TW88xx.Reg.DMA_READWRITE_BUFFER + i, data[i]) + + self.write_reg(TW88xx.Reg.FLASH_BUSY_CONTROL, 0x01) + while True: + busy = self.read_reg(TW88xx.Reg.FLASH_BUSY_CONTROL) + # print(f'busy: {busy:02x}') + if (busy & 0x01) == 0: + break + + data = [] + for i in range(read_len): + data.append(self.read_reg(TW88xx.Reg.DMA_READWRITE_BUFFER + i)) + return bytes(data) + + def prepare_flash_read(self, cmd, read_len): + self.write_reg(TW88xx.Reg.DMA_CONTROL, 0x40 + len(cmd)) + + self.write_reg(TW88xx.Reg.DMA_PAGE, 0x04) + self.write_reg(TW88xx.Reg.DMA_INDEX, 0xD0) + + self.write_reg(TW88xx.Reg.DMA_LENGTH_MID_BYTE, 0) + self.write_reg(TW88xx.Reg.DMA_LENGTH_LOW_BYTE, read_len) + self.write_reg(TW88xx.Reg.DMA_LENGTH_HIGH_BYTE, 0) + + for i in range(len(cmd)): + self.write_reg(TW88xx.Reg.DMA_COMMAND_BUFFER + i, cmd[i]) + + def do_flash_cmd(self, read_len): + self.write_reg(TW88xx.Reg.FLASH_BUSY_CONTROL, 0x01) + while True: + busy = self.read_reg(TW88xx.Reg.FLASH_BUSY_CONTROL) + if (busy & 0x01) == 0: + break + + data = [] + for i in range(read_len): + data.append(self.read_reg(TW88xx.Reg.DMA_READWRITE_BUFFER + i)) + return bytes(data) \ No newline at end of file diff --git a/tw88xx_util.py b/tw88xx_util.py new file mode 100644 index 0000000..6a679b2 --- /dev/null +++ b/tw88xx_util.py @@ -0,0 +1,60 @@ +import argparse + +from tw88xx import TW88xx +parser = argparse.ArgumentParser(description='TW88xx debug utility.') + +parser.add_argument('command', + help='the command to execute', + choices=('dump_flash', 'get_id') + ) +parser.add_argument('-f', '--flash_file', + help='file to read from / write into' + ) +parser.add_argument('-i', '--interface', + help='the i2c interface to use', + choices=('devi2c', 'swd') + ) +parser.add_argument('-j', '--jlink', + help='J-Link (serial) to use for SWD', + default=None + ) +parser.add_argument('-t', '--type', + help='TX88xx type', + choices=('tw8836'), + default='tw8836' + ) + +args = parser.parse_args() + +if args.interface == 'swd': + from i2c_interfaces import JLinkSwdI2CInterface + i2c = JLinkSwdI2CInterface(jlink_serial=args.jlink) + +i2c.open() +if args.type == 'tw8836': + from tw8836 import TW8836 + tw88xx = TW8836(i2c) + +if args.command == 'get_id': + id = tw88xx.read_reg(0x000) + print(f'{id:02x}') + + flash_id = tw88xx.execute_flash_cmd(cmd=[0x9f], read_len=3) + print(flash_id.hex()) + +elif args.command == 'dump_flash': + # tw88xx.prepare_flash_read(cmd=[0x03, 0x1a, 0x9d, 0x84], read_len=8) + tw88xx.execute_flash_cmd(cmd=[0xE9], read_len=0) + data=[] + for block_start in range(0x1a9d84,0x1a9d84+480*480,8): + block = tw88xx.execute_flash_cmd(cmd=[0x03, (block_start>>16) & 0xFF, (block_start>>8) & 0xFF, block_start& 0xFF], read_len=8, data=[0x5a]*8) + data.extend(block) + print(f'{block_start:06x}: {block.hex()}') + + print(bytes(data).hex()) + # tw88xx.execute_flash_cmd(cmd=[0xB7], read_len=0) + # block = tw88xx.execute_flash_cmd(cmd=[0x03, 0x00, 0x00, 0x00], read_len=8) + # print(block.hex()) + # block = tw88xx.execute_flash_cmd(cmd=[0x03, 0x00, 0x00, 0x08], read_len=8) + # print(block.hex()) + # tw88xx.execute_flash_cmd(cmd=[0xE9], read_len=0)