Skip to content

Commit

Permalink
Add optional VCD tracing
Browse files Browse the repository at this point in the history
  • Loading branch information
martijnbastiaan committed Dec 4, 2024
1 parent 652280a commit 3c98407
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 40 deletions.
6 changes: 3 additions & 3 deletions clash-vexriscv-sim/app/VexRiscvChainSimulation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Text.Printf (printf, hPrintf)
import Utils.DebugConfig (DebugConfiguration (..))
import Utils.Cpu (cpu)
import Utils.ProgramLoad (loadProgramDmem)
import VexRiscv (JtagIn (JtagIn), JtagOut (JtagOut), CpuOut (dBusWbM2S, iBusWbM2S))
import VexRiscv (JtagIn (JtagIn), JtagOut (JtagOut), CpuOut (dBusWbM2S, iBusWbM2S), DumpVcd(NoDumpVcd))
import VexRiscv.JtagTcpBridge (vexrJtagBridge)


Expand Down Expand Up @@ -106,15 +106,15 @@ main = do
cpuOutA@(unbundle -> (_circuitA, jtagOutA, _, _iBusA, _dBusA)) =
withClockResetEnable @System clockGen (resetGenN (SNat @2)) enableGen $
let
(circ, jto, writes1, iBus, dBus) = cpu (Just jtagInA) iMemA dMemA
(circ, jto, writes1, iBus, dBus) = cpu NoDumpVcd (Just jtagInA) iMemA dMemA
dBus' = register emptyWishboneS2M dBus
in bundle (circ, jto, writes1, iBus, dBus')

jtagInB = liftA2 jtagDaisyChain jtagInA jtagOutA
cpuOutB@(unbundle -> (_circuitB, jtagOutB, _, _iBusB, _dBusB)) =
withClockResetEnable @System clockGen (resetGenN (SNat @2)) enableGen $
let
(circ, jto, writes1, iBus, dBus) = cpu (Just jtagInB) iMemB dMemB
(circ, jto, writes1, iBus, dBus) = cpu NoDumpVcd (Just jtagInB) iMemB dMemB
dBus' = register emptyWishboneS2M dBus
in bundle (circ, jto, writes1, iBus, dBus')

Expand Down
4 changes: 2 additions & 2 deletions clash-vexriscv-sim/app/VexRiscvSimulation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Clash.Prelude

import Protocols.Wishbone
import VexRiscv (CpuOut(iBusWbM2S, dBusWbM2S))
import VexRiscv (CpuOut(iBusWbM2S, dBusWbM2S), DumpVcd(NoDumpVcd))

import qualified Data.List as L

Expand Down Expand Up @@ -58,7 +58,7 @@ main = do
jtagPort = vexrJtagBridge 7894 jtagOut
cpuOut@(unbundle -> (_circuit, jtagOut, writes, _iBus, _dBus)) =
withClockResetEnable @System clockGen (resetGenN (SNat @2)) enableGen $
let (circ, jto, writes1, iBus, dBus) = cpu (Just jtagPort) iMem dMem
let (circ, jto, writes1, iBus, dBus) = cpu NoDumpVcd (Just jtagPort) iMem dMem
dBus' = register emptyWishboneS2M dBus
in bundle (circ, jto, writes1, iBus, dBus')

Expand Down
5 changes: 3 additions & 2 deletions clash-vexriscv-sim/src/Utils/Cpu.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ cpu ::
-- convenient it is to use this within a design with synchronous resets.
-- , HasAsynchronousReset dom
) =>
DumpVcd ->
Maybe (Signal dom JtagIn) ->
DMemory dom ->
DMemory dom ->
Expand All @@ -52,15 +53,15 @@ cpu ::
, -- dBus responses
Signal dom (WishboneS2M (BitVector 32))
)
cpu jtagIn0 bootIMem bootDMem =
cpu dumpVcd jtagIn0 bootIMem bootDMem =
( cpuOut
, jtagOut
, writes
, iS2M
, dS2M
)
where
(cpuOut, jtagOut) = vexRiscv hasClock (hasReset `unsafeOrReset` jtagReset) input jtagIn1
(cpuOut, jtagOut) = vexRiscv dumpVcd hasClock (hasReset `unsafeOrReset` jtagReset) input jtagIn1

jtagReset =
unsafeFromActiveHigh $ register False $
Expand Down
2 changes: 1 addition & 1 deletion clash-vexriscv-sim/src/Utils/Instance.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ circuit ::
( "CPU_OUTPUT" ::: Signal System CpuOut
, "JTAG_OUT" ::: Signal System JtagOut )
circuit clk rst input jtagIn =
vexRiscv clk rst input jtagIn
vexRiscv NoDumpVcd clk rst input jtagIn
{-# CLASH_OPAQUE circuit #-}
makeTopEntity 'circuit
5 changes: 3 additions & 2 deletions clash-vexriscv-sim/tests/tests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Test.Tasty.Options

import Utils.ProgramLoad (loadProgramDmem)
import Utils.Cpu (cpu)
import VexRiscv (DumpVcd(NoDumpVcd))

import qualified Tests.Jtag
import qualified Tests.JtagChain
Expand All @@ -44,7 +45,7 @@ runProgramExpect act n expected = withSystemTempFile "ELF" $ \fp _ -> do

let _all@(unbundle -> (_circuit, _, writes, _iBus, _dBus)) =
withClockResetEnable @System clockGen (resetGenN (SNat @2)) enableGen $
bundle (cpu Nothing iMem dMem)
bundle (cpu NoDumpVcd Nothing iMem dMem)

let output = L.take (BS.length expected) $
flip mapMaybe (sampleN_lazy n writes) $ \case
Expand Down Expand Up @@ -104,7 +105,7 @@ runTest ::
FilePath ->
TestTree
runTest name mode n elfPath expectPath =
testCase ("Integration test `" <> name <> "` (" <> mode <> ")") $ do
testCase ("Integration test " <> name <> " (" <> mode <> ")") $ do
expected <- BS.readFile expectPath
let act = copyFile elfPath

Expand Down
53 changes: 33 additions & 20 deletions clash-vexriscv/src/VexRiscv.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import Clash.Signal.Internal
import Data.Bifunctor (first)
import Data.String.Interpolate (__i)
import Data.Word (Word64)
import Foreign (Ptr)
import Foreign (Ptr, nullPtr)
import Foreign.Marshal (alloca)
import Foreign.Storable
import Foreign.C.String (newCString)
import GHC.IO (unsafePerformIO, unsafeInterleaveIO)
import GHC.Stack (HasCallStack)
import Language.Haskell.TH.Syntax
Expand Down Expand Up @@ -66,6 +67,8 @@ data CpuOut = CpuOut
}
deriving (Generic, NFDataX, ShowX, Eq, BitPack)

data DumpVcd = DumpVcd FilePath | NoDumpVcd


data Jtag (dom :: Domain)

Expand All @@ -81,14 +84,15 @@ vexRiscv ::
forall dom .
( HasCallStack
, KnownDomain dom) =>
DumpVcd ->
Clock dom ->
Reset dom ->
Signal dom CpuIn ->
Signal dom JtagIn ->
( Signal dom CpuOut
, Signal dom JtagOut
)
vexRiscv clk rst cpuInput jtagInput =
vexRiscv dumpVcd clk rst cpuInput jtagInput =
( CpuOut <$>
(WishboneM2S
<$> iBus_ADR
Expand Down Expand Up @@ -161,7 +165,7 @@ vexRiscv clk rst cpuInput jtagInput =
, dBus_BTE
, debug_resetOut
, jtag_TDO
) = vexRiscv# sourcePath clk rst
) = vexRiscv# dumpVcd sourcePath clk rst
timerInterrupt
externalInterrupt
softwareInterrupt
Expand All @@ -181,7 +185,8 @@ vexRiscv clk rst cpuInput jtagInput =

vexRiscv#
:: KnownDomain dom
=> String
=> DumpVcd
-> String
-> Clock dom
-> Reset dom
-- input signals
Expand Down Expand Up @@ -228,7 +233,7 @@ vexRiscv#
, Signal dom Bit -- ^ debug_resetOut
, Signal dom Bit -- ^ jtag_TDO
)
vexRiscv# !_sourcePath clk rst0
vexRiscv# dumpVcd !_sourcePath clk rst0
timerInterrupt0
externalInterrupt0
softwareInterrupt0
Expand All @@ -243,7 +248,7 @@ vexRiscv# !_sourcePath clk rst0
jtag_TCK0
jtag_TMS0
jtag_TDI0 = unsafePerformIO $ do
(v, initStage1, initStage2, stepRising, stepFalling, _shutDown) <- vexCPU
(v, initStage1, initStage2, stepRising, stepFalling, _shutDown) <- vexCPU dumpVcd

