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?
It seems like traceSignal / dumpVCD are what I want, but I’m running into an issue:
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.
My first guess would be that the traceSignals 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
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 traceSignals 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 traceSignals to evaluate just once, hooking the traced signal into dumpVCD. If there are more traceSignals 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 traceSignals.
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.
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, 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.