diff --git a/clash-shake.cabal b/clash-shake.cabal
--- a/clash-shake.cabal
+++ b/clash-shake.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           clash-shake
-version:        0.1.1
+version:        0.2.0
 synopsis:       Shake rules for building Clash programs
 description:    Shake rules for building Clash programs and synthesizing FPGA
                 configuration. Contains build rules for Xilinx ISE and Xilinx Vivado
@@ -25,6 +25,7 @@
     template/xilinx-vivado/project-build.tcl.mustache
     template/xilinx-vivado/project.tcl.mustache
     template/xilinx-vivado/upload.tcl.mustache
+    template/intel-quartus/project.tcl.mustache
 
 source-repository head
   type: git
@@ -34,6 +35,8 @@
   exposed-modules:
       Clash.Shake
       Clash.Shake.Xilinx
+      Clash.Shake.Intel
+      Clash.Shake.SymbiFlow
   other-modules:
       Paths_clash_shake
   hs-source-dirs:
diff --git a/src/Clash/Shake.hs b/src/Clash/Shake.hs
--- a/src/Clash/Shake.hs
+++ b/src/Clash/Shake.hs
@@ -2,11 +2,15 @@
 module Clash.Shake
     ( HDL(..)
     , nestedPhony
+    , (|>)
+
+    , useConfig
     , ClashKit(..)
     , clashRules
     , SynthKit(..)
+
     , binImage
-    , useConfig
+
     , toolchain
     ) where
 
@@ -161,5 +165,8 @@
     let exe = case (wrap, root) of
             (Just wrap, _) -> [wrap, tool]
             (Nothing, Just root) -> [root </> tool]
-            (Nothing, Nothing) -> error $ printf "%s or %s must be set in build.mk" (name <> "_ROOT") name
+            (Nothing, Nothing) -> [tool]
     return $ exe ++ args
