sync_up: Discussion, add rest of points from thread
[libreriscv.git] / docs / firststeps.mdwn
1 [[!toc ]]
2
3 ---
4
5 # Introduction
6
7 This tutorial intends to shed some light at the first steps for a newcomer.
8 Note that this tutorial is a work in progress; feel free to update it.
9 This tutorial assumes that the environment and and repositories are set.
10 The information on environment can be found at
11 [[HDL workflow]] page, and
12 an easy set of commands is at the [[HDL_workflow/devscripts]] page
13
14 In this tutorial, we will perform these steps:
15
16 0. Checking we're all ready and set.
17 1. Running the first test and observing its results.
18 2. Updating the test so that it enables/disables some functions.
19
20 Note that this tutorial is ISA-centric, since the idea of adding it
21 was born by the times there were several newcomer-oriented tasks around
22 the decoder. Many of key concepts, however, are not ISA-specific, so
23 there is a hope that this guide can be helpful for other tasks.
24
25 # Checking we're all ready and set
26
27 Once we established the environment and cloned openpower-isa repository,
28 we can run the first test we'll use as an example to observe.
29 Since we're starting from decoder, we will work with openpower-isa
30 repository. If you followed the HDL guidelines strictly, you can chroot
31 into libresoc environment and work with the repository immediately,
32 by changing the directory to `${HOME}/src/openpower-isa`.
33
34 schroot -c libresoc /bin/bash
35 cd "${HOME}/src/openpower-isa"
36
37 If for some reason `openpower-isa` repository is not yet in your `src`
38 subdirectory, you need to clone the relevant repository. The recommended
39 way to do it is to use
40 [hdl-dev-repos](https://git.libre-soc.org/?p=dev-env-setup.git;a=blob;f=hdl-dev-repos;hb=HEAD)
41 script.
42
43 The environment is quite newcomer-friendly: the test we intend to dissect
44 is implemented in pure Python, so there is no need to build anything before
45 running the test. All we need is a good ol' Python; however, before proceeding,
46 make sure that Python is at least of version 3.7.2.
47
48 python3 --version
49
50 If the version is less than 3.7.2, consider re-visiting the
51 [HDL workflow](https://libre-soc.org/HDL_workflow/) page and checking that all
52 preparations are done correctly. Again, the preferred way is using simple
53 scripts to keep the life easy. In this case, the script of interest is
54 [install-hdl-apt-reqs](https://git.libre-soc.org/?p=dev-env-setup.git;a=blob;f=install-hdl-apt-reqs;hb=HEAD).
55
56 # Running the very first test
57
58 Once we have the repository and the needed Python version, we can launch
59 our first test suite (assuming we're already inside openpower-isa
60 repository):
61
62 python3 src/openpower/decoder/isa/test_caller.py > /tmp/log
63
64 The test takes a while to complete; once it's ready, you should observe that
65 it exits with success, and all tests from suite report no errors. In case
66 some tests report failures, consider raising a question in #libre-soc on
67 irc.libera.chat, or in
68 [mailing list](https://lists.libre-soc.org/mailman/listinfo/libre-soc-dev).
69 If you choose the latter, again, consider visiting
70 [HDL workflow](https://libre-soc.org/HDL_workflow/) page, to be aware
71 of mailing lists etiquette and preferences. Note, however, that some tests
72 can be skipped; this is acceptable.
73
74 If everything works fine, that's a good sign; let's proceed to the first
75 routine we'd like to dissect.
76
77 # Dissecting the test of interest
78
79 The script we launched contained several tests; amongst others, it checks
80 that addition instruction works correctly. Since we're going to look through
81 this instruction, let's for this time disable other tests so that we would
82 run only test related to addition.
83 Let's edit the test_caller.py script and mark all tests except addition one
84 as those which must be skipped. This goal can be achieved by two ways.
85
86 1. The first option is to rename all methods in `DecoderTestCase` class
87 so that the names start with prefix other than `test_`.
88 2. Alternatively, and this is the recommended way, tests can be temporarily
89 disabled via @unittest.skip decorator applied to the method. This method
90 is mentioned in HDL workflow as well (section 10.4).
91
92 Regardless of the option considered, `test_add`, which we're looking at,
93 should be kept intact. Once all tests but `test_add` are renamed or skipped,
94 re-run the test:
95
96 python3 src/openpower/decoder/isa/test_caller.py > /tmp/log
97
98 This time the suite should complete much faster. Let's observe the log and
99 look for a line `call add add`. This line comes from
100 `src/openpower/decoder/isa/caller.py`, and gives us an important information:
101 we're calling an `add` instruction, and its assembly mnemonic is `add` as well.
102
103 So far so good; we dropped other tests, and now look at the test for `add`
104 instruction. Now we're ready to check how the instruction behaves.
105
106 # A quick look at ADD instruction test
107
108 Let's return to the test and the logs. What the test for `add` instruction does?
109 For reader's convenience, the overall `test_add` code is duplicated here:
110
111 ```
112 def test_add(self):
113 lst = ["add 1, 3, 2"]
114 initial_regs = [0] * 32
115 initial_regs[3] = 0x1234
116 initial_regs[2] = 0x4321
117 with Program(lst, bigendian=False) as program:
118 sim = self.run_tst_program(program, initial_regs)
119 self.assertEqual(sim.gpr(1), SelectableInt(0x5555, 64))
120 ```
121
122 What do we see here? First of all, we have an assembly listing, consisting of
123 exactly one instruction, `"add 1, 3, 2"`. Then we establish the initial values
124 for registers we're going to work with. After that, we instantiate the program
125 with the assembly listing, execute the program, and compare the state of the
126 first general-purpose register (aka GPR) with some predefined value (64-bit
127 integer with value 0x5555).
128
129 Now let's turn to logs to see how they correspond to what we see.
130 The lines of interest are `reading reg RA 3 0` and `reading reg RB 2 0`.
131 These, unsurprisingly, are two registers (`3` and `2`) which act as
132 input parameters to `add` instruction; the result is then placed as
133 an output into register `1`.
134
135 Note that the initial values for registers `3` and `2` are `0x1234` and `0x4321`
136 respectively, and this matches to the input parameters in the logs:
137
138 ```
139 inputs [SelectableInt(value=0x1234, bits=64), SelectableInt(value=0x4321, bits=64)]
140 ```
141
142 The simulator performs the actual computation, obtaining the result, and then
143 updates the general-purpose register we used as an output parameter:
144
145 ```
146 results (SelectableInt(value=0x5555, bits=64),)
147 writing gpr 1 SelectableInt(value=0x5555, bits=64) 0
148 ```
149
150 In the end, we see that our assertion indeed passes:
151
152 ```
153 __eq__ SelectableInt(value=0x5555, bits=64) SelectableInt(value=0x5555, bits=64)
154 ```
155
156 You can play around the test, e.g. modify the input/output registers (there are
157 32 GPRs, so there's a plethora of combinations possible). In the next chapter,
158 we're going to take a deeper look and cover some bits of implementation.
159
160 # Diving into the instruction execution
161
162 One of interesting aspects we saw in the previous chapters is that whenever
163 the test executes (or, more precisely, simulates) some instructions, there's
164 some logic beneath these actions. Let's take a look at execution flow; for
165 now, we won't dive into that many details, but rather take a quick look.
166 We already saw that there are some logs coming from the
167 `src/openpower/decoder/isa/caller.py` script; but how do we end up there?
168 By the time of writing, all tests inside `test_caller.py` use internal
169 method called `run_tst_program`. This method, in turn, calls `run_tst`,
170 and this is the place where the magic happens. In `process` nested function,
171 we actually simulate that our instructions are executed one-by-one, literally
172 calling `yield from simulator.execute_one()`. And this method inside the
173 simulator instance belongs to
174 [`src/openpower/decoder/isa/caller.py`](https://git.libre-soc.org/?p=openpower-isa.git;a=blob;f=src/openpower/decoder/isa/caller.py;hb=HEAD)
175 script.
176 Inside `execute_one` method, the most crucial part is
177 `yield from self.call(opname)` call; and, if we take a look inside of the
178 `call` method, we will see that, aside of the aforementioned log
179 (`log("call", ins_name, asmop)`), this function also takes care of the rest
180 of the magic. This includes a lot of manipulations before and after executing
181 instruction, but the crucial part is quite simple:
182
183 ```
184 # execute actual instruction here (finally)
185 log("inputs", inputs)
186 results = info.func(self, *inputs)
187 log("results", results)
188 ```
189
190 The part of the most interest is `info.func` call; we'll take a look at it
191 in the next chapter.
192
193 # Markdown enters the scene
194
195 If we investigate the `info.func` object, we'll discover that, in case of our
196 `add` instruction, it is actually `fixedarith.op_add` function. Where does it
197 come from?
198
199 This function is defined inside `fixedarith.py`, which is generated from the
200 corresponding Markdown file, `fixedarith.mdwn`. On the first glance, one can
201 find an idea to generate the code from the documentation somewhat surprising;
202 however, this is perfectly reasonable, since we try to stay as close to the
203 documentation as possible. Some Markdown files, like `fixedarith.mdwn`,
204 contain the pseudocode in exactly the same form as it is present in the ISA
205 docs. Let's take a look at `fixedarith.mdwn` and find our instruction.
206
207 As we can see in `# Add` section, there are four `add` variants:
208
209 * add RT,RA,RB (OE=0 Rc=0)
210 * add. RT,RA,RB (OE=0 Rc=1)
211 * addo RT,RA,RB (OE=1 Rc=0)
212 * addo. RT,RA,RB (OE=1 Rc=1)
213
214 All variants are covered in the relevant OpenPOWER ISA. As of time of writing,
215 the most recent edition is 3.0C, and it is available here:
216
217 https://ftp.libre-soc.org/PowerISA_public.v3.0C.pdf
218
219 The instructions of our interest can be found exactly where we'd expect them
220 to be found, at chapter `3.3: Fixed-Point Facility Instructions`. We can see
221 here the instruction encoding, as well as its form (XO) and all information
222 we already found in `fixedarith.mdwn` file.
223
224 All these details are available in some form as part of `info` variable we
225 observed before (at `caller.py` file). The `info` variable is, actually,
226 a simple instance of named tuple; the overall structure of this named tuple
227 is left as an exercise for the reader.
228
229 # Modifying the pseudocode
230
231 We won't dive into all gory details of the decoder, at least for now. What
232 is important for us at this stage is, how can we affect the generated code?
233 As we saw recently, the markdown file is somehow converted into the Python
234 code; but how is the conversion done?
235
236 This is done by the script called pywriter. Again, we omit the exact details,
237 since this script clearly deserves its own documentation; the only crucial
238 information that pywriter script uses PLY (hint: Python-Lex-Yacc) in order
239 to get the work done, and, thanks to PLY magic, it is able to convert ISA
240 pseudocode into something more Pythonic.
241
242 For illustrative purposes, let's modify the pseudocode so that, instead of
243 addition, it performs a subtraction. Let's find the pseudocode for `add`
244 instructions inside `fixedarith.mdwn`...
245
246 ```
247 Pseudo-code:
248
249 RT <- (RA) + (RB)
250 ```
251
252 and modify it to:
253
254
255 ```
256 Pseudo-code:
257
258 RT <- (RA) - (RB)
259 ```
260
261 OK, we changed the pseudocode inside the Markdown, but it hasn't yet changed
262 a word inside the corresponding Python file (`fixedarith.py`). If we attempt
263 to re-run the unit test, nothing happens, since Python code is kept intact.
264
265 In order to force the source code re-generation, `pywriter noall fixedarith`
266 command is used. Once `pywriter` completes its task, we can re-run the test,
267 and it should **fail**. The logs show a different result.
268
269 Work out by hand what 0x1234 - 0x4321 in 64-bit unsigned arithmetic is, and
270 change the assert in the unit test to match. Re-run: the test should now
271 **pass**. These manipulations, again, are left as an exercise for the reader.
272
273 This chapter concludes the First Steps tutorial. We'll take a deeper look
274 at pseudocode generation when we will dive into an example of modifying
275 BCD instructions.