From a589cc2133a2ca14222428af3267ce2f53d9a2f5 Mon Sep 17 00:00:00 2001
From: dam1n19 <dam1n19@soton.ac.uk>
Date: Thu, 6 Jul 2023 12:34:04 +0100
Subject: [PATCH] Cocotb work continuation

---
 .gitignore                        |   6 +-
 flows/makefile.simulate           |   3 +
 verif/cocotb/__init__.py          |   1 +
 verif/cocotb/makefile             |  37 +++++++-
 verif/cocotb/makefile.source      |  25 ------
 verif/cocotb/test_nanosoc_chip.py | 135 ++++++++++++++++++++++++++++++
 6 files changed, 177 insertions(+), 30 deletions(-)
 create mode 100644 verif/cocotb/__init__.py
 delete mode 100644 verif/cocotb/makefile.source
 create mode 100644 verif/cocotb/test_nanosoc_chip.py

diff --git a/.gitignore b/.gitignore
index d65cdde..b70865a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
 # Exclude Simulation Files
-verif/cocotb/modelsim.ini
-verif/cocotb/transcript
+verif/cocotb/*
+!verif/cocotb/makefile
+!verif/cocotb/*.py
+
 
 # Exclude Compiled Binaries
 /software/*/*.elf
diff --git a/flows/makefile.simulate b/flows/makefile.simulate
index 8274c9e..1c77a12 100644
--- a/flows/makefile.simulate
+++ b/flows/makefile.simulate
@@ -45,6 +45,9 @@ endif
 # Cocotb GUI Variable
 GUI ?= 0
 
+# Cocotb Test Location
+COCOTB_TEST_DIR := $(SOCLABS_NANOSOC_TECH_DIR)/verif/cocotb
+
 # Cocotb Scratch Directory
 COCOTB_DIR := $(SIM_TOP_DIR)/cocotb
 COCOTB_SCRATCH_DIR := $(COCOTB_DIR)/scratch
diff --git a/verif/cocotb/__init__.py b/verif/cocotb/__init__.py
new file mode 100644
index 0000000..fc80254
--- /dev/null
+++ b/verif/cocotb/__init__.py
@@ -0,0 +1 @@
+pass
\ No newline at end of file
diff --git a/verif/cocotb/makefile b/verif/cocotb/makefile
index b51957f..ff0f886 100644
--- a/verif/cocotb/makefile
+++ b/verif/cocotb/makefile
@@ -1,6 +1,37 @@
+#-----------------------------------------------------------------------------
+# NanoSoC CocoTB Simulation Makefile 
+# A joint work commissioned on behalf of SoC Labs, under Arm Academic Access license.
+#
+# Contributors
+#
+# David Mapstone (d.a.mapstone@soton.ac.uk)
+#
+# Copyright (C) 2021-3, SoC Labs (www.soclabs.org)
+#-----------------------------------------------------------------------------
+# Copyright cocotb contributors
+# Licensed under the Revised BSD License, see LICENSE for details.
+# SPDX-License-Identifier: BSD-3-Clause
 
-SIM = questa
+# Simulator to Run
+SIM ?= questa
+
+# Python Install to Use
 PYTHON_BIN = /usr/bin/python3.8
-include makefile.source
 
