Browse Source

initial commit of 1st simple UART tests

master
T. Meissner 2 years ago
commit
f49a46399a
8 changed files with 412 additions and 0 deletions
  1. +4
    -0
      docker-setup.sh
  2. +11
    -0
      env-setup.sh
  3. +17
    -0
      requirements.txt
  4. +23
    -0
      setup.py.patch
  5. +48
    -0
      tests/Makefile
  6. +130
    -0
      tests/Uart.py
  7. +85
    -0
      tests/Vai.py
  8. +94
    -0
      tests/tb_uart.py

+ 4
- 0
docker-setup.sh View File

@ -0,0 +1,4 @@
#!/bin/bash
python3 -m pip install -r requirements.txt
python3 -m pip install cocotbext-wishbone/

+ 11
- 0
env-setup.sh View File

@ -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

+ 17
- 0
requirements.txt View File

@ -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

+ 23
- 0
setup.py.patch View File

@ -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",

+ 48
- 0
tests/Makefile View File

@ -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

+ 130
- 0
tests/Uart.py View File

@ -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()

+ 85
- 0
tests/Vai.py View File

@ -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

+ 94
- 0
tests/tb_uart.py View File

@ -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)

Loading…
Cancel
Save