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.

117 lines
3.8 KiB

  1. import cocotb
  2. from cocotb.triggers import FallingEdge, 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) 2022 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(self.dut.clk_i, 10, units="ns") # Create a 10 ns period clock
  29. cocotb.start_soon(self.clock.start())
  30. # Reset coroutine
  31. async def reset(self):
  32. self.dut.reset_i.value = 0
  33. self.dut.valid_i.value = 0
  34. self.dut.mode_i.value = 0
  35. self.dut.key_i.value = 0
  36. self.dut.data_i.value = 0
  37. self.dut.accept_i.value = 0
  38. await Timer(100, units="ns")
  39. self.dut.reset_i.value = 1
  40. # VAI input driver
  41. async def __driver(self):
  42. self.dut.valid_i.value = 0
  43. self.dut.key_i.value = 0
  44. self.dut.data_i.value = 0
  45. while True:
  46. await RisingEdge(self.dut.clk_i)
  47. if not self.dut.valid_i.value:
  48. try:
  49. (mode, key, data) = self.driver_queue.get_nowait()
  50. self.dut.mode_i.value = mode
  51. self.dut.key_i.value = key
  52. self.dut.data_i.value = data
  53. self.dut.valid_i.value = 1
  54. except QueueEmpty:
  55. continue
  56. else:
  57. if self.dut.accept_o.value:
  58. self.dut.valid_i.value = 0
  59. # VAI output receiver
  60. # We ignore data out, we use the output monitor instead
  61. async def __receiver(self):
  62. self.dut.accept_i.value = 0
  63. while True:
  64. await RisingEdge(self.dut.clk_i)
  65. if self.dut.valid_o.value and not self.dut.accept_i.value:
  66. self.dut.accept_i.value = 1
  67. else:
  68. self.dut.accept_i.value = 0
  69. # VAI input monitor
  70. async def __in_monitor(self):
  71. while True:
  72. await RisingEdge(self.dut.clk_i)
  73. if self.dut.valid_i.value and self.dut.accept_o.value:
  74. in_tuple = (self.dut.mode_i.value,
  75. self.dut.key_i.value,
  76. self.dut.data_i.value)
  77. self.in_monitor_queue.put_nowait(in_tuple)
  78. # VAI output monitor
  79. async def __out_monitor(self):
  80. while True:
  81. await RisingEdge(self.dut.clk_i)
  82. if self.dut.valid_o.value and self.dut.accept_i.value:
  83. out_data = self.dut.data_o.value
  84. self.out_monitor_queue.put_nowait(out_data)
  85. # Launching the coroutines using start_soon
  86. def start_tasks(self):
  87. cocotb.start_soon(self.__driver())
  88. cocotb.start_soon(self.__receiver())
  89. cocotb.start_soon(self.__in_monitor())
  90. cocotb.start_soon(self.__out_monitor())
  91. # The get_input() coroutine returns the next VAI input
  92. async def get_input(self):
  93. data = await self.in_monitor_queue.get()
  94. return data
  95. # The get_output() coroutine returns the next VAI output
  96. async def get_output(self):
  97. data = await self.out_monitor_queue.get()
  98. return data
  99. # send_op puts the VAI input operation into the driver queue
  100. async def send_op(self, mode, key, data):
  101. await self.driver_queue.put((mode, key, data))