Skip to content

Commit

Permalink
Merge pull request #30 from clash-lang/use-own-docker
Browse files Browse the repository at this point in the history
Use own Docker image and enable JTAG tests on CI
  • Loading branch information
martijnbastiaan authored Apr 3, 2024
2 parents 1156fda + e15cee8 commit fda5b04
Show file tree
Hide file tree
Showing 15 changed files with 203 additions and 223 deletions.
82 changes: 82 additions & 0 deletions .github/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# syntax=docker/dockerfile:1.2

# SPDX-FileCopyrightText: 2024 Google LLC

# SPDX-License-Identifier: CC0-1.0

ARG UBUNTU_VERSION
FROM ubuntu:$UBUNTU_VERSION AS builder

LABEL vendor="QBayLogic B.V." maintainer="[email protected]"
ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 LC_ALL=C.UTF-8 PREFIX=/opt

ARG DEPS_COMMON="build-essential ca-certificates curl git locales ca-certificates"

RUN apt-get update \
&& apt-get install -y --no-install-recommends $DEPS_COMMON \
&& locale-gen en_US.UTF-8 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

FROM builder AS build-openocd-vexriscv

ARG DEPS_OPENOCD_VEXRISCV="autoconf automake libtool pkg-config libusb-1.0-0-dev libftdi-dev libhidapi-dev libusb-dev libyaml-dev"

RUN apt-get update \
&& apt-get install -y --no-install-recommends $DEPS_OPENOCD_VEXRISCV \
&& git clone --recursive https://github.com/SpinalHDL/openocd_riscv.git \
&& cd openocd_riscv \
&& ./bootstrap \
&& ./configure --enable-ftdi --enable-dummy --prefix=/opt \
&& make -j$(nproc) \
&& make install

FROM builder AS build-verilator

ARG DEPS_VERILATOR="perl python3 make autoconf g++ flex bison ccache libgoogle-perftools-dev numactl perl-doc libfl2 libfl-dev zlib1g zlib1g-dev help2man"
RUN apt-get update \
&& apt-get install -y --no-install-recommends $DEPS_VERILATOR

ARG verilator_version="v5.020"
RUN git clone https://github.com/verilator/verilator verilator \
&& cd verilator \
&& git checkout $verilator_version \
&& autoconf \
&& ./configure --prefix $PREFIX \
&& make PREFIX=$PREFIX -j$(nproc) \
&& make PREFIX=$PREFIX install \
&& cd ../.. \
&& rm -Rf verilator

FROM builder AS build-ghc

ARG ghcup_version="0.1.22.0"

# Must be explicitly set
ARG ghc_version
ARG cabal_version

RUN curl "https://downloads.haskell.org/~ghcup/$ghcup_version/x86_64-linux-ghcup-$ghcup_version" --output /usr/bin/ghcup \
&& chmod +x /usr/bin/ghcup \
&& ghcup install ghc $ghc_version --set \
&& ghcup install cabal $cabal_version --set

FROM builder 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="gnupg pkg-config openjdk-8-jdk gdb-multiarch picocom libtinfo5 libtinfo-dev build-essential curl libc6-dev libgmp10-dev python3 ccache libftdi1 libhidapi-hidraw0 libusb-1.0-0 libyaml-0-2"
RUN apt-get update \
&& apt-get install -y --no-install-recommends $DEPS_RUNTIME \
&& echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | tee /etc/apt/sources.list.d/sbt.list \
&& echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | tee /etc/apt/sources.list.d/sbt_old.list \
&& curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | apt-key add \
&& apt-get update \
&& apt-get install -y --no-install-recommends sbt \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

COPY --from=build-verilator /opt /opt
COPY --from=build-openocd-vexriscv /opt /opt
COPY --from=build-ghc /root/.ghcup /root/.ghcup
48 changes: 48 additions & 0 deletions .github/docker/build-and-publish-docker-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2024 Google LLC

# SPDX-License-Identifier: CC0-1.0
set -xeo pipefail

REPO="ghcr.io/clash-lang"
NAME="clash-vexriscv-ci"
DIR=$(dirname "$0")
now=$(date +%Y%m%d)

if [[ "$1" == "-y" ]]; then
push=y
elif [[ "$1" != "" ]]; then
echo "Unrecognized argument: $1" >&2
exit 1
fi

