Skip to content

Commit

Permalink
vpi packed structs
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Nolte committed Mar 12, 2024
1 parent 1f0380b commit abe99d6
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/cocotb/handle.py
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,15 @@ def value(self, value: LogicArray) -> None:
self.set(value)



class PackedStructObject(LogicObject):
"""A packed struct object.
Some simulators allow packed struct signal access, so we allow converting to that type.
"""
def asHierarchyObject(self):
return HierarchyObject(self._handle, self._path)

class RealObject(ValueObjectBase[float, float]):
"""A real/float simulation object.
Expand Down Expand Up @@ -1267,6 +1276,7 @@ def value(self, value: bytes) -> None:
_type2cls: Dict[int, Type[_ConcreteHandleTypes]] = {
simulator.MODULE: HierarchyObject,
simulator.STRUCTURE: HierarchyObject,
simulator.PACKED_STRUCTURE: PackedStructObject,
simulator.REG: LogicObject,
simulator.NET: LogicObject,
simulator.NETARRAY: ArrayObject[Any, ValueObjectBase[Any, Any]],
Expand Down
1 change: 1 addition & 0 deletions src/cocotb/share/include/gpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ typedef enum gpi_objtype_e {
GPI_STRING = 11,
GPI_GENARRAY = 12,
GPI_PACKAGE = 13,
GPI_PACKED_STRUCTURE = 14,
} gpi_objtype_t;

// When iterating, we can chose to either get child objects, drivers or loads
Expand Down
2 changes: 2 additions & 0 deletions src/cocotb/share/lib/simulator/simulatormodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,8 @@ static int add_module_constants(PyObject *simulator) {
PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY) < 0 ||
PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM) < 0 ||
PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE) < 0 ||
PyModule_AddIntConstant(simulator, "PACKED_STRUCTURE",
GPI_PACKED_STRUCTURE) < 0 ||
PyModule_AddIntConstant(simulator, "REAL", GPI_REAL) < 0 ||
PyModule_AddIntConstant(simulator, "INTEGER", GPI_INTEGER) < 0 ||
PyModule_AddIntConstant(simulator, "STRING", GPI_STRING) < 0 ||
Expand Down
5 changes: 5 additions & 0 deletions src/cocotb/share/lib/vpi/VpiImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ GpiObjHdl *VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl,
case vpiStructNet:
case vpiUnionVar:
case vpiUnionNet:
if (vpi_get(vpiPacked, new_hdl)) {
new_obj =
new VpiSignalObjHdl(this, new_hdl, GPI_PACKED_STRUCTURE, false);
break;
}
new_obj = new VpiObjHdl(this, new_hdl, to_gpi_objtype(type));
break;
case vpiModule:
Expand Down
1 change: 1 addition & 0 deletions src/cocotb/simulator.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ REAL: int
REG: int
STRING: int
STRUCTURE: int
PACKED_STRUCTURE: int
UNKNOWN: int

class gpi_cb_hdl:
Expand Down
10 changes: 10 additions & 0 deletions tests/designs/sample_module/sample_module.sv
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ typedef struct packed
logic a_in;
logic b_out;
} test_if;

typedef struct packed
{
logic val_a;
logic val_b;
logic value;
} test_struct;


`endif

module sample_module #(
Expand All @@ -54,6 +63,7 @@ module sample_module #(
output real stream_out_real,
output integer stream_out_int,
input test_if inout_if,
input test_struct my_struct,
input string stream_in_string,
`endif
input [7:0] stream_in_data,
Expand Down
19 changes: 19 additions & 0 deletions tests/test_cases/test_struct/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 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

include ../../designs/sample_module/Makefile
MODULE:=test_struct

endif
72 changes: 72 additions & 0 deletions tests/test_cases/test_struct/test_struct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Copyright cocotb contributors
# Licensed under the Revised BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-3-Clause

import contextlib

import cocotb
from cocotb.triggers import Timer

test_dec = cocotb.test(
expect_error=AttributeError
if cocotb.SIM_NAME.lower().startswith(("icarus", "ghdl", "nvc"))
else (),
expect_fail=cocotb.SIM_NAME.lower().startswith(("modelsim", "riviera")),
skip=cocotb.SIM_NAME.lower().startswith("verilator"), # verilator already treats packed structs as logic arrays
)


@contextlib.contextmanager
def assert_raises(exc_type):
try:
yield
except exc_type as exc:
cocotb.log.info(f" {exc_type.__name__} raised as expected: {exc}")
else:
raise AssertionError(f"{exc_type.__name__} was not raised")


@test_dec
async def test_struct_format(dut):
"""Test that the correct objects are returned for a struct"""
assert repr(dut.inout_if) == "PackedStructObject(sample_module.inout_if)"

# use value or value to access signal
cocotb.log.info(f"dut.inout_if.value={dut.inout_if.value}")
assert repr(dut.inout_if.value) == "LogicArray('ZZ', Range(1, 'downto', 0))"

hier_obj = dut.inout_if.asHierarchyObject()
assert repr(hier_obj) == "HierarchyObject(sample_module.inout_if)"
cocotb.log.info(f"a_in={hier_obj.a_in.value}")
assert repr(hier_obj.a_in) == "LogicObject(sample_module.inout_if.a_in)"

cocotb.log.info(f"b_out={hier_obj.b_out.value}")
assert repr(hier_obj.b_out) == "LogicObject(sample_module.inout_if.b_out)"


@test_dec
async def test_struct_setting(dut):
"""Test getting and setting setting the value of an entire struct"""

assert dut.inout_if.value.binstr == "ZZ"

# test struct write -> individual signals
dut.inout_if.value = 0
await Timer(1000, "ns")

assert dut.inout_if.value.binstr == "00"

# check inner signals
hier_obj = dut.inout_if.asHierarchyObject()
assert hier_obj.a_in.value == 0
assert hier_obj.b_out.value == 0

# test signal write -> struct value
hier_obj.a_in.value = 1
await Timer(1000, "ns")
assert dut.inout_if.value.binstr == "10"

hier_obj.b_out.value = 1
await Timer(1000, "ns")
assert dut.inout_if.value.binstr == "11"

0 comments on commit abe99d6

Please sign in to comment.