b = self.i.b
# Calculate the sign bit, with a chain of muxes. has to be done
- # this way due to (planned) use of PartitionedSignal. decreases
+ # this way due to (planned) use of SimdSignal. decreases
# readability slightly, but hey.
# Handle opcodes 0b00 and 0b01, copying or inverting the sign bit of B
from nmutil.gtkw import write_gtkw
from ieee754.part_mul_add.partpoints import PartitionPoints
-from ieee754.part.partsig import PartitionedSignal
+from ieee754.part.partsig import SimdSignal
class PartitionedPattern(Elaboratable):
# setup inputs and outputs
operands = list()
for i in range(nops):
- inp = PartitionedSignal(points, width, name=f"i_{i+1}")
+ inp = SimdSignal(points, width, name=f"i_{i+1}")
inp.set_module(m)
operands.append(inp)
if part_out:
output = Signal(out_width)
# perform the operation on the partitioned signals
result = self.op(*operands)
- if isinstance(result, PartitionedSignal):
+ if isinstance(result, SimdSignal):
comb += output.eq(result.sig)
else:
# handle operations that return plain Signals
the class turns into a SIMD variant of Signal. *this is dynamic*.
the basic fundamental idea is: write code once, and if you want a SIMD
-version of it, use PartitionedSignal in place of Signal. job done.
+version of it, use SimdSignal in place of Signal. job done.
this however requires the code to *not* be designed to use nmigen.If,
nmigen.Case, or other constructs: only Mux and other logic.
def getsig(op1):
- if isinstance(op1, PartitionedSignal):
+ if isinstance(op1, SimdSignal):
op1 = op1.sig
return op1
def applyop(op1, op2, op):
- if isinstance(op1, PartitionedSignal):
- result = PartitionedSignal.like(op1)
+ if isinstance(op1, SimdSignal):
+ result = SimdSignal.like(op1)
else:
- result = PartitionedSignal.like(op2)
+ result = SimdSignal.like(op2)
result.m.d.comb += result.sig.eq(op(getsig(op1), getsig(op2)))
return result
# Prototype https://bugs.libre-soc.org/show_bug.cgi?id=713#c53
-# this provides a "compatibility" layer with existing PartitionedSignal
+# this provides a "compatibility" layer with existing SimdSignal
# behaviour. the idea is that this interface defines which "combinations"
# of partition selections are relevant, and as an added bonus it says
# which partition lanes are completely irrelevant (padding, blank).
return 0 # TODO
-class PartitionedSignal(UserValue):
+class SimdSignal(UserValue):
# XXX ################################################### XXX
# XXX Keep these functions in the same order as ast.Value XXX
# XXX ################################################### XXX
@staticmethod
def like(other, *args, **kwargs):
- """Builds a new PartitionedSignal with the same PartitionPoints and
+ """Builds a new SimdSignal with the same PartitionPoints and
Signal properties as the other"""
- result = PartitionedSignal(PartitionPoints(other.partpoints))
+ result = SimdSignal(PartitionPoints(other.partpoints))
result.sig = Signal.like(other.sig, *args, **kwargs)
result.m = other.m
return result
def __Cat__(self, *args, src_loc_at=0):
args = [self] + list(args)
for sig in args:
- assert isinstance(sig, PartitionedSignal), \
- "All PartitionedSignal.__Cat__ arguments must be " \
- "a PartitionedSignal. %s is not." % repr(sig)
+ assert isinstance(sig, SimdSignal), \
+ "All SimdSignal.__Cat__ arguments must be " \
+ "a SimdSignal. %s is not." % repr(sig)
return PCat(self.m, args, self.ptype)
def __Mux__(self, val1, val2):
# print ("partsig mux", self, val1, val2)
assert len(val1) == len(val2), \
- "PartitionedSignal width sources must be the same " \
+ "SimdSignal width sources must be the same " \
"val1 == %d, val2 == %d" % (len(val1), len(val2))
return PMux(self.m, self.partpoints, self, val1, val2, self.ptype)
# unary ops that do not require partitioning
def __invert__(self):
- result = PartitionedSignal.like(self)
+ result = SimdSignal.like(self)
self.m.d.comb += result.sig.eq(~self.sig)
return result
comb += pa.a.eq(op1)
comb += pa.b.eq(op2)
comb += pa.carry_in.eq(carry)
- result = PartitionedSignal.like(self)
+ result = SimdSignal.like(self)
comb += result.sig.eq(pa.output)
return result, pa.carry_out
comb += pa.a.eq(op1)
comb += pa.b.eq(~op2)
comb += pa.carry_in.eq(carry)
- result = PartitionedSignal.like(self)
+ result = SimdSignal.like(self)
comb += result.sig.eq(pa.output)
return result, pa.carry_out
#def __check_shamt(self):
# TODO: detect if the 2nd operand is a Const, a Signal or a
- # PartitionedSignal. if it's a Const or a Signal, a global shift
- # can occur. if it's a PartitionedSignal, that's much more interesting.
+ # SimdSignal. if it's a Const or a Signal, a global shift
+ # can occur. if it's a SimdSignal, that's much more interesting.
def ls_op(self, op1, op2, carry, shr_flag=0):
op1 = getsig(op1)
if isinstance(op2, Const) or isinstance(op2, Signal):
op2 = getsig(op2)
pa = PartitionedDynamicShift(len(op1), self.partpoints)
# else:
- # TODO: case where the *shifter* is a PartitionedSignal but
+ # TODO: case where the *shifter* is a SimdSignal but
# the thing *being* Shifted is a scalar (Signal, expression)
# https://bugs.libre-soc.org/show_bug.cgi?id=718
setattr(self.m.submodules, self.get_modname('ls'), pa)
setattr(self.m.submodules, self.get_modname(opname), pa)
comb = self.m.d.comb
comb += pa.opcode.eq(optype) # set opcode
- if isinstance(op1, PartitionedSignal):
+ if isinstance(op1, SimdSignal):
comb += pa.a.eq(op1.sig)
else:
comb += pa.a.eq(op1)
- if isinstance(op2, PartitionedSignal):
+ if isinstance(op2, SimdSignal):
comb += pa.b.eq(op2.sig)
else:
comb += pa.b.eq(op2)
def __new_sign(self, signed):
shape = Shape(len(self), signed=signed)
- result = PartitionedSignal.like(self, shape=shape)
+ result = SimdSignal.like(self, shape=shape)
self.m.d.comb += result.sig.eq(self.sig)
return result
from nmigen.back.pysim import Simulator, Delay, Settle
from nmigen.cli import rtlil
-from ieee754.part.partsig import PartitionedSignal
+from ieee754.part.partsig import SimdSignal
from ieee754.part_mux.part_mux import PMux
from random import randint
class TestAddMod2(Elaboratable):
def __init__(self, width, partpoints):
self.partpoints = partpoints
- self.a = PartitionedSignal(partpoints, width)
- self.b = PartitionedSignal(partpoints, width)
+ self.a = SimdSignal(partpoints, width)
+ self.b = SimdSignal(partpoints, width)
self.bsig = Signal(width)
self.add_output = Signal(width)
self.ls_output = Signal(width) # left shift
self.lt_output = Signal(len(partpoints)+1)
self.le_output = Signal(len(partpoints)+1)
self.mux_sel2 = Signal(len(partpoints)+1)
- self.mux_sel2 = PartitionedSignal(partpoints, len(partpoints))
+ self.mux_sel2 = SimdSignal(partpoints, len(partpoints))
self.mux2_out = Signal(width)
self.carry_in = Signal(len(partpoints)+1)
self.add_carry_out = Signal(len(partpoints)+1)
class TestMuxMod(Elaboratable):
def __init__(self, width, partpoints):
self.partpoints = partpoints
- self.a = PartitionedSignal(partpoints, width)
- self.b = PartitionedSignal(partpoints, width)
+ self.a = SimdSignal(partpoints, width)
+ self.b = SimdSignal(partpoints, width)
self.mux_sel = Signal(len(partpoints)+1)
- self.mux_sel2 = PartitionedSignal(partpoints, len(partpoints)+1)
+ self.mux_sel2 = SimdSignal(partpoints, len(partpoints)+1)
self.mux_out2 = Signal(width)
def elaborate(self, platform):
class TestCatMod(Elaboratable):
def __init__(self, width, partpoints):
self.partpoints = partpoints
- self.a = PartitionedSignal(partpoints, width)
- self.b = PartitionedSignal(partpoints, width*2)
+ self.a = SimdSignal(partpoints, width)
+ self.b = SimdSignal(partpoints, width*2)
self.cat_out = Signal(width*3)
def elaborate(self, platform):
class TestReplMod(Elaboratable):
def __init__(self, width, partpoints):
self.partpoints = partpoints
- self.a = PartitionedSignal(partpoints, width)
+ self.a = SimdSignal(partpoints, width)
self.repl_sel = Signal(len(partpoints)+1)
self.repl_out = Signal(width*2)
if scalar:
self.a = Signal(width)
else:
- self.a = PartitionedSignal(partpoints, width)
- self.ass_out = PartitionedSignal(partpoints, out_shape)
+ self.a = SimdSignal(partpoints, width)
+ self.ass_out = SimdSignal(partpoints, out_shape)
def elaborate(self, platform):
m = Module()
class TestAddMod(Elaboratable):
def __init__(self, width, partpoints):
self.partpoints = partpoints
- self.a = PartitionedSignal(partpoints, width)
- self.b = PartitionedSignal(partpoints, width)
+ self.a = SimdSignal(partpoints, width)
+ self.b = SimdSignal(partpoints, width)
self.bsig = Signal(width)
self.add_output = Signal(width)
self.ls_output = Signal(width) # left shift
self.run_tst(16, out_width, sign, scalar)
-class TestPartitionedSignal(unittest.TestCase):
+class TestSimdSignal(unittest.TestCase):
def test(self):
width = 16
part_mask = Signal(3) # divide into 4-bits
sim.run()
-# TODO: adapt to PartitionedSignal. perhaps a different style?
+# TODO: adapt to SimdSignal. perhaps a different style?
'''
from nmigen.tests.test_hdl_ast import SignedEnum
def test_matches(self)
from nmutil.extend import ext
from ieee754.part_mul_add.partpoints import PartitionPoints
-from ieee754.part.partsig import PartitionedSignal
+from ieee754.part.partsig import SimdSignal
def get_runlengths(pbit, size):
def __init__(self, shape, assign, ctx):
"""Create a ``PartitionedAssign`` operator
"""
- # work out the length (total of all PartitionedSignals)
+ # work out the length (total of all SimdSignals)
self.assign = assign
self.ptype = ctx
self.shape = shape
mask = ctx.get_mask()
- self.output = PartitionedSignal(mask, self.shape, reset_less=True)
+ self.output = SimdSignal(mask, self.shape, reset_less=True)
self.partition_points = self.output.partpoints
self.mwidth = len(self.partition_points)+1
def get_chunk(self, y, numparts):
x = self.assign
- if not isinstance(x, PartitionedSignal):
+ if not isinstance(x, SimdSignal):
# assume Scalar. totally different rules
end = numparts * (len(x) // self.mwidth)
return x[:end]
- # PartitionedSignal: start at partition point
+ # SimdSignal: start at partition point
keys = [0] + list(x.partpoints.keys()) + [len(x)]
# get current index and increment it (for next Assign chunk)
upto = y[0]
return m
def ports(self):
- if isinstance(self.assign, PartitionedSignal):
+ if isinstance(self.assign, SimdSignal):
return [self.assign.lower(), self.output.lower()]
return [self.assign, self.output.lower()]
from ieee754.part.test.test_partsig import create_simulator
m = Module()
mask = Signal(3)
- a = PartitionedSignal(mask, 32)
+ a = SimdSignal(mask, 32)
m.submodules.ass = ass = PartitionedAssign(signed(48), a, a.ptype)
omask = (1<<len(ass.output))-1
from nmigen.back.pysim import Simulator, Settle
from ieee754.part_mul_add.partpoints import PartitionPoints
-from ieee754.part.partsig import PartitionedSignal
+from ieee754.part.partsig import SimdSignal
from ieee754.part.test.test_partsig import create_simulator
def __init__(self, catlist, ctx):
"""Create a ``PartitionedCat`` operator
"""
- # work out the length (total of all PartitionedSignals)
+ # work out the length (total of all SimdSignals)
self.catlist = catlist
self.ptype = ctx
width = 0
width += len(p.sig)
self.width = width
mask = ctx.get_mask()
- self.output = PartitionedSignal(mask, self.width, reset_less=True)
+ self.output = SimdSignal(mask, self.width, reset_less=True)
self.partition_points = self.output.partpoints
self.mwidth = len(self.partition_points)+1
if __name__ == "__main__":
m = Module()
mask = Signal(3)
- a = PartitionedSignal(mask, 32)
- b = PartitionedSignal(mask, 16)
+ a = SimdSignal(mask, 32)
+ b = SimdSignal(mask, 16)
catlist = [a, b]
m.submodules.cat = cat = PartitionedCat(catlist, a.ptype)
from nmigen.cli import rtlil
from ieee754.part_mul_add.partpoints import PartitionPoints
-from ieee754.part.partsig import PartitionedSignal
+from ieee754.part.partsig import SimdSignal
def get_runlengths(pbit, size):
def __init__(self, repl, qty, ctx):
"""Create a ``PartitionedRepl`` operator
"""
- # work out the length (total of all PartitionedSignals)
+ # work out the length (total of all SimdSignals)
self.repl = repl
self.qty = qty
width, signed = repl.shape()
self.ptype = ctx
self.shape = (width * qty), signed
mask = ctx.get_mask()
- self.output = PartitionedSignal(mask, self.shape, reset_less=True)
+ self.output = SimdSignal(mask, self.shape, reset_less=True)
self.partition_points = self.output.partpoints
self.mwidth = len(self.partition_points)+1
def get_chunk(self, y, numparts):
x = self.repl
- if not isinstance(x, PartitionedSignal):
+ if not isinstance(x, SimdSignal):
# assume Scalar. totally different rules
end = numparts * (len(x) // self.mwidth)
return x[:end]
- # PartitionedSignal: start at partition point
+ # SimdSignal: start at partition point
keys = [0] + list(x.partpoints.keys()) + [len(x)]
# get current index and increment it (for next Repl chunk)
upto = y[0]
return m
def ports(self):
- if isinstance(self.repl, PartitionedSignal):
+ if isinstance(self.repl, SimdSignal):
return [self.repl.lower(), self.output.lower()]
return [self.repl, self.output.lower()]
from ieee754.part.test.test_partsig import create_simulator
m = Module()
mask = Signal(3)
- a = PartitionedSignal(mask, 32)
+ a = SimdSignal(mask, 32)
print ("a.ptype", a.ptype)
m.submodules.repl = repl = PartitionedRepl(a, 2, a.ptype)
omask = (1<<len(repl.output))-1
from nmigen.hdl.dsl import Module
from nmigen.hdl.ir import Elaboratable, Fragment
from nmigen.sim import Simulator, Delay
-from ieee754.part.partsig import PartitionedSignal, PartitionPoints
+from ieee754.part.partsig import SimdSignal, PartitionPoints
import unittest
import textwrap
import subprocess
return retval
-class PartitionedSignalTester:
+class SimdSignalTester:
def __init__(self, m, operation, reference, *layouts,
src_loc_at=0, additional_case_count=30,
assert self.layouts[0].is_compatible(layout)
self.layouts.append(layout)
name = f"input_{len(self.inputs)}"
- ps = PartitionedSignal(
+ ps = SimdSignal(
layout.partition_points_signals(name=name,
src_loc_at=1 + src_loc_at),
layout.width,
self.seed = seed
self.case_number = Signal(64)
self.test_output = operation(tuple(self.inputs))
- assert isinstance(self.test_output, PartitionedSignal)
+ assert isinstance(self.test_output, SimdSignal)
self.test_output_layout = Layout(
self.test_output.partpoints, self.test_output.sig.width)
assert self.test_output_layout.is_compatible(self.layouts[0])
from nmigen.hdl.ast import AnyConst, Assert, Assume, Signal
from nmigen.hdl.dsl import Module
from ieee754.partitioned_signal_tester import (
- PartitionedSignalTester, Layout, Lane, formal)
+ SimdSignalTester, Layout, Lane, formal)
import unittest
(0, 1, 2, 3, 4, 5, 6, 7)])
-class TestPartitionedSignalTester(unittest.TestCase):
+class TestSimdSignalTester(unittest.TestCase):
def test_sim_identity(self):
m = Module()
- PartitionedSignalTester(m,
+ SimdSignalTester(m,
lambda inputs: inputs[0],
lambda lane, inputs: inputs[0],
(0, 8, 16, 24, 32)).run_sim(self)
def test_formal_identity(self):
m = Module()
- PartitionedSignalTester(m,
+ SimdSignalTester(m,
lambda inputs: inputs[0],
lambda lane, inputs: inputs[0],
(0, 8, 16, 24, 32)).run_formal(self)
def test_sim_pass_through_input(self):
for which_input in range(0, 2):
m = Module()
- PartitionedSignalTester(m,
+ SimdSignalTester(m,
lambda inputs: inputs[which_input],
lambda lane, inputs: inputs[which_input],
(0, 8, 16, 24, 32),
def test_formal_pass_through_input(self):
for which_input in range(0, 2):
m = Module()
- PartitionedSignalTester(m,
+ SimdSignalTester(m,
lambda inputs: inputs[which_input],
lambda lane, inputs: inputs[which_input],
(0, 8, 16, 24, 32),