6 Commits

5 changed files with 105 additions and 70 deletions
Split View
  1. +2
    -0
      .gitignore
  2. +16
    -19
      pyuvm_tests/Coverage.py
  3. +38
    -17
      pyuvm_tests/Makefile
  4. +11
    -8
      pyuvm_tests/VaiBfm.py
  5. +38
    -26
      pyuvm_tests/tb_aes.py

+ 2
- 0
.gitignore View File

@ -1 +1,3 @@
cocotbext-wishbone
.ruff_cache
__pycache__

+ 16
- 19
pyuvm_tests/Coverage.py View File

@ -3,41 +3,38 @@ import vsc
# Random stimuli model class
@vsc.randobj
class constraints():
class constraints:
def __init__(self):
self.key = vsc.rand_bit_t(128)
self.data = vsc.rand_bit_t(128)
@vsc.constraint
def c(self):
self.data >= 0 and self.data <= 2**128-1
vsc.dist(self.key, [
vsc.weight(0, 15),
vsc.weight((1,2**128-2), 70),
vsc.weight((2**128-1), 15)])
self.data >= 0 and self.data <= 2**128 - 1
vsc.dist(
self.key,
[
vsc.weight(0, 15),
vsc.weight((1, 2**128 - 2), 70),
vsc.weight((2**128 - 1), 15),
],
)
# Stimuli covergroup
@vsc.covergroup
class covergroup():
class covergroup:
def __init__(self, name="bla"):
self.options.name = name
self.with_sample(
mode = vsc.bit_t(1),
key = vsc.bit_t(128)
)
self.with_sample(mode=vsc.bit_t(1), key=vsc.bit_t(128))
self.enc = vsc.coverpoint(self.mode, bins=dict(
enc = vsc.bin(0)))
self.enc = vsc.coverpoint(self.mode, bins=dict(enc=vsc.bin(0)))
self.dec = vsc.coverpoint(self.mode, bins=dict(
dec = vsc.bin(1)))
self.dec = vsc.coverpoint(self.mode, bins=dict(dec=vsc.bin(1)))
self.key0 = vsc.coverpoint(self.key, bins=dict(
key0 = vsc.bin(0)))
self.key0 = vsc.coverpoint(self.key, bins=dict(key0=vsc.bin(0)))
self.keyF = vsc.coverpoint(self.key, bins=dict(
keyF = vsc.bin(2**128-1)))
self.keyF = vsc.coverpoint(self.key, bins=dict(keyF=vsc.bin(2**128 - 1)))
self.encXkey0 = vsc.cross([self.enc, self.key0])
self.encXkeyF = vsc.cross([self.enc, self.keyF])


+ 38
- 17
pyuvm_tests/Makefile View File