-MODULE = test_nanosoc_chip
+TOPLEVEL_LANG ?= verilog
+
+ifneq ($(TOPLEVEL_LANG),verilog)
+
+all:
+	@echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog"
+clean::
+
+else
+
+TOPLEVEL := nanosoc_tb
+
+include $(SOCLABS_PROJECT_DIR)/simulate/sim/cocotb/makefile.flist
+
+include $(shell cocotb-config --makefiles)/Makefile.sim
+
+endif
diff --git a/verif/cocotb/makefile.source b/verif/cocotb/makefile.source
deleted file mode 100644
index 17cd3a4..0000000
--- a/verif/cocotb/makefile.source
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright cocotb contributors
-# Licensed under the Revised BSD License, see LICENSE for details.
-# SPDX-License-Identifier: BSD-3-Clause
-
-TOPLEVEL_LANG ?= verilog
-
-ifneq ($(TOPLEVEL_LANG),verilog)
-
-all:
-	@echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog"
-clean::
-
-else
-
-TOPLEVEL := nanosoc_tb
-
-PWD=$(shell pwd)
-
-COCOTB?=$(PWD)/../../..
-
-include $(SOCLABS_PROJECT_DIR)/simulate/sim/cocotb/makefile.flist
-
-include $(shell cocotb-config --makefiles)/Makefile.sim
-
-endif
diff --git a/verif/cocotb/test_nanosoc_chip.py b/verif/cocotb/test_nanosoc_chip.py
new file mode 100644
index 0000000..3f2211f
--- /dev/null
+++ b/verif/cocotb/test_nanosoc_chip.py
@@ -0,0 +1,135 @@
+from random import randint, randrange, getrandbits, shuffle
+from collections.abc import Iterable
+
+import logging
+import cocotb
+from cocotb.clock import Clock
+from cocotb.regression import TestFactory
+from cocotb.result import TestFailure
+from cocotb.triggers import ClockCycles, Combine, Join, RisingEdge
+# from cocotb_bus.drivers.amba import (
+#     AXIBurst, AXI4Master, AXIProtocolError, AXIReadBurstLengthMismatch,
+#     AXIxRESP)
+from systemrdltest.python.cmsdk_reg.lib import NormalCallbackSet
+from systemrdltest.python.cmsdk_reg.reg_model import cmsdk_reg
+
+from cocotbext.axi import AxiBus, AxiMaster
+
+CLK_PERIOD = (10, "ns")
+AXI_PREFIX = "S_AXI"
+
+async def setup_dut(dut):
+    cocotb.start_soon(Clock(dut.clk, *CLK_PERIOD).start())
+    axi_bus = AxiBus.from_prefix(dut, AXI_PREFIX)
+    axi_driver = AxiMaster(axi_bus, dut.clk, dut.rstn, reset_active_level=False)
+    dut.rstn.value = 0
+    await ClockCycles(dut.clk, 2)
+    dut.rstn.value = 1
+    await ClockCycles(dut.clk, 2)
+    return axi_driver
+
+def binatodeci(binary):
+    return sum(val*(2**idx) for idx, val in enumerate(reversed(binary)))
+
+@cocotb.coroutine
+async def reg_write(manager, register, data):
+    print("Writing address: "+str(register.address))
+    await manager.write(register.address, data)
+
+@cocotb.coroutine
+async def reg_read(manager, register, length = 1):
+    event = manager.init_read(register.address, length)
+    await event.wait()
+    return event.data.data
+
+@cocotb.coroutine
+async def field_read(manager, register, field, length = 1):
+    reg_read_val = await reg_read(manager, register, length)
+    return getattr(register, field).decode_read_value(binatodeci(reg_read_val))
+
+@cocotb.test()
+async def test_read(dut):
+    """Test Software Readable CMSDK Registers can be read"""
+
+    # Connect AXI4 driver into DUT AXI Signals
+    axim = AxiMaster(AxiBus.from_prefix(dut, AXI_PREFIX), dut.clk, dut.rstn, reset_active_level=False)
+    reg_address_map = cmsdk_reg.cmsdk_reg_cls(callbacks = NormalCallbackSet())
+
+    # Generate a Clock and Perform a Reset
+    await setup_dut(dut)
+
+    # Select a readable register with known reset value
+    test_addr = reg_address_map.pid.PID_4
+    expected_result = test_addr.ID.default
+
+    #Read Register Field
+    read_result = await field_read(axim, test_addr, "ID")
+
+    print(f"Address: {hex(test_addr.address)} | Read Value: {read_result}, Expected Value: {expected_result}")
+    assert read_result == expected_result
+
+@cocotb.test()
+async def test_read(dut):
+    """Test 1 Software Readable CMSDK Register can be read"""
+
+    # Connect AXI4 driver into DUT AXI Signals
+    
+    reg_address_map = cmsdk_reg.cmsdk_reg_cls(callbacks = NormalCallbackSet())
+
+    # Generate a Clock and Perform a Reset
+    axi_driver = await setup_dut(dut)
+
+    # Select a readable register with known reset value
+    test_addr = reg_address_map.pid.PID_4
+    expected_result = test_addr.ID.default
+
+    #Read Register Field
+    read_result = await field_read(axi_driver, test_addr, "ID")
+
+    log = logging.getLogger(f"cocotb.test")
+    log.info(f"Address: {hex(test_addr.address)} | Read Value: {read_result}, Expected Value: {expected_result}")
+    assert read_result == expected_result
+
+@cocotb.test()
+async def test_readable_regs(dut):
+    """Test Software Readable CMSDK Registers can be read"""
+
+    # Connect AXI4 driver into DUT AXI Signals
+    reg_address_map = cmsdk_reg.cmsdk_reg_cls(callbacks = NormalCallbackSet())
+
+    # Generate a Clock and Perform a Reset
+    axi_driver = await setup_dut(dut)
+
+    # Setup Logger
+    log = logging.getLogger(f"cocotb.test")
+
+    # Select a readable register with known reset values
+    sections = list(reg_address_map.get_sections())
+    
+    # Get a list of readable registers
+    readable_reg_list = []
+    for section in sections:
+        for reg in list(section.get_readable_registers()):
+            if isinstance(reg, Iterable):
+                for index in reg:
+                    readable_reg_list.append(index)
+            else:
+                readable_reg_list.append(reg)
+
+    # Randomly select registers in readable list and perform a read
+    shuffle(readable_reg_list)
+    for reg in readable_reg_list:
+        # Get a list of the fields in the register and randomise
+        readable_fields_list = list(reg.readable_fields)
+        shuffle(readable_fields_list)
+
+        for field in readable_fields_list:
+            # Read each field
+            read_result = await field_read(axi_driver, reg, field.inst_name)
+
+            # Get default (reset value) for each field
+            expected_result = field.default
+
+            # Run an assertion against reset values
+            log.info(f"Field: {field.inst_name} @ Address: {hex(reg.address)} | Read Value: {read_result}, Expected Value: {expected_result}")
+            assert read_result == expected_result
\ No newline at end of file
-- 
GitLab