Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
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)