Add checks for multiplication/division instructions.

main
Jean-François Nguyen 2 years ago
parent b3255def24
commit a325393c42

@ -1,7 +1,7 @@
check unique --depth=15 --skip=12
check cia --depth=15
check gpr --depth=15
check insn --depth=15 --exclude=brh,brw,setbc,setbcr,setnbc,setnbcr
check insn --depth=17 --exclude=brh,brw,setbc,setbcr,setnbc,setnbcr

check microwatt:storage:data --depth=15


@ -102,6 +102,7 @@ class MicrowattWrapper(Elaboratable):
HAS_BTC => {has_btc},
HAS_SHORT_MULT => false,
HAS_POWERFV => true,
HAS_ALTOPS => true,
LOG_LENGTH => 0,
ICACHE_NUM_LINES => {icache_num_lines},
ICACHE_NUM_WAYS => {icache_num_ways},
@ -133,7 +134,8 @@ class MicrowattWrapper(Elaboratable):
end architecture behave;
""")

self.pfv = pfv.Interface(mem_aligned=False, illegal_insn_heai=False)
def __init__(self, *, bus_fairness=False, **kwargs):
self.pfv = pfv.Interface(mem_aligned=False, illegal_insn_heai=False, muldiv_altops=True)
self.wb_insn = wishbone.Interface(addr_width=29, data_width=64, granularity=8,
features=("stall",))
self.wb_data = wishbone.Interface(addr_width=29, data_width=64, granularity=8,

@ -28,7 +28,7 @@ class InsnCheck(PowerFVCheck, metaclass=InsnCheckMeta):
def __init__(self, *, depth, skip, core, **kwargs):
super().__init__(depth=depth, skip=skip, core=core, **kwargs)
self.insn = self.insn_cls()
self.spec = self.spec_cls(self.insn, mem_aligned=self.dut.pfv.mem_aligned)
self.spec = self.spec_cls(self.insn, mem_aligned=self.dut.pfv.mem_aligned, muldiv_altops=self.dut.pfv.muldiv_altops)

def testbench(self):
return InsnTestbench(self)

@ -1,4 +1,5 @@
from .addsub import *
from .muldiv import *
from .branch import *
from .syscall import *
from .loadstore import *

@ -0,0 +1,46 @@
from power_fv.insn import const
from power_fv.insn.spec.muldiv import MultiplySpec, DivideSpec
from power_fv.check.insn import InsnCheck


__all__ = [
"MULLI" ,
"MULLW" , "MULLW_" , "MULLWO" , "MULLWO_" ,
"MULHW" , "MULHW_" , "MULHWU" , "MULHWU_" ,
"DIVW" , "DIVW_" , "DIVWO" , "DIVWO_" ,
"DIVWU" , "DIVWU_" , "DIVWUO" , "DIVWUO_" ,
"DIVWE" , "DIVWE_" , "DIVWEO" , "DIVWEO_" ,
"DIVWEU", "DIVWEU_", "DIVWEUO", "DIVWEUO_",
"MODSW" , "MODUW" ,
]


class MULLI (InsnCheck, spec_cls=MultiplySpec, insn_cls=const.MULLI ): pass
class MULLW (InsnCheck, spec_cls=MultiplySpec, insn_cls=const.MULLW ): pass
class MULLW_ (InsnCheck, spec_cls=MultiplySpec, insn_cls=const.MULLW_ ): pass
class MULLWO (InsnCheck, spec_cls=MultiplySpec, insn_cls=const.MULLWO ): pass
class MULLWO_ (InsnCheck, spec_cls=MultiplySpec, insn_cls=const.MULLWO_): pass
class MULHW (InsnCheck, spec_cls=MultiplySpec, insn_cls=const.MULHW ): pass
class MULHW_ (InsnCheck, spec_cls=MultiplySpec, insn_cls=const.MULHW_ ): pass
class MULHWU (InsnCheck, spec_cls=MultiplySpec, insn_cls=const.MULHWU ): pass
class MULHWU_ (InsnCheck, spec_cls=MultiplySpec, insn_cls=const.MULHWU_): pass

class DIVW (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVW ): pass
class DIVW_ (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVW_ ): pass
class DIVWO (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWO ): pass
class DIVWO_ (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWO_ ): pass
class DIVWU (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWU ): pass
class DIVWU_ (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWU_ ): pass
class DIVWUO (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWUO ): pass
class DIVWUO_ (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWUO_ ): pass
class DIVWE (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWE ): pass
class DIVWE_ (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWE_ ): pass
class DIVWEO (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWEO ): pass
class DIVWEO_ (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWEO_ ): pass
class DIVWEU (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWEU ): pass
class DIVWEU_ (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWEU_ ): pass
class DIVWEUO (InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWEUO ): pass
class DIVWEUO_(InsnCheck, spec_cls=DivideSpec, insn_cls=const.DIVWEUO_): pass

class MODSW (InsnCheck, spec_cls=DivideSpec, insn_cls=const.MODSW): pass
class MODUW (InsnCheck, spec_cls=DivideSpec, insn_cls=const.MODUW): pass

@ -143,6 +143,38 @@ class NEG_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0),
class NEGO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(104), f.Rc(0))
class NEGO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(104), f.Rc(1))

# Multiply / Divide / Modulo

class MULLI (WordInsn): _fields = (f.PO( 7), f.RT(), f.RA(), f.SI())
class MULLW (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(235), f.Rc(0))
class MULLW_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(235), f.Rc(1))
class MULLWO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(235), f.Rc(0))
class MULLWO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(235), f.Rc(1))
class MULHW (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.XO( 75), f.Rc(0))
class MULHW_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.XO( 75), f.Rc(1))
class MULHWU (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.XO( 11), f.Rc(0))
class MULHWU_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.XO( 11), f.Rc(1))

class DIVW (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(491), f.Rc(0))
class DIVW_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(491), f.Rc(1))
class DIVWO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(491), f.Rc(0))
class DIVWO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(491), f.Rc(1))
class DIVWU (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(459), f.Rc(0))
class DIVWU_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(459), f.Rc(1))
class DIVWUO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(459), f.Rc(0))
class DIVWUO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(459), f.Rc(1))
class DIVWE (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(427), f.Rc(0))
class DIVWE_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(427), f.Rc(1))
class DIVWEO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(427), f.Rc(0))
class DIVWEO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(427), f.Rc(1))
class DIVWEU (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(395), f.Rc(0))
class DIVWEU_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(395), f.Rc(1))
class DIVWEUO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(395), f.Rc(0))
class DIVWEUO_(WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(395), f.Rc(1))

class MODSW (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.XO_X(779))
class MODUW (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.XO_X(267))

# Compare

class CMPI (WordInsn): _fields = (f.PO(11), f.BF(), f.L_D10(), f.RA(), f.SI())

@ -0,0 +1,295 @@
from amaranth import *
from amaranth.asserts import Assume

from power_fv import pfv
from power_fv.insn.const import *

from . import InsnSpec
from .utils import iea


__all__ = ["MultiplySpec", "DivideSpec"]


class MultiplySpec(InsnSpec, Elaboratable):
def elaborate(self, platform):
m = Module()

m.d.comb += [
self.pfv.stb .eq(1),
self.pfv.insn.eq(Cat(Const(0, 32), self.insn.as_value())),
self.pfv.intr.eq(0),
self.pfv.nia .eq(iea(self.pfv.cia + 4, self.pfv.msr.r_data.sf)),
self.pfv.msr.r_mask.sf.eq(1),
]

src_a = Signal(64)
src_b = Signal(64)
result = Signal(64)
ov_32 = Signal()

# Operand A : (RA) or EXTS((RA)(32:63)) or EXTZ((RA)(32:63))

m.d.comb += [
self.pfv.ra.index.eq(self.insn.RA),
self.pfv.ra.r_stb.eq(1),
]

if isinstance(self.insn, MULLI):
m.d.comb += src_a.eq(self.pfv.ra.r_data)
elif isinstance(self.insn, (
MULLW , MULLW_ , MULLWO, MULLWO_,
MULHW, MULHW_,
)):
m.d.comb += src_a.eq(self.pfv.ra.r_data[:32].as_signed())
elif isinstance(self.insn, (MULHWU, MULHWU_)):
m.d.comb += src_a.eq(self.pfv.ra.r_data[:32].as_unsigned())
else:
assert False

# Operand B : EXTS(SI) or EXTS((RB)(32:63)) or EXTZ((RB)(32:63))

if isinstance(self.insn, MULLI):
m.d.comb += src_b.eq(self.insn.SI)
elif isinstance(self.insn, (
MULLW, MULLW_, MULLWO, MULLWO_,
MULHW, MULHW_,
)):
m.d.comb += [
self.pfv.rb.index .eq(self.insn.RB),
self.pfv.rb.r_stb.eq(1),
src_b.eq(self.pfv.rb.r_data[:32].as_signed()),
]
elif isinstance(self.insn, (MULHWU, MULHWU_)):
m.d.comb += [
self.pfv.rb.index.eq(self.insn.RB),
self.pfv.rb.r_stb.eq(1),
src_b.eq(self.pfv.rb.r_data[:32].as_unsigned())
]
else:
assert False

if self.pfv.muldiv_altops:
altop_res = Signal(64)
altop_mask = Signal(64)
ca_32 = Signal()

if isinstance(self.insn, MULLI):
m.d.comb += altop_mask.eq(0xef31a883837039a0)
elif isinstance(self.insn, (MULLW, MULLW_)):
m.d.comb += altop_mask.eq(0x4931591f31f56de1)
elif isinstance(self.insn, (MULLWO, MULLWO_)):
m.d.comb += altop_mask.eq(0x37291ea821fbaf9d)
elif isinstance(self.insn, (MULHW, MULHW_)):
m.d.comb += altop_mask.eq(0x3426dcf55920989c)
elif isinstance(self.insn, (MULHWU, MULHWU_)):
m.d.comb += altop_mask.eq(0x491edb8a5f695d49)
else:
assert False

# Result : (Operand A + Operand B) ^ Altop Mask

m.d.comb += [
altop_res.eq(src_a + src_b),
ca_32.eq(altop_res[32] ^ src_a[32] ^ src_b[32]),
ov_32.eq((ca_32 ^ altop_res[31]) & ~(src_a[31] ^ src_b[31])),

result.eq(altop_res ^ altop_mask),
]

else:
raise NotImplementedError

# Write the result to RT

m.d.comb += [
self.pfv.rt.index .eq(self.insn.RT),
self.pfv.rt.w_stb .eq(1),
self.pfv.rt.w_data.eq(result),
]

# Set XER.{SO,OV,OV32} if the result overflows 32 bits

if isinstance(self.insn, (MULLWO, MULLWO_)):
m.d.comb += [
self.pfv.xer.w_mask.ov .eq(1),
self.pfv.xer.w_data.ov .eq(ov_32),
self.pfv.xer.w_mask.ov32.eq(1),
self.pfv.xer.w_data.ov32.eq(ov_32),
]
with m.If(ov_32):
m.d.comb += [
self.pfv.xer.w_mask.so.eq(1),
self.pfv.xer.w_data.so.eq(1),
]

# Write CR0

if isinstance(self.insn, (MULLW_, MULLWO_, MULHW_, MULHWU_)):
cr0_w_mask = Record([("so", 1), ("eq_", 1), ("gt", 1), ("lt", 1)])
cr0_w_data = Record([("so", 1), ("eq_", 1), ("gt", 1), ("lt", 1)])

m.d.comb += [
self.pfv.xer.r_mask.so.eq(1),

cr0_w_mask .eq(0b1111),
cr0_w_data.so .eq(Mux(self.pfv.xer.w_mask.so, self.pfv.xer.w_data.so, self.pfv.xer.r_data.so)),
cr0_w_data.eq_.eq(~Mux(self.pfv.msr.r_data.sf, result[:64].any(), result[:32].any())),
cr0_w_data.gt .eq(~(cr0_w_data.lt | cr0_w_data.eq_)),
cr0_w_data.lt .eq(Mux(self.pfv.msr.r_data.sf, result[63], result[31])),

self.pfv.cr.w_mask.cr0.eq(cr0_w_mask),
self.pfv.cr.w_data.cr0.eq(cr0_w_data),
]

return m


class DivideSpec(InsnSpec, Elaboratable):
def elaborate(self, platform):
m = Module()

m.d.comb += [
self.pfv.stb .eq(1),
self.pfv.insn.eq(Cat(Const(0, 32), self.insn.as_value())),
self.pfv.intr.eq(0),
self.pfv.nia .eq(iea(self.pfv.cia + 4, self.pfv.msr.r_data.sf)),
self.pfv.msr.r_mask.sf.eq(1),
]

dividend = Signal(64)
divisor = Signal(64)
result = Signal(64)
ov_32 = Signal()

# Dividend : (RA)(32:63) or (RA)(32:63)<<32

m.d.comb += [
self.pfv.ra.index.eq(self.insn.RA),
self.pfv.ra.r_stb.eq(1),
]

if isinstance(self.insn, (DIVW, DIVW_, DIVWO, DIVWO_, MODSW)):
m.d.comb += dividend.eq(self.pfv.ra.r_data[:32].as_signed())
elif isinstance(self.insn, (DIVWU, DIVWU_, DIVWUO, DIVWUO_, MODUW)):
m.d.comb += dividend.eq(self.pfv.ra.r_data[:32].as_unsigned())
elif isinstance(self.insn, (
DIVWE , DIVWE_ , DIVWEO , DIVWEO_ ,
DIVWEU, DIVWEU_, DIVWEUO, DIVWEUO_,
)):
m.d.comb += dividend.eq(Cat(Const(0, 32), self.pfv.ra.r_data[:32]))
else:
assert False

# Divisor : (RB)(32:63)

m.d.comb += [
self.pfv.rb.index.eq(self.insn.RB),
self.pfv.rb.r_stb.eq(1),
]

if isinstance(self.insn, (
DIVW , DIVW_ , DIVWO , DIVWO_ ,
DIVWE, DIVWE_, DIVWEO, DIVWEO_,
MODSW,
)):
m.d.comb += divisor.eq(self.pfv.rb.r_data[:32].as_signed())
elif isinstance(self.insn, (
DIVWU , DIVWU_ , DIVWUO , DIVWUO_ ,
DIVWEU, DIVWEU_, DIVWEUO, DIVWEUO_,
MODUW ,
)):
m.d.comb += divisor.eq(self.pfv.rb.r_data[:32].as_unsigned())
else:
assert False

if self.pfv.muldiv_altops:
altop_mask = Signal(64)
altop_res = Signal(signed(64))
ca_32 = Signal()

if isinstance(self.insn, (DIVW, DIVW_)):
m.d.comb += altop_mask.eq(0x75a5d4895a3e15ba)
elif isinstance(self.insn, (DIVWO, DIVWO_)):
m.d.comb += altop_mask.eq(0x7098f59fd4822d48)
elif isinstance(self.insn, (DIVWU, DIVWU_)):
m.d.comb += altop_mask.eq(0x769c76af68d11402)
elif isinstance(self.insn, (DIVWUO, DIVWUO_)):
m.d.comb += altop_mask.eq(0x6ec48c33b1fe6a8f)
elif isinstance(self.insn, (DIVWE, DIVWE_)):
m.d.comb += altop_mask.eq(0xdfd9d577965d84d2)
elif isinstance(self.insn, (DIVWEO, DIVWEO_)):
m.d.comb += altop_mask.eq(0x88ec39a41f3b07fd)
elif isinstance(self.insn, (DIVWEU, DIVWEU_)):
m.d.comb += altop_mask.eq(0x8fc71f88b966fcf0)
elif isinstance(self.insn, (DIVWEUO, DIVWEUO_)):
m.d.comb += altop_mask.eq(0x893cca367133b0d3)
elif isinstance(self.insn, MODSW):
m.d.comb += altop_mask.eq(0x5ba1758b11ae4e43)
elif isinstance(self.insn, MODUW):
m.d.comb += altop_mask.eq(0x1feb9d95f9f0cea5)
else:
assert False

# Result : (Dividend - Divisor) ^ Altop Mask

m.d.comb += [
altop_res.eq(dividend.as_signed() - divisor.as_signed()),
ca_32.eq(altop_res[32] ^ dividend[32] ^ divisor[32]),
ov_32.eq((ca_32 ^ altop_res[31]) & ~(dividend[31] ^ divisor[31])),

result.eq(altop_res ^ altop_mask),
]

else:
raise NotImplementedError

# Write the result to RT

m.d.comb += [
self.pfv.rt.index .eq(self.insn.RT),
self.pfv.rt.w_stb .eq(1),
self.pfv.rt.w_data.eq(result),
]

# Set XER.{SO,OV,OV32} if the result overflows 32 bits

if isinstance(self.insn, (
DIVWO , DIVWO_ , DIVWUO , DIVWUO_ ,
DIVWEO, DIVWEO_, DIVWEUO, DIVWEUO_,
)):
m.d.comb += [
self.pfv.xer.w_mask.ov .eq(1),
self.pfv.xer.w_data.ov .eq(ov_32),
self.pfv.xer.w_mask.ov32.eq(1),
self.pfv.xer.w_data.ov32.eq(ov_32),
]
with m.If(ov_32):
m.d.comb += [
self.pfv.xer.w_mask.so.eq(1),
self.pfv.xer.w_data.so.eq(1),
]

# Write CR0

if isinstance(self.insn, (
DIVW_ , DIVWO_ , DIVWU_ , DIVWUO_ ,
DIVWE_, DIVWEO_, DIVWEU_, DIVWEUO_,
)):
cr0_w_mask = Record([("so", 1), ("eq_", 1), ("gt", 1), ("lt", 1)])
cr0_w_data = Record([("so", 1), ("eq_", 1), ("gt", 1), ("lt", 1)])

m.d.comb += [
self.pfv.xer.r_mask.so.eq(1),

cr0_w_mask .eq(0b1111),
cr0_w_data.so .eq(Mux(self.pfv.xer.w_mask.so, self.pfv.xer.w_data.so, self.pfv.xer.r_data.so)),
cr0_w_data.eq_.eq(~Mux(self.pfv.msr.r_data.sf, result[:64].any(), result[:32].any())),
cr0_w_data.gt .eq(~(cr0_w_data.lt | cr0_w_data.eq_)),
cr0_w_data.lt .eq(Mux(self.pfv.msr.r_data.sf, result[63], result[31])),

self.pfv.cr.w_mask.cr0.eq(cr0_w_mask),
self.pfv.cr.w_data.cr0.eq(cr0_w_data),
]

return m

@ -51,10 +51,11 @@ class Interface(Record):
Instruction strobe. Asserted when the processor retires an instruction. Other signals are
only valid when ``stb`` is asserted.
"""
def __init__(self, *, mem_aligned=False, illegal_insn_heai=False,
def __init__(self, *, mem_aligned=False, illegal_insn_heai=False, muldiv_altops=False,
name=None, src_loc_at=0):
self.mem_aligned = bool(mem_aligned)
self.illegal_insn_heai = bool(illegal_insn_heai)
self.muldiv_altops = bool(muldiv_altops)

layout = [
("stb" , unsigned( 1)),

Loading…
Cancel
Save