"""
import gc
-from collections import namedtuple
-from nmigen import Module, Elaboratable, Signal, Cat, Mux
-from nmigen.cli import rtlil
-from soc.decoder.power_enums import (Function, Form, MicrOp,
- In1Sel, In2Sel, In3Sel, OutSel,
- SVEXTRA, SVEtype, SVPtype, # Simple-V
- RC, LdstLen, LDSTMode, CryIn,
- single_bit_flags, CRInSel,
- CROutSel, get_signal_name,
- default_values, insns, asmidx)
-from soc.decoder.power_fields import DecodeFields
-from soc.decoder.power_fieldsn import SigDecode, SignalBitRange
-from soc.decoder.power_svp64 import SVP64RM
+from collections import namedtuple, OrderedDict
+from nmigen import Module, Elaboratable, Signal, Cat, Mux, Const
+from nmigen.cli import rtlil, verilog
+from openpower.decoder.power_enums import (Function, Form, MicrOp,
+ In1Sel, In2Sel, In3Sel, OutSel,
+ SVEXTRA, SVEtype, SVPtype, # Simple-V
+ RC, LdstLen, LDSTMode, CryIn,
+ single_bit_flags, CRInSel,
+ CROutSel, get_signal_name,
+ default_values, insns, asmidx,
+ asmlen)
+from openpower.decoder.power_fields import DecodeFields
+from openpower.decoder.power_fieldsn import SigDecode, SignalBitRange
+from openpower.decoder.power_svp64 import SVP64RM
+
+from openpower.util import log
# key data structure in which the POWER decoder is specified,
# in a hierarchical fashion
power_op_types = {'function_unit': Function,
'internal_op': MicrOp,
'form': Form,
- 'asmcode': 8,
+ 'asmcode': asmlen,
'SV_Etype': SVEtype,
'SV_Ptype': SVPtype,
'in1_sel': In1Sel,
which generates an awful lot of wires, hence the subsetting
"""
- def __init__(self, incl_asm=True, name=None, subset=None):
+ def __init__(self, incl_asm=True, name=None, subset=None, fields=None):
+ self.name = name
self.subset = subset
+ if fields is not None:
+ for k, v in fields.items():
+ setattr(self, k, v)
+ return
debug_report = set()
fields = set()
for field, ptype in power_op_types.items():
debug_report.add(field)
fname = get_pname(field, name)
setattr(self, field, Signal(reset_less=True, name=fname))
- print("PowerOp debug", name, debug_report)
- print(" fields", fields)
+ self._fields = fields
+ # comment out, bit too high debug level
+ #print("PowerOp debug", name, debug_report)
+ #print(" fields", fields)
+
+ @staticmethod
+ def like(other):
+ """PowerOp.like: creates a duplicate of a given PowerOp instance
+ """
+ fields = {}
+ for fname in other._fields:
+ sig = getattr(other, fname, None)
+ if sig is not None:
+ fields[fname] = sig.__class__.like(sig)
+ return PowerOp(subset=other.subset, fields=fields)
def _eq(self, row=None):
if row is None:
if row['CR in'] == '1':
import pdb
pdb.set_trace()
- print(row)
+ log(row)
if row['CR out'] == '0':
import pdb
pdb.set_trace()
- print(row)
- print(row)
+ log(row)
+ log(row)
ldst_mode = row['upd']
if ldst_mode.isdigit():
row['upd'] = int(ldst_mode)
if field not in power_op_csvmap:
continue
csvname = power_op_csvmap[field]
- print(field, ptype, csvname, row)
+ # log(field, ptype, csvname, row)
val = row[csvname]
if csvname == 'upd' and isinstance(val, int): # LDSTMode different
val = ptype(val)
val = ptype[val]
res.append(getattr(self, field).eq(val))
if False:
- print(row.keys())
+ log(row.keys())
asmcode = row['comment']
+ # process the comment field, strip out "equals" for FP
+ if "=" in asmcode:
+ asmcode = asmcode.split("=")[-1]
+ log("asmcode stripping =", asmcode,
+ asmcode in asmidx, hasattr(self, "asmcode"))
if hasattr(self, "asmcode") and asmcode in asmidx:
res.append(self.asmcode.eq(asmidx[asmcode]))
for bit in single_bit_flags:
the constructor is called. all quite messy.
"""
- def __init__(self, width, dec, name=None, col_subset=None, row_subset=None):
+ def __init__(self, width, dec, name=None, col_subset=None,
+ row_subset=None, conditions=None):
+ if conditions is None:
+ # XXX conditions = {}
+ conditions = {'SVP64BREV': Const(0, 1),
+ 'SVP64FFT': Const(0, 1),
+ }
self.actually_does_something = False
self.pname = name
+ self.conditions = conditions
self.col_subset = col_subset
self.row_subsetfn = row_subset
if not isinstance(dec, list):
for d in dec:
if d.suffix is not None and d.suffix >= width:
d.suffix = None
+
self.width = width
+ # create some case statement condition patterns for matching
+ # a single condition. "1----" for the first condition,
+ # "-1----" for the 2nd etc.
+ # also create a matching ordered list of conditions, for the switch,
+ # which will Cat() them together
+ self.ccases = {}
+ self.ckeys = list(conditions.keys())
+ self.ckeys.sort()
+
+ def find_conditions(self, opcodes):
+ # look for conditions, create dictionary entries for them
+ # sorted by opcode
+ rows = OrderedDict() # start as a dictionary, get as list (after)
+ for row in opcodes:
+ condition = row['CONDITIONS']
+ opcode = row['opcode']
+ if condition:
+ # check it's expected
+ assert (condition in self.conditions or
+ (condition[0] == '~' and
+ condition[1:] in self.conditions)), \
+ "condition %s not in %s" % (condition, str(conditions))
+ if opcode not in rows:
+ rows[opcode] = {}
+ rows[opcode][condition] = row
+ else:
+ # check it's unique
+ assert opcode not in rows, \
+ "opcode %s already in rows for %s" % \
+ (opcode, self.pname)
+ rows[opcode] = row
+ # after checking for conditions, get just the values (ordered)
+ return list(rows.values())
+
def suffix_mask(self, d):
return ((1 << d.suffix) - 1)
def divide_opcodes(self, d):
divided = {}
mask = self.suffix_mask(d)
- print("mask", hex(mask))
+ #print("mask", hex(mask))
for row in d.opcodes:
opcode = row['opcode']
if d.opint and '-' not in opcode:
reset_less=True)
eq = []
case_does_something = False
- eq.append(opcode_switch.eq(
- self.opcode_in[d.bitsel[0]:d.bitsel[1]]))
+ look_for = self.opcode_in[d.bitsel[0]:d.bitsel[1]]
+ eq.append(opcode_switch.eq(look_for))
if d.suffix:
opcodes = self.divide_opcodes(d)
+ # TODO opcodes = self.find_conditions(opcodes)
opc_in = Signal(d.suffix, reset_less=True)
eq.append(opc_in.eq(opcode_switch[:d.suffix]))
# begin the dynamic Switch statement here
subdecoder = PowerDecoder(width=32, dec=sd,
name=mname,
col_subset=self.col_subset,
- row_subset=self.row_subsetfn)
+ row_subset=self.row_subsetfn,
+ conditions=self.conditions)
if not subdecoder.tree_analyse():
del subdecoder
continue
if seqs:
case_does_something = True
eq += seqs
- for row in d.opcodes:
- opcode = row['opcode']
+ opcodes = self.find_conditions(d.opcodes)
+ for row in opcodes:
+ # urrr this is an awful hack. if "conditions" are active
+ # get the FIRST item (will be the same opcode), and it
+ # had BETTER have the same unit and also pass other
+ # row subset conditions.
+ if 'opcode' not in row: # must be a "CONDITIONS" dict...
+ is_conditions = True
+ _row = row[list(row.keys())[0]]
+ else:
+ is_conditions = False
+ _row = row
+ opcode = _row['opcode']
if d.opint and '-' not in opcode:
opcode = int(opcode, 0)
- if not row['unit']:
+ if not _row['unit']:
continue
if self.row_subsetfn:
- if not self.row_subsetfn(opcode, row):
+ if not self.row_subsetfn(opcode, _row):
continue
# add in the dynamic Case statement here
- switch_case[opcode] = self.op._eq(row)
+ if is_conditions:
+ switch_case[opcode] = {}
+ for k, crow in row.items():
+ # log("ordered", k, crow)
+ switch_case[opcode][k] = self.op._eq(crow)
+ else:
+ switch_case[opcode] = self.op._eq(row)
self.actually_does_something = True
case_does_something = True
decs.append(cases)
if case_does_something:
eqs += eq
- print("submodule eqs", self.pname, eq)
+ #print("submodule eqs", self.pname, eq)
- print("submodules", self.pname, submodules)
+ #print("submodules", self.pname, submodules)
gc.collect()
return self.actually_does_something
def handle_subdecoders(self, switch_case, submodules, d):
eqs = []
- for dec in d.subdecoders:
- if isinstance(dec, list): # XXX HACK: take first pattern
- dec = dec[0]
- print("subdec", dec.pattern, self.pname)
- mname = get_pname("dec%d" % dec.pattern, self.pname)
- subdecoder = PowerDecoder(self.width, dec,
- name=mname,
- col_subset=self.col_subset,
- row_subset=self.row_subsetfn)
- if not subdecoder.tree_analyse(): # doesn't do anything
- del subdecoder
- continue # skip
- submodules[mname] = subdecoder
- eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
- switch_case[dec.pattern] = self.op.eq(subdecoder.op)
- self.actually_does_something = True
+ for dlist in d.subdecoders:
+ if not isinstance(dlist, list): # XXX HACK: take first pattern
+ dlist = [dlist]
+ for dec in dlist:
+ #print("subdec", dec.pattern, self.pname)
+ mname = get_pname("dec%d" % dec.pattern, self.pname)
+ if mname in submodules:
+ # sigh, HACK...
+ mname += "_1"
+ assert mname not in submodules
+ subdecoder = PowerDecoder(self.width, dec,
+ name=mname,
+ col_subset=self.col_subset,
+ row_subset=self.row_subsetfn,
+ conditions=self.conditions)
+ log("subdecoder", mname, subdecoder)
+ if not subdecoder.tree_analyse(): # doesn't do anything
+ log("analysed, DELETING", mname)
+ del subdecoder
+ continue # skip
+ submodules[mname] = subdecoder
+ eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
+ switch_case[dec.pattern] = self.op.eq(subdecoder.op)
+ self.actually_does_something = True
return eqs
def elaborate(self, platform):
- print("decoder elaborate", self.pname, self.submodules)
+ #print("decoder elaborate", self.pname, self.submodules)
m = Module()
comb = m.d.comb
with m.Switch(switch):
for key, eqs in cases.items():
with m.Case(key):
- comb += eqs
+ # "conditions" are a further switch statement
+ if isinstance(eqs, dict):
+ self.condition_switch(m, eqs)
+ else:
+ comb += eqs
return m
+ def condition_switch(self, m, cases):
+ """against the global list of "conditions", having matched against
+ bits of the opcode, we FINALLY now have to match against some
+ additional "conditions". this is because there can be **MULTIPLE**
+ entries for a given opcode match. here we discern them.
+ """
+ comb = m.d.comb
+ cswitch = []
+ ccases = []
+ for casekey, eqs in cases.items():
+ if casekey.startswith('~'):
+ with m.If(~self.conditions[casekey[1:]]):
+ comb += eqs
+ else:
+ with m.If(self.conditions[casekey]):
+ comb += eqs
+
def ports(self):
return [self.opcode_in] + self.op.ports()
(reverses byte order). See V3.0B p44 1.11.2
"""
- def __init__(self, width, dec, name=None, col_subset=None, row_subset=None):
- PowerDecoder.__init__(self, width, dec, name, col_subset, row_subset)
+ def __init__(self, width, dec, name=None, col_subset=None,
+ row_subset=None, conditions=None):
+ PowerDecoder.__init__(self, width, dec, name,
+ col_subset, row_subset, conditions)
self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in])
self.fields.create_specs()
self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
return m
def ports(self):
- return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
+ res = [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
+ for condition in self.conditions.values():
+ res.append(condition)
+ return res
+
+
+#############################################################
+# PRIMARY FUNCTION SPECIFYING ALTERNATIVE SVP64 POWER DECODER
+
+def create_pdecode_svp64_ldst(name=None, col_subset=None, row_subset=None,
+ include_fp=False):
+ """create_pdecode - creates a cascading hierarchical POWER ISA decoder
+
+ subsetting of the PowerOp decoding is possible by setting col_subset
+ """
+ log("create_pdecode_svp64_ldst", name, col_subset, row_subset, include_fp)
+
+ # some alteration to the CSV files is required for SV so we use
+ # a class to do it
+ isa = SVP64RM()
+ get_csv = isa.get_svp64_csv
+
+ # minor opcodes.
+ pminor = [
+ Subdecoder(pattern=58, opcodes=get_csv("svldst_minor_58.csv"),
+ opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
+ # nope - needs 4-in regs
+ # Subdecoder(pattern=62, opcodes=get_csv("svldst_minor_62.csv"),
+ # opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
+ ]
+
+ # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
+ if False and include_fp:
+ pminor.append(
+ Subdecoder(pattern=63, opcodes=get_csv("minor_63.csv"),
+ opint=False, bitsel=(1, 11), suffix=None,
+ subdecoders=[]),
+ )
+ pminor.append(
+ Subdecoder(pattern=59, opcodes=get_csv("minor_59.csv"),
+ opint=False, bitsel=(1, 11), suffix=None,
+ subdecoders=[]),
+ )
+
+ # top level: extra merged with major
+ dec = []
+ opcodes = get_csv("svldst_major.csv")
+ dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
+ bitsel=(26, 32), suffix=None, subdecoders=pminor))
+
+ return TopPowerDecoder(32, dec, name=name, col_subset=col_subset,
+ row_subset=row_subset)
####################################################
# PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
-def create_pdecode(name=None, col_subset=None, row_subset=None):
+def create_pdecode(name=None, col_subset=None, row_subset=None,
+ include_fp=False, conditions=None):
"""create_pdecode - creates a cascading hierarchical POWER ISA decoder
subsetting of the PowerOp decoding is possible by setting col_subset
"""
+ log("create_pdecode", name, col_subset, row_subset, include_fp)
# some alteration to the CSV files is required for SV so we use
# a class to do it
m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
opint=True, bitsel=(1, 11), suffix=None,
subdecoders=[]))
- m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
- opint=True, bitsel=(1, 6), suffix=None,
- subdecoders=[]))
+ # XXX problem with sub-decoders (can only handle one),
+ # sort this another time
+ # m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
+ # opint=True, bitsel=(1, 6), suffix=None,
+ # subdecoders=[]))
# minor opcodes.
pminor = [
opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
Subdecoder(pattern=22, opcodes=get_csv("minor_22.csv"),
opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
+ Subdecoder(pattern=5, opcodes=get_csv("minor_5.csv"),
+ opint=True, bitsel=(0, 11), suffix=None, subdecoders=[]),
]
+ # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
+ if include_fp:
+ pminor.append(
+ Subdecoder(pattern=63, opcodes=get_csv("minor_63.csv"),
+ opint=False, bitsel=(1, 11), suffix=None,
+ subdecoders=[]),
+ )
+ pminor.append(
+ Subdecoder(pattern=59, opcodes=get_csv("minor_59.csv"),
+ opint=False, bitsel=(1, 11), suffix=None,
+ subdecoders=[]),
+ )
+
# top level: extra merged with major
dec = []
opcodes = get_csv("major.csv")
bitsel=(0, 32), suffix=None, subdecoders=[]))
return TopPowerDecoder(32, dec, name=name, col_subset=col_subset,
- row_subset=row_subset)
+ row_subset=row_subset,
+ conditions=conditions)
+
+# test function from
+# https://github.com/apertus-open-source-cinema/naps/blob/9ebbc0/naps/soc/cli.py#L17
+
+
+def fragment_repr(original):
+ from textwrap import indent
+ attrs_str = "\n"
+ for attr in ['ports', 'drivers', 'statements', 'attrs',
+ 'generated', 'flatten']:
+ attrs_str += f"{attr}={repr(getattr(original, attr))},\n"
+
+ domains_str = "\n"
+ for name, domain in original.domains.items():
+ # TODO: this is not really sound because domains could be non local
+ domains_str += f"{name}: {domain.name}\n"
+ attrs_str += f"domains={{{indent(domains_str, ' ')}}},\n"
+
+ children_str = "\n"
+ for child, name in original.subfragments:
+ children_str += f"[{name}, {fragment_repr(child)}]\n"
+ attrs_str += f"children=[{indent(children_str, ' ')}],\n"
+
+ return f"Fragment({indent(attrs_str, ' ')})"
if __name__ == '__main__':
# row subset
def rowsubsetfn(opcode, row):
- print("row_subset", opcode, row)
- return row['unit'] == 'ALU'
+ log("row_subset", opcode, row)
+ return row['unit'] in ['LDST', 'FPU']
+ conditions = {'SVP64BREV': Signal(name="svp64brev", reset_less=True),
+ 'SVP64FFT': Signal(name="svp64fft", reset_less=True),
+ }
pdecode = create_pdecode(name="rowsub",
- col_subset={'function_unit', 'in1_sel'},
- row_subset=rowsubsetfn)
+ col_subset={'opcode', 'function_unit',
+ 'asmcode',
+ 'in2_sel', 'in3_sel'},
+ row_subset=rowsubsetfn,
+ include_fp=True,
+ conditions=conditions)
vl = rtlil.convert(pdecode, ports=pdecode.ports())
with open("row_subset_decoder.il", "w") as f:
f.write(vl)
+ vl = verilog.convert(pdecode, ports=pdecode.ports())
+ with open("row_subset_decoder.v", "w") as f:
+ f.write(vl)
+
# col subset
- pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'})
+ pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'},
+ conditions=conditions)
vl = rtlil.convert(pdecode, ports=pdecode.ports())
with open("col_subset_decoder.il", "w") as f:
f.write(vl)
- # full decoder
+ from nmigen.hdl.ir import Fragment
+ elaborated = Fragment.get(pdecode, platform=None)
+ elaborated_repr = fragment_repr(elaborated)
+ print(elaborated_repr)
- pdecode = create_pdecode()
+ exit(0)
+
+ exit(0)
+
+ # full decoder
+ pdecode = create_pdecode(include_fp=True)
vl = rtlil.convert(pdecode, ports=pdecode.ports())
with open("decoder.il", "w") as f:
f.write(vl)
+
+ # full SVP64 decoder
+ pdecode = create_pdecode_svp64_ldst(include_fp=True)
+ vl = rtlil.convert(pdecode, ports=pdecode.ports())
+ with open("decoder_svp64.il", "w") as f:
+ f.write(vl)