3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-08-04 02:10:24 +00:00

Finish up functions and tests, TODO: CLI

This commit is contained in:
Gus Smith 2025-06-23 19:20:06 -07:00
parent 51560b0bf6
commit 8a9d724873

View file

@ -1,104 +1,157 @@
; Utilities for simulating Rosette programs. ; Utilities for simulating Rosette programs.
;
; Tests can be run with `raco test <this file>`.
#lang racket/base #lang racket/base
(provide simulate-rosette) (provide simulate-rosette)
(require (only-in rosette bv) (require (only-in rosette bv)
racket/match
racket/list) racket/list)
; Inputs: ; Inputs:
; - function: The function for the module to simulate. This should be a Rosette function generated by ; - function: The function for the module to simulate. This should be a Rosette function generated by
; Yosys's `write_fuctional_rosette` backend. ; Yosys's `write_fuctional_rosette` backend.
; - input-helper, output-helper: association-list-based helpers for input and output struct, generated
; by Yosys's `write_fuctional_rosette` backend with `-assoc-list-helpers` enabled.
; - initial-state: The initial state of the module, as generated by Yosys's `write_fuctional_rosette` ; - initial-state: The initial state of the module, as generated by Yosys's `write_fuctional_rosette`
; backend. ; backend.
(define (simulate-rosette #:function function ; - inputs: A list of association lists. The function will be called with each association list as
#:input-helper input-helper ; inputs, and the state will be threaded through each call.
#:output-helper output-helper ;
#:initial-state initial-state ; Outputs:
#:inputs inputs ; - A list of outputs, one for each cycle. The outputs are a list of the output objects generated by
#:outputs outputs) ; `function`.
(error "TODO: Implement simulate-rosette function")) (define (simulate-rosette #:function function #:initial-state initial-state #:inputs inputs)
(define outputs-and-states
(drop (reverse (foldl (lambda (input acc)
(let* ([outputs (function input (cdr (car acc)))]) (cons outputs acc)))
(list (cons 'unused initial-state))
inputs))
1))
(define outputs (map car outputs-and-states))
outputs)
; Inputs: ; Inputs:
; - config: Association list mapping input name (string) to a configuration value, which is one of the ; - inputs: association list mapping string name to bitwidth.
; following: ; - num-inputs: number of inputs to generate.
; - 'exhaustive: The input should be exhaustively tested. ; TODO(@gussmith23): If `num-inputs` is more than the number of possible values, just enumerate.
; - <integer>: The input should be tested with this many random inputs. When the input is not (define (generate-inputs #:inputs inputs #:num-inputs num-inputs)
; present in the config, it defaults to 'exhaustive. (define (generate-random-input inputs)
; - (map (lambda (pair) (cons (car pair) (bv (random (expt 2 (cdr pair))) (cdr pair)))) inputs))
(define (generate-inputs #:input-helper input-helper (for/list ([_ (range num-inputs)])
#:num-inputs num-inputs (generate-random-input inputs)))
#:config config
#:inputs inputs)
; Fill out missing vallues in the config with 'exhaustive.
(define config
(map (λ (input)
(let ([found (assoc (car input) config)]) (or found (cons (car input) 'exhaustive))))
inputs))
; ; Generate the inputs. ; Generates a clock signal for the given inputs.
; (define generated-inputs ;
; (map (λ (input) ; Given a string of inputs, one per clock cycle, this function generates a clock signal alongside the
; (let ([input-name (car input)] [input-bitwidth (cdr input)]) ; inputs. It does so by alternating the clock signal between 0 and 1 for each cycle, starting with 0.
; (cond ; For example, if the inputs are (list inputs1 inputs2 inputs3), the output will be (list (cons (cons
; [(equal? 'exhaustive (cdr (assoc input-name config))) (list input-name 'exhaustive)] ; "clk" (bv 0 1)) inputs1) (cons (cons "clk" (bv 1 1)) inputs1) (cons (cons "clk" (bv 0 1)) inputs2)
; [(number? (cdr (assoc input-name config))) ; (cons (cons "clk" (bv 1 1)) inputs2) ... ).
; (list input-name (make-random-input input-type (cdr (assoc input-name config))))] ;
; [else (error "Invalid configuration for input" input-name)]))) ; Inputs:
; inputs)) ; - clock-name: The name of the clock signal.
; - inputs: A list of inputs in association list form, as output by `generate-inputs`.
(error "TODO")) ;
; Outputs:
; Helper function: for a given input name, bitwidth, and configuration, generate a list of inputs. ; - A list of association lists, each containing a new clock signal. Will be twice the length of the
; Output: List of Rosette bitvector values for the input. ; inputs list.
(define (generate-inputs-for-one input-name bitwidth config) (define (generate-clock #:clock-name clock-name #:inputs inputs)
(cond (apply append
; If the configuration is 'exhaustive, or if they request a number of inputs that is greater than (for/list ([this-cycle-inputs inputs])
; or equal to the number of possible values for the bitwidth, generate all possible inputs. (list (cons (cons clock-name (bv 0 1)) this-cycle-inputs)
[(or (equal? config 'exhaustive) (and (number? config) (>= config (expt 2 bitwidth)))) (cons (cons clock-name (bv 1 1)) this-cycle-inputs)))))
(for/list ([n (range (expt 2 bitwidth))])
(bv n bitwidth))]
[(and (number? config) (positive? config))
(map (λ (_) (bv (random (expt 2 bitwidth)) bitwidth)) (range config))]
[else (error (format "Invalid configuration ~a for input ~a" config input-name))]))
; This is what gets executed when the script is run. ; This is what gets executed when the script is run.
(module main racket/base (module main racket/base
(require racket/cmdline)) (require racket/cmdline)
; - input-helper, output-helper: association-list-based helpers for input and output struct, generated
; by Yosys's `write_fuctional_rosette` backend with `-assoc-list-helpers` enabled.
)
(module+ test (module+ test
(require rackunit) (require rackunit
(test-case "generate-inputs-for-one" (only-in rosette bv bvadd))
(check-equal? (generate-inputs-for-one "input1" 4 'exhaustive) (test-case "generate-inputs"
(list (bv 0 4) (check-equal? (length (generate-inputs #:inputs (list (cons "input1" 4)) #:num-inputs 10)) 10)
(bv 1 4) ; Check that this call generates a list of one-length lists, each containing a single association
(bv 2 4) ; list with the key "input1" and a random value.
(bv 3 4) (check-true (foldl (lambda (input acc)
(bv 4 4) (and acc (equal? (length input) 1) (equal? (car (first input)) "input1")))
(bv 5 4) #t
(bv 6 4) (generate-inputs #:inputs (list (cons "input1" 4)) #:num-inputs 10))))
(bv 7 4)
(bv 8 4)
(bv 9 4)
(bv 10 4)
(bv 11 4)
(bv 12 4)
(bv 13 4)
(bv 14 4)
(bv 15 4)))
; Requesting fewer inputs than the number of possible values for the bitwidth. (test-case "generate-clock"
(check-equal? (length (generate-inputs-for-one "input2" 3 5)) 5) (define inputs
(list (list (cons "input1" (bv 4 4)) (cons "input2" (bv 3 3)) (cons "input3" (bv 2 2)))
(list (cons "input1" (bv 3 4)) (cons "input2" (bv 4 3)) (cons "input3" (bv 1 2)))
(list (cons "input1" (bv 2 4)) (cons "input2" (bv 5 3)) (cons "input3" (bv 0 2)))))
; Requesting more inputs than the number of possible values for the bitwidth. (check-equal? (length (generate-clock #:clock-name "clk" #:inputs inputs)) 6)
(check-equal? (generate-inputs-for-one "input3" 2 5) (list (bv 0 2) (bv 1 2) (bv 2 2) (bv 3 2)))
; Requesting equal number of inputs as the number of possible values for the bitwidth. (check-equal?
(check-equal? (generate-inputs-for-one "input4" 2 4) (list (bv 0 2) (bv 1 2) (bv 2 2) (bv 3 2))) (generate-clock #:clock-name "clk" #:inputs inputs)
(list (cons (cons "clk" (bv 0 1))
(list (cons "input1" (bv 4 4)) (cons "input2" (bv 3 3)) (cons "input3" (bv 2 2))))
(cons (cons "clk" (bv 1 1))
(list (cons "input1" (bv 4 4)) (cons "input2" (bv 3 3)) (cons "input3" (bv 2 2))))
(cons (cons "clk" (bv 0 1))
(list (cons "input1" (bv 3 4)) (cons "input2" (bv 4 3)) (cons "input3" (bv 1 2))))
(cons (cons "clk" (bv 1 1))
(list (cons "input1" (bv 3 4)) (cons "input2" (bv 4 3)) (cons "input3" (bv 1 2))))
(cons (cons "clk" (bv 0 1))
(list (cons "input1" (bv 2 4)) (cons "input2" (bv 5 3)) (cons "input3" (bv 0 2))))
(cons (cons "clk" (bv 1 1))
(list (cons "input1" (bv 2 4)) (cons "input2" (bv 5 3)) (cons "input3" (bv 0 2)))))))
(test-case "simulate-rosette"
; Requesting invalid configuration should raise an error. ; This function will take association lists as inputs, so the helper function is simply identity.
(check-exn exn:fail? (λ () (generate-inputs-for-one "input5" 2 'invalid-config))) ; This is not generally true of Yosys-generated code. Similarly, this function uses an association
(check-exn exn:fail? (λ () (generate-inputs-for-one "input5" 2 bytes->string/latin-1))))) ; list for state, which is not what Yosys generates, but it's easier for testing.
;
; A one-stage adder. Inputs are registered in one clock cycle, and the output is the sum of the
; two registered inputs.
(define (module-function inputs state)
(let* ([a (cdr (assoc "a" inputs))]
[b (cdr (assoc "b" inputs))]
[clk (cdr (assoc "clk" inputs))]
[old-clk (cdr (assoc "clk" state))]
[prev-a (cdr (assoc "prev-a" state))]
[prev-b (cdr (assoc "prev-b" state))]
[a-reg (cdr (assoc "a-reg" state))]
[b-reg (cdr (assoc "b-reg" state))]
[clk-ticked (and (equal? clk (bv 1 1)) (equal? old-clk (bv 0 1)))]
[new-a-reg (if clk-ticked prev-a a-reg)]
[new-b-reg (if clk-ticked prev-b b-reg)]
[out (list (cons "o" (bvadd new-a-reg new-b-reg)))]
[new-state (list (cons "prev-a" a)
(cons "a-reg" new-a-reg)
(cons "prev-b" b)
(cons "b-reg" new-b-reg)
(cons "clk" clk))])
(cons out new-state)))
(define outputs
(simulate-rosette #:function module-function
#:initial-state (list (cons "a-reg" (bv 0 4))
(cons "b-reg" (bv 0 4))
(cons "prev-a" (bv 0 4))
(cons "prev-b" (bv 0 4))
(cons "clk" (bv 0 1)))
#:inputs
(list (list (cons "clk" (bv 0 1)) (cons "a" (bv 4 4)) (cons "b" (bv 4 4)))
(list (cons "clk" (bv 1 1)) (cons "a" (bv 3 4)) (cons "b" (bv 0 4)))
(list (cons "clk" (bv 0 1)) (cons "a" (bv 10 4)) (cons "b" (bv 9 4)))
(list (cons "clk" (bv 1 1)) (cons "a" (bv 2 4)) (cons "b" (bv -1 4)))
(list (cons "clk" (bv 0 1)) (cons "a" (bv 4 4)) (cons "b" (bv -15 4)))
(list (cons "clk" (bv 1 1)) (cons "a" (bv 0 4)) (cons "b" (bv 0 4))))))
(check-equal? outputs
(list (list (cons "o" (bv 0 4)))
(list (cons "o" (bv 8 4)))
(list (cons "o" (bv 8 4)))
(list (cons "o" (bv 3 4)))
(list (cons "o" (bv 3 4)))
(list (cons "o" (bv -11 4)))))))