# Best way to get pretty waveforms? / issues with dumpVCD

**URL:** <https://clash-lang.discourse.group/t/best-way-to-get-pretty-waveforms-issues-with-dumpvcd/104>\
**Category:** Clash Help\
**Created:** [February 1, 2026, 12:29am UTC](https://clash-lang.discourse.group/t/best-way-to-get-pretty-waveforms-issues-with-dumpvcd/104 "2026-02-01T00:29:21Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![bry](https://yyz2.discourse-cdn.com/free1/user_avatar/clash-lang.discourse.group/bry/32/60_2.png) [@bry](https://clash-lang.discourse.group/u/bry)\
**Post date:** [February 1, 2026, 12:29am UTC](https://clash-lang.discourse.group/t/best-way-to-get-pretty-waveforms-issues-with-dumpvcd/104/1 "2026-02-01T00:29:21Z")

</div>

Is there a native way to produce nice VCD/FST waveforms with reasonably readable annotations for my native Clash tests? I know I can compile Clash to a Verilog test bench and write up a test harness for that, but I’ve heard that the internal signal names will get mangled which would make debugging somewhat difficult. Is it possible to produce waveforms that don’t have this problem?

---

<div class="post-metadata">

**Author:** ![bry](https://yyz2.discourse-cdn.com/free1/user_avatar/clash-lang.discourse.group/bry/32/60_2.png) [@bry](https://clash-lang.discourse.group/u/bry)\
**Post date:** [February 1, 2026, 7:39pm UTC](https://clash-lang.discourse.group/t/best-way-to-get-pretty-waveforms-issues-with-dumpvcd/104/2 "2026-02-01T19:39:23Z")

</div>

It seems like traceSignal / dumpVCD are what I want, but I’m running into an issue:

```auto
main :: IO ()
main = do
    args <- getArgs
    let tname = P.head args
        (ncycles :: Int) = read $ P.head $ P.tail args
        romName = printf "test-data/%s_rom.bin" tname
        vcdName = printf "test-data/%s_trace.vcd" tname
        soc = exposeClockResetEnable (simpleSoc romName)
            systemClockGen
            systemResetGen
            enableGen
    print ncycles 
    result <- dumpVCD (0, ncycles) soc ["PC", "IR"]
    case result of
        Left msg -> error msg
        Right contents -> T.writeFile vcdName contents
    return ()

```

Based on my understanding of dumpVCD, this should simulate for at most ncycles cycles, but it seems to run forever no matter what I put there.

---

<div class="post-metadata">

**Author:** ![DigitalBrains](https://yyz2.discourse-cdn.com/free1/user_avatar/clash-lang.discourse.group/digitalbrains/32/28_2.png) [@DigitalBrains](https://clash-lang.discourse.group/u/DigitalBrains)\
**Post date:** [February 2, 2026, 10:09am UTC](https://clash-lang.discourse.group/t/best-way-to-get-pretty-waveforms-issues-with-dumpvcd/104/3 "2026-02-02T10:09:40Z")

</div>

My first guess would be that the `traceSignal`s are not completely correctly wired up.

The first thing `dumpVCD` does, without considering `(0, ncycles)` yet, is keep simulating `soc` until all of the `[”PC”, “IR”]` traces are available. If one or both never become available, it will endlessly keep simulating the signal.

The idea is that `traceSignal` is wired up such that at some point while simulating `soc`, it becomes necessary to evaluate the `traceSignal` invocation in order to compute the next output value of `soc`. The evaluation of the result of `traceSignal` is what makes the traced signal available to `dumpVCD`. Because of lazy evaluation, `traceSignal` might not be needed to compute the first couple of output samples of `soc`, but if set up correctly, at some point it will be necessary and will be evaluated.

That’s why `traceSignal` outputs the signal it is tracing. If you want to trace a signal named `ir`, you would write

```haskell
ir0 = traceSignal "IR" ir

```

and _then you would use `ir0` in the places where the signal `ir` was used originally_. At some point, the signal `ir0` should affect the next output of `soc`. Now GHC will evaluate `traceSignal “IR” ir` to get the value of `ir0` and that hooks the trace into `dumpVCD`, makes the trace available. Once both `"IR"`and `"PC"`were needed just once to compute the next value of `soc`, `dumpVCD` can finally start doing its real business and output the `ncycles` of waveforms. At this point it doesn’t matter anymore how much the `traceSignal`s and the `soc` interact; all that was needed was for it to occur once. The VCD will just contain all values of the traced signal in the interval regardless of whether those particular values were needed to compute `soc` outputs. In fact, `soc` isn’t even used anymore. Its only role was to get both `traceSignal`s to evaluate just once, hooking the traced signal into `dumpVCD`. If there are more `traceSignal`s than `[”PC”, “IR”]`, they will be in the VCD file if they happened to be evaluated while looking for `[”PC”, “IR”]`, or they will not be if both of `[”PC”, “IR”]` were found before GHC needed to evaluate the extra `traceSignal`s.

Note that clever use of `seq` can also get GHC to evaluate a `traceSignal` even when the traced signal does not affect the output of `soc`, but let’s cross that bridge when we get to it. This could be a nice way to transform your signal before it gets output to VCD.

---

<div class="post-metadata">

**Author:** ![DigitalBrains](https://yyz2.discourse-cdn.com/free1/user_avatar/clash-lang.discourse.group/digitalbrains/32/28_2.png) [@DigitalBrains](https://clash-lang.discourse.group/u/DigitalBrains)\
**Post date:** [February 2, 2026, 10:16am UTC](https://clash-lang.discourse.group/t/best-way-to-get-pretty-waveforms-issues-with-dumpvcd/104/4 "2026-02-02T10:16:40Z")

</div>

Ah, please note that the _output_ of the first clock cycle is at _t_ = 0 s in the VCD file. You cannot see the initial value of the signal in the waveform. This is actually undesirable, and we want to fix it; we’re discussing the issue [here](https://github.com/clash-lang/clash-compiler/discussions/3067), where the first post also compares `sample`, `dumpVCD` and simulating a test bench in Modelsim. This might help you understand the start of the waveform you will get.
