Browse Source

Add simple example verifying a wishbone slave

master
T. Meissner 3 years ago
parent
commit
f656244c55
5 changed files with 220 additions and 5 deletions
  1. +11
    -2
      README.md
  2. +1
    -1
      docker-setup.sh
  3. +4
    -2
      tests/Makefile
  4. +125
    -0
      tests/Sram.py
  5. +79
    -0
      tests/tb_wishbone.py

+ 11
- 2
README.md View File

@ -20,7 +20,7 @@ Have fun!
``` ```
$ git clone https://git.goodcleanfun.de/tmeissner/cocotb_with_ghdl.git $ git clone https://git.goodcleanfun.de/tmeissner/cocotb_with_ghdl.git
$ cd cocotb_with_ghdl $ cd cocotb_with_ghdl
$ git clone https://git.goodcleanfun.de/tmeissner/libvhdl.git
$ ./env-setup.sh
$ docker run --rm -ti --volume=$(pwd):/build -e DISPLAY=$DISPLAY \ $ docker run --rm -ti --volume=$(pwd):/build -e DISPLAY=$DISPLAY \
--volume /tmp/.X11-unix:/tmp/.X11-unix hdlc/sim:scipy /bin/bash --volume /tmp/.X11-unix:/tmp/.X11-unix hdlc/sim:scipy /bin/bash
$ ./docker-setup.sh $ ./docker-setup.sh
@ -110,4 +110,13 @@ make[1]: Leaving directory '/build/tests
### UART ### UART
Simple tests of UART transmitter & receiver of the *libvhdl* project
Simple tests of UART transmitter & receiver of the *libvhdl* project
* `make DUT=uarttx` or `make`
* `make DUT=uartrx`
### Wishbone
Simple tests of Wishbone slave of the *libvhdl* project
* `make DUT=wishbone`

+ 1
- 1
docker-setup.sh View File

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

+ 4
- 2
tests/Makefile View File

@ -11,7 +11,9 @@ else ifeq (${DUT}, uartrx)
else ifeq (${DUT}, wishbone) else ifeq (${DUT}, wishbone)
MODULE := tb_wishbone MODULE := tb_wishbone
TOPLEVEL := wishboneslavee TOPLEVEL := wishboneslavee
SIM_ARGS := -gSimulation=true
SIM_ARGS := -gSimulation=true \
-gAddressWidth=8 \
-gDataWidth=16
else else
$(error ${DUT} not available) $(error ${DUT} not available)
endif endif
@ -45,4 +47,4 @@ results:
.PHONY: clean .PHONY: clean
clean:: clean::
rm -rf *.o __pycache__ uarttx uartrx results
rm -rf *.o __pycache__ uarttx uartrx wishboneslavee results

+ 125
- 0
tests/Sram.py View File

@ -0,0 +1,125 @@
import logging
import cocotb
from cocotb.utils import get_sim_time
from cocotb.triggers import FallingEdge, RisingEdge, Timer, ReadOnly
class Sram:
def __init__(self, clk, wen, ren, adr, din, dout, mem, *args, **kwargs):
self._version = "0.0.1"
self.log = logging.getLogger(f"cocotb.{clk._path}")
self._clk = clk
self._wen = wen
self._ren = ren
self._adr = adr
self._din = din
self._dout = dout
self._mem = mem
self._clkedge = RisingEdge(self._clk)
class SramRead(Sram):
def __init__(self, clk, ren, adr, din, mem, *args, **kwargs):
super().__init__(clk, None, ren, adr, din, None, mem, *args, **kwargs)
self.log.info("SRAM read")
self.log.info(" cocotbext-sram version %s", self._version)
self.log.info(" Copyright (c) 2022 Torsten Meissner")
self._active = None
self._restart()
def _restart(self):
self.log.debug("SramRead._restart()")
if self._active is not None:
self._active.kill()
# Schedule SRAM read to run concurrently
self._active = cocotb.start_soon(self._read())
async def _read(self):
self.log.debug("SramRead._read()")
while True:
await self._clkedge
if self._ren.value == 1:
_data = self._mem[str(self._adr.value)]
self._din.value = _data
self.log.info("Read data: %s from adr: %s", hex(_data), hex(self._adr.value))
class SramWrite(Sram):
def __init__(self, clk, wen, adr, dout, mem, *args, **kwargs):
super().__init__(clk, wen, None, adr, None, dout, mem, *args, **kwargs)
self.log.info("SRAM write")
self.log.info(" cocotbext-sram version %s", self._version)
self.log.info(" Copyright (c) 2022 Torsten Meissner")
self._active = None
self._restart()
def _restart(self):
self.log.debug("SramWrite._restart()")
if self._active is not None:
self._active.kill()
# Schedule SRAM write to run concurrently
self._active = cocotb.start_soon(self._write())
async def _write(self):
self.log.debug("SramWrite._write()")
while True:
await self._clkedge
if self._wen.value == 1:
self._mem[str(self._adr.value)] = self._dout.value
self.log.info("Wrote data: %s to adr: %s", hex(self._dout.value), hex(self._adr.value))
class SramMonitor(Sram):
def __init__(self, clk, wen, ren, adr, din, dout, *args, **kwargs):
super().__init__(clk, wen, ren, adr, din, dout, None, *args, **kwargs)
self.log.info("SRAM monitor")
self.log.info(" cocotbext-sram version %s", self._version)
self.log.info(" Copyright (c) 2022 Torsten Meissner")
self._active = None
self._transactions = {}
self._restart()
def _restart(self):
self.log.debug("SramMonitor._restart()")
if self._active is not None:
self._active.kill()
# Schedule SRAM read to run concurrently
self._active = cocotb.start_soon(self._read())
async def _read(self):
self.log.debug("SramMonitor._read()")
while True:
await self._clkedge
if self._wen.value:
self._transactions[str(get_sim_time('ns'))] = {
"type" : "write",
"adr" : str(self._adr.value),
"data" : str(self._dout.value)}
elif self._ren.value:
await self._clkedge
await ReadOnly()
self._transactions[str(get_sim_time('ns'))] = {
"type" : "read",
"adr" : str(self._adr.value),
"data" : str(self._din.value)}
@property
def transactions(self, index=None):
if index:
key = list(self._transactions.keys())[index]
return {key: self._transactions[key]}
else:
return self._transactions

