Skip to content

Commit 087a289

Browse files
committed
Exercise 5
1 parent 09bb578 commit 087a289

11 files changed

+173
-0
lines changed

05_sync.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Exercise 5: Synchronicity
2+
3+
<p align="right">
4+
A connecting principle, linked to the invisible...
5+
</p>
6+
7+
------
8+
## What you'll do:
9+
10+
Create a module that counts from 1 to 9 and back to 1 again. Formally verify that the output can never be greater than 9 and can never be 0. Cover the case where the output is 3.
11+
12+
## Synchronous domains
13+
14+
Unlike combinatorial domains, synchronous domains are associated with a clock signal and a reset signal. Assignments that are added to a synchronous domain take place on one of its clock's edge, the positive edge by default.
15+
16+
The `sync` domain is the default domain that is always present:
17+
18+
```python
19+
x = Signal()
20+
m.d.sync += x.eq(~x)
21+
```
22+
23+
The above will flip `x` on every positive edge of the sync clock.
24+
25+
![Flipping the x signal, diagram](diagrams/sync_flip_diagram.png)
26+
![Flipping the x signal, waveforms](diagrams/sync_flip.png)
27+
28+
You can only drive a signal from one domain. This would result in a driver-driver conflict:
29+
30+
```python
31+
x = Signal()
32+
m.d.sync += x.eq(~x)
33+
m.d.comb += x.eq(0)
34+
```
35+
36+
## Synchronous resets
37+
38+
A signal driven from a synchronous domain gets reset when that domain's reset signal goes high and the clock's edge happens, or on circuit initialization. The reset value of the signal is set by the `reset` key when creating the signal, and defaults to 0:
39+
40+
```python
41+
x = Signal(reset=1)
42+
m.d.sync += x.eq(~x)
43+
```
44+
45+
![The x signal resets to 1, diagram](diagrams/sync_reset_diagram.png)
46+
![The x signal resets to 1, waveforms](diagrams/sync_reset.png)
47+
48+
You can prevent the signal from getting reset, except on circuit initialization, by using the key `reset_less`:
49+
50+
```python
51+
x = Signal(reset=1, reset_less=True)
52+
m.d.sync += x.eq(~x)
53+
```
54+
55+
![Resetless, diagram](diagrams/sync_resetless.png)
56+
57+
## Synchronous signals are registers
58+
59+
If a synchronous signal isn't assigned on an edge, it retains its value.
60+
61+
```python
62+
x = Signal(8)
63+
load = Signal()
64+
loadval = Signal(8)
65+
66+
with m.If(load):
67+
m.d.sync += x.eq(loadval)
68+
```
69+
70+
For the above, only if `load` is high will `x` be loaded with `loadval` on the sync clock edge.
71+
72+
![Loading, diagram](diagrams/sync_load_diagram.png)
73+
![Loading, waveforms](diagrams/sync_load.png)
74+
75+
You can think of the domain's reset signal as loading all registers in that domain with their reset values (unless they're `reset_less`).
76+
77+
## Bounded model checking
78+
79+
Up until now, we've been using bounded model checking to assert on combinatorial logic. However, with sequential logic, signals can change with time. So, bounded model checking specifies the number of time steps to take after the initial reset. This is specified in the `[options]` section of the sby file, as the value of `depth`. That's why it's called "bounded model checking", because it checks your model only up to the depth you specify.
80+
81+
This means that you will have to know how long to run your verification before you can be sure all your asserts are checked. Later we will see a more powerful technique (induction) which doesn't have such a painful requirement.
82+
83+
What exactly is "a time step"? It's a change in a clock signal. So, a full cycle of the sync clock would be two time steps.
84+
85+
-----
86+
87+
This is only true if the `multiclock` parameter in `[options]` is set to `on`. Unless you really really know what you're doing, leave it set to `on`.
88+
89+
-----
90+
91+
## The domain for Asserts and Covers
92+
93+
Unless you really really know what you're doing, keep asserts and covers in the combinatorial domain. The asserts will be checked on every time step.
94+
95+
## Your turn
96+
97+
You can use the [answers/e05_counter.sby](answers/e05_counter.sby) file to run your formal verification. It is set up for 22 time steps, or 11 cycles of the clock. This should be enough to have the counter cycle around once.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ Work in progress!
77
* [02 - The day after: more if, and switch-case](02_switch.md)
88
* [03 - Life finds a way: parts of signals](03_parts.md)
99
* [04 - Signs: signed signals](04_signs.md)
10+
* [05 - Synchronicity: synchronous signals](05_sync.md)

answers/e05_counter.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Disable pylint's "your name is too short" warning.
2+
# pylint: disable=C0103
3+
from typing import List, Tuple
4+
5+
from nmigen import Signal, Module, Elaboratable
6+
from nmigen.build import Platform
7+
from nmigen.asserts import Assume, Assert, Cover
8+
9+
from util import main
10+
11+
12+
class Counter(Elaboratable):
13+
"""Logic for the Counter module."""
14+
15+
def __init__(self):
16+
self.count = Signal(4, reset=1)
17+
18+
def elaborate(self, _: Platform) -> Module:
19+
"""Implements the logic for the Counter module."""
20+
m = Module()
21+
22+
with m.If(self.count == 9):
23+
m.d.sync += self.count.eq(1)
24+
with m.Else():
25+
m.d.sync += self.count.eq(self.count+1)
26+
27+
return m
28+
29+
@classmethod
30+
def formal(cls) -> Tuple[Module, List[Signal]]:
31+
"""Formal verification for the Counter module."""
32+
m = Module()
33+
m.submodules.c = c = cls()
34+
35+
m.d.comb += Assert((c.count >= 1) & (c.count <= 9))
36+
m.d.comb += Cover(c.count == 3)
37+
38+
return m, []
39+
40+
41+
if __name__ == "__main__":
42+
main(Counter)

answers/e05_counter.sby

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
[tasks]
2+
cover
3+
bmc
4+
5+
[options]
6+
bmc: mode bmc
7+
cover: mode cover
8+
depth 22
9+
multiclock on
10+
11+
[engines]
12+
cover: smtbmc z3
13+
bmc: smtbmc z3
14+
15+
[script]
16+
read_verilog <<END
17+
module \$dff (CLK, D, Q);
18+
parameter WIDTH = 0;
19+
parameter CLK_POLARITY = 1'b1;
20+
input CLK;
21+
input [WIDTH-1:0] D;
22+
output reg [WIDTH-1:0] Q;
23+
\$ff #(.WIDTH(WIDTH)) _TECHMAP_REPLACE_ (.D(D),.Q(Q));
24+
endmodule
25+
END
26+
design -stash dff2ff
27+
read_ilang toplevel.il
28+
proc
29+
techmap -map %dff2ff top/w:clk %co
30+
prep -top top
31+
32+
[files]
33+
toplevel.il

diagrams/sync_flip.png

25.4 KB
Loading

diagrams/sync_flip_diagram.png

24.9 KB
Loading

diagrams/sync_load.png

51.9 KB
Loading

diagrams/sync_load_diagram.png

50.9 KB
Loading

diagrams/sync_reset.png

25.3 KB
Loading

diagrams/sync_reset_diagram.png

42.1 KB
Loading

0 commit comments

Comments
 (0)