Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: amaranth-lang/amaranth
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 438edf4112ef
Choose a base ref
...
head repository: amaranth-lang/amaranth
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 200af07f9f74
Choose a head ref
  • 3 commits
  • 3 files changed
  • 1 contributor

Commits on Aug 26, 2020

  1. sim._pyrtl: optimize uses of reflexive operators.

    When a literal is used on the left-hand side of a numeric operator,
    Python is able to constant-fold some expressions:
    
        >>> dis.dis(lambda x: 0 + 0 + x)
          1           0 LOAD_CONST               1 (0)
                      2 LOAD_FAST                0 (x)
                      4 BINARY_ADD
                      6 RETURN_VALUE
    
    If a literal is used on the right-hand side such that the left-hand
    side is variable, this doesn't happen:
    
        >>> dis.dis(lambda x: x + 0 + 0)
          1           0 LOAD_FAST                0 (x)
                      2 LOAD_CONST               1 (0)
                      4 BINARY_ADD
                      6 LOAD_CONST               1 (0)
                      8 BINARY_ADD
                     10 RETURN_VALUE
    
    PyRTL generates fairly redundant code due to the pervasive masking,
    and because of that, transforming expressions into the former form,
    where possible, improves runtime by about 10% on Minerva SRAM SoC.
    whitequark committed Aug 26, 2020
    Copy the full SHA
    8c6c364 View commit details
  2. vendor.xilinx_7series: unbreak.

    This commit fixes a series of typos introduced in commit 4e208b0.
    whitequark committed Aug 26, 2020
    Copy the full SHA
    abaa909 View commit details
  3. Copy the full SHA
    200af07 View commit details
Showing with 37 additions and 38 deletions.
  1. +23 −23 nmigen/sim/_pyrtl.py
  2. +10 −9 nmigen/vendor/xilinx_7series.py
  3. +4 −6 nmigen/vendor/xilinx_ultrascale.py
46 changes: 23 additions & 23 deletions nmigen/sim/_pyrtl.py
Original file line number Diff line number Diff line change
@@ -103,7 +103,7 @@ def on_Signal(self, value):
def on_Operator(self, value):
def mask(value):
value_mask = (1 << len(value)) - 1
return f"({self(value)} & {value_mask})"
return f"({value_mask} & {self(value)})"

def sign(value):
if value.shape().signed:
@@ -120,9 +120,9 @@ def sign(value):
if value.operator == "b":
return f"bool({mask(arg)})"
if value.operator == "r|":
return f"({mask(arg)} != 0)"
return f"(0 != {mask(arg)})"
if value.operator == "r&":
return f"({mask(arg)} == {(1 << len(arg)) - 1})"
return f"({(1 << len(arg)) - 1} == {mask(arg)})"
if value.operator == "r^":
# Believe it or not, this is the fastest way to compute a sideways XOR in Python.
return f"(format({mask(arg)}, 'b').count('1') % 2)"
@@ -172,28 +172,28 @@ def sign(value):
raise NotImplementedError("Operator '{}' not implemented".format(value.operator)) # :nocov:

def on_Slice(self, value):
return f"(({self(value.value)} >> {value.start}) & {(1 << len(value)) - 1})"
return f"({(1 << len(value)) - 1} & ({self(value.value)} >> {value.start}))"

def on_Part(self, value):
offset_mask = (1 << len(value.offset)) - 1
offset = f"(({self(value.offset)} & {offset_mask}) * {value.stride})"
return f"({self(value.value)} >> {offset} & " \
f"{(1 << value.width) - 1})"
offset = f"({value.stride} * ({offset_mask} & {self(value.offset)}))"
return f"({(1 << value.width) - 1} & " \
f"{self(value.value)} >> {offset})"

def on_Cat(self, value):
gen_parts = []
offset = 0
for part in value.parts:
part_mask = (1 << len(part)) - 1
gen_parts.append(f"(({self(part)} & {part_mask}) << {offset})")
gen_parts.append(f"(({part_mask} & {self(part)}) << {offset})")
offset += len(part)
if gen_parts:
return f"({' | '.join(gen_parts)})"
return f"0"

def on_Repl(self, value):
part_mask = (1 << len(value.value)) - 1
gen_part = self.emitter.def_var("repl", f"{self(value.value)} & {part_mask}")
gen_part = self.emitter.def_var("repl", f"{part_mask} & {self(value.value)}")
gen_parts = []
offset = 0
for _ in range(value.count):
@@ -205,15 +205,15 @@ def on_Repl(self, value):

