Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3a7652362 | ||
|
|
a84ce5447d | ||
|
|
1c03ec4697 | ||
|
|
824eba793d | ||
|
|
a0aad34698 | ||
|
|
ede6270ed7 | ||
|
|
cd1a8b47a5 | ||
|
|
be6d490adb | ||
|
|
39686b849a | ||
|
|
706051cb89 | ||
|
|
3e4f8d7e92 | ||
|
|
afae9e69ff | ||
|
|
035c1ba803 | ||
|
|
873bb1a034 | ||
|
|
2d70e5cbe5 | ||
|
|
35d9742ae8 | ||
|
|
0f20e2e9bf |
6
.github/workflows/regression-tests.yml
vendored
6
.github/workflows/regression-tests.yml
vendored
@@ -9,13 +9,13 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
|
python-version: ["3.7", "3.8", "3.9", "3.10"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ class AxiMasterWrite(Region, Reset):
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
cur_addr = aligned_addr
|
cur_addr = cmd.address
|
||||||
offset = 0
|
offset = 0
|
||||||
cycle_offset = aligned_addr-word_addr
|
cycle_offset = aligned_addr-word_addr
|
||||||
n = 0
|
n = 0
|
||||||
@@ -563,7 +563,10 @@ class AxiMasterWrite(Region, Reset):
|
|||||||
|
|
||||||
await self.w_channel.send(w)
|
await self.w_channel.send(w)
|
||||||
|
|
||||||
cur_addr += num_bytes
|
if k == 0:
|
||||||
|
cur_addr = aligned_addr + num_bytes
|
||||||
|
else:
|
||||||
|
cur_addr += num_bytes
|
||||||
cycle_offset = (cycle_offset + num_bytes) % self.byte_lanes
|
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)
|
||||||
@@ -577,8 +580,7 @@ class AxiMasterWrite(Region, Reset):
|
|||||||
|
|
||||||
bid = int(getattr(b, 'bid', 0))
|
bid = int(getattr(b, 'bid', 0))
|
||||||
|
|
||||||
if self.active_id[bid] <= 0:
|
assert self.active_id[bid] > 0, "unexpected burst ID"
|
||||||
raise Exception(f"Unexpected burst ID {bid}")
|
|
||||||
|
|
||||||
self.tag_context_manager.put_resp(bid, b)
|
self.tag_context_manager.put_resp(bid, b)
|
||||||
|
|
||||||
@@ -591,7 +593,7 @@ class AxiMasterWrite(Region, Reset):
|
|||||||
for burst_length in cmd.burst_list:
|
for burst_length in cmd.burst_list:
|
||||||
b = await context.get_resp()
|
b = await context.get_resp()
|
||||||
|
|
||||||
burst_resp = AxiResp(getattr(b, 'bresp', AxiResp.OKAY))
|
burst_resp = AxiResp(int(getattr(b, 'bresp', AxiResp.OKAY)))
|
||||||
burst_user = int(getattr(b, 'buser', 0))
|
burst_user = int(getattr(b, 'buser', 0))
|
||||||
|
|
||||||
if burst_resp != AxiResp.OKAY:
|
if burst_resp != AxiResp.OKAY:
|
||||||
@@ -600,8 +602,7 @@ class AxiMasterWrite(Region, Reset):
|
|||||||
if burst_user is not None:
|
if burst_user is not None:
|
||||||
user.append(burst_user)
|
user.append(burst_user)
|
||||||
|
|
||||||
if self.active_id[bid] <= 0:
|
assert self.active_id[bid] > 0, "unexpected burst ID"
|
||||||
raise Exception(f"Unexpected burst ID {bid}")
|
|
||||||
|
|
||||||
self.active_id[bid] -= 1
|
self.active_id[bid] -= 1
|
||||||
|
|
||||||
@@ -872,7 +873,7 @@ class AxiMasterRead(Region, Reset):
|
|||||||
|
|
||||||
burst_list = []
|
burst_list = []
|
||||||
|
|
||||||
cur_addr = aligned_addr
|
cur_addr = cmd.address
|
||||||
n = 0
|
n = 0
|
||||||
|
|
||||||
burst_length = 0
|
burst_length = 0
|
||||||
@@ -917,7 +918,10 @@ class AxiMasterRead(Region, Reset):
|
|||||||
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, cmd.size, cmd.prot)
|
arid, cur_addr, burst_length-1, cmd.size, cmd.prot)
|
||||||
|
|
||||||
cur_addr += num_bytes
|
if k == 0:
|
||||||
|
cur_addr = aligned_addr + num_bytes
|
||||||
|
else:
|
||||||
|
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)
|
||||||
self.tag_context_manager.start_cmd(arid, resp_cmd)
|
self.tag_context_manager.start_cmd(arid, resp_cmd)
|
||||||
@@ -925,27 +929,14 @@ class AxiMasterRead(Region, Reset):
|
|||||||
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(getattr(r, 'rid', 0))
|
rid = int(getattr(r, 'rid', 0))
|
||||||
|
|
||||||
if cur_rid is not None and cur_rid != rid:
|
assert self.active_id[rid] > 0, "unexpected burst ID"
|
||||||
raise Exception(f"ID not constant within burst (expected {cur_rid}, got {rid})")
|
|
||||||
|
|
||||||
if self.active_id[rid] <= 0:
|
self.tag_context_manager.put_resp(rid, r)
|
||||||
raise Exception(f"Unexpected burst ID {rid}")
|
|
||||||
|
|
||||||
burst.append(r)
|
|
||||||
cur_rid = rid
|
|
||||||
|
|
||||||
if int(r.rlast):
|
|
||||||
self.tag_context_manager.put_resp(rid, burst)
|
|
||||||
burst = []
|
|
||||||
cur_rid = None
|
|
||||||
|
|
||||||
async def _process_read_resp_id(self, context, cmd):
|
async def _process_read_resp_id(self, context, cmd):
|
||||||
rid = context.current_tag
|
rid = context.current_tag
|
||||||
@@ -966,14 +957,18 @@ class AxiMasterRead(Region, Reset):
|
|||||||
first = True
|
first = True
|
||||||
|
|
||||||
for burst_length in cmd.burst_list:
|
for burst_length in cmd.burst_list:
|
||||||
burst = await context.get_resp()
|
for k in range(burst_length):
|
||||||
|
r = await context.get_resp()
|
||||||
|
|
||||||
if len(burst) != burst_length:
|
assert self.active_id[rid] > 0, "unexpected burst ID"
|
||||||
raise Exception(f"Burst length incorrect (ID {rid}, expected {burst_length}, got {len(burst)}")
|
|
||||||
|
if k == burst_length-1:
|
||||||
|
assert int(r.rlast), "missing rlast at end of burst"
|
||||||
|
else:
|
||||||
|
assert not int(r.rlast), "unexpected rlast within burst"
|
||||||
|
|
||||||
for r in burst:
|
|
||||||
cycle_data = int(r.rdata)
|
cycle_data = int(r.rdata)
|
||||||
cycle_resp = AxiResp(getattr(r, "rresp", AxiResp.OKAY))
|
cycle_resp = AxiResp(int(getattr(r, "rresp", AxiResp.OKAY)))
|
||||||
cycle_user = int(getattr(r, "ruser", 0))
|
cycle_user = int(getattr(r, "ruser", 0))
|
||||||
|
|
||||||
if cycle_resp != AxiResp.OKAY:
|
if cycle_resp != AxiResp.OKAY:
|
||||||
|
|||||||
@@ -115,8 +115,8 @@ class AxiSlaveWrite(Reset):
|
|||||||
addr = int(aw.awaddr)
|
addr = int(aw.awaddr)
|
||||||
length = int(getattr(aw, 'awlen', 0))
|
length = int(getattr(aw, 'awlen', 0))
|
||||||
size = int(getattr(aw, 'awsize', self.max_burst_size))
|
size = int(getattr(aw, 'awsize', self.max_burst_size))
|
||||||
burst = AxiBurstType(getattr(aw, 'awburst', AxiBurstType.INCR))
|
burst = AxiBurstType(int(getattr(aw, 'awburst', AxiBurstType.INCR)))
|
||||||
prot = AxiProt(getattr(aw, 'awprot', AxiProt.NONSECURE))
|
prot = AxiProt(int(getattr(aw, 'awprot', AxiProt.NONSECURE)))
|
||||||
|
|
||||||
self.log.info("Write burst awid: 0x%x awaddr: 0x%08x awlen: %d awsize: %d awprot: %s",
|
self.log.info("Write burst awid: 0x%x awaddr: 0x%08x awlen: %d awsize: %d awprot: %s",
|
||||||
awid, addr, length, size, prot)
|
awid, addr, length, size, prot)
|
||||||
@@ -275,8 +275,8 @@ class AxiSlaveRead(Reset):
|
|||||||
addr = int(ar.araddr)
|
addr = int(ar.araddr)
|
||||||
length = int(getattr(ar, 'arlen', 0))
|
length = int(getattr(ar, 'arlen', 0))
|
||||||
size = int(getattr(ar, 'arsize', self.max_burst_size))
|
size = int(getattr(ar, 'arsize', self.max_burst_size))
|
||||||
burst = AxiBurstType(getattr(ar, 'arburst', AxiBurstType.INCR))
|
burst = AxiBurstType(int(getattr(ar, 'arburst', AxiBurstType.INCR)))
|
||||||
prot = AxiProt(getattr(ar, 'arprot', AxiProt.NONSECURE))
|
prot = AxiProt(int(getattr(ar, 'arprot', AxiProt.NONSECURE)))
|
||||||
|
|
||||||
self.log.info("Read burst arid: 0x%x araddr: 0x%08x arlen: %d arsize: %d arprot: %s",
|
self.log.info("Read burst arid: 0x%x araddr: 0x%08x arlen: %d arsize: %d arprot: %s",
|
||||||
arid, addr, length, size, prot)
|
arid, addr, length, size, prot)
|
||||||
|
|||||||
@@ -286,7 +286,10 @@ class AxiLiteMasterWrite(Region, Reset):
|
|||||||
offset += 1
|
offset += 1
|
||||||
|
|
||||||
aw = self.aw_channel._transaction_obj()
|
aw = self.aw_channel._transaction_obj()
|
||||||
aw.awaddr = word_addr + k*self.byte_lanes
|
if k == 0:
|
||||||
|
aw.awaddr = cmd.address
|
||||||
|
else:
|
||||||
|
aw.awaddr = word_addr + k*self.byte_lanes
|
||||||
aw.awprot = cmd.prot
|
aw.awprot = cmd.prot
|
||||||
|
|
||||||
if not self.wstrb_present and strb != self.strb_mask:
|
if not self.wstrb_present and strb != self.strb_mask:
|
||||||
@@ -311,7 +314,7 @@ class AxiLiteMasterWrite(Region, Reset):
|
|||||||
for k in range(cmd.cycles):
|
for k in range(cmd.cycles):
|
||||||
b = await self.b_channel.recv()
|
b = await self.b_channel.recv()
|
||||||
|
|
||||||
cycle_resp = AxiResp(getattr(b, 'bresp', AxiResp.OKAY))
|
cycle_resp = AxiResp(int(getattr(b, 'bresp', AxiResp.OKAY)))
|
||||||
|
|
||||||
if cycle_resp != AxiResp.OKAY:
|
if cycle_resp != AxiResp.OKAY:
|
||||||
resp = cycle_resp
|
resp = cycle_resp
|
||||||
@@ -494,7 +497,10 @@ class AxiLiteMasterRead(Region, 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_lanes
|
if k == 0:
|
||||||
|
ar.araddr = cmd.address
|
||||||
|
else:
|
||||||
|
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)
|
||||||
@@ -517,7 +523,7 @@ class AxiLiteMasterRead(Region, Reset):
|
|||||||
r = await self.r_channel.recv()
|
r = await self.r_channel.recv()
|
||||||
|
|
||||||
cycle_data = int(r.rdata)
|
cycle_data = int(r.rdata)
|
||||||
cycle_resp = AxiResp(getattr(r, 'rresp', AxiResp.OKAY))
|
cycle_resp = AxiResp(int(getattr(r, 'rresp', AxiResp.OKAY)))
|
||||||
|
|
||||||
if cycle_resp != AxiResp.OKAY:
|
if cycle_resp != AxiResp.OKAY:
|
||||||
resp = cycle_resp
|
resp = cycle_resp
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ class AxiLiteSlaveWrite(Reset):
|
|||||||
self.wstrb_present = hasattr(self.bus.w, "wstrb")
|
self.wstrb_present = hasattr(self.bus.w, "wstrb")
|
||||||
|
|
||||||
self.log.info("AXI lite slave model configuration:")
|
self.log.info("AXI lite slave model configuration:")
|
||||||
self.log.info(" Memory size: %d bytes", len(self.mem))
|
|
||||||
self.log.info(" Address width: %d bits", self.address_width)
|
self.log.info(" Address width: %d bits", self.address_width)
|
||||||
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)
|
||||||
@@ -107,7 +106,7 @@ class AxiLiteSlaveWrite(Reset):
|
|||||||
aw = await self.aw_channel.recv()
|
aw = await self.aw_channel.recv()
|
||||||
|
|
||||||
addr = (int(aw.awaddr) // self.byte_lanes) * self.byte_lanes
|
addr = (int(aw.awaddr) // self.byte_lanes) * self.byte_lanes
|
||||||
prot = AxiProt(getattr(aw, 'awprot', AxiProt.NONSECURE))
|
prot = AxiProt(int(getattr(aw, 'awprot', AxiProt.NONSECURE)))
|
||||||
|
|
||||||
w = await self.w_channel.recv()
|
w = await self.w_channel.recv()
|
||||||
|
|
||||||
@@ -182,7 +181,6 @@ class AxiLiteSlaveRead(Reset):
|
|||||||
self.byte_lanes = self.width // self.byte_size
|
self.byte_lanes = self.width // self.byte_size
|
||||||
|
|
||||||
self.log.info("AXI lite slave model configuration:")
|
self.log.info("AXI lite slave model configuration:")
|
||||||
self.log.info(" Memory size: %d bytes", len(self.mem))
|
|
||||||
self.log.info(" Address width: %d bits", self.address_width)
|
self.log.info(" Address width: %d bits", self.address_width)
|
||||||
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)
|
||||||
@@ -223,7 +221,7 @@ class AxiLiteSlaveRead(Reset):
|
|||||||
ar = await self.ar_channel.recv()
|
ar = await self.ar_channel.recv()
|
||||||
|
|
||||||
addr = (int(ar.araddr) // self.byte_lanes) * self.byte_lanes
|
addr = (int(ar.araddr) // self.byte_lanes) * self.byte_lanes
|
||||||
prot = AxiProt(getattr(ar, 'arprot', AxiProt.NONSECURE))
|
prot = AxiProt(int(getattr(ar, 'arprot', AxiProt.NONSECURE)))
|
||||||
|
|
||||||
r = self.r_channel._transaction_obj()
|
r = self.r_channel._transaction_obj()
|
||||||
r.rresp = AxiResp.OKAY
|
r.rresp = AxiResp.OKAY
|
||||||
@@ -251,5 +249,5 @@ class AxiLiteSlave:
|
|||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.write_if = AxiLiteSlaveWrite(target, bus.write, clock, reset, reset_active_level)
|
self.write_if = AxiLiteSlaveWrite(bus.write, clock, reset, target, reset_active_level)
|
||||||
self.read_if = AxiLiteSlaveRead(target, bus.read, clock, reset, reset_active_level)
|
self.read_if = AxiLiteSlaveRead(bus.read, clock, reset, target, reset_active_level)
|
||||||
|
|||||||
@@ -282,12 +282,13 @@ class AxiStreamBase(Reset):
|
|||||||
self.idle_event = Event()
|
self.idle_event = Event()
|
||||||
self.idle_event.set()
|
self.idle_event.set()
|
||||||
self.active_event = Event()
|
self.active_event = Event()
|
||||||
|
self.wake_event = Event()
|
||||||
|
|
||||||
self.queue_occupancy_bytes = 0
|
self.queue_occupancy_bytes = 0
|
||||||
self.queue_occupancy_frames = 0
|
self.queue_occupancy_frames = 0
|
||||||
|
|
||||||
self.width = len(self.bus.tdata)
|
self.width = len(self.bus.tdata)
|
||||||
self.byte_lanes = 1
|
self.byte_lanes = self.width // 8
|
||||||
|
|
||||||
if self._valid_init is not None and hasattr(self.bus, "tvalid"):
|
if self._valid_init is not None and hasattr(self.bus, "tvalid"):
|
||||||
self.bus.tvalid.setimmediatevalue(self._valid_init)
|
self.bus.tvalid.setimmediatevalue(self._valid_init)
|
||||||
@@ -376,10 +377,23 @@ class AxiStreamPause:
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.pause = False
|
self._pause = False
|
||||||
self._pause_generator = None
|
self._pause_generator = None
|
||||||
self._pause_cr = None
|
self._pause_cr = None
|
||||||
|
|
||||||
|
def _pause_update(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pause(self):
|
||||||
|
return self._pause
|
||||||
|
|
||||||
|
@pause.setter
|
||||||
|
def pause(self, val):
|
||||||
|
if self._pause != val:
|
||||||
|
self._pause_update(val)
|
||||||
|
self._pause = val
|
||||||
|
|
||||||
def set_pause_generator(self, generator=None):
|
def set_pause_generator(self, generator=None):
|
||||||
if self._pause_cr is not None:
|
if self._pause_cr is not None:
|
||||||
self._pause_cr.kill()
|
self._pause_cr.kill()
|
||||||
@@ -425,6 +439,7 @@ class AxiStreamSource(AxiStreamBase, AxiStreamPause):
|
|||||||
frame = AxiStreamFrame(frame)
|
frame = AxiStreamFrame(frame)
|
||||||
await self.queue.put(frame)
|
await self.queue.put(frame)
|
||||||
self.idle_event.clear()
|
self.idle_event.clear()
|
||||||
|
self.active_event.set()
|
||||||
self.queue_occupancy_bytes += len(frame)
|
self.queue_occupancy_bytes += len(frame)
|
||||||
self.queue_occupancy_frames += 1
|
self.queue_occupancy_frames += 1
|
||||||
|
|
||||||
@@ -434,6 +449,7 @@ class AxiStreamSource(AxiStreamBase, AxiStreamPause):
|
|||||||
frame = AxiStreamFrame(frame)
|
frame = AxiStreamFrame(frame)
|
||||||
self.queue.put_nowait(frame)
|
self.queue.put_nowait(frame)
|
||||||
self.idle_event.clear()
|
self.idle_event.clear()
|
||||||
|
self.active_event.set()
|
||||||
self.queue_occupancy_bytes += len(frame)
|
self.queue_occupancy_bytes += len(frame)
|
||||||
self.queue_occupancy_frames += 1
|
self.queue_occupancy_frames += 1
|
||||||
|
|
||||||
@@ -485,17 +501,25 @@ class AxiStreamSource(AxiStreamBase, AxiStreamPause):
|
|||||||
frame_offset = 0
|
frame_offset = 0
|
||||||
self.active = False
|
self.active = False
|
||||||
|
|
||||||
|
has_tready = hasattr(self.bus, "tready")
|
||||||
|
has_tvalid = hasattr(self.bus, "tvalid")
|
||||||
|
has_tlast = hasattr(self.bus, "tlast")
|
||||||
|
has_tkeep = hasattr(self.bus, "tkeep")
|
||||||
|
has_tid = hasattr(self.bus, "tid")
|
||||||
|
has_tdest = hasattr(self.bus, "tdest")
|
||||||
|
has_tuser = hasattr(self.bus, "tuser")
|
||||||
|
|
||||||
clock_edge_event = RisingEdge(self.clock)
|
clock_edge_event = RisingEdge(self.clock)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
# read handshake signals
|
# read handshake signals
|
||||||
tready_sample = (not hasattr(self.bus, "tready")) or self.bus.tready.value
|
tready_sample = (not has_tready) or self.bus.tready.value
|
||||||
tvalid_sample = (not hasattr(self.bus, "tvalid")) or self.bus.tvalid.value
|
tvalid_sample = (not has_tvalid) or self.bus.tvalid.value
|
||||||
|
|
||||||
if (tready_sample and tvalid_sample) or not tvalid_sample:
|
if (tready_sample and tvalid_sample) or not tvalid_sample:
|
||||||
if frame is None and not self.queue.empty():
|
if not frame and not self.queue.empty():
|
||||||
frame = self.queue.get_nowait()
|
frame = self.queue.get_nowait()
|
||||||
self.dequeue_event.set()
|
self.dequeue_event.set()
|
||||||
self.queue_occupancy_bytes -= len(frame)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
@@ -533,26 +557,29 @@ class AxiStreamSource(AxiStreamBase, AxiStreamPause):
|
|||||||
break
|
break
|
||||||
|
|
||||||
self.bus.tdata.value = tdata_val
|
self.bus.tdata.value = tdata_val
|
||||||
if hasattr(self.bus, "tvalid"):
|
if has_tvalid:
|
||||||
self.bus.tvalid.value = 1
|
self.bus.tvalid.value = 1
|
||||||
if hasattr(self.bus, "tlast"):
|
if has_tlast:
|
||||||
self.bus.tlast.value = tlast_val
|
self.bus.tlast.value = tlast_val
|
||||||
if hasattr(self.bus, "tkeep"):
|
if has_tkeep:
|
||||||
self.bus.tkeep.value = tkeep_val
|
self.bus.tkeep.value = tkeep_val
|
||||||
if hasattr(self.bus, "tid"):
|
if has_tid:
|
||||||
self.bus.tid.value = tid_val
|
self.bus.tid.value = tid_val
|
||||||
if hasattr(self.bus, "tdest"):
|
if has_tdest:
|
||||||
self.bus.tdest.value = tdest_val
|
self.bus.tdest.value = tdest_val
|
||||||
if hasattr(self.bus, "tuser"):
|
if has_tuser:
|
||||||
self.bus.tuser.value = tuser_val
|
self.bus.tuser.value = tuser_val
|
||||||
else:
|
else:
|
||||||
if hasattr(self.bus, "tvalid"):
|
if has_tvalid:
|
||||||
self.bus.tvalid.value = 0
|
self.bus.tvalid.value = 0
|
||||||
if hasattr(self.bus, "tlast"):
|
if has_tlast:
|
||||||
self.bus.tlast.value = 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()
|
||||||
|
self.active_event.clear()
|
||||||
|
|
||||||
|
await self.active_event.wait()
|
||||||
|
|
||||||
|
|
||||||
class AxiStreamMonitor(AxiStreamBase):
|
class AxiStreamMonitor(AxiStreamBase):
|
||||||
@@ -571,11 +598,20 @@ class AxiStreamMonitor(AxiStreamBase):
|
|||||||
|
|
||||||
self.read_queue = []
|
self.read_queue = []
|
||||||
|
|
||||||
|
if hasattr(self.bus, "tvalid"):
|
||||||
|
cocotb.start_soon(self._run_tvalid_monitor())
|
||||||
|
if hasattr(self.bus, "tready"):
|
||||||
|
cocotb.start_soon(self._run_tready_monitor())
|
||||||
|
|
||||||
|
def _dequeue(self, frame):
|
||||||
|
pass
|
||||||
|
|
||||||
def _recv(self, frame, compact=True):
|
def _recv(self, frame, compact=True):
|
||||||
if self.queue.empty():
|
if self.queue.empty():
|
||||||
self.active_event.clear()
|
self.active_event.clear()
|
||||||
self.queue_occupancy_bytes -= len(frame)
|
self.queue_occupancy_bytes -= len(frame)
|
||||||
self.queue_occupancy_frames -= 1
|
self.queue_occupancy_frames -= 1
|
||||||
|
self._dequeue(frame)
|
||||||
if compact:
|
if compact:
|
||||||
frame.compact()
|
frame.compact()
|
||||||
return frame
|
return frame
|
||||||
@@ -615,21 +651,45 @@ class AxiStreamMonitor(AxiStreamBase):
|
|||||||
else:
|
else:
|
||||||
await self.active_event.wait()
|
await self.active_event.wait()
|
||||||
|
|
||||||
|
async def _run_tvalid_monitor(self):
|
||||||
|
event = RisingEdge(self.bus.tvalid)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
await event
|
||||||
|
self.wake_event.set()
|
||||||
|
|
||||||
|
async def _run_tready_monitor(self):
|
||||||
|
event = RisingEdge(self.bus.tready)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
await event
|
||||||
|
self.wake_event.set()
|
||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
frame = None
|
frame = None
|
||||||
self.active = False
|
self.active = False
|
||||||
|
|
||||||
|
has_tready = hasattr(self.bus, "tready")
|
||||||
|
has_tvalid = hasattr(self.bus, "tvalid")
|
||||||
|
has_tlast = hasattr(self.bus, "tlast")
|
||||||
|
has_tkeep = hasattr(self.bus, "tkeep")
|
||||||
|
has_tid = hasattr(self.bus, "tid")
|
||||||
|
has_tdest = hasattr(self.bus, "tdest")
|
||||||
|
has_tuser = hasattr(self.bus, "tuser")
|
||||||
|
|
||||||
clock_edge_event = RisingEdge(self.clock)
|
clock_edge_event = RisingEdge(self.clock)
|
||||||
|
|
||||||
|
wake_event = self.wake_event.wait()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
# read handshake signals
|
# read handshake signals
|
||||||
tready_sample = (not hasattr(self.bus, "tready")) or self.bus.tready.value
|
tready_sample = (not has_tready) or self.bus.tready.value
|
||||||
tvalid_sample = (not hasattr(self.bus, "tvalid")) or self.bus.tvalid.value
|
tvalid_sample = (not has_tvalid) or self.bus.tvalid.value
|
||||||
|
|
||||||
if tready_sample and tvalid_sample:
|
if tready_sample and tvalid_sample:
|
||||||
if frame is None:
|
if not frame:
|
||||||
if self.byte_size == 8:
|
if self.byte_size == 8:
|
||||||
frame = AxiStreamFrame(bytearray(), [], [], [], [])
|
frame = AxiStreamFrame(bytearray(), [], [], [], [])
|
||||||
else:
|
else:
|
||||||
@@ -639,16 +699,16 @@ class AxiStreamMonitor(AxiStreamBase):
|
|||||||
|
|
||||||
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 has_tkeep:
|
||||||
frame.tkeep.append((self.bus.tkeep.value.integer >> offset) & 1)
|
frame.tkeep.append((self.bus.tkeep.value.integer >> offset) & 1)
|
||||||
if hasattr(self.bus, "tid"):
|
if has_tid:
|
||||||
frame.tid.append(self.bus.tid.value.integer)
|
frame.tid.append(self.bus.tid.value.integer)
|
||||||
if hasattr(self.bus, "tdest"):
|
if has_tdest:
|
||||||
frame.tdest.append(self.bus.tdest.value.integer)
|
frame.tdest.append(self.bus.tdest.value.integer)
|
||||||
if hasattr(self.bus, "tuser"):
|
if has_tuser:
|
||||||
frame.tuser.append(self.bus.tuser.value.integer)
|
frame.tuser.append(self.bus.tuser.value.integer)
|
||||||
|
|
||||||
if not hasattr(self.bus, "tlast") or self.bus.tlast.value:
|
if not has_tlast or self.bus.tlast.value:
|
||||||
frame.sim_time_end = get_sim_time()
|
frame.sim_time_end = get_sim_time()
|
||||||
self.log.info("RX frame: %s", frame)
|
self.log.info("RX frame: %s", frame)
|
||||||
|
|
||||||
@@ -662,6 +722,9 @@ class AxiStreamMonitor(AxiStreamBase):
|
|||||||
else:
|
else:
|
||||||
self.active = bool(frame)
|
self.active = bool(frame)
|
||||||
|
|
||||||
|
self.wake_event.clear()
|
||||||
|
await wake_event
|
||||||
|
|
||||||
|
|
||||||
class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
||||||
|
|
||||||
@@ -675,11 +738,11 @@ class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
|||||||
def __init__(self, bus, clock, reset=None, reset_active_level=True,
|
def __init__(self, bus, clock, reset=None, reset_active_level=True,
|
||||||
byte_size=None, byte_lanes=None, *args, **kwargs):
|
byte_size=None, byte_lanes=None, *args, **kwargs):
|
||||||
|
|
||||||
super().__init__(bus, clock, reset, reset_active_level, byte_size, byte_lanes, *args, **kwargs)
|
|
||||||
|
|
||||||
self.queue_occupancy_limit_bytes = -1
|
self.queue_occupancy_limit_bytes = -1
|
||||||
self.queue_occupancy_limit_frames = -1
|
self.queue_occupancy_limit_frames = -1
|
||||||
|
|
||||||
|
super().__init__(bus, clock, reset, reset_active_level, byte_size, byte_lanes, *args, **kwargs)
|
||||||
|
|
||||||
def full(self):
|
def full(self):
|
||||||
if self.queue_occupancy_limit_bytes > 0 and self.queue_occupancy_bytes > self.queue_occupancy_limit_bytes:
|
if self.queue_occupancy_limit_bytes > 0 and self.queue_occupancy_bytes > self.queue_occupancy_limit_bytes:
|
||||||
return True
|
return True
|
||||||
@@ -695,21 +758,39 @@ class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
|||||||
if hasattr(self.bus, "tready"):
|
if hasattr(self.bus, "tready"):
|
||||||
self.bus.tready.value = 0
|
self.bus.tready.value = 0
|
||||||
|
|
||||||
|
def _pause_update(self, val):
|
||||||
|
self.wake_event.set()
|
||||||
|
|
||||||
|
def _dequeue(self, frame):
|
||||||
|
self.wake_event.set()
|
||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
frame = None
|
frame = None
|
||||||
self.active = False
|
self.active = False
|
||||||
|
|
||||||
|
has_tready = hasattr(self.bus, "tready")
|
||||||
|
has_tvalid = hasattr(self.bus, "tvalid")
|
||||||
|
has_tlast = hasattr(self.bus, "tlast")
|
||||||
|
has_tkeep = hasattr(self.bus, "tkeep")
|
||||||
|
has_tid = hasattr(self.bus, "tid")
|
||||||
|
has_tdest = hasattr(self.bus, "tdest")
|
||||||
|
has_tuser = hasattr(self.bus, "tuser")
|
||||||
|
|
||||||
clock_edge_event = RisingEdge(self.clock)
|
clock_edge_event = RisingEdge(self.clock)
|
||||||
|
|
||||||
|
wake_event = self.wake_event.wait()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
pause_sample = self.pause
|
||||||
|
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
# read handshake signals
|
# read handshake signals
|
||||||
tready_sample = (not hasattr(self.bus, "tready")) or self.bus.tready.value
|
tready_sample = (not has_tready) or self.bus.tready.value
|
||||||
tvalid_sample = (not hasattr(self.bus, "tvalid")) or self.bus.tvalid.value
|
tvalid_sample = (not has_tvalid) or self.bus.tvalid.value
|
||||||
|
|
||||||
if tready_sample and tvalid_sample:
|
if tready_sample and tvalid_sample:
|
||||||
if frame is None:
|
if not frame:
|
||||||
if self.byte_size == 8:
|
if self.byte_size == 8:
|
||||||
frame = AxiStreamFrame(bytearray(), [], [], [], [])
|
frame = AxiStreamFrame(bytearray(), [], [], [], [])
|
||||||
else:
|
else:
|
||||||
@@ -719,16 +800,16 @@ class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
|||||||
|
|
||||||
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 has_tkeep:
|
||||||
frame.tkeep.append((self.bus.tkeep.value.integer >> offset) & 1)
|
frame.tkeep.append((self.bus.tkeep.value.integer >> offset) & 1)
|
||||||
if hasattr(self.bus, "tid"):
|
if has_tid:
|
||||||
frame.tid.append(self.bus.tid.value.integer)
|
frame.tid.append(self.bus.tid.value.integer)
|
||||||
if hasattr(self.bus, "tdest"):
|
if has_tdest:
|
||||||
frame.tdest.append(self.bus.tdest.value.integer)
|
frame.tdest.append(self.bus.tdest.value.integer)
|
||||||
if hasattr(self.bus, "tuser"):
|
if has_tuser:
|
||||||
frame.tuser.append(self.bus.tuser.value.integer)
|
frame.tuser.append(self.bus.tuser.value.integer)
|
||||||
|
|
||||||
if not hasattr(self.bus, "tlast") or self.bus.tlast.value:
|
if not has_tlast or self.bus.tlast.value:
|
||||||
frame.sim_time_end = get_sim_time()
|
frame.sim_time_end = get_sim_time()
|
||||||
self.log.info("RX frame: %s", frame)
|
self.log.info("RX frame: %s", frame)
|
||||||
|
|
||||||
@@ -742,5 +823,9 @@ class AxiStreamSink(AxiStreamMonitor, AxiStreamPause):
|
|||||||
else:
|
else:
|
||||||
self.active = bool(frame)
|
self.active = bool(frame)
|
||||||
|
|
||||||
if hasattr(self.bus, "tready"):
|
if has_tready:
|
||||||
self.bus.tready.value = (not self.full() and not self.pause)
|
self.bus.tready.value = (not self.full() and not pause_sample)
|
||||||
|
|
||||||
|
if not tvalid_sample or (self.pause and pause_sample) or self.full():
|
||||||
|
self.wake_event.clear()
|
||||||
|
await wake_event
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ class StreamBase(Reset):
|
|||||||
self.idle_event = Event()
|
self.idle_event = Event()
|
||||||
self.idle_event.set()
|
self.idle_event.set()
|
||||||
self.active_event = Event()
|
self.active_event = Event()
|
||||||
|
self.wake_event = Event()
|
||||||
|
|
||||||
self.ready = None
|
self.ready = None
|
||||||
self.valid = None
|
self.valid = None
|
||||||
@@ -163,10 +164,23 @@ class StreamPause:
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.pause = False
|
self._pause = False
|
||||||
self._pause_generator = None
|
self._pause_generator = None
|
||||||
self._pause_cr = None
|
self._pause_cr = None
|
||||||
|
|
||||||
|
def _pause_update(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pause(self):
|
||||||
|
return self._pause
|
||||||
|
|
||||||
|
@pause.setter
|
||||||
|
def pause(self, val):
|
||||||
|
if self._pause != val:
|
||||||
|
self._pause_update(val)
|
||||||
|
self._pause = val
|
||||||
|
|
||||||
def set_pause_generator(self, generator=None):
|
def set_pause_generator(self, generator=None):
|
||||||
if self._pause_cr is not None:
|
if self._pause_cr is not None:
|
||||||
self._pause_cr.kill()
|
self._pause_cr.kill()
|
||||||
@@ -206,12 +220,14 @@ class StreamSource(StreamBase, StreamPause):
|
|||||||
await self.dequeue_event.wait()
|
await self.dequeue_event.wait()
|
||||||
await self.queue.put(obj)
|
await self.queue.put(obj)
|
||||||
self.idle_event.clear()
|
self.idle_event.clear()
|
||||||
|
self.active_event.set()
|
||||||
|
|
||||||
def send_nowait(self, obj):
|
def send_nowait(self, obj):
|
||||||
if self.full():
|
if self.full():
|
||||||
raise QueueFull()
|
raise QueueFull()
|
||||||
self.queue.put_nowait(obj)
|
self.queue.put_nowait(obj)
|
||||||
self.idle_event.clear()
|
self.idle_event.clear()
|
||||||
|
self.active_event.set()
|
||||||
|
|
||||||
def full(self):
|
def full(self):
|
||||||
if self.queue_occupancy_limit > 0 and self.count() >= self.queue_occupancy_limit:
|
if self.queue_occupancy_limit > 0 and self.count() >= self.queue_occupancy_limit:
|
||||||
@@ -255,6 +271,9 @@ class StreamSource(StreamBase, StreamPause):
|
|||||||
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()
|
||||||
|
self.active_event.clear()
|
||||||
|
|
||||||
|
await self.active_event.wait()
|
||||||
|
|
||||||
|
|
||||||
class StreamMonitor(StreamBase):
|
class StreamMonitor(StreamBase):
|
||||||
@@ -264,9 +283,21 @@ class StreamMonitor(StreamBase):
|
|||||||
_valid_init = None
|
_valid_init = None
|
||||||
_ready_init = None
|
_ready_init = None
|
||||||
|
|
||||||
|
def __init__(self, bus, clock, reset=None, reset_active_level=True, *args, **kwargs):
|
||||||
|
super().__init__(bus, clock, reset, reset_active_level, *args, **kwargs)
|
||||||
|
|
||||||
|
if self.valid is not None:
|
||||||
|
cocotb.start_soon(self._run_valid_monitor())
|
||||||
|
if self.ready is not None:
|
||||||
|
cocotb.start_soon(self._run_ready_monitor())
|
||||||
|
|
||||||
|
def _dequeue(self, item):
|
||||||
|
pass
|
||||||
|
|
||||||
def _recv(self, item):
|
def _recv(self, item):
|
||||||
if self.queue.empty():
|
if self.queue.empty():
|
||||||
self.active_event.clear()
|
self.active_event.clear()
|
||||||
|
self._dequeue(item)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
async def recv(self):
|
async def recv(self):
|
||||||
@@ -285,9 +316,25 @@ class StreamMonitor(StreamBase):
|
|||||||
else:
|
else:
|
||||||
await self.active_event.wait()
|
await self.active_event.wait()
|
||||||
|
|
||||||
|
async def _run_valid_monitor(self):
|
||||||
|
event = RisingEdge(self.valid)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
await event
|
||||||
|
self.wake_event.set()
|
||||||
|
|
||||||
|
async def _run_ready_monitor(self):
|
||||||
|
event = RisingEdge(self.ready)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
await event
|
||||||
|
self.wake_event.set()
|
||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
clock_edge_event = RisingEdge(self.clock)
|
clock_edge_event = RisingEdge(self.clock)
|
||||||
|
|
||||||
|
wake_event = self.wake_event.wait()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
@@ -300,6 +347,9 @@ class StreamMonitor(StreamBase):
|
|||||||
self.bus.sample(obj)
|
self.bus.sample(obj)
|
||||||
self.queue.put_nowait(obj)
|
self.queue.put_nowait(obj)
|
||||||
self.active_event.set()
|
self.active_event.set()
|
||||||
|
else:
|
||||||
|
self.wake_event.clear()
|
||||||
|
await wake_event
|
||||||
|
|
||||||
|
|
||||||
class StreamSink(StreamMonitor, StreamPause):
|
class StreamSink(StreamMonitor, StreamPause):
|
||||||
@@ -327,10 +377,20 @@ class StreamSink(StreamMonitor, StreamPause):
|
|||||||
if self.ready is not None:
|
if self.ready is not None:
|
||||||
self.ready.value = 0
|
self.ready.value = 0
|
||||||
|
|
||||||
|
def _pause_update(self, val):
|
||||||
|
self.wake_event.set()
|
||||||
|
|
||||||
|
def _dequeue(self, item):
|
||||||
|
self.wake_event.set()
|
||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
clock_edge_event = RisingEdge(self.clock)
|
clock_edge_event = RisingEdge(self.clock)
|
||||||
|
|
||||||
|
wake_event = self.wake_event.wait()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
pause_sample = self.pause
|
||||||
|
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
# read handshake signals
|
# read handshake signals
|
||||||
@@ -344,7 +404,11 @@ 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.value = (not self.full() and not self.pause)
|
self.ready.value = (not self.full() and not pause_sample)
|
||||||
|
|
||||||
|
if not valid_sample or (self.pause and pause_sample) or self.full():
|
||||||
|
self.wake_event.clear()
|
||||||
|
await wake_event
|
||||||
|
|
||||||
|
|
||||||
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.18"
|
__version__ = "0.1.20"
|
||||||
|
|||||||
23
setup.cfg
23
setup.cfg
@@ -47,14 +47,13 @@ addopts =
|
|||||||
|
|
||||||
# tox configuration
|
# tox configuration
|
||||||
[tox:tox]
|
[tox:tox]
|
||||||
envlist = py36, py37, py38, py39, py310
|
envlist = py37, py38, py39, py310
|
||||||
skip_missing_interpreters = true
|
skip_missing_interpreters = true
|
||||||
minversion = 3.2.0
|
minversion = 3.18.0
|
||||||
requires = virtualenv >= 16.1
|
requires = virtualenv >= 16.1
|
||||||
|
|
||||||
[gh-actions]
|
[gh-actions]
|
||||||
python =
|
python =
|
||||||
3.6: py36
|
|
||||||
3.7: py37
|
3.7: py37
|
||||||
3.8: py38
|
3.8: py38
|
||||||
3.9: py39
|
3.9: py39
|
||||||
@@ -63,19 +62,23 @@ python =
|
|||||||
[testenv]
|
[testenv]
|
||||||
setenv =
|
setenv =
|
||||||
COVERAGE=1
|
COVERAGE=1
|
||||||
|
usedevelop = True
|
||||||
|
|
||||||
deps =
|
deps =
|
||||||
pytest
|
pytest == 7.2.1
|
||||||
pytest-xdist
|
pytest-xdist == 3.1.0
|
||||||
cocotb-test
|
cocotb == 1.7.2
|
||||||
coverage
|
cocotb-bus == 0.2.1
|
||||||
pytest-cov
|
cocotb-test == 0.2.4
|
||||||
|
coverage == 7.0.5
|
||||||
|
pytest-cov == 4.0.0
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
pytest --cov=cocotbext --cov=tests --cov-branch -n auto
|
pytest --cov=cocotbext --cov=tests --cov-branch {posargs:-n auto --verbose}
|
||||||
bash -c 'find . -type f -name "\.coverage" | xargs coverage combine --append'
|
bash -c 'find . -type f -name "\.coverage" | xargs coverage combine --append'
|
||||||
|
coverage report
|
||||||
|
|
||||||
whitelist_externals =
|
allowlist_externals =
|
||||||
bash
|
bash
|
||||||
|
|
||||||
# combine if paths are different
|
# combine if paths are different
|
||||||
|
|||||||
Reference in New Issue
Block a user