clash-prelude-0.99: CAES Language for Synchronous Hardware - Prelude library

Copyright(C) 2015-2016 University of Twente
2017 Google Inc.
LicenseBSD2 (see the file LICENSE)
MaintainerChristiaan Baaij <christiaan.baaij@gmail.com>
Safe HaskellSafe
LanguageHaskell2010
Extensions
  • DeriveDataTypeable
  • DeriveGeneric

Clash.Annotations.TopEntity

Contents

Description

TopEntity annotations allow us to control hierarchy and naming aspects of the CλaSH compiler. We have the Synthesize and TestBench annotation.

Synthesize annotation

The Synthesize annotation allows us to:

  • Assign names to entities (VHDL) / modules ((System)Verilog), and their ports.
  • Put generated HDL files of a logical (sub)entity in their own directory.
  • Use cached versions of generated HDL, i.e., prevent recompilation of (sub)entities that have not changed since the last run. 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 behaviour.

Functions with a Synthesize annotation do must adhere to the following restrictions:

  • Although functions with a Synthesize annotation can of course depend on functions with another Synthesize annotation, they must not be mutually recursive.
  • Functions with a Synthesize annotation must be completely monomorphic and first-order, and cannot have any non-representable arguments or result.

Also take the following into account when using Synthesize annotations.

  • The CλaSH 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. You should therefor also add a {-# NOINLINE f #-} pragma to the functions which you give a Synthesize functions.
  • Functions with a Synthesize annotation will not be specialised on constants.

Finally, the root module, the module which you pass as an argument to the CλaSH compiler must either have:

  • A function with a Synthesize annotation.
  • A function called topEntity.

You apply Synthesize annotations to functions using an ANN pragma:

{-# ANN f (Synthesize {t_name = ..., ...  }) #-}
f x = ...

For example, given the following specification:

module Blinker where

import Clash.Prelude
import Clash.Intel.ClockGen

type Dom50 = Dom "System" 20000

topEntity
  :: Clock Dom50 Source
  -> Reset Dom50 Asynchronous
  -> Signal Dom50 Bit
  -> Signal Dom50 (BitVector 8)
topEntity clk rst key1 =
    let  (pllOut,pllStable) = altpll (SSymbol @ "altpll50") clk rst
         rstSync            = resetSynchronizer pllOut (unsafeToAsyncReset pllStable)
    in   exposeClockReset leds pllOut rstSync
  where
    key1R  = isRising 1 key1
    leds   = mealy blinkerT (1,False,0) key1R

blinkerT (leds,mode,cntr) key1R = ((leds',mode',cntr'),leds)
  where
    -- clock frequency = 50e6  (50 MHz)
    -- led update rate = 333e-3 (every 333ms)
    cnt_max = 16650000 -- 50e6 * 333e-3

    cntr' | cntr == cnt_max = 0
          | otherwise       = cntr + 1

    mode' | key1R     = not mode
          | otherwise = mode

    leds' | cntr == 0 = if mode then complement leds
                                else rotateL leds 1
          | otherwise = leds

The CλaSH compiler would normally generate the following blinker_topentity.vhdl file:

-- Automatically generated VHDL-93
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
use std.textio.all;
use work.all;
use work.blinker_types.all;

entity blinker_topentity is
  port(-- clock
       input_0  : in std_logic;
       -- asynchronous reset: active high
       input_1  : in std_logic;
       input_2  : in std_logic_vector(0 downto 0);
       output_0 : out std_logic_vector(7 downto 0));
end;

architecture structural of blinker_topentity is
begin
  blinker_topentity_0_inst : entity blinker_topentity_0
    port map
      (clk    => input_0
      ,rst    => input_1
      ,key1   => input_2
      ,result => output_0);
end;

However, if we add the following Synthesize annotation in the file:

{-# ANN topEntity
  (Synthesize
    { t_name   = "blinker"
    , t_inputs = [ PortName "CLOCK_50"
                 , PortName "KEY0"
                 , PortName "KEY1" ]
    , t_output = PortName "LED"
    }) #-}

The CλaSH compiler will generate the following blinker.vhdl file instead:

-- Automatically generated VHDL-93
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
use std.textio.all;
use work.all;
use work.blinker_types.all;

entity blinker is
  port(-- clock
       CLOCK_50 : in std_logic;
       -- asynchronous reset: active high
       KEY0     : in std_logic;
       KEY1     : in std_logic_vector(0 downto 0);
       LED      : out std_logic_vector(7 downto 0));
end;

architecture structural of blinker is
begin
  blinker_topentity_inst : entity blinker_topentity
    port map
      (clk    => CLOCK_50
      ,rst    => KEY0
      ,key1   => KEY1
      ,result => LED);
end;

Where we now have:

  • A top-level component that is called blinker.
  • Inputs and outputs that have a user-chosen name: CLOCK_50, KEY0, KEY1, LED, etc.

See the documentation of Synthesize for the meaning of all its fields.

TestBench annotation

Tell what binder is the TestBench for a Synthesize-annotated binder.

So in the following example, f has a Synthesize annotation, and g is the HDL test bench for f.

f :: Bool -> Bool
f = ...
{-# ANN f (defSyn "f") #-}
{-# ANN f (TestBench 'g) #-}

g :: Signal Bool
g = ...

Synopsis

Data types

data TopEntity Source #

TopEntity annotation

Constructors

Synthesize

Instruct the Clash compiler to use this top-level function as a separately synthesizable component.

Fields

  • t_name :: String

    The name the top-level component should have, put in a correspondingly named file.

  • t_inputs :: [PortName]

    List of names that are assigned in-order to the inputs of the component.

  • t_output :: PortName

    Name assigned in-order to the outputs of the component. As a Haskell function can only truly return a single value -- with multiple values "wrapped" by a tuple -- this field is not a list, but a single PortName. Use PortProduct to give names to the individual components of the output tuple.

TestBench Name

Tell what binder is the TestBench for a Synthesize-annotated binder.

So in the following example, f has a Synthesize annotation, and g is the HDL test bench for f.

f :: Bool -> Bool
f = ...
{-# ANN f (defSyn "f") #-}
{-# ANN f (TestBench 'g) #-}

g :: Signal Bool
g = ...

Instances

Data TopEntity Source # 

Methods

gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> TopEntity -> c TopEntity #

gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c TopEntity #

toConstr :: TopEntity -> Constr #

dataTypeOf :: TopEntity -> DataType #

dataCast1 :: Typeable (* -> *) t => (forall d. Data d => c (t d)) -> Maybe (c TopEntity) #

dataCast2 :: Typeable (* -> * -> *) t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c TopEntity) #

gmapT :: (forall b. Data b => b -> b) -> TopEntity -> TopEntity #

gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> TopEntity -> r #

gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> TopEntity -> r #

gmapQ :: (forall d. Data d => d -> u) -> TopEntity -> [u] #

gmapQi :: Int -> (forall d. Data d => d -> u) -> TopEntity -> u #

gmapM :: Monad m => (forall d. Data d => d -> m d) -> TopEntity -> m TopEntity #

gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> TopEntity -> m TopEntity #

gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> TopEntity -> m TopEntity #

Show TopEntity Source # 
Generic TopEntity Source # 

Associated Types

type Rep TopEntity :: * -> * #

type Rep TopEntity Source # 

data PortName Source #

Give port names for arguments/results.

Give a data type and function:

data T = MkT Int Bool

{-# ANN topEntity (defSyn "f") #-}
f :: Int -> T -> (T,Bool)
f a b = ...

Clash would normally generate the following VHDL entity:

entity f is
  port(input_0      : in signed(63 downto 0);
       input_1_0    : in signed(63 downto 0);
       input_1_1    : in boolean;
       output_0_0_0 : out signed(63 downto 0);
       output_0_0_1 : out boolean;
       output_0_1   : out boolean);
end;

However, we can change this by using PortNames. So by:

{-# ANN topEntity
   (Synthesize
      { t_name   = "f"
      , t_inputs = [ PortName "a"
                   , PortName "b" ]
      , t_output = PortName "res" }) #-}
f :: Int -> T -> (T,Bool)
f a b = ...

we get:

entity f is
  port(a   : in signed(63 downto 0);
       b   : in f_types.t;
       res : out f_types.tup2);
end;

If we want to name fields for tuples/records we have to use PortProduct

{-# ANN topEntity
   (Synthesize
      { t_name   = "f"
      , t_inputs = [ PortName "a"
                   , PortProduct "" [ PortName "b", PortName "c" ] ]
      , t_output = PortProduct "res" [PortName "q"] }) #-}
f :: Int -> T -> (T,Bool)
f a b = ...

So that we get:

entity f is
  port(a     : in signed(63 downto 0);
       b     : in signed(63 downto 0);
       c     : in boolean;
       q     : out f_types.t;
       res_1 : out boolean);
end;

Notice how we didn't name the second field of the result, and the second output port got PortProduct name, "res", as a prefix for its name.

Constructors

PortName String

You want a port, with the given name, for the entire argument/type

You can use an empty String ,"" , in case you want an auto-generated name.

PortProduct String [PortName]

You want to assign ports to fields of a product argument/type

The first argument of PortProduct is the name of:

  1. The signal/wire to which the individual ports are aggregated.
  2. The prefix for any unnamed ports below the PortProduct

You can use an empty String ,"" , in case you want an auto-generated name.

Instances

Data PortName Source # 

Methods

gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> PortName -> c PortName #

gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c PortName #

toConstr :: PortName -> Constr #

dataTypeOf :: PortName -> DataType #

dataCast1 :: Typeable (* -> *) t => (forall d. Data d => c (t d)) -> Maybe (c PortName) #

dataCast2 :: Typeable (* -> * -> *) t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c PortName) #

gmapT :: (forall b. Data b => b -> b) -> PortName -> PortName #

gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> PortName -> r #

gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> PortName -> r #

gmapQ :: (forall d. Data d => d -> u) -> PortName -> [u] #

gmapQi :: Int -> (forall d. Data d => d -> u) -> PortName -> u #

gmapM :: Monad m => (forall d. Data d => d -> m d) -> PortName -> m PortName #

gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> PortName -> m PortName #

gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> PortName -> m PortName #

Show PortName Source # 
Generic PortName Source # 

Associated Types

type Rep PortName :: * -> * #

Methods

from :: PortName -> Rep PortName x #

to :: Rep PortName x -> PortName #

type Rep PortName Source # 

Convenience functions

defSyn :: String -> TopEntity Source #

Default Synthesize annotation which has no specified names for the input and output ports.

>>> defSyn "foo"
Synthesize {t_name = "foo", t_inputs = [], t_output = PortName ""}