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.

130 lines
3.5 KiB

  1. import logging
  2. from cocotb.triggers import FallingEdge, RisingEdge, Timer, ReadOnly
  3. class Uart:
  4. """UART base class"""
  5. def __init__(self, txrx, clock, div, bits, parity, *args, **kwargs):
  6. self._version = "0.0.1"
  7. self.log = logging.getLogger(f"cocotb.{txrx._path}")
  8. self._txrx = txrx
  9. self._clock = clock
  10. self._div = div
  11. self._bits = bits
  12. self._par = parity
  13. self._clkedge = RisingEdge(self._clock)
  14. async def _wait_cycle(self):
  15. for x in range(self._div):
  16. await self._clkedge
  17. @staticmethod
  18. def odd_parity(data):
  19. parity = True
  20. while data:
  21. parity = not parity
  22. data = data & (data - 1)
  23. return int(parity)
  24. class UartReceiver(Uart):
  25. def __init__(self, txrx, clock, div, bits, parity, *args, **kwargs):
  26. super().__init__(txrx, clock, div, bits, parity, *args, **kwargs)
  27. self.log.info("UART receiver")
  28. self.log.info(" cocotbext-uart version %s", self._version)
  29. self.log.info(" Copyright (c) 2022 Torsten Meissner")
  30. async def receive(self):
  31. """Receive and return one UART frame"""
  32. # Wait for frame start
  33. await FallingEdge(self._txrx)
  34. # Consume start bit
  35. await self._get_start_bit()
  36. # Receive data bits
  37. self._rec = 0
  38. for x in range(self._bits):
  39. await self._wait_cycle()
  40. await ReadOnly()
  41. self._rec |= bool(self._txrx.value.integer) << x
  42. if self._par:
  43. # Consume parity bit
  44. await self._get_parity_bit()
  45. # Consume stop bit
  46. await self._get_stop_bit()
  47. self.log.info("Received data: %s", hex(self._rec))
  48. return self._rec
  49. async def _get_start_bit(self):
  50. """Consume and check start bit"""
  51. for x in range(int(self._div/2)):
  52. await self._clkedge
  53. await ReadOnly()
  54. if self._txrx.value == 1:
  55. self.log.warning("Start bit set")
  56. async def _get_stop_bit(self):
  57. """Consume and check stop bit"""
  58. await self._wait_cycle()
  59. await ReadOnly()
  60. if self._txrx.value == 0:
  61. self.log.warning("Stop bit not set")
  62. async def _get_parity_bit(self):
  63. """Consume and check parity bit"""
  64. await self._wait_cycle()
  65. await ReadOnly()
  66. if self.odd_parity(self._rec) != self._txrx.value:
  67. self.log.warning("Parity wrong")
  68. class UartDriver(Uart):
  69. def __init__(self, txrx, clock, div, bits, parity, *args, **kwargs):
  70. super().__init__(txrx, clock, div, bits, parity, *args, **kwargs)
  71. self.log.info("UART sender")
  72. self.log.info(" cocotbext-uart version %s", self._version)
  73. self.log.info(" Copyright (c) 2022 Torsten Meissner")
  74. # Drive input defaults (setimmediatevalue to avoid x asserts)
  75. self._txrx.setimmediatevalue(1)
  76. async def send(self, data):
  77. """Send one UART frame"""
  78. self._data = data;
  79. self.log.info("Sending data: %s", hex(self._data))
  80. # Send start bit
  81. await self._send_bit(0)
  82. # Send data bits
  83. for x in range(self._bits):
  84. self._txrx.value = (self._data >> x) & 1
  85. await self._wait_cycle()
  86. if self._par:
  87. # Send parity bit
  88. await self._send_bit(self.odd_parity(self._data))
  89. # Consume stop bit
  90. await self._send_bit(1)
  91. async def _send_bit(self, data):
  92. self._txrx.value = data
  93. await self._wait_cycle()