+
+(|>) :: String -> Action () -> (String, Action ())
+(|>) = (,)
diff --git a/src/Clash/Shake/Intel.hs b/src/Clash/Shake/Intel.hs
new file mode 100644
--- /dev/null
+++ b/src/Clash/Shake/Intel.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE OverloadedStrings, RecordWildCards, TemplateHaskell #-}
+module Clash.Shake.Intel
+    ( Target(..), de0Nano
+    , quartus
+    ) where
+
+import Clash.Shake
+
+import Development.Shake
+import Development.Shake.Command
+import Development.Shake.FilePath
+import Development.Shake.Config
+
+import Text.Mustache
+import qualified Text.Mustache.Compile.TH as TH
+import Data.Aeson
+
+import qualified Data.Text as T
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text.IO as T
+
+data Target = Target
+    { targetFamily :: String
+    , targetDevice :: String
+    }
+
+targetMustache Target{..} =
+    [ "targetFamily"  .= T.pack targetFamily
+    , "targetDevice"  .= T.pack targetDevice
+    ]
+
+de0Nano :: Target
+de0Nano = Target "Cyclone IV E" "EP4CE22F17C6"
+
+quartus :: Target -> ClashKit -> FilePath -> FilePath -> String -> Rules SynthKit
+quartus fpga kit@ClashKit{..} outDir srcDir topName = do
+    let projectName = topName
+        rootDir = joinPath . map (const "..") . splitPath $ outDir
+
+    let quartus tool args = cmd_ (Cwd outDir) =<< toolchain "QUARTUS" tool args
+
+    let getFiles dir pats = getDirectoryFiles srcDir [ dir </> pat | pat <- pats ]
+        hdlSrcs = getFiles "src-hdl" ["*.vhdl", "*.v", "*.sv"]
+        tclSrcs = getFiles "src-hdl" ["*.tcl"]
+        constrSrcs = getFiles "src-hdl" ["*.sdc"]
+        ipCores = getFiles "ip" ["//*.qip"]
+
+    outDir <//> "*.tcl" %> \out -> do
+        srcs1 <- manifestSrcs
+        srcs2 <- hdlSrcs
+        tcls <- tclSrcs
+        constrs <- constrSrcs
+        cores <- ipCores
+
+        let template = $(TH.compileMustacheFile "template/intel-quartus/project.tcl.mustache")
+        let values = object . mconcat $
+                [ [ "project" .= T.pack projectName ]
+                  , [ "top" .= T.pack topName ]
+                  , targetMustache fpga
+                  , [ "srcs" .= mconcat
+                             [ [ object [ "fileName" .= (rootDir </> src) ] | src <- srcs1 ]
+                               , [ object [ "fileName" .= (rootDir </> srcDir </> src) ] | src <- srcs2 ]
+                             ]
+                    ]
+                  , [ "tclSrcs" .= [ object [ "fileName" .= (rootDir </> srcDir </> src) ] | src <- tcls ] ]
+                  , [ "ipcores" .= [ object [ "fileName" .= (rootDir </> srcDir </> core) ] | core <- cores ] ]
+                  , [ "constraintSrcs" .= [ object [ "fileName" .= (rootDir </> srcDir </> src) ] | src <- constrs ] ]
+                ]
+        writeFileChanged out . TL.unpack $ renderMustache template values
+
+    outDir </> "ip" <//> "*" %> \out -> do
+            let src = srcDir </> makeRelative outDir out
+            copyFileChanged src out
+
+    outDir </> topName <.> "sof" %> \_out -> do
+        srcs1 <- manifestSrcs
+        srcs2 <- hdlSrcs
+        cores <- ipCores
+        need $ mconcat
+            [ [ outDir </> projectName <.> "tcl" ]
+              , [ src | src <- srcs1 ]
+              , [ srcDir </> src | src <- srcs2 ]
+              , [ outDir </> core | core <- cores ]
+            ]
+        quartus "quartus_sh" ["-t", projectName <.> "tcl"]
+
+    return $ SynthKit
+        { bitfile = outDir </> topName <.> "sof"
+          , phonies =
+              [ "quartus" |> do
+                  need [outDir </> projectName <.> "tcl"]
+                  quartus "quartus_sh" ["-t", outDir </> projectName <.> "tcl"]
+              ]
+        }
diff --git a/src/Clash/Shake/SymbiFlow.hs b/src/Clash/Shake/SymbiFlow.hs
new file mode 100644
--- /dev/null
+++ b/src/Clash/Shake/SymbiFlow.hs
@@ -0,0 +1,100 @@
+{-# LANGUAGE OverloadedStrings, RecordWildCards, TemplateHaskell #-}
+module Clash.Shake.SymbiFlow
+    ( xilinx7
+    ) where
+
+import Clash.Shake
+import qualified Clash.Shake.Xilinx as Xilinx
+
+import Development.Shake
+import Development.Shake.Command
+import Development.Shake.FilePath
+import Development.Shake.Config
+
+xilinx7 :: Xilinx.Target -> ClashKit -> FilePath -> FilePath -> String -> Rules SynthKit
+xilinx7 target@Xilinx.Target{..} kit@ClashKit{..} outDir srcDir topName = do
+    let rootDir = joinPath . map (const "..") . splitPath $ outDir
+
+    let symbiflow' :: String -> [String] -> Action ()
+        symbiflow' tool args = cmd_ (EchoStdout False) (Cwd outDir) =<< toolchain "SYMBIFLOW" tool args
+        symbiflow :: String -> [String] -> Action ()
+        symbiflow tool args = cmd_ (EchoStdout False) =<< toolchain "SYMBIFLOW" tool args
+
+    let getFiles dir pats = map (srcDir </>) <$> getDirectoryFiles srcDir [ dir </> pat | pat <- pats ]
+        verilogSrcs = getFiles "src-hdl" ["*.v"]
+        xdcSrcs = getFiles "src-hdl" ["*.xdc" ]
+
+    outDir </> topName <.> "eblif" %> \out -> do
+        srcs <- manifestSrcs
+        verilogs <- verilogSrcs
+        xdcs <- xdcSrcs
+        need $ verilogs <> xdcs
+
+        symbiflow' "symbiflow_synth" $
+          [ "-d", targetFamily
+          , "-p", Xilinx.targetPart target
+          , "-t", topName
+          ] ++
+          [ "-v " <> rootDir </> src | src <- srcs <> verilogs ] ++
+          [ "-x " <> rootDir </> xdc | xdc <- xdcs ]
+
+    outDir <//> "*.net" %> \out -> do
+        let eblif = out -<.> "eblif"
+        need [eblif]
+        symbiflow' "symbiflow_pack" $
+          [ "-d", targetDevice <> "_test"
+          , "-e", takeFileName eblif
+          ]
+
+    outDir <//> "*.place" %> \out -> do
+        let eblif = out -<.> "eblif"
+            net = out -<.> "net"
+        need [eblif, net]
+        symbiflow' "symbiflow_place" $
+          [ "-d", targetDevice <> "_test"
+          , "-P", Xilinx.targetPart target
+          , "-e", takeFileName eblif
+          , "-n", takeFileName net
+          ]
+
+    outDir <//> "*.route" %> \out -> do
+        let eblif = out -<.> "eblif"
+            place = out -<.> "place"
+        need [eblif, place]
+        symbiflow' "symbiflow_route" $
+          [ "-d", targetDevice <> "_test"
+          , "-e", takeFileName eblif
+          ]
+
+    outDir <//> "*.fasm" %> \out -> do
+        let eblif = out -<.> "eblif"
+            route = out -<.> "route"
+        need [eblif, route]
+        symbiflow' "symbiflow_write_fasm" $
+          [ "-d", targetDevice <> "_test"
+          , "-e", takeFileName eblif
+          ]
+
+    outDir <//> "*.bit" %> \out -> do
+        let fasm = out -<.> "fasm"
+        need [fasm]
+        symbiflow "symbiflow_write_bitstream" $
+          [ "-d", targetFamily
+          , "-p", Xilinx.targetPart target
+          , "-f", fasm
+          , "-b", out
+          ]
+
+    let bitfile = outDir </> topName <.> "bit"
+
+    return SynthKit
+        { bitfile = bitfile
+        , phonies =
+            [ "upload" |> openFPGALoader ["-c", "digilent"] bitfile
+            ]
+        }
+
+openFPGALoader :: [String] -> FilePath -> Action ()
+openFPGALoader args bitfile = do
+    need [bitfile]
+    cmd_ =<< toolchain "OPENFPGALOADER" "openFPGALoader" (args ++ [bitfile])
diff --git a/src/Clash/Shake/Xilinx.hs b/src/Clash/Shake/Xilinx.hs
--- a/src/Clash/Shake/Xilinx.hs
+++ b/src/Clash/Shake/Xilinx.hs
@@ -1,8 +1,10 @@
 {-# LANGUAGE OverloadedStrings, RecordWildCards, TemplateHaskell #-}
 module Clash.Shake.Xilinx
-    ( XilinxTarget(..), papilioPro, papilioOne, nexysA750T
-    , xilinxISE
-    , xilinxVivado
+    ( Target(..), targetPart
+    , ise
+    , vivado
+
+    , papilioPro, papilioOne, nexysA750T
     ) where
 
 import Clash.Shake
@@ -15,37 +17,42 @@
 import Text.Mustache
 import qualified Text.Mustache.Compile.TH as TH
 import Data.Aeson
+import qualified Data.Aeson.Types as Aeson
 
 import qualified Data.Text as T
 import qualified Data.Text.Lazy as TL
 import qualified Data.Text.IO as T
 
-data XilinxTarget = XilinxTarget
+data Target = Target
     { targetFamily :: String
     , targetDevice :: String
     , targetPackage :: String
-    , targetSpeed :: String
+    , targetSpeed :: Word
     }
 
-targetMustache XilinxTarget{..} =
+targetPart :: Target -> String
+targetPart Target{..} = targetDevice <> targetPackage <> "-" <> show targetSpeed
+
+targetMustache :: Target -> [Aeson.Pair]
+targetMustache target@Target{..} =
     [ "targetFamily"  .= T.pack targetFamily
     , "targetDevice"  .= T.pack targetDevice
     , "targetPackage" .= T.pack targetPackage
-    , "targetSpeed"   .= T.pack targetSpeed
-    , "part"          .= T.pack (targetDevice <> targetPackage <> targetSpeed)
+    , "targetSpeed"   .= targetSpeed
+    , "part"          .= T.pack (targetPart target)
     ]
 
-papilioPro :: XilinxTarget
-papilioPro = XilinxTarget "Spartan6" "xc6slx9" "tqg144" "-2"
+papilioPro :: Target
+papilioPro = Target "Spartan6" "xc6slx9" "tqg144" 2
 
-papilioOne :: XilinxTarget
-papilioOne = XilinxTarget "Spartan3E" "xc3s500e" "vq100" "-5"
+papilioOne :: Target
+papilioOne = Target "Spartan3E" "xc3s500e" "vq100" 5
 
-nexysA750T :: XilinxTarget
-nexysA750T = XilinxTarget "Artrix7" "xc7a50t" "icsg324" "-1L"
+nexysA750T :: Target
+nexysA750T = Target "artix7" "xc7a50t" "csg324" 1
 
-xilinxISE :: XilinxTarget -> ClashKit -> FilePath -> FilePath -> String -> Rules SynthKit
-xilinxISE fpga kit@ClashKit{..} outDir srcDir topName = do
+ise :: Target -> ClashKit -> FilePath -> FilePath -> String -> Rules SynthKit
+ise fpga kit@ClashKit{..} outDir srcDir topName = do
     let projectName = topName
         rootDir = joinPath . map (const "..") . splitPath $ outDir
 
@@ -100,8 +107,8 @@
             ]
         }
 
-xilinxVivado :: XilinxTarget -> ClashKit -> FilePath -> FilePath -> String -> Rules SynthKit
-xilinxVivado fpga kit@ClashKit{..} outDir srcDir topName = do
+vivado :: Target -> ClashKit -> FilePath -> FilePath -> String -> Rules SynthKit
+vivado fpga kit@ClashKit{..} outDir srcDir topName = do
     let projectName = topName
         projectDir = outDir </> projectName
         xpr = projectDir </> projectName <.> "xpr"
@@ -184,6 +191,3 @@
                    vivadoBatch "upload.tcl"
             ]
         }
