-------------------------------------------------------------------------------
--- $Id: Gates.hs#13 2010/10/01 02:55:20 REDMOND\\satnams $
-------------------------------------------------------------------------------

-- | This Lava.Gates module provides a collection of Xilinx low-level
--   components.

module Lava.Gates (module Lava.Gates)
where
import Lava.Combinators
import Lava.Netlist
import Lava.LUTGates
import Lava.PrimitiveGates

-- * Lava Gates
             
-- ** LUT-based gates

-------------------------------------------------------------------------------
--- LUT-based gates
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- | The 'inv' function implements an invertor explicitly with a LUT1.

inv :: Bit -- ^ The input i0
       -> Out Bit -- ^ The output o
inv = lut1gate not "inv"

-------------------------------------------------------------------------------
-- | The 'and2' function implements an AND gate explicitly with a LUT2.

and2 :: (Bit, Bit)  -- ^ inputs (i0, i1)
        -> Out Bit  -- ^ output o
and2 = lut2gate (&&) "and2"

-------------------------------------------------------------------------------
-- | The 'and3' function implements an AND gate explicitly with a LUT3.

and3 :: (Bit, Bit, Bit)  -- ^ inputs (i0, i1, i2)
        -> Out Bit       -- ^ output o
and3 = lut3gate (\i0 i1 i2 -> i0 && i1 && i2) "and3"

-------------------------------------------------------------------------------
-- | The 'and4' function implements an AND gate explicitly with a LUT4.

and4 :: (Bit, Bit, Bit, Bit)  -- ^ inputs (i0, i1, i2, i3)
        -> Out Bit            -- ^ output o
and4 = lut4gate (\i0 i1 i2 i3 -> i0 && i1 && i2 && i3) "and4"

-------------------------------------------------------------------------------
-- | The 'and5' function implements an AND gate explicitly with a LUT5.

and5 :: (Bit, Bit, Bit, Bit, Bit)  -- ^ inputs (i0, i1, i2, i3, i4)
        -> Out Bit                  -- ^ output o
and5 = lut5gate (\i0 i1 i2 i3 i4 -> i0 && i1 && i2 && i3 && i4) "and5"

-------------------------------------------------------------------------------
-- | The 'and6' function implements an AND gate explicitly with a LUT6.

and6 :: (Bit, Bit, Bit, Bit, Bit, Bit)  -- ^ inputs (i0, i1, i2, i3, i4, i5)
        -> Out Bit                  -- ^ output o
and6 = lut6gate (\i0 i1 i2 i3 i4 i5 -> i0 && i1 && i2 && i3 && i4 && i5) "and6"

-------------------------------------------------------------------------------
-- | The 'or2' function implements an OR gate explicitly with a LUT2. 

or2 :: (Bit,  Bit) -- ^ inputs (i0, i1)
        -> Out Bit -- ^ output o
or2 = lut2gate (||) "or"

-------------------------------------------------------------------------------
-- | The 'or3' function implements an AND gate explicitly with a LUT3.

or3 :: (Bit, Bit, Bit)  -- ^ inputs (i0, i1, i2)
        -> Out Bit       -- ^ output o
or3 = lut3gate (\i0 i1 i2 -> i0 || i1 || i2) "or3"

-------------------------------------------------------------------------------
-- | The 'or4' function implements an AND gate explicitly with a LUT4.

or4 :: (Bit, Bit, Bit, Bit)  -- ^ inputs (i0, i1, i2, i3)
        -> Out Bit            -- ^ output o
or4 = lut4gate (\i0 i1 i2 i3 -> i0 || i1 || i2 || i3) "or4"

-------------------------------------------------------------------------------
-- | The 'or5' function implements an AND gate explicitly with a LUT5.

or5 :: (Bit, Bit, Bit, Bit, Bit)  -- ^ inputs (i0, i1, i2, i3, i4)
        -> Out Bit                  -- ^ output o
or5 = lut5gate (\i0 i1 i2 i3 i4 -> i0 || i1 || i2 || i3 || i4) "or5"

-------------------------------------------------------------------------------
-- | The 'and6' function implements an AND gate explicitly with a LUT6.

