Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74dd47ca99 | ||
|
|
cde2056bb0 | ||
|
|
b6870716ed | ||
|
|
44da562db9 | ||
|
|
8dcdbfefb8 | ||
|
|
b8919a095b | ||
|
|
bc7edec289 | ||
|
|
e7c3a31eb0 | ||
|
|
ce907ffbb9 | ||
|
|
95e2d5800d | ||
|
|
82853b31ff | ||
|
|
8bbabd92df | ||
|
|
c060f6c963 | ||
|
|
a767e00ce5 | ||
|
|
d1d7313b98 | ||
|
|
01b43b97f2 | ||
|
|
b5b6df84fe | ||
|
|
babe69f4d3 |
@@ -46,12 +46,12 @@ The first argument to the constructor accepts an `AxiBus` or `AxiLiteBus` object
|
|||||||
|
|
||||||
Once the module is instantiated, read and write operations can be initiated in a couple of different ways.
|
Once the module is instantiated, read and write operations can be initiated in a couple of different ways.
|
||||||
|
|
||||||
First, blocking operations can be carried out with `read()` and `write()` and their associated word-access wrappers. Multiple concurrent operations started from different coroutines are handled correctly. For example:
|
First, operations can be carried out with async blocking `read()`, `write()`, and their associated word-access wrappers. Multiple concurrent operations started from different coroutines are handled correctly, with results returned in the order that the operations complete. For example:
|
||||||
|
|
||||||
await axi_master.write(0x0000, b'test')
|
await axi_master.write(0x0000, b'test')
|
||||||
data = await axi_master.read(0x0000, 4)
|
data = await axi_master.read(0x0000, 4)
|
||||||
|
|
||||||
`read()` and `write()` return `namedtuple` objects containing _address_, _data_ or _length_, and _resp_. This is the preferred style, and this is the only style supported by the word-access wrappers.
|
Additional parameters can be specified to control sideband signals and burst settings. The transfer will be split into one or more bursts according to the AXI specification. All bursts generated from the same call to `read()` or `write()` will use the same ID, which will be automatically generated if not specified. `read()` and `write()` return `namedtuple` objects containing _address_, _data_ or _length_, and _resp_. This is the preferred style, and this is the only style supported by the word-access wrappers.
|
||||||
|
|
||||||
Alternatively, operations can be initiated with non-blocking `init_read()` and `init_write()`. These functions return `Event` objects which are triggered when the operation completes, and the result can be retrieved from `Event.data`. For example:
|
Alternatively, operations can be initiated with non-blocking `init_read()` and `init_write()`. These functions return `Event` objects which are triggered when the operation completes, and the result can be retrieved from `Event.data`. For example:
|
||||||
|
|
||||||
@@ -141,6 +141,7 @@ Once the module is instantiated, the memory contents can be accessed in a couple
|
|||||||
|
|
||||||
axi_ram.write(0x0000, b'test')
|
axi_ram.write(0x0000, b'test')
|
||||||
data = axi_ram.read(0x0000, 4)
|
data = axi_ram.read(0x0000, 4)
|
||||||
|
axi_ram.hexdump(0x0000, 4, prefix="RAM")
|
||||||
|
|
||||||
Multi-port memories can be constructed by passing the `mem` object of the first instance to the other instances. For example, here is how to create a four-port RAM:
|
Multi-port memories can be constructed by passing the `mem` object of the first instance to the other instances. For example, here is how to create a four-port RAM:
|
||||||
|
|
||||||
@@ -242,8 +243,8 @@ Note: _byte_size_, _byte_lanes_, `len(tdata)`, and `len(tkeep)` are all related,
|
|||||||
* _pause_: stall the interface (deassert `tready` or `tvalid`) (source/sink only)
|
* _pause_: stall the interface (deassert `tready` or `tvalid`) (source/sink only)
|
||||||
* _queue_occupancy_bytes_: number of bytes in queue (all)
|
* _queue_occupancy_bytes_: number of bytes in queue (all)
|
||||||
* _queue_occupancy_frames_: number of frames in queue (all)
|
* _queue_occupancy_frames_: number of frames in queue (all)
|
||||||
* _queue_occupancy_limit_bytes_: max number of bytes in queue allowed before tready deassert (sink only)
|
* _queue_occupancy_limit_bytes_: max number of bytes in queue allowed before backpressure is applied (source/sink only)
|
||||||
* _queue_occupancy_limit_frames_: max number of frames in queue allowed before tready deassert (sink only)
|
* _queue_occupancy_limit_frames_: max number of frames in queue allowed before backpressure is applied (source/sink only)
|
||||||
|
|
||||||
#### Methods
|
#### Methods
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ from .stream import define_stream
|
|||||||
|
|
||||||
# Write address channel
|
# Write address channel
|
||||||
AxiAWBus, AxiAWTransaction, AxiAWSource, AxiAWSink, AxiAWMonitor = define_stream("AxiAW",
|
AxiAWBus, AxiAWTransaction, AxiAWSource, AxiAWSink, AxiAWMonitor = define_stream("AxiAW",
|
||||||
signals=["awid", "awaddr", "awlen", "awsize", "awburst", "awprot", "awvalid", "awready"],
|
signals=["awid", "awaddr", "awlen", "awsize", "awburst", "awvalid", "awready"],
|
||||||
optional_signals=["awlock", "awcache", "awqos", "awregion", "awuser"],
|
optional_signals=["awlock", "awcache", "awprot", "awqos", "awregion", "awuser"],
|
||||||
signal_widths={"awlen": 8, "awsize": 3, "awburst": 2, "awlock": 1,
|
signal_widths={"awlen": 8, "awsize": 3, "awburst": 2, "awlock": 1,
|
||||||
"awcache": 4, "awprot": 3, "awqos": 4, "awregion": 4}
|
"awcache": 4, "awprot": 3, "awqos": 4, "awregion": 4}
|
||||||
)
|
)
|
||||||
@@ -41,23 +41,23 @@ AxiWBus, AxiWTransaction, AxiWSource, AxiWSink, AxiWMonitor = define_stream("Axi
|
|||||||
|
|
||||||
# Write response channel
|
# Write response channel
|
||||||
AxiBBus, AxiBTransaction, AxiBSource, AxiBSink, AxiBMonitor = define_stream("AxiB",
|
AxiBBus, AxiBTransaction, AxiBSource, AxiBSink, AxiBMonitor = define_stream("AxiB",
|
||||||
signals=["bid", "bresp", "bvalid", "bready"],
|
signals=["bid", "bvalid", "bready"],
|
||||||
optional_signals=["buser"],
|
optional_signals=["bresp", "buser"],
|
||||||
signal_widths={"bresp": 2}
|
signal_widths={"bresp": 2}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Read address channel
|
# Read address channel
|
||||||
AxiARBus, AxiARTransaction, AxiARSource, AxiARSink, AxiARMonitor = define_stream("AxiAR",
|
AxiARBus, AxiARTransaction, AxiARSource, AxiARSink, AxiARMonitor = define_stream("AxiAR",
|
||||||
signals=["arid", "araddr", "arlen", "arsize", "arburst", "arprot", "arvalid", "arready"],
|
signals=["arid", "araddr", "arlen", "arsize", "arburst", "arvalid", "arready"],
|
||||||
optional_signals=["arlock", "arcache", "arqos", "arregion", "aruser"],
|
optional_signals=["arlock", "arcache", "arprot", "arqos", "arregion", "aruser"],
|
||||||
signal_widths={"arlen": 8, "arsize": 3, "arburst": 2, "arlock": 1,
|
signal_widths={"arlen": 8, "arsize": 3, "arburst": 2, "arlock": 1,
|
||||||
"arcache": 4, "arprot": 3, "arqos": 4, "arregion": 4}
|
"arcache": 4, "arprot": 3, "arqos": 4, "arregion": 4}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Read data channel
|
# Read data channel
|
||||||
AxiRBus, AxiRTransaction, AxiRSource, AxiRSink, AxiRMonitor = define_stream("AxiR",
|
AxiRBus, AxiRTransaction, AxiRSource, AxiRSink, AxiRMonitor = define_stream("AxiR",
|
||||||
signals=["rid", "rdata", "rresp", "rlast", "rvalid", "rready"],
|
signals=["rid", "rdata", "rlast", "rvalid", "rready"],
|
||||||
optional_signals=["ruser"],
|
optional_signals=["rresp", "ruser"],
|
||||||
signal_widths={"rresp": 2, "rlast": 1}
|
signal_widths={"rresp": 2, "rlast": 1}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -49,8 +49,97 @@ AxiReadRespCmd = namedtuple("AxiReadRespCmd", ["address", "length", "size", "cyc
|
|||||||
AxiReadResp = namedtuple("AxiReadResp", ["address", "data", "resp", "user"])
|
AxiReadResp = namedtuple("AxiReadResp", ["address", "data", "resp", "user"])
|
||||||
|
|
||||||
|
|
||||||
|
class TagContext:
|
||||||
|
def __init__(self, manager):
|
||||||
|
self.current_tag = 0
|
||||||
|
self._cmd_queue = Queue()
|
||||||
|
self._current_cmd = None
|
||||||
|
self._resp_queue = Queue()
|
||||||
|
self._cr = None
|
||||||
|
self._manager = manager
|
||||||
|
|
||||||
|
async def get_resp(self):
|
||||||
|
return await self._resp_queue.get()
|
||||||
|
|
||||||
|
def get_resp_nowait(self):
|
||||||
|
return self._resp_queue.get_nowait()
|
||||||
|
|
||||||
|
def _start(self):
|
||||||
|
if self._cr is None:
|
||||||
|
self._cr = cocotb.fork(self._process_queue())
|
||||||
|
|
||||||
|
def _flush(self):
|
||||||
|
flushed_cmds = []
|
||||||
|
if self._cr is not None:
|
||||||
|
self._cr.kill()
|
||||||
|
self._cr = None
|
||||||
|
self._manager._set_idle(self)
|
||||||
|
if self._current_cmd is not None:
|
||||||
|
flushed_cmds.append(self._current_cmd)
|
||||||
|
self._current_cmd = None
|
||||||
|
while not self._cmd_queue.empty():
|
||||||
|
flushed_cmds.append(self._cmd_queue.get_nowait())
|
||||||
|
while not self._resp_queue.empty():
|
||||||
|
self._resp_queue.get_nowait()
|
||||||
|
return flushed_cmds
|
||||||
|
|
||||||
|
async def _process_queue(self):
|
||||||
|
while True:
|
||||||
|
cmd = await self._cmd_queue.get()
|
||||||
|
self._current_cmd = cmd
|
||||||
|
await self._manager._process(self, cmd)
|
||||||
|
self._current_cmd = None
|
||||||
|
|
||||||
|
if self._cmd_queue.empty() and self._resp_queue.empty():
|
||||||
|
self._manager._set_idle(self)
|
||||||
|
|
||||||
|
|
||||||
|
class TagContextManager:
|
||||||
|
def __init__(self, process):
|
||||||
|
self._context_list = []
|
||||||
|
self._context_idle_list = []
|
||||||
|
self._context_mapping = {}
|
||||||
|
self._process = process
|
||||||
|
|
||||||
|
def _get_context(self, tag):
|
||||||
|
if tag in self._context_mapping:
|
||||||
|
return self._context_mapping[tag]
|
||||||
|
elif self._context_idle_list:
|
||||||
|
context = self._context_idle_list.pop()
|
||||||
|
else:
|
||||||
|
context = TagContext(self)
|
||||||
|
self._context_list.append(context)
|
||||||
|
context._start()
|
||||||
|
context.current_tag = tag
|
||||||
|
self._context_mapping[tag] = context
|
||||||
|
return context
|
||||||
|
|
||||||
|
def start_cmd(self, tag, cmd):
|
||||||
|
context = self._get_context(tag)
|
||||||
|
context._cmd_queue.put_nowait(cmd)
|
||||||
|
|
||||||
|
def put_resp(self, tag, resp):
|
||||||
|
context = self._get_context(tag)
|
||||||
|
context._resp_queue.put_nowait(resp)
|
||||||
|
|
||||||
|
def _set_idle(self, context):
|
||||||
|
if context.current_tag in self._context_mapping:
|
||||||
|
del self._context_mapping[context.current_tag]
|
||||||
|
self._context_idle_list.append(context)
|
||||||
|
context.current_tag = None
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
flushed_cmds = []
|
||||||
|
for c in self._context_list:
|
||||||
|
flushed_cmds.extend(c._flush())
|
||||||
|
return flushed_cmds
|
||||||
|
|
||||||
|
|
||||||
class AxiMasterWrite(Reset):
|
class AxiMasterWrite(Reset):
|
||||||
def __init__(self, bus, clock, reset=None, reset_active_level=True, max_burst_len=256):
|
def __init__(self, bus, clock, reset=None, reset_active_level=True, max_burst_len=256):
|
||||||
|
self.bus = bus
|
||||||
|
self.clock = clock
|
||||||
|
self.reset = reset
|
||||||
self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")
|
self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")
|
||||||
|
|
||||||
self.log.info("AXI master (write)")
|
self.log.info("AXI master (write)")
|
||||||
@@ -72,9 +161,7 @@ class AxiMasterWrite(Reset):
|
|||||||
self.cur_id = 0
|
self.cur_id = 0
|
||||||
self.active_id = Counter()
|
self.active_id = Counter()
|
||||||
|
|
||||||
self.int_write_resp_command_queue = [Queue() for k in range(self.id_count)]
|
self.tag_context_manager = TagContextManager(self._process_write_resp_id)
|
||||||
self.current_write_resp_command = [None for k in range(self.id_count)]
|
|
||||||
self.int_write_resp_queue_list = [Queue() for k in range(self.id_count)]
|
|
||||||
|
|
||||||
self.in_flight_operations = 0
|
self.in_flight_operations = 0
|
||||||
self._idle = Event()
|
self._idle = Event()
|
||||||
@@ -82,29 +169,45 @@ class AxiMasterWrite(Reset):
|
|||||||
|
|
||||||
self.width = len(self.w_channel.bus.wdata)
|
self.width = len(self.w_channel.bus.wdata)
|
||||||
self.byte_size = 8
|
self.byte_size = 8
|
||||||
self.byte_width = self.width // self.byte_size
|
self.byte_lanes = self.width // self.byte_size
|
||||||
self.strb_mask = 2**self.byte_width-1
|
self.strb_mask = 2**self.byte_lanes-1
|
||||||
|
|
||||||
self.max_burst_len = max(min(max_burst_len, 256), 1)
|
self.max_burst_len = max(min(max_burst_len, 256), 1)
|
||||||
self.max_burst_size = (self.byte_width-1).bit_length()
|
self.max_burst_size = (self.byte_lanes-1).bit_length()
|
||||||
|
|
||||||
|
self.awlock_present = hasattr(self.bus.aw, "awlock")
|
||||||
|
self.awcache_present = hasattr(self.bus.aw, "awcache")
|
||||||
|
self.awprot_present = hasattr(self.bus.aw, "awprot")
|
||||||
|
self.awqos_present = hasattr(self.bus.aw, "awqos")
|
||||||
|
self.awregion_present = hasattr(self.bus.aw, "awregion")
|
||||||
|
self.awuser_present = hasattr(self.bus.aw, "awuser")
|
||||||
|
self.wuser_present = hasattr(self.bus.w, "wuser")
|
||||||
|
self.buser_present = hasattr(self.bus.b, "buser")
|
||||||
|
|
||||||
self.log.info("AXI master configuration:")
|
self.log.info("AXI master configuration:")
|
||||||
self.log.info(" Address width: %d bits", len(self.aw_channel.bus.awaddr))
|
self.log.info(" Address width: %d bits", len(self.aw_channel.bus.awaddr))
|
||||||
self.log.info(" ID width: %d bits", len(self.aw_channel.bus.awid))
|
self.log.info(" ID width: %d bits", len(self.aw_channel.bus.awid))
|
||||||
self.log.info(" Byte size: %d bits", self.byte_size)
|
self.log.info(" Byte size: %d bits", self.byte_size)
|
||||||
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_width)
|
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
|
||||||
self.log.info(" Max burst size: %d (%d bytes)", self.max_burst_size, 2**self.max_burst_size)
|
self.log.info(" Max burst size: %d (%d bytes)", self.max_burst_size, 2**self.max_burst_size)
|
||||||
self.log.info(" Max burst length: %d cycles (%d bytes)",
|
self.log.info(" Max burst length: %d cycles (%d bytes)",
|
||||||
self.max_burst_len, self.max_burst_len*self.byte_width)
|
self.max_burst_len, self.max_burst_len*self.byte_lanes)
|
||||||
|
|
||||||
assert self.byte_width == len(self.w_channel.bus.wstrb)
|
self.log.info("AXI master signals:")
|
||||||
assert self.byte_width * self.byte_size == self.width
|
for bus in (self.bus.aw, self.bus.w, self.bus.b):
|
||||||
|
for sig in sorted(list(set().union(bus._signals, bus._optional_signals))):
|
||||||
|
if hasattr(bus, sig):
|
||||||
|
self.log.info(" %s width: %d bits", sig, len(getattr(bus, sig)))
|
||||||
|
else:
|
||||||
|
self.log.info(" %s: not present", sig)
|
||||||
|
|
||||||
|
assert self.byte_lanes == len(self.w_channel.bus.wstrb)
|
||||||
|
assert self.byte_lanes * self.byte_size == self.width
|
||||||
|
|
||||||
assert len(self.b_channel.bus.bid) == len(self.aw_channel.bus.awid)
|
assert len(self.b_channel.bus.bid) == len(self.aw_channel.bus.awid)
|
||||||
|
|
||||||
self._process_write_cr = None
|
self._process_write_cr = None
|
||||||
self._process_write_resp_cr = None
|
self._process_write_resp_cr = None
|
||||||
self._process_write_resp_id_cr = None
|
|
||||||
|
|
||||||
self._init_reset(reset, reset_active_level)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
@@ -132,6 +235,27 @@ class AxiMasterWrite(Reset):
|
|||||||
lock = AxiLockType(lock)
|
lock = AxiLockType(lock)
|
||||||
prot = AxiProt(prot)
|
prot = AxiProt(prot)
|
||||||
|
|
||||||
|
if not self.awlock_present and lock != AxiLockType.NORMAL:
|
||||||
|
raise ValueError("awlock sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.awcache_present and cache != 0b0011:
|
||||||
|
raise ValueError("awcache sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.awprot_present and prot != AxiProt.NONSECURE:
|
||||||
|
raise ValueError("awprot sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.awqos_present and qos != 0:
|
||||||
|
raise ValueError("awqos sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.awregion_present and region != 0:
|
||||||
|
raise ValueError("awregion sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.awuser_present and user != 0:
|
||||||
|
raise ValueError("awuser sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.wuser_present and wuser != 0:
|
||||||
|
raise ValueError("wuser sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
if wuser is None:
|
if wuser is None:
|
||||||
wuser = 0
|
wuser = 0
|
||||||
elif isinstance(wuser, int):
|
elif isinstance(wuser, int):
|
||||||
@@ -207,10 +331,6 @@ class AxiMasterWrite(Reset):
|
|||||||
if self._process_write_resp_cr is not None:
|
if self._process_write_resp_cr is not None:
|
||||||
self._process_write_resp_cr.kill()
|
self._process_write_resp_cr.kill()
|
||||||
self._process_write_resp_cr = None
|
self._process_write_resp_cr = None
|
||||||
if self._process_write_resp_id_cr is not None:
|
|
||||||
for cr in self._process_write_resp_id_cr:
|
|
||||||
cr.kill()
|
|
||||||
self._process_write_resp_id_cr = None
|
|
||||||
|
|
||||||
self.aw_channel.clear()
|
self.aw_channel.clear()
|
||||||
self.w_channel.clear()
|
self.w_channel.clear()
|
||||||
@@ -230,21 +350,9 @@ class AxiMasterWrite(Reset):
|
|||||||
self.current_write_command = None
|
self.current_write_command = None
|
||||||
flush_cmd(cmd)
|
flush_cmd(cmd)
|
||||||
|
|
||||||
for q in self.int_write_resp_command_queue:
|
for cmd in self.tag_context_manager.flush():
|
||||||
while not q.empty():
|
|
||||||
cmd = q.get_nowait()
|
|
||||||
flush_cmd(cmd)
|
flush_cmd(cmd)
|
||||||
|
|
||||||
for k in range(len(self.current_write_resp_command)):
|
|
||||||
if self.current_write_resp_command[k]:
|
|
||||||
cmd = self.current_write_resp_command[k]
|
|
||||||
self.current_write_resp_command[k] = None
|
|
||||||
flush_cmd(cmd)
|
|
||||||
|
|
||||||
for q in self.int_write_resp_queue_list:
|
|
||||||
while not q.empty():
|
|
||||||
q.get_nowait()
|
|
||||||
|
|
||||||
self.cur_id = 0
|
self.cur_id = 0
|
||||||
self.active_id = Counter()
|
self.active_id = Counter()
|
||||||
|
|
||||||
@@ -256,8 +364,6 @@ class AxiMasterWrite(Reset):
|
|||||||
self._process_write_cr = cocotb.fork(self._process_write())
|
self._process_write_cr = cocotb.fork(self._process_write())
|
||||||
if self._process_write_resp_cr is None:
|
if self._process_write_resp_cr is None:
|
||||||
self._process_write_resp_cr = cocotb.fork(self._process_write_resp())
|
self._process_write_resp_cr = cocotb.fork(self._process_write_resp())
|
||||||
if self._process_write_resp_id_cr is None:
|
|
||||||
self._process_write_resp_id_cr = [cocotb.fork(self._process_write_resp_id(i)) for i in range(self.id_count)]
|
|
||||||
|
|
||||||
async def _process_write(self):
|
async def _process_write(self):
|
||||||
while True:
|
while True:
|
||||||
@@ -267,10 +373,10 @@ class AxiMasterWrite(Reset):
|
|||||||
num_bytes = 2**cmd.size
|
num_bytes = 2**cmd.size
|
||||||
|
|
||||||
aligned_addr = (cmd.address // num_bytes) * num_bytes
|
aligned_addr = (cmd.address // num_bytes) * num_bytes
|
||||||
word_addr = (cmd.address // self.byte_width) * self.byte_width
|
word_addr = (cmd.address // self.byte_lanes) * self.byte_lanes
|
||||||
|
|
||||||
start_offset = cmd.address % self.byte_width
|
start_offset = cmd.address % self.byte_lanes
|
||||||
end_offset = ((cmd.address + len(cmd.data) - 1) % self.byte_width) + 1
|
end_offset = ((cmd.address + len(cmd.data) - 1) % self.byte_lanes) + 1
|
||||||
|
|
||||||
cycles = (len(cmd.data) + (cmd.address % num_bytes) + num_bytes-1) // num_bytes
|
cycles = (len(cmd.data) + (cmd.address % num_bytes) + num_bytes-1) // num_bytes
|
||||||
|
|
||||||
@@ -303,7 +409,7 @@ class AxiMasterWrite(Reset):
|
|||||||
if k == cycles-1:
|
if k == cycles-1:
|
||||||
stop = end_offset
|
stop = end_offset
|
||||||
|
|
||||||
strb = (self.strb_mask << start) & self.strb_mask & (self.strb_mask >> (self.byte_width - stop))
|
strb = (self.strb_mask << start) & self.strb_mask & (self.strb_mask >> (self.byte_lanes - stop))
|
||||||
|
|
||||||
val = 0
|
val = 0
|
||||||
for j in range(start, stop):
|
for j in range(start, stop):
|
||||||
@@ -350,18 +456,18 @@ class AxiMasterWrite(Reset):
|
|||||||
if isinstance(wuser, int):
|
if isinstance(wuser, int):
|
||||||
w.wuser = wuser
|
w.wuser = wuser
|
||||||
else:
|
else:
|
||||||
if wuser:
|
if wuser and k < len(wuser):
|
||||||
w.wuser = wuser.pop(0)
|
w.wuser = wuser[k]
|
||||||
else:
|
else:
|
||||||
w.wuser = 0
|
w.wuser = 0
|
||||||
|
|
||||||
await self.w_channel.send(w)
|
await self.w_channel.send(w)
|
||||||
|
|
||||||
cur_addr += num_bytes
|
cur_addr += num_bytes
|
||||||
cycle_offset = (cycle_offset + num_bytes) % self.byte_width
|
cycle_offset = (cycle_offset + num_bytes) % self.byte_lanes
|
||||||
|
|
||||||
resp_cmd = AxiWriteRespCmd(cmd.address, len(cmd.data), cmd.size, cycles, cmd.prot, burst_list, cmd.event)
|
resp_cmd = AxiWriteRespCmd(cmd.address, len(cmd.data), cmd.size, cycles, cmd.prot, burst_list, cmd.event)
|
||||||
await self.int_write_resp_command_queue[awid].put(resp_cmd)
|
self.tag_context_manager.start_cmd(awid, resp_cmd)
|
||||||
|
|
||||||
self.current_write_command = None
|
self.current_write_command = None
|
||||||
|
|
||||||
@@ -374,20 +480,17 @@ class AxiMasterWrite(Reset):
|
|||||||
if self.active_id[bid] <= 0:
|
if self.active_id[bid] <= 0:
|
||||||
raise Exception(f"Unexpected burst ID {bid}")
|
raise Exception(f"Unexpected burst ID {bid}")
|
||||||
|
|
||||||
await self.int_write_resp_queue_list[bid].put(b)
|
self.tag_context_manager.put_resp(bid, b)
|
||||||
|
|
||||||
async def _process_write_resp_id(self, bid):
|
async def _process_write_resp_id(self, context, cmd):
|
||||||
while True:
|
bid = context.current_tag
|
||||||
cmd = await self.int_write_resp_command_queue[bid].get()
|
|
||||||
self.current_write_resp_command[bid] = cmd
|
|
||||||
|
|
||||||
resp = AxiResp.OKAY
|
resp = AxiResp.OKAY
|
||||||
user = []
|
user = []
|
||||||
|
|
||||||
for burst_length in cmd.burst_list:
|
for burst_length in cmd.burst_list:
|
||||||
b = await self.int_write_resp_queue_list[bid].get()
|
b = await context.get_resp()
|
||||||
|
|
||||||
burst_id = int(b.bid)
|
|
||||||
burst_resp = AxiResp(b.bresp)
|
burst_resp = AxiResp(b.bresp)
|
||||||
burst_user = int(b.buser)
|
burst_user = int(b.buser)
|
||||||
|
|
||||||
@@ -402,7 +505,10 @@ class AxiMasterWrite(Reset):
|
|||||||
|
|
||||||
self.active_id[bid] -= 1
|
self.active_id[bid] -= 1
|
||||||
|
|
||||||
self.log.info("Write burst complete bid: 0x%x bresp: %s", burst_id, burst_resp)
|
self.log.info("Write burst complete bid: 0x%x bresp: %s", bid, burst_resp)
|
||||||
|
|
||||||
|
if not self.buser_present:
|
||||||
|
user = None
|
||||||
|
|
||||||
self.log.info("Write complete addr: 0x%08x prot: %s resp: %s length: %d",
|
self.log.info("Write complete addr: 0x%08x prot: %s resp: %s length: %d",
|
||||||
cmd.address, cmd.prot, resp, cmd.length)
|
cmd.address, cmd.prot, resp, cmd.length)
|
||||||
@@ -411,8 +517,6 @@ class AxiMasterWrite(Reset):
|
|||||||
|
|
||||||
cmd.event.set(write_resp)
|
cmd.event.set(write_resp)
|
||||||
|
|
||||||
self.current_write_resp_command[bid] = None
|
|
||||||
|
|
||||||
self.in_flight_operations -= 1
|
self.in_flight_operations -= 1
|
||||||
|
|
||||||
if self.in_flight_operations == 0:
|
if self.in_flight_operations == 0:
|
||||||
@@ -421,6 +525,9 @@ class AxiMasterWrite(Reset):
|
|||||||
|
|
||||||
class AxiMasterRead(Reset):
|
class AxiMasterRead(Reset):
|
||||||
def __init__(self, bus, clock, reset=None, reset_active_level=True, max_burst_len=256):
|
def __init__(self, bus, clock, reset=None, reset_active_level=True, max_burst_len=256):
|
||||||
|
self.bus = bus
|
||||||
|
self.clock = clock
|
||||||
|
self.reset = reset
|
||||||
self.log = logging.getLogger(f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")
|
self.log = logging.getLogger(f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")
|
||||||
|
|
||||||
self.log.info("AXI master (read)")
|
self.log.info("AXI master (read)")
|
||||||
@@ -440,9 +547,7 @@ class AxiMasterRead(Reset):
|
|||||||
self.cur_id = 0
|
self.cur_id = 0
|
||||||
self.active_id = Counter()
|
self.active_id = Counter()
|
||||||
|
|
||||||
self.int_read_resp_command_queue = [Queue() for k in range(self.id_count)]
|
self.tag_context_manager = TagContextManager(self._process_read_resp_id)
|
||||||
self.current_read_resp_command = [None for k in range(self.id_count)]
|
|
||||||
self.int_read_resp_queue_list = [Queue() for k in range(self.id_count)]
|
|
||||||
|
|
||||||
self.in_flight_operations = 0
|
self.in_flight_operations = 0
|
||||||
self._idle = Event()
|
self._idle = Event()
|
||||||
@@ -450,27 +555,42 @@ class AxiMasterRead(Reset):
|
|||||||
|
|
||||||
self.width = len(self.r_channel.bus.rdata)
|
self.width = len(self.r_channel.bus.rdata)
|
||||||
self.byte_size = 8
|
self.byte_size = 8
|
||||||
self.byte_width = self.width // self.byte_size
|
self.byte_lanes = self.width // self.byte_size
|
||||||
|
|
||||||
self.max_burst_len = max(min(max_burst_len, 256), 1)
|
self.max_burst_len = max(min(max_burst_len, 256), 1)
|
||||||
self.max_burst_size = (self.byte_width-1).bit_length()
|
self.max_burst_size = (self.byte_lanes-1).bit_length()
|
||||||
|
|
||||||
|
self.arlock_present = hasattr(self.bus.ar, "arlock")
|
||||||
|
self.arcache_present = hasattr(self.bus.ar, "arcache")
|
||||||
|
self.arprot_present = hasattr(self.bus.ar, "arprot")
|
||||||
|
self.arqos_present = hasattr(self.bus.ar, "arqos")
|
||||||
|
self.arregion_present = hasattr(self.bus.ar, "arregion")
|
||||||
|
self.aruser_present = hasattr(self.bus.ar, "aruser")
|
||||||
|
self.ruser_present = hasattr(self.bus.r, "ruser")
|
||||||
|
|
||||||
self.log.info("AXI master configuration:")
|
self.log.info("AXI master configuration:")
|
||||||
self.log.info(" Address width: %d bits", len(self.ar_channel.bus.araddr))
|
self.log.info(" Address width: %d bits", len(self.ar_channel.bus.araddr))
|
||||||
self.log.info(" ID width: %d bits", len(self.ar_channel.bus.arid))
|
self.log.info(" ID width: %d bits", len(self.ar_channel.bus.arid))
|
||||||
self.log.info(" Byte size: %d bits", self.byte_size)
|
self.log.info(" Byte size: %d bits", self.byte_size)
|
||||||
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_width)
|
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
|
||||||
self.log.info(" Max burst size: %d (%d bytes)", self.max_burst_size, 2**self.max_burst_size)
|
self.log.info(" Max burst size: %d (%d bytes)", self.max_burst_size, 2**self.max_burst_size)
|
||||||
self.log.info(" Max burst length: %d cycles (%d bytes)",
|
self.log.info(" Max burst length: %d cycles (%d bytes)",
|
||||||
self.max_burst_len, self.max_burst_len*self.byte_width)
|
self.max_burst_len, self.max_burst_len*self.byte_lanes)
|
||||||
|
|
||||||
assert self.byte_width * self.byte_size == self.width
|
self.log.info("AXI master signals:")
|
||||||
|
for bus in (self.bus.ar, self.bus.r):
|
||||||
|
for sig in sorted(list(set().union(bus._signals, bus._optional_signals))):
|
||||||
|
if hasattr(bus, sig):
|
||||||
|
self.log.info(" %s width: %d bits", sig, len(getattr(bus, sig)))
|
||||||
|
else:
|
||||||
|
self.log.info(" %s: not present", sig)
|
||||||
|
|
||||||
|
assert self.byte_lanes * self.byte_size == self.width
|
||||||
|
|
||||||
assert len(self.r_channel.bus.rid) == len(self.ar_channel.bus.arid)
|
assert len(self.r_channel.bus.rid) == len(self.ar_channel.bus.arid)
|
||||||
|
|
||||||
self._process_read_cr = None
|
self._process_read_cr = None
|
||||||
self._process_read_resp_cr = None
|
self._process_read_resp_cr = None
|
||||||
self._process_read_resp_id_cr = None
|
|
||||||
|
|
||||||
self._init_reset(reset, reset_active_level)
|
self._init_reset(reset, reset_active_level)
|
||||||
|
|
||||||
@@ -501,6 +621,24 @@ class AxiMasterRead(Reset):
|
|||||||
lock = AxiLockType(lock)
|
lock = AxiLockType(lock)
|
||||||
prot = AxiProt(prot)
|
prot = AxiProt(prot)
|
||||||
|
|
||||||
|
if not self.arlock_present and lock != AxiLockType.NORMAL:
|
||||||
|
raise ValueError("arlock sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.arcache_present and cache != 0b0011:
|
||||||
|
raise ValueError("arcache sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.arprot_present and prot != AxiProt.NONSECURE:
|
||||||
|
raise ValueError("arprot sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.arqos_present and qos != 0:
|
||||||
|
raise ValueError("arqos sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.arregion_present and region != 0:
|
||||||
|
raise ValueError("arregion sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
|
if not self.aruser_present and user != 0:
|
||||||
|
raise ValueError("aruser sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
self.in_flight_operations += 1
|
self.in_flight_operations += 1
|
||||||
self._idle.clear()
|
self._idle.clear()
|
||||||
|
|
||||||
@@ -568,10 +706,6 @@ class AxiMasterRead(Reset):
|
|||||||
if self._process_read_resp_cr is not None:
|
if self._process_read_resp_cr is not None:
|
||||||
self._process_read_resp_cr.kill()
|
self._process_read_resp_cr.kill()
|
||||||
self._process_read_resp_cr = None
|
self._process_read_resp_cr = None
|
||||||
if self._process_read_resp_id_cr is not None:
|
|
||||||
for cr in self._process_read_resp_id_cr:
|
|
||||||
cr.kill()
|
|
||||||
self._process_read_resp_id_cr = None
|
|
||||||
|
|
||||||
self.ar_channel.clear()
|
self.ar_channel.clear()
|
||||||
self.r_channel.clear()
|
self.r_channel.clear()
|
||||||
@@ -590,21 +724,9 @@ class AxiMasterRead(Reset):
|
|||||||
self.current_read_command = None
|
self.current_read_command = None
|
||||||
flush_cmd(cmd)
|
flush_cmd(cmd)
|
||||||
|
|
||||||
for q in self.int_read_resp_command_queue:
|
for cmd in self.tag_context_manager.flush():
|
||||||
while not q.empty():
|
|
||||||
cmd = q.get_nowait()
|
|
||||||
flush_cmd(cmd)
|
flush_cmd(cmd)
|
||||||
|
|
||||||
for k in range(len(self.current_read_resp_command)):
|
|
||||||
if self.current_read_resp_command[k]:
|
|
||||||
cmd = self.current_read_resp_command[k]
|
|
||||||
self.current_read_resp_command[k] = None
|
|
||||||
flush_cmd(cmd)
|
|
||||||
|
|
||||||
for q in self.int_read_resp_queue_list:
|
|
||||||
while not q.empty():
|
|
||||||
q.get_nowait()
|
|
||||||
|
|
||||||
self.cur_id = 0
|
self.cur_id = 0
|
||||||
self.active_id = Counter()
|
self.active_id = Counter()
|
||||||
|
|
||||||
@@ -616,8 +738,6 @@ class AxiMasterRead(Reset):
|
|||||||
self._process_read_cr = cocotb.fork(self._process_read())
|
self._process_read_cr = cocotb.fork(self._process_read())
|
||||||
if self._process_read_resp_cr is None:
|
if self._process_read_resp_cr is None:
|
||||||
self._process_read_resp_cr = cocotb.fork(self._process_read_resp())
|
self._process_read_resp_cr = cocotb.fork(self._process_read_resp())
|
||||||
if self._process_read_resp_id_cr is None:
|
|
||||||
self._process_read_resp_id_cr = [cocotb.fork(self._process_read_resp_id(i)) for i in range(self.id_count)]
|
|
||||||
|
|
||||||
async def _process_read(self):
|
async def _process_read(self):
|
||||||
while True:
|
while True:
|
||||||
@@ -680,32 +800,42 @@ class AxiMasterRead(Reset):
|
|||||||
cur_addr += num_bytes
|
cur_addr += num_bytes
|
||||||
|
|
||||||
resp_cmd = AxiReadRespCmd(cmd.address, cmd.length, cmd.size, cycles, cmd.prot, burst_list, cmd.event)
|
resp_cmd = AxiReadRespCmd(cmd.address, cmd.length, cmd.size, cycles, cmd.prot, burst_list, cmd.event)
|
||||||
await self.int_read_resp_command_queue[arid].put(resp_cmd)
|
self.tag_context_manager.start_cmd(arid, resp_cmd)
|
||||||
|
|
||||||
self.current_read_command = None
|
self.current_read_command = None
|
||||||
|
|
||||||
async def _process_read_resp(self):
|
async def _process_read_resp(self):
|
||||||
|
burst = []
|
||||||
|
cur_rid = None
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
r = await self.r_channel.recv()
|
r = await self.r_channel.recv()
|
||||||
|
|
||||||
rid = int(r.rid)
|
rid = int(r.rid)
|
||||||
|
|
||||||
|
if cur_rid is not None and cur_rid != rid:
|
||||||
|
raise Exception(f"ID not constant within burst (expected {cur_rid}, got {rid})")
|
||||||
|
|
||||||
if self.active_id[rid] <= 0:
|
if self.active_id[rid] <= 0:
|
||||||
raise Exception(f"Unexpected burst ID {rid}")
|
raise Exception(f"Unexpected burst ID {rid}")
|
||||||
|
|
||||||
await self.int_read_resp_queue_list[rid].put(r)
|
burst.append(r)
|
||||||
|
cur_rid = rid
|
||||||
|
|
||||||
async def _process_read_resp_id(self, rid):
|
if int(r.rlast):
|
||||||
while True:
|
self.tag_context_manager.put_resp(rid, burst)
|
||||||
cmd = await self.int_read_resp_command_queue[rid].get()
|
burst = []
|
||||||
self.current_read_resp_command[rid] = cmd
|
cur_rid = None
|
||||||
|
|
||||||
|
async def _process_read_resp_id(self, context, cmd):
|
||||||
|
rid = context.current_tag
|
||||||
|
|
||||||
num_bytes = 2**cmd.size
|
num_bytes = 2**cmd.size
|
||||||
|
|
||||||
aligned_addr = (cmd.address // num_bytes) * num_bytes
|
aligned_addr = (cmd.address // num_bytes) * num_bytes
|
||||||
word_addr = (cmd.address // self.byte_width) * self.byte_width
|
word_addr = (cmd.address // self.byte_lanes) * self.byte_lanes
|
||||||
|
|
||||||
start_offset = cmd.address % self.byte_width
|
start_offset = cmd.address % self.byte_lanes
|
||||||
|
|
||||||
cycle_offset = aligned_addr - word_addr
|
cycle_offset = aligned_addr - word_addr
|
||||||
data = bytearray()
|
data = bytearray()
|
||||||
@@ -716,13 +846,14 @@ class AxiMasterRead(Reset):
|
|||||||
first = True
|
first = True
|
||||||
|
|
||||||
for burst_length in cmd.burst_list:
|
for burst_length in cmd.burst_list:
|
||||||
for k in range(burst_length):
|
burst = await context.get_resp()
|
||||||
r = await self.int_read_resp_queue_list[rid].get()
|
|
||||||
|
|
||||||
cycle_id = int(r.rid)
|
if len(burst) != burst_length:
|
||||||
|
raise Exception(f"Burst length incorrect (ID {rid}, expected {burst_length}, got {len(burst)}")
|
||||||
|
|
||||||
|
for r in burst:
|
||||||
cycle_data = int(r.rdata)
|
cycle_data = int(r.rdata)
|
||||||
cycle_resp = AxiResp(r.rresp)
|
cycle_resp = AxiResp(r.rresp)
|
||||||
cycle_last = int(r.rlast)
|
|
||||||
cycle_user = int(r.ruser)
|
cycle_user = int(r.ruser)
|
||||||
|
|
||||||
if cycle_resp != AxiResp.OKAY:
|
if cycle_resp != AxiResp.OKAY:
|
||||||
@@ -737,24 +868,22 @@ class AxiMasterRead(Reset):
|
|||||||
if first:
|
if first:
|
||||||
start = start_offset
|
start = start_offset
|
||||||
|
|
||||||
assert cycle_last == (k == burst_length - 1)
|
|
||||||
|
|
||||||
for j in range(start, stop):
|
for j in range(start, stop):
|
||||||
data.append((cycle_data >> j*8) & 0xff)
|
data.append((cycle_data >> j*8) & 0xff)
|
||||||
|
|
||||||
cycle_offset = (cycle_offset + num_bytes) % self.byte_width
|
cycle_offset = (cycle_offset + num_bytes) % self.byte_lanes
|
||||||
|
|
||||||
first = False
|
first = False
|
||||||
|
|
||||||
if self.active_id[rid] <= 0:
|
|
||||||
raise Exception(f"Unexpected burst ID {rid}")
|
|
||||||
|
|
||||||
self.active_id[rid] -= 1
|
self.active_id[rid] -= 1
|
||||||
|
|
||||||
self.log.info("Read burst complete rid: 0x%x rresp: %s", cycle_id, resp)
|
self.log.info("Read burst complete rid: 0x%x rresp: %s", rid, resp)
|
||||||
|
|
||||||
data = data[:cmd.length]
|
data = data[:cmd.length]
|
||||||
|
|
||||||
|
if not self.ruser_present:
|
||||||
|
user = None
|
||||||
|
|
||||||
self.log.info("Read complete addr: 0x%08x prot: %s resp: %s data: %s",
|
self.log.info("Read complete addr: 0x%08x prot: %s resp: %s data: %s",
|
||||||
cmd.address, cmd.prot, resp, ' '.join((f'{c:02x}' for c in data)))
|
cmd.address, cmd.prot, resp, ' '.join((f'{c:02x}' for c in data)))
|
||||||
|
|
||||||
@@ -762,8 +891,6 @@ class AxiMasterRead(Reset):
|
|||||||
|
|
||||||
cmd.event.set(read_resp)
|
cmd.event.set(read_resp)
|
||||||
|
|
||||||
self.current_read_resp_command[rid] = None
|
|
||||||
|
|
||||||
self.in_flight_operations -= 1
|
self.in_flight_operations -= 1
|
||||||
|
|
||||||
if self.in_flight_operations == 0:
|
if self.in_flight_operations == 0:
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ from .reset import Reset
|
|||||||
|
|
||||||
class AxiRamWrite(Memory, Reset):
|
class AxiRamWrite(Memory, Reset):
|
||||||
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, *args, **kwargs):
|
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, *args, **kwargs):
|
||||||
|
self.bus = bus
|
||||||
|
self.clock = clock
|
||||||
|
self.reset = reset
|
||||||
self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")
|
self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")
|
||||||
|
|
||||||
self.log.info("AXI RAM model (write)")
|
self.log.info("AXI RAM model (write)")
|
||||||
@@ -53,18 +56,26 @@ class AxiRamWrite(Memory, Reset):
|
|||||||
|
|
||||||
self.width = len(self.w_channel.bus.wdata)
|
self.width = len(self.w_channel.bus.wdata)
|
||||||
self.byte_size = 8
|
self.byte_size = 8
|
||||||
self.byte_width = self.width // self.byte_size
|
self.byte_lanes = self.width // self.byte_size
|
||||||
self.strb_mask = 2**self.byte_width-1
|
self.strb_mask = 2**self.byte_lanes-1
|
||||||
|
|
||||||
self.log.info("AXI RAM model configuration:")
|
self.log.info("AXI RAM model configuration:")
|
||||||
self.log.info(" Memory size: %d bytes", len(self.mem))
|
self.log.info(" Memory size: %d bytes", len(self.mem))
|
||||||
self.log.info(" Address width: %d bits", len(self.aw_channel.bus.awaddr))
|
self.log.info(" Address width: %d bits", len(self.aw_channel.bus.awaddr))
|
||||||
self.log.info(" ID width: %d bits", len(self.aw_channel.bus.awid))
|
self.log.info(" ID width: %d bits", len(self.aw_channel.bus.awid))
|
||||||
self.log.info(" Byte size: %d bits", self.byte_size)
|
self.log.info(" Byte size: %d bits", self.byte_size)
|
||||||
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_width)
|
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
|
||||||
|
|
||||||
assert self.byte_width == len(self.w_channel.bus.wstrb)
|
self.log.info("AXI RAM model signals:")
|
||||||
assert self.byte_width * self.byte_size == self.width
|
for bus in (self.bus.aw, self.bus.w, self.bus.b):
|
||||||
|
for sig in sorted(list(set().union(bus._signals, bus._optional_signals))):
|
||||||
|
if hasattr(bus, sig):
|
||||||
|
self.log.info(" %s width: %d bits", sig, len(getattr(bus, sig)))
|
||||||
|
else:
|
||||||
|
self.log.info(" %s: not present", sig)
|
||||||
|
|
||||||
|
assert self.byte_lanes == len(self.w_channel.bus.wstrb)
|
||||||
|
assert self.byte_lanes * self.byte_size == self.width
|
||||||
|
|
||||||
assert len(self.b_channel.bus.bid) == len(self.aw_channel.bus.awid)
|
assert len(self.b_channel.bus.bid) == len(self.aw_channel.bus.awid)
|
||||||
|
|
||||||
@@ -102,7 +113,7 @@ class AxiRamWrite(Memory, Reset):
|
|||||||
awid, addr, length, size, prot)
|
awid, addr, length, size, prot)
|
||||||
|
|
||||||
num_bytes = 2**size
|
num_bytes = 2**size
|
||||||
assert 0 < num_bytes <= self.byte_width
|
assert 0 < num_bytes <= self.byte_lanes
|
||||||
|
|
||||||
aligned_addr = (addr // num_bytes) * num_bytes
|
aligned_addr = (addr // num_bytes) * num_bytes
|
||||||
length += 1
|
length += 1
|
||||||
@@ -120,7 +131,7 @@ class AxiRamWrite(Memory, Reset):
|
|||||||
cur_addr = aligned_addr
|
cur_addr = aligned_addr
|
||||||
|
|
||||||
for n in range(length):
|
for n in range(length):
|
||||||
cur_word_addr = (cur_addr // self.byte_width) * self.byte_width
|
cur_word_addr = (cur_addr // self.byte_lanes) * self.byte_lanes
|
||||||
|
|
||||||
w = await self.w_channel.recv()
|
w = await self.w_channel.recv()
|
||||||
|
|
||||||
@@ -132,12 +143,12 @@ class AxiRamWrite(Memory, Reset):
|
|||||||
|
|
||||||
self.mem.seek(cur_word_addr % self.size)
|
self.mem.seek(cur_word_addr % self.size)
|
||||||
|
|
||||||
data = data.to_bytes(self.byte_width, 'little')
|
data = data.to_bytes(self.byte_lanes, 'little')
|
||||||
|
|
||||||
self.log.debug("Write word awid: 0x%x addr: 0x%08x wstrb: 0x%02x data: %s",
|
self.log.debug("Write word awid: 0x%x addr: 0x%08x wstrb: 0x%02x data: %s",
|
||||||
awid, cur_addr, strb, ' '.join((f'{c:02x}' for c in data)))
|
awid, cur_addr, strb, ' '.join((f'{c:02x}' for c in data)))
|
||||||
|
|
||||||
for i in range(self.byte_width):
|
for i in range(self.byte_lanes):
|
||||||
if strb & (1 << i):
|
if strb & (1 << i):
|
||||||
self.mem.write(data[i:i+1])
|
self.mem.write(data[i:i+1])
|
||||||
else:
|
else:
|
||||||
@@ -161,6 +172,9 @@ class AxiRamWrite(Memory, Reset):
|
|||||||
|
|
||||||
class AxiRamRead(Memory, Reset):
|
class AxiRamRead(Memory, Reset):
|
||||||
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, *args, **kwargs):
|
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, *args, **kwargs):
|
||||||
|
self.bus = bus
|
||||||
|
self.clock = clock
|
||||||
|
self.reset = reset
|
||||||
self.log = logging.getLogger(f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")
|
self.log = logging.getLogger(f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")
|
||||||
|
|
||||||
self.log.info("AXI RAM model (read)")
|
self.log.info("AXI RAM model (read)")
|
||||||
@@ -177,16 +191,24 @@ class AxiRamRead(Memory, Reset):
|
|||||||
|
|
||||||
self.width = len(self.r_channel.bus.rdata)
|
self.width = len(self.r_channel.bus.rdata)
|
||||||
self.byte_size = 8
|
self.byte_size = 8
|
||||||
self.byte_width = self.width // self.byte_size
|
self.byte_lanes = self.width // self.byte_size
|
||||||
|
|
||||||
self.log.info("AXI RAM model configuration:")
|
self.log.info("AXI RAM model configuration:")
|
||||||
self.log.info(" Memory size: %d bytes", len(self.mem))
|
self.log.info(" Memory size: %d bytes", len(self.mem))
|
||||||
self.log.info(" Address width: %d bits", len(self.ar_channel.bus.araddr))
|
self.log.info(" Address width: %d bits", len(self.ar_channel.bus.araddr))
|
||||||
self.log.info(" ID width: %d bits", len(self.ar_channel.bus.arid))
|
self.log.info(" ID width: %d bits", len(self.ar_channel.bus.arid))
|
||||||
self.log.info(" Byte size: %d bits", self.byte_size)
|
self.log.info(" Byte size: %d bits", self.byte_size)
|
||||||
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_width)
|
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
|
||||||
|
|
||||||
assert self.byte_width * self.byte_size == self.width
|
self.log.info("AXI RAM model signals:")
|
||||||
|
for bus in (self.bus.ar, self.bus.r):
|
||||||
|
for sig in sorted(list(set().union(bus._signals, bus._optional_signals))):
|
||||||
|
if hasattr(bus, sig):
|
||||||
|
self.log.info(" %s width: %d bits", sig, len(getattr(bus, sig)))
|
||||||
|
else:
|
||||||
|
self.log.info(" %s: not present", sig)
|
||||||
|
|
||||||
|
assert self.byte_lanes * self.byte_size == self.width
|
||||||
|
|
||||||
assert len(self.r_channel.bus.rid) == len(self.ar_channel.bus.arid)
|
assert len(self.r_channel.bus.rid) == len(self.ar_channel.bus.arid)
|
||||||
|
|
||||||
@@ -223,7 +245,7 @@ class AxiRamRead(Memory, Reset):
|
|||||||
arid, addr, length, size, prot)
|
arid, addr, length, size, prot)
|
||||||
|
|
||||||
num_bytes = 2**size
|
num_bytes = 2**size
|
||||||
assert 0 < num_bytes <= self.byte_width
|
assert 0 < num_bytes <= self.byte_lanes
|
||||||
|
|
||||||
aligned_addr = (addr // num_bytes) * num_bytes
|
aligned_addr = (addr // num_bytes) * num_bytes
|
||||||
length += 1
|
length += 1
|
||||||
@@ -241,11 +263,11 @@ class AxiRamRead(Memory, Reset):
|
|||||||
cur_addr = aligned_addr
|
cur_addr = aligned_addr
|
||||||
|
|
||||||
for n in range(length):
|
for n in range(length):
|
||||||
cur_word_addr = (cur_addr // self.byte_width) * self.byte_width
|
cur_word_addr = (cur_addr // self.byte_lanes) * self.byte_lanes
|
||||||
|
|
||||||
self.mem.seek(cur_word_addr % self.size)
|
self.mem.seek(cur_word_addr % self.size)
|
||||||
|
|
||||||
data = self.mem.read(self.byte_width)
|
data = self.mem.read(self.byte_lanes)
|
||||||
|
|
||||||
r = self.r_channel._transaction_obj()
|
r = self.r_channel._transaction_obj()
|
||||||
r.rid = arid
|
r.rid = arid
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ from .stream import define_stream
|
|||||||
|
|
||||||
# Write address channel
|
# Write address channel
|
||||||
AxiLiteAWBus, AxiLiteAWTransaction, AxiLiteAWSource, AxiLiteAWSink, AxiLiteAWMonitor = define_stream("AxiLiteAW",
|
AxiLiteAWBus, AxiLiteAWTransaction, AxiLiteAWSource, AxiLiteAWSink, AxiLiteAWMonitor = define_stream("AxiLiteAW",
|
||||||
signals=["awaddr", "awprot", "awvalid", "awready"],
|
signals=["awaddr", "awvalid", "awready"],
|
||||||
|
optional_signals=["awprot"],
|
||||||
signal_widths={"awprot": 3}
|
signal_widths={"awprot": 3}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,19 +38,22 @@ AxiLiteWBus, AxiLiteWTransaction, AxiLiteWSource, AxiLiteWSink, AxiLiteWMonitor
|
|||||||
|
|
||||||
# Write response channel
|
# Write response channel
|
||||||
AxiLiteBBus, AxiLiteBTransaction, AxiLiteBSource, AxiLiteBSink, AxiLiteBMonitor = define_stream("AxiLiteB",
|
AxiLiteBBus, AxiLiteBTransaction, AxiLiteBSource, AxiLiteBSink, AxiLiteBMonitor = define_stream("AxiLiteB",
|
||||||
signals=["bresp", "bvalid", "bready"],
|
signals=["bvalid", "bready"],
|
||||||
|
optional_signals=["bresp"],
|
||||||
signal_widths={"bresp": 2}
|
signal_widths={"bresp": 2}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Read address channel
|
# Read address channel
|
||||||
AxiLiteARBus, AxiLiteARTransaction, AxiLiteARSource, AxiLiteARSink, AxiLiteARMonitor = define_stream("AxiLiteAR",
|
AxiLiteARBus, AxiLiteARTransaction, AxiLiteARSource, AxiLiteARSink, AxiLiteARMonitor = define_stream("AxiLiteAR",
|
||||||
signals=["araddr", "arprot", "arvalid", "arready"],
|
signals=["araddr", "arvalid", "arready"],
|
||||||
|
optional_signals=["arprot"],
|
||||||
signal_widths={"arprot": 3}
|
signal_widths={"arprot": 3}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Read data channel
|
# Read data channel
|
||||||
AxiLiteRBus, AxiLiteRTransaction, AxiLiteRSource, AxiLiteRSink, AxiLiteRMonitor = define_stream("AxiLiteR",
|
AxiLiteRBus, AxiLiteRTransaction, AxiLiteRSource, AxiLiteRSink, AxiLiteRMonitor = define_stream("AxiLiteR",
|
||||||
signals=["rdata", "rresp", "rvalid", "rready"],
|
signals=["rdata", "rvalid", "rready"],
|
||||||
|
optional_signals=["rresp"],
|
||||||
signal_widths={"rresp": 2}
|
signal_widths={"rresp": 2}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ AxiLiteReadResp = namedtuple("AxiLiteReadResp", ["address", "data", "resp"])
|
|||||||
|
|
||||||
class AxiLiteMasterWrite(Reset):
|
class AxiLiteMasterWrite(Reset):
|
||||||
def __init__(self, bus, clock, reset=None, reset_active_level=True):
|
def __init__(self, bus, clock, reset=None, reset_active_level=True):
|
||||||
|
self.bus = bus
|
||||||
|
self.clock = clock
|
||||||
|
self.reset = reset
|
||||||
self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")
|
self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")
|
||||||
|
|
||||||
self.log.info("AXI lite master (write)")
|
self.log.info("AXI lite master (write)")
|
||||||
@@ -73,16 +76,26 @@ class AxiLiteMasterWrite(Reset):
|
|||||||
|
|
||||||
self.width = len(self.w_channel.bus.wdata)
|
self.width = len(self.w_channel.bus.wdata)
|
||||||
self.byte_size = 8
|
self.byte_size = 8
|
||||||
self.byte_width = self.width // self.byte_size
|
self.byte_lanes = self.width // self.byte_size
|
||||||
self.strb_mask = 2**self.byte_width-1
|
self.strb_mask = 2**self.byte_lanes-1
|
||||||
|
|
||||||
|
self.awprot_present = hasattr(self.bus.aw, "awprot")
|
||||||
|
|
||||||
self.log.info("AXI lite master configuration:")
|
self.log.info("AXI lite master configuration:")
|
||||||
self.log.info(" Address width: %d bits", len(self.aw_channel.bus.awaddr))
|
self.log.info(" Address width: %d bits", len(self.aw_channel.bus.awaddr))
|
||||||
self.log.info(" Byte size: %d bits", self.byte_size)
|
self.log.info(" Byte size: %d bits", self.byte_size)
|
||||||
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_width)
|
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
|
||||||
|
|
||||||
assert self.byte_width == len(self.w_channel.bus.wstrb)
|
self.log.info("AXI lite master signals:")
|
||||||
assert self.byte_width * self.byte_size == self.width
|
for bus in (self.bus.aw, self.bus.w, self.bus.b):
|
||||||
|
for sig in sorted(list(set().union(bus._signals, bus._optional_signals))):
|
||||||
|
if hasattr(bus, sig):
|
||||||
|
self.log.info(" %s width: %d bits", sig, len(getattr(bus, sig)))
|
||||||
|
else:
|
||||||
|
self.log.info(" %s: not present", sig)
|
||||||
|
|
||||||
|
assert self.byte_lanes == len(self.w_channel.bus.wstrb)
|
||||||
|
assert self.byte_lanes * self.byte_size == self.width
|
||||||
|
|
||||||
self._process_write_cr = None
|
self._process_write_cr = None
|
||||||
self._process_write_resp_cr = None
|
self._process_write_resp_cr = None
|
||||||
@@ -96,6 +109,9 @@ class AxiLiteMasterWrite(Reset):
|
|||||||
if not isinstance(event, Event):
|
if not isinstance(event, Event):
|
||||||
raise ValueError("Expected event object")
|
raise ValueError("Expected event object")
|
||||||
|
|
||||||
|
if not self.awprot_present and prot != AxiProt.NONSECURE:
|
||||||
|
raise ValueError("awprot sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
self.in_flight_operations += 1
|
self.in_flight_operations += 1
|
||||||
self._idle.clear()
|
self._idle.clear()
|
||||||
|
|
||||||
@@ -191,15 +207,15 @@ class AxiLiteMasterWrite(Reset):
|
|||||||
cmd = await self.write_command_queue.get()
|
cmd = await self.write_command_queue.get()
|
||||||
self.current_write_command = cmd
|
self.current_write_command = cmd
|
||||||
|
|
||||||
word_addr = (cmd.address // self.byte_width) * self.byte_width
|
word_addr = (cmd.address // self.byte_lanes) * self.byte_lanes
|
||||||
|
|
||||||
start_offset = cmd.address % self.byte_width
|
start_offset = cmd.address % self.byte_lanes
|
||||||
end_offset = ((cmd.address + len(cmd.data) - 1) % self.byte_width) + 1
|
end_offset = ((cmd.address + len(cmd.data) - 1) % self.byte_lanes) + 1
|
||||||
|
|
||||||
strb_start = (self.strb_mask << start_offset) & self.strb_mask
|
strb_start = (self.strb_mask << start_offset) & self.strb_mask
|
||||||
strb_end = self.strb_mask >> (self.byte_width - end_offset)
|
strb_end = self.strb_mask >> (self.byte_lanes - end_offset)
|
||||||
|
|
||||||
cycles = (len(cmd.data) + (cmd.address % self.byte_width) + self.byte_width-1) // self.byte_width
|
cycles = (len(cmd.data) + (cmd.address % self.byte_lanes) + self.byte_lanes-1) // self.byte_lanes
|
||||||
|
|
||||||
resp_cmd = AxiLiteWriteRespCmd(cmd.address, len(cmd.data), cycles, cmd.prot, cmd.event)
|
resp_cmd = AxiLiteWriteRespCmd(cmd.address, len(cmd.data), cycles, cmd.prot, cmd.event)
|
||||||
await self.int_write_resp_command_queue.put(resp_cmd)
|
await self.int_write_resp_command_queue.put(resp_cmd)
|
||||||
@@ -211,7 +227,7 @@ class AxiLiteMasterWrite(Reset):
|
|||||||
|
|
||||||
for k in range(cycles):
|
for k in range(cycles):
|
||||||
start = 0
|
start = 0
|
||||||
stop = self.byte_width
|
stop = self.byte_lanes
|
||||||
strb = self.strb_mask
|
strb = self.strb_mask
|
||||||
|
|
||||||
if k == 0:
|
if k == 0:
|
||||||
@@ -227,7 +243,7 @@ class AxiLiteMasterWrite(Reset):
|
|||||||
offset += 1
|
offset += 1
|
||||||
|
|
||||||
aw = self.aw_channel._transaction_obj()
|
aw = self.aw_channel._transaction_obj()
|
||||||
aw.awaddr = word_addr + k*self.byte_width
|
aw.awaddr = word_addr + k*self.byte_lanes
|
||||||
aw.awprot = cmd.prot
|
aw.awprot = cmd.prot
|
||||||
|
|
||||||
w = self.w_channel._transaction_obj()
|
w = self.w_channel._transaction_obj()
|
||||||
@@ -271,6 +287,9 @@ class AxiLiteMasterWrite(Reset):
|
|||||||
|
|
||||||
class AxiLiteMasterRead(Reset):
|
class AxiLiteMasterRead(Reset):
|
||||||
def __init__(self, bus, clock, reset=None, reset_active_level=True):
|
def __init__(self, bus, clock, reset=None, reset_active_level=True):
|
||||||
|
self.bus = bus
|
||||||
|
self.clock = clock
|
||||||
|
self.reset = reset
|
||||||
self.log = logging.getLogger(f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")
|
self.log = logging.getLogger(f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")
|
||||||
|
|
||||||
self.log.info("AXI lite master (read)")
|
self.log.info("AXI lite master (read)")
|
||||||
@@ -295,14 +314,24 @@ class AxiLiteMasterRead(Reset):
|
|||||||
|
|
||||||
self.width = len(self.r_channel.bus.rdata)
|
self.width = len(self.r_channel.bus.rdata)
|
||||||
self.byte_size = 8
|
self.byte_size = 8
|
||||||
self.byte_width = self.width // self.byte_size
|
self.byte_lanes = self.width // self.byte_size
|
||||||
|
|
||||||
|
self.arprot_present = hasattr(self.bus.ar, "arprot")
|
||||||
|
|
||||||
self.log.info("AXI lite master configuration:")
|
self.log.info("AXI lite master configuration:")
|
||||||
self.log.info(" Address width: %d bits", len(self.ar_channel.bus.araddr))
|
self.log.info(" Address width: %d bits", len(self.ar_channel.bus.araddr))
|
||||||
self.log.info(" Byte size: %d bits", self.byte_size)
|
self.log.info(" Byte size: %d bits", self.byte_size)
|
||||||
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_width)
|
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
|
||||||
|
|
||||||
assert self.byte_width * self.byte_size == self.width
|
self.log.info("AXI lite master signals:")
|
||||||
|
for bus in (self.bus.ar, self.bus.r):
|
||||||
|
for sig in sorted(list(set().union(bus._signals, bus._optional_signals))):
|
||||||
|
if hasattr(bus, sig):
|
||||||
|
self.log.info(" %s width: %d bits", sig, len(getattr(bus, sig)))
|
||||||
|
else:
|
||||||
|
self.log.info(" %s: not present", sig)
|
||||||
|
|
||||||
|
assert self.byte_lanes * self.byte_size == self.width
|
||||||
|
|
||||||
self._process_read_cr = None
|
self._process_read_cr = None
|
||||||
self._process_read_resp_cr = None
|
self._process_read_resp_cr = None
|
||||||
@@ -316,6 +345,9 @@ class AxiLiteMasterRead(Reset):
|
|||||||
if not isinstance(event, Event):
|
if not isinstance(event, Event):
|
||||||
raise ValueError("Expected event object")
|
raise ValueError("Expected event object")
|
||||||
|
|
||||||
|
if not self.arprot_present and prot != AxiProt.NONSECURE:
|
||||||
|
raise ValueError("arprot sideband signal value specified, but signal is not connected")
|
||||||
|
|
||||||
self.in_flight_operations += 1
|
self.in_flight_operations += 1
|
||||||
self._idle.clear()
|
self._idle.clear()
|
||||||
|
|
||||||
@@ -410,9 +442,9 @@ class AxiLiteMasterRead(Reset):
|
|||||||
cmd = await self.read_command_queue.get()
|
cmd = await self.read_command_queue.get()
|
||||||
self.current_read_command = cmd
|
self.current_read_command = cmd
|
||||||
|
|
||||||
word_addr = (cmd.address // self.byte_width) * self.byte_width
|
word_addr = (cmd.address // self.byte_lanes) * self.byte_lanes
|
||||||
|
|
||||||
cycles = (cmd.length + self.byte_width-1 + (cmd.address % self.byte_width)) // self.byte_width
|
cycles = (cmd.length + self.byte_lanes-1 + (cmd.address % self.byte_lanes)) // self.byte_lanes
|
||||||
|
|
||||||
resp_cmd = AxiLiteReadRespCmd(cmd.address, cmd.length, cycles, cmd.prot, cmd.event)
|
resp_cmd = AxiLiteReadRespCmd(cmd.address, cmd.length, cycles, cmd.prot, cmd.event)
|
||||||
await self.int_read_resp_command_queue.put(resp_cmd)
|
await self.int_read_resp_command_queue.put(resp_cmd)
|
||||||
@@ -422,7 +454,7 @@ class AxiLiteMasterRead(Reset):
|
|||||||
|
|
||||||
for k in range(cycles):
|
for k in range(cycles):
|
||||||
ar = self.ar_channel._transaction_obj()
|
ar = self.ar_channel._transaction_obj()
|
||||||
ar.araddr = word_addr + k*self.byte_width
|
ar.araddr = word_addr + k*self.byte_lanes
|
||||||
ar.arprot = cmd.prot
|
ar.arprot = cmd.prot
|
||||||
|
|
||||||
await self.ar_channel.send(ar)
|
await self.ar_channel.send(ar)
|
||||||
@@ -434,8 +466,8 @@ class AxiLiteMasterRead(Reset):
|
|||||||
cmd = await self.int_read_resp_command_queue.get()
|
cmd = await self.int_read_resp_command_queue.get()
|
||||||
self.current_read_resp_command = cmd
|
self.current_read_resp_command = cmd
|
||||||
|
|
||||||
start_offset = cmd.address % self.byte_width
|
start_offset = cmd.address % self.byte_lanes
|
||||||
end_offset = ((cmd.address + cmd.length - 1) % self.byte_width) + 1
|
end_offset = ((cmd.address + cmd.length - 1) % self.byte_lanes) + 1
|
||||||
|
|
||||||
data = bytearray()
|
data = bytearray()
|
||||||
|
|
||||||
@@ -451,7 +483,7 @@ class AxiLiteMasterRead(Reset):
|
|||||||
resp = cycle_resp
|
resp = cycle_resp
|
||||||
|
|
||||||
start = 0
|
start = 0
|
||||||
stop = self.byte_width
|
stop = self.byte_lanes
|
||||||
|
|
||||||
if k == 0:
|
if k == 0:
|
||||||
start = start_offset
|
start = start_offset
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ from .reset import Reset
|
|||||||
|
|
||||||
class AxiLiteRamWrite(Memory, Reset):
|
class AxiLiteRamWrite(Memory, Reset):
|
||||||
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, *args, **kwargs):
|
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, *args, **kwargs):
|
||||||
|
self.bus = bus
|
||||||
|
self.clock = clock
|
||||||
|
self.reset = reset
|
||||||
self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")
|
self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")
|
||||||
|
|
||||||
self.log.info("AXI lite RAM model (write)")
|
self.log.info("AXI lite RAM model (write)")
|
||||||
@@ -53,17 +56,25 @@ class AxiLiteRamWrite(Memory, Reset):
|
|||||||
|
|
||||||
self.width = len(self.w_channel.bus.wdata)
|
self.width = len(self.w_channel.bus.wdata)
|
||||||
self.byte_size = 8
|
self.byte_size = 8
|
||||||
self.byte_width = self.width // self.byte_size
|
self.byte_lanes = self.width // self.byte_size
|
||||||
self.strb_mask = 2**self.byte_width-1
|
self.strb_mask = 2**self.byte_lanes-1
|
||||||
|
|
||||||
self.log.info("AXI lite RAM model configuration:")
|
self.log.info("AXI lite RAM model configuration:")
|
||||||
self.log.info(" Memory size: %d bytes", len(self.mem))
|
self.log.info(" Memory size: %d bytes", len(self.mem))
|
||||||
self.log.info(" Address width: %d bits", len(self.aw_channel.bus.awaddr))
|
self.log.info(" Address width: %d bits", len(self.aw_channel.bus.awaddr))
|
||||||
self.log.info(" Byte size: %d bits", self.byte_size)
|
self.log.info(" Byte size: %d bits", self.byte_size)
|
||||||
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_width)
|
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
|
||||||
|
|
||||||
assert self.byte_width == len(self.w_channel.bus.wstrb)
|
self.log.info("AXI lite RAM model signals:")
|
||||||
assert self.byte_width * self.byte_size == self.width
|
for bus in (self.bus.aw, self.bus.w, self.bus.b):
|
||||||
|
for sig in sorted(list(set().union(bus._signals, bus._optional_signals))):
|
||||||
|
if hasattr(bus, sig):
|
||||||
|
self.log.info(" %s width: %d bits", sig, len(getattr(bus, sig)))
|
||||||
|
else:
|
||||||
|
self.log.info(" %s: not present", sig)
|
||||||
|
|
||||||
|
assert self.byte_lanes == len(self.w_channel.bus.wstrb)
|
||||||
|
assert self.byte_lanes * self.byte_size == self.width
|
||||||
|
|
||||||
self._process_write_cr = None
|
self._process_write_cr = None
|
||||||
|
|
||||||
@@ -88,7 +99,7 @@ class AxiLiteRamWrite(Memory, Reset):
|
|||||||
while True:
|
while True:
|
||||||
aw = await self.aw_channel.recv()
|
aw = await self.aw_channel.recv()
|
||||||
|
|
||||||
addr = (int(aw.awaddr) // self.byte_width) * self.byte_width
|
addr = (int(aw.awaddr) // self.byte_lanes) * self.byte_lanes
|
||||||
prot = AxiProt(aw.awprot)
|
prot = AxiProt(aw.awprot)
|
||||||
|
|
||||||
w = await self.w_channel.recv()
|
w = await self.w_channel.recv()
|
||||||
@@ -100,12 +111,12 @@ class AxiLiteRamWrite(Memory, Reset):
|
|||||||
|
|
||||||
self.mem.seek(addr % self.size)
|
self.mem.seek(addr % self.size)
|
||||||
|
|
||||||
data = data.to_bytes(self.byte_width, 'little')
|
data = data.to_bytes(self.byte_lanes, 'little')
|
||||||
|
|
||||||
self.log.info("Write data awaddr: 0x%08x awprot: %s wstrb: 0x%02x data: %s",
|
self.log.info("Write data awaddr: 0x%08x awprot: %s wstrb: 0x%02x data: %s",
|
||||||
addr, prot, strb, ' '.join((f'{c:02x}' for c in data)))
|
addr, prot, strb, ' '.join((f'{c:02x}' for c in data)))
|
||||||
|
|
||||||
for i in range(self.byte_width):
|
for i in range(self.byte_lanes):
|
||||||
if strb & (1 << i):
|
if strb & (1 << i):
|
||||||
self.mem.write(data[i:i+1])
|
self.mem.write(data[i:i+1])
|
||||||
else:
|
else:
|
||||||
@@ -119,6 +130,9 @@ class AxiLiteRamWrite(Memory, Reset):
|
|||||||
|
|
||||||
class AxiLiteRamRead(Memory, Reset):
|
class AxiLiteRamRead(Memory, Reset):
|
||||||
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, *args, **kwargs):
|
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, *args, **kwargs):
|
||||||
|
self.bus = bus
|
||||||
|
self.clock = clock
|
||||||
|
self.reset = reset
|
||||||
self.log = logging.getLogger(f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")
|
self.log = logging.getLogger(f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")
|
||||||
|
|
||||||
self.log.info("AXI lite RAM model (read)")
|
self.log.info("AXI lite RAM model (read)")
|
||||||
@@ -135,15 +149,23 @@ class AxiLiteRamRead(Memory, Reset):
|
|||||||
|
|
||||||
self.width = len(self.r_channel.bus.rdata)
|
self.width = len(self.r_channel.bus.rdata)
|
||||||
self.byte_size = 8
|
self.byte_size = 8
|
||||||
self.byte_width = self.width // self.byte_size
|
self.byte_lanes = self.width // self.byte_size
|
||||||
|
|
||||||
self.log.info("AXI lite RAM model configuration:")
|
self.log.info("AXI lite RAM model configuration:")
|
||||||
self.log.info(" Memory size: %d bytes", len(self.mem))
|
self.log.info(" Memory size: %d bytes", len(self.mem))
|
||||||
self.log.info(" Address width: %d bits", len(self.ar_channel.bus.araddr))
|
self.log.info(" Address width: %d bits", len(self.ar_channel.bus.araddr))
|
||||||
self.log.info(" Byte size: %d bits", self.byte_size)
|
self.log.info(" Byte size: %d bits", self.byte_size)
|
||||||
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_width)
|
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
|
||||||
|
|
||||||
assert self.byte_width * self.byte_size == self.width
|
self.log.info("AXI lite RAM model signals:")
|
||||||
|
for bus in (self.bus.ar, self.bus.r):
|
||||||
|
for sig in sorted(list(set().union(bus._signals, bus._optional_signals))):
|
||||||
|
if hasattr(bus, sig):
|
||||||
|
self.log.info(" %s width: %d bits", sig, len(getattr(bus, sig)))
|
||||||
|
else:
|
||||||
|
self.log.info(" %s: not present", sig)
|
||||||
|
|
||||||
|
assert self.byte_lanes * self.byte_size == self.width
|
||||||
|
|
||||||
self._process_read_cr = None
|
self._process_read_cr = None
|
||||||
|
|
||||||
@@ -167,14 +189,14 @@ class AxiLiteRamRead(Memory, Reset):
|
|||||||
while True:
|
while True:
|
||||||
ar = await self.ar_channel.recv()
|
ar = await self.ar_channel.recv()
|
||||||
|
|
||||||
addr = (int(ar.araddr) // self.byte_width) * self.byte_width
|
addr = (int(ar.araddr) // self.byte_lanes) * self.byte_lanes
|
||||||
prot = AxiProt(ar.arprot)
|
prot = AxiProt(ar.arprot)
|
||||||
|
|
||||||
# todo latency
|
# todo latency
|
||||||
|
|
||||||
self.mem.seek(addr % self.size)
|
self.mem.seek(addr % self.size)
|
||||||
|
|
||||||
data = self.mem.read(self.byte_width)
|
data = self.mem.read(self.byte_lanes)
|
||||||
|
|
||||||
r = self.r_channel._transaction_obj()
|
r = self.r_channel._transaction_obj()
|
||||||
r.rdata = int.from_bytes(data, 'little')
|
r.rdata = int.from_bytes(data, 'little')
|
||||||
|
|||||||
@@ -319,25 +319,13 @@ class AxiStreamBase(Reset):
|
|||||||
self.log.info("AXI stream %s configuration:", self._type)
|
self.log.info("AXI stream %s configuration:", self._type)
|
||||||
self.log.info(" Byte size: %d bits", self.byte_size)
|
self.log.info(" Byte size: %d bits", self.byte_size)
|
||||||
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
|
self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes)
|
||||||
self.log.info(" tvalid: %s", "present" if hasattr(self.bus, "tvalid") else "not present")
|
|
||||||
self.log.info(" tready: %s", "present" if hasattr(self.bus, "tready") else "not present")
|
self.log.info("AXI stream %s signals:", self._type)
|
||||||
self.log.info(" tlast: %s", "present" if hasattr(self.bus, "tlast") else "not present")
|
for sig in sorted(list(set().union(self.bus._signals, self.bus._optional_signals))):
|
||||||
if hasattr(self.bus, "tkeep"):
|
if hasattr(self.bus, sig):
|
||||||
self.log.info(" tkeep width: %d bits", len(self.bus.tkeep))
|
self.log.info(" %s width: %d bits", sig, len(getattr(self.bus, sig)))
|
||||||
else:
|
else:
|
||||||
self.log.info(" tkeep: not present")
|
self.log.info(" %s: not present", sig)
|
||||||
if hasattr(self.bus, "tid"):
|
|
||||||
self.log.info(" tid width: %d bits", len(self.bus.tid))
|
|
||||||
else:
|
|
||||||
self.log.info(" tid: not present")
|
|
||||||
if hasattr(self.bus, "tdest"):
|
|
||||||
self.log.info(" tdest width: %d bits", len(self.bus.tdest))
|
|
||||||
else:
|
|
||||||
self.log.info(" tdest: not present")
|
|
||||||
if hasattr(self.bus, "tuser"):
|
|
||||||
self.log.info(" tuser width: %d bits", len(self.bus.tuser))
|
|
||||||
else:
|
|
||||||
self.log.info(" tuser: not present")
|
|
||||||
|
|
||||||
if self.byte_lanes * self.byte_size != self.width:
|
if self.byte_lanes * self.byte_size != self.width:
|
||||||
raise ValueError(f"Bus does not evenly divide into byte lanes "
|
raise ValueError(f"Bus does not evenly divide into byte lanes "
|
||||||
@@ -471,19 +459,19 @@ class AxiStreamSource(AxiStreamBase, AxiStreamPause):
|
|||||||
super()._handle_reset(state)
|
super()._handle_reset(state)
|
||||||
|
|
||||||
if state:
|
if state:
|
||||||
self.bus.tdata <= 0
|
self.bus.tdata.value = 0
|
||||||
if hasattr(self.bus, "tvalid"):
|
if hasattr(self.bus, "tvalid"):
|
||||||
self.bus.tvalid <= 0
|
self.bus.tvalid.value = 0
|
||||||
if hasattr(self.bus, "tlast"):
|
if hasattr(self.bus, "tlast"):
|
||||||
self.bus.tlast <= 0
|
self.bus.tlast.value = 0
|
||||||
if hasattr(self.bus, "tkeep"):
|
if hasattr(self.bus, "tkeep"):
|
||||||
self.bus.tkeep <= 0
|
self.bus.tkeep.value = 0
|
||||||
if hasattr(self.bus, "tid"):
|
if hasattr(self.bus, "tid"):
|
||||||
self.bus.tid <= 0
|
self.bus.tid.value = 0
|
||||||
if hasattr(self.bus, "tdest"):
|
if hasattr(self.bus, "tdest"):
|
||||||
self.bus.tdest <= 0
|
self.bus.tdest.value = 0
|
||||||
if hasattr(self.bus, "tuser"):
|
if hasattr(self.bus, "tuser"):
|
||||||
self.bus.tuser <= 0
|
self.bus.tuser.value = 0
|
||||||
|
|
||||||
if self.current_frame:
|
if self.current_frame:
|
||||||
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
|
self.log.warning("Flushed transmit frame during reset: %s", self.current_frame)
|
||||||
@@ -492,6 +480,7 @@ class AxiStreamSource(AxiStreamBase, AxiStreamPause):
|
|||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
frame = None
|
frame = None
|
||||||
|
frame_offset = 0
|
||||||
self.active = False
|
self.active = False
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@@ -513,6 +502,7 @@ class AxiStreamSource(AxiStreamBase, AxiStreamPause):
|
|||||||
self.log.info("TX frame: %s", frame)
|
self.log.info("TX frame: %s", frame)
|
||||||
frame.normalize()
|
frame.normalize()
|
||||||
self.active = True
|
self.active = True
|
||||||
|
frame_offset = 0
|
||||||
|
|
||||||
if frame and not self.pause:
|
if frame and not self.pause:
|
||||||
tdata_val = 0
|
tdata_val = 0
|
||||||
@@ -523,13 +513,14 @@ class AxiStreamSource(AxiStreamBase, AxiStreamPause):
|
|||||||
tuser_val = 0
|
tuser_val = 0
|
||||||
|
|
||||||
for offset in range(self.byte_lanes):
|
for offset in range(self.byte_lanes):
|
||||||
tdata_val |= (frame.tdata.pop(0) & self.byte_mask) << (offset * self.byte_size)
|
tdata_val |= (frame.tdata[frame_offset] & self.byte_mask) << (offset * self.byte_size)
|
||||||
tkeep_val |= (frame.tkeep.pop(0) & 1) << offset
|
tkeep_val |= (frame.tkeep[frame_offset] & 1) << offset
|
||||||
tid_val = frame.tid.pop(0)
|
tid_val = frame.tid[frame_offset]
|
||||||
tdest_val = frame.tdest.pop(0)
|
tdest_val = frame.tdest[frame_offset]
|
||||||
tuser_val = frame.tuser.pop(0)
|
tuser_val = frame.tuser[frame_offset]
|
||||||
|
frame_offset += 1
|
||||||
|
|
||||||
if len(frame.tdata) == 0:
|
if frame_offset >= len(frame.tdata):
|
||||||
tlast_val = 1
|
tlast_val = 1
|
||||||
frame.sim_time_end = get_sim_time()
|
frame.sim_time_end = get_sim_time()
|
||||||
frame.handle_tx_complete()
|
frame.handle_tx_complete()
|
||||||
@@ -537,24 +528,24 @@ class AxiStreamSource(AxiStreamBase, AxiStreamPause):
|
|||||||
self.current_frame = None
|
self.current_frame = None
|
||||||
break
|
break
|
||||||
|
|
||||||
self.bus.tdata <= tdata_val
|
self.bus.tdata.value = tdata_val
|
||||||
if hasattr(self.bus, "tvalid"):
|
if hasattr(self.bus, "tvalid"):
|
||||||
self.bus.tvalid <= 1
|
self.bus.tvalid.value = 1
|
||||||
if hasattr(self.bus, "tlast"):
|
if hasattr(self.bus, "tlast"):
|
||||||
self.bus.tlast <= tlast_val
|
self.bus.tlast.value = tlast_val
|
||||||
if hasattr(self.bus, "tkeep"):
|
if hasattr(self.bus, "tkeep"):
|
||||||
self.bus.tkeep <= tkeep_val
|
self.bus.tkeep.value = tkeep_val
|
||||||
if hasattr(self.bus, "tid"):
|
if hasattr(self.bus, "tid"):
|
||||||
self.bus.tid <= tid_val
|
self.bus.tid.value = tid_val
|
||||||
if hasattr(self.bus, "tdest"):
|
if hasattr(self.bus, "tdest"):
|
||||||
self.bus.tdest <= tdest_val
|
self.bus.tdest.value = tdest_val
|
||||||
if hasattr(self.bus, "tuser"):
|
if hasattr(self.bus, "tuser"):
|
||||||
self.bus.tuser <= tuser_val
|
self.bus.tuser.value = tuser_val
|
||||||
else:
|
else:
|
||||||
if hasattr(self.bus, "tvalid"):
|
if hasattr(self.bus, "tvalid"):
|
||||||
self.bus.tvalid <= 0
|
self.bus.tvalid.value = 0
|
||||||
if hasattr(self.bus, "tlast"):
|
if hasattr(self.bus, "tlast"):
|
||||||
self.bus.tlast <= 0
|
self.bus.tlast.value = 0
|
||||||
self.active = bool(frame)
|
self.active = bool(frame)
|
||||||
if not frame and self.queue.empty():
|
if not frame and self.queue.empty():
|
||||||
self.idle_event.set()
|
self.idle_event.set()
|
||||||
@@ -638,9 +629,9 @@ class AxiStreamMonitor(AxiStreamBase):
|
|||||||
else:
|
else:
|
||||||
frame = AxiStreamFrame([], [], [], [], [])
|
frame = AxiStreamFrame([], [], [], [], [])
|
||||||
frame.sim_time_start = get_sim_time()
|
frame.sim_time_start = get_sim_time()
|
||||||
|
self.active = True
|
||||||
|
|
||||||
for offset in range(self.byte_lanes):
|
for offset in range(self.byte_lanes):
|
||||||
|
|
||||||
frame.tdata.append((self.bus.tdata.value.integer >> (offset * self.byte_size)) & self.byte_mask)
|
frame.tdata.append((self.bus.tdata.value.integer >> (offset * self.byte_size)) & self.byte_mask)
|
||||||
if hasattr(self.bus, "tkeep"):
|
if hasattr(self.bus, "tkeep"):
|
||||||
frame.tkeep.append((self.bus.tkeep.value.integer >> offset) & 1)
|
frame.tkeep.append((self.bus.tkeep.value.integer >> offset) & 1)
|
||||||
@@ -662,6 +653,8 @@ class AxiStreamMonitor(AxiStreamBase):
|
|||||||
self.active_event.set()
|
self.active_event.set()
|
||||||
|
|
||||||
frame = None
|
frame = None
|
||||||
|
else:
|
||||||
|
self.active = bool(frame)
|
||||||
|
|
||||||
|
|
||||||
class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
||||||
@@ -694,7 +687,7 @@ class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
|||||||
|
|
||||||
if state:
|
if state:
|
||||||
if hasattr(self.bus, "tready"):
|
if hasattr(self.bus, "tready"):
|
||||||
self.bus.tready <= 0
|
self.bus.tready.value = 0
|
||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
frame = None
|
frame = None
|
||||||
@@ -714,9 +707,9 @@ class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
|||||||
else:
|
else:
|
||||||
frame = AxiStreamFrame([], [], [], [], [])
|
frame = AxiStreamFrame([], [], [], [], [])
|
||||||
frame.sim_time_start = get_sim_time()
|
frame.sim_time_start = get_sim_time()
|
||||||
|
self.active = True
|
||||||
|
|
||||||
for offset in range(self.byte_lanes):
|
for offset in range(self.byte_lanes):
|
||||||
|
|
||||||
frame.tdata.append((self.bus.tdata.value.integer >> (offset * self.byte_size)) & self.byte_mask)
|
frame.tdata.append((self.bus.tdata.value.integer >> (offset * self.byte_size)) & self.byte_mask)
|
||||||
if hasattr(self.bus, "tkeep"):
|
if hasattr(self.bus, "tkeep"):
|
||||||
frame.tkeep.append((self.bus.tkeep.value.integer >> offset) & 1)
|
frame.tkeep.append((self.bus.tkeep.value.integer >> offset) & 1)
|
||||||
@@ -738,6 +731,8 @@ class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
|||||||
self.active_event.set()
|
self.active_event.set()
|
||||||
|
|
||||||
frame = None
|
frame = None
|
||||||
|
else:
|
||||||
|
self.active = bool(frame)
|
||||||
|
|
||||||
if hasattr(self.bus, "tready"):
|
if hasattr(self.bus, "tready"):
|
||||||
self.bus.tready <= (not self.full() and not self.pause)
|
self.bus.tready.value = (not self.full() and not self.pause)
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ class StreamSource(StreamBase, StreamPause):
|
|||||||
|
|
||||||
if state:
|
if state:
|
||||||
if self.valid is not None:
|
if self.valid is not None:
|
||||||
self.valid <= 0
|
self.valid.value = 0
|
||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
while True:
|
while True:
|
||||||
@@ -243,11 +243,11 @@ class StreamSource(StreamBase, StreamPause):
|
|||||||
self.bus.drive(self.queue.get_nowait())
|
self.bus.drive(self.queue.get_nowait())
|
||||||
self.dequeue_event.set()
|
self.dequeue_event.set()
|
||||||
if self.valid is not None:
|
if self.valid is not None:
|
||||||
self.valid <= 1
|
self.valid.value = 1
|
||||||
self.active = True
|
self.active = True
|
||||||
else:
|
else:
|
||||||
if self.valid is not None:
|
if self.valid is not None:
|
||||||
self.valid <= 0
|
self.valid.value = 0
|
||||||
self.active = not self.queue.empty()
|
self.active = not self.queue.empty()
|
||||||
if self.queue.empty():
|
if self.queue.empty():
|
||||||
self.idle_event.set()
|
self.idle_event.set()
|
||||||
@@ -319,7 +319,7 @@ class StreamSink(StreamMonitor, StreamPause):
|
|||||||
|
|
||||||
if state:
|
if state:
|
||||||
if self.ready is not None:
|
if self.ready is not None:
|
||||||
self.ready <= 0
|
self.ready.value = 0
|
||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
while True:
|
while True:
|
||||||
@@ -336,7 +336,7 @@ class StreamSink(StreamMonitor, StreamPause):
|
|||||||
self.active_event.set()
|
self.active_event.set()
|
||||||
|
|
||||||
if self.ready is not None:
|
if self.ready is not None:
|
||||||
self.ready <= (not self.full() and not self.pause)
|
self.ready.value = (not self.full() and not self.pause)
|
||||||
|
|
||||||
|
|
||||||
def define_stream(name, signals, optional_signals=None, valid_signal=None, ready_signal=None, signal_widths=None):
|
def define_stream(name, signals, optional_signals=None, valid_signal=None, ready_signal=None, signal_widths=None):
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.1.10"
|
__version__ = "0.1.14"
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ long-description-content-type = text/markdown
|
|||||||
platforms = any
|
platforms = any
|
||||||
classifiers =
|
classifiers =
|
||||||
Development Status :: 3 - Alpha
|
Development Status :: 3 - Alpha
|
||||||
Programming Language :: Python :: 3
|
Framework :: cocotb
|
||||||
License :: OSI Approved :: MIT License
|
License :: OSI Approved :: MIT License
|
||||||
Operating System :: OS Independent
|
Operating System :: OS Independent
|
||||||
|
Programming Language :: Python :: 3
|
||||||
Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
|
Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
|
|||||||
@@ -73,10 +73,10 @@ class TB:
|
|||||||
self.dut.rst.setimmediatevalue(0)
|
self.dut.rst.setimmediatevalue(0)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
self.dut.rst <= 1
|
self.dut.rst.value = 1
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
self.dut.rst <= 0
|
self.dut.rst.value = 0
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ async def run_test_write(dut, idle_inserter=None, backpressure_inserter=None, si
|
|||||||
|
|
||||||
tb = TB(dut)
|
tb = TB(dut)
|
||||||
|
|
||||||
byte_width = tb.axi_master.write_if.byte_width
|
byte_lanes = tb.axi_master.write_if.byte_lanes
|
||||||
max_burst_size = tb.axi_master.write_if.max_burst_size
|
max_burst_size = tb.axi_master.write_if.max_burst_size
|
||||||
|
|
||||||
if size is None:
|
if size is None:
|
||||||
@@ -96,8 +96,8 @@ async def run_test_write(dut, idle_inserter=None, backpressure_inserter=None, si
|
|||||||
tb.set_idle_generator(idle_inserter)
|
tb.set_idle_generator(idle_inserter)
|
||||||
tb.set_backpressure_generator(backpressure_inserter)
|
tb.set_backpressure_generator(backpressure_inserter)
|
||||||
|
|
||||||
for length in list(range(1, byte_width*2))+[1024]:
|
for length in list(range(1, byte_lanes*2))+[1024]:
|
||||||
for offset in list(range(byte_width))+list(range(4096-byte_width, 4096)):
|
for offset in list(range(byte_lanes))+list(range(4096-byte_lanes, 4096)):
|
||||||
tb.log.info("length %d, offset %d", length, offset)
|
tb.log.info("length %d, offset %d", length, offset)
|
||||||
addr = offset+0x1000
|
addr = offset+0x1000
|
||||||
test_data = bytearray([x % 256 for x in range(length)])
|
test_data = bytearray([x % 256 for x in range(length)])
|
||||||
@@ -120,7 +120,7 @@ async def run_test_read(dut, idle_inserter=None, backpressure_inserter=None, siz
|
|||||||
|
|
||||||
tb = TB(dut)
|
tb = TB(dut)
|
||||||
|
|
||||||
byte_width = tb.axi_master.write_if.byte_width
|
byte_lanes = tb.axi_master.write_if.byte_lanes
|
||||||
max_burst_size = tb.axi_master.write_if.max_burst_size
|
max_burst_size = tb.axi_master.write_if.max_burst_size
|
||||||
|
|
||||||
if size is None:
|
if size is None:
|
||||||
@@ -131,8 +131,8 @@ async def run_test_read(dut, idle_inserter=None, backpressure_inserter=None, siz
|
|||||||
tb.set_idle_generator(idle_inserter)
|
tb.set_idle_generator(idle_inserter)
|
||||||
tb.set_backpressure_generator(backpressure_inserter)
|
tb.set_backpressure_generator(backpressure_inserter)
|
||||||
|
|
||||||
for length in list(range(1, byte_width*2))+[1024]:
|
for length in list(range(1, byte_lanes*2))+[1024]:
|
||||||
for offset in list(range(byte_width))+list(range(4096-byte_width, 4096)):
|
for offset in list(range(byte_lanes))+list(range(4096-byte_lanes, 4096)):
|
||||||
tb.log.info("length %d, offset %d", length, offset)
|
tb.log.info("length %d, offset %d", length, offset)
|
||||||
addr = offset+0x1000
|
addr = offset+0x1000
|
||||||
test_data = bytearray([x % 256 for x in range(length)])
|
test_data = bytearray([x % 256 for x in range(length)])
|
||||||
@@ -151,12 +151,12 @@ async def run_test_write_words(dut):
|
|||||||
|
|
||||||
tb = TB(dut)
|
tb = TB(dut)
|
||||||
|
|
||||||
byte_width = tb.axi_master.write_if.byte_width
|
byte_lanes = tb.axi_master.write_if.byte_lanes
|
||||||
|
|
||||||
await tb.cycle_reset()
|
await tb.cycle_reset()
|
||||||
|
|
||||||
for length in list(range(1, 4)):
|
for length in list(range(1, 4)):
|
||||||
for offset in list(range(byte_width)):
|
for offset in list(range(byte_lanes)):
|
||||||
tb.log.info("length %d, offset %d", length, offset)
|
tb.log.info("length %d, offset %d", length, offset)
|
||||||
addr = offset+0x1000
|
addr = offset+0x1000
|
||||||
|
|
||||||
@@ -205,12 +205,12 @@ async def run_test_read_words(dut):
|
|||||||
|
|
||||||
tb = TB(dut)
|
tb = TB(dut)
|
||||||
|
|
||||||
byte_width = tb.axi_master.write_if.byte_width
|
byte_lanes = tb.axi_master.write_if.byte_lanes
|
||||||
|
|
||||||
await tb.cycle_reset()
|
await tb.cycle_reset()
|
||||||
|
|
||||||
for length in list(range(1, 4)):
|
for length in list(range(1, 4)):
|
||||||
for offset in list(range(byte_width)):
|
for offset in list(range(byte_lanes)):
|
||||||
tb.log.info("length %d, offset %d", length, offset)
|
tb.log.info("length %d, offset %d", length, offset)
|
||||||
addr = offset+0x1000
|
addr = offset+0x1000
|
||||||
|
|
||||||
@@ -299,8 +299,8 @@ def cycle_pause():
|
|||||||
if cocotb.SIM_NAME:
|
if cocotb.SIM_NAME:
|
||||||
|
|
||||||
data_width = len(cocotb.top.axi_wdata)
|
data_width = len(cocotb.top.axi_wdata)
|
||||||
byte_width = data_width // 8
|
byte_lanes = data_width // 8
|
||||||
max_burst_size = (byte_width-1).bit_length()
|
max_burst_size = (byte_lanes-1).bit_length()
|
||||||
|
|
||||||
for test in [run_test_write, run_test_read]:
|
for test in [run_test_write, run_test_read]:
|
||||||
|
|
||||||
|
|||||||
@@ -70,10 +70,10 @@ class TB:
|
|||||||
self.dut.rst.setimmediatevalue(0)
|
self.dut.rst.setimmediatevalue(0)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
self.dut.rst <= 1
|
self.dut.rst.value = 1
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
self.dut.rst <= 0
|
self.dut.rst.value = 0
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
|
|
||||||
@@ -82,15 +82,15 @@ async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_ins
|
|||||||
|
|
||||||
tb = TB(dut)
|
tb = TB(dut)
|
||||||
|
|
||||||
byte_width = tb.axil_master.write_if.byte_width
|
byte_lanes = tb.axil_master.write_if.byte_lanes
|
||||||
|
|
||||||
await tb.cycle_reset()
|
await tb.cycle_reset()
|
||||||
|
|
||||||
tb.set_idle_generator(idle_inserter)
|
tb.set_idle_generator(idle_inserter)
|
||||||
tb.set_backpressure_generator(backpressure_inserter)
|
tb.set_backpressure_generator(backpressure_inserter)
|
||||||
|
|
||||||
for length in range(1, byte_width*2):
|
for length in range(1, byte_lanes*2):
|
||||||
for offset in range(byte_width):
|
for offset in range(byte_lanes):
|
||||||
tb.log.info("length %d, offset %d", length, offset)
|
tb.log.info("length %d, offset %d", length, offset)
|
||||||
addr = offset+0x1000
|
addr = offset+0x1000
|
||||||
test_data = bytearray([x % 256 for x in range(length)])
|
test_data = bytearray([x % 256 for x in range(length)])
|
||||||
@@ -113,15 +113,15 @@ async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inse
|
|||||||
|
|
||||||
tb = TB(dut)
|
tb = TB(dut)
|
||||||
|
|
||||||
byte_width = tb.axil_master.write_if.byte_width
|
byte_lanes = tb.axil_master.write_if.byte_lanes
|
||||||
|
|
||||||
await tb.cycle_reset()
|
await tb.cycle_reset()
|
||||||
|
|
||||||
tb.set_idle_generator(idle_inserter)
|
tb.set_idle_generator(idle_inserter)
|
||||||
tb.set_backpressure_generator(backpressure_inserter)
|
tb.set_backpressure_generator(backpressure_inserter)
|
||||||
|
|
||||||
for length in range(1, byte_width*2):
|
for length in range(1, byte_lanes*2):
|
||||||
for offset in range(byte_width):
|
for offset in range(byte_lanes):
|
||||||
tb.log.info("length %d, offset %d", length, offset)
|
tb.log.info("length %d, offset %d", length, offset)
|
||||||
addr = offset+0x1000
|
addr = offset+0x1000
|
||||||
test_data = bytearray([x % 256 for x in range(length)])
|
test_data = bytearray([x % 256 for x in range(length)])
|
||||||
@@ -140,12 +140,12 @@ async def run_test_write_words(dut):
|
|||||||
|
|
||||||
tb = TB(dut)
|
tb = TB(dut)
|
||||||
|
|
||||||
byte_width = tb.axil_master.write_if.byte_width
|
byte_lanes = tb.axil_master.write_if.byte_lanes
|
||||||
|
|
||||||
await tb.cycle_reset()
|
await tb.cycle_reset()
|
||||||
|
|
||||||
for length in list(range(1, 4)):
|
for length in list(range(1, 4)):
|
||||||
for offset in list(range(byte_width)):
|
for offset in list(range(byte_lanes)):
|
||||||
tb.log.info("length %d, offset %d", length, offset)
|
tb.log.info("length %d, offset %d", length, offset)
|
||||||
addr = offset+0x1000
|
addr = offset+0x1000
|
||||||
|
|
||||||
@@ -194,12 +194,12 @@ async def run_test_read_words(dut):
|
|||||||
|
|
||||||
tb = TB(dut)
|
tb = TB(dut)
|
||||||
|
|
||||||
byte_width = tb.axil_master.write_if.byte_width
|
byte_lanes = tb.axil_master.write_if.byte_lanes
|
||||||
|
|
||||||
await tb.cycle_reset()
|
await tb.cycle_reset()
|
||||||
|
|
||||||
for length in list(range(1, 4)):
|
for length in list(range(1, 4)):
|
||||||
for offset in list(range(byte_width)):
|
for offset in list(range(byte_lanes)):
|
||||||
tb.log.info("length %d, offset %d", length, offset)
|
tb.log.info("length %d, offset %d", length, offset)
|
||||||
addr = offset+0x1000
|
addr = offset+0x1000
|
||||||
|
|
||||||
|
|||||||
@@ -63,10 +63,10 @@ class TB:
|
|||||||
self.dut.rst.setimmediatevalue(0)
|
self.dut.rst.setimmediatevalue(0)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
self.dut.rst <= 1
|
self.dut.rst.value = 1
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
self.dut.rst <= 0
|
self.dut.rst.value = 0
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user