add inline comments
[libreriscv.git] / Documentation / notes_on_formal_proofs.mdwn
1 # Notes on Formal Proofs
2
3 If you study the ALU and SPR function unit directories, you'll find a set of formal proofs which I (Samuel A. Falvo II) found very confusing.
4 After some study of the ALU proofs, however, I've come to see some basic patterns.
5 Whether these patterns apply to other proofs throughout the rest of the code-base is unknown; I haven't gotten that far yet.
6 But, for now, this "cheat sheet" of sorts should help newcomers to the project better understand why these Python modules are structured the way they are.
7
8 In the discussion below, I'll be referring to the following URLs:
9
10 - [alu/formal/proof_main_stage.py](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/fu/alu/formal/proof_main_stage.py;hb=HEAD)
11 - [alu/main_stage.py](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/fu/alu/main_stage.py;h=719a9a731e4c9c5f637f574fbf01203fea36df28;hb=HEAD)
12
13 ## Pipeline Stage Architecture
14
15 A pipeline stage appears to have the following overall architecture
16 (there will almost certainly be exceptions to this that I'm not familiar with
17 lkcl: no, there are absolutely none. no exceptions. at all. this is
18 because there is a defined API):
19
20 (from previous stage)
21 OpSubset Operand Inputs (originally from Register File)
22 | |
23 V V
24 +-------------------------+
25 | Pipeline Stage |
26 +-------------------------+
27 | |
28 V V
29 OpSubset Result Outputs
30 (to next stage / register file logic)
31
32 Note that the **Pipeline Stage** is *purely combinatorial logic.*
33 (lkcl: yes. this is noted in the pipeline API, see
34 <https://git.libre-soc.org/?p=nmutil.git;a=blob;f=src/nmutil/stageapi.py;hb=HEAD>)
35 Any state between pipeline stages is instantiated through mechanisms not disclosed here.
36 (IIRC, that logic is located in the `nmutil` package, but my memory is hazy here.)
37 (lkcl: in the pipeline API. see stageapi.py).
38
39 ## class Driver
40
41 The `Driver` class is an `Elaboratable` class (an `nmigen` module)
42 which specifies all the properties that the module under test
43 (in this case, `ALUMainStage`)
44 must satisfy to be considered "correct."
45
46
47 ### `__init__`
48
49 The `__init__` method is the constructor for the class.
50 It prevents `Elaboratable`'s constructor from running.
51 (lkcl: not quite)
52 It's not yet clear to me why this is done.
53 (lkcl: because whitequark found that people were forgetting to add an
54 elaborate function, and wondering why their code failed to work).
55
56 ### `elaborate` method
57
58 Bluntly, this method is (depending on what you're familiar with) a
59 macro or a monad. Its job is to build a *description* of the final
60 output module by appending various objects to `comb`. In this case,
61 the module's job is to specify the correct behavior of a production
62 submodule, `ALUMainStage`. This is fairly basic `nmigen` material,
63 so I won't spend any more time on this.
64
65 #### CompALUOpSubset
66
67 `CompALUOpSubset` is, ultimately, an `nmigen`
68 [record](https://github.com/nmigen/nmigen/blob/master/nmigen/hdl/rec.py#L89)
69 which contains a number of fields related to telling the pipeline stage
70 *what to do*. (Hence the *Op* in the class name.) I won't disclose
71 the fields here, because they're liable to change at any time. What is
72 important to know here is that this record exists for one purpose: to
73 ensure that when the interface to the ALU pipeline stage changes for
74 any reason, *all* consumers of that interface are updated consistently.
75
76 Because this is a record, its fields may be introspected.
77 This happens frequently; it is usually the case that an Op-type record is passed unchanged from stage to stage.
78 However, `nmigen` does not seem to support testing records for equality in formal assertions.
79 (lkcl: it does)
80 To express this constraint without needing to update a pile of use-sites every time the interface changes,
81 you'll find logic
82 [like this](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/fu/alu/formal/proof_main_stage.py;h=5e70d1e885b41ca50d69f57e9ee94b568d001c2e;hb=HEAD#l64).
83
84 (lkcl: which has been established why:
85 <https://bugs.libre-soc.org/show_bug.cgi?id=429#c3> and it is down
86 to FPPipeContext not being a Record, but its member variable - op -
87 *is* a Record).
88
89 **NOTE:** Instantiating one of these records
90 (lkcl: FPPipeContext is not a Record, see above)) *does not* provide
91 these inputs to the module under test.
92 It merely makes the fields of this record available to the formal verification solver so it can fiddle the bits as it explores the design space.
93 The record must be connected directly to the module via a signal assignment;
94 [see line 62 of the source listing.](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/fu/alu/formal/proof_main_stage.py;h=5e70d1e885b41ca50d69f57e9ee94b568d001c2e;hb=HEAD#l62)
95
96 #### ALUPipeSpec
97
98 `ALUPipeSpec` is a similar construct, but it serves a different role than the above class.
99 Looking at its
100 [source code](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/fu/alu/pipe_data.py;h=71363049ba5a437a708e53dfbc3370f17aa394d1;hb=HEAD)
101 , it appears to define bits in one or more *register files*,
102 for both input input the stage and output from the stage.
103
104 This structure is passed to the constructor of the module-under-test.
105 That constructor, ultimately, has the effect of creating a set of inputs (`dut.i`) and outputs (`dut.o`) that matches the register field names.
106 **NOTE:** this process coalesces disparate field bits together!
107 For example, the POWER XER register defines two carry bits at positions 34 and 45 in a 64-bit word.
108 However, when referencing these bits via `dut.i.xer_ca`, they occupy bits 0 and 1.
109 The process reverses for outputs; bits 0 and 1 of the `dut.o.xer_ca` field will be re-distributed to XER register bits 34 and 45 again.
110 See [lines 9 (input) and 19 (output) of the source listing](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/fu/alu/pipe_data.py;h=71363049ba5a437a708e53dfbc3370f17aa394d1;hb=HEAD).
111
112 #### Instantiating the Module Under Test
113
114 Looking at [line 41 through 54](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/fu/alu/formal/proof_main_stage.py;h=5e70d1e885b41ca50d69f57e9ee94b568d001c2e;hb=HEAD#l41),
115 we see the module we want to test actually instantiated.
116 Since register inputs and outputs are frequently accessed, it's worthwhile defining a set of aliases for those signals.
117 Although this somewhat obscures the intent of the code,
118 it will save typing which reduces opportunity for error.
119
120 Lines 56 through 62 connect all the input signals of the submodule to the formal verifier.
121
122 (lkcl: note, it's been established that these lines can be replaced
123 with an Assert on the op and the muxid, see bug #429 comment 3 above).
124
125 #### Properties
126
127 Starting at [line 64](https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/fu/alu/formal/proof_main_stage.py;h=5e70d1e885b41ca50d69f57e9ee94b568d001c2e;hb=HEAD#l64)
128 we find the start of properties which must apply to the submodule.
129