Compare commits

...

16 Commits

Author SHA1 Message Date
Nikolay Puzanov
3aee2d2f38 Fix help message 2024-04-14 16:16:45 +03:00
Nikolay Puzanov
b84ec9a1c5 Remove clock from testbench input 2024-04-14 16:08:20 +03:00
Nikolay Puzanov
1e0bfb58cf Add support for verilator 5.018+ 2023-11-07 14:27:08 +03:00
Nikolay Puzanov
e67558de14 Enable dump in iverilog testbench template 2023-06-02 11:08:51 +03:00
Nikolay Puzanov
54f7e2be54 Enable trace of structs 2022-12-13 11:49:15 +03:00
Nikolay Puzanov
cc605cce85 Top module for all simulators must have an input clock signal 2022-12-09 10:21:40 +03:00
Nikolay Puzanov
2f9f5b6dd1 Disable Save hotkey (to prevent thoughtless use) 2022-12-09 10:13:03 +03:00
Nikolay Puzanov
cb8bc37dfe Disable ligatures 2022-12-09 09:23:07 +03:00
Nikolay Puzanov
1e12d5d3e2 Don't save unmodified code 2022-12-09 09:20:53 +03:00
Nikolay Puzanov
1bc6ec544d Save code only by Save button. Save always to new location 2022-12-09 09:07:13 +03:00
Nikolay Puzanov
d04282e1c7 Add simple scroll of long waveform 2022-12-08 19:06:08 +03:00
Nikolay Puzanov
664b7b1f5c Buttons can move to new line 2022-12-07 18:07:00 +03:00
Nikolay Puzanov
37b7a54d3b Visual improvements 2022-12-07 17:55:21 +03:00
Nikolay Puzanov
01c982fada Fix parsing canvas width 2022-12-07 17:54:50 +03:00
Nikolay Puzanov
931f4d8aa4 Fix signals sorting (put clock on the top) 2022-12-07 17:53:50 +03:00
Nikolay Puzanov
90905544d4 Upcase signal values 2022-12-07 17:53:17 +03:00
5 changed files with 169 additions and 134 deletions

View File

@@ -10,4 +10,11 @@ module testbench;
initial begin
$finish;
end
`ifdef DUMP
initial begin
$dumpfile("testbench.fst");
$dumpvars(0, testbench);
end
`endif
endmodule

View File

@@ -11,19 +11,25 @@
box-sizing: border-box;
margin: 0;
padding: 0;
font-variant-ligatures: none;
}
body {
padding: 3px;
font-size: 14px;
font-family: 'JetBrains Mono', monospace;
font-size: 10pt;
}
pre {
font-family: 'JetBrains Mono', monospace;
font-size: 10pt;
}
#buttons {
color: white;
background-color: #2d3d40;
width: 100%;
height: 38px;
padding: 4px;
padding: 3px;
font-size: inherit;
}
@@ -38,16 +44,13 @@
background-color: #dbdbdb;
margin-left: 1px;
margin-right: 1px;
height: 100%;
margin-top: 2px;
margin-bottom: 2px;
height: 25px;
}
button span.text { padding: 4px; }
#text {
font-family: 'JetBrains Mono', monospace;
font-size: inherit;
}
#editor {
height: 75vh;
width: 100%;
@@ -70,6 +73,12 @@
shape-rendering: crispEdges;
}
svg text {
font-family: 'JetBrains Mono', monospace;
font-size: 10pt;
fill: white;
}
svg #wave-signals {
stroke: #00fcff;
stroke-width: 1;
@@ -77,20 +86,18 @@
svg #wave-clock {
stroke: #fffe9a;
font-size: 8pt;
stroke-width: 1;
}
svg #wave-signals text { font-size: 8pt; }
svg #wave-clock text { font-size: 8pt; }
svg #wave-delim {
stroke: #d0d0d0;
stroke-width: 2;
}
svg text {
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
fill: white;
}
svg #wave-background { fill: #1e2426; }
@media (orientation: landscape) and (not (pointer: coarse)) {
@@ -113,6 +120,11 @@
width: 50%;
height: 100%;
}
#logdiv pre {
position: absolute;
height: 100%;
}
}
</style>
</head>
@@ -120,8 +132,7 @@
<body>
<div id="buttons">
<button onclick="show_help()"><span class="text">?</span></button>
<button onclick="save_code('%SAVECODEURI%')"><span class="text">Save (ctrl-s)</span></button>
<button onclick="save_code('%SAVEASURI%')"><span class="text">Save as new</span></button>
<button onclick="save_code('%SAVECODEURI%')"><span class="text">Save</span></button>
Sim:
<button onclick="send_to_sim('%IVERILOGPOSTURI%')"><span class="text">Icarus</span></button>
<button onclick="send_to_sim('%VERILATORPOSTURI%')"><span class="text">Verilator</span></button>
@@ -181,13 +192,6 @@
alert("%HELPSTRING%");
};
document.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 's' && e.ctrlKey) {
e.preventDefault();
save_code('%SAVECODEURI%');
}
});
</script>
</body>
</html>

