From 840ade09d15ae8626b6d41a14c3d7561ff1d4605 Mon Sep 17 00:00:00 2001 From: Lara Herzog Date: Thu, 29 Jun 2023 14:07:40 +0200 Subject: [PATCH] WIP add JtagBridge --- cabal.project | 2 +- clash-vexriscv-sim/app/HdlTest.hs | 5 + clash-vexriscv-sim/src/Utils/Cpu.hs | 15 +- clash-vexriscv/Makefile | 9 +- clash-vexriscv/clash-vexriscv.cabal | 2 + clash-vexriscv/example-cpu/ExampleCpu.yaml | 1 + clash-vexriscv/example-cpu/build.sbt | 3 +- .../src/main/scala/example/ExampleCpu.scala | 4 +- clash-vexriscv/src/VexRiscv.hs | 10 + clash-vexriscv/src/VexRiscv/JtagTcpBridge.hs | 183 ++++++++++++++++++ 10 files changed, 223 insertions(+), 11 deletions(-) create mode 100644 clash-vexriscv-sim/app/HdlTest.hs create mode 100644 clash-vexriscv/example-cpu/ExampleCpu.yaml create mode 100644 clash-vexriscv/src/VexRiscv/JtagTcpBridge.hs diff --git a/cabal.project b/cabal.project index 5ebd964..41ae579 100644 --- a/cabal.project +++ b/cabal.project @@ -7,7 +7,7 @@ packages: clash-vexriscv-sim/ write-ghc-environment-files: always - +-- executable-dynamic: True with-compiler: ghc-9.0.2 tests: True diff --git a/clash-vexriscv-sim/app/HdlTest.hs b/clash-vexriscv-sim/app/HdlTest.hs new file mode 100644 index 0000000..5f260e7 --- /dev/null +++ b/clash-vexriscv-sim/app/HdlTest.hs @@ -0,0 +1,5 @@ +import Prelude + +main :: IO () +main = do + putStrLn "hello" \ No newline at end of file diff --git a/clash-vexriscv-sim/src/Utils/Cpu.hs b/clash-vexriscv-sim/src/Utils/Cpu.hs index 3891398..61d45c5 100644 --- a/clash-vexriscv-sim/src/Utils/Cpu.hs +++ b/clash-vexriscv-sim/src/Utils/Cpu.hs @@ -12,11 +12,13 @@ import Clash.Prelude import Clash.Signal.Internal (Signal((:-))) import Protocols.Wishbone import VexRiscv +import VexRiscv.JtagTcpBridge import GHC.Stack (HasCallStack) import Utils.ProgramLoad (Memory) import Utils.Interconnect (interconnectTwo) +import System.IO.Unsafe (unsafePerformIO) emptyInput :: Input emptyInput = @@ -52,6 +54,10 @@ cpu :: cpu bootIMem bootDMem = (output, writes, iS2M, dS2M) where output = vexRiscv (emptyInput :- input) + + + (unbundle -> (jtagIn', _debugReset)) = unsafePerformIO $ jtagTcpBridge' 7893 hasReset (jtagOut <$> output) + dM2S = dBusWbM2S <$> output iM2S = unBusAddr . iBusWbM2S <$> output @@ -68,22 +74,19 @@ cpu bootIMem bootDMem = (output, writes, iS2M, dS2M) ((0x0000_0000, dummyS2M) :> (0x4000_0000, bootDS2M) :> Nil) input = - ( \iBus dBus -> + ( \iBus dBus jtagIn -> Input { timerInterrupt = low, externalInterrupt = low, softwareInterrupt = low, iBusWbS2M = makeDefined iBus, dBusWbS2M = makeDefined dBus, - jtagIn = JtagIn { - testModeSelect = low, - testDataIn = low, - testClock = low - } + jtagIn = jtagIn } ) <$> iS2M <*> dS2M + <*> jtagIn' unBusAddr = mapAddr ((`shiftL` 2) . extend @_ @_ @2) diff --git a/clash-vexriscv/Makefile b/clash-vexriscv/Makefile index 49f7cf0..5953f3a 100644 --- a/clash-vexriscv/Makefile +++ b/clash-vexriscv/Makefile @@ -13,7 +13,7 @@ VERILATOR_FLAGS = -CFLAGS '-O3 -fPIC' -Wno-fatal +1364-2001ext+v VERILATOR_CFLAGS = $(shell pkg-config --cflags verilator) FFI_CPPFLAGS = $(VERILATOR_CFLAGS) -fPIC -O3 -I$(VERILATOR_DIR) -all: $(OUT_DIR)/libVexRiscvFFI.a +all: $(OUT_DIR)/libVexRiscvFFI.a # $(OUT_DIR)/libVexRiscvFFI.so clean: rm $(VERILATOR_DIR) -rf @@ -50,3 +50,10 @@ $(OUT_DIR)/libVexRiscvFFI.a: $(OUT_DIR)/VVexRiscv__ALL.a $(OUT_DIR)/impl.o $(OUT $(OUT_DIR)/libVexRiscvFFI.a \ $(OUT_DIR)/impl.o \ $(OUT_DIR)/verilated.o + +$(OUT_DIR)/libVexRiscvFFI.so: $(OUT_DIR)/libVexRiscvFFI.a + rm -f $(OUT_DIR)/libVexRiscvFFI.so + $(CXX) -shared -o $(OUT_DIR)/libVexRiscvFFI.so \ + -Wl,--whole-archive \ + $(OUT_DIR)/libVexRiscvFFI.a \ + -Wl,--no-whole-archive \ No newline at end of file diff --git a/clash-vexriscv/clash-vexriscv.cabal b/clash-vexriscv/clash-vexriscv.cabal index 8fb275f..7a0f4d7 100644 --- a/clash-vexriscv/clash-vexriscv.cabal +++ b/clash-vexriscv/clash-vexriscv.cabal @@ -103,6 +103,7 @@ library exposed-modules: VexRiscv VexRiscv.FFI + VexRiscv.JtagTcpBridge VexRiscv.TH build-depends: base, @@ -116,5 +117,6 @@ library string-interpolate, template-haskell, Glob, + network, extra-libraries: VexRiscvFFI, stdc++ include-dirs: src/ diff --git a/clash-vexriscv/example-cpu/ExampleCpu.yaml b/clash-vexriscv/example-cpu/ExampleCpu.yaml new file mode 100644 index 0000000..3eeb252 --- /dev/null +++ b/clash-vexriscv/example-cpu/ExampleCpu.yaml @@ -0,0 +1 @@ +debug: !!vexriscv.DebugReport {hardwareBreakpointCount: 0} diff --git a/clash-vexriscv/example-cpu/build.sbt b/clash-vexriscv/example-cpu/build.sbt index 9dd4bc8..0321f5f 100644 --- a/clash-vexriscv/example-cpu/build.sbt +++ b/clash-vexriscv/example-cpu/build.sbt @@ -17,7 +17,8 @@ lazy val root = (project in file(".")) libraryDependencies ++= Seq( "com.github.spinalhdl" % "spinalhdl-core_2.11" % spinalVersion, "com.github.spinalhdl" % "spinalhdl-lib_2.11" % spinalVersion, - compilerPlugin("com.github.spinalhdl" % "spinalhdl-idsl-plugin_2.11" % spinalVersion) + compilerPlugin("com.github.spinalhdl" % "spinalhdl-idsl-plugin_2.11" % spinalVersion), + "org.yaml" % "snakeyaml" % "1.8" ) ) diff --git a/clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala b/clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala index bb58d28..a66c6dc 100644 --- a/clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala +++ b/clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala @@ -63,8 +63,8 @@ object ExampleCpu extends App { earlyBranch = false, catchAddressMisaligned = true ), - new DebugPlugin(ClockDomain.current) - // new YamlPlugin("ExampleCpu.yaml") + new DebugPlugin(ClockDomain.current), + new YamlPlugin("ExampleCpu.yaml") ) ) diff --git a/clash-vexriscv/src/VexRiscv.hs b/clash-vexriscv/src/VexRiscv.hs index f0c377c..dbcc7ee 100644 --- a/clash-vexriscv/src/VexRiscv.hs +++ b/clash-vexriscv/src/VexRiscv.hs @@ -19,6 +19,7 @@ import Foreign.Marshal (alloca) import Foreign.Storable import GHC.IO (unsafePerformIO) import GHC.Stack (HasCallStack) +import Protocols import Protocols.Wishbone import VexRiscv.FFI import VexRiscv.TH @@ -53,6 +54,15 @@ data Output = Output } deriving (Generic, NFDataX, ShowX, Eq) + +data Jtag (dom :: Domain) + +instance Protocol (Jtag dom) where + type Fwd (Jtag dom) = Signal dom JtagOut + type Bwd (Jtag dom) = Signal dom JtagIn + + + inputToFFI :: Bool -> Input -> INPUT inputToFFI reset Input {..} = INPUT diff --git a/clash-vexriscv/src/VexRiscv/JtagTcpBridge.hs b/clash-vexriscv/src/VexRiscv/JtagTcpBridge.hs new file mode 100644 index 0000000..1b76440 --- /dev/null +++ b/clash-vexriscv/src/VexRiscv/JtagTcpBridge.hs @@ -0,0 +1,183 @@ +module VexRiscv.JtagTcpBridge where + +import Clash.Prelude + +import Clash.Signal.Internal +import Network.Socket +import Protocols +import Protocols.Internal (CSignal(..)) +import VexRiscv (JtagIn(..), JtagOut(..), Jtag) +import Control.Concurrent (MVar, forkIO, newEmptyMVar, putMVar, takeMVar, tryTakeMVar) +import Control.Monad (when) +import Network.Socket.ByteString (sendAll, recv) + +import qualified Data.ByteString as BS +import System.IO.Unsafe (unsafePerformIO) +import Debug.Trace (trace) +import GHC.IO.Handle (hFlush) +import System.IO (stdout) + +data NetworkThreadToMainMsg + = Connected + | Disconnected + | DataReceived [BitVector 8] + +data MainToNetworkThreadMsg + = ReadMore + | Send (BitVector 8) + +data NetworkThreadState + = NoClient + | PerformRead Socket + | WaitForNextRead Socket + deriving (Show) + +data MainThreadState + = MDisconnected + | MWaitForRead + | MProcessing (BitVector 2) [BitVector 8] + deriving (Show) + +jtagTcpBridge :: + (HiddenClockResetEnable dom) => + PortNumber -> + Circuit + (Jtag dom) + (CSignal dom Bit) +jtagTcpBridge port = + Circuit $ \(jtagOut, _) -> unsafePerformIO $ do + (unbundle -> (jtagIn, debugReset)) <- jtagTcpBridge' port hasReset jtagOut + pure (jtagIn, CSignal debugReset) + +jtagTcpBridge' :: + (KnownDomain dom) => + PortNumber -> + Reset dom -> + Signal dom JtagOut -> + IO (Signal dom (JtagIn, Bit)) + -- ^ JTAG and debugReset +jtagTcpBridge' port rst jtagOut = do + (n2m, m2n) <- server port + + let resets = boolToBit <$> unsafeToHighPolarity rst + + jtagIn <- client n2m m2n MDisconnected jtagOut + + pure $ bundle (jtagIn, resets) +{-# NOINLINE jtagTcpBridge' #-} + +server :: PortNumber -> IO (MVar NetworkThreadToMainMsg, MVar MainToNetworkThreadMsg) +server port = withSocketsDo $ do + sock <- setup + + threadToMainChan <- newEmptyMVar + mainToThreadChan <- newEmptyMVar + + let + thread (dbg -> NoClient) = do + (clientSock, _) <- accept sock + putStrLn "[jtag] connection accepted" + hFlush stdout + + putMVar threadToMainChan Connected + thread (PerformRead clientSock) + thread (dbg -> PerformRead clientSock) = do + buf <- recv clientSock 100 + if BS.null buf then do + putMVar threadToMainChan Disconnected + thread NoClient + else do + let dat = pack <$> BS.unpack buf + putMVar threadToMainChan (DataReceived dat) + thread (WaitForNextRead clientSock) + + thread (dbg -> WaitForNextRead clientSock) = do + msg <- takeMVar mainToThreadChan + case msg of + ReadMore -> thread (PerformRead clientSock) + Send byte -> do + putStrLn "sending data" + sendAll clientSock (BS.singleton $ unpack byte) + putStrLn "data sent" + thread (WaitForNextRead clientSock) + + _ <- forkIO $ thread NoClient + + pure (threadToMainChan, mainToThreadChan) + + where + setup = do + sock <- socket AF_INET Stream 0 + + setSocketOption sock NoDelay 0 + + bind sock (SockAddrInet port (tupleToHostAddress (127, 0, 0, 1))) + + listen sock 1 + + pure sock + +defaultIn :: JtagIn +defaultIn = JtagIn { testModeSelect = low, testDataIn = low, testClock = low } + +dbg :: Show a => a -> a +dbg x = + -- trace (show x) + x + +clientSleep :: BitVector 2 +clientSleep = 4 + +client :: + (KnownDomain dom) => + MVar NetworkThreadToMainMsg -> + MVar MainToNetworkThreadMsg -> + MainThreadState -> + Signal dom JtagOut -> + IO (Signal dom JtagIn) +client n2m m2n (dbg -> MDisconnected) (_out :- outs) = do + msg <- tryTakeMVar n2m + case msg of + Nothing -> + pure $ defaultIn :- unsafePerformIO (client n2m m2n MDisconnected outs) + Just Connected -> do + pure $ defaultIn :- unsafePerformIO (client n2m m2n MWaitForRead outs) + Just Disconnected -> do + errorX "????" + Just (DataReceived _xs) -> do + errorX "????" + +client n2m m2n (dbg -> MWaitForRead) (out :- outs) = do + msg <- tryTakeMVar n2m + case msg of + Nothing -> + pure $ defaultIn :- unsafePerformIO (client n2m m2n MWaitForRead outs) + Just Disconnected -> + pure $ defaultIn :- unsafePerformIO (client n2m m2n MDisconnected outs) + Just (DataReceived xs) -> do + putStrLn $ "received data" + client n2m m2n (MProcessing 0 xs) (out :- outs) + Just Connected -> + errorX "????" + +client n2m m2n (dbg -> MProcessing _ []) (out :- outs) = do + putMVar m2n ReadMore + pure $ out `deepseqX` defaultIn :- unsafePerformIO (client n2m m2n MWaitForRead outs) +client n2m m2n (dbg -> MProcessing 0 (x:xs)) (out :- outs) = do + let tms = x ! (0 :: Int) + tdi = x ! (1 :: Int) + tck = x ! (3 :: Int) + + sendTdo = bitToBool $ x ! (2 :: Int) + + when sendTdo $ do + putStrLn $ "send TDO: " <> show (testDataOut out) + putMVar m2n $ Send $ boolToBV (bitToBool $ testDataOut out) + + let inDat = JtagIn { testModeSelect = tms, testDataIn = tdi, testClock = tck } + pure $ inDat :- unsafePerformIO (client n2m m2n (MProcessing clientSleep xs) outs) +client n2m m2n (dbg -> MProcessing n xs) (out :- outs) = do + pure $ out `deepseqX` defaultIn :- unsafePerformIO (client n2m m2n (MProcessing (n - 1) xs) outs) + + +{-# NOINLINE client #-} \ No newline at end of file