Skip to content

Commit 962610a

Browse files
committed
First clash-testbench prototype
1 parent 117e2ba commit 962610a

19 files changed

+1548
-0
lines changed

cabal.project

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ packages:
99
./clash-prelude
1010
./clash-prelude-hedgehog
1111
./clash-cores
12+
./clash-testbench
1213
./tests
1314

1415
write-ghc-environment-files: always

clash-testbench/LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2023 QBayLogic B.V.
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
7+
1. Redistributions of source code must retain the above copyright notice, this
8+
list of conditions and the following disclaimer.
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation
11+
and/or other materials provided with the distribution.
12+
13+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

clash-testbench/clash-testbench.cabal

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
cabal-version: 2.2
2+
3+
name: clash-testbench
4+
version: 0.1.0.0
5+
synopsis: Design your TestBenches in Clash
6+
description: Design your TestBenches in Clash
7+
bug-reports: https://github.com/clash-lang/clash-compiler/issues
8+
license: BSD-2-Clause
9+
license-file: LICENSE
10+
author: QBayLogic B.V.
11+
maintainer: devops@qbaylogic.com
12+
copyright: Copyright © 2023, QBayLogic B.V.
13+
category: Hardware
14+
15+
library
16+
default-language: Haskell2010
17+
default-extensions:
18+
DataKinds
19+
FlexibleContexts
20+
FlexibleInstances
21+
GADTs
22+
ImplicitParams
23+
LambdaCase
24+
MagicHash
25+
MultiWayIf
26+
NamedFieldPuns
27+
RankNTypes
28+
RecordWildCards
29+
ScopedTypeVariables
30+
TupleSections
31+
TypeApplications
32+
TypeFamilies
33+
ViewPatterns
34+
ghc-options:
35+
-Wall -Wcompat
36+
exposed-modules:
37+
Clash.Testbench
38+
Clash.Testbench.Signal
39+
Clash.Testbench.Input
40+
Clash.Testbench.Output
41+
Clash.Testbench.Simulate
42+
other-modules:
43+
Clash.Testbench.Internal.ID
44+
Clash.Testbench.Internal.Signal
45+
Clash.Testbench.Internal.Monad
46+
Clash.Testbench.Internal.Auto
47+
build-depends:
48+
base,
49+
mtl,
50+
array,
51+
containers,
52+
bytestring,
53+
clash-ffi,
54+
clash-prelude,
55+
hs-source-dirs: src

