| @ -0,0 +1,4 @@ | |||||
| #!/bin/bash | |||||
| python3 -m pip install -r requirements.txt | |||||
| python3 -m pip install cocotbext-wishbone/ | |||||
| @ -0,0 +1,11 @@ | |||||
| #!/bin/bash | |||||
| if [ ! -d cocotbext-wishbone/.git ]; then | |||||
| git clone https://github.com/wallento/cocotbext-wishbone.git | |||||
| cd cocotbext-wishbone && patch < ../setup.py.patch | |||||
| cd .. | |||||
| fi | |||||
| if [ ! -d libvhdl/.git ]; then | |||||
| git clone https://github.com/tmeissner/libvhdl.git | |||||
| fi | |||||
| @ -0,0 +1,17 @@ | |||||
| # basic bus model | |||||
| cocotb-bus | |||||
| # bus functional models | |||||
| cocotbext-axi | |||||
| cocotbext-pcie | |||||
| cocotbext-eth | |||||
| cocotbext-uart | |||||
| cocotbext-spi | |||||
| # verification libraries | |||||
| cocotb-coverage | |||||
| pyvsc | |||||
| pyuvm | |||||
| # utilities | |||||
| wavedrom | |||||
| @ -0,0 +1,23 @@ | |||||
| --- setup.py 2022-02-07 11:35:49.632921471 +0100 | |||||
| +++ setup.py.new 2022-02-07 11:40:11.084930778 +0100 | |||||
| @@ -5,19 +5,13 @@ | |||||
| setuptools.setup( | |||||
| name="cocotbext-wishbone", | |||||
| - use_scm_version={ | |||||
| - "relative_to": __file__, | |||||
| - "write_to": "cocotbext/wishbone/version.py", | |||||
| - }, | |||||
| + version="0.2.1", | |||||
| author="Staf Verhaegen, Mathias Kreider", | |||||
| author_email="staf@stafverhaegen.be, m.kreider@gsi.de", | |||||
| description="Cocotb Wishbone modules", | |||||
| long_description=long_description, | |||||
| packages=["cocotbext.wishbone"], | |||||
| install_requires=['cocotb>=1.6.0', 'cocotb_bus'], | |||||
| - setup_requires=[ | |||||
| - 'setuptools_scm', | |||||
| - ], | |||||
| classifiers=[ | |||||
| "Programming Language :: Python :: 3", | |||||
| "License :: OSI Approved :: BSD License", | |||||
| @ -0,0 +1,48 @@ | |||||
| # Default test | |||||
| DUT ?= uarttx | |||||
| # Test related variables | |||||
| ifeq (${DUT}, uarttx) | |||||
| MODULE := tb_uart | |||||
| TOPLEVEL := ${DUT} | |||||
| else ifeq (${DUT}, uartrx) | |||||
| MODULE := tb_uart | |||||
| TOPLEVEL := ${DUT} | |||||
| else ifeq (${DUT}, wishbone) | |||||
| MODULE := tb_wishbone | |||||
| TOPLEVEL := wishboneslavee | |||||
| SIM_ARGS := -gSimulation=true | |||||
| else | |||||
| $(error ${DUT} not available) | |||||
| endif | |||||
| # Simulator (GHDL) & RTL related | |||||
| SIM := ghdl | |||||
| TOPLEVEL_LANG := vhdl | |||||
| VHDL_SOURCES_libvhdl := ../libvhdl/common/UtilsP.vhd | |||||
| VHDL_SOURCES := ../libvhdl/syn/*.vhd | |||||
| SIM_BUILD := work | |||||
| COMPILE_ARGS := --std=08 | |||||
| SIM_ARGS += \ | |||||
| --wave=results/${TOPLEVEL}.ghw \ | |||||
| --psl-report=results/${TOPLEVEL}_psl.json \ | |||||
| --vpi-trace=results/${TOPLEVEL}_vpi.log | |||||
| # Cocotb related | |||||
| TESTCASE := test_${DUT} | |||||
| COCOTB_LOG_LEVEL := DEBUG | |||||
| CUSTOM_COMPILE_DEPS := results | |||||
| COCOTB_RESULTS_FILE := results/${TOPLEVEL}.xml | |||||
| include $(shell cocotb-config --makefiles)/Makefile.sim | |||||
| results: | |||||
| mkdir -p results | |||||
| .PHONY: clean | |||||
| clean:: | |||||
| rm -rf *.o __pycache__ uarttx uartrx results | |||||
| @ -0,0 +1,130 @@ | |||||
| import logging | |||||
| from cocotb.triggers import FallingEdge, RisingEdge, Timer, ReadOnly | |||||
| class Uart: | |||||
| """UART base class""" | |||||
| def __init__(self, txrx, clock, div, bits, parity, *args, **kwargs): | |||||
| self._version = "0.0.1" | |||||
| self.log = logging.getLogger(f"cocotb.{txrx._path}") | |||||
| self._txrx = txrx | |||||
| self._clock = clock | |||||
| self._div = div | |||||
| self._bits = bits | |||||
| self._par = parity | |||||
| self._clkedge = RisingEdge(self._clock) | |||||
| async def _wait_cycle(self): | |||||
| for x in range(self._div): | |||||
| await self._clkedge | |||||
| @staticmethod | |||||
| def odd_parity(data): | |||||
| parity = True | |||||
| while data: | |||||
| parity = not parity | |||||
| data = data & (data - 1) | |||||
| return int(parity) | |||||
| class UartReceiver(Uart): | |||||
| def __init__(self, txrx, clock, div, bits, parity, *args, **kwargs): | |||||
| super().__init__(txrx, clock, div, bits, parity, *args, **kwargs) | |||||
| self.log.info("UART receiver") | |||||
| self.log.info(" cocotbext-uart version %s", self._version) | |||||
| self.log.info(" Copyright (c) 2022 Torsten Meissner") | |||||
| async def receive(self): | |||||
| """Receive and return one UART frame""" | |||||
| # Wait for frame start | |||||
| await FallingEdge(self._txrx) | |||||
| # Consume start bit | |||||
| await self._get_start_bit() | |||||
| # Receive data bits | |||||
| self._rec = 0 | |||||
| for x in range(self._bits): | |||||
| await self._wait_cycle() | |||||
| await ReadOnly() | |||||
| self._rec |= bool(self._txrx.value.integer) << x | |||||
| if self._par: | |||||
| # Consume parity bit | |||||
| await self._get_parity_bit() | |||||
| # Consume stop bit | |||||
| await self._get_stop_bit() | |||||
| self.log.info("Received data: %s", hex(self._rec)) | |||||
| return self._rec | |||||
| async def _get_start_bit(self): | |||||
| """Consume and check start bit""" | |||||
| for x in range(int(self._div/2)): | |||||
| await self._clkedge | |||||
| await ReadOnly() | |||||
| if self._txrx.value == 1: | |||||
| self.log.warning("Start bit set") | |||||
| async def _get_stop_bit(self): | |||||
| """Consume and check stop bit""" | |||||
| await self._wait_cycle() | |||||
| await ReadOnly() | |||||
| if self._txrx.value == 0: | |||||
| self.log.warning("Stop bit not set") | |||||
| async def _get_parity_bit(self): | |||||
| """Consume and check parity bit""" | |||||
| await self._wait_cycle() | |||||
| await ReadOnly() | |||||
| if self.odd_parity(self._rec) != self._txrx.value: | |||||
| self.log.warning("Parity wrong") | |||||
| class UartDriver(Uart): | |||||
| def __init__(self, txrx, clock, div, bits, parity, *args, **kwargs): | |||||
| super().__init__(txrx, clock, div, bits, parity, *args, **kwargs) | |||||
| self.log.info("UART sender") | |||||
| self.log.info(" cocotbext-uart version %s", self._version) | |||||
| self.log.info(" Copyright (c) 2022 Torsten Meissner") | |||||
| # Drive input defaults (setimmediatevalue to avoid x asserts) | |||||
| self._txrx.setimmediatevalue(1) | |||||
| async def send(self, data): | |||||
| """Send one UART frame""" | |||||
| self._data = data; | |||||
| self.log.info("Sending data: %s", hex(self._data)) | |||||
| # Send start bit | |||||
| await self._send_bit(0) | |||||
| # Send data bits | |||||
| for x in range(self._bits): | |||||
| self._txrx.value = (self._data >> x) & 1 | |||||
| await self._wait_cycle() | |||||
| if self._par: | |||||
| # Send parity bit | |||||
| await self._send_bit(self.odd_parity(self._data)) | |||||
| # Consume stop bit | |||||
| await self._send_bit(1) | |||||
| async def _send_bit(self, data): | |||||
| self._txrx.value = data | |||||
| await self._wait_cycle() | |||||
| @ -0,0 +1,85 @@ | |||||
| import logging | |||||
| from cocotb.triggers import FallingEdge, RisingEdge, Timer, ReadOnly | |||||
| class Vai: | |||||
| """VAI base class""" | |||||
| def __init__(self, clock, data, valid, accept, *args, **kwargs): | |||||
| self._version = "0.0.1" | |||||
| self.log = logging.getLogger(f"cocotb.{data._path}") | |||||
| self._data = data | |||||
| self._valid = valid | |||||
| self._accept = accept | |||||
| self._clock = clock | |||||
| self._clkedge = RisingEdge(self._clock) | |||||
| class VaiDriver(Vai): | |||||
| """Valid-Accept Driver""" | |||||
| def __init__(self, clock, data, valid, accept, *args, **kwargs): | |||||
| super().__init__(clock, data, valid, accept, *args, **kwargs) | |||||
| self.log.info("Valid-accept driver") | |||||
| self.log.info(" cocotbext-vai version %s", self._version) | |||||
| self.log.info(" Copyright (c) 2022 Torsten Meissner") | |||||
| # Drive input defaults (setimmediatevalue to avoid x asserts) | |||||
| self._data.setimmediatevalue(0) | |||||
| self._valid.setimmediatevalue(0) | |||||
| async def send(self, data, sync=True): | |||||
| if sync: | |||||
| await self._clkedge | |||||
| self.log.info("Sending data: %s", hex(data)) | |||||
| self._valid.value = 1 | |||||
| self._data.value = data | |||||
| while True: | |||||
| await ReadOnly() | |||||
| if self._accept.value: | |||||
| break | |||||
| await self._clkedge | |||||
| await self._clkedge | |||||
| self._valid.value = 0 | |||||
| class VaiReceiver(Vai): | |||||
| """Valid-Accept Receiver""" | |||||
| def __init__(self, clock, data, valid, accept, *args, **kwargs): | |||||
| super().__init__(clock, data, valid, accept, *args, **kwargs) | |||||
| self.log.info("Valid-accept receiver") | |||||
| self.log.info(" cocotbext-vai version %s", self._version) | |||||
| self.log.info(" Copyright (c) 2022 Torsten Meissner") | |||||
| # Drive input defaults (setimmediatevalue to avoid x asserts) | |||||
| self._accept.setimmediatevalue(0) | |||||
| async def receive(self, sync=True): | |||||
| if sync: | |||||
| await self._clkedge | |||||
| while True: | |||||
| await ReadOnly() | |||||
| if self._valid.value: | |||||
| break | |||||
| await self._clkedge | |||||
| await self._clkedge | |||||
| self._accept.value = 1 | |||||
| _rec = self._data.value | |||||
| self.log.info("Received data: %s", hex(_rec)) | |||||
| await self._clkedge | |||||
| self._accept.value = 0 | |||||
| return _rec | |||||
| @ -0,0 +1,94 @@ | |||||
| # test_uart.py | |||||
| import logging | |||||
| import random | |||||
| import cocotb | |||||
| import wavedrom | |||||
| from Uart import UartDriver, UartReceiver | |||||
| from Vai import VaiDriver, VaiReceiver | |||||
| from cocotb.clock import Clock | |||||
| from cocotb.triggers import FallingEdge, RisingEdge, Timer, ReadOnly | |||||
| from cocotb.wavedrom import Wavedrom, trace | |||||
| # Reset coroutine | |||||
| async def reset_dut(reset_n, duration_ns): | |||||
| reset_n.value = 0 | |||||
| await Timer(duration_ns, units="ns") | |||||
| reset_n.value = 1 | |||||
| def wave2svg(wave, file): | |||||
| svg = wavedrom.render(wave) | |||||
| svg.saveas(file) | |||||
| @cocotb.test() | |||||
| async def test_uarttx(dut): | |||||
| """ First simple test """ | |||||
| clkedge = RisingEdge(dut.clk_i) | |||||
| # Connect reset | |||||
| reset_n = dut.reset_n_i | |||||
| # Instantiate VAI driver | |||||
| vai_driver = VaiDriver(dut.clk_i, dut.data_i, dut.valid_i, dut.accept_o) | |||||
| # Instantiate UART receiver | |||||
| uart_receiver = UartReceiver(dut.tx_o, dut.clk_i, 10, 8, True); | |||||
| # Drive input defaults (setimmediatevalue to avoid x asserts) | |||||
| dut.data_i.setimmediatevalue(0) | |||||
| dut.valid_i.setimmediatevalue(0) | |||||
| clock = Clock(dut.clk_i, 10, units="ns") # Create a 10 ns period clock | |||||
| cocotb.start_soon(clock.start()) # Start the clock | |||||
| # Execution will block until reset_dut has completed | |||||
| dut._log.info("Hold reset") | |||||
| await reset_dut(reset_n, 100) | |||||
| dut._log.info("Released reset") | |||||
| # Test 10 UART transmissions | |||||
| for i in range(10): | |||||
| await clkedge | |||||
| val = random.randint(0, 255) | |||||
| await vai_driver.send(val) | |||||
| rec = await uart_receiver.receive(); | |||||
| assert rec == val, "UART sent data was incorrect on the {}th cycle".format(i) | |||||
| @cocotb.test() | |||||
| async def test_uartrx(dut): | |||||
| """ First simple test """ | |||||
| clkedge = RisingEdge(dut.clk_i) | |||||
| # Connect reset | |||||
| reset_n = dut.reset_n_i | |||||
| # Instantiate UART driver | |||||
| uart_driver = UartDriver(dut.rx_i, dut.clk_i, 10, 8, True); | |||||
| # Instantiate VAI receiver | |||||
| vai_receiver = VaiReceiver(dut.clk_i, dut.data_o, dut.valid_o, dut.accept_i) | |||||
| # Drive input defaults (setimmediatevalue to avoid x asserts) | |||||
| dut.rx_i.setimmediatevalue(1) | |||||
| dut.accept_i.setimmediatevalue(0) | |||||
| clock = Clock(dut.clk_i, 10, units="ns") # Create a 1 us period clock | |||||
| cocotb.start_soon(clock.start()) # Start the clock | |||||
| # Execution will block until reset_dut has completed | |||||
| dut._log.info("Hold reset") | |||||
| await reset_dut(reset_n, 100) | |||||
| dut._log.info("Released reset") | |||||
| # Test 10 UART transmissions | |||||
| for i in range(10): | |||||
| await clkedge | |||||
| val = random.randint(0, 255) | |||||
| await uart_driver.send(val) | |||||
| rec = await vai_receiver.receive(); | |||||
| assert rec == val, "UART received data was incorrect on the {}th cycle".format(i) | |||||