def on_ArrayProxy(self, value):
index_mask = (1 << len(value.index)) - 1
gen_index = self.emitter.def_var("rhs_index", f"{self(value.index)} & {index_mask}")
gen_index = self.emitter.def_var("rhs_index", f"{index_mask} & {self(value.index)}")
gen_value = self.emitter.gen_var("rhs_proxy")
if value.elems:
gen_elems = []
for index, elem in enumerate(value.elems):
if index == 0:
self.emitter.append(f"if {gen_index} == {index}:")
self.emitter.append(f"if {index} == {gen_index}:")
else:
self.emitter.append(f"elif {gen_index} == {index}:")
self.emitter.append(f"elif {index} == {gen_index}:")
with self.emitter.indent():
self.emitter.append(f"{gen_value} = {self(elem)}")
self.emitter.append(f"else:")
@@ -253,9 +253,9 @@ def on_Signal(self, value):
def gen(arg):
value_mask = (1 << len(value)) - 1
if value.shape().signed:
value_sign = f"sign({arg} & {value_mask}, {-1 << (len(value) - 1)})"
value_sign = f"sign({value_mask} & {arg}, {-1 << (len(value) - 1)})"
else: # unsigned
value_sign = f"{arg} & {value_mask}"
value_sign = f"{value_mask} & {arg}"
self.emitter.append(f"next_{self.state.get_signal(value)} = {value_sign}")
return gen

@@ -267,17 +267,17 @@ def gen(arg):
width_mask = (1 << (value.stop - value.start)) - 1
self(value.value)(f"({self.lrhs(value.value)} & " \
f"{~(width_mask << value.start)} | " \
f"(({arg} & {width_mask}) << {value.start}))")
f"(({width_mask} & {arg}) << {value.start}))")
return gen

def on_Part(self, value):
def gen(arg):
width_mask = (1 << value.width) - 1
offset_mask = (1 << len(value.offset)) - 1
offset = f"(({self.rrhs(value.offset)} & {offset_mask}) * {value.stride})"
offset = f"({value.stride} * ({offset_mask} & {self.rrhs(value.offset)}))"
self(value.value)(f"({self.lrhs(value.value)} & " \
f"~({width_mask} << {offset}) | " \
f"(({arg} & {width_mask}) << {offset}))")
f"(({width_mask} & {arg}) << {offset}))")
return gen

def on_Cat(self, value):
@@ -287,7 +287,7 @@ def gen(arg):
offset = 0
for part in value.parts:
part_mask = (1 << len(part)) - 1
self(part)(f"(({gen_arg} >> {offset}) & {part_mask})")
self(part)(f"({part_mask} & ({gen_arg} >> {offset}))")
offset += len(part)
return gen

@@ -302,9 +302,9 @@ def gen(arg):
gen_elems = []
for index, elem in enumerate(value.elems):
if index == 0:
self.emitter.append(f"if {gen_index} == {index}:")
self.emitter.append(f"if {index} == {gen_index}:")
else:
self.emitter.append(f"elif {gen_index} == {index}:")
self.emitter.append(f"elif {index} == {gen_index}:")
with self.emitter.indent():
self(elem)(arg)
self.emitter.append(f"else:")
@@ -332,7 +332,7 @@ def on_Assign(self, stmt):

