Kansas Lava is a Haskell library that provides abstractions and powerful combinations to describe and simulate hardware circuits. Hardware descriptions are strongly typed and provide a means to describe hardware in a clear manner and express values that flow between components. You can describe both sequential and combinatorial circuits using Kansas Lava. To install the same on Fedora 20 (x86_64), follow the steps described below:

Download petersen/cabal-install repo from https://copr.fedoraproject.org/coprs/petersen/cabal-install/. If you do not have wget installed on your system, install it first using the following command:


# yum install wget

You can then run the following from /etc/yum.repos.d directory:


$ wget https://copr.fedoraproject.org/

Install cabal-install and Git.


# yum install cabal-install git

Download petersen/ghc-7.8 repo from https://copr.fedoraproject.org/coprs/petersen/ghc-7.8/ to /etc/yum.repos.d using:


# wget https://copr.fedoraproject.org/

Install Glasgow Haskell compiler (GHC) using:


# yum install ghc

Update the cabal local package list and install sized types using:


$ cabal update
$ cabal install sized-types

Obtain the latest Kansas Lava code from GitHub using:


$ git clone https://github.com/ku-fpg/

Enter the following into the checked out Kansas Lava directory, compile and install the same as follows:


$ cabal install –only-dependencies
$ cabal build
$ cabal install

The tool can help verify the behaviour of a circuit in simulation, generate VHDL for synthesis and also validate whether the generated VHDL is the same as the simulated circuit.

A simple code for the half adder combinatorial circuit is given below:


import Language.KansasLava

halfAdd :: Signal i Bool -> Signal i
Bool -> (Signal i Bool, Signal i Bool)
halfAdd a b = (carry, sum)
where carry = a `and2` b
sum = a `xor2` b

Execute the above example using Glasgow Haskell compiler (GHCi):


$ ghci halfAdd.hs
ghci> halfAdd low high
ghci> halfAdd high high

Kansas Lava implements the idea of an undefined Signal, where binary functions like and2, or2 and xor2 are short circuited. With unknown values, these function as hardware gates.


ghci> :m + Language.KansasLava

ghci> undefinedS `or2` low
ghci> undefinedS `or2` high
ghci> undefinedS `and2` low
ghci> undefinedS `and2` high
ghci> undefinedS `xor2` low
ghci> undefinedS `xor2` high

mux is a function that can be used to select an input. With low input, it picks the first argument in the tuple, and with high input, it picks the second argument in the tuple.

GPS on Raspberry Pi


ghci> :m + Language.KansasLava

ghci> mux low (0,1) :: Signal c Int

ghci> mux high (0,1) :: Signal c Int

Kansas Lava provides a number of instance declarations and overloadings for the signal module.


instance (Num a, Rep a) => Num (Signal
i a)

instance (Bounded a, Rep a) => Bounded
(Signal i a)

instance (Show a, Bits a, Rep a) => Bits
(Signal i a)

The overloading enables you to use Haskell expressions with Signal c and can be helpful in describing hardware signals.


ghci> :m + Language.KansasLava

ghci> 2 :: Signal c Int
ghci> 2 * 4 :: Signal c Int

ghci> :m + Data.Bits
ghci> 8 `shiftR` 2 :: Signal c Int

The clock class can change a signal with time and is used for sequential values. It provides two important functions: register and delay. The register function accepts an initial value and an input sequence as arguments, while delay function is similar to register function with the initial value undefined. Register function behaves like D edge-triggered flip-flop with an internal clock. toS function converts a set of values into a signal. For example,


ghci> :m + Language.KansasLava

ghci> let xs = toS [1..5] :: Seq Int
ghci> xs
1 | 2 | 3 | 4 | 5 | ? .
ghci> delay xs
? | 1 | 2 | 3 | 4 | 5 | ? .
ghci> register 10 xs
10 | 1 | 2 | 3 | 4 | 5 | ? .

Kansas Lava has support for both fixed-width signed and unsigned numbers. An unsigned X4 is a 4-bit unsigned number.


ghci> :m + Data.Sized.Matrix
ghci> :m + Data.Sized.Unsigned

ghci> 100 :: U4

ghci> :m + Data.Sized.Signed

ghci> 100 :: S8
ghci> 100 + 100 :: S8
ghci> [minBound..maxBound] :: [U4]

It also provides support for fixed-width matrices. A matrix with four elements of boolean type is represented by Matrix U2 bool. These are useful when specifying irregular-sized values in hardware.


ghci> :m + Data.Sized.Matrix

ghci> let m = matrix [1,2,3,4] ::
Matrix U2 Int

ghci> :m + Data.Array.IArray

ghci> m ! 1
ghci> m ! 0

The pack utility provides a means to combine multiple signals into a single signal, and the unpack combinator does the reverse. These allow flexible-type representation of data in the program. For example,


ghci> :m + Language.KansasLava

