- As an example of the used approach, a simple averaging circuit, that lets
- the accumulation of the inputs be done by a subcomponent.
-
- \starttyping
- newtype State s = State s
-
- type AccumState = State Bit
- accum :: Word -> AccumState -> (AccumState, Word)
- accum i (State s) = (State (s + i), s + i)
-
- type AvgState = (AccumState, Word)
- avg :: Word -> AvgState -> (AvgState, Word)
- avg i (State s) = (State s', o)
- where
- (accums, count) = s
- -- Pass our input through the accumulator, which outputs a sum
- (accums', sum) = accum i accums
- -- Increment the count (which will be our new state)
- count' = count + 1
- -- Compute the average
- o = sum / count'
- s' = (accums', count')
- \stoptyping
-
- And the normalized, core-like versions:
-
- \starttyping
- accum i spacked = res
- where
- s = case spacked of (State s) -> s
- s' = s + i
- spacked' = State s'
- o = s + i
- res = (spacked', o)
-
- avg i spacked = res
- where
- s = case spacked of (State s) -> s
- accums = case s of (accums, \_) -> accums
- count = case s of (\_, count) -> count
- accumres = accum i accums
- accums' = case accumres of (accums', \_) -> accums'
- sum = case accumres of (\_, sum) -> sum
- count' = count + 1
- o = sum / count'
- s' = (accums', count')
- spacked' = State s'
- res = (spacked', o)
- \stoptyping
-
-
-
- As noted above, any component of a function's state that is a substate,
- \eg passed on as the state of another function, should have no influence
- on the hardware generated for the calling function. Any state-specific
- \small{VHDL} for this component can be generated entirely within the called
- function. So,we can completely leave out substates from any function.
-
- From this observation, we might think to remove the substates from a
- function's states alltogether, and leave only the state components which
- are actual states of the current function. While doing this would not
- remove any information needed to generate \small{VHDL} from the function, it would
- cause the function definition to become invalid (since we won't have any
- substate to pass to the functions anymore). We could solve the syntactic
- problems by passing \type{undefined} for state variables, but that would
- still break the code on the semantic level (\ie, the function would no
- longer be semantically equivalent to the original input).
-
- To keep the function definition correct until the very end of the process,
- we will not deal with (sub)states until we get to the \small{VHDL} generation.
- Here, we are translating from Core to \small{VHDL}, and we can simply not generate
- \small{VHDL} for substates, effectively removing the substate components
- alltogether.
-
- There are a few important points when ignore substates.
-
- First, we have to have some definition of "substate". Since any state
- argument or return value that represents state must be of the \type{State}
- type, we can simply look at its type. However, we must be careful to
- ignore only {\em substates}, and not a function's own state.
-
- In the example above, this means we should remove \type{accums'} from
- \type{s'}, but not throw away \type{s'} entirely. We should, however,
- remove \type{s'} from the output port of the function, since the state
- will be handled by a \small{VHDL} procedure within the function.
-
- When looking at substates, these can appear in two places: As part of an
- argument and as part of a return value. As noted above, these substates
- can only be used in very specific ways.
-
- \desc{State variables can appear as an argument.} When generating \small{VHDL}, we
- completely ignore the argument and generate no input port for it.
-
- \desc{State variables can be extracted from other state variables.} When
- extracting a state variable from another state variable, this always means
- we're extracting a substate, which we can ignore. So, we simply generate no
- \small{VHDL} for any extraction operation that has a state variable as a result.
-
- \desc{State variables can be passed to functions.} When passing a
- state variable to a function, this always means we're passing a substate
- to a subcomponent. The entire argument can simply be ingored in the
- resulting port map.
-
- \desc{State variables can be returned from functions.} When returning a
- state variable from a function (probably as a part of an algebraic
- datatype), this always mean we're returning a substate from a
- subcomponent. The entire state variable should be ignored in the resulting
- port map. The type binder of the binder that the function call is bound
- to should not include the state type either.
-
- \startdesc{State variables can be inserted into other variables.} When inserting
- a state variable into another variable (usually by constructing that new
- variable using its constructor), we can identify two cases:
-
- \startitemize
- \item The state is inserted into another state variable. In this case,
- the inserted state is a substate, and can be safely left out of the
- constructed variable.
- \item The state is inserted into a non-state variable. This happens when
- building up the return value of a function, where you put state and
- retsult variables together in an algebraic type (usually a tuple). In
- this case, we should leave the state variable out as well, since we
- don't want it to be included as an output port.
- \stopitemize
-
- So, in both cases, we can simply leave out the state variable from the
- resulting value. In the latter case, however, we should generate a state
- proc instead, which assigns the state variable to the input state variable
- at each clock tick.
- \stopdesc
-
- \desc{State variables can appear as (part of) a function result.} When
- generating \small{VHDL}, we can completely ignore any part of a function result
- that has a state type. If the entire result is a state type, this will
- mean the entity will not have an output port. Otherwise, the state
- elements will be removed from the type of the output port.
-
-
- Now, we know how to handle each use of a state variable separately. If we
- look at the whole, we can conclude the following:
-
- \startitemize
- \item A state unpack operation should not generate any \small{VHDL}. The binder
- to which the unpacked state is bound should still be declared, this signal
- will become the register and will hold the current state.
- \item A state pack operation should not generate any \small{VHDL}. The binder th
- which the packed state is bound should not be declared. The binder that is
- packed is the signal that will hold the new state.
- \item Any values of a State type should not be translated to \small{VHDL}. In
- particular, State elements should be removed from tuples (and other
- datatypes) and arguments with a state type should not generate ports.
- \item To make the state actually work, a simple \small{VHDL} proc should be
- generated. This proc updates the state at every clockcycle, by assigning
- the new state to the current state. This will be recognized by synthesis
- tools as a register specification.
- \stopitemize
-
-
- When applying these rules to the example program (in normal form), we will
- get the following result. All the parts that don't generate any value are
- crossed out, leaving some very boring assignments here and there.
+ As an example of the used approach, there is a simple averaging circuit in
+ \in{example}[ex:AvgState]. This circuit lets the accumulation of the
+ inputs be done by a subcomponent, \hs{acc}, but keeps a count of value
+ accumulated in its own state.
+
+
+ \startbuffer[AvgState]
+ -- The state type annotation
+ newtype State s = State s
+
+ -- The accumulator state type
+ type AccState = State Bit
+ -- The accumulator
+ acc :: Word -> AccState -> (AccState, Word)
+ acc i (State s) = let sum = s + i in (State sum, sum)
+
+ -- The averaging circuit state type
+ type AvgState = (AccState, Word)
+ -- The averaging circuit
+ avg :: Word -> AvgState -> (AvgState, Word)
+ avg i (State s) = (State s', o)
+ where
+ (accs, count) = s
+ -- Pass our input through the accumulator, which outputs a sum
+ (accs', sum) = acc i accs
+ -- Increment the count (which will be our new state)
+ count' = count + 1
+ -- Compute the average
+ o = sum / count'
+ s' = (accs', count')
+ \stopbuffer
+
+ \placeexample[here][ex:AvgState]{Simple stateful averaging circuit.}
+ %\startcombination[2*1]
+ {\typebufferhs{AvgState}}%{Haskell description using function applications.}
+ % {\boxedgraphic{AvgState}}{The architecture described by the Haskell description.}
+ %\stopcombination
+ \todo{Picture}
+
+ And the normalized, core-like versions:
+
+ \startbuffer[AvgStateNormal]
+ acc = λi.λspacked.
+ let
+ -- Remove the State newtype
+ s = spacked ▶ Word
+ s' = s + i
+ o = s + i
+ -- Add the State newtype again
+ spacked' = s' ▶ State Word
+ res = (spacked', o)
+ in
+ res
+
+ avg = λi.λspacked.
+ let
+ s = spacked ▶ (AccState, Word)
+ accs = case s of (accs, _) -> accs
+ count = case s of (_, count) -> count
+ accres = acc i accs
+ accs' = case accres of (accs', _) -> accs'
+ sum = case accres of (_, sum) -> sum
+ count' = count + 1
+ o = sum / count'
+ s' = (accs', count')
+ spacked' = s' ▶ State (AccState, Word)
+ res = (spacked', o)
+ in
+ res
+ \stopbuffer
+
+ \placeexample[here][ex:AvgStateNormal]{Normalized version of \in{example}[ex:AvgState]}
+ {\typebufferlam{AvgStateNormal}}