Skip to content

Commit

Permalink
AvalonMM: Initial Support for Burst read/write transactions
Browse files Browse the repository at this point in the history
Incorporates suggestions from Colin.

Co-authored-by: Colin Marquardt <[email protected]>
  • Loading branch information
mawillia and cmarqu committed Apr 2, 2022
1 parent ac55515 commit b883c1c
Showing 1 changed file with 63 additions and 22 deletions.
85 changes: 63 additions & 22 deletions src/cocotb_bus/drivers/avalon.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class AvalonMM(BusDriver):
_signals = ["address"]
_optional_signals = ["readdata", "read", "write", "waitrequest",
"writedata", "readdatavalid", "byteenable",
"cs"]
"cs", "burstcount"]

def __init__(self, entity, name, clock, **kwargs):
BusDriver.__init__(self, entity, name, clock, **kwargs)
Expand All @@ -62,6 +62,9 @@ def __init__(self, entity, name, clock, **kwargs):
if hasattr(self.bus, "cs"):
self.bus.cs.setimmediatevalue(0)

if hasattr(self.bus, "burstcount"):
self.bus.burstcount.setimmediatevalue(1)

v = self.bus.address.value
v.binstr = "x" * len(self.bus.address)
self.bus.address.setimmediatevalue(v)
Expand All @@ -84,7 +87,7 @@ def __len__(self):
return 2**len(self.bus.address)

@coroutine
async def read(self, address: int, sync: bool = True) -> BinaryValue:
async def read(self, address: int, sync: bool = True, burstcount : int = 1) -> BinaryValue:
"""Issue a request to the bus and block until this comes back.
Simulation time still progresses
Expand All @@ -94,9 +97,10 @@ async def read(self, address: int, sync: bool = True) -> BinaryValue:
address: The address to read from.
sync: Wait for rising edge on clock initially.
Defaults to True.
burstcount: The number of words to read (default 1).
Returns:
The read data value.
The read data value, or a list of length *burstcount* of read data values.
Raises:
:any:`TestError`: If master is write-only.
Expand All @@ -105,6 +109,10 @@ async def read(self, address: int, sync: bool = True) -> BinaryValue:
self.log.error("Cannot read - have no read signal")
raise TestError("Attempt to read on a write-only AvalonMaster")

if burstcount > 1 and (not hasattr(self.bus, "burstcount") or not hasattr(self.bus, "readdatavalid")):
self.log.error("Burst requested but bus does not have interface signals defined")
raise TestError("Attempted burst read on non-compliant AvalonMaster Bus")

await self._acquire_lock()

# Apply values for next clock edge
Expand All @@ -116,6 +124,8 @@ async def read(self, address: int, sync: bool = True) -> BinaryValue:
self.bus.byteenable.value = int("1"*len(self.bus.byteenable), 2)
if hasattr(self.bus, "cs"):
self.bus.cs.value = 1
if hasattr(self.bus, "burstcount"):
self.bus.burstcount.value = burstcount

# Wait for waitrequest to be low
if hasattr(self.bus, "waitrequest"):
Expand All @@ -132,32 +142,42 @@ async def read(self, address: int, sync: bool = True) -> BinaryValue:
v.binstr = "x" * len(self.bus.address)
self.bus.address.value = v

if hasattr(self.bus, "readdatavalid"):
while True:
await ReadOnly()
if int(self.bus.readdatavalid):
break
await RisingEdge(self.clock)
if burstcount > 1:
data = []
else:
# Assume readLatency = 1 if no readdatavalid
# FIXME need to configure this,
# should take a dictionary of Avalon properties.
await ReadOnly()
data = self.bus.readdata.value

# Get the data
data = self.bus.readdata.value
for burst_index in range(burstcount):
if hasattr(self.bus, "readdatavalid"):
while True:
await ReadOnly()
if int(self.bus.readdatavalid):
break
await RisingEdge(self.clock)
else:
# Assume readLatency = 1 if no readdatavalid
# FIXME need to configure this,
# should take a dictionary of Avalon properties.
await ReadOnly()

# Get the data
if burstcount == 1:
data = self.bus.readdata.value
else:
data.append(self.bus.readdata.value)
await RisingEdge(self.clock)

self._release_lock()
return data

@coroutine
async def write(self, address: int, value: int) -> None:
async def write(self, address: int, value: Union[int, list]) -> None:
"""Issue a write to the given address with the specified
value.
Args:
address: The address to write to.
value: The data value to write.
value: The data value to write, may be a list for a burst write.
Raises:
:any:`TestError`: If master is read-only.
Expand All @@ -166,24 +186,45 @@ async def write(self, address: int, value: int) -> None:
self.log.error("Cannot write - have no write signal")
raise TestError("Attempt to write on a read-only AvalonMaster")

burstcount = 1
if isinstance(value, list):
burstcount = len(value)
# convert to single integer for backwards compatibility
if burstcount == 1:
value = value[0]

self.log.debug("burstcount was %d", burstcount)

if burstcount > 1 and not hasattr(self.bus, "burstcount"):
self.log.error("Burst requested but bus does not have interface signals defined")
raise TestError("Attempted burst write on non-compliant AvalonMaster Bus")

await self._acquire_lock()

# Apply values to bus
await RisingEdge(self.clock)
self.bus.address.value = address
self.bus.writedata.value = value
self.bus.write.value = 1
if hasattr(self.bus, "byteenable"):
self.bus.byteenable.value = int("1"*len(self.bus.byteenable), 2)
if hasattr(self.bus, "cs"):
self.bus.cs.value = 1
if hasattr(self.bus, "burstcount"):
self.bus.burstcount.value = burstcount

# Wait for waitrequest to be low
if hasattr(self.bus, "waitrequest"):
await self._wait_for_nsignal(self.bus.waitrequest)
for burstindex in range(burstcount):
if burstcount == 1:
self.bus.writedata.value = value
else:
self.bus.writedata.value = value[burstindex]

# Wait for waitrequest to be low
if hasattr(self.bus, "waitrequest"):
await self._wait_for_nsignal(self.bus.waitrequest)

await RisingEdge(self.clock)

# Deassert write
await RisingEdge(self.clock)
self.bus.write.value = 0
if hasattr(self.bus, "byteenable"):
self.bus.byteenable.value = 0
Expand Down

0 comments on commit b883c1c

Please sign in to comment.