518 lines
19 KiB
Python
518 lines
19 KiB
Python
from functools import lru_cache
|
|
from random import randint
|
|
from time import sleep
|
|
from typing import List, Tuple, Optional, Union, Iterable
|
|
|
|
from biopro.impl.selector import Selector
|
|
from biopro.util.console import pc, GREEN, RED
|
|
from .gpio import P3Pin, OutputPin, InputPin
|
|
from .interface_spi import HardwareImplSpiInterface, LowLevelHardwareInterface
|
|
from datetime import datetime
|
|
|
|
MSM_REG_WRITE = 0x01
|
|
MEM_INS_WRITE = 0x02
|
|
MEM_INS_READ = 0x03
|
|
MEM_REG_READ = 0x05
|
|
DEFAULT_REGISTER_VALUE = 0b0100_0011 # 67
|
|
MEM_SIZE = 0x1000
|
|
|
|
_SLEEP_TIME_ = 0.001
|
|
|
|
|
|
@lru_cache(maxsize=4)
|
|
def zero_buffer(size: int) -> List[int]:
|
|
return [0] * size
|
|
|
|
class MultiExtMemSpiInterface(LowLevelHardwareInterface):
|
|
MEM_INS_MARKED1 = [MEM_INS_WRITE, 0, 2, 0x5A, 0xA5]
|
|
MEM_INS_MARKED2 = [MEM_INS_WRITE, 0, 6, 0x5A, 0xA5]
|
|
MEM_INS_MARKED3 = [MEM_INS_WRITE, 0, 10, 0x5A, 0xA5]
|
|
# MEM_INS_RESET = [MEM_INS_WRITE, 0, 2, 1, 1, 0, 0xFF]
|
|
|
|
__slots__ = ('_selector', '_wait_for_first_data', '_spi', '_tx_buffer_header', '_tx_buffer_data',
|
|
'pin_busy', 'pin_mem_req', 'pin_mem_sel', 'pin_ram_sel',
|
|
'_pin_sel_val',
|
|
'_pin_ram_sel_value', '_pin_mem_sel_value', '_pin_mem_req_value',
|
|
'_read_green_times','_read_red_times',
|
|
'_elite_data_len', '_mem_header_len', '_mem_tailer_len', '_single_data_len',
|
|
'_head_wrong_cnt')
|
|
|
|
def __init__(self,
|
|
select: Selector,
|
|
device: Tuple[int, int] = None):
|
|
|
|
self._spi = HardwareImplSpiInterface(device,
|
|
spi_speed=12_000_000)
|
|
|
|
self._elite_data_len = 40
|
|
self._mem_header_len = 3
|
|
self._mem_tailer_len = 6
|
|
self._single_data_len = self._elite_data_len + self._mem_header_len + self._mem_tailer_len
|
|
|
|
# buffer
|
|
self._tx_buffer_header = [0] * 19
|
|
self._tx_buffer_data = [0] * (self._single_data_len * 10 + 3)
|
|
|
|
# memory control pin
|
|
self.pin_busy = OutputPin.get_used(P3Pin.MEM_BZY, True)
|
|
self.pin_mem_req = OutputPin.get_used(P3Pin.MEM_REQ, False)
|
|
self.pin_mem_sel = OutputPin.get_used(P3Pin.MEM_RST, True) # MEM_RST -> actually which memory board is assign
|
|
self.pin_ram_sel: Optional[InputPin] = InputPin.get_used(P3Pin.MEM_SEL) # MEM_SEL -> actually is RAM_SEL, which RAM is assign
|
|
|
|
self._pin_ram_sel_value = [bool(self.pin_ram_sel) for _ in range(Selector.SIZE)]
|
|
self._pin_mem_sel_value = [bool(self.pin_mem_sel) for _ in range(Selector.SIZE)]
|
|
self._pin_mem_req_value = [bool(self.pin_mem_req) for _ in range(Selector.SIZE)]
|
|
|
|
self._read_green_times = 0
|
|
self._read_red_times = 0
|
|
|
|
self._head_wrong_cnt = [0, 0, 0, 0, 0, 0, 0, 0]
|
|
|
|
|
|
self._selector = select
|
|
|
|
self._pin_sel_val = [False for _ in range(Selector.SIZE)]
|
|
self._wait_for_first_data = [True for _ in range(Selector.SIZE)]
|
|
|
|
def set_pin_mem_req(self, value: bool):
|
|
channel = self.select
|
|
self.pin_mem_req.output(value)
|
|
self._pin_mem_req_value[channel] = value
|
|
|
|
def set_pin_mem_sel(self, value: bool):
|
|
channel = self.select
|
|
self.pin_mem_sel.output(value)
|
|
self._pin_mem_sel_value[channel] = value
|
|
|
|
def get_pin_mem_req(self):
|
|
channel = self.select
|
|
return self._pin_mem_req_value[channel]
|
|
|
|
def get_pin_ram_sel(self):
|
|
channel = self.select
|
|
if self.pin_ram_sel.input() == 0:
|
|
self._pin_ram_sel_value[channel] = False
|
|
else:
|
|
self._pin_ram_sel_value[channel] = True
|
|
return self._pin_ram_sel_value[channel]
|
|
|
|
@property
|
|
def select(self) -> int:
|
|
return self._selector.channel
|
|
|
|
@select.setter
|
|
def select(self, value: int):
|
|
self._selector.select(value)
|
|
|
|
def reset(self):
|
|
self._spi.reset()
|
|
|
|
def flush(self):
|
|
pass
|
|
|
|
def close(self):
|
|
self._selector.close()
|
|
self._spi.close()
|
|
|
|
def send_byte(self, data: bytes):
|
|
raise RuntimeError()
|
|
|
|
def recv_byte(self, size: int) -> Optional[bytes]:
|
|
raise RuntimeError()
|
|
|
|
def foreach(self) -> Iterable[int]:
|
|
for channel in self._selector.foreach():
|
|
yield channel
|
|
|
|
def set_wait_flag(self, spi_idx:int = None, value:bool = None):
|
|
self._wait_for_first_data[spi_idx] = value
|
|
|
|
def get_wait_flag(self, spi_idx:int = None) -> Optional[bool]:
|
|
if spi_idx < Selector.SIZE:
|
|
return self._wait_for_first_data[spi_idx]
|
|
else:
|
|
return None
|
|
|
|
def compare_green_data(self, data_first: Union[bytes, List[int]], data_second: Union[bytes, List[int]], data_third: Union[bytes, List[int]], length: int):
|
|
data = []
|
|
data.append(255)
|
|
data.append(255)
|
|
data.append(255)
|
|
|
|
for i in range(3, length):
|
|
if (data_first[i] == data_second[i] or data_first[i] == data_third[i]):
|
|
data.append(data_first[i])
|
|
elif(data_second[i] == data_third[i]):
|
|
data.append(data_second[i])
|
|
else:
|
|
data.append(data_third[i]) # use last data
|
|
self._read_green_times = self._read_green_times + 1
|
|
print("read green data times", self._read_green_times)
|
|
return data
|
|
|
|
def compare_red_data(self, data_first: Union[bytes, List[int]], data_second: Union[bytes, List[int]], data_third: Union[bytes, List[int]], length: int):
|
|
data = []
|
|
data.append(255)
|
|
data.append(255)
|
|
data.append(255)
|
|
|
|
for i in range(3, length):
|
|
if (data_first[i] == data_second[i] or data_first[i] == data_third[i]):
|
|
data.append(data_first[i])
|
|
elif(data_second[i] == data_third[i]):
|
|
data.append(data_second[i])
|
|
else:
|
|
data.append(data_third[i]) # use last data
|
|
self._read_red_times = self._read_red_times + 1
|
|
print("read red data times", self._read_red_times)
|
|
return data
|
|
|
|
def _compare_green_data_addr_and_flag(self, data: Union[bytes, List[int]], device: int):
|
|
|
|
green_data_section1 = data[3:7]
|
|
green_data_section2 = data[7:11]
|
|
green_data_section3 = data[11:15]
|
|
green_data_section = []
|
|
|
|
if green_data_section1 == green_data_section2:
|
|
green_data_section = green_data_section1
|
|
|
|
elif(green_data_section2 == green_data_section3 or green_data_section1 == green_data_section3):
|
|
print("green data not equal: = ", data[3:15])
|
|
print("green data print:", data, device, datetime.now())
|
|
green_data_section = green_data_section3
|
|
|
|
else:
|
|
print("green data not equal: = ", data[3:15])
|
|
print("green data print:", data, device, datetime.now())
|
|
green_data_section = green_data_section3 # use last data
|
|
|
|
|
|
return green_data_section
|
|
|
|
def _print_ram_all_data(self):
|
|
addr = 0
|
|
red_length = int(7000 / 2)
|
|
tx_temp = [0] * (red_length + 3)
|
|
|
|
while True:
|
|
tx_temp[0] = MEM_INS_READ
|
|
tx_temp[1] = ((addr >> 8) & 0xFF)
|
|
tx_temp[2] = (addr & 0xFF)
|
|
|
|
ram_data = []
|
|
ram_data = self._spi.send_byte(tx_temp)
|
|
ram_data[0:3] = [255, 255, 255]
|
|
print(list(ram_data), len(ram_data), addr)
|
|
addr += len(ram_data) - 3
|
|
|
|
if (7000 < addr + len(ram_data)):
|
|
del ram_data
|
|
break
|
|
print()
|
|
|
|
return
|
|
|
|
def recv_memory(self, device: int) -> Optional[bytes]:
|
|
self.pin_busy.output(False)
|
|
|
|
rx = []
|
|
|
|
try:
|
|
address = 0
|
|
|
|
# first read to get data length
|
|
tx_h = self._tx_buffer_header
|
|
|
|
tx_h[0] = MEM_INS_READ
|
|
tx_h[1] = 0
|
|
tx_h[2] = 0
|
|
|
|
|
|
# -------------------------------------read one times-------------------------------------------
|
|
data = []
|
|
flag_print = False
|
|
|
|
data = self._spi.send_byte(tx_h)
|
|
|
|
if (data[0] != 255 or data[1] != 255 or data[2] != 255):
|
|
self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
|
|
if (self._head_wrong_cnt[device] <= 5): # print 5 times
|
|
print('data_first[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times')
|
|
print(list(data))
|
|
|
|
data[0:3] = [255, 255, 255]
|
|
# ----------------------------------------------------------------------------------------------
|
|
|
|
|
|
# -------------------------------------read two times-------------------------------------------
|
|
# data = []
|
|
# data_first = []
|
|
# data_second = []
|
|
# flag_print = False
|
|
|
|
# data_first = self._spi.send_byte(tx_h)
|
|
# data_second = self._spi.send_byte(tx_h)
|
|
|
|
# if (data_first[0] != 255 or data_first[1] != 255 or data_first[2] != 255):
|
|
# self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
|
|
# if (self._head_wrong_cnt[device] < 10):
|
|
# print('data_first[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times')
|
|
# print(list(data_first))
|
|
|
|
# if (data_second[0] != 255 or data_second[1] != 255 or data_second[2] != 255):
|
|
# self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
|
|
# if (self._head_wrong_cnt[device] < 10):
|
|
# print('data_second[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times')
|
|
# print(list(data_second))
|
|
|
|
# if (data_first[3:] == data_second[3:]):
|
|
# data = data_first
|
|
# data[0:3] = [255, 255, 255]
|
|
|
|
# else:
|
|
# data_third = self._spi.send_byte(tx_h)
|
|
# print("read data_third", data_third)
|
|
# data = self.compare_green_data(data_first, data_second, data_third, len(tx_h))
|
|
# ----------------------------------------------------------------------------------------------
|
|
|
|
green_data_section = self._compare_green_data_addr_and_flag(data, device)
|
|
|
|
length = (green_data_section[0] << 8) | green_data_section[1]
|
|
header = data[15]
|
|
elite_data_len = data[17]
|
|
|
|
if length <= 12:
|
|
if length < 12:
|
|
print("length < 12")
|
|
print("green data print:", data, device, datetime.now())
|
|
|
|
if length == 0:
|
|
self._print_ram_all_data()
|
|
|
|
return None
|
|
|
|
if green_data_section[2] != 0xA5 or green_data_section[3] != 0x5A:
|
|
# give a default length if the data header did not update
|
|
print("green data: is not [0xA5, 0x5A], = ", data[5:7])
|
|
print("green data print:", data, device, datetime.now())
|
|
return None
|
|
|
|
if (length >= 4000):
|
|
flag_print = True
|
|
print("green data: big length:", length)
|
|
|
|
if (header != 255):
|
|
flag_print = True
|
|
print("green data: header is not 255: ", header)
|
|
self._print_ram_all_data()
|
|
|
|
if (elite_data_len != self._elite_data_len):
|
|
flag_print = True
|
|
print("green data: length is not", self._elite_data_len, ": ", elite_data_len)
|
|
|
|
if (flag_print):
|
|
print("green data print:", data, device, datetime.now(), '\n')
|
|
|
|
address = 12
|
|
red_length = int(length / 2)
|
|
tx_d = [0] * (red_length + 3)
|
|
|
|
while True:
|
|
tx_d[0] = MEM_INS_READ
|
|
tx_d[1] = ((address >> 8) & 0xFF)
|
|
tx_d[2] = (address & 0xFF)
|
|
# -------------------------------------read one times-------------------------------------------
|
|
data = []
|
|
data = self._spi.send_byte(tx_d)
|
|
data[0:3] = [255, 255, 255]
|
|
# ----------------------------------------------------------------------------------------------
|
|
|
|
# -------------------------------------read two times-------------------------------------------
|
|
# data = []
|
|
# data_first = []
|
|
# data_second = []
|
|
|
|
# data_first = self._spi.send_byte(tx_d)
|
|
# data_second = self._spi.send_byte(tx_d)
|
|
|
|
# if (data_first[3:] == data_second[3:]):
|
|
# data = data_first
|
|
# data[0:3] = [255, 255, 255]
|
|
# else:
|
|
# data_third = self._spi.send_byte(tx_d)
|
|
# print("read red data_third", data_third)
|
|
# data = self.compare_red_data(data_first, data_second, data_third, len(tx_d))
|
|
# ----------------------------------------------------------------------------------------------
|
|
if length + 3 <= address + len(data):
|
|
rx.extend(data[3:3 + length - address])
|
|
del data
|
|
|
|
break
|
|
|
|
else:
|
|
rx.extend(data[3:])
|
|
address += len(data) - 3
|
|
del data
|
|
|
|
# last_index = 0
|
|
# print("_[Debug] @ spi recv data rx, ram_select:", self._pin_ram_sel_value[device], ",", datetime.now())
|
|
# for i in range(0, len(rx), self._single_data_len):
|
|
# last_index = i
|
|
# # print(rx[i:i+self._single_data_len])
|
|
# if i == 0:
|
|
# print(rx[0:8], 'ram:', rx[47])
|
|
|
|
# if last_index != 0:
|
|
# print(rx[last_index:last_index+8], 'ram:', rx[last_index+47])
|
|
# print()
|
|
|
|
# /*
|
|
# * red data formate:
|
|
# * ramHdr, ramHdr, ramHdr, (3B)
|
|
# * 255, #, data_length, (3B)
|
|
# * data, (40B)
|
|
# * red_wrong, red_retry_cnt, green_wrong, green_retry_cnt, (4B)
|
|
# * check_num, (1B)
|
|
# * */
|
|
|
|
# read again if check num is wrong
|
|
index = 0
|
|
check_number_print = False
|
|
for i in range(0, len(rx), self._single_data_len):
|
|
check_sum = sum(rx[i : i + self._single_data_len - 1]) & 0b11111111
|
|
# print(check_sum, rx[i + self._single_data_len - 1])
|
|
|
|
if (check_sum != rx[i + self._single_data_len - 1]):
|
|
print('check_sum wrong, origin value:', 'check_sum =', check_sum, rx[i : i + self._single_data_len])
|
|
tx_d = [0] * (self._single_data_len + 3)
|
|
address = 12 + self._single_data_len * index
|
|
tx_d[0] = MEM_INS_READ
|
|
tx_d[1] = ((address >> 8) & 0xFF)
|
|
tx_d[2] = (address & 0xFF)
|
|
|
|
data = []
|
|
data = self._spi.send_byte(tx_d)
|
|
data[0:3] = [255, 255, 255]
|
|
rx[i : i + self._single_data_len] = data[3:]
|
|
print('check_sum wrong, read again:', 'check_sum =', check_sum, rx[i : i + self._single_data_len])
|
|
check_number_print = True
|
|
index = index + 1
|
|
|
|
if check_number_print:
|
|
print('check_sum wrong:', device, datetime.now())
|
|
for i in range(0, len(rx), self._single_data_len):
|
|
print(rx[i:i+self._single_data_len])
|
|
print()
|
|
|
|
# mark read
|
|
self._spi.send_byte(self.MEM_INS_MARKED1)
|
|
self._spi.send_byte(self.MEM_INS_MARKED2)
|
|
self._spi.send_byte(self.MEM_INS_MARKED3)
|
|
|
|
except BaseException as e:
|
|
print(e)
|
|
|
|
finally:
|
|
# print("\n")
|
|
self.pin_busy.output(True)
|
|
|
|
return bytes(rx)
|
|
|
|
def read_register(self) -> int:
|
|
return self._spi.send_byte([MEM_REG_READ, 0])[1]
|
|
|
|
def write_register(self, register: int):
|
|
self._spi.send_byte([MSM_REG_WRITE, register])
|
|
|
|
def read_memory(self, address: int, length: int) -> List[int]:
|
|
tx = [MEM_INS_READ, ((address >> 8) & 0xFF), (address & 0xFF)]
|
|
tx.extend(zero_buffer(length))
|
|
|
|
return self._spi.send_byte(tx)[3:]
|
|
|
|
def write_memory(self, address: int, data: Union[bytes, List[int]]):
|
|
tx = [MEM_INS_WRITE, ((address >> 8) & 0xFF), (address & 0xFF)]
|
|
|
|
if isinstance(data, bytes):
|
|
tx.extend(iter(data))
|
|
else:
|
|
tx.extend(data)
|
|
|
|
self._spi.send_byte(tx)
|
|
|
|
class ExtMemManager:
|
|
def __init__(self, ext_mem: MultiExtMemSpiInterface):
|
|
self._ext_mem = ext_mem
|
|
|
|
def get_ext_mem_register(self) -> List[Tuple[Optional[int], Optional[int]]]:
|
|
ret = [(None, None) for _ in range(Selector.SIZE)]
|
|
|
|
for channel in self._ext_mem.foreach():
|
|
r = [None, None]
|
|
|
|
self._ext_mem.set_pin_mem_sel(False)
|
|
pin_value = self._ext_mem.get_pin_mem_req()
|
|
self._ext_mem.set_pin_mem_req(not pin_value)
|
|
|
|
m1 = int(self._ext_mem.get_pin_ram_sel())
|
|
# print("m1 = ", m1)
|
|
self._ext_mem.write_register(DEFAULT_REGISTER_VALUE)
|
|
r[m1] = self._ext_mem.read_register()
|
|
sleep(0.040)
|
|
self._ext_mem.set_pin_mem_sel(True)
|
|
sleep(0.200)
|
|
|
|
self._ext_mem.set_pin_mem_sel(False)
|
|
pin_value = self._ext_mem.get_pin_mem_req()
|
|
self._ext_mem.set_pin_mem_req(not pin_value)
|
|
|
|
m2 = int(self._ext_mem.get_pin_ram_sel())
|
|
# print("m2 = ", m2)
|
|
self._ext_mem.write_register(DEFAULT_REGISTER_VALUE)
|
|
r[m2] = self._ext_mem.read_register()
|
|
sleep(0.040)
|
|
self._ext_mem.set_pin_mem_sel(True)
|
|
sleep(0.200)
|
|
|
|
ret[channel] = tuple(r)
|
|
|
|
return ret
|
|
|
|
@staticmethod
|
|
def is_no_device(result: Tuple[Optional[int], Optional[int]]) -> bool:
|
|
r1, r2 = result
|
|
return (r1 is None or r2 is None) and (r1 is None or r1 == 0) and (r2 is None or r2 == 0)
|
|
|
|
@staticmethod
|
|
def is_memory_test_fail(result: Tuple[Optional[int], Optional[int]]) -> int:
|
|
r1, r2 = result
|
|
|
|
if r1 is None or r2 is None:
|
|
return 1
|
|
|
|
if r1 is not None and r1 > 0 and r1 != DEFAULT_REGISTER_VALUE:
|
|
return 2
|
|
|
|
if r2 is not None and r2 > 0 and r2 != DEFAULT_REGISTER_VALUE:
|
|
return 3
|
|
|
|
return 0
|
|
|
|
def get_available_channel(self, result: List[Tuple[Optional[int], Optional[int]]] = None) -> List[int]:
|
|
if result is None:
|
|
result = self.get_ext_mem_register()
|
|
|
|
ret = []
|
|
|
|
for channel, result in enumerate(result):
|
|
if self.is_no_device(result):
|
|
continue
|
|
|
|
if self.is_memory_test_fail(result) != 0:
|
|
continue
|
|
|
|
ret.append(channel)
|
|
|
|
return ret
|