UBUNTU_VERSION=jammy-20240125
GHC_VERSIONS=( "9.4.8" "9.2.8" "9.0.2")
CABAL_VERSIONS=("3.10.2.0" "3.10.2.0" "3.10.2.0")

for i in "${!GHC_VERSIONS[@]}"
do
GHC_VERSION="${GHC_VERSIONS[i]}"
CABAL_VERSION="${CABAL_VERSIONS[i]}"

docker buildx build \
--build-arg UBUNTU_VERSION=${UBUNTU_VERSION} \
--build-arg cabal_version=${CABAL_VERSION} \
--build-arg ghc_version=${GHC_VERSION} \
-t "${REPO}/${NAME}:${GHC_VERSION}-$now" \
"$DIR"
done

if [[ "${push}" == "" ]]; then
read -p "Push to GitHub? (y/N) " push
fi

if [[ $push =~ ^[Yy]$ ]]; then
for i in "${!GHC_VERSIONS[@]}"
do
GHC_VERSION="${GHC_VERSIONS[i]}"
docker push "${REPO}/${NAME}:${GHC_VERSION}-$now"
done
else
echo "Skipping push to container registry"
fi
30 changes: 8 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
run: |
apt-get update
apt-get install -y curl build-essential
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.67 # See Note [Updating Rust versions]
Expand Down Expand Up @@ -69,7 +70,6 @@ jobs:
run: |
apt-get update
apt-get install -y curl build-essential
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.67.1 # See Note [Updating Rust versions]
Expand Down Expand Up @@ -116,7 +116,7 @@ jobs:
- "9.4.8"

container:
image: ghcr.io/clash-lang/clash-ci:${{ matrix.ghc }}-20240221
image: ghcr.io/clash-lang/clash-vexriscv-ci:${{ matrix.ghc }}-20240329

steps:
- name: Checkout
Expand All @@ -132,27 +132,9 @@ jobs:
with:
path: |
~/.local/state/cabal/store/
key: packages-cachebust-2-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze', 'cabal.project') }}
restore-keys: packages-cachebust-2-${{ matrix.ghc }}
key: packages-cachebust-3-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze', 'cabal.project') }}
restore-keys: packages-cachebust-3-${{ matrix.ghc }}

- name: Install build deps
run: |
apt-get update
apt-get install gnupg pkg-config -y
- name: Install Java
run: |
# install Java 8
apt-get update
apt-get install openjdk-8-jdk -y
update-alternatives --config java
update-alternatives --config javac
- name: Install SBT
run: |
echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | tee /etc/apt/sources.list.d/sbt.list
echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | tee /etc/apt/sources.list.d/sbt_old.list
curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | apt-key add
apt-get update
apt-get install sbt -y
- name: Stash existing VexRiscv.v
run: |
cp clash-vexriscv/example-cpu/VexRiscv.v clash-vexriscv/example-cpu/VexRiscv.v.comitted
Expand Down Expand Up @@ -180,6 +162,10 @@ jobs:
run: |
tar -x -f vexriscv-test-binaries.tar
- name: OpenOCD bin symlink
run: |
ln -s /opt/bin/openocd /opt/bin/openocd-vexriscv
- name: Run `clash-vexriscv` unittests
run: |
cabal run clash-vexriscv:unittests
Expand Down
10 changes: 0 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@ opt-level = "z"
[workspace]
members = [
"clash-vexriscv-sim/test-programs",
"debug-test",
]
resolver = "2"
1 change: 1 addition & 0 deletions clash-vexriscv-sim/test-programs/src/bin/print_a.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[CPU] a
File renamed without changes.
1 change: 1 addition & 0 deletions clash-vexriscv-sim/test-programs/src/bin/print_b.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[CPU] b
File renamed without changes.
71 changes: 53 additions & 18 deletions clash-vexriscv-sim/tests/Tests/Jtag.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,31 @@ module Tests.Jtag where

import Prelude

import Control.Monad.Extra (ifM)
import Control.Applicative ((<|>))
import Control.Monad.Extra (ifM, when)
import Data.List.Extra (trim)
import Data.Maybe (fromJust)
import Data.Proxy
import System.Directory (findExecutable)
import System.Exit
import System.IO
import System.Process

import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.Options

import Paths_clash_vexriscv_sim (getDataFileName)

newtype JtagDebug = JtagDebug Bool

