Add sources

This commit is contained in:
Nikolay Puzanov
2023-06-11 16:15:40 +03:00
parent 82f90610fb
commit 686d12bf81
48 changed files with 23261 additions and 0 deletions

29
scripts/bin2initial.scm Executable file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff