You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a series of graded exercises in using nMigen, the Python-based Hardware Design Language, to design and verify digital circuits!
3
+
This is a series of graded exercises in using [Amaranth HDL](https://amaranth-lang.org/docs/amaranth/latest/intro.html), the Python-based Hardware Design Language, to design and verify digital circuits!
4
4
5
5
It's also a work in progress.
6
6
7
-
Please note, the goal is not to be pedantically correct in every explanation. As you progress through the exercises, you'll learn for yourself the breadth of nMigen and what you can do with it!
7
+
Please note, the goal is not to be pedantically correct in every explanation. As you progress through the exercises, you'll learn for yourself the breadth of Amaranth and what you can do with it!
8
8
9
9
## Prerequisite knowledge
10
10
@@ -16,66 +16,26 @@ You will need knowledge of:
16
16
17
17
## Software prerequisites
18
18
19
-
If you're using Windows, I hope you're using Windows Subsystem for Linux. If not, you're on your own.
19
+
YosysHQ has put together a [comprehensive installer](https://github.com/YosysHQ/oss-cad-suite-build#installation) containing everything you need, and then some. Simply follow the instructions.
20
20
21
-
### Option 1: Docker (recommended)
21
+
### Tip for Windows users
22
22
23
-
Victor Muñoz has put together a [docker container](https://github.com/vmunoz82/eda_tools) that has everything you need: Python 3, nMigen, yosys, SymbiYosys, and the Z3, boolector, and yices solvers.
23
+
If you just downloaded the exe file, it will be in your Downloads folder. Run the executable, and it extracts everything to Downloads/oss-cad-suite. You can simply move the oss-cad-suite directory to somewhere more convient.
24
24
25
-
You'll need to get [Docker Desktop](https://www.docker.com/get-started). If you're on Windows, follow the instructions for [Docker/WSL](https://docs.docker.com/docker-for-windows/wsl/) instead.
25
+
*Do not use PowerShell*. Use Command Prompt instead. Always be sure to run `<your-directory>\oss-cad-suite\environment.bat` in Command Prompt before doing any work. You'll know if the environment was set properly if you see `[OSS CAD Suite]` before your prompt:
26
26
27
-
Also, if you're on Windows, you will want to download and run an [X server](https://medium.com/@japheth.yates/the-complete-wsl2-gui-setup-2582828f4577) so that you can use gtkwave from the command line on WSL.
Once you've done that, you can open up WSL (if you're on Windows) and then run [`eda_tools.sh`](https://raw.githubusercontent.com/vmunoz82/eda_tools/main/eda_tools.sh). This will conveniently start up the docker container with all the right options.
30
-
31
-
You may want to replace `-v $HOME:/$HOME` in `eda_tools.sh` with `-v /your/host/working/directory:/some/nice/directory/on/docker`, and then you can access your files in `/some/nice/directory/on/docker` in the docker container. For example, I do my work in WSL in `/mnt/f` so I simply use `-v /mnt/f:/mnt/f`.
32
-
33
-
Remember that the docker container will not save local files (e.g. files in `/workspace`), so make sure you have your working directory mapped!
34
-
35
-
### Option 2: Build it all yourself
36
-
37
-
You will need:
38
-
39
-
* Python 3.6 or above: See below for Python 3.6 on WSL.
40
-
41
-
* Install yosys, Symbiyosys, yices2, and z3.
42
-
*`sudo apt install curl`
43
-
* Now follow the [instructions to install these](https://symbiyosys.readthedocs.io/en/latest/install.html). It is highly recommended to follow those instructions, especially for yosys since the git repo has many more fixes than the official release.
44
-
* Note that when you see `-j$(nproc)`, it means to specify the number of processors your CPU has. You can't really go wrong by using `-j4`. You can go higher if you know you have more.
45
-
* z3 takes a long time to compile. Go watch a YouTube video in the meantime.
* For WSL, get the Windows version unless you want to run an [X server](https://medium.com/@japheth.yates/the-complete-wsl2-gui-setup-2582828f4577).
54
-
* The package for gtkwave for Windows gives you no clue how to install for Windows. Unzip the zip file into, say, C:, and then add C:\gtkwave\bin to your Windows path.
55
-
56
-
## Python and WSL
57
-
58
-
Python 2 and Python 3 are not compatible. This is why this happens:
59
-
60
-
*`python`, `pip` -> Python 2
61
-
*`python3`, `pip3` -> Python 3
62
-
63
-
On my freshly-installed WSL, the only version present is Python 3.8. If it is not on your version, you may want to upgrade to 3.8. Upgrading is beyond the scope of this document.
In installing the yosys prerequisites, Python 2 will be installed. So be aware that when you want to run anything under Python 3, you must use `python3` (or `pip3` for installing), not `python` (or `pip`).
72
-
73
-
# Tip for vscode users:
33
+
## Tip for vscode users
74
34
75
35
Open File > Preferences > Settings, look for pylint args, and add:
This is because pylint doesn't recognize that `nmigen.hdl.dsl._guardedcontextmanager` is a valid context manager. Otherwise pylint will complain for every `with m.If` statement.
41
+
This is because pylint doesn't recognize that `amaranth.hdl.dsl._guardedcontextmanager` is a valid context manager. Otherwise pylint will complain for every `with m.If` statement.
Copy file name to clipboardExpand all lines: 01_input.md
+27-21Lines changed: 27 additions & 21 deletions
Original file line number
Diff line number
Diff line change
@@ -1,11 +1,12 @@
1
1
# Exercise 1: Counting coin
2
2
3
3
<palign="right">
4
-
Khajiit has knowledge... if you have coin.
4
+
<i>Khajiit has knowledge... if you have coin.</i>
5
5
</p>
6
6
7
7
---
8
-
## What you'll do:
8
+
9
+
## What you'll do
9
10
10
11
Create a module that takes as inputs:
11
12
@@ -29,25 +30,25 @@ Use formal verification to:
29
30
30
31
## Step 1: Create a module
31
32
32
-
An nMigen*module* is a Python class that has inputs and outputs, and code that generates the logic for the desired functionality. Note that I didn't say code that *implements* the function. A key concept is that nMigen is a Python library for writing logic. When the module is *elaborated*, your code runs and nMigen outputs the corresponding logic.
33
+
An Amaranth*module* is a Python class that has inputs and outputs, and code that generates the logic for the desired functionality. Note that I didn't say code that *implements* the function. A key concept is that Amaranth is a Python library for writing logic. When the module is *elaborated*, your code runs and Amaranth outputs the corresponding logic.
33
34
34
-
You can think of the logic that nMigen writes as an integrated circuit, and the code that you write as the instructions for how to create a copy of that integrated circuit.
35
+
You can think of the logic that Amaranth writes as an integrated circuit, and the code that you write as the instructions for how to create a copy of that integrated circuit.
35
36
36
-
Modules can use other modules (*submodules*), and you can have as many copies of the module as you want. Your design has one top-level module that contains all of your other modules. When you elaborate the top-level module, nMigen outputs its logic into a file. That file can then be given to other software for synthesis on an FPGA, or for formal verification (i.e. bug hunting).
37
+
Modules can use other modules (*submodules*), and you can have as many copies of the module as you want. Your design has one top-level module that contains all of your other modules. When you elaborate the top-level module, Amaranth outputs its logic into a file. That file can then be given to other software for synthesis on an FPGA, or for formal verification (i.e. bug hunting).
37
38
38
-

39
+

39
40
40
41
You can use the [`skeleton.py`](skeleton.py) file to start. Some key features here are:
41
42
42
43
* Your class is derived from `Elaboratable`
43
44
* Your public (or visible) inputs and outputs are attributes of the class.
44
-
* The class has an `elaborate` function that gets called by nMigen to generate the logic.
45
+
* The class has an `elaborate` function that gets called by Amaranth to generate the logic.
45
46
* The class has a class-level `formal` method for verifying your logic.
46
47
* You can run your class to generate the output. This requires the `main` function in [`util.py`](util.py).
47
48
48
49
## Step 2: Create input and output signals
49
50
50
-
A `Signal` in nMigen is a wire or register with some number of bits. For example:
51
+
A `Signal` in Amaranth is a wire or register with some number of bits. For example:
51
52
52
53
```python
53
54
x = Signal(5) # creates a 5-bit signal named "x".
@@ -64,7 +65,7 @@ For this exercise, the logic is completely *combinatorial*: the outputs depend d
64
65
m.d.comb += y.eq(x)
65
66
```
66
67
67
-
This is just like writing `y = x`, except using the nMigen library, which requires using the `eq` function. That function returns a statement which can then be added to one of the domains in the module. Here, `m.d.comb` is the module's combinatorial domain.
68
+
This is just like writing `y = x`, except using the Amaranth library, which requires using the `eq` function. That function returns a statement which can then be added to one of the domains in the module. Here, `m.d.comb` is the module's combinatorial domain.
68
69
69
70
You can use constants, and arithmetic functions, too:
70
71
@@ -95,7 +96,7 @@ if x == 7:
95
96
y = z
96
97
```
97
98
98
-
The equivalent in nMigen is:
99
+
The equivalent in Amaranth is:
99
100
100
101
```python
101
102
m.d.comb += y.eq(0)
@@ -116,12 +117,12 @@ This does the same thing.
116
117
117
118

118
119
119
-
## Step 4: Make sure it compiles!
120
+
## Step 4: Make sure it compiles
120
121
121
122
You can quickly check that there aren't any syntax errors by just generating the code:
122
123
123
-
```
124
-
python3 your_file.py gen
124
+
```sh
125
+
python your_file.py gen
125
126
```
126
127
127
128
## Step 5: Write some formal verification code
@@ -180,22 +181,22 @@ If the engine cannot satisfy a cover, it will complain. This usually means that
180
181
181
182
Write the asserts and covers according to the requirements of the exercise. Compile again to make sure you haven't introduced any syntax errors.
182
183
183
-
```
184
-
python3 your_file.py gen
184
+
```sh
185
+
python your_file.py gen
185
186
```
186
187
187
188
## Step 6: Run the formal verification engine for covers
188
189
189
190
Running your code with `gen` will have it output a file `toplevel.il`. This can be run through the SymbiYosys formal verification tool. It requires a `.sby` configuration file, which I've included in `answers/e01_to_pennies.sby`.
190
191
191
-
```
192
-
python3 your_class.py gen
192
+
```sh
193
+
python your_class.py gen
193
194
sby -f answers/e01_to_pennies.sby cover
194
195
```
195
196
196
197
First, look for the cover conditions to be satisfied. These are the `Reached cover statement at...` lines below. And, if all your cover statements were satisfied, you'll get a `DONE (PASS, rc=0)` line.
@@ -236,17 +237,22 @@ The cover statements aren't found in the order you put them in your code!
236
237
237
238
Each cover statement found generates a trace where you can see the inputs and outputs, as well as some intermediate signals if they are there, using `gtkwave`:
In this case, the first trace came from the first cover statement in the exercise. The output shows `1DD`, or 477.
244
+
In this case, the first trace came from the first cover statement in the exercise. The output shows `1DD`, or 477. You can tell which cover statement the trace corresponds to by looking at the output from `sby`:
245
+
246
+
```txt
247
+
SBY 15:17:27 [answers/e01_to_pennies_cover] engine_0: ## 0:00:00 Reached cover statement at answers/to_pennies.py:45 in step 0.
Copy file name to clipboardExpand all lines: 04_signs.md
+18-18Lines changed: 18 additions & 18 deletions
Original file line number
Diff line number
Diff line change
@@ -4,8 +4,8 @@
4
4
5
5
Write a module that takes a 64-bit signed number, and negates it in three ways:
6
6
7
-
*Invert and add 1.
8
-
*Directly negate it.
7
+
*Bitwise complement (invert) it and add 1.
8
+
*Arithmetically negate it.
9
9
* Starting from the least significant bit, copy up to and including the first 1, then invert the remaining bits.
10
10
11
11
That last one seems a bit strange, but it works like this (using 8-bit numbers):
@@ -29,8 +29,8 @@ However, in 2's complement, `1000` is -8 while `0111` is 7, which means that if
29
29
x = Signal(signed(4))
30
30
y = Signal(signed(4))
31
31
m.d.comb += [
32
-
x.eq(0b1000),
33
-
y.eq(0b0111),
32
+
x.eq(0b1000),# -8
33
+
y.eq(0b0111),# +7
34
34
Assert(x < y),
35
35
]
36
36
```
@@ -41,8 +41,8 @@ To treat an unsigned (or signed) signal as signed:
41
41
x = Signal(4)
42
42
y = Signal(4)
43
43
m.d.comb += [
44
-
x.eq(0b1000),
45
-
y.eq(0b0111),
44
+
x.eq(0b1000),# 8 (or -8 if signed)
45
+
y.eq(0b0111),# 7
46
46
Assert(x.as_signed() < y.as_signed()),
47
47
]
48
48
```
@@ -55,10 +55,10 @@ Negating a signal always results in a signed signal, regardless of whether the i
55
55
x = Signal(4)
56
56
y = Signal(4)
57
57
m.d.comb += [
58
-
x.eq(0b0011),
59
-
y.eq(0b1101),
58
+
x.eq(0b0011),# 3
59
+
y.eq(0b1101),# 13
60
60
Assert(y > x),
61
-
Assert(-x < x),
61
+
Assert(-x < x),# -x is signed, so this is a signed comparison
62
62
]
63
63
```
64
64
@@ -68,8 +68,8 @@ Equality, on the other hand, does not take into account signedness. It is bit-fo
68
68
x = Signal(4)
69
69
y = Signal(4)
70
70
m.d.comb += [
71
-
x.eq(0b0011),
72
-
y.eq(0b1101),
71
+
x.eq(0b0011),# 3
72
+
y.eq(0b1101),# 13 (or -3 if signed)
73
73
Assert(y > x),
74
74
Assert(-x == y),
75
75
]
@@ -90,10 +90,12 @@ m.d.comb += [
90
90
91
91
### Library: priority encoder
92
92
93
-
A *priority encoder* is a circuit that takes an N-bit input and outputs the position of the first set bit, where "first" could mean most significant or least significant. nMigen has a coding library with a PriorityEncoder module built in, which outputs the position of the first least significant bit set.
93
+
A *priority encoder* is a circuit that takes an N-bit input and outputs the position of the first set bit, where "first" could mean first *most* significant or first *least* significant. Amaranth has a coding library with a `PriorityEncoder` module built in, which outputs the position of the first *least* significant bit set.
94
+
95
+
The encoder has three ports (attributes): `i`, the input, `o`, the output, which is the lowest bit number that is set in the input, and `n`, set if and only if the input is zero. The encoder also has a parameter, `width`, which tells it how many bits are in the input.
94
96
95
97
```python
96
-
fromnmigen.lib.coding import PriorityEncoder
98
+
fromamaranth.lib.coding import PriorityEncoder
97
99
98
100
enc = PriorityEncoder(width=8)
99
101
input= Signal(8)
@@ -107,7 +109,7 @@ m.d.comb += [
107
109
]
108
110
```
109
111
110
-
For example, the input`0101000` will result in the output being 3 and bit_set being high.
112
+
For example, setting `input` to `0101000` will result in `output` being 3 and `bit_set` being high.
111
113
112
114
### Limitations on slices
113
115
@@ -123,13 +125,11 @@ You can achieve the same effect using bit tricks. For example, `1 << y` gives yo
123
125
124
126
What does `(-1 << y) & x` give you?
125
127
126
-
-----
127
-
128
-
Interested in more bit-manipulation tricks? Check out Knuth's The Art of Computer Programming, Volume 4A, chapter 7.1.3 (Bitwise Tricks and Techniques). Hacker's Delight also contains copious bit manipulation fun.
128
+
> Interested in more bit-manipulation tricks? Check out Knuth's [The Art of Computer Programming](https://en.wikipedia.org/wiki/The_Art_of_Computer_Programming), Volume 4A, chapter 7.1.3 (Bitwise Tricks and Techniques). [Hacker's Delight](https://en.wikipedia.org/wiki/Hacker%27s_Delight) also contains copious bit manipulation fun.
129
129
130
130
-----
131
131
132
-
## What you'll do, part 2:
132
+
## What you'll do, part 2
133
133
134
134
Suppose you have a chip that can compare two 16-bit numbers, but only as unsigned numbers. It outputs whether the first is less than the second. Write such a module.
0 commit comments