View File

@@ -33,12 +33,11 @@
(define IVERILOG-EXE "iverilog")
(define VVP-EXE "vvp")
(define VERILATR-EXE "verilator")
(define VERILATOR-EXE "verilator")
(define URI-IVERILOG "iverilog")
(define URI-VERILATOR "verilator")
(define URI-SAVE-CODE "save")
(define URI-SAVEAS-CODE "saveas")
(define LOG-DBG 3)
(define LOG-VERBOSE 2)
@@ -50,9 +49,14 @@
(define DEFAULT-CODE
(string-append
"`timescale 1ps/1ps\n\n"
(format "module ~a (input clock);\n" TOP-MODULE)
;; (format "module ~a (input clock);\n" TOP-MODULE)
(format "module ~a;\n" TOP-MODULE)
" logic clock = 1'b0;\n"
" initial forever #(5ns) clock = ~clock;\n"
"\n"
" initial begin\n"
" $display(\"Hello world!\");\n"
" repeat(10) @(posedge clock);\n"
" $finish();\n"
" end\n"
"endmodule\n"))
@@ -304,10 +308,10 @@
(- x1 x0 (* data-hw 2))
(- y1 y0))
(format-inex "<text x=\"~a\" y=\"~a\">" 0 text-position)
(if (or (eq? sig-type 'real)
(< sig-width 4))
value
(string-upcase
(string-upcase
(if (or (eq? sig-type 'real)
(<= sig-width 4))
value
(vcd-binary->hex value #t)))
"</text></svg>"))))
@@ -320,42 +324,67 @@
;;; Make legend SVG text for VCD
;;;
(define* (vcd-signals->legend signals text-spacing text-position)
(map
(lambda (sig n)
(string-append
(format-inex "<text x=\"0\" y=\"~a\">~a</text>"
(+ (* n text-spacing) text-position)
(vcd-signal-name sig))))
signals
(iota (length signals))))
(if (null? signals)
""
(let ((common-scope-len
(length
(fold
(lambda (scope common)
(let loop ((scope scope)
(common common)
(out '()))
(if (or (null? scope)
(null? common))
(reverse out)
(if (string-ci= (car scope)
(car common))
(loop (cdr scope)
(cdr common)
(cons (car scope) out))
(reverse out)))))
(vcd-signal-scope (car signals))
(map vcd-signal-scope (cdr signals))))))
(map
(lambda (sig n)
(string-append
(format-inex "<text x=\"0\" y=\"~a\">~a</text>"
(+ (* n text-spacing) text-position)
(string-concatenate
(insert-between
(append
(drop (vcd-signal-scope sig)
common-scope-len)
`(,(vcd-signal-name sig)))
".")))))
signals
(iota (length signals))))))
;;;
;;; Create SVG from VCD
;;;
(define* (vcd->svg vcd width #:key
(signal-height 15)
(signal-text-position 13)
(signal-text-position 12)
(margin 5)
(signal-spacing 5)
(legend-width 100)
(legend-width 150)
(extra-delim-y 3))
(let ((tstart (apply min (vcd-timestamps vcd)))
(tend (apply max (vcd-timestamps vcd)))
(signals (sort
(remove
(lambda (sig) (< (length (vcd-signal-scope sig)) 2))
(vcd-signals vcd))
(vcd-signals vcd)
(lambda (a b)
(or
(and (equal? (vcd-signal-name a) "clock")
(not (equal? (vcd-signal-name b) "clock")))
(and (not (equal? (vcd-signal-name b) "clock"))
(or
(and (equal? (vcd-signal-name a) "clock")
(not (equal? (vcd-signal-name b) "clock")))
(< (length (vcd-signal-scope a))
(length (vcd-signal-scope b)))
(< (length (vcd-signal-scope a))
(length (vcd-signal-scope b)))
(string-ci<? (vcd-signal-name a)
(vcd-signal-name b)))))))
(string-ci<? (vcd-signal-name a)
(vcd-signal-name b))))))))
(if (<= tend tstart)
'()
@@ -542,7 +571,7 @@
(with-output-to-file command-file
(lambda ()
(println "+define+TESTBENCH")
(println "-DTESTBENCH")
(println "--timescale 1ps/1ps")
(println "--top-module ~a" top)
(println "--Mdir ~a" (path+ work-dir top))
@@ -555,9 +584,11 @@
(println "--build")
(println "-sv")
(println "-Wno-WIDTH")
(println "+1800-2017ext+sv")
(println "+1800-2023ext+sv")
(println "--timing")
(println "--trace")
(println "--trace-structs")
(println "--trace-depth 1")
(println "--quiet-exit")
(println "~a" verilog-file)
(println "~a.cpp" top))))
@@ -612,7 +643,7 @@
(values status log (vcd-file-read
vcd-file
(lambda (sig)
(= 2 (length (vcd-signal-scope sig))))))
(>= (length (vcd-signal-scope sig)) 2))))
(values status log #f)))))
;;;
@@ -623,7 +654,7 @@
(let* ((command-file (path+ work-dir (format "~a.vc" top)))
(vcd-file (path+ work-dir (format "~a.vcd" top)))
(cmds `(,(format "~a -f ~a"
(wrap-exe VERILATR-EXE verilator-wrap)
(wrap-exe VERILATOR-EXE verilator-wrap)
command-file)
,(wrap-exe (path+ work-dir (format "~a/~a" top top))
verilator-sim-wrap))))
@@ -634,7 +665,7 @@
(values status log (vcd-file-read
vcd-file
(lambda (sig)
(= 2 (length (vcd-signal-scope sig))))))
(>= (length (vcd-signal-scope sig)) 2))))
(values status log #f)))))
;;;
@@ -745,12 +776,22 @@
;;;
;;; Make log HTML
;;;
(define (make-log-html log vcd canvas-width)
(define* (make-log-html log vcd canvas-width #:key
(minimum-sample-width 10)
(maximum-canvas-width 20000))
(if vcd
(format "~a<br/>\n<pre id=\"log\">~a</pre>\n"
(string-concatenate
(vcd->svg vcd canvas-width))
log)
(let ((need-width
(* minimum-sample-width
(length (vcd-timestamps vcd)))))
(format "~a<br/>\n<pre id=\"log\">~a</pre>\n"
(string-concatenate
(vcd->svg vcd
(if (< need-width canvas-width)
canvas-width
(if (> need-width maximum-canvas-width)
maximum-canvas-width
need-width))))
log))
(format "<pre>~a</pre>\n" log)))
;;;
@@ -767,14 +808,12 @@
(iverilog-path (append root-path `(,URI-IVERILOG)))
(verilator-path (append root-path `(,URI-VERILATOR)))
(savecode-path (append root-path `(,URI-SAVE-CODE)))
(saveas-path (append root-path `(,URI-SAVEAS-CODE)))
(index-html
(read-template-text
index-file
`(("IVERILOGPOSTURI" ,(encode-and-join-uri-path iverilog-path))
("VERILATORPOSTURI" ,(encode-and-join-uri-path verilator-path))
("SAVECODEURI" ,(encode-and-join-uri-path savecode-path))
("SAVEASURI" ,(encode-and-join-uri-path saveas-path))
("HELPSTRING",
(string-concatenate
(insert-between
@@ -783,14 +822,13 @@
,(format "Icarus: ~a"
(app-version (wrap-exe IVERILOG-EXE iverilog-wrap) "-V"))
,(format "Verilator: ~a"
(app-version (wrap-exe VERILATR-EXE verilator-wrap)))
(app-version (wrap-exe VERILATOR-EXE verilator-wrap)))
""
"Rules:"
"0. Don't fool around ;)"
"1. The top module must be named 'testbench'."
"2. The top module for the Verilator must have an input clock signal."
"3. Code size should not exceed 10000 characters."
"4. Code execution time no longer than 5 seconds.")
"2. Code size should not exceed 10000 characters."
"3. Code execution time no longer than 5 seconds.")
"\\n"))))))
(iverilog-metatop
(call-with-input-file IVERILOG-METATOP-FILE get-string-all))
@@ -891,10 +929,6 @@
'verilator)))
(logger LOG-DBG "Request ~a simulation" (symbol->string simulator))
(when ref-stor-dir
(save-to-storage (path+ stor-base ref-stor-dir) code))
(let-values
(((log vcd)
(exec-sim simulator
@@ -911,33 +945,39 @@
(let ((canvas-width
(let ((v (assoc "width" query)))
(and v (string->number (cadr v)) DEFAULT-CANVAS-WIDTH))))
(or (and v (string->number (cadr v)))
DEFAULT-CANVAS-WIDTH))))
(make-response
(make-log-html log vcd canvas-width)
#:content-type 'text/plain)))))
;; Save snippet
((or (equal? path savecode-path)
(equal? path saveas-path))
(let ((saveas (equal? path saveas-path)))
(logger LOG-DBG "Request code saving~a"
(if saveas " as new snippet" ""))
(let ((stor-dir
(if (or saveas
(not ref-stor-dir))
((or (equal? path savecode-path))
(logger LOG-DBG "Request code saving")
(let ((old-code
(if ref-stor-dir
(read-from-storage (path+ stor-base ref-stor-dir))
DEFAULT-CODE)))
(if (equal? code old-code)
;; If code is not changed do nothing
(make-response
(encode-and-join-uri-path
(append root-path `(,ref-stor-dir)))
#:content-type 'text/plain)
;; New code save to new location
(let ((stor-dir
(basename
(mkdtemp
(path+
stor-base
(if USE-TIME-IN-SAVE-URL
(format "~a-XXXXXX" (current-time))
"XXXXXX"))))
ref-stor-dir)))
(save-to-storage (path+ stor-base stor-dir) code)
(make-response
(encode-and-join-uri-path
(append root-path `(,stor-dir)))
#:content-type 'text/plain))))
"XXXXXX"))))))
(save-to-storage (path+ stor-base stor-dir) code)
(make-response
(encode-and-join-uri-path
(append root-path `(,stor-dir)))
#:content-type 'text/plain)))))
;; Wrong POST request
(else

View File

@@ -1,12 +1,11 @@
`timescale 1ps/1ps
module __@TOPMODULE@__;
logic clock = 1'b0;
initial forever #(5ns) clock = ~clock;
@TOPMODULE@ @TOPMODULE@ (clock);
@TOPMODULE@ @TOPMODULE@ ();
initial begin
$dumpfile("@WORKDIR@/@TOPMODULE@.vcd");
$dumpvars(0, @TOPMODULE@);
$dumpvars(1, @TOPMODULE@);
$dumplimit(100000);
end
endmodule

View File

@@ -1,58 +1,43 @@
#include "verilated.h"
#include "verilated_vcd_c.h"
#include "V@TOPMODULE@.h"
#include <cstdint>
#include <verilated.h>
#include <verilated_vcd_c.h>
#define DUMPFILE "@WORKDIR@/@TOPMODULE@.vcd"
#define CLOCK_HALF_PERIOD 5000
int main(int argc, char **argv)
{
VerilatedContext *ctx = new VerilatedContext;
ctx->commandArgs(argc, argv);
int main(int argc, char** argv, char**) {
// Setup context, defaults, and parse command line
Verilated::debug(0);
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
contextp->traceEverOn(true);
contextp->commandArgs(argc, argv);
/* Create model instance */
V@TOPMODULE@ *top = new V@TOPMODULE@(ctx);
// Construct the Verilated model, from Vtop.h generated from Verilating
const std::unique_ptr<V@TOPMODULE@> topp{new V@TOPMODULE@{contextp.get()}};
#if (VM_TRACE == 1)
VerilatedVcdC *vcd = new VerilatedVcdC;
ctx->traceEverOn(true);
top->trace(vcd, 99);
topp->trace(vcd, 99);
vcd->open(DUMPFILE);
#endif
top->clock = 0;
/* ---- Evaluation loop ---- */
for (;;) {
/* Eval */
top->eval();
/* Trace steady-state values */
#if (VM_TRACE == 1)
if (vcd) vcd->dump(ctx->time());
#endif
/* Break exactly after calling $finish */
if (ctx->gotFinish()) break;
/* Clock event */
ctx->timeInc(CLOCK_HALF_PERIOD);
top->clock = top->clock ? 0 : 1;
// Simulate until $finish
while (!contextp->gotFinish()) {
// Evaluate model
topp->eval();
vcd->dump(contextp->time());
// Advance time
if (!topp->eventsPending()) break;
contextp->time(topp->nextTimeSlot());
}
top->final();
printf("[%lu] Stop simulation\n", ctx->time());
#if (VM_TRACE == 1)
if (vcd) {
vcd->close();
delete vcd;
if (!contextp->gotFinish()) {
VL_DEBUG_IF(VL_PRINTF("+ Exiting without $finish; no events left\n"););
}
#endif
delete top;
delete ctx;
// Execute 'final' processes
topp->final();
// Print statistical summary report
contextp->statsPrintSummary();
vcd->close();
return 0;
}