11 Commits

Author SHA1 Message Date
Alex Forencich
50cf2af49f Release v0.1.22
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-03-24 01:31:54 -07:00
Alex Forencich
ddfa1e3c92 Update readme
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-03-24 01:28:25 -07:00
Alex Forencich
5e8b246159 Use slices to access memory contents to support both mmap and SparseMemory
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-03-24 01:23:33 -07:00
Alex Forencich
62c2eef4ec Add SparseMemoryRegion object
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-03-23 23:44:29 -07:00
Alex Forencich
ad6012aea5 Update memory models to use SparseMemory
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-03-23 23:43:53 -07:00
Alex Forencich
432bd81011 Add sparse memory model
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-03-23 19:27:57 -07:00
Alex Forencich
bde123e05f Add transfer length checks
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-02-27 16:38:34 -08:00
Alex Forencich
8604017159 For FIXED burst type, issue all bursts with the same starting address
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-02-27 16:36:04 -08:00
Alex Forencich
e21b9ffcc8 Update ubuntu version in CI
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-02-12 17:31:31 -08:00
Alex Forencich
47cd74eb6c Rework parameter handling in makefiles
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-29 19:50:53 -08:00
Alex Forencich
4bf5945aa3 Bump to dev version
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-01-25 18:11:44 -08:00
14 changed files with 198 additions and 61 deletions

View File

@@ -5,7 +5,7 @@ on: [push, pull_request]
jobs:
build:
name: Python ${{matrix.python-version}}
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
strategy:
matrix:

View File

