Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AvalonMM: Initial Support for Burst read/write transactions #44

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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