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.

121 lines
3.8 KiB

  1. import cocotb
  2. from cocotb.triggers import RisingEdge, Timer
  3. from cocotb.queue import QueueEmpty, Queue
  4. from cocotb.clock import Clock
  5. import logging
  6. import enum
  7. import pyuvm
  8. # Logger setup
  9. logging.basicConfig(level=logging.NOTSET)
  10. logger = logging.getLogger()
  11. logger.setLevel(logging.DEBUG)
  12. # AES mode enum
  13. @enum.unique
  14. class Mode(enum.IntEnum):
  15. Encrypt = 0
  16. Decrypt = 1
  17. # VAI BFM with queues for
  18. class VaiBfm(metaclass=pyuvm.Singleton):
  19. """Valid-Accept Bfm"""
  20. def __init__(self):
  21. self.log = logging.getLogger()
  22. self.log.info("Valid-accept BFM")
  23. self.log.info(" Copyright (c) 2024 Torsten Meissner")
  24. self.dut = cocotb.top
  25. self.driver_queue = Queue(maxsize=1)
  26. self.in_monitor_queue = Queue(maxsize=0)
  27. self.out_monitor_queue = Queue(maxsize=0)
  28. self.clock = Clock(
  29. self.dut.clk_i, 10, units="ns"
  30. ) # Create a 10 ns period clock
  31. cocotb.start_soon(self.clock.start())
  32. # Reset coroutine
  33. async def reset(self):
  34. self.dut.reset_i.value = 0
  35. self.dut.valid_i.value = 0
  36. self.dut.mode_i.value = 0
  37. self.dut.key_i.value = 0
  38. self.dut.data_i.value = 0
  39. self.dut.accept_i.value = 0
  40. await Timer(100, units="ns")
  41. self.dut.reset_i.value = 1
  42. # VAI input driver
  43. async def __driver(self):
  44. self.dut.valid_i.value = 0
  45. self.dut.key_i.value = 0
  46. self.dut.data_i.value = 0
  47. while True:
  48. await RisingEdge(self.dut.clk_i)
  49. if not self.dut.valid_i.value:
  50. try:
  51. (mode, key, data) = self.driver_queue.get_nowait()
  52. self.dut.mode_i.value = mode
  53. self.dut.key_i.value = key
  54. self.dut.data_i.value = data
  55. self.dut.valid_i.value = 1
  56. except QueueEmpty:
  57. continue
  58. else:
  59. if self.dut.accept_o.value:
  60. self.dut.valid_i.value = 0
  61. # VAI output receiver
  62. # We ignore data out, we use the output monitor instead
  63. async def __receiver(self):
  64. self.dut.accept_i.value = 0
  65. while True:
  66. await RisingEdge(self.dut.clk_i)
  67. if self.dut.valid_o.value and not self.dut.accept_i.value:
  68. self.dut.accept_i.value = 1
  69. else:
  70. self.dut.accept_i.value = 0
  71. # VAI input monitor
  72. async def __in_monitor(self):
  73. while True:
  74. await RisingEdge(self.dut.clk_i)
  75. if self.dut.valid_i.value and self.dut.accept_o.value:
  76. in_tuple = (
  77. self.dut.mode_i.value,
  78. self.dut.key_i.value,
  79. self.dut.data_i.value,
  80. )
  81. self.in_monitor_queue.put_nowait(in_tuple)
  82. # VAI output monitor
  83. async def __out_monitor(self):
  84. while True:
  85. await RisingEdge(self.dut.clk_i)
  86. if self.dut.valid_o.value and self.dut.accept_i.value:
  87. out_data = self.dut.data_o.value
  88. self.out_monitor_queue.put_nowait(out_data)
  89. # Launching the coroutines using start_soon
  90. def start_tasks(self):
  91. cocotb.start_soon(self.__driver())
  92. cocotb.start_soon(self.__receiver())
  93. cocotb.start_soon(self.__in_monitor())
  94. cocotb.start_soon(self.__out_monitor())
  95. # The get_input() coroutine returns the next VAI input
  96. async def get_input(self):
  97. data = await self.in_monitor_queue.get()
  98. return data
  99. # The get_output() coroutine returns the next VAI output
  100. async def get_output(self):
  101. data = await self.out_monitor_queue.get()
  102. return data
  103. # send_op puts the VAI input operation into the driver queue
  104. async def send_op(self, mode, key, data):
  105. await self.driver_queue.put((mode, key, data))