-- Make sure all the inputs are defined
let
Expand Down Expand Up @@ -366,7 +371,8 @@ vexRiscv# !_sourcePath clk rst0

(
-- ARGs
_
_knownDomain
, _vcdPath
, srcPath
, clk
, rst
Expand Down Expand Up @@ -404,7 +410,7 @@ vexRiscv# !_sourcePath clk rst0
, jtag_TDO

, cpu
) = vecToTuple $ indicesI @35
) = vecToTuple $ indicesI @36
in
InlineYamlPrimitive [Verilog] [__i|
BlackBox:
Expand Down Expand Up @@ -505,23 +511,30 @@ vexRiscv# !_sourcePath clk rst0

-- | Return a function that performs an execution step and a function to free
-- the internal CPU state
vexCPU :: IO
( Ptr VexRiscv
, Ptr VexRiscv -> NON_COMB_INPUT -> IO OUTPUT -- initStage1
, Ptr VexRiscv -> COMB_INPUT -> IO () -- initStage2
, Ptr VexRiscv -> Word64 -> NON_COMB_INPUT -> IO OUTPUT -- rising
, Ptr VexRiscv -> Word64 -> COMB_INPUT -> IO () -- falling
, Ptr VexRiscv -> IO ()
)
vexCPU = do
vexCPU ::
DumpVcd ->
IO
( Ptr VexRiscv
, Ptr VexRiscv -> NON_COMB_INPUT -> IO OUTPUT -- initStage1
, Ptr VexRiscv -> COMB_INPUT -> IO () -- initStage2
, Ptr VexRiscv -> Word64 -> NON_COMB_INPUT -> IO OUTPUT -- rising
, Ptr VexRiscv -> Word64 -> COMB_INPUT -> IO () -- falling
, Ptr VexRiscv -> IO ()
)
vexCPU dumpVcd = do
v <- vexrInit
vcd <- case dumpVcd of
NoDumpVcd -> pure nullPtr
DumpVcd path -> do
vcdPath <- newCString path
vexrInitVcd v vcdPath

let
{-# NOINLINE initStage1 #-}
initStage1 vPtr nonCombInput =
alloca $ \nonCombInputFFI -> alloca $ \outputFFI -> do
poke nonCombInputFFI nonCombInput
vexrInitStage1 vPtr nonCombInputFFI outputFFI
vexrInitStage1 vcd vPtr nonCombInputFFI outputFFI
peek outputFFI

{-# NOINLINE initStage2 #-}
Expand All @@ -534,14 +547,14 @@ vexCPU = do
stepRising vPtr fsSinceLastEvent nonCombInput =
alloca $ \nonCombInputFFI -> alloca $ \outputFFI -> do
poke nonCombInputFFI nonCombInput
vexrStepRisingEdge vPtr fsSinceLastEvent nonCombInputFFI outputFFI
vexrStepRisingEdge vcd vPtr fsSinceLastEvent nonCombInputFFI outputFFI
peek outputFFI

{-# NOINLINE stepFalling #-}
stepFalling vPtr fsSinceLastEvent combInput =
alloca $ \combInputFFI -> do
poke combInputFFI combInput
vexrStepFallingEdge vPtr fsSinceLastEvent combInputFFI
vexrStepFallingEdge vcd vPtr fsSinceLastEvent combInputFFI

shutDown = vexrShutdown

Expand Down
10 changes: 7 additions & 3 deletions clash-vexriscv/src/VexRiscv/FFI.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module VexRiscv.FFI where

import Foreign.Storable
import Foreign.Ptr
import Foreign.C (CString)
import Prelude
import Clash.Prelude
import Data.Word
Expand All @@ -18,15 +19,18 @@ import Data.Word

data VexRiscv

data VerilatedVcdC

data VexRiscvJtagBridge

foreign import ccall unsafe "vexr_init" vexrInit :: IO (Ptr VexRiscv)
foreign import ccall unsafe "vexr_init_vcd" vexrInitVcd :: Ptr VexRiscv -> CString -> IO (Ptr VerilatedVcdC)
foreign import ccall unsafe "vexr_shutdown" vexrShutdown :: Ptr VexRiscv -> IO ()

foreign import ccall unsafe "vexr_init_stage1" vexrInitStage1 :: Ptr VexRiscv -> Ptr NON_COMB_INPUT -> Ptr OUTPUT -> IO ()
foreign import ccall unsafe "vexr_init_stage1" vexrInitStage1 :: Ptr VerilatedVcdC -> Ptr VexRiscv -> Ptr NON_COMB_INPUT -> Ptr OUTPUT -> IO ()
foreign import ccall unsafe "vexr_init_stage2" vexrInitStage2 :: Ptr VexRiscv -> Ptr COMB_INPUT -> IO ()
foreign import ccall unsafe "vexr_step_rising_edge" vexrStepRisingEdge :: Ptr VexRiscv -> Word64 -> Ptr NON_COMB_INPUT -> Ptr OUTPUT -> IO ()
foreign import ccall unsafe "vexr_step_falling_edge" vexrStepFallingEdge :: Ptr VexRiscv -> Word64 -> Ptr COMB_INPUT -> IO ()
foreign import ccall unsafe "vexr_step_rising_edge" vexrStepRisingEdge :: Ptr VerilatedVcdC -> Ptr VexRiscv -> Word64 -> Ptr NON_COMB_INPUT -> Ptr OUTPUT -> IO ()
foreign import ccall unsafe "vexr_step_falling_edge" vexrStepFallingEdge :: Ptr VerilatedVcdC -> Ptr VexRiscv -> Word64 -> Ptr COMB_INPUT -> IO ()

foreign import ccall unsafe "vexr_jtag_bridge_init" vexrJtagBridgeInit :: Word16 -> IO (Ptr VexRiscvJtagBridge)
foreign import ccall unsafe "vexr_jtag_bridge_step" vexrJtagBridgeStep :: Ptr VexRiscvJtagBridge -> Ptr JTAG_OUTPUT -> Ptr JTAG_INPUT -> IO ()
Expand Down
35 changes: 28 additions & 7 deletions clash-vexriscv/src/ffi/impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "VVexRiscv.h"
#include "verilated.h"
#include <verilated_vcd_c.h>
#include "interface.h"

#include <sys/socket.h>
Expand Down Expand Up @@ -31,12 +32,13 @@ typedef struct {

extern "C" {
VVexRiscv* vexr_init();
VerilatedVcdC* vexr_init_vcd(VVexRiscv *top, const char* path);
void vexr_shutdown(VVexRiscv *top);

void vexr_init_stage1(VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *output);
void vexr_init_stage1(VerilatedVcdC *vcd, VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *output);
void vexr_init_stage2(VVexRiscv *top, const COMB_INPUT *input);
void vexr_step_rising_edge(VVexRiscv *top, uint64_t time_add, const NON_COMB_INPUT *input, OUTPUT *output);
void vexr_step_falling_edge(VVexRiscv *top, uint64_t time_add, const COMB_INPUT *input);
void vexr_step_rising_edge(VerilatedVcdC *vcd, VVexRiscv *top, uint64_t time_add, const NON_COMB_INPUT *input, OUTPUT *output);
void vexr_step_falling_edge(VerilatedVcdC *vcd, VVexRiscv *top, uint64_t time_add, const COMB_INPUT *input);

vexr_jtag_bridge_data *vexr_jtag_bridge_init(uint16_t port);
void vexr_jtag_bridge_step(vexr_jtag_bridge_data *d, const JTAG_OUTPUT *output, JTAG_INPUT *input);
Expand All @@ -51,11 +53,21 @@ VVexRiscv* vexr_init()
{
contextp = new VerilatedContext;
VVexRiscv *v = new VVexRiscv(contextp);
Verilated::traceEverOn(true);
v->clk = false;
return v;
}

VerilatedVcdC* vexr_init_vcd(VVexRiscv *top, const char* path)
{
VerilatedVcdC* vcd = new VerilatedVcdC;
Verilated::traceEverOn(true);
// Trace 99 levels of the hierarchy. We only have one level AFAIK, so this
// should be enough :-).
top->trace(vcd, 99);
vcd->open(path);
return vcd;
}

// Set all inputs that cannot combinationaly depend on outputs. I.e., all inputs
// except the Wishbone buses.
void set_non_comb_inputs(VVexRiscv *top, const NON_COMB_INPUT *input)
Expand Down Expand Up @@ -106,14 +118,17 @@ void set_ouputs(VVexRiscv *top, OUTPUT *output)
output->jtag_TDO = top->jtag_tdo;
}

void vexr_init_stage1(VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *output)
void vexr_init_stage1(VerilatedVcdC *vcd, VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *output)
{
// Set all inputs that cannot combinationaly depend on outputs. I.e., all inputs
// except the Wishbone buses.
set_non_comb_inputs(top, input);

// Combinatorially respond to the inputs
top->eval();
if (vcd != NULL) {
vcd->dump(contextp->time());
}
set_ouputs(top, output);

// Advance time by 50 nanoseconds. This is an arbitrary value. Ideally, we would
Expand All @@ -134,7 +149,7 @@ void vexr_shutdown(VVexRiscv *top)
}


void vexr_step_rising_edge(VVexRiscv *top, uint64_t time_add, const NON_COMB_INPUT *input, OUTPUT *output)
void vexr_step_rising_edge(VerilatedVcdC *vcd, VVexRiscv *top, uint64_t time_add, const NON_COMB_INPUT *input, OUTPUT *output)
{
// Advance time since last event. Note that this is 0 for the first call to
// this function. To get a sensisble waveform, vexr_init has already advanced
Expand All @@ -146,12 +161,15 @@ void vexr_step_rising_edge(VVexRiscv *top, uint64_t time_add, const NON_COMB_INP

top->clk = true;
top->eval();
if (vcd != NULL) {
vcd->dump(contextp->time());
}

// Set all outputs
set_ouputs(top, output);
}

void vexr_step_falling_edge(VVexRiscv *top, uint64_t time_add, const COMB_INPUT *input)
void vexr_step_falling_edge(VerilatedVcdC *vcd, VVexRiscv *top, uint64_t time_add, const COMB_INPUT *input)
{
// advance time since last event
contextp->timeInc(time_add); // time_add is in femtoseconds, timeinc expects picoseconds
Expand All @@ -162,6 +180,9 @@ void vexr_step_falling_edge(VVexRiscv *top, uint64_t time_add, const COMB_INPUT

// Evaluate the simulation
top->eval();
if (vcd != NULL) {
vcd->dump(contextp->time());
}
}

vexr_jtag_bridge_data *vexr_jtag_bridge_init(uint16_t port)
Expand Down

0 comments on commit 3c98407

Please sign in to comment.