+ 79
- 0
tests/tb_wishbone.py View File

@ -0,0 +1,79 @@
# test_uart.py
import logging
import random
import cocotb
import pprint
from collections import defaultdict
from Sram import SramRead, SramWrite, SramMonitor
from cocotb.clock import Clock
from cocotb.triggers import FallingEdge, RisingEdge, Timer, ReadOnly
from cocotbext.wishbone.driver import WishboneMaster, WBOp
# Reset coroutine
async def reset_dut(reset_n, duration_ns):
reset_n.value = 1
await Timer(duration_ns, units="ns")
reset_n.value = 0
@cocotb.test()
async def test_wishbone(dut):
""" First simple test """
clkedge = RisingEdge(dut.wbclk_i)
# Connect reset
reset = dut.wbrst_i
# Create empty SRAM memory
memory = defaultdict()
mem_read = SramRead(dut.wbclk_i, dut.localren_o,
dut.localadress_o, dut.localdata_i, memory);
mem_write = SramWrite(dut.wbclk_i, dut.localwen_o,
dut.localadress_o, dut.localdata_o, memory);
sram_monitor = SramMonitor(dut.wbclk_i, dut.localwen_o, dut.localren_o,
dut.localadress_o, dut.localdata_i, dut.localdata_o);
wbmaster = WishboneMaster(dut, "", dut.wbclk_i,
width=16, # size of data bus
timeout=10, # in clock cycle number
signals_dict={"cyc": "wbcyc_i",
"stb": "wbstb_i",
"we": "wbwe_i",
"adr": "wbadr_i",
"datwr":"wbdat_i",
"datrd":"wbdat_o",
"ack": "wback_o" })
# Drive input defaults (setimmediatevalue to avoid x asserts)
dut.wbcyc_i.setimmediatevalue(0)
dut.wbstb_i.setimmediatevalue(0)
dut.wbwe_i.setimmediatevalue(0)
dut.wbadr_i.setimmediatevalue(0)
dut.wbdat_i.setimmediatevalue(0)
clock = Clock(dut.wbclk_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, 100)
dut._log.info("Released reset")
# Test 10 Wishbone transmissions
for i in range(10):
await clkedge
adr = random.randint(0, 255)
data = random.randint(0, 2**16-1)
await wbmaster.send_cycle([WBOp(adr=adr, dat=data)])
rec = await wbmaster.send_cycle([WBOp(adr=adr)])
# Example to print transactions collected by SRAM monitor
with open('results/sram_transactions.log', 'w', encoding='utf-8') as f:
f.write((f"{'Time':7}{'Type':7}{'Adr':11}{'Data'}\n"))
for k, v in sram_monitor.transactions.items():
f.write((f"{k:7}{v['type']:7}{v['adr']:11}{v['data']} \n"))

Loading…
Cancel
Save