Add helper objects for AXI masters
This commit is contained in:
@@ -22,7 +22,7 @@ THE SOFTWARE.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from collections import deque
|
from collections import deque, namedtuple
|
||||||
|
|
||||||
import cocotb
|
import cocotb
|
||||||
from cocotb.triggers import Event
|
from cocotb.triggers import Event
|
||||||
@@ -32,6 +32,20 @@ from .version import __version__
|
|||||||
from .constants import AxiBurstType, AxiLockType, AxiProt, AxiResp
|
from .constants import AxiBurstType, AxiLockType, AxiProt, AxiResp
|
||||||
from .axi_channels import AxiAWSource, AxiWSource, AxiBSink, AxiARSource, AxiRSink
|
from .axi_channels import AxiAWSource, AxiWSource, AxiBSink, AxiARSource, AxiRSink
|
||||||
|
|
||||||
|
# AXI master write helper objects
|
||||||
|
AxiWriteCmd = namedtuple("AxiWriteCmd", ["address", "data", "burst", "size", "lock",
|
||||||
|
"cache", "prot", "qos", "region", "user", "token"])
|
||||||
|
AxiWriteRespCmd = namedtuple("AxiWriteRespCmd", ["address", "length", "size", "cycles",
|
||||||
|
"prot", "burst_list", "token"])
|
||||||
|
AxiWriteResp = namedtuple("AxiWriteResp", ["address", "length", "resp", "user", "token"])
|
||||||
|
|
||||||
|
# AXI master read helper objects
|
||||||
|
AxiReadCmd = namedtuple("AxiReadCmd", ["address", "length", "burst", "size", "lock",
|
||||||
|
"cache", "prot", "qos", "region", "user", "token"])
|
||||||
|
AxiReadRespCmd = namedtuple("AxiReadRespCmd", ["address", "length", "size", "cycles",
|
||||||
|
"prot", "burst_list", "token"])
|
||||||
|
AxiReadResp = namedtuple("AxiReadResp", ["address", "data", "resp", "user", "token"])
|
||||||
|
|
||||||
|
|
||||||
class AxiMasterWrite(object):
|
class AxiMasterWrite(object):
|
||||||
def __init__(self, entity, name, clock, reset=None, max_burst_len=256):
|
def __init__(self, entity, name, clock, reset=None, max_burst_len=256):
|
||||||
@@ -99,7 +113,8 @@ class AxiMasterWrite(object):
|
|||||||
|
|
||||||
self.in_flight_operations += 1
|
self.in_flight_operations += 1
|
||||||
|
|
||||||
self.write_command_queue.append((address, data, burst, size, lock, cache, prot, qos, region, user, token))
|
cmd = AxiWriteCmd(address, bytearray(data), burst, size, lock, cache, prot, qos, region, user, token)
|
||||||
|
self.write_command_queue.append(cmd)
|
||||||
self.write_command_sync.set()
|
self.write_command_sync.set()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
@@ -126,17 +141,17 @@ class AxiMasterWrite(object):
|
|||||||
if token is not None:
|
if token is not None:
|
||||||
if token in self.write_resp_set:
|
if token in self.write_resp_set:
|
||||||
for resp in self.write_resp_queue:
|
for resp in self.write_resp_queue:
|
||||||
if resp[-1] == token:
|
if resp.token == token:
|
||||||
self.write_resp_queue.remove(resp)
|
self.write_resp_queue.remove(resp)
|
||||||
self.active_tokens.remove(resp[-1])
|
self.active_tokens.remove(resp.token)
|
||||||
self.write_resp_set.remove(resp[-1])
|
self.write_resp_set.remove(resp.token)
|
||||||
return resp
|
return resp
|
||||||
return None
|
return None
|
||||||
if self.write_resp_queue:
|
if self.write_resp_queue:
|
||||||
resp = self.write_resp_queue.popleft()
|
resp = self.write_resp_queue.popleft()
|
||||||
if resp[-1] is not None:
|
if resp.token is not None:
|
||||||
self.active_tokens.remove(resp[-1])
|
self.active_tokens.remove(resp.token)
|
||||||
self.write_resp_set.remove(resp[-1])
|
self.write_resp_set.remove(resp.token)
|
||||||
return resp
|
return resp
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -145,7 +160,7 @@ class AxiMasterWrite(object):
|
|||||||
token = object()
|
token = object()
|
||||||
self.init_write(address, data, burst, size, lock, cache, prot, qos, region, user, token)
|
self.init_write(address, data, burst, size, lock, cache, prot, qos, region, user, token)
|
||||||
await self.wait_for_token(token)
|
await self.wait_for_token(token)
|
||||||
return self.get_write_resp(token)[1:3]
|
return self.get_write_resp(token)
|
||||||
|
|
||||||
async def write_words(self, address, data, ws=2, burst=AxiBurstType.INCR, size=None,
|
async def write_words(self, address, data, ws=2, 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):
|
||||||
@@ -185,23 +200,23 @@ class AxiMasterWrite(object):
|
|||||||
self.write_command_sync.clear()
|
self.write_command_sync.clear()
|
||||||
await self.write_command_sync.wait()
|
await self.write_command_sync.wait()
|
||||||
|
|
||||||
addr, data, burst, size, lock, cache, prot, qos, region, user, token = self.write_command_queue.popleft()
|
cmd = self.write_command_queue.popleft()
|
||||||
|
|
||||||
num_bytes = self.byte_width
|
if cmd.size is None:
|
||||||
|
|
||||||
if size is None:
|
|
||||||
size = self.max_burst_size
|
size = self.max_burst_size
|
||||||
|
num_bytes = self.byte_width
|
||||||
else:
|
else:
|
||||||
num_bytes = 2**size
|
size = cmd.size
|
||||||
|
num_bytes = 2**cmd.size
|
||||||
assert 0 < num_bytes <= self.byte_width
|
assert 0 < num_bytes <= self.byte_width
|
||||||
|
|
||||||
aligned_addr = (addr // num_bytes) * num_bytes
|
aligned_addr = (cmd.address // num_bytes) * num_bytes
|
||||||
word_addr = (addr // self.byte_width) * self.byte_width
|
word_addr = (cmd.address // self.byte_width) * self.byte_width
|
||||||
|
|
||||||
start_offset = addr % self.byte_width
|
start_offset = cmd.address % self.byte_width
|
||||||
end_offset = ((addr + len(data) - 1) % self.byte_width) + 1
|
end_offset = ((cmd.address + len(cmd.data) - 1) % self.byte_width) + 1
|
||||||
|
|
||||||
cycles = (len(data) + (addr % num_bytes) + num_bytes-1) // num_bytes
|
cycles = (len(cmd.data) + (cmd.address % num_bytes) + num_bytes-1) // num_bytes
|
||||||
|
|
||||||
cur_addr = aligned_addr
|
cur_addr = aligned_addr
|
||||||
offset = 0
|
offset = 0
|
||||||
@@ -213,7 +228,7 @@ class AxiMasterWrite(object):
|
|||||||
burst_length = 0
|
burst_length = 0
|
||||||
|
|
||||||
self.log.info("Write start addr: 0x%08x prot: %s data: %s",
|
self.log.info("Write start addr: 0x%08x prot: %s data: %s",
|
||||||
addr, prot, ' '.join((f'{c:02x}' for c in data)))
|
cmd.address, cmd.prot, ' '.join((f'{c:02x}' for c in cmd.data)))
|
||||||
|
|
||||||
for k in range(cycles):
|
for k in range(cycles):
|
||||||
start = cycle_offset
|
start = cycle_offset
|
||||||
@@ -228,7 +243,7 @@ class AxiMasterWrite(object):
|
|||||||
|
|
||||||
val = 0
|
val = 0
|
||||||
for j in range(start, stop):
|
for j in range(start, stop):
|
||||||
val |= bytearray(data)[offset] << j*8
|
val |= cmd.data[offset] << j*8
|
||||||
offset += 1
|
offset += 1
|
||||||
|
|
||||||
if n >= burst_length:
|
if n >= burst_length:
|
||||||
@@ -253,18 +268,18 @@ class AxiMasterWrite(object):
|
|||||||
aw.awaddr = cur_addr
|
aw.awaddr = cur_addr
|
||||||
aw.awlen = burst_length-1
|
aw.awlen = burst_length-1
|
||||||
aw.awsize = size
|
aw.awsize = size
|
||||||
aw.awburst = burst
|
aw.awburst = cmd.burst
|
||||||
aw.awlock = lock
|
aw.awlock = cmd.lock
|
||||||
aw.awcache = cache
|
aw.awcache = cmd.cache
|
||||||
aw.awprot = prot
|
aw.awprot = cmd.prot
|
||||||
aw.awqos = qos
|
aw.awqos = cmd.qos
|
||||||
aw.awregion = region
|
aw.awregion = cmd.region
|
||||||
aw.awuser = user
|
aw.awuser = cmd.user
|
||||||
|
|
||||||
await self.aw_channel.drive(aw)
|
await self.aw_channel.drive(aw)
|
||||||
|
|
||||||
self.log.info("Write burst start awid: 0x%x awaddr: 0x%08x awlen: %d awsize: %d awprot: %s",
|
self.log.info("Write burst start awid: 0x%x awaddr: 0x%08x awlen: %d awsize: %d awprot: %s",
|
||||||
awid, cur_addr, burst_length-1, size, prot)
|
awid, cur_addr, burst_length-1, size, cmd.prot)
|
||||||
|
|
||||||
n += 1
|
n += 1
|
||||||
|
|
||||||
@@ -278,7 +293,8 @@ 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
|
||||||
|
|
||||||
self.int_write_resp_command_queue.append((addr, len(data), size, cycles, prot, burst_list, token))
|
resp_cmd = AxiWriteRespCmd(cmd.address, len(cmd.data), size, cycles, cmd.prot, burst_list, cmd.token)
|
||||||
|
self.int_write_resp_command_queue.append(resp_cmd)
|
||||||
self.int_write_resp_command_sync.set()
|
self.int_write_resp_command_sync.set()
|
||||||
|
|
||||||
async def _process_write_resp(self):
|
async def _process_write_resp(self):
|
||||||
@@ -287,12 +303,12 @@ class AxiMasterWrite(object):
|
|||||||
self.int_write_resp_command_sync.clear()
|
self.int_write_resp_command_sync.clear()
|
||||||
await self.int_write_resp_command_sync.wait()
|
await self.int_write_resp_command_sync.wait()
|
||||||
|
|
||||||
addr, length, size, cycles, prot, burst_list, token = self.int_write_resp_command_queue.popleft()
|
cmd = self.int_write_resp_command_queue.popleft()
|
||||||
|
|
||||||
resp = AxiResp.OKAY
|
resp = AxiResp.OKAY
|
||||||
user = []
|
user = []
|
||||||
|
|
||||||
for bid, burst_length in burst_list:
|
for bid, burst_length in cmd.burst_list:
|
||||||
self.int_write_resp_queue_list.setdefault(bid, deque())
|
self.int_write_resp_queue_list.setdefault(bid, deque())
|
||||||
while True:
|
while True:
|
||||||
if self.int_write_resp_queue_list[bid]:
|
if self.int_write_resp_queue_list[bid]:
|
||||||
@@ -323,12 +339,12 @@ class AxiMasterWrite(object):
|
|||||||
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", burst_id, burst_resp)
|
||||||
|
|
||||||
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",
|
||||||
addr, prot, resp, length)
|
cmd.address, cmd.prot, resp, cmd.length)
|
||||||
|
|
||||||
self.write_resp_queue.append((addr, length, resp, user, token))
|
self.write_resp_queue.append(AxiWriteResp(cmd.address, cmd.length, resp, user, cmd.token))
|
||||||
self.write_resp_sync.set()
|
self.write_resp_sync.set()
|
||||||
if token is not None:
|
if cmd.token is not None:
|
||||||
self.write_resp_set.add(token)
|
self.write_resp_set.add(cmd.token)
|
||||||
self.in_flight_operations -= 1
|
self.in_flight_operations -= 1
|
||||||
|
|
||||||
|
|
||||||
@@ -395,7 +411,8 @@ class AxiMasterRead(object):
|
|||||||
|
|
||||||
self.in_flight_operations += 1
|
self.in_flight_operations += 1
|
||||||
|
|
||||||
self.read_command_queue.append((address, length, burst, size, lock, cache, prot, qos, region, user, token))
|
cmd = AxiReadCmd(address, length, burst, size, lock, cache, prot, qos, region, user, token)
|
||||||
|
self.read_command_queue.append(cmd)
|
||||||
self.read_command_sync.set()
|
self.read_command_sync.set()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
@@ -422,17 +439,17 @@ class AxiMasterRead(object):
|
|||||||
if token is not None:
|
if token is not None:
|
||||||
if token in self.read_data_set:
|
if token in self.read_data_set:
|
||||||
for resp in self.read_data_queue:
|
for resp in self.read_data_queue:
|
||||||
if resp[-1] == token:
|
if resp.token == token:
|
||||||
self.read_data_queue.remove(resp)
|
self.read_data_queue.remove(resp)
|
||||||
self.active_tokens.remove(resp[-1])
|
self.active_tokens.remove(resp.token)
|
||||||
self.read_data_set.remove(resp[-1])
|
self.read_data_set.remove(resp.token)
|
||||||
return resp
|
return resp
|
||||||
return None
|
return None
|
||||||
if self.read_data_queue:
|
if self.read_data_queue:
|
||||||
resp = self.read_data_queue.popleft()
|
resp = self.read_data_queue.popleft()
|
||||||
if resp[-1] is not None:
|
if resp.token is not None:
|
||||||
self.active_tokens.remove(resp[-1])
|
self.active_tokens.remove(resp.token)
|
||||||
self.read_data_set.remove(resp[-1])
|
self.read_data_set.remove(resp.token)
|
||||||
return resp
|
return resp
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -441,14 +458,14 @@ class AxiMasterRead(object):
|
|||||||
token = object()
|
token = object()
|
||||||
self.init_read(address, length, burst, size, lock, cache, prot, qos, region, user, token)
|
self.init_read(address, length, burst, size, lock, cache, prot, qos, region, user, token)
|
||||||
await self.wait_for_token(token)
|
await self.wait_for_token(token)
|
||||||
return self.get_read_data(token)[1:3]
|
return self.get_read_data(token)
|
||||||
|
|
||||||
async def read_words(self, address, count, ws=2, burst=AxiBurstType.INCR, size=None,
|
async def read_words(self, address, count, ws=2, 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):
|
||||||
data = await self.read(address, count*ws, burst, size, lock, cache, prot, qos, region, user)
|
data = await self.read(address, count*ws, burst, size, lock, cache, prot, qos, region, user)
|
||||||
words = []
|
words = []
|
||||||
for k in range(count):
|
for k in range(count):
|
||||||
words.append(int.from_bytes(data[0][ws*k:ws*(k+1)], 'little'))
|
words.append(int.from_bytes(data.data[ws*k:ws*(k+1)], 'little'))
|
||||||
return words
|
return words
|
||||||
|
|
||||||
async def read_dwords(self, address, count, burst=AxiBurstType.INCR, size=None,
|
async def read_dwords(self, address, count, burst=AxiBurstType.INCR, size=None,
|
||||||
@@ -461,7 +478,7 @@ class AxiMasterRead(object):
|
|||||||
|
|
||||||
async def read_byte(self, address, burst=AxiBurstType.INCR, size=None,
|
async def read_byte(self, address, 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):
|
||||||
return (await self.read(address, 1, burst, size, lock, cache, prot, qos, region, user))[0]
|
return (await self.read(address, 1, burst, size, lock, cache, prot, qos, region, user)).data[0]
|
||||||
|
|
||||||
async def read_word(self, address, ws=2, burst=AxiBurstType.INCR, size=None,
|
async def read_word(self, address, ws=2, 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):
|
||||||
@@ -481,19 +498,19 @@ class AxiMasterRead(object):
|
|||||||
self.read_command_sync.clear()
|
self.read_command_sync.clear()
|
||||||
await self.read_command_sync.wait()
|
await self.read_command_sync.wait()
|
||||||
|
|
||||||
addr, length, burst, size, lock, cache, prot, qos, region, user, token = self.read_command_queue.popleft()
|
cmd = self.read_command_queue.popleft()
|
||||||
|
|
||||||
num_bytes = self.byte_width
|
if cmd.size is None:
|
||||||
|
|
||||||
if size is None:
|
|
||||||
size = self.max_burst_size
|
size = self.max_burst_size
|
||||||
|
num_bytes = self.byte_width
|
||||||
else:
|
else:
|
||||||
num_bytes = 2**size
|
size = cmd.size
|
||||||
|
num_bytes = 2**cmd.size
|
||||||
assert 0 < num_bytes <= self.byte_width
|
assert 0 < num_bytes <= self.byte_width
|
||||||
|
|
||||||
aligned_addr = (addr // num_bytes) * num_bytes
|
aligned_addr = (cmd.address // num_bytes) * num_bytes
|
||||||
|
|
||||||
cycles = (length + num_bytes-1 + (addr % num_bytes)) // num_bytes
|
cycles = (cmd.length + num_bytes-1 + (cmd.address % num_bytes)) // num_bytes
|
||||||
|
|
||||||
burst_list = []
|
burst_list = []
|
||||||
|
|
||||||
@@ -502,7 +519,7 @@ class AxiMasterRead(object):
|
|||||||
|
|
||||||
burst_length = 0
|
burst_length = 0
|
||||||
|
|
||||||
self.log.info("Read start addr: 0x%08x prot: %s", addr, prot)
|
self.log.info("Read start addr: 0x%08x prot: %s", cmd.address, cmd.prot)
|
||||||
|
|
||||||
for k in range(cycles):
|
for k in range(cycles):
|
||||||
|
|
||||||
@@ -528,22 +545,23 @@ class AxiMasterRead(object):
|
|||||||
ar.araddr = cur_addr
|
ar.araddr = cur_addr
|
||||||
ar.arlen = burst_length-1
|
ar.arlen = burst_length-1
|
||||||
ar.arsize = size
|
ar.arsize = size
|
||||||
ar.arburst = burst
|
ar.arburst = cmd.burst
|
||||||
ar.arlock = lock
|
ar.arlock = cmd.lock
|
||||||
ar.arcache = cache
|
ar.arcache = cmd.cache
|
||||||
ar.arprot = prot
|
ar.arprot = cmd.prot
|
||||||
ar.arqos = qos
|
ar.arqos = cmd.qos
|
||||||
ar.arregion = region
|
ar.arregion = cmd.region
|
||||||
ar.aruser = user
|
ar.aruser = cmd.user
|
||||||
|
|
||||||
await self.ar_channel.drive(ar)
|
await self.ar_channel.drive(ar)
|
||||||
|
|
||||||
self.log.info("Read burst start arid: 0x%x araddr: 0x%08x arlen: %d arsize: %d arprot: %s",
|
self.log.info("Read burst start arid: 0x%x araddr: 0x%08x arlen: %d arsize: %d arprot: %s",
|
||||||
arid, cur_addr, burst_length-1, size, prot)
|
arid, cur_addr, burst_length-1, size, cmd.prot)
|
||||||
|
|
||||||
cur_addr += num_bytes
|
cur_addr += num_bytes
|
||||||
|
|
||||||
self.int_read_resp_command_queue.append((addr, length, size, cycles, prot, burst_list, token))
|
resp_cmd = AxiReadRespCmd(cmd.address, cmd.length, size, cycles, cmd.prot, burst_list, cmd.token)
|
||||||
|
self.int_read_resp_command_queue.append(resp_cmd)
|
||||||
self.int_read_resp_command_sync.set()
|
self.int_read_resp_command_sync.set()
|
||||||
|
|
||||||
async def _process_read_resp(self):
|
async def _process_read_resp(self):
|
||||||
@@ -552,14 +570,14 @@ class AxiMasterRead(object):
|
|||||||
self.int_read_resp_command_sync.clear()
|
self.int_read_resp_command_sync.clear()
|
||||||
await self.int_read_resp_command_sync.wait()
|
await self.int_read_resp_command_sync.wait()
|
||||||
|
|
||||||
addr, length, size, cycles, prot, burst_list, token = self.int_read_resp_command_queue.popleft()
|
cmd = self.int_read_resp_command_queue.popleft()
|
||||||
|
|
||||||
num_bytes = 2**size
|
num_bytes = 2**cmd.size
|
||||||
|
|
||||||
aligned_addr = (addr // num_bytes) * num_bytes
|
aligned_addr = (cmd.address // num_bytes) * num_bytes
|
||||||
word_addr = (addr // self.byte_width) * self.byte_width
|
word_addr = (cmd.address // self.byte_width) * self.byte_width
|
||||||
|
|
||||||
start_offset = addr % self.byte_width
|
start_offset = cmd.address % self.byte_width
|
||||||
|
|
||||||
cycle_offset = aligned_addr - word_addr
|
cycle_offset = aligned_addr - word_addr
|
||||||
data = bytearray()
|
data = bytearray()
|
||||||
@@ -569,7 +587,7 @@ class AxiMasterRead(object):
|
|||||||
|
|
||||||
first = True
|
first = True
|
||||||
|
|
||||||
for rid, burst_length in burst_list:
|
for rid, burst_length in cmd.burst_list:
|
||||||
for k in range(burst_length):
|
for k in range(burst_length):
|
||||||
self.int_read_resp_queue_list.setdefault(rid, deque())
|
self.int_read_resp_queue_list.setdefault(rid, deque())
|
||||||
while True:
|
while True:
|
||||||
@@ -617,15 +635,15 @@ class AxiMasterRead(object):
|
|||||||
|
|
||||||
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", cycle_id, resp)
|
||||||
|
|
||||||
data = data[:length]
|
data = data[:cmd.length]
|
||||||
|
|
||||||
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",
|
||||||
addr, 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((addr, data, resp, user, token))
|
self.read_data_queue.append(AxiReadResp(cmd.address, data, resp, user, cmd.token))
|
||||||
self.read_data_sync.set()
|
self.read_data_sync.set()
|
||||||
if token is not None:
|
if cmd.token is not None:
|
||||||
self.read_data_set.add(token)
|
self.read_data_set.add(cmd.token)
|
||||||
self.in_flight_operations -= 1
|
self.in_flight_operations -= 1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ THE SOFTWARE.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from collections import deque
|
from collections import deque, namedtuple
|
||||||
|
|
||||||
import cocotb
|
import cocotb
|
||||||
from cocotb.triggers import Event
|
from cocotb.triggers import Event
|
||||||
@@ -32,6 +32,16 @@ from .version import __version__
|
|||||||
from .constants import AxiProt, AxiResp
|
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
|
||||||
|
AxiLiteWriteCmd = namedtuple("AxiLiteWriteCmd", ["address", "data", "prot", "token"])
|
||||||
|
AxiLiteWriteRespCmd = namedtuple("AxiLiteWriteRespCmd", ["address", "length", "cycles", "prot", "token"])
|
||||||
|
AxiLiteWriteResp = namedtuple("AxiLiteWriteResp", ["address", "length", "resp", "token"])
|
||||||
|
|
||||||
|
# AXI lite master read
|
||||||
|
AxiLiteReadCmd = namedtuple("AxiLiteReadCmd", ["address", "length", "prot", "token"])
|
||||||
|
AxiLiteReadRespCmd = namedtuple("AxiLiteReadRespCmd", ["address", "length", "cycles", "prot", "token"])
|
||||||
|
AxiLiteReadResp = namedtuple("AxiLiteReadResp", ["address", "data", "resp", "token"])
|
||||||
|
|
||||||
|
|
||||||
class AxiLiteMasterWrite(object):
|
class AxiLiteMasterWrite(object):
|
||||||
def __init__(self, entity, name, clock, reset=None):
|
def __init__(self, entity, name, clock, reset=None):
|
||||||
@@ -85,7 +95,7 @@ class AxiLiteMasterWrite(object):
|
|||||||
|
|
||||||
self.in_flight_operations += 1
|
self.in_flight_operations += 1
|
||||||
|
|
||||||
self.write_command_queue.append((address, data, prot, token))
|
self.write_command_queue.append(AxiLiteWriteCmd(address, bytearray(data), prot, token))
|
||||||
self.write_command_sync.set()
|
self.write_command_sync.set()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
@@ -112,17 +122,17 @@ class AxiLiteMasterWrite(object):
|
|||||||
if token is not None:
|
if token is not None:
|
||||||
if token in self.write_resp_set:
|
if token in self.write_resp_set:
|
||||||
for resp in self.write_resp_queue:
|
for resp in self.write_resp_queue:
|
||||||
if resp[-1] == token:
|
if resp.token == token:
|
||||||
self.write_resp_queue.remove(resp)
|
self.write_resp_queue.remove(resp)
|
||||||
self.active_tokens.remove(resp[-1])
|
self.active_tokens.remove(resp.token)
|
||||||
self.write_resp_set.remove(resp[-1])
|
self.write_resp_set.remove(resp.token)
|
||||||
return resp
|
return resp
|
||||||
return None
|
return None
|
||||||
if self.write_resp_queue:
|
if self.write_resp_queue:
|
||||||
resp = self.write_resp_queue.popleft()
|
resp = self.write_resp_queue.popleft()
|
||||||
if resp[-1] is not None:
|
if resp.token is not None:
|
||||||
self.active_tokens.remove(resp[-1])
|
self.active_tokens.remove(resp.token)
|
||||||
self.write_resp_set.remove(resp[-1])
|
self.write_resp_set.remove(resp.token)
|
||||||
return resp
|
return resp
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -130,7 +140,7 @@ class AxiLiteMasterWrite(object):
|
|||||||
token = object()
|
token = object()
|
||||||
self.init_write(address, data, prot, token)
|
self.init_write(address, data, prot, token)
|
||||||
await self.wait_for_token(token)
|
await self.wait_for_token(token)
|
||||||
return self.get_write_resp(token)[1:2]
|
return self.get_write_resp(token)
|
||||||
|
|
||||||
async def write_words(self, address, data, ws=2, prot=AxiProt.NONSECURE):
|
async def write_words(self, address, data, ws=2, prot=AxiProt.NONSECURE):
|
||||||
words = data
|
words = data
|
||||||
@@ -163,25 +173,26 @@ class AxiLiteMasterWrite(object):
|
|||||||
self.write_command_sync.clear()
|
self.write_command_sync.clear()
|
||||||
await self.write_command_sync.wait()
|
await self.write_command_sync.wait()
|
||||||
|
|
||||||
address, data, prot, token = self.write_command_queue.popleft()
|
cmd = self.write_command_queue.popleft()
|
||||||
|
|
||||||
word_addr = (address // self.byte_width) * self.byte_width
|
word_addr = (cmd.address // self.byte_width) * self.byte_width
|
||||||
|
|
||||||
start_offset = address % self.byte_width
|
start_offset = cmd.address % self.byte_width
|
||||||
end_offset = ((address + len(data) - 1) % self.byte_width) + 1
|
end_offset = ((cmd.address + len(cmd.data) - 1) % self.byte_width) + 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_width - end_offset)
|
||||||
|
|
||||||
cycles = (len(data) + (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
|
||||||
|
|
||||||
self.int_write_resp_command_queue.append((address, len(data), cycles, prot, token))
|
resp_cmd = AxiLiteWriteRespCmd(cmd.address, len(cmd.data), cycles, cmd.prot, cmd.token)
|
||||||
|
self.int_write_resp_command_queue.append(resp_cmd)
|
||||||
self.int_write_resp_command_sync.set()
|
self.int_write_resp_command_sync.set()
|
||||||
|
|
||||||
offset = 0
|
offset = 0
|
||||||
|
|
||||||
self.log.info("Write start addr: 0x%08x prot: %s data: %s",
|
self.log.info("Write start addr: 0x%08x prot: %s data: %s",
|
||||||
address, prot, ' '.join((f'{c:02x}' for c in data)))
|
cmd.address, cmd.prot, ' '.join((f'{c:02x}' for c in cmd.data)))
|
||||||
|
|
||||||
for k in range(cycles):
|
for k in range(cycles):
|
||||||
start = 0
|
start = 0
|
||||||
@@ -197,12 +208,12 @@ class AxiLiteMasterWrite(object):
|
|||||||
|
|
||||||
val = 0
|
val = 0
|
||||||
for j in range(start, stop):
|
for j in range(start, stop):
|
||||||
val |= bytearray(data)[offset] << j*8
|
val |= cmd.data[offset] << j*8
|
||||||
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_width
|
||||||
aw.awprot = prot
|
aw.awprot = cmd.prot
|
||||||
|
|
||||||
w = self.w_channel._transaction_obj()
|
w = self.w_channel._transaction_obj()
|
||||||
w.wdata = val
|
w.wdata = val
|
||||||
@@ -217,11 +228,11 @@ class AxiLiteMasterWrite(object):
|
|||||||
self.int_write_resp_command_sync.clear()
|
self.int_write_resp_command_sync.clear()
|
||||||
await self.int_write_resp_command_sync.wait()
|
await self.int_write_resp_command_sync.wait()
|
||||||
|
|
||||||
addr, length, cycles, prot, token = self.int_write_resp_command_queue.popleft()
|
cmd = self.int_write_resp_command_queue.popleft()
|
||||||
|
|
||||||
resp = AxiResp.OKAY
|
resp = AxiResp.OKAY
|
||||||
|
|
||||||
for k in range(cycles):
|
for k in range(cmd.cycles):
|
||||||
await self.b_channel.wait()
|
await self.b_channel.wait()
|
||||||
b = self.b_channel.recv()
|
b = self.b_channel.recv()
|
||||||
|
|
||||||
@@ -231,12 +242,12 @@ class AxiLiteMasterWrite(object):
|
|||||||
resp = cycle_resp
|
resp = cycle_resp
|
||||||
|
|
||||||
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",
|
||||||
addr, prot, resp, length)
|
cmd.address, cmd.prot, resp, cmd.length)
|
||||||
|
|
||||||
self.write_resp_queue.append((addr, length, resp, token))
|
self.write_resp_queue.append(AxiLiteWriteResp(cmd.address, cmd.length, resp, cmd.token))
|
||||||
self.write_resp_sync.set()
|
self.write_resp_sync.set()
|
||||||
if token is not None:
|
if cmd.token is not None:
|
||||||
self.write_resp_set.add(token)
|
self.write_resp_set.add(cmd.token)
|
||||||
self.in_flight_operations -= 1
|
self.in_flight_operations -= 1
|
||||||
|
|
||||||
|
|
||||||
@@ -289,7 +300,7 @@ class AxiLiteMasterRead(object):
|
|||||||
|
|
||||||
self.in_flight_operations += 1
|
self.in_flight_operations += 1
|
||||||
|
|
||||||
self.read_command_queue.append((address, length, prot, token))
|
self.read_command_queue.append(AxiLiteReadCmd(address, length, prot, token))
|
||||||
self.read_command_sync.set()
|
self.read_command_sync.set()
|
||||||
|
|
||||||
def idle(self):
|
def idle(self):
|
||||||
@@ -316,17 +327,17 @@ class AxiLiteMasterRead(object):
|
|||||||
if token is not None:
|
if token is not None:
|
||||||
if token in self.read_data_set:
|
if token in self.read_data_set:
|
||||||
for resp in self.read_data_queue:
|
for resp in self.read_data_queue:
|
||||||
if resp[-1] == token:
|
if resp.token == token:
|
||||||
self.read_data_queue.remove(resp)
|
self.read_data_queue.remove(resp)
|
||||||
self.active_tokens.remove(resp[-1])
|
self.active_tokens.remove(resp.token)
|
||||||
self.read_data_set.remove(resp[-1])
|
self.read_data_set.remove(resp.token)
|
||||||
return resp
|
return resp
|
||||||
return None
|
return None
|
||||||
if self.read_data_queue:
|
if self.read_data_queue:
|
||||||
resp = self.read_data_queue.popleft()
|
resp = self.read_data_queue.popleft()
|
||||||
if resp[-1] is not None:
|
if resp.token is not None:
|
||||||
self.active_tokens.remove(resp[-1])
|
self.active_tokens.remove(resp.token)
|
||||||
self.read_data_set.remove(resp[-1])
|
self.read_data_set.remove(resp.token)
|
||||||
return resp
|
return resp
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -334,13 +345,13 @@ class AxiLiteMasterRead(object):
|
|||||||
token = object()
|
token = object()
|
||||||
self.init_read(address, length, prot, token)
|
self.init_read(address, length, prot, token)
|
||||||
await self.wait_for_token(token)
|
await self.wait_for_token(token)
|
||||||
return self.get_read_data(token)[1:2]
|
return self.get_read_data(token)
|
||||||
|
|
||||||
async def read_words(self, address, count, ws=2, prot=AxiProt.NONSECURE):
|
async def read_words(self, address, count, ws=2, prot=AxiProt.NONSECURE):
|
||||||
data = await self.read(address, count*ws, prot)
|
data = await self.read(address, count*ws, prot)
|
||||||
words = []
|
words = []
|
||||||
for k in range(count):
|
for k in range(count):
|
||||||
words.append(int.from_bytes(data[0][ws*k:ws*(k+1)], 'little'))
|
words.append(int.from_bytes(data.data[ws*k:ws*(k+1)], 'little'))
|
||||||
return words
|
return words
|
||||||
|
|
||||||
async def read_dwords(self, address, count, prot=AxiProt.NONSECURE):
|
async def read_dwords(self, address, count, prot=AxiProt.NONSECURE):
|
||||||
@@ -350,7 +361,7 @@ class AxiLiteMasterRead(object):
|
|||||||
return await self.read_words(address, count, 8, prot)
|
return await self.read_words(address, count, 8, prot)
|
||||||
|
|
||||||
async def read_byte(self, address, prot=AxiProt.NONSECURE):
|
async def read_byte(self, address, prot=AxiProt.NONSECURE):
|
||||||
return (await self.read(address, 1, prot))[0]
|
return (await self.read(address, 1, prot)).data[0]
|
||||||
|
|
||||||
async def read_word(self, address, ws=2, prot=AxiProt.NONSECURE):
|
async def read_word(self, address, ws=2, prot=AxiProt.NONSECURE):
|
||||||
return (await self.read_words(address, 1, ws, prot))[0]
|
return (await self.read_words(address, 1, ws, prot))[0]
|
||||||
@@ -367,22 +378,23 @@ class AxiLiteMasterRead(object):
|
|||||||
self.read_command_sync.clear()
|
self.read_command_sync.clear()
|
||||||
await self.read_command_sync.wait()
|
await self.read_command_sync.wait()
|
||||||
|
|
||||||
address, length, prot, token = self.read_command_queue.popleft()
|
cmd = self.read_command_queue.popleft()
|
||||||
|
|
||||||
word_addr = (address // self.byte_width) * self.byte_width
|
word_addr = (cmd.address // self.byte_width) * self.byte_width
|
||||||
|
|
||||||
cycles = (length + self.byte_width-1 + (address % self.byte_width)) // self.byte_width
|
cycles = (cmd.length + self.byte_width-1 + (cmd.address % self.byte_width)) // self.byte_width
|
||||||
|
|
||||||
self.int_read_resp_command_queue.append((address, length, cycles, prot, token))
|
resp_cmd = AxiLiteReadRespCmd(cmd.address, cmd.length, cycles, cmd.prot, cmd.token)
|
||||||
|
self.int_read_resp_command_queue.append(resp_cmd)
|
||||||
self.int_read_resp_command_sync.set()
|
self.int_read_resp_command_sync.set()
|
||||||
|
|
||||||
self.log.info("Read start addr: 0x%08x prot: %s length: %d",
|
self.log.info("Read start addr: 0x%08x prot: %s length: %d",
|
||||||
address, prot, length)
|
cmd.address, cmd.prot, cmd.length)
|
||||||
|
|
||||||
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_width
|
||||||
ar.arprot = prot
|
ar.arprot = cmd.prot
|
||||||
|
|
||||||
await self.ar_channel.drive(ar)
|
await self.ar_channel.drive(ar)
|
||||||
|
|
||||||
@@ -392,16 +404,16 @@ class AxiLiteMasterRead(object):
|
|||||||
self.int_read_resp_command_sync.clear()
|
self.int_read_resp_command_sync.clear()
|
||||||
await self.int_read_resp_command_sync.wait()
|
await self.int_read_resp_command_sync.wait()
|
||||||
|
|
||||||
addr, length, cycles, prot, token = self.int_read_resp_command_queue.popleft()
|
cmd = self.int_read_resp_command_queue.popleft()
|
||||||
|
|
||||||
start_offset = addr % self.byte_width
|
start_offset = cmd.address % self.byte_width
|
||||||
end_offset = ((addr + length - 1) % self.byte_width) + 1
|
end_offset = ((cmd.address + cmd.length - 1) % self.byte_width) + 1
|
||||||
|
|
||||||
data = bytearray()
|
data = bytearray()
|
||||||
|
|
||||||
resp = AxiResp.OKAY
|
resp = AxiResp.OKAY
|
||||||
|
|
||||||
for k in range(cycles):
|
for k in range(cmd.cycles):
|
||||||
await self.r_channel.wait()
|
await self.r_channel.wait()
|
||||||
r = self.r_channel.recv()
|
r = self.r_channel.recv()
|
||||||
|
|
||||||
@@ -416,19 +428,19 @@ class AxiLiteMasterRead(object):
|
|||||||
|
|
||||||
if k == 0:
|
if k == 0:
|
||||||
start = start_offset
|
start = start_offset
|
||||||
if k == cycles-1:
|
if k == cmd.cycles-1:
|
||||||
stop = end_offset
|
stop = end_offset
|
||||||
|
|
||||||
for j in range(start, stop):
|
for j in range(start, stop):
|
||||||
data.extend(bytearray([(cycle_data >> j*8) & 0xff]))
|
data.extend(bytearray([(cycle_data >> j*8) & 0xff]))
|
||||||
|
|
||||||
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",
|
||||||
addr, 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((addr, data, resp, token))
|
self.read_data_queue.append(AxiLiteReadResp(cmd.address, data, resp, cmd.token))
|
||||||
self.read_data_sync.set()
|
self.read_data_sync.set()
|
||||||
if token is not None:
|
if cmd.token is not None:
|
||||||
self.read_data_set.add(token)
|
self.read_data_set.add(cmd.token)
|
||||||
self.in_flight_operations -= 1
|
self.in_flight_operations -= 1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ async def run_test_read(dut, idle_inserter=None, backpressure_inserter=None, siz
|
|||||||
|
|
||||||
data = await tb.axi_master.read(addr, length, size=size)
|
data = await tb.axi_master.read(addr, length, size=size)
|
||||||
|
|
||||||
assert data[0] == test_data
|
assert data.data == test_data
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
@@ -169,7 +169,7 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
|
|||||||
await Timer(random.randint(1, 100), 'ns')
|
await Timer(random.randint(1, 100), 'ns')
|
||||||
|
|
||||||
data = await master.read(addr, length)
|
data = await master.read(addr, length)
|
||||||
assert data[0] == test_data
|
assert data.data == test_data
|
||||||
|
|
||||||
workers = []
|
workers = []
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inse
|
|||||||
|
|
||||||
data = await tb.axil_master.read(addr, length)
|
data = await tb.axil_master.read(addr, length)
|
||||||
|
|
||||||
assert data[0] == test_data
|
assert data.data == test_data
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
@@ -158,7 +158,7 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
|
|||||||
await Timer(random.randint(1, 100), 'ns')
|
await Timer(random.randint(1, 100), 'ns')
|
||||||
|
|
||||||
data = await master.read(addr, length)
|
data = await master.read(addr, length)
|
||||||
assert data[0] == test_data
|
assert data.data == test_data
|
||||||
|
|
||||||
workers = []
|
workers = []
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user