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.

232 lines
7.4 KiB

  1. from cocotb.queue import Queue
  2. from cocotb.triggers import RisingEdge, Timer, Combine
  3. from pyuvm import *
  4. import cocotb
  5. import pyuvm
  6. import vsc
  7. from vsc import get_coverage_report
  8. from VaiBfm import VaiBfm, Mode
  9. from Coverage import constraints, covergroup
  10. from Crypto.Cipher import AES
  11. @pyuvm.test()
  12. class AesTest(uvm_test):
  13. def build_phase(self):
  14. self.env = AesEnv("env", self)
  15. def end_of_elaboration_phase(self):
  16. self.test_all = TestAllSeq.create("test_all")
  17. async def run_phase(self):
  18. self.raise_objection()
  19. await self.test_all.start()
  20. self.drop_objection()
  21. @pyuvm.test()
  22. class ParallelTest(AesTest):
  23. def end_of_elaboration_phase(self):
  24. uvm_factory().set_type_override_by_type(TestAllSeq, TestAllParallelSeq)
  25. return super().end_of_elaboration_phase()
  26. # Virtual sequence that starts other sequences
  27. class TestAllSeq(uvm_sequence):
  28. async def body(self):
  29. # get the sequencer handle
  30. seqr = ConfigDB().get(None, "", "SEQR")
  31. enc_rand_seq = EncRandSeq("enc_random")
  32. dec_rand_seq = DecRandSeq("dec_random")
  33. await enc_rand_seq.start(seqr)
  34. await dec_rand_seq.start(seqr)
  35. # Running encryption and decryption sequences in parallel
  36. class TestAllParallelSeq(uvm_sequence):
  37. async def body(self):
  38. seqr = ConfigDB().get(None, "", "SEQR")
  39. enc_rand_seq = EncRandSeq("enc_random")
  40. dec_rand_seq = DecRandSeq("dec_random")
  41. enc_rand_task = cocotb.start_soon(enc_rand_seq.start(seqr))
  42. dec_rand_task = cocotb.start_soon(dec_rand_seq.start(seqr))
  43. await Combine(enc_rand_task, dec_rand_task)
  44. # Sequence item which holds the stimuli for one operation
  45. class AesSeqItem(uvm_sequence_item):
  46. def __init__(self, name, mode, key, data):
  47. super().__init__(name)
  48. self.mode = mode
  49. self.key = key
  50. self.data = data
  51. def __eq__(self, other):
  52. same = self.mode == other.mode and self.key == other.key and self.data == other.data
  53. return same
  54. def __str__(self):
  55. return f"{self.get_name()} : Mode: 0b{self.mode:01x} \
  56. Key: 0x{self.key:016x} Data: 0x{self.data:016x}"
  57. # Abstract basis sequence class
  58. # set_operands() has to be implemented by class that inherits from this class
  59. class BaseSeq(uvm_sequence):
  60. async def body(self):
  61. self.cr = constraints()
  62. for _ in range(20):
  63. aes_tr = AesSeqItem("aes_tr", 0, 0, 0)
  64. await self.start_item(aes_tr)
  65. self.set_operands(aes_tr)
  66. await self.finish_item(aes_tr)
  67. def set_operands(self, tr):
  68. pass
  69. # Sequence for encryption tests with random stimuli
  70. class EncRandSeq(BaseSeq):
  71. def set_operands(self, tr):
  72. self.cr.randomize()
  73. tr.mode = 0
  74. tr.key = self.cr.key
  75. tr.data = self.cr.data
  76. # Sequence for decryption tests with random stimuli
  77. class DecRandSeq(BaseSeq):
  78. def set_operands(self, tr):
  79. self.cr.randomize()
  80. tr.mode = 1
  81. tr.key = self.cr.key
  82. tr.data = self.cr.data
  83. class Driver(uvm_driver):
  84. def build_phase(self):
  85. self.ap = uvm_analysis_port("ap", self)
  86. def start_of_simulation_phase(self):
  87. self.bfm = VaiBfm()
  88. async def launch_tb(self):
  89. await self.bfm.reset()
  90. self.bfm.start_tasks()
  91. async def run_phase(self):
  92. await self.launch_tb()
  93. while True:
  94. op = await self.seq_item_port.get_next_item()
  95. await self.bfm.send_op(op.mode, op.key, op.data)
  96. result = await self.bfm.get_output()
  97. self.ap.write(result)
  98. self.seq_item_port.item_done()
  99. class Scoreboard(uvm_component):
  100. def build_phase(self):
  101. self.input_fifo = uvm_tlm_analysis_fifo("input_fifo", self)
  102. self.output_fifo = uvm_tlm_analysis_fifo("output_fifo", self)
  103. self.input_get_port = uvm_get_port("input_get_port", self)
  104. self.output_get_port = uvm_get_port("output_get_port", self)
  105. self.input_export = self.input_fifo.analysis_export
  106. self.output_export = self.output_fifo.analysis_export
  107. self.passed = True
  108. def connect_phase(self):
  109. self.input_get_port.connect(self.input_fifo.get_export)
  110. self.output_get_port.connect(self.output_fifo.get_export)
  111. def check_phase(self):
  112. while self.output_get_port.can_get():
  113. _, result = self.output_get_port.try_get()
  114. op_success, op = self.input_get_port.try_get()
  115. if not op_success:
  116. self.logger.critical(f"result {result} had no input operation")
  117. else:
  118. (mode, key, data) = op
  119. aes = AES.new(key.buff, AES.MODE_ECB)
  120. if not mode:
  121. reference = aes.encrypt(data.buff)
  122. else:
  123. reference = aes.decrypt(data.buff)
  124. if result.buff == reference:
  125. self.logger.info(f"PASSED: {Mode(mode).name} {data.hex()} with key "
  126. f"{key.hex()} = {result.hex()}")
  127. else:
  128. self.logger.error(f"FAILED: {Mode(mode).name} {data.hex()} with key "
  129. f"{key.hex()} = 0x{result.hex()}, "
  130. f"expected {reference.hex()}")
  131. self.passed = False
  132. def report_phase(self):
  133. assert self.passed, "Test failed"
  134. class Monitor(uvm_component):
  135. def __init__(self, name, parent, method_name):
  136. super().__init__(name, parent)
  137. self.bfm = VaiBfm()
  138. self.get_method = getattr(self.bfm, method_name)
  139. def build_phase(self):
  140. self.ap = uvm_analysis_port("ap", self)
  141. async def run_phase(self):
  142. while True:
  143. datum = await self.get_method()
  144. self.logger.debug(f"MONITORED {datum}")
  145. self.ap.write(datum)
  146. # Coverage collector and checker
  147. class Coverage(uvm_subscriber):
  148. def start_of_simulation_phase(self):
  149. self.cg = covergroup()
  150. try:
  151. self.disable_errors = ConfigDB().get(
  152. self, "", "DISABLE_COVERAGE_ERRORS")
  153. except UVMConfigItemNotFound:
  154. self.disable_errors = False
  155. def write(self, data):
  156. (mode, key, _) = data
  157. self.cg.sample(mode, key)
  158. def report_phase(self):
  159. if not self.disable_errors:
  160. if self.cg.get_coverage() != 100.0:
  161. self.logger.warning(
  162. f"Functional coverage incomplete.")
  163. else:
  164. self.logger.info("Covered all operations")
  165. with open('results/tb_aes_fcover.txt', 'a', encoding='utf-8') as f:
  166. f.write(get_coverage_report(details=True))
  167. vsc.write_coverage_db('results/tb_aes_fcover.xml')
  168. # AES test bench environment
  169. # Creates instances of components and connects them
  170. class AesEnv(uvm_env):
  171. def build_phase(self):
  172. self.seqr = uvm_sequencer("seqr", self)
  173. ConfigDB().set(None, "*", "SEQR", self.seqr)
  174. self.driver = Driver.create("driver", self)
  175. self.input_mon = Monitor("input_mon", self, "get_input")
  176. self.coverage = Coverage("coverage", self)
  177. self.scoreboard = Scoreboard("scoreboard", self)
  178. def connect_phase(self):
  179. self.driver.seq_item_port.connect(self.seqr.seq_item_export)
  180. self.input_mon.ap.connect(self.scoreboard.input_export)
  181. self.input_mon.ap.connect(self.coverage.analysis_export)
  182. self.driver.ap.connect(self.scoreboard.output_export)