clash-testbench/example/Calculator.hs

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE DeriveAnyClass #-}
3+
{-# LANGUAGE DeriveGeneric #-}
4+
{-# LANGUAGE DeriveLift #-}
5+
{-# LANGUAGE FlexibleContexts #-}
6+
{-# LANGUAGE NoImplicitPrelude #-}
7+
{-# LANGUAGE TypeFamilies #-}
8+
module Calculator where
9+
10+
import Clash.Prelude hiding (Word)
11+
12+
type Word = Signed 4
13+
data OPC a = ADD | MUL | Imm a | Pop | Push
14+
deriving (Lift, Generic, BitPack, NFDataX, Show)
15+
16+
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
17+
(f .: g) a b = f (g a b)
18+
19+
infixr 9 .:
20+
21+
alu :: Num a => OPC a -> a -> a -> Maybe a
22+
alu ADD = Just .: (+)
23+
alu MUL = Just .: (*)
24+
alu (Imm i) = const . const (Just i)
25+
alu _ = const . const Nothing
26+
27+
pu :: (Num a, Num b)
28+
=> (OPC a -> a -> a -> Maybe a)
29+
-> (a, a, b) -- Current state
30+
-> (a, OPC a) -- Input
31+
-> ( (a, a, b) -- New state
32+
, (b, Maybe a) -- Output
33+
)
34+
pu _ (op1, _, cnt) (dmem, Pop) = ((dmem, op1, cnt - 1), (cnt, Nothing) )
35+
pu _ (op1, op2, cnt) ( _, Push) = ((op1, op2, cnt + 1) , (cnt, Nothing) )
36+
pu a (op1, op2, cnt) ( _, opc) = ((op1, op2, cnt) , (cnt, a opc op1 op2))
37+
38+
datamem :: (KnownNat n, Integral i)
39+
=> Vec n a -- Current state
40+
-> (i, Maybe a) -- Input
41+
-> (Vec n a, a) -- (New state, Output)
42+
datamem mem (addr,Nothing) = (mem ,mem !! addr)
43+
datamem mem (addr,Just val) = (replace addr val mem,mem !! addr)
44+
45+
topEntity
46+
:: Clock System
47+
-> Reset System
48+
-> Enable System
49+
-> Signal System (OPC Word)
50+
-> Signal System (Maybe Word)
51+
topEntity = exposeClockResetEnable go where
52+
go i = val where
53+
(addr,val) = (pu alu <^> (0,0,0 :: Unsigned 3)) (mem,i)
54+
mem = (datamem <^> initMem) (addr,val)
55+
initMem = replicate d8 0
56+
{-# NOINLINE topEntity #-}

clash-testbench/example/LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2023 QBayLogic B.V.
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
7+
1. Redistributions of source code must retain the above copyright notice, this
8+
list of conditions and the following disclaimer.
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation
11+
and/or other materials provided with the distribution.
12+
13+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

clash-testbench/example/Main.hs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{-# LANGUAGE RecursiveDo #-}
2+
module Main where
3+
4+
import Clash.Testbench
5+
6+
import Calculator (OPC(..))
7+
import qualified Calculator (topEntity)
8+
9+
myTestbench
10+
:: TB ()
11+
myTestbench = mdo
12+
input <- inputFromList Pop [Imm 1, Push, Imm 2, Push, Pop, Pop, Pop, ADD]
13+
output <- ("topEntity" @@ Calculator.topEntity) auto auto auto input
14+
watch input
15+
watch output
16+
17+
main :: IO ()
18+
main = simulate 10 myTestbench
19+
20+
foreign export ccall "clash_ffi_main"
21+
ffiMain :: IO ()
22+
23+
ffiMain :: IO ()
24+
ffiMain = simulateFFI 10 myTestbench

clash-testbench/example/Setup.hs

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
module Main where
2+
3+
import Control.Monad
4+
import Data.Maybe
5+
import Distribution.PackageDescription.Utils
6+
import Distribution.Simple
7+
import Distribution.Simple.Build
8+
import Distribution.Simple.BuildPaths
9+
import Distribution.Simple.Setup
10+
import Distribution.Simple.Utils
11+
import Distribution.System
12+
import Distribution.Types.ForeignLib
13+
import Distribution.Types.ForeignLibType
14+
import Distribution.Types.GenericPackageDescription
15+
import Distribution.Types.HookedBuildInfo
16+
import Distribution.Types.LocalBuildInfo
17+
import Distribution.Types.PackageDescription
18+
import Distribution.Types.UnqualComponentName
19+
import Distribution.Verbosity
20+
import System.Directory
21+
import System.FilePath
22+
23+
main :: IO ()
24+
main =
25+
defaultMainWithHooks simpleUserHooks
26+
{ postBuild = ffiPostBuild }
27+
28+
ffiPostBuild
29+
:: Args
30+
-> BuildFlags
31+
-> PackageDescription
32+
-> LocalBuildInfo
33+
-> IO ()
34+
ffiPostBuild args flags desc info = do
35+
-- Create lib/ in the project directory
36+
let outPath = takeDirectory (fromJust $ pkgDescrFile info) </> "lib"
37+
createDirectoryIfMissing True outPath
38+
39+
-- Copy each foreign library to lib/
40+
forM_ (foreignLibs desc) $ \flib ->
41+
let name = unUnqualComponentName (foreignLibName flib)
42+
dLib = buildDir info </> name </> flibTargetName info flib
43+
in copySoAsVpl outPath dLib
44+
45+
-- Do the normal post-build hook action
46+
postBuild simpleUserHooks args flags desc info
47+
48+
-- | Get the name of the library that will be written to disk when building
49+
-- the library. Lifted from `Distribution.Simple.GHC`.
50+
--
51+
flibTargetName :: LocalBuildInfo -> ForeignLib -> String
52+
flibTargetName lbi flib =
53+
case (os, foreignLibType flib) of
54+
(Windows, ForeignLibNativeShared) -> nm <.> "dll"
55+
(Windows, ForeignLibNativeStatic) -> nm <.> "lib"
56+
(Linux, ForeignLibNativeShared) -> "lib" ++ nm <.> versionedExt
57+
(_other, ForeignLibNativeShared) ->
58+
"lib" ++ nm <.> dllExtension (hostPlatform lbi)
59+
(_other, ForeignLibNativeStatic) ->
60+
"lib" ++ nm <.> staticLibExtension (hostPlatform lbi)
61+
(_any, ForeignLibTypeUnknown) -> cabalBug "unknown foreign lib type"
62+
where
63+
nm :: String
64+
nm = unUnqualComponentName $ foreignLibName flib
65+
66+
os :: OS
67+
os = let (Platform _ os') = hostPlatform lbi
68+
in os'
69+
70+
-- If a foreign lib foo has lib-version-info 5:1:2 or
71+
-- lib-version-linux 3.2.1, it should be built as libfoo.so.3.2.1
72+
-- Libtool's version-info data is translated into library versions in a
73+
-- nontrivial way: so refer to libtool documentation.
74+
versionedExt :: String
75+
versionedExt =
76+
let nums = foreignLibVersion flib os
77+
in foldl (<.>) "so" (map show nums)
78+
79+
-- | Copy a file to the same directory, but change the extension to .vpl. This
80+
-- is needed for iverilog, as it will not load VPI modules which do not have
81+
-- either a .vpi or .vpl extension, unlike other simulators which will load
82+
-- the .so file that cabal normally produces.
83+
--
84+
copySoAsVpl :: FilePath -> FilePath -> IO ()
85+
copySoAsVpl outDir so =
86+
-- We use installMaybeExecutable file because it preserves the permissions
87+
-- of the original file. On my machine, just using installExecutableFile
88+
-- meant the permissions were *slightly* different.
89+
let outPath = replaceDirectory (replaceExtensions so "vpl") outDir
90+
in installMaybeExecutableFile verbose so outPath
91+

clash-testbench/example/cabal.project

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
packages: . .. ../../clash-ghc ../../clash-lib ../../clash-prelude ../../clash-ffi
2+
3+
write-ghc-environment-files: always
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
cabal-version: 2.4
2+
name: clash-testbench-example
3+
version: 0.1.0.0
4+
synopsis: Exmaple for using clash-testbench
5+
description: Exmaple for using clash-testbench
6+
bug-reports: https://github.com/clash-lang/clash-compiler/issues
7+
license: BSD-2-Clause
8+
license-file: LICENSE
9+
author: QBayLogic B.V.
10+
maintainer: devops@qbaylogic.com
11+
copyright: Copyright © 2023, QBayLogic B.V.
12+
category: Hardware
13+
14+
common basic-config
15+
default-language: Haskell2010
16+
ghc-options:
17+
-Wall -Wcompat
18+
-fplugin GHC.TypeLits.Extra.Solver
19+
-fplugin GHC.TypeLits.Normalise
20+
-fplugin GHC.TypeLits.KnownNat.Solver
21+
build-depends:
22+
base,
23+
clash-prelude,
24+
clash-testbench,
25+
ghc-typelits-extra,
26+
ghc-typelits-knownnat,
27+
ghc-typelits-natnormalise,
28+
29+
custom-setup
30+
setup-depends:
31+
base >= 4.11 && < 5,
32+
Cabal >= 2.4 && < 3.7,
33+
directory >= 1.3.6 && < 1.4,
34+
filepath >= 1.4.2 && < 1.5,
35+
36+
executable simulate
37+
import: basic-config
38+
main-is: Main.hs
39+
other-modules: Calculator
40+
-- this option is required, since clash-ffi and clash-testbench come
41+
-- with unresovled symbols for the VPI interface
42+
ghc-options: -optl -Wl,--unresolved-symbols=ignore-in-object-files
43+
44+
foreign-library simulate-ffi
45+
import: basic-config
46+
other-modules: Main
47+
Calculator
48+
type: native-shared
49+
lib-version-info: 0:1:0
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/sh
2+
3+
# This is just a minimalistic script for demonstrating the process of
4+
# running the clash-testbench example using the Icarus Verilog VVP
5+
# runtime engine. The script is not designed to work in any possible
6+
# system environment and may not work immediately for you. It is
7+
# intended to serve as an easy starter instead. Adapt it to your needs
8+
# if it's not working out-of-the-box for you.
9+
10+
###############################
11+
12+
# Adjust these variables if the tools are not in your PATH already
13+
14+
# Cabal
15+
# https://www.haskell.org/cabal
16+
CABAL=cabal
17+
# Clash
18+
# https://github.com/clash-lang/clash-compiler
19+
CLASH="${CABAL} run clash --"
20+
# Icarus Verilog VVP runtime engine
21+
# http://iverilog.icarus.com
22+
IVERILOG=iverilog
23+
VVP=vvp
24+
25+
###############################
26+
27+
${CABAL} build clash-testbench-example || exit $?
28+
${CLASH} --verilog Calculator.hs || exit $?
29+
${IVERILOG} verilog/Calculator.topEntity/topEntity.v -o Calculator.vvp \
30+
|| exit $?
31+
echo ""
32+
echo "Running Icarus Verilog VVP runtime engine:"
33+
echo ""
34+
${VVP} -Mlib -mlibsimulate-ffi Calculator.vvp
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{-|
2+
Copyright: (C) 2023 Google Inc.
3+
License: BSD2 (see the file LICENSE)
4+
Maintainer: QBayLogic B.V. <devops@qbaylogic.com>
5+
6+
Design your TestBenches in Clash
7+
-}
8+
module Clash.Testbench
9+
( module Clash.Testbench.Signal
10+
, module Clash.Testbench.Input
11+
, module Clash.Testbench.Output
12+
, module Clash.Testbench.Simulate
13+
) where
14+
15+
import Clash.Testbench.Signal
16+
import Clash.Testbench.Input
17+
import Clash.Testbench.Output
18+
import Clash.Testbench.Simulate

0 commit comments

Comments
 (0)