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.

181 lines
5.5 KiB

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