From 612e72177321ead79248a8dcf49b783f8153d967 Mon Sep 17 00:00:00 2001
From: Matthijs Kooijman
Date: Wed, 20 May 2009 11:48:03 +0200
Subject: [PATCH] Add the latest version of the flat functions document.
This document is unfinished and will probably be deprecated in the future,
but is still added to preserve history.
---
FlatFunctions.lout | 927 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 927 insertions(+)
create mode 100644 FlatFunctions.lout
diff --git a/FlatFunctions.lout b/FlatFunctions.lout
new file mode 100644
index 0000000..36eb137
--- /dev/null
+++ b/FlatFunctions.lout
@@ -0,0 +1,927 @@
+@SysInclude { report }
+@SysInclude { tbl }
+@SysInclude { haskell }
+def @SectionLink right sec {sec @CrossLink {section @NumberOf sec}}
+
+@Report
+ @InitialSpace {tex} # Treat multiple spaces as one
+ @Title {From Haskell to VHDL}
+ @Author {Matthijs Kooijman}
+ @Institution {University of Twente}
+ @DateLine {Yes}
+ @CoverSheet {No}
+# @OptimizePages {Yes}
+//
+@Section
+ @Title {Introduction}
+@Begin
+ @LP The aim of this project is to design a structural hardware
+ description language, embedded in the functional language Haskell.
+ This means that a program written in (a subset of) Haskell will be
+ translated into hardware, using a VHDL description. The original
+ Haskell program is a structural description, meaning that the
+ programmer explicitly specifies hardware and area vs time trade-offs
+ are already made explicit in the description and the translator is not
+ required to do any scheduling.
+
+ @LP The compilation process is divided roughly into four steps:
+
+ @LeftList
+ @ParagraphItem { Parsing, typechecking and simplifying the Haskell
+ source(s). By using the API that GHC makes available, this step is
+ fairly easy. We can feed a Haskell source file into GHC, which then
+ performs all the neccesary steps to create a @I Core module, which
+ is a simplified representation of the Haskell source file.}
+
+ @ParagraphItem { Flattening the function that is the top of the design, and
+ (recursively) any functions it requires. Flattening is the process
+ of removing nesting from expressions in the functions and making the
+ routes values take through an expression more explicit. This results
+ in a flat list of flat functions.}
+
+ @ParagraphItem { Processing the flattened functions. This step does
+ transformations such as deciding which function arguments will be
+ used as state values, decide names for signals, etc.}
+
+ @ParagraphItem { Translate the flat functions generated so far into VHDL.
+ This is a fairly direct translation, all information should be
+ present by now.}
+ @EndList
+
+ @LP In this document we will focus on the second step. The first step
+ is completely handled by GHC and results in a Core module. The
+ representation GHC uses for the Core language is the subject of
+ @SectionLink {Core}. The second step translates
+ a Core module to a set of flat functions. The representation of a flat
+ function is the subject of @SectionLink {FlatFunctions}. The fourth
+ step is also partly discussed in this section. Finally, the actual
+ flattening that happens in the second step is the subject of
+ @SectionLink {Flattening}. The third step remains undiscussed for now.
+@End @Section
+
+@Section
+ @Title { The Core language }
+ @Tag {Core}
+@Begin
+ @LP GHC uses an internal representation for Haskell programs called @I
+ {Core} . Core is essentially a small programming language, but we
+ focus on the representation GHC uses. Inside GHC a Core program is
+ stored using a number of custom data types, which we will describe
+ here.
+
+ @LP The descriptions given here are therefore very similar to Haskell
+ source, and can usually be interpreted as such. To simplify this
+ discussion, some details that are irrelevant from our point of view
+ are left out, but each of these simplifications is noted in the text.
+
+ @LP There are two main aspects of Core: Its typing system and its
+ bindings and expression structure.
+
+ @LP So far, I've focussed on the expression signature without looking closely at
+ the typing structure, except where needed. Thus, I will describe the
+ expression structure a bit here and provide examples to show the relation with
+ Haskell source programs.
+
+@BeginSubSections
+@SubSection
+ @Title { Core bindings }
+@Begin
+@LP A central role in the core language is played by the @I binding. A binding binds
+one or more expressions to one or more identifiers (@I binders). A core program is
+essentially a set of bindings, each of which bind to an identifier on global
+scope (i.e., you get one binding for each function that is defined).
+
+The @I CoreBind type is defined as follows:
+
+@ID @Haskell {
+ NonRec CoreBndr CoreExpr
+ | Rec [(CoreBndr, CoreExpr)]
+}
+
+@LP The @I NonRec constructor is the simplest: It binds a single expression to a
+single binder. The @I Rec constructors takes a list of binder, expression
+pairs, and binds each expression to the corresponding binder. These bindings
+happen on the same scope level, meaning that each expression can use any of
+the (other) bindings, creating mutually recursive references.
+
+@LP When a program is initially compiled to Core, it consists of a single @I Rec
+binding, containing all the bindings in the original haskell source. These
+bindings are allowed to reference each other, possibly recursively, but might
+not. One of the Core simplification passes splits out this large @I Rec
+constructor into as much small @I Recs and @I NonRecs as possible.
+
+@LP The @I CoreBnder type is used in @I Let expressions as well, see below.
+@End @SubSection
+
+@SubSection
+ @Title { Core expressions }
+@Begin
+@LP The main expression type is @I CoreExpr, @FootNote {In reality, @I CoreExpr
+is an alias for @I {Expr CoreBndr}, but different types of binders are only
+used by GHC internally.} which has the following
+constructors @FootNote{The @I Note constructor was omitted, since it only
+serves as annotation and does not influence semantics of the expression.}.
+
+@DP
+@Tbl
+ aformat { @Cell @I A | @Cell B }
+{
+ @Rowa
+ A {Var Id}
+ B {A variable reference}
+ @Rowa
+ A {Lit Literal}
+ B {A literal value}
+ @Rowa
+ A {App CoreExpr CoreExpr}
+ B {An application. The first expression is the function or data
+ constructor to apply, the second expression is the argument to apply
+ the function to. Function application always applies a single
+ argument, for functions with multiple arguments application is applied
+ recursively.}
+ @Rowa
+ A {Lam CoreBndr CoreExpr}
+ B {A lambda expression. When this expression is applied to some argument,
+ the argument is bound to the binder given and the result is the
+ given expression. Or, slightly more formal, the result is the given
+ expression with all @I Var expressions corresponding to the binder
+ replaced with the argument the entire lambda is applied to.}
+ @Rowa
+ A {Let CoreBind CoreExpr}
+ B {
+ A let expression. The given bind is one or more binder, expression
+ pairs. Each expression is evaluated and bound to the corresponding
+ binder. The result is of the entire let expression is then the given
+ expression.
+
+ @DP Or, slightly more formal, evaluates all expressions inside the given bind and
+ returns the given expression with all @I Var expressions corresponding to
+ the binders in the given bind replaced by their respective evaluated
+ expressions.
+ }
+ @Rowa
+ A {
+ Case CoreExpr [CoreAlt]
+ @FootNote {In reality, there are also another Type and CoreBndr
+ arguments. However, the Type argument only contains the type of the
+ entire expression, and thus is redundant and not useful for our purpose.
+ The CoreBndr is a binder to which the scrutinee is bound, but seems
+ this is only used for forcing evaluation of a term. Therefore, these two
+ are left out in this discussion.}
+ }
+ B {
+ A case expression, which can be used to do conditional evaluation and
+ data type ``unpacking''. The given expression (``scrutinee'') is
+ evaluated, bound to the given binder and depending on its value on of
+ the CoreAlts is chosen whose expression is returned.
+
+ @DP A @I CoreAlt is a three tuple describing the alternative:
+
+ @IndentedDisplay @I {(AltCon, [CoreBndr], CoreExpr)}
+
+ @DP Here, the @I AltCon is an alternative constructor which can matched
+ to the scrutinee. An alternative constructor can be one of the default
+ alternative (which matches any value not matched by other alternative
+ constructors in the list), a literal (which matches if the scrutinee
+ evaluates to a literal or a data constructor, which matches if the value
+ is constructed using that specific data constructor.
+
+ @DP In the last case, when a data constructor is used for the
+ alternative, the arguments to the data constructor are extracted from
+ the scrutinee and bound to the corresponding binders (from the second
+ element of the @I CoreAlt tuple).
+
+ @DP The last element from the tuple is the expression to return, with
+ the extra bindings in effect.
+ }
+ @Rowa
+ A {
+ Cast CoreExpr Type
+ }
+ B {
+ Cast the given expression to the given type. This plays an import role
+ when working with @F newtype declarations and type classes. The exact
+ workings of this expression are a bit shady, but for now we can get away
+ with ignore it.
+ }
+ @Rowa
+ A {
+ Type Type
+ }
+ B {
+ This expression turns a type into an expression, so it can be applied to
+ functions to implement polymorphic functions and type classes. It cannot
+ be used anywhere else.
+
+ Again, we can can get away with ignore this expression for now.
+ }
+}
+@DP
+@BeginSubSubSections
+ @SubSubSection
+ @Title {Examples}
+ @Tag {CoreExamples}
+ @Begin
+
+ @LP To get a clearer example of how Haskell programs translate to the Core
+ language, a number of programs are given in both Haskell syntax and the
+ equivalent core representation. These are all simple examples, focused on
+ showing a particular aspect of the Core language.
+
+ @LP In these examples, we will use the following data type. Since most of the
+ primitive datatypes and operators are involved in some kind of
+ polymorphism (Even the + operator is tricky), those are less useful for
+ simple examples. Using the following @I Bit datatype will also make these
+ examples more appropriate for our goals.
+
+ @ID @Haskell {
+ data Bit = High | Low
+ }
+
+ @Display @Heading {Fixed value}
+ @LP This is a trivial example which shows a simple data constructor without
+ arguments bound to the binders ``high'' and ``low''. Note that in
+ reality, the binder and @I Id values in @I Var nodes include some extra
+ info about the module it is defined in and what kind of identifier it
+ is, but that is left out here for simplicity.
+
+ @CNP @ID @Example
+ @Title {Haskell code}
+ {
+ @LP @Haskell {
+high = High
+low = Low}}
+
+ @LP This haskell source results in the following list of @I
+ CoreBndrs.
+
+ @CNP @ID @Example
+ @Title {Core Structure}
+ {
+ @LP @Haskell {
+[ NonRec ``high'' (Var High)
+, NonRec ``low'; (Var Low)]}}
+
+ @LP In subsequent examples I will leave out the @I NonRec constructor and
+ binder name and just show the resulting @I CoreExpr.
+
+ @Display @Heading {Function arguments}
+ This example shows a simple function that simply returns its argument.
+
+ @CNP @ID @Example
+ @Title {Haskell code}
+ {
+ @LP @Haskell {
+wire :: Bit -> Bit
+wire a = a}}
+
+ @CNP @ID @Example
+ @Title {Core Structure}
+ {
+ @LP @Haskell {
+Lam a (Var a)}}
+
+ @Display @Heading { Pattern matching }
+ This example shows a function with a single argument which uses
+ pattern matching, which results in a simple case statement.
+
+ @CNP @ID @Example
+ @Title {Haskell Code}
+ {
+ @LP @Haskell {
+inv :: Bit -> Bit
+inv High = Low
+inv Low = High}}
+
+ @ID @Example
+ @Title {Core Structure}
+ {
+ @LP @Haskell {
+Lam ds (Case
+ (Var ds)
+ [
+ (High, [], (Var Low)),
+ (Low, [], (Var High))
+ ]
+)}}
+
+ @LP Note that in the tuples describing the alternatives, the first element
+ is a data constructor directly, while the third argument is an expression
+ referring to a dataconstructor (henc the Var constructor).
+
+ @Display @Heading {Function application}
+ This example shows a function that applies other functions (with one and
+ two arguments) to its own arguments to do its work. It also illustrates
+ functions accepting multiple arguments.
+
+ @LP For this example we assume there is a function @I nand available to
+ returns the negated and of its two arguments.
+
+ @CNP @ID @Example
+ @Title {Haskell code}
+ {
+ @LP @Haskell {
+or :: Bit -> Bit -> Bit
+or a b = inv (nand a b)}}
+
+ @CNP @ID @Example
+ @Title {Core structure}
+ {
+ @LP @Haskell {
+Lam a (Lam b (App
+ (Var inv)
+ (App
+ (App
+ (Var nand)
+ (Var a))
+ (Var b)
+ )
+))}}
+
+ @Display @Heading {Tuples}
+ This example shows a function that accepts a tuple of two values and
+ returns a tuple of two values again.
+
+ @LP This example assumes the availability of two functions @I and and
+ @I xor, with obvious meaning.
+
+ @CNP @ID @Example
+ @Title {Haskell code}
+ {
+ @LP @Haskell {
+-- Accepts two values and returns the sum and carry out respectively.
+half_adder :: (Bit, Bit) -> (Bit, Bit)
+half_adder (a, b) =
+ (and a b, xor a b)}}
+
+ @LP Since the core structure corresponding to this (still pretty trivial)
+ function will be quite complex already, we will first show the desugared and
+ simplified version of the function, to which the core structure directly
+ corresponds.
+
+ @CNP @ID @Example
+ @Title {Simplified haskell code}
+ {
+ @LP @Haskell {
+ half_adder = \ds ->
+ Case ds of
+ (a, b) ->
+ (,) (and a b) (xor a b)
+}}
+ @CNP @ID @Example
+ @Title {Core structure}
+ {
+ @LP @Haskell
+ {
+Lam ds (Case (Var ds)
+ [(
+ (,),
+ [a, b],
+ (App
+ (App
+ (Var (,))
+ (App
+ (App
+ (Var and)
+ (Var a)
+ )
+ (Var b)
+ )
+ )
+ (App
+ (App
+ (Var xor)
+ (Var a)
+ )
+ (Var b)
+ )
+ )
+ )]
+)}}
+
+
+ @LP Note the (,) in this example, which is the data constructor for a
+ two-tuple. It can be passed two values and will construct a tuple from
+ them. @FootNote {In reality, this dataconstructor takes @I four arguments,
+ namely the types of each of the elements and then the elements themselves.
+ This is because the two-tuple type really takes type arguments, which are
+ made explicit on every call to its data constructors.}
+
+ @Display @Heading {Let expressions}
+ This example shows a function that uses a let expression to use a value
+ twice.
+
+ @CNP @ID @Example
+ @Title {Haskell code}
+ {
+ @LP @Haskell {
+f :: Bit -> (Bit, Bit)
+f a =
+ (x, and a x)
+ where
+ x = xor a a}}
+
+ @CNP @ID @Example
+ @Title {Simplified haskell code}
+ {
+ @LP @Haskell {
+ f = \a ->
+ let x = xor a a in
+ (,) x (and a x)
+}}
+ @CNP @ID @Example
+ @Title {Core structure}
+ {
+ @LP @Haskell
+ {
+Lam a (Let
+ [(NonRec
+ x
+ (App
+ (App
+ (Var xor)
+ (Var a)
+ )
+ (Var a)
+ )
+ )]
+ (App
+ (App
+ (Var (,))
+ (Var x)
+ )
+ (App
+ (App
+ (Var and)
+ (Var a)
+ )
+ (Var x)
+ )
+ )
+)}}
+
+ @Display @Heading {Pattern matching}
+ This example shows a multiplexer, i.e., a function that selects one
+ of two inputs, based on a third input. This illustrates how pattern
+ matching is translated into core and can be used to create
+ conditional signal connections.
+
+ @CNP @ID @Example
+ @Title {Haskell code}
+ {
+ @LP @Haskell
+ {
+mux2 :: Bit -> (Bit, Bit) -> Bit
+mux2 Low (a, b) = a
+mux2 High (a, b) = b}}
+
+ @CNP @ID @Example
+ @Title {Simplified haskell code}
+ {
+ @LP @Haskell {
+ mux2 = \ds ds1 -> case ds of
+ Low -> case ds1 of (a, b) -> a
+ High -> case ds1 of (a, b) -> b}}
+
+ @CNP @ID @Example
+ @Title {Core structure}
+ {
+ @LP @Haskell
+ {
+Lam ds (Lam ds1 (Case (Var ds)
+ [(
+ Low,
+ [],
+ (Case (Var ds1)
+ [(
+ (,)
+ [a, b],
+ (Var a)
+ )]
+ )
+ ),(
+ High,
+ [],
+ (Case (Var ds1)
+ [(
+ (,)
+ [a, b],
+ (Var b)
+ )]
+ )
+ )]
+))}}
+ @End @SubSubSection
+@EndSubSubSections
+@End @SubSection
+@EndSubSections
+@End @Section
+
+@Section
+ @Title {Flat Functions}
+ @Tag {FlatFunctions}
+@Begin
+ @LP As an intermediate between Core and VHDL, we use "flat" functions (the name
+ is open for suggestions). A flat function is a simplified version of the
+ function, which defines a number of signals (corresponding to single haskell
+ values) which can be used in a number of places and are defined to a single
+ value. A single value in this case means something that will probably result
+ in a single VHDL signal or port, so a 2-tuple results in two signals, for
+ example.
+
+ @LP The function is flat, because there are no directly nested expressions. A
+ function application can only use signals, not nested applications or other
+ expressions. These signals can of course be defined as the result of an
+ expression or function application, so the original expressions can still be
+ expressed.
+
+ @LP A flat function is defined as a single @I FlatFunction constructor with
+ the following fields @FootNote {The implementation additionally has a @I
+ sigs fields listing all the signals, but since that list is directly
+ deducable from the @I args and @I defs fields, it is left out here}:
+
+ @DP
+ @Tbl
+ aformat { @Cell @I A | @Cell B }
+ {
+ @Rowa
+ A {args :: [SignalMap]}
+ B {A list that maps each argument to this function to a (number of)
+ signal(s). In other words, this field specifies to which signals the
+ function arguments should be assigned when this function is called.}
+ @Rowa
+ A {res :: SignalMap}
+ B {A map that maps the return value to a (number of) signal(s).
+ signal(s). In other words, this field specifies which signals contain
+ the function result when this function is called.}
+ @Rowa
+ A {defs :: [SignalDef]}
+ B {Definitions for each of the signals in the function. Each @I SignalDef
+ defines one ore more signals, and each signal should be defined exactly
+ once. Note that the signals in @I args don't have a @I SignalDef entry.}
+ }
+
+ @LP The type @I Signal plays a central role in a flat function. It
+ identifies a signal. It's exact representation is not that important (the
+ current implementation uses an Int), as long as it can be compared and
+ unique new @I Signals can be generated.
+
+ @LP The @I SignalMap type has the following two constructors:
+
+ @ID @Haskell {
+Single Signal
+| Tuple [SignalMap]
+ }
+
+ @LP This means we can support any type that can be translated to a signal
+ directly (currently only @I Bit and @I Bool) and tuples containing those
+ types. A signal map allows us to decompose a Haskell value into individual
+ pieces that can be used in the resulting VHDL.
+
+ @LP The @I SignalDef type is slightly more complex. For one or more signals,
+ it specifies how they are defined. In the resulting hardware, these can be
+ mapped to simple signal assignments or port mappings on component
+ instantiations. The type has the following constructors:
+
+ @DP @Tbl
+ aformat { @Cell @I A | @Cell B }
+ {
+ @Rowa
+ A {lines @Break {
+FApp
+ func :: HsFunction @FootNote {In the current implementation, this
+ value can also be a representation of a higher order function.
+ However, since this is not functional yet, it is left out of this
+ discussion.}
+ args :: [SignalMap]
+ res :: SignalMap
+ }}
+ B {A function application. The function that is called is identified by
+ the given @I HsFunction, and passed the values in the given
+ @I SignalMaps as arguments. The result of the function will be
+ assigned to the @I SignalMap in @I res.}
+ @Rowa
+ A {lines @Break {
+CondDef
+ cond :: Signal
+ true :: Signal
+ false :: Signal
+ res :: Signal
+ }}
+ B {A conditional signal definition. If the signal pointed to by @I cond
+ is True, @I true is assigned to @I res, else @I false is assigned to
+ @I res. The @I cond signal must thus be of type @I Bool.
+
+ This keeps conditional assignment very simple, all complexity in the
+ conditions will be put into @I SignalExpr in @I UncondDef. }
+ @Rowa
+ A {lines @Break {
+UncondDef
+ src :: SignalExpr
+ dst :: Signal}}
+ B {Unconditional signal definition. In the most basic form (assigning a
+ simple signal to another signal) this is not often useful (only in
+ specific cases where two signals cannot be merged, when assigning an
+ output port to an input port for example).
+
+ The more interesting use for unconditional definition is when the
+ @I SignalExpr is more complicated and allows for more complexity. }
+ }
+
+ @LD @Heading {Identifying functions}
+ @LP To identify a function to call we use the @I HsFunction type. This
+ type is used for two purposes. First, it contains the name of the
+ function to call, so the corresponding @I CoreBndr and @I CoreExpr
+ can be found in the current module. Secondly, an @I HsFunction
+ contains information on the use of each argument and the return
+ value. In particular, it stores for each (part of an) argument or
+ return value if it should become an input or output Port, or a state
+ internal to the called function.
+
+ @LP The @I HsFunction type looks as follows:
+
+ @ID @Haskell {
+HsFunction {
+ func :: String,
+ args :: [UseMap],
+ res :: UseMap
+}}
+ @LP Here, the UseMap type is defined similar to the @I SignalMap
+ type @FootNote{In reality, both @I SignalMap and @I UseMap are
+ generalized into a type @I HsValueMap which takes an element type as
+ a type parameter}:
+
+ @ID @Haskell {
+Single Use
+| Tuple [Use]}
+
+ @LP The @I Use type is defined as follows. The @I StateId argument
+ to the @I State constructor is a unique identifier for the state
+ variable, which can be used to match input and output state
+ variables together. This state id should be unique within either
+ arguments or the return value, but should be matched between them
+ (@I i.e., there should be exactly one @I State use with id 0 in the
+ arguments, and exactly once in the return value) @FootNote{In the
+ current implementation, there is third constructor in the @I Use
+ type for arguments that get higher order functions passed in. Since
+ this is not functional yet, this is left out of this discussion}.
+
+ @ID @Haskell {
+Port
+| State StateId}
+
+ @LP The last type used in this description is @I SignalExpr. It allows for certain
+ expressions in the resulting VHDL, currently only comparison. The type has
+ the following constructors:
+
+ @DP @Tbl
+ aformat { @Cell @I A | @Cell B }
+ {
+ @Rowa
+ A {
+ Sig Signal
+ }
+ B {
+ A simple signal reference.
+ }
+ @Rowa
+ A {
+ Literal String
+ }
+ B {
+ A literal value. The given string is the resulting VHDL value. Perhaps
+ this should better be something less VHDL-specific, but this works for
+ now.
+ }
+ @Rowa
+ A {
+ Eq Signal Signal
+ }
+ B {
+ Equality comparison. This is the only comparison supported for now and
+ results in a @I Bool signal that can be used in a @I CondDef.
+ }
+ }
+
+ @BeginSubSections
+ @SubSection
+ @Title {Examples}
+ @Begin
+ @LP To ease the understanding of this datastructure, we will show
+ the flattened equivalents of some of the examples given in
+ @SectionLink {CoreExamples}. When reading these examples, don't
+ forget that the signal id's themselves are interchangeable, as
+ long as they remain unique and still link the right expressions
+ together (@I i.e., the actual numbers are not relevant).
+ @Display @Heading {Fixed value}
+
+ @CNP @ID @Example
+ @Title {Haskell code}
+ {
+ @LP @Haskell {high = High}}
+
+ @LP This Haskell source results in the following @I FlatFunction.
+
+ @CNP @ID @Example
+ @Title {Flat function}
+ {
+ @LP @Haskell {
+FlatFunction {
+ sigs = [0],
+ args = [],
+ res = (Single 0),
+ defs = [UncondDef (Lit "'1'") 0]
+}}}
+
+ @Display @Heading {Function application}
+
+ @CNP @ID @Example
+ @Title {Haskell code}
+ {
+ @LP @Haskell {
+or :: Bit -> Bit -> Bit
+or a b = inv (nand a b)}}
+
+ @CNP @ID @Example
+ @Title {Flat function}
+ {
+ @LP @Haskell {
+FlatFunction {
+ sigs = [0, 1, 2, 3],
+ args = [Single 0, Single 1],
+ res = (Single 3),
+ defs = [
+ (FApp
+ (HsFunction "nand" [Single Port, Single Port] (Single Port))
+ [Single 0, Single 1]
+ (Single 2)
+ ),
+ (FApp
+ (HsFunction "inv" [Single Port] (Single Port))
+ [Single 2]
+ (Single 3)
+ )
+ ]
+}}}
+
+ @Display @Heading {Pattern matching}
+ @CNP @ID @Example
+ @Title {Haskell code}
+ {
+ @LP @Haskell
+ {
+mux2 :: Bit -> (Bit, Bit) -> Bit
+mux2 Low (a, b) = a
+mux2 High (a, b) = b}}
+
+ @CNP @ID @Example
+ @Title {Flat function}
+ {
+ @LP @Haskell {
+FlatFunction {
+ sigs = [0, 1, 2, 3, 4, 5],
+ args = [Single 0, Tuple [Single 3, Single 4]],
+ res = (Single 5),
+ defs = [
+ (UncondDef (Lit "'1'") 1),
+ (UncondDef (Eq 0 1) 2),
+ (CondDef 2 4 3 5)
+ ]
+}}}
+ @End @SubSection
+ @EndSubSections
+
+@End @Section
+
+@Section
+ @Title {Flattening of functions}
+ @Tag {Flattening}
+@Begin
+ @LP The most interesting part of all this is of course the translation of our
+ Haskell functions (in Core format) to flattened functions. From there, the
+ translation of VHDL is relatively easy.
+
+ @LP This section details the "flattening" of a function. To this end, we define
+ a function @I flattenFunction which translates a (nonrecursive) binding to a
+ flat function.
+
+ @LP The syntax used for these functions is not quite Haskell. It is similar,
+ but some functions can have side effects or don't return any value. They
+ are probably best read as if the lines are sequentially executed (though
+ pattern matching does happen as is normal in Haskell). This notation is
+ probably closest to the monadic "do" notation in Haskell, but with the
+ special syntax for that ("do", "<-" and "return") implied.
+
+ @LP @Haskell {
+flattenFunction (NonRec b expr) =
+ (args, res) = flattenExpr expr
+ defs = getDefs
+ FlatFunction args res defs
+ }
+
+ @LP This is a rather simple function, but it does need some explaination. First
+ of all, the flattenExpr function has some hidden state (In the
+ implementation, this is state is made explicit using a @I State @I Monad).
+ To understand the above function, it is sufficient to know that the @I
+ getDefs function simply returns all the definitions that were added in @I
+ flattenExpr using the @I addDef function.
+
+ @LP Similary, in the below function definitions, the @I insertBind function
+ inserts a signal bind (that binds a haskell identifier to a signal in the
+ flat function) into the current table of binds. This binds remains in the
+ table for any other functions called by the caller of @I insertBind, but is
+ removed again when that caller "returns". The @I lookupBind function looks
+ up such a bind in this table.
+
+ @LP Now, the actual work is done by the @I flattenExpr function, which processes
+ an arbitrary expressions and returns a tuple with argument @I SignalMaps and
+ a result @I SignalMap. The argument @I SignalMaps map each argument that
+ should be passed to the expression (when it has a function type, this is an
+ empty list for non-function types). The signals these maps map to are the
+ signals to which the arguments are assumed to have been assigned. In the
+ final @I FlatFunction, these maps are used to tell to which signals the
+ input ports should be bound.
+
+ @LP The result @I SignalMap is the inverse of the argument @I SIgnalMaps. It
+ contains the signals that the results of the expression are bound to.
+
+ @Display @Heading {Lambda expressions}
+ @LP @Haskell {
+flattenExpr (Lam b expr) =
+ arg = genSignals b
+ insertBind (b <= arg)
+ (args, res) = flattenExpr expr
+ (arg:args, res)
+ }
+
+ @Display @Heading {Variable references}
+ @LP @Haskell {
+flattenExpr (Var id) =
+ case id of
+ ``localid'' ->
+ ([], lookupBind id)
+ () ->
+ ([], Tuple [])
+ ``datacon'' ->
+ res = genSignals id
+ lit = Lit (dataconToLit id)
+ addDef UncondDef {src = lit, dst = s}
+
+ ([], res)
+dataconToLit datacon =
+ case datacon of
+ Low -> "'0'"
+ High -> "'1'"
+ True -> "true"
+ False -> "false"
+ }
+
+ @LP Here, @I genSignals generates a map of new (unique) signals. The structure
+ on the map is based on the (Haskell) type of whatever is passed to it, such
+ as the value bound to the identifier in this case.
+
+ @LP The @I ``datacon'' and @I ``localid'' patterns should be read as matching
+ when the id matching against refers to a local identifier (local to the
+ function or expression containing it) or a dataconstructor (such as @I True,
+ @I False, @I High or @I (,)).
+
+
+ @Display @Heading {Let expressions}
+ @LP @Haskell {
+flattenExpr (Let (NonRec b bexpr) expr) =
+ (b_args, b_res) = flattenExpr bexpr
+ insertBind (b <= b_res)
+ flattenExpr expr
+ }
+
+ @Display @Heading {Case expressions}
+ @LP @Haskell {
+flattenExpr (Case scrut alts) =
+ (args, res) = flattenExpr scrut
+ flattenAlts res alts
+
+flattenAlt scrut alt =
+ TODO
+
+flattenAlts scrut [alt] =
+ flattenAlt scrut alt
+
+flattenAlts scrut (alt:alts)
+ (true_args, true_res) = flattenAlt scrut alt
+ (false_args, false_res) = flattenAlts scrut alts
+ (altcon, bind_vars, expr) = alt
+ lit = Lit (dataconToLit)
+
+ TODO
+ }
+
+ @Display @Heading {Function applications}
+ @LP @Haskell {
+flattenExpr (App f arg) =
+ TODO
+ }
+
+
+@End @Section
+
+# vim: set sts=2 sw=2 expandtab:
--
2.20.1