Examples of using cocotb for functional verification of VHDL designs with GHDL.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
5.6 KiB

  1. import logging
  2. import random
  3. import cocotb
  4. import pprint
  5. from Vai import VaiDriver, VaiReceiver, VaiMonitor
  6. from cocotb.clock import Clock
  7. from cocotb.queue import Queue
  8. from cocotb.triggers import FallingEdge, RisingEdge, Timer, ReadOnly
  9. from Crypto.Cipher import AES
  10. from Crypto.Util.number import long_to_bytes, getRandomNBitInteger
  11. import vsc
  12. # Reset coroutine
  13. async def reset_dut(reset_n, duration_ns):
  14. reset_n.value = 0
  15. await Timer(duration_ns, units="ns")
  16. reset_n.value = 1
  17. # Stimuli model class
  18. @vsc.randobj
  19. class constraints():
  20. def __init__(self):
  21. self.key = vsc.rand_bit_t(128)
  22. self.data = vsc.rand_bit_t(128)
  23. @vsc.constraint
  24. def c(self):
  25. self.data >= 0 and self.data <= 2**128-1
  26. vsc.dist(self.key, [
  27. vsc.weight(0, 25),
  28. vsc.weight((1,2**128-2), 50),
  29. vsc.weight((2**128-1), 25)])
  30. # Stimuli covergroup
  31. @vsc.covergroup
  32. class covergroup():
  33. def __init__(self):
  34. self.with_sample(
  35. mode = vsc.bit_t(1),
  36. key = vsc.bit_t(128)
  37. )
  38. self.enc = vsc.coverpoint(self.mode, bins=dict(
  39. enc = vsc.bin(0)))
  40. self.dec = vsc.coverpoint(self.mode, bins=dict(
  41. dec = vsc.bin(1)))
  42. self.key0 = vsc.coverpoint(self.key, bins=dict(
  43. key0 = vsc.bin(0)))
  44. self.keyF = vsc.coverpoint(self.key, bins=dict(
  45. keyF = vsc.bin(2**128-1)))
  46. self.encXkey0 = vsc.cross([self.enc, self.key0])
  47. self.encXkeyF = vsc.cross([self.enc, self.keyF])
  48. self.decXkey0 = vsc.cross([self.dec, self.key0])
  49. self.decXkeyF = vsc.cross([self.dec, self.keyF])
  50. async def cg_sample(cg, queue):
  51. while True:
  52. _data = await queue.get()
  53. cg.sample(_data[0].value, _data[1].value)
  54. @cocotb.test(skip=False)
  55. async def test_aes_enc(dut):
  56. """ Test AES encryption """
  57. clkedge = RisingEdge(dut.clk_i)
  58. # Connect reset
  59. reset = dut.reset_i
  60. _input = [dut.mode_i, dut.key_i, dut.data_i]
  61. _output = dut.data_o
  62. # DUT input side
  63. vai_driver = VaiDriver(dut.clk_i, _input, dut.valid_i, dut.accept_o)
  64. vai_in_queue = cocotb.queue.Queue()
  65. vai_in_monitor = VaiMonitor(dut.clk_i, _input, dut.valid_i, dut.accept_o, vai_in_queue)
  66. # DUT output side
  67. vai_receiver = VaiReceiver(dut.clk_i, dut.data_o, dut.valid_o, dut.accept_i)
  68. vai_out_monitor = VaiMonitor(dut.clk_i, _output, dut.valid_o, dut.accept_i)
  69. cr = constraints()
  70. cg = covergroup()
  71. cocotb.start_soon(cg_sample(cg, vai_in_queue))
  72. # Drive input defaults (setimmediatevalue to avoid x asserts)
  73. dut.mode_i.setimmediatevalue(0)
  74. dut.key_i.setimmediatevalue(0)
  75. dut.data_i.setimmediatevalue(0)
  76. dut.valid_i.setimmediatevalue(0)
  77. dut.accept_i.setimmediatevalue(0)
  78. clock = Clock(dut.clk_i, 10, units="ns") # Create a 10 ns period clock
  79. cocotb.start_soon(clock.start()) # Start the clock
  80. # Execution will block until reset_dut has completed
  81. dut._log.info("Hold reset")
  82. await reset_dut(reset, 100)
  83. dut._log.info("Released reset")
  84. # Test 10 AES calculations
  85. for i in range(10):
  86. # Get now random stimuli
  87. cr.randomize()
  88. _key = cr.key
  89. _data = cr.data
  90. await clkedge
  91. # Drive AES inputs
  92. await vai_driver.send([0, _key, _data])
  93. # Calc reference data
  94. _aes = AES.new(_key.to_bytes(16, 'big'), AES.MODE_ECB)
  95. _ref = _aes.encrypt(_data.to_bytes(16, 'big'))
  96. # Get DUT output data
  97. _rec = await vai_receiver.receive()
  98. # Equivalence check
  99. assert _rec.buff == _ref, \
  100. f"Encrypt error, got 0x{_rec.buff.hex()}, expected 0x{_ref.hex()}"
  101. @cocotb.test(skip=False)
  102. async def test_aes_dec(dut):
  103. """ Test AES decryption """
  104. clkedge = RisingEdge(dut.clk_i)
  105. # Connect reset
  106. reset = dut.reset_i
  107. _input = [dut.mode_i, dut.key_i, dut.data_i]
  108. _output = dut.data_o
  109. # DUT input side
  110. vai_driver = VaiDriver(dut.clk_i, _input, dut.valid_i, dut.accept_o)
  111. vai_in_queue = cocotb.queue.Queue()
  112. vai_in_monitor = VaiMonitor(dut.clk_i, _input, dut.valid_i, dut.accept_o, vai_in_queue)
  113. # DUT output side
  114. vai_receiver = VaiReceiver(dut.clk_i, dut.data_o, dut.valid_o, dut.accept_i)
  115. vai_out_monitor = VaiMonitor(dut.clk_i, _output, dut.valid_o, dut.accept_i)
  116. cr = constraints()
  117. cg = covergroup()
  118. cocotb.start_soon(cg_sample(cg, vai_in_queue))
  119. # Drive input defaults (setimmediatevalue to avoid x asserts)
  120. dut.mode_i.setimmediatevalue(0)
  121. dut.key_i.setimmediatevalue(0)
  122. dut.data_i.setimmediatevalue(0)
  123. dut.valid_i.setimmediatevalue(0)
  124. dut.accept_i.setimmediatevalue(0)
  125. clock = Clock(dut.clk_i, 10, units="ns") # Create a 10 ns period clock
  126. cocotb.start_soon(clock.start()) # Start the clock
  127. # Execution will block until reset_dut has completed
  128. await reset_dut(reset, 100)
  129. dut._log.info("Released reset")
  130. # Test 10 AES calculations
  131. for i in range(10):
  132. # Get now random stimuli
  133. cr.randomize()
  134. _key = cr.key
  135. _data = cr.data
  136. await clkedge
  137. # Drive AES inputs
  138. await vai_driver.send([1, _key, _data])
  139. # Calc reference data
  140. _aes = AES.new(_key.to_bytes(16, 'big'), AES.MODE_ECB)
  141. _ref = _aes.decrypt(_data.to_bytes(16, 'big'))
  142. # Get DUT output data
  143. _rec = await vai_receiver.receive()
  144. # Equivalence check
  145. assert _rec.buff == _ref, \
  146. f"Decrypt error, got 0x{_rec.buff.hex()}, expected 0x{_ref.hex()}"
  147. with open('results/tb_aes_fcover.txt', 'w', encoding='utf-8') as f:
  148. f.write(vsc.get_coverage_report())