Add sources
This commit is contained in:
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
Reference in New Issue
Block a user