x393  1.0
FPGAcodeforElphelNC393camera
x393interfaces.py
Go to the documentation of this file.
1 from __future__ import print_function
2 
3 
4 """
5 # Copyright (C) 2016, Elphel.inc.
6 # Implementation of AXI4-based buses used in Elpphel x393 camera project
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http:#www.gnu.org/licenses/>.
20 @author: Andrey Filippov
21 @copyright: 2016 Elphel, Inc.
22 @license: GPLv3.0+
23 @contact: andrey@elphel.coml
24 
25 Uses code from https://github.com/potentialventures/cocotb/blob/master/cocotb/drivers/amba.py
26 Below are the copyright/license notices of the amba.py
27 ------------------------------------------------------
28 
29 Copyright (c) 2014 Potential Ventures Ltd
30 All rights reserved.
31 
32 Redistribution and use in source and binary forms, with or without
33 modification, are permitted provided that the following conditions are met:
34  * Redistributions of source code must retain the above copyright
35  notice, this list of conditions and the following disclaimer.
36  * Redistributions in binary form must reproduce the above copyright
37  notice, this list of conditions and the following disclaimer in the
38  documentation and/or other materials provided with the distribution.
39  * Neither the name of Potential Ventures Ltd,
40  SolarFlare Communications Inc nor the
41  names of its contributors may be used to endorse or promote products
42  derived from this software without specific prior written permission.
43 
44 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
45 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
46 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
47 DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY
48 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
49 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
50 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
51 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
52 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
53 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. '''
54 """
55 
56 
57 import cocotb
58 from cocotb.triggers import Timer, RisingEdge, FallingEdge, Edge, ReadOnly, Lock
59 from cocotb.drivers import BusDriver
60 from cocotb.result import ReturnValue
61 from cocotb.binary import BinaryValue
62 
63 import re
64 import binascii
65 import array
66 import struct
67 #import logging
68 
69 #channels
70 AR_CHN="AR"
71 AW_CHN="AW"
72 R_CHN="R"
73 W_CHN="W"
74 B_CHN="B"
75 
76 def _float_signals(signals):
77  if not isinstance (signals,(list,tuple)):
78  signals = (signals,)
79  for signal in signals:
80  v = signal.value
81  v.binstr = "z" * len(signal)
82  signal <= v
83 
84 
85 
86 class MAXIGPReadError(Exception):
87 # print ("MAXIGPReadError")
88  pass
89 
90 class PSBus(BusDriver):
91  """
92  Small subset of Zynq registers, used to access SAXI_HP* registers
93  """
94  _signals=[ # i/o from the DUT side
95  "clk", # output
96  "addr", # input [31:0]
97  "wr", # input
98  "rd", # input
99  "din", # input [31:0]
100  "dout"] #output [31:0]
101  def __init__(self, entity, name, clock):
102  BusDriver.__init__(self, entity, name, clock)
103  self.busy_channel = Lock("%s_busy"%(name))
104  self.bus.wr.setimmediatevalue(0)
105  self.bus.rd.setimmediatevalue(0)
106  _float_signals((self.bus.addr, self.bus.din))
107  self.name = name
108 
109  @cocotb.coroutine
110  def write_reg(self,addr,data):
111  yield self.busy_channel.acquire()
112  #Only wait if it is too late (<1/2 cycle)
113  if not int(self.clock):
114  yield RisingEdge(self.clock)
115  self.bus.addr <= addr
116  self.bus.din <= data
117  self.bus.wr <= 1
118  yield RisingEdge(self.clock)
119  self.bus.wr <= 0
120  _float_signals((self.bus.addr, self.bus.din))
121  self.busy_channel.release()
122 
123  @cocotb.coroutine
124  def read_reg(self,addr):
125  yield self.busy_channel.acquire()
126  #Only wait if it is too late (<1/2 cycle)
127  if not int(self.clock):
128  yield RisingEdge(self.clock)
129  self.bus.addr <= addr
130  self.bus.rd <= 1
131  yield RisingEdge(self.clock)
132  try:
133  data = self.bus.dout.value.integer
134  except:
135  bv = self.bus.dout.value
136  bv.binstr = re.sub("[^1]","0",bv.binstr)
137  data = bv.integer
138  self.bus.rd <= 0
139  _float_signals((self.bus.addr, ))
140  self.busy_channel.release()
141  raise ReturnValue(data)
142 
143 class SAXIWrSim(BusDriver):
144  """
145  Connects to host side of simul_axi_wr (just writes to system memory) (both GP and HP)
146  No locks are used, single instance should be connected to a particular port
147  """
148  _signals=[ # i/o from the DUT side
149  # read address channel
150  "wr_address", # output[31:0]
151  "wid", # output[5:0]
152  "wr_valid", # output
153  "wr_ready", # input
154  "wr_data", # output[63:0]
155  "wr_stb", # output[7:0]
156  "bresp_latency"] # input[3:0] // just constant
157 # "wr_cap", # output[2:0]
158 # "wr_qos"] # output[3:0]
159  _fmt=None
160  _memfile = None
161  _data_bytes = 8
162  _address_lsb = 3
163  def __init__(self, entity, name, clock, mempath, memhigh=0x40000000, data_bytes=8, autoflush=True, blatency=5):
164  """
165  @param entity Device under test
166  @param name port names prefix (DUT has I/O ports <name>_<signal>
167  @clock clock that drives this interface
168  @param mempath operation system path of the memory image (1GB now - 0..0x3fffffff)
169  @param memhigh memory high address
170  @param data_bytes data width, in bytes
171  @param autoflush flush file after each write
172  @param blatency number of cycles to delay write response (b) channel
173  """
174 # self.log.setLevel(logging.DEBUG)
175  BusDriver.__init__(self, entity, name, clock)
176  self.name = name
177  self.log.debug ("SAXIWrSim: name='%s', mempath='%s', memhigh=0x%08x, data_bytes=%d, autoflush=%s, blatency=%d"%
178  (name,mempath,memhigh,data_bytes, str(autoflush), blatency))
179 # self.log.debug ("SAXIWrSim.__init__(): super done")
180  #Open file to use as system memory
181  try:
182  self._memfile=open(mempath, 'r+') #keep old file if it exists already
183  except:
184  self._memfile=open(mempath, 'w+') #create a new file if it does not exist
185  self.log.info ("SAXIWrSim(%s): created a new 'memory' file %s"%(name,mempath)) #
186  #Extend to full size
187  self._memfile.seek(memhigh-1)
188  readOK=False
189  try:
190  readOK = len(self._memfile.read(1))>0
191  self.log.debug ("Read from 0x%08x"%(memhigh-1)) #
192 
193  except:
194  pass
195  if not readOK:
196  self._memfile.seek(memhigh-1)
197  self._memfile.write(chr(0))
198  self._memfile.flush()
199  self.log.info("Wrote to 0x%08x to extend file to full size"%(memhigh-1)) #
200 
201  self.autoflush=autoflush
202  self.bus.wr_ready.setimmediatevalue(1) # always ready
203  self.bus.bresp_latency.setimmediatevalue(blatency)
204  if data_bytes > 4:
205  self._data_bytes = 8
206  self._address_lsb = 3
207  self._fmt= "<Q"
208  elif data_bytes > 2:
209  self._data_bytes = 4
210  self._address_lsb = 2
211  self._fmt= "<L"
212  elif data_bytes > 1:
213  self._data_bytes = 2
214  self._address_lsb = 1
215  self._fmt= "<H"
216  else:
217  self._data_bytes = 1
218  self._address_lsb = 0
219  self._fmt= "<B"
220  self.log.debug ("SAXIWrSim(%s) init done"%(self.name))
221 
222  def flush(self):
223  self._memfile.flush()
224 
225  @cocotb.coroutine
226  def saxi_wr_run(self):
227  self.log.debug ("SAXIWrSim(%s).saxi_wr_run"%(self.name))
228  while True:
229  if not self.bus.wr_ready.value:
230  break #exit
231  while True:
232  yield ReadOnly()
233  if self.bus.wr_valid.value:
234  break
235  yield RisingEdge(self.clock)
236 # yield RisingEdge(self.clock)
237  #Here write data
238  try:
239  address = self.bus.wr_address.value.integer
240  except:
241  self.log.warning ("SAXIWrSim() tried to write to unknown memory address")
242  adress = None
243  yield RisingEdge(self.clock)
244  continue
245  if address & ((1 << self._address_lsb) - 1):
246  self.log.warning ("SAXIWrSim() Write memory address is not aligned to %d-byte words"%(self._data_bytes))
247  address = (address >> self._address_lsb) << self._address_lsb;
248  self._memfile.seek(address)
249 
250  try:
251  data = self.bus.wr_data.value.integer
252  except:
253  self.log.warning ("SAXIWrSim() writing undefined data")
254  bv = self.bus.wr_data.value
255  bv.binstr = re.sub("[^1]","0",bv.binstr)
256  data = bv.integer
257  sdata=struct.pack(self._fmt,data)
258  bv = self.bus.wr_data.value
259  bv.binstr= re.sub("[^0]","1",bv.binstr) # only 0 suppresses write to this byte
260  while len(bv.binstr) < self._data_bytes: # very unlikely
261  bv.binstr = "1"+bv.binstr
262  if bv.integer == self._data_bytes:
263  self._memfile.write(sdata)
264  else:
265  for i in range (self._data_bytes):
266  if bv.binstr[-1-i] != 0:
267  self._memfile.write(sdata[i])
268  else:
269  self._memfile.seek(1,1)
270  if self.autoflush:
271  self._memfile.flush()
272  self.log.debug ("SAXIWrSim() 0x%x -> 0x%x, mask = 0x%x"%(address,data,bv.integer))
273  yield RisingEdge(self.clock)
274 
275 
276 class SAXIRdSim(BusDriver):
277  """
278  Connects to host side of simul_axi_rd (just writes to system memory) (both GP and HP)
279  No locks are used, single instance should be connected to a particular port
280  """
281  _signals=[ # i/o from the DUT side
282  # read address channel
283  "rd_address", # output[31:0]
284  "rid", # output[5:0]
285  "rd_valid", # input
286  "rd_ready", # output
287  "rd_data", # input[63:0]
288  "rd_resp"] # input[1:0]
289  _fmt=None
290  _memfile = None
291  _data_bytes = 8
292  _address_lsb = 3
293  def __init__(self, entity, name, clock, mempath, memhigh=0x40000000, data_bytes=8):
294  """
295  @param entity Device under test
296  @param name port names prefix (DUT has I/O ports <name>_<signal>
297  @clock clock that drives this interface
298  @param mempath operation system path of the memory image (1GB now - 0..0x3fffffff)
299  @param memhigh memory high address
300  @param data_bytes data width, in bytes
301 
302  """
303 # self.log.setLevel(logging.DEBUG)
304  BusDriver.__init__(self, entity, name, clock)
305  self.name = name
306  self.log.debug ("SAXIRdSim: name='%s', mempath='%s', memhigh=0x%08x, data_bytes=%d"%
307  (name,mempath,memhigh,data_bytes))
308 # self._memfile=open(mempath, 'r+')
309  #Open file to use as system memory
310  try:
311  self._memfile=open(mempath, 'r+') #keep old file if it exists already
312  except:
313  self._memfile=open(mempath, 'w+') #create a new file if it does not exist
314  self.log.info ("SAXIRdSim(%s): created a new 'memory' file %s"%(name,mempath)) #
315  #Extend to full size
316  self._memfile.seek(memhigh-1)
317  readOK=False
318  try:
319  readOK = len(self._memfile.read(1))>0
320  self.log.debug ("Read from 0x%08x"%(memhigh-1)) #
321 
322  except:
323  pass
324  if not readOK:
325  self._memfile.seek(memhigh-1)
326  self._memfile.write(chr(0))
327  self._memfile.flush()
328  self.log.info("Wrote to 0x%08x to extend file to full size"%(memhigh-1)) #
329 
330  self.bus.rd_valid.setimmediatevalue(0)
331 
332  if data_bytes > 4:
333  self._data_bytes = 8
334  self._address_lsb = 3
335  self._fmt= "<Q"
336  elif data_bytes > 2:
337  self._data_bytes = 4
338  self._address_lsb = 2
339  self._fmt= "<L"
340  elif data_bytes > 1:
341  self._data_bytes = 2
342  self._address_lsb = 1
343  self._fmt= "<H"
344  else:
345  self._data_bytes = 1
346  self._address_lsb = 0
347  self._fmt= "<B"
348  self.log.debug("SAXIRdSim(%s) init done"%(self.name))
349 
350  @cocotb.coroutine
351  def saxi_test(self):
352  self.log.info ("SAXIRdSim(%s).saxi_test"%(self.name))
353  yield Timer(1000)
354 
355  @cocotb.coroutine
356  def saxi_rd_run(self):
357  self.log.info ("SAXIRdSim(%s).saxi_wr_run"%(self.name))
358  while True:
359 # if not self.bus.rd_valid.value:
360 # break #exit
361  while True:
362  yield FallingEdge(self.clock)
363  if self.bus.rd_ready.value:
364  break
365  self.bus.rd_valid <= 1
366 # yield RisingEdge(self.clock)
367  #Here write data
368  try:
369  address = self.bus.rd_address.value.integer
370  except:
371  self.log.warning ("SAXIRdSim() tried to write to unknown memory address")
372  adress = None
373  if address & ((1 << self._address_lsb) - 1):
374  self.log.warning ("SAXIRdSim() Write memory address is not aligned to %d-byte words"%(self._data_bytes))
375  address = (address >> self._address_lsb) << self._address_lsb;
376  self._memfile.seek(address)
377  rresp=0
378  try:
379  rs = self._memfile.read(self._data_bytes)
380  except:
381  self.log.warning ("SAXIRdSim() failed reading %d bytes form 0x%08x"%(self._data_bytes, address))
382  rs = None
383  if not rs is None:
384  try:
385  data = struct.unpack(self._fmt,rs)
386  except:
387  self.log.warning ("SAXIRdSim():Can not unpack memory data @ address 0x%08x"%(address))
388  data=None
389  if (not address is None) and (not data is None):
390  self.bus.rd_resp <= 0
391  self.bus.rd_data <= data
392  else:
393  self.bus.rd_resp <= 2 # error
394  _float_signals((self.bus.rd_data,))
395 
396  self.bus.rd_valid <= 1
397  yield RisingEdge(self.clock)
398  self.bus.rd_valid <= 0
399  _float_signals((self.bus.rd_data,self.bus.rd_resp))
400 
401 
402 
403 
404 
405 class MAXIGPMaster(BusDriver):
406  """
407  Implements subset of AXI4 used in x393 project for Xilinx Zynq MAXIGP*
408  """
409  _signals=[ # i/o from the DUT side
410  # read address channel
411  "araddr", # input [31:0]
412  "arready", # output
413  "arvalid", # input
414  "arid", # input [11:0]
415  "arlock", # input [1:0]
416  "arcache", # input [3:0]
417  "arprot", # input [2:0]
418  "arlen", # input [3:0]
419  "arsize", # input [1:0]
420  "arburst", # input [1:0],
421  "arqos", # input [3:0]
422  # axi ps master gp0: read data
423  "rdata", # output [31:0]
424  "rvalid", # output
425  "rready", # output (normally input, but temporarily in passive ready mode)
426  "rid", # output [11:0]
427  "rlast", # output
428  "rresp", # output [1:0]
429  # axi ps master gp0: write address
430  "awaddr", # input [31:0]
431  "awvalid", # input
432  "awready", # output
433  "awid", # input [11:0]
434  "awlock", # input [1:0]
435  "awcache", # input [3:0]
436  "awprot", # input [2:0]
437  "awlen", # input [3:0]
438  "awsize", # input [1:0]
439  "awburst", # input [1:0]
440  "awqos", # input [3:0]
441  # axi ps master gp0: write data
442  "wdata", # input [31:0]
443  "wvalid", # input
444  "wready", # output
445  "wid", # input [11:0]
446  "wlast", # input
447  "wstb", # input [3:0]
448  # axi ps master gp0: write response
449  "bvalid", # output
450  "bready", # output (normally input, but temporarily in passive ready mode)
451  "bid", # output [11:0]
452  "bresp", # output [1:0]
453  # x393 specific signals (controls AXI latencies
454  "xtra_rdlag", #input [3:0]
455  "xtra_blag" #input [3:0]
456  ]
457  _channels = [AR_CHN,AW_CHN,R_CHN,W_CHN,B_CHN]
458  def __init__(self, entity, name, clock, rdlag=None, blag=None):
459  BusDriver.__init__(self, entity, name, clock)
460 # self.log.setLevel(logging.DEBUG)
461  self.name = name
462  # set read and write back channels simulation lag between AXI sets valid and host responds with
463  # ready. If None - drive these signals
464  self.log.debug ("MAXIGPMaster.__init__(): super done")
465 
466  if rdlag is None:
467  self.bus.rready.setimmediatevalue(1)
468  else:
469  self.bus.xtra_rdlag.setimmediatevalue(rdlag)
470  if blag is None:
471  self.bus.bready.setimmediatevalue(1)
472  else:
473  self.bus.xtra_blag.setimmediatevalue(blag)
474  self.bus.awvalid.setimmediatevalue(0)
475  self.bus.wvalid.setimmediatevalue(0)
476  self.bus.arvalid.setimmediatevalue(0)
477  #just in case - set unimplemented in Zynq
478  self.bus.arlock.setimmediatevalue(0)
479  self.bus.arcache.setimmediatevalue(0)
480  self.bus.arprot.setimmediatevalue(0)
481  self.bus.arqos.setimmediatevalue(0)
482  self.bus.awlock.setimmediatevalue(0)
483  self.bus.awcache.setimmediatevalue(0)
484  self.bus.awprot.setimmediatevalue(0)
485  self.bus.awqos.setimmediatevalue(0)
486  self.busy_channels = {}
487  self.log.debug ("MAXIGPMaster.__init__(): pre-lock done")
488 
489  #Locks on each subchannel
490  for chn in self._channels:
491  self.log.debug ("MAXIGPMaster.__init__(): chn = %s"%(chn))
492  self.busy_channels[chn]=Lock("%s_%s_busy"%(name,chn))
493 
494  @cocotb.coroutine
495  def _send_write_address(self, address, delay, id, dlen, dsize, burst):
496  """
497  Send write address with parameters
498  @param address binary byte address for (first) burst start
499  @param delay Latency sending address in clock cycles
500  @param id transaction ID
501  @param dlen burst length (1..16)
502  @param dsize - data width - (1 << dsize) bytes (MAXIGP has only 2 bits while AXI specifies 3) 2 means 32 bits
503  @param burst burst type (0 - fixed, 1 - increment, 2 - wrap, 3 - reserved)
504  """
505 # self.log.debug ("MAXIGPMaster._send_write_address(",address,", ",delay,", ",id,", ",dlen ,", ",dsize, ", ", burst)
506  yield self.busy_channels[AW_CHN].acquire()
507  self.log.debug ("MAXIGPMaster._send_write_address(): acquired lock")
508  for _ in range(delay):
509  yield RisingEdge(self.clock)
510  self.log.debug ("MAXIGPMaster._send_write_address(): delay over")
511  self.bus.awvalid <= 1
512  self.bus.awid <= id
513  self.bus.awsize <= dsize
514  self.bus.awburst <= burst
515  while dlen > 16:
516  self.bus.awaddr <= address
517  address += 16*(1 << dsize)
518  dlen -= 16
519  self.bus.awlen <= 15
520  while True:
521  yield ReadOnly()
522  if self.bus.awready.value:
523  break
524  yield RisingEdge(self.clock)
525  yield RisingEdge(self.clock)
526  self.bus.awaddr <= address
527  self.bus.awlen <= dlen -1
528  self.log.debug ("1.MAXIGPMaster._send_write_address(), address=0x%08x, dlen = 0x%x"%(address, dlen))
529  while True:
530  yield ReadOnly()
531  if self.bus.awready.value:
532  break
533  yield RisingEdge(self.clock)
534  yield RisingEdge(self.clock)
535  self.bus.awvalid <= 0
536  # FLoat all assigned bus signals but awvalid
537  _float_signals((self.bus.awaddr,self.bus.awid, self.bus.awlen, self.bus.awsize,self.bus.awburst))
538  self.busy_channels[AW_CHN].release()
539  self.log.debug ("MAXIGPMaster._send_write_address(): released lock %s"%(AW_CHN))
540 
541  @cocotb.coroutine
542  def _send_read_address(self, address, delay, id, dlen, dsize, burst):
543  """
544  Send write address with parameters
545  @param address binary byte address for (first) burst start
546  @param delay Latency sending address in clock cycles
547  @param id transaction ID
548  @param dlen burst length (1..16)
549  @param dsize - data width - (1 << dsize) bytes (MAXIGP has only 2 bits while AXI specifies 3) 2 means 32 bits
550  @param burst burst type (0 - fixed, 1 - increment, 2 - wrap, 3 - reserved)
551  """
552 # self.log.debug ("MAXIGPMaster._send_write_address(",address,", ",delay,", ",id,", ",dlen ,", ",dsize, ", ", burst)
553  yield self.busy_channels[AR_CHN].acquire()
554  self.log.debug ("MAXIGPMaster._send_write_address(): acquired lock")
555  for _ in range(delay):
556  yield RisingEdge(self.clock)
557  self.log.debug ("MAXIGPMaster._send_read_address(): delay over")
558  self.bus.arvalid <= 1
559  self.bus.arid <= id
560  self.bus.arsize <= dsize
561  self.bus.arburst <= burst
562  while dlen > 16:
563  self.bus.araddr <= address
564  address += 16*(1 << dsize)
565  dlen -= 16
566  self.bus.arlen <= 15
567  while True:
568  yield ReadOnly()
569  if self.bus.arready.value:
570  break
571  yield RisingEdge(self.clock)
572  yield RisingEdge(self.clock)
573  self.bus.araddr <= address
574  self.bus.arlen <= dlen -1
575  self.log.debug ("1.MAXIGPMaster._send_read_address(), address=0x%08x, dlen = 0x%x"%(address, dlen))
576  while True:
577  yield ReadOnly()
578  if self.bus.arready.value:
579  break
580  yield RisingEdge(self.clock)
581  yield RisingEdge(self.clock)
582  self.bus.arvalid <= 0
583  # FLoat all assigned bus signals but awvalid
584  _float_signals((self.bus.araddr,self.bus.arid, self.bus.arlen, self.bus.arsize,self.bus.arburst))
585  self.busy_channels[AR_CHN].release()
586  self.log.debug ("MAXIGPMaster._send_read_address(): released lock %s"%(AR_CHN))
587 
588  @cocotb.coroutine
589  def _send_write_data(self, data, wrstb, delay, id, dsize):
590  """
591  Send a data word or a list of data words (supports multi-burst)
592  @param data a list/tuple of words to send
593  @param wrstb - write mask list (same size as data)
594  @param delay latency in clock cycles
595  @param id transaction ID
596  @param dsize - data width - (1 << dsize) bytes (MAXIGP has only 2 bits while AXI specifies 3) 2 means 32 bits
597  """
598  self.log.debug ("MAXIGPMaster._send_write_data("+str(data)+", "+str(wrstb)+", "+str(delay)+", "+str(id)+", "+str(dsize))
599  yield self.busy_channels[W_CHN].acquire()
600  self.log.debug ("MAXIGPMaster._send_write_data(): acquired lock")
601  for cycle in range(delay):
602  yield RisingEdge(self.clock)
603  self.log.debug ("MAXIGPMaster._send_write_data(): delay over")
604  self.bus.wvalid <= 1
605  self.bus.wid <= id
606  for i,val_wstb in enumerate(zip(data,wrstb)):
607  self.log.debug ("MAXIGPMaster._send_write_data(), i= %d, val_stb=%s "%(i,str(val_wstb)))
608  if (i == (len(data) - 1)) or ((i % 16) == 15):
609  self.bus.wlast <= 1
610  else:
611  self.bus.wlast <= 0
612  self.bus.wdata <= val_wstb[0]
613  self.bus.wstb <= val_wstb[1]
614  while True:
615  yield ReadOnly()
616  if self.bus.wready.value:
617  break
618  yield RisingEdge(self.clock)
619  yield RisingEdge(self.clock)
620  self.bus.wvalid <= 0
621  # FLoat all assigned bus signals but wvalid
622  _float_signals((self.bus.wdata,self.bus.wstb,self.bus.wlast))
623  self.busy_channels[W_CHN].release()
624  self.log.debug ("MAXIGPMaster._send_write_data(): released lock %s"%(W_CHN))
625  raise ReturnValue(dsize)
626 
627 
628  @cocotb.coroutine
629  def _get_read_data(self, address, id, dlen, dsize, delay):
630  """
631  Send a data word or a list of data words (supports multi-burst)
632  @param address start address to read data from (just for logging)
633  @param id expected receive data ID
634  @param dlen number of words to read
635  @param dsize - data width - (1 << dsize) bytes (MAXIGP has only 2 bits while AXI specifies 3) 2 means 32 bits
636  @param delay latency in clock cycles
637  """
638  self.log.debug ("MAXIGPMaster._get_read_data("+str(address)+", "+str(id)+", "+str(dlen)+", "+str(dsize)+", "+str(delay))
639  yield self.busy_channels[R_CHN].acquire()
640  self.log.debug ("MAXIGPMaster._get_read_data(): acquired lock")
641  for cycle in range(delay):
642  yield RisingEdge(self.clock)
643  self.log.debug ("MAXIGPMaster._get_read_data(): delay over")
644  self.bus.rready <= 1
645  data=[]
646  for i in range(dlen):
647  self.log.debug ("MAXIGPMaster._get_read_data(), i= %d"%(i))
648  while True:
649  yield ReadOnly()
650  if self.bus.rvalid.value:
651  try:
652  data.append(self.bus.rdata.value.integer)
653  except:
654  bv = self.bus.rdata.value
655  bv.binstr = re.sub("[^1]","0",bv.binstr)
656  data.append(bv.integer)
657  rid = int(self.bus.rid.value)
658  if rid != id:
659  self.log.error("Read data 0x%x ID mismatch - expected: 0x%x, got 0x%x"%(address+i,id, rid))
660  break
661  yield RisingEdge(self.clock)
662  yield RisingEdge(self.clock)
663  self.bus.rready <= 0
664  # FLoat all assigned bus signals but wvalid
665 # _float_signals((self.bus.wdata,self.bus.wstb,self.bus.wlast))
666  self.busy_channels[R_CHN].release()
667  self.log.debug ("MAXIGPMaster._get_read_data(): released lock %s"%(R_CHN))
668  raise ReturnValue(data)
669 
670 
671 
672  @cocotb.coroutine
673  def axi_write(self, address, value, byte_enable=None,
674  id=0, dsize=2, burst=1,address_latency=0,
675  data_latency=0):
676  self.log.debug("axi_write")
677  """
678  Write a data burst.
679  @param address binary byte address for burst start
680  @param value - a value or a list of values (supports multi-burst, but no interrupts between bursts)
681  @param byte_enable - byte enable mask. Should be None (all enabled) or have the same number of items as data
682  @param id transaction ID
683  @param dsize - data width - (1 << dsize) bytes (MAXIGP has only 2 bits while AXI specifies 3) 2 means 32 bits
684  @param burst burst type (0 - fixed, 1 - increment, 2 - wrap, 3 - reserved)
685  @param address_latency latency sending address in clock cycles
686  @param data_latency latency sending data in clock cycles
687  """
688  #Only wait if it is too late (<1/2 cycle)
689  if not int(self.clock):
690  yield RisingEdge(self.clock)
691 
692 # self.log.debug ("1.MAXIGPMaster.write(",address,", ",value, ",", byte_enable,", ",address_latency,",",
693 # data_latency,", ",id,", ",dsize,", ", burst)
694  if not isinstance(value, (list,tuple)):
695  value = (value,)
696  if not isinstance(byte_enable, (list,tuple)):
697  if byte_enable is None:
698  byte_enable = (1 << (1 << dsize)) - 1
699  byte_enable = [byte_enable]*len(value)
700  if None in byte_enable:
701  for i in range(len(byte_enable)):
702  if byte_enable[i] is None:
703  byte_enable[i] = (1 << (1 << dsize)) - 1
704  #assert len(value) == len(byte_enable), ("values and byte enable arrays have different lengths: %d and %d"%
705  # (len(value),len(byte_enable)))
706 
707 # self.log.debug ("2.MAXIGPMaster.write(",address,", ",value, ",", byte_enable,", ",address_latency,",",
708 # data_latency,", ",id,", ",dsize,", ", burst)
709 
710  c_addr = cocotb.fork(self._send_write_address(address= address,
711  delay= address_latency,
712  id = id,
713  dlen = len(value),
714  dsize = dsize,
715  burst = burst))
716 
717  c_data = cocotb.fork(self._send_write_data(data = value,
718  wrstb = byte_enable,
719  delay= data_latency,
720  id = id,
721  dsize = dsize))
722 
723  if c_addr:
724  self.log.debug ("c_addr.join()")
725  yield c_addr.join()
726  if c_data:
727  self.log.debug ("c_data.join()")
728  yield c_data.join()
729 # yield RisingEdge(self.clock)
730  self.log.debug ("axi_write:All done")
731  raise ReturnValue(0)
732 
733  @cocotb.coroutine
734  def axi_read(self, address, id = 0, dlen = 1, dsize = 2, burst = 1, address_latency = 0, data_latency= 0 ):
735  """
736  Receive data form AXI port
737  @param address start address to read data from
738  @param id expected receive data ID
739  @param dlen number of words to read
740  @param dsize - data width - (1 << dsize) bytes (MAXIGP has only 2 bits while AXI specifies 3) 2 means 32 bits
741  @param burst burst type (0 - fixed, 1 - increment, 2 - wrap, 3 - reserved)
742  @param address_latency latency sending address in clock cycles
743  @param data_latency latency sending data in clock cycles
744  @return A list of BinaryValue objects
745  """
746  #Only wait if it is too late (<1/2 cycle)
747  if not int(self.clock):
748  yield RisingEdge(self.clock)
749 
750  c_addr = cocotb.fork(self._send_read_address(address= address,
751  delay= address_latency,
752  id = id,
753  dlen = dlen,
754  dsize = dsize,
755  burst = burst))
756 
757  c_data = cocotb.fork(self._get_read_data (address= address,
758  id = id,
759  dlen = dlen,
760  dsize = dsize,
761  delay = data_latency))
762  if c_addr:
763  self.log.debug ("c_addr.join()")
764  yield c_addr.join()
765  if c_data:
766  self.log.debug ("c_data.join()")
767  data_rv=yield c_data.join()
768 # yield RisingEdge(self.clock)
769  self.log.debug ("axi_read:All done, returning, data_rv="+str(data_rv))
770  raise ReturnValue(data_rv)
def read_reg( self, addr)
def __init__( self, entity, name, clock, mempath, memhigh=0x40000000, data_bytes=8)
def __init__( self, entity, name, clock, mempath, memhigh=0x40000000, data_bytes=8, autoflush=True, blatency=5)
def __init__( self, entity, name, clock)
def write_reg( self, addr, data)
def _float_signals( signals)