parent
81c35a5922
commit
2d59242bf7
|
@ -1,4 +1,5 @@
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
from contextlib import contextmanager
|
||||||
import io
|
import io
|
||||||
|
|
||||||
from ..utils import bits_for
|
from ..utils import bits_for
|
||||||
|
@ -47,89 +48,141 @@ def _const(value):
|
||||||
assert False, f"Invalid constant {value!r}"
|
assert False, f"Invalid constant {value!r}"
|
||||||
|
|
||||||
|
|
||||||
class _Namer:
|
def _src(src_loc):
|
||||||
|
if src_loc is None:
|
||||||
|
return None
|
||||||
|
file, line = src_loc
|
||||||
|
return f"{file}:{line}"
|
||||||
|
|
||||||
|
|
||||||
|
class Emitter:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
self._indent = ""
|
||||||
self._anon = 0
|
self._lines = []
|
||||||
self._index = 0
|
self.port_id = 0
|
||||||
self._names = set()
|
|
||||||
|
|
||||||
def anonymous(self):
|
def __call__(self, line=None):
|
||||||
name = f"U$${self._anon}"
|
if line is not None:
|
||||||
assert name not in self._names
|
self._lines.append(f"{self._indent}{line}\n")
|
||||||
self._anon += 1
|
else:
|
||||||
return name
|
self._lines.append("\n")
|
||||||
|
|
||||||
def _make_name(self, name, local):
|
@contextmanager
|
||||||
if name is None:
|
def indent(self):
|
||||||
self._index += 1
|
orig = self._indent
|
||||||
name = f"${self._index}"
|
self._indent += " "
|
||||||
elif not local and name[0] not in "\\$":
|
yield
|
||||||
name = f"\\{name}"
|
self._indent = orig
|
||||||
while name in self._names:
|
|
||||||
self._index += 1
|
|
||||||
name = f"{name}${self._index}"
|
|
||||||
self._names.add(name)
|
|
||||||
return name
|
|
||||||
|
|
||||||
|
|
||||||
class _BufferedBuilder:
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self._buffer = io.StringIO()
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self._buffer.getvalue()
|
return "".join(self._lines)
|
||||||
|
|
||||||
def _append(self, fmt, *args, **kwargs):
|
|
||||||
self._buffer.write(fmt.format(*args, **kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
class _AttrBuilder:
|
class Design:
|
||||||
def __init__(self, emit_src, *args, **kwargs):
|
def __init__(self, emit_src=True):
|
||||||
super().__init__(*args, **kwargs)
|
self.modules = {}
|
||||||
self.emit_src = emit_src
|
self.emit_src = emit_src
|
||||||
|
|
||||||
def _attribute(self, name, value, *, indent=0):
|
def module(self, name, **kwargs):
|
||||||
self._append("{}attribute \\{} {}\n",
|
assert name not in self.modules
|
||||||
" " * indent, name, _const(value))
|
self.modules[name] = res = Module(name, emit_src=self.emit_src, **kwargs)
|
||||||
|
return res
|
||||||
|
|
||||||
def _attributes(self, attrs, *, src=None, **kwargs):
|
def __str__(self):
|
||||||
for name, value in attrs.items():
|
emitter = Emitter()
|
||||||
self._attribute(name, value, **kwargs)
|
for module in self.modules.values():
|
||||||
if src and self.emit_src:
|
module.emit(emitter)
|
||||||
self._attribute("src", src, **kwargs)
|
return str(emitter)
|
||||||
|
|
||||||
|
|
||||||
class _Builder(_BufferedBuilder, _Namer):
|
class Module:
|
||||||
def __init__(self, emit_src):
|
def __init__(self, name, src_loc=None, attrs=None, emit_src=True):
|
||||||
super().__init__()
|
self.name = name
|
||||||
|
self._auto_index = 0
|
||||||
|
self.contents = {}
|
||||||
|
self.connections = []
|
||||||
|
self.attributes = {"generator": "Amaranth"}
|
||||||
self.emit_src = emit_src
|
self.emit_src = emit_src
|
||||||
|
if src_loc is not None and emit_src:
|
||||||
|
self.attributes["src"] = _src(src_loc)
|
||||||
|
if attrs is not None:
|
||||||
|
self.attributes.update(attrs)
|
||||||
|
|
||||||
def module(self, name=None, attrs={}, *, src=None):
|
def _auto_name(self):
|
||||||
name = self._make_name(name, local=False)
|
self._auto_index += 1
|
||||||
return _ModuleBuilder(self, name, attrs, src=src)
|
return f"${self._auto_index}"
|
||||||
|
|
||||||
|
def _name(self, name):
|
||||||
|
if name is None:
|
||||||
|
name = self._auto_name()
|
||||||
|
else:
|
||||||
|
name = f"\\{name}"
|
||||||
|
assert name not in self.contents
|
||||||
|
return name
|
||||||
|
|
||||||
|
def wire(self, width, *, name=None, **kwargs):
|
||||||
|
name = self._name(name)
|
||||||
|
if not self.emit_src and "src_loc" in kwargs:
|
||||||
|
del kwargs["src_loc"]
|
||||||
|
self.contents[name] = res = Wire(width, name=name, **kwargs)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def cell(self, kind, name=None, **kwargs):
|
||||||
|
name = self._name(name)
|
||||||
|
if not self.emit_src and "src_loc" in kwargs:
|
||||||
|
del kwargs["src_loc"]
|
||||||
|
self.contents[name] = res = Cell(kind, name=name, **kwargs)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def memory(self, width, depth, name=None, **kwargs):
|
||||||
|
name = self._name(name)
|
||||||
|
if not self.emit_src and "src_loc" in kwargs:
|
||||||
|
del kwargs["src_loc"]
|
||||||
|
self.contents[name] = res = Memory(width, depth, name=name, **kwargs)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def process(self, *, name=None, **kwargs):
|
||||||
|
name = self._name(name)
|
||||||
|
if not self.emit_src and "src_loc" in kwargs:
|
||||||
|
del kwargs["src_loc"]
|
||||||
|
self.contents[name] = res = Process(name=name, **kwargs)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def connect(self, lhs, rhs):
|
||||||
|
self.connections.append((lhs, rhs))
|
||||||
|
|
||||||
|
def attribute(self, name, value):
|
||||||
|
assert name not in self.attributes
|
||||||
|
self.attributes[name] = value
|
||||||
|
|
||||||
|
def emit(self, line):
|
||||||
|
line.port_id = 0
|
||||||
|
for name, value in self.attributes.items():
|
||||||
|
line(f"attribute \\{name} {_const(value)}")
|
||||||
|
line(f"module \\{self.name}")
|
||||||
|
line()
|
||||||
|
with line.indent():
|
||||||
|
for item in self.contents.values():
|
||||||
|
item.emit(line)
|
||||||
|
for (lhs, rhs) in self.connections:
|
||||||
|
line(f"connect {lhs} {rhs}")
|
||||||
|
if self.connections:
|
||||||
|
line()
|
||||||
|
line("end")
|
||||||
|
line()
|
||||||
|
|
||||||
|
|
||||||
class _ModuleBuilder(_AttrBuilder, _BufferedBuilder, _Namer):
|
def _make_attributes(attrs, src_loc):
|
||||||
def __init__(self, rtlil, name, attrs, *, src=None):
|
res = {}
|
||||||
super().__init__(emit_src=rtlil.emit_src)
|
if src_loc is not None:
|
||||||
self.rtlil = rtlil
|
res["src"] = _src(src_loc)
|
||||||
self.name = name
|
if attrs is not None:
|
||||||
self.src = src
|
res.update(attrs)
|
||||||
self.attrs = {"generator": "Amaranth"}
|
return res
|
||||||
self.attrs.update(attrs)
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self._attributes(self.attrs, src=self.src)
|
|
||||||
self._append("module {}\n", self.name)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, *args):
|
class Wire:
|
||||||
self._append("end\n")
|
def __init__(self, width, *, name, src_loc=None, attrs=None, signed=False, port_kind=None):
|
||||||
self.rtlil._buffer.write(str(self))
|
|
||||||
|
|
||||||
def wire(self, width, port_id=None, port_kind=None, name=None, attrs={}, src="", signed=False):
|
|
||||||
# Very large wires are unlikely to work. Verilog 1364-2005 requires the limit on vectors
|
# Very large wires are unlikely to work. Verilog 1364-2005 requires the limit on vectors
|
||||||
# to be at least 2**16 bits, and Yosys 0.9 cannot read RTLIL with wires larger than 2**32
|
# to be at least 2**16 bits, and Yosys 0.9 cannot read RTLIL with wires larger than 2**32
|
||||||
# bits. In practice, wires larger than 2**16 bits, although accepted, cause performance
|
# bits. In practice, wires larger than 2**16 bits, although accepted, cause performance
|
||||||
|
@ -137,131 +190,192 @@ class _ModuleBuilder(_AttrBuilder, _BufferedBuilder, _Namer):
|
||||||
if width > 2 ** 16:
|
if width > 2 ** 16:
|
||||||
raise OverflowError("Wire created at {} is {} bits wide, which is unlikely to "
|
raise OverflowError("Wire created at {} is {} bits wide, which is unlikely to "
|
||||||
"synthesize correctly"
|
"synthesize correctly"
|
||||||
.format(src or "unknown location", width))
|
.format(_src(src_loc) or "unknown location", width))
|
||||||
|
self.name = name
|
||||||
|
self.width = width
|
||||||
|
self.signed = signed
|
||||||
|
self.port_kind = port_kind
|
||||||
|
self.attributes = _make_attributes(attrs, src_loc)
|
||||||
|
|
||||||
self._attributes(attrs, src=src, indent=1)
|
def attribute(self, name, value):
|
||||||
name = self._make_name(name, local=False)
|
assert name not in self.attributes
|
||||||
signed = " signed" if signed else ""
|
self.attributes[name] = value
|
||||||
if port_id is None:
|
|
||||||
self._append(" wire width {}{} {}\n", width, signed, name)
|
def emit(self, line):
|
||||||
|
for name, value in self.attributes.items():
|
||||||
|
line(f"attribute \\{name} {_const(value)}")
|
||||||
|
signed = " signed" if self.signed else ""
|
||||||
|
if self.port_kind is None:
|
||||||
|
line(f"wire width {self.width}{signed} {self.name}")
|
||||||
else:
|
else:
|
||||||
assert port_kind in ("input", "output", "inout")
|
line(f"wire width {self.width} {self.port_kind} {line.port_id} {signed} {self.name}")
|
||||||
# By convention, Yosys ports named $\d+ are positional, so there is no way to use
|
line.port_id += 1
|
||||||
# a port with such a name. See amaranth-lang/amaranth#733.
|
line()
|
||||||
assert port_id is not None
|
|
||||||
self._append(" wire width {} {} {}{} {}\n", width, port_kind, port_id, signed, name)
|
|
||||||
return name
|
|
||||||
|
|
||||||
def connect(self, lhs, rhs):
|
|
||||||
self._append(" connect {} {}\n", lhs, rhs)
|
|
||||||
|
|
||||||
def memory(self, width, size, name=None, attrs={}, src=""):
|
|
||||||
self._attributes(attrs, src=src, indent=1)
|
|
||||||
name = self._make_name(name, local=False)
|
|
||||||
self._append(" memory width {} size {} {}\n", width, size, name)
|
|
||||||
return name
|
|
||||||
|
|
||||||
def cell(self, kind, name=None, params={}, ports={}, attrs={}, src=""):
|
|
||||||
self._attributes(attrs, src=src, indent=1)
|
|
||||||
name = self._make_name(name, local=False)
|
|
||||||
self._append(" cell {} {}\n", kind, name)
|
|
||||||
for param, value in params.items():
|
|
||||||
if isinstance(value, float):
|
|
||||||
self._append(" parameter real \\{} \"{!r}\"\n",
|
|
||||||
param, value)
|
|
||||||
elif _signed(value):
|
|
||||||
self._append(" parameter signed \\{} {}\n",
|
|
||||||
param, _const(value))
|
|
||||||
else:
|
|
||||||
self._append(" parameter \\{} {}\n",
|
|
||||||
param, _const(value))
|
|
||||||
for port, wire in ports.items():
|
|
||||||
self._append(" connect \\{} {}\n", port, wire)
|
|
||||||
self._append(" end\n")
|
|
||||||
return name
|
|
||||||
|
|
||||||
def process(self, name=None, attrs={}, src=""):
|
|
||||||
name = self._make_name(name, local=True)
|
|
||||||
return _ProcessBuilder(self, name, attrs, src)
|
|
||||||
|
|
||||||
|
|
||||||
class _ProcessBuilder(_AttrBuilder, _BufferedBuilder):
|
class Cell:
|
||||||
def __init__(self, rtlil, name, attrs, src):
|
def __init__(self, kind, *, name, ports=None, parameters=None, attrs=None, src_loc=None):
|
||||||
super().__init__(emit_src=rtlil.emit_src)
|
self.kind = kind
|
||||||
self.rtlil = rtlil
|
self.name = name
|
||||||
self.name = name
|
self.parameters = parameters or {}
|
||||||
self.attrs = {}
|
self.ports = ports or {}
|
||||||
self.src = src
|
self.attributes = _make_attributes(attrs, src_loc)
|
||||||
|
|
||||||
def __enter__(self):
|
def port(self, name, value):
|
||||||
self._attributes(self.attrs, src=self.src, indent=1)
|
assert name not in self.ports
|
||||||
self._append(" process {}\n", self.name)
|
self.ports[name] = value
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def parameter(self, name, value):
|
||||||
self._append(" end\n")
|
assert name not in self.parameters
|
||||||
self.rtlil._buffer.write(str(self))
|
self.parameters[name] = value
|
||||||
|
|
||||||
def case(self):
|
def attribute(self, name, value):
|
||||||
return _CaseBuilder(self, indent=2)
|
assert name not in self.attributes
|
||||||
|
self.attributes[name] = value
|
||||||
|
|
||||||
|
def emit(self, line):
|
||||||
|
for name, value in self.attributes.items():
|
||||||
|
line(f"attribute \\{name} {_const(value)}")
|
||||||
|
line(f"cell {self.kind} {self.name}")
|
||||||
|
with line.indent():
|
||||||
|
for name, value in self.parameters.items():
|
||||||
|
if isinstance(value, float):
|
||||||
|
line(f"parameter real \\{name} \"{value!r}\"")
|
||||||
|
elif _signed(value):
|
||||||
|
line(f"parameter signed \\{name} {_const(value)}")
|
||||||
|
else:
|
||||||
|
line(f"parameter \\{name} {_const(value)}")
|
||||||
|
for name, value in self.ports.items():
|
||||||
|
line(f"connect \\{name} {value}")
|
||||||
|
line(f"end")
|
||||||
|
line()
|
||||||
|
|
||||||
|
|
||||||
class _CaseBuilder:
|
class Memory:
|
||||||
def __init__(self, rtlil, indent):
|
def __init__(self, width, depth, *, name, attrs=None, src_loc=None):
|
||||||
self.rtlil = rtlil
|
self.width = width
|
||||||
self.indent = indent
|
self.depth = depth
|
||||||
|
self.name = name
|
||||||
|
self.attributes = _make_attributes(attrs, src_loc)
|
||||||
|
|
||||||
def _append(self, *args, **kwargs):
|
def attribute(self, name, value):
|
||||||
self.rtlil._append(*args, **kwargs)
|
assert name not in self.attributes
|
||||||
|
self.attributes[name] = value
|
||||||
|
|
||||||
def __enter__(self):
|
def emit(self, line):
|
||||||
return self
|
for name, value in self.attributes.items():
|
||||||
|
line(f"attribute \\{name} {_const(value)}")
|
||||||
|
line(f"memory width {self.width} size {self.depth} {self.name}")
|
||||||
|
line()
|
||||||
|
|
||||||
def __exit__(self, *args):
|
|
||||||
pass
|
def _emit_process_contents(contents, emit):
|
||||||
|
index = 0
|
||||||
|
while index < len(contents) and isinstance(contents[index], Assignment):
|
||||||
|
contents[index].emit(emit)
|
||||||
|
index += 1
|
||||||
|
while index < len(contents):
|
||||||
|
if isinstance(contents[index], Assignment):
|
||||||
|
emit(f"switch {{}}")
|
||||||
|
with emit.indent():
|
||||||
|
emit(f"case")
|
||||||
|
with emit.indent():
|
||||||
|
while index < len(contents) and isinstance(contents[index], Assignment):
|
||||||
|
contents[index].emit(emit)
|
||||||
|
index += 1
|
||||||
|
emit(f"end")
|
||||||
|
else:
|
||||||
|
contents[index].emit(emit)
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
|
||||||
|
class Process:
|
||||||
|
def __init__(self, *, name, attrs=None, src_loc=None):
|
||||||
|
self.name = name
|
||||||
|
self.contents = []
|
||||||
|
self.attributes = _make_attributes(attrs, src_loc)
|
||||||
|
|
||||||
|
def attribute(self, name, value):
|
||||||
|
assert name not in self.attributes
|
||||||
|
self.attributes[name] = value
|
||||||
|
|
||||||
def assign(self, lhs, rhs):
|
def assign(self, lhs, rhs):
|
||||||
self._append("{}assign {} {}\n", " " * self.indent, lhs, rhs)
|
self.contents.append(Assignment(lhs, rhs))
|
||||||
|
|
||||||
def switch(self, cond, attrs={}, src=""):
|
def switch(self, sel):
|
||||||
return _SwitchBuilder(self.rtlil, cond, attrs, src, self.indent)
|
res = Switch(sel)
|
||||||
|
self.contents.append(res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def emit(self, line):
|
||||||
|
for name, value in self.attributes.items():
|
||||||
|
line(f"attribute \\{name} {_const(value)}")
|
||||||
|
line(f"process {self.name}")
|
||||||
|
with line.indent():
|
||||||
|
_emit_process_contents(self.contents, line)
|
||||||
|
line(f"end")
|
||||||
|
line()
|
||||||
|
|
||||||
|
|
||||||
class _SwitchBuilder(_AttrBuilder):
|
class Assignment:
|
||||||
def __init__(self, rtlil, cond, attrs, src, indent):
|
def __init__(self, lhs, rhs):
|
||||||
super().__init__(emit_src=rtlil.emit_src)
|
self.lhs = lhs
|
||||||
self.rtlil = rtlil
|
self.rhs = rhs
|
||||||
self.cond = cond
|
|
||||||
self.attrs = attrs
|
|
||||||
self.src = src
|
|
||||||
self.indent = indent
|
|
||||||
|
|
||||||
def _append(self, *args, **kwargs):
|
def emit(self, line):
|
||||||
self.rtlil._append(*args, **kwargs)
|
line(f"assign {self.lhs} {self.rhs}")
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self._attributes(self.attrs, src=self.src, indent=self.indent)
|
|
||||||
self._append("{}switch {}\n", " " * self.indent, self.cond)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, *args):
|
class Switch:
|
||||||
self._append("{}end\n", " " * self.indent)
|
def __init__(self, sel):
|
||||||
|
self.sel = sel
|
||||||
|
self.cases = []
|
||||||
|
|
||||||
def case(self, *values, attrs={}, src=""):
|
def case(self, patterns):
|
||||||
self._attributes(attrs, src=src, indent=self.indent + 1)
|
res = Case(patterns)
|
||||||
if values == ():
|
if patterns:
|
||||||
self._append("{}case\n", " " * (self.indent + 1))
|
# RTLIL doesn't support cases with empty pattern list (they get interpreted
|
||||||
|
# as a default case instead, which is batshit and the exact opposite of
|
||||||
|
# what we want). When such a case is requested, return a case so that
|
||||||
|
# the caller can emit stuff into it, but don't actually include it in
|
||||||
|
# the switch.
|
||||||
|
self.cases.append(res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def default(self):
|
||||||
|
res = Case(())
|
||||||
|
self.cases.append(res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def emit(self, line):
|
||||||
|
line(f"switch {self.sel}")
|
||||||
|
with line.indent():
|
||||||
|
for case in self.cases:
|
||||||
|
case.emit(line)
|
||||||
|
line("end")
|
||||||
|
|
||||||
|
|
||||||
|
class Case:
|
||||||
|
def __init__(self, patterns):
|
||||||
|
self.patterns = patterns
|
||||||
|
self.contents = []
|
||||||
|
|
||||||
|
def assign(self, lhs, rhs):
|
||||||
|
self.contents.append(Assignment(lhs, rhs))
|
||||||
|
|
||||||
|
def switch(self, sel):
|
||||||
|
res = Switch(sel)
|
||||||
|
self.contents.append(res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def emit(self, line):
|
||||||
|
if self.patterns:
|
||||||
|
patterns = ", ".join(f"{len(pattern)}'{pattern}" for pattern in self.patterns)
|
||||||
|
line(f"case {patterns}")
|
||||||
else:
|
else:
|
||||||
self._append("{}case {}\n", " " * (self.indent + 1),
|
line(f"case")
|
||||||
", ".join(f"{len(value)}'{value}" for value in values))
|
with line.indent():
|
||||||
return _CaseBuilder(self.rtlil, self.indent + 2)
|
_emit_process_contents(self.contents, line)
|
||||||
|
|
||||||
|
|
||||||
def _src(src_loc):
|
|
||||||
if src_loc is None:
|
|
||||||
return None
|
|
||||||
file, line = src_loc
|
|
||||||
return f"{file}:{line}"
|
|
||||||
|
|
||||||
|
|
||||||
class MemoryInfo:
|
class MemoryInfo:
|
||||||
|
@ -316,7 +430,7 @@ class ModuleEmitter:
|
||||||
if isinstance(cell, _nir.Memory):
|
if isinstance(cell, _nir.Memory):
|
||||||
self.memories[cell_idx] = MemoryInfo(
|
self.memories[cell_idx] = MemoryInfo(
|
||||||
self.builder.memory(cell.width, cell.depth, name=cell.name,
|
self.builder.memory(cell.width, cell.depth, name=cell.name,
|
||||||
attrs=cell.attributes, src=_src(cell.src_loc)))
|
attrs=cell.attributes, src_loc=cell.src_loc).name)
|
||||||
|
|
||||||
for cell_idx in self.module.cells:
|
for cell_idx in self.module.cells:
|
||||||
cell = self.netlist.cells[cell_idx]
|
cell = self.netlist.cells[cell_idx]
|
||||||
|
@ -392,9 +506,9 @@ class ModuleEmitter:
|
||||||
shape = signal.shape()
|
shape = signal.shape()
|
||||||
wire = self.builder.wire(width=shape.width, signed=shape.signed,
|
wire = self.builder.wire(width=shape.width, signed=shape.signed,
|
||||||
name=name, attrs=attrs,
|
name=name, attrs=attrs,
|
||||||
src=_src(signal.src_loc))
|
src_loc=signal.src_loc)
|
||||||
self.sigport_wires[name] = (wire, value)
|
self.sigport_wires[name] = (wire, value)
|
||||||
self.name_map[signal] = (*self.module.name, wire[1:])
|
self.name_map[signal] = (*self.module.name, wire.name[1:])
|
||||||
|
|
||||||
def emit_port_wires(self):
|
def emit_port_wires(self):
|
||||||
named_signals = {name: signal for signal, name in self.module.signal_names.items()}
|
named_signals = {name: signal for signal, name in self.module.signal_names.items()}
|
||||||
|
@ -403,9 +517,9 @@ class ModuleEmitter:
|
||||||
if name in named_signals:
|
if name in named_signals:
|
||||||
signed = named_signals[name].shape().signed
|
signed = named_signals[name].shape().signed
|
||||||
wire = self.builder.wire(width=len(value), signed=signed,
|
wire = self.builder.wire(width=len(value), signed=signed,
|
||||||
port_id=port_id, port_kind=flow.value,
|
port_kind=flow.value,
|
||||||
name=name, attrs=self.value_attrs.get(value, {}),
|
name=name, attrs=self.value_attrs.get(value, {}),
|
||||||
src=_src(self.value_src_loc.get(value)))
|
src_loc=self.value_src_loc.get(value))
|
||||||
self.sigport_wires[name] = (wire, value)
|
self.sigport_wires[name] = (wire, value)
|
||||||
if flow == _nir.ModuleNetFlow.Output:
|
if flow == _nir.ModuleNetFlow.Output:
|
||||||
continue
|
continue
|
||||||
|
@ -425,9 +539,9 @@ class ModuleEmitter:
|
||||||
attrs = {}
|
attrs = {}
|
||||||
src_loc = None
|
src_loc = None
|
||||||
wire = self.builder.wire(width=len(value),
|
wire = self.builder.wire(width=len(value),
|
||||||
port_id=port_id, port_kind=dir.value,
|
port_kind=dir.value,
|
||||||
name=name, attrs=attrs,
|
name=name, attrs=attrs,
|
||||||
src=_src(src_loc))
|
src_loc=src_loc)
|
||||||
for bit, net in enumerate(value):
|
for bit, net in enumerate(value):
|
||||||
self.ionets[net] = (wire, bit)
|
self.ionets[net] = (wire, bit)
|
||||||
|
|
||||||
|
@ -516,9 +630,9 @@ class ModuleEmitter:
|
||||||
bit += 1
|
bit += 1
|
||||||
width = end_pos - begin_pos
|
width = end_pos - begin_pos
|
||||||
if width == 1:
|
if width == 1:
|
||||||
chunks.append(f"{wire} [{start_bit}]")
|
chunks.append(f"{wire.name} [{start_bit}]")
|
||||||
else:
|
else:
|
||||||
chunks.append(f"{wire} [{start_bit + width - 1}:{start_bit}]")
|
chunks.append(f"{wire.name} [{start_bit + width - 1}:{start_bit}]")
|
||||||
begin_pos = end_pos
|
begin_pos = end_pos
|
||||||
|
|
||||||
if len(chunks) == 1:
|
if len(chunks) == 1:
|
||||||
|
@ -538,9 +652,9 @@ class ModuleEmitter:
|
||||||
bit += 1
|
bit += 1
|
||||||
width = end_pos - begin_pos
|
width = end_pos - begin_pos
|
||||||
if width == 1:
|
if width == 1:
|
||||||
chunks.append(f"{wire} [{start_bit}]")
|
chunks.append(f"{wire.name} [{start_bit}]")
|
||||||
else:
|
else:
|
||||||
chunks.append(f"{wire} [{start_bit + width - 1}:{start_bit}]")
|
chunks.append(f"{wire.name} [{start_bit + width - 1}:{start_bit}]")
|
||||||
begin_pos = end_pos
|
begin_pos = end_pos
|
||||||
|
|
||||||
if len(chunks) == 1:
|
if len(chunks) == 1:
|
||||||
|
@ -550,7 +664,7 @@ class ModuleEmitter:
|
||||||
def emit_connects(self):
|
def emit_connects(self):
|
||||||
for name, (wire, value) in self.sigport_wires.items():
|
for name, (wire, value) in self.sigport_wires.items():
|
||||||
if name not in self.driven_sigports:
|
if name not in self.driven_sigports:
|
||||||
self.builder.connect(wire, self.sigspec(value))
|
self.builder.connect(wire.name, self.sigspec(value))
|
||||||
|
|
||||||
def emit_submodules(self):
|
def emit_submodules(self):
|
||||||
for submodule_idx in self.module.submodules:
|
for submodule_idx in self.module.submodules:
|
||||||
|
@ -563,7 +677,7 @@ class ModuleEmitter:
|
||||||
for name, (value, _dir) in submodule.io_ports.items():
|
for name, (value, _dir) in submodule.io_ports.items():
|
||||||
ports[name] = self.io_sigspec(value)
|
ports[name] = self.io_sigspec(value)
|
||||||
self.builder.cell(f"\\{dotted_name}", submodule.name[-1], ports=ports,
|
self.builder.cell(f"\\{dotted_name}", submodule.name[-1], ports=ports,
|
||||||
src=_src(submodule.cell_src_loc))
|
src_loc=submodule.cell_src_loc)
|
||||||
|
|
||||||
def emit_assignment_list(self, cell_idx, cell):
|
def emit_assignment_list(self, cell_idx, cell):
|
||||||
def emit_assignments(case, cond):
|
def emit_assignments(case, cond):
|
||||||
|
@ -573,30 +687,13 @@ class ModuleEmitter:
|
||||||
# letting parent invocation take care of the remaining assignments.
|
# letting parent invocation take care of the remaining assignments.
|
||||||
nonlocal pos
|
nonlocal pos
|
||||||
|
|
||||||
emitted_switch = False
|
|
||||||
while pos < len(cell.assignments):
|
while pos < len(cell.assignments):
|
||||||
assign = cell.assignments[pos]
|
assign = cell.assignments[pos]
|
||||||
if assign.cond == cond and not emitted_switch:
|
if assign.cond == cond:
|
||||||
# Not nested, and we didn't emit a switch yet, so emit the assignment.
|
# Not nested, so emit the assignment.
|
||||||
case.assign(self.sigspec(lhs[assign.start:assign.start + len(assign.value)]),
|
case.assign(self.sigspec(lhs[assign.start:assign.start + len(assign.value)]),
|
||||||
self.sigspec(assign.value))
|
self.sigspec(assign.value))
|
||||||
pos += 1
|
pos += 1
|
||||||
elif assign.cond == cond:
|
|
||||||
# Not nested, but we emitted a subswitch. Wrap the assignments in a dummy
|
|
||||||
# switch. This is necessary because Yosys executes all assignments before all
|
|
||||||
# subswitches (but allows you to mix asssignments and switches in RTLIL, for
|
|
||||||
# maximum confusion).
|
|
||||||
with case.switch("{ }") as switch:
|
|
||||||
with switch.case("") as subcase:
|
|
||||||
while pos < len(cell.assignments):
|
|
||||||
assign = cell.assignments[pos]
|
|
||||||
if assign.cond == cond:
|
|
||||||
subcase.assign(self.sigspec(lhs[assign.start:assign.start +
|
|
||||||
len(assign.value)]),
|
|
||||||
self.sigspec(assign.value))
|
|
||||||
pos += 1
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
# Condition doesn't match this case's condition — either we encountered
|
# Condition doesn't match this case's condition — either we encountered
|
||||||
# a nested condition, or we should break out. Try to find out exactly
|
# a nested condition, or we should break out. Try to find out exactly
|
||||||
|
@ -627,31 +724,25 @@ class ModuleEmitter:
|
||||||
break
|
break
|
||||||
# Now emit cases for all PriorityMatch inputs, in sequence. Consume as many
|
# Now emit cases for all PriorityMatch inputs, in sequence. Consume as many
|
||||||
# assignments as possible along the way.
|
# assignments as possible along the way.
|
||||||
with case.switch(self.sigspec(test)) as switch:
|
switch = case.switch(self.sigspec(test))
|
||||||
for bit, net in enumerate(priority_cell.inputs):
|
for bit, net in enumerate(priority_cell.inputs):
|
||||||
subcond = _nir.Net.from_cell(priority_cell_idx, bit)
|
subcond = _nir.Net.from_cell(priority_cell_idx, bit)
|
||||||
if net == _nir.Net.from_const(1):
|
if net == _nir.Net.from_const(1):
|
||||||
patterns = ()
|
emit_assignments(switch.default(), subcond)
|
||||||
else:
|
else:
|
||||||
# Validate the above assumptions.
|
# Validate the above assumptions.
|
||||||
matches_cell = self.netlist.cells[net.cell]
|
matches_cell = self.netlist.cells[net.cell]
|
||||||
assert isinstance(matches_cell, _nir.Matches)
|
assert isinstance(matches_cell, _nir.Matches)
|
||||||
assert test == matches_cell.value
|
assert test == matches_cell.value
|
||||||
patterns = matches_cell.patterns
|
patterns = matches_cell.patterns
|
||||||
# RTLIL cannot support empty pattern sets.
|
emit_assignments(switch.case(patterns), subcond)
|
||||||
assert patterns
|
|
||||||
with switch.case(*patterns) as subcase:
|
|
||||||
emit_assignments(subcase, subcond)
|
|
||||||
emitted_switch = True
|
|
||||||
|
|
||||||
lhs = _nir.Value(_nir.Net.from_cell(cell_idx, bit) for bit in range(len(cell.default)))
|
lhs = _nir.Value(_nir.Net.from_cell(cell_idx, bit) for bit in range(len(cell.default)))
|
||||||
with self.builder.process(src=_src(cell.src_loc)) as proc:
|
proc = self.builder.process(src_loc=cell.src_loc)
|
||||||
with proc.case() as root_case:
|
proc.assign(self.sigspec(lhs), self.sigspec(cell.default))
|
||||||
root_case.assign(self.sigspec(lhs), self.sigspec(cell.default))
|
pos = 0 # nonlocally used in `emit_assignments`
|
||||||
|
emit_assignments(proc, _nir.Net.from_const(1))
|
||||||
pos = 0 # nonlocally used in `emit_assignments`
|
assert pos == len(cell.assignments)
|
||||||
emit_assignments(root_case, _nir.Net.from_const(1))
|
|
||||||
assert pos == len(cell.assignments)
|
|
||||||
|
|
||||||
def emit_operator(self, cell_idx, cell):
|
def emit_operator(self, cell_idx, cell):
|
||||||
UNARY_OPERATORS = {
|
UNARY_OPERATORS = {
|
||||||
|
@ -693,12 +784,12 @@ class ModuleEmitter:
|
||||||
operand, = cell.inputs
|
operand, = cell.inputs
|
||||||
self.builder.cell(cell_type, ports={
|
self.builder.cell(cell_type, ports={
|
||||||
"A": self.sigspec(operand),
|
"A": self.sigspec(operand),
|
||||||
"Y": self.cell_wires[cell_idx]
|
"Y": self.cell_wires[cell_idx].name
|
||||||
}, params={
|
}, parameters={
|
||||||
"A_SIGNED": False,
|
"A_SIGNED": False,
|
||||||
"A_WIDTH": len(operand),
|
"A_WIDTH": len(operand),
|
||||||
"Y_WIDTH": cell.width,
|
"Y_WIDTH": cell.width,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
elif len(cell.inputs) == 2:
|
elif len(cell.inputs) == 2:
|
||||||
cell_type, a_signed, b_signed = BINARY_OPERATORS[cell.operator]
|
cell_type, a_signed, b_signed = BINARY_OPERATORS[cell.operator]
|
||||||
operand_a, operand_b = cell.inputs
|
operand_a, operand_b = cell.inputs
|
||||||
|
@ -707,43 +798,43 @@ class ModuleEmitter:
|
||||||
self.builder.cell(cell_type, ports={
|
self.builder.cell(cell_type, ports={
|
||||||
"A": self.sigspec(operand_a),
|
"A": self.sigspec(operand_a),
|
||||||
"B": self.sigspec(operand_b),
|
"B": self.sigspec(operand_b),
|
||||||
"Y": result,
|
"Y": result.name,
|
||||||
}, params={
|
}, parameters={
|
||||||
"A_SIGNED": a_signed,
|
"A_SIGNED": a_signed,
|
||||||
"B_SIGNED": b_signed,
|
"B_SIGNED": b_signed,
|
||||||
"A_WIDTH": len(operand_a),
|
"A_WIDTH": len(operand_a),
|
||||||
"B_WIDTH": len(operand_b),
|
"B_WIDTH": len(operand_b),
|
||||||
"Y_WIDTH": cell.width,
|
"Y_WIDTH": cell.width,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
nonzero = self.builder.wire(1)
|
nonzero = self.builder.wire(1)
|
||||||
self.builder.cell("$reduce_bool", ports={
|
self.builder.cell("$reduce_bool", ports={
|
||||||
"A": self.sigspec(operand_b),
|
"A": self.sigspec(operand_b),
|
||||||
"Y": nonzero,
|
"Y": nonzero.name,
|
||||||
}, params={
|
}, parameters={
|
||||||
"A_SIGNED": False,
|
"A_SIGNED": False,
|
||||||
"A_WIDTH": len(operand_b),
|
"A_WIDTH": len(operand_b),
|
||||||
"Y_WIDTH": 1,
|
"Y_WIDTH": 1,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
self.builder.cell("$mux", ports={
|
self.builder.cell("$mux", ports={
|
||||||
"S": nonzero,
|
"S": nonzero.name,
|
||||||
"A": self.sigspec(_nir.Value.zeros(cell.width)),
|
"A": self.sigspec(_nir.Value.zeros(cell.width)),
|
||||||
"B": result,
|
"B": result.name,
|
||||||
"Y": self.cell_wires[cell_idx]
|
"Y": self.cell_wires[cell_idx].name
|
||||||
}, params={
|
}, parameters={
|
||||||
"WIDTH": cell.width,
|
"WIDTH": cell.width,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
else:
|
else:
|
||||||
self.builder.cell(cell_type, ports={
|
self.builder.cell(cell_type, ports={
|
||||||
"A": self.sigspec(operand_a),
|
"A": self.sigspec(operand_a),
|
||||||
"B": self.sigspec(operand_b),
|
"B": self.sigspec(operand_b),
|
||||||
"Y": self.cell_wires[cell_idx],
|
"Y": self.cell_wires[cell_idx].name,
|
||||||
}, params={
|
}, parameters={
|
||||||
"A_SIGNED": a_signed,
|
"A_SIGNED": a_signed,
|
||||||
"B_SIGNED": b_signed,
|
"B_SIGNED": b_signed,
|
||||||
"A_WIDTH": len(operand_a),
|
"A_WIDTH": len(operand_a),
|
||||||
"B_WIDTH": len(operand_b),
|
"B_WIDTH": len(operand_b),
|
||||||
"Y_WIDTH": cell.width,
|
"Y_WIDTH": cell.width,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
else:
|
else:
|
||||||
assert cell.operator == "m"
|
assert cell.operator == "m"
|
||||||
condition, if_true, if_false = cell.inputs
|
condition, if_true, if_false = cell.inputs
|
||||||
|
@ -751,10 +842,10 @@ class ModuleEmitter:
|
||||||
"S": self.sigspec(condition),
|
"S": self.sigspec(condition),
|
||||||
"A": self.sigspec(if_false),
|
"A": self.sigspec(if_false),
|
||||||
"B": self.sigspec(if_true),
|
"B": self.sigspec(if_true),
|
||||||
"Y": self.cell_wires[cell_idx]
|
"Y": self.cell_wires[cell_idx].name
|
||||||
}, params={
|
}, parameters={
|
||||||
"WIDTH": cell.width,
|
"WIDTH": cell.width,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
|
|
||||||
def emit_part(self, cell_idx, cell):
|
def emit_part(self, cell_idx, cell):
|
||||||
if cell.stride == 1:
|
if cell.stride == 1:
|
||||||
|
@ -762,38 +853,38 @@ class ModuleEmitter:
|
||||||
offset_width = len(cell.offset)
|
offset_width = len(cell.offset)
|
||||||
else:
|
else:
|
||||||
stride = _ast.Const(cell.stride)
|
stride = _ast.Const(cell.stride)
|
||||||
offset_width = len(cell.offset) + stride.width
|
offset_width = len(cell.offset) + len(stride)
|
||||||
offset = self.builder.wire(offset_width)
|
offset = self.builder.wire(offset_width).name
|
||||||
self.builder.cell("$mul", ports={
|
self.builder.cell("$mul", ports={
|
||||||
"A": self.sigspec(cell.offset),
|
"A": self.sigspec(cell.offset),
|
||||||
"B": _const(stride),
|
"B": _const(stride),
|
||||||
"Y": offset,
|
"Y": offset,
|
||||||
}, params={
|
}, parameters={
|
||||||
"A_SIGNED": False,
|
"A_SIGNED": False,
|
||||||
"B_SIGNED": False,
|
"B_SIGNED": False,
|
||||||
"A_WIDTH": len(cell.offset),
|
"A_WIDTH": len(cell.offset),
|
||||||
"B_WIDTH": stride.width,
|
"B_WIDTH": len(stride),
|
||||||
"Y_WIDTH": offset_width,
|
"Y_WIDTH": offset_width,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
self.builder.cell("$shift", ports={
|
self.builder.cell("$shift", ports={
|
||||||
"A": self.sigspec(cell.value),
|
"A": self.sigspec(cell.value),
|
||||||
"B": offset,
|
"B": offset,
|
||||||
"Y": self.cell_wires[cell_idx],
|
"Y": self.cell_wires[cell_idx].name,
|
||||||
}, params={
|
}, parameters={
|
||||||
"A_SIGNED": cell.value_signed,
|
"A_SIGNED": cell.value_signed,
|
||||||
"B_SIGNED": False,
|
"B_SIGNED": False,
|
||||||
"A_WIDTH": len(cell.value),
|
"A_WIDTH": len(cell.value),
|
||||||
"B_WIDTH": offset_width,
|
"B_WIDTH": offset_width,
|
||||||
"Y_WIDTH": cell.width,
|
"Y_WIDTH": cell.width,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
|
|
||||||
def emit_flip_flop(self, cell_idx, cell):
|
def emit_flip_flop(self, cell_idx, cell):
|
||||||
ports = {
|
ports = {
|
||||||
"D": self.sigspec(cell.data),
|
"D": self.sigspec(cell.data),
|
||||||
"CLK": self.sigspec(cell.clk),
|
"CLK": self.sigspec(cell.clk),
|
||||||
"Q": self.cell_wires[cell_idx]
|
"Q": self.cell_wires[cell_idx].name
|
||||||
}
|
}
|
||||||
params = {
|
parameters = {
|
||||||
"WIDTH": len(cell.data),
|
"WIDTH": len(cell.data),
|
||||||
"CLK_POLARITY": {
|
"CLK_POLARITY": {
|
||||||
"pos": True,
|
"pos": True,
|
||||||
|
@ -805,9 +896,9 @@ class ModuleEmitter:
|
||||||
else:
|
else:
|
||||||
cell_type = "$adff"
|
cell_type = "$adff"
|
||||||
ports["ARST"] = self.sigspec(cell.arst)
|
ports["ARST"] = self.sigspec(cell.arst)
|
||||||
params["ARST_POLARITY"] = True
|
parameters["ARST_POLARITY"] = True
|
||||||
params["ARST_VALUE"] = _ast.Const(cell.init, len(cell.data))
|
parameters["ARST_VALUE"] = _ast.Const(cell.init, len(cell.data))
|
||||||
self.builder.cell(cell_type, ports=ports, params=params, src=_src(cell.src_loc))
|
self.builder.cell(cell_type, ports=ports, parameters=parameters, src_loc=cell.src_loc)
|
||||||
|
|
||||||
def emit_io_buffer(self, cell_idx, cell):
|
def emit_io_buffer(self, cell_idx, cell):
|
||||||
if cell.dir is not _nir.IODirection.Input:
|
if cell.dir is not _nir.IODirection.Input:
|
||||||
|
@ -818,11 +909,11 @@ class ModuleEmitter:
|
||||||
"Y": self.io_sigspec(cell.port),
|
"Y": self.io_sigspec(cell.port),
|
||||||
"A": self.sigspec(cell.o),
|
"A": self.sigspec(cell.o),
|
||||||
"EN": self.sigspec(cell.oe),
|
"EN": self.sigspec(cell.oe),
|
||||||
}, params={
|
}, parameters={
|
||||||
"WIDTH": len(cell.port),
|
"WIDTH": len(cell.port),
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
if cell.dir is not _nir.IODirection.Output:
|
if cell.dir is not _nir.IODirection.Output:
|
||||||
self.builder.connect(self.cell_wires[cell_idx], self.io_sigspec(cell.port))
|
self.builder.connect(self.cell_wires[cell_idx].name, self.io_sigspec(cell.port))
|
||||||
|
|
||||||
def emit_memory(self, cell_idx, cell):
|
def emit_memory(self, cell_idx, cell):
|
||||||
memory_info = self.memories[cell_idx]
|
memory_info = self.memories[cell_idx]
|
||||||
|
@ -834,13 +925,13 @@ class ModuleEmitter:
|
||||||
for bit in range(cell.width)
|
for bit in range(cell.width)
|
||||||
),
|
),
|
||||||
"EN": self.sigspec(_nir.Value.ones(cell.width)),
|
"EN": self.sigspec(_nir.Value.ones(cell.width)),
|
||||||
}, params={
|
}, parameters={
|
||||||
"MEMID": memory_info.memid,
|
"MEMID": memory_info.memid,
|
||||||
"ABITS": 0,
|
"ABITS": 0,
|
||||||
"WIDTH": cell.width,
|
"WIDTH": cell.width,
|
||||||
"WORDS": cell.depth,
|
"WORDS": cell.depth,
|
||||||
"PRIORITY": 0,
|
"PRIORITY": 0,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
|
|
||||||
def emit_write_port(self, cell_idx, cell):
|
def emit_write_port(self, cell_idx, cell):
|
||||||
memory_info = self.memories[cell.memory]
|
memory_info = self.memories[cell.memory]
|
||||||
|
@ -850,7 +941,7 @@ class ModuleEmitter:
|
||||||
"EN": self.sigspec(cell.en),
|
"EN": self.sigspec(cell.en),
|
||||||
"CLK": self.sigspec(cell.clk),
|
"CLK": self.sigspec(cell.clk),
|
||||||
}
|
}
|
||||||
params = {
|
parameters = {
|
||||||
"MEMID": memory_info.memid,
|
"MEMID": memory_info.memid,
|
||||||
"ABITS": len(cell.addr),
|
"ABITS": len(cell.addr),
|
||||||
"WIDTH": len(cell.data),
|
"WIDTH": len(cell.data),
|
||||||
|
@ -862,13 +953,13 @@ class ModuleEmitter:
|
||||||
"PORTID": memory_info.write_port_ids[cell_idx],
|
"PORTID": memory_info.write_port_ids[cell_idx],
|
||||||
"PRIORITY_MASK": 0,
|
"PRIORITY_MASK": 0,
|
||||||
}
|
}
|
||||||
self.builder.cell(f"$memwr_v2", ports=ports, params=params, src=_src(cell.src_loc))
|
self.builder.cell(f"$memwr_v2", ports=ports, parameters=parameters, src_loc=cell.src_loc)
|
||||||
|
|
||||||
def emit_read_port(self, cell_idx, cell):
|
def emit_read_port(self, cell_idx, cell):
|
||||||
memory_info = self.memories[cell.memory]
|
memory_info = self.memories[cell.memory]
|
||||||
ports = {
|
ports = {
|
||||||
"ADDR": self.sigspec(cell.addr),
|
"ADDR": self.sigspec(cell.addr),
|
||||||
"DATA": self.cell_wires[cell_idx],
|
"DATA": self.cell_wires[cell_idx].name,
|
||||||
"ARST": self.sigspec(_nir.Net.from_const(0)),
|
"ARST": self.sigspec(_nir.Net.from_const(0)),
|
||||||
"SRST": self.sigspec(_nir.Net.from_const(0)),
|
"SRST": self.sigspec(_nir.Net.from_const(0)),
|
||||||
}
|
}
|
||||||
|
@ -879,7 +970,7 @@ class ModuleEmitter:
|
||||||
1 << memory_info.write_port_ids[write_port_cell_index]
|
1 << memory_info.write_port_ids[write_port_cell_index]
|
||||||
for write_port_cell_index in cell.transparent_for
|
for write_port_cell_index in cell.transparent_for
|
||||||
)
|
)
|
||||||
params = {
|
parameters = {
|
||||||
"MEMID": memory_info.memid,
|
"MEMID": memory_info.memid,
|
||||||
"ABITS": len(cell.addr),
|
"ABITS": len(cell.addr),
|
||||||
"WIDTH": cell.width,
|
"WIDTH": cell.width,
|
||||||
|
@ -895,7 +986,7 @@ class ModuleEmitter:
|
||||||
"EN": self.sigspec(_nir.Net.from_const(1)),
|
"EN": self.sigspec(_nir.Net.from_const(1)),
|
||||||
"CLK": self.sigspec(_nir.Net.from_const(0)),
|
"CLK": self.sigspec(_nir.Net.from_const(0)),
|
||||||
})
|
})
|
||||||
params.update({
|
parameters.update({
|
||||||
"CLK_ENABLE": False,
|
"CLK_ENABLE": False,
|
||||||
"CLK_POLARITY": True,
|
"CLK_POLARITY": True,
|
||||||
})
|
})
|
||||||
|
@ -904,14 +995,14 @@ class ModuleEmitter:
|
||||||
"EN": self.sigspec(cell.en),
|
"EN": self.sigspec(cell.en),
|
||||||
"CLK": self.sigspec(cell.clk),
|
"CLK": self.sigspec(cell.clk),
|
||||||
})
|
})
|
||||||
params.update({
|
parameters.update({
|
||||||
"CLK_ENABLE": True,
|
"CLK_ENABLE": True,
|
||||||
"CLK_POLARITY": {
|
"CLK_POLARITY": {
|
||||||
"pos": True,
|
"pos": True,
|
||||||
"neg": False,
|
"neg": False,
|
||||||
}[cell.clk_edge],
|
}[cell.clk_edge],
|
||||||
})
|
})
|
||||||
self.builder.cell(f"$memrd_v2", ports=ports, params=params, src=_src(cell.src_loc))
|
self.builder.cell(f"$memrd_v2", ports=ports, parameters=parameters, src_loc=cell.src_loc)
|
||||||
|
|
||||||
def emit_print(self, cell_idx, cell):
|
def emit_print(self, cell_idx, cell):
|
||||||
args = []
|
args = []
|
||||||
|
@ -964,50 +1055,51 @@ class ModuleEmitter:
|
||||||
"EN": self.sigspec(cell.en),
|
"EN": self.sigspec(cell.en),
|
||||||
"ARGS": self.sigspec(_nir.Value(args)),
|
"ARGS": self.sigspec(_nir.Value(args)),
|
||||||
}
|
}
|
||||||
params = {
|
parameters = {
|
||||||
"FORMAT": "".join(format),
|
"FORMAT": "".join(format),
|
||||||
"ARGS_WIDTH": len(args),
|
"ARGS_WIDTH": len(args),
|
||||||
"PRIORITY": -cell_idx,
|
"PRIORITY": -cell_idx,
|
||||||
}
|
}
|
||||||
if isinstance(cell, (_nir.AsyncPrint, _nir.AsyncProperty)):
|
if isinstance(cell, (_nir.AsyncPrint, _nir.AsyncProperty)):
|
||||||
ports["TRG"] = self.sigspec(_nir.Value())
|
ports["TRG"] = self.sigspec(_nir.Value())
|
||||||
params["TRG_ENABLE"] = False
|
parameters["TRG_ENABLE"] = False
|
||||||
params["TRG_WIDTH"] = 0
|
parameters["TRG_WIDTH"] = 0
|
||||||
params["TRG_POLARITY"] = 0
|
parameters["TRG_POLARITY"] = 0
|
||||||
if isinstance(cell, (_nir.SyncPrint, _nir.SyncProperty)):
|
if isinstance(cell, (_nir.SyncPrint, _nir.SyncProperty)):
|
||||||
ports["TRG"] = self.sigspec(cell.clk)
|
ports["TRG"] = self.sigspec(cell.clk)
|
||||||
params["TRG_ENABLE"] = True
|
parameters["TRG_ENABLE"] = True
|
||||||
params["TRG_WIDTH"] = 1
|
parameters["TRG_WIDTH"] = 1
|
||||||
params["TRG_POLARITY"] = cell.clk_edge == "pos"
|
parameters["TRG_POLARITY"] = cell.clk_edge == "pos"
|
||||||
if isinstance(cell, (_nir.AsyncPrint, _nir.SyncPrint)):
|
if isinstance(cell, (_nir.AsyncPrint, _nir.SyncPrint)):
|
||||||
self.builder.cell(f"$print", params=params, ports=ports, src=_src(cell.src_loc))
|
self.builder.cell(f"$print", parameters=parameters, ports=ports, src_loc=cell.src_loc)
|
||||||
if isinstance(cell, (_nir.AsyncProperty, _nir.SyncProperty)):
|
if isinstance(cell, (_nir.AsyncProperty, _nir.SyncProperty)):
|
||||||
params["FLAVOR"] = cell.kind
|
parameters["FLAVOR"] = cell.kind
|
||||||
ports["A"] = self.sigspec(cell.test)
|
ports["A"] = self.sigspec(cell.test)
|
||||||
self.builder.cell(f"$check", params=params, ports=ports, src=_src(cell.src_loc))
|
self.builder.cell(f"$check", parameters=parameters, ports=ports, src_loc=cell.src_loc)
|
||||||
|
|
||||||
def emit_any_value(self, cell_idx, cell):
|
def emit_any_value(self, cell_idx, cell):
|
||||||
self.builder.cell(f"${cell.kind}", ports={
|
self.builder.cell(f"${cell.kind}", ports={
|
||||||
"Y": self.cell_wires[cell_idx],
|
"Y": self.cell_wires[cell_idx].name,
|
||||||
}, params={
|
}, parameters={
|
||||||
"WIDTH": cell.width,
|
"WIDTH": cell.width,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
|
|
||||||
def emit_initial(self, cell_idx, cell):
|
def emit_initial(self, cell_idx, cell):
|
||||||
self.builder.cell("$initstate", ports={
|
self.builder.cell("$initstate", ports={
|
||||||
"Y": self.cell_wires[cell_idx],
|
"Y": self.cell_wires[cell_idx].name,
|
||||||
}, src=_src(cell.src_loc))
|
}, src_loc=cell.src_loc)
|
||||||
|
|
||||||
def emit_instance(self, cell_idx, cell):
|
def emit_instance(self, cell_idx, cell):
|
||||||
ports = {}
|
ports = {}
|
||||||
for name, nets in cell.ports_i.items():
|
for name, nets in cell.ports_i.items():
|
||||||
ports[name] = self.sigspec(nets)
|
ports[name] = self.sigspec(nets)
|
||||||
for name in cell.ports_o:
|
for name in cell.ports_o:
|
||||||
ports[name] = self.instance_wires[cell_idx, name]
|
ports[name] = self.instance_wires[cell_idx, name].name
|
||||||
for name, (ionets, _dir) in cell.ports_io.items():
|
for name, (ionets, _dir) in cell.ports_io.items():
|
||||||
ports[name] = self.io_sigspec(ionets)
|
ports[name] = self.io_sigspec(ionets)
|
||||||
self.builder.cell(f"\\{cell.type}", cell.name, ports=ports, params=cell.parameters,
|
self.builder.cell(f"\\{cell.type}", cell.name, ports=ports,
|
||||||
attrs=cell.attributes, src=_src(cell.src_loc))
|
parameters=cell.parameters, attrs=cell.attributes,
|
||||||
|
src_loc=cell.src_loc)
|
||||||
|
|
||||||
def emit_cells(self):
|
def emit_cells(self):
|
||||||
for cell_idx in self.module.cells:
|
for cell_idx in self.module.cells:
|
||||||
|
@ -1071,16 +1163,15 @@ def convert_fragment(fragment, ports=(), name="top", *, emit_src=True, **kwargs)
|
||||||
name_map = _ast.SignalDict()
|
name_map = _ast.SignalDict()
|
||||||
netlist = _ir.build_netlist(fragment, ports=ports, name=name, **kwargs)
|
netlist = _ir.build_netlist(fragment, ports=ports, name=name, **kwargs)
|
||||||
empty_checker = EmptyModuleChecker(netlist)
|
empty_checker = EmptyModuleChecker(netlist)
|
||||||
builder = _Builder(emit_src=emit_src)
|
builder = Design(emit_src=emit_src)
|
||||||
for module_idx, module in enumerate(netlist.modules):
|
for module_idx, module in enumerate(netlist.modules):
|
||||||
if empty_checker.is_empty(module_idx):
|
if empty_checker.is_empty(module_idx):
|
||||||
continue
|
continue
|
||||||
attrs = {}
|
module_builder = builder.module(".".join(module.name), src_loc=module.src_loc)
|
||||||
if module_idx == 0:
|
if module_idx == 0:
|
||||||
attrs["top"] = 1
|
module_builder.attribute("top", 1)
|
||||||
with builder.module(".".join(module.name), attrs=attrs, src=_src(module.src_loc)) as module_builder:
|
ModuleEmitter(module_builder, netlist, module, name_map,
|
||||||
ModuleEmitter(module_builder, netlist, module, name_map,
|
empty_checker=empty_checker).emit()
|
||||||
empty_checker=empty_checker).emit()
|
|
||||||
return str(builder), name_map
|
return str(builder), name_map
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -928,9 +928,6 @@ class NetlistEmitter:
|
||||||
elems = []
|
elems = []
|
||||||
for patterns, elem, in value.cases:
|
for patterns, elem, in value.cases:
|
||||||
if patterns is not None:
|
if patterns is not None:
|
||||||
if not patterns:
|
|
||||||
# Hack: empty pattern set cannot be supported by RTLIL.
|
|
||||||
continue
|
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
assert len(pattern) == len(test)
|
assert len(pattern) == len(test)
|
||||||
cell = _nir.Matches(module_idx, value=test, patterns=patterns,
|
cell = _nir.Matches(module_idx, value=test, patterns=patterns,
|
||||||
|
@ -1069,9 +1066,6 @@ class NetlistEmitter:
|
||||||
elems = []
|
elems = []
|
||||||
for patterns, elem in lhs.cases:
|
for patterns, elem in lhs.cases:
|
||||||
if patterns is not None:
|
if patterns is not None:
|
||||||
if not patterns:
|
|
||||||
# Hack: empty pattern set cannot be supported by RTLIL.
|
|
||||||
continue
|
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
assert len(pattern) == len(test)
|
assert len(pattern) == len(test)
|
||||||
cell = _nir.Matches(module_idx, value=test, patterns=patterns,
|
cell = _nir.Matches(module_idx, value=test, patterns=patterns,
|
||||||
|
@ -1169,9 +1163,6 @@ class NetlistEmitter:
|
||||||
case_stmts = []
|
case_stmts = []
|
||||||
for patterns, stmts, case_src_loc in stmt.cases:
|
for patterns, stmts, case_src_loc in stmt.cases:
|
||||||
if patterns is not None:
|
if patterns is not None:
|
||||||
if not patterns:
|
|
||||||
# Hack: empty pattern set cannot be supported by RTLIL.
|
|
||||||
continue
|
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
assert len(pattern) == len(test)
|
assert len(pattern) == len(test)
|
||||||
cell = _nir.Matches(module_idx, value=test, patterns=patterns,
|
cell = _nir.Matches(module_idx, value=test, patterns=patterns,
|
||||||
|
|
1834
tests/test_back_rtlil.py
Normal file
1834
tests/test_back_rtlil.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1632,32 +1632,33 @@ class AssignTestCase(FHDLTestCase):
|
||||||
(input 's5' 0.2:10)
|
(input 's5' 0.2:10)
|
||||||
(input 's6' 0.10:18)
|
(input 's6' 0.10:18)
|
||||||
(input 's7' 0.18:26)
|
(input 's7' 0.18:26)
|
||||||
(output 's1' 9.0:8)
|
(output 's1' 10.0:8)
|
||||||
(output 's2' 10.0:8)
|
(output 's2' 11.0:8)
|
||||||
(output 's3' 12.0:8)
|
(output 's3' 12.0:8)
|
||||||
(output 's4' 11.0:8)
|
(output 's4' 13.0:8)
|
||||||
)
|
)
|
||||||
(cell 0 0 (top
|
(cell 0 0 (top
|
||||||
(input 's5' 2:10)
|
(input 's5' 2:10)
|
||||||
(input 's6' 10:18)
|
(input 's6' 10:18)
|
||||||
(input 's7' 18:26)
|
(input 's7' 18:26)
|
||||||
(output 's1' 9.0:8)
|
(output 's1' 10.0:8)
|
||||||
(output 's2' 10.0:8)
|
(output 's2' 11.0:8)
|
||||||
(output 's3' 12.0:8)
|
(output 's3' 12.0:8)
|
||||||
(output 's4' 11.0:8)
|
(output 's4' 13.0:8)
|
||||||
))
|
))
|
||||||
(cell 1 0 (matches 0.2:6 0001))
|
(cell 1 0 (matches 0.2:6 0001))
|
||||||
(cell 2 0 (matches 0.2:6 0010 0011))
|
(cell 2 0 (matches 0.2:6 0010 0011))
|
||||||
(cell 3 0 (matches 0.2:6 11--))
|
(cell 3 0 (matches 0.2:6 ))
|
||||||
(cell 4 0 (priority_match 1 (cat 1.0 2.0 3.0)))
|
(cell 4 0 (matches 0.2:6 11--))
|
||||||
(cell 5 0 (matches 0.6:10 0100))
|
(cell 5 0 (priority_match 1 (cat 1.0 2.0 3.0 4.0)))
|
||||||
(cell 6 0 (matches 0.6:10 0101))
|
(cell 6 0 (matches 0.6:10 0100))
|
||||||
(cell 7 0 (matches 0.6:10 0110))
|
(cell 7 0 (matches 0.6:10 0101))
|
||||||
(cell 8 0 (priority_match 1 (cat 5.0 6.0 7.0 1'd1)))
|
(cell 8 0 (matches 0.6:10 0110))
|
||||||
(cell 9 0 (assignment_list 8'd0 (4.0 0:8 0.10:18) (8.0 0:8 0.18:26)))
|
(cell 9 0 (priority_match 1 (cat 6.0 7.0 8.0 1'd1)))
|
||||||
(cell 10 0 (assignment_list 8'd0 (4.1 0:8 0.10:18) (8.1 0:8 0.18:26)))
|
(cell 10 0 (assignment_list 8'd0 (5.0 0:8 0.10:18) (9.0 0:8 0.18:26)))
|
||||||
(cell 11 0 (assignment_list 8'd0 (4.2 0:8 0.10:18) (8.3 0:8 0.18:26)))
|
(cell 11 0 (assignment_list 8'd0 (5.1 0:8 0.10:18) (9.1 0:8 0.18:26)))
|
||||||
(cell 12 0 (assignment_list 8'd0 (8.2 0:8 0.18:26)))
|
(cell 12 0 (assignment_list 8'd0 (5.2 0:8 0.10:18) (9.2 0:8 0.18:26)))
|
||||||
|
(cell 13 0 (assignment_list 8'd0 (5.3 0:8 0.10:18) (9.3 0:8 0.18:26)))
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
@ -3042,7 +3043,7 @@ class RhsTestCase(FHDLTestCase):
|
||||||
(input 'i8ud' 0.26:34)
|
(input 'i8ud' 0.26:34)
|
||||||
(input 'i4' 0.34:38)
|
(input 'i4' 0.34:38)
|
||||||
(output 'o1' (cat 5.0:8 2'd0))
|
(output 'o1' (cat 5.0:8 2'd0))
|
||||||
(output 'o2' (cat 9.0:8 2'd0))
|
(output 'o2' (cat 10.0:8 2'd0))
|
||||||
)
|
)
|
||||||
(cell 0 0 (top
|
(cell 0 0 (top
|
||||||
(input 'i8ua' 2:10)
|
(input 'i8ua' 2:10)
|
||||||
|
@ -3051,7 +3052,7 @@ class RhsTestCase(FHDLTestCase):
|
||||||
(input 'i8ud' 26:34)
|
(input 'i8ud' 26:34)
|
||||||
(input 'i4' 34:38)
|
(input 'i4' 34:38)
|
||||||
(output 'o1' (cat 5.0:8 2'd0))
|
(output 'o1' (cat 5.0:8 2'd0))
|
||||||
(output 'o2' (cat 9.0:8 2'd0))
|
(output 'o2' (cat 10.0:8 2'd0))
|
||||||
))
|
))
|
||||||
(cell 1 0 (matches 0.34:38 0001))
|
(cell 1 0 (matches 0.34:38 0001))
|
||||||
(cell 2 0 (matches 0.34:38 0010 0011))
|
(cell 2 0 (matches 0.34:38 0010 0011))
|
||||||
|
@ -3063,12 +3064,14 @@ class RhsTestCase(FHDLTestCase):
|
||||||
(4.2 0:8 0.18:26)
|
(4.2 0:8 0.18:26)
|
||||||
))
|
))
|
||||||
(cell 6 0 (matches 0.34:38 0100 0101))
|
(cell 6 0 (matches 0.34:38 0100 0101))
|
||||||
(cell 7 0 (matches 0.34:38 0110 0111))
|
(cell 7 0 (matches 0.34:38 ))
|
||||||
(cell 8 0 (priority_match 1 (cat 6.0 7.0 1'd1)))
|
(cell 8 0 (matches 0.34:38 0110 0111))
|
||||||
(cell 9 0 (assignment_list 8'd0
|
(cell 9 0 (priority_match 1 (cat 6.0 7.0 8.0 1'd1)))
|
||||||
(8.0 0:8 0.2:10)
|
(cell 10 0 (assignment_list 8'd0
|
||||||
(8.1 0:8 0.18:26)
|
(9.0 0:8 0.2:10)
|
||||||
(8.2 0:8 0.26:34)
|
(9.1 0:8 0.10:18)
|
||||||
|
(9.2 0:8 0.18:26)
|
||||||
|
(9.3 0:8 0.26:34)
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
Loading…
Reference in a new issue