6 Commits

5 changed files with 105 additions and 70 deletions
Unified 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 cocotbext-wishbone
.ruff_cache
__pycache__

+ 16
- 19
pyuvm_tests/Coverage.py View File

@ -3,41 +3,38 @@ import vsc
# Random stimuli model class # Random stimuli model class
@vsc.randobj @vsc.randobj
class constraints():
class constraints:
def __init__(self): def __init__(self):
self.key = vsc.rand_bit_t(128) self.key = vsc.rand_bit_t(128)
self.data = vsc.rand_bit_t(128) self.data = vsc.rand_bit_t(128)
@vsc.constraint @vsc.constraint
def c(self): 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 # Stimuli covergroup
@vsc.covergroup @vsc.covergroup
class covergroup():
class covergroup:
def __init__(self, name="bla"): def __init__(self, name="bla"):
self.options.name = name 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.encXkey0 = vsc.cross([self.enc, self.key0])
self.encXkeyF = vsc.cross([self.enc, self.keyF]) self.encXkeyF = vsc.cross([self.enc, self.keyF])


+ 38
- 17
pyuvm_tests/Makefile View File

@ -5,12 +5,18 @@ DUT ?= aes
EXT := ../ext EXT := ../ext
ifeq (${DUT}, wishbone) ifeq (${DUT}, wishbone)
TOPLEVEL := wishboneslavee
SIM_ARGS := -gSimulation=true \
-gAddressWidth=8 \
-gDataWidth=16
TOPLEVEL := wishboneslavee
SIM_ARGS := -gSimulation=true \
-gAddressWidth=8 \
-gDataWidth=16
else else
TOPLEVEL := ${DUT}
TOPLEVEL := ${DUT}
endif
ifeq (check, $(firstword $(MAKECMDGOALS)))
ifeq (FIX, $(lastword $(MAKECMDGOALS)))
RUFF_ARGS := --fix
endif
endif endif
# Cocotb related # Cocotb related
@ -24,28 +30,43 @@ SIM ?= ghdl
TOPLEVEL_LANG := vhdl TOPLEVEL_LANG := vhdl
VHDL_SOURCES_libvhdl := ${EXT}/libvhdl/common/UtilsP.vhd VHDL_SOURCES_libvhdl := ${EXT}/libvhdl/common/UtilsP.vhd
VHDL_SOURCES := ${EXT}/libvhdl/syn/* \ VHDL_SOURCES := ${EXT}/libvhdl/syn/* \
${EXT}/cryptocores/aes/rtl/vhdl/*.vhd
${EXT}/cryptocores/aes/rtl/vhdl/*.vhd
SIM_BUILD := build SIM_BUILD := build
ifeq (${SIM}, ghdl) 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 else
EXTRA_ARGS := --std=08
VHDL_LIB_ORDER := libvhdl
EXTRA_ARGS := --std=08
VHDL_LIB_ORDER := libvhdl
endif endif
ifneq (, $(shell which cocotb-config))
include $(shell cocotb-config --makefiles)/Makefile.sim 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: results:
mkdir -p results mkdir -p results
.PHONY: clean
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 import cocotb
from cocotb.triggers import FallingEdge, RisingEdge, Timer
from cocotb.triggers import RisingEdge, Timer
from cocotb.queue import QueueEmpty, Queue from cocotb.queue import QueueEmpty, Queue
from cocotb.clock import Clock from cocotb.clock import Clock
import logging import logging
@ -20,7 +20,7 @@ class Mode(enum.IntEnum):
Decrypt = 1 Decrypt = 1
# VAI BFM with queues for
# VAI BFM with queues for
class VaiBfm(metaclass=pyuvm.Singleton): class VaiBfm(metaclass=pyuvm.Singleton):
"""Valid-Accept Bfm""" """Valid-Accept Bfm"""
@ -32,7 +32,9 @@ class VaiBfm(metaclass=pyuvm.Singleton):
self.driver_queue = Queue(maxsize=1) self.driver_queue = Queue(maxsize=1)
self.in_monitor_queue = Queue(maxsize=0) self.in_monitor_queue = Queue(maxsize=0)
self.out_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()) cocotb.start_soon(self.clock.start())
# Reset coroutine # Reset coroutine
@ -82,9 +84,11 @@ class VaiBfm(metaclass=pyuvm.Singleton):
while True: while True:
await RisingEdge(self.dut.clk_i) await RisingEdge(self.dut.clk_i)
if self.dut.valid_i.value and self.dut.accept_o.value: 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) self.in_monitor_queue.put_nowait(in_tuple)
# VAI output monitor # VAI output monitor
@ -95,7 +99,6 @@ class VaiBfm(metaclass=pyuvm.Singleton):
out_data = self.dut.data_o.value out_data = self.dut.data_o.value
self.out_monitor_queue.put_nowait(out_data) self.out_monitor_queue.put_nowait(out_data)
# Launching the coroutines using start_soon # Launching the coroutines using start_soon
def start_tasks(self): def start_tasks(self):
cocotb.start_soon(self.__driver()) 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 # send_op puts the VAI input operation into the driver queue
async def send_op(self, mode, key, data): 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 vsc import get_coverage_report
from VaiBfm import VaiBfm, Mode from VaiBfm import VaiBfm, Mode
from Coverage import constraints, covergroup from Coverage import constraints, covergroup
from Crypto.Cipher import AES from Crypto.Cipher import AES
import cocotb
import pyuvm
import vsc
@pyuvm.test() @pyuvm.test()
@ -34,7 +47,6 @@ class ParallelTest(AesTest):
# Virtual sequence that starts other sequences # Virtual sequence that starts other sequences
class TestAllSeq(uvm_sequence): class TestAllSeq(uvm_sequence):
async def body(self): async def body(self):
# get the sequencer handle # get the sequencer handle
seqr = ConfigDB().get(None, "", "SEQR") seqr = ConfigDB().get(None, "", "SEQR")
@ -46,7 +58,6 @@ class TestAllSeq(uvm_sequence):
# Running encryption and decryption sequences in parallel # Running encryption and decryption sequences in parallel
class TestAllParallelSeq(uvm_sequence): class TestAllParallelSeq(uvm_sequence):
async def body(self): async def body(self):
seqr = ConfigDB().get(None, "", "SEQR") seqr = ConfigDB().get(None, "", "SEQR")
enc_rand_seq = EncRandSeq("enc_random") enc_rand_seq = EncRandSeq("enc_random")
@ -58,7 +69,6 @@ class TestAllParallelSeq(uvm_sequence):
# Sequence item which holds the stimuli for one operation # Sequence item which holds the stimuli for one operation
class AesSeqItem(uvm_sequence_item): class AesSeqItem(uvm_sequence_item):
def __init__(self, name, mode, key, data): def __init__(self, name, mode, key, data):
super().__init__(name) super().__init__(name)
self.mode = mode self.mode = mode
@ -66,7 +76,11 @@ class AesSeqItem(uvm_sequence_item):
self.data = data self.data = data
def __eq__(self, other): 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 return same
def __str__(self): def __str__(self):
@ -77,7 +91,6 @@ class AesSeqItem(uvm_sequence_item):
# Abstract basis sequence class # Abstract basis sequence class
# set_operands() has to be implemented by class that inherits from this class # set_operands() has to be implemented by class that inherits from this class
class BaseSeq(uvm_sequence): class BaseSeq(uvm_sequence):
async def body(self): async def body(self):
self.cr = constraints() self.cr = constraints()
for _ in range(20): for _ in range(20):
@ -130,7 +143,6 @@ class Driver(uvm_driver):
class Scoreboard(uvm_component): class Scoreboard(uvm_component):
def build_phase(self): def build_phase(self):
self.input_fifo = uvm_tlm_analysis_fifo("input_fifo", self) self.input_fifo = uvm_tlm_analysis_fifo("input_fifo", self)
self.output_fifo = uvm_tlm_analysis_fifo("output_fifo", self) self.output_fifo = uvm_tlm_analysis_fifo("output_fifo", self)
@ -158,12 +170,16 @@ class Scoreboard(uvm_component):
else: else:
reference = aes.decrypt(data.buff) reference = aes.decrypt(data.buff)
if result.buff == reference: 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: 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 self.passed = False
def report_phase(self): def report_phase(self):
@ -188,12 +204,10 @@ class Monitor(uvm_component):
# Coverage collector and checker # Coverage collector and checker
class Coverage(uvm_subscriber): class Coverage(uvm_subscriber):
def start_of_simulation_phase(self): def start_of_simulation_phase(self):
self.cg = covergroup() self.cg = covergroup()
try: try:
self.disable_errors = ConfigDB().get(
self, "", "DISABLE_COVERAGE_ERRORS")
self.disable_errors = ConfigDB().get(self, "", "DISABLE_COVERAGE_ERRORS")
except UVMConfigItemNotFound: except UVMConfigItemNotFound:
self.disable_errors = False self.disable_errors = False
@ -204,19 +218,17 @@ class Coverage(uvm_subscriber):
def report_phase(self): def report_phase(self):
if not self.disable_errors: if not self.disable_errors:
if self.cg.get_coverage() != 100.0: if self.cg.get_coverage() != 100.0:
self.logger.warning(
f"Functional coverage incomplete.")
self.logger.warning("Functional coverage incomplete.")
else: else:
self.logger.info("Covered all operations") 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)) 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 # AES test bench environment
# Creates instances of components and connects them # Creates instances of components and connects them
class AesEnv(uvm_env): class AesEnv(uvm_env):
def build_phase(self): def build_phase(self):
self.seqr = uvm_sequencer("seqr", self) self.seqr = uvm_sequencer("seqr", self)
ConfigDB().set(None, "*", "SEQR", self.seqr) ConfigDB().set(None, "*", "SEQR", self.seqr)


Loading…
Cancel
Save