This module uses CSV tables in a hierarchical/peer cascading fashion,
to create a multi-level instruction decoder by recognising appropriate
-patterns. The output is a flattened (1-level) series of fields suitable
-for a simple RISC engine.
+patterns. The output is a wide, flattened (1-level) series of bitfields,
+suitable for a simple RISC engine.
This is based on Anton Blanchard's excellent microwatt work:
https://github.com/antonblanchard/microwatt/blob/master/decode1.vhdl
(reading the CSV files, constructing the hierarchy), creating the HDL
AST with for-loops generating switch-case statements.
-PowerDecoder takes a *list* of CSV files with an associated bit-range
-that it is requested to match against the "opcode" row of the CSV file.
-This pattern can be either an integer, a binary number, *or* a wildcard
-nmigen Case pattern of the form "001--1-100".
+Where "normal" HDL would do this, in laborious excruciating detail:
-Subdecoders are *additional* cases with further decoding. The "pattern"
-argument is specified as one of the Case statements (a peer of the opcode
-row in the CSV file), and thus further fields of the opcode may be decoded
-giving increasing levels of detail.
+ switch (opcode & major_mask_bits):
+ case opcode_2: decode_opcode_2()
+ case opcode_19:
+ switch (opcode & minor_19_mask_bits)
+ case minor_opcode_19_operation_X:
+ case minor_opcode_19_operation_y:
+
+we take *full* advantage of the decoupling between python and the
+nmigen AST data structure, to do this:
+
+ with m.Switch(opcode & self.mask):
+ for case_bitmask in subcases:
+ with m.If(opcode & case_bitmask): {do_something}
+
+this includes specifying the information sufficient to perform subdecoding.
+
+create_pdecode()
+
+ the full hierarchical tree for decoding POWER9 is specified here
+
+PowerDecoder
+
+ takes a *list* of CSV files with an associated bit-range that it
+ is requested to match against the "opcode" row of the CSV file.
+ This pattern can be either an integer, a binary number, *or* a
+ wildcard nmigen Case pattern of the form "001--1-100".
+
+Subdecoders
+
+ these are *additional* cases with further decoding. The "pattern"
+ argument is specified as one of the Case statements (a peer of the
+ opcode row in the CSV file), and thus further fields of the opcode
+ may be decoded giving increasing levels of detail.
Top Level:
"""
-from nmigen import Module, Elaboratable, Signal
+from collections import namedtuple
+from nmigen import Module, Elaboratable, Signal, Cat, Mux
from nmigen.cli import rtlil
-from power_enums import (Function, Form, InternalOp, In1Sel, In2Sel, In3Sel,
- OutSel, RC, LdstLen, CryIn, get_csv, single_bit_flags,
+from soc.decoder.power_enums import (Function, Form, InternalOp,
+ In1Sel, In2Sel, In3Sel, OutSel, RC, LdstLen,
+ CryIn, get_csv, single_bit_flags,
get_signal_name, default_values)
-from collections import namedtuple
-from power_fields import DecodeFields
-from power_fieldsn import SigDecode, SignalBitRange
+from soc.decoder.power_fields import DecodeFields
+from soc.decoder.power_fieldsn import SigDecode, SignalBitRange
+
-Subdecoder = namedtuple("Subdecoder", ["pattern", "opcodes", "opint",
- "bitsel", "suffix", "subdecoders"])
+# key data structure in which the POWER decoder is specified,
+# in a hierarchical fashion
+Subdecoder = namedtuple("Subdecoder",
+ ["pattern", # the major pattern to search for (e.g. major opcode)
+ "opcodes", # a dictionary of minor patterns to find
+ "opint", # true => the pattern must not be in "10----11" format
+ "bitsel", # the bits (as a range) against which "pattern" matches
+ "suffix", # shift the opcode down before decoding
+ "subdecoders" # list of further subdecoders for *additional* matches,
+ # *ONLY* after "pattern" has *ALSO* been matched against.
+ ])
class PowerOp:
"""PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
+
+ this is an internal data structure, set up by reading CSV files
+ (which uses _eq to initialise each instance, not eq)
+
+ the "public" API (as far as actual usage as a useful decoder is concerned)
+ is Decode2ToExecute1Type
"""
def __init__(self):
def _eq(self, row=None):
if row is None:
row = default_values
+ # TODO: this conversion process from a dict to an object
+ # should really be done using e.g. namedtuple and then
+ # call eq not _eq
res = [self.function_unit.eq(Function[row['unit']]),
self.form.eq(Form[row['form']]),
self.internal_op.eq(InternalOp[row['internal op']]),
comb = m.d.comb
# note: default opcode is "illegal" as this is a combinatorial block
+ # this only works because OP_ILLEGAL=0 and the default (unset) is 0
# go through the list of CSV decoders first
for d in self.dec:
opcodes = self.divide_opcodes(d)
opc_in = Signal(d.suffix, reset_less=True)
comb += opc_in.eq(opcode_switch[:d.suffix])
+ # begin the dynamic Switch statement here
with m.Switch(opc_in):
for key, row in opcodes.items():
bitsel = (d.suffix+d.bitsel[0], d.bitsel[1])
subdecoder = PowerDecoder(width=32, dec=sd)
setattr(m.submodules, "dec_sub%d" % key, subdecoder)
comb += subdecoder.opcode_in.eq(self.opcode_in)
+ # add in the dynamic Case statement here
with m.Case(key):
comb += self.op.eq(subdecoder.op)
else:
opcode = int(opcode, 0)
if not row['unit']:
continue
+ # add in the dynamic Case statement here
with m.Case(opcode):
comb += self.op._eq(row)
return m
return [self.opcode_in] + self.op.ports()
-class TopPowerDecoder(PowerDecoder, DecodeFields):
+class TopPowerDecoder(PowerDecoder):
+ """TopPowerDecoder
+
+ top-level hierarchical decoder for POWER ISA
+ bigendian dynamically switches between big and little endian decoding
+ (reverses byte order). See V3.0B p44 1.11.2
+ """
def __init__(self, width, dec):
PowerDecoder.__init__(self, width, dec)
- DecodeFields.__init__(self, SignalBitRange, [self.opcode_in])
- self.create_specs()
+ self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in])
+ self.fields.create_specs()
+ self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
+ self.bigendian = Signal(reset_less=True)
+
+ for name, value in self.fields.common_fields.items():
+ sig = Signal(value[0:-1].shape(), reset_less=True, name=name)
+ setattr(self, name, sig)
+
+ # create signals for all field forms
+ self.form_names = forms = self.fields.instrs.keys()
+ self.sigforms = {}
+ for form in forms:
+ fields = self.fields.instrs[form]
+ fk = fields.keys()
+ Fields = namedtuple("Fields", fk)
+ sf = {}
+ for k, value in fields.items():
+ name = "%s_%s" % (form, k)
+ sig = Signal(value[0:-1].shape(), reset_less=True, name=name)
+ sf[k] = sig
+ instr = Fields(**sf)
+ setattr(self, "Form%s" % form, instr)
+ self.sigforms[form] = instr
+
+ def elaborate(self, platform):
+ m = PowerDecoder.elaborate(self, platform)
+ comb = m.d.comb
+ raw_be = self.raw_opcode_in
+ l = []
+ for i in range(0, self.width, 8):
+ l.append(raw_be[i:i+8])
+ l.reverse()
+ raw_le = Cat(*l)
+ comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
+
+ # add all signal from commonly-used fields
+ for name, value in self.fields.common_fields.items():
+ sig = getattr(self, name)
+ comb += sig.eq(value[0:-1])
+
+ # link signals for all field forms
+ forms = self.form_names
+ for form in forms:
+ sf = self.sigforms[form]
+ fields = self.fields.instrs[form]
+ for k, value in fields.items():
+ sig = getattr(sf, k)
+ comb += sig.eq(value[0:-1])
+ return m
+
+ def ports(self):
+ return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
+
+
+####################################################
+# PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
def create_pdecode():
+ """create_pdecode - creates a cascading hierarchical POWER ISA decoder
+ """
# minor 19 has extra patterns
m19 = []
pminor = [
m19,
Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"),
- opint=True, bitsel=(1, 6), suffix=None, subdecoders=[]),
+ opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
Subdecoder(pattern=31, opcodes=get_csv("minor_31.csv"),
opint=True, bitsel=(1, 11), suffix=0b00101, subdecoders=[]),
Subdecoder(pattern=58, opcodes=get_csv("minor_58.csv"),