Replace token with event

This commit is contained in:
Alex Forencich
2020-12-02 16:07:25 -08:00
parent 17a1481a1b
commit a48da65f55
3 changed files with 171 additions and 258 deletions

View File

@@ -50,12 +50,23 @@ First, non-blocking operations can be started with `init_read()` and `init_write
await axi_master.wait() await axi_master.wait()
data = axi_master.get_read_data() data = axi_master.get_read_data()
Alternatively, an event object can be provided as an argument to `init_read()` and `init_write()`, and the result can be retrieved from `Event.data`. For example:
event = Event()
axi_master.init_write(0x0000, b'test', event=event)
await event.wait()
resp = event.data
event = Event()
axi_master.init_read(0x0000, 4, event=event)
await event.wait()
resp = event.data
Second, 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: Second, 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:
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()`, `write()`, `get_read_data()`, and `get_write_resp()` return `namedtuple` objects containing _address_, _data_ or _length_, _resp_, and _token_. `read()`, `write()`, `get_read_data()`, and `get_write_resp()` return `namedtuple` objects containing _address_, _data_ or _length_, and _resp_.
#### `AxiMaster` and `AxiLiteMaster` constructor parameters #### `AxiMaster` and `AxiLiteMaster` constructor parameters
@@ -73,13 +84,13 @@ Second, blocking operations can be carried out with `read()` and `write()` and t
* `init_read(address, length, ...)`: initiate reading _length_ bytes, starting at _address_ * `init_read(address, length, ...)`: initiate reading _length_ bytes, starting at _address_
* `init_write(address, data, ...)`: initiate writing _data_ (bytes), starting from _address_ * `init_write(address, data, ...)`: initiate writing _data_ (bytes), starting from _address_
* `idle()`: returns _True_ when there are no outstanding operations in progress * `idle()`: returns _True_ when there are no outstanding operations in progress
* `wait(token)`: blocking wait until all outstanding operations complete, or a specific operation if token is provided * `wait()`: blocking wait until all outstanding operations complete
* `wait_read(token)`: wait until all outstanding read operations complete, or a specific operation if token is provided * `wait_read()`: wait until all outstanding read operations complete
* `wait_write(token)`: wait until all outstanding write operations complete, or a specific operation if token is provided * `wait_write()`: wait until all outstanding write operations complete
* `read_data_ready(token)`: determine if any read read data is available, check specific operation if token is provided * `read_data_ready()`: determine if any read read data is available
* `get_read_data(token)`: fetch first available read data, or read data for specific operation if token is provided * `get_read_data()`: fetch first available read data
* `write_resp_ready(token)`: determine if any write response is available, check specific operation if token is provided * `write_resp_ready()`: determine if any write response is available
* `get_write_resp(token)`: fetch first available write response, or write response for specific operation if token is provided * `get_write_resp()`: fetch first available write response
* `read(address, length, ...)`: read _length_ bytes, starting at _address_ * `read(address, length, ...)`: read _length_ bytes, starting at _address_
* `read_words(address, count, byteorder, ws, ...)`: read _count_ _ws_-byte words, starting at _address_, default word size of `2`, default _byteorder_ `"little"` * `read_words(address, count, byteorder, ws, ...)`: read _count_ _ws_-byte words, starting at _address_, default word size of `2`, default _byteorder_ `"little"`
* `read_dwords(address, count, byteorder, ...)`: read _count_ 4-byte dwords, starting at _address_, default _byteorder_ `"little"` * `read_dwords(address, count, byteorder, ...)`: read _count_ 4-byte dwords, starting at _address_, default _byteorder_ `"little"`
@@ -109,12 +120,12 @@ Second, blocking operations can be carried out with `read()` and `write()` and t
* _region_: AXI region field, default `0` * _region_: AXI region field, default `0`
* _user_: AXI user signal (awuser/aruser), default `0` * _user_: AXI user signal (awuser/aruser), default `0`
* _wuser_: AXI wuser signal, default `0` (write-related methods only) * _wuser_: AXI wuser signal, default `0` (write-related methods only)
* _token_: Token used to retrieve result for operation, default `None` (`init_read()` and `init_write()` only) * _event_: `Event` object used to wait on and retrieve result for specific operation, default `None` (`init_read()` and `init_write()` only). If provided, the event will be triggered when the operation completes and the result returned via `Event.data` instead of `get_read_data()` or `get_write_resp()`.
#### Additional optional arguments for `AxiLiteMaster` #### Additional optional arguments for `AxiLiteMaster`
* _prot_: AXI protection flags, default `AxiProt.NONSECURE` * _prot_: AXI protection flags, default `AxiProt.NONSECURE`
* _token_: Token used to retrieve result for operation, default `None` (`init_read()` and `init_write()` only) * _event_: `Event` object used to wait on and retrieve result for specific operation, default `None` (`init_read()` and `init_write()` only). If provided, the event will be triggered when the operation completes and the result returned via `Event.data` instead of `get_read_data()` or `get_write_resp()`.
### AXI and AXI lite RAM ### AXI and AXI lite RAM

