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