Download ppt

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
A Functional-Logic Library for Wired
Matthew Naylor and Collin Runciman – University of York
Emil Axelsson – Chalmers University
Haskell Workshop 2007
Acknowledgement
This project recieves Intel-custom funding from the
Semiconductor Research Corporation
Overview
Background: Digital circuit design
Wired: Low-level hardware design in Haskell
LP: Logic programming in Haskell
Wired implemented using LP
Circuit abstraction levels
Just like software, digital circuits can be described at various
levels of abstraction:
Netlist: Directed graph with simple Boolean gates as nodes
Placed netlist: Netlist where every gate has been assigned
a unique position on the chip
Final layout: Placed netlist where every connection has
been assigned a unique route in the metal layers
Netlist-to-layout is normally done by automatic tools
The problem…
Integrated circuit performance is largely determined by
wire properties
Long wire  longer signal delay, more power dissipation
Many long wires  bigger chip area
The abstract netlist view leaves wires implicit!
Conclusion: Hard to reason about performance at the
netlist level
Evidence: Large parts of Intel’s chips are designed through
manual layout editing
Can Haskell help?
Haskell has already proven to be useful for hardware
description at the (placed) netlist level (Lava, Hydra)
Idea: Take the Lava approach further and describe the full
layout, including wiring in Haskell
Result: Wired
Wired example
Wires and gates are
conceptually the same thing!
bitMult = row (and2 *=* wiring)
where
wiring = (wire *||* wireT0 *||* wire)
`withConstraintW` (<== sigStruct)
Netlist and layout
described
simultaneously
Functional circuit model
Lava represents circuits as functions
Inputs and outputs are determined syntactically
But where is the input here?
Relational circuit model
One relational combinator captures a whole family of
functional ones (signal-flow abstraction)
Relational circuit model
One relational wire captures a whole family of functional
ones (signal-flow abstraction)
Relational circuit model
Size inference
x
64
LP: Logic programming in Haskell
Old Wired implementation very ad-hoc and inefficient
Matthew Naylor spotted that Curry was better suited for
implementing Wired
This led to the birth of the library LP
Inspired by Curry and Typed logical variables in Haskell. Claessen
and Ljunglöf (HW’2000)
Easy construction/deconstruction of logical terms
Supports residuation (heavily used in Wired)
Supports non-determinism
Logical terms
Definition
data Uni = Var VarID | Ctr Int [Uni] | Int Int
newtype Term a = Term { uni :: Uni }
type VarID = Int
Constructors
nil :: Term [a]
nil = Term (Ctr 0 [])
(|>) :: Term a -> Term [a] -> Term [a]
a |> as = Term (Ctr 1 [uni a, uni as])
int :: Int -> Term Int
int n = Term (Int n)
...
Automating data type creation
(nil ::: (|>)) = datatype (cons0 [] \/ cons2 (:))
(true ::: false) = datatype (cons0 True \/ cons0 False)
Logic programs
Non-determinism + state
data LP a = LP { runLP :: State -> [(a, State)] }
type State = (Map VarID Val, VarID)
type Residual = Uni -> LP ()
data Val = Bound Uni | Unbound [Residual]
Possibly bound
to a term
Current variable
mapping, and a fresh
ID
Actual implementation uses
Hinze’s two-continuation monad
transformer and IORef’s
Basic LP interface
(>>) interpreted as
instance Monad LP ...
instance MonadPlus LP ...
(?) :: LP a -> LP a -> LP a
(?) = mplus
newVar
:: Val -> LP VarID
readVar
:: VarID -> LP Val
writeVar :: VarID -> Val -> LP ()
conjunction
Disjunction + failure
Unification
unify :: Uni -> Uni -> LP ()
unify a b = do
ra <- root a; rb <- root b
unif ra rb
where
unif (Var v)
(Var w)
| v == w = return ()
unif (Var v)
b
= bindVar v b
unif a
(Var w)
= bindVar w a
unif (Int a)
(Int b)
| a == b = return ()
unif (Ctr n as) (Ctr m bs) | n == m = unif' as bs
unif _
_
= mzero
unif' []
[]
= return ()
unif' (a:as) (b:bs) = unify a b >> unif' as bs
Logical class
class Logical a
where
free :: LP a
(===) :: a -> a -> LP ()
instance Logical (Term a)
where
free = return . Term . Var =<< unboundVar
a === b = unify (uni a) (uni b)
instance (Logical a, Logical b) => Logical (a,b)
where
free = liftM2 (,) free free
(a0,a1) === (b0,b1) = a0 === b0 >> a1 === b1
Example: Append
app ::
->
->
->
Term [a]
Term [a]
Term [a]
LP ()
app as bs cs
= do as===nil
bs===cs
? do ((a,as'),cs’) <- free
a|>as' === as
a|>cs' === cs
app as' bs cs'
testApp = run $ do
as' <- free
let as = 1|>2|>3|> as'
cs = 1|>2|>3|>4|>5|> nil
bs <- free
app as bs cs
return bs
*Main> testApp
[[4,5],[5],[]]
Pattern matching
pat --> rhs = return (pat,rhs)
caseOf a as = do
(pat,rhs) <- free >>= as
pat === a
rhs
app as bs cs = caseOf (as,cs) alts where
alts ((a,as'),cs')
= (nil,cs')
--> (bs===cs)
? (a|>as', a|>cs') --> app as' bs cs'
Residuation
rigid :: Logical b => (Uni -> LP b) -> (Uni -> LP b)
...
Does not accept variables
(pure function)
Accepts variables
Residuation
rigid :: Logical b => (Uni -> LP b) -> (Uni -> LP b)
...
data Val = Bound Uni | Unbound [Residual]
type Residual = Uni -> LP ()
bindVar :: VarID -> Uni -> LP ()
bindVar v a = do
Unbound rs <- readVar v
writeVar v (Bound a)
rs `resumeOn` a
Used by unify to
instantiate a variable
Rigid deconstruction/arithmetic
unint :: Logical b => Term Int
-> (Int -> LP b)
-> LP b
unint a f = rigid (\(Int a) -> f a) (uni a)
liftInt2 f a b = unint a (\a ->
unint b (\b ->
return (int (f a b))))
(|+|), (|-|), (|*|)
:: Term Int -> Term Int -> LP (Term Int)
(|+|) = liftInt2 (+)
(|-|) = liftInt2 (-)
(|*|) = liftInt2 (*)
Residuation example
(<==) :: Logical a => a -> LP a -> LP ()
a <== m = (a===) =<< m
testResid = run $ do
(res,x) <- free
res <== x |-| 3
x
<== 5 |*| 2
return res
*Main> testResid
[7]
Determistic relations
a <+> b = do
c <- a |+| b
b <== c |-| a
a <== c |-| b
return c
testPlus =
a
<20 <==
return
If any two of a, b and c are known,
the third one will be inferred
run $ do
free
((1<+>) =<< (2<+>) =<< (3<+>a))
a
*Main> testPlus
[14]
• Completely deterministic
• Abstract information flow
Exactly what Wired needs!
Wired
data Circuit w n s
{ west
::
, north ::
, south ::
, east
::
, sizeX ::
, sizeY ::
, layout ::
}
e = Circ
w
n
s
e
Term Size
Term Size
Term Layout
Ports :
Contain geometry and
signals of each side
n
type Circ w n s e = LP (Circuit w n s e)
w
e
s
Wired: Below composition
nH
wH
sL
circL *=* circH = do
Circ wL nL sL eL szxL szyL layL <- circL
Circ wH nH sH eH szxH szyH layH <- circH
nL === sH
szy <- szyL <+> szyH
let lay = (term (layL :=: layH))
return $
Circ (wL,wH) nH sL (eL,eH) szxL szy lay
Connects netlists
and exchanges
geometrical info
eH
*=*
nL
wL
eL
sL
nH
(eL,eH)
(...)
Circ wL x sL eL
Circ wH nH x eH
Circ (wL,wH) nH sL (eL,eH)
(wL,wH)
(*=*) ::
=>
->
->
sL
Size inference revisited
x
64
Connection pattern: row
rowN :: (...)
rowN n circ =
where
rowNN 0 =
rowNN n =
=> Term Int -> Circ x n s x -> Circ x [n] [s] x
unint n rowNN
nilY
circ *||~ rowNN (n-1)
row :: (...) => Circ y n s y -> Circ y [n] [s] y
row circ = do
cR <- free
Length determined by context.
l
<- lengthR (north cR)
l <== lengthR (south cR)
lengthR is a rigid version of
cR <== rowN l circ
return cR
length.
Bit multiplier revisited
bitMult = row (and2 *=* wiring)
where
wiring = (wire *||* wireT0 *||* wire)
`withConstraintW` (<== sigStruct)
*Main> renderCircuit "circ" (bitMult `ofLengthX` 6)
Example: Sklansky
sklansky 0 op opFO = rowN 1 $ nilX `withConstraint` \c ->
north c <== (structure =<< asLP south =<< op)
sklansky d op opFO = consW (joinL *=* skl) ~||~ consE (joinR *=* skl)
where
skl
= sklansky (d-1) op opFO
op'
= op *=*alignLeft
opFO' = opFO*=*alignLeft
joinL = row (busY*=*busY) ~||* (busT1*=*busY)
joinR = row opFO' ~||* op'
Example: Sklansky
*Main> renderCircuit "circ" $ sklansky 3 dot dotFO
Related work
Lava:
Claessen, Sheeran, Singh. CHARME’2001.
Logic programming in Haskell:
Typed logical variables in Haskell. Claessen and Ljunglöf, HW’2000.
Backtracking, interleaving and terminating monad transformers.
Kiselyov, Shan, Friedman and Sabry, ICFP’2005.
SparseCheck (Matthew Naylor 2007)
Future work
LP:
More forms of non-determinism (breadth-first, fair backtracking…)
Lazy narrowing
Wired:
Working on new much simplified version (might not need LP…)
Producing real layout for evaluation
Examples…
Unification
unboundVar :: LP VarID
unboundVar = newVar (Unbound [])
bindVar :: VarID -> Uni -> LP ()
bindVar v a = writeVar v (Bound a)
ifBound :: VarID -> (Uni -> LP b) -> LP b -> LP b
ifBound v t f = readVar v >>= decons
where
decons (Bound a)
= t a
decons (Unbound _) = f
root :: Uni -> LP Uni
root (Var v) = ifBound v root (return (Var v))
root a
= return a
Residuation
rigid' :: Logical b => (Uni -> LP b) -> (Uni -> LP b)
...
data Val = Bound Uni | Unbound [Residual]
type Residual = Uni -> LP ()
bindVar :: VarID -> Uni -> LP ()
bindVar v a = do
Unbound rs <- readVar v
writeVar v (Bound a)
rs `resumeOn` a
Used by unify to
instantiate a variable
resumeOn ::
[Residual] -> Uni -> LP ()
resumeOn rs (Var v) = do
Unbound ss <- readVar v
writeVar v (Unbound (rs ++ ss))
resumeOn rs a = mapM_ (resume a) rs
where
resume a g = g a
Residuation
rigid' :: Logical b
=> (Uni -> LP b)
-> (Uni -> LP b)
rigid' f a = do
ar <- root a
b <- free
let g x = f x >>= (===b)
[g] `resumeOn` ar
return b
b gives access to the (future)
result of the computation
rigid :: Logical b
=> (Term a -> LP b)
-> (Term a -> LP b)
rigid f a = rigid' (f . Term) (uni a)
Related documents