import logging
|
|
import random
|
|
import cocotb
|
|
import pprint
|
|
from Vai import VaiDriver, VaiReceiver, VaiMonitor
|
|
from cocotb.clock import Clock
|
|
from cocotb.queue import Queue
|
|
from cocotb.triggers import FallingEdge, RisingEdge, Timer, ReadOnly
|
|
from Crypto.Cipher import AES
|
|
from Crypto.Util.number import long_to_bytes, getRandomNBitInteger
|
|
import vsc
|
|
|
|
|
|
# Reset coroutine
|
|
async def reset_dut(reset_n, duration_ns):
|
|
reset_n.value = 0
|
|
await Timer(duration_ns, units="ns")
|
|
reset_n.value = 1
|
|
|
|
|
|
# Stimuli model class
|
|
@vsc.randobj
|
|
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, 25),
|
|
vsc.weight((1,2**128-2), 50),
|
|
vsc.weight((2**128-1), 25)])
|
|
|
|
|
|
# Stimuli covergroup
|
|
@vsc.covergroup
|
|
class covergroup():
|
|
def __init__(self):
|
|
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.dec = vsc.coverpoint(self.mode, bins=dict(
|
|
dec = vsc.bin(1)))
|
|
|
|
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.encXkey0 = vsc.cross([self.enc, self.key0])
|
|
self.encXkeyF = vsc.cross([self.enc, self.keyF])
|
|
|
|
self.decXkey0 = vsc.cross([self.dec, self.key0])
|
|
self.decXkeyF = vsc.cross([self.dec, self.keyF])
|
|
|
|
|
|
async def cg_sample(cg, queue):
|
|
while True:
|
|
_data = await queue.get()
|
|
cg.sample(_data[0].value, _data[1].value)
|
|
|
|
|
|
@cocotb.test(skip=False)
|
|
async def test_aes_enc(dut):
|
|
""" Test AES encryption """
|
|
|
|
clkedge = RisingEdge(dut.clk_i)
|
|
|
|
# Connect reset
|
|
reset = dut.reset_i
|
|
|
|
_input = [dut.mode_i, dut.key_i, dut.data_i]
|
|
_output = dut.data_o
|
|
# DUT input side
|
|
vai_driver = VaiDriver(dut.clk_i, _input, dut.valid_i, dut.accept_o)
|
|
vai_in_queue = cocotb.queue.Queue()
|
|
vai_in_monitor = VaiMonitor(dut.clk_i, _input, dut.valid_i, dut.accept_o, vai_in_queue)
|
|
# DUT output side
|
|
vai_receiver = VaiReceiver(dut.clk_i, dut.data_o, dut.valid_o, dut.accept_i)
|
|
vai_out_monitor = VaiMonitor(dut.clk_i, _output, dut.valid_o, dut.accept_i)
|
|
|
|
cr = constraints()
|
|
cg = covergroup()
|
|
cocotb.start_soon(cg_sample(cg, vai_in_queue))
|
|
|
|
# Drive input defaults (setimmediatevalue to avoid x asserts)
|
|
dut.mode_i.setimmediatevalue(0)
|
|
dut.key_i.setimmediatevalue(0)
|
|
dut.data_i.setimmediatevalue(0)
|
|
dut.valid_i.setimmediatevalue(0)
|
|
dut.accept_i.setimmediatevalue(0)
|
|
|
|
clock = Clock(dut.clk_i, 10, units="ns") # Create a 10 ns period clock
|
|
cocotb.start_soon(clock.start()) # Start the clock
|
|
|
|
# Execution will block until reset_dut has completed
|
|
dut._log.info("Hold reset")
|
|
await reset_dut(reset, 100)
|
|
dut._log.info("Released reset")
|
|
|
|
# Test 10 AES calculations
|
|
for i in range(10):
|
|
# Get now random stimuli
|
|
cr.randomize()
|
|
_key = cr.key
|
|
_data = cr.data
|
|
await clkedge
|
|
# Drive AES inputs
|
|
await vai_driver.send([0, _key, _data])
|
|
# Calc reference data
|
|
_aes = AES.new(_key.to_bytes(16, 'big'), AES.MODE_ECB)
|
|
_ref = _aes.encrypt(_data.to_bytes(16, 'big'))
|
|
# Get DUT output data
|
|
_rec = await vai_receiver.receive()
|
|
# Equivalence check
|
|
assert _rec.buff == _ref, \
|
|
f"Encrypt error, got 0x{_rec.buff.hex()}, expected 0x{_ref.hex()}"
|
|
|
|
|
|
@cocotb.test(skip=False)
|
|
async def test_aes_dec(dut):
|
|
""" Test AES decryption """
|
|
|
|
clkedge = RisingEdge(dut.clk_i)
|
|
|
|
# Connect reset
|
|
reset = dut.reset_i
|
|
|
|
_input = [dut.mode_i, dut.key_i, dut.data_i]
|
|
_output = dut.data_o
|
|
# DUT input side
|
|
vai_driver = VaiDriver(dut.clk_i, _input, dut.valid_i, dut.accept_o)
|
|
vai_in_queue = cocotb.queue.Queue()
|
|
vai_in_monitor = VaiMonitor(dut.clk_i, _input, dut.valid_i, dut.accept_o, vai_in_queue)
|
|
# DUT output side
|
|
vai_receiver = VaiReceiver(dut.clk_i, dut.data_o, dut.valid_o, dut.accept_i)
|
|
vai_out_monitor = VaiMonitor(dut.clk_i, _output, dut.valid_o, dut.accept_i)
|
|
|
|
cr = constraints()
|
|
cg = covergroup()
|
|
cocotb.start_soon(cg_sample(cg, vai_in_queue))
|
|
|
|
# Drive input defaults (setimmediatevalue to avoid x asserts)
|
|
dut.mode_i.setimmediatevalue(0)
|
|
dut.key_i.setimmediatevalue(0)
|
|
dut.data_i.setimmediatevalue(0)
|
|
dut.valid_i.setimmediatevalue(0)
|
|
dut.accept_i.setimmediatevalue(0)
|
|
|
|
clock = Clock(dut.clk_i, 10, units="ns") # Create a 10 ns period clock
|
|
cocotb.start_soon(clock.start()) # Start the clock
|
|
|
|
# Execution will block until reset_dut has completed
|
|
await reset_dut(reset, 100)
|
|
dut._log.info("Released reset")
|
|
|
|
# Test 10 AES calculations
|
|
for i in range(10):
|
|
# Get now random stimuli
|
|
cr.randomize()
|
|
_key = cr.key
|
|
_data = cr.data
|
|
await clkedge
|
|
# Drive AES inputs
|
|
await vai_driver.send([1, _key, _data])
|
|
# Calc reference data
|
|
_aes = AES.new(_key.to_bytes(16, 'big'), AES.MODE_ECB)
|
|
_ref = _aes.decrypt(_data.to_bytes(16, 'big'))
|
|
# Get DUT output data
|
|
_rec = await vai_receiver.receive()
|
|
# Equivalence check
|
|
assert _rec.buff == _ref, \
|
|
f"Decrypt error, got 0x{_rec.buff.hex()}, expected 0x{_ref.hex()}"
|
|
|
|
with open('results/tb_aes_fcover.txt', 'w', encoding='utf-8') as f:
|
|
f.write(vsc.get_coverage_report())
|