Add sources
This commit is contained in:
parent
82f90610fb
commit
686d12bf81
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
results.txt
|
||||||
45
run.sh
Executable file
45
run.sh
Executable file
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BUILD=__build.sh
|
||||||
|
RUN=__run.sh
|
||||||
|
|
||||||
|
if [ -n "$1" ]
|
||||||
|
then
|
||||||
|
tests=$1
|
||||||
|
else
|
||||||
|
tests=$(ls -1d test-*)
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo >> results.txt
|
||||||
|
echo "---------- Simulator's benchmark -----------" >> results.txt
|
||||||
|
echo $(date) >> results.txt
|
||||||
|
echo >> results.txt
|
||||||
|
|
||||||
|
for test_dir in $tests
|
||||||
|
do
|
||||||
|
if [ ! -d "$test_dir" ]
|
||||||
|
then
|
||||||
|
echo "Directory $test_dit is not exists. Break"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -e $test_dir/$BUILD -a -e $test_dir/$RUN ]
|
||||||
|
then
|
||||||
|
echo "#### Run benchmark in $test_dir"
|
||||||
|
|
||||||
|
cd $test_dir
|
||||||
|
./$BUILD
|
||||||
|
start_ms=$(date +%s%N | cut -b1-13)
|
||||||
|
./$RUN
|
||||||
|
stop_ms=$(date +%s%N | cut -b1-13)
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
ms=$(expr $stop_ms - $start_ms)
|
||||||
|
echo "#### $test_dir: $ms milliseconds"
|
||||||
|
echo
|
||||||
|
echo "$test_dir: $ms" >> results.txt
|
||||||
|
else
|
||||||
|
echo "Skip $test_dir directory"
|
||||||
|
fi
|
||||||
|
done
|
||||||
29
scripts/bin2initial.scm
Executable file
29
scripts/bin2initial.scm
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env guile
|
||||||
|
!#
|
||||||
|
|
||||||
|
;; -*- geiser-scheme-implementation: guile -*-
|
||||||
|
|
||||||
|
(use-modules
|
||||||
|
(ice-9 binary-ports)
|
||||||
|
(ice-9 format)
|
||||||
|
(srfi srfi-11)
|
||||||
|
(rnrs bytevectors))
|
||||||
|
|
||||||
|
(define (print-verilog-header binary-file reg-name)
|
||||||
|
(format #t "initial begin\n")
|
||||||
|
(let ((words (call-with-input-file binary-file
|
||||||
|
(lambda (port)
|
||||||
|
(bytevector->uint-list
|
||||||
|
(get-bytevector-all port) 'little 4)))))
|
||||||
|
(for-each
|
||||||
|
(lambda (x n)
|
||||||
|
(format #t " ~a[~a] = 32'h~8,'0x;\n" reg-name n x))
|
||||||
|
words (iota (length words))))
|
||||||
|
(format #t "end\n"))
|
||||||
|
|
||||||
|
(let ((args (command-line)))
|
||||||
|
(if (not (= (length args) 3))
|
||||||
|
(format #t "Usage: ~a <BINARY_FILE_NAME> <RAM_REG_NAME>\n" (car args))
|
||||||
|
(let ((file-name (cadr args))
|
||||||
|
(reg-name (caddr args)))
|
||||||
|
(print-verilog-header file-name reg-name))))
|
||||||
28
scripts/bin2mem.scm
Executable file
28
scripts/bin2mem.scm
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env guile
|
||||||
|
!#
|
||||||
|
|
||||||
|
;; -*- geiser-scheme-implementation: guile -*-
|
||||||
|
|
||||||
|
(use-modules
|
||||||
|
(ice-9 binary-ports)
|
||||||
|
(ice-9 format)
|
||||||
|
(srfi srfi-11)
|
||||||
|
(rnrs bytevectors))
|
||||||
|
|
||||||
|
(define (print-verilog-header binary-file mem-size)
|
||||||
|
(let ((words (call-with-input-file binary-file
|
||||||
|
(lambda (port)
|
||||||
|
(bytevector->uint-list
|
||||||
|
(get-bytevector-all port) 'little 4)))))
|
||||||
|
(for-each
|
||||||
|
(lambda (x)
|
||||||
|
(format #t "~8,'0x\n" x))
|
||||||
|
(append words
|
||||||
|
(make-list (- mem-size (length words)) 0)))))
|
||||||
|
|
||||||
|
(let ((args (command-line)))
|
||||||
|
(if (not (= (length args) 3))
|
||||||
|
(format #t "Usage: ~a <BINARY_FILE_NAME> <MEM_SIZE_KB>\n" (car args))
|
||||||
|
(let ((file-name (cadr args))
|
||||||
|
(mem-size (floor (/ (string->number (caddr args)) 4))))
|
||||||
|
(print-verilog-header file-name mem-size))))
|
||||||
244
scripts/common.scm
Normal file
244
scripts/common.scm
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
(cond-expand
|
||||||
|
(guile
|
||||||
|
(define-module (common))
|
||||||
|
|
||||||
|
(import (srfi srfi-1)
|
||||||
|
(srfi srfi-26)
|
||||||
|
(srfi srfi-28)
|
||||||
|
(srfi srfi-60)
|
||||||
|
(ice-9 textual-ports))
|
||||||
|
|
||||||
|
(export log2 clog2 round-to
|
||||||
|
one? power-of-two?
|
||||||
|
transpose
|
||||||
|
number->string-binary
|
||||||
|
number->string-binary-slice
|
||||||
|
number->string-hex
|
||||||
|
number->bits
|
||||||
|
string-c-radix->number
|
||||||
|
has-duplicates? find-duplicates
|
||||||
|
insert-between
|
||||||
|
string-replace-text
|
||||||
|
string-split-str
|
||||||
|
string-split-trim
|
||||||
|
get-word
|
||||||
|
substitute
|
||||||
|
read-template
|
||||||
|
|
||||||
|
make-mux-selectors))
|
||||||
|
(else
|
||||||
|
(error "Guile is required")))
|
||||||
|
|
||||||
|
;;; Log2
|
||||||
|
(define (log2 x)
|
||||||
|
(/ (log x) (log 2)))
|
||||||
|
|
||||||
|
;;; Ceiling of log2 ($clog2 function in SV)
|
||||||
|
(define (clog2 x)
|
||||||
|
(inexact->exact (ceiling (log2 x))))
|
||||||
|
|
||||||
|
;;; Check a number is a power of two
|
||||||
|
(define (power-of-two? x)
|
||||||
|
(let ((l (round (log2 x))))
|
||||||
|
(= (expt 2 l) x)))
|
||||||
|
|
||||||
|
;;; Check for (x == 1)
|
||||||
|
(define (one? x) (= x 1))
|
||||||
|
|
||||||
|
;;; Round to the 'n' decimal place
|
||||||
|
(define (round-to n num)
|
||||||
|
(let ((k (expt 10 n)))
|
||||||
|
(/ (round (* num k)) k)))
|
||||||
|
|
||||||
|
;;; Transpose of matrix (list of lists)
|
||||||
|
(define (transpose m)
|
||||||
|
(apply map (cons list m)))
|
||||||
|
|
||||||
|
;;; Convert number to binary string of length 'len'
|
||||||
|
(define (number->string-binary n len)
|
||||||
|
(list->string
|
||||||
|
(reverse
|
||||||
|
(map (lambda (x) (if x #\1 #\0))
|
||||||
|
(list-tabulate len (lambda (i) (bit-set? i n)))))))
|
||||||
|
|
||||||
|
;;; Convert number to binary and slice from msb to lsb
|
||||||
|
(define (number->string-binary-slice n msb lsb)
|
||||||
|
(list->string
|
||||||
|
(reverse
|
||||||
|
(drop
|
||||||
|
(map (lambda (x) (if x #\1 #\0))
|
||||||
|
(list-tabulate (+ msb 1) (lambda (i) (bit-set? i n))))
|
||||||
|
lsb))))
|
||||||
|
|
||||||
|
;;; Convert number to hex with length l (padded left with 0)
|
||||||
|
(define (number->string-hex n l)
|
||||||
|
(let* ((s (number->string n 16))
|
||||||
|
(sl (string-length s)))
|
||||||
|
(if (<= l sl)
|
||||||
|
s
|
||||||
|
(string-append (make-string (- l sl) #\0) s))))
|
||||||
|
|
||||||
|
;;; Convert number to bit list
|
||||||
|
(define (number->bits x len)
|
||||||
|
(map (lambda (n) (if (bit-set? n x) 1 0)) (iota len)))
|
||||||
|
|
||||||
|
;;; Convert arbitrary radix string in C-format (0x, 0b 0..) to number
|
||||||
|
(define (string-c-radix->number str)
|
||||||
|
(if (and str (string? str))
|
||||||
|
(let ((str (string-trim-both str)))
|
||||||
|
(cond
|
||||||
|
((string-every #\0 str) 0)
|
||||||
|
((string-prefix? "0x" str)
|
||||||
|
(string->number (substring str 2) 16))
|
||||||
|
((string-prefix? "0b" str)
|
||||||
|
(string->number (substring str 2) 2))
|
||||||
|
((string-prefix? "0" str)
|
||||||
|
(string->number (substring str 1) 8))
|
||||||
|
(else
|
||||||
|
(string->number str 10))))
|
||||||
|
#f))
|
||||||
|
|
||||||
|
;;; Check list for duplicates
|
||||||
|
(define (has-duplicates? items less)
|
||||||
|
(if (< (length items) 2)
|
||||||
|
#f
|
||||||
|
(let ((sorted (sort items less)))
|
||||||
|
(any (lambda (a b) (and (not (less a b))
|
||||||
|
(not (less b a))))
|
||||||
|
sorted
|
||||||
|
(append (cdr sorted)
|
||||||
|
`(,(car sorted)))))))
|
||||||
|
|
||||||
|
;;; Return first duplicated item or #f if no duplicates
|
||||||
|
(define (find-duplicates items less)
|
||||||
|
(if (null? items)
|
||||||
|
#f
|
||||||
|
(let ((sorted (sort items less)))
|
||||||
|
(let loop ((item (car sorted))
|
||||||
|
(rest (cdr sorted)))
|
||||||
|
(cond
|
||||||
|
((null? rest) #f)
|
||||||
|
((and (not (less item (car rest)))
|
||||||
|
(not (less (car rest) item))) item)
|
||||||
|
(else (loop (car rest) (cdr rest))))))))
|
||||||
|
|
||||||
|
;;; In the list b0 leaves only the last most significant (other than b1) bit
|
||||||
|
(define (bits-first-diff-msb b0 b1)
|
||||||
|
(let loop ((b0 (reverse b0))
|
||||||
|
(b1 (reverse b1))
|
||||||
|
(keep '()))
|
||||||
|
(if (null? b0)
|
||||||
|
keep
|
||||||
|
(let ((b0b (car b0))
|
||||||
|
(b0s (cdr b0)))
|
||||||
|
(if (= b0b (car b1))
|
||||||
|
(loop b0s (cdr b1) (cons #f keep))
|
||||||
|
(append (make-list (length b0s) #f) (cons b0b keep)))))))
|
||||||
|
|
||||||
|
;;; Return bit lists of address selectors
|
||||||
|
;;; If list item is #f then bit is not care
|
||||||
|
;;; First element of each list is a address
|
||||||
|
;;; Example:
|
||||||
|
;;; (make-mux-selectors '(#x10 #x20 #x30)) ->
|
||||||
|
;;; ((#x30 #f #f #f #f 1 1)
|
||||||
|
;;; (#x20 #f #f #f #f 0 1)
|
||||||
|
;;; (#x10 #f #f #f #f #f 0))
|
||||||
|
(define (make-mux-selectors addrs)
|
||||||
|
(let ((bit-width (apply max (map integer-length addrs)))
|
||||||
|
(addrs (sort addrs >)))
|
||||||
|
(map
|
||||||
|
(lambda (addr)
|
||||||
|
(let ((others (remove (cut = addr <>) addrs))
|
||||||
|
(abits (number->bits addr bit-width)))
|
||||||
|
(cons
|
||||||
|
addr
|
||||||
|
(apply map
|
||||||
|
(lambda bits
|
||||||
|
(let ((abit (car bits))
|
||||||
|
(obits (cdr bits)))
|
||||||
|
(if (every not obits) #f abit)))
|
||||||
|
(cons
|
||||||
|
abits
|
||||||
|
(map
|
||||||
|
(lambda (other)
|
||||||
|
(let ((obits (number->bits other bit-width)))
|
||||||
|
(bits-first-diff-msb abits obits)))
|
||||||
|
others))))))
|
||||||
|
addrs)))
|
||||||
|
|
||||||
|
;;; Insert object between list items
|
||||||
|
(define (insert-between lst x)
|
||||||
|
(if (or (null? lst)
|
||||||
|
(null? (cdr lst)))
|
||||||
|
lst
|
||||||
|
(cons* (car lst) x
|
||||||
|
(insert-between (cdr lst) x))))
|
||||||
|
|
||||||
|
;;; Racket-like string-replace
|
||||||
|
(define* (string-replace-text str from to #:key (all #t))
|
||||||
|
(let ((flen (string-length from))
|
||||||
|
(tlen (string-length to)))
|
||||||
|
(let replace ((str str) (idx 0))
|
||||||
|
(if (>= idx (string-length str))
|
||||||
|
str
|
||||||
|
(let ((occ (string-contains str from idx)))
|
||||||
|
(if occ
|
||||||
|
(let ((str (string-replace str to occ (+ occ flen))))
|
||||||
|
(if all
|
||||||
|
(replace str (+ occ tlen 1))
|
||||||
|
str))
|
||||||
|
str))))))
|
||||||
|
|
||||||
|
;;; Substitute template
|
||||||
|
(define (substitute text template-format subst-list)
|
||||||
|
(fold (lambda (s out)
|
||||||
|
(string-replace-text
|
||||||
|
out
|
||||||
|
(format template-format (first s))
|
||||||
|
(format "~a" (second s))))
|
||||||
|
text subst-list))
|
||||||
|
|
||||||
|
;;; Read template and substitute replacements
|
||||||
|
;;; Returns list of strings (lines)
|
||||||
|
(define (read-template template-file template-format subst-list)
|
||||||
|
(let ((ls (call-with-input-file template-file
|
||||||
|
(lambda (port)
|
||||||
|
(let loop ((l '()))
|
||||||
|
(let ((s (get-line port)))
|
||||||
|
(if (eof-object? s)
|
||||||
|
(reverse l)
|
||||||
|
(loop (cons s l)))))))))
|
||||||
|
(map (lambda (str)
|
||||||
|
(substitute str template-format subst-list))
|
||||||
|
ls)))
|
||||||
|
|
||||||
|
;;; Split the string STR into a list of the substrings delimited by DELIMITER
|
||||||
|
(define (string-split-str str delimiter)
|
||||||
|
(if (string-null? str)
|
||||||
|
'()
|
||||||
|
(let ((didx (string-contains str delimiter)))
|
||||||
|
(if didx
|
||||||
|
(cons (substring str 0 didx)
|
||||||
|
(string-split-str
|
||||||
|
(substring str (+ didx (string-length delimiter)))
|
||||||
|
delimiter))
|
||||||
|
(list str)))))
|
||||||
|
|
||||||
|
;;; Split string and remove empty itemes
|
||||||
|
(define (string-split-trim str pred?)
|
||||||
|
(remove string-null?
|
||||||
|
(string-split str pred?)))
|
||||||
|
|
||||||
|
;;; Get word delimited by pred? from port
|
||||||
|
(define* (get-word port #:optional (pred? char-whitespace?))
|
||||||
|
(let get-word-rec ((chlist '()))
|
||||||
|
(let ((c (get-char port)))
|
||||||
|
(if (eof-object? c)
|
||||||
|
(if (null? chlist)
|
||||||
|
#f
|
||||||
|
(list->string (reverse chlist)))
|
||||||
|
(if (pred? c)
|
||||||
|
(if (null? chlist)
|
||||||
|
(get-word-rec chlist)
|
||||||
|
(list->string (reverse chlist)))
|
||||||
|
(get-word-rec (cons c chlist)))))))
|
||||||
66
scripts/optargs.scm
Normal file
66
scripts/optargs.scm
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
(define-module (optargs))
|
||||||
|
|
||||||
|
(import (srfi srfi-1)
|
||||||
|
(srfi srfi-11)
|
||||||
|
(srfi srfi-37))
|
||||||
|
|
||||||
|
(export parse-opts
|
||||||
|
option-get)
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; TODO: Write docs
|
||||||
|
;;;
|
||||||
|
(define (option-get opts name)
|
||||||
|
(let ((opt (assoc name opts)))
|
||||||
|
(if opt
|
||||||
|
(cdr opt)
|
||||||
|
#f)))
|
||||||
|
|
||||||
|
(define (option-set opts name value)
|
||||||
|
(if (assoc name opts)
|
||||||
|
(map (lambda (opt)
|
||||||
|
(if (equal? (car opt) name)
|
||||||
|
(cons name value)
|
||||||
|
opt))
|
||||||
|
opts)
|
||||||
|
(alist-cons name value opts)))
|
||||||
|
|
||||||
|
(define (option-add opts name value)
|
||||||
|
(if (assoc name opts)
|
||||||
|
(option-set opts name
|
||||||
|
(cons value
|
||||||
|
(option-get opts name)))
|
||||||
|
(alist-cons name `(,value) opts)))
|
||||||
|
|
||||||
|
;;; opt-spec - '(("option" #\o) [none | required | optional | multiple])
|
||||||
|
(define (parse-opts args . opt-spec)
|
||||||
|
(args-fold
|
||||||
|
;; args
|
||||||
|
args
|
||||||
|
;; options
|
||||||
|
(map (lambda (spec)
|
||||||
|
(let* ((names (list-ref spec 0))
|
||||||
|
(type (list-ref spec 1))
|
||||||
|
(name (car names))
|
||||||
|
(req? (eq? type 'required))
|
||||||
|
(opt? (eq? type 'optional))
|
||||||
|
(many? (eq? type 'multiple)))
|
||||||
|
(option names (or many? req?) opt?
|
||||||
|
(if many?
|
||||||
|
(lambda (opt nm arg opts rest error)
|
||||||
|
(values (if arg
|
||||||
|
(option-add opts name arg)
|
||||||
|
opts)
|
||||||
|
rest
|
||||||
|
error))
|
||||||
|
(lambda (opt nm arg opts rest error)
|
||||||
|
(values (option-set opts name (if arg arg #t)) rest error))))))
|
||||||
|
opt-spec)
|
||||||
|
;; unrecognized options
|
||||||
|
(lambda (opt name arg opts rest error)
|
||||||
|
(values opts rest name))
|
||||||
|
;; operands
|
||||||
|
(lambda (operand opts rest error)
|
||||||
|
(values opts (cons operand rest) error))
|
||||||
|
;; seeds
|
||||||
|
'() '() #f))
|
||||||
424
scripts/picorv32-bus-mux-gen.scm
Executable file
424
scripts/picorv32-bus-mux-gen.scm
Executable file
@ -0,0 +1,424 @@
|
|||||||
|
#!/usr/bin/env -S guile -e "main" -s
|
||||||
|
!#
|
||||||
|
|
||||||
|
;; -*- geiser-scheme-implementation: guile -*-
|
||||||
|
|
||||||
|
(add-to-load-path (dirname (current-filename)))
|
||||||
|
|
||||||
|
(import
|
||||||
|
(srfi srfi-1) ; Lists
|
||||||
|
(srfi srfi-11) ; let-values
|
||||||
|
(srfi srfi-28) ; Simple format
|
||||||
|
(common)
|
||||||
|
(optargs))
|
||||||
|
|
||||||
|
;;; Default address width
|
||||||
|
(define ADDR_WIDTH 32)
|
||||||
|
|
||||||
|
;;; Default data width
|
||||||
|
(define DATA_WIDTH 32)
|
||||||
|
|
||||||
|
;;; Count of indentation spaces
|
||||||
|
(define INDENT 2)
|
||||||
|
|
||||||
|
;;; Convert arbitrary radix string in C-format to number
|
||||||
|
(define (string-c-radix->number str)
|
||||||
|
(if (and str (string? str))
|
||||||
|
(let ((str (string-trim-both str)))
|
||||||
|
(cond
|
||||||
|
((string-every #\0 str) 0)
|
||||||
|
((string-prefix? "0x" str)
|
||||||
|
(string->number (substring str 2) 16))
|
||||||
|
((string-prefix? "0b" str)
|
||||||
|
(string->number (substring str 2) 2))
|
||||||
|
((string-prefix? "0" str)
|
||||||
|
(string->number (substring str 1) 8))
|
||||||
|
(else
|
||||||
|
(string->number str 10))))
|
||||||
|
#f))
|
||||||
|
|
||||||
|
;;; Print to stderr
|
||||||
|
(define (warning . rest)
|
||||||
|
(display "Warning: " (current-error-port))
|
||||||
|
(display (apply format rest) (current-error-port))
|
||||||
|
(newline (current-error-port)))
|
||||||
|
|
||||||
|
(define (error . rest)
|
||||||
|
(display "Error: " (current-error-port))
|
||||||
|
(display (apply format rest) (current-error-port))
|
||||||
|
(newline (current-error-port)))
|
||||||
|
|
||||||
|
(define (error-and-exit . rest)
|
||||||
|
(apply error rest)
|
||||||
|
(exit EXIT_FAILURE))
|
||||||
|
|
||||||
|
;;; Println with indentationm
|
||||||
|
;;; (-> [indent] format-string [parameters])
|
||||||
|
(define (->> . fmt)
|
||||||
|
(cond
|
||||||
|
((null? fmt) #f)
|
||||||
|
((number? (car fmt))
|
||||||
|
(let ((indent (car fmt))
|
||||||
|
(rest (cdr fmt)))
|
||||||
|
(when (not (null? rest))
|
||||||
|
(display (list->string (make-list (* indent INDENT) #\space)))
|
||||||
|
(display (apply format rest)))))
|
||||||
|
(else
|
||||||
|
(display (apply format fmt)))))
|
||||||
|
|
||||||
|
(define (-> . fmt)
|
||||||
|
(apply ->> fmt)
|
||||||
|
(newline))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; ----------------------------------------------------------------------
|
||||||
|
;;; -------------------------- VERILOG BACKEND ---------------------------
|
||||||
|
;;; ----------------------------------------------------------------------
|
||||||
|
;;;
|
||||||
|
|
||||||
|
;;; Print module header
|
||||||
|
(define (print-verilog-module-header slaves module-name)
|
||||||
|
(let ((slaves-count (length slaves)))
|
||||||
|
(-> 0 "// This file is auto-generated. Do not edit")
|
||||||
|
(->)
|
||||||
|
(-> 0 "// Slaves address ranges:")
|
||||||
|
(for-each
|
||||||
|
(lambda (slave n)
|
||||||
|
(let ((b (car slave))
|
||||||
|
(s (cdr slave)))
|
||||||
|
(-> 0 "// ~a - 0x~a-0x~a"
|
||||||
|
n
|
||||||
|
(number->string-hex b (/ ADDR_WIDTH 4))
|
||||||
|
(number->string-hex (- (+ b s) 1) (/ ADDR_WIDTH 4)))))
|
||||||
|
slaves
|
||||||
|
(iota slaves-count))
|
||||||
|
(->)
|
||||||
|
(-> 0 "// i_slave_rdata bits:")
|
||||||
|
(for-each
|
||||||
|
(lambda (n)
|
||||||
|
(-> 0 "// ~a: i_slave_rdata[~a:~a]"
|
||||||
|
n
|
||||||
|
(- (* (+ n 1) DATA_WIDTH) 1)
|
||||||
|
(* n DATA_WIDTH)))
|
||||||
|
(iota slaves-count))
|
||||||
|
(->)
|
||||||
|
(-> 0 "module ~a" module-name)
|
||||||
|
(-> 1 "(input wire clock,")
|
||||||
|
(-> 1 " input wire reset,")
|
||||||
|
(->)
|
||||||
|
(-> 1 " // PicoRV32 memory interface")
|
||||||
|
(-> 1 " // Look-ahead address and multiplexed signals")
|
||||||
|
(-> 1 " // Some bits of address may not be used")
|
||||||
|
(-> 1 " /* verilator lint_off UNUSED */")
|
||||||
|
(-> 1 " input wire [~a:0] i_la_addr," (- ADDR_WIDTH 1))
|
||||||
|
(-> 1 " /* verilator lint_on UNUSED */")
|
||||||
|
(-> 1 " output wire [~a:0] o_rdata," (- DATA_WIDTH 1))
|
||||||
|
(-> 1 " input wire i_valid,")
|
||||||
|
(-> 1 " output wire o_ready,")
|
||||||
|
(->)
|
||||||
|
(-> 1 " // Slaves interface")
|
||||||
|
(-> 1 " input wire [~a:0] i_slave_rdata," (- (* slaves-count DATA_WIDTH) 1))
|
||||||
|
(-> 1 " output wire [~a:0] o_slave_valid," (- slaves-count 1))
|
||||||
|
(-> 1 " input wire [~a:0] i_slave_ready);" (- slaves-count 1))
|
||||||
|
(->)))
|
||||||
|
|
||||||
|
;;; Print module footer
|
||||||
|
(define (print-verilog-module-footer module-name)
|
||||||
|
(-> 0 "endmodule // ~a" module-name)
|
||||||
|
(-> 0 "`default_nettype wire"))
|
||||||
|
|
||||||
|
;;; Print selectors
|
||||||
|
(define (print-verilog-selectors slaves)
|
||||||
|
(let ((count (length slaves))
|
||||||
|
(addrs (map car slaves)))
|
||||||
|
|
||||||
|
(-> 1 "wire [~a:0] selector;" (- count 1))
|
||||||
|
(-> 1 "reg [~a:0] selector_reg;" (- count 1))
|
||||||
|
(->)
|
||||||
|
(-> 1 "always @(posedge clock)")
|
||||||
|
(-> 2 "if (reset)")
|
||||||
|
(-> 3 "selector_reg <= ~a'd0;" count)
|
||||||
|
(-> 2 "else")
|
||||||
|
(-> 3 "if (!i_valid)")
|
||||||
|
(-> 4 "selector_reg <= selector;")
|
||||||
|
(->)
|
||||||
|
|
||||||
|
(let ((selectors (make-mux-selectors addrs)))
|
||||||
|
(for-each
|
||||||
|
(lambda (addr n)
|
||||||
|
(let ((selector (cdr (assq addr selectors))))
|
||||||
|
(if (every not selector)
|
||||||
|
(-> 1 "assign selector[~a] = 1'b1;" n)
|
||||||
|
(begin
|
||||||
|
(-> 1 "assign selector[~a] =" n)
|
||||||
|
(let loop ((bits selector)
|
||||||
|
(n 0)
|
||||||
|
(need-and-sign #f))
|
||||||
|
(if (null? bits) #f
|
||||||
|
(begin
|
||||||
|
(let ((bit (car bits)))
|
||||||
|
(loop (cdr bits) (+ n 1)
|
||||||
|
(if bit
|
||||||
|
(begin
|
||||||
|
(when need-and-sign (-> " &&"))
|
||||||
|
(->> 2 "i_la_addr[~a] == 1'b~a" n bit)
|
||||||
|
#t)
|
||||||
|
need-and-sign))))))
|
||||||
|
(-> ";")))
|
||||||
|
(->)))
|
||||||
|
addrs (iota count)))))
|
||||||
|
|
||||||
|
;;; Print one range body
|
||||||
|
(define (print-verilog-body slaves)
|
||||||
|
(let ((slaves-count (length slaves)))
|
||||||
|
(-> 1 "assign o_slave_valid = selector_reg & {~a{i_valid}};" slaves-count)
|
||||||
|
(-> 1 "assign o_ready = |(i_slave_ready & selector_reg);")
|
||||||
|
(->)
|
||||||
|
(-> 1 "assign o_rdata =")
|
||||||
|
(for-each
|
||||||
|
(lambda (n)
|
||||||
|
(->> 2 "(i_slave_rdata[~a:~a] & {~a{selector_reg[~a]}})"
|
||||||
|
(- (* DATA_WIDTH (+ n 1)) 1)
|
||||||
|
(* DATA_WIDTH n)
|
||||||
|
DATA_WIDTH
|
||||||
|
n)
|
||||||
|
(-> "~a" (if (= n (- slaves-count 1)) ";" " |")))
|
||||||
|
(iota slaves-count))
|
||||||
|
(->)))
|
||||||
|
|
||||||
|
;;; Print formal
|
||||||
|
(define (print-verilog-formal slaves module-name)
|
||||||
|
(let ((slaves-count (length slaves)))
|
||||||
|
(-> 0 "`ifdef FORMAL")
|
||||||
|
(->)
|
||||||
|
|
||||||
|
(-> 1 "always @(*) begin : formal_selector")
|
||||||
|
(-> 2 "integer ones, n;")
|
||||||
|
(-> 2 "ones = 0;")
|
||||||
|
(->)
|
||||||
|
(-> 2 "// Check for selector is zero or one-hot value")
|
||||||
|
(-> 2 "for (n = 0; n < ~a; n = n + 1)" slaves-count)
|
||||||
|
(-> 3 "if (selector[n] == 1'b1)")
|
||||||
|
(-> 4 "ones = ones + 1;")
|
||||||
|
(->)
|
||||||
|
(-> 2 "assert(ones < 2);")
|
||||||
|
(->)
|
||||||
|
(-> 2 "// Check for correct address ranges decode")
|
||||||
|
(for-each
|
||||||
|
(lambda (slave n)
|
||||||
|
(let ((b (car slave))
|
||||||
|
(s (cdr slave)))
|
||||||
|
(-> 2 "if (i_la_addr >= ~a'h~a && i_la_addr <= ~a'h~a)"
|
||||||
|
ADDR_WIDTH (number->string b 16)
|
||||||
|
ADDR_WIDTH (number->string (- (+ b s) 1) 16))
|
||||||
|
(-> 3 "assert(selector[~a] == 1'b1);" n)))
|
||||||
|
slaves
|
||||||
|
(iota slaves-count))
|
||||||
|
(-> 1 "end")
|
||||||
|
(->)
|
||||||
|
|
||||||
|
(-> 1 "// Check multiplexer")
|
||||||
|
(-> 1 "always @(*) begin : formal_mux")
|
||||||
|
(-> 2 "case (selector_reg)")
|
||||||
|
(for-each
|
||||||
|
(lambda (n)
|
||||||
|
(-> 3 "~a'b~a: begin"
|
||||||
|
slaves-count
|
||||||
|
(list->string
|
||||||
|
(map (lambda (x) (if (= x n) #\1 #\0))
|
||||||
|
(reverse (iota slaves-count)))))
|
||||||
|
(-> 4 "assert(o_rdata == i_slave_rdata[~a:~a]);"
|
||||||
|
(- (* (+ n 1) DATA_WIDTH) 1)
|
||||||
|
(* n DATA_WIDTH))
|
||||||
|
(-> 4 "assert(o_ready == i_slave_ready[~a]);" n)
|
||||||
|
(-> 4 "assert(o_slave_valid[~a] == i_valid);" n)
|
||||||
|
(-> 3 "end")
|
||||||
|
)
|
||||||
|
(iota slaves-count))
|
||||||
|
(-> 3 "~a'b~a: begin" slaves-count (make-string slaves-count #\0))
|
||||||
|
(-> 4 "assert(o_rdata == ~a'd0);" DATA_WIDTH)
|
||||||
|
(-> 4 "assert(o_ready == 1'b0);")
|
||||||
|
(-> 4 "assert(o_slave_valid == ~a'd0);" slaves-count)
|
||||||
|
(-> 3 "end")
|
||||||
|
(-> 2 "endcase")
|
||||||
|
(-> 1 "end")
|
||||||
|
(->)
|
||||||
|
|
||||||
|
(-> 1 "// Assume module is not in reset state")
|
||||||
|
(-> 1 "always @(*) assume(reset == 1'b0);")
|
||||||
|
(->)
|
||||||
|
(-> 1 "// Make flag that the past is valid")
|
||||||
|
(-> 1 "reg have_past = 1'b0;")
|
||||||
|
(-> 1 "always @(posedge clock) have_past <= 1'b1;")
|
||||||
|
(->)
|
||||||
|
(-> 1 "// Check for selector_reg is valid and stable when i_valid is 1")
|
||||||
|
(-> 1 "always @(posedge clock) begin")
|
||||||
|
(-> 2 "if (have_past)")
|
||||||
|
(-> 3 "if (i_valid)")
|
||||||
|
(-> 4 "if ($rose(i_valid))")
|
||||||
|
(-> 5 "assert(selector_reg == $past(selector));")
|
||||||
|
(-> 4 "else")
|
||||||
|
(-> 5 "assert($stable(selector_reg));")
|
||||||
|
(-> 1 "end")
|
||||||
|
(->)
|
||||||
|
|
||||||
|
(-> 0 "`endif // FORMAL")
|
||||||
|
(->)))
|
||||||
|
|
||||||
|
;;; Print verilog code for slaves
|
||||||
|
(define (print-verilog slaves module-name)
|
||||||
|
(print-verilog-module-header slaves module-name)
|
||||||
|
(print-verilog-selectors slaves)
|
||||||
|
(print-verilog-body slaves)
|
||||||
|
(print-verilog-formal slaves module-name)
|
||||||
|
(print-verilog-module-footer module-name))
|
||||||
|
|
||||||
|
(define (print-sby-script module-name)
|
||||||
|
(-> "# To run formal verification call SymbiYosys:")
|
||||||
|
(-> "# $ sby -f ~a.sby" module-name)
|
||||||
|
(->)
|
||||||
|
(-> "[options]")
|
||||||
|
(-> "mode prove")
|
||||||
|
(->)
|
||||||
|
(-> "[engines]")
|
||||||
|
(-> "smtbmc boolector")
|
||||||
|
(->)
|
||||||
|
(-> "[script]")
|
||||||
|
(-> "read -vlog95 -formal ~a.v" module-name)
|
||||||
|
(-> "prep -top ~a" module-name)
|
||||||
|
(->)
|
||||||
|
(-> "[files]")
|
||||||
|
(-> "~a.v" module-name))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Main
|
||||||
|
;;;
|
||||||
|
|
||||||
|
;;; Check for slave address ranges for intersection
|
||||||
|
(define (slaves-intersected? slaves)
|
||||||
|
(let ((sorted (sort slaves (lambda (a b) (< (car a) (car b))))))
|
||||||
|
(let check ((slave (car sorted))
|
||||||
|
(slaves (cdr sorted)))
|
||||||
|
(if (null? slaves)
|
||||||
|
#f
|
||||||
|
(let ((next (car slaves)))
|
||||||
|
(let ((b0 (car slave))
|
||||||
|
(s0 (cdr slave))
|
||||||
|
(b1 (car next)))
|
||||||
|
(if (> (+ b0 s0) b1)
|
||||||
|
#t
|
||||||
|
(check next (cdr slaves)))))))))
|
||||||
|
|
||||||
|
(define (print-help app-name)
|
||||||
|
(with-output-to-port (current-error-port)
|
||||||
|
(lambda ()
|
||||||
|
(-> "Usage: ~a [OPTION]... [FILE]" app-name)
|
||||||
|
(-> "Make verilog module of PicoRV bus multiplexer.")
|
||||||
|
(-> "Optional FILE - is an address spaces description file.")
|
||||||
|
(-> "")
|
||||||
|
(-> "Options:")
|
||||||
|
(-> " -s, --slave ADDRESS_RANGE Add slave address range")
|
||||||
|
(-> " -m, --module MODULE_NAME Verilog module name (optional)")
|
||||||
|
(-> " -f, --formal Print script (sby) for SymbiYosys")
|
||||||
|
(-> " -h, --help Print this message and exit")
|
||||||
|
(-> "")
|
||||||
|
(-> "Where ADDRESS_RANGE is string of BASE_ADDRESS+LENGTH")
|
||||||
|
(-> "")
|
||||||
|
(-> "Generate mux for two address ranges [0..0x0fff] and [0x1000..0x1fff]:")
|
||||||
|
(-> " ~a -s 0x0+0x1000 -s 0x1000+0x1000" app-name)
|
||||||
|
(-> "")
|
||||||
|
(-> "If FILE is specified --slave (-s) option will ignored.")
|
||||||
|
(-> "")
|
||||||
|
(-> "Source code and issue tracker: <https://github.com/punzik/>"))))
|
||||||
|
|
||||||
|
|
||||||
|
(define (main args)
|
||||||
|
(debug-disable 'backtrace)
|
||||||
|
(let-values
|
||||||
|
(((opts rest err)
|
||||||
|
(parse-opts (cdr args)
|
||||||
|
'(("slave" #\s) multiple)
|
||||||
|
'(("module" #\m) required)
|
||||||
|
'(("help" #\h) none)
|
||||||
|
'(("formal" #\f) none))))
|
||||||
|
|
||||||
|
(if err
|
||||||
|
(begin
|
||||||
|
(error "Unknown option\n")
|
||||||
|
(print-help (car args))
|
||||||
|
(exit -1))
|
||||||
|
|
||||||
|
(let ((slaves (option-get opts "slave"))
|
||||||
|
(mod-name (option-get opts "module"))
|
||||||
|
(help (option-get opts "help"))
|
||||||
|
(formal (option-get opts "formal"))
|
||||||
|
(file-name (if (null? rest) #f (car rest))))
|
||||||
|
|
||||||
|
(if (or help (not slaves))
|
||||||
|
(print-help (car args))
|
||||||
|
(let-values
|
||||||
|
(((slaves mod-name)
|
||||||
|
(if file-name
|
||||||
|
;; Read config from file
|
||||||
|
(let ((cfg (with-input-from-file file-name read)))
|
||||||
|
(values
|
||||||
|
(map (lambda (sl) (cons (car sl) (cadr sl)))
|
||||||
|
(filter list? cfg))
|
||||||
|
(if mod-name
|
||||||
|
mod-name
|
||||||
|
(find string? cfg))))
|
||||||
|
;; Use arguments
|
||||||
|
(values
|
||||||
|
(sort
|
||||||
|
(map (lambda (slave-opt)
|
||||||
|
(let ((base+size (string-split slave-opt #\+)))
|
||||||
|
(if (not (= (length base+size) 2))
|
||||||
|
(error-and-exit "Wrong slave format")
|
||||||
|
(let ((base (string-c-radix->number (first base+size)))
|
||||||
|
(size (string-c-radix->number (second base+size))))
|
||||||
|
(if (not (and base size))
|
||||||
|
(error-and-exit "Wrong address/size number format '~a+~a'" base size)
|
||||||
|
(cons base size))))))
|
||||||
|
slaves)
|
||||||
|
(lambda (a b) (< (car a) (car b))))
|
||||||
|
mod-name))))
|
||||||
|
|
||||||
|
(let ((module-name
|
||||||
|
(if mod-name
|
||||||
|
mod-name
|
||||||
|
(format "picorv32_busmux_1x~a" (length slaves)))))
|
||||||
|
|
||||||
|
;; Check slaves integrity
|
||||||
|
(cond
|
||||||
|
;; Address space size is zero
|
||||||
|
((any (lambda (slave) (zero? (cdr slave))) slaves)
|
||||||
|
(error-and-exit "Address space size is zero"))
|
||||||
|
|
||||||
|
;; Address space size is not power of two
|
||||||
|
((any (lambda (slave) (not (power-of-two? (cdr slave)))) slaves)
|
||||||
|
(error-and-exit "Address space size is not power of two"))
|
||||||
|
|
||||||
|
;; Base address is not divisible by address space size
|
||||||
|
((any (lambda (slave) (not
|
||||||
|
(zero?
|
||||||
|
(remainder (car slave)
|
||||||
|
(cdr slave)))))
|
||||||
|
slaves)
|
||||||
|
(error-and-exit "Base address is not divisible by address space size"))
|
||||||
|
|
||||||
|
;; Address range is not in range of 2^ADDR_WIDTH
|
||||||
|
((any (lambda (slave)
|
||||||
|
(> (+ (car slave) (cdr slave))
|
||||||
|
(expt 2 ADDR_WIDTH)))
|
||||||
|
slaves)
|
||||||
|
(error-and-exit "Slave address is out of ~a bit range" ADDR_WIDTH))
|
||||||
|
|
||||||
|
;; Address ranges intersected
|
||||||
|
((slaves-intersected? slaves)
|
||||||
|
(error-and-exit "Slave address ranges is intersected"))
|
||||||
|
|
||||||
|
;; All OK
|
||||||
|
(else
|
||||||
|
(if formal
|
||||||
|
(print-sby-script module-name)
|
||||||
|
(print-verilog slaves module-name)))))))))))
|
||||||
1346
scripts/register-gen.scm
Executable file
1346
scripts/register-gen.scm
Executable file
File diff suppressed because it is too large
Load Diff
15
source/bus_mux.sby
Normal file
15
source/bus_mux.sby
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# To run formal verification call SymbiYosys:
|
||||||
|
# $ sby -f bus_mux.sby
|
||||||
|
|
||||||
|
[options]
|
||||||
|
mode prove
|
||||||
|
|
||||||
|
[engines]
|
||||||
|
smtbmc boolector
|
||||||
|
|
||||||
|
[script]
|
||||||
|
read -vlog95 -formal bus_mux.v
|
||||||
|
prep -top bus_mux
|
||||||
|
|
||||||
|
[files]
|
||||||
|
bus_mux.v
|
||||||
114
source/bus_mux.v
Normal file
114
source/bus_mux.v
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// This file is auto-generated. Do not edit
|
||||||
|
|
||||||
|
// Slaves address ranges:
|
||||||
|
// 0 - 0x00000000-0x0000ffff
|
||||||
|
// 1 - 0x01000000-0x01000fff
|
||||||
|
|
||||||
|
// i_slave_rdata bits:
|
||||||
|
// 0: i_slave_rdata[31:0]
|
||||||
|
// 1: i_slave_rdata[63:32]
|
||||||
|
|
||||||
|
module bus_mux
|
||||||
|
(input wire clock,
|
||||||
|
input wire reset,
|
||||||
|
|
||||||
|
// PicoRV32 memory interface
|
||||||
|
// Look-ahead address and multiplexed signals
|
||||||
|
// Some bits of address may not be used
|
||||||
|
/* verilator lint_off UNUSED */
|
||||||
|
input wire [31:0] i_la_addr,
|
||||||
|
/* verilator lint_on UNUSED */
|
||||||
|
output wire [31:0] o_rdata,
|
||||||
|
input wire i_valid,
|
||||||
|
output wire o_ready,
|
||||||
|
|
||||||
|
// Slaves interface
|
||||||
|
input wire [63:0] i_slave_rdata,
|
||||||
|
output wire [1:0] o_slave_valid,
|
||||||
|
input wire [1:0] i_slave_ready);
|
||||||
|
|
||||||
|
wire [1:0] selector;
|
||||||
|
reg [1:0] selector_reg;
|
||||||
|
|
||||||
|
always @(posedge clock)
|
||||||
|
if (reset)
|
||||||
|
selector_reg <= 2'd0;
|
||||||
|
else
|
||||||
|
if (!i_valid)
|
||||||
|
selector_reg <= selector;
|
||||||
|
|
||||||
|
assign selector[0] =
|
||||||
|
i_la_addr[24] == 1'b0;
|
||||||
|
|
||||||
|
assign selector[1] =
|
||||||
|
i_la_addr[24] == 1'b1;
|
||||||
|
|
||||||
|
assign o_slave_valid = selector_reg & {2{i_valid}};
|
||||||
|
assign o_ready = |(i_slave_ready & selector_reg);
|
||||||
|
|
||||||
|
assign o_rdata =
|
||||||
|
(i_slave_rdata[31:0] & {32{selector_reg[0]}}) |
|
||||||
|
(i_slave_rdata[63:32] & {32{selector_reg[1]}});
|
||||||
|
|
||||||
|
`ifdef FORMAL
|
||||||
|
|
||||||
|
always @(*) begin : formal_selector
|
||||||
|
integer ones, n;
|
||||||
|
ones = 0;
|
||||||
|
|
||||||
|
// Check for selector is zero or one-hot value
|
||||||
|
for (n = 0; n < 2; n = n + 1)
|
||||||
|
if (selector[n] == 1'b1)
|
||||||
|
ones = ones + 1;
|
||||||
|
|
||||||
|
assert(ones < 2);
|
||||||
|
|
||||||
|
// Check for correct address ranges decode
|
||||||
|
if (i_la_addr >= 32'h0 && i_la_addr <= 32'hffff)
|
||||||
|
assert(selector[0] == 1'b1);
|
||||||
|
if (i_la_addr >= 32'h1000000 && i_la_addr <= 32'h1000fff)
|
||||||
|
assert(selector[1] == 1'b1);
|
||||||
|
end
|
||||||
|
|
||||||
|
// Check multiplexer
|
||||||
|
always @(*) begin : formal_mux
|
||||||
|
case (selector_reg)
|
||||||
|
2'b01: begin
|
||||||
|
assert(o_rdata == i_slave_rdata[31:0]);
|
||||||
|
assert(o_ready == i_slave_ready[0]);
|
||||||
|
assert(o_slave_valid[0] == i_valid);
|
||||||
|
end
|
||||||
|
2'b10: begin
|
||||||
|
assert(o_rdata == i_slave_rdata[63:32]);
|
||||||
|
assert(o_ready == i_slave_ready[1]);
|
||||||
|
assert(o_slave_valid[1] == i_valid);
|
||||||
|
end
|
||||||
|
2'b00: begin
|
||||||
|
assert(o_rdata == 32'd0);
|
||||||
|
assert(o_ready == 1'b0);
|
||||||
|
assert(o_slave_valid == 2'd0);
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// Assume module is not in reset state
|
||||||
|
always @(*) assume(reset == 1'b0);
|
||||||
|
|
||||||
|
// Make flag that the past is valid
|
||||||
|
reg have_past = 1'b0;
|
||||||
|
always @(posedge clock) have_past <= 1'b1;
|
||||||
|
|
||||||
|
// Check for selector_reg is valid and stable when i_valid is 1
|
||||||
|
always @(posedge clock) begin
|
||||||
|
if (have_past)
|
||||||
|
if (i_valid)
|
||||||
|
if ($rose(i_valid))
|
||||||
|
assert(selector_reg == $past(selector));
|
||||||
|
else
|
||||||
|
assert($stable(selector_reg));
|
||||||
|
end
|
||||||
|
|
||||||
|
`endif // FORMAL
|
||||||
|
|
||||||
|
endmodule // bus_mux
|
||||||
|
`default_nettype wire
|
||||||
38
source/firmware/.clang-format
Normal file
38
source/firmware/.clang-format
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 4
|
||||||
|
UseTab: false
|
||||||
|
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
AlignConsecutiveMacros: true
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: Align
|
||||||
|
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowShortBlocksOnASingleLine: Empty
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||||
|
|
||||||
|
BinPackArguments: true
|
||||||
|
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterControlStatement: MultiLine
|
||||||
|
AfterFunction: true
|
||||||
|
AfterStruct: true
|
||||||
|
BeforeElse: true
|
||||||
|
BeforeWhile: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpacesBeforeTrailingComments: 4
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
4
source/firmware/.gitignore
vendored
Normal file
4
source/firmware/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.elf
|
||||||
|
*.bin
|
||||||
|
*.map
|
||||||
|
*.asm
|
||||||
44
source/firmware/Makefile
Normal file
44
source/firmware/Makefile
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
PROJECT := fw
|
||||||
|
SOURCES := crt0.s main.c uprintf.c
|
||||||
|
CPU_RAM_REG := ram_reg
|
||||||
|
|
||||||
|
ARCH := riscv32-none-elf
|
||||||
|
CFLAGS := -O2 -Wall -march=rv32i -mabi=ilp32 -mstrict-align \
|
||||||
|
-nostartfiles \
|
||||||
|
-ffunction-sections -lgcc \
|
||||||
|
-Wl,-Tpicorv32-minimal.ld,-static,-Map,$(PROJECT).map
|
||||||
|
|
||||||
|
# CFLAGS := -O3 -Wall -march=rv32i -mabi=ilp32 -mstrict-align \
|
||||||
|
# -nostartfiles \
|
||||||
|
# -ffunction-sections \
|
||||||
|
# -ffreestanding -lgcc \
|
||||||
|
# -Wl,-T,picorv32-minimal.ld,-static,-Map,$(PROJECT).map
|
||||||
|
|
||||||
|
# -nostdlib
|
||||||
|
|
||||||
|
ELF = $(PROJECT).elf
|
||||||
|
BIN = $(PROJECT).bin
|
||||||
|
ASM = $(PROJECT).asm
|
||||||
|
SVH = $(PROJECT).svh
|
||||||
|
MEM = $(PROJECT).mem
|
||||||
|
|
||||||
|
all: $(ELF) $(BIN) $(MEM) Makefile
|
||||||
|
|
||||||
|
$(ELF): $(SOURCES) picorv32-minimal.ld Makefile
|
||||||
|
$(ARCH)-gcc $(CFLAGS) -o $(ELF) $(SOURCES)
|
||||||
|
|
||||||
|
$(BIN): $(ELF)
|
||||||
|
$(ARCH)-objcopy -O binary $(ELF) $(BIN)
|
||||||
|
|
||||||
|
$(SVH): $(BIN)
|
||||||
|
../../scripts/bin2initial.scm $(BIN) $(CPU_RAM_REG) > $(SVH)
|
||||||
|
|
||||||
|
$(MEM): $(BIN)
|
||||||
|
../../scripts/bin2mem.scm $(BIN) 65536 > $(MEM)
|
||||||
|
|
||||||
|
disasm: $(ASM)
|
||||||
|
$(ASM): $(ELF)
|
||||||
|
$(ARCH)-objdump -d $(ELF) > $(ASM)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(ELF) $(BIN) $(PROJECT).map $(SVH) $(MEM)
|
||||||
7
source/firmware/crt0.s
Normal file
7
source/firmware/crt0.s
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.section .init, "ax"
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
la sp, __stack_top
|
||||||
|
add s0, sp, zero
|
||||||
|
jal zero, main
|
||||||
|
.end
|
||||||
16384
source/firmware/fw.mem
Normal file
16384
source/firmware/fw.mem
Normal file
File diff suppressed because it is too large
Load Diff
86
source/firmware/main.c
Normal file
86
source/firmware/main.c
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include "../io_reg.h"
|
||||||
|
#include "uprintf.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void put_char(char c)
|
||||||
|
{
|
||||||
|
IO_REG_CONSOLE = c | IO_REG_CONSOLE_SEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define N 50
|
||||||
|
#define CHUNK 4
|
||||||
|
#define ARR_LEN (10 * N / 3 + 1)
|
||||||
|
|
||||||
|
static int arr[ARR_LEN];
|
||||||
|
|
||||||
|
void print_digit(int d)
|
||||||
|
{
|
||||||
|
static int cnt = 0;
|
||||||
|
|
||||||
|
p("%d", d);
|
||||||
|
cnt++;
|
||||||
|
|
||||||
|
if (cnt == CHUNK) {
|
||||||
|
p("\n");
|
||||||
|
cnt = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See: https://en.wikipedia.org/wiki/Spigot_algorithm */
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
p("\nComputation of %d first digits of PI\n", N);
|
||||||
|
|
||||||
|
for (int i = 0; i < ARR_LEN; i++)
|
||||||
|
arr[i] = 2;
|
||||||
|
|
||||||
|
int nines = 0;
|
||||||
|
int predigit = 0;
|
||||||
|
|
||||||
|
for (int j = 1; j < N + 1; j++) {
|
||||||
|
int q = 0;
|
||||||
|
|
||||||
|
for (int i = ARR_LEN; i > 0; i--) {
|
||||||
|
int x = 10 * arr[i - 1] + q * i;
|
||||||
|
arr[i - 1] = x % (2 * i - 1);
|
||||||
|
q = x / (2 * i - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr[0] = q % 10;
|
||||||
|
q = q / 10;
|
||||||
|
|
||||||
|
if (9 == q)
|
||||||
|
nines++;
|
||||||
|
else if (10 == q) {
|
||||||
|
print_digit(predigit + 1);
|
||||||
|
|
||||||
|
for (int k = 0; k < nines; k++)
|
||||||
|
print_digit(0);
|
||||||
|
|
||||||
|
predigit = 0;
|
||||||
|
nines = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print_digit(predigit);
|
||||||
|
predigit = q;
|
||||||
|
|
||||||
|
if (0 != nines) {
|
||||||
|
for (int k = 0; k < nines; k++)
|
||||||
|
print_digit(9);
|
||||||
|
|
||||||
|
nines = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p("%d", predigit);
|
||||||
|
|
||||||
|
p("\nDONE\n");
|
||||||
|
|
||||||
|
/* Stop simulation */
|
||||||
|
IO_REG_CTRL = IO_REG_CTRL_STOP;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
};
|
||||||
|
}
|
||||||
34
source/firmware/picorv32-minimal.ld
Normal file
34
source/firmware/picorv32-minimal.ld
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
MEMORY { ram (rx) : ORIGIN = 0, LENGTH = 65536 }
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x00;
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
*(.init*)
|
||||||
|
*(.text*)
|
||||||
|
} > ram
|
||||||
|
|
||||||
|
.bss (NOLOAD) :
|
||||||
|
{
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
} > ram
|
||||||
|
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
*(.data*)
|
||||||
|
} > ram
|
||||||
|
|
||||||
|
.stack (NOLOAD) :
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
. = ALIGN(4);
|
||||||
|
. = . + STACK_SIZE;
|
||||||
|
*/
|
||||||
|
|
||||||
|
. = ORIGIN(ram) + LENGTH(ram) - 4;
|
||||||
|
. = ALIGN(4);
|
||||||
|
__stack_top = .;
|
||||||
|
} > ram
|
||||||
|
}
|
||||||
16
source/firmware/shell.nix
Normal file
16
source/firmware/shell.nix
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{ nixpkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
let cross-rv5 = import <nixpkgs> {
|
||||||
|
crossSystem = {
|
||||||
|
config = "riscv32-none-elf";
|
||||||
|
gcc = { arch = "rv32i"; abi = "ilp32"; };
|
||||||
|
libc = "newlib";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
cross-rv5.mkShell {
|
||||||
|
nativeBuildInputs = [ nixpkgs.gnumake nixpkgs.guile_3_0 ];
|
||||||
|
shellHook = ''
|
||||||
|
export NIX_SHELL_NAME="riscv"
|
||||||
|
'';
|
||||||
|
}
|
||||||
396
source/firmware/uprintf.c
Normal file
396
source/firmware/uprintf.c
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
#include "uprintf.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ------------------- Простая замена printf ------------------------
|
||||||
|
*
|
||||||
|
* Спецификаторы в форматной строке так же как и в printf начинаются
|
||||||
|
* со знака %. После этого знака может идти спецификатор типа с
|
||||||
|
* опциональным указанием ширины выравнивания, символа для
|
||||||
|
* выравнивания и направления выравнивания.
|
||||||
|
*
|
||||||
|
* Первым должен идти спецификатор символа выравнивания - знак '
|
||||||
|
* (одинарная скобка). После него - символ для выравнивания.
|
||||||
|
*
|
||||||
|
* По умолчанию числа выравниваются символом '0' по правому краю, а
|
||||||
|
* строки пробелом по левому краю.
|
||||||
|
*
|
||||||
|
* Далее идет указатель ширины - десятичное число со знаком,
|
||||||
|
* обозначающее минимальную длину выводимой строки. Если строка
|
||||||
|
* получается меньше этого числа, недостающая длина добирается
|
||||||
|
* символами выравнивания. Если ширина указана в виде отрицательного
|
||||||
|
* числа, то выравнивание выполняется по правому краю. Если для числа
|
||||||
|
* не указан символ выравнивания, то оно всегда выравнивается символом
|
||||||
|
* '0' по правому краю. Вместо числа может стоять символ '*',
|
||||||
|
* говоряший о том, что ширину нужно брать из аргумента функции.
|
||||||
|
*
|
||||||
|
* Спецификатор типа может иметь префикс 'l', обозначающий длинное целое
|
||||||
|
* (64 бита) и/или префикс 'u', обозначающий беззнаковое число.
|
||||||
|
*
|
||||||
|
* Спецификаторы типа:
|
||||||
|
* d, i Десятичное целое.
|
||||||
|
* o Восьмиричное целое.
|
||||||
|
* b Двоичное целое.
|
||||||
|
* x Шестнадцатиричное целое (строчными).
|
||||||
|
* X Шестнадцатиричное целое (прописные).
|
||||||
|
* c Символ.
|
||||||
|
* s Строка. Если указана ширина, то выравнивание по левому краю.
|
||||||
|
*
|
||||||
|
* Пример:
|
||||||
|
* p("I:+%'=-6i+\n", 10);
|
||||||
|
* Вывод: I:+====10+
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char abet[2][16] = {{'0', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
'9', 'A', 'B', 'C', 'D', 'E', 'F'},
|
||||||
|
{'0', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
'9', 'a', 'b', 'c', 'd', 'e', 'f'}};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PS_REGULAR = 0,
|
||||||
|
PS_JSYM_SPEC,
|
||||||
|
PS_JSYM,
|
||||||
|
PS_WIDTH_SIGN,
|
||||||
|
PS_WIDTH_ARG,
|
||||||
|
PS_WIDTH,
|
||||||
|
PS_TYPE,
|
||||||
|
} pstate;
|
||||||
|
|
||||||
|
static int l_strlen(const char *str)
|
||||||
|
{
|
||||||
|
int l = 0;
|
||||||
|
while (*str++)
|
||||||
|
l++;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper functions for p() */
|
||||||
|
static void print_string(put_char_func pc, const char *str, int width,
|
||||||
|
char wchr)
|
||||||
|
{
|
||||||
|
int sl, w;
|
||||||
|
|
||||||
|
if (width < 0) {
|
||||||
|
sl = l_strlen(str);
|
||||||
|
for (w = -width; w > sl; w--)
|
||||||
|
pc(wchr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (sl = 0; *str; str++, sl++)
|
||||||
|
pc(*str);
|
||||||
|
|
||||||
|
if (width > 0) {
|
||||||
|
for (w = width; w > sl; w--)
|
||||||
|
pc(wchr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
static void div(uint32_t n, uint32_t d, uint32_t *q, uint32_t *r)
|
||||||
|
{
|
||||||
|
uint32_t _q = 0;
|
||||||
|
uint32_t _r = 0;
|
||||||
|
uint32_t b = 0x80000000L;
|
||||||
|
|
||||||
|
if (d == 0) return;
|
||||||
|
|
||||||
|
while (b) {
|
||||||
|
_r <<= 1;
|
||||||
|
_r |= (n & b) ? 1 : 0;
|
||||||
|
|
||||||
|
if (_r >= d) {
|
||||||
|
_r -= d;
|
||||||
|
_q |= b;
|
||||||
|
}
|
||||||
|
|
||||||
|
b >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*q = _q;
|
||||||
|
*r = _r;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void print_decimal(put_char_func pc, uint32_t u, int negative,
|
||||||
|
unsigned int base, int width, int lcase, char wchr)
|
||||||
|
{
|
||||||
|
if (base > 16) base = 16;
|
||||||
|
if (base < 2) base = 2;
|
||||||
|
if (lcase != 0) lcase = 1;
|
||||||
|
|
||||||
|
char s[66];
|
||||||
|
int si = 64;
|
||||||
|
uint32_t l;
|
||||||
|
|
||||||
|
s[si--] = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// div(u, base, &u, &l);
|
||||||
|
l = u % base;
|
||||||
|
u = u / base;
|
||||||
|
s[si--] = abet[lcase][l];
|
||||||
|
} while (u > 0);
|
||||||
|
|
||||||
|
if (negative) {
|
||||||
|
if (wchr == '0') {
|
||||||
|
pc('-');
|
||||||
|
|
||||||
|
if (width > 0)
|
||||||
|
width--;
|
||||||
|
else if (width < 0)
|
||||||
|
width++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
s[si--] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
si++;
|
||||||
|
|
||||||
|
print_string(pc, s + si, width, wchr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_unsigned(put_char_func pc, uint32_t s, unsigned int base,
|
||||||
|
int width, int lcase, char wchr)
|
||||||
|
{
|
||||||
|
print_decimal(pc, s, 0, base, width, lcase, wchr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_signed(put_char_func pc, int64_t s, unsigned int base,
|
||||||
|
int width, int lcase, char wchr)
|
||||||
|
{
|
||||||
|
if (s < 0)
|
||||||
|
print_decimal(pc, (uint32_t)-s, 1, base, width, lcase, wchr);
|
||||||
|
else
|
||||||
|
print_decimal(pc, (uint32_t)s, 0, base, width, lcase, wchr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_isdigit(char c)
|
||||||
|
{
|
||||||
|
return (c >= '0' && c <= '9') ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Like a vprintf */
|
||||||
|
void pv(put_char_func pc, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
/* Initialization for supress gcc warnings */
|
||||||
|
int width = 0, wsign = 1, lng = 0, sgn = 0, ab = 0;
|
||||||
|
|
||||||
|
/* Width adjustment character */
|
||||||
|
char wchr = 0;
|
||||||
|
|
||||||
|
unsigned int base = 0;
|
||||||
|
char c;
|
||||||
|
int64_t d;
|
||||||
|
|
||||||
|
pstate st = PS_REGULAR;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
c = *fmt;
|
||||||
|
|
||||||
|
if (c == 0) break;
|
||||||
|
|
||||||
|
switch (st) {
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
case PS_REGULAR:
|
||||||
|
fmt++;
|
||||||
|
|
||||||
|
if (c == '%') {
|
||||||
|
st = PS_JSYM_SPEC;
|
||||||
|
lng = 0;
|
||||||
|
sgn = 1;
|
||||||
|
width = 0;
|
||||||
|
wsign = 1;
|
||||||
|
base = 10;
|
||||||
|
ab = 0;
|
||||||
|
wchr = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pc(c);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
case PS_JSYM_SPEC:
|
||||||
|
if (c == '\'') {
|
||||||
|
fmt++;
|
||||||
|
st = PS_JSYM;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
st = PS_WIDTH_SIGN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
case PS_JSYM:
|
||||||
|
fmt++;
|
||||||
|
wchr = c;
|
||||||
|
|
||||||
|
st = PS_WIDTH_SIGN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
case PS_WIDTH_SIGN:
|
||||||
|
if (c == '-') {
|
||||||
|
fmt++;
|
||||||
|
wsign = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = PS_WIDTH_ARG;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
case PS_WIDTH_ARG:
|
||||||
|
if (c == '*') {
|
||||||
|
fmt++;
|
||||||
|
width = va_arg(ap, int);
|
||||||
|
st = PS_TYPE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
st = PS_WIDTH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
case PS_WIDTH:
|
||||||
|
if (l_isdigit(c)) {
|
||||||
|
fmt++;
|
||||||
|
width = width * 10 + (c - '0');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
st = PS_TYPE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
case PS_TYPE:
|
||||||
|
fmt++;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'l':
|
||||||
|
lng = 1;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case 'u':
|
||||||
|
sgn = 0;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
case 'i':
|
||||||
|
case 'b':
|
||||||
|
case 'o':
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
if ((lng) && (sgn))
|
||||||
|
d = (int64_t)va_arg(ap, long long);
|
||||||
|
else if ((lng) && (!sgn))
|
||||||
|
d = (int64_t)va_arg(ap, unsigned long long);
|
||||||
|
else if ((!lng) && (sgn))
|
||||||
|
d = (int64_t)va_arg(ap, int);
|
||||||
|
else
|
||||||
|
d = (int64_t)va_arg(ap, unsigned int);
|
||||||
|
|
||||||
|
ab = 0;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'd':
|
||||||
|
case 'i':
|
||||||
|
base = 10;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
base = 2;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
base = 8;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
ab = 1;
|
||||||
|
case 'X':
|
||||||
|
base = 16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wchr) {
|
||||||
|
wchr = '0';
|
||||||
|
wsign = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sgn)
|
||||||
|
print_signed(pc, d, base, (wsign > 0) ? width : -width, ab,
|
||||||
|
wchr);
|
||||||
|
else
|
||||||
|
print_unsigned(pc, (uint32_t)d, base,
|
||||||
|
(wsign > 0) ? width : -width, ab, wchr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
pc((char)va_arg(ap, int));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
if (!wchr) wchr = ' ';
|
||||||
|
print_string(pc, va_arg(ap, char *),
|
||||||
|
(wsign > 0) ? width : -width, wchr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '%':
|
||||||
|
pc('%');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pc('%');
|
||||||
|
pc(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
st = PS_REGULAR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
default:
|
||||||
|
st = PS_REGULAR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Universal printf */
|
||||||
|
void pp(put_char_func pc, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
pv(pc, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print to console */
|
||||||
|
void p(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
pv(put_char, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print to string */
|
||||||
|
int psn(char *str, int size, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
/* Nested function. GCC specific */
|
||||||
|
void put_char_str(char c)
|
||||||
|
{
|
||||||
|
if (n < size) {
|
||||||
|
*str++ = c;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pv(put_char_str, fmt, ap);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
15
source/firmware/uprintf.h
Normal file
15
source/firmware/uprintf.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef _UPRINTF
|
||||||
|
#define _UPRINTF
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
typedef void (*put_char_func)(char c);
|
||||||
|
extern void put_char(char c);
|
||||||
|
|
||||||
|
void pv(put_char_func pc, const char *fmt, va_list ap);
|
||||||
|
void pp(put_char_func pc, const char *fmt, ...);
|
||||||
|
|
||||||
|
void p(const char *fmt, ...);
|
||||||
|
int psn(char *str, int size, const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif /* _UPRINTF */
|
||||||
8
source/generate.sh
Executable file
8
source/generate.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
../scripts/register-gen.scm io.reg > io_reg.v
|
||||||
|
../scripts/register-gen.scm -c io.reg > io_reg.h
|
||||||
|
../scripts/register-gen.scm -t io.reg > io_reg.txt
|
||||||
|
../scripts/picorv32-bus-mux-gen.scm -s 0+0x10000 -s 0x01000000+0x1000 -m bus_mux > bus_mux.v
|
||||||
|
../scripts/picorv32-bus-mux-gen.scm -s 0+0x10000 -s 0x01000000+0x1000 -m bus_mux -f > bus_mux.sby
|
||||||
16
source/io.reg
Normal file
16
source/io.reg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
;; -*- lisp -*-
|
||||||
|
|
||||||
|
((base #x01000000)
|
||||||
|
(address-width 32)
|
||||||
|
(data-width 32)
|
||||||
|
byte-enable
|
||||||
|
|
||||||
|
(reg "ctrl"
|
||||||
|
(info "Control register")
|
||||||
|
(bits 1 "stop" w (reset #b0)))
|
||||||
|
|
||||||
|
(reg "console" read-notify
|
||||||
|
(info "Virtual console port")
|
||||||
|
(bits 8 "data" rw (info "Read/write char from/to console"))
|
||||||
|
(bits 1 "send" hs (info "Write 1 to send symbol"))
|
||||||
|
(bits 1 "valid" r (info "Symbol in DATA is valid"))))
|
||||||
17
source/io_reg.h
Normal file
17
source/io_reg.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef _IO_REG_H_
|
||||||
|
#define _IO_REG_H_
|
||||||
|
|
||||||
|
#define IO_REG_BASE 0x1000000
|
||||||
|
|
||||||
|
/* -- Register 'CTRL' -- */
|
||||||
|
#define IO_REG_CTRL (*(volatile uint32_t*)(IO_REG_BASE + 0x00000000))
|
||||||
|
#define IO_REG_CTRL_STOP (1 << 0)
|
||||||
|
|
||||||
|
/* -- Register 'CONSOLE' -- */
|
||||||
|
#define IO_REG_CONSOLE (*(volatile uint32_t*)(IO_REG_BASE + 0x00000004))
|
||||||
|
#define IO_REG_CONSOLE_DATA__MASK 0x000000ff
|
||||||
|
#define IO_REG_CONSOLE_DATA__SHIFT 0
|
||||||
|
#define IO_REG_CONSOLE_SEND (1 << 8)
|
||||||
|
#define IO_REG_CONSOLE_VALID (1 << 9)
|
||||||
|
|
||||||
|
#endif // _IO_REG_H_
|
||||||
30
source/io_reg.txt
Normal file
30
source/io_reg.txt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
Register map of IO_REG (base: 0x1000000)
|
||||||
|
========================================
|
||||||
|
|
||||||
|
| Offset | Name | Description |
|
||||||
|
|------------+---------+----------------------|
|
||||||
|
| 0x00000000 | CTRL | Control register |
|
||||||
|
| 0x00000004 | CONSOLE | Virtual console port |
|
||||||
|
|
||||||
|
|
||||||
|
CTRL Register (0x00000000)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Control register
|
||||||
|
|
||||||
|
| Bits | Name | Mode | Reset | Description |
|
||||||
|
|------+------+------+-------+-------------|
|
||||||
|
| 0 | STOP | WO | 0 | |
|
||||||
|
|
||||||
|
|
||||||
|
CONSOLE Register (0x00000004)
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Virtual console port
|
||||||
|
|
||||||
|
| Bits | Name | Mode | Reset | Description |
|
||||||
|
|------+-------+------+-------+---------------------------------|
|
||||||
|
| 9 | VALID | RO | 0 | Symbol in DATA is valid |
|
||||||
|
| 8 | SEND | HS | 0 | Write 1 to send symbol |
|
||||||
|
| 7:0 | DATA | RW | 0 | Read/write char from/to console |
|
||||||
|
|
||||||
98
source/io_reg.v
Normal file
98
source/io_reg.v
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// This file is auto-generated. Do not edit
|
||||||
|
|
||||||
|
module io_reg
|
||||||
|
(input wire clock,
|
||||||
|
input wire reset,
|
||||||
|
|
||||||
|
/* ---- Access bus ---- */
|
||||||
|
/* verilator lint_off UNUSED */
|
||||||
|
input wire [31:0] i_addr,
|
||||||
|
input wire [31:0] i_data,
|
||||||
|
output wire [31:0] o_data,
|
||||||
|
input wire [3:0] i_ben,
|
||||||
|
input wire i_write,
|
||||||
|
input wire i_read,
|
||||||
|
/* verilator lint_on UNUSED */
|
||||||
|
|
||||||
|
/* ---- 'ctrl' ---- */
|
||||||
|
output wire o_ctrl_stop,
|
||||||
|
|
||||||
|
/* ---- 'console' ---- */
|
||||||
|
output wire o_console__rnotify,
|
||||||
|
input wire [7:0] i_console_data,
|
||||||
|
output wire [7:0] o_console_data,
|
||||||
|
output wire o_console_send_hsreq,
|
||||||
|
input wire i_console_send_hsack,
|
||||||
|
input wire i_console_send,
|
||||||
|
input wire i_console_valid);
|
||||||
|
|
||||||
|
/* ---- Address decoder ---- */
|
||||||
|
wire ctrl_select;
|
||||||
|
wire console_select;
|
||||||
|
|
||||||
|
assign ctrl_select =
|
||||||
|
i_addr[2] == 1'b0;
|
||||||
|
|
||||||
|
assign console_select =
|
||||||
|
i_addr[2] == 1'b1;
|
||||||
|
|
||||||
|
|
||||||
|
/* ---- 'ctrl' ---- */
|
||||||
|
reg ctrl_stop;
|
||||||
|
assign o_ctrl_stop = ctrl_stop;
|
||||||
|
|
||||||
|
always @(posedge clock)
|
||||||
|
if (reset)
|
||||||
|
ctrl_stop <= 1'b0;
|
||||||
|
else
|
||||||
|
if (ctrl_select && i_write) begin
|
||||||
|
if (i_ben[0]) ctrl_stop <= i_data[0];
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
/* ---- 'console' ---- */
|
||||||
|
reg [7:0] console_data;
|
||||||
|
assign o_console_data = console_data;
|
||||||
|
|
||||||
|
always @(posedge clock)
|
||||||
|
if (reset)
|
||||||
|
console_data <= 8'b0;
|
||||||
|
else
|
||||||
|
if (console_select && i_write) begin
|
||||||
|
if (i_ben[0]) console_data[7:0] <= i_data[7:0];
|
||||||
|
end
|
||||||
|
|
||||||
|
reg console_send_hsreq;
|
||||||
|
assign o_console_send_hsreq = console_send_hsreq;
|
||||||
|
|
||||||
|
always @(posedge clock)
|
||||||
|
if (reset)
|
||||||
|
console_send_hsreq <= 1'b0;
|
||||||
|
else begin
|
||||||
|
if (console_select && i_write && i_ben[1]) console_send_hsreq <= i_data[8];
|
||||||
|
else console_send_hsreq <= console_send_hsreq & (~i_console_send_hsack);
|
||||||
|
end
|
||||||
|
|
||||||
|
assign o_console__rnotify = console_select & i_read;
|
||||||
|
|
||||||
|
/* ---- Read multiplexer ---- */
|
||||||
|
reg [31:0] data_ctrl;
|
||||||
|
reg [31:0] data_console;
|
||||||
|
|
||||||
|
assign o_data =
|
||||||
|
data_ctrl |
|
||||||
|
data_console;
|
||||||
|
|
||||||
|
always @(*) begin
|
||||||
|
data_ctrl = 32'd0;
|
||||||
|
data_console = 32'd0;
|
||||||
|
|
||||||
|
if (console_select) begin
|
||||||
|
data_console[7:0] = i_console_data;
|
||||||
|
data_console[8] = i_console_send;
|
||||||
|
data_console[9] = i_console_valid;
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule // io_reg
|
||||||
3044
source/picorv32.v
Normal file
3044
source/picorv32.v
Normal file
File diff suppressed because it is too large
Load Diff
179
source/picorv32_tcm.sv
Normal file
179
source/picorv32_tcm.sv
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
`timescale 1ps/1ps
|
||||||
|
|
||||||
|
module picorv32_tcm #(parameter ADDR_WIDTH = 8,
|
||||||
|
parameter USE_LOOK_AHEAD = 0,
|
||||||
|
parameter USE_ADDR_MUX = 1,
|
||||||
|
parameter MEM_INIT_FILE = "")
|
||||||
|
(input wire clock,
|
||||||
|
/* verilator lint_off UNUSED */
|
||||||
|
// Not used in look-ahead mode
|
||||||
|
input wire reset,
|
||||||
|
/* verilator lint_on UNUSED */
|
||||||
|
|
||||||
|
/* PicoRV32 bus interface */
|
||||||
|
input wire mem_valid,
|
||||||
|
output wire mem_ready,
|
||||||
|
input wire [ADDR_WIDTH-1:0] mem_addr,
|
||||||
|
input wire [31:0] mem_wdata,
|
||||||
|
input wire [3:0] mem_wstrb,
|
||||||
|
output reg [31:0] mem_rdata,
|
||||||
|
|
||||||
|
// PicoRV32 look-ahead address.
|
||||||
|
// Not used in non-look-ahead mode.
|
||||||
|
/* verilator lint_off UNUSED */
|
||||||
|
input wire [ADDR_WIDTH-1:0] mem_la_addr
|
||||||
|
/* verilator lint_off UNUSED */
|
||||||
|
);
|
||||||
|
|
||||||
|
logic [31:0] ram[0:(2 ** (ADDR_WIDTH-2))-1];
|
||||||
|
if (MEM_INIT_FILE != "")
|
||||||
|
initial $readmemh(MEM_INIT_FILE, ram);
|
||||||
|
|
||||||
|
/* verilator lint_off UNUSED */
|
||||||
|
// Bits [1:0] are not used
|
||||||
|
logic [ADDR_WIDTH-1:0] byte_addr;
|
||||||
|
/* verilator lint_on UNUSED */
|
||||||
|
logic [ADDR_WIDTH-3:0] word_addr;
|
||||||
|
logic write;
|
||||||
|
|
||||||
|
assign word_addr = byte_addr[ADDR_WIDTH-1:2];
|
||||||
|
|
||||||
|
always_ff @(posedge clock) begin
|
||||||
|
for (int n = 0; n < 4; n += 1)
|
||||||
|
if (write && mem_wstrb[n])
|
||||||
|
ram[word_addr][n*8 +: 8] <= mem_wdata[n*8 +: 8];
|
||||||
|
|
||||||
|
mem_rdata <= ram[word_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
if (USE_LOOK_AHEAD == 0) begin : no_look_ahead
|
||||||
|
logic data_ready;
|
||||||
|
|
||||||
|
always_ff @(posedge clock)
|
||||||
|
if (reset) data_ready <= 1'b0;
|
||||||
|
else
|
||||||
|
// Don't use ternary operator to prevent
|
||||||
|
// X-propagation from PicoRV32 core
|
||||||
|
// data_ready <= mem_valid & ~(|mem_wstrb);
|
||||||
|
if (mem_valid && mem_wstrb == '0)
|
||||||
|
data_ready <= 1'b1;
|
||||||
|
else
|
||||||
|
data_ready <= 1'b0;
|
||||||
|
|
||||||
|
|
||||||
|
assign byte_addr = mem_addr;
|
||||||
|
assign write = mem_valid & (|mem_wstrb);
|
||||||
|
assign mem_ready = data_ready | write;
|
||||||
|
end
|
||||||
|
else begin : look_ahead
|
||||||
|
logic data_ready;
|
||||||
|
|
||||||
|
always_ff @(posedge clock)
|
||||||
|
if (reset) data_ready <= 1'b0;
|
||||||
|
else
|
||||||
|
// Don't use ternary operator to prevent
|
||||||
|
// X-propagation from PicoRV32 core
|
||||||
|
// data_ready <= ~(mem_valid && (|mem_wstrb));
|
||||||
|
if (mem_valid && mem_wstrb != '0)
|
||||||
|
data_ready <= 1'b0;
|
||||||
|
else
|
||||||
|
data_ready <= 1'b1;
|
||||||
|
|
||||||
|
/* mem_la_addr валиден как минимум один такт после поднятия mem_valid.
|
||||||
|
* Т.е. в принципе можно обойтись без мультиплескора. В формальной части
|
||||||
|
* добавлено соответствуюшее утверждение. */
|
||||||
|
if (USE_ADDR_MUX == 0)
|
||||||
|
assign byte_addr = mem_la_addr[ADDR_WIDTH-1:0];
|
||||||
|
else
|
||||||
|
assign byte_addr = mem_valid ?
|
||||||
|
mem_addr[ADDR_WIDTH-1:0] :
|
||||||
|
mem_la_addr[ADDR_WIDTH-1:0];
|
||||||
|
|
||||||
|
assign write = mem_valid & (|mem_wstrb);
|
||||||
|
assign mem_ready = data_ready;
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef FORMAL
|
||||||
|
// Past valid flag
|
||||||
|
logic have_past = 1'b0;
|
||||||
|
always_ff @(posedge clock) have_past <= 1'b1;
|
||||||
|
|
||||||
|
// Assumptions
|
||||||
|
always @(*) assume(reset == 1'b0);
|
||||||
|
|
||||||
|
always @(posedge clock)
|
||||||
|
if (have_past) begin
|
||||||
|
// mem_addr <= mem_la_addr
|
||||||
|
if ($rose(mem_valid))
|
||||||
|
assume(mem_addr == $past(mem_la_addr));
|
||||||
|
|
||||||
|
// Stable mem_addr and mem_data when mem_valid is active
|
||||||
|
if (mem_valid) begin
|
||||||
|
assume($stable(mem_addr));
|
||||||
|
assume($stable(mem_wdata));
|
||||||
|
assume($stable(mem_wstrb));
|
||||||
|
end
|
||||||
|
|
||||||
|
// Assume mem_valid will not cleared while mem_ready is not active
|
||||||
|
if ($past(mem_valid) && !$past(mem_ready))
|
||||||
|
assume(mem_valid == 1'b1);
|
||||||
|
|
||||||
|
// Assume mem_valid will cleared after memory transaction complete
|
||||||
|
if ($past(mem_valid) && $past(mem_ready))
|
||||||
|
assume(mem_valid == 1'b0);
|
||||||
|
|
||||||
|
// WARN: May be wrong assumption
|
||||||
|
// Assume mem_add == mem_la_addr on first clock cycle of mem_valid activity
|
||||||
|
if ($rose(mem_valid))
|
||||||
|
assume(mem_addr == mem_la_addr);
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
// Initial mem_valid = 1'b0
|
||||||
|
assume(mem_valid == 1'b0);
|
||||||
|
end
|
||||||
|
|
||||||
|
// Data read
|
||||||
|
always_ff @(posedge clock)
|
||||||
|
if (have_past)
|
||||||
|
if (mem_valid && mem_ready && mem_wstrb == '0)
|
||||||
|
assert(mem_rdata == ram[mem_addr[ADDR_WIDTH-1:2]]);
|
||||||
|
|
||||||
|
// Data write
|
||||||
|
always_ff @(posedge clock) begin
|
||||||
|
logic [3:0] mem_wstrb_past;
|
||||||
|
|
||||||
|
if (have_past) begin
|
||||||
|
mem_wstrb_past = $past(mem_wstrb);
|
||||||
|
|
||||||
|
if ($past(mem_valid) && $past(mem_ready) && mem_wstrb_past != '0)
|
||||||
|
for (int n = 0; n < 4; n += 1)
|
||||||
|
if (mem_wstrb_past[n])
|
||||||
|
assert(ram[$past(mem_addr[ADDR_WIDTH-1:2])][n*8 +: 8] == $past(mem_wdata[n*8 +: 8]));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Mem ready
|
||||||
|
always_ff @(posedge clock)
|
||||||
|
if (have_past)
|
||||||
|
if (USE_LOOK_AHEAD == 0) begin
|
||||||
|
// Write transaction
|
||||||
|
if (mem_wstrb != '0 && mem_valid)
|
||||||
|
assert(mem_ready);
|
||||||
|
|
||||||
|
// First cycle of read transaction
|
||||||
|
if (mem_wstrb == '0 && !$past(mem_valid)&& mem_valid)
|
||||||
|
assert(!mem_ready);
|
||||||
|
|
||||||
|
// Second cycle of read transaction
|
||||||
|
if ($past(mem_wstrb) == '0 && $past(mem_valid) && mem_valid)
|
||||||
|
assert(mem_ready);
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
// In look-ahead mode mem_ready always active
|
||||||
|
if (mem_valid)
|
||||||
|
assert(mem_ready);
|
||||||
|
end
|
||||||
|
|
||||||
|
`endif
|
||||||
|
|
||||||
|
endmodule // picorv32_tcm
|
||||||
5
source/sources.f
Normal file
5
source/sources.f
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
../source/bus_mux.v
|
||||||
|
../source/io_reg.v
|
||||||
|
../source/picorv32_tcm.sv
|
||||||
|
../source/picorv32.v
|
||||||
|
../source/testbench.sv
|
||||||
211
source/testbench.sv
Normal file
211
source/testbench.sv
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
`timescale 1ps/1ps
|
||||||
|
|
||||||
|
module testbench (input clock);
|
||||||
|
parameter MEM_ADDR_WIDTH = 16;
|
||||||
|
|
||||||
|
logic reset = 1'b1;
|
||||||
|
|
||||||
|
/* verilator lint_off UNUSED */
|
||||||
|
logic cpu_mem_valid;
|
||||||
|
logic cpu_mem_instr;
|
||||||
|
logic cpu_mem_ready;
|
||||||
|
logic [31:0] cpu_mem_addr;
|
||||||
|
logic [31:0] cpu_mem_wdata;
|
||||||
|
logic [ 3:0] cpu_mem_wstrb;
|
||||||
|
logic [31:0] cpu_mem_rdata;
|
||||||
|
|
||||||
|
// Look-Ahead Interface
|
||||||
|
logic cpu_mem_la_read;
|
||||||
|
logic cpu_mem_la_write;
|
||||||
|
logic [31:0] cpu_mem_la_addr;
|
||||||
|
logic [31:0] cpu_mem_la_wdata;
|
||||||
|
logic [ 3:0] cpu_mem_la_wstrb;
|
||||||
|
/* verilator lint_on UNUSED */
|
||||||
|
|
||||||
|
// PicoRV32 // Defaults
|
||||||
|
picorv32 #(.ENABLE_COUNTERS(0), // = 1,
|
||||||
|
.ENABLE_COUNTERS64(0), // = 1,
|
||||||
|
.ENABLE_REGS_16_31(1), // = 1,
|
||||||
|
.ENABLE_REGS_DUALPORT(1), // = 1,
|
||||||
|
.LATCHED_MEM_RDATA(0), // = 0,
|
||||||
|
.TWO_STAGE_SHIFT(1), // = 1,
|
||||||
|
.BARREL_SHIFTER(0), // = 0,
|
||||||
|
.TWO_CYCLE_COMPARE(0), // = 0,
|
||||||
|
.TWO_CYCLE_ALU(0), // = 0,
|
||||||
|
.COMPRESSED_ISA(0), // = 0,
|
||||||
|
.CATCH_MISALIGN(1), // = 1,
|
||||||
|
.CATCH_ILLINSN(1), // = 1,
|
||||||
|
.ENABLE_PCPI(0), // = 0,
|
||||||
|
.ENABLE_MUL(0), // = 0,
|
||||||
|
.ENABLE_FAST_MUL(0), // = 0,
|
||||||
|
.ENABLE_DIV(0), // = 0,
|
||||||
|
.ENABLE_IRQ(0), // = 0,
|
||||||
|
.ENABLE_IRQ_QREGS(0), // = 1,
|
||||||
|
.ENABLE_IRQ_TIMER(0), // = 1,
|
||||||
|
.ENABLE_TRACE(0), // = 0,
|
||||||
|
.REGS_INIT_ZERO(0), // = 0,
|
||||||
|
.MASKED_IRQ(32'h 0000_0000), // = 32'h 0000_0000,
|
||||||
|
.LATCHED_IRQ(32'h ffff_ffff), // = 32'h ffff_ffff,
|
||||||
|
.PROGADDR_RESET(32'h 0000_0000), // = 32'h 0000_0000,
|
||||||
|
.PROGADDR_IRQ(32'h 0000_0010), // = 32'h 0000_0010,
|
||||||
|
.STACKADDR(32'h ffff_ffff)) // = 32'h ffff_ffff
|
||||||
|
picorv32
|
||||||
|
(.clk(clock),
|
||||||
|
.resetn(~reset),
|
||||||
|
|
||||||
|
.mem_valid(cpu_mem_valid), // output reg
|
||||||
|
.mem_instr(cpu_mem_instr), // output reg
|
||||||
|
.mem_ready(cpu_mem_ready), // input
|
||||||
|
.mem_addr(cpu_mem_addr), // output reg [31:0]
|
||||||
|
.mem_wdata(cpu_mem_wdata), // output reg [31:0]
|
||||||
|
.mem_wstrb(cpu_mem_wstrb), // output reg [ 3:0]
|
||||||
|
.mem_rdata(cpu_mem_rdata), // input [31:0]
|
||||||
|
|
||||||
|
// Look-Ahead Interface
|
||||||
|
.mem_la_read(cpu_mem_la_read), // output
|
||||||
|
.mem_la_write(cpu_mem_la_write), // output
|
||||||
|
.mem_la_addr(cpu_mem_la_addr), // output [31:0]
|
||||||
|
.mem_la_wdata(cpu_mem_la_wdata), // output reg [31:0]
|
||||||
|
.mem_la_wstrb(cpu_mem_la_wstrb), // output reg [ 3:0]
|
||||||
|
|
||||||
|
// Unused
|
||||||
|
/* verilator lint_off PINCONNECTEMPTY */
|
||||||
|
.pcpi_valid(), // output reg
|
||||||
|
.pcpi_insn(), // output reg [31:0]
|
||||||
|
.pcpi_rs1(), // output [31:0]
|
||||||
|
.pcpi_rs2(), // output [31:0]
|
||||||
|
.pcpi_wr(1'b0), // input
|
||||||
|
.pcpi_rd(32'd0), // input [31:0]
|
||||||
|
.pcpi_wait(1'b0), // input
|
||||||
|
.pcpi_ready(1'b0), // input
|
||||||
|
.irq(32'd0), // input [31:0]
|
||||||
|
.eoi(), // output reg [31:0]
|
||||||
|
.trap(), // output reg
|
||||||
|
.trace_valid(), // output reg
|
||||||
|
.trace_data() // output reg [35:0]
|
||||||
|
/* verilator lint_on PINCONNECTEMPTY */
|
||||||
|
);
|
||||||
|
|
||||||
|
// -- Bus multiplexer
|
||||||
|
// Slaves address ranges:
|
||||||
|
// 0 - 0x00000000-0x0000ffff
|
||||||
|
// 1 - 0x01000000-0x01000fff
|
||||||
|
|
||||||
|
// i_slave_rdata bits:
|
||||||
|
// 0: i_slave_rdata[31:0]
|
||||||
|
// 1: i_slave_rdata[63:32]
|
||||||
|
|
||||||
|
logic [31:0] rdata_ram;
|
||||||
|
logic [31:0] rdata_reg;
|
||||||
|
logic valid_ram;
|
||||||
|
logic ready_ram;
|
||||||
|
logic valid_reg;
|
||||||
|
logic ready_reg;
|
||||||
|
|
||||||
|
bus_mux bus_mux
|
||||||
|
(.clock, .reset,
|
||||||
|
// CPU
|
||||||
|
.i_la_addr(cpu_mem_la_addr),
|
||||||
|
.o_rdata(cpu_mem_rdata),
|
||||||
|
.i_valid(cpu_mem_valid),
|
||||||
|
.o_ready(cpu_mem_ready),
|
||||||
|
// Slaves
|
||||||
|
.i_slave_rdata({rdata_reg, rdata_ram}),
|
||||||
|
.o_slave_valid({valid_reg, valid_ram}),
|
||||||
|
.i_slave_ready({ready_reg, ready_ram}));
|
||||||
|
|
||||||
|
// -- CPU memory
|
||||||
|
picorv32_tcm #(.ADDR_WIDTH(MEM_ADDR_WIDTH),
|
||||||
|
.USE_LOOK_AHEAD(1),
|
||||||
|
.USE_ADDR_MUX(0),
|
||||||
|
.MEM_INIT_FILE("../source/firmware/fw.mem"))
|
||||||
|
picorv32_tcm
|
||||||
|
(.clock, .reset,
|
||||||
|
|
||||||
|
/* PicoRV32 bus interface */
|
||||||
|
.mem_valid(valid_ram),
|
||||||
|
.mem_ready(ready_ram),
|
||||||
|
.mem_addr(cpu_mem_addr[MEM_ADDR_WIDTH-1:0]),
|
||||||
|
.mem_wdata(cpu_mem_wdata),
|
||||||
|
.mem_wstrb(cpu_mem_wstrb),
|
||||||
|
.mem_rdata(rdata_ram),
|
||||||
|
.mem_la_addr(cpu_mem_la_addr[MEM_ADDR_WIDTH-1:0]));
|
||||||
|
|
||||||
|
// -- Registers
|
||||||
|
// Reg 'ctrl'
|
||||||
|
logic ctrl_stop;
|
||||||
|
|
||||||
|
// Reg 'console'
|
||||||
|
logic [7:0] i_console_data;
|
||||||
|
logic [7:0] o_console_data;
|
||||||
|
logic console_send;
|
||||||
|
|
||||||
|
logic reg_write;
|
||||||
|
logic reg_read;
|
||||||
|
|
||||||
|
assign ready_reg = 1'b1;
|
||||||
|
assign reg_write = valid_reg & |(cpu_mem_wdata);
|
||||||
|
assign reg_read = valid_reg & &(~cpu_mem_wdata);
|
||||||
|
assign i_console_data = 8'ha5;
|
||||||
|
|
||||||
|
io_reg io_reg
|
||||||
|
(.clock, .reset,
|
||||||
|
|
||||||
|
// CPU
|
||||||
|
.i_addr({16'd0, cpu_mem_addr[15:0]}),
|
||||||
|
.i_data(cpu_mem_wdata),
|
||||||
|
.o_data(rdata_reg),
|
||||||
|
.i_ben(cpu_mem_wstrb),
|
||||||
|
.i_write(reg_write),
|
||||||
|
.i_read(reg_read),
|
||||||
|
|
||||||
|
// Reg 'ctrl'
|
||||||
|
.o_ctrl_stop(ctrl_stop),
|
||||||
|
|
||||||
|
// Reg 'console'
|
||||||
|
.i_console_data(i_console_data),
|
||||||
|
.o_console_data(o_console_data),
|
||||||
|
.o_console_send_hsreq(console_send),
|
||||||
|
|
||||||
|
// Unused
|
||||||
|
/* verilator lint_off PINCONNECTEMPTY */
|
||||||
|
.o_console__rnotify(),
|
||||||
|
.i_console_send_hsack(1'b1),
|
||||||
|
.i_console_send(1'b0),
|
||||||
|
.i_console_valid(1'b1)
|
||||||
|
/* verilator lint_on PINCONNECTEMPTY */
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
localparam RESET_DURATION = 5;
|
||||||
|
int reset_counter = RESET_DURATION;
|
||||||
|
|
||||||
|
always_ff @(posedge clock)
|
||||||
|
if (reset_counter == 0)
|
||||||
|
reset <= 1'b0;
|
||||||
|
else
|
||||||
|
reset_counter <= reset_counter - 1;
|
||||||
|
|
||||||
|
// Print console output
|
||||||
|
// always_ff @(posedge clock)
|
||||||
|
// if (!reset && console_send)
|
||||||
|
// $write("%c", o_console_data);
|
||||||
|
|
||||||
|
initial
|
||||||
|
forever begin
|
||||||
|
@(posedge clock);
|
||||||
|
if (!reset && console_send) begin
|
||||||
|
$write("%c", o_console_data);
|
||||||
|
$fflush;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Wait for complete
|
||||||
|
initial begin
|
||||||
|
while (reset || ctrl_stop == 1'b0) @(posedge clock);
|
||||||
|
|
||||||
|
@(posedge clock);
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule // testbench
|
||||||
1
test-iverilog/.dir-locals.el
Normal file
1
test-iverilog/.dir-locals.el
Normal file
@ -0,0 +1 @@
|
|||||||
|
((verilog-mode . ((flycheck-verilator-include-path . ("../source")))))
|
||||||
1
test-iverilog/.gitignore
vendored
Normal file
1
test-iverilog/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
top
|
||||||
3
test-iverilog/__build.sh
Executable file
3
test-iverilog/__build.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
iverilog -g2012 -o top -f ../source/sources.f top.sv
|
||||||
3
test-iverilog/__run.sh
Executable file
3
test-iverilog/__run.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
vvp -n ./top
|
||||||
7
test-iverilog/top.sv
Normal file
7
test-iverilog/top.sv
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
`timescale 1ps/1ps
|
||||||
|
|
||||||
|
module top;
|
||||||
|
logic clock = 1'b0;
|
||||||
|
initial forever #(10ns/2) clock = ~clock;
|
||||||
|
testbench testbench (clock);
|
||||||
|
endmodule
|
||||||
1
test-modelsim/.dir-locals.el
Normal file
1
test-modelsim/.dir-locals.el
Normal file
@ -0,0 +1 @@
|
|||||||
|
((verilog-mode . ((flycheck-verilator-include-path . ("../source")))))
|
||||||
2
test-modelsim/.gitignore
vendored
Normal file
2
test-modelsim/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
testbench
|
||||||
|
transcript
|
||||||
5
test-modelsim/__build.sh
Executable file
5
test-modelsim/__build.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
rm -rf testbench
|
||||||
|
vlog -sv -work testbench -vopt -f ../source/sources.f top.sv
|
||||||
3
test-modelsim/__run.sh
Executable file
3
test-modelsim/__run.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
vsim -c -batch -voptargs=+acc=npr -do "run -all" -quiet -lib testbench top
|
||||||
7
test-modelsim/top.sv
Normal file
7
test-modelsim/top.sv
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
`timescale 1ps/1ps
|
||||||
|
|
||||||
|
module top;
|
||||||
|
logic clock = 1'b0;
|
||||||
|
initial forever #(10ns/2) clock = ~clock;
|
||||||
|
testbench testbench (clock);
|
||||||
|
endmodule
|
||||||
38
test-verilator/.clang-format
Normal file
38
test-verilator/.clang-format
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 4
|
||||||
|
UseTab: false
|
||||||
|
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
AlignConsecutiveMacros: true
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: Align
|
||||||
|
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowShortBlocksOnASingleLine: Empty
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||||
|
|
||||||
|
BinPackArguments: true
|
||||||
|
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterControlStatement: MultiLine
|
||||||
|
AfterFunction: true
|
||||||
|
AfterStruct: true
|
||||||
|
BeforeElse: true
|
||||||
|
BeforeWhile: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpacesBeforeTrailingComments: 4
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
2
test-verilator/.gitignore
vendored
Normal file
2
test-verilator/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
testbench
|
||||||
|
compile_flags.txt
|
||||||
17
test-verilator/Makefile
Normal file
17
test-verilator/Makefile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
TOP_MODULE = testbench
|
||||||
|
|
||||||
|
SOURCES = top.cpp clock_generator.cpp
|
||||||
|
FLAGS_FILE = ../source/sources.f
|
||||||
|
INCLUDES =
|
||||||
|
|
||||||
|
FLAGS = -Wno-WIDTH -cc --top-module $(TOP_MODULE) +1800-2017ext+sv \
|
||||||
|
--timing --Mdir $(TOP_MODULE) -o $(TOP_MODULE) -f $(FLAGS_FILE) \
|
||||||
|
--timescale "1ps/1ps" --threads 1
|
||||||
|
|
||||||
|
# FLAGS += --trace
|
||||||
|
|
||||||
|
all: $(SOURCES)
|
||||||
|
verilator $(FLAGS) --exe --build $(INCLUDES) $(SOURCES)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(TOP_MODULE)
|
||||||
5
test-verilator/__build.sh
Executable file
5
test-verilator/__build.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
make clean
|
||||||
|
make
|
||||||
3
test-verilator/__run.sh
Executable file
3
test-verilator/__run.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
./testbench/testbench
|
||||||
95
test-verilator/clock_generator.cpp
Normal file
95
test-verilator/clock_generator.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include "clock_generator.hpp"
|
||||||
|
|
||||||
|
ClockGenerator::~ClockGenerator()
|
||||||
|
{
|
||||||
|
while (clocks) {
|
||||||
|
clock *next = clocks->next;
|
||||||
|
delete clocks;
|
||||||
|
clocks = next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void ClockGenerator::add_clock(uint8_t &net, uint64_t period, uint64_t skew)
|
||||||
|
{
|
||||||
|
if (skew >= period) throw "The skew value cannot exceed the period";
|
||||||
|
|
||||||
|
clock *clk = new clock(net, period, skew);
|
||||||
|
net = (clk->position < clk->period / 2) ? 0 : 1;
|
||||||
|
|
||||||
|
if (clocks == NULL)
|
||||||
|
clocks = clk;
|
||||||
|
else {
|
||||||
|
clock *last = clocks;
|
||||||
|
while (last->next)
|
||||||
|
last = last->next;
|
||||||
|
last->next = clk;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
uint64_t ClockGenerator::next_event(void)
|
||||||
|
{
|
||||||
|
uint64_t time_to_next = UINT64_MAX;
|
||||||
|
clock *clk = clocks;
|
||||||
|
|
||||||
|
while (clk) {
|
||||||
|
uint64_t ttn;
|
||||||
|
|
||||||
|
if (clk->position < clk->period / 2)
|
||||||
|
ttn = clk->period / 2 - clk->position;
|
||||||
|
else
|
||||||
|
ttn = clk->period - clk->position;
|
||||||
|
|
||||||
|
if (time_to_next > ttn) time_to_next = ttn;
|
||||||
|
|
||||||
|
clk = clk->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
clk = clocks;
|
||||||
|
while (clk) {
|
||||||
|
uint8_t next_val;
|
||||||
|
|
||||||
|
clk->position += time_to_next;
|
||||||
|
if (clk->position >= clk->period) clk->position -= clk->period;
|
||||||
|
|
||||||
|
next_val = (clk->position < clk->period / 2) ? 0 : 1;
|
||||||
|
clk->posedge = (next_val == 1 && clk->net == 0) ? true : false;
|
||||||
|
clk->negedge = (next_val == 0 && clk->net == 1) ? true : false;
|
||||||
|
clk->net = next_val;
|
||||||
|
|
||||||
|
clk = clk->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return time_to_next;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ClockGenerator::is_posegde(uint8_t &net)
|
||||||
|
{
|
||||||
|
clock *clk = clocks;
|
||||||
|
bool posedge = false;
|
||||||
|
|
||||||
|
while (clk) {
|
||||||
|
if (std::addressof(net) == std::addressof(clk->net)) {
|
||||||
|
posedge = clk->posedge;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
clk = clk->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return posedge;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ClockGenerator::is_negedge(uint8_t &net)
|
||||||
|
{
|
||||||
|
clock *clk = clocks;
|
||||||
|
bool negedge = false;
|
||||||
|
|
||||||
|
while (clk) {
|
||||||
|
if (std::addressof(net) == std::addressof(clk->net)) {
|
||||||
|
negedge = clk->negedge;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
clk = clk->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return negedge;
|
||||||
|
};
|
||||||
31
test-verilator/clock_generator.hpp
Normal file
31
test-verilator/clock_generator.hpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef _CLOCK_GENERATOR_HPP
|
||||||
|
#define _CLOCK_GENERATOR_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class ClockGenerator
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
struct clock {
|
||||||
|
clock(uint8_t &net, uint64_t period, uint64_t position) :
|
||||||
|
net(net), period(period), position(position), next(NULL),
|
||||||
|
posedge(false), negedge(false) {};
|
||||||
|
|
||||||
|
uint8_t &net;
|
||||||
|
uint64_t period;
|
||||||
|
uint64_t position;
|
||||||
|
bool posedge;
|
||||||
|
bool negedge;
|
||||||
|
clock *next;
|
||||||
|
} *clocks = NULL;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~ClockGenerator();
|
||||||
|
void add_clock(uint8_t &net, uint64_t period, uint64_t skew);
|
||||||
|
uint64_t next_event(void);
|
||||||
|
bool is_posegde(uint8_t &net);
|
||||||
|
bool is_negedge(uint8_t &net);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _CLOCK_GENERATOR_HPP
|
||||||
19
test-verilator/shell.nix
Normal file
19
test-verilator/shell.nix
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
with pkgs;
|
||||||
|
let
|
||||||
|
flags-file = "compile_flags.txt";
|
||||||
|
in
|
||||||
|
mkShell {
|
||||||
|
packages = [ gnumake verilator ];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo -n > ${flags-file}
|
||||||
|
echo -DVM_TRACE=1 >> ${flags-file}
|
||||||
|
echo -xc++ >> ${flags-file}
|
||||||
|
echo -I./testbench >> ${flags-file}
|
||||||
|
echo -I${verilator}/share/verilator/include >> ${flags-file}
|
||||||
|
echo -I${clang}/resource-root/include >> ${flags-file}
|
||||||
|
echo -I${glibc.dev}/include >> ${flags-file}
|
||||||
|
'';
|
||||||
|
}
|
||||||
74
test-verilator/top.cpp
Normal file
74
test-verilator/top.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include "Vtestbench.h"
|
||||||
|
#include "clock_generator.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <verilated.h>
|
||||||
|
#include <verilated_vcd_c.h>
|
||||||
|
|
||||||
|
#define DUMPFILE "testbench.vcd"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
VerilatedContext *ctx = new VerilatedContext;
|
||||||
|
ctx->commandArgs(argc, argv);
|
||||||
|
|
||||||
|
/* Create model instance */
|
||||||
|
Vtestbench *top = new Vtestbench(ctx);
|
||||||
|
|
||||||
|
#if (VM_TRACE == 1)
|
||||||
|
VerilatedVcdC *vcd = new VerilatedVcdC;
|
||||||
|
ctx->traceEverOn(true);
|
||||||
|
top->trace(vcd, 99);
|
||||||
|
vcd->open(DUMPFILE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Create clock source */
|
||||||
|
ClockGenerator *clk = new ClockGenerator;
|
||||||
|
|
||||||
|
/* Add clocks and go to first event */
|
||||||
|
clk->add_clock(top->clock, 10000, 0);
|
||||||
|
clk->next_event();
|
||||||
|
|
||||||
|
/* Cycle counter */
|
||||||
|
uint64_t cycle = 0;
|
||||||
|
|
||||||
|
/* ---- Evaluation loop ---- */
|
||||||
|
while (!ctx->gotFinish()) {
|
||||||
|
/* Clock event */
|
||||||
|
ctx->timeInc(clk->next_event());
|
||||||
|
|
||||||
|
/* Get output values (before clock edge) */
|
||||||
|
if (clk->is_posegde(top->clock)) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Eval */
|
||||||
|
top->eval();
|
||||||
|
|
||||||
|
/* Put input values (after clock edge)*/
|
||||||
|
if (clk->is_posegde(top->clock)) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trace steady-state values */
|
||||||
|
#if (VM_TRACE == 1)
|
||||||
|
if (vcd) vcd->dump(ctx->time());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
cycle ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
top->final();
|
||||||
|
printf("[%lu] Stop simulation\n", ctx->time());
|
||||||
|
|
||||||
|
#if (VM_TRACE == 1)
|
||||||
|
if (vcd) {
|
||||||
|
vcd->close();
|
||||||
|
delete vcd;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
delete top;
|
||||||
|
delete ctx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user