From 111257b21f9ae4c26b82699ba950599695eee44e Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Fri, 23 Apr 2021 15:26:03 +0100 Subject: [PATCH] move over to openpower-isa repo --- src/soc/decoder/decode2execute1.py | 133 +------ src/soc/decoder/power_decoder.py | 610 +---------------------------- src/soc/decoder/power_fields.py | 258 +----------- src/soc/decoder/power_fieldsn.py | 82 +--- src/soc/decoder/selectable_int.py | 563 +------------------------- 5 files changed, 20 insertions(+), 1626 deletions(-) diff --git a/src/soc/decoder/decode2execute1.py b/src/soc/decoder/decode2execute1.py index 6333dcdf..9f670097 100644 --- a/src/soc/decoder/decode2execute1.py +++ b/src/soc/decoder/decode2execute1.py @@ -1,130 +1,5 @@ -"""Decode2ToExecute1Type +# moved to openpower-isa +# https://git.libre-soc.org/?p=openpower-isa.git;a=summary +# wildcard imports here ONLY to support migration -based on Anton Blanchard microwatt decode2.vhdl - -""" -from nmigen import Signal, Record -from nmutil.iocontrol import RecordObject -from soc.decoder.power_enums import (MicrOp, CryIn, Function, - SPRfull, SPRreduced, LDSTMode) -from soc.consts import TT -from soc.experiment.mem_types import LDSTException - - -class Data(Record): - - def __init__(self, width, name): - name_ok = "%s_ok" % name - layout = ((name, width), (name_ok, 1)) - Record.__init__(self, layout) - self.data = getattr(self, name) # convenience - self.ok = getattr(self, name_ok) # convenience - self.data.reset_less = True # grrr - self.reset_less = True # grrr - - def ports(self): - return [self.data, self.ok] - - -class IssuerDecode2ToOperand(RecordObject): - """IssuerDecode2ToOperand - - contains the subset of fields needed for Issuer to decode the instruction - and get register rdflags signals set up. it also doubles up as the - "Trap" temporary store, because part of the Decoder's job is to - identify whether a trap / interrupt / exception should occur. - """ - - def __init__(self, name=None): - - RecordObject.__init__(self, name=name) - - # current "state" (TODO: this in its own Record) - self.msr = Signal(64, reset_less=True) - self.cia = Signal(64, reset_less=True) - - # instruction, type and decoded information - self.insn = Signal(32, reset_less=True) # original instruction - self.insn_type = Signal(MicrOp, reset_less=True) - self.fn_unit = Signal(Function, reset_less=True) - self.lk = Signal(reset_less=True) - self.rc = Data(1, "rc") - self.oe = Data(1, "oe") - self.input_carry = Signal(CryIn, reset_less=True) - self.traptype = Signal(TT.size, reset_less=True) # trap main_stage.py - self.ldst_exc = LDSTException("exc") - self.trapaddr = Signal(13, reset_less=True) - self.read_cr_whole = Data(8, "cr_rd") # CR full read mask - self.write_cr_whole = Data(8, "cr_wr") # CR full write mask - self.is_32bit = Signal(reset_less=True) - - -class Decode2ToOperand(IssuerDecode2ToOperand): - - def __init__(self, name=None): - - IssuerDecode2ToOperand.__init__(self, name=name) - - # instruction, type and decoded information - self.imm_data = Data(64, name="imm") - self.invert_in = Signal(reset_less=True) - self.zero_a = Signal(reset_less=True) - self.output_carry = Signal(reset_less=True) - self.input_cr = Signal(reset_less=True) # instr. has a CR as input - self.output_cr = Signal(reset_less=True) # instr. has a CR as output - self.invert_out = Signal(reset_less=True) - self.is_32bit = Signal(reset_less=True) - self.is_signed = Signal(reset_less=True) - self.data_len = Signal(4, reset_less=True) # bytes - self.byte_reverse = Signal(reset_less=True) - self.sign_extend = Signal(reset_less=True)# do we need this? - self.ldst_mode = Signal(LDSTMode, reset_less=True) # LD/ST mode - self.write_cr0 = Signal(reset_less=True) - - -class Decode2ToExecute1Type(RecordObject): - - def __init__(self, name=None, asmcode=True, opkls=None, do=None, - regreduce_en=False): - - if regreduce_en: - SPR = SPRreduced - else: - SPR = SPRfull - - if do is None and opkls is None: - opkls = Decode2ToOperand - - RecordObject.__init__(self, name=name) - - if asmcode: - self.asmcode = Signal(8, reset_less=True) # only for simulator - self.write_reg = Data(7, name="rego") - self.write_ea = Data(7, name="ea") # for LD/ST in update mode - self.read_reg1 = Data(7, name="reg1") - self.read_reg2 = Data(7, name="reg2") - self.read_reg3 = Data(7, name="reg3") - self.write_spr = Data(SPR, name="spro") - self.read_spr1 = Data(SPR, name="spr1") - #self.read_spr2 = Data(SPR, name="spr2") # only one needed - - self.xer_in = Signal(3, reset_less=True) # xer might be read - self.xer_out = Signal(reset_less=True) # xer might be written - - self.read_fast1 = Data(3, name="fast1") - self.read_fast2 = Data(3, name="fast2") - self.write_fast1 = Data(3, name="fasto1") - self.write_fast2 = Data(3, name="fasto2") - - self.read_cr1 = Data(7, name="cr_in1") - self.read_cr2 = Data(7, name="cr_in2") - self.read_cr3 = Data(7, name="cr_in2") - self.write_cr = Data(7, name="cr_out") - - # decode operand data - print ("decode2execute init", name, opkls, do) - #assert name is not None, str(opkls) - if do is not None: - self.do = do - else: - self.do = opkls(name) +from openpower.decoder.decode2execute1 import * diff --git a/src/soc/decoder/power_decoder.py b/src/soc/decoder/power_decoder.py index eb6df3cf..1ca6af06 100644 --- a/src/soc/decoder/power_decoder.py +++ b/src/soc/decoder/power_decoder.py @@ -1,607 +1,5 @@ -"""Cascading Power ISA Decoder +# moved to openpower-isa +# https://git.libre-soc.org/?p=openpower-isa.git;a=summary +# wildcard imports here ONLY to support migration -License: LGPLv3+ - -# Copyright (C) 2020 Luke Kenneth Casson Leighton -# Copyright (C) 2020 Michael Nolan - -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 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 - -The basic principle is that the python code does the heavy lifting -(reading the CSV files, constructing the hierarchy), creating the HDL -AST with for-loops generating switch-case statements. - -Where "normal" HDL would do this, in laborious excruciating 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 - subsetting is possible by specifying col_subset (row_subset TODO) - -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: - - [ (extra.csv: bit-fields entire 32-bit range - opcode -> matches - 000000---------------01000000000 -> ILLEGAL instruction - 01100000000000000000000000000000 -> SIM_CONFIG instruction - ................................ -> - ), - (major.csv: first 6 bits ONLY - opcode -> matches - 001100 -> ALU,OP_ADD (add) - 001101 -> ALU,OP_ADD (another type of add) - ...... -> ... - ...... -> ... - subdecoders: - 001011 this must match *MAJOR*.CSV - [ (minor_19.csv: bits 21 through 30 inclusive: - opcode -> matches - 0b0000000000 -> ALU,OP_MCRF - ............ -> .... - ), - (minor_19_00000.csv: bits 21 through 25 inclusive: - opcode -> matches - 0b00010 -> ALU,add_pcis - ) - ] - ), - ] - - -""" - -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 - -# key data structure in which the POWER decoder is specified, -# in a hierarchical fashion -Subdecoder = namedtuple( # fix autoformatter - "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 - # the bits (as a range) against which "pattern" matches - "bitsel", - "suffix", # shift the opcode down before decoding - "subdecoders" # list of further subdecoders for *additional* matches, - # *ONLY* after "pattern" has *ALSO* been matched against. - ]) - -power_op_types = {'function_unit': Function, - 'internal_op': MicrOp, - 'form': Form, - 'asmcode': 8, - 'SV_Etype': SVEtype, - 'SV_Ptype': SVPtype, - 'in1_sel': In1Sel, - 'in2_sel': In2Sel, - 'in3_sel': In3Sel, - 'out_sel': OutSel, - 'cr_in': CRInSel, - 'cr_out': CROutSel, - 'sv_in1': SVEXTRA, - 'sv_in2': SVEXTRA, - 'sv_in3': SVEXTRA, - 'sv_out': SVEXTRA, - 'sv_out2': SVEXTRA, - 'sv_cr_in': SVEXTRA, - 'sv_cr_out': SVEXTRA, - 'ldst_len': LdstLen, - 'upd': LDSTMode, - 'rc_sel': RC, - 'cry_in': CryIn - } - -power_op_csvmap = {'function_unit': 'unit', - 'form': 'form', - 'internal_op': 'internal op', - 'in1_sel': 'in1', - 'in2_sel': 'in2', - 'in3_sel': 'in3', - 'out_sel': 'out', - 'sv_in1': 'sv_in1', - 'sv_in2': 'sv_in2', - 'sv_in3': 'sv_in3', - 'sv_out': 'sv_out', - 'sv_out2': 'sv_out2', - 'sv_cr_in': 'sv_cr_in', - 'sv_cr_out': 'sv_cr_out', - 'SV_Etype': 'SV_Etype', - 'SV_Ptype': 'SV_Ptype', - 'cr_in': 'CR in', - 'cr_out': 'CR out', - 'ldst_len': 'ldst len', - 'upd': 'upd', - 'rc_sel': 'rc', - 'cry_in': 'cry in', - } - - -def get_pname(field, pname): - if pname is None: - return field - return "%s_%s" % (pname, field) - - -class PowerOp: - """PowerOp - a dynamic class that stores (subsets of) CSV rows of data - about a PowerISA instruction. this is a "micro-code" expanded format - which generates an awful lot of wires, hence the subsetting - """ - - def __init__(self, incl_asm=True, name=None, subset=None): - self.subset = subset - debug_report = set() - fields = set() - for field, ptype in power_op_types.items(): - fields.add(field) - if subset and field not in subset: - continue - fname = get_pname(field, name) - setattr(self, field, Signal(ptype, reset_less=True, name=fname)) - debug_report.add(field) - for bit in single_bit_flags: - field = get_signal_name(bit) - fields.add(field) - if subset and field not in subset: - continue - 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) - - 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 - if False: # debugging - if row['CR in'] == '1': - import pdb - pdb.set_trace() - print(row) - if row['CR out'] == '0': - import pdb - pdb.set_trace() - print(row) - print(row) - ldst_mode = row['upd'] - if ldst_mode.isdigit(): - row['upd'] = int(ldst_mode) - res = [] - for field, ptype in power_op_types.items(): - if not hasattr(self, field): - continue - if field not in power_op_csvmap: - continue - csvname = power_op_csvmap[field] - print(field, ptype, csvname, row) - val = row[csvname] - if csvname == 'upd' and isinstance(val, int): # LDSTMode different - val = ptype(val) - else: - val = ptype[val] - res.append(getattr(self, field).eq(val)) - if False: - print(row.keys()) - asmcode = row['comment'] - if hasattr(self, "asmcode") and asmcode in asmidx: - res.append(self.asmcode.eq(asmidx[asmcode])) - for bit in single_bit_flags: - field = get_signal_name(bit) - if not hasattr(self, field): - continue - sig = getattr(self, field) - res.append(sig.eq(int(row.get(bit, 0)))) - return res - - def _get_eq(self, res, field, otherop): - copyfrom = getattr(otherop, field, None) - copyto = getattr(self, field, None) - if copyfrom is not None and copyto is not None: - res.append(copyto.eq(copyfrom)) - - def eq(self, otherop): - res = [] - for field in power_op_types.keys(): - self._get_eq(res, field, otherop) - for bit in single_bit_flags: - self._get_eq(res, get_signal_name(bit), otherop) - return res - - def ports(self): - res = [] - for field in power_op_types.keys(): - if hasattr(self, field): - res.append(getattr(self, field)) - if hasattr(self, "asmcode"): - res.append(self.asmcode) - for field in single_bit_flags: - field = get_signal_name(field) - if hasattr(self, field): - res.append(getattr(self, field)) - return res - - -class PowerDecoder(Elaboratable): - """PowerDecoder - decodes an incoming opcode into the type of operation - - this is a recursive algorithm, creating Switch statements that can - have further match-and-decode on other parts of the opcode field before - finally landing at a "this CSV entry details gets returned" thing. - - the complicating factor is the row and col subsetting. column subsetting - dynamically chooses only the CSV columns requested, whilst row subsetting - allows a function to be called on the row to determine if the Case - statement is to be generated for that row. this not only generates - completely different Decoders, it also means that some sub-decoders - will turn up blank (empty switch statements). if that happens we do - not want the parent to include a Mux for an entirely blank switch statement - so we have to store the switch/case statements in a tree, and - post-analyse it. - - the reason for the tree is because elaborate can only be called *after* - the constructor is called. all quite messy. - """ - - def __init__(self, width, dec, name=None, col_subset=None, row_subset=None): - self.actually_does_something = False - self.pname = name - self.col_subset = col_subset - self.row_subsetfn = row_subset - if not isinstance(dec, list): - dec = [dec] - self.dec = dec - self.opcode_in = Signal(width, reset_less=True) - - self.op = PowerOp(name=name, subset=col_subset) - for d in dec: - if d.suffix is not None and d.suffix >= width: - d.suffix = None - self.width = width - - 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)) - for row in d.opcodes: - opcode = row['opcode'] - if d.opint and '-' not in opcode: - opcode = int(opcode, 0) - key = opcode & mask - opcode = opcode >> d.suffix - if key not in divided: - divided[key] = [] - r = row.copy() - r['opcode'] = opcode - divided[key].append(r) - return divided - - def tree_analyse(self): - self.decs = decs = [] - self.submodules = submodules = {} - self.eqs = eqs = [] - - # go through the list of CSV decoders first - for d in self.dec: - cases = [] - opcode_switch = Signal(d.bitsel[1] - d.bitsel[0], - reset_less=True) - eq = [] - case_does_something = False - eq.append(opcode_switch.eq( - self.opcode_in[d.bitsel[0]:d.bitsel[1]])) - if d.suffix: - opcodes = self.divide_opcodes(d) - opc_in = Signal(d.suffix, reset_less=True) - eq.append(opc_in.eq(opcode_switch[:d.suffix])) - # begin the dynamic Switch statement here - switch_case = {} - cases.append([opc_in, switch_case]) - sub_eqs = [] - for key, row in opcodes.items(): - bitsel = (d.suffix+d.bitsel[0], d.bitsel[1]) - sd = Subdecoder(pattern=None, opcodes=row, - bitsel=bitsel, suffix=None, - opint=False, subdecoders=[]) - mname = get_pname("dec_sub%d" % key, self.pname) - subdecoder = PowerDecoder(width=32, dec=sd, - name=mname, - col_subset=self.col_subset, - row_subset=self.row_subsetfn) - if not subdecoder.tree_analyse(): - del subdecoder - continue - submodules[mname] = subdecoder - sub_eqs.append(subdecoder.opcode_in.eq(self.opcode_in)) - # add in the dynamic Case statement here - switch_case[key] = self.op.eq(subdecoder.op) - self.actually_does_something = True - case_does_something = True - if case_does_something: - eq += sub_eqs - else: - # TODO: arguments, here (all of them) need to be a list. - # a for-loop around the *list* of decoder args. - switch_case = {} - cases.append([opcode_switch, switch_case]) - seqs = self.handle_subdecoders(switch_case, submodules, d) - if seqs: - case_does_something = True - eq += seqs - for row in d.opcodes: - opcode = row['opcode'] - if d.opint and '-' not in opcode: - opcode = int(opcode, 0) - if not row['unit']: - continue - if self.row_subsetfn: - if not self.row_subsetfn(opcode, row): - continue - # add in the dynamic Case statement here - switch_case[opcode] = self.op._eq(row) - self.actually_does_something = True - case_does_something = True - - if cases: - decs.append(cases) - if case_does_something: - eqs += eq - print("submodule eqs", self.pname, eq) - - 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 - - return eqs - - def elaborate(self, platform): - print("decoder elaborate", self.pname, self.submodules) - m = Module() - comb = m.d.comb - - comb += self.eqs - - for mname, subdecoder in self.submodules.items(): - setattr(m.submodules, mname, subdecoder) - - for switch_case in self.decs: - for (switch, cases) in switch_case: - with m.Switch(switch): - for key, eqs in cases.items(): - with m.Case(key): - comb += eqs - return m - - def ports(self): - return [self.opcode_in] + self.op.ports() - - -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, name=None, col_subset=None, row_subset=None): - PowerDecoder.__init__(self, width, dec, name, col_subset, row_subset) - 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 fname, value in self.fields.common_fields.items(): - signame = get_pname(fname, name) - sig = Signal(value[0:-1].shape(), reset_less=True, name=signame) - setattr(self, fname, sig) - - # create signals for all field forms - forms = self.form_names - 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(): - fname = "%s_%s" % (form, k) - sig = Signal(value[0:-1].shape(), reset_less=True, name=fname) - sf[k] = sig - instr = Fields(**sf) - setattr(self, "Form%s" % form, instr) - self.sigforms[form] = instr - - self.tree_analyse() - - @property - def form_names(self): - return self.fields.instrs.keys() - - def elaborate(self, platform): - m = PowerDecoder.elaborate(self, platform) - comb = m.d.comb - # sigh duplicated in SVP64PowerDecoder - # raw opcode in assumed to be in LE order: byte-reverse it to get BE - raw_le = self.raw_opcode_in - l = [] - for i in range(0, self.width, 8): - l.append(raw_le[i:i+8]) - l.reverse() - raw_be = Cat(*l) - comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le)) - - # add all signal from commonly-used fields - for fname, value in self.fields.common_fields.items(): - sig = getattr(self, fname) - 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(name=None, col_subset=None, row_subset=None): - """create_pdecode - creates a cascading hierarchical POWER ISA decoder - - subsetting of the PowerOp decoding is possible by setting col_subset - """ - - # 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 19 has extra patterns - m19 = [] - 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=[])) - - # minor opcodes. - pminor = [ - m19, - Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"), - 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"), - opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]), - Subdecoder(pattern=62, opcodes=get_csv("minor_62.csv"), - 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=[]), - ] - - # top level: extra merged with major - dec = [] - opcodes = get_csv("major.csv") - dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes, - bitsel=(26, 32), suffix=None, subdecoders=pminor)) - opcodes = get_csv("extra.csv") - dec.append(Subdecoder(pattern=None, opint=False, opcodes=opcodes, - bitsel=(0, 32), suffix=None, subdecoders=[])) - - return TopPowerDecoder(32, dec, name=name, col_subset=col_subset, - row_subset=row_subset) - - -if __name__ == '__main__': - - if True: - # row subset - - def rowsubsetfn(opcode, row): - print("row_subset", opcode, row) - return row['unit'] == 'ALU' - - pdecode = create_pdecode(name="rowsub", - col_subset={'function_unit', 'in1_sel'}, - row_subset=rowsubsetfn) - vl = rtlil.convert(pdecode, ports=pdecode.ports()) - with open("row_subset_decoder.il", "w") as f: - f.write(vl) - - # col subset - - pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'}) - vl = rtlil.convert(pdecode, ports=pdecode.ports()) - with open("col_subset_decoder.il", "w") as f: - f.write(vl) - - # full decoder - - pdecode = create_pdecode() - vl = rtlil.convert(pdecode, ports=pdecode.ports()) - with open("decoder.il", "w") as f: - f.write(vl) +from openpower.decoder.power_decoder import * diff --git a/src/soc/decoder/power_fields.py b/src/soc/decoder/power_fields.py index d0fe85cb..6a0f70c7 100644 --- a/src/soc/decoder/power_fields.py +++ b/src/soc/decoder/power_fields.py @@ -1,255 +1,5 @@ -from collections import OrderedDict, namedtuple -from soc.decoder.power_enums import find_wiki_file +# moved to openpower-isa +# https://git.libre-soc.org/?p=openpower-isa.git;a=summary +# wildcard imports here ONLY to support migration - -class BitRange(OrderedDict): - """BitRange: remaps from straight indices (0,1,2..) to bit numbers - """ - - def __getitem__(self, subscript): - if isinstance(subscript, slice): - return list(self.values())[subscript] - else: - return OrderedDict.__getitem__(self, subscript) - - -def decode_instructions(form): - res = {} - accum = [] - for l in form: - if l.strip().startswith("Formats"): - l = l.strip().split(":")[-1] - l = l.replace(" ", "") - l = l.split(",") - for fmt in l: - if fmt not in res: - res[fmt] = [accum[0]] - else: - res[fmt].append(accum[0]) - accum = [] - else: - accum.append(l.strip()) - return res - - -def decode_form_header(hdr): - res = {} - count = 0 - hdr = hdr.strip() - for f in hdr.split("|"): - if not f: - continue - if f[0].isdigit(): - idx = int(f.strip().split(' ')[0]) - res[count] = idx - count += len(f) + 1 - return res - - -def find_unique(d, key): - if key not in d: - return key - idx = 1 - while "%s_%d" % (key, idx) in d: - idx += 1 - return "%s_%d" % (key, idx) - - -def decode_line(header, line): - line = line.strip() - res = {} - count = 0 - prev_fieldname = None - for f in line.split("|"): - if not f: - continue - end = count + len(f) + 1 - fieldname = f.strip() - if not fieldname or fieldname.startswith('/'): - if prev_fieldname is not None: - res[prev_fieldname] = (res[prev_fieldname], header[count]) - prev_fieldname = None - count = end - continue - bitstart = header[count] - if prev_fieldname is not None: - res[prev_fieldname] = (res[prev_fieldname], bitstart) - res[fieldname] = bitstart - count = end - prev_fieldname = fieldname - res[prev_fieldname] = (bitstart, 32) - return res - - -def decode_form(form): - header = decode_form_header(form[0]) - res = [] - for line in form[1:]: - dec = decode_line(header, line) - if dec: - res.append(dec) - fields = {} - falternate = {} - for l in res: - for k, (start, end) in l.items(): - if k in fields: - if (start, end) == fields[k]: - continue # already in and matching for this Form - if k in falternate: - alternate = "%s_%d" % (k, falternate[k]) - if (start, end) == fields[alternate]: - continue - falternate[k] = fidx = falternate.get(k, 0) + 1 - fields["%s_%d" % (k, fidx)] = (start, end) - else: - fields[k] = (start, end) - return fields - - -class DecodeFields: - - def __init__(self, bitkls=BitRange, bitargs=(), fname=None, - name_on_wiki=None): - self.bitkls = bitkls - self.bitargs = bitargs - if fname is None: - assert name_on_wiki is None - fname = "fields.txt" - name_on_wiki = "fields.text" - self.fname = find_wiki_file(name_on_wiki) - - @property - def form_names(self): - return self.instrs.keys() - - def create_specs(self): - self.forms, self.instrs = self.decode_fields() - forms = self.form_names - #print ("specs", self.forms, forms) - for form in forms: - fields = self.instrs[form] - fk = fields.keys() - Fields = namedtuple("Fields", fk) - instr = Fields(**fields) - setattr(self, "Form%s" % form, instr) - # now add in some commonly-used fields (should be done automatically) - # note that these should only be ones which are the same on all Forms - # note: these are from microwatt insn_helpers.vhdl - self.common_fields = { - "PO": self.Formall.PO, - "RS": self.FormX.RS, - "RT": self.FormX.RT, - "RA": self.FormX.RA, - "RB": self.FormX.RB, - "SI": self.FormD.SI, - "UI": self.FormD.UI, - "L": self.FormD.L, - "SH32": self.FormM.SH, - "sh": self.FormMD.sh, - "MB32": self.FormM.MB, - "ME32": self.FormM.ME, - "LI": self.FormI.LI, - "LK": self.FormI.LK, - "AA": self.FormB.AA, - "Rc": self.FormX.Rc, - "OE": self.FormXO.OE, - "BD": self.FormB.BD, - "BF": self.FormX.BF, - "CR": self.FormXL.XO, - "BB": self.FormXL.BB, - "BA": self.FormXL.BA, - "BT": self.FormXL.BT, - "FXM": self.FormXFX.FXM, - "BO": self.FormXL.BO, - "BI": self.FormXL.BI, - "BH": self.FormXL.BH, - "D": self.FormD.D, - "DS": self.FormDS.DS, - "TO": self.FormX.TO, - "BC": self.FormA.BC, - "SH": self.FormX.SH, - "ME": self.FormM.ME, - "MB": self.FormM.MB, - "SPR": self.FormXFX.SPR} - for k, v in self.common_fields.items(): - setattr(self, k, v) - - def decode_fields(self): - with open(self.fname) as f: - txt = f.readlines() - #print ("decode", txt) - forms = {} - reading_data = False - for l in txt: - l = l.strip() - if len(l) == 0: - continue - if reading_data: - if l[0] == '#': - reading_data = False - else: - forms[heading].append(l) - if not reading_data: - assert l[0] == '#' - heading = l[1:].strip() - # if heading.startswith('1.6.28'): # skip instr fields for now - # break - heading = heading.split(' ')[-1] - reading_data = True - forms[heading] = [] - - res = {} - inst = {} - - for hdr, form in forms.items(): - if heading == 'Fields': - i = decode_instructions(form) - for form, field in i.items(): - inst[form] = self.decode_instruction_fields(field) - # else: - # res[hdr] = decode_form(form) - return res, inst - - def decode_instruction_fields(self, fields): - res = {} - for field in fields: - f, spec = field.strip().split(" ") - ss = spec[1:-1].split(",") - fs = f.split(",") - if len(fs) > 1: - individualfields = [] - for f0, s0 in zip(fs, ss): - txt = "%s (%s)" % (f0, s0) - individualfields.append(txt) - if len(fs) > 1: - res.update(self.decode_instruction_fields( - individualfields)) - d = self.bitkls(*self.bitargs) - idx = 0 - for s in ss: - s = s.split(':') - if len(s) == 1: - d[idx] = int(s[0]) - idx += 1 - else: - start = int(s[0]) - end = int(s[1]) - while start <= end: - d[idx] = start - idx += 1 - start += 1 - f = f.replace(",", "_") - unique = find_unique(res, f) - res[unique] = d - - return res - - -if __name__ == '__main__': - dec = DecodeFields() - dec.create_specs() - forms, instrs = dec.forms, dec.instrs - for form, fields in instrs.items(): - print("Form", form) - for field, bits in fields.items(): - print("\tfield", field, bits) +from openpower.decoder.power_fields import * diff --git a/src/soc/decoder/power_fieldsn.py b/src/soc/decoder/power_fieldsn.py index 852dd15b..243bda89 100644 --- a/src/soc/decoder/power_fieldsn.py +++ b/src/soc/decoder/power_fieldsn.py @@ -1,79 +1,5 @@ -from collections import OrderedDict -from soc.decoder.power_fields import DecodeFields, BitRange -from nmigen import Module, Elaboratable, Signal, Cat -from nmigen.cli import rtlil -from copy import deepcopy +# moved to openpower-isa +# https://git.libre-soc.org/?p=openpower-isa.git;a=summary +# wildcard imports here ONLY to support migration - -class SignalBitRange(BitRange): - def __init__(self, signal): - BitRange.__init__(self) - self.signal = signal - - def __deepcopy__(self, memo): - signal = deepcopy(self.signal, memo) - retval = SignalBitRange(signal=signal) - for k, v in self.items(): - k = deepcopy(k, memo) - v = deepcopy(v, memo) - retval[k] = v - return retval - - def _rev(self, k): - width = self.signal.width - return width-1-k - - def __getitem__(self, subs): - # *sigh* field numberings are bit-inverted. PowerISA 3.0B section 1.3.2 - if isinstance(subs, slice): - res = [] - start, stop, step = subs.start, subs.stop, subs.step - if step is None: - step = 1 - if start is None: - start = 0 - if stop is None: - stop = -1 - if start < 0: - start = len(self) + start + 1 - if stop < 0: - stop = len(self) + stop + 1 - for t in range(start, stop, step): - t = len(self) - 1 - t # invert field back - k = OrderedDict.__getitem__(self, t) - res.append(self.signal[self._rev(k)]) # reverse-order here - return Cat(*res) - else: - if subs < 0: - subs = len(self) + subs - subs = len(self) - 1 - subs # invert field back - k = OrderedDict.__getitem__(self, subs) - return self.signal[self._rev(k)] # reverse-order here - - -class SigDecode(Elaboratable): - - def __init__(self, width): - self.opcode_in = Signal(width, reset_less=False) - self.df = DecodeFields(SignalBitRange, [self.opcode_in]) - self.df.create_specs() - - def elaborate(self, platform): - m = Module() - comb = m.d.comb - return m - - def ports(self): - return [self.opcode_in] - - -def create_sigdecode(): - s = SigDecode(32) - return s - - -if __name__ == '__main__': - sigdecode = create_sigdecode() - vl = rtlil.convert(sigdecode, ports=sigdecode.ports()) - with open("decoder.il", "w") as f: - f.write(vl) +from openpower.decoder.power_fieldsn import * diff --git a/src/soc/decoder/selectable_int.py b/src/soc/decoder/selectable_int.py index 152764ca..99cf5f2c 100644 --- a/src/soc/decoder/selectable_int.py +++ b/src/soc/decoder/selectable_int.py @@ -1,560 +1,5 @@ -import unittest -from copy import copy -from soc.decoder.power_fields import BitRange -from operator import (add, sub, mul, floordiv, truediv, mod, or_, and_, xor, - neg, inv, lshift, rshift) +# moved to openpower-isa +# https://git.libre-soc.org/?p=openpower-isa.git;a=summary +# wildcard imports here ONLY to support migration - -def check_extsign(a, b): - if isinstance(b, FieldSelectableInt): - b = b.get_range() - if isinstance(b, int): - return SelectableInt(b, a.bits) - if b.bits != 256: - return b - return SelectableInt(b.value, a.bits) - - -class FieldSelectableInt: - """FieldSelectableInt: allows bit-range selection onto another target - """ - - def __init__(self, si, br): - self.si = si # target selectable int - if isinstance(br, list) or isinstance(br, tuple): - _br = BitRange() - for i, v in enumerate(br): - _br[i] = v - br = _br - self.br = br # map of indices. - - def eq(self, b): - if isinstance(b, SelectableInt): - for i in range(b.bits): - self[i] = b[i] - else: - self.si = copy(b.si) - self.br = copy(b.br) - - def _op(self, op, b): - vi = self.get_range() - vi = op(vi, b) - return self.merge(vi) - - def _op1(self, op): - vi = self.get_range() - vi = op(vi) - return self.merge(vi) - - def __getitem__(self, key): - print("getitem", key, self.br) - if isinstance(key, SelectableInt): - key = key.value - if isinstance(key, int): - key = self.br[key] # don't do POWER 1.3.4 bit-inversion - return self.si[key] - if isinstance(key, slice): - key = self.br[key] - return selectconcat(*[self.si[x] for x in key]) - - def __setitem__(self, key, value): - if isinstance(key, SelectableInt): - key = key.value - key = self.br[key] # don't do POWER 1.3.4 bit-inversion - if isinstance(key, int): - return self.si.__setitem__(key, value) - else: - if not isinstance(value, SelectableInt): - value = SelectableInt(value, bits=len(key)) - for i, k in enumerate(key): - self.si[k] = value[i] - - def __negate__(self): - return self._op1(negate) - - def __invert__(self): - return self._op1(inv) - - def __add__(self, b): - return self._op(add, b) - - def __sub__(self, b): - return self._op(sub, b) - - def __mul__(self, b): - return self._op(mul, b) - - def __div__(self, b): - return self._op(truediv, b) - - def __mod__(self, b): - return self._op(mod, b) - - def __and__(self, b): - return self._op(and_, b) - - def __or__(self, b): - return self._op(or_, b) - - def __xor__(self, b): - return self._op(xor, b) - - def get_range(self): - vi = SelectableInt(0, len(self.br)) - for k, v in self.br.items(): - vi[k] = self.si[v] - return vi - - def merge(self, vi): - fi = copy(self) - for i, v in fi.br.items(): - fi.si[v] = vi[i] - return fi - - def __repr__(self): - return "FieldSelectableInt(si=%s, br=%s)" % (self.si, self.br) - - def asint(self, msb0=False): - res = 0 - brlen = len(self.br) - for i, key in self.br.items(): - bit = self.si[key].value - #print("asint", i, key, bit) - res |= bit << ((brlen-i-1) if msb0 else i) - return res - - -class FieldSelectableIntTestCase(unittest.TestCase): - def test_arith(self): - a = SelectableInt(0b10101, 5) - b = SelectableInt(0b011, 3) - br = BitRange() - br[0] = 0 - br[1] = 2 - br[2] = 3 - fs = FieldSelectableInt(a, br) - c = fs + b - print(c) - #self.assertEqual(c.value, a.value + b.value) - - def test_select(self): - a = SelectableInt(0b00001111, 8) - br = BitRange() - br[0] = 0 - br[1] = 1 - br[2] = 4 - br[3] = 5 - fs = FieldSelectableInt(a, br) - - self.assertEqual(fs.get_range(), 0b0011) - - def test_select_range(self): - a = SelectableInt(0b00001111, 8) - br = BitRange() - br[0] = 0 - br[1] = 1 - br[2] = 4 - br[3] = 5 - fs = FieldSelectableInt(a, br) - - self.assertEqual(fs[2:4], 0b11) - - fs[0:2] = 0b10 - self.assertEqual(fs.get_range(), 0b1011) - -class SelectableInt: - """SelectableInt - a class that behaves exactly like python int - - this class is designed to mirror precisely the behaviour of python int. - the only difference is that it must contain the context of the bitwidth - (number of bits) associated with that integer. - - FieldSelectableInt can then operate on partial bits, and because there - is a bit width associated with SelectableInt, slices operate correctly - including negative start/end points. - """ - - def __init__(self, value, bits): - if isinstance(value, SelectableInt): - value = value.value - mask = (1 << bits) - 1 - self.value = value & mask - self.bits = bits - self.overflow = (value & ~mask) != 0 - - def eq(self, b): - self.value = b.value - self.bits = b.bits - - def to_signed_int(self): - print ("to signed?", self.value & (1<<(self.bits-1)), self.value) - if self.value & (1<<(self.bits-1)) != 0: # negative - res = self.value - (1<> b.value, self.bits) - - def __getitem__(self, key): - if isinstance(key, SelectableInt): - key = key.value - if isinstance(key, int): - assert key < self.bits, "key %d accessing %d" % (key, self.bits) - assert key >= 0 - # NOTE: POWER 3.0B annotation order! see p4 1.3.2 - # MSB is indexed **LOWEST** (sigh) - key = self.bits - (key + 1) - - value = (self.value >> key) & 1 - print("getitem", key, self.bits, hex(self.value), value) - return SelectableInt(value, 1) - elif isinstance(key, slice): - assert key.step is None or key.step == 1 - assert key.start < key.stop - assert key.start >= 0 - assert key.stop <= self.bits - - stop = self.bits - key.start - start = self.bits - key.stop - - bits = stop - start - #print ("__getitem__ slice num bits", start, stop, bits) - mask = (1 << bits) - 1 - value = (self.value >> start) & mask - print("getitem", stop, start, self.bits, hex(self.value), value) - return SelectableInt(value, bits) - - def __setitem__(self, key, value): - if isinstance(key, SelectableInt): - key = key.value - print("setitem", key, self.bits, hex(self.value)) - if isinstance(key, int): - assert key < self.bits - assert key >= 0 - key = self.bits - (key + 1) - if isinstance(value, SelectableInt): - assert value.bits == 1 - value = value.value - - value = value << key - mask = 1 << key - self.value = (self.value & ~mask) | (value & mask) - elif isinstance(key, slice): - assert key.step is None or key.step == 1 - assert key.start < key.stop - assert key.start >= 0 - assert key.stop <= self.bits - - stop = self.bits - key.start - start = self.bits - key.stop - - bits = stop - start - #print ("__setitem__ slice num bits", bits) - if isinstance(value, SelectableInt): - assert value.bits == bits, "%d into %d" % (value.bits, bits) - value = value.value - mask = ((1 << bits) - 1) << start - value = value << start - self.value = (self.value & ~mask) | (value & mask) - - def __ge__(self, other): - if isinstance(other, FieldSelectableInt): - other = other.get_range() - if isinstance(other, SelectableInt): - other = check_extsign(self, other) - assert other.bits == self.bits - other = other.to_signed_int() - if isinstance(other, int): - return onebit(self.to_signed_int() >= other) - assert False - - def __le__(self, other): - if isinstance(other, FieldSelectableInt): - other = other.get_range() - if isinstance(other, SelectableInt): - other = check_extsign(self, other) - assert other.bits == self.bits - other = other.to_signed_int() - if isinstance(other, int): - return onebit(self.to_signed_int() <= other) - assert False - - def __gt__(self, other): - if isinstance(other, FieldSelectableInt): - other = other.get_range() - if isinstance(other, SelectableInt): - other = check_extsign(self, other) - assert other.bits == self.bits - other = other.to_signed_int() - if isinstance(other, int): - return onebit(self.to_signed_int() > other) - assert False - - def __lt__(self, other): - print ("SelectableInt lt", self, other) - if isinstance(other, FieldSelectableInt): - other = other.get_range() - if isinstance(other, SelectableInt): - other = check_extsign(self, other) - assert other.bits == self.bits - other = other.to_signed_int() - if isinstance(other, int): - a = self.to_signed_int() - res = onebit(a < other) - print (" a < b", a, other, res) - return res - assert False - - def __eq__(self, other): - print("__eq__", self, other) - if isinstance(other, FieldSelectableInt): - other = other.get_range() - if isinstance(other, SelectableInt): - other = check_extsign(self, other) - assert other.bits == self.bits - other = other.value - print (" eq", other, self.value, other == self.value) - if isinstance(other, int): - return onebit(other == self.value) - assert False - - def narrow(self, bits): - assert bits <= self.bits - return SelectableInt(self.value, bits) - - def __bool__(self): - return self.value != 0 - - def __repr__(self): - return "SelectableInt(value=0x{:x}, bits={})".format(self.value, - self.bits) - - def __len__(self): - return self.bits - - def asint(self): - return self.value - - -def onebit(bit): - return SelectableInt(1 if bit else 0, 1) - - -def selectltu(lhs, rhs): - """ less-than (unsigned) - """ - if isinstance(rhs, SelectableInt): - rhs = rhs.value - return onebit(lhs.value < rhs) - - -def selectgtu(lhs, rhs): - """ greater-than (unsigned) - """ - if isinstance(rhs, SelectableInt): - rhs = rhs.value - return onebit(lhs.value > rhs) - - -# XXX this probably isn't needed... -def selectassign(lhs, idx, rhs): - if isinstance(idx, tuple): - if len(idx) == 2: - lower, upper = idx - step = None - else: - lower, upper, step = idx - toidx = range(lower, upper, step) - fromidx = range(0, upper-lower, step) # XXX eurgh... - else: - toidx = [idx] - fromidx = [0] - for t, f in zip(toidx, fromidx): - lhs[t] = rhs[f] - - -def selectconcat(*args, repeat=1): - if repeat != 1 and len(args) == 1 and isinstance(args[0], int): - args = [SelectableInt(args[0], 1)] - if repeat != 1: # multiplies the incoming arguments - tmp = [] - for i in range(repeat): - tmp += args - args = tmp - res = copy(args[0]) - for i in args[1:]: - if isinstance(i, FieldSelectableInt): - i = i.si - assert isinstance(i, SelectableInt), "can only concat SIs, sorry" - res.bits += i.bits - res.value = (res.value << i.bits) | i.value - print("concat", repeat, res) - return res - - -class SelectableIntTestCase(unittest.TestCase): - def test_arith(self): - a = SelectableInt(5, 8) - b = SelectableInt(9, 8) - c = a + b - d = a - b - e = a * b - f = -a - g = abs(f) - h = abs(a) - self.assertEqual(c.value, a.value + b.value) - self.assertEqual(d.value, (a.value - b.value) & 0xFF) - self.assertEqual(e.value, (a.value * b.value) & 0xFF) - self.assertEqual(f.value, (-a.value) & 0xFF) - self.assertEqual(c.bits, a.bits) - self.assertEqual(d.bits, a.bits) - self.assertEqual(e.bits, a.bits) - self.assertEqual(a.bits, f.bits) - self.assertEqual(a.bits, h.bits) - - def test_logic(self): - a = SelectableInt(0x0F, 8) - b = SelectableInt(0xA5, 8) - c = a & b - d = a | b - e = a ^ b - f = ~a - self.assertEqual(c.value, a.value & b.value) - self.assertEqual(d.value, a.value | b.value) - self.assertEqual(e.value, a.value ^ b.value) - self.assertEqual(f.value, 0xF0) - - def test_get(self): - a = SelectableInt(0xa2, 8) - # These should be big endian - self.assertEqual(a[7], 0) - self.assertEqual(a[0:4], 10) - self.assertEqual(a[4:8], 2) - - def test_set(self): - a = SelectableInt(0x5, 8) - a[7] = SelectableInt(0, 1) - self.assertEqual(a, 4) - a[4:8] = 9 - self.assertEqual(a, 9) - a[0:4] = 3 - self.assertEqual(a, 0x39) - a[0:4] = a[4:8] - self.assertEqual(a, 0x99) - - def test_concat(self): - a = SelectableInt(0x1, 1) - c = selectconcat(a, repeat=8) - self.assertEqual(c, 0xff) - self.assertEqual(c.bits, 8) - a = SelectableInt(0x0, 1) - c = selectconcat(a, repeat=8) - self.assertEqual(c, 0x00) - self.assertEqual(c.bits, 8) - - def test_repr(self): - for i in range(65536): - a = SelectableInt(i, 16) - b = eval(repr(a)) - self.assertEqual(a, b) - - def test_cmp(self): - a = SelectableInt(10, bits=8) - b = SelectableInt(5, bits=8) - self.assertTrue(a > b) - self.assertFalse(a < b) - self.assertTrue(a != b) - self.assertFalse(a == b) - - def test_unsigned(self): - a = SelectableInt(0x80, bits=8) - b = SelectableInt(0x7f, bits=8) - self.assertTrue(a > b) - self.assertFalse(a < b) - self.assertTrue(a != b) - self.assertFalse(a == b) - - -if __name__ == "__main__": - unittest.main() +from openpower.decoder.selectable_int import * -- 2.30.2