- Notifications
You must be signed in to change notification settings - Fork44
A Just-In-Time Compiler for Verilog from VMware Research
License
vmware-archive/cascade
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
FPGAs can exceed the performance of general-purpose CPUs by several orders ofmagnitude and offer dramatically lower cost and time to market than ASICs.While the benefits are substantial, programming an FPGA can be an extremelyslow process. Trivial programs can take several minutes to compile using atraditional compiler, and complex designs can take hours or longer.
Cascade is a novel solution to this problem, the world's first just-in-timecompiler for Verilog. Cascade executes code immediately in a softwaresimulator, and performs compilation in the background. When compilation isfinished, the code is moved into hardware, and from the user’s perspective itsimply gets faster over time. Cascade's ability to move code back and forthbetween software and hardware also makes it the first platform to providegeneric support for the execution of unsynthesizable Verilog from hardware. Theeffects are substantial. Cascade encourages more frequent compilation, reducesthe time required for developers to produce working hardware designs, andtransforms HDL development into something which closely resembes writingJavaScript or Python. It takes the first steps towards bridging the gap betweenprogramming software and programming hardware.
Much of the work which has gone into building Cascade has been documented inconference proceedings. A complete list of publications (hopefully with more tocome) is below. Note however, that Cascade is under active development. In somecases, its implementation may have diverged what is described in thesetexts. The most up-to-date information on Cascade's implementation is alwaysits source code.
Recent Updates:
- 12/19 Cascade supports theULX3S as a backend target.
- 11/19 Cascade usesVerilator as an intermediate compilation pass between software simulation and hardware.
- 10/19 Cascade provides experimental support for Xilinx FPGAs on Amazon F1 (release coming soon).
- 9/19 Cascade can be linked into C++ projects as a library. This allows Cacade to target itself as a backend target.
- 7/19 Cascade supports unsynthesizable file I/O primitives from hardware.
- 5/19 Cascade supports theDE10 Nano as a backend target.
- Dependencies
- Building Cascade
- Using Cascade
- Environments
- Support for Synthesizable Verilog
- Support for Unsynthesizable Verilog
- Standard Library
- Target-Specific Components
- Adding Support for New Backends
- FAQ
Cascade should build successfully on OSX and most Linux distributions.
- Clone this repository
$ git clone https://github.com/vmware/cascade
- Run the setup script
$cd cascade$ ./setup
The setup script should guide you through installing dependencies, aswell as configuring, building and testing Cascade.
Start Cascade by typing
$ cascade
This will place you in a Read-Evaluate-Print-Loop (REPL). Code which is typedhere is appended to the source of the (initially empty) top-level (root) moduleand evaluated immediately. Try defining a wire.
>>>wirex;
The Verilog specification requires that code inside ofinitial
blocks isexecuted exactly once when a program begins executing. Because Cascade is adynamic environment, we generalize that specification: code inside ofinitial
blocks is executed exactly once immediately after it is compiled.Try printing the value of the wire you just defined. Cascade uses a two-value modelfor signals. Unknown logic values (X) are set to zero, and high-impedence values (Z)are assigned non-deterministic values.
>>>initial$display(x);>>>0
Now try printing a variable which hasn't been defined.
>>>initial$display(y);>>> Typechecker Error:>>>> In final line of userinput:>>> Referenece to unresolved identifier: y
Anything you enter into the REPL is lexed, parsed, type-checked, and compiled.If any part of this process fails, Cascade will produce an error message andthe remainder of your text will be ignored. If you type multiple statements,anything which compiles successfully before the error is encountered cannot beundone. Below,x
andy
are declared successfully, but theredeclaration ofx
produces an error.
>>>wirex,y,x;>>> Typechecker Error:>>>> In final line of userinput:>>> A variable namedx already appears in this scope.>>> Previous declaration appears in previous userinput.>>>initial$display(y);>>>0
You can declare and instantiate modules from the REPL as well. Note however,that declarations will be type-checked in the global scope. Variables whichyou may have declared in the root module will not be visible here. It isn'tuntil a module is instantiated that it can access program state.
>>>moduleFoo(>>>inputwirex,>>>outputwire y>>> );>>>assign y=x;>>>endmodule>>>wire q,r;>>> Foo f(q,r);
If you don't want to type your entire program into the REPL you can use theinclude statement, wherepath/
is assumed to be relative to your currentworking directory.
>>> `include"path/to/file.v"
If you'd like to use additional search paths, you can start Cascade using the-I
flag and provide a list of semicolon-separated alternatives. Cascadewill try each of these paths as a prefix, in order, until it finds a match.
$ cascade -I path/to/dir1:path/to/dir2
Alternately, you can start Cascade with the-e
flag and the name of a file to include.
$ cascade -e path/to/file.v
Finally, Cascade will stop running whenever a program invokes the$finish
task.
>>>initial$finish;Goodbye!
You can also force a shutdown by typingCtrl-C
orCtrl-D
.
>>>moduleFoo(); wir... I give up... arg... ^C
Cascade can also be called directly from C++ code. Cascade's command line interfaceis a thin-wrapper around a small set of functions. A minimal exampleis shown below. Further dicussion of the concepts in this example appears in subsequentsections of this README.
#include<cassert>#include<iostream>#include<sstream>#include<cascade.h>usingnamespacecascade;usingnamespacestd;intmain() {// Create an instance of Cascade. Cascade is thread-safe. Multiple instances// may share the same address space. Cascade cascade;// Configuration options. These methods should all be called before// cascade starts running. cascade.set_include_dirs(...); cascade.set_enable_inlining(...); cascade.set_open_loop_target(...); cascade.set_quartus_server(...); cascade.set_profile_interval(...);// Cascade exposes its six i/o streams (the standard STDIN, STDOUT, and// STDERR, along with three additional STDWARN, STDINFO, STDLOG) as// C++ streambufs. These are initially mapped to nullptr. Changes to this// mapping should also be made before cascade starts running. cascade.set_stdin(cin.rdbuf()); cascade.set_stdout(cout.rdbuf()); cascade.set_stderr(cerr.rdbuf()); cascade.set_stdwarn(cerr.rdbuf()); cascade.set_stdinfo(clog.rdbuf()); cascade.set_stdlog(...);// Start cascade. This method returns immediately. cascade.run();// Cascade will run until the user's program invokes the $finish() task or the// user requests that it stop running. The request_stop() method returns immediately.// The wait_for_stop() method will block until either of the above conditions// is satisified. cascade.request_stop(); cascade.wait_for_stop();// Stopping cascade only pauses its execution. All previous state is retained.// To continue running, call run() again. cascade.run();// Cascade is a c++ ostream. While it is running, any text provided to it// via the << operator, will be eval'ed. Since every program must begin with an// march file, you can use an include statement to eval one. cascade <<"`include\"path/to/march.v\"";// Because cascade runs asynchronously, it has no way of knowing when user input// has ended. The user can force this by using the flush() method, or passing// cascade a c++ endl. Be careful with using endl to separate multi-line inputs.// This may cause cascade to prematurely evaluate user input. When in doubt, prefer '\n'. cascade.flush(); cascade << endl;// The results of the eval statements which have taken place since the previous// flush are available through ostream status bits. Cascade is placed in// in the eof state when it encounters an eof character, and in the bad state when// an eval results in a parse or type error. Because cascade runs asynchronously, the// only way to make sure an eval has run to completion is to request a stop. The standard// mechanism for clearing an ostream's state bits is to use the clear() method.assert(!cascade.bad());// Not guaranteed to see the result of the previous eval. cascade.stop_now();// Syntactic sugar for request_stop(); wait_for_stop();assert(!cascade.bad());// Both guaranteed to see the result of the previous eval.assert(!cascade.eof()); cascade.clear();// Clears eof and bad bits.// While cascade is stopped, it is safe to replace its rdbuf. For example: stringstreamss("wire x; initial $display(\"Hello, world!\");"); cascade.rdbuf(ss.rdbuf()); cascade.run();// Note however, that most c++ implementations assign non-standard semantics to// cin. It's safe to switch cascade's rdbuf to cin. But once this is done, it is// no longer safe to change it again. cascade.stop_now(); cascade.rdbuf(cin.rdbuf()); cascade.rdbuf(ss.rdbuf());// UNDEFINED! cascade.run();// Block until the user's program invokes the $finish() task. cascade.wait_for_stop();return0;}
To build a program that uses Cascade as a library, statically link against libcascade. If you installed cascade to a directoryother than/usr/local/
you'll need to provide alternate values for the-I
and-L
flags.
$ g++ --std=c++17 -I/usr/local/include/cascade my_program.cc -lcascade
By default, Cascade runs in software. You can invoke thisbehavior explicitly using the--sw
flag.
$ cascade --march sw
This environment declares the following module and instantiates it into thetop-level module for you:
moduleClock(outputwire val);endmoduleClock clock;
This module represents the global clock. Its value toggles between zero and oneevery cycle. Try typing the following (and remember that you can type Ctrl-C toquit):
>>>always @(clock.val)$display(clock.val);>>>0>>>1>>>0>>>1>>> ...
This global clock can be used to implement sequential circuits, such as thebarrel shifter shown below.
>>>moduleBShift(>>>inputwire clk,>>>outputreg[7:0] val>>> );>>>always @(posedge clk)begin>>> val<= (val==8'h80) ?8'h01 : (val<<1);>>>end>>>endmodule>>>wire[7:0]x;>>> BShift bs(clock.val,x);
Compared to a traditional compiler which assumes a fixed clock rate, Cascade'sclock isvirtual: the amount of time between ticks can vary from one cycle tothe next, and is a function of both how large your program and is and how oftenand how much I/O it performs. This abstraction is the key mechanism by whichCascade is able to move programs between software and hardware withoutinvolving the user.
Up until this point the code we've looked at has been entirely compute-based.However most hardware programs involve the use of I/Operipherals. Before we get into real hardware, first try running Cascade'svirtual software FPGA.
$ sw_fpga
Cascade's virtual FPGA provides an ncurses GUI with four buttons, one reset,and eight leds. You can toggle the buttons using the1 2 3 4
keys, togglethe reset using ther
key, and shut down the virtual FPGA by typingq
. Cascade's software backend will automatically detect the virtual FPGA and expose its peripheralsas modules which are implicitly declared and instantiated in the top-levelmodule:
modulePad(outputwire[3:0] val);endmodulePad pad();moduleReset(outputwire val);endmoduleReset reset();moduleLed(outputwire[7:0] val);endmoduleLed led();
Try writing a simple program that connects the pads to the leds.
>>>assign led.val= pad.val;
Toggling the pads should now change the values of the leds for as long asCascade is running.
Cascade currently provides support for two hardware backends: theTerasicDE10 NanoSoCand theULX3S. When Cascade is run on either of these targets, instead of mapping compute and ledsonto virtual components, it can map them directly onto real hardware.
Before using the de10 backend you'll first need to installIntel's Quartus Lite IDE on a network-accessible 64-bit Linux machine. You'll also need to run Cascade's compilation server on that machine.
$ quartus_server --path <quartus/install/dir> --port 9900
Alternatively, you can use Quartus Lite IDE installed on a remote host:
$ quartus_server --tunnel-command <command/like/ssh> --path <quartus/install/dir> --port 9900
Next you'll need an SD card image for your DE10 with a valid installation of Cascade. Cascade can generatethis image for you automatically or you can download a prebuilt imagehere (todo). Reboot your DE10 using this image and run Cascade as usual (but on the DE10, use sudo).
$ cd cascade$ sudo cascade --march de10 --quartus_host <64-bit Linux IP> --quartus_port <64-bit Linux port>
Assuming Cascade is able to successfully connect to the FPGA fabric, you willbe presented with a similar environment to the one you encountered when using the software backend. The onlydifference is that instead of a Reset module, Cascade will implicitly declarethe following module, which represents the DE10's Arduino header.
moduleGPIO(inputwire[7:0] val);endmoduleGPIO gpio();
Try repeating the example from the previous section and watch real buttonstoggle real leds.
Cascade supports the ULX3S using the entirely open sourceYosys->NextPNR->ujprog toolchain. Before getting started, you'll need to follow the directionshere for installing Yosys and NextPNR, andhere for installing ujprog. Make sure that all components are installed to the standard/usr/local
directory tree.
Next, you should be able to run Cascade as usual.
$ cascade --march ulx3s
Cascade does not currently support any of the I/O peripherals on the ULX3s, but it can target its reprogrammable fabric to improve virtual clock frequency for most applications.
If you'd like more information on how Cascade transitions code betweensoftware and hardware, trying using the--enable_info
flag. This will cause Cascade to printstatus updates to the REPL whenever part of your program begins execution in a new context.
$ cascade --enable_info
In general, you can expect your virtual clock frequency to increase as more and more of your logictransitions to hardware. Providing the--profile <n>
flag will cause Cascade to periodically (every seconds) print the current time and Cascade's virtual clock frequency to the REPL. To see this effect, try executing a very long-running program.
$ cascade --march <sw|de10|ulx3s> -e share/cascade/test/benchmark/bitcoin/run_25.v --enable_info --profile 3
Cascade currently supports a large --- though certainly not complete --- subsetof the Verilog 2005 Standard. The following partial list should give a goodimpression of what Cascade is capable of.
Feature Class | Feature | Supported | In Progress | Will Not Support |
---|---|---|---|---|
Compiler Directives | `define | x | ||
`undef | x | |||
`ifdef | x | |||
`ifndef | x | |||
`elsif | x | |||
`else w | x | |||
`endif | x | |||
`include | x | |||
Primitive Types | Net Declarations | x | ||
Reg Declarations | x | |||
Integer Declarations | x | |||
Real Declarations | x | |||
Time Declarations | x | |||
Realtime Declarations | x | |||
Array Declarations | x | |||
Expressions | Arithmetic Operators | x | ||
Bitwise Operators | x | |||
Logical Operators | x | |||
Concatentation Operators | x | |||
Conditional Operators | x | |||
Bit/Part Select | x | |||
Strings | x | |||
Real Constants | x | |||
Parameters | Parameter Declarations | x | ||
Localparam Declarations | x | |||
Defparam Statements | x | |||
Module Declarations | Input Ports | x | ||
Output Ports | x | |||
Inout Ports | x | |||
Module Instantiations | Named Parameter Binding | x | ||
Ordered Parameter Binding | x | |||
Named Port Binding | x | |||
Ordered Port Binding | x | |||
Instantiation Arrays | x | |||
Generate Constructs | Genvar Declarations | x | ||
Case Generate Constructs | x | |||
If Generate Constructs | x | |||
Loop Generate Constructs | x |
Cascade provides support for many of the unsynthesizable system tasks which aredescribed in the 2005 specification, along with a few others which are uniqueto a just-in-time enviornment. One of the things that makes Cascade so powerfulis that it supports the execution of unsynthesizable systems taskseven when aprogram is running in hardware. With Cascade, there's no reason to shy awayfrom the use of$display()
as a debugging tool. Unsynthesizable systemtasks are guaranteed to run correctly on every target.
A complete listing of the system tasks which Cascade supports, along with abrief description of their behavior is shown below.
Feature Class | Feature | Supported | In Progress | Will Not Support |
---|---|---|---|---|
Printf | $display(fmt, args...) | x | ||
$write(fmt, args...) | x | |||
Scanf | $scanf(fmt, args...) | x | ||
Debugging | $monitor(var) | x | ||
$list(name) | x | |||
$showscopes(n) | x | |||
$showvars(vars...) | x | |||
Logging | $info(fmt, args...) | x | ||
$warning(fmt, args...) | x | |||
$error(fmt, args...) | x | |||
Simulation Control | $finish(code) | x | ||
$fatal(code, fmt, args...) | x | |||
Virtualization | $save(file) | x | ||
$restart(file) | x | |||
$retarget(march) | x | |||
File I/O | $fopen(path, mode) | x | ||
$fclose(fd) | x | |||
$fdisplay(fd, fmt, args...) | x | |||
$feof(fd) | x | |||
$fflush(fd) | x | |||
$fgetc(fd) | x | |||
$fgets(str, fd) | x | |||
$fread(fd, var) | x | |||
$fscanf(fd, fmt, args...) | x | |||
$fseek(fd, off, dir) | x | |||
$ftell(fd) | x | |||
$fwrite(fd, fmt, args...) | x | |||
$rewind(fd, off, dir) | x | |||
$ungetc(c, dir) | x |
The printf family of system tasks can be used to emit debugging statements tostdout (the REPL). Both use the same printf-style of argument passing. Aformatting string which may be delimitted with variable placeholders (%d, %x, etc...
) is followed by a list of program variables whose runtime valuesare substituted for those placeholders. Both printf-style system tasks behaveidentically. The only difference is that$display()
automatically appendsa newline character to the end of its output.
The scan system task can be used to read values from stdin. However thisfeature is only useful when Cascade is used as a library, as when Cascade isrun in a REPL, it dedicates stdin to parsing code.
The debugging-family of system tasks can be used to print information aboutthe program which Cascade is currently running.$list
displays source code,$showvars
displays information about program variables, and$showscopes
displays information about program scopes.
The logging-family of system tasks behave identically to the printf-family ofsystem tasks. The only difference is that their output can be filtered based onthe arguments that you provide when you run Cascade. By default,$warning()
and$error()
messages are printed to the REPL. You candisable this behavior off by running Cascade with the--disable_warning
and--disable_error
flags. In contrast,$info()
messages are notprinted to the REPL by default. You can enable this behavior by running Cascadewith the--enable_info
flag.
The$finish()
system task can be used to shut Cascade downprogrammatically. Evaluating the$finish()
task with an argument otherthan 0 will cause Cascade to emit a status message before shutting down. Youcan think of the$fatal()
system task as a combination of the$finish()
and$error()
system tasks. The following two programs areidentical.
initial $fatal(1,"format string %d",42);
initialbegin $error("format string %d",42);$finish(1);end
While Cascade was originally designed as a developer aid, its ability to breaka program into pieces and move those pieces seamlessly between software andhardware turns out to be the key engineering primitive which is necessary forFPGA virtualization. The virtualization-family of system tasks expose thisfunctionality.
The$save()
and$restart()
tasks can be used to save the state of arunning program to a file, and then reload that state at a later tmie ratherthan having to run the program again from scratch. The following example showshow to configure two buttons to suspend and resume the execution of a program.
always @(pad.val)beginif (pad.val[0])begin$save("path/to/file");$finish;endelseif (pad.val[1])begin$restart("path/to/file");end
The$retarget()
task can be used to reconfigure Cascade as though it wasrun with a different--march
file while a program is executing. This maybe valuable for transitioning a running program from one hardware target toanother. The following example shows how to configure two buttons to toggle theuse of JIT-compilation mid-execution.
always @(pad.val)beginif (pad.val[0])begin $retarget("de10");endelse bif (pad.val[1])begin $retarget("de10_jit");endend
The family of file i/o tasks provide an abstract mechanism for interacting withfile streams. The following example shows how to read the contents of a file,one cycle at a time. Note that$fread()
is sensitive to the size of itssecond argument and will read as many bytes as necessary to produce a value forthat variable.
integer s=$fopen("path/to/file","r");reg[31:0]x=0;always @(posedge clock.val)begin $fread(s,x);if ($feof(s))begin$finish;end$display(x);end
The following example shows how you can use both$fread()
and$fwrite()
tasks in conjunction with the$feof
task to stream datato and from your program, regardless of whether it is running in software orhardware.
moduleCompute(inputwire[31:0]x,outputwire[31:0] y);assign y=2*x;endmodule;reg[31:0]x;wire[31:0] y;Compute c(x,y);integer i=$fopen("path/to/input","r");integer o=$fopen("path/to/output");always @(posedge clock.val)begin $fread(i,x);if ($feof(i))begin$finish;end $fwrite(o,"%x", y);end
In addition to the tasks described above, the$fseek()
task can be usedto reset the position from which$fread()
tasks are performed. Note thatCascade uses an eventual consistency model for$fdisplay()
statements.Attempting to interleave reads and writes to the same stream may result inunexpected behavior unless the user forces a sync by invoking the$fflush()
task.
In addition to supporting both synthesizable and unsynthesizable Verilog,Cascade also provides a Standard Library of I/O peripherals. You can think ofthis library as an abstract representation of target-specific hardware. Bytargeting the components in Cascade's Standard Library, rather than thespecific peripherals associated with a hardware target, there is a good chancethat your program will run in multiple environments without modification. We'vealready seen examples of many of the peripherals in Cascade's Standard Library.A complete listing, along with the--march
targets which support them, isshown below.
Component | sw | de10 | ulx3s |
---|---|---|---|
Clock | x | x | x |
Led | x | x | |
Pad | x | x | |
Reset | x | ||
GPIO | x |
Some--march
targets may instantiate modules which serve as wrappersaround target-specific hardware features. For example, a target might provide aBram
module to expose a particular type of storage, or aPcie
module to expose a particular type of I/O. While these wrappers will typicallyexpose the highest levels of performance to the user, they do so at the priceof portability. Writing a program which relies on a particular low-levelfeature makes it unlikely that Cascade will be able to execute that program onan--march
target other than the one it was designed for.
The target-specific hardware features exposed by an--march
target, alongwith the standard library components it supports, can be displayed by runningCascade with the--enable_info
flag.
Adding support for a new backend begins with writing an--march
file.This file will contain instantiations for the Standard Library components thatyour backend supports, as well as any target-specific components you wish tomake available to the user. This file should also contain an$info()
message which describes this support, along with anything else you'd like theuser to know. Save this file asdata/march/my_backend.v
. Once you've doneso, you can run Cascade with the--march my_backend
flag. An example isshown below.
`ifndef __MY_BACKEND_V`define __MY_BACKEND_V// An --march file must first include the declarations in the Standard Library`include"data/stdlib/stdlib.v"// Next, an --march file must instantiate the root module. The __target annotation is// used to pass information to the Cascade compiler about how to compile the user's code.// __target is a semicolon-separated list of backend compilation passes to use. If only one// value is provided as below, then Cascade will not run in JIT-mode. To enable JIT-mode,// use the following string instead: "sw;my_backend".(* __target="my_backend"*)Root root();// At a minimum, a backend must instantiate a global Standard Library clock// which will never leave software. Any code which follows the instantiation of// the root is placed inside of the root, and any instantiations which appear// inside of the root inherit its annotations. The __target="sw" annotation// overrides the value you placed on the root and guarantees that this module// will never leave software simulation.(* __target="sw"*)Clock clock();// This instantiation will expose an abstract bank of four Standard Library// LEDs. As above, the __target="my_backend" annotation overrides the value// you placed on the root. This ensures that this module will be compiled// directly to your backend rather than beginning execution in software// simulation.(* __target="my_backend"*)Led#(4) led();// This declaration will expose a target-specific pci connection.// Target-specific declaration only need to specify their input and output// pins. The actual implementation will be provided by your compiler.modulePci();inputwire ...outputwire ...endmodule// This instantiation will expose your target-specific module. The __std="pci"// annotation will be used to distinguish this instantiation from user code. As// above, the __target="my_backend" annotation overrides the value you placed// on the root.(* __std="pci", __target="my_backend"*)Pci pci();// At this point you may have noticed that an --march file is just Verilog// code which is run before transferring control to the user. Now that your// environment is set up, in addition to $info() statements, you could place// any arbitrary code here as well.initialbegin $info("My Backend"); $info(" Supports the following Standard Library Components: Clock, Led"); $info(" Supports the following Target-Specific Components: Pci");end`endif
Next, you'll need to create a compiler for your backend. Take a look at theCoreCompiler
class which is defined insrc/target/core_compiler.h
.Your compiler must be a subclass of this type, and be able to compile userlogic as well as instantiations of any of the Standard Library components youintroduced in your--march
file. For the example shown above, this meansthat you would need to override the following methods:
virtual Led*compile_led(Interface* interface, ModuleDeclaration* md);virtual Logic*compile_logic(Interface* interface, ModuleDeclaration* md);
Additionally, because the example contains at least one target-specificcomponent, you would also need to override the following method:
virtual Custom*compile_custom(Interface* interface, ModuleDeclaration* md);
Details on implementing these methods can be found insrc/target/core_compiler.h
. In short, the goal of this class is transformaModuleDeclaration
(Verilog source code) for an instantiated module intoaCore
(target-specific executable implementation of that code), of whichLed
,Logic
, andCustom
are sub-types, whose implementationrelies on anInterface
(which will be compiled for you) to communicateback and forth with the Cascade runtime as necessary.
The definitions forCore
andInterface
can be found insrc/target/core.h
andsrc/target/interface.h
respectively. Yourcompiler will need to return target-specific instances of these classes. Bothfiles contain detailed information on the invariants that Cascade imposes onthe implementation of aCore
and the functionality provided by anInterface
. Briefly, aCore
is responsible for implementing supportfor the Cascade runtime ABI. This ABI is the mechanism by which Cascadeorchestrates the communication and computation necessary to satisfy thesemantics of Verilog while at the same time providing support for thejust-in-time features described above.
Once you've finished implementing your compiler, you'll need to register it byadding a few lines of code tosrc/cascade/cascade.cc
.
Cascade::Cascade() : eval_(this), iostream(&sb_), sb_() {// ... runtime_.get_compiler()->set("sw",newSwCompiler()); runtime_.get_compiler()->set("my_backend",newMyBackendCompiler());// ...}
Rebuild your source and... everything should just work... Happy debugging!
Some package managers fail to update the flex header fileFlexLexer.h
when upgrading versions.Locate your copy ofFlexLexer.h
and look up the definition ofyyFlexLexer::yyin
. Ifthis variable has typestd::istream*
, your file is out of date and you will need to patch it.The current version of this file, which should have been installed can be found in Flex's top-levelsrc/
directory.
Module declarations are typechecked in the global scope, separate from the restof your program. While this allows Cascade to catch many errors atdeclaration-time, there are some properties of Verilog programs which can onlybe verified at instantiation-time. If Cascade emits a warning, it is generallybecause it cannot statically prove that the module you declared willinstantiate correctly in every possible program context.
Why does Cascade warn that x is undeclared when I declare Foo, but not when I instantiate it (Part 1)?
localparamx=0;moduleFoo();wire q=x;endmoduleFoo f();
The local parameterx
was declared in the root module, and the moduleFoo
was declared in its own scope. In general, there is no way forCascade to guarantee that you will instantiateFoo
in a context where allof its declaration-time unresolved variables will be resolvable. In this case,it is statically provable, but Cascade doesn't know how to do so. WhenFoo
is instantiated, Cascade can verify that there is a variable namedp
which is reachable from the scope in whichf
appears. No furtherwarnings or errors are necessary. Here is a more general example:
moduleFoo();assignx.y.z=1;endmodule// ...begin :xbegin : yregz;endend// ...Foo f();// This instantiation will succeed because a variable named z// is reachable through the hierarchical name x.y.z from f.// ...begin : qreg r;end// ...Foo f();// This instantiation will fail because the only variable// reachable from f is q.r.
Why does Cascade warn that x is undeclared when I declare Foo, but not when I instantiate it (Part 2)?
module #(parameter N) Foo();genvar i;for (i=0; i< N; i=i+1)begin : GENregx;endwire q= GEN[5].x;endmoduleFoo#(8) f();
The registerx
was declared in a loop generate construct with boundsdetermined by a parameter. In general, there is no way for Cascade to guaranteethat you will instantiateFoo
with a parameter binding such that all ofits declaration-time unresolved variables are resolvable. WhenFoo
isinstantiated withN=8
, Cascade can verify that there is a variable namedGEN[5].x
. No further warnings or errors are necessary.
More generally, Cascade will defer typechecking for code that appears inside ofgenerate constructs until instantiation-time.
I get it, but it seems like there's something about pretty much every module declaration that Cascade can't prove.
The truth hurts. Remember that if you'd like to disable warnings you can type.
$ cascade --disable_warning