back.rtlil: implement Part.
This commit is contained in:
parent
f968678937
commit
87cd045ac3
|
@ -63,7 +63,7 @@ Compatibility summary
|
||||||
<br>Note: values no longer valid as keys in `dict` and `set`; use `ValueDict` and `ValueSet` instead.
|
<br>Note: values no longer valid as keys in `dict` and `set`; use `ValueDict` and `ValueSet` instead.
|
||||||
- (+) `wrap` → `Value.wrap`
|
- (+) `wrap` → `Value.wrap`
|
||||||
- (+) `_Operator` → `Operator`
|
- (+) `_Operator` → `Operator`
|
||||||
- (+) `Mux` → `Mux`
|
- (+) `Mux` id
|
||||||
- (+) `_Slice` → `Slice`, `stop=`→`end=`, `.stop`→`.end`
|
- (+) `_Slice` → `Slice`, `stop=`→`end=`, `.stop`→`.end`
|
||||||
- (+) `_Part` → `Part`
|
- (+) `_Part` → `Part`
|
||||||
- (+) `Cat` id, `.l`→`.operands`
|
- (+) `Cat` id, `.l`→`.operands`
|
||||||
|
|
|
@ -204,12 +204,20 @@ def src(src_loc):
|
||||||
return "{}:{}".format(file, line)
|
return "{}:{}".format(file, line)
|
||||||
|
|
||||||
|
|
||||||
|
class LegalizeValue(Exception):
|
||||||
|
def __init__(self, value, branches):
|
||||||
|
self.value = value
|
||||||
|
self.branches = list(branches)
|
||||||
|
|
||||||
|
|
||||||
class _ValueCompilerState:
|
class _ValueCompilerState:
|
||||||
def __init__(self, rtlil):
|
def __init__(self, rtlil):
|
||||||
self.rtlil = rtlil
|
self.rtlil = rtlil
|
||||||
self.wires = ast.ValueDict()
|
self.wires = ast.ValueDict()
|
||||||
self.driven = ast.ValueDict()
|
self.driven = ast.ValueDict()
|
||||||
self.ports = ast.ValueDict()
|
self.ports = ast.ValueDict()
|
||||||
|
|
||||||
|
self.expansions = ast.ValueDict()
|
||||||
|
|
||||||
def add_driven(self, signal, sync):
|
def add_driven(self, signal, sync):
|
||||||
self.driven[signal] = sync
|
self.driven[signal] = sync
|
||||||
|
@ -255,11 +263,26 @@ class _ValueCompilerState:
|
||||||
wire_curr, wire_next = self.resolve(signal, prefix)
|
wire_curr, wire_next = self.resolve(signal, prefix)
|
||||||
return wire_curr
|
return wire_curr
|
||||||
|
|
||||||
|
def expand(self, value):
|
||||||
|
return self.expansions.get(value, value)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def expand_to(self, value, expansion):
|
||||||
|
try:
|
||||||
|
assert value not in self.expansions
|
||||||
|
self.expansions[value] = expansion
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
del self.expansions[value]
|
||||||
|
|
||||||
|
|
||||||
class _ValueCompiler(xfrm.AbstractValueTransformer):
|
class _ValueCompiler(xfrm.AbstractValueTransformer):
|
||||||
def __init__(self, state):
|
def __init__(self, state):
|
||||||
self.s = state
|
self.s = state
|
||||||
|
|
||||||
|
def on_value(self, value):
|
||||||
|
return super().on_value(self.s.expand(value))
|
||||||
|
|
||||||
def on_unknown(self, value):
|
def on_unknown(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
|
@ -426,7 +449,26 @@ class _RHSValueCompiler(_ValueCompiler):
|
||||||
return sigspec
|
return sigspec
|
||||||
|
|
||||||
def on_Part(self, value):
|
def on_Part(self, value):
|
||||||
raise NotImplementedError
|
lhs, rhs = value.value, value.offset
|
||||||
|
lhs_bits, lhs_sign = lhs.shape()
|
||||||
|
rhs_bits, rhs_sign = rhs.shape()
|
||||||
|
res_bits, res_sign = value.shape()
|
||||||
|
res = self.s.rtlil.wire(width=res_bits)
|
||||||
|
# Note: Verilog's x[o+:w] construct produces a $shiftx cell, not a $shift cell.
|
||||||
|
# However, Migen's semantics defines the out-of-range bits to be zero, so it is correct
|
||||||
|
# to use a $shift cell here instead, even though it produces less idiomatic Verilog.
|
||||||
|
self.s.rtlil.cell("$shift", ports={
|
||||||
|
"\\A": self(lhs),
|
||||||
|
"\\B": self(rhs),
|
||||||
|
"\\Y": res,
|
||||||
|
}, params={
|
||||||
|
"A_SIGNED": lhs_sign,
|
||||||
|
"A_WIDTH": lhs_bits,
|
||||||
|
"B_SIGNED": rhs_sign,
|
||||||
|
"B_WIDTH": rhs_bits,
|
||||||
|
"Y_WIDTH": res_bits,
|
||||||
|
}, src=src(value.src_loc))
|
||||||
|
return res
|
||||||
|
|
||||||
def on_Repl(self, value):
|
def on_Repl(self, value):
|
||||||
return "{{ {} }}".format(" ".join(self(value.value) for _ in range(value.count)))
|
return "{{ {} }}".format(" ".join(self(value.value) for _ in range(value.count)))
|
||||||
|
@ -453,7 +495,11 @@ class _LHSValueCompiler(_ValueCompiler):
|
||||||
return self(value)
|
return self(value)
|
||||||
|
|
||||||
def on_Part(self, value):
|
def on_Part(self, value):
|
||||||
raise NotImplementedError
|
offset = self.s.expand(value.offset)
|
||||||
|
if isinstance(offset, ast.Const):
|
||||||
|
return self(ast.Slice(value.value, offset.value, offset.value + value.width))
|
||||||
|
else:
|
||||||
|
raise LegalizeValue(value.offset, range(0, (1 << len(value.offset) - 1)))
|
||||||
|
|
||||||
def on_Repl(self, value):
|
def on_Repl(self, value):
|
||||||
raise TypeError # :nocov:
|
raise TypeError # :nocov:
|
||||||
|
@ -463,10 +509,22 @@ class _LHSValueCompiler(_ValueCompiler):
|
||||||
|
|
||||||
|
|
||||||
class _StatementCompiler(xfrm.AbstractStatementTransformer):
|
class _StatementCompiler(xfrm.AbstractStatementTransformer):
|
||||||
def __init__(self, rhs_compiler, lhs_compiler):
|
def __init__(self, state, rhs_compiler, lhs_compiler):
|
||||||
|
self.state = state
|
||||||
self.rhs_compiler = rhs_compiler
|
self.rhs_compiler = rhs_compiler
|
||||||
self.lhs_compiler = lhs_compiler
|
self.lhs_compiler = lhs_compiler
|
||||||
|
|
||||||
|
self._case = None
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def case(self, switch, value):
|
||||||
|
try:
|
||||||
|
old_case = self._case
|
||||||
|
with switch.case(value) as self._case:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
self._case = old_case
|
||||||
|
|
||||||
def on_Assign(self, stmt):
|
def on_Assign(self, stmt):
|
||||||
if isinstance(stmt, ast.Assign):
|
if isinstance(stmt, ast.Assign):
|
||||||
lhs_bits, lhs_sign = stmt.lhs.shape()
|
lhs_bits, lhs_sign = stmt.lhs.shape()
|
||||||
|
@ -477,15 +535,27 @@ class _StatementCompiler(xfrm.AbstractStatementTransformer):
|
||||||
# In RTLIL, LHS and RHS of assignment must have exactly same width.
|
# In RTLIL, LHS and RHS of assignment must have exactly same width.
|
||||||
rhs_sigspec = self.rhs_compiler.match_shape(
|
rhs_sigspec = self.rhs_compiler.match_shape(
|
||||||
stmt.rhs, lhs_bits, rhs_sign)
|
stmt.rhs, lhs_bits, rhs_sign)
|
||||||
self.case.assign(self.lhs_compiler(stmt.lhs), rhs_sigspec)
|
self._case.assign(self.lhs_compiler(stmt.lhs), rhs_sigspec)
|
||||||
|
|
||||||
def on_Switch(self, stmt):
|
def on_Switch(self, stmt):
|
||||||
with self.case.switch(self.rhs_compiler(stmt.test)) as switch:
|
with self._case.switch(self.rhs_compiler(stmt.test)) as switch:
|
||||||
for value, stmts in stmt.cases.items():
|
for value, stmts in stmt.cases.items():
|
||||||
old_case = self.case
|
with self.case(switch, value):
|
||||||
with switch.case(value) as self.case:
|
|
||||||
self.on_statements(stmts)
|
self.on_statements(stmts)
|
||||||
self.case = old_case
|
|
||||||
|
def on_statement(self, stmt):
|
||||||
|
try:
|
||||||
|
super().on_statement(stmt)
|
||||||
|
except LegalizeValue as legalize:
|
||||||
|
with self._case.switch(self.rhs_compiler(legalize.value)) as switch:
|
||||||
|
bits, sign = legalize.value.shape()
|
||||||
|
tests = ["{:0{}b}".format(v, bits) for v in legalize.branches]
|
||||||
|
tests[-1] = "-" * bits
|
||||||
|
for branch, test in zip(legalize.branches, tests):
|
||||||
|
with self.case(switch, test):
|
||||||
|
branch_value = ast.Const(branch, (bits, sign))
|
||||||
|
with self.state.expand_to(legalize.value, branch_value):
|
||||||
|
super().on_statement(stmt)
|
||||||
|
|
||||||
def on_statements(self, stmts):
|
def on_statements(self, stmts):
|
||||||
for stmt in stmts:
|
for stmt in stmts:
|
||||||
|
@ -497,7 +567,7 @@ def convert_fragment(builder, fragment, name, top):
|
||||||
compiler_state = _ValueCompilerState(module)
|
compiler_state = _ValueCompilerState(module)
|
||||||
rhs_compiler = _RHSValueCompiler(compiler_state)
|
rhs_compiler = _RHSValueCompiler(compiler_state)
|
||||||
lhs_compiler = _LHSValueCompiler(compiler_state)
|
lhs_compiler = _LHSValueCompiler(compiler_state)
|
||||||
stmt_compiler = _StatementCompiler(rhs_compiler, lhs_compiler)
|
stmt_compiler = _StatementCompiler(compiler_state, rhs_compiler, lhs_compiler)
|
||||||
|
|
||||||
# Register all signals driven in the current fragment. This must be done first, as it
|
# Register all signals driven in the current fragment. This must be done first, as it
|
||||||
# affects further codegen; e.g. whether sig$next signals will be generated and used.
|
# affects further codegen; e.g. whether sig$next signals will be generated and used.
|
||||||
|
@ -541,7 +611,7 @@ def convert_fragment(builder, fragment, name, top):
|
||||||
case.assign(lhs_compiler(signal), rhs_compiler(prev_value))
|
case.assign(lhs_compiler(signal), rhs_compiler(prev_value))
|
||||||
|
|
||||||
# Convert statements into decision trees.
|
# Convert statements into decision trees.
|
||||||
stmt_compiler.case = case
|
stmt_compiler._case = case
|
||||||
stmt_compiler(fragment.statements)
|
stmt_compiler(fragment.statements)
|
||||||
|
|
||||||
# For every signal in the sync domain, assign \sig's initial value (which will end up
|
# For every signal in the sync domain, assign \sig's initial value (which will end up
|
||||||
|
|
|
@ -890,13 +890,21 @@ class ValueKey:
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
if isinstance(self.value, Const):
|
if isinstance(self.value, Const):
|
||||||
return hash(self.value)
|
return hash(self.value.value)
|
||||||
elif isinstance(self.value, Signal):
|
elif isinstance(self.value, Signal):
|
||||||
return hash(id(self.value))
|
return hash(id(self.value))
|
||||||
|
elif isinstance(self.value, Operator):
|
||||||
|
return hash((self.value.op, tuple(ValueKey(o) for o in self.value.operands)))
|
||||||
elif isinstance(self.value, Slice):
|
elif isinstance(self.value, Slice):
|
||||||
return hash((ValueKey(self.value.value), self.value.start, self.value.end))
|
return hash((ValueKey(self.value.value), self.value.start, self.value.end))
|
||||||
|
elif isinstance(self.value, Part):
|
||||||
|
return hash((ValueKey(self.value.value), ValueKey(self.value.offset),
|
||||||
|
self.value.width))
|
||||||
|
elif isinstance(self.value, Cat):
|
||||||
|
return hash(tuple(ValueKey(o) for o in self.value.operands))
|
||||||
else: # :nocov:
|
else: # :nocov:
|
||||||
raise TypeError("Object '{!r}' cannot be used as a key in value collections")
|
raise TypeError("Object '{!r}' cannot be used as a key in value collections"
|
||||||
|
.format(self.value))
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not isinstance(other, ValueKey):
|
if not isinstance(other, ValueKey):
|
||||||
|
@ -905,15 +913,28 @@ class ValueKey:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if isinstance(self.value, Const):
|
if isinstance(self.value, Const):
|
||||||
return self.value == other.value
|
return self.value.value == other.value.value
|
||||||
elif isinstance(self.value, Signal):
|
elif isinstance(self.value, Signal):
|
||||||
return id(self.value) == id(other.value)
|
return id(self.value) == id(other.value)
|
||||||
|
elif isinstance(self.value, Operator):
|
||||||
|
return (self.value.op == other.value.op and
|
||||||
|
len(self.value.operands) == len(other.value.operands) and
|
||||||
|
all(ValueKey(a) == ValueKey(b)
|
||||||
|
for a, b in zip(self.value.operands, other.value.operands)))
|
||||||
elif isinstance(self.value, Slice):
|
elif isinstance(self.value, Slice):
|
||||||
return (ValueKey(self.value.value) == ValueKey(other.value.value) and
|
return (ValueKey(self.value.value) == ValueKey(other.value.value) and
|
||||||
self.value.start == other.value.start and
|
self.value.start == other.value.start and
|
||||||
self.value.end == other.value.end)
|
self.value.end == other.value.end)
|
||||||
|
elif isinstance(self.value, Part):
|
||||||
|
return (ValueKey(self.value.value) == ValueKey(other.value.value) and
|
||||||
|
ValueKey(self.value.offset) == ValueKey(other.value.offset) and
|
||||||
|
self.value.width == other.value.width)
|
||||||
|
elif isinstance(self.value, Cat):
|
||||||
|
return all(ValueKey(a) == ValueKey(b)
|
||||||
|
for a, b in zip(self.value.operands, other.value.operands))
|
||||||
else: # :nocov:
|
else: # :nocov:
|
||||||
raise TypeError("Object '{!r}' cannot be used as a key in value collections")
|
raise TypeError("Object '{!r}' cannot be used as a key in value collections"
|
||||||
|
.format(self.value))
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
if not isinstance(other, ValueKey):
|
if not isinstance(other, ValueKey):
|
||||||
|
|
Loading…
Reference in a new issue