Compare commits
23 Commits
c6c6744b22
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aee2d2f38 | ||
|
|
b84ec9a1c5 | ||
|
|
1e0bfb58cf | ||
|
|
e67558de14 | ||
|
|
54f7e2be54 | ||
|
|
cc605cce85 | ||
|
|
2f9f5b6dd1 | ||
|
|
cb8bc37dfe | ||
|
|
1e12d5d3e2 | ||
|
|
1bc6ec544d | ||
|
|
d04282e1c7 | ||
|
|
664b7b1f5c | ||
|
|
37b7a54d3b | ||
|
|
01c982fada | ||
|
|
931f4d8aa4 | ||
|
|
90905544d4 | ||
|
|
ab5db7f6e8 | ||
|
|
e8ba09ecab | ||
|
|
daa744f8b2 | ||
|
|
c91003e34b | ||
|
|
53412381fb | ||
|
|
536bde92df | ||
|
|
b9cd29ad87 |
@@ -10,4 +10,11 @@ module testbench;
|
|||||||
initial begin
|
initial begin
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
`ifdef DUMP
|
||||||
|
initial begin
|
||||||
|
$dumpfile("testbench.fst");
|
||||||
|
$dumpvars(0, testbench);
|
||||||
|
end
|
||||||
|
`endif
|
||||||
endmodule
|
endmodule
|
||||||
|
|||||||
@@ -11,19 +11,25 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
font-variant-ligatures: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
font-size: 14px;
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#buttons {
|
#buttons {
|
||||||
color: white;
|
color: white;
|
||||||
background-color: #2d3d40;
|
background-color: #2d3d40;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 38px;
|
padding: 3px;
|
||||||
padding: 4px;
|
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,16 +44,13 @@
|
|||||||
background-color: #dbdbdb;
|
background-color: #dbdbdb;
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
margin-right: 1px;
|
margin-right: 1px;
|
||||||
height: 100%;
|
margin-top: 2px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
height: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button span.text { padding: 4px; }
|
button span.text { padding: 4px; }
|
||||||
|
|
||||||
#text {
|
|
||||||
font-family: 'JetBrains Mono', monospace;
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
#editor {
|
#editor {
|
||||||
height: 75vh;
|
height: 75vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -63,6 +66,40 @@
|
|||||||
overflow: scroll;
|
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)) {
|
@media (orientation: landscape) and (not (pointer: coarse)) {
|
||||||
body {
|
body {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -84,7 +121,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#log {
|
#logdiv pre {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
@@ -95,21 +132,18 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
<button onclick="show_help()"><span class="text">?</span></button>
|
<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('%SAVECODEURI%')"><span class="text">Save</span></button>
|
||||||
<button onclick="save_code('%SAVEASURI%')"><span class="text">Save as new</span></button>
|
|
||||||
Sim:
|
Sim:
|
||||||
<button onclick="send_to_sim('%IVERILOGPOSTURI%')"><span class="text">Icarus</span></button>
|
<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 onclick="send_to_sim('%VERILATORPOSTURI%')"><span class="text">Verilator</span></button>
|
||||||
<!-- button><span class="text">Verilator</span></button -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="text">
|
<div id="text">
|
||||||
<div id="editor">@CODE@</div>
|
<div id="editor">@CODE@</div>
|
||||||
<div id="logdiv">
|
<div id="logdiv"></div>
|
||||||
<pre id="log"></pre>
|
|
||||||
</div>
|
|
||||||
</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/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 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">
|
<script type="text/javascript" charset="utf-8">
|
||||||
@@ -123,10 +157,11 @@
|
|||||||
enableBasicAutocompletion : true,
|
enableBasicAutocompletion : true,
|
||||||
enableLiveAutocompletion : true
|
enableLiveAutocompletion : true
|
||||||
});
|
});
|
||||||
const log_area = document.getElementById('log');
|
const log_area = document.getElementById('logdiv');
|
||||||
function send_to_sim(uri) {
|
function send_to_sim(uri) {
|
||||||
|
query = uri + "?width=" + log_area.clientWidth;
|
||||||
log_area.innerHTML = "Please wait...";
|
log_area.innerHTML = "Please wait...";
|
||||||
fetch(uri,
|
fetch(query,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -157,13 +192,6 @@
|
|||||||
alert("%HELPSTRING%");
|
alert("%HELPSTRING%");
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('keydown', (e) => {
|
|
||||||
if (e.key.toLowerCase() === 's' && e.ctrlKey) {
|
|
||||||
e.preventDefault();
|
|
||||||
save_code();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
(ice-9 popen))
|
(ice-9 popen))
|
||||||
|
|
||||||
(import (embddr common)
|
(import (embddr common)
|
||||||
(embddr optargs))
|
(embddr optargs)
|
||||||
|
(embddr vcd))
|
||||||
|
|
||||||
(define INDEX-FILE "index.html")
|
(define INDEX-FILE "index.html")
|
||||||
(define DELETE-WORK-DIR #t)
|
(define DELETE-WORK-DIR #t)
|
||||||
@@ -28,15 +29,15 @@
|
|||||||
(define SNIPPET-FILE "code.sv")
|
(define SNIPPET-FILE "code.sv")
|
||||||
(define IVERILOG-METATOP-FILE "top_iverilog.sv")
|
(define IVERILOG-METATOP-FILE "top_iverilog.sv")
|
||||||
(define VERILATOR-CPP-FILE "top_verilator.cpp")
|
(define VERILATOR-CPP-FILE "top_verilator.cpp")
|
||||||
|
(define USE-TIME-IN-SAVE-URL #f)
|
||||||
|
|
||||||
(define IVERILOG-EXE "iverilog")
|
(define IVERILOG-EXE "iverilog")
|
||||||
(define VVP-EXE "vvp")
|
(define VVP-EXE "vvp")
|
||||||
(define VERILATR-EXE "verilator")
|
(define VERILATOR-EXE "verilator")
|
||||||
|
|
||||||
(define URI-IVERILOG "iverilog")
|
(define URI-IVERILOG "iverilog")
|
||||||
(define URI-VERILATOR "verilator")
|
(define URI-VERILATOR "verilator")
|
||||||
(define URI-SAVE-CODE "save")
|
(define URI-SAVE-CODE "save")
|
||||||
(define URI-SAVEAS-CODE "saveas")
|
|
||||||
|
|
||||||
(define LOG-DBG 3)
|
(define LOG-DBG 3)
|
||||||
(define LOG-VERBOSE 2)
|
(define LOG-VERBOSE 2)
|
||||||
@@ -48,13 +49,20 @@
|
|||||||
(define DEFAULT-CODE
|
(define DEFAULT-CODE
|
||||||
(string-append
|
(string-append
|
||||||
"`timescale 1ps/1ps\n\n"
|
"`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"
|
" initial begin\n"
|
||||||
" $display(\"Hello world!\");\n"
|
" $display(\"Hello world!\");\n"
|
||||||
|
" repeat(10) @(posedge clock);\n"
|
||||||
" $finish();\n"
|
" $finish();\n"
|
||||||
" end\n"
|
" end\n"
|
||||||
"endmodule\n"))
|
"endmodule\n"))
|
||||||
|
|
||||||
|
(define DEFAULT-CANVAS-WIDTH 800)
|
||||||
|
|
||||||
(define (multistring . strings)
|
(define (multistring . strings)
|
||||||
(apply string-append
|
(apply string-append
|
||||||
(insert-between strings "\n")))
|
(insert-between strings "\n")))
|
||||||
@@ -77,7 +85,7 @@
|
|||||||
;;; Logger
|
;;; Logger
|
||||||
;;;
|
;;;
|
||||||
(define (logger . args)
|
(define (logger . args)
|
||||||
(when (not (null? args))
|
(unless (null? args)
|
||||||
(let ((prefix
|
(let ((prefix
|
||||||
(format "~a | "
|
(format "~a | "
|
||||||
(strftime "%c" (localtime (current-time))))))
|
(strftime "%c" (localtime (current-time))))))
|
||||||
@@ -178,7 +186,7 @@
|
|||||||
(when (or (not max-file-size)
|
(when (or (not max-file-size)
|
||||||
(< readed max-file-size))
|
(< readed max-file-size))
|
||||||
(let ((data (get-bytevector-n in max-read-length)))
|
(let ((data (get-bytevector-n in max-read-length)))
|
||||||
(when (not (eof-object? data))
|
(unless (eof-object? data)
|
||||||
(put-bytevector port data)
|
(put-bytevector port data)
|
||||||
(loop (+ readed (bytevector-length data))))))))
|
(loop (+ readed (bytevector-length data))))))))
|
||||||
#:binary #t))))
|
#:binary #t))))
|
||||||
@@ -195,6 +203,246 @@
|
|||||||
#:content-type content-type
|
#:content-type content-type
|
||||||
#:content-type-params content-type-params))
|
#: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
|
;;; Execute system command and capture stdout and stderr to string
|
||||||
;;;
|
;;;
|
||||||
@@ -303,8 +551,8 @@
|
|||||||
|
|
||||||
(with-output-to-file command-file
|
(with-output-to-file command-file
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(println "~a" verilog-file)
|
|
||||||
(println "~a" metatop-file)
|
(println "~a" metatop-file)
|
||||||
|
(println "~a" verilog-file)
|
||||||
(println "+define+TESTBENCH")
|
(println "+define+TESTBENCH")
|
||||||
(println "+timescale+1ps/1ps"))))
|
(println "+timescale+1ps/1ps"))))
|
||||||
work-dir))
|
work-dir))
|
||||||
@@ -323,7 +571,7 @@
|
|||||||
|
|
||||||
(with-output-to-file command-file
|
(with-output-to-file command-file
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(println "+define+TESTBENCH")
|
(println "-DTESTBENCH")
|
||||||
(println "--timescale 1ps/1ps")
|
(println "--timescale 1ps/1ps")
|
||||||
(println "--top-module ~a" top)
|
(println "--top-module ~a" top)
|
||||||
(println "--Mdir ~a" (path+ work-dir top))
|
(println "--Mdir ~a" (path+ work-dir top))
|
||||||
@@ -336,65 +584,89 @@
|
|||||||
(println "--build")
|
(println "--build")
|
||||||
(println "-sv")
|
(println "-sv")
|
||||||
(println "-Wno-WIDTH")
|
(println "-Wno-WIDTH")
|
||||||
(println "+1800-2017ext+sv")
|
(println "+1800-2023ext+sv")
|
||||||
(println "--timing")
|
(println "--timing")
|
||||||
(println "--trace")
|
(println "--trace")
|
||||||
|
(println "--trace-structs")
|
||||||
|
(println "--trace-depth 1")
|
||||||
(println "--quiet-exit")
|
(println "--quiet-exit")
|
||||||
(println "~a" verilog-file)
|
(println "~a" verilog-file)
|
||||||
(println "~a.cpp" top))))
|
(println "~a.cpp" top))))
|
||||||
work-dir))
|
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
|
;;; 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)
|
(define (exec-sim-iverilog top work-dir iverilog-wrap vvp-wrap)
|
||||||
(let ((command-file (path+ work-dir (format "~a.vc" top)))
|
(let* ((command-file (path+ work-dir (format "~a.vc" top)))
|
||||||
(exe-file (path+ work-dir (format "~a.out" top))))
|
(exe-file (path+ work-dir (format "~a.out" top)))
|
||||||
;; Compile
|
(vcd-file (path+ work-dir (format "~a.vcd" top)))
|
||||||
(let ((cmdline (format "~a -g2012 -s __~a__ -o ~a -c~a"
|
(cmds `(,(format "~a -g2012 -s __~a__ -o ~a -c~a"
|
||||||
(wrap-exe IVERILOG-EXE iverilog-wrap)
|
(wrap-exe IVERILOG-EXE iverilog-wrap)
|
||||||
top exe-file command-file)))
|
top exe-file command-file)
|
||||||
(let-values (((status out time)
|
,(format "~a -N ~a" (wrap-exe VVP-EXE vvp-wrap) exe-file))))
|
||||||
(system-to-string-with-time cmdline)))
|
|
||||||
(let ((compile-log
|
|
||||||
(exe-log-pretty cmdline status out time)))
|
|
||||||
(if (not (zero? status))
|
|
||||||
(values status compile-log)
|
|
||||||
|
|
||||||
;; Execute
|
(let-values (((status log)
|
||||||
(let ((cmdline (format "~a -N ~a" (wrap-exe VVP-EXE vvp-wrap) exe-file)))
|
(exec-sequence cmds)))
|
||||||
(let-values (((status out time)
|
(if (zero? status)
|
||||||
(system-to-string-with-time cmdline)))
|
(values status log (vcd-file-read
|
||||||
(let ((execution-log
|
vcd-file
|
||||||
(exe-log-pretty cmdline status out time)))
|
(lambda (sig)
|
||||||
(values status (string-append compile-log execution-log)))))))))))
|
(>= (length (vcd-signal-scope sig)) 2))))
|
||||||
|
(values status log #f)))))
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
;;; Compile sources and execute simulation with Verilator
|
;;; 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)
|
(define (exec-sim-verilator top work-dir verilator-wrap verilator-sim-wrap)
|
||||||
;; Compile
|
|
||||||
(let* ((command-file (path+ work-dir (format "~a.vc" top)))
|
(let* ((command-file (path+ work-dir (format "~a.vc" top)))
|
||||||
(cmdline (format "~a -f ~a"
|
(vcd-file (path+ work-dir (format "~a.vcd" top)))
|
||||||
(wrap-exe VERILATR-EXE verilator-wrap)
|
(cmds `(,(format "~a -f ~a"
|
||||||
command-file)))
|
(wrap-exe VERILATOR-EXE verilator-wrap)
|
||||||
(let-values (((status out time)
|
command-file)
|
||||||
(system-to-string-with-time cmdline)))
|
,(wrap-exe (path+ work-dir (format "~a/~a" top top))
|
||||||
(let ((compile-log
|
verilator-sim-wrap))))
|
||||||
(exe-log-pretty cmdline status out time)))
|
|
||||||
(if (not (zero? status))
|
|
||||||
(values status compile-log)
|
|
||||||
|
|
||||||
;; Execute
|
(let-values (((status log)
|
||||||
(let ((cmdline (wrap-exe (path+ work-dir (format "~a/~a" top top))
|
(exec-sequence cmds)))
|
||||||
verilator-sim-wrap)))
|
(if (zero? status)
|
||||||
(let-values (((status out time)
|
(values status log (vcd-file-read
|
||||||
(system-to-string-with-time cmdline)))
|
vcd-file
|
||||||
(let ((execution-log
|
(lambda (sig)
|
||||||
(exe-log-pretty cmdline status out time)))
|
(>= (length (vcd-signal-scope sig)) 2))))
|
||||||
(values status (string-append compile-log execution-log))))))))))
|
(values status log #f)))))
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
;;; Execute simulation
|
;;; Execute simulation
|
||||||
@@ -404,38 +676,40 @@
|
|||||||
(verilator-wrap "") (verilator-sim-wrap "")
|
(verilator-wrap "") (verilator-sim-wrap "")
|
||||||
(verilator-cpp "") (verilator-build-jobs 0))
|
(verilator-cpp "") (verilator-build-jobs 0))
|
||||||
(let-values
|
(let-values
|
||||||
(((work-dir status log)
|
(((work-dir status log vcd)
|
||||||
(cond
|
(cond
|
||||||
;; Run Icarus Verilog
|
;; Run Icarus Verilog
|
||||||
((eq? simulator 'iverilog)
|
((eq? simulator 'iverilog)
|
||||||
(let ((work-dir (make-iverilog-workdir code metatop base top)))
|
(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)))
|
(exec-sim-iverilog top work-dir iverilog-wrap vvp-wrap)))
|
||||||
(values work-dir status log))))
|
(values work-dir status log vcd))))
|
||||||
|
|
||||||
;; Run Verilator
|
;; Run Verilator
|
||||||
((eq? simulator 'verilator)
|
((eq? simulator 'verilator)
|
||||||
(let ((work-dir (make-verilator-workdir code verilator-cpp verilator-build-jobs base top)))
|
(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)))
|
(exec-sim-verilator top work-dir verilator-wrap verilator-sim-wrap)))
|
||||||
(values work-dir status log))))
|
(values work-dir status log vcd))))
|
||||||
|
|
||||||
;; Inknown simulator
|
;; Inknown simulator
|
||||||
(else
|
(else
|
||||||
(values #f #f #f)))))
|
(values #f #f #f #f)))))
|
||||||
|
|
||||||
(if (not work-dir)
|
(if (not work-dir)
|
||||||
("ERROR: Unknown simulator")
|
(values ("ERROR: Unknown simulator") #f)
|
||||||
(begin
|
(begin
|
||||||
;; Delete work dir
|
;; Delete work dir
|
||||||
(when DELETE-WORK-DIR
|
(when DELETE-WORK-DIR
|
||||||
(delete-recursive work-dir))
|
(delete-recursive work-dir))
|
||||||
|
|
||||||
;; Return log
|
;; Return (values log vcd)
|
||||||
|
(values
|
||||||
(string-append
|
(string-append
|
||||||
log
|
log
|
||||||
(format "-----------------\nSimulation complete~a\n"
|
(format "-----------------\nSimulation complete~a\n"
|
||||||
(if (zero? status) " succesfully"" with errors")))))))
|
(if (zero? status) " succesfully"" with errors")))
|
||||||
|
vcd)))))
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
;;; Get app version
|
;;; Get app version
|
||||||
@@ -453,7 +727,7 @@
|
|||||||
;;; Get storage dir from URI
|
;;; Get storage dir from URI
|
||||||
;;;
|
;;;
|
||||||
(define (get-storage-dir uri root-path)
|
(define (get-storage-dir uri root-path)
|
||||||
(string-trim
|
(string-trim-both
|
||||||
(substring (uri-path uri)
|
(substring (uri-path uri)
|
||||||
(string-length root-path))
|
(string-length root-path))
|
||||||
#\/))
|
#\/))
|
||||||
@@ -499,12 +773,33 @@
|
|||||||
(call-with-input-file (path+ path SNIPPET-FILE)
|
(call-with-input-file (path+ path SNIPPET-FILE)
|
||||||
get-string-all))
|
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
|
;;; Web page handler
|
||||||
;;;
|
;;;
|
||||||
(define (make-page-handler host root index-file
|
(define (make-page-handler host root index-file
|
||||||
work-base stor-base
|
work-base stor-base
|
||||||
max-code-size
|
max-code-size sanitize
|
||||||
iverilog-wrap vvp-wrap
|
iverilog-wrap vvp-wrap
|
||||||
verilator-wrap verilator-sim-wrap verilator-build-jobs)
|
verilator-wrap verilator-sim-wrap verilator-build-jobs)
|
||||||
|
|
||||||
@@ -513,14 +808,12 @@
|
|||||||
(iverilog-path (append root-path `(,URI-IVERILOG)))
|
(iverilog-path (append root-path `(,URI-IVERILOG)))
|
||||||
(verilator-path (append root-path `(,URI-VERILATOR)))
|
(verilator-path (append root-path `(,URI-VERILATOR)))
|
||||||
(savecode-path (append root-path `(,URI-SAVE-CODE)))
|
(savecode-path (append root-path `(,URI-SAVE-CODE)))
|
||||||
(saveas-path (append root-path `(,URI-SAVEAS-CODE)))
|
|
||||||
(index-html
|
(index-html
|
||||||
(read-template-text
|
(read-template-text
|
||||||
index-file
|
index-file
|
||||||
`(("IVERILOGPOSTURI" ,(encode-and-join-uri-path iverilog-path))
|
`(("IVERILOGPOSTURI" ,(encode-and-join-uri-path iverilog-path))
|
||||||
("VERILATORPOSTURI" ,(encode-and-join-uri-path verilator-path))
|
("VERILATORPOSTURI" ,(encode-and-join-uri-path verilator-path))
|
||||||
("SAVECODEURI" ,(encode-and-join-uri-path savecode-path))
|
("SAVECODEURI" ,(encode-and-join-uri-path savecode-path))
|
||||||
("SAVEASURI" ,(encode-and-join-uri-path saveas-path))
|
|
||||||
("HELPSTRING",
|
("HELPSTRING",
|
||||||
(string-concatenate
|
(string-concatenate
|
||||||
(insert-between
|
(insert-between
|
||||||
@@ -529,14 +822,13 @@
|
|||||||
,(format "Icarus: ~a"
|
,(format "Icarus: ~a"
|
||||||
(app-version (wrap-exe IVERILOG-EXE iverilog-wrap) "-V"))
|
(app-version (wrap-exe IVERILOG-EXE iverilog-wrap) "-V"))
|
||||||
,(format "Verilator: ~a"
|
,(format "Verilator: ~a"
|
||||||
(app-version (wrap-exe VERILATR-EXE verilator-wrap)))
|
(app-version (wrap-exe VERILATOR-EXE verilator-wrap)))
|
||||||
""
|
""
|
||||||
"Rules:"
|
"Rules:"
|
||||||
"0. Don't fool around ;)"
|
"0. Don't fool around ;)"
|
||||||
"1. The top module must be named 'testbench'."
|
"1. The top module must be named 'testbench'."
|
||||||
"2. The top module for the Verilator must have an input clock signal."
|
"2. Code size should not exceed 10000 characters."
|
||||||
"3. Code size should not exceed 10000 characters."
|
"3. Code execution time no longer than 5 seconds.")
|
||||||
"4. Code execution time no longer than 5 seconds.")
|
|
||||||
"\\n"))))))
|
"\\n"))))))
|
||||||
(iverilog-metatop
|
(iverilog-metatop
|
||||||
(call-with-input-file IVERILOG-METATOP-FILE get-string-all))
|
(call-with-input-file IVERILOG-METATOP-FILE get-string-all))
|
||||||
@@ -566,9 +858,17 @@
|
|||||||
(<= (string-length code) max-code-size))
|
(<= (string-length code) max-code-size))
|
||||||
code
|
code
|
||||||
(substring code 0 max-code-size)))
|
(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 ~a:~a" (request-method request) path)
|
||||||
|
(logger LOG-VERBOSE "Request query:~a" query)
|
||||||
(logger LOG-DBG " stor:'~a' len:~a/~a"
|
(logger LOG-DBG " stor:'~a' len:~a/~a"
|
||||||
ref-stor-dir
|
ref-stor-dir
|
||||||
(request-content-length request)
|
(request-content-length request)
|
||||||
@@ -620,62 +920,64 @@
|
|||||||
;;
|
;;
|
||||||
((eq? 'POST (request-method request))
|
((eq? 'POST (request-method request))
|
||||||
(cond
|
(cond
|
||||||
;; Run iverilog simulation
|
;; Run simulation
|
||||||
((equal? path iverilog-path)
|
((or (equal? path iverilog-path)
|
||||||
(logger LOG-DBG "Request iverilog simulation")
|
(equal? path verilator-path))
|
||||||
|
(let ((simulator
|
||||||
|
(if (equal? path iverilog-path)
|
||||||
|
'iverilog
|
||||||
|
'verilator)))
|
||||||
|
|
||||||
(when ref-stor-dir
|
(logger LOG-DBG "Request ~a simulation" (symbol->string simulator))
|
||||||
(save-to-storage (path+ stor-base ref-stor-dir) code))
|
(let-values
|
||||||
|
(((log vcd)
|
||||||
(make-response
|
(exec-sim simulator
|
||||||
(exec-sim 'iverilog
|
(if sanitize (sanitize-verilog code) code)
|
||||||
(sanitize-verilog code)
|
|
||||||
work-base
|
work-base
|
||||||
TOP-MODULE
|
TOP-MODULE
|
||||||
#:metatop iverilog-metatop
|
#:metatop iverilog-metatop
|
||||||
#:vvp-wrap vvp-wrap
|
#:vvp-wrap vvp-wrap
|
||||||
#:iverilog-wrap iverilog-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-wrap verilator-wrap
|
||||||
#:verilator-sim-wrap verilator-sim-wrap
|
#:verilator-sim-wrap verilator-sim-wrap
|
||||||
#:verilator-cpp verilator-cpp
|
#:verilator-cpp verilator-cpp
|
||||||
#:verilator-build-jobs verilator-build-jobs)
|
#: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
|
;; Save snippet
|
||||||
((or (equal? path savecode-path)
|
((or (equal? path savecode-path))
|
||||||
(equal? path saveas-path))
|
(logger LOG-DBG "Request code saving")
|
||||||
(let ((saveas (equal? path saveas-path)))
|
(let ((old-code
|
||||||
(logger LOG-DBG "Request code saving~a"
|
(if ref-stor-dir
|
||||||
(if saveas " as new snippet" ""))
|
(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
|
(let ((stor-dir
|
||||||
(if (or saveas
|
|
||||||
(not ref-stor-dir))
|
|
||||||
(basename
|
(basename
|
||||||
(mkdtemp
|
(mkdtemp
|
||||||
(path+
|
(path+
|
||||||
stor-base
|
stor-base
|
||||||
(format "~a-XXXXXX"
|
(if USE-TIME-IN-SAVE-URL
|
||||||
(current-time)))))
|
(format "~a-XXXXXX" (current-time))
|
||||||
ref-stor-dir)))
|
"XXXXXX"))))))
|
||||||
(save-to-storage (path+ stor-base stor-dir) code)
|
(save-to-storage (path+ stor-base stor-dir) code)
|
||||||
(make-response
|
(make-response
|
||||||
(encode-and-join-uri-path
|
(encode-and-join-uri-path
|
||||||
(append root-path `(,stor-dir)))
|
(append root-path `(,stor-dir)))
|
||||||
#:content-type 'text/plain))))
|
#:content-type 'text/plain)))))
|
||||||
|
|
||||||
;; Wrong POST request
|
;; Wrong POST request
|
||||||
(else
|
(else
|
||||||
@@ -716,6 +1018,7 @@
|
|||||||
(-> " --verilator-sim-wrap PATH Verilator simulation executable wrapper.")
|
(-> " --verilator-sim-wrap PATH Verilator simulation executable wrapper.")
|
||||||
(-> " --verilator-build-jobs N Verilator parallel build.")
|
(-> " --verilator-build-jobs N Verilator parallel build.")
|
||||||
(-> " --max-len LEN Set maximum code size in symbols. Default: 0 (infinite)")
|
(-> " --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: ./")
|
(-> " --work-base PATH Set work base path. Default: ./")
|
||||||
(-> " --stor-base PATH Set snippets storage path. Default: ./")
|
(-> " --stor-base PATH Set snippets storage path. Default: ./")
|
||||||
(-> " --log-level LEVEL Set log level from 0 (quiet) to 10 (verbose). Default: 1./")
|
(-> " --log-level LEVEL Set log level from 0 (quiet) to 10 (verbose). Default: 1./")
|
||||||
@@ -725,7 +1028,7 @@
|
|||||||
|
|
||||||
(define (string-trim-if-string str)
|
(define (string-trim-if-string str)
|
||||||
(if (string? str)
|
(if (string? str)
|
||||||
(string-trim str)
|
(string-trim-both str)
|
||||||
str))
|
str))
|
||||||
|
|
||||||
(define (main args)
|
(define (main args)
|
||||||
@@ -743,24 +1046,26 @@
|
|||||||
'(("verilator-sim-wrap") required)
|
'(("verilator-sim-wrap") required)
|
||||||
'(("verilator-build-jobs") required)
|
'(("verilator-build-jobs") required)
|
||||||
'(("max-len") required)
|
'(("max-len") required)
|
||||||
|
'(("dont-sanitize") none)
|
||||||
'(("work-base") required)
|
'(("work-base") required)
|
||||||
'(("stor-base") required)
|
'(("stor-base") required)
|
||||||
'(("log-level") required)
|
'(("log-level") required)
|
||||||
'(("help" #\h) none))))
|
'(("help" #\h) none))))
|
||||||
|
|
||||||
(let ((addr (string-trim (or (option-get opts "addr") "127.0.0.1")))
|
(let ((addr (string-trim-both (or (option-get opts "addr") "127.0.0.1")))
|
||||||
(port (string->number (string-trim (or (option-get opts "port") "8080"))))
|
(port (string->number (string-trim-both (or (option-get opts "port") "8080"))))
|
||||||
(host (string-trim (or (option-get opts "host") "http://127.0.0.1:8080")))
|
(host (string-trim-both (or (option-get opts "host") "http://127.0.0.1:8080")))
|
||||||
(root (string-trim (or (option-get opts "root") "")))
|
(root (string-trim-both (or (option-get opts "root") "")))
|
||||||
(vvp-wrap (string-trim-if-string (option-get opts "vvp-wrap")))
|
(vvp-wrap (string-trim-if-string (option-get opts "vvp-wrap")))
|
||||||
(iverilog-wrap (string-trim-if-string (option-get opts "iverilog-wrap")))
|
(iverilog-wrap (string-trim-if-string (option-get opts "iverilog-wrap")))
|
||||||
(verilator-wrap (string-trim-if-string (option-get opts "verilator-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-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"))))
|
(verilator-build-jobs (string->number (string-trim-both (or (option-get opts "verilator-build-jobs") "0"))))
|
||||||
(max-code-size (string->number (string-trim (or (option-get opts "max-len") "0"))))
|
(max-code-size (string->number (string-trim-both (or (option-get opts "max-len") "0"))))
|
||||||
(work-base (string-trim (or (option-get opts "work-base") "./")))
|
(sanitize (not (option-get opts "dont-sanitize")))
|
||||||
(stor-base (string-trim (or (option-get opts "stor-base") "./")))
|
(work-base (string-trim-both (or (option-get opts "work-base") "./")))
|
||||||
(log-level (string->number (string-trim (or (option-get opts "log-level") "1")))))
|
(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
|
(cond
|
||||||
(err
|
(err
|
||||||
@@ -782,6 +1087,7 @@
|
|||||||
(logger LOG-INFO "verilator simulator wrapper: '~a'" verilator-sim-wrap)
|
(logger LOG-INFO "verilator simulator wrapper: '~a'" verilator-sim-wrap)
|
||||||
(logger LOG-INFO "verilator build jobs: ~a" verilator-build-jobs)
|
(logger LOG-INFO "verilator build jobs: ~a" verilator-build-jobs)
|
||||||
(logger LOG-INFO "Max code size: ~a" max-code-size)
|
(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 "Work base path: '~a'" work-base)
|
||||||
(logger LOG-INFO "Storage base path: '~a'" stor-base)
|
(logger LOG-INFO "Storage base path: '~a'" stor-base)
|
||||||
(logger LOG-INFO "Log level: '~a'" log-level)
|
(logger LOG-INFO "Log level: '~a'" log-level)
|
||||||
@@ -789,7 +1095,7 @@
|
|||||||
(run-server
|
(run-server
|
||||||
(make-page-handler host root INDEX-FILE
|
(make-page-handler host root INDEX-FILE
|
||||||
work-base stor-base
|
work-base stor-base
|
||||||
max-code-size
|
max-code-size sanitize
|
||||||
iverilog-wrap vvp-wrap
|
iverilog-wrap vvp-wrap
|
||||||
verilator-wrap verilator-sim-wrap verilator-build-jobs)
|
verilator-wrap verilator-sim-wrap verilator-build-jobs)
|
||||||
'http `(#:host ,addr #:port ,port)))))))
|
'http `(#:host ,addr #:port ,port)))))))
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
`timescale 1ps/1ps
|
`timescale 1ps/1ps
|
||||||
|
|
||||||
module __@TOPMODULE@__;
|
module __@TOPMODULE@__;
|
||||||
logic clock = 1'b0;
|
@TOPMODULE@ @TOPMODULE@ ();
|
||||||
initial forever #(5ns) clock = ~clock;
|
|
||||||
@TOPMODULE@ @TOPMODULE@ (clock);
|
|
||||||
initial begin
|
initial begin
|
||||||
$dumpfile("@WORKDIR@/@TOPMODULE@.vcd");
|
$dumpfile("@WORKDIR@/@TOPMODULE@.vcd");
|
||||||
$dumpvars(0, @TOPMODULE@);
|
$dumpvars(1, @TOPMODULE@);
|
||||||
$dumplimit(100000);
|
$dumplimit(100000);
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|||||||
@@ -1,58 +1,43 @@
|
|||||||
|
#include "verilated.h"
|
||||||
|
#include "verilated_vcd_c.h"
|
||||||
#include "V@TOPMODULE@.h"
|
#include "V@TOPMODULE@.h"
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <verilated.h>
|
|
||||||
#include <verilated_vcd_c.h>
|
|
||||||
|
|
||||||
#define DUMPFILE "@WORKDIR@/@TOPMODULE@.vcd"
|
#define DUMPFILE "@WORKDIR@/@TOPMODULE@.vcd"
|
||||||
#define CLOCK_HALF_PERIOD 5000
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char** argv, char**) {
|
||||||
{
|
// Setup context, defaults, and parse command line
|
||||||
VerilatedContext *ctx = new VerilatedContext;
|
Verilated::debug(0);
|
||||||
ctx->commandArgs(argc, argv);
|
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||||
|
contextp->traceEverOn(true);
|
||||||
|
contextp->commandArgs(argc, argv);
|
||||||
|
|
||||||
/* Create model instance */
|
// Construct the Verilated model, from Vtop.h generated from Verilating
|
||||||
V@TOPMODULE@ *top = new V@TOPMODULE@(ctx);
|
const std::unique_ptr<V@TOPMODULE@> topp{new V@TOPMODULE@{contextp.get()}};
|
||||||
|
|
||||||
#if (VM_TRACE == 1)
|
|
||||||
VerilatedVcdC *vcd = new VerilatedVcdC;
|
VerilatedVcdC *vcd = new VerilatedVcdC;
|
||||||
ctx->traceEverOn(true);
|
topp->trace(vcd, 99);
|
||||||
top->trace(vcd, 99);
|
|
||||||
vcd->open(DUMPFILE);
|
vcd->open(DUMPFILE);
|
||||||
#endif
|
|
||||||
|
|
||||||
top->clock = 0;
|
// Simulate until $finish
|
||||||
|
while (!contextp->gotFinish()) {
|
||||||
/* ---- Evaluation loop ---- */
|
// Evaluate model
|
||||||
for (;;) {
|
topp->eval();
|
||||||
/* Eval */
|
vcd->dump(contextp->time());
|
||||||
top->eval();
|
// Advance time
|
||||||
|
if (!topp->eventsPending()) break;
|
||||||
/* Trace steady-state values */
|
contextp->time(topp->nextTimeSlot());
|
||||||
#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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
top->final();
|
if (!contextp->gotFinish()) {
|
||||||
printf("[%lu] Stop simulation\n", ctx->time());
|
VL_DEBUG_IF(VL_PRINTF("+ Exiting without $finish; no events left\n"););
|
||||||
|
}
|
||||||
|
|
||||||
#if (VM_TRACE == 1)
|
// Execute 'final' processes
|
||||||
if (vcd) {
|
topp->final();
|
||||||
|
|
||||||
|
// Print statistical summary report
|
||||||
|
contextp->statsPrintSummary();
|
||||||
vcd->close();
|
vcd->close();
|
||||||
delete vcd;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
delete top;
|
|
||||||
delete ctx;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user