@@ -155,7 +155,7 @@ It is also possible to extend these modules; operation can be customized by over
### AXI and AXI lite RAM
The `AxiRam` and `AxiLiteRam` classes implement AXI RAMs and are capable of completing read and write operations from upstream AXI masters. The `AxiRam` module is capable of handling narrow bursts. These modules are extensions of the corresponding `AxiSlave` and `AxiLiteSlave` modules.
The `AxiRam` and `AxiLiteRam` classes implement AXI RAMs and are capable of completing read and write operations from upstream AXI masters. The `AxiRam` module is capable of handling narrow bursts. These modules are extensions of the corresponding `AxiSlave` and `AxiLiteSlave` modules. Internally, `SparseMemory` is used to support emulating very large memories.
The `AxiRam` is a wrapper around `AxiRamWrite` and `AxiRamRead`. Similarly, `AxiLiteRam` is a wrapper around `AxiLiteRamWrite` and `AxiLiteRamRead`. If a read-only or write-only interface is required instead of a full interface, use the corresponding read-only or write-only variant, the usage and API are exactly the same.
@@ -163,7 +163,7 @@ To use these modules, import the one you need and connect it to the DUT:
from cocotbext.axi import AxiBus, AxiRam
axi_ram = AxiRam(AxiBus.from_prefix(dut, "m_axi"), dut.clk, dut.rst, size=2**16)
axi_ram = AxiRam(AxiBus.from_prefix(dut, "m_axi"), dut.clk, dut.rst, size=2**32)
The first argument to the constructor accepts an `AxiBus` or `AxiLiteBus` object. These objects are containers for the interface signals and include class methods to automate connections.
@@ -175,7 +175,7 @@ Once the module is instantiated, the memory contents can be accessed in a couple
Multi-port memories can be constructed by passing the `mem` object of the first instance to the other instances. For example, here is how to create a four-port RAM:
axi_ram_p1 = AxiRam(AxiBus.from_prefix(dut, "m00_axi"), dut.clk, dut.rst, size=2**16)
axi_ram_p1 = AxiRam(AxiBus.from_prefix(dut, "m00_axi"), dut.clk, dut.rst, size=2**32)
axi_ram_p2 = AxiRam(AxiBus.from_prefix(dut, "m01_axi"), dut.clk, dut.rst, mem=axi_ram_p1.mem)
axi_ram_p3 = AxiRam(AxiBus.from_prefix(dut, "m02_axi"), dut.clk, dut.rst, mem=axi_ram_p1.mem)
axi_ram_p4 = AxiRam(AxiBus.from_prefix(dut, "m03_axi"), dut.clk, dut.rst, mem=axi_ram_p1.mem)
@@ -186,12 +186,12 @@ Multi-port memories can be constructed by passing the `mem` object of the first
* _clock_: clock signal
* _reset_: reset signal (optional)
* _reset_active_level_: reset active level (optional, default `True`)
* _size_: memory size in bytes (optional, default 1024)
* _mem_: mmap object to use (optional, overrides _size_)
* _size_: memory size in bytes (optional, default `2**64`)
* _mem_: `mmap` or `SparseMemory` backing object to use (optional, overrides _size_)
#### Attributes:
* _mem_: directly access shared `mmap` object
* _mem_: directly access shared `mmap` or `SparseMemory` backing object
#### Methods
@@ -334,6 +334,8 @@ The address space abstraction provides a framework for cross-connecting multiple
`MemoryRegion` is an extension of `Region` that uses an `mmap` instance to handle memory operations. `MemoryRegion` also provides hex dump methods as well as indexing and slicing.
`SparseMemoryRegion` is similar to `MemoryRegion` but is backed by `SparseMemory` instead of `mmap` and as such can emulate extremely large regions of address space.
`PeripheralRegion` is an extension of `Region` that can wrap another object that implements `read()` and `write()`, as an alternative to extending `Region`.
`AddressSpace` is the core object for handling address spaces. `Region` objects can be registered with `AddressSpace` with specified base address, size, and offset. The `AddressSpace` object will then direct `read()` and `write()` operations to the appropriate `Region`s, splitting requests appropriately when necessary and translating addresses. Regions registered with `offset` other than `None` are translated such that accesses to base address + N map to N + offset. Regions registered with an `offset` of `None` are not translated. `Region` objects registered with the same `AddressSpace` cannot overlap, however the same `Region` can be registered multiple times. `AddressSpace` also provides a method for creating `Pool` objects.
@@ -344,14 +346,14 @@ The address space abstraction provides a framework for cross-connecting multiple
This is a simple example that shows how the address space abstraction components can be used to connect a DUT to a simulated host system, including simulated RAM, an AXI interface from the DUT for DMA, and an AXI lite interface to the DUT for control.
from cocotbext.axi import AddressSpace, MemoryRegion
from cocotbext.axi import AddressSpace, SparseMemoryRegion
from cocotbext.axi import AxiBus, AxiLiteMaster, AxiSlave
# system address space
address_space = AddressSpace(2**32)
# RAM
ram = MemoryRegion(2**24)
ram = SparseMemoryRegion(2**24)
address_space.register_region(ram, 0x0000_0000)
ram_pool = address_space.create_window_pool(0x0000_0000, 2**20)

View File

@@ -27,7 +27,7 @@ from .version import __version__
from .constants import AxiBurstType, AxiBurstSize, AxiLockType, AxiCacheBit, AxiProt, AxiResp
from .address_space import MemoryInterface, Window, WindowPool
from .address_space import Region, MemoryRegion, PeripheralRegion
from .address_space import Region, MemoryRegion, SparseMemoryRegion, PeripheralRegion
from .address_space import AddressSpace, Pool
from .axis import AxiStreamFrame, AxiStreamBus, AxiStreamSource, AxiStreamSink, AxiStreamMonitor

View File

@@ -25,6 +25,7 @@ THE SOFTWARE.
import mmap
from .buddy_allocator import BuddyAllocator
from .sparse_memory import SparseMemory
from .utils import hexdump, hexdump_lines, hexdump_str
@@ -216,6 +217,35 @@ class MemoryRegion(Region):
return bytes(self.mem)
class SparseMemoryRegion(Region):
def __init__(self, size=2**64, mem=None, **kwargs):
super().__init__(size, **kwargs)
if mem is None:
mem = SparseMemory(size)
self.mem = mem
async def _read(self, address, length, **kwargs):
return self.mem.read(address, length)
async def _write(self, address, data, **kwargs):
self.mem.write(address, data)
def hexdump(self, address, length, prefix=""):
self.mem.hexdump(address, length, prefix=prefix)
def hexdump_lines(self, address, length, prefix=""):
return self.mem.hexdump_lines(address, length, prefix=prefix)
def hexdump_str(self, address, length, prefix=""):
return self.mem.hexdump_str(address, length, prefix=prefix)
def __getitem__(self, key):
return self.mem[key]
def __setitem__(self, key, value):
self.mem[key] = value
class PeripheralRegion(Region):
def __init__(self, obj, size, **kwargs):
super().__init__(size, **kwargs)

View File

@@ -291,6 +291,9 @@ class AxiMasterWrite(Region, Reset):
if isinstance(data, int):
raise ValueError("Expected bytes or bytearray for data")
if burst != AxiBurstType.FIXED and address+len(data) >= 2**self.address_width:
raise ValueError("Requested transfer overruns end of address space")
if awid is None or awid < 0:
awid = None
elif awid > self.id_count:
@@ -357,6 +360,9 @@ class AxiMasterWrite(Region, Reset):
if isinstance(data, int):
raise ValueError("Expected bytes or bytearray for data")
if burst != AxiBurstType.FIXED and address+len(data) >= 2**self.address_width:
raise ValueError("Requested transfer overruns end of address space")
if awid is None or awid < 0:
awid = None
elif awid > self.id_count:
@@ -563,7 +569,9 @@ class AxiMasterWrite(Region, Reset):
await self.w_channel.send(w)
if k == 0:
if cmd.burst == AxiBurstType.FIXED:
cur_addr = cmd.address
elif k == 0:
cur_addr = aligned_addr + num_bytes
else:
cur_addr += num_bytes
@@ -715,6 +723,9 @@ class AxiMasterRead(Region, Reset):
if length < 0:
raise ValueError("Read length must be positive")
if burst != AxiBurstType.FIXED and address+length >= 2**self.address_width:
raise ValueError("Requested transfer overruns end of address space")
if arid is None or arid < 0:
arid = None
elif arid > self.id_count:
@@ -769,6 +780,9 @@ class AxiMasterRead(Region, Reset):
if length < 0:
raise ValueError("Read length must be positive")
if burst != AxiBurstType.FIXED and address+length >= 2**self.address_width:
raise ValueError("Requested transfer overruns end of address space")
if arid is None or arid < 0:
arid = None
elif arid > self.id_count:
@@ -918,7 +932,9 @@ class AxiMasterRead(Region, Reset):
self.log.info("Read burst start arid: 0x%x araddr: 0x%08x arlen: %d arsize: %d arprot: %s",
arid, cur_addr, burst_length-1, cmd.size, cmd.prot)
if k == 0:
if cmd.burst == AxiBurstType.FIXED:
cur_addr = cmd.address
elif k == 0:
cur_addr = aligned_addr + num_bytes
else:
cur_addr += num_bytes

View File

@@ -27,7 +27,7 @@ from .memory import Memory
class AxiRamWrite(AxiSlaveWrite, Memory):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, **kwargs):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=2**64, mem=None, **kwargs):
super().__init__(bus, clock, reset, reset_active_level=reset_active_level, size=size, mem=mem, **kwargs)
async def _write(self, address, data):
@@ -35,7 +35,7 @@ class AxiRamWrite(AxiSlaveWrite, Memory):
class AxiRamRead(AxiSlaveRead, Memory):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, **kwargs):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=2**64, mem=None, **kwargs):
super().__init__(bus, clock, reset, reset_active_level=reset_active_level, size=size, mem=mem, **kwargs)
async def _read(self, address, length):
@@ -43,7 +43,7 @@ class AxiRamRead(AxiSlaveRead, Memory):
class AxiRam(Memory):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, **kwargs):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=2**64, mem=None, **kwargs):
self.write_if = None
self.read_if = None

View File

