zamiaCAD does contain a full VHDL compiler (a parser for Verilog is almost done, parsers for other hardware description languages like AHDL are planned) and project build (make) facility. The whole build process is directed by the BuildPath.txt file which informs the builder where to find sources and what libraries to put them into.
Since sources may be stored on high-latency network mount points (e.g. NFS or AFS mounts) zamiaCAD has a built-in file system cache (FSCache) which it uses to cache files and directory information on you local disk for speed.
Parsing all VHDL sources accessible through the directories configured in a project's BuildPath.txt can be very time consuming and is often not neccesary since a significant portion of the design units defined in those files might never actually get instantiated or used in the project (this is especially true for technology/simulation libraries). Therefore, zamiaCAD comes with a very high-speed VHDL indexer which will only extract information about which design units are declared in which files. The actual VHDL parser is then used in an on-demand manner only on local source files (those which reside in your eclipse workspace) and files that define design units which are actually needed during elaboration.
Please note: At the time of this writing, the code base is still in a state of flux. Currently, the classes we are talking about here reside in a package called com.zamiacad.instgraph and carry the outdated IG prefix. The old com.zamiacad.zil package still exists and will be removed as soon as all legacy code has been ported to the new IG infrastructre (which then might be re-named to ZIL).
The image below gives you an overview of the class hierarchy of the ZIL/IG graph data structure in zamiaCAD . The diagram is not complete, many more classes exist below the ones depicted, this is just meant to give you a quick overview to get you started. Remember that if you're using the eclipse ide, you can always get a complete, auto-generated class hierarchy of any class by placing the cursor on the class name and press F4.
During elaboration various static values have to be computed (e.g. range boundaries for the type system or conditions and loop ranges when unrolling generate statement). For this purpose a complete interpreter has been implemented in the package com.zamaicad.instgraph.interpreter. The interpreter is implemented as a stack machine and supports all operations that can occur during elaboration, including math and logic operators and (potentially recursive) subprogram calls.
The whole ZIL/IG graph is designed in a way that is very scalable in terms of size and performance. Therefore, the graph is persisted in a nearly transparent way using ZDB (effectively by using plain old java serialization. In order to be able to do that the graph needs to partitioned in a smart way so individual piece can be persisted independently.
In ZIL/IG this is mainly done on the ContainerItem level: containers store declarative items such as Objects (Variables, Signals, Files, Constants), Types and Subprograms - but they never keep direct references to these objects but instead persist them in ZDB and just keep the database identifier (DBID - ZDB's equivalent to a primary key in a conventional relational database). Also, any other class that contains references to one of these objects stores DBIDs instead (this mainly applies to references to IGType) so we never persist these objects twice by accident.
For performance reasons, we cannot completely abstract away the effects of this persistence concept. However, in most places convenience functions like getType() exist which will silently and transparently do lazy loading on those objects which we cannot keep references on. In order to do that, IGItem (the base class of all ZIL/IG classes) has reference to the current ZDB instance plus a string that contains the name of the project it belongs to. Using these two pieces of data it is able to recover any reference to the ZamiaProject, the DUManager, the IGManager and of course ZDB itself by using ZDB's getOwner() method and the ZamiaProjectMap singleton.
ZDB is zamiaCAD 's own built-on object database. ZDB is based on Java's serialization feature which it uses to persist any POJO (plain old java object). ZDB allows users to easily and efficiently
for speed, a certain number of objects (configurable at compile time) are kept in RAM using a recently used cache eviction strategy.
This article gives you an introduction and overview of zamiaCAD 's simulator infrastructure from a developer's perspective.
Please note: This article contains preliminary information. The ZIL/IG-based simulation infrastructure is under construction at the time of this writing.
The ZIL/IG based simulation infrastructure resides in the com.zamiacad.instgraph.sim package.
zamiaCAD supports multiple simulator implementations, called simulator plugins. These plugins may be true simulators or waveform import plugins. Common to all of them is the interface com.zamiacad.instgraph.sim.IGISimulator which all simulator plugins need to implement.
Simulator plugins may rely on the ZIL/IG infrastructure in the Intermediate Layer. Of particular interest might be the math and logic operator implementations in IGStaticValue and the interpreter which resides in com.zamiacad.instgraph.interpreter.
Also, the types computed in the intermediate layer can directly be used for simulation.
The reference simulator resides in com.zamiacad.instgraph.sim.ref and relies heavily on the Intermediate Layer infrastructure. Basically it just adds a timing model for signals to the existing interpreter.
The VCD import plugin resides in com.zamiacad.instgraph.sim.vcd. It comes with a built-in VCD parser built using JavaCC and has been tested using GHDL-generated VCD files.
The RTL reference simulator resides in the package com.zamacad.rtl.sim. It relies on ZIL/IG code for math and logic operations (IGStaticValue). The package com.zamiacad.rtl.sim.behaviors contains classes implementing the semantics of the various RTL Graph node types. The simulator itself is an event based simulator residing in com.zamiacad.rtl.sim.Simulator that fully supports the VHDL timing model.
For interpreter modules (non-synthesizable parts of the design) the simulator will fall back to the ZIL/IG interpreter in com.zamiacad.instgraph.interpreter.
At the time of this writing, no other simulators besides the reference simulator exist. However, the infrastructure is there to support multiple simulator implementations. A new simulator implementation would have to implement the com.zamiacad.rtl.sim.ISimulator interface.
This article gives you a quick overview of how zamiaCAD will go about synthesizing an elaborated design into an RTL Graph. The RTL Graph primitives are described in RTL Modules.
As it turns out, VHDL code that is synthesizable according to IEEE 1076.6 basically consists of three constructs that are relevant (besides declarations and dependencies which have already been dealt with during parsing and elaboration):
These statements are concurrent statements so the order is not important, we can treat them as an unordered set of statements while processing them.
So, basically all we have to do for each module (architecture/entity pair) is figure out what signal bindings are generated in it.
entity e is port (A, B, C : in bit; X, Y, Z: out bit); end; architecture arch of e is signal S1, S2, S3 : bit; begin c1: foo port map (...); -- Component instantiations even: if a = '1' generate I1 : foo generic map ... end generate; Z <= A xor B; -- Signal assignments X <= A and C WHEN f(a, b) ELSE '0'; reg : process -- Process statements begin ... end process reg; end;
A Binding in zamiaCAD is a data structure holding information about
The basic idea here is to create a 1:1 mapping of VHDL constructs to RTL graph nodes. e.g.
in a more advanced version of the algorithm scheduling and allocation techniques could be used at this point to optimize the resulting graph for area and/or speed.
The biggest problem with this approach is partial signal assignments, e.g.
y[i] <= a or b; z.foo <= a and b;
to solve that, zamiaCAD uses TargetOperation nodes in the resulting graph. These TargetOperation nodes accept and generate pairs of inputs/outputs - one data and one enable signal each. The enable signal will carry the information which of the data signal elements is actually "valid" at any point in time. Examples of constructs where TargetOperation nodes get generated are:
Also, special nodes exists allowing to merging multiple TargetOperation bindings into one resulting signal using multiplexers.
For processes, zamiaCAD has to deal with
Therefore, a two-pass transformation algorithm is applied first:
The result of each pass can be dumped out as VHDL which makes debugging easier.
At this point we're left with signal assignments and if statements only. In a third and final pass we generate signal bindings. Since processes can produce synchroneous (clocked) and asynchroneous (latched) assignments, we need to expand the SignalBinding data structure to hold this information:
Once all bindings have been computed for a module, we have to decide which signals will need inferred memory elements:
Some VHDL constructs are not synthesizable, e.g.
assert j<i report "internal error";
wait for 100 ns;
To preserve these in the RTL Graph (and in order to generate an executable model) zamiaCAD will insert interpreter components:
architecture test of COUNTER_TB is ... begin implementation: COUNTER port map (REG_IN => REG_IN, ENABLE => ENABLE, ...); clk <= not clk after period / 2; process begin wait until clk'event and clk = '0'; ENABLE <= '0'; MODE <= '0'; res <= '1'; wait until clk'event and clk = '0'; ...
Generated interpreter code:
PUSH 0000000000000000000000000000000000000001100001101010000 WAIT FOR PUSH /COUNTER_TB(TEST)/intcomp_0:CLK PUSH /COUNTER_TB(TEST)/intcomp_0:CLK NOT DRIVE
generated RTL Graph:
only on the state signal
=> Signal becomes FSM output signal
architecture BEHAVIORAL of CONTROLER is type t_state is (s_reset, s_if1, s_if2, s_id, s_alu, s_ldih, ...); signal state, next_state : t_state; begin reg : process begin wait until clk'event and clk = '1'; state <= next_state; end process reg; transition : process (state, RES, IR, READY, Z_OUT) begin next_state <= state; if res = '1' then next_state <=s_reset; else case state is when s_reset => next_state <= s_if1; when s_if1 => if ready = '0' then next_state <= s_if2; ... output: process begin ... when s_if2 => IR_EN <= '1'; ADR_SEL <= '0'; RD <= '1'; when s_id => PC_EN <= '1'; PC_MODE <= '0'; when s_alu => REGIN_SEL <= "00"; RF_LE <= '1'; RF_HE <= '1'; when s_ldih => REGIN_SEL <= "10"; RF_HE <= '1';
Recovered FSM graph: