├── .gitignore ├── icestick.pcf ├── Makefile ├── Demo.hs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | verilog/ 3 | demo.blif 4 | demo.txt 5 | demo.bin 6 | *.hi 7 | *.o 8 | Demo 9 | -------------------------------------------------------------------------------- /icestick.pcf: -------------------------------------------------------------------------------- 1 | set_io LED1 99 2 | set_io LED2 98 3 | set_io LED3 97 4 | set_io LED4 96 5 | set_io LED5 95 6 | set_io system1000 21 7 | 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | upload: demo.bin 3 | iceprog demo.bin 4 | 5 | test: Demo 6 | ./Demo 7 | 8 | clean: 9 | @rm -rf verilog 10 | @rm -f demo.blif demo.txt demo.bin 11 | 12 | Demo: Demo.hs 13 | clash Demo.hs 14 | 15 | verilog/Main/blinker.v: Demo.hs 16 | clash --verilog Demo.hs 17 | 18 | demo.blif: verilog/Main/blinker.v 19 | (cd verilog/Main; yosys -p "synth_ice40 -blif ../../demo.blif" $$(ls *.v|grep -v testbench.v)) 20 | 21 | demo.txt: demo.blif 22 | arachne-pnr -d 1k -p icestick.pcf demo.blif -o demo.txt 23 | 24 | demo.bin: demo.txt 25 | icepack demo.txt demo.bin 26 | 27 | -------------------------------------------------------------------------------- /Demo.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeFamilies #-} 3 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 4 | {-# LANGUAGE QuasiQuotes #-} 5 | {-# LANGUAGE ScopedTypeVariables #-} 6 | {-# LANGUAGE BinaryLiterals #-} 7 | -- | This module implements a simple digital stopwatch. 8 | module Main where 9 | 10 | import Prelude () -- no implicit Prelude! 11 | import Control.Applicative 12 | import Control.Arrow 13 | import qualified Data.List as List 14 | import Data.Traversable 15 | 16 | import CLaSH.Prelude 17 | import CLaSH.Sized.Vector 18 | import CLaSH.Signal 19 | import CLaSH.Signal.Explicit 20 | import CLaSH.Signal.Bundle 21 | 22 | type Word5 = Unsigned 5 23 | 24 | -- | Gives a signal once every second, for the duration of a single System Clock cycle. 25 | everySecond :: Signal Bool -- ^ trigger every one second 26 | everySecond = counter' fpgaFrequency 27 | -- $ (2 :: Word17) ^ (15 :: Word17) * (1000 :: Word17) -- or Signed 16? 28 | 29 | -- Here, I am using 32 bit arithmetic to count second, 30 | -- instead of splitting it into smaller counters and connecting them. 31 | fpgaFrequency :: Unsigned 27 32 | fpgaFrequency = 32768000 -- real 32MHz 33 | 34 | -- | Counter that cycles every time it gets a given @True@ signal at the input, 35 | -- and itself gives the current count, and also the @True@ signal only 36 | -- when the limit is reached. 37 | counter :: (Num s, Eq s) => 38 | s -> -- ^ number of clock cycles before overflow and reset 39 | Signal Bool -> -- ^ input trigger for the clock cycle (not the clock domain!) 40 | Unbundled (s, Bool) 41 | counter limit = fsm <^> 0 42 | where 43 | fsm st False = (st, (st, False)) 44 | fsm st True | limit == st+1 = (0, (0, True )) 45 | fsm st True = (st+1, (st+1, False)) 46 | 47 | -- | Simple counter without any inputs. 48 | -- Gives true signal only when the given limit is reached. 49 | counter' :: (Num s, Eq s) => 50 | s -> -- ^ number of clock cycles before overflow and reset 51 | Signal Bool 52 | counter' limit = (fsm <^> 0) $ signal () 53 | where 54 | fsm st () | limit == st = (0, True) 55 | | otherwise = (st+1, False) 56 | 57 | {-# ANN topEntity 58 | (defTop 59 | { t_name = "blinker" 60 | , t_inputs = [] 61 | , t_outputs = ["LED1", "LED2", "LED3", "LED4", "LED5"] 62 | , t_extraIn = [] 63 | , t_clocks = [ 64 | ] 65 | }) #-} 66 | 67 | -- | Top entity to implement 68 | topEntity :: Signal (Bit, Bit, Bit, Bit, Bit) 69 | topEntity = unpak <$> secondsCounter 70 | where 71 | secondPulse = counter' fpgaFrequency 72 | (secondsCounter, _) = counter (2^5) secondPulse 73 | 74 | unpak :: Word5 -> (Bit, Bit, Bit, Bit, Bit) 75 | unpak w = (w!0 76 | ,w!1 77 | ,w!2 78 | ,w!3 79 | ,w!4) 80 | 81 | -- * Here are helpers for simulation. 82 | -- | Takes every nth step of simulated signal. 83 | takeEvery :: Int -> [a] -> [a] 84 | takeEvery n = go 85 | where 86 | go [] = [] 87 | go (b:bs) = b:go (List.drop (fromIntegral n) bs) 88 | 89 | main :: IO () 90 | main = print 91 | $ takeEvery ((fromIntegral fpgaFrequency `div` 10)) 92 | $ sampleN (10*fromIntegral fpgaFrequency) topEntity 93 | 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Haskell deployed using fully open source toolchain to iCE40 2 | =========================================================== 3 | 4 | This is a simple demo for compiling of [Haskell](http://www.haskell.org) 5 | code into FPGA configuration. 6 | 7 | [_Field Programmable Gate Arrays_](https://en.wikipedia.org/wiki/Field-programmable_gate_array) (FPGAs) are 8 | a cheap and fast tool for prototyping hardware descriptions that can be 9 | later used for creating chips. 10 | 11 | [_Haskell_](http://www.haskell.org) is a lazy functional programming language, 12 | which lends itself readily to both very high level descriptions of software, 13 | and very low level descriptions of hardware, due its solid mathematical underpinnings. 14 | 15 | This demo uses fully open source toolchain: 16 | 17 | * [CλaSH](http://www.clash-lang.org/) for compiling Haskell into Verilog, 18 | * [Yosys](http://www.clifford.at/yosys/) [Verilog](https://en.wikipedia.org/wiki/Verilog) compiler to compile Verilog code into `.blif` format, 19 | * [Arachne Place-N-Route](https://github.com/cseed/arachne-pnr) to perform routing onto the _Lattice ICE40 H1K_ device, 20 | * [IceStorm](http://www.clifford.at/icestorm/) toolchain in order to generate FPGA bitstream and upload it 21 | into [Lattice IceStick](http://latticesemi.com/iCEstick) device. 22 | 23 | _This project used as a *project template* for your own experiments 24 | with Haskell and FPGAs._ In this case, please remove `README.md` file after cloning. 25 | 26 | This one uses a single counter to show a binary stopwatch with a range of 2⁵ seconds. 27 | 28 | Installing toolchain: 29 | --------------------- 30 | 1. First install the [IceStorm](http://www.clifford.at/icestorm/) toolchain: 31 | On the latest _Ubuntu_ you may install from the repository: 32 | ``` 33 | sudo apt-get install -y fpga-icestorm yosys arachne-pnr 34 | ``` 35 | 36 | Otherwise you might compile from the latest source: 37 | * IceStorm utilities themselves: 38 | 39 | ```bash 40 | git clone https://github.com/cliffordwolf/icestorm.git icestorm 41 | make -j4 -DPREFIX=$HOME/icestorm 42 | make -DPREFIX=$HOME/icestorm install 43 | ``` 44 | 45 | For Linux you also might want to enable write access through FTDI USB device: 46 | 47 | ``` 48 | cat - < /etc/udev/rules.d/53-lattice-ftdi.rules 49 | ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" 50 | EOF 51 | ``` 52 | 53 | * [Arachne PNR](https://github.com/cseed/arachne-pnr) tool: 54 | 55 | ```bash 56 | git clone https://github.com/cseed/arachne-pnr.git arachne-pnr 57 | cd arachne-pnr 58 | make -j$(nproc) -DDEST_DIR=$HOME/icestorm -DICEBOX=$HOME/icestorm/share/icebox/ 59 | make install 60 | ``` 61 | 62 | * Yosys Verilog compiler: 63 | 64 | ```bash 65 | git clone https://github.com/cliffordwolf/yosys.git yosys 66 | cd yosys 67 | make -j$(nproc) -DPREFIX=$HOME/icestorm 68 | make -DPREFIX=$HOME/icestorm install 69 | ``` 70 | 2. [CλaSH](http://www.clash-lang.org/) compiler based on [GHC](https://www.haskell.org/ghc/): 71 | * To install GHC and Cabal on Linux: 72 | 73 | ``` 74 | apt-get install ghc cabal-install 75 | ``` 76 | * To install GHC on Windows it is recommended to either use `.msi` package 77 | of [Haskell Platform](https://www.haskell.org/platform/) 78 | or [Stack](http://docs.haskellstack.org/en/stable/README/) installation utility. 79 | * From within this environment, use `cabal-install` to setup `clash-ghc` package: 80 | 81 | ```bash 82 | cabal install clash-ghc 83 | ``` 84 | 85 | Files in this repository: 86 | ---------------- 87 | 88 | * `Demo.hs` - contains Haskell code for the 32-second timer. 89 | 90 | * `icestick.pcf` - assigns names to _Lattice iCE40 H1K_ pins on [iCEStick](http://latticesemi.com/iCEstick) board. 91 | 92 | * `icoboard.pcf` - assigns names to _Lattice iCE40 H8K_ pins on [IcoBoard](http://www.icoboard.org). 93 | 94 | Compilation steps: 95 | ------------------ 96 | While there will be Makefile, you might want to look through the build process step by step: 97 | 98 | 1. For simulation just compile it with `clash` as a Haskell program, and run: 99 | 100 | ```bash 101 | clash Demo.hs 102 | ``` 103 | 104 | 2. For Verilog generation: 105 | 106 | * run interpreter: `clash --interactive Demo.hs` 107 | * enter `:verilog` in the interpreter to generate `.verilog` code 108 | 109 | 3. To compile Verilog into `.blif` netlist format: 110 | 111 | ``` 112 | yosys -p "synth_ice40 -blif demo.blif" Verilog/Main/Demo_TopEntity.v 113 | ``` 114 | 115 | 4. To route `.blif` netlist onto Lattice IceStick device: 116 | 117 | ``` 118 | arachne-pnr -d 1k -p demo.pcf demo.blif -o demo.txt 119 | ``` 120 | 121 | 5. To compile routed netlist into bitstream: 122 | 123 | ``` 124 | icepack demo.txt demo.bin 125 | ``` 126 | 127 | 6. To upload bitstream onto the FPGA: 128 | 129 | ``` 130 | iceprog demo.bin 131 | ``` 132 | NOTE: If you forgot to add the relevant udev rule, you might need to use `sudo` here. 133 | 134 | Or use `Makefile`: 135 | 136 | ``` 137 | make test 138 | make upload 139 | ``` 140 | 141 | Future plans: 142 | ------------- 143 | It would be nice to wrap Ice40 PLL configuration as a custom block: 144 | 145 | * For Verilog code see: [iCEStick PLLs](https://www.reddit.com/r/yosys/comments/3yrq6d/are_plls_supported_on_the_icestick_hw/) 146 | 147 | * Wrapping of custom blocks in CλaSH code is 148 | [described in this documentation](http://hackage.haskell.org/package/clash-prelude-0.7.5/docs/CLaSH-Tutorial.html#g:13). 149 | 150 | * Unfortunately making it work requires some [change in the way CλaSH handles clock annotations](https://github.com/clash-lang/clash-compiler/issues/145). 151 | 152 | _Until then one needs to change Verilog code by themselves, asserting `1` for `system1000_rstn`_ 153 | --------------------------------------------------------------------------------