@@ -182,6 +182,9 @@ class AxiLiteMasterWrite(Region, Reset):
if isinstance(data, int):
raise ValueError("Expected bytes or bytearray for data")
if address+len(data) >= 2**self.address_width:
raise ValueError("Requested transfer overruns end of address space")
if not self.awprot_present and prot != AxiProt.NONSECURE:
raise ValueError("awprot sideband signal value specified, but signal is not connected")
@@ -419,6 +422,12 @@ class AxiLiteMasterRead(Region, Reset):
if address < 0 or address >= 2**self.address_width:
raise ValueError("Address out of range")
if length < 0:
raise ValueError("Read length must be positive")
if address+length >= 2**self.address_width:
raise ValueError("Requested transfer overruns end of address space")
if not self.arprot_present and prot != AxiProt.NONSECURE:
raise ValueError("arprot sideband signal value specified, but signal is not connected")

View File

@@ -27,7 +27,7 @@ from .memory import Memory
class AxiLiteRamWrite(AxiLiteSlaveWrite, Memory):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, **kwargs):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=2**64, mem=None, **kwargs):
super().__init__(bus, clock, reset, reset_active_level=reset_active_level, size=size, mem=mem, **kwargs)
async def _write(self, address, data):
@@ -35,7 +35,7 @@ class AxiLiteRamWrite(AxiLiteSlaveWrite, Memory):
class AxiLiteRamRead(AxiLiteSlaveRead, Memory):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, **kwargs):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=2**64, mem=None, **kwargs):
super().__init__(bus, clock, reset, reset_active_level=reset_active_level, size=size, mem=mem, **kwargs)
async def _read(self, address, length):
@@ -43,7 +43,7 @@ class AxiLiteRamRead(AxiLiteSlaveRead, Memory):
class AxiLiteRam(Memory):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=1024, mem=None, **kwargs):
def __init__(self, bus, clock, reset=None, reset_active_level=True, size=2**64, mem=None, **kwargs):
self.write_if = None
self.read_if = None

View File

@@ -22,27 +22,24 @@ THE SOFTWARE.
"""
import mmap
from .sparse_memory import SparseMemory
from .utils import hexdump, hexdump_lines, hexdump_str
class Memory:
def __init__(self, size=1024, mem=None, **kwargs):
def __init__(self, size=2**64, mem=None, **kwargs):
if mem is not None:
self.mem = mem
else:
self.mem = mmap.mmap(-1, size)
self.mem = SparseMemory(size)
self.size = len(self.mem)
super().__init__(**kwargs)
def read(self, address, length):
self.mem.seek(address)
return self.mem.read(length)
return self.mem[address:address+length]
def write(self, address, data):
self.mem.seek(address)
self.mem.write(bytes(data))
self.mem[address:address+len(data)] = data
def write_words(self, address, data, byteorder='little', ws=2):
words = data

View File

@@ -0,0 +1,111 @@
"""
Copyright (c) 2023 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
from .utils import hexdump, hexdump_lines, hexdump_str
class SparseMemory:
def __init__(self, size):
self.size = size
self.segs = {}
def read(self, address, length, **kwargs):
if address < 0 or address >= self.size:
raise ValueError("address out of range")
if length < 0:
raise ValueError("invalid length")
if address+length > self.size:
raise ValueError("operation out of range")
data = bytearray()
while length > 0:
block_offset = address & 0xfff
block_addr = address - block_offset
block_len = min(4096 - block_offset, length)
try:
block = self.segs[block_addr]
except KeyError:
block = b'\x00'*4096
data.extend(block[block_offset:block_offset+block_len])
address += block_len
length -= block_len
return bytes(data)
def write(self, address, data, **kwargs):
if address < 0 or address >= self.size:
raise ValueError("address out of range")
if address+len(data) > self.size:
raise ValueError("operation out of range")
offset = 0
length = len(data)
while length > 0:
block_offset = address & 0xfff
block_addr = address - block_offset
block_len = min(4096 - block_offset, length)
try:
block = self.segs[block_addr]
except KeyError:
block = bytearray(4096)
self.segs[block_addr] = block
block[block_offset:block_offset+block_len] = data[offset:offset+block_len]
address += block_len
offset += block_len
length -= block_len
def clear(self):
self.segs.clear()
def hexdump(self, address, length, prefix=""):
hexdump(self.read(address, length), prefix=prefix, offset=address)
def hexdump_lines(self, address, length, prefix=""):
return hexdump_lines(self.read(address, length), prefix=prefix, offset=address)
def hexdump_str(self, address, length, prefix=""):
return hexdump_str(self.read(address, length), prefix=prefix, offset=address)
def __len__(self):
return self.size
def __getitem__(self, key):
if isinstance(key, int):
return self.read(key, 1)[0]
elif isinstance(key, slice):
start, stop, step = key.indices(self.size)
if step == 1:
return self.read(start, stop-start)
else:
raise IndexError("specified step size is not supported")
def __setitem__(self, key, value):
if isinstance(key, int):
self.write(key, [value])
elif isinstance(key, slice):
start, stop, step = key.indices(self.size)
if step == 1:
value = bytes(value)
if stop-start != len(value):
raise IndexError("slice assignment is wrong size")
return self.write(start, value)
else:
raise IndexError("specified step size is not supported")

