Verilisp
About
|
Download
|
Verilisp for non-Lispers
|
Download cpu.vl
|
Library
About Verilisp
Verilisp is a collection of common lisp macros which spit out verilog HDL code, effectively turning common lisp into a prettier frontend to verilog.
The frontend to verilisp is a python script "verilisp.py" which munges names so you can say 'and' when you're talking about the verilog primitive 'and' gate instead of the builtin common lisp macro. To access the builtin macro 'and', use 'l_and' [which stands for 'lisp and']. If you want to say 'v_and' for the verilog primitive, and 'and' for the builtin macro, feel free to bypass verilisp.py by piping your verilisp file right to __verilisp__.cl.
For sample verilisp code, see the files in the tests directory in the zip.
Note that verilisp.py also wraps all of its input file in an eval-backquote-progn, so you don't have to.
To generate verilog code from verilisp code,
- Install Python [version 2.0 or later].
- Install Common Lisp.
- Unzip verilisp.zip somewhere convenient ["C:\verilisp\", "/usr/share/verilisp/", "~/verilisp/", etc.].
- Open a command line.
- Navigate to the directory containing verilisp.py.
- Run "python verilisp.py YOUR_VERILISP_FILE".
If a verilog file is not created in the same directory as your verilisp file,
- make sure that your verilisp code is valid common lisp syntax,
- make sure that running 'python' runs the interactive python interpreter,
- make sure that running 'clisp' runs the interactive common lisp interpreter,
- determine whether the error messages are generated by python or by lisp, and post the messages to a relevant mailing list/forum, or email me.
I implemented a read-eval-print loop in both verilisp.py [which munges the names for you] and __verilisp__.cl [which does no munging]. Pass a single argument "-" to verilisp.py for the REP loop; run __verilisp__.cl normally.
Run "python verilisp.py" to run the automated test benches.
Verilisp for non-Lispers
If you don't speak lisp, the first question in your mind is probably why verilisp exists in the first place.
First, lisp is one of the better ways to notate trees using ASCII. Trees come up very often in computer science, and hardware design is no exception. Verilog source can be structured like a tree, and lisp syntax allows elimination of extraneous keywords such as "begin", "end", "endmodule", "endfunction", etc.:
(module name (signature signature)
declarations
(if (condition)
statements
)
(always (signals)
statements
)
(initial
statements
)
)
Additionally, boolean expressions can be structured like trees:
(
a
and
(b or c)
and
(c or (d and e))
)
or, in lisp syntax:
(and
a
(or b c)
(or
c
(and d e)
)
)
Verilisp's expand macro allows the above boolean expression to be translated into the equivalent verilog code:
and and0 (out, a, temp0, temp1);
or or0 (temp0, b, c);
or or1 (temp1, c, temp2);
and and1 (temp2, d, e);
Now that you are looking for trees, which is more intuitive? Which would you trust yourself to write at 4am? [Imagine that, instead of an example boolean expression which simplifies to (and a c (or b (and d e))), you have a pipelined cpu with hundreds of wires and busses whizzing overhead.]
"Just wait! It gets better! I'll throw in macros FOR FREE!"
One of the main reasons common lisp was chosen as the language for verilisp was for its macro system.
For normal functions [eg. those named "foo"], when you say "(foo a b)", lisp first evaluates the arguments before passing their values to the function foo. So, if i wanted verilisp to name wires "a" and "b", i would have to tell the interpreter that i didn't want it to evaluate a and b before calling foo -- i would have to quote them: "(foo 'a 'b)".
But, if foo were a macro, i wouldn't have to do this. i wouldn't have to sprinkle quotes all over my code and pull my hair out when i forget one. If foo were a macro, the arguments wouldn't be evaluated -- they'd be passed to the macro as symbols or lists of symbols. Now, i can say "(foo a b)".
"Ok, that's nice. So what if i don't know what i want to name a and b?"
Another feature of common lisp's macro system is the reader macros known as backquote, comma, and comma-at. They work together to let you build lists [and therefore lisp code] more easily. For example, " `(foo ,a ,b)" is the same as "(list 'foo a b)" and " `(foo ,@(list a b))", all of which first look up a and b in the current namespace, and insert their values into a list whose first element is the symbol 'foo. This is how you force lisp to evaluate arguments before passing them to a macro: you build code which calls the macro.
Also, a feature that verilisp uses behind the scenes is that, when a macro returns code [lists of symbols], that code is evaluated in place of the macro call. You don't really need to know about this feature until you want to write a macro which generates verilog code.
Library
I've taken the liberty of writing a few macros which generate handy things. Say "(use adders)" or "(use muxes)" to import macros which generate arbitrarily-sized adders or muxes, respectively. The 'use' macro is intelligent enough not to import a package more than once, but not smart enough to find the package unless you a) link ./lib/ to /usr/lib/verilisp/, or b) say '(add-verilisp-path "/home/user/verilisp/lib/")' in your code. The default path includes "./lib/" and "/usr/lib/verilisp/"
adders
Uses: muxes
Generates on import:
- 1-bit full adder named "full_adder" with signature ((output sum c_out) (input a b c_in))
Defines macros:
- (make-adder name n-bits)
make a n-bits -bit ripple/carry adder with signature ((output (,n-bits out) overflow) (input (,n-bits a b)))
- (make-addsuber name n-bits)
make a n-bits -bit ripple/carry adder/subtracter with signature ((output (,n-bits out) overflow) (input (,n-bits a b) subtract))
- (make-csaddsuber name n-bits)
make a n-bits -bit optimized carry-select adder/subtracter with signature ((output (,n-bits out) overflow) (input (,n-bits a b) subtract))
alus
Uses: adders cloers dividers multipliers muxes shifters
Defines macros:
clocks
Uses: nil
Defines macros:
- (make-clock name &optional (clock-time 1))
make a behavioral clock with signature ((output clk)) which rises every clock-time simulation steps.
cloers
Uses: muxes adders
Defines macros:
- (make-log-adder name n-in-bits)
make a module with signature ((output (,(+ 1 (log n-in-bits 2)) out)) (input (,n-in-bits in)))
where "out" counts the number of wires in "in" which are on/high/true.
- (make-cloer name n-bits)
makes a module with signature ((output (,n-bits out)) (input (,n-bits in)))
where out counts the leading ones in in.
eg, if n-bits=8, if n=11101010, then out=00000011=310.
counters
*unfinished*
Uses: ffs
Generates on import:
- (jkff q clk j k) ; as made by (make-ff jkff jk)
Defines macros:
- (make-counter name n-bits)
make a frequency divider with signature ((output (,n-bits out)) (input in))
out represents the number of times in has gone from low to high.
decoders
Uses: nil
Defines macros:
- (make-dec name n-bits &optional enable)
make a decoder with signature ((output (,(1<< n-bits) out)) (input (,n-bits in)))
if enable is specified and non-nil, take an additional input "enable".
dff
Uses: ffs
Generates on import:
- (dff (output q) (input clk d)) # transparent nand dff
Defines macros: nil
dividers
*unfinished*
Uses: adders
Defines macros:
- (make-divider name n-bits)
make a divider with signature ((output (,(* n-bits 2) out)) (input (,n-bits a b) signed))
ffs
Uses: clock muxes
Defines macros:
- (make-ff name type &key auto enable)
type is one of "p" [for a user-defined primitive], "dt" [for a transparent nand d flip flip], "d" [for a nand dff], "sr", "jk". any other type generates a behavioral dff.
if auto is not nil, an additional module wrapping "name" will be generated [named ,auto] which does not take a clock input, using instead the clock generated by use-ing the unit_clock module.
if enable is not nil, an additional module wrapping "name" will be generated [named ,enable] which takes an additional input which determines whether or not to update q, effectively acting as a "write-enable" flag.
if both auto and enable are specified, only a single wrapper module will be generated [named ,auto].
fpadders
*unfinished*
Uses: adders
Defines macros:
- (make-fpaddsuber name n-bits)
make a floating point adder with signature ((output (,n-bits out) overflow) (input (,n-bits a b) subtract))
fpmultipliers
*unfinished*
Uses: adders multipliers
Defines macros:
- (make-fpmultiplier name n-bits)
make a floating point multiplier with signature ((output (,n-bits out) overflow) (input (,n-bits a b)))
fpshifters
*unfinished*
Uses: adders
Defines macros:
- (make-fpshifter name n-bits n-bits-exp &key adder)
make a floating point shifter with signature ((output (,n-bits out) overflow) (input (,n-bits in) (,n-bits-exp shamt)))
fpus
*unfinished*
Uses: nil
Defines macros:
mems
Uses:
Defines macros:
- (make-mem name word-size input-file &key n-words (input-format 'b) (buf-delay 100) addr-size writable debug-on-write)
make a behavioral memory unit which $readmemb's input-file initially [unless input-format is specified to be 'h, in which case it is $readmemh'd].
if addr-size is not specified, it defaults to word-size.
if n-words is not specified, it is calculated to be (1<< addr-size).
if writable is specified and non-nil, the signature of the generated module is ((output (,word-size out)) (input (,addr-size addr) (,word-size wr_data) wren clk)); if writable is nil, the signature is ((output (,word-size out)) (input (,addr-size addr))).
if debug-on-write, a message is $display'd when data is written.
multipliers
Uses: nil
Defines macros:
- (make-multiplier mul32 32)
make a multiplier with signature (mul32 (64 out) (32 a b)).
muxes
Uses: nil
Generates on import:
- (unit_mux (output out) (input sel a b))
Defines macros:
- (make-mux n-bits &optional (width 1))
make a mux named name with (1<< n-bits) inputs, each of which is width bits wide.
for example, the signature of the module created by (make-mux foo 3 4) is ((output (4 out)) (input (3 selectors) (4 in0 in1 in2 in3 in4 in5 in6 in7))).
note that this macro tries to cut down on the number of generated lines of verilog by keeping a global variable mapping 1-bit mux names to widths, and using a bob-barker function to re-use the largest usable mux.
negaters
Uses: adders
Defines macros:
- (make-negater name n-bits &key adder)
generates a module named name with the signature ((output (,n-bits out) overflow) (input (,n-bits in))), and which adds 1 to the not of in.
regfiles
Uses: ffs muxes decoders
Defines macros:
- (make-reg name word-size &key dffe resettable)
make a register with signature
((output (,word-size out)) (input (,word-size in) write_enable clk reset)) ; if resettable is specified and non-nil
((output (,word-size out)) (input (,word-size in) write_enable clk)) ; otherwise
note that this macro tries to cut down on the number of generated lines of verilog by keeping a global variable mapping register names to sizes, and using a bob-barker function to re-use the largest usable register.
- (make-regfilename word-size &key address-size n-readers reg dffe decoder mux zero-reg)
make a register file with signature ((output (,word-size out_a out_b)) (input (,address-size address_a address_b address_w) (,word-size data_w) write_flag clk))
shifters
Uses: muxes
Defines macros:
- (make-arithmetic-shifter ash32 32)
make a 32-bit arithmetic shifter with signature ((output (32 out)) (input (32 in) (5 shamt) direction))
- (make-circular-shifter bsh32 32)
make a 32-bit barrel shifter with signature ((output (32 out)) (input (32 in) (5 shamt)))
- (make-logical-shifter bsh32 32)
make a 32-bit logical shifter with signature ((output (32 out)) (input (32 in) (5 shamt) direction))
signexts
Uses: nil
Generates on import: nil
Defines macros:
- (make-sign-extender signext16to32 16 32)
generates a module with the signature (signext16to32 (output (32 out)) (input (32 in)))
tables
Uses: adders
Defines macros:
testers
Uses: nil
Defines macros:
- (make-tester tester alu32 "alu32.vcd" ((32 1) 3) ((32 2) (5 1) (6 1))) ; make a module named tester which exhaustively tests module alu32, dumping everything to "alu32.vcd". specify that the module alu32 outputs a 32-bit bus and 3 wires, and takes for input two 32-bit busses, a 5-bit bus, and a 6-bit bus.
unit_clock
Uses: clocks
Generates on import:
- unit_clock, as made by make-clock. used in ffs if the auto keyword is specified.