This commit is contained in:
Patrick Moessler 2022-01-09 18:20:49 +01:00
commit accbed7d3b
4 changed files with 496 additions and 0 deletions

327
i2c_interfaces.py Normal file
View file

@ -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)

9
tw8836.py Normal file
View file

@ -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)

100
tw88xx.py Normal file
View file

@ -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)

60
tw88xx_util.py Normal file
View file

@ -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)