View File

@@ -34,17 +34,17 @@ from .axi_channels import AxiAWSource, AxiWSource, AxiBSink, AxiARSource, AxiRSi
# AXI master write helper objects # AXI master write helper objects
AxiWriteCmd = namedtuple("AxiWriteCmd", ["address", "data", "awid", "burst", "size", AxiWriteCmd = namedtuple("AxiWriteCmd", ["address", "data", "awid", "burst", "size",
"lock", "cache", "prot", "qos", "region", "user", "wuser", "token"]) "lock", "cache", "prot", "qos", "region", "user", "wuser", "event"])
AxiWriteRespCmd = namedtuple("AxiWriteRespCmd", ["address", "length", "size", "cycles", AxiWriteRespCmd = namedtuple("AxiWriteRespCmd", ["address", "length", "size", "cycles",
"prot", "burst_list", "token"]) "prot", "burst_list", "event"])
AxiWriteResp = namedtuple("AxiWriteResp", ["address", "length", "resp", "user", "token"]) AxiWriteResp = namedtuple("AxiWriteResp", ["address", "length", "resp", "user"])
# AXI master read helper objects # AXI master read helper objects
AxiReadCmd = namedtuple("AxiReadCmd", ["address", "length", "arid", "burst", "size", AxiReadCmd = namedtuple("AxiReadCmd", ["address", "length", "arid", "burst", "size",
"lock", "cache", "prot", "qos", "region", "user", "token"]) "lock", "cache", "prot", "qos", "region", "user", "event"])
AxiReadRespCmd = namedtuple("AxiReadRespCmd", ["address", "length", "size", "cycles", AxiReadRespCmd = namedtuple("AxiReadRespCmd", ["address", "length", "size", "cycles",
"prot", "burst_list", "token"]) "prot", "burst_list", "event"])
AxiReadResp = namedtuple("AxiReadResp", ["address", "data", "resp", "user", "token"]) AxiReadResp = namedtuple("AxiReadResp", ["address", "data", "resp", "user"])
class AxiMasterWrite(object): class AxiMasterWrite(object):
@@ -62,8 +62,6 @@ class AxiMasterWrite(object):
self.w_channel = AxiWSource(entity, name, clock, reset) self.w_channel = AxiWSource(entity, name, clock, reset)
self.b_channel = AxiBSink(entity, name, clock, reset) self.b_channel = AxiBSink(entity, name, clock, reset)
self.active_tokens = set()
self.write_command_queue = deque() self.write_command_queue = deque()
self.write_command_sync = Event() self.write_command_sync = Event()
self.write_resp_queue = deque() self.write_resp_queue = deque()
@@ -106,11 +104,10 @@ class AxiMasterWrite(object):
cocotb.fork(self._process_write_resp()) cocotb.fork(self._process_write_resp())
def init_write(self, address, data, awid=None, burst=AxiBurstType.INCR, size=None, lock=AxiLockType.NORMAL, def init_write(self, address, data, awid=None, burst=AxiBurstType.INCR, size=None, lock=AxiLockType.NORMAL,
cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0, token=None): cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0, event=None):
if token is not None:
if token in self.active_tokens: if event is not None and not isinstance(event, Event):
raise Exception("Token is not unique") raise ValueError("Expected event object")
self.active_tokens.add(token)
if awid is None or awid < 0: if awid is None or awid < 0:
awid = None awid = None
@@ -137,54 +134,32 @@ class AxiMasterWrite(object):
self.in_flight_operations += 1 self.in_flight_operations += 1
cmd = AxiWriteCmd(address, bytearray(data), awid, burst, size, lock, cmd = AxiWriteCmd(address, bytearray(data), awid, burst, size, lock,
cache, prot, qos, region, user, wuser, token) cache, prot, qos, region, user, wuser, event)
self.write_command_queue.append(cmd) self.write_command_queue.append(cmd)
self.write_command_sync.set() self.write_command_sync.set()
def idle(self): def idle(self):
return not self.in_flight_operations return not self.in_flight_operations
async def wait(self, token=None): async def wait(self):
if token is None: while not self.idle():
while not self.idle(): self.write_resp_sync.clear()
self.write_resp_sync.clear() await self.write_resp_sync.wait()
await self.write_resp_sync.wait()
else:
if token not in self.active_tokens:
raise ValueError("Unknown token")
while token not in self.write_resp_set:
self.write_resp_sync.clear()
await self.write_resp_sync.wait()
def write_resp_ready(self, token=None): def write_resp_ready(self):
if token is not None:
return token in self.write_resp_set
return bool(self.write_resp_queue) return bool(self.write_resp_queue)
def get_write_resp(self, token=None): def get_write_resp(self):
if token is not None:
if token in self.write_resp_set:
for resp in self.write_resp_queue:
if resp.token == token:
self.write_resp_queue.remove(resp)
self.active_tokens.remove(resp.token)
self.write_resp_set.remove(resp.token)
return resp
return None
if self.write_resp_queue: if self.write_resp_queue:
resp = self.write_resp_queue.popleft() return self.write_resp_queue.popleft()
if resp.token is not None:
self.active_tokens.remove(resp.token)
self.write_resp_set.remove(resp.token)
return resp
return None return None
async def write(self, address, data, awid=None, burst=AxiBurstType.INCR, size=None, async def write(self, address, data, awid=None, burst=AxiBurstType.INCR, size=None,
lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0): lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0):
token = object() event = Event()
self.init_write(address, data, awid, burst, size, lock, cache, prot, qos, region, user, wuser, token) self.init_write(address, data, awid, burst, size, lock, cache, prot, qos, region, user, wuser, event)
await self.wait(token) await event.wait()
return self.get_write_resp(token) return event.data
async def write_words(self, address, data, byteorder='little', ws=2, awid=None, burst=AxiBurstType.INCR, size=None, async def write_words(self, address, data, byteorder='little', ws=2, awid=None, burst=AxiBurstType.INCR, size=None,
lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0): lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0):
@@ -327,7 +302,7 @@ class AxiMasterWrite(object):
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_width
resp_cmd = AxiWriteRespCmd(cmd.address, len(cmd.data), cmd.size, cycles, cmd.prot, burst_list, cmd.token) resp_cmd = AxiWriteRespCmd(cmd.address, len(cmd.data), cmd.size, cycles, cmd.prot, burst_list, cmd.event)
self.int_write_resp_command_queue.append(resp_cmd) self.int_write_resp_command_queue.append(resp_cmd)
self.int_write_resp_command_sync.set() self.int_write_resp_command_sync.set()
@@ -378,10 +353,14 @@ class AxiMasterWrite(object):
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)
self.write_resp_queue.append(AxiWriteResp(cmd.address, cmd.length, resp, user, cmd.token)) write_resp = AxiWriteResp(cmd.address, cmd.length, resp, user)
self.write_resp_sync.set()
if cmd.token is not None: if cmd.event is not None:
self.write_resp_set.add(cmd.token) cmd.event.set(write_resp)
else:
self.write_resp_queue.append(write_resp)
self.write_resp_sync.set()
self.in_flight_operations -= 1 self.in_flight_operations -= 1
@@ -399,8 +378,6 @@ class AxiMasterRead(object):
self.ar_channel = AxiARSource(entity, name, clock, reset) self.ar_channel = AxiARSource(entity, name, clock, reset)
self.r_channel = AxiRSink(entity, name, clock, reset) self.r_channel = AxiRSink(entity, name, clock, reset)
self.active_tokens = set()
self.read_command_queue = deque() self.read_command_queue = deque()
self.read_command_sync = Event() self.read_command_sync = Event()
self.read_data_queue = deque() self.read_data_queue = deque()
@@ -441,11 +418,10 @@ class AxiMasterRead(object):
cocotb.fork(self._process_read_resp()) cocotb.fork(self._process_read_resp())
def init_read(self, address, length, arid=None, burst=AxiBurstType.INCR, size=None, def init_read(self, address, length, arid=None, burst=AxiBurstType.INCR, size=None,
lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, token=None): lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, event=None):
if token is not None:
if token in self.active_tokens: if event is not None and not isinstance(event, Event):
raise Exception("Token is not unique") raise ValueError("Expected event object")
self.active_tokens.add(token)
if length < 0: if length < 0:
raise ValueError("Read length must be positive") raise ValueError("Read length must be positive")
@@ -467,54 +443,32 @@ class AxiMasterRead(object):
self.in_flight_operations += 1 self.in_flight_operations += 1
cmd = AxiReadCmd(address, length, arid, burst, size, lock, cache, prot, qos, region, user, token) cmd = AxiReadCmd(address, length, arid, burst, size, lock, cache, prot, qos, region, user, event)
self.read_command_queue.append(cmd) self.read_command_queue.append(cmd)
self.read_command_sync.set() self.read_command_sync.set()
def idle(self): def idle(self):
return not self.in_flight_operations return not self.in_flight_operations
async def wait(self, token=None): async def wait(self):
if token is None: while not self.idle():
while not self.idle(): self.read_data_sync.clear()
self.read_data_sync.clear() await self.read_data_sync.wait()
await self.read_data_sync.wait()
else:
if token not in self.active_tokens:
raise ValueError("Unknown token")
while token not in self.read_data_set:
self.read_data_sync.clear()
await self.read_data_sync.wait()
def read_data_ready(self, token=None): def read_data_ready(self):
if token is not None:
return token in self.read_data_set
return bool(self.read_data_queue) return bool(self.read_data_queue)
def get_read_data(self, token=None): def get_read_data(self):
if token is not None:
if token in self.read_data_set:
for resp in self.read_data_queue:
if resp.token == token:
self.read_data_queue.remove(resp)
self.active_tokens.remove(resp.token)
self.read_data_set.remove(resp.token)
return resp
return None
if self.read_data_queue: if self.read_data_queue:
resp = self.read_data_queue.popleft() return self.read_data_queue.popleft()
if resp.token is not None:
self.active_tokens.remove(resp.token)
self.read_data_set.remove(resp.token)
return resp
return None return None
async def read(self, address, length, arid=None, burst=AxiBurstType.INCR, size=None, async def read(self, address, length, arid=None, burst=AxiBurstType.INCR, size=None,
lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0): lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):
token = object() event = Event()
self.init_read(address, length, arid, burst, size, lock, cache, prot, qos, region, user, token) self.init_read(address, length, arid, burst, size, lock, cache, prot, qos, region, user, event)
await self.wait(token) await event.wait()
return self.get_read_data(token) return event.data
async def read_words(self, address, count, byteorder='little', ws=2, arid=None, burst=AxiBurstType.INCR, size=None, async def read_words(self, address, count, byteorder='little', ws=2, arid=None, burst=AxiBurstType.INCR, size=None,
lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0): lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):
@@ -616,7 +570,7 @@ class AxiMasterRead(object):
cur_addr += num_bytes cur_addr += num_bytes
resp_cmd = AxiReadRespCmd(cmd.address, cmd.length, cmd.size, cycles, cmd.prot, burst_list, cmd.token) resp_cmd = AxiReadRespCmd(cmd.address, cmd.length, cmd.size, cycles, cmd.prot, burst_list, cmd.event)
self.int_read_resp_command_queue.append(resp_cmd) self.int_read_resp_command_queue.append(resp_cmd)
self.int_read_resp_command_sync.set() self.int_read_resp_command_sync.set()
@@ -699,10 +653,14 @@ class AxiMasterRead(object):
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)))
self.read_data_queue.append(AxiReadResp(cmd.address, data, resp, user, cmd.token)) read_resp = AxiReadResp(cmd.address, data, resp, user)
self.read_data_sync.set()
if cmd.token is not None: if cmd.event is not None:
self.read_data_set.add(cmd.token) cmd.event.set(read_resp)
else:
self.read_data_queue.append(read_resp)
self.read_data_sync.set()
self.in_flight_operations -= 1 self.in_flight_operations -= 1
@@ -715,44 +673,38 @@ class AxiMaster(object):
self.read_if = AxiMasterRead(entity, name, clock, reset, max_burst_len) self.read_if = AxiMasterRead(entity, name, clock, reset, max_burst_len)
def init_read(self, address, length, burst=AxiBurstType.INCR, size=None, def init_read(self, address, length, burst=AxiBurstType.INCR, size=None,
lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, token=None): lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, event=None):
self.read_if.init_read(address, length, burst, size, lock, cache, prot, qos, region, user, token) self.read_if.init_read(address, length, burst, size, lock, cache, prot, qos, region, user, event)
def init_write(self, address, data, burst=AxiBurstType.INCR, size=None, lock=AxiLockType.NORMAL, def init_write(self, address, data, burst=AxiBurstType.INCR, size=None, lock=AxiLockType.NORMAL,
cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0, token=None): cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0, event=None):
self.write_if.init_write(address, data, burst, size, lock, cache, prot, qos, region, user, wuser, token) self.write_if.init_write(address, data, burst, size, lock, cache, prot, qos, region, user, wuser, event)
def idle(self): def idle(self):
return (not self.read_if or self.read_if.idle()) and (not self.write_if or self.write_if.idle()) return (not self.read_if or self.read_if.idle()) and (not self.write_if or self.write_if.idle())
async def wait(self, token=None): async def wait(self):
if token is None: while not self.idle():
while not self.idle(): await self.write_if.wait()
await self.write_if.wait() await self.read_if.wait()
await self.read_if.wait()
else:
if token in self.write_if.active_tokens:
await self.write_if.wait(token)
else:
await self.read_if.wait(token)
async def wait_read(self, token=None): async def wait_read(self):
await self.read_if.wait(token) await self.read_if.wait()
async def wait_write(self, token=None): async def wait_write(self):
await self.write_if.wait(token) await self.write_if.wait()
def read_data_ready(self, token=None): def read_data_ready(self):
return self.read_if.read_data_ready(token) return self.read_if.read_data_ready()
def get_read_data(self, token=None): def get_read_data(self):
return self.read_if.get_read_data(token) return self.read_if.get_read_data()
def write_resp_ready(self, token=None): def write_resp_ready(self):
return self.write_if.write_resp_ready(token) return self.write_if.write_resp_ready()
def get_write_resp(self, token=None): def get_write_resp(self):
return self.write_if.get_write_resp(token) return self.write_if.get_write_resp()
async def read(self, address, length, arid=None, burst=AxiBurstType.INCR, size=None, async def read(self, address, length, arid=None, burst=AxiBurstType.INCR, size=None,
lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0): lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):