ghci> let p = toS (cycle [False, True])
:: Seq Bool
ghci> let q = toS [1..] :: Seq Int
ghci> let pq = pack (p, q) :: Seq (Bool,
ghci> takeS 2 pq
(low,1) | (high,2) | (?,?) .

Versatile Object Counter

Apart from signal types, circuits can also be built from Haskell functions.

Consider a ROM that stores the cube of a value. funMapXXX function when used directly is similar to an asynchronous ROM.


ghci> :m + Language.KansasLava
ghci> :set -XScopedTypeVariables

ghci> let cubeROM = funMapXXX(\(x::Int)
-> return(x*x*x))

ghci> takeS 5 $ cubeROM $ toS [1..] ::
Seq Int
1 | 8 | 27 | 64 | 125 | ? .

Kansas Lava also provides various coerce functions that can be used between two signal types, as long as both types have the same bit-width. For example,


ghci> :m + Language.KansasLava

ghci> let a = toS (cycle [False, True])
:: Seq Bool
ghci> takeS 5 a
low | high | low | high | low | ? .

ghci> takeS 5 $ (unsigned) a :: Seq Int
0 | 1 | 0 | 1 | 0 | ? .

Circuits can be debugged using probes, which present a way to observe intermediate values in shallow embedded circuits without modifying the circuit interface. These are inserted in the function using probeS function.


ghci> :m + Language.KansasLava

ghci> setProbesAsTrace (appendFile
ghci> let xs = toS (cycle [False, True])
:: Seq Bool
ghci> let ys = probeS “ys” (takeS 5 $
(unsigned) xs :: Seq Int)
ghci> takeS 5 ys
0 | 1 | 0 | 1 | 0 | ? .
ghci> :!cat DEBUG

An important purpose of Kansas Lava is to produce useful VHDL. Hardware descriptions are converted into efficient VHDL. Kansas Lava programs are extracted into a Kansas Lava entity graph (KLEG), which is an abstract representation of the netlist. This can be optimised and written into VHDL. The generic netlist rendering tool can also target Verilog. Kansas Lava can also generate and execute a VHDL test bench. mkTestbench function can be used to generate a test bench.

The code is listed below:


import Language.KansasLava
import Language.KansasLava.Fabric
import Language.KansasLava.VHDL

halfAdd :: Signal i Bool -> Signal i
Bool -> (Signal i Bool, Signal i Bool)
halfAdd a b = (carry, sum)
where carry = a `and2` b
sum = a `xor2` b

fabric :: Fabric ()
fabric = do
i0 i1 let (c,s) = halfAdd i0 i1
outStdLogic “carry” c
outStdLogic “sum” s

main = do
k writeVhdlCircuit “halfAdd” “halfAdd.
vhd” k
mkTestbench “halfAdd” “.” k

The above code can be compiled and run using:

$ ghc –make halfAdd.hs

$ ./halfAdd

Following files are created in the directory where halfAdd is executed: halfAdd.vhd, halfAdd_tb.vhd and halfAdd.do. halfAdd.vhd contains the VHDL implementation of the circuit, halfAdd_tb.vhd contains testbench VHDL and halfAdd.do contains a ModelSim script to run the test bench.

halfAdd.vhd file is listed below:



library IEEE;
use work.lava.all;
use work.all;
entity halfAdd is
port(clk : in std_logic;
i0 : in std_logic;
i1 : in std_logic;
carry : out std_logic;
sum : out std_logic);
end entity halfAdd;
architecture str of halfAdd is
signal sig_6_o0 : std_logic;
signal sig_7_o0 : std_logic;
signal sig_2_o0 : std_logic;
signal sig_3_o0 : std_logic;
signal sig_5_o0 : std_logic;
signal sig_4_o0 : std_logic;
sig_6_o0 <= sig_7_o0;
sig_7_o0 <= (sig_4_o0 xor sig_5_o0);
sig_2_o0 <= sig_3_o0;
sig_3_o0 <= (sig_4_o0 and sig_5_o0);
sig_5_o0 <= i1;
sig_4_o0 <= i0;
carry <= sig_2_o0;
sum <= sig_6_o0;
end architecture str;

halfAdd_tb.vhd file is listed below:


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_textio.all;
library std;
use std.textio.all;
library work;
entity halfAdd_tb is
end entity halfAdd_tb;
architecture sim of halfAdd_tb is
signal clk :
std_logic := ‘1’;
signal rst : std_logic := ‘0’;
constant input_size : integer := 16;
constant output_size : integer := 16;
signal input : std_logic_vector(3 downto 0):= (others => ‘0’);
signal output : std_logic_vector(3
downto 0);
runtest: process is
FILE halfAdd_input : TEXT open read_
mode IS “halfAdd.in.tbf”;
FILE halfAdd_output : TEXT open write_
mode IS “halfAdd.out.tbf”;
VARIABLE line_in,line_out : LINE;
variable input_var : std_logic_vector(3
downto 0);
variable output_var : std_logic_
vector(3 downto 0);
variable needs_rst : boolean := false;
while not endfile (halfAdd_input) loop
READLINE(halfAdd_input, line_in);
clk <= ‘1’;
wait for 5 ns;
input <= input_var;
if needs_rst then
rst <= ‘1’;
end if;
output(3 downto 2) <= input_var(3
downto 2);
wait for 20 ns;
clk <= ‘0’;
wait for 20 ns;
if needs_rst then
rst <= ‘0’;
needs_rst := false;
end if; output_var := output;
WRITE(line_out, output_var);
WRITELINE(halfAdd_output, line_out);
wait for 5 ns;
end loop;
end process;
dut: entity work.halfAdd
port map (
clk => clk,
i1 => input(2),
i0 => input(3),
sum => output(0),
carry => output(1)

end architecture sim;

halfAdd.do file is listed below:


vlib mywork
vcom -work mywork Lava.vhd
if [catch {vcom -work mywork halfAdd.
vhd} einfo] {
puts $einfo
} else {
vcom -work mywork halfAdd_tb.vhd
vsim -lib mywork halfAdd_tb
add wave -r /*
run -all

Download source code: click here

Shakthi Kannan is MS in information technology from Rochester Institute of Technology, Rochester, New York, USA. Currently, he is working as senior R&D engineer at Manufacturing System Insights, Chennai. He is a software enthusiast who blogs at shakthimaan.com


Please enter your comment!
Please enter your name here