-
-(|>) :: String -> Action () -> (String, Action ())
-(|>) = (,)
diff --git a/template/intel-quartus/project.tcl.mustache b/template/intel-quartus/project.tcl.mustache
new file mode 100644
--- /dev/null
+++ b/template/intel-quartus/project.tcl.mustache
@@ -0,0 +1,111 @@
+# Mustache template for Intel Quartus projects
+# for more details see: Quartus II Handbook Version 11.0 Volume 2
+# and Quartus II Scripting Reference Manual
+
+set myScript "{{project}}.tcl"
+
+# Load Quartus Prime Tcl Project packages
+package require ::quartus::project
+
+# Add the next line to get the execute_flow command
+package require ::quartus::flow
+
+set need_to_close_project 0
+set make_assignments 1
+
+# This procedure will open a quartus project
+proc Open {prj_name} {
+    global need_to_close_project
+    # Check that the right project is open
+    if {[is_project_open]} {
+        if {[string compare $quartus(project) $prj_name]} {
+            puts "Project is not open"
+            set make_assignments 0
+        }
+    } else {
+        # Only open if not already open
+        if {[project_exists $prj_name]} {
+            set current_revision [get_current_revision $prj_name]
+                project_open -revision $current_revision $prj_name
+        } else {
+            project_new -revision $prj_name $prj_name
+        }
+        set need_to_close_project 1
+    }
+}
+
+# This procedure adds files to quartus .qsf file
+proc Addfile {src_file {library "lib"} } {
+    if [regexp {.vhdl?$} $src_file] {
+        set_global_assignment -name VHDL_FILE "$src_file" -library "$library"
+    } elseif [regexp {.sv?$} $src_file] {
+        set_global_assignment -name SYSTEMVERILOG_FILE "$src_file" -library "$library"
+    } elseif [regexp {.v?$} $src_file] {
+        set_global_assignment -name VERILOG_FILE "$src_file" -library "$library"
+    } else {
+        puts "Unknown file type: $src_file"
+    }
+    # Commit assignments
+    export_assignments
+}
+
+# Open project
+Open "{{project}}"
+
+# Make assignments
+if {$make_assignments} {
+    set_global_assignment -name FAMILY "{{targetFamily}}"
+    set_global_assignment -name DEVICE "{{targetDevice}}"
+    set_global_assignment -name PROJECT_OUTPUT_DIRECTORY "./"
+    set_global_assignment -name NUM_PARALLEL_PROCESSORS 2
+    set_global_assignment -name EDA_SIMULATION_TOOL "ModelSim-Altera (Verilog)"
+    set_global_assignment -name EDA_NETLIST_WRITER_OUTPUT_DIR ./netlist -section_id eda_simulation
+    set_global_assignment -name EDA_OUTPUT_DATA_FORMAT Verilog -section_id eda_simulation
+    set_global_assignment -name TIMEQUEST_MULTICORNER_ANALYSIS ON
+    set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
+    set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT -section_id Top
+    set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
+    set_global_assignment -name VERILOG_INPUT_VERSION SYSTEMVERILOG_2005
+    set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO"
+    set_global_assignment -name RESERVE_FLASH_NCE_AFTER_CONFIGURATION "USE AS REGULAR IO"
+    set_global_assignment -name RESERVE_DATA0_AFTER_CONFIGURATION "USE AS REGULAR IO"
+    set_global_assignment -name RESERVE_DATA1_AFTER_CONFIGURATION "USE AS REGULAR IO"
+    set_global_assignment -name RESERVE_DCLK_AFTER_CONFIGURATION "USE AS REGULAR IO"
+    set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
+
+    # Setup top entity
+    set_global_assignment -name TOP_LEVEL_ENTITY "{{top}}"
+
+    # Source settings
+    {{#tclSrcs}}
+    source "{{fileName}}"
+    {{/tclSrcs}}
+
+    # Source constrains
+    {{#constraintSrcs}}
+    set_global_assignment -name SDC_FILE "{{fileName}}"
+    {{/constraintSrcs}}
+
+    puts "$myScript: Adding sources to project..."
+
+    # Add QIP files (from ip dir)
+    {{#ipcores}}
+    set_global_assignment -name QIP_FILE "{{fileName}}"
+    {{/ipcores}}
+
+    # Add sources to the project
+    {{#srcs}}
+    Addfile "{{fileName}}"
+    {{/srcs}}
+
+    # Commit assignments
+    export_assignments
+}
+
+execute_flow -compile
+
+# Close project
+if {$need_to_close_project} {
+    puts "Closing project"
+    project_close
+}
diff --git a/template/xilinx-ise/project.tcl.mustache b/template/xilinx-ise/project.tcl.mustache
--- a/template/xilinx-ise/project.tcl.mustache
+++ b/template/xilinx-ise/project.tcl.mustache
@@ -134,7 +134,7 @@
    project set family "{{targetFamily}}"
    project set device "{{targetDevice}}"
    project set package "{{targetPackage}}"
-   project set speed "{{targetSpeed}}"
+   project set speed "-{{targetSpeed}}"
    project set top_level_module_type "HDL"
    project set synthesis_tool "XST (VHDL/Verilog)"
    project set simulator "ISim (VHDL/Verilog)"
@@ -176,7 +176,7 @@
    if { ! [ file exists "ipcore_dir/{{name}}.vhd" ] } {
        puts "$myScript: Regenerating {{name}}"
        cd ipcore_dir
-       run_cg_regen "{{name}}" {{targetDevice}}{{targetSpeed}}{{targetPackage}} VHDL CURRENT
+       run_cg_regen "{{name}}" "{{part}}" VHDL CURRENT
        cd ..
    }
    {{/ipcores}}