or6 :: (Bit, Bit, Bit, Bit, Bit, Bit)  -- ^ inputs (i0, i1, i2, i3, i4, i5)
        -> Out Bit                  -- ^ output o
or6 = lut6gate (\i0 i1 i2 i3 i4 i5 -> i0 || i1 || i2 || i3 || i4 || i5) "or6"

-------------------------------------------------------------------------------
-- | The 'nor2' function implements an NOR gate explicitly with a LUT2. 

nor2 :: (Bit,  Bit) -- ^ inputs (i0, i1)
        -> Out Bit  -- ^ output o
nor2 = lut2gate (\i0 i1 -> not (i0 || i1)) "nor"

-------------------------------------------------------------------------------
-- | The 'xor2' function implements an XOR gate explicitly with a LUT2.

xor2 :: (Bit,  Bit) -- ^ inputs (i0, i1)
        -> Out Bit  -- ^ output o
xor2 = lut2gate (/=) "xor"

-------------------------------------------------------------------------------
-- | The 'xnor2' function implements an XOR gate explicitly with a LUT2.

xnor2 :: (Bit,  Bit) -- ^ inputs (i0, i1)
          -> Out Bit -- ^ output o
xnor2 = lut2gate (==) "nxor"

-------------------------------------------------------------------------------
-- | A multiplexor implemented with a LUT3

mux :: (Bit, (Bit,  Bit)) -- ^ inputs (s, (i0, i1))
       -> Out Bit -- ^ output o
mux (s, (i0, i1)) = lut3gate (\s i0 i1 -> if s then i1 else i0) "mux" 
                             (s, i0, i1)

-------------------------------------------------------------------------------

-- ** Carry-chain elements

-------------------------------------------------------------------------------

muxcy :: (Bit, (Bit, Bit)) -- ^ (s, (di, ci))
         -> Out Bit        -- ^ o
muxcy (s, (di, ci))
  = do [o] <- primitiveGate  "muxcy" [("ci",ci), ("di", di), ("s", s)] ["o"] 
              (Just (1,1))
       return o

-------------------------------------------------------------------------------

muxcy_d :: Bit                -- ^ d
           -> Bit             -- ^ ci 
           -> Bit             -- ^ di
           -> Out (Bit, Bit)  -- ^ (o, lo)