View File

@@ -1 +1 @@
__version__ = "0.1.20"
__version__ = "0.1.22"

View File

@@ -45,15 +45,7 @@ export PARAM_RUSER_WIDTH ?= 1
ifeq ($(SIM), icarus)
PLUSARGS += -fst
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).ADDR_WIDTH=$(PARAM_ADDR_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).STRB_WIDTH=$(PARAM_STRB_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).AWUSER_WIDTH=$(PARAM_AWUSER_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).WUSER_WIDTH=$(PARAM_WUSER_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).BUSER_WIDTH=$(PARAM_BUSER_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).ARUSER_WIDTH=$(PARAM_ARUSER_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).RUSER_WIDTH=$(PARAM_RUSER_WIDTH)
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v
@@ -62,15 +54,7 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH -Wno-CASEINCOMPLETE
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
COMPILE_ARGS += -GADDR_WIDTH=$(PARAM_ADDR_WIDTH)
COMPILE_ARGS += -GSTRB_WIDTH=$(PARAM_STRB_WIDTH)
COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH)
COMPILE_ARGS += -GAWUSER_WIDTH=$(PARAM_AWUSER_WIDTH)
COMPILE_ARGS += -GWUSER_WIDTH=$(PARAM_WUSER_WIDTH)
COMPILE_ARGS += -GBUSER_WIDTH=$(PARAM_BUSER_WIDTH)
COMPILE_ARGS += -GARUSER_WIDTH=$(PARAM_ARUSER_WIDTH)
COMPILE_ARGS += -GRUSER_WIDTH=$(PARAM_RUSER_WIDTH)
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst

View File

@@ -39,9 +39,7 @@ export PARAM_STRB_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
ifeq ($(SIM), icarus)
PLUSARGS += -fst
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).ADDR_WIDTH=$(PARAM_ADDR_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).STRB_WIDTH=$(PARAM_STRB_WIDTH)
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v
@@ -50,9 +48,7 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
COMPILE_ARGS += -GADDR_WIDTH=$(PARAM_ADDR_WIDTH)
COMPILE_ARGS += -GSTRB_WIDTH=$(PARAM_STRB_WIDTH)
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst

View File

@@ -41,11 +41,7 @@ export PARAM_USER_WIDTH ?= 1
ifeq ($(SIM), icarus)
PLUSARGS += -fst
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).KEEP_WIDTH=$(PARAM_KEEP_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).DEST_WIDTH=$(PARAM_DEST_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).USER_WIDTH=$(PARAM_USER_WIDTH)
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v
@@ -54,11 +50,7 @@ ifeq ($(SIM), icarus)
else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
COMPILE_ARGS += -GKEEP_WIDTH=$(PARAM_KEEP_WIDTH)
COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH)
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst