sim: implement Format.* for memories in VCD.

This commit is contained in:
Wanda 2024-04-15 12:34:09 +02:00 committed by Catherine
parent 625dac376a
commit f243cea0fb
2 changed files with 90 additions and 25 deletions

View file

@ -172,23 +172,60 @@ class _VCDWriter:
for memory, memory_name in memories.items():
self.vcd_memory_vars[memory] = vcd_vars = []
self.gtkw_memory_names[memory] = gtkw_names = []
width = Shape.cast(memory.shape).width
if width > 1:
suffix = f"[{width - 1}:0]"
else:
suffix = ""
for idx, init in enumerate(memory._init._raw):
field_name = "\\" + memory_name[-1] + f"[{idx}]"
var_scope = memory_name[:-1]
vcd_var = self.vcd_writer.register_var(
scope=var_scope, name=field_name,
var_type="wire", size=width, init=init,
)
vcd_vars.append(vcd_var)
gtkw_field_name = field_name + suffix
gtkw_name = ".".join((*var_scope, gtkw_field_name))
gtkw_names.append(gtkw_name)
for idx, row in enumerate(memory):
row_vcd_vars = []
row_gtkw_names = []
var_scope = memory_name[:-1]
def add_mem_var(path, var_type, var_size, var_init, value):
field_name = "\\" + memory_name[-1] + f"[{idx}]"
for item in path:
if isinstance(item, int):
field_name += f"[{item}]"
else:
field_name += f".{item}"
row_vcd_vars.append((self.vcd_writer.register_var(
scope=var_scope, name=field_name, var_type=var_type,
size=var_size, init=var_init
), value))
if var_size > 1:
suffix = f"[{var_size - 1}:0]"
else:
suffix = ""
row_gtkw_names.append(".".join((*var_scope, field_name)) + suffix)
def add_mem_wire_var(path, value):
add_mem_var(path, "wire", len(value), eval_value(self.state, value), value)
def add_mem_format_var(path, fmt):
add_mem_var(path, "string", 1, eval_format(self.state, fmt), fmt)
def add_mem_format(path, fmt):
if isinstance(fmt, Format.Struct):
add_mem_wire_var(path, fmt._value)
for name, subfmt in fmt._fields.items():
add_mem_format(path + (name,), subfmt)
elif isinstance(fmt, Format.Array):
add_mem_wire_var(path, fmt._value)
for idx, subfmt in enumerate(fmt._fields):
add_mem_format(path + (idx,), subfmt)
elif (isinstance(fmt, Format) and
len(fmt._chunks) == 1 and
isinstance(fmt._chunks[0], tuple) and
fmt._chunks[0][1] == ""):
add_mem_wire_var(path, fmt._chunks[0][0])
else:
add_mem_format_var(path, fmt)
if isinstance(memory._shape, ShapeCastable):
fmt = memory._shape.format(memory._shape(row), "")
add_mem_format((), fmt)
else:
add_mem_wire_var((), row)
vcd_vars.append(row_vcd_vars)
gtkw_names.append(row_gtkw_names)
self.vcd_process_vars = {}
if fs_per_delta == 0:
@ -221,9 +258,15 @@ class _VCDWriter:
var_value = repr(eval_value(self.state, signal))
self.vcd_writer.change(vcd_var, timestamp, var_value)
def update_memory(self, timestamp, memory, addr, value):
vcd_var = self.vcd_memory_vars[memory][addr]
self.vcd_writer.change(vcd_var, timestamp, value)
def update_memory(self, timestamp, memory, addr):
if memory not in self.vcd_memory_vars:
return
for vcd_var, repr in self.vcd_memory_vars[memory][addr]:
if isinstance(repr, Value):
var_value = eval_value(self.state, repr)
else:
var_value = eval_format(self.state, repr)
self.vcd_writer.change(vcd_var, timestamp, var_value)
def update_process(self, timestamp, process, command):
try:
@ -249,11 +292,12 @@ class _VCDWriter:
for name in self.gtkw_signal_names[trace]:
self.gtkw_save.trace(name)
elif isinstance(trace, MemoryData):
for name in self.gtkw_memory_names[trace]:
self.gtkw_save.trace(name)
for row_names in self.gtkw_memory_names[trace]:
for name in row_names:
self.gtkw_save.trace(name)
elif isinstance(trace, MemoryData._Row):
name = self.gtkw_memory_names[trace._memory][trace._index]
self.gtkw_save.trace(name)
for name in self.gtkw_memory_names[trace._memory][trace._index]:
self.gtkw_save.trace(name)
else:
assert False # :nocov:
@ -524,7 +568,7 @@ class PySimEngine(BaseEngine):
signal_state.signal)
elif isinstance(change, _PyMemoryChange):
vcd_writer.update_memory(now_plus_deltas, change.state.memory,
change.addr, change.state.data[change.addr])
change.addr)
else:
assert False # :nocov:

View file

@ -10,7 +10,8 @@ from amaranth.hdl._cd import *
with warnings.catch_warnings():
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
from amaranth.hdl.rec import *
from amaranth.hdl._dsl import *
from amaranth.hdl._dsl import *
from amaranth.hdl._mem import MemoryData
from amaranth.hdl._ir import *
from amaranth.sim import *
from amaranth.sim._pyeval import eval_format
@ -1355,6 +1356,26 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
with self.assertSimulation(Module(), traces=[sig]) as sim:
sim.add_testbench(testbench)
def test_mem_shape(self):
class MyEnum(enum.Enum, shape=2):
A = 0
B = 1
C = 2
mem1 = MemoryData(shape=8, depth=4, init=[1, 2, 3])
mem2 = MemoryData(shape=MyEnum, depth=4, init=[MyEnum.A, MyEnum.B, MyEnum.C])
mem3 = MemoryData(shape=data.StructLayout({"a": signed(3), "b": 2}), depth=4, init=[{"a": 2, "b": 1}])
def testbench():
yield Delay(1e-6)
yield mem1[0].eq(4)
yield mem2[3].eq(MyEnum.C)
yield mem3[2].eq(mem3._shape.const({"a": -1, "b": 2}))
yield Delay(1e-6)
with self.assertSimulation(Module(), traces=[mem1, mem2, mem3]) as sim:
sim.add_testbench(testbench)
class SimulatorRegressionTestCase(FHDLTestCase):
def test_bug_325(self):