Skip to content

Commit 14564bf

Browse files
committed
Add Clash.Tutorial on Synthesize annotations
1 parent 27f6a1c commit 14564bf

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

compiler-user-guide/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- [Language](./developing-hardware/language.md)
1313
- [Prelude](./developing-hardware/prelude.md)
1414
- [Flags](./developing-hardware/flags.md)
15+
- [Controlling HDL generation](./developing-hardware/annotations.md)
1516
- [User-defined primitives](./developing-hardware/primitives.md)
1617
- [Limitations of the compiler](./developing-hardware/limitations.md)
1718
- [Hacking on Clash](./hacking-on-clash/index.md)
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Synthesize annotations: controlling VHDL/(System)Verilog generation.
2+
3+
`Synthesize` annotations allow us to control hierarchy and naming aspects of the Clash compiler, specifically, they allow us to:
4+
5+
* Assign names to entities (VHDL) / modules ((System)Verilog), and their ports.
6+
* Put generated HDL files of a logical (sub)entity in their own directory.
7+
* Use cached versions of generated HDL, i.e., prevent recompilation of (sub)entities that have not changed since the last run.
8+
Caching is based on a `.manifest` which is generated alongside the HDL; deleting this file means deleting the cache; changing this file will result in *undefined* behavior.
9+
10+
Functions with a 'Synthesize' annotation must adhere to the following restrictions:
11+
12+
* Although functions with a `Synthesize` annotation can of course depend on functions with another `Synthesize` annotation, they must not be mutually recursive.
13+
* Functions with a `Synthesize` annotation must be completely *monomorphic* and *first-order*, and cannot have any *non-representable* arguments or result.
14+
15+
Also take the following into account when using `Synthesize` annotations.
16+
17+
* The Clash compiler is based on the GHC Haskell compiler, and the GHC machinery does not understand `Synthesize` annotations and it might subsequently decide to inline those functions.
18+
You should therefor also add a `{-# OPAQUE f #-}` pragma to the functions which you give a `Synthesize` functions.
19+
* Functions with a `Synthesize` annotation will not be specialized on constants.
20+
21+
Finally, the root module, the module which you pass as an argument to the Clash compiler must either have:
22+
23+
* A function with a `Synthesize` annotation.
24+
* A function called *topEntity*.
25+
26+
You apply `Synthesize` annotations to functions using an `ANN` pragma:
27+
28+
``` haskell
29+
{-# OPAQUE topEntity #-}
30+
{-# ANN topEntity (Synthesize {t_name = ..., ... }) #-}
31+
topEntity x = ...
32+
```
33+
34+
For example, given the following specification:
35+
36+
``` haskell
37+
module Blinker where
38+
39+
import Clash.Signal
40+
import Clash.Prelude
41+
import Clash.Intel.ClockGen
42+
43+
createDomain vSystem{vName="DomInput", vPeriod=20000, vResetPolarity=ActiveLow}
44+
createDomain vSystem{vName="Dom100", vPeriod=10000}
45+
46+
topEntity
47+
:: Clock DomInput
48+
-> Reset DomInput
49+
-> Signal Dom100 Bit
50+
-> Signal Dom100 (BitVector 8)
51+
topEntity clk rst =
52+
exposeClockResetEnable (mealy blinkerT (1,False,0) . isRising 1) pllOut pllRst enableGen
53+
where
54+
(pllOut,pllRst) = altpllSync clk rst
55+
56+
blinkerT (leds,mode,cntr) key1R = ((ledsN,modeN,cntrN),leds)
57+
where
58+
-- clock frequency = 100e6 (100 MHz)
59+
-- led update rate = 333e-3 (every 333ms)
60+
cnt_max = maxBound :: Index 33300000 -- 100e6 * 333e-3
61+
62+
cntrN | cntr == cnt_max = 0
63+
| otherwise = cntr + 1
64+
65+
modeN | key1R = not mode
66+
| otherwise = mode
67+
68+
ledsN | cntr == 0 = if mode then complement leds
69+
else rotateL leds 1
70+
| otherwise = leds
71+
```
72+
73+
The Clash compiler will normally generate the following `topentity.vhdl` file:
74+
75+
``` vhdl
76+
-- Automatically generated VHDL-93
77+
library IEEE;
78+
use IEEE.STD_LOGIC_1164.ALL;
79+
use IEEE.NUMERIC_STD.ALL;
80+
use IEEE.MATH_REAL.ALL;
81+
use std.textio.all;
82+
use work.all;
83+
use work.Blinker_topEntity_types.all;
84+
85+
entity topEntity is
86+
port(-- clock
87+
clk : in Blinker_topEntity_types.clk_DomInput;
88+
-- reset
89+
rst : in Blinker_topEntity_types.rst_DomInput;
90+
eta : in std_logic;
91+
result : out std_logic_vector(7 downto 0));
92+
end;
93+
94+
architecture structural of topEntity is
95+
...
96+
end;
97+
```
98+
99+
However, if we add the following `Synthesize` annotation in the file:
100+
101+
``` haskell
102+
{-# OPAQUE topEntity #-}
103+
{-# ANN topEntity
104+
(Synthesize
105+
{ t_name = "blinker"
106+
, t_inputs = [PortName "CLOCK_50", PortName "KEY0", PortName "KEY1"]
107+
, t_output = PortName "LED"
108+
}) #-}
109+
```
110+
111+
The Clash compiler will generate the following `blinker.vhdl` file instead:
112+
113+
``` vhdl
114+
-- Automatically generated VHDL-93
115+
library IEEE;
116+
use IEEE.STD_LOGIC_1164.ALL;
117+
use IEEE.NUMERIC_STD.ALL;
118+
use IEEE.MATH_REAL.ALL;
119+
use std.textio.all;
120+
use work.all;
121+
use work.blinker_types.all;
122+
123+
entity blinker is
124+
port(-- clock
125+
CLOCK_50 : in blinker_types.clk_DomInput;
126+
-- reset
127+
KEY0 : in blinker_types.rst_DomInput;
128+
KEY1 : in std_logic;
129+
LED : out std_logic_vector(7 downto 0));
130+
end;
131+
132+
architecture structural of blinker is
133+
...
134+
end;
135+
```
136+
137+
Where we now have:
138+
139+
* A top-level component that is called `blinker`.
140+
* Inputs and outputs that have a *user*-chosen name: `CLOCK_50`, `KEY0`, `KEY1`, `LED`, etc.
141+
142+
See the documentation of `Synthesize` for the meaning of all its fields.

0 commit comments

Comments
 (0)