From 89305b9e0bf1c552a47a6eebc153fb45b65e96f7 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Thu, 21 Sep 2023 09:32:35 +0200 Subject: [PATCH] Deduplicated code, isolated common sequence definitions Signed-off-by: Maciej Kurc --- verification/block/dma/sequences.py | 253 +++++++++++++++++++ verification/block/dma/test_address.py | 58 +---- verification/block/dma/test_debug_address.py | 58 +---- verification/block/dma/test_debug_read.py | 104 +------- verification/block/dma/test_debug_write.py | 105 +------- verification/block/dma/test_ecc.py | 59 ++--- verification/block/dma/test_read.py | 104 +------- verification/block/dma/test_write.py | 105 +------- verification/block/dma/testbench.py | 9 +- 9 files changed, 303 insertions(+), 552 deletions(-) create mode 100644 verification/block/dma/sequences.py diff --git a/verification/block/dma/sequences.py b/verification/block/dma/sequences.py new file mode 100644 index 00000000000..749979ed894 --- /dev/null +++ b/verification/block/dma/sequences.py @@ -0,0 +1,253 @@ +# Copyright (c) 2023 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +import random +import struct + +from cocotb.triggers import ClockCycles +from pyuvm import * +from testbench import ( + BaseEnv, + BaseTest, + BusReadItem, + BusWriteItem, + DebugReadItem, + DebugWriteItem, + MemReadItem, + MemWriteItem, +) + +# ============================================================================= + + +class MemWriteSequence(uvm_sequence): + """ + A sequence of random memory writes + """ + + def __init__(self, name, mem, dwidth=32): + super().__init__(name) + self.mem = mem + self.dwidth = dwidth + + async def body(self): + mem_base = ConfigDB().get(None, "", self.mem + "_BASE") + mem_size = ConfigDB().get(None, "", self.mem + "_SIZE") + + align = ConfigDB().get(None, "", "ADDR_ALIGN") + + count = ConfigDB().get(None, "", "TEST_ITERATIONS") + burst = ConfigDB().get(None, "", "TEST_BURST_LEN") + gap = ConfigDB().get(None, "", "TEST_BURST_GAP") + + for j in range(count): + for i in range(burst): + addr = mem_base + random.randrange(0, mem_size) + addr = (addr // align) * align + + # Determine how to pack data to bytes + if self.dwidth == 32: + fmt = "= iccm_base and addr < iccm_base + iccm_size: + continue + if addr >= dccm_base and addr < dccm_base + dccm_size: + continue + if addr >= pic_base and addr < pic_base + pic_size: + continue + + break + + # Randomize read/write + if random.random() >= 0.5: + item = BusReadItem(addr) + else: + # Determine how to pack data to bytes + if self.dwidth == 32: + fmt = " # SPDX-License-Identifier: Apache-2.0 -import random -import struct - import pyuvm from cocotb.triggers import ClockCycles from pyuvm import * from scoreboards import AccessScoreboard -from testbench import BaseEnv, BaseTest, BusReadItem, BusWriteItem - -# ============================================================================= - - -class TestSequenceRange(uvm_sequence): - """ - A sequencer that generates random bus read/write requests to addresses - outside the range accepted by the DMA module - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - iccm_base = ConfigDB().get(None, "", "ICCM_BASE") - iccm_size = ConfigDB().get(None, "", "ICCM_SIZE") - - dccm_base = ConfigDB().get(None, "", "DCCM_BASE") - dccm_size = ConfigDB().get(None, "", "DCCM_SIZE") - - pic_base = ConfigDB().get(None, "", "PIC_BASE") - pic_size = ConfigDB().get(None, "", "PIC_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(50): - # Crude address randomizer - while True: - addr = random.randrange(0, (1 << 32) - 1) - addr = (addr // align) * align - - if addr >= iccm_base and addr < iccm_base + iccm_size: - continue - if addr >= dccm_base and addr < dccm_base + dccm_size: - continue - if addr >= pic_base and addr < pic_base + pic_size: - continue - - break - - # Randomize read/write - if random.random() >= 0.5: - item = BusReadItem(addr) - else: - data = random.randrange(0, (1 << 64) - 1) - item = BusWriteItem(addr, struct.pack(" # SPDX-License-Identifier: Apache-2.0 -import random -import struct - import pyuvm from cocotb.triggers import ClockCycles from pyuvm import * from scoreboards import AccessScoreboard -from testbench import BaseEnv, BaseTest, BusReadItem, BusWriteItem - -# ============================================================================= - - -class TestSequenceRange(uvm_sequence): - """ - A sequencer that generates random bus read/write requests to addresses - outside the range accepted by the DMA module - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - iccm_base = ConfigDB().get(None, "", "ICCM_BASE") - iccm_size = ConfigDB().get(None, "", "ICCM_SIZE") - - dccm_base = ConfigDB().get(None, "", "DCCM_BASE") - dccm_size = ConfigDB().get(None, "", "DCCM_SIZE") - - pic_base = ConfigDB().get(None, "", "PIC_BASE") - pic_size = ConfigDB().get(None, "", "PIC_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(50): - # Crude address randomizer - while True: - addr = random.randrange(0, (1 << 32) - 1) - addr = (addr // align) * align - - if addr >= iccm_base and addr < iccm_base + iccm_size: - continue - if addr >= dccm_base and addr < dccm_base + dccm_size: - continue - if addr >= pic_base and addr < pic_base + pic_size: - continue - - break - - # Randomize read/write - if random.random() >= 0.5: - item = BusReadItem(addr) - else: - data = random.randrange(0, (1 << 32) - 1) - item = BusWriteItem(addr, data) - - await self.start_item(item) - await self.finish_item(item) - +from sequences import InvalidAddressSequence +from testbench import BaseEnv, BaseTest # ============================================================================= @@ -92,7 +40,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequenceRange.create("stimulus") + self.seq = InvalidAddressSequence("stimulus", dwidth=32) # The debug bus is 32-bit wide async def run(self): await self.seq.start(self.env.dbg_seqr) diff --git a/verification/block/dma/test_debug_read.py b/verification/block/dma/test_debug_read.py index 90d73ef2024..41958f5d014 100644 --- a/verification/block/dma/test_debug_read.py +++ b/verification/block/dma/test_debug_read.py @@ -1,112 +1,16 @@ # Copyright (c) 2023 Antmicro # SPDX-License-Identifier: Apache-2.0 -import random -import struct -from collections import defaultdict import pyuvm from cocotb.triggers import ClockCycles from pyuvm import * from scoreboards import ReadScoreboard +from sequences import AnyMemReadSequence, MemReadSequence from testbench import BaseEnv, BaseTest, BusReadItem, BusWriteItem # ============================================================================= -# TODO: The following sequences are identical to those in test_read.py -# should be isolated to a common file. - - -class TestSequenceDCCM(uvm_sequence): - """ - A sequence of random DCCM reads - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - dccm_base = ConfigDB().get(None, "", "DCCM_BASE") - dccm_size = ConfigDB().get(None, "", "DCCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(4): - for i in range(6): - addr = dccm_base + random.randrange(0, dccm_size) - addr = (addr // align) * align - - item = BusReadItem(addr) - await self.start_item(item) - await self.finish_item(item) - - await ClockCycles(cocotb.top.clk, 5) - - -class TestSequenceICCM(uvm_sequence): - """ - A sequence of random ICCM reads - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - iccm_base = ConfigDB().get(None, "", "ICCM_BASE") - iccm_size = ConfigDB().get(None, "", "ICCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(4): - for i in range(6): - addr = iccm_base + random.randrange(0, iccm_size) - addr = (addr // align) * align - - item = BusReadItem(addr) - await self.start_item(item) - await self.finish_item(item) - - await ClockCycles(cocotb.top.clk, 5) - - -class TestSequenceBoth(uvm_sequence): - """ - A sequence of random ICCM or DCCM reads - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - iccm_base = ConfigDB().get(None, "", "ICCM_BASE") - iccm_size = ConfigDB().get(None, "", "ICCM_SIZE") - - dccm_base = ConfigDB().get(None, "", "DCCM_BASE") - dccm_size = ConfigDB().get(None, "", "DCCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(4): - for i in range(6): - mem_base, mem_size = random.choice( - [ - (iccm_base, iccm_size), - (dccm_base, dccm_size), - ] - ) - - addr = mem_base + random.randrange(0, mem_size) - addr = (addr // align) * align - - item = BusReadItem(addr) - await self.start_item(item) - await self.finish_item(item) - - await ClockCycles(cocotb.top.clk, 5) - - -# ============================================================================= - class TestEnv(BaseEnv): def build_phase(self): @@ -137,7 +41,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequenceDCCM.create("stimulus") + self.seq = MemReadSequence("stimulus", "DCCM") async def run(self): await self.seq.start(self.env.dbg_seqr) @@ -154,7 +58,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequenceICCM.create("stimulus") + self.seq = MemReadSequence("stimulus", "ICCM") async def run(self): await self.seq.start(self.env.dbg_seqr) @@ -171,7 +75,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequenceBoth.create("stimulus") + self.seq = AnyMemReadSequence("stimulus") async def run(self): await self.seq.start(self.env.dbg_seqr) diff --git a/verification/block/dma/test_debug_write.py b/verification/block/dma/test_debug_write.py index 60c95d2b37b..6e8af65bd86 100644 --- a/verification/block/dma/test_debug_write.py +++ b/verification/block/dma/test_debug_write.py @@ -3,107 +3,12 @@ import random import struct -from collections import defaultdict import pyuvm -from cocotb.triggers import ClockCycles from pyuvm import * from scoreboards import WriteScoreboard -from testbench import BaseEnv, BaseTest, BusReadItem, BusWriteItem - -# ============================================================================= - - -class TestSequenceDCCM(uvm_sequence): - """ - A sequence of random DCCM writes - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - dccm_base = ConfigDB().get(None, "", "DCCM_BASE") - dccm_size = ConfigDB().get(None, "", "DCCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(4): - for i in range(6): - addr = dccm_base + random.randrange(0, dccm_size) - addr = (addr // align) * align - data = random.randrange(0, (1 << 32) - 1) - - item = BusWriteItem(addr, data) - await self.start_item(item) - await self.finish_item(item) - - await ClockCycles(cocotb.top.clk, 5) - - -class TestSequenceICCM(uvm_sequence): - """ - A sequence of random ICCM writes - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - iccm_base = ConfigDB().get(None, "", "ICCM_BASE") - iccm_size = ConfigDB().get(None, "", "ICCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(4): - for i in range(6): - addr = iccm_base + random.randrange(0, iccm_size) - addr = (addr // align) * align - data = random.randrange(0, (1 << 32) - 1) - - item = BusWriteItem(addr, data) - await self.start_item(item) - await self.finish_item(item) - - await ClockCycles(cocotb.top.clk, 5) - - -class TestSequenceBoth(uvm_sequence): - """ - A sequence of random ICCM or DCCM writes - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - iccm_base = ConfigDB().get(None, "", "ICCM_BASE") - iccm_size = ConfigDB().get(None, "", "ICCM_SIZE") - - dccm_base = ConfigDB().get(None, "", "DCCM_BASE") - dccm_size = ConfigDB().get(None, "", "DCCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(4): - for i in range(6): - mem_base, mem_size = random.choice( - [ - (iccm_base, iccm_size), - (dccm_base, dccm_size), - ] - ) - - addr = mem_base + random.randrange(0, mem_size) - addr = (addr // align) * align - data = random.randrange(0, (1 << 32) - 1) - - item = BusWriteItem(addr, data) - await self.start_item(item) - await self.finish_item(item) - - await ClockCycles(cocotb.top.clk, 5) - +from sequences import AnyMemWriteSequence, MemWriteSequence +from testbench import BaseEnv, BaseTest # ============================================================================= @@ -137,7 +42,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequenceDCCM.create("stimulus") + self.seq = MemWriteSequence("stimulus", "DCCM", dwidth=32) async def run(self): await self.seq.start(self.env.dbg_seqr) @@ -154,7 +59,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequenceICCM.create("stimulus") + self.seq = MemWriteSequence("stimulus", "ICCM", dwidth=32) async def run(self): await self.seq.start(self.env.dbg_seqr) @@ -171,7 +76,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequenceBoth.create("stimulus") + self.seq = AnyMemWriteSequence("stimulus", dwidth=32) async def run(self): await self.seq.start(self.env.dbg_seqr) diff --git a/verification/block/dma/test_ecc.py b/verification/block/dma/test_ecc.py index 7c7e15924c1..4eb3fd3625d 100644 --- a/verification/block/dma/test_ecc.py +++ b/verification/block/dma/test_ecc.py @@ -1,13 +1,12 @@ # Copyright (c) 2023 Antmicro # SPDX-License-Identifier: Apache-2.0 -import random -import struct from collections import defaultdict import pyuvm from cocotb.triggers import ClockCycles from pyuvm import * +from sequences import AnyMemReadSequence from testbench import ( BaseEnv, BaseTest, @@ -20,43 +19,6 @@ # ============================================================================= -class TestSequence(uvm_sequence): - """ - A sequence of random ICCM or DCCM reads. ECC failure injection is - randomized as well. - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - iccm_base = ConfigDB().get(None, "", "ICCM_BASE") - iccm_size = ConfigDB().get(None, "", "ICCM_SIZE") - - dccm_base = ConfigDB().get(None, "", "DCCM_BASE") - dccm_size = ConfigDB().get(None, "", "DCCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for i in range(50): - mem_base, mem_size = random.choice( - [ - (iccm_base, iccm_size), - (dccm_base, dccm_size), - ] - ) - - addr = mem_base + random.randrange(0, mem_size) - addr = (addr // align) * align - - item = BusReadItem(addr) - await self.start_item(item) - await self.finish_item(item) - - -# ============================================================================= - - class Scoreboard(uvm_component): """ """ @@ -115,6 +77,9 @@ def check_phase(self): self.passed = False # Check reads + have_ecc_err = False + have_ecc_ok = False + for addr, pair in reads.items(): if "axi" not in pair: self.logger.error("No AXI transfer for access to 0x{:08X}".format(addr)) @@ -142,6 +107,20 @@ def check_phase(self): ) self.passed = False + # Check if there were errors injected + if pair["mem"]: + have_ecc_err = True + else: + have_ecc_ok = True + + if not have_ecc_err: + self.logger.error("There were no ECC errors injected!") + self.passed = False + + if not have_ecc_ok: + self.logger.error("There were only ECC errors injected!") + self.passed = False + def final_phase(self): if not self.passed: self.logger.critical("{} reports a failure".format(type(self))) @@ -183,7 +162,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequence.create("stimulus") + self.seq = AnyMemReadSequence("stimulus") async def run(self): await self.seq.start(self.env.axi_seqr) diff --git a/verification/block/dma/test_read.py b/verification/block/dma/test_read.py index bd5d14c4cdb..58c049ee5dd 100644 --- a/verification/block/dma/test_read.py +++ b/verification/block/dma/test_read.py @@ -1,106 +1,12 @@ # Copyright (c) 2023 Antmicro # SPDX-License-Identifier: Apache-2.0 -import random -import struct -from collections import defaultdict - import pyuvm from cocotb.triggers import ClockCycles from pyuvm import * from scoreboards import ReadScoreboard -from testbench import BaseEnv, BaseTest, BusReadItem, BusWriteItem - -# ============================================================================= - - -class TestSequenceDCCM(uvm_sequence): - """ - A sequence of random DCCM reads - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - dccm_base = ConfigDB().get(None, "", "DCCM_BASE") - dccm_size = ConfigDB().get(None, "", "DCCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(4): - for i in range(6): - addr = dccm_base + random.randrange(0, dccm_size) - addr = (addr // align) * align - - item = BusReadItem(addr) - await self.start_item(item) - await self.finish_item(item) - - await ClockCycles(cocotb.top.clk, 5) - - -class TestSequenceICCM(uvm_sequence): - """ - A sequence of random ICCM reads - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - iccm_base = ConfigDB().get(None, "", "ICCM_BASE") - iccm_size = ConfigDB().get(None, "", "ICCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(4): - for i in range(6): - addr = iccm_base + random.randrange(0, iccm_size) - addr = (addr // align) * align - - item = BusReadItem(addr) - await self.start_item(item) - await self.finish_item(item) - - await ClockCycles(cocotb.top.clk, 5) - - -class TestSequenceBoth(uvm_sequence): - """ - A sequence of random ICCM or DCCM reads - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - iccm_base = ConfigDB().get(None, "", "ICCM_BASE") - iccm_size = ConfigDB().get(None, "", "ICCM_SIZE") - - dccm_base = ConfigDB().get(None, "", "DCCM_BASE") - dccm_size = ConfigDB().get(None, "", "DCCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(4): - for i in range(6): - mem_base, mem_size = random.choice( - [ - (iccm_base, iccm_size), - (dccm_base, dccm_size), - ] - ) - - addr = mem_base + random.randrange(0, mem_size) - addr = (addr // align) * align - - item = BusReadItem(addr) - await self.start_item(item) - await self.finish_item(item) - - await ClockCycles(cocotb.top.clk, 5) - +from sequences import AnyMemReadSequence, MemReadSequence +from testbench import BaseEnv, BaseTest # ============================================================================= @@ -134,7 +40,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequenceDCCM.create("stimulus") + self.seq = MemReadSequence("stimulus", "DCCM") async def run(self): await self.seq.start(self.env.axi_seqr) @@ -151,7 +57,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequenceICCM.create("stimulus") + self.seq = MemReadSequence("stimulus", "ICCM") async def run(self): await self.seq.start(self.env.axi_seqr) @@ -168,7 +74,7 @@ def __init__(self, name, parent): def end_of_elaboration_phase(self): super().end_of_elaboration_phase() - self.seq = TestSequenceBoth.create("stimulus") + self.seq = AnyMemReadSequence("stimulus") async def run(self): await self.seq.start(self.env.axi_seqr) diff --git a/verification/block/dma/test_write.py b/verification/block/dma/test_write.py index 8cc7b1f8970..b502abc9095 100644 --- a/verification/block/dma/test_write.py +++ b/verification/block/dma/test_write.py @@ -3,107 +3,12 @@ import random import struct -from collections import defaultdict import pyuvm -from cocotb.triggers import ClockCycles from pyuvm import * from scoreboards import WriteScoreboard -from testbench import BaseEnv, BaseTest, BusReadItem, BusWriteItem - -# ============================================================================= - - -class TestSequenceDCCM(uvm_sequence): - """ - A sequence of random DCCM writes - """ - - def __init__(self, name): - super().__init__(name) - - async def body(self): - dccm_base = ConfigDB().get(None, "", "DCCM_BASE") - dccm_size = ConfigDB().get(None, "", "DCCM_SIZE") - - align = ConfigDB().get(None, "", "ADDR_ALIGN") - - for j in range(4): - for i in range(6): - addr = dccm_base + random.randrange(0, dccm_size) - addr = (addr // align) * align - data = random.randrange(0, (1 << 64) - 1) - - item = BusWriteItem(addr, struct.pack("