Files
controller-wisetopdataserver/python/biopro/impl/ext_mem.py
T
2022-01-07 16:14:06 +08:00

463 lines
16 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
_RUNTIME_COMPILE = False
_SLEEP_TIME_ = 0.001
@lru_cache(maxsize=4)
def zero_buffer(size: int) -> List[int]:
return [0] * size
class MultiExtMemSpiInterface(LowLevelHardwareInterface):
MEM_INS_MARKED = [MEM_INS_WRITE, 0, 2, 1, 1]
MEM_INS_RESET = [MEM_INS_WRITE, 0, 2, 1, 1, 0, 0xFF]
__slots__ = ('_selector', '_wait_for_first_data', '_spi', '_tx_buffer', '_tx_buffer_header', '_tx_buffer_data',
'pin_busy', 'pin_request', 'pin_reset', 'pin_sel',
'_pin_sel_val',
'_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 # XXX temp parameter
)
self._elite_data_len = 120
self._mem_header_len = 3
self._mem_tailer_len = 8
self._single_data_len = self._elite_data_len + self._mem_header_len + self._mem_tailer_len
# buffer
self._tx_buffer = [0] * 512
self._tx_buffer_header = [0] * 11
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_request = OutputPin.get_used(P3Pin.MEM_REQ, True)
self.pin_reset = OutputPin.get_used(P3Pin.MEM_RST, True)
self.pin_sel: Optional[InputPin] = InputPin.get_used(P3Pin.MEM_SEL)
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)]
@property
def select(self) -> int:
return self._selector.channel
@select.setter
def select(self, value: int):
self._selector.select(value)
def changed(self, flip=False) -> bool:
channel = self._selector.channel
old = self._pin_sel_val[channel]
value = bool(self.pin_sel)
if flip:
self._pin_sel_val[channel] = value
return value != old
def reset(self):
self._spi.reset()
def close(self):
self._selector.close()
self._spi.close()
def flush(self):
self.pin_reset.output(False)
self.pin_reset.output(True)
def send_byte(self, data: bytes):
raise RuntimeError()
def recv_byte(self, size: int) -> Optional[bytes]:
raise RuntimeError()
# def flush_all(self):
# for _ in self._selector.foreach():
# self.pin_reset.output(False)
# self.pin_reset.output(True)
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 request_data(self):
self.pin_request.output(False)
sleep(0.001)
# count = 0
# for i in range(300):
# count = count + 1
self.pin_request.output(True)
# sleep(0.001) -> 1.2ms
# no sleep -> 5us ~ 12us (central can't receive)
# for i in range
# (1000) -> 500us ~ 1ms
# (500) -> 200us ~ 500us
# (300) -> 120us ~ 270us (seldom)
# (200) -> 70us ~ 160us (skip req about every 64 time) x
# (100) -> 40us ~ 100us (skip req sometimes) x
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 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] < 6):
print('data_first[0:3] != [255, 255, 255]', device)
print(list(data[0:7]))
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)
# print(list(data_first[0:7]))
# 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)
# print(list(data_second[0:7]))
# 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))
# ----------------------------------------------------------------------------------------------
# debug use
# green_data = []
# green_data = data
# print("_[Debug] @ spi green data = ", green_data)
length = (data[3] << 8) | data[4]
header = data[7]
elite_data_len = data[9]
if length <= 4:
# print("length <= 4")
# print("green data print:", data, device, datetime.now())
return None
if data[5] != 0 or data[6] != 0:
# give a default length if the data header did not update
length = 7000
print("green data: is not [0,0], = ", data[5:7])
print("green data print:", data, device, datetime.now())
return None
if (length >= 7000):
flag_print = True
print("green data: big length:", length)
if (header != 255):
flag_print = True
print("green data: header is not 255: ", header)
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())
# neulive
# address += len(data) - 11
# tx_d = self._tx_buffer_data
# red_length = len(tx_d)
# elite read all data of ram
# address = 4
# red_length = length - 1
# tx_d = [0] * red_length
address = 4
red_length = int(length / 2)
tx_d = [0] * red_length
# elite read len(_tx_buffer_data) byte
# address = 4
# tx_d = self._tx_buffer_data
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
# print("_[Debug] @ spi recv data rx = ")
# for i in range(0, len(rx), self._single_data_len):
# print(rx[i:i+self._single_data_len])
# print()
# /*
# * red data formate:
# * ramHdr, ramHdr, ramHdr, (3B)
# * 255, #, data_length, (3B)
# * data, (20B)
# * red_wrong, red_retry_cnt, green_wrong, green_retry_cnt, (4B)
# * 255, #, data_length, (3B)
# * */
# mark read
self._spi.send_byte(self.MEM_INS_MARKED)
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._mem_sel = InputPin.get_used(P3Pin.MEM_SEL, pull_up_down=True)
self._mem_req = OutputPin.get_used(P3Pin.MEM_REQ, initial=True)
self._ext_mem = ext_mem
print('ExtMemManager.....')
print()
def mem_request(self):
self._mem_req.pulse()
sleep(_SLEEP_TIME_)
def get_ext_mem_register(self) -> List[Tuple[Optional[int], Optional[int]]]:
print('get_ext_mem_register......')
print()
ret = [(None, None) for _ in range(Selector.SIZE)]
for channel in self._ext_mem.foreach():
r = [None, None]
self.mem_request()
sleep(0.01)
m1 = int(self._mem_sel)
# print("m1 = ", m1)
self._ext_mem.write_register(DEFAULT_REGISTER_VALUE)
r[m1] = self._ext_mem.read_register()
self.mem_request()
sleep(0.01)
m2 = int(self._mem_sel)
# print("m2 = ", m2)
self._ext_mem.write_register(DEFAULT_REGISTER_VALUE)
r[m2] = self._ext_mem.read_register()
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]:
print('get_available_channel......')
print()
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