View File

@@ -33,14 +33,14 @@ from .constants import AxiProt, AxiResp
from .axil_channels import AxiLiteAWSource, AxiLiteWSource, AxiLiteBSink, AxiLiteARSource, AxiLiteRSink from .axil_channels import AxiLiteAWSource, AxiLiteWSource, AxiLiteBSink, AxiLiteARSource, AxiLiteRSink
# AXI lite master write # AXI lite master write
AxiLiteWriteCmd = namedtuple("AxiLiteWriteCmd", ["address", "data", "prot", "token"]) AxiLiteWriteCmd = namedtuple("AxiLiteWriteCmd", ["address", "data", "prot", "event"])
AxiLiteWriteRespCmd = namedtuple("AxiLiteWriteRespCmd", ["address", "length", "cycles", "prot", "token"]) AxiLiteWriteRespCmd = namedtuple("AxiLiteWriteRespCmd", ["address", "length", "cycles", "prot", "event"])
AxiLiteWriteResp = namedtuple("AxiLiteWriteResp", ["address", "length", "resp", "token"]) AxiLiteWriteResp = namedtuple("AxiLiteWriteResp", ["address", "length", "resp"])
# AXI lite master read # AXI lite master read
AxiLiteReadCmd = namedtuple("AxiLiteReadCmd", ["address", "length", "prot", "token"]) AxiLiteReadCmd = namedtuple("AxiLiteReadCmd", ["address", "length", "prot", "event"])
AxiLiteReadRespCmd = namedtuple("AxiLiteReadRespCmd", ["address", "length", "cycles", "prot", "token"]) AxiLiteReadRespCmd = namedtuple("AxiLiteReadRespCmd", ["address", "length", "cycles", "prot", "event"])
AxiLiteReadResp = namedtuple("AxiLiteReadResp", ["address", "data", "resp", "token"]) AxiLiteReadResp = namedtuple("AxiLiteReadResp", ["address", "data", "resp"])
class AxiLiteMasterWrite(object): class AxiLiteMasterWrite(object):
@@ -58,8 +58,6 @@ class AxiLiteMasterWrite(object):
self.w_channel = AxiLiteWSource(entity, name, clock, reset) self.w_channel = AxiLiteWSource(entity, name, clock, reset)
self.b_channel = AxiLiteBSink(entity, name, clock, reset) self.b_channel = AxiLiteBSink(entity, name, clock, reset)
self.active_tokens = set()
self.write_command_queue = deque() self.write_command_queue = deque()
self.write_command_sync = Event() self.write_command_sync = Event()
self.write_resp_queue = deque() self.write_resp_queue = deque()
@@ -87,60 +85,36 @@ class AxiLiteMasterWrite(object):
cocotb.fork(self._process_write()) cocotb.fork(self._process_write())
cocotb.fork(self._process_write_resp()) cocotb.fork(self._process_write_resp())
def init_write(self, address, data, prot=AxiProt.NONSECURE, token=None): def init_write(self, address, data, prot=AxiProt.NONSECURE, event=None):
if token is not None: if event is not None and not isinstance(event, Event):
if token in self.active_tokens: raise ValueError("Expected event object")
raise Exception("Token is not unique")
self.active_tokens.add(token)
self.in_flight_operations += 1 self.in_flight_operations += 1
self.write_command_queue.append(AxiLiteWriteCmd(address, bytearray(data), prot, token)) self.write_command_queue.append(AxiLiteWriteCmd(address, bytearray(data), prot, event))
self.write_command_sync.set() self.write_command_sync.set()
def idle(self): def idle(self):
return not self.in_flight_operations return not self.in_flight_operations
async def wait(self, token=None): async def wait(self):
if token is None: while not self.idle():
while not self.idle(): self.write_resp_sync.clear()
self.write_resp_sync.clear() await self.write_resp_sync.wait()
await self.write_resp_sync.wait()
else:
if token not in self.active_tokens:
raise ValueError("Unknown token")
while token not in self.write_resp_set:
self.write_resp_sync.clear()
await self.write_resp_sync.wait()
def write_resp_ready(self, token=None): def write_resp_ready(self):
if token is not None:
return token in self.write_resp_set
return bool(self.write_resp_queue) return bool(self.write_resp_queue)
def get_write_resp(self, token=None): def get_write_resp(self):
if token is not None:
if token in self.write_resp_set:
for resp in self.write_resp_queue:
if resp.token == token:
self.write_resp_queue.remove(resp)
self.active_tokens.remove(resp.token)
self.write_resp_set.remove(resp.token)
return resp
return None
if self.write_resp_queue: if self.write_resp_queue:
resp = self.write_resp_queue.popleft() return self.write_resp_queue.popleft()
if resp.token is not None:
self.active_tokens.remove(resp.token)
self.write_resp_set.remove(resp.token)
return resp
return None return None
async def write(self, address, data, prot=AxiProt.NONSECURE): async def write(self, address, data, prot=AxiProt.NONSECURE):
token = object() event = Event()
self.init_write(address, data, prot, token) self.init_write(address, data, prot, event)
await self.wait(token) await event.wait()
return self.get_write_resp(token) return event.data
async def write_words(self, address, data, byteorder='little', ws=2, prot=AxiProt.NONSECURE): async def write_words(self, address, data, byteorder='little', ws=2, prot=AxiProt.NONSECURE):
words = data words = data
@@ -185,7 +159,7 @@ class AxiLiteMasterWrite(object):
cycles = (len(cmd.data) + (cmd.address % self.byte_width) + self.byte_width-1) // self.byte_width cycles = (len(cmd.data) + (cmd.address % self.byte_width) + self.byte_width-1) // self.byte_width
resp_cmd = AxiLiteWriteRespCmd(cmd.address, len(cmd.data), cycles, cmd.prot, cmd.token) resp_cmd = AxiLiteWriteRespCmd(cmd.address, len(cmd.data), cycles, cmd.prot, cmd.event)
self.int_write_resp_command_queue.append(resp_cmd) self.int_write_resp_command_queue.append(resp_cmd)
self.int_write_resp_command_sync.set() self.int_write_resp_command_sync.set()
@@ -244,10 +218,14 @@ class AxiLiteMasterWrite(object):
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)
self.write_resp_queue.append(AxiLiteWriteResp(cmd.address, cmd.length, resp, cmd.token)) write_resp = AxiLiteWriteResp(cmd.address, cmd.length, resp)
self.write_resp_sync.set()
if cmd.token is not None: if cmd.event is not None:
self.write_resp_set.add(cmd.token) cmd.event.set(write_resp)
else:
self.write_resp_queue.append(write_resp)
self.write_resp_sync.set()
self.in_flight_operations -= 1 self.in_flight_operations -= 1
@@ -265,8 +243,6 @@ class AxiLiteMasterRead(object):
self.ar_channel = AxiLiteARSource(entity, name, clock, reset) self.ar_channel = AxiLiteARSource(entity, name, clock, reset)
self.r_channel = AxiLiteRSink(entity, name, clock, reset) self.r_channel = AxiLiteRSink(entity, name, clock, reset)
self.active_tokens = set()
self.read_command_queue = deque() self.read_command_queue = deque()
self.read_command_sync = Event() self.read_command_sync = Event()
self.read_data_queue = deque() self.read_data_queue = deque()
@@ -292,60 +268,36 @@ class AxiLiteMasterRead(object):
cocotb.fork(self._process_read()) cocotb.fork(self._process_read())
cocotb.fork(self._process_read_resp()) cocotb.fork(self._process_read_resp())
def init_read(self, address, length, prot=AxiProt.NONSECURE, token=None): def init_read(self, address, length, prot=AxiProt.NONSECURE, event=None):
if token is not None: if event is not None and not isinstance(event, Event):
if token in self.active_tokens: raise ValueError("Expected event object")
raise Exception("Token is not unique")
self.active_tokens.add(token)
self.in_flight_operations += 1 self.in_flight_operations += 1
self.read_command_queue.append(AxiLiteReadCmd(address, length, prot, token)) self.read_command_queue.append(AxiLiteReadCmd(address, length, prot, event))
self.read_command_sync.set() self.read_command_sync.set()
def idle(self): def idle(self):
return not self.in_flight_operations return not self.in_flight_operations
async def wait(self, token=None): async def wait(self):
if token is None: while not self.idle():
while not self.idle(): self.read_data_sync.clear()
self.read_data_sync.clear() await self.read_data_sync.wait()
await self.read_data_sync.wait()
else:
if token not in self.active_tokens:
raise ValueError("Unknown token")
while token not in self.read_data_set:
self.read_data_sync.clear()
await self.read_data_sync.wait()
def read_data_ready(self, token=None): def read_data_ready(self):
if token is not None:
return token in self.read_data_set
return bool(self.read_data_queue) return bool(self.read_data_queue)
def get_read_data(self, token=None): def get_read_data(self):
if token is not None:
if token in self.read_data_set:
for resp in self.read_data_queue:
if resp.token == token:
self.read_data_queue.remove(resp)
self.active_tokens.remove(resp.token)
self.read_data_set.remove(resp.token)
return resp
return None
if self.read_data_queue: if self.read_data_queue:
resp = self.read_data_queue.popleft() return self.read_data_queue.popleft()
if resp.token is not None:
self.active_tokens.remove(resp.token)
self.read_data_set.remove(resp.token)
return resp
return None return None
async def read(self, address, length, prot=AxiProt.NONSECURE): async def read(self, address, length, prot=AxiProt.NONSECURE):
token = object() event = Event()
self.init_read(address, length, prot, token) self.init_read(address, length, prot, event)
await self.wait(token) await event.wait()
return self.get_read_data(token) return event.data
async def read_words(self, address, count, byteorder='little', ws=2, prot=AxiProt.NONSECURE): async def read_words(self, address, count, byteorder='little', ws=2, prot=AxiProt.NONSECURE):
data = await self.read(address, count*ws, prot) data = await self.read(address, count*ws, prot)
@@ -384,7 +336,7 @@ class AxiLiteMasterRead(object):
cycles = (cmd.length + self.byte_width-1 + (cmd.address % self.byte_width)) // self.byte_width cycles = (cmd.length + self.byte_width-1 + (cmd.address % self.byte_width)) // self.byte_width
resp_cmd = AxiLiteReadRespCmd(cmd.address, cmd.length, cycles, cmd.prot, cmd.token) resp_cmd = AxiLiteReadRespCmd(cmd.address, cmd.length, cycles, cmd.prot, cmd.event)
self.int_read_resp_command_queue.append(resp_cmd) self.int_read_resp_command_queue.append(resp_cmd)
self.int_read_resp_command_sync.set() self.int_read_resp_command_sync.set()
@@ -437,10 +389,14 @@ class AxiLiteMasterRead(object):
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)))
self.read_data_queue.append(AxiLiteReadResp(cmd.address, data, resp, cmd.token)) read_resp = AxiLiteReadResp(cmd.address, data, resp)
self.read_data_sync.set()
if cmd.token is not None: if cmd.event is not None:
self.read_data_set.add(cmd.token) cmd.event.set(read_resp)
else:
self.read_data_queue.append(read_resp)
self.read_data_sync.set()
self.in_flight_operations -= 1 self.in_flight_operations -= 1
@@ -452,43 +408,37 @@ class AxiLiteMaster(object):
self.write_if = AxiLiteMasterWrite(entity, name, clock, reset) self.write_if = AxiLiteMasterWrite(entity, name, clock, reset)
self.read_if = AxiLiteMasterRead(entity, name, clock, reset) self.read_if = AxiLiteMasterRead(entity, name, clock, reset)
def init_read(self, address, length, prot=AxiProt.NONSECURE, token=None): def init_read(self, address, length, prot=AxiProt.NONSECURE, event=None):
self.read_if.init_read(address, length, prot, token) self.read_if.init_read(address, length, prot, event)
def init_write(self, address, data, prot=AxiProt.NONSECURE, token=None): def init_write(self, address, data, prot=AxiProt.NONSECURE, event=None):
self.write_if.init_write(address, data, prot, token) self.write_if.init_write(address, data, prot, event)
def idle(self): def idle(self):
return (not self.read_if or self.read_if.idle()) and (not self.write_if or self.write_if.idle()) return (not self.read_if or self.read_if.idle()) and (not self.write_if or self.write_if.idle())
async def wait(self, token=None): async def wait(self):
if token is None: while not self.idle():
while not self.idle(): await self.write_if.wait()
await self.write_if.wait() await self.read_if.wait()
await self.read_if.wait()
else:
if token in self.write_if.active_tokens:
await self.write_if.wait(token)
else:
await self.read_if.wait(token)
async def wait_read(self, token=None): async def wait_read(self):
await self.read_if.wait(token) await self.read_if.wait()
async def wait_write(self, token=None): async def wait_write(self):
await self.write_if.wait(token) await self.write_if.wait()
def read_data_ready(self, token=None): def read_data_ready(self):
return self.read_if.read_data_ready(token) return self.read_if.read_data_ready()
def get_read_data(self, token=None): def get_read_data(self):
return self.read_if.get_read_data(token) return self.read_if.get_read_data()
def write_resp_ready(self, token=None): def write_resp_ready(self):
return self.write_if.write_resp_ready(token) return self.write_if.write_resp_ready()
def get_write_resp(self, token=None): def get_write_resp(self):
return self.write_if.get_write_resp(token) return self.write_if.get_write_resp()
async def read(self, address, length, prot=AxiProt.NONSECURE): async def read(self, address, length, prot=AxiProt.NONSECURE):
return await self.read_if.read(address, length, prot) return await self.read_if.read(address, length, prot)