# SPDX-License-Identifier: LGPLv3+
# Copyright (C) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
# Funded by NLnet http://nlnet.nl
+
+# sigh this entire module is a laborious mess. it really should be
+# auto-generated from the insndb/types.py database but a technique
+# for doing so (similar to python HTML/XML-node-walking) is needed
+
"""SVP64 RM (Remap) Record.
https://libre-soc.org/openpower/sv/svp64/
"""
from nmigen import Elaboratable, Module, Signal, Const
-from openpower.decoder.power_enums import (SVP64RMMode, Function, SVPtype,
- SVP64PredMode, SVP64sat, SVP64LDSTmode,
+from openpower.decoder.power_enums import (SVP64RMMode, Function, SVPType,
+ SVMode,
+ SVP64PredMode, SVP64Sat, SVP64LDSTmode,
SVP64BCPredMode, SVP64BCVLSETMode,
SVP64BCGate, SVP64BCCTRMode,
- SVP64width
+ SVP64Width
)
from openpower.consts import EXTRA3, SVP64MODE
from openpower.sv.svp64 import SVP64Rec
sv_input_record_layout = [
('sv_pred_sz', 1), # predicate source zeroing
('sv_pred_dz', 1), # predicate dest zeroing
- ('sv_saturate', SVP64sat),
+ ('sv_saturate', SVP64Sat),
('sv_ldstmode', SVP64LDSTmode),
- ('SV_Ptype', SVPtype),
+ ('SV_Ptype', SVPType),
+ ('SV_mode', SVMode),
#('sv_RC1', 1),
]
"""RM Mode
-there are four Mode variants, two for LD/ST, one for Branch-Conditional,
+there are five Mode variants, two for LD/ST, one for Branch-Conditional,
and one for everything else
https://libre-soc.org/openpower/sv/svp64/
https://libre-soc.org/openpower/sv/ldst/
https://libre-soc.org/openpower/sv/branches/
+https://libre-soc.org/openpower/sv/crops/
LD/ST immed:
-00 0 zz els normal mode (with element-stride option)
-01 inv CR-bit Rc=1: ffirst CR sel
-01 inv els RC1 Rc=0: ffirst z/nonz
-10 N zz els sat mode: N=0/1 u/s
-11 inv CR-bit Rc=1: pred-result CR sel
-11 inv els RC1 Rc=0: pred-result z/nonz
+
+| 0 | 1 | 2 | 3 4 | description |
+|---|---| --- |---------|--------------------------- |
+|els| 0 | PI | zz LF | simple mode |
+|VLi| 1 | inv | CR-bit | ffirst CR sel |
LD/ST indexed:
-00 0 sz dz normal mode
-00 1 rsvd reserved
-01 inv CR-bit Rc=1: ffirst CR sel
-01 inv dz RC1 Rc=0: ffirst z/nonz
-10 N sz dz sat mode: N=0/1 u/s
-11 inv CR-bit Rc=1: pred-result CR sel
-11 inv zz RC1 Rc=0: pred-result z/nonz
+
+| 0 | 1 | 2 | 3 4 | description |
+|---|---| --- |---------|--------------------------- |
+|els| 0 | PI | zz SEA | simple mode |
+|VLi| 1 | inv | CR-bit | ffirst CR sel |
Arithmetic:
-| 0-1 | 2 | 3 4 | description |
-| --- | --- |---------|-------------------------- |
-| 00 | 0 | dz sz | simple mode |
-| 00 | 1 | 0 RG | scalar reduce mode (mapreduce), SUBVL=1 |
-| 00 | 1 | SVM 0 | subvector reduce mode, SUBVL>1 |
-| 00 | 1 | / 1 | reserved |
-| 01 | inv | CR-bit | Rc=1: ffirst CR sel |
-| 01 | inv | VLi RC1 | Rc=0: ffirst z/nonz |
-| 10 | N | dz sz | sat mode: N=0/1 u/s, SUBVL=1 |
-| 10 | N | zz 0 | sat mode: N=0/1 u/s, SUBVL>1 |
-| 10 | N | / 1 | reserved |
-| 11 | inv | CR-bit | Rc=1: pred-result CR sel |
-| 11 | inv | zz RC1 | Rc=0: pred-result z/nonz |
+
+| 0-1 | 2 | 3 4 | description |
+| ------ | --- |---------|----------------------------------|
+| 0 0 | 0 | dz sz | simple mode |
+| 0 0 | 1 | RG 0 | scalar reduce mode (mapreduce) |
+| 0 0 | 1 | / 1 | reserved |
+| 1 0 | N | dz sz | sat mode: N=0/1 u/s |
+| VLi 1 | inv | CR-bit | Rc=1: ffirst CR sel |
+| VLi 1 | inv | zz RC1 | Rc=0: ffirst z/nonz |
+
+CROps:
+
+|6 | 7 |19:20|21 | 22:23 | description |
+|--|---|-----|---|---------|------------------|
+|/ | / |0 0 |RG | dz sz | simple mode |
+|/ | / |1 0 |RG | dz sz | scalar reduce mode (mapreduce) |
+|zz|SNZ|VLI 1|inv| CR-bit | Ffirst 3-bit mode |
+|/ |SNZ|VLI 1|inv| dz sz | Ffirst 5-bit mode (implies CR-bit from result) |
Branch Conditional:
+
note that additional BC modes are in *other bits*, specifically
the element-width fields: SVP64Rec.ewsrc and SVP64Rec.elwidth
##### inputs #####
self.rm_in = SVP64Rec(name=name)
self.fn_in = Signal(Function) # LD/ST and Branch is different
+ self.sv_mode = Signal(SVMode) # BRANCH/LDST_IMM/CROP etc.
self.svp64_vf_in = Signal() # Vertical-First Mode
- self.ptype_in = Signal(SVPtype)
+ self.ptype_in = Signal(SVPType)
self.rc_in = Signal()
+ self.cr_5bit_in = Signal() # if CR field was 5-bit
+ self.cr_2bit_in = Signal() # bottom 2 bits of CR field
self.ldst_ra_vec = Signal() # set when RA is vec, indicate Index mode
self.ldst_imz_in = Signal() # set when LD/ST immediate is zero
+ self.ldst_postinc = Signal() # set when LD/ST immediate post-inc set
+ self.ldst_ffirst = Signal() # set when LD/ST immediate fail-first set
##### outputs #####
self.pred_dz = Signal(1) # predicate dest zeroing
# Modes n stuff
- self.ew_src = Signal(SVP64width) # source elwidth
- self.ew_dst = Signal(SVP64width) # dest elwidth
+ self.ew_src = Signal(SVP64Width) # source elwidth
+ self.ew_dst = Signal(SVP64Width) # dest elwidth
self.subvl= Signal(2) # subvl
- self.saturate = Signal(SVP64sat)
+ self.saturate = Signal(SVP64Sat)
self.RC1 = Signal()
+ self.vli = Signal()
self.cr_sel = Signal(2) # bit of CR to test (index 0-3)
self.inv = Signal(1) # and whether it's inverted (like branch BO)
self.map_evm = Signal(1)
# decode pieces of mode
is_ldst = Signal()
is_bc = Signal()
+ is_cr = Signal()
+ is_ldstimm = Signal()
comb += is_ldst.eq(self.fn_in == Function.LDST)
- comb += is_bc.eq(self.fn_in == Function.BRANCH)
+ comb += is_bc.eq(self.fn_in == Function.BRANCH) # XXX TODO use SV Mode
+ comb += is_cr.eq(self.sv_mode == SVMode.CROP.value)
+ comb += is_ldstimm.eq(self.sv_mode == SVMode.LDST_IMM.value)
mode2 = sel(m, mode, SVP64MODE.MOD2)
cr = sel(m, mode, SVP64MODE.CR)
+ #####################
+ # Branch-Conditional decoding
+ #####################
with m.If(is_bc):
- # Branch-Conditional is completely different
# Counter-Test Mode.
with m.If(mode[SVP64MODE.BC_CTRTEST]):
with m.If(self.rm_in.ewsrc[1]):
comb += self.bc_lru.eq(self.rm_in.elwidth[0])
comb += self.bc_vsb.eq(self.rm_in.ewsrc[0])
+ #####################
+ # CRops decoding
+ #####################
+ with m.Elif(is_cr):
+ with m.Switch(mode2):
+ with m.Case(0, 2): # needs further decoding (LDST no mapreduce)
+ with m.If(mode[SVP64MODE.REDUCE]):
+ comb += self.mode.eq(SVP64RMMode.MAPREDUCE)
+ with m.Else():
+ comb += self.mode.eq(SVP64RMMode.NORMAL)
+ with m.Case(1,3):
+ comb += self.mode.eq(SVP64RMMode.FFIRST) # fail-first
+
+ # extract failfirst
+ with m.If(self.mode == SVP64RMMode.FFIRST): # fail-first
+ comb += self.inv.eq(mode[SVP64MODE.INV])
+ comb += self.vli.eq(mode[SVP64MODE.VLI])
+ with m.If(self.cr_5bit_in):
+ comb += self.cr_sel.eq(0b10) # EQ bit index is implicit
+ with m.Else():
+ comb += self.cr_sel.eq(cr)
+
+ #####################
+ # LDST decoding (both Idx and Imm - should be separate)
+ #####################
+ with m.Elif(is_ldst):
+ with m.Switch(mode2):
+ with m.Case(0, 2): # needs further decoding (LDST no mapreduce)
+ comb += self.mode.eq(SVP64RMMode.NORMAL)
+ comb += self.ldst_postinc.eq(mode[SVP64MODE.LDI_PI])
+ comb += self.ldst_ffirst.eq(mode[SVP64MODE.LDI_FF])
+ with m.Case(1, 3):
+ comb += self.mode.eq(SVP64RMMode.FFIRST) # ffirst
+
+ # extract zeroing
+ with m.If(is_ldst & ~is_ldstimm): # LDST-Indexed
+ with m.Switch(mode2):
+ with m.Case(0,2):
+ # sz/dz only when mode2[0] = 0
+ comb += self.pred_sz.eq(mode[SVP64MODE.DZ])
+ comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
+
+ with m.Elif(is_ldstimm): # LDST-Immediate
+ with m.Switch(mode2):
+ with m.Case(0,2): # simple mode
+ # use zz
+ comb += self.pred_sz.eq(mode[SVP64MODE.ZZ])
+ comb += self.pred_dz.eq(mode[SVP64MODE.ZZ])
+
+ # extract failfirst
+ with m.If(self.mode == SVP64RMMode.FFIRST): # fail-first
+ comb += self.inv.eq(mode[SVP64MODE.INV])
+ with m.If(is_ldst):
+ comb += self.vli.eq(mode[SVP64MODE.LDST_VLI])
+ with m.If(self.rc_in):
+ comb += self.cr_sel.eq(cr)
+ with m.Else():
+ # only when Rc=0
+ comb += self.RC1.eq(mode[SVP64MODE.RC1])
+ with m.If(~is_ldst):
+ comb += self.vli.eq(mode[SVP64MODE.VLI])
+ comb += self.cr_sel.eq(0b10) # EQ bit index is implicit
+
+ # extract saturate
+ with m.Switch(mode2):
+ with m.Case(2):
+ with m.If(mode[SVP64MODE.N]):
+ comb += self.saturate.eq(SVP64Sat.UNSIGNED)
+ with m.Else():
+ comb += self.saturate.eq(SVP64Sat.SIGNED)
+ with m.Default():
+ comb += self.saturate.eq(SVP64Sat.NONE)
+
+ # do elwidth/elwidth_src extract
+ comb += self.ew_src.eq(self.rm_in.ewsrc)
+ comb += self.ew_dst.eq(self.rm_in.elwidth)
+ comb += self.subvl.eq(self.rm_in.subvl)
+
+ # extract els (element strided mode bit)
+ # see https://libre-soc.org/openpower/sv/ldst/
+ els = Signal()
+ with m.Switch(mode2):
+ with m.Case(0, 2):
+ comb += els.eq(mode[SVP64MODE.LDST_ELS])
+ with m.Case(1, 3):
+ with m.If(self.rc_in):
+ comb += els.eq(mode[SVP64MODE.ELS_FFIRST_PRED])
+
+ # RA is vectorised
+ with m.If(self.ldst_ra_vec):
+ comb += self.ldstmode.eq(SVP64LDSTmode.INDEXED)
+ # not element-strided, therefore unit...
+ with m.Elif(~els):
+ comb += self.ldstmode.eq(SVP64LDSTmode.UNITSTRIDE)
+ # but if the LD/ST immediate is zero, allow cache-inhibited
+ # loads from same location, therefore don't do element-striding
+ with m.Elif(~self.ldst_imz_in):
+ comb += self.ldstmode.eq(SVP64LDSTmode.ELSTRIDE)
+
+ ######################
+ # arith decoding
+ ######################
with m.Else():
- # combined arith / ldst decoding due to similarity
with m.Switch(mode2):
with m.Case(0): # needs further decoding (LDST no mapreduce)
- with m.If(is_ldst):
- comb += self.mode.eq(SVP64RMMode.NORMAL)
- with m.Elif(mode[SVP64MODE.REDUCE]):
+ with m.If(mode[SVP64MODE.REDUCE]):
comb += self.mode.eq(SVP64RMMode.MAPREDUCE)
with m.Else():
comb += self.mode.eq(SVP64RMMode.NORMAL)
- with m.Case(1):
- comb += self.mode.eq(SVP64RMMode.FFIRST) # fail-first
+ with m.Case(1,3):
+ comb += self.mode.eq(SVP64RMMode.FFIRST) # ffirst
with m.Case(2):
comb += self.mode.eq(SVP64RMMode.SATURATE) # saturate
- with m.Case(3):
- comb += self.mode.eq(SVP64RMMode.PREDRES) # pred result
# extract "reverse gear" for mapreduce mode
with m.If((~is_ldst) & # not for LD/ST
# extract zeroing
with m.Switch(mode2):
- with m.Case(0): # needs further decoding (LDST no mapreduce)
- with m.If(is_ldst):
- # XXX TODO, work out which of these is most
- # appropriate set both? or just the one?
- # or one if LD, the other if ST?
- comb += self.pred_sz.eq(mode[SVP64MODE.DZ])
- comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
- with m.Elif(mode[SVP64MODE.REDUCE]):
- with m.If(self.rm_in.subvl == Const(0, 2)): # no SUBVL
- comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
- with m.Else():
+ with m.Case(0):
+ # normal-mode only active in simple mode
+ with m.If(~mode[SVP64MODE.REDUCE]): # bit 2 is zero?
comb += self.pred_sz.eq(mode[SVP64MODE.SZ])
comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
- with m.Case(1, 3):
- with m.If(is_ldst):
- with m.If(~self.ldst_ra_vec):
- comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
- with m.Elif(self.rc_in):
- comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
with m.Case(2):
- with m.If(is_ldst & ~self.ldst_ra_vec):
- comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
- with m.Else():
- comb += self.pred_sz.eq(mode[SVP64MODE.SZ])
- comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
+ # sat-mode all good...
+ comb += self.pred_sz.eq(mode[SVP64MODE.SZ])
+ comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
+ with m.Case(3):
+ # predicate-result has ZZ
+ with m.If(self.rc_in):
+ comb += self.pred_sz.eq(mode[SVP64MODE.ZZ])
+ comb += self.pred_dz.eq(mode[SVP64MODE.ZZ])
# extract failfirst
with m.If(self.mode == SVP64RMMode.FFIRST): # fail-first
+ comb += self.inv.eq(mode[SVP64MODE.INV])
with m.If(self.rc_in):
- comb += self.inv.eq(mode[SVP64MODE.INV])
comb += self.cr_sel.eq(cr)
+ with m.Else():
+ # only when Rc=0
+ comb += self.RC1.eq(mode[SVP64MODE.RC1])
+ with m.If(~is_ldst):
+ comb += self.vli.eq(mode[SVP64MODE.VLI])
+ comb += self.cr_sel.eq(0b10) # EQ bit index is implicit
# extract saturate
with m.Switch(mode2):
with m.Case(2):
with m.If(mode[SVP64MODE.N]):
- comb += self.saturate.eq(SVP64sat.UNSIGNED)
+ comb += self.saturate.eq(SVP64Sat.UNSIGNED)
with m.Else():
- comb += self.saturate.eq(SVP64sat.SIGNED)
+ comb += self.saturate.eq(SVP64Sat.SIGNED)
with m.Default():
- comb += self.saturate.eq(SVP64sat.NONE)
+ comb += self.saturate.eq(SVP64Sat.NONE)
# do elwidth/elwidth_src extract
comb += self.ew_src.eq(self.rm_in.ewsrc)
comb += self.ew_dst.eq(self.rm_in.elwidth)
comb += self.subvl.eq(self.rm_in.subvl)
- # extract els (element strided mode bit)
- # see https://libre-soc.org/openpower/sv/ldst/
- els = Signal()
- with m.If(is_ldst):
- with m.Switch(mode2):
- with m.Case(0):
- comb += els.eq(mode[SVP64MODE.ELS_NORMAL])
- with m.Case(2):
- comb += els.eq(mode[SVP64MODE.ELS_SAT])
- with m.Case(1, 3):
- with m.If(self.rc_in):
- comb += els.eq(mode[SVP64MODE.ELS_FFIRST_PRED])
-
- # RA is vectorised
- with m.If(self.ldst_ra_vec):
- comb += self.ldstmode.eq(SVP64LDSTmode.INDEXED)
- # not element-strided, therefore unit...
- with m.Elif(~els):
- comb += self.ldstmode.eq(SVP64LDSTmode.UNITSTRIDE)
- # but if the LD/ST immediate is zero, allow cache-inhibited
- # loads from same location, therefore don't do element-striding
- with m.Elif(~self.ldst_imz_in):
- comb += self.ldstmode.eq(SVP64LDSTmode.ELSTRIDE)
+ ######################
+ # Common fields (not many, sigh)
+ ######################
# extract src/dest predicate. use EXTRA3.MASK because EXTRA2.MASK
# is in exactly the same bits
srcmask = sel(m, self.rm_in.extra, EXTRA3.MASK)
dstmask = self.rm_in.mask
- with m.If(self.ptype_in == SVPtype.P2):
+ with m.If(self.ptype_in == SVPType.P2):
comb += self.srcpred.eq(srcmask)
with m.Else():
comb += self.srcpred.eq(dstmask)
with m.Else():
comb += self.predmode.eq(SVP64PredMode.INT) # non-zero src: INT
- # TODO: detect zeroing mode, saturation mode, a few more.
-
return m