@ -5,12 +5,18 @@ DUT ?= aes
EXT := ../ext
ifeq (${DUT}, wishbone)
TOPLEVEL := wishboneslavee
SIM_ARGS := -gSimulation=true \
-gAddressWidth=8 \
-gDataWidth=16
TOPLEVEL := wishboneslavee
SIM_ARGS := -gSimulation=true \
-gAddressWidth=8 \
-gDataWidth=16
else
TOPLEVEL := ${DUT}
TOPLEVEL := ${DUT}
endif
ifeq (check, $(firstword $(MAKECMDGOALS)))
ifeq (FIX, $(lastword $(MAKECMDGOALS)))
RUFF_ARGS := --fix
endif
endif
# Cocotb related
@ -24,28 +30,43 @@ SIM ?= ghdl
TOPLEVEL_LANG := vhdl
VHDL_SOURCES_libvhdl := ${EXT}/libvhdl/common/UtilsP.vhd
VHDL_SOURCES := ${EXT}/libvhdl/syn/* \
${EXT}/cryptocores/aes/rtl/vhdl/*.vhd
${EXT}/cryptocores/aes/rtl/vhdl/*.vhd
SIM_BUILD := build
ifeq (${SIM}, ghdl)
COMPILE_ARGS := --std=08
SIM_ARGS += \
--wave=results/${MODULE}.ghw \
--psl-report=results/${MODULE}_psl.json \
--vpi-trace=results/${MODULE}_vpi.log
COMPILE_ARGS := --std=08
SIM_ARGS += \
--wave=results/${MODULE}.ghw \
--psl-report=results/${MODULE}_psl.json \
--vpi-trace=results/${MODULE}_vpi.log
else
EXTRA_ARGS := --std=08
VHDL_LIB_ORDER := libvhdl
EXTRA_ARGS := --std=08
VHDL_LIB_ORDER := libvhdl
endif
ifneq (, $(shell which cocotb-config))
include $(shell cocotb-config --makefiles)/Makefile.sim
else
$(warning WARNING: cocotb not found)
endif
check format:
ifneq (, $(shell which ruff))
ruff $@ *.py $(RUFF_ARGS)
else
@echo "ERROR: ruff not found"; exit 1
endif
FIX:
@#
results:
mkdir -p results
.PHONY: clean
clean::
rm -rf *.o __pycache__ uarttx uartrx wishboneslavee aes results $(SIM_BUILD)
rm -rf *.o uarttx uartrx wishboneslavee aes results $(SIM_BUILD)
cleanall: clean
rm -rf .ruff_cache __pycache__
.PHONY: clean cleanall check format FIX

+ 11
- 8
pyuvm_tests/VaiBfm.py View File

@ -1,5 +1,5 @@
import cocotb
from cocotb.triggers import FallingEdge, RisingEdge, Timer
from cocotb.triggers import RisingEdge, Timer
from cocotb.queue import QueueEmpty, Queue
from cocotb.clock import Clock
import logging
@ -20,7 +20,7 @@ class Mode(enum.IntEnum):
Decrypt = 1
# VAI BFM with queues for
# VAI BFM with queues for
class VaiBfm(metaclass=pyuvm.Singleton):
"""Valid-Accept Bfm"""
@ -32,7 +32,9 @@ class VaiBfm(metaclass=pyuvm.Singleton):
self.driver_queue = Queue(maxsize=1)
self.in_monitor_queue = Queue(maxsize=0)
self.out_monitor_queue = Queue(maxsize=0)
self.clock = Clock(self.dut.clk_i, 10, units="ns") # Create a 10 ns period clock
self.clock = Clock(
self.dut.clk_i, 10, units="ns"
) # Create a 10 ns period clock
cocotb.start_soon(self.clock.start())
# Reset coroutine
@ -82,9 +84,11 @@ class VaiBfm(metaclass=pyuvm.Singleton):
while True:
await RisingEdge(self.dut.clk_i)
if self.dut.valid_i.value and self.dut.accept_o.value:
in_tuple = (self.dut.mode_i.value,
self.dut.key_i.value,
self.dut.data_i.value)
in_tuple = (
self.dut.mode_i.value,
self.dut.key_i.value,
self.dut.data_i.value,
)
self.in_monitor_queue.put_nowait(in_tuple)
# VAI output monitor
@ -95,7 +99,6 @@ class VaiBfm(metaclass=pyuvm.Singleton):
out_data = self.dut.data_o.value
self.out_monitor_queue.put_nowait(out_data)
# Launching the coroutines using start_soon
def start_tasks(self):
cocotb.start_soon(self.__driver())
@ -115,4 +118,4 @@ class VaiBfm(metaclass=pyuvm.Singleton):
# send_op puts the VAI input operation into the driver queue
async def send_op(self, mode, key, data):
await self.driver_queue.put((mode, key, data))
await self.driver_queue.put((mode, key, data))

+ 38
- 26
pyuvm_tests/tb_aes.py View File

@ -1,14 +1,27 @@
from cocotb.queue import Queue
from cocotb.triggers import RisingEdge, Timer, Combine
from pyuvm import *
import cocotb
import pyuvm
import vsc
from cocotb.triggers import Combine
from pyuvm import (
uvm_test,
uvm_sequence,
uvm_sequence_item,
uvm_sequencer,
uvm_driver,
uvm_component,
uvm_subscriber,
uvm_env,
uvm_factory,
uvm_analysis_port,
uvm_tlm_analysis_fifo,
uvm_get_port,
ConfigDB,
UVMConfigItemNotFound,
)
from vsc import get_coverage_report
from VaiBfm import VaiBfm, Mode
from Coverage import constraints, covergroup
from Crypto.Cipher import AES
import cocotb
import pyuvm
import vsc
@pyuvm.test()
@ -34,7 +47,6 @@ class ParallelTest(AesTest):
# Virtual sequence that starts other sequences
class TestAllSeq(uvm_sequence):
async def body(self):
# get the sequencer handle
seqr = ConfigDB().get(None, "", "SEQR")
@ -46,7 +58,6 @@ class TestAllSeq(uvm_sequence):
# Running encryption and decryption sequences in parallel
class TestAllParallelSeq(uvm_sequence):
async def body(self):
seqr = ConfigDB().get(None, "", "SEQR")
enc_rand_seq = EncRandSeq("enc_random")
@ -58,7 +69,6 @@ class TestAllParallelSeq(uvm_sequence):
# Sequence item which holds the stimuli for one operation
class AesSeqItem(uvm_sequence_item):
def __init__(self, name, mode, key, data):
super().__init__(name)
self.mode = mode
@ -66,7 +76,11 @@ class AesSeqItem(uvm_sequence_item):
self.data = data
def __eq__(self, other):
same = self.mode == other.mode and self.key == other.key and self.data == other.data
same = (
self.mode == other.mode
and self.key == other.key
and self.data == other.data
)
return same
def __str__(self):
@ -77,7 +91,6 @@ class AesSeqItem(uvm_sequence_item):
# Abstract basis sequence class
# set_operands() has to be implemented by class that inherits from this class
class BaseSeq(uvm_sequence):
async def body(self):
self.cr = constraints()
for _ in range(20):
@ -130,7 +143,6 @@ class Driver(uvm_driver):
class Scoreboard(uvm_component):
def build_phase(self):
self.input_fifo = uvm_tlm_analysis_fifo("input_fifo", self)
self.output_fifo = uvm_tlm_analysis_fifo("output_fifo", self)
@ -158,12 +170,16 @@ class Scoreboard(uvm_component):
else:
reference = aes.decrypt(data.buff)
if result.buff == reference:
self.logger.info(f"PASSED: {Mode(mode).name} {data.hex()} with key "
f"{key.hex()} = {result.hex()}")
self.logger.info(
f"PASSED: {Mode(mode).name} {data.hex()} with key "
f"{key.hex()} = {result.hex()}"
)
else:
self.logger.error(f"FAILED: {Mode(mode).name} {data.hex()} with key "
f"{key.hex()} = 0x{result.hex()}, "
f"expected {reference.hex()}")
self.logger.error(
f"FAILED: {Mode(mode).name} {data.hex()} with key "
f"{key.hex()} = 0x{result.hex()}, "
f"expected {reference.hex()}"
)
self.passed = False
def report_phase(self):
@ -188,12 +204,10 @@ class Monitor(uvm_component):
# Coverage collector and checker
class Coverage(uvm_subscriber):
def start_of_simulation_phase(self):
self.cg = covergroup()
try:
self.disable_errors = ConfigDB().get(
self, "", "DISABLE_COVERAGE_ERRORS")
self.disable_errors = ConfigDB().get(self, "", "DISABLE_COVERAGE_ERRORS")
except UVMConfigItemNotFound:
self.disable_errors = False
@ -204,19 +218,17 @@ class Coverage(uvm_subscriber):
def report_phase(self):
if not self.disable_errors:
if self.cg.get_coverage() != 100.0:
self.logger.warning(
f"Functional coverage incomplete.")
self.logger.warning("Functional coverage incomplete.")
else:
self.logger.info("Covered all operations")
with open('results/tb_aes_fcover.txt', 'a', encoding='utf-8') as f:
with open("results/tb_aes_fcover.txt", "a", encoding="utf-8") as f:
f.write(get_coverage_report(details=True))
vsc.write_coverage_db('results/tb_aes_fcover.xml')
vsc.write_coverage_db("results/tb_aes_fcover.xml")
# AES test bench environment
# Creates instances of components and connects them
class AesEnv(uvm_env):
def build_phase(self):
self.seqr = uvm_sequencer("seqr", self)
ConfigDB().set(None, "*", "SEQR", self.seqr)


Loading…
Cancel
Save