muxcy_d ci di s 
  = do [o, lo] <- primitiveGate "muxcy_d" [("ci",ci), ("di", di), ("s", s)] 
                                ["o", "lo"] (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

muxcy_l :: Bit                -- ^ d
           -> Bit             -- ^ ci 
           -> Bit             -- ^ di
           -> Out (Bit, Bit)  -- ^ (o, lo)
muxcy_l ci di s 
  = do [o, lo] <- primitiveGate "muxcy_l" [("ci",ci), ("di", di), ("s", s)] 
                                ["o", "lo"] (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

muxf5 :: Bit    -- ^ i0
         -> Bit -- ^ i1
         -> Bit -- ^ s
         -> Out Bit -- ^o
muxf5 i0 i1 s
  = do [o] <- primitiveGate  "muxf5" [("i0",i0), ("i1", i1), ("s", s)] ["o"] 
              (Just (1,1))
       return o

-------------------------------------------------------------------------------

muxf5_d :: Bit                -- ^ i0
           -> Bit             -- ^ i1 
           -> Bit             -- ^ i2
           -> Out (Bit, Bit)  -- ^ (o, lo)
muxf5_d i0 i1 s
  = do [o, lo] <- primitiveGate  "muxf5_d" [("i0",i0), ("i1", i1), ("s", s)] 
                                 ["o"]  (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

muxf5_l :: Bit                -- ^ i0
           -> Bit             -- ^ i1 
           -> Bit             -- ^ i2
           -> Out (Bit, Bit)  -- ^ (o, lo)
muxf5_l i0 i1 s
  = do [o, lo] <- primitiveGate  "muxf5_l" [("i0",i0), ("i1", i1), ("s", s)] 
                                           ["o", "lo"]  (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

muxf6 :: Bit    -- ^ i0
         -> Bit -- ^ i1
         -> Bit -- ^ s
         -> Out Bit -- ^o
muxf6 i0 i1 s
  = do [o] <- primitiveGate  "muxf6" [("i0",i0), ("i1", i1), ("s", s)] ["o"] 
              (Just (1,1))
       return o

-------------------------------------------------------------------------------

muxf6_d :: Bit                -- ^ i0
           -> Bit             -- ^ i1 
           -> Bit             -- ^ i2
           -> Out (Bit, Bit)  -- ^ (o, lo)
muxf6_d i0 i1 s
  = do [o, lo] <- primitiveGate  "muxf6_d" [("i0",i0), ("i1", i1), ("s", s)] 
                                 ["o", "lo"]  (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

muxf6_l :: Bit                -- ^ i0
           -> Bit             -- ^ i1 
           -> Bit             -- ^ i2
           -> Out (Bit, Bit)  -- ^ (o, lo)
muxf6_l i0 i1 s
  = do [o, lo] <- primitiveGate  "muxf6_l" [("i0",i0), ("i1", i1), ("s", s)] 
                                           ["o", "lo"]  (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

muxf7 :: Bit    -- ^ i0
         -> Bit -- ^ i1
         -> Bit -- ^ s
         -> Out Bit -- ^o
muxf7 i0 i1 s
  = do [o] <- primitiveGate  "muxf7" [("i0",i0), ("i1", i1), ("s", s)] ["o"] 
              (Just (1,1))
       return o

-------------------------------------------------------------------------------

muxf7_d :: Bit                -- ^ i0
           -> Bit             -- ^ i1 
           -> Bit             -- ^ i2
           -> Out (Bit, Bit)  -- ^ (o, lo)
muxf7_d i0 i1 s
  = do [o, lo] <- primitiveGate  "muxf7_d" [("i0",i0), ("i1", i1), ("s", s)] 
                                 ["o", "lo"]  (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

muxf7_l :: Bit                -- ^ i0
           -> Bit             -- ^ i1 
           -> Bit             -- ^ i2
           -> Out (Bit, Bit)  -- ^ (o, lo)
muxf7_l i0 i1 s
  = do [o, lo] <- primitiveGate  "muxf7_l" [("i0",i0), ("i1", i1), ("s", s)] 
                                           ["o", "lo"]  (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

muxf8 :: Bit    -- ^ i0
         -> Bit -- ^ i1
         -> Bit -- ^ s
         -> Out Bit -- ^o
muxf8 i0 i1 s
  = do [o] <- primitiveGate  "muxf8" [("i0",i0), ("i1", i1), ("s", s)] ["o"] 
              (Just (1,1))
       return o

-------------------------------------------------------------------------------

muxf8_d :: Bit                -- ^ i0
           -> Bit             -- ^ i1 
           -> Bit             -- ^ i2
           -> Out (Bit, Bit)  -- ^ (o, lo)
muxf8_d i0 i1 s
  = do [o, lo] <- primitiveGate  "muxf8_d" [("i0",i0), ("i1", i1), ("s", s)] 
                                 ["o"]  (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

muxf8_l :: Bit                -- ^ i0
           -> Bit             -- ^ i1 
           -> Bit             -- ^ i2
           -> Out (Bit, Bit)  -- ^ (o, lo)
muxf8_l i0 i1 s
  = do [o, lo] <- primitiveGate  "muxf8_l" [("i0",i0), ("i1", i1), ("s", s)] 
                                           ["o", "lo"]  (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

xorcy :: (Bit, Bit)        -- ^ (li, ci)
         -> Out Bit        -- ^ o
xorcy (li, ci) 
  = do [o] <- primitiveGate  "xorcy" [("ci",ci), ("li", li)] ["o"] 
              (Just (1,1))
       return o

-------------------------------------------------------------------------------

xorcy_d :: Bit               -- ^ ci
           -> Bit            -- ^ di
           -> Out (Bit, Bit) -- ^ (ci, li)
xorcy_d ci li 
  = do [o, lo] <- primitiveGate  "xorcy_d" [("ci",ci), ("li", li)] ["o"] 
                  (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

xorcy_l :: Bit               -- ^ ci
           -> Bit            -- ^ di
           -> Out (Bit, Bit) -- ^ (ci, li)
xorcy_l ci li 
  = do [o, lo] <- primitiveGate  "xorcy_l" [("ci",ci), ("li", li)] ["o", "lo"] 
                                 (Just (1,1))
       return (o, lo)

-------------------------------------------------------------------------------

-- ** Flip-flops

-------------------------------------------------------------------------------

fd :: Bit        -- ^ clk
      -> Bit     -- ^ i
      -> Out Bit -- ^ q
fd clk i 
  = do [q] <- primitiveGate  "fd" [("c",clk), ("d", i)] ["q"] (Just (1,1))
       return q

-------------------------------------------------------------------------------

fdc :: Bit        -- ^ clk
       -> Bit     -- clr 
       -> Bit     -- ^i
       -> Out Bit -- ^ q
fdc clk clr i 
  = do [q] <- primitiveGate  "fdc" [("c",clk), ("clr", clr), ("d", i)] 
                             ["q"] (Just (1,1))
       return q

-------------------------------------------------------------------------------

fdc_1 :: Bit        -- ^ clk
         -> Bit     -- ^ clr
         -> Bit     -- ^ i
         -> Out Bit -- ^ q
fdc_1 clk clr i 
  = do [q] <- primitiveGate  "fdc_1" [("c",clk), ("clr", clr), ("d", i)] 
                             ["q"] (Just (1,1))
       return q

-------------------------------------------------------------------------------

fdce :: Bit        -- ^ clk 
        -> Bit     -- ^ ce
        -> Bit     -- ^ clr
        -> Bit     -- ^ d
        -> Out Bit -- ^ q
fdce clk ce clr d
  = do [q] <- primitiveGate  "fdce" [("c",clk), ("clr", clr), ("d", d), 
                             ("ce", ce)]  ["q"] (Just (1,1))
       return q

-------------------------------------------------------------------------------

fdce_1 :: Bit        -- ^ clk
          -> Bit     -- ^ ce
          -> Bit     -- ^ clr
          -> Bit     -- ^ d
          -> Out Bit -- ^ q
fdce_1 clk ce clr d
  = do [q] <- primitiveGate  "fdce_1" [("c",clk), ("clr", clr), ("d", d), 
                             ("ce", ce)]  ["q"] (Just (1,1))
       return q

-------------------------------------------------------------------------------

fdcp :: Bit -- ^ clk
        -> Bit -- ^ clr
        -> Bit  -- ^ pre
        -> Bit -- ^ d
        -> Out Bit -- ^ q
fdcp clk clr pre d
  = do [q] <- primitiveGate  "fdcp" [("c",clk), ("clr", clr), ("d", d), 
                             ("pre", pre)]  ["q"] (Just (1,1))
       return q

-------------------------------------------------------------------------------

fdcpe :: Bit -- ^ clk
         -> Bit     -- ^ ce
         -> Bit     -- ^ clr
         -> Bit     -- ^ pre
         -> Bit     -- ^ d
         -> Out Bit -- ^ q
fdcpe clk ce clr pre d
  = do [q] <- primitiveGate  "fdcpe" [("c",clk), ("clr", clr), ("d", d), 
                             ("pre", pre), ("ce",ce)]  ["q"] (Just (1,1))
       return q

-------------------------------------------------------------------------------

fdcpe_1 :: Bit -- ^ clk
         -> Bit     -- ^ ce
         -> Bit     -- ^ clr
         -> Bit     -- ^ pre
         -> Bit     -- ^ d
         -> Out Bit -- ^ q
fdcpe_1 clk ce clr pre d
  = do [q] <- primitiveGate  "fdcpe_1" [("c",clk), ("clr", clr), ("d", d), 
                             ("pre", pre), ("ce",ce)]  ["q"] (Just (1,1))
       return q

-------------------------------------------------------------------------------

-- ** Shift-register primitives

-------------------------------------------------------------------------------
-- | 16-bit shift register look-up table with clock enable

srl16e :: Bit        -- ^ d 
          -> Bit     -- ^ clk
          -> Bit     -- ^ ce
          -> Bit     -- ^ a0
          -> Bit     -- ^ a1
          -> Bit     -- ^ a2
          -> Bit     -- ^ a3
          -> Out Bit -- ^ q
srl16e d clk ce a0 a1 a2 a3
  = do [q] <- primitiveGate "srl16e" [("c",clk), ("ce",ce), ("a0",a0),
                                      ("a1",a1), ("a2",a2), ("a3",a3)]
                            ["q"] Nothing
       return q

-------------------------------------------------------------------------------

-------------------------------------------------------------------------------

-- ** Gates implemented in place of a slice latch

-------------------------------------------------------------------------------
-- |  Two input and gate implemented in place of a slice latch

and2b1l :: Bit        -- ^ di
           -> Bit     -- ^ sri
           -> Out Bit -- ^ o
and2b1l di sri
  = do [o] <- primitiveGate  "and2b1l" [("di",di), ("sri", sri)] ["o"] 
              (Just (1,1))
       return o

-------------------------------------------------------------------------------
-- |  Two input and gate implemented in place of a slice latch

or2l :: Bit        -- ^ di
        -> Bit     -- ^ sri
        -> Out Bit -- ^ o
or2l di sri
  = do [o] <- primitiveGate  "or2l" [("di",di), ("sri", sri)] ["o"] 
              (Just (1,1))
       return o

-------------------------------------------------------------------------------

-- ** Buffers

-------------------------------------------------------------------------------
--- BUFFERS
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- | Dedicated input clock buffer 

ibufg :: Bit        -- ^ i
         -> Out Bit -- ^ o
ibufg i
  = do [o] <- primitiveGate  "ibufg" [("i", i)] ["o"] Nothing
       return o

-------------------------------------------------------------------------------
-- | Global clock buffer

bufg :: Bit        -- ^ i
         -> Out Bit -- ^ o
bufg i
  = do [o] <- primitiveGate  "bufg" [("i", i)] ["o"] Nothing
       return o

-------------------------------------------------------------------------------
-- | Global clock buffer

bufgp :: Bit        -- ^ i
         -> Out Bit -- ^ o
bufgp i
  = do [o] <- primitiveGate  "bufgp" [("i", i)] ["o"] Nothing
       return o

-------------------------------------------------------------------------------
-- | Output buffer

obufg :: Bit        -- ^ i
         -> Out Bit -- ^ o
obufg i
  = do [o] <- primitiveGate  "obuf" [("i", i)] ["o"] Nothing
       return o

-------------------------------------------------------------------------------

-- ** Double data rate (DDR) components

-------------------------------------------------------------------------------
-- | Output buffer

obufds :: Bit              -- ^ i
         -> Out (Bit, Bit) -- ^ (o, ob)
obufds i
  = do [o, ob] <- primitiveGate  "bufds" [("i", i)] ["o", "ob"] Nothing
       return (o, ob)

-------------------------------------------------------------------------------

-- * Muxing of buses

-------------------------------------------------------------------------------

muxBit :: Bit -> (Bit, Bit) -> Out Bit
muxBit sel (d0, d1) = lut3gate muxFn "muxBit" (sel, d0, d1)

-------------------------------------------------------------------------------

muxBit' :: Bit -> (Bit, Bit) -> Out Bit
muxBit' sel (d0, d1) = lut3gate muxFn' "muxBit'" (sel, d0, d1)

-------------------------------------------------------------------------------
-- The behaviour of a multiplexor.

muxFn :: Bool -> Bool -> Bool -> Bool
muxFn sel d0 d1
  = if sel then
      d1
    else
      d0

-------------------------------------------------------------------------------
-- A multiplexor which has an inverted control input w.r.t. muxBit

muxFn' :: Bool -> Bool -> Bool -> Bool
muxFn' sel d0 d1
  = if sel then
      d0
    else
      d1
-------------------------------------------------------------------------------

muxBus :: (Bit, ([Bit], [Bit])) -> Out [Bit]
muxBus (sel, (a,b)) = (ziP >-> maP (muxBit sel)) (a, b)

-------------------------------------------------------------------------------

muxBus' :: (Bit, ([Bit], [Bit])) -> Out [Bit]
muxBus' (sel, (a,b)) = (ziP >->  maP (muxBit' sel)) (a, b)

-------------------------------------------------------------------------------

vreg :: Bit -> [Bit] -> Out [Bit]
vreg clk = maP (fd clk)

-------------------------------------------------------------------------------