uC chip interface arduino  0.9.0
A interface for async and neuromrphic IC testing
Loading...
Searching...
No Matches
interface_async.py
Go to the documentation of this file.
2# This file is part of the Firmware project to interface with small Async or Neuromorphic chips
3# Copyright (C) 2023 Ole Richter - University of Groningen
4# Copyright (C) 2024 Vincent Jassies - University of Groningen
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19
20from .header import ConfigMainHeader, Data32bitHeader, ConfigSubHeader
21from .packet import ConfigPacket, Data32bitPacket
22import logging, time
23
25 def __init__(self, api_object, interface_id, direction):
26 """ constructor for the async interface
27 @param api_object: uC_api parent object
28 @param interface_id: id of the interface this object is responcible for
29 @param direction: "TO_CHIP" or "FROM_CHIP"
30 """
31 # status of the interface
32 # 0 means not activated
33 # 1 means activation pending
34 # 2 means activted
35 # -1 means error
36 self.__status = 0
37 self.__status_timestamp = 0
38 # mode of the interface
39 # "4Phase_Chigh_Dhigh" means 4 phase clock with high active clock and high active data
40 # "4Phase_Clow_Dhigh" means 4 phase clock with low active clock and high active data
41 # "2Phase" means 2 phase clock with high active data
42 # "4Phase_MCP23017" means 4 phase clock with high active clock and high active data and MCP23017 port extender
43 # "NONE" means not set
44 self.__mode = "NONE"
45 self.__mode_timestamp = 0
46 # request pin id on the uC
47 self.__req_pin = -1
48 self.__req_pin_timestamp = 0
49 # ack pin id on the uC
50 self.__ack_pin = -1
51 self.__ack_pin_timestamp = 0
52 # data width of the interface
53 self.__data_width = -1
55 # data pin ids of the interface
56 self.__data_pins = [-1]*32
57 self.__data_pins_timestamp = [0]*32
58 # how much the request of the handshake is delayed multiplied by 20us
59 self.__req_delay = -1
61 # data send to the chip
62 self.__data_from_chip = []
64 # data recived from the chip
65 self.__data_to_chip = []
66 self.__data_to_chip_times = []
67 # errors
68 self.__errors = []
69 # the parent object
70 self.__api = api_object
71 # the direction of the interface
72 # "TO_CHIP" or "FROM_CHIP"
73 self.__direction = direction
74 # the human readable name of the interface
75 self.__name = "ASYNC_"+direction+str(interface_id)
76 if direction == "TO_CHIP":
77 self.__name = "ASYNC_"+direction+str(interface_id)
78 # set the headers this object is responcible for
79 if interface_id == 0:
80 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_TO_CHIP0, Data32bitHeader.IN_ASYNC_TO_CHIP0]
81 elif interface_id == 1:
82 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_TO_CHIP1, Data32bitHeader.IN_ASYNC_TO_CHIP1]
83 elif interface_id == 2:
84 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_TO_CHIP2, Data32bitHeader.IN_ASYNC_TO_CHIP2]
85 elif interface_id == 3:
86 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_TO_CHIP3, Data32bitHeader.IN_ASYNC_TO_CHIP3]
87 elif interface_id == 4:
88 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_TO_CHIP4, Data32bitHeader.IN_ASYNC_TO_CHIP4]
89 elif interface_id == 5:
90 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_TO_CHIP5, Data32bitHeader.IN_ASYNC_TO_CHIP5]
91 elif interface_id == 6:
92 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_TO_CHIP6, Data32bitHeader.IN_ASYNC_TO_CHIP6]
93 elif interface_id == 7:
94 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_TO_CHIP7, Data32bitHeader.IN_ASYNC_TO_CHIP7]
95 else:
96 logging.error("only 8 AER to chip interfaces are supported at the moment")
97 elif direction == "FROM_CHIP":
98 if interface_id == 0:
99 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_FROM_CHIP0, Data32bitHeader.OUT_ASYNC_FROM_CHIP0]
100 elif interface_id == 1:
101 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_FROM_CHIP1, Data32bitHeader.OUT_ASYNC_FROM_CHIP1]
102 elif interface_id == 2:
103 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_FROM_CHIP2, Data32bitHeader.OUT_ASYNC_FROM_CHIP2]
104 elif interface_id == 3:
105 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_FROM_CHIP3, Data32bitHeader.OUT_ASYNC_FROM_CHIP3]
106 elif interface_id == 4:
107 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_FROM_CHIP4, Data32bitHeader.OUT_ASYNC_FROM_CHIP4]
108 elif interface_id == 5:
109 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_FROM_CHIP5, Data32bitHeader.OUT_ASYNC_FROM_CHIP5]
110 elif interface_id == 6:
111 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_FROM_CHIP6, Data32bitHeader.OUT_ASYNC_FROM_CHIP6]
112 elif interface_id == 7:
113 self.__header = [ConfigMainHeader.IN_CONF_ASYNC_FROM_CHIP7, Data32bitHeader.OUT_ASYNC_FROM_CHIP7]
114 else:
115 logging.error("only 8 AER to chip interfaces are supported at the moment")
116 else:
117 logging.error("AER unknown direction only TO_CHIP and FROM CHIP allowed")
118
119 def __str__(self):
120 self.update()
121 state_str = ("active" if self.__status == 2 else ("activation pending" if self.__status == 1 else ("not active" if self.__status == 0 else "error" )))
122 return "ASYNC_" + str(self.__direction) + \
123 "\nHeader: " + str(self.__header) + \
124 "\nStatus: " + state_str + " at " + str(self.__status_timestamp) + "us" + \
125 "\nType "+ str(self.__mode) +" at " + str(self.__mode_timestamp) + "us" + \
126 "\nHS req pin "+ str(self.__req_pin) +" at " + str(self.__req_pin_timestamp) + "us with delay of 20us*" + str(self.__req_delay) +" at " + str(self.__req_delay_timestamp) + "us" + \
127 "\nHS ack pin "+ str(self.__ack_pin) +" at " + str(self.__ack_pin_timestamp) + \
128 "\nData width "+ str(self.__data_width) +" at " + str(self.__data_width_timestamp) + "us" + \
129 "\nData pins "+ str(self.__data_pins[:self.__data_width]) +" at " + str(self.__data_pins_timestamp[:self.__data_width]) + "us" + \
130 "\nSend: "+ str(self.__data_to_chip) +" at " + str(self.__data_to_chip_times) + "us" + \
131 "\nRecived: "+ str(self.__data_from_chip) +" at " + str(self.__data_from_chip_times) + "us" + \
132 "\nERRORS: "+str(self.__errors) + "\n"
133
134 def header(self):
135 """ get the header this object is responcible for
136 @return: list of headers
137 """
138 return self.__header
139
140 def status(self):
141 """ get the human readable status of the interface
142 @return: tuple of status and timestamp
143 """
144 self.update()
145 state_str = ("active" if self.__status == 2 else ("activation pending" if self.__status == 1 else ("not active" if self.__status == 0 else "error" )))
146 return (state_str,self.__status_timestamp)
147
148 def interface_type(self):
149 """ get the human readable type of the interface
150 @return: tuple of type and timestamp of to what and when the type was actually set on the uC)
151 """
152 self.update()
153 return (self.__mode,self.__mode_timestamp)
154
155 def data_pins(self):
156 """ get the data pins of the interface as they are on the uC
157 @return: tuple of data pins and their timestamps of to what and when the pins were actually set on the uC
158 """
159 self.update()
160 return (self.__data_pins[:self.__data_width],self.__data_pins_timestamp[:self.__data_width])
161
162 def data_width(self):
163 """ get the data width of the interface as it is on the uC
164 @return: tuple of data width and its timestamp of to what and when the width was actually set on the uC
165 """
166 self.update()
167 return (self.__data_width,self.__data_width_timestamp)
168
169 def req_pin(self):
170 """ get the request pin of the interface as it is on the uC
171 @return: tuple of request pin and its timestamp of to what and when the pin was actually set on the uC
172 """
173 self.update()
174 return (self.__req_pin,self.__req_pin_timestamp)
175
176 def ack_pin(self):
177 """ get the ack pin of the interface as it is on the uC
178 @return: tuple of ack pin and its timestamp of to what and when the pin was actually set on the uC
179 """
180 self.update()
181 return (self.__ack_pin,self.__ack_pin_timestamp)
182
183 def req_delay(self):
184 """ get the request delay of the interface as it is on the uC
185 @return: tuple of request delay and its timestamp of to what and when the delay was actually set on the uC
186 """
187 self.update()
188 return (self.__req_delay,self.__req_delay_timestamp)
189
190 def data_from_chip(self):
191 """ get the data recived from the chip
192 will retun 2 lists: one with the word recoded and one with the time when it was recorded, linked by index
193 @return: tuple of the of data list and their timestamp list - index matched
194 """
195 self.update()
196 return (self.__data_from_chip, data_from_chip_times)
197
198 def data_to_chip(self):
199 """ get the data send to the chip when they are actually send off by the uC
200 data_to_chip will retun the data send by the uC to the device under test (DUT)
201
202 will retun 2 lists: one with the word send and one with the exact time when it was send, linked by index
203
204 the time might differ slightly from the time you sheduled the send word,
205 as it is the time when it was send out and the uC can only send one word at a time
206
207 @return: tuple of the of data list and their timestamp list (of when the uC send them) - index matched
208 """
209 self.update()
210 return (self.__data_to_chip, self.__data_to_chip_times)
211
213 """ get the data recived from the chip and clear the buffer
214 will retun 2 lists: one with the word recoded and one with the time when it was recorded, linked by index
215
216 @return: tuple of the of data list and their timestamp list - index matched
217 """
218 self.update()
219 data = self.__data_from_chip
220 time = self.__data_from_chip_times
221 self.__data_from_chip = []
222 self.__data_from_chip_times = []
223 return (data, time)
224
226 """ get the data send to the chip when they are actually send off by the uC and clear the buffer
227 data_to_chip will retun the data send by the uC to the device under test (DUT)
228
229 will retun 2 lists: one with the word send and one with the exact time when it was send, linked by index
230
231 the time might differ slightly from the time you sheduled the send word,
232 as it is the time when it was send out and the uC can only send one word at a time
233
234 @return: tuple of the of data list and their timestamp list (of when the uC send them) - index matched
235 """
236 self.update()
237 data = self.__data_to_chip
238 time = self.__data_to_chip_times
239 self.__data_to_chip = []
240 self.__data_to_chip_times = []
241 return (data, time)
242
243 def errors(self):
244 """ get the list of errors associated with this interface object
245 @return: list of errors
246 """
247 self.update()
248 return self.__errors
249
250
251 def process_packet(self, packet):
252 """ process a packet from the uC, update the status of the interface object and store the data
253 @param packet: packet object to be processed
254 """
255 if packet.header() in self.__header:
256 # process any acknolage config packet (config sucessfully set on the uC)
257 if packet.header() == self.__header[0]:
258 # interface is active
259 if packet.config_header() == ConfigSubHeader.CONF_ACTIVE:
260 self.__status = 2
261 self.__status_timestamp = packet.time()
262 return
263 # the mode of the interface
264 elif packet.config_header() == ConfigSubHeader.CONF_TYPE:
265 """@todo replace with type"""
266 if packet.value() == 0:
267 self.__mode = "4Phase_Chigh_Dhigh"
268 elif packet.value() == 1:
269 self.__mode = "4Phase_Clow_Dhigh"
270 elif packet.value() == 10:
271 self.__mode = "2Phase"
272 elif packet.value() == 20:
273 self.__mode = "4Phase_MCP23017"
274 else:
275 logging.error("unknown async type returned")
276 self.__mode_timestamp = packet.time()
277 return
278 # the ack pin of the interface
279 elif packet.config_header() == ConfigSubHeader.CONF_ACK:
280 self.__ack_pin = packet.value()
281 self.__ack_pin_timestamp = packet.time()
282 return
283 # the request pin of the interface
284 elif packet.config_header() == ConfigSubHeader.CONF_REQ:
285 self.__req_pin = packet.value()
286 self.__req_pin_timestamp = packet.time()
287 return
288 # the data width of the interface
289 elif packet.config_header() == ConfigSubHeader.CONF_WIDTH:
290 self.__data_width = packet.value()
291 self.__data_width_timestamp = packet.time()
292 return
293 # the data pins of the interface
294 elif packet.config_header() < 32:
295 self.__data_pins[packet.config_header()] = packet.value()
296 self.__data_pins_timestamp[packet.config_header()] = packet.time()
297 return
298 # the request line delay of the interface
299 elif packet.config_header() == ConfigSubHeader.CONF_REQ_DELAY:
300 self.__req_delay = packet.value()
301 self.__req_delay_timestamp = packet.time()
302 return
303 # process any data packet (data send from the uC to the chip or recived from the chip)
304 elif packet.header() == self.__header[1]:
305 if self.__direction == "TO_CHIP":
306 self.__data_to_chip.append(packet.value())
307 self.__data_to_chip_times.append(packet.time())
308 return
309 elif self.__direction == "FROM_CHIP":
310 self.__data_from_chip.append(packet.value())
311 self.__data_from_chip_times.append(packet.time())
312 return
313 # all other packets are errors or not supported, disable the interface
314 self.__errors.append(str(packet))
315 self.__status = -1
316
317 def activate(self, req_pin, ack_pin, data_width, data_pins, mode="4Phase_Chigh_Dhigh", req_delay = 0, time = 0):
318 """ activate the interface on the uC with the given parameters
319
320 Mode:
321 - "4Phase_Chigh_Dhigh" means 4 phase clock with high active clock and high active data
322 - "4Phase_Clow_Dhigh" means 4 phase clock with low active clock and high active data
323 - "2Phase" means 2 phase clock with high active data
324 - "4Phase_MCP23017" means 4 phase clock with high active clock and high active data and MCP23017 port extender, pin IDs are not used in this mode
325
326 @param req_pin: request pin id on the uC
327 @param ack_pin: ack pin id on the uC
328 @param data_width: data width of the interface
329 @param data_pins: list of data pin ids on the uC
330 @param mode: mode of the interface - "4Phase_Chigh_Dhigh", "4Phase_Clow_Dhigh", "2Phase", "4Phase_MCP23017"
331 @param req_delay: how much the request of the handshake is delayed multiplied by 20us
332 @param time: the exec_time when the uc should activate the interface, 0 means as soon as possible
333 """
334 # only activate if not already activated or pending activation
335 if self.__status >= 1:
336 logging.warning("ASYNC_to_chip interface "+str(self.__header[0])+" is already activated or waiting activation, doing nothing")
337 else:
338 # send the configuration to the uC as individual packets
339 if mode == "4Phase_Chigh_Dhigh":
340 self.__api.send_packet(ConfigPacket(header = self.__header[0], config_header = ConfigSubHeader.CONF_TYPE, value = 0, time = time))
341 self.__status = 1
342 elif mode == "4Phase_Clow_Dhigh":
343 self.__api.send_packet(ConfigPacket(header = self.__header[0], config_header = ConfigSubHeader.CONF_TYPE, value = 1, time = time))
344 self.__status = 1
345 elif mode == "2Phase_Chigh_Dhigh":
346 logging.warning("pin mode not implmented yet")
347 return
348 elif mode == "2Phase_Clow_Dhigh":
349 logging.warning("pin mode not implmented yet")
350 return
351 elif mode == "4Phase_MCP23017":
352 self.__api.send_packet(ConfigPacket(header = self.__header[0], config_header = ConfigSubHeader.CONF_TYPE, value = 20, time = time))
353 self.__status = 1
354 else:
355 logging.error("pin.activate got wrong type "+str(pin_mode)+" only 4Phase_Chigh_Dhigh, 4Phase_Clow_Dhigh, 2Phase_Chigh_Dhigh, 2Phase_Clow_Dhigh are allowed, more modes implemted on request")
356 return
357 self.__api.send_packet(ConfigPacket(header = self.__header[0], config_header = ConfigSubHeader.CONF_ACK, value = ack_pin, time = time))
358 self.__api.send_packet(ConfigPacket(header = self.__header[0], config_header = ConfigSubHeader.CONF_REQ, value = req_pin, time = time))
359 self.__api.send_packet(ConfigPacket(header = self.__header[0], config_header = ConfigSubHeader.CONF_WIDTH, value = data_width, time = time))
360 for pin in range(data_width):
361 self.__api.send_packet(ConfigPacket(header = self.__header[0], config_header = ConfigSubHeader(pin), value = data_pins[pin], time = time))
362 self.__api.send_packet(ConfigPacket(header = self.__header[0], config_header = ConfigSubHeader.CONF_REQ_DELAY, value = req_delay, time = time))
363 # after all configuration is send, send activation request
364 self.__api.send_packet(ConfigPacket(header = self.__header[0], config_header = ConfigSubHeader.CONF_ACTIVE, time = time))
365 # set the status to pending confirmation
366 self.__status = 1
367
368 def send(self, word, time = 0):
369 """ send a word to the chip
370 @param word: word to be send
371 @param time: the exec_time when the uc should send the word, 0 means as soon as possible
372 """
373 self.update()
374 if self.__direction == "TO_CHIP":
375 # we dont check the status here anymore as the uC will report the error anyway
376 self.__api.send_packet(Data32bitPacket(header = self.__header[1], value = word, time = time))
377
378 else:
379 logging.error("AER to chip interface "+str(self.__header[1])+" is reading interface - word is not sent.")
380
381 def update(self):
382 self.__api.update_state()
ConfigSubHeader are all command headers used in ConfigPacket in conjunction with ConfigMainHeader.
Definition: header.py:688
def status(self)
get the human readable status of the interface
def process_packet(self, packet)
process a packet from the uC, update the status of the interface object and store the data
def __init__(self, api_object, interface_id, direction)
constructor for the async interface
def data_to_chip_and_clear(self)
get the data send to the chip when they are actually send off by the uC and clear the buffer data_to_...
def data_from_chip_and_clear(self)
get the data recived from the chip and clear the buffer will retun 2 lists: one with the word recoded...
def send(self, word, time=0)
send a word to the chip
def interface_type(self)
get the human readable type of the interface
def activate(self, req_pin, ack_pin, data_width, data_pins, mode="4Phase_Chigh_Dhigh", req_delay=0, time=0)
activate the interface on the uC with the given parameters
def data_width(self)
get the data width of the interface as it is on the uC
def req_delay(self)
get the request delay of the interface as it is on the uC
def data_pins(self)
get the data pins of the interface as they are on the uC
def ack_pin(self)
get the ack pin of the interface as it is on the uC
def req_pin(self)
get the request pin of the interface as it is on the uC
def errors(self)
get the list of errors associated with this interface object
def data_from_chip(self)
get the data recived from the chip will retun 2 lists: one with the word recoded and one with the tim...
def data_to_chip(self)
get the data send to the chip when they are actually send off by the uC data_to_chip will retun the d...
The ConfigPacket is used to cumunicate configuration instructions with the uC all availible instructi...
Definition: packet.py:375
The Data32bitPacket is used to send 32bit data instructions to the uC all availible instructions are ...
Definition: packet.py:125