instance IsOption JtagDebug where
defaultValue = JtagDebug False
parseValue = fmap JtagDebug . safeReadBool
optionName = return "jtag-debug"
optionHelp = return "While waiting for outputs of subprocesses, print them to stderr"
optionCLParser = flagCLParser Nothing (JtagDebug True)

cabalListBin :: String -> IO FilePath
cabalListBin name = do
trim <$> readProcess "cabal" ["-v0", "list-bin", name] ""
Expand All @@ -38,20 +51,34 @@ getOpenOcdCfgPath = getDataFileName "data/vexriscv_sim.cfg"
getGdbCmdPath :: IO FilePath
getGdbCmdPath = getDataFileName "data/vexriscv_gdb.cfg"

expectLine :: Handle -> String -> Assertion
expectLine h expected = do
getGdb :: HasCallStack => IO String
getGdb = do
gdbMultiArch <- findExecutable "gdb-multiarch"
gdb <- findExecutable "gdb"
case gdbMultiArch <|> gdb of
Nothing -> fail "Neither gdb-multiarch nor gdb found in PATH"
Just x -> pure x

expectLine :: Bool -> Handle -> String -> Assertion
expectLine debug h expected = do
line <- hGetLine h
when debug $ do
hPutStr stderr "> "
hPutStrLn stderr line
ifM
(pure $ null line)
(expectLine h expected)
(expectLine debug h expected)
(expected @?= line)

waitForLine :: Handle -> String -> IO ()
waitForLine h expected = do
waitForLine :: Bool -> Handle -> String -> IO ()
waitForLine debug h expected = do
line <- hGetLine h
when debug $ do
hPutStr stderr "> "
hPutStrLn stderr line
if line == expected
then pure ()
else waitForLine h expected
else waitForLine debug h expected

-- | Run three processes in parallel:
--
Expand All @@ -60,13 +87,17 @@ waitForLine h expected = do
-- 3. GDB. It connects to the OpenOCD GDB server and bunch of commands. See the
-- file produced by 'getGdbCmdPath' for the commands.
--
test :: Assertion
test = do
test ::
-- | Print debug output of subprocesses
Bool ->
Assertion
test debug = do
simulateExecPath <- getSimulateExecPath
printElfPath <- getPrintElfPath
projectRoot <- getProjectRoot
openocdCfgPath <- getOpenOcdCfgPath
gdbCmdPath <- getGdbCmdPath
gdb <- getGdb

let
vexRiscvProc = (proc simulateExecPath [printElfPath]){
Expand All @@ -79,32 +110,36 @@ test = do
, cwd = Just projectRoot
}

gdbProc = (proc "gdb" ["--command", gdbCmdPath]){
gdbProc = (proc gdb ["--command", gdbCmdPath]){
std_out = CreatePipe, -- Comment this line to see GDB output
cwd = Just projectRoot
}

withCreateProcess vexRiscvProc $ \_ (fromJust -> vexRiscvStdOut) _ _ -> do
hSetBuffering vexRiscvStdOut LineBuffering
expectLine vexRiscvStdOut "[CPU] a"
expectLine debug vexRiscvStdOut "[CPU] a"

-- CPU has started, so we can start OpenOCD
withCreateProcess openOcdProc $ \_ _ (fromJust -> openOcdStdErr) _ -> do
hSetBuffering openOcdStdErr LineBuffering
waitForLine openOcdStdErr "Halting processor"
waitForLine debug openOcdStdErr "Halting processor"

-- OpenOCD has started, so we can start GDB
withCreateProcess gdbProc $ \_ _ _ gdbProcHandle -> do
expectLine vexRiscvStdOut "[CPU] a"
expectLine vexRiscvStdOut "[CPU] b"
expectLine debug vexRiscvStdOut "[CPU] a"
expectLine debug vexRiscvStdOut "[CPU] b"

gdbExitCode <- waitForProcess gdbProcHandle
ExitSuccess @?= gdbExitCode

tests :: TestTree
tests = testGroup "JTAG"
[ testCase "Basic GDB commands, breakpoints, and program loading" test
]
tests = askOption $ \(JtagDebug debug) ->
testGroup "JTAG"
[ testCase "Basic GDB commands, breakpoints, and program loading" (test debug)
]

main :: IO ()
main = defaultMain tests
main =
defaultMainWithIngredients
(includingOptions [Option (Proxy :: Proxy JtagDebug)] : defaultIngredients)
tests
Loading

0 comments on commit fda5b04

Please sign in to comment.