Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vivado support in CI #2256

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .ci/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,12 @@ FROM ubuntu:$UBUNTU_VERSION AS run
LABEL vendor="QBayLogic B.V." maintainer="[email protected]"
ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 LC_ALL=C.UTF-8 PATH="$PATH:/opt/bin:/root/.ghcup/bin"

ARG DEPS_RUNTIME="ca-certificates ccache curl g++ gcc git jq libc6-dev libgmp10-dev libgnat-9 libllvm11 libreadline8 libtinfo-dev libtcl8.6 make perl python3 ssh zlib1g-dev zlibc zstd"
ARG DEPS_RUNTIME="ca-certificates ccache curl g++ gcc git jq libc6-dev libgmp10-dev libgnat-9 libllvm11 libreadline8 libtinfo-dev libtcl8.6 make perl python3 ssh zlib1g-dev zlibc zstd locales libtinfo5"
RUN apt-get update \
&& apt-get install -y --no-install-recommends $DEPS_RUNTIME \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
&& rm -rf /var/lib/apt/lists/* \
&& locale-gen en_US.UTF-8

COPY --from=build-ghdl /opt /opt
COPY --from=build-iverilog /opt /opt
Expand Down
2 changes: 1 addition & 1 deletion .ci/gitlab/benchmark.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.benchmark:
image: ghcr.io/clash-lang/clash-ci-$GHC_VERSION:2022-05-10
image: ghcr.io/clash-lang/clash-ci-$GHC_VERSION:2022-06-18
stage: test
timeout: 2 hours
variables:
Expand Down
2 changes: 1 addition & 1 deletion .ci/gitlab/common.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.common:
image: ghcr.io/clash-lang/clash-ci-$GHC_VERSION:2022-05-10
image: ghcr.io/clash-lang/clash-ci-$GHC_VERSION:2022-06-18
timeout: 2 hours
stage: build
variables:
Expand Down
54 changes: 51 additions & 3 deletions .ci/gitlab/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,62 @@ prelude:doctests:
suite:vhdl:
extends: .test-common-local
script:
- cabal v2-run -- clash-testsuite -j$THREADS -p .VHDL --hide-successes
- cabal v2-run -- clash-testsuite -j$THREADS -p .VHDL --hide-successes --no-vivado

suite:verilog:
extends: .test-common-local
script:
- cabal v2-run -- clash-testsuite -j$THREADS -p .Verilog --hide-successes
- cabal v2-run -- clash-testsuite -j$THREADS -p .Verilog --hide-successes --no-vivado

suite:systemverilog:
extends: .test-common-local
script:
- cabal v2-run -- clash-testsuite -j$THREADS -p .SystemVerilog --hide-successes --no-modelsim
- cabal v2-run -- clash-testsuite -j$THREADS -p .SystemVerilog --hide-successes --no-modelsim --no-vivado

# Vivado is quite slow, so we only run a subset of the tests on development branches
# with it. The full testsuite gets run with Vivado every night on 'master'.
suite:cores:
extends: .test-common-local
script:
- source /opt/tools/Xilinx/Vivado/2022.1/settings64.sh
- cabal v2-run -- clash-testsuite -j$THREADS -p Cores --hide-successes --no-modelsim --no-ghdl --no-iverilog --no-verilator --no-symbiyosys
tags:
- local
- vivado-2022.1-standard


# Tests run on local fast machines with Vivado installed. We only run these at night
# to save resources - as Vivado is quite slow to execute.
.test-common-local-nightly:
extends: .test-common-local
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" # When schedueled (at night)
- if: $CI_PIPELINE_SOURCE == "trigger" # When triggered (manual triggers)
- if: '$CI_COMMIT_TAG != null' # When tags are set (releases)

suite:vivado:vhdl:
extends: .test-common-local #-nightly
script:
- source /opt/tools/Xilinx/Vivado/2022.1/settings64.sh
- cabal v2-run -- clash-testsuite -j$THREADS -p .VHDL --hide-successes --no-modelsim --no-ghdl --no-iverilog --no-verilator --no-symbiyosys
tags:
- local
- vivado-2022.1-standard

suite:vivado:verilog:
extends: .test-common-local #-nightly
script:
- source /opt/tools/Xilinx/Vivado/2022.1/settings64.sh
- cabal v2-run -- clash-testsuite -j$THREADS -p .Verilog --hide-successes --no-modelsim --no-ghdl --no-iverilog --no-verilator --no-symbiyosys
tags:
- local
- vivado-2022.1-standard

suite:vivado:systemverilog:
extends: .test-common-local #-nightly
script:
- source /opt/tools/Xilinx/Vivado/2022.1/settings64.sh
- cabal v2-run -- clash-testsuite -j$THREADS -p .SystemVerilog --hide-successes --no-modelsim --no-ghdl --no-iverilog --no-verilator --no-symbiyosys
tags:
- local
- vivado-2022.1-standard
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:

# Run steps inside the clash CI docker image
container:
image: ghcr.io/clash-lang/clash-ci-${{ matrix.ghc }}:2022-05-10
image: ghcr.io/clash-lang/clash-ci-${{ matrix.ghc }}:2022-06-18

env:
THREADS: 2
Expand Down
10 changes: 10 additions & 0 deletions tests/clash-testsuite.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ maintainer: Christiaan Baaij <[email protected]>
copyright: Copyright © 2015 University of Twente
category: Testing
build-type: Simple
data-files:
tcl/clash_namespace.tcl

flag cosim
description:
Expand Down Expand Up @@ -86,6 +88,7 @@ library
shouldwork/LoadModules

exposed-modules:
Clash.Vivado
Test.Tasty.Common
Test.Tasty.Clash
Test.Tasty.Clash.CoreTest
Expand All @@ -96,11 +99,18 @@ library
Test.Tasty.SymbiYosys
Test.Tasty.Program
Test.Tasty.Verilator
Test.Tasty.Vivado
Test.Tasty.Clash.CollectSimResults

-- From tests/shouldwork/LoadModules
T1796

other-modules:
Paths_clash_testsuite

autogen-modules:
Paths_clash_testsuite

build-depends:
deepseq,
concurrent-extra,
Expand Down
149 changes: 149 additions & 0 deletions tests/src/Clash/Vivado.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
{-# LANGUAGE QuasiQuotes #-}

-- | Generate a TCL script to simulate generated VHDL
--
-- Run with @vivado -mode batch -source ...@
module Clash.Vivado ( HdlSource (..), fromModuleName ) where

import Paths_clash_testsuite (getDataDir)

import Clash.Driver.Manifest (readManifest, Manifest (..))
import Clash.Annotations.Primitive (HDL (..))

import Data.String.Interpolate (i)
import Data.List (isSuffixOf)

import System.FilePath ((</>))
import qualified Data.Text as T

data HdlSource = HdlSource { directory :: FilePath, buildTarget :: HDL }

data SimProj = SimProj { vhdlFiles :: [(T.Text, FilePath)]
, verilogFiles :: [(T.Text, FilePath)]
, ipGen :: [FilePath]
} deriving Show

instance Semigroup SimProj where
(<>) (SimProj vhd0 v0 ip0) (SimProj vhd1 v1 ip1) =
SimProj (vhd0<>vhd1) (v0<>v1) (ip0<>ip1)

instance Monoid SimProj where
mempty = SimProj [] [] []

thread :: [a -> a] -> a -> a
thread = foldr (.) id

-- | Given something like @topEntity_dcfifo_240A2A9D3FF8D64B.tcl@, return the
-- IP name, @dcfifo@
parseTcl :: FilePath -> String
parseTcl fp = takeWhile (/= '_') (drop 1 $ dropWhile (/= '_') fp)

mSimProj :: FilePath -> T.Text -> FilePath -> SimProj -> SimProj
mSimProj dir lib fp p@(SimProj vhd v ip)
| ".vhdl" `isSuffixOf` fp = SimProj ((lib, dir </> fp):vhd) v ip
| ".vhd" `isSuffixOf` fp = SimProj ((lib, dir </> fp):vhd) v ip
| ".v" `isSuffixOf` fp = SimProj vhd ((lib, dir </> fp):v) ip
| ".tcl" `isSuffixOf` fp = SimProj vhd v ((dir </> fp):ip)
| otherwise = p

simProjFromClashEntities ::
-- | HDL source dir
FilePath ->
-- | Qualified name
String ->
IO SimProj
simProjFromClashEntities hdlDir qualName = do
(Just mHdl) <- readManifest (hdlDir </> qualName ++ "/clash-manifest.json")
let deps = T.unpack <$> transitiveDependencies mHdl
nextSimProj <- traverse (simProjFromClashEntities hdlDir) deps
pure $ mconcat nextSimProj <> asSimProj qualName mHdl

fromModuleName ::
HdlSource ->
-- | Module name
String ->
-- | Entity
String ->
-- | TCL script
IO String
fromModuleName src moduleName entity = do
proj <- simProjFromClashEntities (directory src) (moduleName ++ "." ++ entity)
cabalDir <- getDataDir
pure $ mkTcl cabalDir entity src proj

asSimProj :: FilePath -> Manifest -> SimProj
asSimProj dir m =
let fps = fst <$> fileNames m
in thread (mSimProj dir (topComponent m) <$> fps) mempty

mkTcl ::
FilePath ->
-- | Cabal @data@ dir
String ->
-- | Entity
HdlSource ->
SimProj ->
String
mkTcl cabalDir entity hdl (SimProj vhdls vs tcls) =
[i|
source #{cabalDir}/tcl/clash_namespace.tcl

# create clash namespace
namespace eval clash {}

set clash::hdl_dir #{hdlDir}

# for cleanup
file delete -force xilinx_tmp
file mkdir xilinx_tmp
cd xilinx_tmp

create_project -in_memory

set clash::ips [list]
|]

++ concatMap (\tcl ->
[i|clash::loadTclIface #{parseTcl tcl} ${clash::hdl_dir}/#{tcl}\n|]) tcls

++
[i|

set clash::ips [clash::runClashScripts]
set clash::ipFiles [get_property IP_FILE [get_ips $clash::ips]]

read_ip $clash::ipFiles

generate_target {synthesis simulation} [get_ips $clash::ips]

foreach modName $clash::ips {
add_files .gen/sources_1/ip/${modName}/synth
}
|]

++ readSources

++ [i|
update_compile_order -fileset [current_fileset]

set_property top #{entity} [get_fileset sim_1]

save_project_as sim -force

launch_simulation -simset sim_1 -mode behavioral

run -all

quit
|]
where
hdlDir = directory hdl
readSources = case buildTarget hdl of
Verilog ->
concatMap (\v ->
[i|read_verilog ${clash::hdl_dir}/#{v}\n|]) (snd <$> vs)
VHDL ->
concatMap (\(lib, vhdl) ->
[i|read_vhdl -library #{lib} ${clash::hdl_dir}/#{vhdl}\n|])
vhdls
SystemVerilog -> error "SystemVerilog not implemented for Vivado tests"
22 changes: 22 additions & 0 deletions tests/src/Test/Tasty/Clash.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import Test.Tasty.Iverilog
import Test.Tasty.Modelsim
import Test.Tasty.SymbiYosys
import Test.Tasty.Verilator
import Test.Tasty.Vivado

{- Note [copy data files hack]

Expand Down Expand Up @@ -129,6 +130,8 @@ data TestOptions =
, verilate :: Verilate
-- ^ Whether to run compatible tests through verilator as well as / in
-- place of the simulator that would ordinarily be used.
, vivado :: Bool
-- ^ Whether to simulate via Vivado (such as when one depends on Xilinx IP)
}

allTargets :: [HDL]
Expand All @@ -149,6 +152,7 @@ instance Default TestOptions where
, buildTargets=BuildAuto
, vvpStdoutNonEmptyFail=True
, verilate=SimAndVerilate
, vivado=False
}

-- | Directory where testbenches live.
Expand Down Expand Up @@ -383,6 +387,20 @@ verilatorTests opts@TestOptions{..} tmpDir = (buildTests, simTests)
| t <- getBuildTargets opts
]

-- | When we need Xilinx IP
vivadoTests
:: HDL
-> TestOptions
-> IO FilePath
-> String
-> [(TestName, TestTree)]
vivadoTests target opts tmpDir modName =
[ ( "Vivado"
, singleTest "Vivado" (VivadoTest target tmpDir modName t)
)
| t <- getBuildTargets opts
]

-- | Generate a test tree for running SymbiYosys
sbyTests :: TestOptions -> IO FilePath -> ([(TestName, TestTree)])
sbyTests opts@TestOptions {..} tmpDir =
Expand All @@ -407,6 +425,10 @@ runTest1 modName opts@TestOptions{..} path target =
<> (case verificationTool of
Nothing -> []
Just SymbiYosys -> tail $ sequenceTests (show target : path) (clashTest tmpDir : sbyTests opts tmpDir))
<> if vivado
then
tail (sequenceTests (show target : path) (clashTest tmpDir : vivadoTests target opts tmpDir modName))
else []
where
mkTmpDir = flip createTempDirectory "clash-test" =<< getCanonicalTemporaryDirectory
sourceDir = List.foldl' (</>) sourceDirectory (reverse (tail path))
Expand Down
3 changes: 3 additions & 0 deletions tests/src/Test/Tasty/Program.hs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ data ExpectOutput a
| ExpectStdErr a
| ExpectEither a
| ExpectNotStdErr a
| ExpectNotStdOut a
| ExpectNothing
deriving Functor

Expand Down Expand Up @@ -356,6 +357,8 @@ runFailingProgram testExitCode program args stdO errOnEmptyStderr expectedCode e
unexpectedStd "stdout or stderr" program args code stderrT stdoutT r
ExpectNotStdErr r | cleanNewlines r `T.isInfixOf` cleanNewlines stderrT ->
unexpectedNonEmptyStderr program args code stderrT stdoutT
ExpectNotStdOut r | cleanNewlines r `T.isInfixOf` cleanNewlines stdoutT ->
unexpectedNonEmptyStdout program args code stderrT stdoutT
_ ->
if testExitCode then
case expectedCode of
Expand Down
Loading