Compare commits

..

23 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
Nikolay Puzanov
ab5db7f6e8 Add waveforms 2022-12-07 17:00:51 +03:00
Nikolay Puzanov
e8ba09ecab Add VCD to SVG conversion functions 2022-12-06 20:06:26 +03:00
Nikolay Puzanov
daa744f8b2 Replace 'when not' to 'unless' 2022-12-05 20:26:04 +03:00
Nikolay Puzanov
c91003e34b Replace string-trim (left only trim) by string-trim-both 2022-12-04 19:53:56 +03:00
Nikolay Puzanov
53412381fb Optionally enable timestamp in save snippet uri 2022-12-04 12:09:32 +03:00
Nikolay Puzanov
536bde92df Allow to disable code sanitization 2022-12-04 11:29:25 +03:00
Nikolay Puzanov
b9cd29ad87 Fix Crtl-S hotkey 2022-12-04 11:29:10 +03:00
5 changed files with 525 additions and 200 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%;
@@ -63,6 +66,40 @@
overflow: scroll;
}
svg {
fill: none;
stroke: white;
stroke-width: 0;
shape-rendering: crispEdges;
}
svg text {
font-family: 'JetBrains Mono', monospace;
font-size: 10pt;
fill: white;
}
svg #wave-signals {
stroke: #00fcff;
stroke-width: 1;
}
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 #wave-background { fill: #1e2426; }
@media (orientation: landscape) and (not (pointer: coarse)) {
body {
display: flex;
@@ -84,7 +121,7 @@
height: 100%;
}
#log {
#logdiv pre {
position: absolute;
height: 100%;
}
@@ -95,21 +132,18 @@
<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>
<!-- button><span class="text">Verilator</span></button -->
</div>
<div id="text">
<div id="editor">@CODE@</div>
<div id="logdiv">
<pre id="log"></pre>
</div>
<div id="logdiv"></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.13.1/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.13.1/ext-language_tools.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
@@ -123,10 +157,11 @@
enableBasicAutocompletion : true,
enableLiveAutocompletion : true
});
const log_area = document.getElementById('log');
const log_area = document.getElementById('logdiv');
function send_to_sim(uri) {
query = uri + "?width=" + log_area.clientWidth;
log_area.innerHTML = "Please wait...";
fetch(uri,
fetch(query,
{
method: 'POST',
headers: {
@@ -157,13 +192,6 @@
alert("%HELPSTRING%");
};
document.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 's' && e.ctrlKey) {
e.preventDefault();
save_code();
}
});
</script>
</body>
</html>

View File

@@ -19,7 +19,8 @@
(ice-9 popen))
(import (embddr common)
(embddr optargs))
(embddr optargs)
(embddr vcd))
(define INDEX-FILE "index.html")
(define DELETE-WORK-DIR #t)
@@ -28,15 +29,15 @@
(define SNIPPET-FILE "code.sv")
(define IVERILOG-METATOP-FILE "top_iverilog.sv")
(define VERILATOR-CPP-FILE "top_verilator.cpp")
(define USE-TIME-IN-SAVE-URL #f)
(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)
@@ -48,13 +49,20 @@
(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"))
(define DEFAULT-CANVAS-WIDTH 800)
(define (multistring . strings)
(apply string-append
(insert-between strings "\n")))
@@ -77,7 +85,7 @@
;;; Logger
;;;
(define (logger . args)
(when (not (null? args))
(unless (null? args)
(let ((prefix
(format "~a | "
(strftime "%c" (localtime (current-time))))))
@@ -178,7 +186,7 @@
(when (or (not max-file-size)
(< readed max-file-size))
(let ((data (get-bytevector-n in max-read-length)))
(when (not (eof-object? data))
(unless (eof-object? data)
(put-bytevector port data)
(loop (+ readed (bytevector-length data))))))))
#:binary #t))))
@@ -195,6 +203,246 @@
#:content-type content-type
#:content-type-params content-type-params))
;;;
;;; Simple format with convert input number to inexact numbers
;;;
(define (format-inex . args)
(apply format
(map (lambda (arg)
(if (number? arg)
(exact->inexact
(/ (round (* arg 100)) 100))
arg))
args)))
;;;
;;; Make SVG drawing of VCD signals
;;;
(define* (vcd-signal->svg signal tstart tend x y width height text-position
#:key (id #f) (data-hw 3))
(let* ((time-per-pixel (/ (- tend tstart) width))
(sig-width (vcd-signal-size signal))
(sig-type (vcd-signal-type signal))
(y0 y)
(y1 (+ y height))
(yz (+ y (/ height 2)))
(half-dy (/ (- y1 y0) 2))
(id (if id (format " id=\"~a\"" id) "")))
(let next-sample ((samples (vcd-signal-get signal))
(value (if (eq? sig-type 'real)
0
(make-string sig-width #\x)))
(time tstart)
(svg '()))
(if (null? samples)
svg
(let ((sample-time (car (car samples)))
(sample-value (cdr (car samples))))
(if (and (< sample-time tend)
(or (< (- sample-time time) time-per-pixel)
(and (equal? value sample-value)
(not (eq? sig-type 'event)))))
(next-sample
(cdr samples)
(if (<= sample-time tstart)
sample-value
value)
time svg)
(next-sample
(cdr samples) sample-value sample-time
(cons
(cond
((or (eq? sig-type 'bits)
(eq? sig-type 'real))
(if (and (= sig-width 1)
(not (eq? sig-type 'real))
(not (char-ci=? (string-ref value 0) #\x)))
;; Scalar
(let ((x0 (+ x (/ (- time tstart) time-per-pixel)))
(x1 (+ x (/ (- sample-time tstart) time-per-pixel))))
(string-append
(format-inex "<path~a d=\"M~a ~a v~a\"/>" id x0 y0 (- y1 y0))
(format-inex "<path~a d=\"M~a ~a h~a\"/>"
id x0
(cond
((equal? value "0") y1)
((equal? value "1") y0)
(else yz))
(- x1 x0))))
;; Vector or Real
(let ((x0 (+ x (/ (- time tstart) time-per-pixel)))
(x1 (+ x (/ (- sample-time tstart) time-per-pixel))))
(string-append
;; Horizontal lines
(let ((x0 (+ x0 (if (<= time tstart) 0 data-hw)))
(x1 (- x1 (if (>= sample-time tend) 0 data-hw))))
(format-inex
"<path~a d=\"M~a ~a L~a ~a M~a ~a L~a ~a\"/>"
id x0 y0 x1 y0 x0 y1 x1 y1))
;; Left cross
(if (<= time tstart)
""
(format-inex "<path~a d=\"M~a ~a l~a ~a\"/><path d=\"M~a ~a l~a ~a\"/>"
id
x0 yz data-hw (- half-dy)
x0 yz data-hw (+ half-dy)))
;; Right cross
(if (>= sample-time tend)
""
(format-inex "<path~a d=\"M~a ~a l~a ~a\"/><path d=\"M~a ~a l~a ~a\"/>"
id
x1 yz (- data-hw) (- half-dy)
x1 yz (- data-hw) (+ half-dy)))
;; Text
(format-inex "<svg~a x=\"~a\" y=\"~a\" width=\"~a\" height=\"~a\">"
id
(+ x0 data-hw)
y0
(- x1 x0 (* data-hw 2))
(- y1 y0))
(format-inex "<text x=\"~a\" y=\"~a\">" 0 text-position)
(string-upcase
(if (or (eq? sig-type 'real)
(<= sig-width 4))
value
(vcd-binary->hex value #t)))
"</text></svg>"))))
;; Event TODO
((eq? sig-type 'event)
""))
svg))))))))
;;;
;;; Make legend SVG text for VCD
;;;
(define* (vcd-signals->legend signals text-spacing text-position)
(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 12)
(margin 5)
(signal-spacing 5)
(legend-width 150)
(extra-delim-y 3))
(let ((tstart (apply min (vcd-timestamps vcd)))
(tend (apply max (vcd-timestamps vcd)))
(signals (sort
(vcd-signals vcd)
(lambda (a b)
(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)))
(string-ci<? (vcd-signal-name a)
(vcd-signal-name b))))))))
(if (<= tend tstart)
'()
(let ((signals-x (+ (* 2 margin) legend-width))
(signals-w (- width legend-width (* 3 margin)))
(height
(+ (* 2 margin)
(* signal-height (length signals))
(* signal-spacing (- (length signals) 1)))))
(append
;; Header
`(,(format-inex "<svg id=\"wave\" width=\"~a\" height=\"~a\"" width height)
,(format "preserveAspectRatio=\"xMidYMin slice\" role=\"img\">")
,(format "<g id=\"wave-background\"><rect width=\"100%\" height=\"100%\"/></g>"))
;; Legend
`(,(format-inex "<svg id=\"wave-legend\" x=\"~a\" y=\"~a\" width=\"~a\" height=\"~a\">"
margin margin legend-width (- height (* 2 margin))))
(vcd-signals->legend signals
(+ signal-height signal-spacing)
signal-text-position)
'("</svg>")
;; Clock
`(,(format "<g id=\"wave-clock\">"))
(vcd-signal->svg (car signals) tstart tend
signals-x margin
signals-w signal-height signal-text-position)
'("</g>")
;; Rest
`(,(format "<g id=\"wave-signals\">"))
(fold
(lambda (sig n out)
(append
out
(vcd-signal->svg sig tstart tend
signals-x
(+ margin
(* n signal-height)
(* n signal-spacing))
signals-w signal-height signal-text-position)))
'()
(cdr signals)
(iota (length
(cdr signals))
1))
'("</g>")
;; Delimiter
`(,(format-inex "<g id=\"wave-delim\"><path d=\"M~a ~a v~a\"/></g>"
(+ legend-width (* 2 margin)) (- margin extra-delim-y)
(- height (* 2 (- margin extra-delim-y)))))
;; Close svg tag
'("</svg>"))))))
;;;
;;; Execute system command and capture stdout and stderr to string
;;;
@@ -303,8 +551,8 @@
(with-output-to-file command-file
(lambda ()
(println "~a" verilog-file)
(println "~a" metatop-file)
(println "~a" verilog-file)
(println "+define+TESTBENCH")
(println "+timescale+1ps/1ps"))))
work-dir))
@@ -323,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))
@@ -336,65 +584,89 @@
(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))))
work-dir))
;;;
;;; Execute secuence of commands and return (values status "execution log")
;;; Break execution on error
;;;
(define (exec-sequence cmds)
(let-values
(((status logs)
(let next-cmd ((cmds cmds)
(logs '()))
(if (null? cmds)
(values 0 logs)
(let ((cmd (car cmds)))
(let-values (((status out time)
(system-to-string-with-time cmd)))
(let ((logs (cons (exe-log-pretty cmd status out time) logs)))
(if (zero? status)
(next-cmd (cdr cmds) logs)
(values status logs)))))))))
(values status (string-concatenate (reverse logs)))))
;;;
;;; Read and parse VCD file
;;;
(define* (vcd-file-read file #:optional (signal-need? (lambda (s) #t)))
(if (file-exists? file)
(guard #f
(call-with-input-file file
(cut vcd-parse <> signal-need?)))
#f))
;;;
;;; Compile sources and execute simulation with Icarus Verilog
;;; Returns (values status log)
;;; Returns (values status log vcd)
;;;
(define (exec-sim-iverilog top work-dir iverilog-wrap vvp-wrap)
(let ((command-file (path+ work-dir (format "~a.vc" top)))
(exe-file (path+ work-dir (format "~a.out" top))))
;; Compile
(let ((cmdline (format "~a -g2012 -s __~a__ -o ~a -c~a"
(wrap-exe IVERILOG-EXE iverilog-wrap)
top exe-file command-file)))
(let-values (((status out time)
(system-to-string-with-time cmdline)))
(let ((compile-log
(exe-log-pretty cmdline status out time)))
(if (not (zero? status))
(values status compile-log)
(let* ((command-file (path+ work-dir (format "~a.vc" top)))
(exe-file (path+ work-dir (format "~a.out" top)))
(vcd-file (path+ work-dir (format "~a.vcd" top)))
(cmds `(,(format "~a -g2012 -s __~a__ -o ~a -c~a"
(wrap-exe IVERILOG-EXE iverilog-wrap)
top exe-file command-file)
,(format "~a -N ~a" (wrap-exe VVP-EXE vvp-wrap) exe-file))))
;; Execute
(let ((cmdline (format "~a -N ~a" (wrap-exe VVP-EXE vvp-wrap) exe-file)))
(let-values (((status out time)
(system-to-string-with-time cmdline)))
(let ((execution-log
(exe-log-pretty cmdline status out time)))
(values status (string-append compile-log execution-log)))))))))))
(let-values (((status log)
(exec-sequence cmds)))
(if (zero? status)
(values status log (vcd-file-read
vcd-file
(lambda (sig)
(>= (length (vcd-signal-scope sig)) 2))))
(values status log #f)))))
;;;
;;; Compile sources and execute simulation with Verilator
;;; Returns (values status log)
;;; Returns (values status log vcd)
;;;
(define (exec-sim-verilator top work-dir verilator-wrap verilator-sim-wrap)
;; Compile
(let* ((command-file (path+ work-dir (format "~a.vc" top)))
(cmdline (format "~a -f ~a"
(wrap-exe VERILATR-EXE verilator-wrap)
command-file)))
(let-values (((status out time)
(system-to-string-with-time cmdline)))
(let ((compile-log
(exe-log-pretty cmdline status out time)))
(if (not (zero? status))
(values status compile-log)
(vcd-file (path+ work-dir (format "~a.vcd" top)))
(cmds `(,(format "~a -f ~a"
(wrap-exe VERILATOR-EXE verilator-wrap)
command-file)
,(wrap-exe (path+ work-dir (format "~a/~a" top top))
verilator-sim-wrap))))
;; Execute
(let ((cmdline (wrap-exe (path+ work-dir (format "~a/~a" top top))
verilator-sim-wrap)))
(let-values (((status out time)
(system-to-string-with-time cmdline)))
(let ((execution-log
(exe-log-pretty cmdline status out time)))
(values status (string-append compile-log execution-log))))))))))
(let-values (((status log)
(exec-sequence cmds)))
(if (zero? status)
(values status log (vcd-file-read
vcd-file
(lambda (sig)
(>= (length (vcd-signal-scope sig)) 2))))
(values status log #f)))))
;;;
;;; Execute simulation
@@ -404,38 +676,40 @@
(verilator-wrap "") (verilator-sim-wrap "")
(verilator-cpp "") (verilator-build-jobs 0))
(let-values
(((work-dir status log)
(((work-dir status log vcd)
(cond
;; Run Icarus Verilog
((eq? simulator 'iverilog)
(let ((work-dir (make-iverilog-workdir code metatop base top)))
(let-values (((status log)
(let-values (((status log vcd)
(exec-sim-iverilog top work-dir iverilog-wrap vvp-wrap)))
(values work-dir status log))))
(values work-dir status log vcd))))
;; Run Verilator
((eq? simulator 'verilator)
(let ((work-dir (make-verilator-workdir code verilator-cpp verilator-build-jobs base top)))
(let-values (((status log)
(let-values (((status log vcd)
(exec-sim-verilator top work-dir verilator-wrap verilator-sim-wrap)))
(values work-dir status log))))
(values work-dir status log vcd))))
;; Inknown simulator
(else
(values #f #f #f)))))
(values #f #f #f #f)))))
(if (not work-dir)
("ERROR: Unknown simulator")
(values ("ERROR: Unknown simulator") #f)
(begin
;; Delete work dir
(when DELETE-WORK-DIR
(delete-recursive work-dir))
;; Return log
(string-append
log
(format "-----------------\nSimulation complete~a\n"
(if (zero? status) " succesfully"" with errors")))))))
;; Return (values log vcd)
(values
(string-append
log
(format "-----------------\nSimulation complete~a\n"
(if (zero? status) " succesfully"" with errors")))
vcd)))))
;;;
;;; Get app version
@@ -453,7 +727,7 @@
;;; Get storage dir from URI
;;;
(define (get-storage-dir uri root-path)
(string-trim
(string-trim-both
(substring (uri-path uri)
(string-length root-path))
#\/))
@@ -499,12 +773,33 @@
(call-with-input-file (path+ path SNIPPET-FILE)
get-string-all))
;;;
;;; Make log HTML
;;;
(define* (make-log-html log vcd canvas-width #:key
(minimum-sample-width 10)
(maximum-canvas-width 20000))
(if vcd
(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)))
;;;
;;; Web page handler
;;;
(define (make-page-handler host root index-file
work-base stor-base
max-code-size
max-code-size sanitize
iverilog-wrap vvp-wrap
verilator-wrap verilator-sim-wrap verilator-build-jobs)
@@ -513,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
@@ -529,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))
@@ -566,9 +858,17 @@
(<= (string-length code) max-code-size))
code
(substring code 0 max-code-size)))
"")))
""))
;; Request query
(query (let ((q (uri-query (request-uri request))))
(if q
(map (lambda (qstr) (string-split q #\=))
(string-split q #\;))
'()))))
(logger LOG-VERBOSE "Request ~a:~a" (request-method request) path)
(logger LOG-VERBOSE "Request query:~a" query)
(logger LOG-DBG " stor:'~a' len:~a/~a"
ref-stor-dir
(request-content-length request)
@@ -620,62 +920,64 @@
;;
((eq? 'POST (request-method request))
(cond
;; Run iverilog simulation
((equal? path iverilog-path)
(logger LOG-DBG "Request iverilog simulation")
;; Run simulation
((or (equal? path iverilog-path)
(equal? path verilator-path))
(let ((simulator
(if (equal? path iverilog-path)
'iverilog
'verilator)))
(when ref-stor-dir
(save-to-storage (path+ stor-base ref-stor-dir) code))
(logger LOG-DBG "Request ~a simulation" (symbol->string simulator))
(let-values
(((log vcd)
(exec-sim simulator
(if sanitize (sanitize-verilog code) code)
work-base
TOP-MODULE
#:metatop iverilog-metatop
#:vvp-wrap vvp-wrap
#:iverilog-wrap iverilog-wrap
#:verilator-wrap verilator-wrap
#:verilator-sim-wrap verilator-sim-wrap
#:verilator-cpp verilator-cpp
#:verilator-build-jobs verilator-build-jobs)))
(make-response
(exec-sim 'iverilog
(sanitize-verilog code)
work-base
TOP-MODULE
#:metatop iverilog-metatop
#:vvp-wrap vvp-wrap
#:iverilog-wrap iverilog-wrap)
#:content-type 'text/plain))
;; Run verilator simulation
((equal? path verilator-path)
(logger LOG-DBG "Request verilator simulation")
(when ref-stor-dir
(save-to-storage (path+ stor-base ref-stor-dir) code))
(make-response
(exec-sim 'verilator
(sanitize-verilog code)
work-base
TOP-MODULE
#:verilator-wrap verilator-wrap
#:verilator-sim-wrap verilator-sim-wrap
#:verilator-cpp verilator-cpp
#:verilator-build-jobs verilator-build-jobs)
#:content-type 'text/plain))
(let ((canvas-width
(let ((v (assoc "width" query)))
(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
(format "~a-XXXXXX"
(current-time)))))
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))))
(if USE-TIME-IN-SAVE-URL
(format "~a-XXXXXX" (current-time))
"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
@@ -716,6 +1018,7 @@
(-> " --verilator-sim-wrap PATH Verilator simulation executable wrapper.")
(-> " --verilator-build-jobs N Verilator parallel build.")
(-> " --max-len LEN Set maximum code size in symbols. Default: 0 (infinite)")
(-> " --dont-sanitize Do not sanitize verilog code (dangerous)")
(-> " --work-base PATH Set work base path. Default: ./")
(-> " --stor-base PATH Set snippets storage path. Default: ./")
(-> " --log-level LEVEL Set log level from 0 (quiet) to 10 (verbose). Default: 1./")
@@ -725,7 +1028,7 @@
(define (string-trim-if-string str)
(if (string? str)
(string-trim str)
(string-trim-both str)
str))
(define (main args)
@@ -743,24 +1046,26 @@
'(("verilator-sim-wrap") required)
'(("verilator-build-jobs") required)
'(("max-len") required)
'(("dont-sanitize") none)
'(("work-base") required)
'(("stor-base") required)
'(("log-level") required)
'(("help" #\h) none))))
(let ((addr (string-trim (or (option-get opts "addr") "127.0.0.1")))
(port (string->number (string-trim (or (option-get opts "port") "8080"))))
(host (string-trim (or (option-get opts "host") "http://127.0.0.1:8080")))
(root (string-trim (or (option-get opts "root") "")))
(let ((addr (string-trim-both (or (option-get opts "addr") "127.0.0.1")))
(port (string->number (string-trim-both (or (option-get opts "port") "8080"))))
(host (string-trim-both (or (option-get opts "host") "http://127.0.0.1:8080")))
(root (string-trim-both (or (option-get opts "root") "")))
(vvp-wrap (string-trim-if-string (option-get opts "vvp-wrap")))
(iverilog-wrap (string-trim-if-string (option-get opts "iverilog-wrap")))
(verilator-wrap (string-trim-if-string (option-get opts "verilator-wrap")))
(verilator-sim-wrap (string-trim-if-string (option-get opts "verilator-sim-wrap")))
(verilator-build-jobs (string->number (string-trim (or (option-get opts "verilator-build-jobs") "0"))))
(max-code-size (string->number (string-trim (or (option-get opts "max-len") "0"))))
(work-base (string-trim (or (option-get opts "work-base") "./")))
(stor-base (string-trim (or (option-get opts "stor-base") "./")))
(log-level (string->number (string-trim (or (option-get opts "log-level") "1")))))
(verilator-build-jobs (string->number (string-trim-both (or (option-get opts "verilator-build-jobs") "0"))))
(max-code-size (string->number (string-trim-both (or (option-get opts "max-len") "0"))))
(sanitize (not (option-get opts "dont-sanitize")))
(work-base (string-trim-both (or (option-get opts "work-base") "./")))
(stor-base (string-trim-both (or (option-get opts "stor-base") "./")))
(log-level (string->number (string-trim-both (or (option-get opts "log-level") "1")))))
(cond
(err
@@ -782,6 +1087,7 @@
(logger LOG-INFO "verilator simulator wrapper: '~a'" verilator-sim-wrap)
(logger LOG-INFO "verilator build jobs: ~a" verilator-build-jobs)
(logger LOG-INFO "Max code size: ~a" max-code-size)
(logger LOG-INFO "Sanitize code: ~a" sanitize)
(logger LOG-INFO "Work base path: '~a'" work-base)
(logger LOG-INFO "Storage base path: '~a'" stor-base)
(logger LOG-INFO "Log level: '~a'" log-level)
@@ -789,7 +1095,7 @@
(run-server
(make-page-handler host root INDEX-FILE
work-base stor-base
max-code-size
max-code-size sanitize
iverilog-wrap vvp-wrap
verilator-wrap verilator-sim-wrap verilator-build-jobs)
'http `(#:host ,addr #:port ,port)))))))

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;
}