3 <http://bugs.libre-riscv.org/show_bug.cgi?id=186>
5 The decoder is in charge of translating the POWER instruction stream into operations that can be handled by our backend. It will have an extra input bit, set via a MSR, that will switch on GPU instructions.
7 Source code: <https://git.libre-riscv.org/?p=soc.git;a=tree;f=src/soc/decoder;hb=HEAD>
11 The decoder has been written in python, to parse straight CSV files and other information taken directly from the Power ISA Standards PDF files. This significantly reduces the possibility of manual transcription errors and greatly reduces code size. Based on Anton Blanchard's excellent microwatt design, these tables are in [[openpower/isatables]] which includes links to download the csv files.
13 The top level decoder object recursively drops through progressive levels of case statement groups, covering additional portions of the incoming instruction bits. More on this technique - for which python and nmigen were *specifically* and strategically chosen - is outlined here <http://lists.libre-riscv.org/pipermail/libre-riscv-dev/2020-March/004882.html>
15 The PowerDecoder2, on encountering for example an ADD
16 operation, needs to know whether Rc=0/1, whether OE=0/1, whether
17 RB is to be read, whether an immediate is to be read and so on.
18 With all of this information being specified in the CSV files, on
19 a per-instruction basis, it is simply a matter of expanding that
20 information out into a data structure called Decode2ToExecute1Type.
21 From there it becomes easily possible for other parts of the processor
22 to take appropriate action.
24 * [Decode2ToExecute1Type](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/decoder/decode2execute1.py;hb=HEAD)
26 ## Link to Function Units
28 The Decoder (PowerDecode2) knows which registers are needed, however what
31 * which Register file ports to connect to (this is defined by regspecs)
32 * the order of those regfile ports (again: defined by regspecs)
34 Neither do the Phase-aware Function Units (derived from MultiCompUnit)
35 themselves know anything about the PowerDecoder, and they certainly
36 do not know when a given instruction will need to tell *them* to read
37 RA, or RB. For example: negation of RA only requires one operand,
38 where add RA, RB requires two. Who tells whom that information, when
39 the ALU's job is simply to add, and the Decoder's job is simply to decode?
41 This is where a special function called "rdflags()" comes into play.
42 rdflags works closely in conjunction with regspecs and the PowerDecoder2,
43 in each Function Unit's "pipe\_data.py" file. It defines the flags that
44 determine, from current instruction, whether the Function Unit actually
45 *wants* any given Register Read Ports activated or not.
47 That dynamically-determined information will then actively disable
48 (or allow) Register file Read requests (rd.req) on a per-port basis.
52 class ALUInputData(IntegerData):
53 regspec = [('INT', 'ra', '0:63'), # RA
54 ('INT', 'rb', '0:63'), # RB/immediate
55 ('XER', 'xer_so', '32'), # XER bit 32: SO
56 ('XER', 'xer_ca', '34,45')] # XER bit 34/45: CA/CA32
58 This shows us that, for the ALU pipeline, it expects two INTEGER
59 operands (RA and RB) both 64-bit, and it expects XER SO, CA and CA32
60 bits. However this information - as to which operands are required -
63 Continuing from the OP_ADD example, where inspection of the CSV files
64 (or the ISA tables) shows that we optionally need xer_so (OE=1),
65 optionally need xer_ca (Rc=1), and even optionally need RB (add with
66 immediate), we begin to understand that a dynamic system linking the
67 PowerDecoder2 information to the Function Units is needed. This is
68 where power\_regspec\_map.py comes into play.
70 def regspec_decode_read(e, regfile, name):
72 # Int register numbering is *unary* encoded
74 return e.read_reg1.ok, 1<<e.read_reg1.data
76 return e.read_reg2.ok, 1<<e.read_reg2.data
78 Here we can see that, for INTEGER registers, if the Function Unit
79 has a connection (an incoming operand) named "RA", the tuple returned
80 contains two crucial pieces of information:
82 1. The field from PowerDecoder2 which tells us if RA is even actually
83 required by this (decoded) instruction
84 2. The INTEGER Register file read port activation signal (its read-enable
85 line-activation) which, if sent to the INTEGER Register file, will
86 request the actual register required by this current (decoded)
89 Thus we have the *dynamic* information - not hardcoded in RTL but
90 specified in *python* - encoding both if (first item of tuple) and
91 what (second item of tuple) each Function Unit receives, and this
92 for each and every operand. A corresponding process exists for write,
95 * [[architecture/regfile]]
96 * [CompUnits](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/fu/compunits/compunits.py;hb=HEAD)
97 * Example [ALU pipe_data.py specification](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/fu/alu/pipe_data.py;hb=HEAD)
98 * [power_regspec_map.py](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/decoder/power_regspec_map.py;hb=HEAD)
100 ## Fixed point instructions
102 - addi, addis, mulli - fairly straightforward - extract registers and immediate and translate to the appropriate op
103 - addic, addic., subfic - similar to above, but now carry needs to be saved somewhere
104 - add[o][.], subf[o][.], adde\*, subfe\*, addze\*, neg\*, mullw\*, divw\* - These are more fun. They need to set the carry (if . is present) and overflow (if o is present) flags, as well as taking in the carry flag for the extended versions.
105 - addex - uses the overflow flag as a carry in, and if CY is set to 1, sets overflow like it would carry.
106 - cmp, cmpi - sets bits of the selected comparison result register based on whether the comparison result was greater than, less than, or equal to
107 - andi., ori, andis., oris, xori, xoris - similar to above, though the and versions set the flags in CR0
108 - and\*, or\*, xor\*, nand\*, eqv\*, andc\*, orc\* - similar to the register-register arithmetic instructions above
112 The Decoder uses a class called PowerOp which get instantiated
113 for every instruction. PowerOp class instantiation has member signals
114 whose values get set respectively for each instruction.
116 We use Python Enums to help with common decoder values.
117 Below is the POWER add insruction.
119 | opcode | unit | internal op | in1 | in2 | in3 | out | CR in | CR out | inv A | inv out | cry in | cry out | ldst len | BR | sgn ext | upd | rsrv | 32b | sgn | rc | lk | sgl pipe | comment | form |
120 |--------------|------|-------------|-----|-----|------|-----|-------|--------|-------|---------|--------|---------|----------|----|---------|-----|------|-----|-----|----|----|----------|---------|------|
121 | 0b0100001010 | ALU | OP_ADD | RA | RB | NONE | RT | 0 | 0 | 0 | 0 | ZERO | 0 | NONE | 0 | 0 | 0 | 0 | 0 | 0 | RC | 0 | 0 | add | XO |
123 Here is an example of a toy multiplexer that sets various fields in the
124 PowerOP signal class to the correct values for the add instruction when
125 select is set equal to 1. This should give you a feel for how we work with
128 from nmigen import Module, Elaboratable, Signal, Cat, Mux
129 from soc.decoder.power_enums import (Function, Form, InternalOp,
130 In1Sel, In2Sel, In3Sel, OutSel, RC, LdstLen,
131 CryIn, get_csv, single_bit_flags,
132 get_signal_name, default_values)
133 from soc.decoder.power_fields import DecodeFields
134 from soc.decoder.power_fieldsn import SigDecode, SignalBitRange
135 from soc.decoder.power_decoder import PowerOp
137 class Op_Add_Example(Elaboratable):
139 self.select = Signal(reset_less=True)
140 self.op_add = PowerOp()
142 def elaborate(self, platform):
146 with m.If(self.select == 1):
147 m.d.comb += op_add.function_unit.eq(Function.ALU)
148 m.d.comb += op_add.form.eq(Form.XO)
149 m.d.comb += op_add.internal_op.eq(InternalOp.OP_ADD)
150 m.d.comb += op_add.in1_sel.eq(In1Sel.RA)
151 m.d.comb += op_add.in2_sel.eq(In2Sel.RB)
152 m.d.comb += op_add.in3_sel.eq(In3Sel.NONE)
153 m.d.comb += op_add.out_sel.eq(OutSel.RT)
154 m.d.comb += op_add.rc_sel.eq(RC.RC)
155 m.d.comb += op_add.ldst_len.eq(LdstLen.NONE)
156 m.d.comb += op_add.cry_in.eq(CryIn.ZERO)
160 from nmigen.back import verilog
161 verilog_file = "op_add_example.v"
162 top = Op_Add_Example()
163 f = open(verilog_file, "w")
164 verilog = verilog.convert(top, name='top', strip_internal_attrs=True,
165 ports=top.op_add.ports())
167 print(f"Verilog Written to: {verilog_file}")
169 The [actual POWER9 Decoder](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/decoder/power_decoder2.py;hb=HEAD)
170 uses this principle, in conjunction with reading the information shown
171 in the table above from CSV files (as opposed to hardcoding them in
172 python source). These [[CSV files|openpower/isatables]],
173 being machine-readable in a wide variety
174 of programming languages, are conveniently available for use by
175 other projects well beyond just this SOC.
177 This also demonstrates one of the design aspects taken in this project: to
178 *combine* the power of python's full capabilities in order to create
179 advanced dynamically generated HDL, rather than (as done with MyHDL)
180 limit python code to a subset of its full capabilities.
182 The CSV Files are loaded by
183 [power_decoder.py](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/decoder/power_decoder.py;hb=HEAD)
184 and are used to construct a hierarchical cascade of switch statements. The original code came from
185 [microwatt](https://github.com/antonblanchard/microwatt/blob/master/decode1.vhdl)
186 where the original hardcoded cascade can be seen.
188 The docstring for power_decoder.py gives more details: each level in the hierarchy, just as in the original decode1.vhdl, will take slices of the instruction bitpattern, match against it, and if successful will continue with further subdecoders until a line is met that contains the required Operand Information (a PowerOp) exactly as shown at the top of this page.
190 In this way, different sections of the instruction are successively decoded (major opcode, then minor opcode, then sub-patterns under those) until the required instruction is fully recognised, and the hierarchical cascade of switch patterns results in a flat interpretation being produced that is useful internally.
192 # second explanation / walkthrough
194 we (manually) extracted the pseudo-code from the v3.0B specification
196 <https://git.libre-soc.org/?p=libreriscv.git;a=blob;f=openpower/isa/fixedlogical.mdwn;hb=HEAD>
198 then wrote a parser and language translator (aka compiler) to convert
199 those code-fragments to python:
200 <https://git.libre-soc.org/?p=soc.git;a=tree;f=src/soc/decoder/pseudo;hb=HEAD>
202 then went to a lot of trouble over the course of several months to
203 co-simulate them, update them, and make them accurate according to the
205 <https://git.libre-soc.org/?p=libreriscv.git;a=blob;f=openpower/isa/fixedarith.mdwn;h=470a833ca2b8a826f5511c4122114583ef169e55;hb=HEAD#l721>
207 and created a fully-functioning python-based OpenPOWER ISA simulator:
208 <https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/decoder/isa/caller.py;hb=HEAD>
210 there is absolutely no reason why this language-translator (aka compiler)
212 <https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/decoder/pseudo/parser.py;hb=HEAD>
214 should not be joined by another compiler, targetting c for use inside
215 the linux kernel or, another compiler which auto-generates c++ for use
216 inside power-gem5, such that this:
217 <https://github.com/power-gem5/gem5/blob/cae53531103ebc5bccddf874db85f2659b64000a/src/arch/power/isa/decoder.isa#L1214>
219 becomes an absolute breeze to update.
221 note that we maintain a decoder which is based on Microwatt: we extracted
222 microwatt's decode1.vhdl into CSV files, and parse them in python as
223 hierarchical recursive data structures:
224 <https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/decoder/power_decoder.py;hb=HEAD>
226 where the actual CSV files that it reads are here:
227 <https://git.libre-soc.org/?p=libreriscv.git;a=tree;f=openpower/isatables;hb=HEAD>
229 this is then combined with *another* table that was extracted from the
231 <https://git.libre-soc.org/?p=libreriscv.git;a=blob;f=openpower/isatables/fields.text;hb=HEAD>
233 (the parser for that recognises "vertical bars" as being
235 <https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/decoder/power_fields.py;hb=HEAD>
237 and FINALLY - and this is about the only major piece of code that
238 actually involves any kind of manual code - again it is based on Microwatt
239 decode2.vhdl - we put everything together to turn a binary opcode into
240 "something that needs to be executed":
241 <https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/decoder/power_decoder2.py;hb=HEAD>
243 so our OpenPOWER simulator is actually based on:
245 * machine-readable CSV files
246 * machine-readable Field-Form files
247 * machine-readable spec-accurate pseudocode files
249 the only reason we haven't used those to turn it into HDL is because
250 doing so is a massive research project, where a first pass would be
251 highly likely to generate sub-optimal HDL