def on_Switch(self, stmt):
gen_test = self.emitter.def_var("test",
f"{self.rhs(stmt.test)} & {(1 << len(stmt.test)) - 1}")
f"{(1 << len(stmt.test)) - 1} & {self.rhs(stmt.test)}")
for index, (patterns, stmts) in enumerate(stmt.cases.items()):
gen_checks = []
if not patterns:
@@ -342,10 +342,10 @@ def on_Switch(self, stmt):
if "-" in pattern:
mask = int("".join("0" if b == "-" else "1" for b in pattern), 2)
value = int("".join("0" if b == "-" else b for b in pattern), 2)
gen_checks.append(f"({gen_test} & {mask}) == {value}")
gen_checks.append(f"{value} == ({mask} & {gen_test})")
else:
value = int(pattern, 2)
gen_checks.append(f"{gen_test} == {value}")
gen_checks.append(f"{value} == {gen_test}")
if index == 0:
self.emitter.append(f"if {' or '.join(gen_checks)}:")
else:
19 changes: 10 additions & 9 deletions nmigen/vendor/xilinx_7series.py
Original file line number Diff line number Diff line change
@@ -317,10 +317,11 @@ def create_missing_domain(self, name):
ready = Signal()
m.submodules += Instance("STARTUPE2", o_EOS=ready)
m.domains += ClockDomain("sync", reset_less=self.default_rst is None)
# Actually use BUFGCTRL configured as BUFGCE, since using BUFGCE causes sim/synth
# mismatches with Vivado 2019.2, and the suggested workaround (SIM_DEVICE parameter)
# breaks Vivado 2017.4.
# Actually use BUFGCTRL configured as BUFGCE, since using BUFGCE causes
# sim/synth mismatches with Vivado 2019.2, and the suggested workaround
# (SIM_DEVICE parameter) breaks Vivado 2017.4.
m.submodules += Instance("BUFGCTRL",
p_SIM_DEVICE="7SERIES",
i_I0=clk_i, i_S0=C(1, 1), i_CE0=ready, i_IGNORE0=C(0, 1),
i_I1=C(1, 1), i_S1=C(0, 1), i_CE1=C(0, 1), i_IGNORE1=C(1, 1),
o_O=ClockSignal("sync")
@@ -473,7 +474,7 @@ def get_output(self, pin, port, attrs, invert):
return m

def get_tristate(self, pin, port, attrs, invert):
if toolchain == "Symbiflow":
if self.toolchain == "Symbiflow":
return super().get_tristate(pin, port, attrs, invert)

self._check_feature("single-ended tristate", pin, attrs,
@@ -489,7 +490,7 @@ def get_tristate(self, pin, port, attrs, invert):
return m

def get_input_output(self, pin, port, attrs, invert):
if toolchain == "Symbiflow":
if self.toolchain == "Symbiflow":
return super().get_input_output(pin, port, attrs, invert)

self._check_feature("single-ended input/output", pin, attrs,
@@ -506,7 +507,7 @@ def get_input_output(self, pin, port, attrs, invert):
return m

def get_diff_input(self, pin, port, attrs, invert):
if toolchain == "Symbiflow":
if self.toolchain == "Symbiflow":
return super().get_diff_input(pin, port, attrs, invert)

self._check_feature("differential input", pin, attrs,
@@ -521,7 +522,7 @@ def get_diff_input(self, pin, port, attrs, invert):
return m

def get_diff_output(self, pin, port, attrs, invert):
if toolchain == "Symbiflow":
if self.toolchain == "Symbiflow":
return super().get_diff_output(pin, port, attrs, invert)

self._check_feature("differential output", pin, attrs,
@@ -536,7 +537,7 @@ def get_diff_output(self, pin, port, attrs, invert):
return m

def get_diff_tristate(self, pin, port, attrs, invert):
if toolchain == "Symbiflow":
if self.toolchain == "Symbiflow":
return super().get_diff_tristate(pin, port, attrs, invert)

self._check_feature("differential tristate", pin, attrs,
@@ -552,7 +553,7 @@ def get_diff_tristate(self, pin, port, attrs, invert):
return m

def get_diff_input_output(self, pin, port, attrs, invert):
if toolchain == "Symbiflow":
if self.toolchain == "Symbiflow":
return super().get_diff_input_output(pin, port, attrs, invert)

self._check_feature("differential input/output", pin, attrs,
10 changes: 4 additions & 6 deletions nmigen/vendor/xilinx_ultrascale.py
Original file line number Diff line number Diff line change
@@ -168,12 +168,10 @@ def create_missing_domain(self, name):
ready = Signal()
m.submodules += Instance("STARTUPE3", o_EOS=ready)
m.domains += ClockDomain("sync", reset_less=self.default_rst is None)
# Actually use BUFGCTRL configured as BUFGCE, since using BUFGCE causes sim/synth
# mismatches with Vivado 2019.2, and the suggested workaround (SIM_DEVICE parameter)
# breaks Vivado 2017.4.
m.submodules += Instance("BUFGCTRL",
i_I0=clk_i, i_S0=C(1, 1), i_CE0=ready, i_IGNORE0=C(0, 1),
i_I1=C(1, 1), i_S1=C(0, 1), i_CE1=C(0, 1), i_IGNORE1=C(1, 1),
m.submodules += Instance("BUFGCE",
p_SIM_DEVICE="ULTRASCALE",
i_CE=ready,
i_I=clk_i,
o_O=ClockSignal("sync")
)
if self.default_rst is not None: