diff --git a/embddr/vcd.scm b/embddr/vcd.scm index 203e14e..b4ffabc 100644 --- a/embddr/vcd.scm +++ b/embddr/vcd.scm @@ -12,6 +12,9 @@ (import (embddr common)) (export vcd-parse + vcd-timescale + vcd-timestamps + vcd-signals (signal-scope . vcd-signal-scope) (signal-name . vcd-signal-name) (signal-type . vcd-signal-type) @@ -26,7 +29,7 @@ (vcd-new timescale timestamps signals) vcd? (timescale vcd-timescale) - (timestamps vcd-tstamps) + (timestamps vcd-timestamps) (signals vcd-signals)) ;;; @@ -170,118 +173,127 @@ (else (next-cmd timescale scope tags+signals)))))))) ;;; -;;; Read data block and return list of lists (time changes ..) +;;; Read data changes sample and return list of (time changes ...) ;;; -(define (read-data port) - (let read-rec ((samples '()) - (time #f) - (changes '())) +(define (read-sample port) + (let read-rec ((time #f) (changes '())) (let ((line (get-line port))) (if (eof-object? line) - (reverse - (if (and time (not (null? changes))) - (cons (cons time changes) samples) - samples)) + (if time (cons time changes) #f) (let ((line (string-trim-both line))) (cond ((or (string-null? line) - (equal? (string-ref line 0) #\$)) - (read-rec samples time changes)) + ;; (equal? (string-ref line 0) #\$) + (not + (let ((c (string-ref line 0))) + (or (eq? c #\0) + (eq? c #\1) + (eq? c #\x) + (eq? c #\X) + (eq? c #\z) + (eq? c #\Z) + (eq? c #\b) + (eq? c #\r) + (eq? c #\#))))) + (read-rec time changes)) ((equal? (string-ref line 0) #\#) - (read-rec - (if (and time (not (null? changes))) - (cons (cons time changes) samples) - samples) - (let ((time (string->number (substring line 1)))) - (if time - time - (raise `(vcd-syntsx-error "Bad time format '~a'" line)))) - '())) + (if time + (begin + (unget-string port (format "~a\n" line)) + (cons time changes)) + (let ((time (string->number (substring line 1)))) + (if time + (read-rec time '()) + (raise `(vcd-syntsx-error "Bad time format '~a'" line)))))) (else - (read-rec samples time (cons line changes))))))))) + (read-rec time (cons line changes))))))))) ;;; -;;; Parse VCD data block +;;; Read and parse VCD data block ;;; Returns record ;;; -(define (parse-data samples timescale tags+signals) - (let* ((samples-count (length samples)) - (tags+signals-ht (alist->hash-table tags+signals))) - - (for-each - (lambda (sample) - (let ((time (car sample))) - (for-each - (lambda (line) - (let ((c0 (string-ref line 0)) - (rest (substring line 1))) - ;; (display (format "-- c0:'~a' rest:'~a'\n" c0 rest)) - (let-values - (((parse-value tag value) - (cond - ;; Scalar value - ((or (equal? c0 #\0) - (equal? c0 #\1) - (char-ci=? c0 #\x) - (char-ci=? c0 #\z)) - (values parse-binary-value - rest - (string (char-downcase c0)))) - - ;; Binary or real value - ((or (char-ci=? c0 #\b) - (char-ci=? c0 #\r)) - - ;; Fast but unsafe - (let ((val-idx (string-index rest #\space))) - (if val-idx - (let ((value (substring rest 0 val-idx)) - (tag (substring rest (+ val-idx 1)))) - (values (if (char-ci=? c0 #\b) - parse-binary-value - parse-real-value) - tag - value)) - (raise `(vcd-syntax-error - ,(format "Bad value change string '~a'" line))))) - - ;; Slow but safety - ;; (let ((ss (string-split-trim rest char-whitespace?))) - ;; (if (= (length ss) 2) - ;; (let ((value (first ss)) - ;; (tag (second ss))) - ;; (values (if (char-ci=? c0 #\b) - ;; parse-binary-value - ;; parse-real-value) - ;; tag - ;; value)) - ;; (raise `(vcd-syntax-error - ;; ,(format "Bad value change string '~a'" line))))) - )))) - - (let ((sigs (hash-table-ref/default tags+signals-ht tag #f))) - (unless (null? sigs) +(define (read-data port timescale tags+signals) + (let* ((tags+signals-ht (alist->hash-table tags+signals)) + (timestamps + (let loop ((timestamps '())) + (let ((sample (read-sample port))) + (if sample + (let ((time (car sample))) (for-each - (lambda (sig) - (signal-add! - sig - (cons time (parse-value value (signal-size sig))))) - sigs)))))) - (cdr sample)))) - samples) + (lambda (line) + (let ((c0 (string-ref line 0)) + (rest (substring line 1))) + (let-values + (((parse-value tag value) + (cond + ;; Scalar value + ((or (equal? c0 #\0) + (equal? c0 #\1) + (char-ci=? c0 #\x) + (char-ci=? c0 #\z)) + (values parse-binary-value + rest + (string (char-downcase c0)))) - (vcd-new timescale - (map car samples) - (let ((signals (fold (lambda (tag+sig out) - (append (cdr tag+sig) out)) - '() tags+signals)) - (last-time (car (last samples)))) - (map (lambda (sig) - (signal-set! sig (reverse (signal-get sig))) - sig) - signals))))) + ;; Binary or real value + ((or (char-ci=? c0 #\b) + (char-ci=? c0 #\r)) + + ;; Fast but unsafe + ;; Does not remove leading and trailing spaces + ;; and spaces between value and tag + (let ((val-idx (string-index rest #\space))) + (if val-idx + (let ((value (substring rest 0 val-idx)) + (tag (substring rest (+ val-idx 1)))) + (values (if (char-ci=? c0 #\b) + parse-binary-value + parse-real-value) + tag + value)) + (raise `(vcd-syntax-error + ,(format "Bad value change string '~a'" line))))))))) + + ;; Update register values list + (let ((sigs (hash-table-ref/default tags+signals-ht tag #f))) + (unless (null? sigs) + (for-each + (lambda (sig) + (signal-add! + sig + (cons time (parse-value value (signal-size sig))))) + sigs)))))) + (cdr sample)) + + (loop (cons time timestamps))) + timestamps))))) + + (let ((signals (fold (lambda (tag+sig out) + (append (cdr tag+sig) out)) + '() tags+signals))) + (if (null? timestamps) + (vcd-new timescale timestamps signals) + (vcd-new timescale + (reverse timestamps) + ;; Complete values to last time stamp + (let ((last-time (car timestamps))) + (map (lambda (sig) + (signal-set! + sig + (reverse + (let ((values (signal-get sig))) + (if (null? values) + '() + (let ((last-val (car values))) + (if (= last-time (car last-val)) + values + (cons (cons last-time + (cdr last-val)) + values))))))) + sig) + signals))))))) ;;; ;;; Parse whole VCD @@ -290,39 +302,43 @@ (define (vcd-parse port) (call-with-values (lambda () (read-header port)) - (cut parse-data - (read-data port) - <...>))) + (cut read-data port <...>))) ;;; ;;; Convert 4-state binary string to hex ;;; -(define (vcd-binary->hex binstr) - (string-concatenate - (map - (lambda (quad) - (if (string-every char-numeric? quad) - (number->string - (string->number quad 2) - 16) - (if (string-every (cut char-ci=? #\z <>) quad) - "z" - "x"))) - (let split-by-quad ((str (string-reverse binstr)) - (quads '())) - (cond - ((string-null? str) quads) - ((<= (string-length str) 4) - (cons (string-append - (make-string - (- 4 (string-length str)) - (let ((h (string-ref str 0))) - (if (char-numeric? h) #\0 h))) - (string-reverse str)) - quads)) - (else - (split-by-quad - (string-drop str 4) - (cons - (string-reverse - (string-take str 4)) quads)))))))) +(define* (vcd-binary->hex binstr #:optional (trim #f)) + (let ((hex + (string-concatenate + (map + (lambda (quad) + (if (string-every char-numeric? quad) + (number->string + (string->number quad 2) + 16) + (if (string-every (cut char-ci=? #\z <>) quad) + "z" + "x"))) + (let split-by-quad ((str (string-reverse binstr)) + (quads '())) + (cond + ((string-null? str) quads) + ((<= (string-length str) 4) + (cons (string-append + (make-string + (- 4 (string-length str)) + (let ((h (string-ref str 0))) + (if (char-numeric? h) #\0 h))) + (string-reverse str)) + quads)) + (else + (split-by-quad + (string-drop str 4) + (cons + (string-reverse + (string-take str 4)) quads))))))))) + (if trim + (if (eq? (string-ref hex (- (string-length hex) 1)) #\0) + "0" + (string-append (string-trim hex #\0))) + hex)))